Skip to content

Conversation

@will-bogusz
Copy link

@will-bogusz will-bogusz commented May 28, 2025

Fixes #615 - Feature Request: Bulk Dependency Management

Changes Made

Enhanced Existing Commands (No New Commands Added):

  • Extended add-dependency and remove-dependency CLI commands to support multiple IDs
  • Enhanced add_dependency and remove_dependency MCP tools with bulk capabilities
  • Added intelligent detection to automatically handle single vs. multiple IDs

Core Implementation:

  • Added parseBulkTaskIds() function supporting range syntax ("7-10") and comma-separated lists ("7,8,9")
  • Modified addDependency() and removeDependency() functions to detect multiple IDs and delegate to internal bulk processing
  • Maintained full backward compatibility - all existing usage continues to work unchanged

Removed Complexity:

  • Eliminated separate bulk commands in favor of extending existing ones
  • Removed duplicate MCP tools and consolidated functionality
  • Simplified documentation and user experience

Why This Approach

The original issue highlighted inefficient bulk operations burning through Cursor's 25 tool call limit. Instead of adding new commands, we extended existing commands to
transparently handle both single and bulk operations:

  • Single API: Users don't need to learn new commands
  • Automatic Detection: Commands intelligently detect bulk vs. single operations
  • Full Compatibility: All existing scripts/workflows continue working
  • Tool Call Efficiency: One command can now handle multiple dependencies

How It Works

Before: 6 separate commands for tasks x,y depending on a,b,c
task-master add-dependency --id=x --depends-on=a
task-master add-dependency --id=x --depends-on=b
// ... 4 more commands

After: 1 command handles all relationships
task-master add-dependency --id="x,y" --depends-on="a,b,c"
// OR with ranges
task-master add-dependency --id="7-10" --depends-on="1-5"

The implementation detects multiple IDs via parseBulkTaskIds() and automatically uses internal bulk processing with validation, error handling, and atomic operations. Single IDs
continue using the original optimized code path.

Result: Solves the tool call efficiency problem while maintaining a clean, intuitive API.

Summary by CodeRabbit

  • New Features

    • Add/remove dependency commands accept multiple task IDs (ranges and comma-separated lists), supporting bulk operations with atomic updates, circular-dependency detection, dry-run and silent modes, and summarized results.
  • Documentation

    • Command and tool help updated with clearer usage and examples for ranges/lists and multi-ID handling.
  • Tests

    • Added unit and integration tests for bulk ID parsing, multi-ID add/remove flows, and routing to bulk operations.

@changeset-bot
Copy link

changeset-bot bot commented May 28, 2025

⚠️ No Changeset found

Latest commit: 31977d5

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

@eyaltoledano
Copy link
Owner

Thanks @will-bogusz -- let's please add a description, and I think the implementation for this could simply live within the existing add/remove-dependency tools with some new flags to manage them in bulk. You could also simply add comma-separated ID support to extend the existing functions rather than creating brand new ones.

@will-bogusz
Copy link
Author

will-bogusz commented May 29, 2025

@eyaltoledano thanks - had meant to clean and polish it up earlier.. I've filled out the description and linked the original issue now

Regarding extending the existing commands vs new bulk ones - I think it's a good call to consolidate instead of adding brand new ones, but I was concerned about making the basic commands too fragile/unreliable from a tool call perspective with the new syntax/parameters

I went ahead and updated the tool descriptions to help avoid this, but will defer to you if you want any other changes before this is good to go

@Crunchyman-ralph
Copy link
Collaborator

Thanks for the PR @will-bogusz, right after the ai sdk v5 migration PR, I will be looking into polishing this up if needs be and merging it!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 23, 2025

Walkthrough

Bulk dependency support was added: CLI and MCP tool descriptions now accept ranges and comma-separated lists. The dependency manager gained parsing utilities, bulk add/remove APIs with validation (including circular checks), dry-run and atomic apply support, adjusted exports, and expanded unit tests for bulk behaviors.

Changes

Cohort / File(s) Summary
MCP tool descriptions
mcp-server/src/tools/add-dependency.js, mcp-server/src/tools/remove-dependency.js
Widened CLI/MCP tool help text and parameter docs to document support for ranges and comma-separated lists for id and dependsOn; no runtime logic changes in these files.
CLI command handlers
scripts/modules/commands.js
Command handlers updated to pass raw ID specs (ranges, lists, dot-notation) through to dependency manager; descriptions/options updated to reflect multi-ID handling and examples.
Dependency manager (bulk ops)
scripts/modules/dependency-manager.js
Added parseBulkTaskIds, bulkAddDependencies, bulkRemoveDependencies; addDependency/removeDependency route to bulk flows when appropriate; implemented getTaskFromPath, circular-dependency simulation, dry-run/silent modes, atomic apply with summaries; adjusted imports/IO and exported new bulk utilities while removing several legacy single-task helpers.
Direct function passthroughs
mcp-server/src/core/direct-functions/add-dependency.js, mcp-server/src/core/direct-functions/remove-dependency.js
ID normalization removed; raw id and dependsOn strings (including ranges/lists/dot-notation) are forwarded to core dependency APIs.
Unit tests
tests/unit/dependency-manager.test.js
Added tests covering parseBulkTaskIds, range/list parsing, duplicates/invalid formats, and bulk add/remove routing; adapted fixtures and mocks for bulk behavior.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User/CLI
  participant C as commands.js
  participant DM as dependency-manager
  participant FS as File System
  participant TM as task-manager.generateTaskFiles

  U->>C: run add/remove-dependency --id=7-10 --depends-on=11,12,15-16 [--dry-run]
  C->>DM: bulkAddDependencies / bulkRemoveDependencies(tasksPath, taskSpec, dependencySpec, options)

  rect rgba(230,240,255,0.6)
    note right of DM: Parse & expand ranges/lists
    DM->>DM: parseBulkTaskIds(taskSpec/dependencySpec)
  end

  rect rgba(240,230,255,0.6)
    note right of DM: Resolve tasks, validate, simulate for cycles
    DM->>DM: getTaskFromPath / simulate changes / detect circular deps
  end

  alt Dry-run
    DM-->>C: return dry-run report (no writes)
    C-->>U: display preview
  else Apply
    DM->>FS: write updated task JSON
    DM->>TM: regenerate task files
    TM->>FS: write generated files
    DM-->>C: summary report
    C-->>U: success/failure output
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly and concisely describes the primary change by indicating that this pull request adds bulk dependency management support, accurately reflecting the core feature implemented.
Linked Issues Check ✅ Passed This implementation fully addresses the objectives of issue #615 by extending both CLI and MCP tools to support ranges and comma-separated lists, introducing parseBulkTaskIds, routing single versus bulk operations, providing atomic batch validation with rollback and circular dependency detection, preserving backward compatibility, and adding dry-run and logging capabilities.
Out of Scope Changes Check ✅ Passed All code changes relate directly to implementing bulk dependency management as specified and no unrelated features or refactors outside the scope of extending dependency commands, parsing utilities, or bulk operation logic have been introduced.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Caution

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

⚠️ Outside diff range comments (1)
scripts/modules/commands.js (1)

72-78: Remove stale imports — DependencyError and DEPENDENCY_ERROR_CODES are not exported

scripts/modules/dependency-manager.js export block does not include those identifiers, but scripts/modules/commands.js still imports them (import block ≈ lines 72–78). This will throw at import time — remove the two named imports from commands.js or re-export them from dependency-manager.js if they’re needed.

📜 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 c2fc61d and cfcb45d.

📒 Files selected for processing (5)
  • mcp-server/src/tools/add-dependency.js (1 hunks)
  • mcp-server/src/tools/remove-dependency.js (1 hunks)
  • scripts/modules/commands.js (2 hunks)
  • scripts/modules/dependency-manager.js (14 hunks)
  • tests/unit/dependency-manager.test.js (2 hunks)
🧰 Additional context used
📓 Path-based instructions (18)
mcp-server/src/tools/*.js

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

MCP server tools in mcp-server/src/tools/*.js must have their execute methods wrapped with the withNormalizedProjectRoot higher-order function from tools/utils.js to ensure consistent path handling.

mcp-server/src/tools/*.js: MCP tools must follow a specific structure: use server.addTool with snake_case tool names, define parameters using Zod, and implement the execute function as an async function.
All MCP tool execute methods that require access to the project root MUST be wrapped with the withNormalizedProjectRoot Higher-Order Function (HOF) from mcp-server/src/tools/utils.js.
MCP tools should always call *Direct wrappers instead of executeTaskMasterCommand, except as a fallback if a direct function is not yet implemented.
Use camelCase with Tool suffix for tool registration functions in mcp-server/src/tools/.
Use snake_case for tool names exposed to MCP clients in server.addTool definitions.
Log the start of execution with arguments (sanitized if sensitive), log successful completion with result summary, log all error conditions with appropriate log levels, and include the cache status in result logs in MCP tool files.
Do not log entire large data structures or sensitive information in MCP tool files.
Use handleApiResult to format and return the response from MCP tools.

mcp-server/src/tools/*.js: Create tool definitions in 'mcp-server/src/tools/', use Zod for parameter validation, include optional tag parameter for multi-context support, and follow established naming conventions.
For long-running operations that should not block the client, use the AsyncOperationManager in MCP tools and implement progress reporting.

Files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
mcp-server/src/{tools,core/direct-functions}/*.js

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

mcp-server/src/{tools,core/direct-functions}/*.js: Use kebab-case for all file names in mcp-server/src/tools/ and mcp-server/src/core/direct-functions/.
Use helpers from mcp-server/src/tools/utils.js, mcp-server/src/core/utils/path-utils.js, and mcp-server/src/core/utils/ai-client-utils.js for centralized utilities.

Files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
mcp-server/src/tools/**/*.js

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

MCP tool files in mcp-server/src/tools/ must call the corresponding direct function wrapper and pass the result to handleApiResult(result, log) from mcp-server/src/tools/utils.js, ensuring telemetryData is included in the final MCP response.

Files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
**/*.js

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

**/*.js: Declare and initialize global variables at the top of modules to avoid hoisting issues.
Use proper function declarations to avoid hoisting issues and initialize variables before they are referenced.
Do not reference variables before their declaration in module scope.
Use dynamic imports (import()) to avoid initialization order issues in modules.

Files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
  • scripts/modules/commands.js
  • tests/unit/dependency-manager.test.js
  • scripts/modules/dependency-manager.js
mcp-server/src/{core/utils,tools}/**/*.js

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

Place utilities specifically designed to support the MCP server implementation into the appropriate subdirectories within mcp-server/src/ (e.g., path/core logic helpers in mcp-server/src/core/utils/, tool execution/response helpers in mcp-server/src/tools/utils.js).

Files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
scripts/modules/commands.js

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

scripts/modules/commands.js: Centralize all LLM calls through generateTextService or generateObjectService.
Do not import or call anything from the old ai-services.js, ai-client-factory.js, or ai-client-utils.js files.
Do not fetch AI-specific parameters (model ID, max tokens, temp) using config-manager.js getters for the AI call. Pass the role instead.
Do not implement fallback or retry logic outside ai-services-unified.js.
Do not handle API key resolution outside the service layer (it uses utils.js internally).
Determine the appropriate role (main, research, fallback) in your core logic and pass it to the service.
Pass the session object (received in the context parameter, especially from direct function wrappers) to the service call when in MCP context.
Use generateTextService and implement robust manual JSON parsing (with Zod validation after parsing) when structured output is needed, as generateObjectService has shown unreliability with some providers/schemas.
Be aware of potential reliability issues with generateObjectService across different providers and complex schemas. Prefer generateTextService + manual parsing as a more robust alternative for structured data needs.

scripts/modules/commands.js: All new user-facing commands should be added to 'scripts/modules/commands.js'.
Use consistent patterns for option naming and help text in CLI commands.
Follow the Commander.js model for subcommand structure in CLI commands.
When using callbacks (like in Commander.js commands), define them separately to allow testing the callback logic independently.
Add help text to the command definition and update 'dev_workflow.mdc' with command reference when adding a new feature.
Follow the established pattern in 'commands.js' for CLI command implementation, using Commander.js for argument parsing, including comprehensive help text and examples, and supporting tagged task context awareness.
Provide clear error messages for common failu...

Files:

  • scripts/modules/commands.js
scripts/modules/*.js

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

Each module in scripts/modules/ should be focused on a single responsibility, following the modular architecture (e.g., commands.js for CLI command handling, task-manager.js for task data and core logic, dependency-manager.js for dependency management, ui.js for CLI output formatting, ai-services-unified.js for AI service integration, config-manager.js for configuration management, utils.js for utility functions).

scripts/modules/*.js: Export all core functions, helper functions, and utility methods needed by your new function or command from their respective modules. Explicitly review the module's export block to ensure every required dependency is included.
Pass all required parameters to functions you call within your implementation and verify that direct function parameters match their core function counterparts.
Use consistent file naming conventions: 'task_${id.toString().padStart(3, '0')}.txt', use path.join for composing file paths, and use appropriate file extensions (.txt for tasks, .json for data).
Use structured error objects with code and message properties, include clear error messages, and handle both function-specific and file system errors.
Import all silent mode utilities together from 'scripts/modules/utils.js' and always use isSilentMode() to check global silent mode status. Wrap core function calls within direct functions using enableSilentMode() and disableSilentMode() in a try/finally block if the core function might produce console output.
Core functions should check outputFormat === 'text' before displaying UI elements and use internal logging that respects silent mode.
Design functions to accept dependencies as parameters (dependency injection) and avoid hard-coded dependencies that are difficult to mock.
Keep pure logic separate from I/O operations or UI rendering to allow testing the logic without mocking complex dependencies.
When implementing core logic for new features, do so in 'scripts/modules/' before CLI or MCP interfaces, and d...

Files:

  • scripts/modules/commands.js
  • scripts/modules/dependency-manager.js
scripts/modules/**

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

When using the MCP server, restart it if core logic in scripts/modules or MCP tool/direct function definitions change.

Files:

  • scripts/modules/commands.js
  • scripts/modules/dependency-manager.js
scripts/modules/*

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

scripts/modules/*: Every command that reads or writes tasks.json must be tag-aware
All command files must import getCurrentTag from utils.js
Every CLI command that operates on tasks must include the --tag CLI option
All commands must resolve the tag using the pattern: options.tag || getCurrentTag(projectRoot) || 'master'
All commands must find projectRoot with error handling before proceeding
All commands must pass { projectRoot, tag } as context to core functions
MCP direct functions must accept and use a context object containing projectRoot and tag, and pass them to core functions
Do not hard-code tag resolution (e.g., const tag = options.tag || 'master';); always use getCurrentTag
Do not omit the --tag CLI option in commands that operate on tasks
Do not omit the context parameter when calling core functions from commands
Do not call readJSON or writeJSON without passing projectRoot and tag

Files:

  • scripts/modules/commands.js
  • scripts/modules/dependency-manager.js
tests/{unit,integration,e2e,fixtures}/**/*.js

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

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/.

Files:

  • tests/unit/dependency-manager.test.js
tests/unit/*.js

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

Each module should have a corresponding unit test file in tests/unit/ that reflects the module structure (one test file per module).

Files:

  • tests/unit/dependency-manager.test.js
**/*.{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:

  • tests/unit/dependency-manager.test.js
**/*.test.js

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

**/*.test.js: Never use asynchronous operations in tests. Make all mocks return synchronous values when possible.
Always mock tests properly based on the way the tested functions are defined and used.
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.
Use fixtures from tests/fixtures/ for consistent sample data across tests.
Always declare mocks before importing the modules being tested in Jest test files.
Use jest.spyOn() after imports to create spies on mock functions and reference these spies in test assertions.
When testing functions with callbacks, get the callback from your mock's call arguments, execute it directly with test inputs, and verify the results.
For ES modules, use jest.mock() before static imports and jest.unstable_mockModule() before dynamic imports to mock dependencies.
Reset mock functions (mockFn.mockReset()) before dynamic imports if they might have been called previously.
When verifying console assertions, assert against the actual arguments passed (single formatted string), not multiple arguments.
Use mock-fs to mock file system operations in tests, and restore the file system after each test.
Mock API calls (e.g., Anthropic/Claude) by mocking the entire module and providing predictable responses.
Set mock environment variables in test setup and restore them after each test.
Maintain test fixtures separate from test logic.
Follow the mock-first-then-import pattern for all Jest mocks.
Do not define mock variables before jest.mock() calls (they won't be accessible due to hoisting).
Use test-specific file paths (e.g., 'test-tasks.json') for all file operations in tests.
Mock readJSON and writeJSON to avoid real file system interactions in tests.
Verify file operations use the correct paths in expect statements.
Use different file paths for each test to avoid test interdependence.
Verify modifications on the in-memory task objects passed to w...

Files:

  • tests/unit/dependency-manager.test.js
tests/unit/**/*.test.js

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

tests/unit/**/*.test.js: Unit tests must be located in tests/unit/, test individual functions and utilities in isolation, mock all external dependencies, and keep tests small, focused, and fast.
Do not include actual command execution in unit tests.

Files:

  • tests/unit/dependency-manager.test.js
tests/{unit,integration,e2e}/**/*.test.js

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

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.
When mocking the Commander.js chain, mock ALL chainable methods (option, argument, action, on, etc.) and return this (or the mock object) from all chainable method mocks.
Explicitly handle all options, including defaults and shorthand flags (e.g., -p for --prompt), and include null/undefined checks in test implementations for parameters that might be optional.
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.
Mock the action handlers for CLI commands and verify they're called with correct arguments.
Use sample task fixtures for consistent test data, mock file system operations, and test both success and error paths for task operations.
Mock console output and verify correct formatting in UI function tests. Use flexible assertions like toContain() or toMatch() for formatted output.
Mock chalk functions to return the input text to make testing easier while still verifying correct function calls.

Files:

  • tests/unit/dependency-manager.test.js
**/*.{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:

  • tests/unit/dependency-manager.test.js
tests/{unit,integration,e2e}/**

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

Organize test directories by test type (unit, integration, e2e) and mirror source structure where possible

Files:

  • tests/unit/dependency-manager.test.js
scripts/modules/dependency-manager.js

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

scripts/modules/dependency-manager.js: Represent task dependencies as arrays of task IDs
Use numeric IDs for direct task references
Use string IDs with dot notation (e.g., "1.2") for subtask references
Do not mix reference types without proper conversion
Allow numeric subtask IDs to reference other subtasks within the same parent
Convert between formats appropriately when needed
Do not create circular dependencies between subtasks
Validate that referenced tasks exist before adding dependencies
Provide clear error messages for non-existent dependencies
Remove references to non-existent tasks during validation
Check for circular dependencies before adding new relationships
Use graph traversal algorithms (DFS) to detect cycles
Provide clear error messages explaining the circular chain
Prevent tasks from depending on themselves
Handle both direct and indirect self-dependencies
Format task and dependency IDs consistently when adding dependencies
Check for existing dependencies to prevent duplicates
Sort dependencies for better readability
Check if the dependency exists before removing
Handle different ID formats consistently when removing dependencies
Provide feedback about the removal result
Use Set objects to identify and remove duplicate dependencies
Handle both numeric and string ID formats when removing duplicates
Check for and remove references to non-existent tasks during cleanup
Check for and remove self-references during cleanup
Track and report changes made during dependency cleanup
Use visual indicators to show dependency status (✅/⏱️)
Format dependency lists consistently for visualization
Use depth-first search (DFS) for cycle detection
Track visited nodes and recursion stack during cycle detection
Support both task and subtask dependencies in cycle detection

Features that handle task relationships belong in 'scripts/modules/dependency-manager.js'.

Files:

  • scripts/modules/dependency-manager.js
🧠 Learnings (36)
📓 Common learnings
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: public/assets/.windsurfrules:0-0
Timestamp: 2025-09-22T16:42:10.070Z
Learning: Manage dependencies with add-dependency and remove-dependency; system prevents circular/duplicates and regenerates task files
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Format task and dependency IDs consistently when adding dependencies
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Format task and dependency IDs consistently when adding dependencies
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Represent task dependencies as arrays of task IDs
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Represent task dependencies as arrays of task IDs
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dev_workflow.mdc:0-0
Timestamp: 2025-07-18T17:10:12.881Z
Learning: Use the `add_dependency` and `remove_dependency` commands to manage task dependencies, ensuring no circular or duplicate dependencies.
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/dependency-manager.js : Features that handle task relationships belong in 'scripts/modules/dependency-manager.js'.
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Use numeric IDs for direct task references
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Use numeric IDs for direct task references
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Format task and dependency IDs consistently when adding dependencies

Applied to files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
  • scripts/modules/commands.js
  • tests/unit/dependency-manager.test.js
  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Format dependency lists consistently for visualization

Applied to files:

  • mcp-server/src/tools/add-dependency.js
📚 Learning: 2025-09-22T16:42:10.070Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: public/assets/.windsurfrules:0-0
Timestamp: 2025-09-22T16:42:10.070Z
Learning: Manage dependencies with add-dependency and remove-dependency; system prevents circular/duplicates and regenerates task files

Applied to files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
  • scripts/modules/commands.js
  • scripts/modules/dependency-manager.js
📚 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 mcp-server/src/tools/*.js : Create tool definitions in 'mcp-server/src/tools/', use Zod for parameter validation, include optional tag parameter for multi-context support, and follow established naming conventions.

Applied to files:

  • mcp-server/src/tools/add-dependency.js
📚 Learning: 2025-07-18T17:11:36.732Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/mcp.mdc:0-0
Timestamp: 2025-07-18T17:11:36.732Z
Learning: Applies to mcp-server/src/tools/*.js : MCP tools must follow a specific structure: use server.addTool with snake_case tool names, define parameters using Zod, and implement the execute function as an async function.

Applied to files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Use consistent formatting for task files, include all task properties in text files, and format dependencies with status indicators.

Applied to files:

  • mcp-server/src/tools/remove-dependency.js
  • scripts/modules/commands.js
  • tests/unit/dependency-manager.test.js
  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Represent task dependencies as arrays of task IDs

Applied to files:

  • mcp-server/src/tools/remove-dependency.js
  • scripts/modules/commands.js
  • tests/unit/dependency-manager.test.js
  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Handle different ID formats consistently when removing dependencies

Applied to files:

  • mcp-server/src/tools/remove-dependency.js
  • scripts/modules/commands.js
  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Remove references to non-existent tasks during validation

Applied to files:

  • mcp-server/src/tools/remove-dependency.js
  • tests/unit/dependency-manager.test.js
  • scripts/modules/dependency-manager.js
📚 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/dependency-manager.js : Features that handle task relationships belong in 'scripts/modules/dependency-manager.js'.

Applied to files:

  • scripts/modules/commands.js
  • tests/unit/dependency-manager.test.js
  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Use numeric IDs for direct task references

Applied to files:

  • scripts/modules/commands.js
📚 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:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Do not create circular dependencies between subtasks

Applied to files:

  • tests/unit/dependency-manager.test.js
  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Support both task and subtask dependencies in cycle detection

Applied to files:

  • tests/unit/dependency-manager.test.js
  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Check for and remove references to non-existent tasks during cleanup

Applied to files:

  • tests/unit/dependency-manager.test.js
  • scripts/modules/dependency-manager.js
📚 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:

  • tests/unit/dependency-manager.test.js
📚 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:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Track and report changes made during dependency cleanup

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Validate that referenced tasks exist before adding dependencies

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Prevent tasks from depending on themselves

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Check for circular dependencies before adding new relationships

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/task-manager/* : All core functions in scripts/modules/task-manager/ must use readJSON(tasksPath, projectRoot, tag) and writeJSON(tasksPath, data, projectRoot, tag)

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-31T22:08:16.039Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/taskmaster.mdc:0-0
Timestamp: 2025-07-31T22:08:16.039Z
Learning: After making changes to tasks.json, run the generate command to keep individual task files up to date.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 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 : Regenerate task files after destructive operations, passing all required parameters to generation functions, and provide an option to skip regeneration if needed.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-09-22T16:42:54.285Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: public/assets/AGENTS.md:0-0
Timestamp: 2025-09-22T16:42:54.285Z
Learning: Applies to public/assets/.taskmaster/tasks/tasks.json : If tasks.json is manually changed, run task-master generate to regenerate task files

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Generate task files from the current tag context, include tag information in generated files, and do not mix tasks from different tags in file generation.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-09-22T16:42:10.070Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: public/assets/.windsurfrules:0-0
Timestamp: 2025-09-22T16:42:10.070Z
Learning: Use task-master generate to create individual task files from tasks.json; supports --file and --output options and overwrites existing files

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-31T22:07:49.716Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/commands.mdc:0-0
Timestamp: 2025-07-31T22:07:49.716Z
Learning: Applies to scripts/modules/commands.js : Follow the provided structure for removing subtasks, including options for conversion, file path, and regeneration, with detailed error handling.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 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:

  • scripts/modules/dependency-manager.js
📚 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:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Extract tasks from PRD documents using AI, create them in the current tag context (defaulting to 'master'), provide clear prompts to guide AI task generation, and validate/clean up AI-generated tasks.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:10:12.881Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dev_workflow.mdc:0-0
Timestamp: 2025-07-18T17:10:12.881Z
Learning: Applies to tasks.json : Use the `tasks.json` file (generated by Taskmaster) to store the project's task list, including tags and task structures.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:18:17.759Z
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 : Use tag resolution functions for all task data access, provide backward compatibility with legacy format, and default to 'master' tag when no tag is specified.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T05:38:17.352Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#943
File: scripts/modules/task-manager/move-task.js:24-24
Timestamp: 2025-07-18T05:38:17.352Z
Learning: In the Claude Task Master system, core task-manager functions are designed with fallback mechanisms for missing projectRoot parameters using the pattern `const projectRoot = providedProjectRoot || findProjectRoot();`. The readJSON and writeJSON functions have default parameters (projectRoot = null, tag = null) and handle missing parameters gracefully. Adding strict validation to these core functions would break the established flexible architecture pattern.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/* : Every command that reads or writes tasks.json must be tag-aware

Applied to files:

  • scripts/modules/dependency-manager.js
🔇 Additional comments (7)
mcp-server/src/tools/add-dependency.js (1)

23-35: Descriptions updated correctly to document bulk support

The tool name, description, and parameter docs clearly communicate ranges and comma-separated lists. No issues.

mcp-server/src/tools/remove-dependency.js (1)

23-35: Descriptions updated correctly to document bulk support

The tool name, description, and parameter docs clearly communicate ranges and comma-separated lists. No issues.

scripts/modules/dependency-manager.js (5)

642-646: Banner gating looks good

Only showing the banner when not in silent mode aligns with guidelines.


769-774: Banner gating looks good (fixDependenciesCommand)

Only showing the banner when not in silent mode aligns with guidelines.


1309-1411: parseBulkTaskIds implementation LGTM

Covers ranges, mixed formats, subtasks, and deduplication. Good validations and error messages.


1721-1727: getTaskFromPath helper LGTM

Utility is straightforward and used correctly.


2027-2030: Exports updated appropriately

New bulk APIs are exported. Looks good.

Comment on lines 2535 to 2509
.description(
'Add dependencies to task(s). Supports ranges and comma-separated lists.'
)
.option(
'-i, --id <id>',
'Task ID(s) to add dependencies to (e.g., "7", "7-10", "7,8,9")'
)
.option(
'-d, --depends-on <id>',
'Task ID(s) that will become dependencies (e.g., "1", "1-5", "1,3,5")'
)
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
.option(
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

Remove duplicate -f option and stop parsing IDs to numbers (breaks bulk ranges)

  • Duplicate definition of -f conflicts with the canonical TASKMASTER_TASKS_FILE option below.
  • Parsing IDs to numbers reduces "7-10" to 7, breaking bulk routing. Pass raw strings and let dependency-manager parse.

Apply this diff to remove the duplicate option:

-    .option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')

And update the action handler outside this hunk:

-      const formattedTaskId = taskId.includes('.') ? taskId : parseInt(taskId, 10);
-      const formattedDependencyId = dependencyId.includes('.')
-        ? dependencyId
-        : parseInt(dependencyId, 10);
-
-      await addDependency(
-        taskMaster.getTasksPath(),
-        formattedTaskId,
-        formattedDependencyId,
+      // Pass raw IDs (supporting ranges/lists and dot notation)
+      await addDependency(
+        taskMaster.getTasksPath(),
+        taskId,
+        dependencyId,
         {
           projectRoot: taskMaster.getProjectRoot(),
           tag
         }
       );
📝 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
.description(
'Add dependencies to task(s). Supports ranges and comma-separated lists.'
)
.option(
'-i, --id <id>',
'Task ID(s) to add dependencies to (e.g., "7", "7-10", "7,8,9")'
)
.option(
'-d, --depends-on <id>',
'Task ID(s) that will become dependencies (e.g., "1", "1-5", "1,3,5")'
)
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
.option(
.description(
'Add dependencies to task(s). Supports ranges and comma-separated lists.'
)
.option(
'-i, --id <id>',
'Task ID(s) to add dependencies to (e.g., "7", "7-10", "7,8,9")'
)
.option(
'-d, --depends-on <id>',
'Task ID(s) that will become dependencies (e.g., "1", "1-5", "1,3,5")'
)
.option(
🤖 Prompt for AI Agents
In scripts/modules/commands.js around lines 2535 to 2547, remove the duplicate
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json') from
this command definition (the canonical TASKMASTER_TASKS_FILE option is defined
elsewhere) and update the command's action handler so it no longer coerces or
maps id and dependsOn values to Number; instead pass the raw string values
(e.g., "7", "7-10", "7,8,9") through to the dependency-manager so range and
comma parsing remains intact.

Comment on lines 2602 to 2574
'Remove dependencies from task(s). Supports ranges and comma-separated lists.'
)
.option(
'-i, --id <id>',
'Task ID(s) to remove dependencies from (e.g., "7", "7-10", "7,8,9")'
)
.option(
'-d, --depends-on <id>',
'Task ID(s) to remove as dependencies (e.g., "1", "1-5", "1,3,5")'
)
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
.option(
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

Remove duplicate -f option and stop parsing IDs to numbers (breaks bulk ranges)

Same issues as add-dependency.

Apply this diff to remove the duplicate option:

-    .option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')

And update the action handler outside this hunk:

-      const formattedTaskId = taskId.includes('.') ? taskId : parseInt(taskId, 10);
-      const formattedDependencyId = dependencyId.includes('.')
-        ? dependencyId
-        : parseInt(dependencyId, 10);
-
-      await removeDependency(
-        taskMaster.getTasksPath(),
-        formattedTaskId,
-        formattedDependencyId,
+      // Pass raw IDs (supporting ranges/lists and dot notation)
+      await removeDependency(
+        taskMaster.getTasksPath(),
+        taskId,
+        dependencyId,
         {
           projectRoot: taskMaster.getProjectRoot(),
           tag
         }
       );
📝 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
'Remove dependencies from task(s). Supports ranges and comma-separated lists.'
)
.option(
'-i, --id <id>',
'Task ID(s) to remove dependencies from (e.g., "7", "7-10", "7,8,9")'
)
.option(
'-d, --depends-on <id>',
'Task ID(s) to remove as dependencies (e.g., "1", "1-5", "1,3,5")'
)
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
.option(
'Remove dependencies from task(s). Supports ranges and comma-separated lists.'
)
.option(
'-i, --id <id>',
'Task ID(s) to remove dependencies from (e.g., "7", "7-10", "7,8,9")'
)
.option(
'-d, --depends-on <id>',
'Task ID(s) to remove as dependencies (e.g., "1", "1-5", "1,3,5")'
)
.option(
Suggested change
'Remove dependencies from task(s). Supports ranges and comma-separated lists.'
)
.option(
'-i, --id <id>',
'Task ID(s) to remove dependencies from (e.g., "7", "7-10", "7,8,9")'
)
.option(
'-d, --depends-on <id>',
'Task ID(s) to remove as dependencies (e.g., "1", "1-5", "1,3,5")'
)
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
.option(
// Pass raw IDs (supporting ranges/lists and dot notation)
await removeDependency(
taskMaster.getTasksPath(),
taskId,
dependencyId,
{
projectRoot: taskMaster.getProjectRoot(),
tag
}
);
🤖 Prompt for AI Agents
In scripts/modules/commands.js around lines 2602 to 2613, remove the duplicate
.option('-f, --file <file>', ...) declaration and stop coercing task ID strings
into numeric types in the action handler (which breaks range and comma-separated
IDs like "7-10" or "7,8,9"). Delete the duplicate option call in this hunk, then
update the action handler referenced outside this block so it preserves ID
arguments as raw strings (no Number(...) or parseInt) and processes ranges/CSV
strings as-is; mirror the same changes you applied for add-dependency.

Comment on lines 2000 to 2004
writeJSON(tasksPath, originalData);
throw new Error(
`Bulk operation failed and was rolled back: ${error.message}`
);
}
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

Rollback should also be tag-aware (bulk remove)

Apply this diff:

-      writeJSON(tasksPath, originalData);
+      writeJSON(tasksPath, originalData, projectRoot, tag);
📝 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
writeJSON(tasksPath, originalData);
throw new Error(
`Bulk operation failed and was rolled back: ${error.message}`
);
}
writeJSON(tasksPath, originalData, projectRoot, tag);
throw new Error(
`Bulk operation failed and was rolled back: ${error.message}`
);
}
🤖 Prompt for AI Agents
In scripts/modules/dependency-manager.js around lines 2000-2004, the rollback
after a failed bulk remove currently only writes originalData back but does not
restore tag state; update the catch/rollback to be tag-aware by restoring any
tag metadata and associations that were changed during the bulk operation.
Specifically, ensure you saved a copy of the original tags/tagsIndex before the
bulk remove, and in the rollback write those original tag files/structures
(e.g., tags.json, tagIndex or in-memory tag maps) back to disk or memory
alongside originalData, update any in-memory caches to the restored values, and
keep the write operations atomic (write temp files then replace) and handle
errors from those writes. Ensure the rollback uses the saved original tag
snapshot variable (created before the bulk remove) rather than recomputing, and
add logging for success/failure of the tag restore.

@Crunchyman-ralph
Copy link
Collaborator

ai sdk v5 merged!

Lets review and merge this!

@Crunchyman-ralph Crunchyman-ralph force-pushed the feature/bulk-dependencies branch from cfcb45d to b036b1e Compare October 8, 2025 19:57
@Crunchyman-ralph
Copy link
Collaborator

nice @will-bogusz I did a quick rebase, thank you for adding updates, can you test that this still works, I will be working on fixing the reviews from coderabbit and merging this to next.

Help would be appreciated, so let me know what you'll be taking on so we don't work on the same thing

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 (5)
scripts/modules/dependency-manager.js (3)

112-119: Validate numeric subtask dependencies relative to parent (avoid false positives)

When adding a dependency to a subtask, a numeric dep like “1” should refer to a sibling subtask (parentId.1). The current existence check runs before subtask context is known and may pass by matching a top-level task.

  • Move the existence check after targetTask/isSubtask are determined.
  • For subtask context and numeric dep < 100, validate existence of ${parentId}.${depNum}.

Suggested changes:

-  // Check if the dependency task or subtask actually exists
-  if (!taskExists(data.tasks, formattedDependencyId)) {
-    log(
-      'error',
-      `Dependency target ${formattedDependencyId} does not exist in tasks.json`
-    );
-    process.exit(1);
-  }
@@
   } else {
     // Regular task (not a subtask)
     targetTask = data.tasks.find((t) => t.id === formattedTaskId);
@@
   }
+
+  // Now that we know if we're in a subtask context, validate dependency existence correctly
+  let existsCheckId = formattedDependencyId;
+  if (
+    isSubtask &&
+    typeof formattedDependencyId === 'number' &&
+    formattedDependencyId < 100
+  ) {
+    const [parentIdStr] = String(formattedTaskId).split('.');
+    existsCheckId = `${parentIdStr}.${formattedDependencyId}`;
+  }
+  if (!taskExists(data.tasks, existsCheckId)) {
+    log('error', `Dependency target ${existsCheckId} does not exist in tasks.json`);
+    process.exit(1);
+  }

[Based on learnings]

Also applies to: 150-158


448-450: Consider regenerating task files after removal (or clarify behavior)

For parity with add and to keep generated artifacts in sync, regenerate with tag context.

-  // Regenerate task files
-  // await generateTaskFiles(tasksPath, path.dirname(tasksPath));
+  // Regenerate task files
+  await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
+    projectRoot: context.projectRoot,
+    tag: context.tag
+  });

262-266: Uncomment and invoke generateTaskFiles with projectRoot and tag

Replace the commented-out stub and log with:

-    // Generate updated task files
-    // await generateTaskFiles(tasksPath, path.dirname(tasksPath));
-    log('info', 'Task files regenerated with updated dependencies.');
+    // Generate updated task files
+    await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
+      projectRoot: context.projectRoot,
+      tag: context.tag
+    });
+    log('info', 'Task files regenerated with updated dependencies.');
tests/unit/dependency-manager.test.js (2)

60-71: Critical import issues remain from previous review.

Past review identified these issues, which are still present:

  1. Missing comma after bulkRemoveDependencies (line 70) before closing brace
  2. Missing imports for addDependency and removeDependency which are referenced in placeholder tests at lines 878, 884, 891, and 897
  3. Unverified export of canMoveWithDependencies — past review noted this function isn't exported from dependency-manager.js

Apply this diff to fix the import statement:

 import {
 	validateTaskDependencies,
 	isCircularDependency,
 	removeDuplicateDependencies,
 	cleanupSubtaskDependencies,
 	ensureAtLeastOneIndependentSubtask,
 	validateAndFixDependencies,
 	canMoveWithDependencies,
 	parseBulkTaskIds,
 	bulkAddDependencies,
-	bulkRemoveDependencies
+	bulkRemoveDependencies,
+	addDependency,
+	removeDependency
 } from '../../scripts/modules/dependency-manager.js';

If canMoveWithDependencies is not exported from dependency-manager.js, either:

  • Export it from the module, or
  • Remove the import and related tests (lines 901-1008), or
  • Import it from the correct module where it's defined

901-1008: Export canMoveWithDependencies from dependency-manager.js
canMoveWithDependencies is defined at line 1823 in scripts/modules/dependency-manager.js but not exported, causing import failures in tests/unit/dependency-manager.test.js. Add an export (e.g., prefix the function with export or add export { canMoveWithDependencies };).

♻️ Duplicate comments (4)
scripts/modules/commands.js (2)

2498-2557: Stop coercing IDs; pass raw strings to preserve ranges and CSV for bulk

Parsing to numbers reduces "7-10" to 7 and breaks bulk routing in dependency-manager.

Apply:

-      const formattedTaskId = taskId.includes('.') 
-        ? taskId 
-        : parseInt(taskId, 10);
-      const formattedDependencyId = dependencyId.includes('.')
-        ? dependencyId
-        : parseInt(dependencyId, 10);
-
-      await addDependency(
+      // Pass raw IDs (support ranges, CSV, and dot notation)
+      await addDependency(
         taskMaster.getTasksPath(),
-        formattedTaskId,
-        formattedDependencyId,
+        taskId,
+        dependencyId,
         {
           projectRoot: taskMaster.getProjectRoot(),
           tag
         }
       );

2563-2623: Same fix for remove-dependency: pass raw IDs (don’t parse to numbers)

Ensure ranges/CSV are preserved.

-      const formattedTaskId = taskId.includes('.') 
-        ? taskId 
-        : parseInt(taskId, 10);
-      const formattedDependencyId = dependencyId.includes('.')
-        ? dependencyId
-        : parseInt(dependencyId, 10);
-
-      await removeDependency(
+      // Pass raw IDs (support ranges, CSV, and dot notation)
+      await removeDependency(
         taskMaster.getTasksPath(),
-        formattedTaskId,
-        formattedDependencyId,
+        taskId,
+        dependencyId,
         {
           projectRoot: taskMaster.getProjectRoot(),
           tag
         }
       );
scripts/modules/dependency-manager.js (2)

2169-2177: Make bulk add atomic (abort on any validation errors, unless dry-run)

Current threshold-based continue allows partial application. Per PR objective, bulk ops must be all-or-nothing.

Apply:

-    // If there are validation errors and we're not in dry-run mode, decide whether to continue
-    if (validationErrors.length > 0 && !dryRun) {
-      const errorRatio = validationErrors.length / totalPossibleOperations;
-      if (errorRatio > 0.5) {
-        throw new Error(
-          `Too many validation errors (${validationErrors.length}/${totalPossibleOperations}). Aborting bulk operation.`
-        );
-      }
-    }
+    // Abort on any validation errors (unless dry-run) to ensure atomicity
+    if (validationErrors.length > 0 && !dryRun) {
+      throw new Error(
+        `Validation failed (${validationErrors.length}/${totalPossibleOperations}). Aborting without changes.`
+      );
+    }

2477-2515: Bulk remove should be atomic: abort on any validation errors (unless dry-run)

Currently removals proceed even with validation errors. Enforce all-or-nothing.

Insert before dry-run return:

-    // Dry-run mode: just report what would be done
+    // Abort on validation errors (unless dry-run) to preserve all-or-nothing semantics
+    if (validationErrors.length > 0 && !dryRun) {
+      throw new Error(
+        `Validation failed (${validationErrors.length}/${totalPossibleOperations}). Aborting without changes.`
+      );
+    }
+
+    // Dry-run mode: just report what would be done
📜 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 cfcb45d and b036b1e.

📒 Files selected for processing (5)
  • mcp-server/src/tools/add-dependency.js (1 hunks)
  • mcp-server/src/tools/remove-dependency.js (1 hunks)
  • scripts/modules/commands.js (2 hunks)
  • scripts/modules/dependency-manager.js (4 hunks)
  • tests/unit/dependency-manager.test.js (2 hunks)
🧰 Additional context used
📓 Path-based instructions (19)
mcp-server/src/tools/*.js

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

MCP server tools in mcp-server/src/tools/*.js must have their execute methods wrapped with the withNormalizedProjectRoot higher-order function from tools/utils.js to ensure consistent path handling.

mcp-server/src/tools/*.js: MCP tools must follow a specific structure: use server.addTool with snake_case tool names, define parameters using Zod, and implement the execute function as an async function.
All MCP tool execute methods that require access to the project root MUST be wrapped with the withNormalizedProjectRoot Higher-Order Function (HOF) from mcp-server/src/tools/utils.js.
MCP tools should always call *Direct wrappers instead of executeTaskMasterCommand, except as a fallback if a direct function is not yet implemented.
Use camelCase with Tool suffix for tool registration functions in mcp-server/src/tools/.
Use snake_case for tool names exposed to MCP clients in server.addTool definitions.
Log the start of execution with arguments (sanitized if sensitive), log successful completion with result summary, log all error conditions with appropriate log levels, and include the cache status in result logs in MCP tool files.
Do not log entire large data structures or sensitive information in MCP tool files.
Use handleApiResult to format and return the response from MCP tools.

mcp-server/src/tools/*.js: Create tool definitions in 'mcp-server/src/tools/', use Zod for parameter validation, include optional tag parameter for multi-context support, and follow established naming conventions.
For long-running operations that should not block the client, use the AsyncOperationManager in MCP tools and implement progress reporting.

Files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
mcp-server/src/{tools,core/direct-functions}/*.js

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

mcp-server/src/{tools,core/direct-functions}/*.js: Use kebab-case for all file names in mcp-server/src/tools/ and mcp-server/src/core/direct-functions/.
Use helpers from mcp-server/src/tools/utils.js, mcp-server/src/core/utils/path-utils.js, and mcp-server/src/core/utils/ai-client-utils.js for centralized utilities.

Files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
mcp-server/src/tools/**/*.js

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

MCP tool files in mcp-server/src/tools/ must call the corresponding direct function wrapper and pass the result to handleApiResult(result, log) from mcp-server/src/tools/utils.js, ensuring telemetryData is included in the final MCP response.

Files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
**/*.js

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

**/*.js: Declare and initialize global variables at the top of modules to avoid hoisting issues.
Use proper function declarations to avoid hoisting issues and initialize variables before they are referenced.
Do not reference variables before their declaration in module scope.
Use dynamic imports (import()) to avoid initialization order issues in modules.

Files:

  • mcp-server/src/tools/add-dependency.js
  • scripts/modules/dependency-manager.js
  • tests/unit/dependency-manager.test.js
  • mcp-server/src/tools/remove-dependency.js
  • scripts/modules/commands.js
mcp-server/src/{core/utils,tools}/**/*.js

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

Place utilities specifically designed to support the MCP server implementation into the appropriate subdirectories within mcp-server/src/ (e.g., path/core logic helpers in mcp-server/src/core/utils/, tool execution/response helpers in mcp-server/src/tools/utils.js).

Files:

  • mcp-server/src/tools/add-dependency.js
  • mcp-server/src/tools/remove-dependency.js
scripts/modules/*.js

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

Each module in scripts/modules/ should be focused on a single responsibility, following the modular architecture (e.g., commands.js for CLI command handling, task-manager.js for task data and core logic, dependency-manager.js for dependency management, ui.js for CLI output formatting, ai-services-unified.js for AI service integration, config-manager.js for configuration management, utils.js for utility functions).

scripts/modules/*.js: Export all core functions, helper functions, and utility methods needed by your new function or command from their respective modules. Explicitly review the module's export block to ensure every required dependency is included.
Pass all required parameters to functions you call within your implementation and verify that direct function parameters match their core function counterparts.
Use consistent file naming conventions: 'task_${id.toString().padStart(3, '0')}.txt', use path.join for composing file paths, and use appropriate file extensions (.txt for tasks, .json for data).
Use structured error objects with code and message properties, include clear error messages, and handle both function-specific and file system errors.
Import all silent mode utilities together from 'scripts/modules/utils.js' and always use isSilentMode() to check global silent mode status. Wrap core function calls within direct functions using enableSilentMode() and disableSilentMode() in a try/finally block if the core function might produce console output.
Core functions should check outputFormat === 'text' before displaying UI elements and use internal logging that respects silent mode.
Design functions to accept dependencies as parameters (dependency injection) and avoid hard-coded dependencies that are difficult to mock.
Keep pure logic separate from I/O operations or UI rendering to allow testing the logic without mocking complex dependencies.
When implementing core logic for new features, do so in 'scripts/modules/' before CLI or MCP interfaces, and d...

Files:

  • scripts/modules/dependency-manager.js
  • scripts/modules/commands.js
scripts/modules/dependency-manager.js

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

scripts/modules/dependency-manager.js: Represent task dependencies as arrays of task IDs
Use numeric IDs for direct task references
Use string IDs with dot notation (e.g., "1.2") for subtask references
Do not mix reference types without proper conversion
Allow numeric subtask IDs to reference other subtasks within the same parent
Convert between formats appropriately when needed
Do not create circular dependencies between subtasks
Validate that referenced tasks exist before adding dependencies
Provide clear error messages for non-existent dependencies
Remove references to non-existent tasks during validation
Check for circular dependencies before adding new relationships
Use graph traversal algorithms (DFS) to detect cycles
Provide clear error messages explaining the circular chain
Prevent tasks from depending on themselves
Handle both direct and indirect self-dependencies
Format task and dependency IDs consistently when adding dependencies
Check for existing dependencies to prevent duplicates
Sort dependencies for better readability
Check if the dependency exists before removing
Handle different ID formats consistently when removing dependencies
Provide feedback about the removal result
Use Set objects to identify and remove duplicate dependencies
Handle both numeric and string ID formats when removing duplicates
Check for and remove references to non-existent tasks during cleanup
Check for and remove self-references during cleanup
Track and report changes made during dependency cleanup
Use visual indicators to show dependency status (✅/⏱️)
Format dependency lists consistently for visualization
Use depth-first search (DFS) for cycle detection
Track visited nodes and recursion stack during cycle detection
Support both task and subtask dependencies in cycle detection

Features that handle task relationships belong in 'scripts/modules/dependency-manager.js'.

Files:

  • scripts/modules/dependency-manager.js
scripts/modules/**

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

When using the MCP server, restart it if core logic in scripts/modules or MCP tool/direct function definitions change.

Files:

  • scripts/modules/dependency-manager.js
  • scripts/modules/commands.js
scripts/modules/*

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

scripts/modules/*: Every command that reads or writes tasks.json must be tag-aware
All command files must import getCurrentTag from utils.js
Every CLI command that operates on tasks must include the --tag CLI option
All commands must resolve the tag using the pattern: options.tag || getCurrentTag(projectRoot) || 'master'
All commands must find projectRoot with error handling before proceeding
All commands must pass { projectRoot, tag } as context to core functions
MCP direct functions must accept and use a context object containing projectRoot and tag, and pass them to core functions
Do not hard-code tag resolution (e.g., const tag = options.tag || 'master';); always use getCurrentTag
Do not omit the --tag CLI option in commands that operate on tasks
Do not omit the context parameter when calling core functions from commands
Do not call readJSON or writeJSON without passing projectRoot and tag

Files:

  • scripts/modules/dependency-manager.js
  • scripts/modules/commands.js
tests/{unit,integration,e2e,fixtures}/**/*.js

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

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/.

Files:

  • tests/unit/dependency-manager.test.js
tests/unit/*.js

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

Each module should have a corresponding unit test file in tests/unit/ that reflects the module structure (one test file per module).

Files:

  • tests/unit/dependency-manager.test.js
**/*.{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:

  • tests/unit/dependency-manager.test.js
**/*.test.js

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

**/*.test.js: Never use asynchronous operations in tests. Make all mocks return synchronous values when possible.
Always mock tests properly based on the way the tested functions are defined and used.
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.
Use fixtures from tests/fixtures/ for consistent sample data across tests.
Always declare mocks before importing the modules being tested in Jest test files.
Use jest.spyOn() after imports to create spies on mock functions and reference these spies in test assertions.
When testing functions with callbacks, get the callback from your mock's call arguments, execute it directly with test inputs, and verify the results.
For ES modules, use jest.mock() before static imports and jest.unstable_mockModule() before dynamic imports to mock dependencies.
Reset mock functions (mockFn.mockReset()) before dynamic imports if they might have been called previously.
When verifying console assertions, assert against the actual arguments passed (single formatted string), not multiple arguments.
Use mock-fs to mock file system operations in tests, and restore the file system after each test.
Mock API calls (e.g., Anthropic/Claude) by mocking the entire module and providing predictable responses.
Set mock environment variables in test setup and restore them after each test.
Maintain test fixtures separate from test logic.
Follow the mock-first-then-import pattern for all Jest mocks.
Do not define mock variables before jest.mock() calls (they won't be accessible due to hoisting).
Use test-specific file paths (e.g., 'test-tasks.json') for all file operations in tests.
Mock readJSON and writeJSON to avoid real file system interactions in tests.
Verify file operations use the correct paths in expect statements.
Use different file paths for each test to avoid test interdependence.
Verify modifications on the in-memory task objects passed to w...

Files:

  • tests/unit/dependency-manager.test.js
tests/unit/**/*.test.js

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

tests/unit/**/*.test.js: Unit tests must be located in tests/unit/, test individual functions and utilities in isolation, mock all external dependencies, and keep tests small, focused, and fast.
Do not include actual command execution in unit tests.

Files:

  • tests/unit/dependency-manager.test.js
tests/{unit,integration,e2e}/**/*.test.js

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

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.
When mocking the Commander.js chain, mock ALL chainable methods (option, argument, action, on, etc.) and return this (or the mock object) from all chainable method mocks.
Explicitly handle all options, including defaults and shorthand flags (e.g., -p for --prompt), and include null/undefined checks in test implementations for parameters that might be optional.
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.
Mock the action handlers for CLI commands and verify they're called with correct arguments.
Use sample task fixtures for consistent test data, mock file system operations, and test both success and error paths for task operations.
Mock console output and verify correct formatting in UI function tests. Use flexible assertions like toContain() or toMatch() for formatted output.
Mock chalk functions to return the input text to make testing easier while still verifying correct function calls.

Files:

  • tests/unit/dependency-manager.test.js
**/*.{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:

  • tests/unit/dependency-manager.test.js
tests/{unit,integration,e2e}/**

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

Organize test directories by test type (unit, integration, e2e) and mirror source structure where possible

Files:

  • tests/unit/dependency-manager.test.js
**/*.{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:

  • tests/unit/dependency-manager.test.js
scripts/modules/commands.js

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

scripts/modules/commands.js: Centralize all LLM calls through generateTextService or generateObjectService.
Do not import or call anything from the old ai-services.js, ai-client-factory.js, or ai-client-utils.js files.
Do not fetch AI-specific parameters (model ID, max tokens, temp) using config-manager.js getters for the AI call. Pass the role instead.
Do not implement fallback or retry logic outside ai-services-unified.js.
Do not handle API key resolution outside the service layer (it uses utils.js internally).
Determine the appropriate role (main, research, fallback) in your core logic and pass it to the service.
Pass the session object (received in the context parameter, especially from direct function wrappers) to the service call when in MCP context.
Use generateTextService and implement robust manual JSON parsing (with Zod validation after parsing) when structured output is needed, as generateObjectService has shown unreliability with some providers/schemas.
Be aware of potential reliability issues with generateObjectService across different providers and complex schemas. Prefer generateTextService + manual parsing as a more robust alternative for structured data needs.

scripts/modules/commands.js: All new user-facing commands should be added to 'scripts/modules/commands.js'.
Use consistent patterns for option naming and help text in CLI commands.
Follow the Commander.js model for subcommand structure in CLI commands.
When using callbacks (like in Commander.js commands), define them separately to allow testing the callback logic independently.
Add help text to the command definition and update 'dev_workflow.mdc' with command reference when adding a new feature.
Follow the established pattern in 'commands.js' for CLI command implementation, using Commander.js for argument parsing, including comprehensive help text and examples, and supporting tagged task context awareness.
Provide clear error messages for common failu...

Files:

  • scripts/modules/commands.js
🧠 Learnings (36)
📓 Common learnings
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: assets/.windsurfrules:0-0
Timestamp: 2025-09-24T15:12:12.658Z
Learning: Manage dependencies with task-master add-dependency and remove-dependency; validate and fix using validate-dependencies and fix-dependencies
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Format task and dependency IDs consistently when adding dependencies

Applied to files:

  • scripts/modules/dependency-manager.js
  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/task-manager/* : All core functions in scripts/modules/task-manager/ must accept a context parameter and use it to extract projectRoot and tag

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Tasks must be organized into separate contexts (tags) within tasks.json, using the tagged format: {"master": {"tasks": [...]}, "feature-branch": {"tasks": [...]}}. Legacy format {"tasks": [...]} must be silently migrated to the tagged format on first use.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/task-manager/* : All core functions in scripts/modules/task-manager/ must use readJSON(tasksPath, projectRoot, tag) and writeJSON(tasksPath, data, projectRoot, tag)

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/* : Every command that reads or writes tasks.json must be tag-aware

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/* : Do not call readJSON or writeJSON without passing projectRoot and tag

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:18:17.759Z
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 : Use tag resolution functions for all task data access, provide backward compatibility with legacy format, and default to 'master' tag when no tag is specified.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T05:38:17.352Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#943
File: scripts/modules/task-manager/move-task.js:24-24
Timestamp: 2025-07-18T05:38:17.352Z
Learning: In the Claude Task Master system, core task-manager functions are designed with fallback mechanisms for missing projectRoot parameters using the pattern `const projectRoot = providedProjectRoot || findProjectRoot();`. The readJSON and writeJSON functions have default parameters (projectRoot = null, tag = null) and handle missing parameters gracefully. Adding strict validation to these core functions would break the established flexible architecture pattern.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/* : All commands must pass { projectRoot, tag } as context to core functions

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Implement silent migration from legacy to tagged format in the readJSON() function, detect legacy format, convert automatically, and preserve all existing task data during migration.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Generate task files from the current tag context, include tag information in generated files, and do not mix tasks from different tags in file generation.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Tasks must be accessed and updated through the tag resolution layer using getTasksForTag(data, tagName) and setTasksForTag(data, tagName, tasks); direct manipulation of the tagged structure in core functions is not allowed.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/generate.js : The generate command must use tag-aware file naming: task_XXX.txt for master, task_XXX_<tag>.txt for other tags

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Use AI to generate detailed subtasks within the current tag context, considering complexity analysis for subtask counts and ensuring proper IDs for newly created subtasks.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Use tag resolution functions to maintain backward compatibility, returning legacy format to core functions and not exposing the tagged structure to existing core logic.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-31T22:07:49.716Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/commands.mdc:0-0
Timestamp: 2025-07-31T22:07:49.716Z
Learning: Applies to scripts/modules/commands.js : Regenerate task files after destructive operations, passing all required parameters to generation functions, and provide an option to skip regeneration if needed.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Remove references to non-existent tasks during validation

Applied to files:

  • scripts/modules/dependency-manager.js
  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Provide feedback about the removal result

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Represent task dependencies as arrays of task IDs

Applied to files:

  • scripts/modules/dependency-manager.js
  • tests/unit/dependency-manager.test.js
  • scripts/modules/commands.js
📚 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/dependency-manager.js : Features that handle task relationships belong in 'scripts/modules/dependency-manager.js'.

Applied to files:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Support both task and subtask dependencies in cycle detection

Applied to files:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Do not create circular dependencies between subtasks

Applied to files:

  • tests/unit/dependency-manager.test.js
📚 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:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Validate that referenced tasks exist before adding dependencies

Applied to files:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Check for and remove references to non-existent tasks during cleanup

Applied to files:

  • tests/unit/dependency-manager.test.js
📚 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:

  • tests/unit/dependency-manager.test.js
📚 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:

  • tests/unit/dependency-manager.test.js
📚 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:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Use consistent formatting for task files, include all task properties in text files, and format dependencies with status indicators.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-09-24T15:12:12.658Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: assets/.windsurfrules:0-0
Timestamp: 2025-09-24T15:12:12.658Z
Learning: Manage dependencies with task-master add-dependency and remove-dependency; validate and fix using validate-dependencies and fix-dependencies

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Handle different ID formats consistently when removing dependencies

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Use numeric IDs for direct task references

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Allow numeric subtask IDs to reference other subtasks within the same parent

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Use string IDs with dot notation (e.g., "1.2") for subtask references

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Handle both numeric and string ID formats when removing duplicates

Applied to files:

  • scripts/modules/commands.js
🧬 Code graph analysis (1)
scripts/modules/dependency-manager.js (5)
mcp-server/src/core/direct-functions/add-dependency.js (3)
  • tasksPath (64-64)
  • taskId (67-68)
  • dependencyId (69-72)
scripts/modules/utils.js (9)
  • taskId (900-900)
  • id (940-940)
  • id (1019-1019)
  • options (1147-1152)
  • options (1430-1430)
  • parentId (928-930)
  • parentId (982-984)
  • parentTask (931-931)
  • parentTask (985-985)
mcp-server/src/core/direct-functions/remove-dependency.js (2)
  • taskId (65-66)
  • dependencyId (67-70)
scripts/modules/task-manager/task-exists.js (6)
  • parts (10-10)
  • id (14-14)
  • id (34-34)
  • parentId (19-19)
  • subtaskId (20-20)
  • parentTask (23-23)
scripts/modules/task-manager/generate-task-files.js (1)
  • options (23-23)
🔇 Additional comments (1)
tests/unit/dependency-manager.test.js (1)

844-872: LGTM! Comprehensive parsing tests.

The parseBulkTaskIds test suite provides good coverage of the parsing functionality with tests for single IDs, lists, ranges, mixed formats, deduplication, and error cases.

Comment on lines 874 to 886
describe('enhanced addDependency', () => {
test('should handle single IDs (backward compatibility)', () => {
// This test verifies the enhanced function still works with single IDs
// The actual implementation details are tested in the original addDependency tests
expect(typeof addDependency).toBe('function');
});

test('should detect and handle multiple IDs', () => {
// This test verifies the function can detect multiple ID formats
// The bulk processing logic is tested in the bulkAddDependencies tests
expect(typeof addDependency).toBe('function');
});
});
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

Placeholder tests provide no coverage for bulk routing logic.

These tests only verify function existence (typeof checks) without testing actual behavior. According to the PR objectives, addDependency was extended to detect and route multiple IDs to bulk processing while preserving single-ID backward compatibility.

Missing coverage:

  • Single ID handling (backward compatibility verification)
  • Multiple ID detection (comma-separated, ranges)
  • Routing to bulk processing when multiple IDs detected
  • Error handling for invalid formats

Replace the placeholder tests with actual behavioral tests. Example:

 		describe('enhanced addDependency', () => {
-			test('should handle single IDs (backward compatibility)', () => {
-				// This test verifies the enhanced function still works with single IDs
-				// The actual implementation details are tested in the original addDependency tests
-				expect(typeof addDependency).toBe('function');
+			test('should handle single IDs (backward compatibility)', async () => {
+				mockReadJSON.mockReturnValue({ tasks: [
+					{ id: 1, dependencies: [] },
+					{ id: 2, dependencies: [] }
+				]});
+
+				await addDependency(TEST_TASKS_PATH, 1, 2, { projectRoot: '/test' });
+
+				expect(mockWriteJSON).toHaveBeenCalledWith(
+					TEST_TASKS_PATH,
+					expect.objectContaining({
+						tasks: expect.arrayContaining([
+							expect.objectContaining({ id: 1, dependencies: [2] })
+						])
+					}),
+					'/test',
+					undefined
+				);
 			});
 
-			test('should detect and handle multiple IDs', () => {
-				// This test verifies the function can detect multiple ID formats
-				// The bulk processing logic is tested in the bulkAddDependencies tests
-				expect(typeof addDependency).toBe('function');
+			test('should detect and route comma-separated IDs to bulk processing', async () => {
+				mockReadJSON.mockReturnValue({ tasks: [
+					{ id: 1, dependencies: [] },
+					{ id: 2, dependencies: [] },
+					{ id: 3, dependencies: [] },
+					{ id: 4, dependencies: [] }
+				]});
+
+				await addDependency(TEST_TASKS_PATH, '1,2', '3,4', { projectRoot: '/test' });
+
+				const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
+				const savedData = writeCall[1];
+				expect(savedData.tasks.find(t => t.id === 1).dependencies).toContain(3);
+				expect(savedData.tasks.find(t => t.id === 1).dependencies).toContain(4);
+				expect(savedData.tasks.find(t => t.id === 2).dependencies).toContain(3);
+				expect(savedData.tasks.find(t => t.id === 2).dependencies).toContain(4);
 			});
+
+			test('should detect and route range IDs to bulk processing', async () => {
+				mockReadJSON.mockReturnValue({ tasks: [
+					{ id: 1, dependencies: [] },
+					{ id: 2, dependencies: [] },
+					{ id: 3, dependencies: [] }
+				]});
+
+				await addDependency(TEST_TASKS_PATH, '1-2', 3, { projectRoot: '/test' });
+
+				const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
+				const savedData = writeCall[1];
+				expect(savedData.tasks.find(t => t.id === 1).dependencies).toContain(3);
+				expect(savedData.tasks.find(t => t.id === 2).dependencies).toContain(3);
+			});
 		});
📝 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('enhanced addDependency', () => {
test('should handle single IDs (backward compatibility)', () => {
// This test verifies the enhanced function still works with single IDs
// The actual implementation details are tested in the original addDependency tests
expect(typeof addDependency).toBe('function');
});
test('should detect and handle multiple IDs', () => {
// This test verifies the function can detect multiple ID formats
// The bulk processing logic is tested in the bulkAddDependencies tests
expect(typeof addDependency).toBe('function');
});
});
describe('enhanced addDependency', () => {
test('should handle single IDs (backward compatibility)', async () => {
mockReadJSON.mockReturnValue({
tasks: [
{ id: 1, dependencies: [] },
{ id: 2, dependencies: [] }
]
});
await addDependency(TEST_TASKS_PATH, 1, 2, { projectRoot: '/test' });
expect(mockWriteJSON).toHaveBeenCalledWith(
TEST_TASKS_PATH,
expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({ id: 1, dependencies: [2] })
])
}),
'/test',
undefined
);
});
test('should detect and route comma-separated IDs to bulk processing', async () => {
mockReadJSON.mockReturnValue({
tasks: [
{ id: 1, dependencies: [] },
{ id: 2, dependencies: [] },
{ id: 3, dependencies: [] },
{ id: 4, dependencies: [] }
]
});
await addDependency(TEST_TASKS_PATH, '1,2', '3,4', { projectRoot: '/test' });
const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
const savedData = writeCall[1];
expect(savedData.tasks.find(t => t.id === 1).dependencies).toContain(3);
expect(savedData.tasks.find(t => t.id === 1).dependencies).toContain(4);
expect(savedData.tasks.find(t => t.id === 2).dependencies).toContain(3);
expect(savedData.tasks.find(t => t.id === 2).dependencies).toContain(4);
});
test('should detect and route range IDs to bulk processing', async () => {
mockReadJSON.mockReturnValue({
tasks: [
{ id: 1, dependencies: [] },
{ id: 2, dependencies: [] },
{ id: 3, dependencies: [] }
]
});
await addDependency(TEST_TASKS_PATH, '1-2', 3, { projectRoot: '/test' });
const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
const savedData = writeCall[1];
expect(savedData.tasks.find(t => t.id === 1).dependencies).toContain(3);
expect(savedData.tasks.find(t => t.id === 2).dependencies).toContain(3);
});
});
🤖 Prompt for AI Agents
In tests/unit/dependency-manager.test.js around lines 874 to 886, the two
placeholder tests only check typeof addDependency and do not exercise the new
behavior; replace them with behavioral tests that (1) call addDependency with a
single valid ID and assert it invokes the original single-ID flow (e.g., returns
expected single-item result or calls single-item helper), (2) call addDependency
with multiple IDs in comma-separated form and assert it detects multiple IDs and
routes to bulkAddDependencies (use a spy/mock on bulkAddDependencies to verify
it was called with the parsed IDs), (3) call addDependency with a range format
and assert it parses the range into individual IDs and routes to
bulkAddDependencies, and (4) call addDependency with invalid formats and assert
it throws or returns the expected error; set up and tear down spies/mocks as
needed and assert call counts and arguments rather than just typeof.

Comment on lines 888 to 898
describe('enhanced removeDependency', () => {
test('should handle single IDs (backward compatibility)', () => {
// This test verifies the enhanced function still works with single IDs
expect(typeof removeDependency).toBe('function');
});

test('should detect and handle multiple IDs', () => {
// This test verifies the function can detect multiple ID formats
expect(typeof removeDependency).toBe('function');
});
});
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

Placeholder tests provide no coverage for bulk routing logic.

Same issue as addDependency tests: only existence checks, no behavioral verification.

Replace with actual behavioral tests similar to the enhanced addDependency example above, but testing removal instead of addition:

 		describe('enhanced removeDependency', () => {
-			test('should handle single IDs (backward compatibility)', () => {
-				// This test verifies the enhanced function still works with single IDs
-				expect(typeof removeDependency).toBe('function');
+			test('should handle single IDs (backward compatibility)', async () => {
+				mockReadJSON.mockReturnValue({ tasks: [
+					{ id: 1, dependencies: [2] },
+					{ id: 2, dependencies: [] }
+				]});
+
+				await removeDependency(TEST_TASKS_PATH, 1, 2, { projectRoot: '/test' });
+
+				const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
+				const savedData = writeCall[1];
+				expect(savedData.tasks.find(t => t.id === 1).dependencies).not.toContain(2);
 			});
 
-			test('should detect and handle multiple IDs', () => {
-				// This test verifies the function can detect multiple ID formats
-				expect(typeof removeDependency).toBe('function');
+			test('should detect and route multiple IDs to bulk processing', async () => {
+				mockReadJSON.mockReturnValue({ tasks: [
+					{ id: 1, dependencies: [3, 4] },
+					{ id: 2, dependencies: [3, 4] },
+					{ id: 3, dependencies: [] },
+					{ id: 4, dependencies: [] }
+				]});
+
+				await removeDependency(TEST_TASKS_PATH, '1,2', '3,4', { projectRoot: '/test' });
+
+				const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
+				const savedData = writeCall[1];
+				expect(savedData.tasks.find(t => t.id === 1).dependencies).toEqual([]);
+				expect(savedData.tasks.find(t => t.id === 2).dependencies).toEqual([]);
 			});
 		});
📝 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('enhanced removeDependency', () => {
test('should handle single IDs (backward compatibility)', () => {
// This test verifies the enhanced function still works with single IDs
expect(typeof removeDependency).toBe('function');
});
test('should detect and handle multiple IDs', () => {
// This test verifies the function can detect multiple ID formats
expect(typeof removeDependency).toBe('function');
});
});
describe('enhanced removeDependency', () => {
test('should handle single IDs (backward compatibility)', async () => {
mockReadJSON.mockReturnValue({ tasks: [
{ id: 1, dependencies: [2] },
{ id: 2, dependencies: [] }
]});
await removeDependency(TEST_TASKS_PATH, 1, 2, { projectRoot: '/test' });
const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
const savedData = writeCall[1];
expect(savedData.tasks.find(t => t.id === 1).dependencies).not.toContain(2);
});
test('should detect and route multiple IDs to bulk processing', async () => {
mockReadJSON.mockReturnValue({ tasks: [
{ id: 1, dependencies: [3, 4] },
{ id: 2, dependencies: [3, 4] },
{ id: 3, dependencies: [] },
{ id: 4, dependencies: [] }
]});
await removeDependency(TEST_TASKS_PATH, '1,2', '3,4', { projectRoot: '/test' });
const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
const savedData = writeCall[1];
expect(savedData.tasks.find(t => t.id === 1).dependencies).toEqual([]);
expect(savedData.tasks.find(t => t.id === 2).dependencies).toEqual([]);
});
});
🤖 Prompt for AI Agents
In tests/unit/dependency-manager.test.js around lines 888 to 898, the two
placeholder tests only assert the existence of removeDependency; replace them
with behavioral tests that mirror the enhanced addDependency tests but for
removal: 1) test single-ID backward compatibility by calling removeDependency
with a single ID and asserting it invokes the underlying single-delete path
(mock the underlying manager/service and expect the correct call/returned
value), 2) test bulk handling by calling removeDependency with multiple ID
formats (array and comma-separated string) and asserting it routes to the
bulk/removeMany path with the expected normalized ID list and returns the
expected result; use existing Jest mocks/spies used in addDependency tests to
verify calls, include assertions for error propagation and edge cases (empty
input) consistent with the enhanced addDependency behavior.

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

Caution

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

⚠️ Outside diff range comments (8)
scripts/modules/dependency-manager.js (1)

262-266: Fix misleading regeneration message

You log that task files were regenerated, but the call is commented out. Either remove the log or actually regenerate with tag context.

-    // await generateTaskFiles(tasksPath, path.dirname(tasksPath));
-
-    log('info', 'Task files regenerated with updated dependencies.');
+    await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
+      projectRoot: context.projectRoot,
+      tag: context.tag
+    });
+    log('info', 'Task files regenerated with updated dependencies.');

As per coding guidelines

mcp-server/src/core/direct-functions/add-dependency.js (3)

30-41: Use standardized error code

Prefer INPUT_VALIDATION_ERROR for missing required args to align with wrapper conventions.

-          code: 'MISSING_ARGUMENT',
+          code: 'INPUT_VALIDATION_ERROR',

As per coding guidelines


74-85: Ensure silent mode restoration; use try/finally

Guard against leaks if core throws.

-    // Enable silent mode to prevent console logs from interfering with JSON response
-    enableSilentMode();
-
-    // Create context object
-    const context = { projectRoot, tag };
-
-    // Call the core function using the provided path
-    await addDependency(tasksPath, taskId, dependencyId, context);
-
-    // Restore normal logging
-    disableSilentMode();
-
-    return {
+    enableSilentMode();
+    // Create context object
+    const context = { projectRoot, tag };
+    try {
+      await addDependency(tasksPath, taskId, dependencyId, context);
+      return {
         success: true,
         data: {
           message: `Successfully added dependency: Task ${taskId} now depends on ${dependencyId}`,
           taskId: taskId,
           dependencyId: dependencyId
         }
-    };
+      };
+    } finally {
+      disableSilentMode();
+    }

As per coding guidelines


78-82: Pass MCP context and logger wrapper; guard against core process.exit

  • Include commandName and outputType: 'mcp', and pass a logger wrapper as mcpLog per guidelines.
  • Core addDependency/removeDependency call process.exit on errors; in MCP this can terminate the server. Update core to throw when context.outputType === 'mcp' (or a noExit flag) and let this wrapper return a structured error.
-import {
-  enableSilentMode,
-  disableSilentMode
-} from '../../../../scripts/modules/utils.js';
+import {
+  enableSilentMode,
+  disableSilentMode,
+  logWrapper
+} from '../../../../scripts/modules/utils.js';
@@
-    const context = { projectRoot, tag };
+    const context = {
+      projectRoot,
+      tag,
+      commandName: 'add-dependency',
+      outputType: 'mcp',
+      mcpLog: logWrapper(log)
+    };

Follow-up: adjust core add/remove to throw instead of process.exit when outputType === 'mcp'. As per coding guidelines

mcp-server/src/core/direct-functions/remove-dependency.js (3)

28-38: Use standardized error code

Prefer INPUT_VALIDATION_ERROR for missing tasksJsonPath.

-          code: 'MISSING_ARGUMENT',
+          code: 'INPUT_VALIDATION_ERROR',

As per coding guidelines


72-83: Ensure silent mode restoration; use try/finally

Mirror the add-dependency wrapper.

-    // Enable silent mode to prevent console logs from interfering with JSON response
-    enableSilentMode();
-    // Call the core function using the provided tasksPath
-    await removeDependency(tasksPath, taskId, dependencyId, {
-      projectRoot,
-      tag
-    });
-    // Restore normal logging
-    disableSilentMode();
-    return {
+    enableSilentMode();
+    try {
+      await removeDependency(tasksPath, taskId, dependencyId, {
+        projectRoot,
+        tag
+      });
+      return {
         success: true,
         data: {
           message: `Successfully removed dependency: Task ${taskId} no longer depends on ${dependencyId}`,
           taskId: taskId,
           dependencyId: dependencyId
         }
-    };
+      };
+    } finally {
+      disableSilentMode();
+    }

As per coding guidelines


75-80: Pass MCP context and logger wrapper; guard against core process.exit

Include commandName/outputType/mcpLog and make core throw (not exit) in MCP paths.

-    await removeDependency(tasksPath, taskId, dependencyId, {
-      projectRoot,
-      tag
-    });
+    await removeDependency(tasksPath, taskId, dependencyId, {
+      projectRoot,
+      tag,
+      commandName: 'remove-dependency',
+      outputType: 'mcp',
+      mcpLog: logWrapper(log)
+    });

As per coding guidelines

scripts/modules/commands.js (1)

2501-2515: Expose dry-run in CLI and propagate to core

Add a --dry-run flag and pass it through context so bulk ops can use it.

 .command('add-dependency')
 .description(
   'Add dependencies to task(s). Supports ranges and comma-separated lists.'
 )
 .option(
   '-i, --id <id>',
   'Task ID(s) to add dependencies to (e.g., "7", "7-10", "7,8,9")'
 )
 .option(
   '-d, --depends-on <id>',
   'Task ID(s) that will become dependencies (e.g., "1", "1-5", "1,3,5")'
 )
+.option('--dry-run', 'Validate and show changes without writing', false)
@@
-      await addDependency(taskMaster.getTasksPath(), taskId, dependencyId, {
-        projectRoot: taskMaster.getProjectRoot(),
-        tag
-      });
+      await addDependency(taskMaster.getTasksPath(), taskId, dependencyId, {
+        projectRoot: taskMaster.getProjectRoot(),
+        tag,
+        dryRun: !!options.dryRun
+      });

Pair with the dependency-manager change to forward context.dryRun to bulk. As per coding guidelines

Also applies to: 2540-2544

♻️ Duplicate comments (3)
scripts/modules/dependency-manager.js (1)

2356-2634: Bulk remove is not atomic — add abort-on-errors; tweak dry-run hint

Unlike bulk add, removals proceed even with validation errors. Abort when any validation issues exist (unless dry-run) to preserve all-or-nothing semantics.

       if (!silent) {
         log(
           'info',
           `Validation complete: ${validOperations}/${totalPossibleOperations} operations are valid`
         );
         if (validationErrors.length > 0) {
           log('warn', `Found ${validationErrors.length} validation errors:`);
           validationErrors.forEach((error) => log('warn', `  - ${error}`));
         }
       }
 
+      // Abort on validation errors (unless dry-run) to keep operations atomic
+      if (validationErrors.length > 0 && !dryRun) {
+        throw new Error(
+          `Validation failed (${validationErrors.length}/${totalPossibleOperations}). Aborting without changes.`
+        );
+      }
+
       // Dry-run mode: just report what would be done
       if (dryRun) {

Also update the dry-run hint:

-              `${chalk.yellow('Use --confirm to apply changes')}`,
+              `${chalk.yellow('Re-run without --dry-run to apply changes')}`,

As per coding guidelines

tests/unit/dependency-manager.test.js (2)

906-913: Placeholder test provides no coverage for routing logic

This test only checks typeof addDependency and doesn't verify the core PR objective: detecting multiple IDs and routing to bulkAddDependencies. Per past reviews, this was already flagged as missing behavioral coverage.

The comment claims "integration" testing but doesn't exercise the routing behavior. You need to verify:

  1. Single ID input triggers the original single-task code path
  2. Multiple IDs (comma-separated or range) trigger bulk processing
  3. Errors propagate correctly

Replace with behavioral tests:

-		test('integration: function routes to bulk when multiple IDs detected', () => {
-			// This behavior is tested through integration tests and cross-level dependency tests
-			// The implementation at dependency-manager.js:56-94 shows:
-			// 1. parseBulkTaskIds is called for both taskId and dependencyId
-			// 2. If either array has length > 1, bulkAddDependencies is called
-			// 3. Otherwise, original single-task logic executes
-			expect(typeof addDependency).toBe('function');
-		});
+		test('should route single ID to original logic (backward compatibility)', async () => {
+			mockReadJSON.mockReturnValue({
+				tasks: [
+					{ id: 1, dependencies: [] },
+					{ id: 2, dependencies: [] }
+				]
+			});
+
+			const { addDependency } = await import('../../scripts/modules/dependency-manager.js');
+			await addDependency(TEST_TASKS_PATH, 1, 2, { projectRoot: '/test' });
+
+			const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
+			expect(writeCall).toBeDefined();
+			const savedData = writeCall[1];
+			expect(savedData.tasks.find(t => t.id === 1).dependencies).toContain(2);
+		});
+
+		test('should route comma-separated IDs to bulk processing', async () => {
+			mockReadJSON.mockReturnValue({
+				tasks: [
+					{ id: 1, dependencies: [] },
+					{ id: 2, dependencies: [] },
+					{ id: 3, dependencies: [] }
+				]
+			});
+
+			const { addDependency } = await import('../../scripts/modules/dependency-manager.js');
+			await addDependency(TEST_TASKS_PATH, '1,2', 3, { projectRoot: '/test' });
+
+			const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
+			const savedData = writeCall[1];
+			expect(savedData.tasks.find(t => t.id === 1).dependencies).toContain(3);
+			expect(savedData.tasks.find(t => t.id === 2).dependencies).toContain(3);
+		});
+
+		test('should route range IDs to bulk processing', async () => {
+			mockReadJSON.mockReturnValue({
+				tasks: [
+					{ id: 1, dependencies: [] },
+					{ id: 2, dependencies: [] },
+					{ id: 3, dependencies: [] }
+				]
+			});
+
+			const { addDependency } = await import('../../scripts/modules/dependency-manager.js');
+			await addDependency(TEST_TASKS_PATH, '1-2', 3, { projectRoot: '/test' });
+
+			const writeCall = mockWriteJSON.mock.calls.find(([p]) => p === TEST_TASKS_PATH);
+			const savedData = writeCall[1];
+			expect(savedData.tasks.find(t => t.id === 1).dependencies).toContain(3);
+			expect(savedData.tasks.find(t => t.id === 2).dependencies).toContain(3);
+		});

938-985: Skipped tests provide no coverage

These .skip tests were added to address previous review feedback but remain disabled. Either implement them or remove the .skip markers to expose the missing coverage.

The skipped tests would verify:

  1. Single ID backward compatibility for removeDependency
  2. Bulk ID routing for removal operations

Remove .skip and implement the tests, or use the pattern from the enhanced addDependency suggestion above:

-		test.skip('should handle single IDs (backward compatibility)', async () => {
+		test('should route single ID to original logic (backward compatibility)', async () => {
 			mockReadJSON.mockImplementation(() => ({
 				tasks: [
 					{ id: 1, dependencies: [2] },
 					{ id: 2, dependencies: [] }
 				]
 			}));
 
 			const { removeDependency } = await import(
 				'../../scripts/modules/dependency-manager.js'
 			);
 			await removeDependency(TEST_TASKS_PATH, 1, 2, { projectRoot: '/test' });
 
 			const writeCall = mockWriteJSON.mock.calls.find(
 				([p]) => p === TEST_TASKS_PATH
 			);
 			expect(writeCall).toBeDefined();
 			const savedData = writeCall[1];
 			expect(
 				savedData.tasks.find((t) => t.id === 1).dependencies
 			).not.toContain(2);
 		});
 
-		test.skip('should detect and route multiple IDs to bulk processing', async () => {
+		test('should route multiple IDs to bulk processing', async () => {
 			mockReadJSON.mockImplementation(() => ({
 				tasks: [
 					{ id: 1, dependencies: [3, 4] },
 					{ id: 2, dependencies: [3, 4] },
 					{ id: 3, dependencies: [] },
 					{ id: 4, dependencies: [] }
 				]
 			}));
 
 			const { removeDependency } = await import(
 				'../../scripts/modules/dependency-manager.js'
 			);
 			await removeDependency(TEST_TASKS_PATH, '1,2', '3,4', {
 				projectRoot: '/test'
 			});
 
 			const writeCall = mockWriteJSON.mock.calls.find(
 				([p]) => p === TEST_TASKS_PATH
 			);
 			expect(writeCall).toBeDefined();
 			const savedData = writeCall[1];
 			expect(savedData.tasks.find((t) => t.id === 1).dependencies).toEqual([]);
 			expect(savedData.tasks.find((t) => t.id === 2).dependencies).toEqual([]);
 		});
📜 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 b036b1e and 31977d5.

📒 Files selected for processing (5)
  • mcp-server/src/core/direct-functions/add-dependency.js (1 hunks)
  • mcp-server/src/core/direct-functions/remove-dependency.js (1 hunks)
  • scripts/modules/commands.js (3 hunks)
  • scripts/modules/dependency-manager.js (4 hunks)
  • tests/unit/dependency-manager.test.js (6 hunks)
🧰 Additional context used
📓 Path-based instructions (19)
scripts/modules/*.js

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

Each module in scripts/modules/ should be focused on a single responsibility, following the modular architecture (e.g., commands.js for CLI command handling, task-manager.js for task data and core logic, dependency-manager.js for dependency management, ui.js for CLI output formatting, ai-services-unified.js for AI service integration, config-manager.js for configuration management, utils.js for utility functions).

scripts/modules/*.js: Export all core functions, helper functions, and utility methods needed by your new function or command from their respective modules. Explicitly review the module's export block to ensure every required dependency is included.
Pass all required parameters to functions you call within your implementation and verify that direct function parameters match their core function counterparts.
Use consistent file naming conventions: 'task_${id.toString().padStart(3, '0')}.txt', use path.join for composing file paths, and use appropriate file extensions (.txt for tasks, .json for data).
Use structured error objects with code and message properties, include clear error messages, and handle both function-specific and file system errors.
Import all silent mode utilities together from 'scripts/modules/utils.js' and always use isSilentMode() to check global silent mode status. Wrap core function calls within direct functions using enableSilentMode() and disableSilentMode() in a try/finally block if the core function might produce console output.
Core functions should check outputFormat === 'text' before displaying UI elements and use internal logging that respects silent mode.
Design functions to accept dependencies as parameters (dependency injection) and avoid hard-coded dependencies that are difficult to mock.
Keep pure logic separate from I/O operations or UI rendering to allow testing the logic without mocking complex dependencies.
When implementing core logic for new features, do so in 'scripts/modules/' before CLI or MCP interfaces, and d...

Files:

  • scripts/modules/dependency-manager.js
  • scripts/modules/commands.js
scripts/modules/dependency-manager.js

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

scripts/modules/dependency-manager.js: Represent task dependencies as arrays of task IDs
Use numeric IDs for direct task references
Use string IDs with dot notation (e.g., "1.2") for subtask references
Do not mix reference types without proper conversion
Allow numeric subtask IDs to reference other subtasks within the same parent
Convert between formats appropriately when needed
Do not create circular dependencies between subtasks
Validate that referenced tasks exist before adding dependencies
Provide clear error messages for non-existent dependencies
Remove references to non-existent tasks during validation
Check for circular dependencies before adding new relationships
Use graph traversal algorithms (DFS) to detect cycles
Provide clear error messages explaining the circular chain
Prevent tasks from depending on themselves
Handle both direct and indirect self-dependencies
Format task and dependency IDs consistently when adding dependencies
Check for existing dependencies to prevent duplicates
Sort dependencies for better readability
Check if the dependency exists before removing
Handle different ID formats consistently when removing dependencies
Provide feedback about the removal result
Use Set objects to identify and remove duplicate dependencies
Handle both numeric and string ID formats when removing duplicates
Check for and remove references to non-existent tasks during cleanup
Check for and remove self-references during cleanup
Track and report changes made during dependency cleanup
Use visual indicators to show dependency status (✅/⏱️)
Format dependency lists consistently for visualization
Use depth-first search (DFS) for cycle detection
Track visited nodes and recursion stack during cycle detection
Support both task and subtask dependencies in cycle detection

Features that handle task relationships belong in 'scripts/modules/dependency-manager.js'.

Files:

  • scripts/modules/dependency-manager.js
scripts/modules/**

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

When using the MCP server, restart it if core logic in scripts/modules or MCP tool/direct function definitions change.

Files:

  • scripts/modules/dependency-manager.js
  • scripts/modules/commands.js
scripts/modules/*

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

scripts/modules/*: Every command that reads or writes tasks.json must be tag-aware
All command files must import getCurrentTag from utils.js
Every CLI command that operates on tasks must include the --tag CLI option
All commands must resolve the tag using the pattern: options.tag || getCurrentTag(projectRoot) || 'master'
All commands must find projectRoot with error handling before proceeding
All commands must pass { projectRoot, tag } as context to core functions
MCP direct functions must accept and use a context object containing projectRoot and tag, and pass them to core functions
Do not hard-code tag resolution (e.g., const tag = options.tag || 'master';); always use getCurrentTag
Do not omit the --tag CLI option in commands that operate on tasks
Do not omit the context parameter when calling core functions from commands
Do not call readJSON or writeJSON without passing projectRoot and tag

Files:

  • scripts/modules/dependency-manager.js
  • scripts/modules/commands.js
**/*.js

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

**/*.js: Declare and initialize global variables at the top of modules to avoid hoisting issues.
Use proper function declarations to avoid hoisting issues and initialize variables before they are referenced.
Do not reference variables before their declaration in module scope.
Use dynamic imports (import()) to avoid initialization order issues in modules.

Files:

  • scripts/modules/dependency-manager.js
  • mcp-server/src/core/direct-functions/remove-dependency.js
  • scripts/modules/commands.js
  • mcp-server/src/core/direct-functions/add-dependency.js
  • tests/unit/dependency-manager.test.js
mcp-server/src/core/direct-functions/*.js

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

mcp-server/src/core/direct-functions/*.js: Direct function files in mcp-server/src/core/direct-functions/.js must implement silent mode using enableSilentMode and disableSilentMode from scripts/modules/utils.js, and always restore silent mode state using a try/finally block.
Direct function parameters in mcp-server/src/core/direct-functions/
.js must match the corresponding core function signatures; do not add extra parameters not present in the core function.
Always use the isSilentMode() function to check silent mode state in mcp-server/src/core/direct-functions/*.js; do not access global variables like silentMode directly.

mcp-server/src/core/direct-functions/*.js: Check that all helper functions your direct function needs are properly exported from their source modules and import these dependencies explicitly at the top of your file in direct functions.
Verify the signature of core functions you're calling in direct functions and ensure all required parameters are provided, passing explicit values for required parameters rather than relying on defaults.
Use path.join() instead of string concatenation for file paths, and follow established file naming conventions (e.g., 'task_001.txt') in direct functions.
Wrap core function calls and AI calls in try/catch blocks in direct functions, log errors with appropriate severity and context, and return standardized error objects with code and message.
When calling a core function from a direct function, pass the log object provided by FastMCP wrapped in the standard logWrapper object as the mcpLog property in the options argument.
Wrap core function calls within direct functions using enableSilentMode() and disableSilentMode() in a try/finally block if the core function might produce console output that isn't reliably controlled by passing { mcpLog } or an outputFormat parameter.
Do not wrap calls to the unified AI service (generateTextService, generateObjectService) in silent mode; their logging is handled internal...

Files:

  • mcp-server/src/core/direct-functions/remove-dependency.js
  • mcp-server/src/core/direct-functions/add-dependency.js
mcp-server/src/{tools,core/direct-functions}/*.js

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

mcp-server/src/{tools,core/direct-functions}/*.js: Use kebab-case for all file names in mcp-server/src/tools/ and mcp-server/src/core/direct-functions/.
Use helpers from mcp-server/src/tools/utils.js, mcp-server/src/core/utils/path-utils.js, and mcp-server/src/core/utils/ai-client-utils.js for centralized utilities.

Files:

  • mcp-server/src/core/direct-functions/remove-dependency.js
  • mcp-server/src/core/direct-functions/add-dependency.js
mcp-server/src/core/direct-functions/**/*.js

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

mcp-server/src/core/direct-functions/**/*.js: Direct function wrappers in mcp-server/src/core/direct-functions/ must call the corresponding core logic function, passing commandName and outputType: 'mcp' in the context, and outputFormat: 'json' if supported.
Direct function wrappers in mcp-server/src/core/direct-functions/ must include coreResult.telemetryData as a field within the data object of the successful response.

In direct functions (mcp-server/src/core/direct-functions/*) that call core functions (scripts/modules/*), ensure console output from the core function is suppressed to avoid breaking MCP JSON responses, preferably by passing an outputFormat parameter or by wrapping the call with silent mode.

Files:

  • mcp-server/src/core/direct-functions/remove-dependency.js
  • mcp-server/src/core/direct-functions/add-dependency.js
{scripts/modules/utils.js,mcp-server/src/core/direct-functions/**/*.js}

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

{scripts/modules/utils.js,mcp-server/src/core/direct-functions/**/*.js}: Use the logger wrapper pattern when passing loggers to prevent mcpLog[level] is not a function errors; do not pass the FastMCP log object directly as mcpLog to core functions.
Ensure silent mode is disabled in a finally block to prevent it from staying enabled.

Files:

  • mcp-server/src/core/direct-functions/remove-dependency.js
  • mcp-server/src/core/direct-functions/add-dependency.js
scripts/modules/commands.js

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

scripts/modules/commands.js: Centralize all LLM calls through generateTextService or generateObjectService.
Do not import or call anything from the old ai-services.js, ai-client-factory.js, or ai-client-utils.js files.
Do not fetch AI-specific parameters (model ID, max tokens, temp) using config-manager.js getters for the AI call. Pass the role instead.
Do not implement fallback or retry logic outside ai-services-unified.js.
Do not handle API key resolution outside the service layer (it uses utils.js internally).
Determine the appropriate role (main, research, fallback) in your core logic and pass it to the service.
Pass the session object (received in the context parameter, especially from direct function wrappers) to the service call when in MCP context.
Use generateTextService and implement robust manual JSON parsing (with Zod validation after parsing) when structured output is needed, as generateObjectService has shown unreliability with some providers/schemas.
Be aware of potential reliability issues with generateObjectService across different providers and complex schemas. Prefer generateTextService + manual parsing as a more robust alternative for structured data needs.

scripts/modules/commands.js: All new user-facing commands should be added to 'scripts/modules/commands.js'.
Use consistent patterns for option naming and help text in CLI commands.
Follow the Commander.js model for subcommand structure in CLI commands.
When using callbacks (like in Commander.js commands), define them separately to allow testing the callback logic independently.
Add help text to the command definition and update 'dev_workflow.mdc' with command reference when adding a new feature.
Follow the established pattern in 'commands.js' for CLI command implementation, using Commander.js for argument parsing, including comprehensive help text and examples, and supporting tagged task context awareness.
Provide clear error messages for common failu...

Files:

  • scripts/modules/commands.js
tests/{unit,integration,e2e,fixtures}/**/*.js

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

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/.

Files:

  • tests/unit/dependency-manager.test.js
tests/unit/*.js

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

Each module should have a corresponding unit test file in tests/unit/ that reflects the module structure (one test file per module).

Files:

  • tests/unit/dependency-manager.test.js
**/*.{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:

  • tests/unit/dependency-manager.test.js
**/*.test.js

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

**/*.test.js: Never use asynchronous operations in tests. Make all mocks return synchronous values when possible.
Always mock tests properly based on the way the tested functions are defined and used.
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.
Use fixtures from tests/fixtures/ for consistent sample data across tests.
Always declare mocks before importing the modules being tested in Jest test files.
Use jest.spyOn() after imports to create spies on mock functions and reference these spies in test assertions.
When testing functions with callbacks, get the callback from your mock's call arguments, execute it directly with test inputs, and verify the results.
For ES modules, use jest.mock() before static imports and jest.unstable_mockModule() before dynamic imports to mock dependencies.
Reset mock functions (mockFn.mockReset()) before dynamic imports if they might have been called previously.
When verifying console assertions, assert against the actual arguments passed (single formatted string), not multiple arguments.
Use mock-fs to mock file system operations in tests, and restore the file system after each test.
Mock API calls (e.g., Anthropic/Claude) by mocking the entire module and providing predictable responses.
Set mock environment variables in test setup and restore them after each test.
Maintain test fixtures separate from test logic.
Follow the mock-first-then-import pattern for all Jest mocks.
Do not define mock variables before jest.mock() calls (they won't be accessible due to hoisting).
Use test-specific file paths (e.g., 'test-tasks.json') for all file operations in tests.
Mock readJSON and writeJSON to avoid real file system interactions in tests.
Verify file operations use the correct paths in expect statements.
Use different file paths for each test to avoid test interdependence.
Verify modifications on the in-memory task objects passed to w...

Files:

  • tests/unit/dependency-manager.test.js
tests/unit/**/*.test.js

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

tests/unit/**/*.test.js: Unit tests must be located in tests/unit/, test individual functions and utilities in isolation, mock all external dependencies, and keep tests small, focused, and fast.
Do not include actual command execution in unit tests.

Files:

  • tests/unit/dependency-manager.test.js
tests/{unit,integration,e2e}/**/*.test.js

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

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.
When mocking the Commander.js chain, mock ALL chainable methods (option, argument, action, on, etc.) and return this (or the mock object) from all chainable method mocks.
Explicitly handle all options, including defaults and shorthand flags (e.g., -p for --prompt), and include null/undefined checks in test implementations for parameters that might be optional.
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.
Mock the action handlers for CLI commands and verify they're called with correct arguments.
Use sample task fixtures for consistent test data, mock file system operations, and test both success and error paths for task operations.
Mock console output and verify correct formatting in UI function tests. Use flexible assertions like toContain() or toMatch() for formatted output.
Mock chalk functions to return the input text to make testing easier while still verifying correct function calls.

Files:

  • tests/unit/dependency-manager.test.js
**/*.{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:

  • tests/unit/dependency-manager.test.js
tests/{unit,integration,e2e}/**

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

Organize test directories by test type (unit, integration, e2e) and mirror source structure where possible

Files:

  • tests/unit/dependency-manager.test.js
**/*.{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:

  • tests/unit/dependency-manager.test.js
🧠 Learnings (42)
📓 Common learnings
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: assets/.windsurfrules:0-0
Timestamp: 2025-09-24T15:12:12.658Z
Learning: Manage dependencies with task-master add-dependency and remove-dependency; validate and fix using validate-dependencies and fix-dependencies
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Format task and dependency IDs consistently when adding dependencies

Applied to files:

  • scripts/modules/dependency-manager.js
  • mcp-server/src/core/direct-functions/remove-dependency.js
  • scripts/modules/commands.js
  • mcp-server/src/core/direct-functions/add-dependency.js
  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/task-manager/* : All core functions in scripts/modules/task-manager/ must accept a context parameter and use it to extract projectRoot and tag

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/task-manager/* : All core functions in scripts/modules/task-manager/ must use readJSON(tasksPath, projectRoot, tag) and writeJSON(tasksPath, data, projectRoot, tag)

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/* : Every command that reads or writes tasks.json must be tag-aware

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:18:17.759Z
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 : Use tag resolution functions for all task data access, provide backward compatibility with legacy format, and default to 'master' tag when no tag is specified.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/* : Do not call readJSON or writeJSON without passing projectRoot and tag

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T05:38:17.352Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#943
File: scripts/modules/task-manager/move-task.js:24-24
Timestamp: 2025-07-18T05:38:17.352Z
Learning: In the Claude Task Master system, core task-manager functions are designed with fallback mechanisms for missing projectRoot parameters using the pattern `const projectRoot = providedProjectRoot || findProjectRoot();`. The readJSON and writeJSON functions have default parameters (projectRoot = null, tag = null) and handle missing parameters gracefully. Adding strict validation to these core functions would break the established flexible architecture pattern.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Tasks must be organized into separate contexts (tags) within tasks.json, using the tagged format: {"master": {"tasks": [...]}, "feature-branch": {"tasks": [...]}}. Legacy format {"tasks": [...]} must be silently migrated to the tagged format on first use.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Implement silent migration from legacy to tagged format in the readJSON() function, detect legacy format, convert automatically, and preserve all existing task data during migration.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Tasks must be accessed and updated through the tag resolution layer using getTasksForTag(data, tagName) and setTasksForTag(data, tagName, tasks); direct manipulation of the tagged structure in core functions is not allowed.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Generate task files from the current tag context, include tag information in generated files, and do not mix tasks from different tags in file generation.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Use tag resolution functions to maintain backward compatibility, returning legacy format to core functions and not exposing the tagged structure to existing core logic.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:10:12.881Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dev_workflow.mdc:0-0
Timestamp: 2025-07-18T17:10:12.881Z
Learning: Applies to tasks.json : Use the `tasks.json` file (generated by Taskmaster) to store the project's task list, including tags and task structures.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/generate.js : The generate command must use tag-aware file naming: task_XXX.txt for master, task_XXX_<tag>.txt for other tags

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:13:30.188Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tags.mdc:0-0
Timestamp: 2025-07-18T17:13:30.188Z
Learning: Applies to scripts/modules/* : All commands must pass { projectRoot, tag } as context to core functions

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Use AI to generate detailed subtasks within the current tag context, considering complexity analysis for subtask counts and ensuring proper IDs for newly created subtasks.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Remove references to non-existent tasks during validation

Applied to files:

  • scripts/modules/dependency-manager.js
  • scripts/modules/commands.js
  • tests/unit/dependency-manager.test.js
📚 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 : Regenerate task files after destructive operations, passing all required parameters to generation functions, and provide an option to skip regeneration if needed.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Allow numeric subtask IDs to reference other subtasks within the same parent

Applied to files:

  • scripts/modules/dependency-manager.js
  • scripts/modules/commands.js
  • mcp-server/src/core/direct-functions/add-dependency.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Subtasks must use consistent properties, maintain simple numeric IDs unique within the parent task, and must not duplicate parent task properties.

Applied to files:

  • scripts/modules/dependency-manager.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Support both task and subtask dependencies in cycle detection

Applied to files:

  • scripts/modules/dependency-manager.js
  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Use string IDs with dot notation (e.g., "1.2") for subtask references

Applied to files:

  • scripts/modules/dependency-manager.js
  • scripts/modules/commands.js
  • mcp-server/src/core/direct-functions/add-dependency.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Represent task dependencies as arrays of task IDs

Applied to files:

  • scripts/modules/dependency-manager.js
  • mcp-server/src/core/direct-functions/remove-dependency.js
  • scripts/modules/commands.js
  • mcp-server/src/core/direct-functions/add-dependency.js
  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Handle different ID formats consistently when removing dependencies

Applied to files:

  • mcp-server/src/core/direct-functions/remove-dependency.js
  • scripts/modules/commands.js
  • mcp-server/src/core/direct-functions/add-dependency.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Use numeric IDs for direct task references

Applied to files:

  • mcp-server/src/core/direct-functions/remove-dependency.js
  • scripts/modules/commands.js
  • mcp-server/src/core/direct-functions/add-dependency.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Check for and remove references to non-existent tasks during cleanup

Applied to files:

  • mcp-server/src/core/direct-functions/remove-dependency.js
  • scripts/modules/commands.js
  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:14:29.399Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/tasks.mdc:0-0
Timestamp: 2025-07-18T17:14:29.399Z
Learning: Applies to scripts/modules/task-manager.js : Use consistent formatting for task files, include all task properties in text files, and format dependencies with status indicators.

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Track and report changes made during dependency cleanup

Applied to files:

  • scripts/modules/commands.js
📚 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/dependency-manager.js : Features that handle task relationships belong in 'scripts/modules/dependency-manager.js'.

Applied to files:

  • scripts/modules/commands.js
  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Handle both numeric and string ID formats when removing duplicates

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-09-24T15:12:12.658Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: assets/.windsurfrules:0-0
Timestamp: 2025-09-24T15:12:12.658Z
Learning: Manage dependencies with task-master add-dependency and remove-dependency; validate and fix using validate-dependencies and fix-dependencies

Applied to files:

  • scripts/modules/commands.js
📚 Learning: 2025-07-18T17:10:12.881Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dev_workflow.mdc:0-0
Timestamp: 2025-07-18T17:10:12.881Z
Learning: Use the `add_dependency` and `remove_dependency` commands to manage task dependencies, ensuring no circular or duplicate dependencies.

Applied to files:

  • scripts/modules/commands.js
📚 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:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Do not create circular dependencies between subtasks

Applied to files:

  • tests/unit/dependency-manager.test.js
📚 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:

  • tests/unit/dependency-manager.test.js
📚 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:

  • tests/unit/dependency-manager.test.js
📚 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 : For ES modules, use jest.mock() before static imports and jest.unstable_mockModule() before dynamic imports to mock dependencies.

Applied to files:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Validate that referenced tasks exist before adding dependencies

Applied to files:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:40.548Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:40.548Z
Learning: Applies to scripts/modules/dependency-manager.js : Prevent tasks from depending on themselves

Applied to files:

  • tests/unit/dependency-manager.test.js
📚 Learning: 2025-07-18T17:09:45.690Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/dependencies.mdc:0-0
Timestamp: 2025-07-18T17:09:45.690Z
Learning: Applies to scripts/modules/dependency-manager.js : Check for circular dependencies before adding new relationships

Applied to files:

  • tests/unit/dependency-manager.test.js
📚 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:

  • tests/unit/dependency-manager.test.js
🧬 Code graph analysis (5)
scripts/modules/dependency-manager.js (3)
mcp-server/src/core/direct-functions/add-dependency.js (4)
  • tasksPath (64-64)
  • taskId (67-67)
  • dependencyId (68-68)
  • context (78-78)
mcp-server/src/core/direct-functions/remove-dependency.js (3)
  • tasksPath (62-62)
  • taskId (65-65)
  • dependencyId (66-66)
scripts/modules/task-manager/task-exists.js (6)
  • parts (10-10)
  • id (14-14)
  • id (34-34)
  • parentId (19-19)
  • subtaskId (20-20)
  • parentTask (23-23)
mcp-server/src/core/direct-functions/remove-dependency.js (6)
mcp-server/src/core/direct-functions/add-dependency.js (2)
  • taskId (67-67)
  • dependencyId (68-68)
scripts/modules/dependency-manager.js (2)
  • taskId (1063-1065)
  • taskId (2011-2011)
scripts/modules/utils.js (3)
  • taskId (900-900)
  • id (940-940)
  • id (1019-1019)
mcp-server/src/core/direct-functions/expand-task.js (1)
  • taskId (78-78)
mcp-server/src/core/direct-functions/set-task-status.js (1)
  • taskId (69-69)
scripts/modules/task-manager/set-task-status.js (1)
  • taskId (105-105)
scripts/modules/commands.js (3)
mcp-server/src/core/direct-functions/add-dependency.js (2)
  • taskId (67-67)
  • dependencyId (68-68)
mcp-server/src/core/direct-functions/remove-dependency.js (2)
  • taskId (65-65)
  • dependencyId (66-66)
scripts/modules/dependency-manager.js (2)
  • taskId (1063-1065)
  • taskId (2011-2011)
mcp-server/src/core/direct-functions/add-dependency.js (5)
mcp-server/src/core/direct-functions/remove-dependency.js (2)
  • taskId (65-65)
  • dependencyId (66-66)
scripts/modules/dependency-manager.js (2)
  • taskId (1063-1065)
  • taskId (2011-2011)
scripts/modules/utils.js (3)
  • taskId (900-900)
  • id (940-940)
  • id (1019-1019)
mcp-server/src/core/direct-functions/expand-task.js (1)
  • taskId (78-78)
scripts/modules/task-manager/set-task-status.js (1)
  • taskId (105-105)
tests/unit/dependency-manager.test.js (1)
tests/unit/scripts/modules/dependency-manager/fix-dependencies-command.test.js (1)
  • originalExit (10-10)
🔇 Additional comments (7)
scripts/modules/dependency-manager.js (1)

1919-2029: parseBulkTaskIds: solid parsing and validation

Range, CSV, subtask support, dedupe, and descending-range check are correct. LGTM.

mcp-server/src/core/direct-functions/add-dependency.js (1)

66-69: Passing raw IDs to support ranges/CSV/dot — LGTM

This correctly defers parsing to the core/bulk path.

mcp-server/src/core/direct-functions/remove-dependency.js (1)

64-67: Passing raw IDs to support ranges/CSV/dot — LGTM

Defers parsing to the core/bulk path consistently with add-dependency.

scripts/modules/commands.js (1)

2498-2508: Help text update — LGTM

Clear docs for ranges/CSV on add-dependency.

tests/unit/dependency-manager.test.js (3)

6-6: LGTM: Imports aligned with bulk dependency features

The imports correctly include addDependency, parseBulkTaskIds, canMoveWithDependencies, and the cross-level test fixture. These additions support testing the bulk dependency management functionality introduced in this PR.

Also applies to: 58-58, 65-67


81-92: LGTM: Proper test isolation with process.exit mocking

The setup correctly mocks process.exit to prevent tests from terminating the test runner and restores it in afterEach. The default mockReadJSON return value ensures clean state between tests.

Also applies to: 161-163


854-882: LGTM: Comprehensive parseBulkTaskIds test coverage

The test suite properly validates:

  • Single ID parsing
  • Comma-separated lists
  • Range syntax
  • Mixed formats
  • Duplicate removal
  • Error handling for invalid formats

These tests provide solid behavioral coverage of the parsing utility.

Comment on lines +2551 to +2560
'Remove dependencies from task(s). Supports ranges and comma-separated lists.'
)
.option(
'-i, --id <id>',
'Task ID(s) to remove dependencies from (e.g., "7", "7-10", "7,8,9")'
)
.option(
'-d, --depends-on <id>',
'Task ID(s) to remove as dependencies (e.g., "1", "1-5", "1,3,5")'
)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Expose dry-run in remove-dependency and propagate to core

Mirror add-dependency for removals.

 .command('remove-dependency')
 .description(
   'Remove dependencies from task(s). Supports ranges and comma-separated lists.'
 )
 .option(
   '-i, --id <id>',
   'Task ID(s) to remove dependencies from (e.g., "7", "7-10", "7,8,9")'
 )
 .option(
   '-d, --depends-on <id>',
   'Task ID(s) to remove as dependencies (e.g., "1", "1-5", "1,3,5")'
 )
+.option('--dry-run', 'Validate and show changes without writing', false)
@@
-      await removeDependency(taskMaster.getTasksPath(), taskId, dependencyId, {
-        projectRoot: taskMaster.getProjectRoot(),
-        tag
-      });
+      await removeDependency(taskMaster.getTasksPath(), taskId, dependencyId, {
+        projectRoot: taskMaster.getProjectRoot(),
+        tag,
+        dryRun: !!options.dryRun
+      });

As per coding guidelines

Also applies to: 2592-2596

🤖 Prompt for AI Agents
In scripts/modules/commands.js around lines 2551-2560 (and also apply the same
change at 2592-2596), the remove-dependency CLI command lacks the dry-run option
and doesn't propagate the flag to the core removal logic; mirror the
add-dependency implementation by adding the dry-run option (e.g., '-n,
--dry-run') to the command options and ensure the command handler accepts the
dryRun boolean and forwards it into the core removeDependency function call (or
options object) so removals can be simulated without making changes.

Comment on lines +57 to +94
// Check if we're dealing with multiple IDs by attempting to parse them
try {
const taskIds = parseBulkTaskIds(String(taskId));
const dependencyIds = parseBulkTaskIds(String(dependencyId));

// If either has multiple IDs, use bulk processing
if (taskIds.length > 1 || dependencyIds.length > 1) {
log(
'info',
`Adding dependencies in bulk: ${dependencyIds.join(', ')} to tasks ${taskIds.join(', ')}...`
);

const result = await bulkAddDependencies(
tasksPath,
String(taskId),
String(dependencyId),
{
silent: false,
context
}
);

if (!result.success) {
log('error', `Bulk add dependencies failed: ${result.error}`);
process.exit(1);
}

return;
}

// Single IDs - use the original logic with first elements
taskId = taskIds[0];
dependencyId = dependencyIds[0];
} catch (error) {
// If parsing fails, treat as single IDs (original behavior)
// This maintains backward compatibility for non-bulk format IDs
}

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

Do not silently treat malformed bulk specs as single IDs

If parsing fails but inputs contain bulk syntax ("," or "-"), abort with an error instead of falling back to single-ID behavior. This prevents accidental mis-targeting (e.g., "7-10a" becoming 7).

Apply this pattern to both catch blocks:

-  } catch (error) {
-    // If parsing fails, treat as single IDs (original behavior)
-    // This maintains backward compatibility for non-bulk format IDs
-  }
+  } catch (error) {
+    const looksBulk =
+      /[,-]/.test(String(taskId)) || /[,-]/.test(String(dependencyId));
+    if (looksBulk) {
+      log('error', `Invalid bulk ID specification: ${error.message}`);
+      process.exit(1);
+    }
+    // Otherwise, fall back to single-ID behavior
+  }

As per coding guidelines

Also applies to: 284-322

🤖 Prompt for AI Agents
In scripts/modules/dependency-manager.js around lines 57-94 (and apply the same
change to 284-322), the catch blocks currently swallow parse errors and fall
back to single-ID behavior; instead detect whether the original inputs contain
bulk syntax and abort. Inside each catch, check the raw inputs (e.g.
String(taskId) and String(dependencyId)) for ',' or '-' and if either contains
those characters log an error with the parse exception context and exit with
non-zero status (process.exit(1)); only if neither input contains bulk syntax
continue with single-ID behavior or rethrow. Ensure the error message clearly
references the malformed bulk spec and include the caught error details.

Comment on lines +69 to +77
const result = await bulkAddDependencies(
tasksPath,
String(taskId),
String(dependencyId),
{
silent: false,
context
}
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Surface dryRun from context to bulk ops

Expose CLI/Direct-function dry-run to bulk helpers for consistent behavior.

-      const result = await bulkAddDependencies(
+      const result = await bulkAddDependencies(
         tasksPath,
         String(taskId),
         String(dependencyId),
         {
-          silent: false,
-          context
+          silent: false,
+          dryRun: !!(context && context.dryRun),
+          context
         }
       );
-      const result = await bulkRemoveDependencies(
+      const result = await bulkRemoveDependencies(
         tasksPath,
         String(taskId),
         String(dependencyId),
         {
-          silent: false,
-          context
+          silent: false,
+          dryRun: !!(context && context.dryRun),
+          context
         }
       );

Based on learnings

Also applies to: 297-305

🤖 Prompt for AI Agents
In scripts/modules/dependency-manager.js around lines 69-77 (and also apply the
same change at lines 297-305), the call to bulkAddDependencies does not pass a
dryRun flag from the provided context, so CLI/direct-function dry-run behavior
isn't propagated; update the options object passed to bulkAddDependencies to
include dryRun: context?.dryRun (or context.dryRun) so the helper respects the
dry-run mode, and do the same for the other bulk helper invocation at 297-305.

Comment on lines +2040 to +2330
async function bulkAddDependencies(
tasksPath,
taskSpec,
dependencySpec,
options = {}
) {
const { dryRun = false, silent = false, context = {} } = options;

try {
if (!silent) {
log('info', `Starting bulk dependency addition...`);
}

// Parse task and dependency specifications
const taskIds = parseBulkTaskIds(taskSpec);
const dependencyIds = parseBulkTaskIds(dependencySpec);

if (!silent) {
log(
'info',
`Parsed ${taskIds.length} tasks and ${dependencyIds.length} dependencies`
);
}

// Read tasks data
const data = readJSON(tasksPath, context.projectRoot, context.tag);
if (!data || !data.tasks) {
throw new Error('No valid tasks found in tasks.json');
}

// Validation phase
const validationErrors = [];
const operations = [];

// Validate all tasks exist
for (const taskId of taskIds) {
if (!taskExists(data.tasks, taskId)) {
validationErrors.push(`Task ${taskId} does not exist`);
continue;
}

// Find the target task/subtask
let targetTask = null;
let taskPath = [];

if (typeof taskId === 'string' && taskId.includes('.')) {
const [parentId, subtaskId] = taskId.split('.').map(Number);
const parentTask = data.tasks.find((t) => t.id === parentId);
if (parentTask && parentTask.subtasks) {
targetTask = parentTask.subtasks.find((s) => s.id === subtaskId);
taskPath = [
'tasks',
data.tasks.indexOf(parentTask),
'subtasks',
parentTask.subtasks.indexOf(targetTask)
];
}
} else {
targetTask = data.tasks.find((t) => t.id === taskId);
taskPath = ['tasks', data.tasks.indexOf(targetTask)];
}

if (!targetTask) {
validationErrors.push(`Cannot find task object for ${taskId}`);
continue;
}

// Check each dependency
for (const dependencyId of dependencyIds) {
// Validate dependency exists
if (!taskExists(data.tasks, dependencyId)) {
validationErrors.push(`Dependency ${dependencyId} does not exist`);
continue;
}

// Check for self-dependency
if (String(taskId) === String(dependencyId)) {
validationErrors.push(`Task ${taskId} cannot depend on itself`);
continue;
}

// Check if dependency already exists
if (
targetTask.dependencies &&
targetTask.dependencies.some(
(d) => String(d) === String(dependencyId)
)
) {
if (!silent) {
log(
'warn',
`Dependency ${dependencyId} already exists for task ${taskId}, skipping`
);
}
continue;
}

// Check for circular dependencies (simulate adding the dependency)
const tempData = JSON.parse(JSON.stringify(data));
const tempTargetTask = getTaskFromPath(tempData, taskPath);
if (!tempTargetTask.dependencies) tempTargetTask.dependencies = [];
tempTargetTask.dependencies.push(dependencyId);

if (isCircularDependency(tempData.tasks, dependencyId, [taskId])) {
validationErrors.push(
`Adding dependency ${dependencyId} to task ${taskId} would create a circular dependency`
);
continue;
}

// Operation is valid, add to operations list
operations.push({
taskId,
dependencyId,
taskPath
});
}
}

// Report validation results
const totalPossibleOperations = taskIds.length * dependencyIds.length;
const validOperations = operations.length;

if (!silent) {
log(
'info',
`Validation complete: ${validOperations}/${totalPossibleOperations} operations are valid`
);

if (validationErrors.length > 0) {
log('warn', `Found ${validationErrors.length} validation errors:`);
validationErrors.forEach((error) => log('warn', ` - ${error}`));
}
}

// If there are validation errors and we're not in dry-run mode, abort atomically
if (validationErrors.length > 0 && !dryRun) {
throw new Error(
`Validation failed (${validationErrors.length}/${totalPossibleOperations}). Aborting without changes.`
);
}

// Dry-run mode: just report what would be done
if (dryRun) {
const report = {
success: true,
dryRun: true,
summary: {
totalTasks: taskIds.length,
totalDependencies: dependencyIds.length,
validOperations: validOperations,
errors: validationErrors.length
},
operations: operations.map((op) => ({
task: op.taskId,
dependency: op.dependencyId
})),
errors: validationErrors
};

if (!silent && !isSilentMode()) {
console.log(
boxen(
chalk.blue(`Bulk Dependency Addition - DRY RUN\n\n`) +
`${chalk.cyan('Tasks:')} ${taskIds.join(', ')}\n` +
`${chalk.cyan('Dependencies:')} ${dependencyIds.join(', ')}\n\n` +
`${chalk.cyan('Valid operations:')} ${validOperations}\n` +
`${chalk.cyan('Validation errors:')} ${validationErrors.length}\n\n` +
`${chalk.yellow('Use --confirm to apply changes')}`,
{
padding: 1,
borderColor: 'blue',
borderStyle: 'round',
margin: { top: 1 }
}
)
);
}

return report;
}

// Execute operations atomically
if (validOperations === 0) {
if (!silent) {
log('info', 'No valid operations to perform');
}
return {
success: true,
summary: {
operationsPerformed: 0,
errors: validationErrors.length
},
errors: validationErrors
};
}

// Create a backup of the original data for rollback
const originalData = JSON.parse(JSON.stringify(data));

try {
// Apply all operations
for (const operation of operations) {
const targetTask = getTaskFromPath(data, operation.taskPath);
if (!targetTask.dependencies) {
targetTask.dependencies = [];
}
targetTask.dependencies.push(operation.dependencyId);

// Sort dependencies
targetTask.dependencies.sort((a, b) => {
if (typeof a === 'number' && typeof b === 'number') {
return a - b;
} else if (typeof a === 'string' && typeof b === 'string') {
const [aParent, aChild] = a.split('.').map(Number);
const [bParent, bChild] = b.split('.').map(Number);
return aParent !== bParent ? aParent - bParent : aChild - bChild;
} else if (typeof a === 'number') {
return -1;
} else {
return 1;
}
});
}

// Save changes
writeJSON(tasksPath, data, context.projectRoot, context.tag);

// Generate updated task files
await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
projectRoot: context.projectRoot,
tag: context.tag
});

if (!silent) {
log(
'success',
`Successfully added ${validOperations} dependency relationships`
);

if (!isSilentMode()) {
console.log(
boxen(
chalk.green(`Bulk Dependencies Added Successfully\n\n`) +
`${chalk.cyan('Operations performed:')} ${validOperations}\n` +
`${chalk.cyan('Tasks affected:')} ${[...new Set(operations.map((op) => op.taskId))].length}\n` +
`${chalk.cyan('Dependencies added:')} ${[...new Set(operations.map((op) => op.dependencyId))].length}`,
{
padding: 1,
borderColor: 'green',
borderStyle: 'round',
margin: { top: 1 }
}
)
);
}
}

return {
success: true,
summary: {
operationsPerformed: validOperations,
tasksAffected: [...new Set(operations.map((op) => op.taskId))].length,
dependenciesAdded: [
...new Set(operations.map((op) => op.dependencyId))
].length,
errors: validationErrors.length
},
operations: operations.map((op) => ({
task: op.taskId,
dependency: op.dependencyId
})),
errors: validationErrors
};
} catch (error) {
// Rollback on error
writeJSON(tasksPath, originalData, context.projectRoot, context.tag);
throw new Error(
`Bulk operation failed and was rolled back: ${error.message}`
);
}
} catch (error) {
if (!silent) {
log('error', `Bulk add dependencies failed: ${error.message}`);
}
return {
success: false,
error: error.message
};
}
}
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

Bulk add: atomic, tag-aware, clear reporting — LGTM; tweak dry-run hint

Implementation meets atomicity and tag-awareness goals. Suggest aligning the dry-run hint with CLI flags.

-              `${chalk.yellow('Use --confirm to apply changes')}`,
+              `${chalk.yellow('Re-run without --dry-run to apply changes')}`,
📝 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
async function bulkAddDependencies(
tasksPath,
taskSpec,
dependencySpec,
options = {}
) {
const { dryRun = false, silent = false, context = {} } = options;
try {
if (!silent) {
log('info', `Starting bulk dependency addition...`);
}
// Parse task and dependency specifications
const taskIds = parseBulkTaskIds(taskSpec);
const dependencyIds = parseBulkTaskIds(dependencySpec);
if (!silent) {
log(
'info',
`Parsed ${taskIds.length} tasks and ${dependencyIds.length} dependencies`
);
}
// Read tasks data
const data = readJSON(tasksPath, context.projectRoot, context.tag);
if (!data || !data.tasks) {
throw new Error('No valid tasks found in tasks.json');
}
// Validation phase
const validationErrors = [];
const operations = [];
// Validate all tasks exist
for (const taskId of taskIds) {
if (!taskExists(data.tasks, taskId)) {
validationErrors.push(`Task ${taskId} does not exist`);
continue;
}
// Find the target task/subtask
let targetTask = null;
let taskPath = [];
if (typeof taskId === 'string' && taskId.includes('.')) {
const [parentId, subtaskId] = taskId.split('.').map(Number);
const parentTask = data.tasks.find((t) => t.id === parentId);
if (parentTask && parentTask.subtasks) {
targetTask = parentTask.subtasks.find((s) => s.id === subtaskId);
taskPath = [
'tasks',
data.tasks.indexOf(parentTask),
'subtasks',
parentTask.subtasks.indexOf(targetTask)
];
}
} else {
targetTask = data.tasks.find((t) => t.id === taskId);
taskPath = ['tasks', data.tasks.indexOf(targetTask)];
}
if (!targetTask) {
validationErrors.push(`Cannot find task object for ${taskId}`);
continue;
}
// Check each dependency
for (const dependencyId of dependencyIds) {
// Validate dependency exists
if (!taskExists(data.tasks, dependencyId)) {
validationErrors.push(`Dependency ${dependencyId} does not exist`);
continue;
}
// Check for self-dependency
if (String(taskId) === String(dependencyId)) {
validationErrors.push(`Task ${taskId} cannot depend on itself`);
continue;
}
// Check if dependency already exists
if (
targetTask.dependencies &&
targetTask.dependencies.some(
(d) => String(d) === String(dependencyId)
)
) {
if (!silent) {
log(
'warn',
`Dependency ${dependencyId} already exists for task ${taskId}, skipping`
);
}
continue;
}
// Check for circular dependencies (simulate adding the dependency)
const tempData = JSON.parse(JSON.stringify(data));
const tempTargetTask = getTaskFromPath(tempData, taskPath);
if (!tempTargetTask.dependencies) tempTargetTask.dependencies = [];
tempTargetTask.dependencies.push(dependencyId);
if (isCircularDependency(tempData.tasks, dependencyId, [taskId])) {
validationErrors.push(
`Adding dependency ${dependencyId} to task ${taskId} would create a circular dependency`
);
continue;
}
// Operation is valid, add to operations list
operations.push({
taskId,
dependencyId,
taskPath
});
}
}
// Report validation results
const totalPossibleOperations = taskIds.length * dependencyIds.length;
const validOperations = operations.length;
if (!silent) {
log(
'info',
`Validation complete: ${validOperations}/${totalPossibleOperations} operations are valid`
);
if (validationErrors.length > 0) {
log('warn', `Found ${validationErrors.length} validation errors:`);
validationErrors.forEach((error) => log('warn', ` - ${error}`));
}
}
// If there are validation errors and we're not in dry-run mode, abort atomically
if (validationErrors.length > 0 && !dryRun) {
throw new Error(
`Validation failed (${validationErrors.length}/${totalPossibleOperations}). Aborting without changes.`
);
}
// Dry-run mode: just report what would be done
if (dryRun) {
const report = {
success: true,
dryRun: true,
summary: {
totalTasks: taskIds.length,
totalDependencies: dependencyIds.length,
validOperations: validOperations,
errors: validationErrors.length
},
operations: operations.map((op) => ({
task: op.taskId,
dependency: op.dependencyId
})),
errors: validationErrors
};
if (!silent && !isSilentMode()) {
console.log(
boxen(
chalk.blue(`Bulk Dependency Addition - DRY RUN\n\n`) +
`${chalk.cyan('Tasks:')} ${taskIds.join(', ')}\n` +
`${chalk.cyan('Dependencies:')} ${dependencyIds.join(', ')}\n\n` +
`${chalk.cyan('Valid operations:')} ${validOperations}\n` +
`${chalk.cyan('Validation errors:')} ${validationErrors.length}\n\n` +
`${chalk.yellow('Use --confirm to apply changes')}`,
{
padding: 1,
borderColor: 'blue',
borderStyle: 'round',
margin: { top: 1 }
}
)
);
}
return report;
}
// Execute operations atomically
if (validOperations === 0) {
if (!silent) {
log('info', 'No valid operations to perform');
}
return {
success: true,
summary: {
operationsPerformed: 0,
errors: validationErrors.length
},
errors: validationErrors
};
}
// Create a backup of the original data for rollback
const originalData = JSON.parse(JSON.stringify(data));
try {
// Apply all operations
for (const operation of operations) {
const targetTask = getTaskFromPath(data, operation.taskPath);
if (!targetTask.dependencies) {
targetTask.dependencies = [];
}
targetTask.dependencies.push(operation.dependencyId);
// Sort dependencies
targetTask.dependencies.sort((a, b) => {
if (typeof a === 'number' && typeof b === 'number') {
return a - b;
} else if (typeof a === 'string' && typeof b === 'string') {
const [aParent, aChild] = a.split('.').map(Number);
const [bParent, bChild] = b.split('.').map(Number);
return aParent !== bParent ? aParent - bParent : aChild - bChild;
} else if (typeof a === 'number') {
return -1;
} else {
return 1;
}
});
}
// Save changes
writeJSON(tasksPath, data, context.projectRoot, context.tag);
// Generate updated task files
await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
projectRoot: context.projectRoot,
tag: context.tag
});
if (!silent) {
log(
'success',
`Successfully added ${validOperations} dependency relationships`
);
if (!isSilentMode()) {
console.log(
boxen(
chalk.green(`Bulk Dependencies Added Successfully\n\n`) +
`${chalk.cyan('Operations performed:')} ${validOperations}\n` +
`${chalk.cyan('Tasks affected:')} ${[...new Set(operations.map((op) => op.taskId))].length}\n` +
`${chalk.cyan('Dependencies added:')} ${[...new Set(operations.map((op) => op.dependencyId))].length}`,
{
padding: 1,
borderColor: 'green',
borderStyle: 'round',
margin: { top: 1 }
}
)
);
}
}
return {
success: true,
summary: {
operationsPerformed: validOperations,
tasksAffected: [...new Set(operations.map((op) => op.taskId))].length,
dependenciesAdded: [
...new Set(operations.map((op) => op.dependencyId))
].length,
errors: validationErrors.length
},
operations: operations.map((op) => ({
task: op.taskId,
dependency: op.dependencyId
})),
errors: validationErrors
};
} catch (error) {
// Rollback on error
writeJSON(tasksPath, originalData, context.projectRoot, context.tag);
throw new Error(
`Bulk operation failed and was rolled back: ${error.message}`
);
}
} catch (error) {
if (!silent) {
log('error', `Bulk add dependencies failed: ${error.message}`);
}
return {
success: false,
error: error.message
};
}
}
if (!silent && !isSilentMode()) {
console.log(
boxen(
chalk.blue(`Bulk Dependency Addition - DRY RUN\n\n`) +
`${chalk.cyan('Tasks:')} ${taskIds.join(', ')}\n` +
`${chalk.cyan('Dependencies:')} ${dependencyIds.join(', ')}\n\n` +
`${chalk.cyan('Valid operations:')} ${validOperations}\n` +
`${chalk.cyan('Validation errors:')} ${validationErrors.length}\n\n` +
`${chalk.yellow('Re-run without --dry-run to apply changes')}`,
{
padding: 1,
borderColor: 'blue',
borderStyle: 'round',
margin: { top: 1 }
}
)
);
}
🤖 Prompt for AI Agents
In scripts/modules/dependency-manager.js around lines 2040 to 2330, the dry-run
box message currently hardcodes "Use --confirm to apply changes"; change this to
use the actual CLI flag or runtime-config value instead of a literal string so
the hint always matches the CLI (e.g., reference the module/constant that
defines the confirm/apply flag or use an options.confirmFlag value if provided,
falling back to "--confirm" only as a last resort), update the box text
accordingly, and ensure any referenced symbol is imported or passed in so the
message remains correct and testable.

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.

3 participants