mirror of
https://github.com/eyaltoledano/claude-task-master.git
synced 2026-01-29 22:02:04 +00:00
fix: suppress config warnings during Sentry init and API mode detection (#1482)
This commit is contained in:
@@ -9,7 +9,9 @@
|
||||
*/
|
||||
|
||||
import { join } from 'node:path';
|
||||
import { findProjectRoot } from '@tm/core';
|
||||
import { AuthManager, findProjectRoot } from '@tm/core';
|
||||
import { setSuppressConfigWarnings } from './modules/config-manager.js';
|
||||
|
||||
import dotenv from 'dotenv';
|
||||
import { initializeSentry } from '../src/telemetry/sentry.js';
|
||||
|
||||
@@ -35,6 +37,19 @@ if (process.env.DEBUG === '1') {
|
||||
console.error('DEBUG - dev.js received args:', process.argv.slice(2));
|
||||
}
|
||||
|
||||
// Suppress config warnings if user is authenticated (API mode)
|
||||
// When authenticated, we don't need local config - everything is remote
|
||||
try {
|
||||
const authManager = AuthManager.getInstance();
|
||||
const hasValidSession = await authManager.hasValidSession();
|
||||
if (hasValidSession) {
|
||||
setSuppressConfigWarnings(true);
|
||||
}
|
||||
} catch {
|
||||
setSuppressConfigWarnings(false);
|
||||
// Auth check failed, continue without suppressing
|
||||
}
|
||||
|
||||
// Use dynamic import to ensure dotenv.config() runs before module-level code executes
|
||||
const { runCLI } = await import('./modules/commands.js');
|
||||
|
||||
|
||||
@@ -72,7 +72,8 @@ import {
|
||||
getDebugFlag,
|
||||
getDefaultNumTasks,
|
||||
isApiKeySet,
|
||||
isConfigFilePresent
|
||||
isConfigFilePresent,
|
||||
setSuppressConfigWarnings
|
||||
} from './config-manager.js';
|
||||
|
||||
import {
|
||||
@@ -161,13 +162,17 @@ function isConnectedToHamster() {
|
||||
}
|
||||
|
||||
// Fallback: Check if storage type is 'api' (user selected Hamster during init)
|
||||
// Suppress warnings during this check since we're detecting API mode
|
||||
setSuppressConfigWarnings(true);
|
||||
try {
|
||||
const config = getConfig();
|
||||
const config = getConfig(null, false, { storageType: 'api' });
|
||||
if (config?.storage?.type === 'api') {
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
// Config check failed, continue
|
||||
} finally {
|
||||
setSuppressConfigWarnings(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -72,6 +72,23 @@ const DEFAULTS = {
|
||||
let loadedConfig = null;
|
||||
let loadedConfigRoot = null; // Track which root loaded the config
|
||||
|
||||
/**
|
||||
* Suppress config file warnings (useful during API mode detection)
|
||||
* Uses global object so it can be shared across modules without circular deps
|
||||
* @param {boolean} suppress - Whether to suppress warnings
|
||||
*/
|
||||
export function setSuppressConfigWarnings(suppress) {
|
||||
global._tmSuppressConfigWarnings = suppress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if config warnings are currently suppressed
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isConfigWarningSuppressed() {
|
||||
return global._tmSuppressConfigWarnings === true;
|
||||
}
|
||||
|
||||
// Custom Error for configuration issues
|
||||
class ConfigurationError extends Error {
|
||||
constructor(message) {
|
||||
@@ -80,9 +97,10 @@ class ConfigurationError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
function _loadAndValidateConfig(explicitRoot = null) {
|
||||
function _loadAndValidateConfig(explicitRoot = null, options = {}) {
|
||||
const defaults = DEFAULTS; // Use the defined defaults
|
||||
let rootToUse = explicitRoot;
|
||||
const { storageType } = options;
|
||||
let configSource = explicitRoot
|
||||
? `explicit root (${explicitRoot})`
|
||||
: 'defaults (no root provided yet)';
|
||||
@@ -114,7 +132,7 @@ function _loadAndValidateConfig(explicitRoot = null) {
|
||||
if (hasProjectMarkers) {
|
||||
// Only try to find config if we have project markers
|
||||
// This prevents the repeated warnings during init
|
||||
configPath = findConfigPath(null, { projectRoot: rootToUse });
|
||||
configPath = findConfigPath(null, { projectRoot: rootToUse, storageType });
|
||||
}
|
||||
|
||||
if (configPath) {
|
||||
@@ -203,29 +221,36 @@ function _loadAndValidateConfig(explicitRoot = null) {
|
||||
}
|
||||
} else {
|
||||
// Config file doesn't exist at the determined rootToUse.
|
||||
if (explicitRoot) {
|
||||
// Only warn if an explicit root was *expected*.
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
`Warning: Configuration file not found at provided project root (${explicitRoot}). Using default configuration. Run 'task-master models --setup' to configure.`
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Don't warn about missing config during initialization
|
||||
// Only warn if this looks like an existing project (has .taskmaster dir or legacy config marker)
|
||||
const hasTaskmasterDir = fs.existsSync(
|
||||
path.join(rootToUse, TASKMASTER_DIR)
|
||||
);
|
||||
const hasLegacyMarker = fs.existsSync(
|
||||
path.join(rootToUse, LEGACY_CONFIG_FILE)
|
||||
);
|
||||
// Skip warnings if:
|
||||
// 1. Global suppress flag is set (during API mode detection)
|
||||
// 2. storageType is explicitly 'api' (remote storage mode - no local config expected)
|
||||
const shouldWarn = !isConfigWarningSuppressed() && storageType !== 'api';
|
||||
|
||||
if (hasTaskmasterDir || hasLegacyMarker) {
|
||||
if (shouldWarn) {
|
||||
if (explicitRoot) {
|
||||
// Warn about explicit root not having config
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
`Warning: Configuration file not found at derived root (${rootToUse}). Using defaults.`
|
||||
`Warning: Configuration file not found at provided project root (${explicitRoot}). Using default configuration. Run 'task-master models --setup' to configure.`
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Don't warn about missing config during initialization
|
||||
// Only warn if this looks like an existing project (has .taskmaster dir or legacy config marker)
|
||||
const hasTaskmasterDir = fs.existsSync(
|
||||
path.join(rootToUse, TASKMASTER_DIR)
|
||||
);
|
||||
const hasLegacyMarker = fs.existsSync(
|
||||
path.join(rootToUse, LEGACY_CONFIG_FILE)
|
||||
);
|
||||
|
||||
if (hasTaskmasterDir || hasLegacyMarker) {
|
||||
console.warn(
|
||||
chalk.yellow(
|
||||
`Warning: Configuration file not found at derived root (${rootToUse}). Using defaults.`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Keep config as defaults
|
||||
@@ -241,9 +266,11 @@ function _loadAndValidateConfig(explicitRoot = null) {
|
||||
* Handles MCP initialization context gracefully.
|
||||
* @param {string|null} explicitRoot - Optional explicit path to the project root.
|
||||
* @param {boolean} forceReload - Force reloading the config file.
|
||||
* @param {object} options - Optional configuration options.
|
||||
* @param {'api'|'file'|'auto'} [options.storageType] - Storage type to suppress warnings for API mode.
|
||||
* @returns {object} The loaded configuration object.
|
||||
*/
|
||||
function getConfig(explicitRoot = null, forceReload = false) {
|
||||
function getConfig(explicitRoot = null, forceReload = false, options = {}) {
|
||||
// Determine if a reload is necessary
|
||||
const needsLoad =
|
||||
!loadedConfig ||
|
||||
@@ -251,7 +278,7 @@ function getConfig(explicitRoot = null, forceReload = false) {
|
||||
(explicitRoot && explicitRoot !== loadedConfigRoot);
|
||||
|
||||
if (needsLoad) {
|
||||
const newConfig = _loadAndValidateConfig(explicitRoot); // _load handles null explicitRoot
|
||||
const newConfig = _loadAndValidateConfig(explicitRoot, options); // _load handles null explicitRoot
|
||||
|
||||
// Only update the global cache if loading was forced or if an explicit root
|
||||
// was provided (meaning we attempted to load a specific project's config).
|
||||
|
||||
@@ -4,8 +4,10 @@ import { createHash } from 'crypto';
|
||||
* Provides error tracking and AI operation monitoring
|
||||
*/
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { getAnonymousTelemetryEnabled } from '../../scripts/modules/config-manager.js';
|
||||
import { resolveEnvVariable } from '../../scripts/modules/utils.js';
|
||||
import {
|
||||
getAnonymousTelemetryEnabled,
|
||||
setSuppressConfigWarnings
|
||||
} from '../../scripts/modules/config-manager.js';
|
||||
|
||||
let isInitialized = false;
|
||||
|
||||
@@ -41,6 +43,8 @@ export function initializeSentry(options = {}) {
|
||||
// Check if user has opted out of anonymous telemetry
|
||||
// This applies to local storage users only
|
||||
// Hamster users don't use local config (API storage), so this check doesn't affect them
|
||||
// Suppress config warnings during this check to avoid noisy output at startup
|
||||
setSuppressConfigWarnings(true);
|
||||
try {
|
||||
const telemetryEnabled = getAnonymousTelemetryEnabled(options.projectRoot);
|
||||
|
||||
@@ -54,6 +58,8 @@ export function initializeSentry(options = {}) {
|
||||
} catch (error) {
|
||||
// If there's an error checking telemetry preferences (e.g., config not available yet),
|
||||
// default to enabled. This ensures telemetry works during initialization.
|
||||
} finally {
|
||||
setSuppressConfigWarnings(false);
|
||||
}
|
||||
|
||||
// Use internal Sentry DSN for Task Master telemetry
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
TASKMASTER_TASKS_FILE
|
||||
} from '../constants/paths.js';
|
||||
import { getLoggerOrDefault } from './logger-utils.js';
|
||||
import { isConfigWarningSuppressed } from '../../scripts/modules/config-manager.js';
|
||||
|
||||
/**
|
||||
* Normalize project root to ensure it doesn't end with .taskmaster
|
||||
@@ -453,15 +454,22 @@ export function findConfigPath(explicitPath = null, args = null, log = null) {
|
||||
}
|
||||
|
||||
// Only warn once per command execution to prevent spam during init
|
||||
const warningKey = `config_warning_${projectRoot}`;
|
||||
// Skip warning if:
|
||||
// Global suppress flag is set (during API mode detection)
|
||||
const shouldSkipWarning =
|
||||
isConfigWarningSuppressed() || args?.storageType === 'api';
|
||||
|
||||
if (!global._tmConfigWarningsThisRun) {
|
||||
global._tmConfigWarningsThisRun = new Set();
|
||||
}
|
||||
if (!shouldSkipWarning) {
|
||||
const warningKey = `config_warning_${projectRoot}`;
|
||||
|
||||
if (!global._tmConfigWarningsThisRun.has(warningKey)) {
|
||||
global._tmConfigWarningsThisRun.add(warningKey);
|
||||
logger.warn?.(`No configuration file found in project: ${projectRoot}`);
|
||||
if (!global._tmConfigWarningsThisRun) {
|
||||
global._tmConfigWarningsThisRun = new Set();
|
||||
}
|
||||
|
||||
if (!global._tmConfigWarningsThisRun.has(warningKey)) {
|
||||
global._tmConfigWarningsThisRun.add(warningKey);
|
||||
logger.warn?.(`No configuration file found in project: ${projectRoot}`);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -63,7 +63,9 @@ jest.unstable_mockModule('../../../scripts/modules/config-manager.js', () => ({
|
||||
getDebugFlag: jest.fn(() => false),
|
||||
getLogLevel: jest.fn(() => 'info'),
|
||||
isProxyEnabled: jest.fn(() => false),
|
||||
getAnonymousTelemetryEnabled: jest.fn(() => true)
|
||||
getAnonymousTelemetryEnabled: jest.fn(() => true),
|
||||
setSuppressConfigWarnings: jest.fn(),
|
||||
isConfigWarningSuppressed: jest.fn(() => false)
|
||||
}));
|
||||
|
||||
// Mock utils
|
||||
|
||||
@@ -38,7 +38,9 @@ jest.unstable_mockModule('../../../scripts/modules/utils.js', () => ({
|
||||
|
||||
jest.unstable_mockModule('../../../scripts/modules/config-manager.js', () => ({
|
||||
isProxyEnabled: jest.fn(() => false),
|
||||
getAnonymousTelemetryEnabled: jest.fn(() => true)
|
||||
getAnonymousTelemetryEnabled: jest.fn(() => true),
|
||||
setSuppressConfigWarnings: jest.fn(),
|
||||
isConfigWarningSuppressed: jest.fn(() => false)
|
||||
}));
|
||||
|
||||
// Import after mocking
|
||||
|
||||
@@ -17,7 +17,9 @@ jest.unstable_mockModule('../../../scripts/modules/utils.js', () => ({
|
||||
|
||||
jest.unstable_mockModule('../../../scripts/modules/config-manager.js', () => ({
|
||||
isProxyEnabled: jest.fn(() => false),
|
||||
getAnonymousTelemetryEnabled: jest.fn(() => true)
|
||||
getAnonymousTelemetryEnabled: jest.fn(() => true),
|
||||
setSuppressConfigWarnings: jest.fn(),
|
||||
isConfigWarningSuppressed: jest.fn(() => false)
|
||||
}));
|
||||
|
||||
// Import after mocking
|
||||
|
||||
@@ -189,7 +189,9 @@ jest.unstable_mockModule(
|
||||
getAllProviders: jest.fn(() => ['anthropic', 'openai', 'perplexity']),
|
||||
getVertexProjectId: jest.fn(() => undefined),
|
||||
getVertexLocation: jest.fn(() => undefined),
|
||||
hasCodebaseAnalysis: jest.fn(() => false)
|
||||
hasCodebaseAnalysis: jest.fn(() => false),
|
||||
setSuppressConfigWarnings: jest.fn(),
|
||||
isConfigWarningSuppressed: jest.fn(() => false)
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user