Skip to content

Release 0.29.0#1297

Merged
Crunchyman-ralph merged 14 commits intomainfrom
next
Oct 14, 2025
Merged

Release 0.29.0#1297
Crunchyman-ralph merged 14 commits intomainfrom
next

Conversation

@Crunchyman-ralph
Copy link
Collaborator

@Crunchyman-ralph Crunchyman-ralph commented Oct 11, 2025

Co-authored-by: Claude noreply@anthropic.com# 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

    • Official Claude Code plugin delivery with new /taskmaster command namespace and integrated plugin install flow; CLI shows a "What's New" highlights section after auto-update.
  • Enhancements

    • expand --all now leverages complexity-analysis recommendations for subtask counts.
    • Async auth/context and improved status messaging with automatic token refresh.
  • Bug Fixes

    • Cross-level dependency creation and persistence fixed.
  • Documentation

    • New RPG PRD template and plugin guide; updated quick-start and migration guidance.
  • Chores

    • Release/version and changelog updates for the RC release.

@changeset-bot
Copy link

changeset-bot bot commented Oct 11, 2025

🦋 Changeset detected

Latest commit: e308cf4

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 11, 2025

Walkthrough

Adds a Claude Code plugin and marketplace metadata; migrates many in-repo Claude docs to plugin-based docs under the /taskmaster namespace; adds an RPG PRD template and docs/init wiring; surfaces changelog "What's New" highlights in CLI auto-update; threads complexity-report paths through expand-all; fixes cross-level dependency handling; and implements multiple auth/refresh, storage, test, and version-bump changes.

Changes

Cohort / File(s) Summary
Changesets & release config
.changeset/*.md, /.changeset/pre.json, /.changeset/config.json
Multiple changesets and .changeset/pre.json added; .changeset/config.json ignore list updated to include @tm/claude-code-plugin.
Claude Code marketplace descriptor
.claude-plugin/marketplace.json
New marketplace descriptor listing the taskmaster plugin.
Claude Code plugin package & manifests
packages/claude-code-plugin/*, packages/claude-code-plugin/.claude-plugin/plugin.json, packages/claude-code-plugin/package.json, packages/claude-code-plugin/mcp.json, packages/claude-code-plugin/.gitignore
New plugin package, plugin manifest, MCP config, and packaging metadata for distributing Task Master via Claude Code.
Plugin docs / namespace migration
packages/claude-code-plugin/commands/*, packages/claude-code-plugin/commands/help.md, packages/claude-code-plugin/tm-main.md
Added plugin help docs and renamed example command prefixes from /project:tm/... to /taskmaster:... across plugin docs.
Remove legacy in-repo Claude docs
.claude/agents/*, .claude/commands/tm/**, assets/claude/**
Bulk deletion of many in-repo Claude/agent/command markdown docs; docs moved to plugin or removed.
Auto-update: changelog highlights
apps/cli/src/utils/auto-update.ts, scripts/modules/commands.js, tests/unit/commands.test.js
Added parseChangelogHighlights, UpdateInfo.highlights?: string[], fetch/parse changelog highlights when an update exists, and display "What's New" in notifications; tests updated.
Expand-all: complexity report plumbing
mcp-server/src/tools/expand-all.js, mcp-server/src/core/direct-functions/expand-all-tasks.js, mcp-server/src/core/utils/path-utils.js, mcp-server/src/core/task-master-core.js, tests/unit/scripts/modules/task-manager/expand-all-tasks.test.js
Added resolveComplexityReportOutputPath, compute/log complexityReportPath, pass it through to expandAllTasksDirect/expandTask; tests verify propagation.
Dependency manager & tests: cross-level IDs
scripts/modules/dependency-manager.js, tests/fixtures/sample-tasks.js, tests/unit/dependency-manager.test.js
Dependency lookup and circular-check logic updated to handle numeric vs dotted subtask IDs and mixed ID formats; added fixtures and extended cross-level dependency tests.
RPG PRD template & docs + init
.taskmaster/templates/example_prd_rpg.txt, assets/example_prd_rpg.txt, apps/docs/capabilities/rpg-method.mdx, apps/docs/getting-started/quick-start/prd-quick.mdx, scripts/init.js
Adds RPG PRD template and documentation, updates quick-start guidance and init script to copy the RPG template and adjust next-step instructions.
Claude profile behavior
src/profiles/claude.js
Replaces local .claude copy/remove behavior with deprecation notices and instructions to use/uninstall the Claude Code plugin; retains MCP→Claude conversion.
Version bump & changelogs
package.json, CHANGELOG.md, apps/extension/package.json, apps/extension/CHANGELOG.md
Version bumped to 0.29.0-rc.1, changelogs updated; extension version bumped and task-master-ai moved to devDependencies.
CLAUDE marketplace docs
CLAUDE_CODE_PLUGIN.md
Adds marketplace overview and contributor workflow doc.
Tests & fixtures updates
tests/fixture/test-tasks.json, tests/fixtures/sample-tasks.js, tests/unit/**, tests/unit/scripts/modules/task-manager/expand-all-tasks.test.js
Removed old fixture, added new sample fixtures, and updated/extended unit tests (auto-update, dependency-manager, expand-all, auth refresh).
Auth & async credential flow
packages/tm-core/src/auth/auth-manager.ts, apps/cli/src/commands/auth.command.ts, apps/cli/src/commands/context.command.ts, packages/tm-core/tests/**, packages/tm-core/src/auth/credential-store.spec.ts, packages/tm-core/tests/integration/**
Introduces async credential refresh with concurrency-safe refreshPromise; getCredentials/getContext become async; added tests for refresh/concurrency and edge cases.
Storage / services async updates
packages/tm-core/src/repositories/supabase/supabase-task-repository.ts, packages/tm-core/src/services/export.service.ts, packages/tm-core/src/storage/api-storage.ts, packages/tm-core/src/storage/storage-factory.ts
Internal calls updated to await async auth methods; getBriefIdOrThrow made async; storage factory uses getCredentialsSync where synchronous behavior is required.
Small packaging & tooling changes
packages/tm-core/package.json, package.json
Added devDependency strip-literal; package version and published files list updated to include CHANGELOG.md.
Misc docs & examples
README.md, apps/docs/*, packages/claude-code-plugin/commands/*.md, assets/*
README additions, docs updates for template selection, many in-repo command docs removed and replaced/renamed in plugin docs.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as CLI (task-master)
  participant Updater as Auto-update utils
  participant Repo as Remote (CHANGELOG.md)

  User->>CLI: run command
  CLI->>Updater: checkForUpdate(currentVersion)
  Updater->>Updater: fetch latestVersion
  alt update available
    Updater->>Repo: GET CHANGELOG.md
    Repo-->>Updater: changelog text
    Updater->>Updater: parseChangelogHighlights(changelog, latestVersion)
    Updater-->>CLI: { latestVersion, highlights[] }
    CLI->>Updater: displayUpgradeNotification(currentVersion, latestVersion, highlights)
  else up-to-date
    Updater-->>CLI: no update
  end
Loading
sequenceDiagram
  autonumber
  actor Dev as Developer
  participant Tool as MCP Tool (/expand-all)
  participant Path as Path Utils
  participant Core as expandAllTasksDirect
  participant Exp as expandTask

  Dev->>Tool: run expand-all (with tag/opts)
  Tool->>Path: resolveComplexityReportOutputPath(explicitPath?, { projectRoot, tag })
  Path-->>Tool: complexityReportPath
  Tool->>Core: expandAllTasksDirect({ ..., complexityReportPath })
  loop per selected task
    Core->>Exp: expandTask(taskId, { context: { complexityReportPath, ... }})
    Exp-->>Core: expansion result
  end
  Core-->>Dev: expansion summary
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • eyaltoledano

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Release 0.29.0” clearly and concisely communicates the primary purpose of this PR—preparing and cutting the 0.29.0 release—and directly aligns with the version bumps, changelog entries, and release candidate metadata present in the changeset. It is specific, focused, and avoids unnecessary detail, making it easy for teammates to understand the PR’s intent at a glance.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch next

📜 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 11b7354 and e308cf4.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • .changeset/pre.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/*

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

Create appropriate changesets for new features, use semantic versioning, include tagged system information in release notes, and document breaking changes if any.

Files:

  • .changeset/pre.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: typecheck
  • GitHub Check: build
  • GitHub Check: Test
  • GitHub Check: Test
  • GitHub Check: update-docs

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: 8

Caution

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

⚠️ Outside diff range comments (4)
src/profiles/claude.js (2)

8-23: Remove unused helper function.

The copyRecursiveSync function is no longer called after the migration to plugin-based command distribution. This is dead code that should be removed to improve maintainability.

Apply this diff to remove the unused function:

-// Helper function to recursively copy directory (adopted from Roo profile)
-function copyRecursiveSync(src, dest) {
-	const exists = fs.existsSync(src);
-	const stats = exists && fs.statSync(src);
-	const isDirectory = exists && stats.isDirectory();
-	if (isDirectory) {
-		if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
-		fs.readdirSync(src).forEach((childItemName) => {
-			copyRecursiveSync(
-				path.join(src, childItemName),
-				path.join(dest, childItemName)
-			);
-		});
-	} else {
-		fs.copyFileSync(src, dest);
-	}
-}
-

26-37: Remove unused helper function.

The removeDirectoryRecursive function is no longer called after the migration to plugin-based command distribution. This is dead code that should be removed to improve maintainability.

Apply this diff to remove the unused function:

-// Helper function to recursively remove directory
-function removeDirectoryRecursive(dirPath) {
-	if (fs.existsSync(dirPath)) {
-		try {
-			fs.rmSync(dirPath, { recursive: true, force: true });
-			return true;
-		} catch (err) {
-			log('error', `Failed to remove directory ${dirPath}: ${err.message}`);
-			return false;
-		}
-	}
-	return true;
-}
-
packages/claude-code-plugin/commands/tm-main.md (1)

11-143: Add blank lines after headings to satisfy markdownlint.
markdownlint is complaining (MD022/MD031) because every ### heading (and the fenced blocks later on) is immediately followed by content with no blank line. Insert an empty line after each heading/fence so the docs render correctly and the lint step passes.

-### `/taskmaster:init`
-- `init-project` - Initialize new project (handles PRD files intelligently)
+### `/taskmaster:init`
+
+- `init-project` - Initialize new project (handles PRD files intelligently)

Apply the same adjustment everywhere this pattern occurs.

tests/unit/commands.test.js (1)

281-361: Avoid dynamic imports in tests.
The coding guidelines explicitly forbid async/dynamic imports in tests—only use synchronous, top-level imports. Please switch to a static import (or another synchronous approach) so the suite stays deterministic.
As per coding guidelines

-import { compareVersions } from '@tm/cli';
+import { compareVersions } from '@tm/cli';
+import {
+	displayUpgradeNotification,
+	parseChangelogHighlights
+} from '../../apps/cli/src/utils/auto-update.js';
@@
-describe('Update check functionality', () => {
-	let displayUpgradeNotification;
-	let parseChangelogHighlights;
-	let consoleLogSpy;
-
-	beforeAll(async () => {
-		// Import from @tm/cli instead of commands.js
-		const cliModule = await import('../../apps/cli/src/utils/auto-update.js');
-		displayUpgradeNotification = cliModule.displayUpgradeNotification;
-		parseChangelogHighlights = cliModule.parseChangelogHighlights;
-	});
+describe('Update check functionality', () => {
+	let consoleLogSpy;

</blockquote></details>

</blockquote></details>

- Comprehensive documentation at [docs.task-master.dev/capabilities/rpg-method](https://docs.task-master.dev/capabilities/rpg-method)
- Tool recommendations for code-context-aware PRD creation (Claude Code, Cursor, Gemini CLI, Codex/Grok)

The RPG template complements the existing `example_prd.txt` and provides a more structured approach for complex projects requiring clear module boundaries and dependency chains. No newline at end of file
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 | 🟡 Minor

Add final newline.

The file is missing a final newline character. Add one for proper formatting.

Apply this diff:

 The RPG template complements the existing `example_prd.txt` and provides a more structured approach for complex projects requiring clear module boundaries and dependency chains.
+
📝 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
The RPG template complements the existing `example_prd.txt` and provides a more structured approach for complex projects requiring clear module boundaries and dependency chains.
The RPG template complements the existing `example_prd.txt` and provides a more structured approach for complex projects requiring clear module boundaries and dependency chains.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

17-17: Files should end with a single newline character

(MD047, single-trailing-newline)

🤖 Prompt for AI Agents
In .changeset/nice-ways-hope.md at line 17, the file lacks a trailing newline;
open the file and add a final newline character at the end of the file so the
last line ends with a newline (ensure the file ends with '\n').

"task-master-ai": minor
---

Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks.
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 | 🟡 Minor

Remove trailing space.

Line 5 has a trailing space. Remove it for cleaner formatting.

Apply this diff:

-Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks. 
+Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks.
📝 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
Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks.
Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

5-5: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)


5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🤖 Prompt for AI Agents
In .changeset/smart-owls-relax.md around line 5, remove the trailing space at
the end of the line ("Enhance `expand_all` to intelligently use complexity
analysis recommendations when expanding tasks. ") so the line ends immediately
after the period; update the file to delete that trailing whitespace and save.

Comment on lines +37 to +49
```
claude-task-master/
├── .claude-plugin/
│ └── marketplace.json # Marketplace manifest (at repo root)
├── packages/claude-code-plugin/
│ ├── src/build.ts # Build tooling
│ └── [generated plugin files]
└── assets/claude/ # Plugin source files
├── commands/
└── agents/
```
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add a language hint to the fenced block.

Markdownlint flags this block because the fence lacks a language. Tag it as text (or similar) so the linter passes and tooling can render it consistently.

-```
+```text
 claude-task-master/
 ├── .claude-plugin/
 │   └── marketplace.json        # Marketplace manifest (at repo root)
 │
 ├── packages/claude-code-plugin/
 │   ├── src/build.ts           # Build tooling
 │   └── [generated plugin files]
 │
 └── assets/claude/              # Plugin source files
     ├── commands/
     └── agents/

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

37-37: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In CLAUDE_CODE_PLUGIN.md around lines 37 to 49 the fenced code block lacks a
language hint which triggers markdownlint; update the opening fence to include a
language tag such as "text" (e.g., replace withtext) so the block is
explicitly labeled and the linter/tooling renders it consistently.


</details>

<!-- fingerprinting:phantom:poseidon:sabertoothed -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment on lines +1 to +9
Show help for Task Master AI commands.

Arguments: $ARGUMENTS

Display help for Task Master commands. If arguments provided, show specific command help.

## Task Master AI Command Help

### Quick Navigation
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Promote the intro sentence to an H1.

The file starts with plain text, so markdownlint flags it. Make it the top-level heading and rename the duplicate heading below to keep the hierarchy clean.

-Show help for Task Master AI commands.
-
-Arguments: $ARGUMENTS
-
-Display help for Task Master commands. If arguments provided, show specific command help.
-
-## Task Master AI Command Help
+# Task Master AI Command Help
+
+Arguments: $ARGUMENTS
+
+Display help for Task Master commands. If arguments provided, show specific command help.
+
+## Overview
📝 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
Show help for Task Master AI commands.
Arguments: $ARGUMENTS
Display help for Task Master commands. If arguments provided, show specific command help.
## Task Master AI Command Help
### Quick Navigation
# Task Master AI Command Help
Arguments: $ARGUMENTS
Display help for Task Master commands. If arguments provided, show specific command help.
## Overview
### Quick Navigation
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🤖 Prompt for AI Agents
In packages/claude-code-plugin/commands/help.md around lines 1 to 9, the first
sentence is plain text and should be promoted to an H1; change "Show help for
Task Master AI commands." to "# Show help for Task Master AI commands." and
rename the duplicate "## Task Master AI Command Help" below to a lower-level
heading (e.g., "## Command Help" or "### Task Master AI Command Help") so the
Markdown heading hierarchy is correct and markdownlint no longer flags the file.

Comment on lines +2 to +7
"mcpServers": {
"task-master-ai": {
"type": "stdio",
"command": "npx",
"args": ["-y", "task-master-ai"]
}
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 | 🟠 Major

Provide required API credentials for the MCP server.
The Task Master MCP server expects its API key environment variables in the config; without them the npx task-master-ai command will fail to authenticate, so the plugin won't work. Please add the documented env block (e.g., TASK_MASTER_API_KEY and related secrets) under this server entry. Based on learnings

🤖 Prompt for AI Agents
In packages/claude-code-plugin/mcp.json around lines 2 to 7, the Task Master MCP
server entry lacks required API credential environment variables; add an "env"
block under the "task-master-ai" server entry containing the documented keys
(e.g., TASK_MASTER_API_KEY and any related secrets like TASK_MASTER_API_SECRET
or TASK_MASTER_ORG_ID as required by the service) so that the "npx
task-master-ai" command can authenticate; ensure variable names match the
service docs, keep values empty or reference secure secret injection (do not
hardcode secrets), and validate the JSON structure after adding the env object.

Comment on lines 108 to +114
function onRemoveRulesProfile(targetDir) {
// Remove .claude directory recursively
const claudeDir = path.join(targetDir, '.claude');
if (removeDirectoryRecursive(claudeDir)) {
log('debug', `[Claude] Removed .claude directory from ${claudeDir}`);
}
// Note: .claude directory (commands/agents) are now managed by Claude Code plugin
// We no longer remove them here - users should uninstall the plugin separately
log(
'info',
'[Claude] To remove Task Master commands/agents, uninstall the plugin with: /plugin uninstall taskmaster'
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

Consider migration guidance for orphaned .claude directories.

The plugin uninstall guidance is clear. However, users who previously installed Task Master will have orphaned .claude directories that won't be cleaned up by this removal process. Consider adding a note or cleanup step to help users migrate cleanly.

Optionally, you could add a check and informational message:

 function onRemoveRulesProfile(targetDir) {
 	// Note: .claude directory (commands/agents) are now managed by Claude Code plugin
 	// We no longer remove them here - users should uninstall the plugin separately
 	log(
 		'info',
 		'[Claude] To remove Task Master commands/agents, uninstall the plugin with: /plugin uninstall taskmaster'
 	);
+
+	// Check for legacy .claude directory and inform user
+	const legacyClaudeDir = path.join(targetDir, '.claude');
+	if (fs.existsSync(legacyClaudeDir)) {
+		log(
+			'info',
+			'[Claude] Found legacy .claude directory. You may safely remove it manually after uninstalling the plugin.'
+		);
+	}
 
 	// Clean up CLAUDE.md import

🏁 Script executed:

#!/bin/bash
echo "Checking for fs import/require..."
grep -R -E "(require\(['\"]fs['\"]|import fs)" -n src/profiles/claude.js || echo "→ no fs import/require found"
echo "Checking for path import/require..."
grep -R -E "(require\(['\"]path['\"]|import path)" -n src/profiles/claude.js || echo "→ no path import/require found"

Length of output: 119


Add migration guidance for legacy .claude directories.

Users upgrading from older versions may have orphaned .claude folders that aren’t removed by the new plugin uninstall process. In onRemoveRulesProfile, add a check and info log, for example:

 function onRemoveRulesProfile(targetDir) {
 	// Note: .claude directory (commands/agents) are now managed by Claude Code plugin
 	// We no longer remove them here - users should uninstall the plugin separately
 	log(
 		'info',
 		'[Claude] To remove Task Master commands/agents, uninstall the plugin with: /plugin uninstall taskmaster'
 	);

+	// Inform users about any legacy .claude directory
+	const legacyClaudeDir = path.join(targetDir, '.claude');
+	if (fs.existsSync(legacyClaudeDir)) {
+		log(
+			'info',
+			'[Claude] Found a legacy .claude directory. You may remove it manually after uninstalling the plugin.'
+		);
+	}

 	// Clean up CLAUDE.md import
📝 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
function onRemoveRulesProfile(targetDir) {
// Remove .claude directory recursively
const claudeDir = path.join(targetDir, '.claude');
if (removeDirectoryRecursive(claudeDir)) {
log('debug', `[Claude] Removed .claude directory from ${claudeDir}`);
}
// Note: .claude directory (commands/agents) are now managed by Claude Code plugin
// We no longer remove them here - users should uninstall the plugin separately
log(
'info',
'[Claude] To remove Task Master commands/agents, uninstall the plugin with: /plugin uninstall taskmaster'
);
function onRemoveRulesProfile(targetDir) {
// Note: .claude directory (commands/agents) are now managed by Claude Code plugin
// We no longer remove them here - users should uninstall the plugin separately
log(
'info',
'[Claude] To remove Task Master commands/agents, uninstall the plugin with: /plugin uninstall taskmaster'
);
// Inform users about any legacy .claude directory
const legacyClaudeDir = path.join(targetDir, '.claude');
if (fs.existsSync(legacyClaudeDir)) {
log(
'info',
'[Claude] Found a legacy .claude directory. You may remove it manually after uninstalling the plugin.'
);
}
// Clean up CLAUDE.md import
}
🤖 Prompt for AI Agents
In src/profiles/claude.js around lines 108 to 114, the onRemoveRulesProfile
function currently tells users to uninstall the plugin but does not provide
guidance for legacy .claude directories; add a non-destructive migration check
that detects an existing .claude directory under the targetDir, logs an
informational message advising the user how to safely remove or back up that
legacy directory (include the full path in the message), and do not auto-delete
anything—only warn and show the recommended command(s) (for example: move, rm,
or backup instructions) so users upgrading from older versions can clean up
orphaned folders.

Comment on lines +954 to +1025
beforeEach(async () => {
// Ensure a fresh module instance so ESM mocks apply to dynamic imports
jest.resetModules();
originalExit = process.exit;
process.exit = jest.fn();

// For ESM dynamic imports, use the same pattern
await jest.unstable_mockModule('../../scripts/modules/utils.js', () => ({
log: mockLog,
readJSON: mockReadJSON,
writeJSON: mockWriteJSON,
taskExists: mockTaskExists,
formatTaskId: mockFormatTaskId,
findCycles: mockFindCycles,
traverseDependencies: jest.fn(() => []),
isSilentMode: jest.fn(() => true),
findProjectRoot: jest.fn(() => '/test'),
resolveEnvVariable: jest.fn(() => undefined),
isEmpty: jest.fn((v) =>
v == null
? true
: Array.isArray(v)
? v.length === 0
: typeof v === 'object'
? Object.keys(v).length === 0
: false
),
enableSilentMode: jest.fn(),
disableSilentMode: jest.fn(),
getTaskManager: jest.fn(async () => ({})),
getTagAwareFilePath: jest.fn(
(basePath, _tag, projectRoot = '.') => basePath
),
readComplexityReport: jest.fn(() => null)
}));

// Also mock transitive imports to keep dependency surface minimal
await jest.unstable_mockModule('../../scripts/modules/ui.js', () => ({
displayBanner: jest.fn()
}));
await jest.unstable_mockModule(
'../../scripts/modules/task-manager/generate-task-files.js',
() => ({ default: jest.fn() })
);
// Set up test data that matches the issue report
// Clone fixture data before each test to prevent mutation issues
mockReadJSON.mockImplementation(() =>
structuredClone(crossLevelDependencyTasks)
);

// Configure mockTaskExists to properly validate cross-level dependencies
mockTaskExists.mockImplementation((tasks, taskId) => {
if (typeof taskId === 'string' && taskId.includes('.')) {
const [parentId, subtaskId] = taskId.split('.').map(Number);
const task = tasks.find((t) => t.id === parentId);
return (
task &&
task.subtasks &&
task.subtasks.some((st) => st.id === subtaskId)
);
}

const numericId =
typeof taskId === 'string' ? parseInt(taskId, 10) : taskId;
return tasks.some((task) => task.id === numericId);
});

mockFormatTaskId.mockImplementation((id) => {
if (typeof id === 'string' && id.includes('.')) return id; // keep dot notation
return parseInt(id, 10); // normalize top-level task IDs to number
});
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider reducing mock setup duplication.

The mock setup logic is duplicated between the main beforeEach (lines 82-151) and the cross-level tests' beforeEach (lines 954-1025). Specifically, the mockTaskExists and mockFormatTaskId implementations are nearly identical.

While the duplication ensures test isolation (which is good), consider extracting these common mock implementations into helper functions to improve maintainability.

Example refactor:

// At the top of the test file
const createMockTaskExists = () => {
  return jest.fn((tasks, taskId) => {
    if (typeof taskId === 'string' && taskId.includes('.')) {
      const [parentId, subtaskId] = taskId.split('.').map(Number);
      const task = tasks.find((t) => t.id === parentId);
      return task && task.subtasks && task.subtasks.some((st) => st.id === subtaskId);
    }
    const numericId = typeof taskId === 'string' ? parseInt(taskId, 10) : taskId;
    return tasks.some((task) => task.id === numericId);
  });
};

// Then in beforeEach blocks:
mockTaskExists.mockImplementation(createMockTaskExists());

Also applies to: 82-151

🤖 Prompt for AI Agents
In tests/unit/dependency-manager.test.js around lines 82-151 and 954-1025, the
mockTaskExists and mockFormatTaskId implementations are duplicated between the
main beforeEach and the cross-level beforeEach; extract these implementations
into reusable helpers at the top of the file (e.g., createMockTaskExists and
createMockFormatTaskId returning jest.fn implementations) and replace the inline
mockImplementation blocks in both beforeEach blocks with calls to those helpers
to DRY the setup while preserving test isolation.

🧹 Nitpick | 🔵 Trivial

Consider the implications of using unstable Jest APIs.

The test suite uses jest.unstable_mockModule() (lines 961-988) which is marked as unstable and may change in future Jest versions. While this is necessary for proper ESM mocking in the cross-level dependency tests, consider:

  1. Adding a comment documenting why the unstable API is necessary
  2. Monitoring Jest release notes for changes to this API
  3. Planning migration to stable APIs when available
🤖 Prompt for AI Agents
In tests/unit/dependency-manager.test.js around lines 954 to 1025, the test uses
jest.unstable_mockModule() (lines ~961-988) which is an unstable API; add a
short inline comment above those calls explaining why the unstable API is
required for ESM dynamic-import mocking in these cross-level dependency tests,
note that it may change in future Jest releases, and add a TODO to monitor Jest
release notes and plan migration to the stable API when available.

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: 7

Caution

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

⚠️ Outside diff range comments (1)
src/profiles/claude.js (1)

7-37: Remove unused helper functions.

The copyRecursiveSync and removeDirectoryRecursive functions are no longer used after the migration to plugin-based distribution. These functions were previously used for copying the .claude directory but are now dead code.

Apply this diff to remove the unused code:

-// Helper function to recursively copy directory (adopted from Roo profile)
-function copyRecursiveSync(src, dest) {
-	const exists = fs.existsSync(src);
-	const stats = exists && fs.statSync(src);
-	const isDirectory = exists && stats.isDirectory();
-	if (isDirectory) {
-		if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
-		fs.readdirSync(src).forEach((childItemName) => {
-			copyRecursiveSync(
-				path.join(src, childItemName),
-				path.join(dest, childItemName)
-			);
-		});
-	} else {
-		fs.copyFileSync(src, dest);
-	}
-}
-
-// Helper function to recursively remove directory
-function removeDirectoryRecursive(dirPath) {
-	if (fs.existsSync(dirPath)) {
-		try {
-			fs.rmSync(dirPath, { recursive: true, force: true });
-			return true;
-		} catch (err) {
-			log('error', `Failed to remove directory ${dirPath}: ${err.message}`);
-			return false;
-		}
-	}
-	return true;
-}
-


Add changelog highlights to auto-update notifications

When the CLI auto-updates to a new version, it now displays a "What's New" section. No newline at end of file
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 | 🟡 Minor

Add trailing newline for markdownlint compliance.

markdownlint (MD047) flags this file because it’s missing a trailing newline; please add one to keep CI green.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

7-7: Files should end with a single newline character

(MD047, single-trailing-newline)

🤖 Prompt for AI Agents
In .changeset/auto-update-changelog-highlights.md around line 7, the file is
missing a trailing newline which triggers markdownlint MD047; fix this by adding
a single newline character at the end of the file so the file ends with a blank
line.

Comment on lines 4 to 13
"initialVersions": {
"task-master-ai": "0.28.0",
"@tm/cli": "",
"docs": "0.0.5",
"extension": "0.25.5",
"@tm/ai-sdk-provider-grok-cli": "",
"@tm/build-config": "",
"@tm/claude-code-plugin": "0.0.1",
"@tm/core": ""
},
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 | 🔴 Critical

🧩 Analysis chain

Verify empty string versions in initialVersions.

Several packages have empty strings as their initialVersions:

  • @tm/cli
  • @tm/ai-sdk-provider-grok-cli
  • @tm/build-config
  • @tm/core

Empty string versions may cause issues during pre-release version calculation. Typically, initialVersions should contain the actual starting version for each package (e.g., "0.0.0" or the current published version).

Please run the following script to verify the current versions of these packages and confirm whether empty strings are intentional:

Alternatively, verify the changeset documentation to confirm whether empty strings are supported for packages that don't have a published version yet.


🏁 Script executed:

#!/bin/bash
# Description: Check current versions of packages with empty initialVersions

echo "Checking package versions..."

for pkg in "packages/cli" "packages/ai-sdk-provider-grok-cli" "packages/build-config" "packages/core"; do
  if [ -f "$pkg/package.json" ]; then
    echo ""
    echo "Package: $pkg"
    jq -r '.name + " v" + .version' "$pkg/package.json"
  fi
done

echo ""
echo "Verifying if empty strings are valid for changeset pre-release mode..."

Length of output: 226


🏁 Script executed:

#!/bin/bash
echo "1) Listing all package.json under packages/:"
find packages -maxdepth 2 -type f -name package.json

echo -e "\n2) Inspecting package.json files for @tm/cli, @tm/core, ai-sdk-provider-grok-cli, build-config:"
for pkg in "@tm/cli" "@tm/core" "@tm/ai-sdk-provider-grok-cli" "@tm/build-config"; do
  files=$(rg -l "\"name\": \"$pkg\"" -g 'packages/**/package.json')
  if [ -z "$files" ]; then
    echo "No package.json found for $pkg"
  else
    for file in $files; do
      echo -e "\n-- $file --"
      jq '{name: .name, version: .version}' "$file"
    done
  fi
done

Length of output: 615


Provide valid initialVersions entries or remove nonexistent packages
The .changeset/pre.json currently has empty strings for:

  • @tm/ai-sdk-provider-grok-cli
  • @tm/build-config
  • @tm/core
    …and also lists @tm/cli, which isn’t in the workspace.
    Set each to its actual published version (or use "0.0.0" for unpublished), and remove or correct the @tm/cli entry.
🤖 Prompt for AI Agents
In .changeset/pre.json around lines 4-13, the initialVersions object contains
empty string values for @tm/ai-sdk-provider-grok-cli, @tm/build-config, and
@tm/core and an entry for @tm/cli which is not in the workspace; replace each
empty string with the actual published version (or use "0.0.0" if unpublished)
and either remove the @tm/cli key or correct it to the real workspace package
name/version so all entries are valid package versions.

"task-master-ai": minor
---

Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks.
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Remove trailing space.

Line 5 has a trailing space that should be removed for consistency with markdown best practices.

Apply this diff:

-Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks. 
+Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks.
📝 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
Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks.
Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

5-5: Trailing spaces
Expected: 0 or 2; Actual: 1

(MD009, no-trailing-spaces)


5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🤖 Prompt for AI Agents
In .changeset/smart-owls-relax.md around line 5, there is a trailing space at
the end of the line "Enhance `expand_all` to intelligently use complexity
analysis recommendations when expanding tasks. " — remove the trailing
whitespace so the line ends immediately after the period to comply with Markdown
formatting conventions.

"plugins": [
{
"name": "taskmaster",
"source": "./packages/claude-code-plugin",
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 11, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify plugin source path resolution.

The plugin source is specified as "./packages/claude-code-plugin", which is a relative path. Ensure that this path correctly resolves from the location of marketplace.json in the .claude-plugin/ directory to the actual plugin package.

Given the file structure, this should resolve to ../.claude-plugin/../packages/claude-code-plugin from the marketplace.json location, which may not be the intended path.

Run the following script to verify the path resolution:


🏁 Script executed:

#!/bin/bash
# Description: Verify the plugin source path exists and is accessible

MARKETPLACE_FILE=".claude-plugin/marketplace.json"
PLUGIN_SOURCE=$(jq -r '.plugins[0].source' "$MARKETPLACE_FILE")

echo "Marketplace file: $MARKETPLACE_FILE"
echo "Plugin source (as specified): $PLUGIN_SOURCE"
echo ""

# Resolve the path relative to marketplace.json location
MARKETPLACE_DIR=$(dirname "$MARKETPLACE_FILE")
RESOLVED_PATH="$MARKETPLACE_DIR/$PLUGIN_SOURCE"

echo "Resolved path: $RESOLVED_PATH"
echo ""

if [ -d "$RESOLVED_PATH" ]; then
  echo "✓ Plugin source directory exists"
  echo ""
  echo "Contents:"
  ls -la "$RESOLVED_PATH" | head -10
else
  echo "✗ Plugin source directory does NOT exist"
  echo ""
  echo "Expected directory should contain:"
  echo "  - .claude-plugin/plugin.json"
  echo "  - package.json"
  echo "  - commands/"
  echo "  - agents/"
fi

Length of output: 322


Fix plugin source path
In .claude-plugin/marketplace.json, change source from "./packages/claude-code-plugin" to "../packages/claude-code-plugin" so it correctly resolves to packages/claude-code-plugin.

🤖 Prompt for AI Agents
In .claude-plugin/marketplace.json around line 14, the "source" path is
incorrect; update the value from "./packages/claude-code-plugin" to
"../packages/claude-code-plugin" so the path correctly resolves to the
repository's packages/claude-code-plugin directory; save the file and run any
local validation or package resolution to confirm the plugin loads correctly.

Copy link
Owner

Choose a reason for hiding this comment

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

@Crunchyman-ralph is this a legit issue?

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +15 to +27
## 🎉 New: Claude Code Plugin

Task Master AI commands and agents are now distributed as a proper Claude Code plugin:
- **49 slash commands** with clean naming (`/task-master-ai:command-name`)
- **3 specialized AI agents** (task-orchestrator, task-executor, task-checker)
- **MCP server integration** for deep Claude Code integration

**Installation:**

```bash
/plugin marketplace add eyaltoledano/claude-task-master
/plugin install taskmaster@taskmaster
```
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 | 🟡 Minor

Add the missing blank line around the subheading.

markdownlint (MD022) is flagging the ## 🎉 New: Claude Code Plugin header because it isn’t separated from surrounding text by blank lines. Please insert an empty line before and after that heading to keep the changelog lint‑clean.

🤖 Prompt for AI Agents
In CHANGELOG.md around lines 15 to 27, the second-level heading "## 🎉 New:
Claude Code Plugin" is not separated from surrounding text which triggers
markdownlint MD022; insert a single blank line immediately before that heading
and another blank line immediately after it so the heading is isolated from the
preceding and following paragraphs and the lint error is resolved.

Comment on lines +37 to +49
```
claude-task-master/
├── .claude-plugin/
│ └── marketplace.json # Marketplace manifest (at repo root)
├── packages/claude-code-plugin/
│ ├── src/build.ts # Build tooling
│ └── [generated plugin files]
└── assets/claude/ # Plugin source files
├── commands/
└── agents/
```
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 | 🟡 Minor

Specify fenced code block language to satisfy markdownlint

markdownlint (MD040) flags this fenced block because it lacks an explicit language. Please add a language such as text to keep CI clean.

Apply this diff:

-```
+```text
 claude-task-master/
 ├── .claude-plugin/
 │   └── marketplace.json        # Marketplace manifest (at repo root)
 │
 ├── packages/claude-code-plugin/
 │   ├── src/build.ts           # Build tooling
 │   └── [generated plugin files]
 │
 └── assets/claude/              # Plugin source files
     ├── commands/
     └── agents/

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

37-37: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In CLAUDE_CODE_PLUGIN.md around lines 37 to 49, the fenced code block lacks a
language spec which triggers markdownlint MD040; update the block by changing
the opening fence from totext so the snippet is fenced as text (i.e.,
replace the existing backticks with a backtick fence that includes "text"
immediately after the opening triple backticks).


</details>

<!-- fingerprinting:phantom:triton:sabertoothed -->

<!-- This is an auto-generated comment by CodeRabbit -->

disableSilentMode
} from '../../../../scripts/modules/utils.js';
import { createLogWrapper } from '../../tools/utils.js';
import { resolveComplexityReportOutputPath } from '../../../../src/utils/path-utils.js';
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 | 🟡 Minor

Use the MCP-specific path utilities wrapper.

The import should reference mcp-server/src/core/utils/path-utils.js instead of src/utils/path-utils.js. The MCP wrapper ensures proper logging integration with the FastMCP logger.

Apply this diff:

-import { resolveComplexityReportOutputPath } from '../../../../src/utils/path-utils.js';
+import { resolveComplexityReportOutputPath } from '../../utils/path-utils.js';

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In mcp-server/src/core/direct-functions/expand-all-tasks.js around line 11 the
import points to '../../../../src/utils/path-utils.js' but should use the
MCP-specific wrapper to integrate FastMCP logging; update the import path to
'../../../../core/utils/path-utils.js' (i.e.
mcp-server/src/core/utils/path-utils.js) so the module imports the MCP wrapper
instead of the top-level src utility.

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: 1

📜 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 9a22622 and 8f60a05.

⛔ Files ignored due to path filters (1)
  • images/hamster-hiring.png is excluded by !**/*.png
📒 Files selected for processing (1)
  • README.md (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: update-docs

Comment on lines +313 to +318
## Join Our Team

<a href="https://tryhamster.com" target="_blank">
<img src="./images/hamster-hiring.png" alt="Join Hamster's founding team" />
</a>

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 | 🔴 Critical

🧩 Analysis chain

Harden external link; verify image exists

Add rel="noopener noreferrer" to the target="_blank" link to prevent reverse‑tabnabbing. Also confirm images/hamster-hiring.png is committed.

Proposed change:

-<a href="https://tryhamster.com" target="_blank">
+<a href="https://tryhamster.com" target="_blank" rel="noopener noreferrer">
   <img src="./images/hamster-hiring.png" alt="Join Hamster's founding team" />
 </a>

Run to verify the image file:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Referencing occurrences in README:"
rg -n 'hamster-hiring\.png' -C2 README.md || true

echo -e "\nChecking file presence:"
fd -a 'hamster-hiring.png' -t f || { echo "ERROR: images/hamster-hiring.png not found"; exit 1; }

echo -e "\nFile details:"
fd -a 'hamster-hiring.png' -t f | xargs -I{} sh -c 'ls -lh "{}"; file "{}" || true'

Length of output: 392


Add rel="noopener noreferrer" to external link

target="_blank" opens a new tab and can enable reverse-tabnabbing; include rel attributes for safety.

-<a href="https://tryhamster.com" target="_blank">
+<a href="https://tryhamster.com" target="_blank" rel="noopener noreferrer">
   <img src="./images/hamster-hiring.png" alt="Join Hamster's founding team" />
 </a>
📝 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
## Join Our Team
<a href="https://tryhamster.com" target="_blank">
<img src="./images/hamster-hiring.png" alt="Join Hamster's founding team" />
</a>
## Join Our Team
<a href="https://tryhamster.com" target="_blank" rel="noopener noreferrer">
<img src="./images/hamster-hiring.png" alt="Join Hamster's founding team" />
</a>
🤖 Prompt for AI Agents
In README.md around lines 313 to 318 the external anchor uses target="_blank"
without rel attributes, which risks reverse-tabnabbing; update the anchor tag to
include rel="noopener noreferrer" (or rel="noopener noreferrer nofollow" if
desired) so the new tab cannot access the opener and to follow security best
practices.

eyaltoledano
eyaltoledano previously approved these changes Oct 12, 2025
Copy link
Owner

@eyaltoledano eyaltoledano left a comment

Choose a reason for hiding this comment

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

up up and away

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
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: 2

Caution

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

⚠️ Outside diff range comments (2)
apps/cli/src/commands/context.command.ts (1)

129-141: Don’t gate on isAuthenticated(); attempt auto‑refresh first

Using isAuthenticated() blocks flows when a refresh token is present but access token is expired. Call await getCredentials() to trigger auto‑refresh and proceed if non‑null.

Apply this diff (repeat the pattern for all gated blocks):

@@
-    // Check authentication first
-    if (!this.authManager.isAuthenticated()) {
-      console.log(chalk.yellow('✗ Not authenticated'));
-      console.log(chalk.gray('\n  Run "tm auth login" to authenticate first'));
-
-      return {
-        success: false,
-        action: 'show',
-        message: 'Not authenticated'
-      };
-    }
+    // Attempt auto-refresh; treat null as unauthenticated
+    const creds = await this.authManager.getCredentials();
+    if (!creds) {
+      console.log(chalk.yellow('✗ Not authenticated'));
+      console.log(chalk.gray('\n  Run "tm auth login" to authenticate first'));
+      return { success: false, action: 'show', message: 'Not authenticated' };
+    }
@@
-      // Check authentication
-      if (!this.authManager.isAuthenticated()) {
+      // Attempt auto-refresh; treat null as unauthenticated
+      const creds = await this.authManager.getCredentials();
+      if (!creds) {
         ui.displayError('Not authenticated. Run "tm auth login" first.');
         process.exit(1);
       }
@@
-      // Check authentication
-      if (!this.authManager.isAuthenticated()) {
+      const creds = await this.authManager.getCredentials();
+      if (!creds) {
         ui.displayError('Not authenticated. Run "tm auth login" first.');
         process.exit(1);
       }
@@
-      // Check authentication
-      if (!this.authManager.isAuthenticated()) {
+      const creds = await this.authManager.getCredentials();
+      if (!creds) {
         ui.displayError('Not authenticated. Run "tm auth login" first.');
         process.exit(1);
       }
@@
-      // Check authentication
-      if (!this.authManager.isAuthenticated()) {
+      const creds = await this.authManager.getCredentials();
+      if (!creds) {
         ui.displayError('Not authenticated. Run "tm auth login" first.');
         process.exit(1);
       }
@@
-      // Check authentication
-      if (!this.authManager.isAuthenticated()) {
+      const creds = await this.authManager.getCredentials();
+      if (!creds) {
         ui.displayError('Not authenticated. Run "tm auth login" first.');
         process.exit(1);
       }

Also applies to: 201-206, 281-285, 387-391, 433-437, 457-461

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

55-57: Use existing logger instance

Reuse this.logger instead of creating another logger.

-      const logger = getLogger('AuthManager');
-      logger.debug('No existing session to restore');
+      this.logger.debug('No existing session to restore');
♻️ Duplicate comments (2)
CHANGELOG.md (1)

21-23: Insert blank lines around the subheading.

We still need a blank line above and below ## 🎉 New: Claude Code Plugin to keep markdownlint happy (MD022). Please add the spacing.

.changeset/pre.json (1)

4-13: Invalid initialVersions entries will break pre-release.

Changesets expects every initialVersions value to be a valid semver string for an actual workspace package. Empty strings cause the pre-enter flow to throw, and @tm/cli isn’t even part of the workspace, so the tooling can’t resolve it. Please give each package a real starting version (use "0.0.0" if it hasn’t shipped yet) and drop or correct the stray @tm/cli entry before running the RC workflow.

   "initialVersions": {
     "task-master-ai": "0.29.0-rc.0",
-    "@tm/cli": "",
     "docs": "0.0.5",
     "extension": "0.25.6-rc.0",
-    "@tm/ai-sdk-provider-grok-cli": "",
-    "@tm/build-config": "",
+    "@tm/ai-sdk-provider-grok-cli": "0.0.0",
+    "@tm/build-config": "0.0.0",
     "@tm/claude-code-plugin": "0.0.1",
-    "@tm/core": ""
+    "@tm/core": "0.0.0"
   },
📜 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 663aa2d and 11b7354.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • .changeset/config.json (1 hunks)
  • .changeset/pre.json (1 hunks)
  • .changeset/silent-bushes-grow.md (1 hunks)
  • CHANGELOG.md (1 hunks)
  • apps/cli/src/commands/auth.command.ts (5 hunks)
  • apps/cli/src/commands/context.command.ts (10 hunks)
  • apps/cli/src/commands/export.command.ts (1 hunks)
  • package.json (2 hunks)
  • packages/tm-core/package.json (1 hunks)
  • packages/tm-core/src/auth/auth-manager.spec.ts (3 hunks)
  • packages/tm-core/src/auth/auth-manager.ts (5 hunks)
  • packages/tm-core/src/auth/credential-store.spec.ts (1 hunks)
  • packages/tm-core/src/repositories/supabase/supabase-task-repository.ts (4 hunks)
  • packages/tm-core/src/services/export.service.ts (3 hunks)
  • packages/tm-core/src/storage/api-storage.ts (3 hunks)
  • packages/tm-core/src/storage/storage-factory.ts (2 hunks)
  • packages/tm-core/tests/auth/auth-refresh.test.ts (1 hunks)
  • packages/tm-core/tests/integration/auth-token-refresh.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{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/tests/integration/auth-token-refresh.test.ts
  • packages/tm-core/src/auth/credential-store.spec.ts
  • packages/tm-core/tests/auth/auth-refresh.test.ts
  • packages/tm-core/src/auth/auth-manager.spec.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/tests/integration/auth-token-refresh.test.ts
  • packages/tm-core/src/auth/credential-store.spec.ts
  • packages/tm-core/tests/auth/auth-refresh.test.ts
  • packages/tm-core/src/auth/auth-manager.spec.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/tests/integration/auth-token-refresh.test.ts
  • packages/tm-core/src/auth/credential-store.spec.ts
  • packages/tm-core/tests/auth/auth-refresh.test.ts
  • packages/tm-core/src/auth/auth-manager.spec.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/tests/integration/auth-token-refresh.test.ts
  • packages/tm-core/tests/auth/auth-refresh.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{test,spec}.{js,jsx,ts,tsx}: NEVER use async/await in test functions unless testing actual asynchronous operations
Use synchronous top-level imports in tests; avoid dynamic await import()
Keep test bodies synchronous whenever possible

Files:

  • packages/tm-core/tests/integration/auth-token-refresh.test.ts
  • packages/tm-core/src/auth/credential-store.spec.ts
  • packages/tm-core/tests/auth/auth-refresh.test.ts
  • packages/tm-core/src/auth/auth-manager.spec.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
.changeset/*.md

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

.changeset/*.md: When running npm run changeset or npx changeset add, provide a concise summary of the changes for the CHANGELOG.md in imperative mood, typically a single line, and not a detailed Git commit message.
The changeset summary should be user-facing, describing what changed in the released version that is relevant to users or consumers of the package.
Do not use your detailed Git commit message body as the changeset summary.

Files:

  • .changeset/silent-bushes-grow.md
.changeset/*

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

Create appropriate changesets for new features, use semantic versioning, include tagged system information in release notes, and document breaking changes if any.

Files:

  • .changeset/silent-bushes-grow.md
  • .changeset/pre.json
  • .changeset/config.json
.changeset/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Changeset entries should be user-facing, describing the end-user impact rather than code specifics

Files:

  • .changeset/silent-bushes-grow.md
🧠 Learnings (3)
📚 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/tests/integration/auth-token-refresh.test.ts
📚 Learning: 2025-09-26T19:10:32.906Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1252
File: tsconfig.json:22-28
Timestamp: 2025-09-26T19:10:32.906Z
Learning: In the eyaltoledano/claude-task-master repository, all internal tm/ package path mappings in tsconfig.json consistently point to TypeScript source files (e.g., "./packages/*/src/index.ts") rather than built JavaScript. This is intentional architecture because tsdown bundles internal packages directly from source during build time, eliminating the need for separate compilation of internal packages.

Applied to files:

  • package.json
📚 Learning: 2025-08-06T21:13:05.240Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1091
File: assets/claude/agents/task-orchestrator.md:69-78
Timestamp: 2025-08-06T21:13:05.240Z
Learning: For Claude agent instruction files in .claude/agents/ and assets/claude/agents/ directories, ignore markdownlint issues (like MD031 blanks-around-fences, MD040 fenced-code-language, MD041 missing top-level heading) as these files follow Claude's specific agent format requirements rather than standard markdown documentation conventions.

Applied to files:

  • CHANGELOG.md
🧬 Code graph analysis (5)
packages/tm-core/tests/integration/auth-token-refresh.test.ts (1)
packages/tm-core/src/auth/auth-manager.ts (1)
  • AuthManager (26-337)
packages/tm-core/src/auth/auth-manager.ts (4)
packages/tm-core/src/logger/factory.ts (1)
  • getLogger (23-44)
packages/tm-core/src/auth/types.ts (3)
  • AuthCredentials (5-14)
  • AuthConfig (39-43)
  • UserContext (16-22)
packages/tm-core/src/auth/credential-store.ts (1)
  • CredentialStore (21-265)
packages/tm-core/src/clients/supabase-client.ts (1)
  • SupabaseAuthClient (16-312)
packages/tm-core/tests/auth/auth-refresh.test.ts (1)
packages/tm-core/src/auth/auth-manager.ts (1)
  • AuthManager (26-337)
apps/cli/src/commands/context.command.ts (1)
packages/tm-core/src/auth/types.ts (1)
  • UserContext (16-22)
apps/cli/src/commands/auth.command.ts (1)
packages/tm-core/src/auth/types.ts (1)
  • AuthCredentials (5-14)
🪛 markdownlint-cli2 (0.18.1)
.changeset/silent-bushes-grow.md

5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

CHANGELOG.md

35-35: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build
  • GitHub Check: typecheck
  • GitHub Check: Test
  • GitHub Check: Test
  • GitHub Check: update-docs
🔇 Additional comments (18)
apps/cli/src/commands/export.command.ts (1)

106-106: LGTM! Context retrieval is now asynchronous.

The change correctly awaits getContext(), aligning with the broader shift to asynchronous authentication flows across the CLI.

.changeset/silent-bushes-grow.md (1)

1-5: LGTM! Changeset follows conventions.

The changeset appropriately documents the authentication refresh token improvement as a patch-level change. The static analysis hint about missing a top-level heading is a false positive—changesets follow a specific frontmatter format and don't require markdown headings.

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

24-29: LGTM! Mock updated for async credential retrieval.

The CredentialStore mock correctly adds getInstance and resetInstance static methods and updates getCredentials to accept optional parameters, aligning with the new asynchronous credential handling pattern.

Also applies to: 33-35


93-111: LGTM! Test properly handles async credential retrieval.

The test correctly marks the function as async and awaits getCredentials(), ensuring proper testing of the asynchronous authentication flow.

packages/tm-core/src/services/export.service.ts (3)

108-108: LGTM! Context retrieval is now asynchronous.

The changes correctly await getContext(), aligning with the broader shift to asynchronous authentication flows.

Also applies to: 235-235


382-382: LGTM! Credentials retrieval is now asynchronous.

The change correctly awaits getCredentials(), consistent with the asynchronous authentication pattern.


365-379: No remaining /tasks/bulk references. The endpoint was updated consistently; please ensure /ai/api/v1/briefs/${briefId}/tasks matches the latest API spec.

packages/tm-core/src/repositories/supabase/supabase-task-repository.ts (2)

50-58: LGTM! Brief ID retrieval is now asynchronous.

The method signature correctly changed to async and returns Promise<string>, with getContext() properly awaited. This aligns with the broader asynchronous authentication pattern.


64-64: LGTM! All call sites properly updated.

All invocations of getBriefIdOrThrow() correctly await the promise, ensuring proper asynchronous handling throughout the repository.

Also applies to: 117-117, 160-160

apps/cli/src/commands/auth.command.ts (3)

146-152: LGTM! Status display enhanced with async handling and improved UX.

The changes correctly make displayStatus() asynchronous and await getCredentials(). The enhanced expiration display with time remaining (hours/minutes) provides better user feedback compared to the previous implementation.

Also applies to: 174-241


328-342: LGTM! Existing authentication check now asynchronous.

The change correctly awaits getCredentials() when checking for existing authentication, maintaining consistency with the asynchronous pattern.


493-495: LGTM! Public API signature updated for async credentials.

The return type correctly changed to Promise<AuthCredentials | null>, reflecting the asynchronous nature of credential retrieval.

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

75-89: LGTM! Deliberate use of synchronous credential retrieval.

The changes correctly use getCredentialsSync() instead of the async variant, which is appropriate for the storage factory initialization context. The added comment clearly documents the synchronous behavior and lack of auto-refresh, making the design decision explicit.

Also applies to: 106-122

packages/tm-core/src/storage/api-storage.ts (3)

122-122: LGTM! Context retrieval is now asynchronous in cache loading.

The change correctly awaits getContext() when loading tags into cache, aligning with the asynchronous authentication pattern.


155-155: LGTM! Context retrieval is now asynchronous in task loading.

The change correctly awaits getContext() when loading tasks, ensuring proper asynchronous handling of authentication context.


321-321: LGTM! Context retrieval is now asynchronous in tag listing.

The change correctly awaits getContext() when listing tags, maintaining consistency with the asynchronous authentication flow.

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

42-289: Solid coverage; behavior matches CredentialStore semantics

Scenarios and persistence checks look correct, including 30s skew and normalization. Temp FS isolation + singleton reset are clean.

packages/tm-core/tests/auth/auth-refresh.test.ts (1)

53-107: Good coverage of refresh semantics and concurrency dedup

Assertions match the new async flow and single‑refresh guarantee.

Also applies to: 109-175

Comment on lines 353 to 358
return {
success: true,
action: 'select-brief',
context: this.authManager.getContext() || undefined,
context: (await this.authManager.getContext()) || undefined,
message: `Selected brief: ${selectedBrief.name}`
};
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 | 🟡 Minor

Message uses possibly undefined selectedBrief.name

Briefs are displayed without a name; message may show “undefined”. Use the computed briefName or fall back to ID.

-          message: `Selected brief: ${selectedBrief.name}`
+          message: `Selected brief: ${briefName}`
@@
-          message: 'Cleared brief selection'
+          message: 'Cleared brief selection'

Also applies to: 371-373

🤖 Prompt for AI Agents
In apps/cli/src/commands/context.command.ts around lines 353-358 (and also apply
same fix to lines 371-373), the user-facing message interpolates
selectedBrief.name which may be undefined; instead reference the computed
briefName variable (or fall back to selectedBrief.id) when building the message
string. Update the return objects to use `briefName ?? selectedBrief.id` (or the
local computed briefName) for the message template so it never shows "undefined"
and ensure the same change is applied to both return sites.

Comment on lines +12 to +15
import type { Session } from '@supabase/supabase-js';
import { AuthManager } from '../../src/auth/auth-manager';
import { CredentialStore } from '../../src/auth/credential-store';
import type { AuthCredentials } from '../../src/auth/types';
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 | 🟠 Major

Tests access private field supabaseClient; will fail type-check

Spy on the client’s prototype instead of indexing a private field.

Apply these changes:

  • Add import:
@@
-import type { Session } from '@supabase/supabase-js';
+import type { Session } from '@supabase/supabase-js';
+import { SupabaseAuthClient } from '../../src/clients/supabase-client';
  • Replace each spy like vi.spyOn(authManager['supabaseClient'], 'refreshSession') with:
-vi.spyOn(
-  authManager['supabaseClient'],
-  'refreshSession'
-).mockImplementation(mockRefreshSession);
+vi.spyOn(SupabaseAuthClient.prototype, 'refreshSession')
+  .mockImplementation(mockRefreshSession);

And analogous replacements for .mockResolvedValue(...) and .mockRejectedValue(...).

Optionally, for “Multiple Concurrent Calls,” assert single invocation to reflect refreshPromise coalescing:

- expect(mockRefreshSession).toHaveBeenCalled();
+ expect(mockRefreshSession).toHaveBeenCalledTimes(1);

As per coding guidelines

Also applies to: 95-101, 128-131, 160-163, 196-199, 266-269, 293-297, 341-344, 371-374

🤖 Prompt for AI Agents
In packages/tm-core/tests/integration/auth-token-refresh.test.ts around lines
12-15, the tests access the private field authManager['supabaseClient'] which
breaks type-checking; import SupabaseClient from '@supabase/supabase-js' and
replace spies that index the private field (e.g.
vi.spyOn(authManager['supabaseClient'], 'refreshSession')) with spies on the
prototype (vi.spyOn(SupabaseClient.prototype, 'refreshSession')), then adjust
subsequent uses of .mockResolvedValue(. . .) and .mockRejectedValue(. . .) to
operate on that prototype spy (using
.mockResolvedValueOnce/.mockRejectedValueOnce where appropriate), and for the
“Multiple Concurrent Calls” test assert the prototype method was called exactly
once to reflect refreshPromise coalescing; apply the same replacements to the
other affected ranges (95-101, 128-131, 160-163, 196-199, 266-269, 293-297,
341-344, 371-374).

eyaltoledano
eyaltoledano previously approved these changes Oct 13, 2025
@Crunchyman-ralph Crunchyman-ralph merged commit c4f5d89 into main Oct 14, 2025
17 checks passed
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.

2 participants