Skip to content

chore: address oauth PR concerns#1184

Merged
Crunchyman-ralph merged 8 commits intonext-typescriptfrom
ralph/implement.auth.ham.tweaks
Sep 7, 2025
Merged

chore: address oauth PR concerns#1184
Crunchyman-ralph merged 8 commits intonext-typescriptfrom
ralph/implement.auth.ham.tweaks

Conversation

@Crunchyman-ralph
Copy link
Collaborator

@Crunchyman-ralph Crunchyman-ralph commented Sep 4, 2025

What type of PR is this?

  • 🐛 Bug fix
  • ✨ Feature
  • 🔌 Integration
  • 📝 Docs
  • 🧹 Refactor
  • Other:

Description

Related Issues

How to Test This

# Example commands or steps

Expected result:

Contributor Checklist

  • Created changeset: npm run changeset
  • Tests pass: npm test
  • Format check passes: npm run format-check (or npm run format to fix)
  • Addressed CodeRabbit comments (if any)
  • Linked related issues (if any)
  • Manually tested the changes

Changelog Entry


For Maintainers

  • PR title follows conventional commits
  • Target branch correct
  • Labels added
  • Milestone assigned (if applicable)

Summary by CodeRabbit

  • New Features

    • Token refresh support and more reliable logout.
    • Automatic storage selection (“auto”): uses API when configured/authenticated, otherwise falls back to local.
    • Credential handling enhancements: clock-skew tolerant expiry checks, corrupt-file quarantine, and cleanup utility.
  • Breaking Changes

    • Removed API key authentication and header retrieval.
    • Renamed isUsingApiStorage to isApiExplicitlyConfigured; getStorageConfig now includes basePath and returns a new runtime type.
  • Bug Fixes

    • More robust credential parsing and clearer errors when API storage configuration is incomplete.
  • Tests

    • Added comprehensive tests for AuthManager and CredentialStore.

@changeset-bot
Copy link

changeset-bot bot commented Sep 4, 2025

⚠️ No Changeset found

Latest commit: 94bff83

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 4, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Implements ESM path updates across modules, adds AuthManager singleton reset and token refresh, enhances CredentialStore with expiry normalization, corruption quarantine, and cleanup, updates config interfaces (RuntimeStorageConfig, StorageType) and ConfigManager API, introduces StorageFactory.createFromStorageConfig with auto storage detection and API credential merging, and adjusts exports/tests accordingly.

Changes

Cohort / File(s) Summary
AuthManager and OAuth flow
packages/tm-core/src/auth/auth-manager.ts, packages/tm-core/src/auth/oauth-service.ts
AuthManager: nullable singleton, config warning on subsequent calls, resetInstance(), refreshToken(), improved logout; removed API-key methods. OAuth service imports switched to .js.
CredentialStore enhancements + tests
packages/tm-core/src/auth/credential-store.ts, packages/tm-core/src/auth/credential-store.test.ts
Adds clock skew, expiresAt normalization (number runtime, ISO persisted), quarantine corrupt files, cleanupCorruptFiles(), stricter save validation; comprehensive Vitest suite covering read/save/clear/cleanup.
Auth types and public exports
packages/tm-core/src/auth/types.ts, packages/tm-core/src/auth/config.ts, packages/tm-core/src/auth/index.ts
AuthCredentials expiresAt widened to string
Supabase client exports
packages/tm-core/src/clients/supabase-client.ts, packages/tm-core/src/clients/index.ts
Update imports and re-exports to .js paths; no logic changes.
Config surface and behavior
packages/tm-core/src/config/config-manager.ts, packages/tm-core/src/interfaces/configuration.interface.ts, packages/tm-core/src/config/config-manager.spec.ts
Introduces RuntimeStorageConfig and StorageType; getStorageConfig() now returns RuntimeStorageConfig with basePath; rename isUsingApiStorage()→isApiExplicitlyConfigured(); re-init after updateConfig; tests updated.
Storage factory and service integration
packages/tm-core/src/storage/storage-factory.ts, packages/tm-core/src/services/task-service.ts, packages/tm-core/src/services/index.ts
Adds StorageFactory.createFromStorageConfig(RuntimeStorageConfig,…); supports 'auto' type, API field validation, merges AuthManager credentials, chooses API/file at runtime; TaskService uses new factory; service export path updated.
Root exports and indices
packages/tm-core/src/index.ts, packages/tm-core/src/interfaces/index.ts, packages/tm-core/src/interfaces/storage.interface.ts, packages/tm-core/src/parser/index.ts, packages/tm-core/src/utils/index.ts
Re-export paths standardized to .js (ESM); no functional changes.
Logger import typing
packages/tm-core/src/logger/factory.ts
Makes LoggerConfig a type-only import; no runtime change.
TypeScript config
packages/tm-core/tsconfig.json
moduleResolution: bundler; moduleDetection: force; adds Node types.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App
  participant AM as AuthManager
  participant CS as CredentialStore
  participant SB as SupabaseAuthClient
  participant Log as Logger

  rect rgb(240,248,255)
  note right of AM: Token refresh flow
  App->>AM: refreshToken()
  AM->>CS: getCredentials({ allowExpired: true })
  alt missing refreshToken
    AM-->>App: throw AuthenticationError('REFRESH_FAILED')
  else have refreshToken
    AM->>SB: refreshSession(refreshToken)
    alt success
      SB-->>AM: { access_token, refresh_token, expires_at }
      AM->>CS: saveCredentials(updated)
      AM-->>App: updated credentials
    else failure
      AM->>Log: warn("Failed to refresh session")
      AM-->>App: throw AuthenticationError('REFRESH_FAILED')
    end
  end
  end
Loading
sequenceDiagram
  autonumber
  actor App
  participant AM as AuthManager
  participant SF as StorageFactory
  participant CM as ConfigManager
  participant Log as Logger

  rect rgb(245,255,245)
  note right of SF: Storage selection (auto/configured)
  App->>CM: getStorageConfig()
  CM-->>App: RuntimeStorageConfig
  App->>SF: createFromStorageConfig(storageConfig, projectPath)
  alt storage.type == 'api'
    alt required fields present
      SF-->>App: ApiStorage
    else missing fields
      SF-->>App: throw TaskMasterError(missing fields)
    end
  else storage.type == 'auto'
    alt API explicitly configured
      SF-->>App: ApiStorage (configured)
    else AuthManager has credentials
      SF->>AM: getInstance().getCredentials()
      SF-->>App: ApiStorage (merged credentials)
    else
      SF-->>App: FileStorage
    end
  else storage.type == 'file'
    SF-->>App: FileStorage
  end
  end
Loading
sequenceDiagram
  autonumber
  actor App
  participant AM as AuthManager
  participant Log as Logger

  rect rgb(255,250,240)
  note right of AM: Singleton lifecycle
  App->>AM: getInstance(configA)
  AM-->>App: instance A
  App->>AM: getInstance(configB)
  alt already initialized and configB provided
    AM->>Log: warn("Config ignored after initialization")
  end
  AM-->>App: same instance A
  App->>AM: resetInstance()
  AM-->>App: void
  App->>AM: getInstance(configB)
  AM-->>App: new instance B
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • eyaltoledano

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84056d6 and 94bff83.

📒 Files selected for processing (16)
  • packages/tm-core/src/auth/config.ts (1 hunks)
  • packages/tm-core/src/auth/credential-store.test.ts (1 hunks)
  • packages/tm-core/src/auth/credential-store.ts (4 hunks)
  • packages/tm-core/src/auth/index.ts (1 hunks)
  • packages/tm-core/src/auth/oauth-service.ts (1 hunks)
  • packages/tm-core/src/clients/index.ts (1 hunks)
  • packages/tm-core/src/clients/supabase-client.ts (1 hunks)
  • packages/tm-core/src/index.ts (2 hunks)
  • packages/tm-core/src/interfaces/configuration.interface.ts (2 hunks)
  • packages/tm-core/src/interfaces/index.ts (1 hunks)
  • packages/tm-core/src/interfaces/storage.interface.ts (1 hunks)
  • packages/tm-core/src/logger/factory.ts (1 hunks)
  • packages/tm-core/src/parser/index.ts (1 hunks)
  • packages/tm-core/src/services/index.ts (1 hunks)
  • packages/tm-core/src/utils/index.ts (1 hunks)
  • packages/tm-core/tsconfig.json (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ralph/implement.auth.ham.tweaks

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
packages/tm-core/src/auth/credential-store.ts (2)

45-55: Use explicit checks for expiry; avoid truthy guards that skip 0 and NaN cases.

-// Check if token is expired (API keys never expire)
-const isApiKey = authData.tokenType === 'api_key';
-if (
-  !isApiKey &&
-  expiresAtMs &&
-  expiresAtMs < Date.now() &&
-  !options?.allowExpired
-) {
+// Check if token is expired (API keys never expire)
+const isApiKey = authData.tokenType === 'api_key';
+if (
+  !isApiKey &&
+  expiresAtMs !== undefined &&
+  Date.now() >= expiresAtMs &&
+  !options?.allowExpired
+) {
   this.logger.warn('Authentication token has expired');
   return null;
 }

129-133: Consider exposing a boolean hasValidCredentials() variant that surfaces expiry reason.

Optional: return a discriminated union with reason ('missing' | 'expired' | 'invalid') for better UX/telemetry.

packages/tm-core/src/interfaces/configuration.interface.ts (1)

107-122: Deduplicate properties and use the StorageType alias to avoid drift.

StorageSettings already inherits type, apiEndpoint, apiAccessToken. Redeclare only when changing variance. Prefer StorageType here; keep apiConfigured optional but consider readonly.

-export interface StorageSettings
-  extends Omit<RuntimeStorageConfig, 'apiConfigured'> {
-  /** Storage backend type - 'auto' detects based on auth status */
-  type: 'file' | 'api' | 'auto';
-  /** Base path for file storage */
-  basePath?: string;
-  /** API endpoint for API storage (Hamster integration) */
-  apiEndpoint?: string;
-  /** Access token for API authentication */
-  apiAccessToken?: string;
+export interface StorageSettings
+  extends Omit<RuntimeStorageConfig, 'apiConfigured'> {
+  /** Storage backend type - 'auto' detects based on auth status */
+  type: StorageType;
+  /** Base path for file storage */
+  basePath?: string;
@@
-  apiConfigured?: boolean;
+  /** Computed internally */
+  readonly apiConfigured?: boolean;
packages/tm-core/src/config/config-manager.ts (3)

140-156: Regression: file-storage basePath is dropped by new RuntimeStorageConfig flow.

getStorageConfig() no longer surfaces storage.basePath. Downstream StorageFactory.createFromStorageConfig() wraps only this runtime object, so createFileStorage() falls back to projectPath and ignores user-configured basePath. This breaks existing setups that rely on a custom storage path.

Apply both diffs to carry basePath through the runtime surface.

  1. interfaces/configuration.interface.ts — add basePath to RuntimeStorageConfig:
 export interface RuntimeStorageConfig {
 	/** Storage backend type */
 	type: StorageType;
+	/** Base path for file storage (if configured) */
+	basePath?: string;
 	/** API endpoint for API storage (Hamster integration) */
 	apiEndpoint?: string;
 	/** Access token for API authentication */
 	apiAccessToken?: string;
 	/**
 	 * Indicates whether API is configured (has endpoint or token)
 	 * @computed Derived automatically from presence of apiEndpoint or apiAccessToken
 	 * @internal Should not be set manually - computed by ConfigManager
 	 */
 	apiConfigured: boolean;
 }
  1. config-manager.ts — include basePath in the returned object:
 	getStorageConfig(): RuntimeStorageConfig {
 		const storage = this.config.storage;

 		// Return the configured type (including 'auto')
 		const storageType = storage?.type || 'auto';
+		const basePath = storage?.basePath;

 		if (storageType === 'api' || storageType === 'auto') {
 			return {
 				type: storageType,
+				basePath,
 				apiEndpoint: storage?.apiEndpoint,
 				apiAccessToken: storage?.apiAccessToken,
 				apiConfigured: Boolean(storage?.apiEndpoint || storage?.apiAccessToken)
 			};
 		}

-		return { type: storageType, apiConfigured: false };
+		return { type: storageType, basePath, apiConfigured: false };
 	}

213-222: Bug: updateConfig() doesn’t actually re-merge due to initialize() guard.

initialize() early-returns when initialized is true, so updates never reload precedence stacks.

Apply this minimal fix:

 	async updateConfig(updates: PartialConfiguration): Promise<void> {
 		// Merge updates into current config
 		Object.assign(this.config, updates);

 		// Save to persistence
 		await this.persistence.saveConfig(this.config);

 		// Re-initialize to respect precedence
-		await this.initialize();
+		this.initialized = false;
+		await this.initialize();
 	}

188-190: Clarify or adjust isUsingApiStorage to handle ‘auto’
Refactor: either rename this to isApiExplicitlyConfigured() (to reflect that it only checks type === 'api') or update it to compute the runtime‐resolved storage type—e.g. via a new StorageFactory.getEffectiveStorageType(config)—so that it returns true when ‘auto’ resolves to API.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6dd910f and 91b5f81.

📒 Files selected for processing (7)
  • packages/tm-core/src/auth/auth-manager.test.ts (1 hunks)
  • packages/tm-core/src/auth/auth-manager.ts (0 hunks)
  • packages/tm-core/src/auth/credential-store.ts (4 hunks)
  • packages/tm-core/src/config/config-manager.ts (2 hunks)
  • packages/tm-core/src/interfaces/configuration.interface.ts (2 hunks)
  • packages/tm-core/src/services/task-service.ts (1 hunks)
  • packages/tm-core/src/storage/storage-factory.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • packages/tm-core/src/auth/auth-manager.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/git_workflow.mdc)

**/*.{test,spec}.{js,ts,jsx,tsx}: Create a test file and ensure all tests pass when all subtasks are complete; commit tests if added or modified
When all subtasks are complete, run final testing using the appropriate test runner (e.g., npm test, jest, or manual testing)

Files:

  • packages/tm-core/src/auth/auth-manager.test.ts
**/*.{test,spec}.*

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

Test files should follow naming conventions: .test., .spec., or _test. depending on the language

Files:

  • packages/tm-core/src/auth/auth-manager.test.ts
**/?(*.)+(spec|test).ts

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

In JavaScript/TypeScript projects using Jest, test files should match *.test.ts and *.spec.ts patterns

Files:

  • packages/tm-core/src/auth/auth-manager.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

**/*.test.ts: Use established mocking patterns for dependencies such as bcrypt and Prisma in test files
Test all code paths, including edge cases and error scenarios, in test files
Use descriptive names for test functions, such as should_return_error_for_invalid_input

Files:

  • packages/tm-core/src/auth/auth-manager.test.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/src/auth/config.ts:5-7
Timestamp: 2025-09-02T21:51:27.921Z
Learning: The user Crunchyman-ralph prefers not to use node: scheme imports (e.g., 'node:os', 'node:path') for Node.js core modules and considers suggestions to change bare imports to node: scheme as too nitpicky.
📚 Learning: 2025-08-03T12:13:33.875Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/test_workflow.mdc:0-0
Timestamp: 2025-08-03T12:13:33.875Z
Learning: Applies to **/*.test.ts : Use established mocking patterns for dependencies such as bcrypt and Prisma in test files

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{integration,e2e}/**/*.test.js : Properly mock session objects when required by functions, and reset environment variables between tests if modified.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-08-03T12:13:33.875Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/test_workflow.mdc:0-0
Timestamp: 2025-08-03T12:13:33.875Z
Learning: Applies to {tests/setup.ts,tests/setup/integration.ts,tests/teardown.ts} : Test setup files should be created at tests/setup.ts, tests/setup/integration.ts, and tests/teardown.ts

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:06:04.909Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_providers.mdc:0-0
Timestamp: 2025-07-18T17:06:04.909Z
Learning: Applies to tests/unit/ai-providers/*.test.js : Create unit tests for the new provider in tests/unit/ai-providers/<provider-name>.test.js, mocking ai-sdk/<provider-name> and core ai module functions, and testing all exported functions for correct behavior and error handling.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
🧬 Code graph analysis (5)
packages/tm-core/src/auth/credential-store.ts (2)
tests/unit/config-manager.test.mjs (1)
  • baseName (192-192)
packages/tm-core/src/logger/logger.ts (1)
  • error (163-166)
packages/tm-core/src/storage/storage-factory.ts (1)
packages/tm-core/src/interfaces/configuration.interface.ts (2)
  • RuntimeStorageConfig (88-101)
  • IConfiguration (200-242)
packages/tm-core/src/services/task-service.ts (1)
packages/tm-core/src/storage/storage-factory.ts (1)
  • StorageFactory (19-261)
packages/tm-core/src/auth/auth-manager.test.ts (1)
packages/tm-core/src/auth/auth-manager.ts (1)
  • AuthManager (18-145)
packages/tm-core/src/config/config-manager.ts (1)
packages/tm-core/src/interfaces/configuration.interface.ts (1)
  • RuntimeStorageConfig (88-101)
🪛 ast-grep (0.38.6)
packages/tm-core/src/auth/credential-store.ts

[warning] 148-148: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^${baseName}\\.corrupt-\\d+$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🔇 Additional comments (8)
packages/tm-core/src/auth/auth-manager.test.ts (1)

24-30: LGTM: verifies singleton identity.

packages/tm-core/src/auth/credential-store.ts (1)

63-76: Quarantine logic LGTM; good defensive handling.

packages/tm-core/src/interfaces/configuration.interface.ts (3)

76-83: LGTM: clear storage type union including 'auto'.


84-102: LGTM: RuntimeStorageConfig shape is sensible; apiConfigured as computed flag makes sense.


425-433: Verification complete – no downstream updates required.
All getStorageConfig() and StorageFactory.createFromStorageConfig() signatures and call sites match the new RuntimeStorageConfig, and there are no remaining direct references to moved storage settings.

packages/tm-core/src/config/config-manager.ts (1)

9-12: Type import changes look good.

Importing RuntimeStorageConfig aligns this module with the new runtime storage surface.

packages/tm-core/src/storage/storage-factory.ts (2)

213-217: Validation for 'auto' type LGTM.

Recognizing 'auto' explicitly removes false-positive validation errors.


6-11: Import path correct – no changes needed. packages/tm-core/src/storage/file-storage/index.ts exports FileStorage as both default and named.

@Crunchyman-ralph Crunchyman-ralph changed the title chore: address concerns chore: address oauth PR concerns Sep 4, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/tm-core/src/auth/auth-manager.ts (4)

20-21: Type-safe singleton: avoid null casts and allow reset with undefined.

Current null as any is unsafe. Make the static field optional and handle undefined.

-export class AuthManager {
-	private static instance: AuthManager;
+export class AuthManager {
+	private static instance: AuthManager | undefined;
@@
 	static resetInstance(): void {
-		AuthManager.instance = null as any;
+		AuthManager.instance = undefined;
 	}

Also applies to: 50-52


115-135: Wrap refresh failures with a consistent AuthenticationError.

Preserve cause while returning a typed error code.

-		try {
+		try {
 			// Use Supabase client to refresh the token
 			const response = await this.supabaseClient.refreshSession(
 				authData.refreshToken
 			);
@@
 			this.credentialStore.saveCredentials(newAuthData);
 			return newAuthData;
 		} catch (error) {
-			throw error;
+			throw new AuthenticationError(
+				'Failed to refresh authentication token',
+				'REFRESH_FAILED',
+				error
+			);
 		}

140-147: Use project logger instead of console.warn.

Aligns with logging strategy and testability.

-		} catch (error) {
-			// Log but don't throw - we still want to clear local credentials
-			console.warn('Failed to sign out from Supabase:', error);
+		} catch (error) {
+			// Log but don't throw - we still want to clear local credentials
+			getLogger('AuthManager').warn('Failed to sign out from Supabase:', error);
 		}

81-98: API key validation: consider centralizing constraints.

The length check is arbitrary; if there’s a schema (e.g., prefix or charset), validate against it and document. Otherwise keep minimal to avoid false negatives.

♻️ Duplicate comments (4)
packages/tm-core/src/auth/credential-store.ts (2)

155-161: Escape baseName in dynamic regex to avoid false matches/ReDoS.

-			const baseName = path.basename(this.config.configFile);
-			const corruptPattern = new RegExp(`^${baseName}\\.corrupt-\\d+$`);
+			const baseName = path.basename(this.config.configFile);
+			const escaped = baseName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+			const corruptPattern = new RegExp(`^${escaped}\\.corrupt-\\d+$`);

165-186: Reduce I/O: filter Dirents before stat.

-			const files = fs.readdirSync(dir);
+			const entries = fs.readdirSync(dir, { withFileTypes: true });
 			const now = Date.now();
 
-			for (const file of files) {
-				if (corruptPattern.test(file)) {
-					const filePath = path.join(dir, file);
+			for (const entry of entries) {
+				if (!entry.isFile()) continue;
+				const file = entry.name;
+				if (corruptPattern.test(file)) {
+					const filePath = path.join(dir, file);
packages/tm-core/src/auth/auth-manager.test.ts (2)

21-24: Clear mocks between tests to avoid leakage.

  beforeEach(() => {
    // Reset singleton before each test
    AuthManager.resetInstance();
+   vi.clearAllMocks();
  });

55-57: Make warning assertion resilient to wording changes.

-    expect(mockLogger.warn).toHaveBeenCalledWith(
-      'getInstance called with config after initialization; config is ignored.'
-    );
+    expect(mockLogger.warn).toHaveBeenCalledWith(
+      expect.stringMatching(/config.*after initialization.*ignored/i)
+    );
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 91b5f81 and 70ef129.

📒 Files selected for processing (3)
  • packages/tm-core/src/auth/auth-manager.test.ts (1 hunks)
  • packages/tm-core/src/auth/auth-manager.ts (2 hunks)
  • packages/tm-core/src/auth/credential-store.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/git_workflow.mdc)

**/*.{test,spec}.{js,ts,jsx,tsx}: Create a test file and ensure all tests pass when all subtasks are complete; commit tests if added or modified
When all subtasks are complete, run final testing using the appropriate test runner (e.g., npm test, jest, or manual testing)

Files:

  • packages/tm-core/src/auth/auth-manager.test.ts
**/*.{test,spec}.*

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

Test files should follow naming conventions: .test., .spec., or _test. depending on the language

Files:

  • packages/tm-core/src/auth/auth-manager.test.ts
**/?(*.)+(spec|test).ts

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

In JavaScript/TypeScript projects using Jest, test files should match *.test.ts and *.spec.ts patterns

Files:

  • packages/tm-core/src/auth/auth-manager.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

**/*.test.ts: Use established mocking patterns for dependencies such as bcrypt and Prisma in test files
Test all code paths, including edge cases and error scenarios, in test files
Use descriptive names for test functions, such as should_return_error_for_invalid_input

Files:

  • packages/tm-core/src/auth/auth-manager.test.ts
🧠 Learnings (16)
📓 Common learnings
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/src/auth/config.ts:5-7
Timestamp: 2025-09-02T21:51:27.921Z
Learning: The user Crunchyman-ralph prefers not to use node: scheme imports (e.g., 'node:os', 'node:path') for Node.js core modules and considers suggestions to change bare imports to node: scheme as too nitpicky.
📚 Learning: 2025-08-03T12:13:33.875Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/test_workflow.mdc:0-0
Timestamp: 2025-08-03T12:13:33.875Z
Learning: Applies to **/*.test.ts : Use established mocking patterns for dependencies such as bcrypt and Prisma in test files

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{integration,e2e}/**/*.test.js : Properly mock session objects when required by functions, and reset environment variables between tests if modified.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:06:04.909Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_providers.mdc:0-0
Timestamp: 2025-07-18T17:06:04.909Z
Learning: Applies to tests/unit/ai-providers/*.test.js : Create unit tests for the new provider in tests/unit/ai-providers/<provider-name>.test.js, mocking ai-sdk/<provider-name> and core ai module functions, and testing all exported functions for correct behavior and error handling.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Be careful with how you mock or stub functions that depend on module state; use factory functions in mocks to ensure proper initialization order.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Use jest.spyOn() after imports to create spies on mock functions and reference these spies in test assertions.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : Do not try to use the real action implementation without proper mocking, and do not mock Commander partially—either mock it completely or test the action directly.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Follow the test file organization: mocks must be set up before importing modules under test, and spies on mocked modules should be set up after imports.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Do not import real AI service clients in tests; create fully mocked versions that return predictable responses.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Troubleshoot mock functions not being called by ensuring mocks are defined before imports, referencing the correct mock instance, and verifying import paths.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/**/*.test.js : Follow the mock-first-then-import pattern and use jest.spyOn() for testing in Jest, clearing mocks between tests.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : When testing CLI commands built with Commander.js, test the command action handlers directly rather than trying to mock the entire Commander.js chain.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Clear mocks between tests with jest.clearAllMocks() in beforeEach.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Isolate tests by properly mocking shared resources and resetting state in beforeEach and afterEach hooks.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Do not rely on environment variables for API keys in tests; set mock environment variables in test setup.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Verify file operations use the correct paths in expect statements.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
🧬 Code graph analysis (2)
packages/tm-core/src/auth/auth-manager.ts (2)
packages/tm-core/src/logger/factory.ts (1)
  • getLogger (23-44)
packages/tm-core/src/logger/index.ts (1)
  • getLogger (8-8)
packages/tm-core/src/auth/credential-store.ts (2)
packages/tm-core/src/auth/types.ts (1)
  • AuthenticationError (73-85)
packages/tm-core/src/logger/logger.ts (1)
  • error (163-166)
🪛 ast-grep (0.38.6)
packages/tm-core/src/auth/credential-store.ts

[warning] 158-158: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^${baseName}\\.corrupt-\\d+$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🔇 Additional comments (2)
packages/tm-core/src/auth/auth-manager.test.ts (2)

8-18: Logger mock pattern LGTM.

Shared singleton mock ensures assertions hit the same instance used by production code.


60-72: No-warning case LGTM.

Asserts on the shared mock; clear and correct.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/tm-core/src/interfaces/configuration.interface.ts (1)

423-427: Provide defaults for required storage booleans.

StorageSettings declares enableBackup, enableCompression, and atomicOperations as required booleans, but DEFAULT_CONFIG_VALUES.STORAGE lacks defaults, risking undefined fields in defaults and downstream code.

Apply:

 STORAGE: {
   TYPE: 'auto' as const,
   ENCODING: 'utf8' as BufferEncoding,
-  MAX_BACKUPS: 5
+  MAX_BACKUPS: 5,
+  // New boolean defaults for required fields
+  ENABLE_BACKUP: false,
+  ENABLE_COMPRESSION: false,
+  ATOMIC_OPERATIONS: true
 },

And ensure the factory maps these to:

  • storage.enableBackup = DEFAULT_CONFIG_VALUES.STORAGE.ENABLE_BACKUP
  • storage.enableCompression = DEFAULT_CONFIG_VALUES.STORAGE.ENABLE_COMPRESSION
  • storage.atomicOperations = DEFAULT_CONFIG_VALUES.STORAGE.ATOMIC_OPERATIONS
packages/tm-core/src/storage/storage-factory.ts (1)

58-86: Clarify error when 'api' is only partially configured (better DX)

If one of apiEndpoint/apiAccessToken is missing and there is no session, the message “not authenticated” can mislead. Include which fields are missing and actionable guidance.

Apply:

 case 'api':
-  if (!StorageFactory.isHamsterAvailable(config)) {
-    // Check if authenticated via AuthManager
-    const authManager = AuthManager.getInstance();
-    if (!authManager.isAuthenticated()) {
-      throw new TaskMasterError(
-        'API storage configured but not authenticated. Run: tm auth login',
-        ERROR_CODES.MISSING_CONFIGURATION,
-        { storageType: 'api' }
-      );
-    }
+  if (!StorageFactory.isHamsterAvailable(config)) {
+    const missing: string[] = [];
+    if (!config.storage?.apiEndpoint) missing.push('apiEndpoint');
+    if (!config.storage?.apiAccessToken) missing.push('apiAccessToken');
+
+    // Check if authenticated via AuthManager
+    const authManager = AuthManager.getInstance();
+    if (!authManager.isAuthenticated()) {
+      throw new TaskMasterError(
+        `API storage not fully configured (${missing.join(', ') || 'credentials missing'}). Run: tm auth login, or set the missing field(s).`,
+        ERROR_CODES.MISSING_CONFIGURATION,
+        { storageType: 'api', missing }
+      );
+    }
packages/tm-core/src/auth/auth-manager.ts (1)

117-124: Use the logger instead of console.warn for consistency

Route sign-out warnings through the logging system.

Apply:

-    } catch (error) {
-      // Log but don't throw - we still want to clear local credentials
-      console.warn('Failed to sign out from Supabase:', error);
-    }
+    } catch (error) {
+      // Log but don't throw - we still want to clear local credentials
+      getLogger('AuthManager').warn('Failed to sign out from Supabase:', error);
+    }
♻️ Duplicate comments (5)
packages/tm-core/src/auth/auth-manager.test.ts (1)

62-64: Make warning assertion resilient to wording.

Avoid brittle exact-string match; assert via regex/substring.

Apply:

-    expect(mockLogger.warn).toHaveBeenCalledWith(
-      'getInstance called with config after initialization; config is ignored.'
-    );
+    expect(mockLogger.warn).toHaveBeenCalledWith(
+      expect.stringMatching(/config.*after initialization.*ignored/i)
+    );
packages/tm-core/src/storage/storage-factory.ts (1)

27-37: Propagation of projectPath fixed — resolves prior review

Passing { storage: storageConfig, projectPath } into create() correctly populates ApiStorage.projectId. Looks good.

packages/tm-core/src/auth/auth-manager.ts (1)

20-20: Type the singleton as nullable; drop ! and as any

Make instance AuthManager | null to avoid null as any in resetInstance() and the non-null assertion on return.

Apply:

-export class AuthManager {
-  private static instance: AuthManager;
+export class AuthManager {
+  private static instance: AuthManager | null = null;
@@
-  static getInstance(config?: Partial<AuthConfig>): AuthManager {
-    if (!AuthManager.instance) {
-      AuthManager.instance = new AuthManager(config);
-    } else if (config) {
+  static getInstance(config?: Partial<AuthConfig>): AuthManager {
+    if (!AuthManager.instance) {
+      AuthManager.instance = new AuthManager(config);
+    } else if (config) {
       // Warn if config is provided after initialization
       const logger = getLogger('AuthManager');
       logger.warn(
         'getInstance called with config after initialization; config is ignored.'
       );
     }
-    return AuthManager.instance!;
+    return AuthManager.instance;
   }
@@
-  static resetInstance(): void {
-    AuthManager.instance = null as any;
-  }
+  static resetInstance(): void {
+    AuthManager.instance = null;
+  }

Also applies to: 34-45, 50-52

packages/tm-core/src/auth/credential-store.ts (2)

32-44: Good fix: robust timestamp normalization.

Explicit finite/NaN checks handle both number and ISO string correctly and address the prior NaN truthiness bug.


100-125: Good improvements: no input mutation and strict validation.

Cloning to add savedAt and validating expiresAt with Number.isNaN/finite looks solid. Matches prior suggestions.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 70ef129 and 538d745.

📒 Files selected for processing (8)
  • packages/tm-core/src/auth/auth-manager.test.ts (1 hunks)
  • packages/tm-core/src/auth/auth-manager.ts (2 hunks)
  • packages/tm-core/src/auth/credential-store.test.ts (1 hunks)
  • packages/tm-core/src/auth/credential-store.ts (4 hunks)
  • packages/tm-core/src/auth/types.ts (2 hunks)
  • packages/tm-core/src/config/config-manager.ts (4 hunks)
  • packages/tm-core/src/interfaces/configuration.interface.ts (1 hunks)
  • packages/tm-core/src/storage/storage-factory.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/git_workflow.mdc)

**/*.{test,spec}.{js,ts,jsx,tsx}: Create a test file and ensure all tests pass when all subtasks are complete; commit tests if added or modified
When all subtasks are complete, run final testing using the appropriate test runner (e.g., npm test, jest, or manual testing)

Files:

  • packages/tm-core/src/auth/credential-store.test.ts
  • packages/tm-core/src/auth/auth-manager.test.ts
**/*.{test,spec}.*

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

Test files should follow naming conventions: .test., .spec., or _test. depending on the language

Files:

  • packages/tm-core/src/auth/credential-store.test.ts
  • packages/tm-core/src/auth/auth-manager.test.ts
**/?(*.)+(spec|test).ts

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

In JavaScript/TypeScript projects using Jest, test files should match *.test.ts and *.spec.ts patterns

Files:

  • packages/tm-core/src/auth/credential-store.test.ts
  • packages/tm-core/src/auth/auth-manager.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

**/*.test.ts: Use established mocking patterns for dependencies such as bcrypt and Prisma in test files
Test all code paths, including edge cases and error scenarios, in test files
Use descriptive names for test functions, such as should_return_error_for_invalid_input

Files:

  • packages/tm-core/src/auth/credential-store.test.ts
  • packages/tm-core/src/auth/auth-manager.test.ts
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/git_workflow.mdc:0-0
Timestamp: 2025-07-18T17:10:31.810Z
Learning: Pull Request descriptions must use the provided template, including Task Overview, Subtasks Completed, Implementation Details, Testing, Breaking Changes, and Related Tasks
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/src/auth/config.ts:5-7
Timestamp: 2025-09-02T21:51:27.921Z
Learning: The user Crunchyman-ralph prefers not to use node: scheme imports (e.g., 'node:os', 'node:path') for Node.js core modules and considers suggestions to change bare imports to node: scheme as too nitpicky.
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : Use sample task fixtures for consistent test data, mock file system operations, and test both success and error paths for task operations.

Applied to files:

  • packages/tm-core/src/auth/credential-store.test.ts
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/**/*.test.js : Test core logic independently with both data formats, mock file system operations, test tag resolution behavior, and verify migration compatibility in unit tests.

Applied to files:

  • packages/tm-core/src/auth/credential-store.test.ts
📚 Learning: 2025-09-03T12:16:15.866Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/package.json:13-64
Timestamp: 2025-09-03T12:16:15.866Z
Learning: For internal packages in the claude-task-master project, Crunchyman-ralph prefers pointing package.json "types" entries to src .ts files rather than dist .d.ts files for better developer experience (DX), as the packages are not being exported as SDKs.

Applied to files:

  • packages/tm-core/src/auth/credential-store.ts
📚 Learning: 2025-08-03T12:13:33.875Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/test_workflow.mdc:0-0
Timestamp: 2025-08-03T12:13:33.875Z
Learning: Applies to **/*.test.ts : Use established mocking patterns for dependencies such as bcrypt and Prisma in test files

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Be careful with how you mock or stub functions that depend on module state; use factory functions in mocks to ensure proper initialization order.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : Do not try to use the real action implementation without proper mocking, and do not mock Commander partially—either mock it completely or test the action directly.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{integration,e2e}/**/*.test.js : Properly mock session objects when required by functions, and reset environment variables between tests if modified.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Use jest.spyOn() after imports to create spies on mock functions and reference these spies in test assertions.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Do not import real AI service clients in tests; create fully mocked versions that return predictable responses.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : When testing CLI commands built with Commander.js, test the command action handlers directly rather than trying to mock the entire Commander.js chain.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Follow the test file organization: mocks must be set up before importing modules under test, and spies on mocked modules should be set up after imports.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Troubleshoot mock functions not being called by ensuring mocks are defined before imports, referencing the correct mock instance, and verifying import paths.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : Mock the action handlers for CLI commands and verify they're called with correct arguments.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Clear mocks between tests with jest.clearAllMocks() in beforeEach.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/**/*.test.js : Follow the mock-first-then-import pattern and use jest.spyOn() for testing in Jest, clearing mocks between tests.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Isolate tests by properly mocking shared resources and resetting state in beforeEach and afterEach hooks.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Do not rely on environment variables for API keys in tests; set mock environment variables in test setup.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Verify file operations use the correct paths in expect statements.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
🧬 Code graph analysis (6)
packages/tm-core/src/auth/credential-store.test.ts (2)
packages/tm-core/src/auth/credential-store.ts (1)
  • CredentialStore (11-220)
packages/tm-core/src/auth/types.ts (2)
  • AuthCredentials (5-13)
  • AuthenticationError (75-87)
packages/tm-core/src/auth/credential-store.ts (2)
packages/tm-core/src/logger/logger.ts (1)
  • error (163-166)
packages/tm-core/src/auth/types.ts (1)
  • AuthenticationError (75-87)
packages/tm-core/src/storage/storage-factory.ts (1)
packages/tm-core/src/interfaces/configuration.interface.ts (2)
  • RuntimeStorageConfig (88-103)
  • IConfiguration (198-240)
packages/tm-core/src/config/config-manager.ts (1)
packages/tm-core/src/interfaces/configuration.interface.ts (1)
  • RuntimeStorageConfig (88-103)
packages/tm-core/src/auth/auth-manager.test.ts (1)
packages/tm-core/src/auth/auth-manager.ts (1)
  • AuthManager (19-136)
packages/tm-core/src/auth/auth-manager.ts (1)
packages/tm-core/src/logger/factory.ts (1)
  • getLogger (23-44)
🪛 ast-grep (0.38.6)
packages/tm-core/src/auth/credential-store.ts

[warning] 182-182: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^${escapedBaseName}\\.corrupt-\\d+$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🔇 Additional comments (13)
packages/tm-core/src/interfaces/configuration.interface.ts (2)

77-83: LGTM: StorageType addition is clear and future-proof.

The union and docs read well and set up the new 'auto' mode cleanly.


115-121: Confirm persistence doesn’t serialize computed apiConfigured
StorageSettings already marks apiConfigured readonly, but verify that ConfigPersistence.saveConfig is always invoked with the merged PartialConfiguration (rather than the output of getStorageConfig) so that the computed apiConfigured field never ends up in the persisted JSON.

packages/tm-core/src/auth/types.ts (3)

75-87: LGTM: AuthenticationError carries code and cause.

Appending cause stack aids debugging. No changes needed.


67-71: Removal of ‘INVALID_API_KEY’ is safe—no references found in codebase
Global searches across all packages, tests, snapshots, and documentation returned zero occurrences of INVALID_API_KEY, so there are no remaining call sites to update.


10-12: expiresAt handling is consistently normalized and parsed Supabase-client and OAuth-service both emit ISO strings; the credential-store correctly parses strings via Date.parse and uses numeric timestamps directly, then re-serializes to ISO on save; existing tests cover all string/number/invalid cases.

packages/tm-core/src/auth/auth-manager.test.ts (1)

81-92: LGTM: resetInstance recreates a fresh singleton.

Good coverage for the reset path.

packages/tm-core/src/storage/storage-factory.ts (2)

214-218: Validation for 'auto' looks correct

Treating 'auto' as valid and deferring to runtime detection is consistent with the factory logic.


143-169: All StorageFactory.create invocations supply projectPath
The only call-site is the internal wrapper at storage-factory.ts:33–35, which passes projectPath; no external direct create() or new ApiStorage invocations were found.

packages/tm-core/src/config/config-manager.ts (2)

9-12: Imports updated to include RuntimeStorageConfig — good

Keeps the return type aligned with the shared interface.


228-229: Re-initialization trigger after updates — good

Resetting initialized ensures precedence is reapplied on subsequent initialize().

packages/tm-core/src/auth/credential-store.ts (1)

45-49: Policy check: rejecting credentials without expiresAt.

You’re returning null (warn) when expiresAt is missing/invalid. Confirm this is intentional for all token types now that API keys are removed; otherwise consider downgrading to debug or allowing a configurable fallback.

packages/tm-core/src/auth/credential-store.test.ts (2)

24-26: Good: logger mocked to keep tests quiet and assertable.


239-263: Solid check for ISO persistence.

Verifies that saved expiresAt is a valid ISO string; nicely done.

Comment on lines +195 to 197
isApiExplicitlyConfigured(): boolean {
return this.getStorageConfig().type === 'api';
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Rename is breaking; consider a deprecation shim or verify usages

isApiExplicitlyConfigured() replaces isUsingApiStorage(). Ensure no remaining callers of the old name, or add a temporary alias with a deprecation notice.

Search:

Optionally add a temporary alias:

// Deprecated: remove in the next minor
/** @deprecated Use isApiExplicitlyConfigured() */
isUsingApiStorage(): boolean {
  // eslint-disable-next-line no-console
  console.warn('[deprecate] ConfigManager.isUsingApiStorage() → use isApiExplicitlyConfigured()');
  return this.isApiExplicitlyConfigured();
}

🏁 Script executed:

#!/bin/bash
# Find usages of the old API to assess blast radius
rg -nP -C2 '\bisUsingApiStorage\s*\('

Length of output: 771


Remove legacy isUsingApiStorage references in tests and finalize the rename
Update packages/tm-core/src/config/config-manager.spec.ts (around lines 311–328) to replace all isUsingApiStorage() calls with isApiExplicitlyConfigured(), then remove the now‐unused isUsingApiStorage() method from config-manager.ts.

🤖 Prompt for AI Agents
In packages/tm-core/src/config/config-manager.ts around lines 195–197, tests
still reference the legacy isUsingApiStorage() and the method should be removed
after updating tests; open packages/tm-core/src/config/config-manager.spec.ts
(around lines 311–328) and replace all calls to isUsingApiStorage() with
isApiExplicitlyConfigured(), run tests, then delete the now-unused
isUsingApiStorage() method from config-manager.ts and remove any imports/usages
elsewhere; ensure linter/test pass and update any related type exports or docs
if necessary.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
package.json (1)

117-135: Add Vitest dependencies; retain Jest until tests migrate
package.json devDependencies: add "vitest": "^2.x" and "@vitest/coverage-v8": "^2.x". Migrate test mocks from jest.* to vi.* (or enable Jest compatibility) before removing jest, jest-environment-node, and @types/jest.

packages/tm-core/src/interfaces/configuration.interface.ts (1)

118-129: Add defaults for new storage booleans or make them optional

enableBackup, enableCompression and atomicOperations are required by the interface but not defined in DEFAULT_CONFIG_VALUES.STORAGE—this breaks existing configs. Either:

  • Add these defaults under STORAGE in DEFAULT_CONFIG_VALUES (e.g. ENABLE_BACKUP, ENABLE_COMPRESSION, ATOMIC_OPERATIONS), or
  • Change these properties to optional (enableBackup?: boolean;, etc.) so the factory can backfill them.
packages/tm-core/src/config/config-manager.spec.ts (3)

316-327: Add a complementary negative case for 'auto' with partial API settings.

To lock in the “explicitly configured” semantics, add a case where storage.type is auto but API fields exist and assert isApiExplicitlyConfigured() remains false.

+    it('should keep isApiExplicitlyConfigured false for auto with API fields', async () => {
+      (manager as any).config = {
+        storage: { type: 'auto', apiEndpoint: 'https://api.example.com' }
+      };
+      expect(manager.isApiExplicitlyConfigured()).toBe(false);
+    });

170-181: Fix expectation to tolerate added fields in storage config (now includes basePath).

getStorageConfig() returns additional keys (e.g., basePath). Replace strict equality with a partial match and assert basePath separately to avoid brittle failures.

-      const storage = manager.getStorageConfig();
-      expect(storage).toEqual({ type: 'auto', apiConfigured: false });
+      const storage = manager.getStorageConfig();
+      expect(storage).toMatchObject({ type: 'auto', apiConfigured: false });
+      expect(storage.basePath).toBe(testProjectRoot);

333-343: Update mocks and assertions for RuntimeStateManager method rename.

ConfigManager.getActiveTag() now calls stateManager.getCurrentTag() and setActiveTag() calls stateManager.setCurrentTag(). The test’s mock exposes getActiveTag/setActiveTag, causing runtime errors.

-    vi.mocked(RuntimeStateManager).mockImplementation(
-      () =>
-        ({
-          loadState: vi.fn().mockResolvedValue({ activeTag: 'master' }),
-          saveState: vi.fn().mockResolvedValue(undefined),
-          getActiveTag: vi.fn().mockReturnValue('master'),
-          setActiveTag: vi.fn().mockResolvedValue(undefined),
-          getState: vi.fn().mockReturnValue({ activeTag: 'master' }),
-          updateMetadata: vi.fn().mockResolvedValue(undefined),
-          clearState: vi.fn().mockResolvedValue(undefined)
-        }) as any
-    );
+    vi.mocked(RuntimeStateManager).mockImplementation(
+      () =>
+        ({
+          loadState: vi.fn().mockResolvedValue({ activeTag: 'master' }),
+          saveState: vi.fn().mockResolvedValue(undefined),
+          getCurrentTag: vi.fn().mockReturnValue('master'),
+          setCurrentTag: vi.fn().mockResolvedValue(undefined),
+          getState: vi.fn().mockReturnValue({ activeTag: 'master' }),
+          updateMetadata: vi.fn().mockResolvedValue(undefined),
+          clearState: vi.fn().mockResolvedValue(undefined)
+        }) as any
+    );
@@
-      const stateManager = (manager as any).stateManager;
-      expect(stateManager.setActiveTag).toHaveBeenCalledWith('feature-branch');
+      const stateManager = (manager as any).stateManager;
+      expect(stateManager.setCurrentTag).toHaveBeenCalledWith('feature-branch');
packages/tm-core/src/storage/storage-factory.ts (1)

46-49: Ensure projectPath is always present inside config during create().
createApiStorage reads from config.projectPath; set it up-front.

 static create(
   config: Partial<IConfiguration>,
   projectPath: string
 ): IStorage {
+    // Ensure projectPath is available to downstream creators
+    config = { ...config, projectPath: config.projectPath ?? projectPath };
♻️ Duplicate comments (7)
packages/tm-core/src/interfaces/configuration.interface.ts (1)

88-103: Readonly apiConfigured: resolved as requested earlier.

Marking apiConfigured as readonly matches its computed/internal nature. Thanks for addressing the prior feedback.

packages/tm-core/src/auth/auth-manager.test.ts (1)

74-79: Also restore spied implementations between tests.

Helps avoid cross-test leakage when spies replace implementations.

 beforeEach(() => {
   // Reset singleton before each test
   AuthManager.resetInstance();
   vi.clearAllMocks();
   CredentialStoreSpy.mockClear();
+  vi.restoreAllMocks();
 });
packages/tm-core/src/storage/storage-factory.ts (2)

32-38: Good fix: propagate projectPath into wrapped config.
Addresses the earlier gap so ApiStorage.projectId is set.


5-12: Confirm TS module resolution for “.js” specifiers in TS sources.
Using ESM “.js” specifiers in .ts requires NodeNext/Bundler resolution.

#!/bin/bash
# Show module settings and find .js specifiers in tm-core
jq '.compilerOptions | {module, moduleResolution, allowImportingTsExtensions}' packages/tm-core/tsconfig.json
rg -n --type=ts -g 'packages/tm-core/**' "from '.*\\.js'"
packages/tm-core/src/auth/auth-manager.ts (1)

34-45: Return non-null singleton or narrow type.
The return type is AuthManager but you return possibly null; add non-null assertion.

-  static getInstance(config?: Partial<AuthConfig>): AuthManager {
+  static getInstance(config?: Partial<AuthConfig>): AuthManager {
@@
-    return AuthManager.instance;
+    return AuthManager.instance!;
   }
packages/tm-core/src/auth/credential-store.test.ts (2)

293-301: Nice fix: no double invocation in throw assertions.

Switched to capturing the error once and asserting type/message, addressing the prior nit.

Also applies to: 312-320, 331-339


412-452: Coverage added for clearCredentials — looks good.

Tests for exists/non-exists and failure-to-unlink paths close the gap noted earlier.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 538d745 and 84056d6.

📒 Files selected for processing (10)
  • package.json (1 hunks)
  • packages/tm-core/src/auth/auth-manager.test.ts (1 hunks)
  • packages/tm-core/src/auth/auth-manager.ts (3 hunks)
  • packages/tm-core/src/auth/credential-store.test.ts (1 hunks)
  • packages/tm-core/src/auth/credential-store.ts (4 hunks)
  • packages/tm-core/src/config/config-manager.spec.ts (2 hunks)
  • packages/tm-core/src/config/config-manager.ts (4 hunks)
  • packages/tm-core/src/interfaces/configuration.interface.ts (1 hunks)
  • packages/tm-core/src/storage/storage-factory.ts (5 hunks)
  • packages/tm-core/tsconfig.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/git_workflow.mdc)

**/*.{test,spec}.{js,ts,jsx,tsx}: Create a test file and ensure all tests pass when all subtasks are complete; commit tests if added or modified
When all subtasks are complete, run final testing using the appropriate test runner (e.g., npm test, jest, or manual testing)

Files:

  • packages/tm-core/src/config/config-manager.spec.ts
  • packages/tm-core/src/auth/auth-manager.test.ts
  • packages/tm-core/src/auth/credential-store.test.ts
**/*.{test,spec}.*

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

Test files should follow naming conventions: .test., .spec., or _test. depending on the language

Files:

  • packages/tm-core/src/config/config-manager.spec.ts
  • packages/tm-core/src/auth/auth-manager.test.ts
  • packages/tm-core/src/auth/credential-store.test.ts
**/?(*.)+(spec|test).ts

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

In JavaScript/TypeScript projects using Jest, test files should match *.test.ts and *.spec.ts patterns

Files:

  • packages/tm-core/src/config/config-manager.spec.ts
  • packages/tm-core/src/auth/auth-manager.test.ts
  • packages/tm-core/src/auth/credential-store.test.ts
package.json

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

Add and update test scripts in package.json to include test, test:watch, test:coverage, test:unit, test:integration, test:e2e, and test:ci

Files:

  • package.json
**/*.test.ts

📄 CodeRabbit inference engine (.cursor/rules/test_workflow.mdc)

**/*.test.ts: Use established mocking patterns for dependencies such as bcrypt and Prisma in test files
Test all code paths, including edge cases and error scenarios, in test files
Use descriptive names for test functions, such as should_return_error_for_invalid_input

Files:

  • packages/tm-core/src/auth/auth-manager.test.ts
  • packages/tm-core/src/auth/credential-store.test.ts
🧠 Learnings (30)
📓 Common learnings
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/git_workflow.mdc:0-0
Timestamp: 2025-07-18T17:10:31.810Z
Learning: Pull Request descriptions must use the provided template, including Task Overview, Subtasks Completed, Implementation Details, Testing, Breaking Changes, and Related Tasks
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/src/auth/config.ts:5-7
Timestamp: 2025-09-02T21:51:27.921Z
Learning: The user Crunchyman-ralph prefers not to use node: scheme imports (e.g., 'node:os', 'node:path') for Node.js core modules and considers suggestions to change bare imports to node: scheme as too nitpicky.
📚 Learning: 2025-08-03T12:13:33.875Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/test_workflow.mdc:0-0
Timestamp: 2025-08-03T12:13:33.875Z
Learning: Applies to package.json : Add and update test scripts in package.json to include test, test:watch, test:coverage, test:unit, test:integration, test:e2e, and test:ci

Applied to files:

  • package.json
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/**/*.test.js : Test core logic independently with both data formats, mock file system operations, test tag resolution behavior, and verify migration compatibility in unit tests.

Applied to files:

  • package.json
  • packages/tm-core/src/auth/credential-store.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Verify modifications on the in-memory task objects passed to writeJSON.

Applied to files:

  • package.json
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : Use sample task fixtures for consistent test data, mock file system operations, and test both success and error paths for task operations.

Applied to files:

  • package.json
  • packages/tm-core/src/auth/credential-store.test.ts
📚 Learning: 2025-07-18T17:07:39.336Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2025-07-18T17:07:39.336Z
Learning: Applies to tests/{unit,integration,e2e,fixtures}/**/*.js : Test files must be organized as follows: unit tests in tests/unit/, integration tests in tests/integration/, end-to-end tests in tests/e2e/, and test fixtures in tests/fixtures/.

Applied to files:

  • package.json
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/**/*.test.js : Test CLI and MCP interfaces with real task data, verify end-to-end workflows across tag contexts, and test error scenarios and recovery in integration tests.

Applied to files:

  • package.json
📚 Learning: 2025-07-18T17:10:31.810Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/git_workflow.mdc:0-0
Timestamp: 2025-07-18T17:10:31.810Z
Learning: Applies to **/*.{test,spec}.{js,ts,jsx,tsx} : Create a test file if needed and ensure all tests pass before completing a task

Applied to files:

  • package.json
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{integration,e2e}/**/*.test.js : Properly mock session objects when required by functions, and reset environment variables between tests if modified.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-08-03T12:13:33.875Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/test_workflow.mdc:0-0
Timestamp: 2025-08-03T12:13:33.875Z
Learning: Applies to **/*.test.ts : Use established mocking patterns for dependencies such as bcrypt and Prisma in test files

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Do not import real AI service clients in tests; create fully mocked versions that return predictable responses.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : Do not try to use the real action implementation without proper mocking, and do not mock Commander partially—either mock it completely or test the action directly.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Be careful with how you mock or stub functions that depend on module state; use factory functions in mocks to ensure proper initialization order.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Use jest.spyOn() after imports to create spies on mock functions and reference these spies in test assertions.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : When testing CLI commands built with Commander.js, test the command action handlers directly rather than trying to mock the entire Commander.js chain.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Follow the test file organization: mocks must be set up before importing modules under test, and spies on mocked modules should be set up after imports.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Troubleshoot mock functions not being called by ensuring mocks are defined before imports, referencing the correct mock instance, and verifying import paths.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : Mock the action handlers for CLI commands and verify they're called with correct arguments.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Clear mocks between tests with jest.clearAllMocks() in beforeEach.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:12:57.903Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/new_features.mdc:0-0
Timestamp: 2025-07-18T17:12:57.903Z
Learning: Applies to scripts/modules/**/*.test.js : Follow the mock-first-then-import pattern and use jest.spyOn() for testing in Jest, clearing mocks between tests.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Isolate tests by properly mocking shared resources and resetting state in beforeEach and afterEach hooks.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Do not rely on environment variables for API keys in tests; set mock environment variables in test setup.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Verify file operations use the correct paths in expect statements.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:07:39.336Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2025-07-18T17:07:39.336Z
Learning: Module dependencies should be mocked before importing the test module, following Jest's hoisting behavior.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Do not define mock variables before jest.mock() calls (they won't be accessible due to hoisting).

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Always declare mocks before importing the modules being tested in Jest test files.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Follow the mock-first-then-import pattern for all Jest mocks.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-07-18T17:16:13.793Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Set mock environment variables in test setup and restore them after each test.

Applied to files:

  • packages/tm-core/src/auth/auth-manager.test.ts
📚 Learning: 2025-09-03T12:16:15.866Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/package.json:13-64
Timestamp: 2025-09-03T12:16:15.866Z
Learning: For internal packages in the claude-task-master project, Crunchyman-ralph prefers pointing package.json "types" entries to src .ts files rather than dist .d.ts files for better developer experience (DX), as the packages are not being exported as SDKs.

Applied to files:

  • packages/tm-core/src/auth/credential-store.ts
📚 Learning: 2025-07-18T17:08:48.695Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/commands.mdc:0-0
Timestamp: 2025-07-18T17:08:48.695Z
Learning: Applies to scripts/modules/commands.js : Use path.join() to construct file paths, follow established naming conventions (like task_001.txt), check file existence before deletion, and handle file deletion errors gracefully.

Applied to files:

  • packages/tm-core/src/auth/credential-store.ts
🧬 Code graph analysis (6)
packages/tm-core/src/config/config-manager.ts (1)
packages/tm-core/src/interfaces/configuration.interface.ts (1)
  • RuntimeStorageConfig (88-103)
packages/tm-core/src/storage/storage-factory.ts (2)
packages/tm-core/src/interfaces/configuration.interface.ts (3)
  • RuntimeStorageConfig (88-103)
  • IConfiguration (196-238)
  • StorageSettings (109-129)
packages/tm-core/src/auth/auth-manager.ts (1)
  • AuthManager (19-136)
packages/tm-core/src/auth/auth-manager.test.ts (1)
packages/tm-core/src/auth/auth-manager.ts (1)
  • AuthManager (19-136)
packages/tm-core/src/auth/credential-store.test.ts (2)
packages/tm-core/src/auth/credential-store.ts (1)
  • CredentialStore (21-238)
packages/tm-core/src/auth/types.ts (2)
  • AuthCredentials (5-13)
  • AuthenticationError (75-87)
packages/tm-core/src/auth/auth-manager.ts (2)
packages/tm-core/src/logger/factory.ts (1)
  • getLogger (23-44)
packages/tm-core/src/logger/logger.ts (1)
  • error (163-166)
packages/tm-core/src/auth/credential-store.ts (3)
packages/tm-core/src/logger/factory.ts (1)
  • getLogger (23-44)
packages/tm-core/src/auth/types.ts (2)
  • AuthConfig (30-34)
  • AuthenticationError (75-87)
packages/tm-core/src/auth/config.ts (1)
  • getAuthConfig (32-37)
🔇 Additional comments (15)
packages/tm-core/src/interfaces/configuration.interface.ts (1)

77-83: StorageType addition looks good.

Union and docs align with downstream factory logic. No action.

packages/tm-core/src/config/config-manager.spec.ts (1)

312-314: LGTM: new explicit API check semantics.

The test correctly uses the renamed isApiExplicitlyConfigured() and expects false by default.

packages/tm-core/src/auth/auth-manager.test.ts (2)

88-106: LGTM: config propagation is asserted via constructor spy.

Good verification that the initial config reaches dependencies.


15-17: No changes needed: mock path matches SUT import.
The mock path '../logger/index.js' exactly matches the import in auth-manager.ts.

packages/tm-core/src/config/config-manager.ts (3)

140-161: Good: runtime storage config now returns deterministic basePath and api flags.

basePath ?? this.projectRoot avoids undefined; apiConfigured reflects endpoint/token presence.


224-230: Re-init after update: explicit initialized = false is correct.

Ensures precedence is reapplied on the next merge; no further changes needed.


195-197: No remaining callers of isUsingApiStorage Verified via project-wide search; safe to proceed with the rename.

packages/tm-core/src/auth/credential-store.ts (3)

45-76: Token expiry normalization and skew handling look solid.
Good finite checks, normalization, and 30s skew to avoid edge flakiness.


86-99: Corrupt-file quarantine and cleanup are robust.
Unique quarantine filenames and non-regex matching mitigate ReDoS and collisions.

Also applies to: 193-237


115-141: Safe persistence without mutating input; ISO serialization validated.
Good security on dir/file modes and atomic rename.

packages/tm-core/src/auth/credential-store.test.ts (5)

13-26: Good use of fs and logger mocks; aligns with team learnings.

Automocking fs and injecting a mock logger matches our testing guidelines and the retrieved learning about mocking filesystem and exercising success/error paths.


46-117: Happy path and invalid timestamp cases are well covered.

Covers ISO, number, invalid string/NaN/Infinity, and missing expiresAt with correct warning assertions.


378-394: Corrupt file quarantine path is well tested.

Validates rename and warning without overfitting the filename suffix.


454-528: hasValidCredentials coverage is thorough.

Valid, expired, missing, invalid JSON, and default allowExpired=false are all exercised with appropriate log assertions.


5-12: Style note: bare core-module imports match team preference.

Using 'fs', 'path', 'os' without the node: prefix aligns with Crunchyman-ralph’s stated preference.

⛔ Skipped due to learnings
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1178
File: packages/tm-core/src/auth/config.ts:5-7
Timestamp: 2025-09-02T21:51:27.921Z
Learning: The user Crunchyman-ralph prefers not to use node: scheme imports (e.g., 'node:os', 'node:path') for Node.js core modules and considers suggestions to change bare imports to node: scheme as too nitpicky.
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-18T17:18:17.759Z
Learning: Applies to {scripts/modules/utils.js,mcp-server/src/core/utils/path-utils.js,mcp-server/src/tools/utils.js} : Keep utilities relevant to their location, export all utility functions in a single statement per file, group related exports together, export configuration constants, do not use default exports, and do not create circular dependencies.
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/test_workflow.mdc:0-0
Timestamp: 2025-08-03T12:13:33.875Z
Learning: Applies to **/*.test.ts : Use established mocking patterns for dependencies such as bcrypt and Prisma in test files
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to **/*.test.js : Verify file operations use the correct paths in expect statements.
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tests.mdc:0-0
Timestamp: 2025-07-18T17:16:13.793Z
Learning: Applies to tests/{unit,integration,e2e}/**/*.test.js : Use sample task fixtures for consistent test data, mock file system operations, and test both success and error paths for task operations.
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/test_workflow.mdc:0-0
Timestamp: 2025-08-03T12:13:33.875Z
Learning: Applies to {tests/integration/**/*.test.ts,src/**/*.integration.test.ts} : Use supertest for API endpoint testing in integration test files

Comment on lines +21 to +38
vi.mock('./credential-store.js', () => {
return {
CredentialStore: class {
constructor(config: any) {
CredentialStoreSpy(config);
this.getCredentials = vi.fn(() => null);
}
getCredentials() {
return null;
}
saveCredentials() {}
clearCredentials() {}
hasValidCredentials() {
return false;
}
}
};
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Remove duplicate getCredentials definitions in the mock class.

The constructor assignment shadows the prototype method. Keep one definition to avoid confusion.

 vi.mock('./credential-store.js', () => {
   return {
     CredentialStore: class {
       constructor(config: any) {
         CredentialStoreSpy(config);
-        this.getCredentials = vi.fn(() => null);
       }
-      getCredentials() {
-        return null;
-      }
+      getCredentials = vi.fn(() => null);
       saveCredentials() {}
       clearCredentials() {}
       hasValidCredentials() {
         return false;
       }
     }
   };
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
vi.mock('./credential-store.js', () => {
return {
CredentialStore: class {
constructor(config: any) {
CredentialStoreSpy(config);
this.getCredentials = vi.fn(() => null);
}
getCredentials() {
return null;
}
saveCredentials() {}
clearCredentials() {}
hasValidCredentials() {
return false;
}
}
};
});
vi.mock('./credential-store.js', () => {
return {
CredentialStore: class {
constructor(config: any) {
CredentialStoreSpy(config);
}
getCredentials = vi.fn(() => null);
saveCredentials() {}
clearCredentials() {}
hasValidCredentials() {
return false;
}
}
};
});
🤖 Prompt for AI Agents
In packages/tm-core/src/auth/auth-manager.test.ts around lines 21 to 38, the
mock class defines getCredentials twice (once assigned inside the constructor
and once as a prototype method) which is redundant and confusing; remove the
duplicate by deleting the constructor assignment (this.getCredentials = vi.fn(()
=> null)) and keep the single prototype getCredentials() { return null; } (or
alternatively keep the constructor assignment and remove the prototype method)
so the mock exposes only one clear implementation, and ensure the constructor
still calls CredentialStoreSpy(config).

Comment on lines +56 to +68
vi.mock('../clients/supabase-client.js', () => {
return {
SupabaseAuthClient: class {
constructor() {}
refreshSession() {
return Promise.resolve({});
}
signOut() {
return Promise.resolve();
}
}
};
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Stub realistic shapes for auth client responses (future-proof refresh/logout tests).

Return fields used by refreshToken() to prevent accidental breakage when coverage expands.

     SupabaseAuthClient: class {
       constructor() {}
-      refreshSession() {
-        return Promise.resolve({});
+      refreshSession() {
+        return Promise.resolve({
+          token: 'newAccess',
+          refreshToken: 'newRefresh',
+          expiresAt: Date.now() + 3600_000
+        });
       }
       signOut() {
         return Promise.resolve();
       }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
vi.mock('../clients/supabase-client.js', () => {
return {
SupabaseAuthClient: class {
constructor() {}
refreshSession() {
return Promise.resolve({});
}
signOut() {
return Promise.resolve();
}
}
};
});
vi.mock('../clients/supabase-client.js', () => {
return {
SupabaseAuthClient: class {
constructor() {}
refreshSession() {
return Promise.resolve({
token: 'newAccess',
refreshToken: 'newRefresh',
expiresAt: Date.now() + 3600_000
});
}
signOut() {
return Promise.resolve();
}
}
};
});
🤖 Prompt for AI Agents
In packages/tm-core/src/auth/auth-manager.test.ts around lines 56 to 68, the
SupabaseAuthClient mock returns empty objects which don't match the real client
shape and can break refresh/logout tests; update the mock so refreshSession()
resolves with the realistic shape used by refreshToken (e.g., an object with
data: { session: { access_token, refresh_token, expires_at }, user: { id, email
} }, error: null) and signOut() resolves with { error: null } (or the equivalent
fields your code expects) so tests remain accurate as coverage expands.

Comment on lines +108 to +136
it('should warn when config is provided after initialization', () => {
// Clear previous calls
mockLogger.warn.mockClear();

// First call with config
AuthManager.getInstance({ baseUrl: 'https://first.auth.com' });

// Second call with different config
AuthManager.getInstance({ baseUrl: 'https://second.auth.com' });

// Verify warning was logged
expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringMatching(/config.*after initialization.*ignored/i)
);
});

it('should not warn when no config is provided after initialization', () => {
// Clear previous calls
mockLogger.warn.mockClear();

// First call with config
AuthManager.getInstance({ configDir: '/test/config' });

// Second call without config
AuthManager.getInstance();

// Verify no warning was logged
expect(mockLogger.warn).not.toHaveBeenCalled();
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add coverage for refresh and logout paths.

Cover error when no refresh token, success path with save, and logout clearing credentials + remote sign-out call.

I can draft these tests quickly. Example additions:

it('refreshToken throws when no refresh token', async () => {
  const mgr = AuthManager.getInstance();
  await expect(mgr.refreshToken()).rejects.toThrow(/No refresh token/i);
});

it('logout clears credentials and calls remote sign-out', async () => {
  const mgr = AuthManager.getInstance();
  const store = (mgr as any).credentialStore;
  const client = (mgr as any).supabaseClient;
  vi.spyOn(store, 'clearCredentials');
  vi.spyOn(client, 'signOut');
  await mgr.logout();
  expect(client.signOut).toHaveBeenCalled();
  expect(store.clearCredentials).toHaveBeenCalled();
});

Also applies to: 138-149

Comment on lines +185 to +207
it('should check token expiration correctly', () => {
const expiredTimestamp = Date.now() - 3600000; // 1 hour ago
const mockCredentials = {
token: 'expired-token',
userId: 'user-expired',
expiresAt: expiredTimestamp,
tokenType: 'standard',
savedAt: new Date().toISOString()
};

vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue(
JSON.stringify(mockCredentials)
);

const result = store.getCredentials();

expect(result).toBeNull();
expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringContaining('Authentication token has expired'),
expect.any(Object)
);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add boundary test for clock skew (tokens expiring within 30s).

Cover the “about to expire” window to prevent regressions around CLOCK_SKEW_MS.

Apply this diff near the existing expiry test:

+    it('should treat tokens expiring within the skew window as expired', () => {
+      const soon = Date.now() + 29_000; // within 30s skew
+      const mockCredentials = {
+        token: 'soon-expiring',
+        userId: 'user-soon',
+        expiresAt: soon,
+        tokenType: 'standard',
+        savedAt: new Date().toISOString()
+      };
+      vi.mocked(fs.existsSync).mockReturnValue(true);
+      vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockCredentials));
+      const result = store.getCredentials();
+      expect(result).toBeNull();
+      expect(mockLogger.warn).toHaveBeenCalledWith(
+        expect.stringContaining('Authentication token has expired'),
+        expect.any(Object)
+      );
+    });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should check token expiration correctly', () => {
const expiredTimestamp = Date.now() - 3600000; // 1 hour ago
const mockCredentials = {
token: 'expired-token',
userId: 'user-expired',
expiresAt: expiredTimestamp,
tokenType: 'standard',
savedAt: new Date().toISOString()
};
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue(
JSON.stringify(mockCredentials)
);
const result = store.getCredentials();
expect(result).toBeNull();
expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringContaining('Authentication token has expired'),
expect.any(Object)
);
});
it('should check token expiration correctly', () => {
const expiredTimestamp = Date.now() - 3600000; // 1 hour ago
const mockCredentials = {
token: 'expired-token',
userId: 'user-expired',
expiresAt: expiredTimestamp,
tokenType: 'standard',
savedAt: new Date().toISOString()
};
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue(
JSON.stringify(mockCredentials)
);
const result = store.getCredentials();
expect(result).toBeNull();
expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringContaining('Authentication token has expired'),
expect.any(Object)
);
});
it('should treat tokens expiring within the skew window as expired', () => {
const soon = Date.now() + 29_000; // within 30s skew
const mockCredentials = {
token: 'soon-expiring',
userId: 'user-soon',
expiresAt: soon,
tokenType: 'standard',
savedAt: new Date().toISOString()
};
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify(mockCredentials));
const result = store.getCredentials();
expect(result).toBeNull();
expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringContaining('Authentication token has expired'),
expect.any(Object)
);
});
🤖 Prompt for AI Agents
In packages/tm-core/src/auth/credential-store.test.ts around lines 185 to 207,
add a boundary unit test that verifies tokens expiring within the CLOCK_SKEW_MS
window (e.g., within 30s) are treated as expired: create mockCredentials with
expiresAt = Date.now() + 25000 (or CLOCK_SKEW_MS - 5000), mock
fs.existsSync/readFileSync as in the existing test, call store.getCredentials(),
assert it returns null, and assert mockLogger.warn was called with a message
containing 'Authentication token has expired' (or similar) and an object; follow
the same test structure and expectations as the existing expiry test to ensure
consistent behavior around clock skew.

Comment on lines +341 to +356
it('should handle missing expiresAt when saving', () => {
const credentials: AuthCredentials = {
token: 'test-token',
userId: 'user-no-expiry',
tokenType: 'standard',
savedAt: new Date().toISOString()
// No expiresAt
};

store.saveCredentials(credentials);

const writtenData = vi.mocked(fs.writeFileSync).mock
.calls[0][1] as string;
const parsed = JSON.parse(writtenData);
expect(parsed.expiresAt).toBeUndefined();
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider asserting savedAt normalization too.

Confirm persisted savedAt is an ISO string (and that input object wasn’t mutated, which you already test).

Example:

 const parsed = JSON.parse(writtenData);
 expect(parsed.expiresAt).toBeUndefined();
+expect(typeof parsed.savedAt).toBe('string');
+expect(new Date(parsed.savedAt).toISOString()).toBe(parsed.savedAt);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should handle missing expiresAt when saving', () => {
const credentials: AuthCredentials = {
token: 'test-token',
userId: 'user-no-expiry',
tokenType: 'standard',
savedAt: new Date().toISOString()
// No expiresAt
};
store.saveCredentials(credentials);
const writtenData = vi.mocked(fs.writeFileSync).mock
.calls[0][1] as string;
const parsed = JSON.parse(writtenData);
expect(parsed.expiresAt).toBeUndefined();
});
it('should handle missing expiresAt when saving', () => {
const credentials: AuthCredentials = {
token: 'test-token',
userId: 'user-no-expiry',
tokenType: 'standard',
savedAt: new Date().toISOString()
// No expiresAt
};
store.saveCredentials(credentials);
const writtenData = vi.mocked(fs.writeFileSync).mock
.calls[0][1] as string;
const parsed = JSON.parse(writtenData);
expect(parsed.expiresAt).toBeUndefined();
expect(typeof parsed.savedAt).toBe('string');
expect(new Date(parsed.savedAt).toISOString()).toBe(parsed.savedAt);
});
🤖 Prompt for AI Agents
In packages/tm-core/src/auth/credential-store.test.ts around lines 341 to 356,
add assertions to verify savedAt is persisted as an ISO string and that the
input object wasn’t mutated: after parsing writtenData assert parsed.savedAt is
a string and that new Date(parsed.savedAt).toISOString() === parsed.savedAt (or
simply equals the original credentials.savedAt if that was an ISO string), and
keep/ensure the existing assertion that the original credentials.savedAt remains
unchanged.

Comment on lines +530 to +572
describe('cleanupCorruptFiles', () => {
it('should remove old corrupt files', () => {
const now = Date.now();
const oldFile = 'auth.json.corrupt-' + (now - 8 * 24 * 60 * 60 * 1000); // 8 days old
const newFile = 'auth.json.corrupt-' + (now - 1000); // 1 second old

vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readdirSync).mockReturnValue([
{ name: oldFile, isFile: () => true },
{ name: newFile, isFile: () => true },
{ name: 'auth.json', isFile: () => true }
] as any);
vi.mocked(fs.statSync).mockImplementation((filePath) => {
if (filePath.includes(oldFile)) {
return { mtimeMs: now - 8 * 24 * 60 * 60 * 1000 } as any;
}
return { mtimeMs: now - 1000 } as any;
});
vi.mocked(fs.unlinkSync).mockImplementation(() => undefined);

store.cleanupCorruptFiles();

expect(fs.unlinkSync).toHaveBeenCalledWith(
expect.stringContaining(oldFile)
);
expect(fs.unlinkSync).not.toHaveBeenCalledWith(
expect.stringContaining(newFile)
);
});

it('should handle cleanup errors gracefully', () => {
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readdirSync).mockImplementation(() => {
throw new Error('Permission denied');
});

// Should not throw
expect(() => store.cleanupCorruptFiles()).not.toThrow();
expect(mockLogger.debug).toHaveBeenCalledWith(
expect.stringContaining('Error during corrupt file cleanup')
);
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add negative case: ignore non-numeric corrupt suffixes.

Ensure files like auth.json.corrupt-latest or auth.json.corrupt-123abc are skipped per /^\d+$/ rule.

Append this test:

+  it('should ignore corrupt files with non-numeric suffix', () => {
+    vi.mocked(fs.existsSync).mockReturnValue(true);
+    vi.mocked(fs.readdirSync).mockReturnValue([
+      { name: 'auth.json.corrupt-latest', isFile: () => true },
+      { name: 'auth.json.corrupt-123abc', isFile: () => true }
+    ] as any);
+    store.cleanupCorruptFiles();
+    expect(fs.unlinkSync).not.toHaveBeenCalled();
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
describe('cleanupCorruptFiles', () => {
it('should remove old corrupt files', () => {
const now = Date.now();
const oldFile = 'auth.json.corrupt-' + (now - 8 * 24 * 60 * 60 * 1000); // 8 days old
const newFile = 'auth.json.corrupt-' + (now - 1000); // 1 second old
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readdirSync).mockReturnValue([
{ name: oldFile, isFile: () => true },
{ name: newFile, isFile: () => true },
{ name: 'auth.json', isFile: () => true }
] as any);
vi.mocked(fs.statSync).mockImplementation((filePath) => {
if (filePath.includes(oldFile)) {
return { mtimeMs: now - 8 * 24 * 60 * 60 * 1000 } as any;
}
return { mtimeMs: now - 1000 } as any;
});
vi.mocked(fs.unlinkSync).mockImplementation(() => undefined);
store.cleanupCorruptFiles();
expect(fs.unlinkSync).toHaveBeenCalledWith(
expect.stringContaining(oldFile)
);
expect(fs.unlinkSync).not.toHaveBeenCalledWith(
expect.stringContaining(newFile)
);
});
it('should handle cleanup errors gracefully', () => {
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readdirSync).mockImplementation(() => {
throw new Error('Permission denied');
});
// Should not throw
expect(() => store.cleanupCorruptFiles()).not.toThrow();
expect(mockLogger.debug).toHaveBeenCalledWith(
expect.stringContaining('Error during corrupt file cleanup')
);
});
});
describe('cleanupCorruptFiles', () => {
it('should remove old corrupt files', () => {
const now = Date.now();
const oldFile = 'auth.json.corrupt-' + (now - 8 * 24 * 60 * 60 * 1000); // 8 days old
const newFile = 'auth.json.corrupt-' + (now - 1000); // 1 second old
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readdirSync).mockReturnValue([
{ name: oldFile, isFile: () => true },
{ name: newFile, isFile: () => true },
{ name: 'auth.json', isFile: () => true }
] as any);
vi.mocked(fs.statSync).mockImplementation((filePath) => {
if (filePath.includes(oldFile)) {
return { mtimeMs: now - 8 * 24 * 60 * 60 * 1000 } as any;
}
return { mtimeMs: now - 1000 } as any;
});
vi.mocked(fs.unlinkSync).mockImplementation(() => undefined);
store.cleanupCorruptFiles();
expect(fs.unlinkSync).toHaveBeenCalledWith(
expect.stringContaining(oldFile)
);
expect(fs.unlinkSync).not.toHaveBeenCalledWith(
expect.stringContaining(newFile)
);
});
it('should handle cleanup errors gracefully', () => {
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readdirSync).mockImplementation(() => {
throw new Error('Permission denied');
});
// Should not throw
expect(() => store.cleanupCorruptFiles()).not.toThrow();
expect(mockLogger.debug).toHaveBeenCalledWith(
expect.stringContaining('Error during corrupt file cleanup')
);
});
it('should ignore corrupt files with non-numeric suffix', () => {
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readdirSync).mockReturnValue([
{ name: 'auth.json.corrupt-latest', isFile: () => true },
{ name: 'auth.json.corrupt-123abc', isFile: () => true }
] as any);
store.cleanupCorruptFiles();
expect(fs.unlinkSync).not.toHaveBeenCalled();
});
});

Comment on lines +109 to 119
export interface StorageSettings
extends Omit<RuntimeStorageConfig, 'apiConfigured'> {
/** Base path for file storage */
basePath?: string;
/**
* Indicates whether API is configured
* @computed Derived automatically from presence of apiEndpoint or apiAccessToken
* @internal Should not be set manually in user config - computed by ConfigManager
*/
readonly apiConfigured?: boolean;
/** Enable automatic backups */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Avoid redundant basePath declaration; keep computed flag optional/readonly.

  • basePath is already inherited from Omit<RuntimeStorageConfig, 'apiConfigured'>; redeclaring it is noise.
 export interface StorageSettings
   extends Omit<RuntimeStorageConfig, 'apiConfigured'> {
-  /** Base path for file storage */
-  basePath?: string;
   /**
    * Indicates whether API is configured
    * @computed Derived automatically from presence of apiEndpoint or apiAccessToken
    * @internal Should not be set manually in user config - computed by ConfigManager
    */
   readonly apiConfigured?: boolean;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface StorageSettings
extends Omit<RuntimeStorageConfig, 'apiConfigured'> {
/** Base path for file storage */
basePath?: string;
/**
* Indicates whether API is configured
* @computed Derived automatically from presence of apiEndpoint or apiAccessToken
* @internal Should not be set manually in user config - computed by ConfigManager
*/
readonly apiConfigured?: boolean;
/** Enable automatic backups */
export interface StorageSettings
extends Omit<RuntimeStorageConfig, 'apiConfigured'> {
/**
* Indicates whether API is configured
* @computed Derived automatically from presence of apiEndpoint or apiAccessToken
* @internal Should not be set manually in user config - computed by ConfigManager
*/
readonly apiConfigured?: boolean;
/** Enable automatic backups */
// …rest of properties…
}
🤖 Prompt for AI Agents
In packages/tm-core/src/interfaces/configuration.interface.ts around lines 109
to 119, the StorageSettings interface redundantly redeclares basePath which is
already inherited from Omit<RuntimeStorageConfig, 'apiConfigured'>; remove the
explicit basePath declaration and its comment to avoid duplication, leaving only
the readonly optional apiConfigured property and any other unique properties;
ensure JSDoc for apiConfigured remains intact to indicate it is computed and
internal.

Comment on lines +78 to 88
const nextStorage: StorageSettings = {
...(config.storage as StorageSettings),
type: 'api',
apiAccessToken: credentials.token,
apiEndpoint:
config.storage?.apiEndpoint ||
process.env.HAMSTER_API_URL ||
'https://tryhamster.com/api'
} as any; // Cast to any to bypass strict type checking for partial config
};
config.storage = nextStorage;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix runtime crash: spreading possibly undefined config.storage.
If config.storage is undefined, object spread throws. Use a safe base. Also assert token presence.

Apply:

-            const nextStorage: StorageSettings = {
-              ...(config.storage as StorageSettings),
-              type: 'api',
-              apiAccessToken: credentials.token,
-              apiEndpoint:
-                config.storage?.apiEndpoint ||
-                process.env.HAMSTER_API_URL ||
-                'https://tryhamster.com/api'
-            };
-            config.storage = nextStorage;
+            if (!credentials.token) {
+              throw new TaskMasterError('Authenticated but missing access token', ERROR_CODES.MISSING_CONFIGURATION, { storageType: 'api' });
+            }
+            const base = (config.storage ?? {}) as Partial<StorageSettings>;
+            const nextStorage: StorageSettings = {
+              ...base,
+              type: 'api',
+              apiAccessToken: credentials.token!,
+              apiEndpoint:
+                base.apiEndpoint ??
+                process.env.HAMSTER_API_URL ??
+                'https://tryhamster.com/api'
+            } as StorageSettings;
+            config.storage = nextStorage;

Repeat similarly for the block below:

-            const nextStorage: StorageSettings = {
-              ...(config.storage as StorageSettings),
-              type: 'api',
-              apiAccessToken: credentials.token,
-              apiEndpoint:
-                config.storage?.apiEndpoint ||
-                process.env.HAMSTER_API_URL ||
-                'https://tryhamster.com/api'
-            };
-            config.storage = nextStorage;
+            if (!credentials.token) {
+              throw new TaskMasterError('Authenticated but missing access token', ERROR_CODES.MISSING_CONFIGURATION, { storageType: 'api' });
+            }
+            const base = (config.storage ?? {}) as Partial<StorageSettings>;
+            const nextStorage: StorageSettings = {
+              ...base,
+              type: 'api',
+              apiAccessToken: credentials.token!,
+              apiEndpoint:
+                base.apiEndpoint ??
+                process.env.HAMSTER_API_URL ??
+                'https://tryhamster.com/api'
+            } as StorageSettings;
+            config.storage = nextStorage;

Also applies to: 108-118

🤖 Prompt for AI Agents
In packages/tm-core/src/storage/storage-factory.ts around lines 78-88 (and
likewise around 108-118), the code spreads config.storage which may be undefined
and reads credentials.token without asserting it; change the spread to use a
safe base object (e.g. {...(config.storage || {})}) and validate that
credentials.token exists before assigning apiAccessToken (throw or return an
explicit error if missing), then set apiEndpoint the same way; apply the same
safe-spread and token-assertion fix to the later block at 108-118.

"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"moduleResolution": "bundler",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Switch to bundler resolution: good, but tighten ESM settings and verify path aliases at runtime.

  • OK to switch; matches explicit .js imports and Vitest/Vite pipelines.
  • Add these for safer ESM interop and DX:
    • "verbatimModuleSyntax": true
    • "moduleDetection": "force"
    • If Node types are used in tm-core (e.g., BufferEncoding in configuration.interface.ts), add "types": ["node"] here.
   "forceConsistentCasingInFileNames": true,
   "moduleResolution": "bundler",
+  "verbatimModuleSyntax": true,
+  "moduleDetection": "force",
+  "types": ["node"],
   "resolveJsonModule": true,

Run to ensure no extensionless relative imports remain (required under bundler for runtime ESM):


🏁 Script executed:

#!/bin/bash
rg -nP --type=ts -g 'packages/tm-core/src/**' \
  -e 'from\s+[\"\'](\.{1,2}\/[^\"\'']+)[\"\'']' | \
  grep -vE '\.(js|mjs|cjs|json)[\"\'']$' || echo "OK: all relative imports have explicit extensions"

Length of output: 234


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Find all TS relative imports and filter out those with explicit .js/.mjs/.cjs/.json extensions
rg -nP --type=ts -g 'packages/tm-core/src/**' "from\s+['\"]\.\.?/[^'\"]+['\"]" \
  | grep -vP "\.(js|mjs|cjs|json)['\"]$" \
  || echo "OK: all relative imports have explicit extensions"

Length of output: 15832


Tighten ESM settings and add explicit extensions to all relative imports

  • In packages/tm-core/tsconfig.json under compilerOptions, add:
       "forceConsistentCasingInFileNames": true,
       "moduleResolution": "bundler",
    +  "verbatimModuleSyntax": true,
    +  "moduleDetection": "force",
    +  "types": ["node"],
       "resolveJsonModule": true,
  • Runtime ESM (with bundler resolution) requires file extensions on all relative imports. Update every import in packages/tm-core/src that lacks an explicit extension (e.g. change
    import … from './id-generator';
    to
    import … from './id-generator.js';).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"moduleResolution": "bundler",
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler",
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"types": ["node"],
"resolveJsonModule": true,
🤖 Prompt for AI Agents
In packages/tm-core/tsconfig.json around line 26, you set "moduleResolution":
"bundler" which enforces runtime ESM semantics; update every relative import in
packages/tm-core/src to include explicit file extensions (e.g. change import
from './id-generator' to './id-generator.js'), ensuring both import and dynamic
import paths use .js, and confirm any index imports use './folder/index.js' or
'./folder/index.js' as appropriate; run a full compile and adjust any path
aliases or build tooling (rollup/tsup) to preserve .js extensions in emitted
output so runtime resolves correctly.

@Crunchyman-ralph Crunchyman-ralph merged commit b9e3eec into next-typescript Sep 7, 2025
1 of 2 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Sep 7, 2025
16 tasks
@coderabbitai coderabbitai bot mentioned this pull request Sep 9, 2025
16 tasks
This was referenced Sep 11, 2025
This was referenced Oct 12, 2025
@coderabbitai coderabbitai bot mentioned this pull request Oct 25, 2025
16 tasks
sfc-gh-dflippo pushed a commit to sfc-gh-dflippo/task-master-ai that referenced this pull request Dec 4, 2025
sfc-gh-dflippo pushed a commit to sfc-gh-dflippo/task-master-ai that referenced this pull request Dec 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant