Skip to content

Commit 0e7c068

Browse files
fix: suppress config warnings during Sentry init and API mode detection (#1482)
1 parent 8674007 commit 0e7c068

File tree

9 files changed

+108
-39
lines changed

9 files changed

+108
-39
lines changed

scripts/dev.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
*/
1010

1111
import { join } from 'node:path';
12-
import { findProjectRoot } from '@tm/core';
12+
import { AuthManager, findProjectRoot } from '@tm/core';
13+
import { setSuppressConfigWarnings } from './modules/config-manager.js';
14+
1315
import dotenv from 'dotenv';
1416
import { initializeSentry } from '../src/telemetry/sentry.js';
1517

@@ -35,6 +37,19 @@ if (process.env.DEBUG === '1') {
3537
console.error('DEBUG - dev.js received args:', process.argv.slice(2));
3638
}
3739

40+
// Suppress config warnings if user is authenticated (API mode)
41+
// When authenticated, we don't need local config - everything is remote
42+
try {
43+
const authManager = AuthManager.getInstance();
44+
const hasValidSession = await authManager.hasValidSession();
45+
if (hasValidSession) {
46+
setSuppressConfigWarnings(true);
47+
}
48+
} catch {
49+
setSuppressConfigWarnings(false);
50+
// Auth check failed, continue without suppressing
51+
}
52+
3853
// Use dynamic import to ensure dotenv.config() runs before module-level code executes
3954
const { runCLI } = await import('./modules/commands.js');
4055

scripts/modules/commands.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ import {
7272
getDebugFlag,
7373
getDefaultNumTasks,
7474
isApiKeySet,
75-
isConfigFilePresent
75+
isConfigFilePresent,
76+
setSuppressConfigWarnings
7677
} from './config-manager.js';
7778

7879
import {
@@ -161,13 +162,17 @@ function isConnectedToHamster() {
161162
}
162163

163164
// Fallback: Check if storage type is 'api' (user selected Hamster during init)
165+
// Suppress warnings during this check since we're detecting API mode
166+
setSuppressConfigWarnings(true);
164167
try {
165-
const config = getConfig();
168+
const config = getConfig(null, false, { storageType: 'api' });
166169
if (config?.storage?.type === 'api') {
167170
return true;
168171
}
169172
} catch {
170173
// Config check failed, continue
174+
} finally {
175+
setSuppressConfigWarnings(false);
171176
}
172177

173178
return false;

scripts/modules/config-manager.js

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,23 @@ const DEFAULTS = {
7272
let loadedConfig = null;
7373
let loadedConfigRoot = null; // Track which root loaded the config
7474

75+
/**
76+
* Suppress config file warnings (useful during API mode detection)
77+
* Uses global object so it can be shared across modules without circular deps
78+
* @param {boolean} suppress - Whether to suppress warnings
79+
*/
80+
export function setSuppressConfigWarnings(suppress) {
81+
global._tmSuppressConfigWarnings = suppress;
82+
}
83+
84+
/**
85+
* Check if config warnings are currently suppressed
86+
* @returns {boolean}
87+
*/
88+
export function isConfigWarningSuppressed() {
89+
return global._tmSuppressConfigWarnings === true;
90+
}
91+
7592
// Custom Error for configuration issues
7693
class ConfigurationError extends Error {
7794
constructor(message) {
@@ -80,9 +97,10 @@ class ConfigurationError extends Error {
8097
}
8198
}
8299

83-
function _loadAndValidateConfig(explicitRoot = null) {
100+
function _loadAndValidateConfig(explicitRoot = null, options = {}) {
84101
const defaults = DEFAULTS; // Use the defined defaults
85102
let rootToUse = explicitRoot;
103+
const { storageType } = options;
86104
let configSource = explicitRoot
87105
? `explicit root (${explicitRoot})`
88106
: 'defaults (no root provided yet)';
@@ -114,7 +132,7 @@ function _loadAndValidateConfig(explicitRoot = null) {
114132
if (hasProjectMarkers) {
115133
// Only try to find config if we have project markers
116134
// This prevents the repeated warnings during init
117-
configPath = findConfigPath(null, { projectRoot: rootToUse });
135+
configPath = findConfigPath(null, { projectRoot: rootToUse, storageType });
118136
}
119137

120138
if (configPath) {
@@ -203,29 +221,36 @@ function _loadAndValidateConfig(explicitRoot = null) {
203221
}
204222
} else {
205223
// Config file doesn't exist at the determined rootToUse.
206-
if (explicitRoot) {
207-
// Only warn if an explicit root was *expected*.
208-
console.warn(
209-
chalk.yellow(
210-
`Warning: Configuration file not found at provided project root (${explicitRoot}). Using default configuration. Run 'task-master models --setup' to configure.`
211-
)
212-
);
213-
} else {
214-
// Don't warn about missing config during initialization
215-
// Only warn if this looks like an existing project (has .taskmaster dir or legacy config marker)
216-
const hasTaskmasterDir = fs.existsSync(
217-
path.join(rootToUse, TASKMASTER_DIR)
218-
);
219-
const hasLegacyMarker = fs.existsSync(
220-
path.join(rootToUse, LEGACY_CONFIG_FILE)
221-
);
222-
223-
if (hasTaskmasterDir || hasLegacyMarker) {
224+
// Skip warnings if:
225+
// 1. Global suppress flag is set (during API mode detection)
226+
// 2. storageType is explicitly 'api' (remote storage mode - no local config expected)
227+
const shouldWarn = !isConfigWarningSuppressed() && storageType !== 'api';
228+
229+
if (shouldWarn) {
230+
if (explicitRoot) {
231+
// Warn about explicit root not having config
224232
console.warn(
225233
chalk.yellow(
226-
`Warning: Configuration file not found at derived root (${rootToUse}). Using defaults.`
234+
`Warning: Configuration file not found at provided project root (${explicitRoot}). Using default configuration. Run 'task-master models --setup' to configure.`
227235
)
228236
);
237+
} else {
238+
// Don't warn about missing config during initialization
239+
// Only warn if this looks like an existing project (has .taskmaster dir or legacy config marker)
240+
const hasTaskmasterDir = fs.existsSync(
241+
path.join(rootToUse, TASKMASTER_DIR)
242+
);
243+
const hasLegacyMarker = fs.existsSync(
244+
path.join(rootToUse, LEGACY_CONFIG_FILE)
245+
);
246+
247+
if (hasTaskmasterDir || hasLegacyMarker) {
248+
console.warn(
249+
chalk.yellow(
250+
`Warning: Configuration file not found at derived root (${rootToUse}). Using defaults.`
251+
)
252+
);
253+
}
229254
}
230255
}
231256
// Keep config as defaults
@@ -241,17 +266,19 @@ function _loadAndValidateConfig(explicitRoot = null) {
241266
* Handles MCP initialization context gracefully.
242267
* @param {string|null} explicitRoot - Optional explicit path to the project root.
243268
* @param {boolean} forceReload - Force reloading the config file.
269+
* @param {object} options - Optional configuration options.
270+
* @param {'api'|'file'|'auto'} [options.storageType] - Storage type to suppress warnings for API mode.
244271
* @returns {object} The loaded configuration object.
245272
*/
246-
function getConfig(explicitRoot = null, forceReload = false) {
273+
function getConfig(explicitRoot = null, forceReload = false, options = {}) {
247274
// Determine if a reload is necessary
248275
const needsLoad =
249276
!loadedConfig ||
250277
forceReload ||
251278
(explicitRoot && explicitRoot !== loadedConfigRoot);
252279

253280
if (needsLoad) {
254-
const newConfig = _loadAndValidateConfig(explicitRoot); // _load handles null explicitRoot
281+
const newConfig = _loadAndValidateConfig(explicitRoot, options); // _load handles null explicitRoot
255282

256283
// Only update the global cache if loading was forced or if an explicit root
257284
// was provided (meaning we attempted to load a specific project's config).

src/telemetry/sentry.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import { createHash } from 'crypto';
44
* Provides error tracking and AI operation monitoring
55
*/
66
import * as Sentry from '@sentry/node';
7-
import { getAnonymousTelemetryEnabled } from '../../scripts/modules/config-manager.js';
8-
import { resolveEnvVariable } from '../../scripts/modules/utils.js';
7+
import {
8+
getAnonymousTelemetryEnabled,
9+
setSuppressConfigWarnings
10+
} from '../../scripts/modules/config-manager.js';
911

1012
let isInitialized = false;
1113

@@ -41,6 +43,8 @@ export function initializeSentry(options = {}) {
4143
// Check if user has opted out of anonymous telemetry
4244
// This applies to local storage users only
4345
// Hamster users don't use local config (API storage), so this check doesn't affect them
46+
// Suppress config warnings during this check to avoid noisy output at startup
47+
setSuppressConfigWarnings(true);
4448
try {
4549
const telemetryEnabled = getAnonymousTelemetryEnabled(options.projectRoot);
4650

@@ -54,6 +58,8 @@ export function initializeSentry(options = {}) {
5458
} catch (error) {
5559
// If there's an error checking telemetry preferences (e.g., config not available yet),
5660
// default to enabled. This ensures telemetry works during initialization.
61+
} finally {
62+
setSuppressConfigWarnings(false);
5763
}
5864

5965
// Use internal Sentry DSN for Task Master telemetry

src/utils/path-utils.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
TASKMASTER_TASKS_FILE
2424
} from '../constants/paths.js';
2525
import { getLoggerOrDefault } from './logger-utils.js';
26+
import { isConfigWarningSuppressed } from '../../scripts/modules/config-manager.js';
2627

2728
/**
2829
* Normalize project root to ensure it doesn't end with .taskmaster
@@ -453,15 +454,22 @@ export function findConfigPath(explicitPath = null, args = null, log = null) {
453454
}
454455

455456
// Only warn once per command execution to prevent spam during init
456-
const warningKey = `config_warning_${projectRoot}`;
457+
// Skip warning if:
458+
// Global suppress flag is set (during API mode detection)
459+
const shouldSkipWarning =
460+
isConfigWarningSuppressed() || args?.storageType === 'api';
457461

458-
if (!global._tmConfigWarningsThisRun) {
459-
global._tmConfigWarningsThisRun = new Set();
460-
}
462+
if (!shouldSkipWarning) {
463+
const warningKey = `config_warning_${projectRoot}`;
464+
465+
if (!global._tmConfigWarningsThisRun) {
466+
global._tmConfigWarningsThisRun = new Set();
467+
}
461468

462-
if (!global._tmConfigWarningsThisRun.has(warningKey)) {
463-
global._tmConfigWarningsThisRun.add(warningKey);
464-
logger.warn?.(`No configuration file found in project: ${projectRoot}`);
469+
if (!global._tmConfigWarningsThisRun.has(warningKey)) {
470+
global._tmConfigWarningsThisRun.add(warningKey);
471+
logger.warn?.(`No configuration file found in project: ${projectRoot}`);
472+
}
465473
}
466474

467475
return null;

tests/unit/ai-providers/claude-code-structured-output.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ jest.unstable_mockModule('../../../scripts/modules/config-manager.js', () => ({
6363
getDebugFlag: jest.fn(() => false),
6464
getLogLevel: jest.fn(() => 'info'),
6565
isProxyEnabled: jest.fn(() => false),
66-
getAnonymousTelemetryEnabled: jest.fn(() => true)
66+
getAnonymousTelemetryEnabled: jest.fn(() => true),
67+
setSuppressConfigWarnings: jest.fn(),
68+
isConfigWarningSuppressed: jest.fn(() => false)
6769
}));
6870

6971
// Mock utils

tests/unit/ai-providers/gemini-cli-structured-output.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ jest.unstable_mockModule('../../../scripts/modules/utils.js', () => ({
3838

3939
jest.unstable_mockModule('../../../scripts/modules/config-manager.js', () => ({
4040
isProxyEnabled: jest.fn(() => false),
41-
getAnonymousTelemetryEnabled: jest.fn(() => true)
41+
getAnonymousTelemetryEnabled: jest.fn(() => true),
42+
setSuppressConfigWarnings: jest.fn(),
43+
isConfigWarningSuppressed: jest.fn(() => false)
4244
}));
4345

4446
// Import after mocking

tests/unit/ai-providers/zai-provider.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ jest.unstable_mockModule('../../../scripts/modules/utils.js', () => ({
1717

1818
jest.unstable_mockModule('../../../scripts/modules/config-manager.js', () => ({
1919
isProxyEnabled: jest.fn(() => false),
20-
getAnonymousTelemetryEnabled: jest.fn(() => true)
20+
getAnonymousTelemetryEnabled: jest.fn(() => true),
21+
setSuppressConfigWarnings: jest.fn(),
22+
isConfigWarningSuppressed: jest.fn(() => false)
2123
}));
2224

2325
// Import after mocking

tests/unit/scripts/modules/task-manager/analyze-task-complexity.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,9 @@ jest.unstable_mockModule(
189189
getAllProviders: jest.fn(() => ['anthropic', 'openai', 'perplexity']),
190190
getVertexProjectId: jest.fn(() => undefined),
191191
getVertexLocation: jest.fn(() => undefined),
192-
hasCodebaseAnalysis: jest.fn(() => false)
192+
hasCodebaseAnalysis: jest.fn(() => false),
193+
setSuppressConfigWarnings: jest.fn(),
194+
isConfigWarningSuppressed: jest.fn(() => false)
193195
})
194196
);
195197

0 commit comments

Comments
 (0)