#
tokens: 38085/50000 2/975 files (page 53/69)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 53 of 69. Use http://codebase.md/eyaltoledano/claude-task-master?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .changeset
│   ├── config.json
│   └── README.md
├── .claude
│   ├── commands
│   │   └── dedupe.md
│   └── TM_COMMANDS_GUIDE.md
├── .claude-plugin
│   └── marketplace.json
├── .coderabbit.yaml
├── .cursor
│   ├── mcp.json
│   └── rules
│       ├── ai_providers.mdc
│       ├── ai_services.mdc
│       ├── architecture.mdc
│       ├── changeset.mdc
│       ├── commands.mdc
│       ├── context_gathering.mdc
│       ├── cursor_rules.mdc
│       ├── dependencies.mdc
│       ├── dev_workflow.mdc
│       ├── git_workflow.mdc
│       ├── glossary.mdc
│       ├── mcp.mdc
│       ├── new_features.mdc
│       ├── self_improve.mdc
│       ├── tags.mdc
│       ├── taskmaster.mdc
│       ├── tasks.mdc
│       ├── telemetry.mdc
│       ├── test_workflow.mdc
│       ├── tests.mdc
│       ├── ui.mdc
│       └── utilities.mdc
├── .cursorignore
├── .env.example
├── .github
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   ├── enhancements---feature-requests.md
│   │   └── feedback.md
│   ├── PULL_REQUEST_TEMPLATE
│   │   ├── bugfix.md
│   │   ├── config.yml
│   │   ├── feature.md
│   │   └── integration.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── scripts
│   │   ├── auto-close-duplicates.mjs
│   │   ├── backfill-duplicate-comments.mjs
│   │   ├── check-pre-release-mode.mjs
│   │   ├── parse-metrics.mjs
│   │   ├── release.mjs
│   │   ├── tag-extension.mjs
│   │   ├── utils.mjs
│   │   └── validate-changesets.mjs
│   └── workflows
│       ├── auto-close-duplicates.yml
│       ├── backfill-duplicate-comments.yml
│       ├── ci.yml
│       ├── claude-dedupe-issues.yml
│       ├── claude-docs-trigger.yml
│       ├── claude-docs-updater.yml
│       ├── claude-issue-triage.yml
│       ├── claude.yml
│       ├── extension-ci.yml
│       ├── extension-release.yml
│       ├── log-issue-events.yml
│       ├── pre-release.yml
│       ├── release-check.yml
│       ├── release.yml
│       ├── update-models-md.yml
│       └── weekly-metrics-discord.yml
├── .gitignore
├── .kiro
│   ├── hooks
│   │   ├── tm-code-change-task-tracker.kiro.hook
│   │   ├── tm-complexity-analyzer.kiro.hook
│   │   ├── tm-daily-standup-assistant.kiro.hook
│   │   ├── tm-git-commit-task-linker.kiro.hook
│   │   ├── tm-pr-readiness-checker.kiro.hook
│   │   ├── tm-task-dependency-auto-progression.kiro.hook
│   │   └── tm-test-success-task-completer.kiro.hook
│   ├── settings
│   │   └── mcp.json
│   └── steering
│       ├── dev_workflow.md
│       ├── kiro_rules.md
│       ├── self_improve.md
│       ├── taskmaster_hooks_workflow.md
│       └── taskmaster.md
├── .manypkg.json
├── .mcp.json
├── .npmignore
├── .nvmrc
├── .taskmaster
│   ├── CLAUDE.md
│   ├── config.json
│   ├── docs
│   │   ├── autonomous-tdd-git-workflow.md
│   │   ├── MIGRATION-ROADMAP.md
│   │   ├── prd-tm-start.txt
│   │   ├── prd.txt
│   │   ├── README.md
│   │   ├── research
│   │   │   ├── 2025-06-14_how-can-i-improve-the-scope-up-and-scope-down-comm.md
│   │   │   ├── 2025-06-14_should-i-be-using-any-specific-libraries-for-this.md
│   │   │   ├── 2025-06-14_test-save-functionality.md
│   │   │   ├── 2025-06-14_test-the-fix-for-duplicate-saves-final-test.md
│   │   │   └── 2025-08-01_do-we-need-to-add-new-commands-or-can-we-just-weap.md
│   │   ├── task-template-importing-prd.txt
│   │   ├── tdd-workflow-phase-0-spike.md
│   │   ├── tdd-workflow-phase-1-core-rails.md
│   │   ├── tdd-workflow-phase-1-orchestrator.md
│   │   ├── tdd-workflow-phase-2-pr-resumability.md
│   │   ├── tdd-workflow-phase-3-extensibility-guardrails.md
│   │   ├── test-prd.txt
│   │   └── tm-core-phase-1.txt
│   ├── reports
│   │   ├── task-complexity-report_autonomous-tdd-git-workflow.json
│   │   ├── task-complexity-report_cc-kiro-hooks.json
│   │   ├── task-complexity-report_tdd-phase-1-core-rails.json
│   │   ├── task-complexity-report_tdd-workflow-phase-0.json
│   │   ├── task-complexity-report_test-prd-tag.json
│   │   ├── task-complexity-report_tm-core-phase-1.json
│   │   ├── task-complexity-report.json
│   │   └── tm-core-complexity.json
│   ├── state.json
│   ├── tasks
│   │   ├── task_001_tm-start.txt
│   │   ├── task_002_tm-start.txt
│   │   ├── task_003_tm-start.txt
│   │   ├── task_004_tm-start.txt
│   │   ├── task_007_tm-start.txt
│   │   └── tasks.json
│   └── templates
│       ├── example_prd_rpg.md
│       └── example_prd.md
├── .vscode
│   ├── extensions.json
│   └── settings.json
├── apps
│   ├── cli
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── command-registry.ts
│   │   │   ├── commands
│   │   │   │   ├── auth.command.ts
│   │   │   │   ├── autopilot
│   │   │   │   │   ├── abort.command.ts
│   │   │   │   │   ├── commit.command.ts
│   │   │   │   │   ├── complete.command.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── next.command.ts
│   │   │   │   │   ├── resume.command.ts
│   │   │   │   │   ├── shared.ts
│   │   │   │   │   ├── start.command.ts
│   │   │   │   │   └── status.command.ts
│   │   │   │   ├── briefs.command.ts
│   │   │   │   ├── context.command.ts
│   │   │   │   ├── export.command.ts
│   │   │   │   ├── list.command.ts
│   │   │   │   ├── models
│   │   │   │   │   ├── custom-providers.ts
│   │   │   │   │   ├── fetchers.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── prompts.ts
│   │   │   │   │   ├── setup.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── next.command.ts
│   │   │   │   ├── set-status.command.ts
│   │   │   │   ├── show.command.ts
│   │   │   │   ├── start.command.ts
│   │   │   │   └── tags.command.ts
│   │   │   ├── index.ts
│   │   │   ├── lib
│   │   │   │   └── model-management.ts
│   │   │   ├── types
│   │   │   │   └── tag-management.d.ts
│   │   │   ├── ui
│   │   │   │   ├── components
│   │   │   │   │   ├── cardBox.component.ts
│   │   │   │   │   ├── dashboard.component.ts
│   │   │   │   │   ├── header.component.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── next-task.component.ts
│   │   │   │   │   ├── suggested-steps.component.ts
│   │   │   │   │   └── task-detail.component.ts
│   │   │   │   ├── display
│   │   │   │   │   ├── messages.ts
│   │   │   │   │   └── tables.ts
│   │   │   │   ├── formatters
│   │   │   │   │   ├── complexity-formatters.ts
│   │   │   │   │   ├── dependency-formatters.ts
│   │   │   │   │   ├── priority-formatters.ts
│   │   │   │   │   ├── status-formatters.spec.ts
│   │   │   │   │   └── status-formatters.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── layout
│   │   │   │       ├── helpers.spec.ts
│   │   │   │       └── helpers.ts
│   │   │   └── utils
│   │   │       ├── auth-helpers.ts
│   │   │       ├── auto-update.ts
│   │   │       ├── brief-selection.ts
│   │   │       ├── display-helpers.ts
│   │   │       ├── error-handler.ts
│   │   │       ├── index.ts
│   │   │       ├── project-root.ts
│   │   │       ├── task-status.ts
│   │   │       ├── ui.spec.ts
│   │   │       └── ui.ts
│   │   ├── tests
│   │   │   ├── integration
│   │   │   │   └── commands
│   │   │   │       └── autopilot
│   │   │   │           └── workflow.test.ts
│   │   │   └── unit
│   │   │       ├── commands
│   │   │       │   ├── autopilot
│   │   │       │   │   └── shared.test.ts
│   │   │       │   ├── list.command.spec.ts
│   │   │       │   └── show.command.spec.ts
│   │   │       └── ui
│   │   │           └── dashboard.component.spec.ts
│   │   ├── tsconfig.json
│   │   └── vitest.config.ts
│   ├── docs
│   │   ├── archive
│   │   │   ├── ai-client-utils-example.mdx
│   │   │   ├── ai-development-workflow.mdx
│   │   │   ├── command-reference.mdx
│   │   │   ├── configuration.mdx
│   │   │   ├── cursor-setup.mdx
│   │   │   ├── examples.mdx
│   │   │   └── Installation.mdx
│   │   ├── best-practices
│   │   │   ├── advanced-tasks.mdx
│   │   │   ├── configuration-advanced.mdx
│   │   │   └── index.mdx
│   │   ├── capabilities
│   │   │   ├── cli-root-commands.mdx
│   │   │   ├── index.mdx
│   │   │   ├── mcp.mdx
│   │   │   ├── rpg-method.mdx
│   │   │   └── task-structure.mdx
│   │   ├── CHANGELOG.md
│   │   ├── command-reference.mdx
│   │   ├── configuration.mdx
│   │   ├── docs.json
│   │   ├── favicon.svg
│   │   ├── getting-started
│   │   │   ├── api-keys.mdx
│   │   │   ├── contribute.mdx
│   │   │   ├── faq.mdx
│   │   │   └── quick-start
│   │   │       ├── configuration-quick.mdx
│   │   │       ├── execute-quick.mdx
│   │   │       ├── installation.mdx
│   │   │       ├── moving-forward.mdx
│   │   │       ├── prd-quick.mdx
│   │   │       ├── quick-start.mdx
│   │   │       ├── requirements.mdx
│   │   │       ├── rules-quick.mdx
│   │   │       └── tasks-quick.mdx
│   │   ├── introduction.mdx
│   │   ├── licensing.md
│   │   ├── logo
│   │   │   ├── dark.svg
│   │   │   ├── light.svg
│   │   │   └── task-master-logo.png
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── style.css
│   │   ├── tdd-workflow
│   │   │   ├── ai-agent-integration.mdx
│   │   │   └── quickstart.mdx
│   │   ├── vercel.json
│   │   └── whats-new.mdx
│   ├── extension
│   │   ├── .vscodeignore
│   │   ├── assets
│   │   │   ├── banner.png
│   │   │   ├── icon-dark.svg
│   │   │   ├── icon-light.svg
│   │   │   ├── icon.png
│   │   │   ├── screenshots
│   │   │   │   ├── kanban-board.png
│   │   │   │   └── task-details.png
│   │   │   └── sidebar-icon.svg
│   │   ├── CHANGELOG.md
│   │   ├── components.json
│   │   ├── docs
│   │   │   ├── extension-CI-setup.md
│   │   │   └── extension-development-guide.md
│   │   ├── esbuild.js
│   │   ├── LICENSE
│   │   ├── package.json
│   │   ├── package.mjs
│   │   ├── package.publish.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── components
│   │   │   │   ├── ConfigView.tsx
│   │   │   │   ├── constants.ts
│   │   │   │   ├── TaskDetails
│   │   │   │   │   ├── AIActionsSection.tsx
│   │   │   │   │   ├── DetailsSection.tsx
│   │   │   │   │   ├── PriorityBadge.tsx
│   │   │   │   │   ├── SubtasksSection.tsx
│   │   │   │   │   ├── TaskMetadataSidebar.tsx
│   │   │   │   │   └── useTaskDetails.ts
│   │   │   │   ├── TaskDetailsView.tsx
│   │   │   │   ├── TaskMasterLogo.tsx
│   │   │   │   └── ui
│   │   │   │       ├── badge.tsx
│   │   │   │       ├── breadcrumb.tsx
│   │   │   │       ├── button.tsx
│   │   │   │       ├── card.tsx
│   │   │   │       ├── collapsible.tsx
│   │   │   │       ├── CollapsibleSection.tsx
│   │   │   │       ├── dropdown-menu.tsx
│   │   │   │       ├── label.tsx
│   │   │   │       ├── scroll-area.tsx
│   │   │   │       ├── separator.tsx
│   │   │   │       ├── shadcn-io
│   │   │   │       │   └── kanban
│   │   │   │       │       └── index.tsx
│   │   │   │       └── textarea.tsx
│   │   │   ├── extension.ts
│   │   │   ├── index.ts
│   │   │   ├── lib
│   │   │   │   └── utils.ts
│   │   │   ├── services
│   │   │   │   ├── config-service.ts
│   │   │   │   ├── error-handler.ts
│   │   │   │   ├── notification-preferences.ts
│   │   │   │   ├── polling-service.ts
│   │   │   │   ├── polling-strategies.ts
│   │   │   │   ├── sidebar-webview-manager.ts
│   │   │   │   ├── task-repository.ts
│   │   │   │   ├── terminal-manager.ts
│   │   │   │   └── webview-manager.ts
│   │   │   ├── test
│   │   │   │   └── extension.test.ts
│   │   │   ├── utils
│   │   │   │   ├── configManager.ts
│   │   │   │   ├── connectionManager.ts
│   │   │   │   ├── errorHandler.ts
│   │   │   │   ├── event-emitter.ts
│   │   │   │   ├── logger.ts
│   │   │   │   ├── mcpClient.ts
│   │   │   │   ├── notificationPreferences.ts
│   │   │   │   └── task-master-api
│   │   │   │       ├── cache
│   │   │   │       │   └── cache-manager.ts
│   │   │   │       ├── index.ts
│   │   │   │       ├── mcp-client.ts
│   │   │   │       ├── transformers
│   │   │   │       │   └── task-transformer.ts
│   │   │   │       └── types
│   │   │   │           └── index.ts
│   │   │   └── webview
│   │   │       ├── App.tsx
│   │   │       ├── components
│   │   │       │   ├── AppContent.tsx
│   │   │       │   ├── EmptyState.tsx
│   │   │       │   ├── ErrorBoundary.tsx
│   │   │       │   ├── PollingStatus.tsx
│   │   │       │   ├── PriorityBadge.tsx
│   │   │       │   ├── SidebarView.tsx
│   │   │       │   ├── TagDropdown.tsx
│   │   │       │   ├── TaskCard.tsx
│   │   │       │   ├── TaskEditModal.tsx
│   │   │       │   ├── TaskMasterKanban.tsx
│   │   │       │   ├── ToastContainer.tsx
│   │   │       │   └── ToastNotification.tsx
│   │   │       ├── constants
│   │   │       │   └── index.ts
│   │   │       ├── contexts
│   │   │       │   └── VSCodeContext.tsx
│   │   │       ├── hooks
│   │   │       │   ├── useTaskQueries.ts
│   │   │       │   ├── useVSCodeMessages.ts
│   │   │       │   └── useWebviewHeight.ts
│   │   │       ├── index.css
│   │   │       ├── index.tsx
│   │   │       ├── providers
│   │   │       │   └── QueryProvider.tsx
│   │   │       ├── reducers
│   │   │       │   └── appReducer.ts
│   │   │       ├── sidebar.tsx
│   │   │       ├── types
│   │   │       │   └── index.ts
│   │   │       └── utils
│   │   │           ├── logger.ts
│   │   │           └── toast.ts
│   │   └── tsconfig.json
│   └── mcp
│       ├── CHANGELOG.md
│       ├── package.json
│       ├── src
│       │   ├── index.ts
│       │   ├── shared
│       │   │   ├── types.ts
│       │   │   └── utils.ts
│       │   └── tools
│       │       ├── autopilot
│       │       │   ├── abort.tool.ts
│       │       │   ├── commit.tool.ts
│       │       │   ├── complete.tool.ts
│       │       │   ├── finalize.tool.ts
│       │       │   ├── index.ts
│       │       │   ├── next.tool.ts
│       │       │   ├── resume.tool.ts
│       │       │   ├── start.tool.ts
│       │       │   └── status.tool.ts
│       │       ├── README-ZOD-V3.md
│       │       └── tasks
│       │           ├── get-task.tool.ts
│       │           ├── get-tasks.tool.ts
│       │           └── index.ts
│       ├── tsconfig.json
│       └── vitest.config.ts
├── assets
│   ├── .windsurfrules
│   ├── AGENTS.md
│   ├── claude
│   │   └── TM_COMMANDS_GUIDE.md
│   ├── config.json
│   ├── env.example
│   ├── example_prd_rpg.txt
│   ├── example_prd.txt
│   ├── GEMINI.md
│   ├── gitignore
│   ├── kiro-hooks
│   │   ├── tm-code-change-task-tracker.kiro.hook
│   │   ├── tm-complexity-analyzer.kiro.hook
│   │   ├── tm-daily-standup-assistant.kiro.hook
│   │   ├── tm-git-commit-task-linker.kiro.hook
│   │   ├── tm-pr-readiness-checker.kiro.hook
│   │   ├── tm-task-dependency-auto-progression.kiro.hook
│   │   └── tm-test-success-task-completer.kiro.hook
│   ├── roocode
│   │   ├── .roo
│   │   │   ├── rules-architect
│   │   │   │   └── architect-rules
│   │   │   ├── rules-ask
│   │   │   │   └── ask-rules
│   │   │   ├── rules-code
│   │   │   │   └── code-rules
│   │   │   ├── rules-debug
│   │   │   │   └── debug-rules
│   │   │   ├── rules-orchestrator
│   │   │   │   └── orchestrator-rules
│   │   │   └── rules-test
│   │   │       └── test-rules
│   │   └── .roomodes
│   ├── rules
│   │   ├── cursor_rules.mdc
│   │   ├── dev_workflow.mdc
│   │   ├── self_improve.mdc
│   │   ├── taskmaster_hooks_workflow.mdc
│   │   └── taskmaster.mdc
│   └── scripts_README.md
├── bin
│   └── task-master.js
├── biome.json
├── CHANGELOG.md
├── CLAUDE_CODE_PLUGIN.md
├── CLAUDE.md
├── context
│   ├── chats
│   │   ├── add-task-dependencies-1.md
│   │   └── max-min-tokens.txt.md
│   ├── fastmcp-core.txt
│   ├── fastmcp-docs.txt
│   ├── MCP_INTEGRATION.md
│   ├── mcp-js-sdk-docs.txt
│   ├── mcp-protocol-repo.txt
│   ├── mcp-protocol-schema-03262025.json
│   └── mcp-protocol-spec.txt
├── CONTRIBUTING.md
├── docs
│   ├── claude-code-integration.md
│   ├── CLI-COMMANDER-PATTERN.md
│   ├── command-reference.md
│   ├── configuration.md
│   ├── contributor-docs
│   │   ├── testing-roo-integration.md
│   │   └── worktree-setup.md
│   ├── cross-tag-task-movement.md
│   ├── examples
│   │   ├── claude-code-usage.md
│   │   └── codex-cli-usage.md
│   ├── examples.md
│   ├── licensing.md
│   ├── mcp-provider-guide.md
│   ├── mcp-provider.md
│   ├── migration-guide.md
│   ├── models.md
│   ├── providers
│   │   ├── codex-cli.md
│   │   └── gemini-cli.md
│   ├── README.md
│   ├── scripts
│   │   └── models-json-to-markdown.js
│   ├── task-structure.md
│   └── tutorial.md
├── images
│   ├── hamster-hiring.png
│   └── logo.png
├── index.js
├── jest.config.js
├── jest.resolver.cjs
├── LICENSE
├── llms-install.md
├── mcp-server
│   ├── server.js
│   └── src
│       ├── core
│       │   ├── __tests__
│       │   │   └── context-manager.test.js
│       │   ├── context-manager.js
│       │   ├── direct-functions
│       │   │   ├── add-dependency.js
│       │   │   ├── add-subtask.js
│       │   │   ├── add-tag.js
│       │   │   ├── add-task.js
│       │   │   ├── analyze-task-complexity.js
│       │   │   ├── cache-stats.js
│       │   │   ├── clear-subtasks.js
│       │   │   ├── complexity-report.js
│       │   │   ├── copy-tag.js
│       │   │   ├── create-tag-from-branch.js
│       │   │   ├── delete-tag.js
│       │   │   ├── expand-all-tasks.js
│       │   │   ├── expand-task.js
│       │   │   ├── fix-dependencies.js
│       │   │   ├── generate-task-files.js
│       │   │   ├── initialize-project.js
│       │   │   ├── list-tags.js
│       │   │   ├── models.js
│       │   │   ├── move-task-cross-tag.js
│       │   │   ├── move-task.js
│       │   │   ├── next-task.js
│       │   │   ├── parse-prd.js
│       │   │   ├── remove-dependency.js
│       │   │   ├── remove-subtask.js
│       │   │   ├── remove-task.js
│       │   │   ├── rename-tag.js
│       │   │   ├── research.js
│       │   │   ├── response-language.js
│       │   │   ├── rules.js
│       │   │   ├── scope-down.js
│       │   │   ├── scope-up.js
│       │   │   ├── set-task-status.js
│       │   │   ├── update-subtask-by-id.js
│       │   │   ├── update-task-by-id.js
│       │   │   ├── update-tasks.js
│       │   │   ├── use-tag.js
│       │   │   └── validate-dependencies.js
│       │   ├── task-master-core.js
│       │   └── utils
│       │       ├── env-utils.js
│       │       └── path-utils.js
│       ├── custom-sdk
│       │   ├── errors.js
│       │   ├── index.js
│       │   ├── json-extractor.js
│       │   ├── language-model.js
│       │   ├── message-converter.js
│       │   └── schema-converter.js
│       ├── index.js
│       ├── logger.js
│       ├── providers
│       │   └── mcp-provider.js
│       └── tools
│           ├── add-dependency.js
│           ├── add-subtask.js
│           ├── add-tag.js
│           ├── add-task.js
│           ├── analyze.js
│           ├── clear-subtasks.js
│           ├── complexity-report.js
│           ├── copy-tag.js
│           ├── delete-tag.js
│           ├── expand-all.js
│           ├── expand-task.js
│           ├── fix-dependencies.js
│           ├── generate.js
│           ├── get-operation-status.js
│           ├── index.js
│           ├── initialize-project.js
│           ├── list-tags.js
│           ├── models.js
│           ├── move-task.js
│           ├── next-task.js
│           ├── parse-prd.js
│           ├── README-ZOD-V3.md
│           ├── remove-dependency.js
│           ├── remove-subtask.js
│           ├── remove-task.js
│           ├── rename-tag.js
│           ├── research.js
│           ├── response-language.js
│           ├── rules.js
│           ├── scope-down.js
│           ├── scope-up.js
│           ├── set-task-status.js
│           ├── tool-registry.js
│           ├── update-subtask.js
│           ├── update-task.js
│           ├── update.js
│           ├── use-tag.js
│           ├── utils.js
│           └── validate-dependencies.js
├── mcp-test.js
├── output.json
├── package-lock.json
├── package.json
├── packages
│   ├── ai-sdk-provider-grok-cli
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── errors.test.ts
│   │   │   ├── errors.ts
│   │   │   ├── grok-cli-language-model.ts
│   │   │   ├── grok-cli-provider.test.ts
│   │   │   ├── grok-cli-provider.ts
│   │   │   ├── index.ts
│   │   │   ├── json-extractor.test.ts
│   │   │   ├── json-extractor.ts
│   │   │   ├── message-converter.test.ts
│   │   │   ├── message-converter.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   ├── build-config
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src
│   │   │   └── tsdown.base.ts
│   │   └── tsconfig.json
│   ├── claude-code-plugin
│   │   ├── .claude-plugin
│   │   │   └── plugin.json
│   │   ├── .gitignore
│   │   ├── agents
│   │   │   ├── task-checker.md
│   │   │   ├── task-executor.md
│   │   │   └── task-orchestrator.md
│   │   ├── CHANGELOG.md
│   │   ├── commands
│   │   │   ├── add-dependency.md
│   │   │   ├── add-subtask.md
│   │   │   ├── add-task.md
│   │   │   ├── analyze-complexity.md
│   │   │   ├── analyze-project.md
│   │   │   ├── auto-implement-tasks.md
│   │   │   ├── command-pipeline.md
│   │   │   ├── complexity-report.md
│   │   │   ├── convert-task-to-subtask.md
│   │   │   ├── expand-all-tasks.md
│   │   │   ├── expand-task.md
│   │   │   ├── fix-dependencies.md
│   │   │   ├── generate-tasks.md
│   │   │   ├── help.md
│   │   │   ├── init-project-quick.md
│   │   │   ├── init-project.md
│   │   │   ├── install-taskmaster.md
│   │   │   ├── learn.md
│   │   │   ├── list-tasks-by-status.md
│   │   │   ├── list-tasks-with-subtasks.md
│   │   │   ├── list-tasks.md
│   │   │   ├── next-task.md
│   │   │   ├── parse-prd-with-research.md
│   │   │   ├── parse-prd.md
│   │   │   ├── project-status.md
│   │   │   ├── quick-install-taskmaster.md
│   │   │   ├── remove-all-subtasks.md
│   │   │   ├── remove-dependency.md
│   │   │   ├── remove-subtask.md
│   │   │   ├── remove-subtasks.md
│   │   │   ├── remove-task.md
│   │   │   ├── setup-models.md
│   │   │   ├── show-task.md
│   │   │   ├── smart-workflow.md
│   │   │   ├── sync-readme.md
│   │   │   ├── tm-main.md
│   │   │   ├── to-cancelled.md
│   │   │   ├── to-deferred.md
│   │   │   ├── to-done.md
│   │   │   ├── to-in-progress.md
│   │   │   ├── to-pending.md
│   │   │   ├── to-review.md
│   │   │   ├── update-single-task.md
│   │   │   ├── update-task.md
│   │   │   ├── update-tasks-from-id.md
│   │   │   ├── validate-dependencies.md
│   │   │   └── view-models.md
│   │   ├── mcp.json
│   │   └── package.json
│   ├── tm-bridge
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── add-tag-bridge.ts
│   │   │   ├── bridge-types.ts
│   │   │   ├── bridge-utils.ts
│   │   │   ├── expand-bridge.ts
│   │   │   ├── index.ts
│   │   │   ├── tags-bridge.ts
│   │   │   ├── update-bridge.ts
│   │   │   └── use-tag-bridge.ts
│   │   └── tsconfig.json
│   └── tm-core
│       ├── .gitignore
│       ├── CHANGELOG.md
│       ├── docs
│       │   └── listTasks-architecture.md
│       ├── package.json
│       ├── POC-STATUS.md
│       ├── README.md
│       ├── src
│       │   ├── common
│       │   │   ├── constants
│       │   │   │   ├── index.ts
│       │   │   │   ├── paths.ts
│       │   │   │   └── providers.ts
│       │   │   ├── errors
│       │   │   │   ├── index.ts
│       │   │   │   └── task-master-error.ts
│       │   │   ├── interfaces
│       │   │   │   ├── configuration.interface.ts
│       │   │   │   ├── index.ts
│       │   │   │   └── storage.interface.ts
│       │   │   ├── logger
│       │   │   │   ├── factory.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── logger.spec.ts
│       │   │   │   └── logger.ts
│       │   │   ├── mappers
│       │   │   │   ├── TaskMapper.test.ts
│       │   │   │   └── TaskMapper.ts
│       │   │   ├── types
│       │   │   │   ├── database.types.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── legacy.ts
│       │   │   │   └── repository-types.ts
│       │   │   └── utils
│       │   │       ├── git-utils.ts
│       │   │       ├── id-generator.ts
│       │   │       ├── index.ts
│       │   │       ├── path-helpers.ts
│       │   │       ├── path-normalizer.spec.ts
│       │   │       ├── path-normalizer.ts
│       │   │       ├── project-root-finder.spec.ts
│       │   │       ├── project-root-finder.ts
│       │   │       ├── run-id-generator.spec.ts
│       │   │       └── run-id-generator.ts
│       │   ├── index.ts
│       │   ├── modules
│       │   │   ├── ai
│       │   │   │   ├── index.ts
│       │   │   │   ├── interfaces
│       │   │   │   │   └── ai-provider.interface.ts
│       │   │   │   └── providers
│       │   │   │       ├── base-provider.ts
│       │   │   │       └── index.ts
│       │   │   ├── auth
│       │   │   │   ├── auth-domain.spec.ts
│       │   │   │   ├── auth-domain.ts
│       │   │   │   ├── config.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── managers
│       │   │   │   │   ├── auth-manager.spec.ts
│       │   │   │   │   └── auth-manager.ts
│       │   │   │   ├── services
│       │   │   │   │   ├── context-store.ts
│       │   │   │   │   ├── oauth-service.ts
│       │   │   │   │   ├── organization.service.ts
│       │   │   │   │   ├── supabase-session-storage.spec.ts
│       │   │   │   │   └── supabase-session-storage.ts
│       │   │   │   └── types.ts
│       │   │   ├── briefs
│       │   │   │   ├── briefs-domain.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── services
│       │   │   │   │   └── brief-service.ts
│       │   │   │   ├── types.ts
│       │   │   │   └── utils
│       │   │   │       └── url-parser.ts
│       │   │   ├── commands
│       │   │   │   └── index.ts
│       │   │   ├── config
│       │   │   │   ├── config-domain.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── managers
│       │   │   │   │   ├── config-manager.spec.ts
│       │   │   │   │   └── config-manager.ts
│       │   │   │   └── services
│       │   │   │       ├── config-loader.service.spec.ts
│       │   │   │       ├── config-loader.service.ts
│       │   │   │       ├── config-merger.service.spec.ts
│       │   │   │       ├── config-merger.service.ts
│       │   │   │       ├── config-persistence.service.spec.ts
│       │   │   │       ├── config-persistence.service.ts
│       │   │   │       ├── environment-config-provider.service.spec.ts
│       │   │   │       ├── environment-config-provider.service.ts
│       │   │   │       ├── index.ts
│       │   │   │       ├── runtime-state-manager.service.spec.ts
│       │   │   │       └── runtime-state-manager.service.ts
│       │   │   ├── dependencies
│       │   │   │   └── index.ts
│       │   │   ├── execution
│       │   │   │   ├── executors
│       │   │   │   │   ├── base-executor.ts
│       │   │   │   │   ├── claude-executor.ts
│       │   │   │   │   └── executor-factory.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── services
│       │   │   │   │   └── executor-service.ts
│       │   │   │   └── types.ts
│       │   │   ├── git
│       │   │   │   ├── adapters
│       │   │   │   │   ├── git-adapter.test.ts
│       │   │   │   │   └── git-adapter.ts
│       │   │   │   ├── git-domain.ts
│       │   │   │   ├── index.ts
│       │   │   │   └── services
│       │   │   │       ├── branch-name-generator.spec.ts
│       │   │   │       ├── branch-name-generator.ts
│       │   │   │       ├── commit-message-generator.test.ts
│       │   │   │       ├── commit-message-generator.ts
│       │   │   │       ├── scope-detector.test.ts
│       │   │   │       ├── scope-detector.ts
│       │   │   │       ├── template-engine.test.ts
│       │   │   │       └── template-engine.ts
│       │   │   ├── integration
│       │   │   │   ├── clients
│       │   │   │   │   ├── index.ts
│       │   │   │   │   └── supabase-client.ts
│       │   │   │   ├── integration-domain.ts
│       │   │   │   └── services
│       │   │   │       ├── export.service.ts
│       │   │   │       ├── task-expansion.service.ts
│       │   │   │       └── task-retrieval.service.ts
│       │   │   ├── reports
│       │   │   │   ├── index.ts
│       │   │   │   ├── managers
│       │   │   │   │   └── complexity-report-manager.ts
│       │   │   │   └── types.ts
│       │   │   ├── storage
│       │   │   │   ├── adapters
│       │   │   │   │   ├── activity-logger.ts
│       │   │   │   │   ├── api-storage.ts
│       │   │   │   │   └── file-storage
│       │   │   │   │       ├── file-operations.ts
│       │   │   │   │       ├── file-storage.ts
│       │   │   │   │       ├── format-handler.ts
│       │   │   │   │       ├── index.ts
│       │   │   │   │       └── path-resolver.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── services
│       │   │   │   │   └── storage-factory.ts
│       │   │   │   └── utils
│       │   │   │       └── api-client.ts
│       │   │   ├── tasks
│       │   │   │   ├── entities
│       │   │   │   │   └── task.entity.ts
│       │   │   │   ├── parser
│       │   │   │   │   └── index.ts
│       │   │   │   ├── repositories
│       │   │   │   │   ├── supabase
│       │   │   │   │   │   ├── dependency-fetcher.ts
│       │   │   │   │   │   ├── index.ts
│       │   │   │   │   │   └── supabase-repository.ts
│       │   │   │   │   └── task-repository.interface.ts
│       │   │   │   ├── services
│       │   │   │   │   ├── preflight-checker.service.ts
│       │   │   │   │   ├── tag.service.ts
│       │   │   │   │   ├── task-execution-service.ts
│       │   │   │   │   ├── task-loader.service.ts
│       │   │   │   │   └── task-service.ts
│       │   │   │   └── tasks-domain.ts
│       │   │   ├── ui
│       │   │   │   └── index.ts
│       │   │   └── workflow
│       │   │       ├── managers
│       │   │       │   ├── workflow-state-manager.spec.ts
│       │   │       │   └── workflow-state-manager.ts
│       │   │       ├── orchestrators
│       │   │       │   ├── workflow-orchestrator.test.ts
│       │   │       │   └── workflow-orchestrator.ts
│       │   │       ├── services
│       │   │       │   ├── test-result-validator.test.ts
│       │   │       │   ├── test-result-validator.ts
│       │   │       │   ├── test-result-validator.types.ts
│       │   │       │   ├── workflow-activity-logger.ts
│       │   │       │   └── workflow.service.ts
│       │   │       ├── types.ts
│       │   │       └── workflow-domain.ts
│       │   ├── subpath-exports.test.ts
│       │   ├── tm-core.ts
│       │   └── utils
│       │       └── time.utils.ts
│       ├── tests
│       │   ├── auth
│       │   │   └── auth-refresh.test.ts
│       │   ├── integration
│       │   │   ├── auth-token-refresh.test.ts
│       │   │   ├── list-tasks.test.ts
│       │   │   └── storage
│       │   │       └── activity-logger.test.ts
│       │   ├── mocks
│       │   │   └── mock-provider.ts
│       │   ├── setup.ts
│       │   └── unit
│       │       ├── base-provider.test.ts
│       │       ├── executor.test.ts
│       │       └── smoke.test.ts
│       ├── tsconfig.json
│       └── vitest.config.ts
├── README-task-master.md
├── README.md
├── scripts
│   ├── create-worktree.sh
│   ├── dev.js
│   ├── init.js
│   ├── list-worktrees.sh
│   ├── modules
│   │   ├── ai-services-unified.js
│   │   ├── bridge-utils.js
│   │   ├── commands.js
│   │   ├── config-manager.js
│   │   ├── dependency-manager.js
│   │   ├── index.js
│   │   ├── prompt-manager.js
│   │   ├── supported-models.json
│   │   ├── sync-readme.js
│   │   ├── task-manager
│   │   │   ├── add-subtask.js
│   │   │   ├── add-task.js
│   │   │   ├── analyze-task-complexity.js
│   │   │   ├── clear-subtasks.js
│   │   │   ├── expand-all-tasks.js
│   │   │   ├── expand-task.js
│   │   │   ├── find-next-task.js
│   │   │   ├── generate-task-files.js
│   │   │   ├── is-task-dependent.js
│   │   │   ├── list-tasks.js
│   │   │   ├── migrate.js
│   │   │   ├── models.js
│   │   │   ├── move-task.js
│   │   │   ├── parse-prd
│   │   │   │   ├── index.js
│   │   │   │   ├── parse-prd-config.js
│   │   │   │   ├── parse-prd-helpers.js
│   │   │   │   ├── parse-prd-non-streaming.js
│   │   │   │   ├── parse-prd-streaming.js
│   │   │   │   └── parse-prd.js
│   │   │   ├── remove-subtask.js
│   │   │   ├── remove-task.js
│   │   │   ├── research.js
│   │   │   ├── response-language.js
│   │   │   ├── scope-adjustment.js
│   │   │   ├── set-task-status.js
│   │   │   ├── tag-management.js
│   │   │   ├── task-exists.js
│   │   │   ├── update-single-task-status.js
│   │   │   ├── update-subtask-by-id.js
│   │   │   ├── update-task-by-id.js
│   │   │   └── update-tasks.js
│   │   ├── task-manager.js
│   │   ├── ui.js
│   │   ├── update-config-tokens.js
│   │   ├── utils
│   │   │   ├── contextGatherer.js
│   │   │   ├── fuzzyTaskSearch.js
│   │   │   └── git-utils.js
│   │   └── utils.js
│   ├── task-complexity-report.json
│   ├── test-claude-errors.js
│   └── test-claude.js
├── sonar-project.properties
├── src
│   ├── ai-providers
│   │   ├── anthropic.js
│   │   ├── azure.js
│   │   ├── base-provider.js
│   │   ├── bedrock.js
│   │   ├── claude-code.js
│   │   ├── codex-cli.js
│   │   ├── gemini-cli.js
│   │   ├── google-vertex.js
│   │   ├── google.js
│   │   ├── grok-cli.js
│   │   ├── groq.js
│   │   ├── index.js
│   │   ├── lmstudio.js
│   │   ├── ollama.js
│   │   ├── openai-compatible.js
│   │   ├── openai.js
│   │   ├── openrouter.js
│   │   ├── perplexity.js
│   │   ├── xai.js
│   │   ├── zai-coding.js
│   │   └── zai.js
│   ├── constants
│   │   ├── commands.js
│   │   ├── paths.js
│   │   ├── profiles.js
│   │   ├── rules-actions.js
│   │   ├── task-priority.js
│   │   └── task-status.js
│   ├── profiles
│   │   ├── amp.js
│   │   ├── base-profile.js
│   │   ├── claude.js
│   │   ├── cline.js
│   │   ├── codex.js
│   │   ├── cursor.js
│   │   ├── gemini.js
│   │   ├── index.js
│   │   ├── kilo.js
│   │   ├── kiro.js
│   │   ├── opencode.js
│   │   ├── roo.js
│   │   ├── trae.js
│   │   ├── vscode.js
│   │   ├── windsurf.js
│   │   └── zed.js
│   ├── progress
│   │   ├── base-progress-tracker.js
│   │   ├── cli-progress-factory.js
│   │   ├── parse-prd-tracker.js
│   │   ├── progress-tracker-builder.js
│   │   └── tracker-ui.js
│   ├── prompts
│   │   ├── add-task.json
│   │   ├── analyze-complexity.json
│   │   ├── expand-task.json
│   │   ├── parse-prd.json
│   │   ├── README.md
│   │   ├── research.json
│   │   ├── schemas
│   │   │   ├── parameter.schema.json
│   │   │   ├── prompt-template.schema.json
│   │   │   ├── README.md
│   │   │   └── variant.schema.json
│   │   ├── update-subtask.json
│   │   ├── update-task.json
│   │   └── update-tasks.json
│   ├── provider-registry
│   │   └── index.js
│   ├── schemas
│   │   ├── add-task.js
│   │   ├── analyze-complexity.js
│   │   ├── base-schemas.js
│   │   ├── expand-task.js
│   │   ├── parse-prd.js
│   │   ├── registry.js
│   │   ├── update-subtask.js
│   │   ├── update-task.js
│   │   └── update-tasks.js
│   ├── task-master.js
│   ├── ui
│   │   ├── confirm.js
│   │   ├── indicators.js
│   │   └── parse-prd.js
│   └── utils
│       ├── asset-resolver.js
│       ├── create-mcp-config.js
│       ├── format.js
│       ├── getVersion.js
│       ├── logger-utils.js
│       ├── manage-gitignore.js
│       ├── path-utils.js
│       ├── profiles.js
│       ├── rule-transformer.js
│       ├── stream-parser.js
│       └── timeout-manager.js
├── test-clean-tags.js
├── test-config-manager.js
├── test-prd.txt
├── test-tag-functions.js
├── test-version-check-full.js
├── test-version-check.js
├── tests
│   ├── e2e
│   │   ├── e2e_helpers.sh
│   │   ├── parse_llm_output.cjs
│   │   ├── run_e2e.sh
│   │   ├── run_fallback_verification.sh
│   │   └── test_llm_analysis.sh
│   ├── fixtures
│   │   ├── .taskmasterconfig
│   │   ├── sample-claude-response.js
│   │   ├── sample-prd.txt
│   │   └── sample-tasks.js
│   ├── helpers
│   │   └── tool-counts.js
│   ├── integration
│   │   ├── claude-code-error-handling.test.js
│   │   ├── claude-code-optional.test.js
│   │   ├── cli
│   │   │   ├── commands.test.js
│   │   │   ├── complex-cross-tag-scenarios.test.js
│   │   │   └── move-cross-tag.test.js
│   │   ├── manage-gitignore.test.js
│   │   ├── mcp-server
│   │   │   └── direct-functions.test.js
│   │   ├── move-task-cross-tag.integration.test.js
│   │   ├── move-task-simple.integration.test.js
│   │   ├── profiles
│   │   │   ├── amp-init-functionality.test.js
│   │   │   ├── claude-init-functionality.test.js
│   │   │   ├── cline-init-functionality.test.js
│   │   │   ├── codex-init-functionality.test.js
│   │   │   ├── cursor-init-functionality.test.js
│   │   │   ├── gemini-init-functionality.test.js
│   │   │   ├── opencode-init-functionality.test.js
│   │   │   ├── roo-files-inclusion.test.js
│   │   │   ├── roo-init-functionality.test.js
│   │   │   ├── rules-files-inclusion.test.js
│   │   │   ├── trae-init-functionality.test.js
│   │   │   ├── vscode-init-functionality.test.js
│   │   │   └── windsurf-init-functionality.test.js
│   │   └── providers
│   │       └── temperature-support.test.js
│   ├── manual
│   │   ├── progress
│   │   │   ├── parse-prd-analysis.js
│   │   │   ├── test-parse-prd.js
│   │   │   └── TESTING_GUIDE.md
│   │   └── prompts
│   │       ├── prompt-test.js
│   │       └── README.md
│   ├── README.md
│   ├── setup.js
│   └── unit
│       ├── ai-providers
│       │   ├── base-provider.test.js
│       │   ├── claude-code.test.js
│       │   ├── codex-cli.test.js
│       │   ├── gemini-cli.test.js
│       │   ├── lmstudio.test.js
│       │   ├── mcp-components.test.js
│       │   ├── openai-compatible.test.js
│       │   ├── openai.test.js
│       │   ├── provider-registry.test.js
│       │   ├── zai-coding.test.js
│       │   ├── zai-provider.test.js
│       │   ├── zai-schema-introspection.test.js
│       │   └── zai.test.js
│       ├── ai-services-unified.test.js
│       ├── commands.test.js
│       ├── config-manager.test.js
│       ├── config-manager.test.mjs
│       ├── dependency-manager.test.js
│       ├── init.test.js
│       ├── initialize-project.test.js
│       ├── kebab-case-validation.test.js
│       ├── manage-gitignore.test.js
│       ├── mcp
│       │   └── tools
│       │       ├── __mocks__
│       │       │   └── move-task.js
│       │       ├── add-task.test.js
│       │       ├── analyze-complexity.test.js
│       │       ├── expand-all.test.js
│       │       ├── get-tasks.test.js
│       │       ├── initialize-project.test.js
│       │       ├── move-task-cross-tag-options.test.js
│       │       ├── move-task-cross-tag.test.js
│       │       ├── remove-task.test.js
│       │       └── tool-registration.test.js
│       ├── mcp-providers
│       │   ├── mcp-components.test.js
│       │   └── mcp-provider.test.js
│       ├── parse-prd.test.js
│       ├── profiles
│       │   ├── amp-integration.test.js
│       │   ├── claude-integration.test.js
│       │   ├── cline-integration.test.js
│       │   ├── codex-integration.test.js
│       │   ├── cursor-integration.test.js
│       │   ├── gemini-integration.test.js
│       │   ├── kilo-integration.test.js
│       │   ├── kiro-integration.test.js
│       │   ├── mcp-config-validation.test.js
│       │   ├── opencode-integration.test.js
│       │   ├── profile-safety-check.test.js
│       │   ├── roo-integration.test.js
│       │   ├── rule-transformer-cline.test.js
│       │   ├── rule-transformer-cursor.test.js
│       │   ├── rule-transformer-gemini.test.js
│       │   ├── rule-transformer-kilo.test.js
│       │   ├── rule-transformer-kiro.test.js
│       │   ├── rule-transformer-opencode.test.js
│       │   ├── rule-transformer-roo.test.js
│       │   ├── rule-transformer-trae.test.js
│       │   ├── rule-transformer-vscode.test.js
│       │   ├── rule-transformer-windsurf.test.js
│       │   ├── rule-transformer-zed.test.js
│       │   ├── rule-transformer.test.js
│       │   ├── selective-profile-removal.test.js
│       │   ├── subdirectory-support.test.js
│       │   ├── trae-integration.test.js
│       │   ├── vscode-integration.test.js
│       │   ├── windsurf-integration.test.js
│       │   └── zed-integration.test.js
│       ├── progress
│       │   └── base-progress-tracker.test.js
│       ├── prompt-manager.test.js
│       ├── prompts
│       │   ├── expand-task-prompt.test.js
│       │   └── prompt-migration.test.js
│       ├── scripts
│       │   └── modules
│       │       ├── commands
│       │       │   ├── move-cross-tag.test.js
│       │       │   └── README.md
│       │       ├── dependency-manager
│       │       │   ├── circular-dependencies.test.js
│       │       │   ├── cross-tag-dependencies.test.js
│       │       │   └── fix-dependencies-command.test.js
│       │       ├── task-manager
│       │       │   ├── add-subtask.test.js
│       │       │   ├── add-task.test.js
│       │       │   ├── analyze-task-complexity.test.js
│       │       │   ├── clear-subtasks.test.js
│       │       │   ├── complexity-report-tag-isolation.test.js
│       │       │   ├── expand-all-tasks.test.js
│       │       │   ├── expand-task.test.js
│       │       │   ├── find-next-task.test.js
│       │       │   ├── generate-task-files.test.js
│       │       │   ├── list-tasks.test.js
│       │       │   ├── models-baseurl.test.js
│       │       │   ├── move-task-cross-tag.test.js
│       │       │   ├── move-task.test.js
│       │       │   ├── parse-prd-schema.test.js
│       │       │   ├── parse-prd.test.js
│       │       │   ├── remove-subtask.test.js
│       │       │   ├── remove-task.test.js
│       │       │   ├── research.test.js
│       │       │   ├── scope-adjustment.test.js
│       │       │   ├── set-task-status.test.js
│       │       │   ├── setup.js
│       │       │   ├── update-single-task-status.test.js
│       │       │   ├── update-subtask-by-id.test.js
│       │       │   ├── update-task-by-id.test.js
│       │       │   └── update-tasks.test.js
│       │       ├── ui
│       │       │   └── cross-tag-error-display.test.js
│       │       └── utils-tag-aware-paths.test.js
│       ├── task-finder.test.js
│       ├── task-manager
│       │   ├── clear-subtasks.test.js
│       │   ├── move-task.test.js
│       │   ├── tag-boundary.test.js
│       │   └── tag-management.test.js
│       ├── task-master.test.js
│       ├── ui
│       │   └── indicators.test.js
│       ├── ui.test.js
│       ├── utils-strip-ansi.test.js
│       └── utils.test.js
├── tsconfig.json
├── tsdown.config.ts
├── turbo.json
└── update-task-migration-plan.md
```

# Files

--------------------------------------------------------------------------------
/.taskmaster/docs/autonomous-tdd-git-workflow.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Summary
  2 | 
  3 | - Put the existing git and test workflows on rails: a repeatable, automated process that can run autonomously, with guardrails and a compact TUI for visibility.
  4 | 
  5 | - Flow: for a selected task, create a branch named with the tag + task id → generate tests for the first subtask (red) using the Surgical Test Generator → implement code (green) → verify tests → commit → repeat per subtask → final verify → push → open PR against the default branch.
  6 | 
  7 | - Build on existing rules: .cursor/rules/git_workflow.mdc, .cursor/rules/test_workflow.mdc, .claude/agents/surgical-test-generator.md, and existing CLI/core services.
  8 | 
  9 | ## Goals
 10 | 
 11 | - Deterministic, resumable automation to execute the TDD loop per subtask with minimal human intervention.
 12 | 
 13 | - Strong guardrails: never commit to the default branch; only commit when tests pass; enforce status transitions; persist logs/state for debuggability.
 14 | 
 15 | - Visibility: a compact terminal UI (like lazygit) to pick tag, view tasks, and start work; right-side pane opens an executor terminal (via tmux) for agent coding.
 16 | 
 17 | - Extensible: framework-agnostic test generation via the Surgical Test Generator; detect and use the repo’s test command for execution with coverage thresholds.
 18 | 
 19 | ## Non‑Goals (initial)
 20 | 
 21 | - Full multi-language runner parity beyond detection and executing the project’s test command.
 22 | 
 23 | - Complex GUI; start with CLI/TUI + tmux pane. IDE/extension can hook into the same state later.
 24 | 
 25 | - Rich executor selection UX (codex/gemini/claude) — we’ll prompt per run; defaults can come later.
 26 | 
 27 | ## Success Criteria
 28 | 
 29 | - One command can autonomously complete a task's subtasks via TDD and open a PR when done.
 30 | 
 31 | - All commits made on a branch that includes the tag and task id (see Branch Naming); no commits to the default branch directly.
 32 | 
 33 | - Every subtask iteration: failing tests added first (red), then code added to pass them (green), commit only after green.
 34 | 
 35 | - End-to-end logs + artifacts stored in .taskmaster/reports/runs/<timestamp-or-id>/.
 36 | 
 37 | ## Success Metrics (Phase 1)
 38 | 
 39 | - **Adoption**: 80% of tasks in a pilot repo completed via `tm autopilot`
 40 | - **Safety**: 0 commits to default branch; 100% of commits have green tests
 41 | - **Efficiency**: Average time from task start to PR < 30min for simple subtasks
 42 | - **Reliability**: < 5% of runs require manual intervention (timeout/conflicts)
 43 | 
 44 | ## User Stories
 45 | 
 46 | - As a developer, I can run tm autopilot <taskId> and watch a structured, safe workflow execute.
 47 | 
 48 | - As a reviewer, I can inspect commits per subtask, and a PR summarizing the work when the task completes.
 49 | 
 50 | - As an operator, I can see current step, active subtask, tests status, and logs in a compact CLI view and read a final run report.
 51 | 
 52 | ## Example Workflow Traces
 53 | 
 54 | ### Happy Path: Complete a 3-subtask feature
 55 | 
 56 | ```bash
 57 | # Developer starts
 58 | $ tm autopilot 42
 59 | → Checks preflight: ✓ clean tree, ✓ npm test detected
 60 | → Creates branch: analytics/task-42-user-metrics
 61 | → Subtask 42.1: "Add metrics schema"
 62 |   RED: generates test_metrics_schema.test.js → 3 failures
 63 |   GREEN: implements schema.js → all pass
 64 |   COMMIT: "feat(metrics): add metrics schema (task 42.1)"
 65 | → Subtask 42.2: "Add collection endpoint"
 66 |   RED: generates test_metrics_endpoint.test.js → 5 failures
 67 |   GREEN: implements api/metrics.js → all pass
 68 |   COMMIT: "feat(metrics): add collection endpoint (task 42.2)"
 69 | → Subtask 42.3: "Add dashboard widget"
 70 |   RED: generates test_metrics_widget.test.js → 4 failures
 71 |   GREEN: implements components/MetricsWidget.jsx → all pass
 72 |   COMMIT: "feat(metrics): add dashboard widget (task 42.3)"
 73 | → Final: all 3 subtasks complete
 74 |   ✓ Run full test suite → all pass
 75 |   ✓ Coverage check → 85% (meets 80% threshold)
 76 |   PUSH: confirms with user → pushed to origin
 77 |   PR: opens #123 "Task #42 [analytics]: User metrics tracking"
 78 | 
 79 | ✓ Task 42 complete. PR: https://github.com/org/repo/pull/123
 80 |   Run report: .taskmaster/reports/runs/2025-01-15-142033/
 81 | ```
 82 | 
 83 | ### Error Recovery: Failing tests timeout
 84 | 
 85 | ```bash
 86 | $ tm autopilot 42
 87 | → Subtask 42.2 GREEN phase: attempt 1 fails (2 tests still red)
 88 | → Subtask 42.2 GREEN phase: attempt 2 fails (1 test still red)
 89 | → Subtask 42.2 GREEN phase: attempt 3 fails (1 test still red)
 90 | 
 91 | ⚠️  Paused: Could not achieve green state after 3 attempts
 92 | 📋 State saved to: .taskmaster/reports/runs/2025-01-15-142033/
 93 |     Last error: "POST /api/metrics returns 500 instead of 201"
 94 | 
 95 | Next steps:
 96 |   - Review diff: git diff HEAD
 97 |   - Inspect logs: cat .taskmaster/reports/runs/2025-01-15-142033/log.jsonl
 98 |   - Check test output: cat .taskmaster/reports/runs/2025-01-15-142033/test-results/subtask-42.2-green-attempt3.json
 99 |   - Resume after manual fix: tm autopilot --resume
100 | 
101 | # Developer manually fixes the issue, then:
102 | $ tm autopilot --resume
103 | → Resuming subtask 42.2 GREEN phase
104 |   GREEN: all tests pass
105 |   COMMIT: "feat(metrics): add collection endpoint (task 42.2)"
106 | → Continuing to subtask 42.3...
107 | ```
108 | 
109 | ### Dry Run: Preview before execution
110 | 
111 | ```bash
112 | $ tm autopilot 42 --dry-run
113 | Autopilot Plan for Task #42 [analytics]: User metrics tracking
114 | ─────────────────────────────────────────────────────────────
115 | Preflight:
116 |   ✓ Working tree is clean
117 |   ✓ Test command detected: npm test
118 |   ✓ Tools available: git, gh, node, npm
119 |   ✓ Current branch: main (will create new branch)
120 | 
121 | Branch & Tag:
122 |   → Create branch: analytics/task-42-user-metrics
123 |   → Set active tag: analytics
124 | 
125 | Subtasks (3 pending):
126 |   1. 42.1: Add metrics schema
127 |      - RED: generate tests in src/__tests__/schema.test.js
128 |      - GREEN: implement src/schema.js
129 |      - COMMIT: "feat(metrics): add metrics schema (task 42.1)"
130 | 
131 |   2. 42.2: Add collection endpoint [depends on 42.1]
132 |      - RED: generate tests in src/api/__tests__/metrics.test.js
133 |      - GREEN: implement src/api/metrics.js
134 |      - COMMIT: "feat(metrics): add collection endpoint (task 42.2)"
135 | 
136 |   3. 42.3: Add dashboard widget [depends on 42.2]
137 |      - RED: generate tests in src/components/__tests__/MetricsWidget.test.jsx
138 |      - GREEN: implement src/components/MetricsWidget.jsx
139 |      - COMMIT: "feat(metrics): add dashboard widget (task 42.3)"
140 | 
141 | Finalization:
142 |   → Run full test suite with coverage
143 |   → Push branch to origin (will confirm)
144 |   → Create PR targeting main
145 | 
146 | Run without --dry-run to execute.
147 | ```
148 | 
149 | ## High‑Level Workflow
150 | 
151 | 1) Pre‑flight
152 | 
153 |    - Verify clean working tree or confirm staging/commit policy (configurable).
154 | 
155 |    - Detect repo type and the project’s test command (e.g., npm test, pnpm test, pytest, go test).
156 | 
157 |    - Validate tools: git, gh (optional for PR), node/npm, and (if used) claude CLI.
158 | 
159 |    - Load TaskMaster state and selected task; if no subtasks exist, automatically run “expand” before working.
160 | 
161 | 2) Branch & Tag Setup
162 | 
163 |    - Checkout default branch and update (optional), then create a branch using Branch Naming (below).
164 | 
165 |    - Map branch ↔ tag via existing tag management; explicitly set active tag to the branch’s tag.
166 | 
167 | 3) Subtask Loop (for each pending/in-progress subtask in dependency order)
168 | 
169 |    - Select next eligible subtask using tm-core TaskService getNextTask() and subtask eligibility logic.
170 | 
171 |    - Red: generate or update failing tests for the subtask
172 | 
173 |      - Use the Surgical Test Generator system prompt .claude/agents/surgical-test-generator.md) to produce high-signal tests following project conventions.
174 | 
175 |      - Run tests to confirm red; record results. If not red (already passing), skip to next subtask or escalate.
176 | 
177 |    - Green: implement code to pass tests
178 | 
179 |      - Use executor to implement changes (initial: claude CLI prompt with focused context).
180 | 
181 |      - Re-run tests until green or timeout/backoff policy triggers.
182 | 
183 |    - Commit: when green
184 | 
185 |      - Commit tests + code with conventional commit message. Optionally update subtask status to done.
186 | 
187 |      - Persist run step metadata/logs.
188 | 
189 | 4) Finalization
190 | 
191 |    - Run full test suite and coverage (if configured); optionally lint/format.
192 | 
193 |    - Commit any final adjustments.
194 | 
195 |    - Push branch (ask user to confirm); create PR (via gh pr create) targeting the default branch. Title format: Task #<id> [<tag>]: <title>.
196 | 
197 | 5) Post‑Run
198 | 
199 |    - Update task status if desired (e.g., review).
200 | 
201 |    - Persist run report (JSON + markdown summary) to .taskmaster/reports/runs/<run-id>/.
202 | 
203 | ## Guardrails
204 | 
205 | - Never commit to the default branch.
206 | 
207 | - Commit only if all tests (targeted and suite) pass; allow override flags.
208 | 
209 | - Enforce 80% coverage thresholds (lines/branches/functions/statements) by default; configurable.
210 | 
211 | - Timebox/model ops and retries; if not green within N attempts, pause with actionable state for resume.
212 | 
213 | - Always log actions, commands, and outcomes; include dry-run mode.
214 | 
215 | - Ask before branch creation, pushing, and opening a PR unless --no-confirm is set.
216 | 
217 | ## Integration Points (Current Repo)
218 | 
219 | - CLI: apps/cli provides command structure and UI components.
220 | 
221 |   - New command: tm autopilot (alias: task-master autopilot).
222 | 
223 |   - Reuse UI components under apps/cli/src/ui/components/ for headers/task details/next-task.
224 | 
225 | - Core services: packages/tm-core
226 | 
227 |   - TaskService for selection, status, tags.
228 | 
229 |   - TaskExecutionService for prompt formatting and executor prep.
230 | 
231 |   - Executors: claude executor and ExecutorFactory to run external tools.
232 | 
233 |   - Proposed new: WorkflowOrchestrator to drive the autonomous loop and emit progress events.
234 | 
235 | - Tag/Git utilities: scripts/modules/utils/git-utils.js and scripts/modules/task-manager/tag-management.js for branch→tag mapping and explicit tag switching.
236 | 
237 | - Rules: .cursor/rules/git_workflow.mdc and .cursor/rules/test_workflow.mdc to steer behavior and ensure consistency.
238 | 
239 | - Test generation prompt: .claude/agents/surgical-test-generator.md.
240 | 
241 | ## Proposed Components
242 | 
243 | - Orchestrator (tm-core): WorkflowOrchestrator (new)
244 | 
245 |   - State machine driving phases: Preflight → Branch/Tag → SubtaskIter (Red/Green/Commit) → Finalize → PR.
246 | 
247 |   - Exposes an evented API (progress events) that the CLI can render.
248 | 
249 |   - Stores run state artifacts.
250 | 
251 | - Test Runner Adapter
252 | 
253 |   - Detects and runs tests via the project’s test command (e.g., npm test), with targeted runs where feasible.
254 | 
255 |   - API: runTargeted(files/pattern), runAll(), report summary (failures, duration, coverage), enforce 80% threshold by default.
256 | 
257 | - Git/PR Adapter
258 | 
259 |   - Encapsulates git ops: branch create/checkout, add/commit, push.
260 | 
261 |   - Optional gh integration to open PR; fallback to instructions if gh unavailable.
262 | 
263 |   - Confirmation gates for branch creation and pushes.
264 | 
265 | - Prompt/Exec Adapter
266 | 
267 |   - Uses existing executor service to call the selected coding assistant (initially claude) with tight prompts: task/subtask context, surgical tests first, then minimal code to green.
268 | 
269 | - Run State + Reporting
270 | 
271 |   - JSONL log of steps, timestamps, commands, test results.
272 | 
273 |   - Markdown summary for PR description and post-run artifact.
274 | 
275 | ## CLI UX (MVP)
276 | 
277 | - Command: tm autopilot [taskId]
278 | 
279 |   - Flags: --dry-run, --no-push, --no-pr, --no-confirm, --force, --max-attempts <n>, --runner <auto|custom>, --commit-scope <scope>
280 | 
281 |   - Output: compact header (project, tag, branch), current phase, subtask line, last test summary, next actions.
282 | 
283 | - Resume: If interrupted, tm autopilot --resume picks up from last checkpoint in run state.
284 | 
285 | ### TUI with tmux (Linear Execution)
286 | 
287 | - Left pane: Tag selector, task list (status/priority), start/expand shortcuts; "Start" triggers the next task or a selected task.
288 | 
289 | - Right pane: Executor terminal (tmux split) that runs the coding agent (claude-code/codex). Autopilot can hand over to the right pane during green.
290 | 
291 | - MCP integration: use MCP tools for task queries/updates and for shell/test invocations where available.
292 | 
293 | ## TUI Layout (tmux-based)
294 | 
295 | ### Pane Structure
296 | 
297 | ```
298 | ┌─────────────────────────────────────┬──────────────────────────────────┐
299 | │ Task Navigator (left)               │ Executor Terminal (right)        │
300 | │                                     │                                  │
301 | │ Project: my-app                     │ $ tm autopilot --executor-mode   │
302 | │ Branch: analytics/task-42           │ > Running subtask 42.2 GREEN...  │
303 | │ Tag: analytics                      │ > Implementing endpoint...       │
304 | │                                     │ > Tests: 3 passed, 0 failed      │
305 | │ Tasks:                              │ > Ready to commit                │
306 | │ → 42 [in-progress] User metrics     │                                  │
307 | │   → 42.1 [done] Schema              │ [Live output from Claude Code]   │
308 | │   → 42.2 [active] Endpoint ◀        │                                  │
309 | │   → 42.3 [pending] Dashboard        │                                  │
310 | │                                     │                                  │
311 | │ [s] start  [p] pause  [q] quit      │                                  │
312 | └─────────────────────────────────────┴──────────────────────────────────┘
313 | ```
314 | 
315 | ### Implementation Notes
316 | 
317 | - **Left pane**: `apps/cli/src/ui/tui/navigator.ts` (new, uses `blessed` or `ink`)
318 | - **Right pane**: spawned via `tmux split-window -h` running `tm autopilot --executor-mode`
319 | - **Communication**: shared state file `.taskmaster/state/current-run.json` + file watching or event stream
320 | - **Keybindings**:
321 |   - `s` - Start selected task
322 |   - `p` - Pause/resume current run
323 |   - `q` - Quit (with confirmation if run active)
324 |   - `↑/↓` - Navigate task list
325 |   - `Enter` - Expand/collapse subtasks
326 | 
327 | ## Prompt Composition (Detailed)
328 | 
329 | ### System Prompt Assembly
330 | 
331 | Prompts are composed in three layers:
332 | 
333 | 1. **Base rules** (loaded in order from `.cursor/rules/` and `.claude/agents/`):
334 |    - `git_workflow.mdc` → git commit conventions, branch policy, PR guidelines
335 |    - `test_workflow.mdc` → TDD loop requirements, coverage thresholds, test structure
336 |    - `surgical-test-generator.md` → test generation methodology, project-specific test patterns
337 | 
338 | 2. **Task context injection**:
339 |    ```
340 |    You are implementing:
341 |    Task #42 [analytics]: User metrics tracking
342 |    Subtask 42.2: Add collection endpoint
343 | 
344 |    Description:
345 |    Implement POST /api/metrics endpoint to collect user metrics events
346 | 
347 |    Acceptance criteria:
348 |    - POST /api/metrics accepts { userId, eventType, timestamp }
349 |    - Validates input schema (reject missing/invalid fields)
350 |    - Persists to database
351 |    - Returns 201 on success with created record
352 |    - Returns 400 on validation errors
353 | 
354 |    Dependencies:
355 |    - Subtask 42.1 (metrics schema) is complete
356 | 
357 |    Current phase: RED (generate failing tests)
358 |    Test command: npm test
359 |    Test file convention: src/**/*.test.js (vitest framework detected)
360 |    Branch: analytics/task-42-user-metrics
361 |    Project language: JavaScript (Node.js)
362 |    ```
363 | 
364 | 3. **Phase-specific instructions**:
365 |    - **RED phase**: "Generate minimal failing tests for this subtask. Do NOT implement any production code. Only create test files. Confirm tests fail with clear error messages indicating missing implementation."
366 |    - **GREEN phase**: "Implement minimal code to pass the failing tests. Follow existing project patterns in `src/`. Only modify files necessary for this subtask. Keep changes focused and reviewable."
367 | 
368 | ### Example Full Prompt (RED Phase)
369 | 
370 | ```markdown
371 | <SYSTEM PROMPT>
372 | [Contents of .cursor/rules/git_workflow.mdc]
373 | [Contents of .cursor/rules/test_workflow.mdc]
374 | [Contents of .claude/agents/surgical-test-generator.md]
375 | 
376 | <TASK CONTEXT>
377 | You are implementing:
378 | Task #42.2: Add collection endpoint
379 | 
380 | Description:
381 | Implement POST /api/metrics endpoint to collect user metrics events
382 | 
383 | Acceptance criteria:
384 | - POST /api/metrics accepts { userId, eventType, timestamp }
385 | - Validates input schema (reject missing/invalid fields)
386 | - Persists to database using MetricsSchema from subtask 42.1
387 | - Returns 201 on success with created record
388 | - Returns 400 on validation errors with details
389 | 
390 | Dependencies: Subtask 42.1 (metrics schema) is complete
391 | 
392 | <INSTRUCTION>
393 | Generate failing tests for this subtask. Follow project conventions:
394 | - Test file: src/api/__tests__/metrics.test.js
395 | - Framework: vitest (detected from package.json)
396 | - Test cases to cover:
397 |   * POST /api/metrics with valid payload → should return 201 (will fail: endpoint not implemented)
398 |   * POST /api/metrics with missing userId → should return 400 (will fail: validation not implemented)
399 |   * POST /api/metrics with invalid timestamp → should return 400 (will fail: validation not implemented)
400 |   * POST /api/metrics should persist to database → should save record (will fail: persistence not implemented)
401 | 
402 | Do NOT implement the endpoint code yet. Only create test file(s).
403 | Confirm tests fail with messages like "Cannot POST /api/metrics" or "endpoint not defined".
404 | 
405 | Output format:
406 | 1. File path to create: src/api/__tests__/metrics.test.js
407 | 2. Complete test code
408 | 3. Command to run: npm test src/api/__tests__/metrics.test.js
409 | ```
410 | 
411 | ### Example Full Prompt (GREEN Phase)
412 | 
413 | ```markdown
414 | <SYSTEM PROMPT>
415 | [Contents of .cursor/rules/git_workflow.mdc]
416 | [Contents of .cursor/rules/test_workflow.mdc]
417 | 
418 | <TASK CONTEXT>
419 | Task #42.2: Add collection endpoint
420 | [same context as RED phase]
421 | 
422 | <CURRENT STATE>
423 | Tests created in RED phase:
424 | - src/api/__tests__/metrics.test.js
425 | - 5 tests written, all failing as expected
426 | 
427 | Test output:
428 | ```
429 | FAIL src/api/__tests__/metrics.test.js
430 |   POST /api/metrics
431 |     ✗ should return 201 with valid payload (endpoint not found)
432 |     ✗ should return 400 with missing userId (endpoint not found)
433 |     ✗ should return 400 with invalid timestamp (endpoint not found)
434 |     ✗ should persist to database (endpoint not found)
435 | ```
436 | 
437 | <INSTRUCTION>
438 | Implement minimal code to make all tests pass.
439 | 
440 | Guidelines:
441 | - Create/modify file: src/api/metrics.js
442 | - Use existing patterns from src/api/ (e.g., src/api/users.js for reference)
443 | - Import MetricsSchema from subtask 42.1 (src/models/schema.js)
444 | - Implement validation, persistence, and response handling
445 | - Follow project error handling conventions
446 | - Keep implementation focused on this subtask only
447 | 
448 | After implementation:
449 | 1. Run tests: npm test src/api/__tests__/metrics.test.js
450 | 2. Confirm all 5 tests pass
451 | 3. Report results
452 | 
453 | Output format:
454 | 1. File(s) created/modified
455 | 2. Implementation code
456 | 3. Test command and results
457 | ```
458 | 
459 | ### Prompt Loading Configuration
460 | 
461 | See `.taskmaster/config.json` → `prompts` section for paths and load order.
462 | 
463 | ## Configuration Schema
464 | 
465 | ### .taskmaster/config.json
466 | 
467 | ```json
468 | {
469 |   "autopilot": {
470 |     "enabled": true,
471 |     "requireCleanWorkingTree": true,
472 |     "commitTemplate": "{type}({scope}): {msg}",
473 |     "defaultCommitType": "feat",
474 |     "maxGreenAttempts": 3,
475 |     "testTimeout": 300000
476 |   },
477 |   "test": {
478 |     "runner": "auto",
479 |     "coverageThresholds": {
480 |       "lines": 80,
481 |       "branches": 80,
482 |       "functions": 80,
483 |       "statements": 80
484 |     },
485 |     "targetedRunPattern": "**/*.test.js"
486 |   },
487 |   "git": {
488 |     "branchPattern": "{tag}/task-{id}-{slug}",
489 |     "pr": {
490 |       "enabled": true,
491 |       "base": "default",
492 |       "bodyTemplate": ".taskmaster/templates/pr-body.md"
493 |     }
494 |   },
495 |   "prompts": {
496 |     "rulesPath": ".cursor/rules",
497 |     "testGeneratorPath": ".claude/agents/surgical-test-generator.md",
498 |     "loadOrder": ["git_workflow.mdc", "test_workflow.mdc"]
499 |   }
500 | }
501 | ```
502 | 
503 | ### Configuration Fields
504 | 
505 | #### autopilot
506 | - `enabled` (boolean): Enable/disable autopilot functionality
507 | - `requireCleanWorkingTree` (boolean): Require clean git state before starting
508 | - `commitTemplate` (string): Template for commit messages (tokens: `{type}`, `{scope}`, `{msg}`)
509 | - `defaultCommitType` (string): Default commit type (feat, fix, chore, etc.)
510 | - `maxGreenAttempts` (number): Maximum retry attempts to achieve green tests (default: 3)
511 | - `testTimeout` (number): Timeout in milliseconds per test run (default: 300000 = 5min)
512 | 
513 | #### test
514 | - `runner` (string): Test runner detection mode (`"auto"` or explicit command like `"npm test"`)
515 | - `coverageThresholds` (object): Minimum coverage percentages required
516 |   - `lines`, `branches`, `functions`, `statements` (number): Threshold percentages (0-100)
517 | - `targetedRunPattern` (string): Glob pattern for targeted subtask test runs
518 | 
519 | #### git
520 | - `branchPattern` (string): Branch naming pattern (tokens: `{tag}`, `{id}`, `{slug}`)
521 | - `pr.enabled` (boolean): Enable automatic PR creation
522 | - `pr.base` (string): Target branch for PRs (`"default"` uses repo default, or specify like `"main"`)
523 | - `pr.bodyTemplate` (string): Path to PR body template file (optional)
524 | 
525 | #### prompts
526 | - `rulesPath` (string): Directory containing rule files (e.g., `.cursor/rules`)
527 | - `testGeneratorPath` (string): Path to test generator prompt file
528 | - `loadOrder` (array): Order to load rule files from `rulesPath`
529 | 
530 | ### Environment Variables
531 | 
532 | ```bash
533 | # Required for executor
534 | ANTHROPIC_API_KEY=sk-ant-...          # Claude API key
535 | 
536 | # Optional: for PR creation
537 | GITHUB_TOKEN=ghp_...                  # GitHub personal access token
538 | 
539 | # Optional: for other executors (future)
540 | OPENAI_API_KEY=sk-...
541 | GOOGLE_API_KEY=...
542 | ```
543 | 
544 | ## Run Artifacts & Observability
545 | 
546 | ### Per-Run Artifact Structure
547 | 
548 | Each autopilot run creates a timestamped directory with complete traceability:
549 | 
550 | ```
551 | .taskmaster/reports/runs/2025-01-15-142033/
552 | ├── manifest.json          # run metadata (task id, start/end time, status)
553 | ├── log.jsonl              # timestamped event stream
554 | ├── commits.txt            # list of commit SHAs made during run
555 | ├── test-results/
556 | │   ├── subtask-42.1-red.json
557 | │   ├── subtask-42.1-green.json
558 | │   ├── subtask-42.2-red.json
559 | │   ├── subtask-42.2-green-attempt1.json
560 | │   ├── subtask-42.2-green-attempt2.json
561 | │   ├── subtask-42.2-green-attempt3.json
562 | │   └── final-suite.json
563 | └── pr.md                  # generated PR body
564 | ```
565 | 
566 | ### manifest.json Format
567 | 
568 | ```json
569 | {
570 |   "runId": "2025-01-15-142033",
571 |   "taskId": "42",
572 |   "tag": "analytics",
573 |   "branch": "analytics/task-42-user-metrics",
574 |   "startTime": "2025-01-15T14:20:33Z",
575 |   "endTime": "2025-01-15T14:45:12Z",
576 |   "status": "completed",
577 |   "subtasksCompleted": ["42.1", "42.2", "42.3"],
578 |   "subtasksFailed": [],
579 |   "totalCommits": 3,
580 |   "prUrl": "https://github.com/org/repo/pull/123",
581 |   "finalCoverage": {
582 |     "lines": 85.3,
583 |     "branches": 82.1,
584 |     "functions": 88.9,
585 |     "statements": 85.0
586 |   }
587 | }
588 | ```
589 | 
590 | ### log.jsonl Format
591 | 
592 | Event stream in JSON Lines format for easy parsing and debugging:
593 | 
594 | ```jsonl
595 | {"ts":"2025-01-15T14:20:33Z","phase":"preflight","status":"ok","details":{"testCmd":"npm test","gitClean":true}}
596 | {"ts":"2025-01-15T14:20:45Z","phase":"branch","status":"ok","branch":"analytics/task-42-user-metrics"}
597 | {"ts":"2025-01-15T14:21:00Z","phase":"red","subtask":"42.1","status":"ok","tests":{"failed":3,"passed":0}}
598 | {"ts":"2025-01-15T14:22:15Z","phase":"green","subtask":"42.1","status":"ok","tests":{"passed":3,"failed":0},"attempts":2}
599 | {"ts":"2025-01-15T14:22:20Z","phase":"commit","subtask":"42.1","status":"ok","sha":"a1b2c3d","message":"feat(metrics): add metrics schema (task 42.1)"}
600 | {"ts":"2025-01-15T14:23:00Z","phase":"red","subtask":"42.2","status":"ok","tests":{"failed":5,"passed":0}}
601 | {"ts":"2025-01-15T14:25:30Z","phase":"green","subtask":"42.2","status":"error","tests":{"passed":3,"failed":2},"attempts":3,"error":"Max attempts reached"}
602 | {"ts":"2025-01-15T14:25:35Z","phase":"pause","reason":"max_attempts","nextAction":"manual_review"}
603 | ```
604 | 
605 | ### Test Results Format
606 | 
607 | Each test run stores detailed results:
608 | 
609 | ```json
610 | {
611 |   "subtask": "42.2",
612 |   "phase": "green",
613 |   "attempt": 3,
614 |   "timestamp": "2025-01-15T14:25:30Z",
615 |   "command": "npm test src/api/__tests__/metrics.test.js",
616 |   "exitCode": 1,
617 |   "duration": 2340,
618 |   "summary": {
619 |     "total": 5,
620 |     "passed": 3,
621 |     "failed": 2,
622 |     "skipped": 0
623 |   },
624 |   "failures": [
625 |     {
626 |       "test": "POST /api/metrics should return 201 with valid payload",
627 |       "error": "Expected status 201, got 500",
628 |       "stack": "..."
629 |     }
630 |   ],
631 |   "coverage": {
632 |     "lines": 78.5,
633 |     "branches": 75.0,
634 |     "functions": 80.0,
635 |     "statements": 78.5
636 |   }
637 | }
638 | ```
639 | 
640 | ## Execution Model
641 | 
642 | ### Orchestration vs Direct Execution
643 | 
644 | The autopilot system uses an **orchestration model** rather than direct code execution:
645 | 
646 | **Orchestrator Role** (tm-core WorkflowOrchestrator):
647 | - Maintains state machine tracking current phase (RED/GREEN/COMMIT) per subtask
648 | - Validates preconditions (tests pass, git state clean, etc.)
649 | - Returns "work units" describing what needs to be done next
650 | - Records completion and advances to next phase
651 | - Persists state for resumability
652 | 
653 | **Executor Role** (Claude Code/AI session via MCP):
654 | - Queries orchestrator for next work unit
655 | - Executes the work (generates tests, writes code, runs tests, makes commits)
656 | - Reports results back to orchestrator
657 | - Handles file operations and tool invocations
658 | 
659 | **Why This Approach?**
660 | - Leverages existing AI capabilities (Claude Code) rather than duplicating them
661 | - MCP protocol provides clean separation between state management and execution
662 | - Allows human oversight and intervention at each phase
663 | - Simpler to implement: orchestrator is pure state logic, no code generation needed
664 | - Enables multiple executor types (Claude Code, other AI tools, human developers)
665 | 
666 | **Example Flow**:
667 | ```typescript
668 | // Claude Code (via MCP) queries orchestrator
669 | const workUnit = await orchestrator.getNextWorkUnit('42');
670 | // => {
671 | //      phase: 'RED',
672 | //      subtask: '42.1',
673 | //      action: 'Generate failing tests for metrics schema',
674 | //      context: { title, description, dependencies, testFile: 'src/__tests__/schema.test.js' }
675 | //    }
676 | 
677 | // Claude Code executes the work (writes test file, runs tests)
678 | // Then reports back
679 | await orchestrator.completeWorkUnit('42', '42.1', 'RED', {
680 |   success: true,
681 |   testsCreated: ['src/__tests__/schema.test.js'],
682 |   testsFailed: 3
683 | });
684 | 
685 | // Query again for next phase
686 | const nextWorkUnit = await orchestrator.getNextWorkUnit('42');
687 | // => { phase: 'GREEN', subtask: '42.1', action: 'Implement code to pass tests', ... }
688 | ```
689 | 
690 | ## Design Decisions
691 | 
692 | ### Why commit per subtask instead of per task?
693 | 
694 | **Decision**: Commit after each subtask's green state, not after the entire task.
695 | 
696 | **Rationale**:
697 | - Atomic commits make code review easier (reviewers can see logical progression)
698 | - Easier to revert a single subtask if it causes issues downstream
699 | - Matches the TDD loop's natural checkpoint and cognitive boundary
700 | - Provides resumability points if the run is interrupted
701 | 
702 | **Trade-off**: More commits per task (can use squash-merge in PRs if desired)
703 | 
704 | ### Why not support parallel subtask execution?
705 | 
706 | **Decision**: Sequential subtask execution in Phase 1; parallel execution deferred to Phase 3.
707 | 
708 | **Rationale**:
709 | - Subtasks often have implicit dependencies (e.g., schema before endpoint, endpoint before UI)
710 | - Simpler orchestrator state machine (less complexity = faster to ship)
711 | - Parallel execution requires explicit dependency DAG and conflict resolution
712 | - Can be added in Phase 3 once core workflow is proven stable
713 | 
714 | **Trade-off**: Slower for truly independent subtasks (mitigated by keeping subtasks small and focused)
715 | 
716 | ### Why require 80% coverage by default?
717 | 
718 | **Decision**: Enforce 80% coverage threshold (lines/branches/functions/statements) before allowing commits.
719 | 
720 | **Rationale**:
721 | - Industry standard baseline for production code quality
722 | - Forces test generation to be comprehensive, not superficial
723 | - Configurable per project via `.taskmaster/config.json` if too strict
724 | - Prevents "green tests" that only test happy paths
725 | 
726 | **Trade-off**: May require more test generation iterations; can be lowered per project
727 | 
728 | ### Why use tmux instead of a rich GUI?
729 | 
730 | **Decision**: MVP uses tmux split panes for TUI, not Electron/web-based GUI.
731 | 
732 | **Rationale**:
733 | - Tmux is universally available on dev machines; no installation burden
734 | - Terminal-first workflows match developer mental model (no context switching)
735 | - Simpler to implement and maintain; can add GUI later via extensions
736 | - State stored in files allows IDE/extension integration without coupling
737 | 
738 | **Trade-off**: Less visual polish than GUI; requires tmux familiarity
739 | 
740 | ### Why not support multiple executors (codex/gemini/claude) in Phase 1?
741 | 
742 | **Decision**: Start with Claude executor only; add others in Phase 2+.
743 | 
744 | **Rationale**:
745 | - Reduces scope and complexity for initial delivery
746 | - Claude Code already integrated with existing executor service
747 | - Executor abstraction already exists; adding more is straightforward later
748 | - Different executors may need different prompt strategies (requires experimentation)
749 | 
750 | **Trade-off**: Users locked to Claude initially; can work around with manual executor selection
751 | 
752 | ## Risks and Mitigations
753 | 
754 | - Model hallucination/large diffs: restrict prompt scope; enforce minimal changes; show diff previews (optional) before commit.
755 | 
756 | - Flaky tests: allow retries, isolate targeted runs for speed, then full suite before commit.
757 | 
758 | - Environment variability: detect runners/tools; provide fallbacks and actionable errors.
759 | 
760 | - PR creation fails: still push and print manual commands; persist PR body to reuse.
761 | 
762 | ## Open Questions
763 | 
764 | 1) Slugging rules for branch names; any length limits or normalization beyond {slug} token sanitize?
765 | 
766 | 2) PR body standard sections beyond run report (e.g., checklist, coverage table)?
767 | 
768 | 3) Default executor prompt fine-tuning once codex/gemini integration is available.
769 | 
770 | 4) Where to store persistent TUI state (pane layout, last selection) in .taskmaster/state.json?
771 | 
772 | ## Branch Naming
773 | 
774 | - Include both the tag and the task id in the branch name to make lineage explicit.
775 | 
776 | - Default pattern: <tag>/task-<id>[-slug] (e.g., master/task-12, tag-analytics/task-4-user-auth).
777 | 
778 | - Configurable via .taskmaster/config.json: git.branchPattern supports tokens {tag}, {id}, {slug}.
779 | 
780 | ## PR Base Branch
781 | 
782 | - Use the repository’s default branch (detected via git) unless overridden.
783 | 
784 | - Title format: Task #<id> [<tag>]: <title>.
785 | 
786 | ## RPG Mapping (Repository Planning Graph)
787 | 
788 | Functional nodes (capabilities):
789 | 
790 | - Autopilot Orchestration → drives TDD loop and lifecycle
791 | 
792 | - Test Generation (Surgical) → produces failing tests from subtask context
793 | 
794 | - Test Execution + Coverage → runs suite, enforces thresholds
795 | 
796 | - Git/Branch/PR Management → safe operations and PR creation
797 | 
798 | - TUI/Terminal Integration → interactive control and visibility via tmux
799 | 
800 | - MCP Integration → structured task/status/context operations
801 | 
802 | Structural nodes (code organization):
803 | 
804 | - packages/tm-core:
805 | 
806 |   - services/workflow-orchestrator.ts (new)
807 | 
808 |   - services/test-runner-adapter.ts (new)
809 | 
810 |   - services/git-adapter.ts (new)
811 | 
812 |   - existing: task-service.ts, task-execution-service.ts, executors/*
813 | 
814 | - apps/cli:
815 | 
816 |   - src/commands/autopilot.command.ts (new)
817 | 
818 |   - src/ui/tui/ (new tmux/TUI helpers)
819 | 
820 | - scripts/modules:
821 | 
822 |   - reuse utils/git-utils.js, task-manager/tag-management.js
823 | 
824 | - .claude/agents/:
825 | 
826 |   - surgical-test-generator.md
827 | 
828 | Edges (data/control flow):
829 | 
830 | - Autopilot → Test Generation → Test Execution → Git Commit → loop
831 | 
832 | - Autopilot → Git Adapter (branch, tag, PR)
833 | 
834 | - Autopilot → TUI (event stream) → tmux pane control
835 | 
836 | - Autopilot → MCP tools for task/status updates
837 | 
838 | - Test Execution → Coverage gate → Autopilot decision
839 | 
840 | Topological traversal (implementation order):
841 | 
842 | 1) Git/Test adapters (foundations)
843 | 
844 | 2) Orchestrator skeleton + events
845 | 
846 | 3) CLI autopilot command and dry-run
847 | 
848 | 4) Surgical test-gen integration and execution gate
849 | 
850 | 5) PR creation, run reports, resumability
851 | 
852 | ## Phased Roadmap
853 | 
854 | - Phase 0: Spike
855 | 
856 |   - Implement CLI skeleton tm autopilot with dry-run showing planned steps from a real task + subtasks.
857 | 
858 |   - Detect test runner (package.json) and git state; render a preflight report.
859 | 
860 | - Phase 1: Core Rails (State Machine & Orchestration)
861 | 
862 |   - Implement WorkflowOrchestrator in tm-core as a **state machine** that tracks TDD phases per subtask.
863 | 
864 |   - Orchestrator **guides** the current AI session (Claude Code/MCP client) rather than executing code itself.
865 | 
866 |   - Add Git/Test adapters for status checks and validation (not direct execution).
867 | 
868 |   - WorkflowOrchestrator API:
869 |     - `getNextWorkUnit(taskId)` → returns next phase to execute (RED/GREEN/COMMIT) with context
870 |     - `completeWorkUnit(taskId, subtaskId, phase, result)` → records completion and advances state
871 |     - `getRunState(taskId)` → returns current progress and resumability data
872 | 
873 |   - MCP integration: expose work unit endpoints so Claude Code can query "what to do next" and report back.
874 | 
875 |   - Branch/tag mapping via existing tag-management APIs.
876 | 
877 |   - Run report persisted under .taskmaster/reports/runs/ with state checkpoints for resumability.
878 | 
879 | - Phase 2: PR + Resumability
880 | 
881 |   - Add gh PR creation with well-formed body using the run report.
882 | 
883 |   - Introduce resumable checkpoints and --resume flag.
884 | 
885 |   - Add coverage enforcement and optional lint/format step.
886 | 
887 | - Phase 3: Extensibility + Guardrails
888 | 
889 |   - Add support for basic pytest/go test adapters.
890 | 
891 |   - Add safeguards: diff preview mode, manual confirm gates, aggressive minimal-change prompts.
892 | 
893 |   - Optional: small TUI panel and extension panel leveraging the same run state file.
894 | 
895 | ## References (Repo)
896 | 
897 | - Test Workflow: .cursor/rules/test_workflow.mdc
898 | 
899 | - Git Workflow: .cursor/rules/git_workflow.mdc
900 | 
901 | - CLI: apps/cli/src/commands/start.command.ts, apps/cli/src/ui/components/*.ts
902 | 
903 | - Core Services: packages/tm-core/src/services/task-service.ts, task-execution-service.ts
904 | 
905 | - Executors: packages/tm-core/src/executors/*
906 | 
907 | - Git Utilities: scripts/modules/utils/git-utils.js
908 | 
909 | - Tag Management: scripts/modules/task-manager/tag-management.js
910 | 
911 |  - Surgical Test Generator: .claude/agents/surgical-test-generator.md
912 | 
913 | 
```

--------------------------------------------------------------------------------
/tests/unit/dependency-manager.test.js:
--------------------------------------------------------------------------------

```javascript
   1 | /**
   2 |  * Dependency Manager module tests
   3 |  */
   4 | 
   5 | import { jest } from '@jest/globals';
   6 | import {
   7 | 	sampleTasks,
   8 | 	crossLevelDependencyTasks
   9 | } from '../fixtures/sample-tasks.js';
  10 | 
  11 | // Create mock functions that we can control in tests
  12 | const mockTaskExists = jest.fn();
  13 | const mockFormatTaskId = jest.fn();
  14 | const mockFindCycles = jest.fn();
  15 | const mockLog = jest.fn();
  16 | const mockReadJSON = jest.fn();
  17 | const mockWriteJSON = jest.fn();
  18 | 
  19 | // Mock the utils module using the same pattern as move-task-cross-tag.test.js
  20 | jest.mock('../../scripts/modules/utils.js', () => ({
  21 | 	log: mockLog,
  22 | 	readJSON: mockReadJSON,
  23 | 	writeJSON: mockWriteJSON,
  24 | 	taskExists: mockTaskExists,
  25 | 	formatTaskId: mockFormatTaskId,
  26 | 	findCycles: mockFindCycles,
  27 | 	traverseDependencies: jest.fn(() => []),
  28 | 	isSilentMode: jest.fn(() => true),
  29 | 	findProjectRoot: jest.fn(() => '/test'),
  30 | 	resolveEnvVariable: jest.fn(() => undefined),
  31 | 	isEmpty: jest.fn((v) =>
  32 | 		v == null
  33 | 			? true
  34 | 			: Array.isArray(v)
  35 | 				? v.length === 0
  36 | 				: typeof v === 'object'
  37 | 					? Object.keys(v).length === 0
  38 | 					: false
  39 | 	),
  40 | 	// Common extras
  41 | 	enableSilentMode: jest.fn(),
  42 | 	disableSilentMode: jest.fn(),
  43 | 	getTaskManager: jest.fn(async () => ({})),
  44 | 	getTagAwareFilePath: jest.fn((basePath, _tag, projectRoot = '.') => basePath),
  45 | 	readComplexityReport: jest.fn(() => null)
  46 | }));
  47 | 
  48 | jest.mock('path');
  49 | jest.mock('chalk', () => ({
  50 | 	green: jest.fn((text) => `<green>${text}</green>`),
  51 | 	yellow: jest.fn((text) => `<yellow>${text}</yellow>`),
  52 | 	red: jest.fn((text) => `<red>${text}</red>`),
  53 | 	cyan: jest.fn((text) => `<cyan>${text}</cyan>`),
  54 | 	bold: jest.fn((text) => `<bold>${text}</bold>`)
  55 | }));
  56 | 
  57 | jest.mock('boxen', () => jest.fn((text) => `[boxed: ${text}]`));
  58 | 
  59 | // Now import SUT after mocks are in place
  60 | import {
  61 | 	validateTaskDependencies,
  62 | 	isCircularDependency,
  63 | 	removeDuplicateDependencies,
  64 | 	cleanupSubtaskDependencies,
  65 | 	ensureAtLeastOneIndependentSubtask,
  66 | 	validateAndFixDependencies,
  67 | 	canMoveWithDependencies
  68 | } from '../../scripts/modules/dependency-manager.js';
  69 | 
  70 | jest.mock('../../scripts/modules/ui.js', () => ({
  71 | 	displayBanner: jest.fn()
  72 | }));
  73 | 
  74 | jest.mock('../../scripts/modules/task-manager.js', () => ({
  75 | 	generateTaskFiles: jest.fn()
  76 | }));
  77 | 
  78 | // Use a temporary path for test files - Jest will clean up the temp directory
  79 | const TEST_TASKS_PATH = '/tmp/jest-test-tasks.json';
  80 | 
  81 | describe('Dependency Manager Module', () => {
  82 | 	beforeEach(() => {
  83 | 		jest.clearAllMocks();
  84 | 
  85 | 		// Set default implementations
  86 | 		mockTaskExists.mockImplementation((tasks, id) => {
  87 | 			if (Array.isArray(tasks)) {
  88 | 				if (typeof id === 'string' && id.includes('.')) {
  89 | 					const [taskId, subtaskId] = id.split('.').map(Number);
  90 | 					const task = tasks.find((t) => t.id === taskId);
  91 | 					return (
  92 | 						task &&
  93 | 						task.subtasks &&
  94 | 						task.subtasks.some((st) => st.id === subtaskId)
  95 | 					);
  96 | 				}
  97 | 				return tasks.some(
  98 | 					(task) => task.id === (typeof id === 'string' ? parseInt(id, 10) : id)
  99 | 				);
 100 | 			}
 101 | 			return false;
 102 | 		});
 103 | 
 104 | 		mockFormatTaskId.mockImplementation((id) => {
 105 | 			if (typeof id === 'string' && id.includes('.')) {
 106 | 				return id;
 107 | 			}
 108 | 			return parseInt(id, 10);
 109 | 		});
 110 | 
 111 | 		mockFindCycles.mockImplementation((tasks) => {
 112 | 			// Simplified cycle detection for testing
 113 | 			const dependencyMap = new Map();
 114 | 
 115 | 			// Build dependency map
 116 | 			tasks.forEach((task) => {
 117 | 				if (task.dependencies) {
 118 | 					dependencyMap.set(task.id, task.dependencies);
 119 | 				}
 120 | 			});
 121 | 
 122 | 			const visited = new Set();
 123 | 			const recursionStack = new Set();
 124 | 
 125 | 			function dfs(taskId) {
 126 | 				visited.add(taskId);
 127 | 				recursionStack.add(taskId);
 128 | 
 129 | 				const dependencies = dependencyMap.get(taskId) || [];
 130 | 				for (const depId of dependencies) {
 131 | 					if (!visited.has(depId)) {
 132 | 						if (dfs(depId)) return true;
 133 | 					} else if (recursionStack.has(depId)) {
 134 | 						return true;
 135 | 					}
 136 | 				}
 137 | 
 138 | 				recursionStack.delete(taskId);
 139 | 				return false;
 140 | 			}
 141 | 
 142 | 			// Check for cycles starting from each unvisited node
 143 | 			for (const taskId of dependencyMap.keys()) {
 144 | 				if (!visited.has(taskId)) {
 145 | 					if (dfs(taskId)) return true;
 146 | 				}
 147 | 			}
 148 | 
 149 | 			return false;
 150 | 		});
 151 | 	});
 152 | 
 153 | 	describe('isCircularDependency function', () => {
 154 | 		test('should detect a direct circular dependency', () => {
 155 | 			const tasks = [
 156 | 				{ id: 1, dependencies: [2] },
 157 | 				{ id: 2, dependencies: [1] }
 158 | 			];
 159 | 
 160 | 			const result = isCircularDependency(tasks, 1);
 161 | 			expect(result).toBe(true);
 162 | 		});
 163 | 
 164 | 		test('should detect an indirect circular dependency', () => {
 165 | 			const tasks = [
 166 | 				{ id: 1, dependencies: [2] },
 167 | 				{ id: 2, dependencies: [3] },
 168 | 				{ id: 3, dependencies: [1] }
 169 | 			];
 170 | 
 171 | 			const result = isCircularDependency(tasks, 1);
 172 | 			expect(result).toBe(true);
 173 | 		});
 174 | 
 175 | 		test('should return false for non-circular dependencies', () => {
 176 | 			const tasks = [
 177 | 				{ id: 1, dependencies: [2] },
 178 | 				{ id: 2, dependencies: [3] },
 179 | 				{ id: 3, dependencies: [] }
 180 | 			];
 181 | 
 182 | 			const result = isCircularDependency(tasks, 1);
 183 | 			expect(result).toBe(false);
 184 | 		});
 185 | 
 186 | 		test('should handle a task with no dependencies', () => {
 187 | 			const tasks = [
 188 | 				{ id: 1, dependencies: [] },
 189 | 				{ id: 2, dependencies: [1] }
 190 | 			];
 191 | 
 192 | 			const result = isCircularDependency(tasks, 1);
 193 | 			expect(result).toBe(false);
 194 | 		});
 195 | 
 196 | 		test('should handle a task depending on itself', () => {
 197 | 			const tasks = [{ id: 1, dependencies: [1] }];
 198 | 
 199 | 			const result = isCircularDependency(tasks, 1);
 200 | 			expect(result).toBe(true);
 201 | 		});
 202 | 
 203 | 		test('should handle subtask dependencies correctly', () => {
 204 | 			const tasks = [
 205 | 				{
 206 | 					id: 1,
 207 | 					dependencies: [],
 208 | 					subtasks: [
 209 | 						{ id: 1, dependencies: ['1.2'] },
 210 | 						{ id: 2, dependencies: ['1.3'] },
 211 | 						{ id: 3, dependencies: ['1.1'] }
 212 | 					]
 213 | 				}
 214 | 			];
 215 | 
 216 | 			// This creates a circular dependency: 1.1 -> 1.2 -> 1.3 -> 1.1
 217 | 			const result = isCircularDependency(tasks, '1.1', ['1.3', '1.2']);
 218 | 			expect(result).toBe(true);
 219 | 		});
 220 | 
 221 | 		test('should allow non-circular subtask dependencies within same parent', () => {
 222 | 			const tasks = [
 223 | 				{
 224 | 					id: 1,
 225 | 					dependencies: [],
 226 | 					subtasks: [
 227 | 						{ id: 1, dependencies: [] },
 228 | 						{ id: 2, dependencies: ['1.1'] },
 229 | 						{ id: 3, dependencies: ['1.2'] }
 230 | 					]
 231 | 				}
 232 | 			];
 233 | 
 234 | 			// This is a valid dependency chain: 1.3 -> 1.2 -> 1.1
 235 | 			const result = isCircularDependency(tasks, '1.1', []);
 236 | 			expect(result).toBe(false);
 237 | 		});
 238 | 
 239 | 		test('should properly handle dependencies between subtasks of the same parent', () => {
 240 | 			const tasks = [
 241 | 				{
 242 | 					id: 1,
 243 | 					dependencies: [],
 244 | 					subtasks: [
 245 | 						{ id: 1, dependencies: [] },
 246 | 						{ id: 2, dependencies: ['1.1'] },
 247 | 						{ id: 3, dependencies: [] }
 248 | 					]
 249 | 				}
 250 | 			];
 251 | 
 252 | 			// Check if adding a dependency from subtask 1.3 to 1.2 creates a circular dependency
 253 | 			// This should be false as 1.3 -> 1.2 -> 1.1 is a valid chain
 254 | 			mockTaskExists.mockImplementation(() => true);
 255 | 			const result = isCircularDependency(tasks, '1.3', ['1.2']);
 256 | 			expect(result).toBe(false);
 257 | 		});
 258 | 
 259 | 		test('should correctly detect circular dependencies in subtasks of the same parent', () => {
 260 | 			const tasks = [
 261 | 				{
 262 | 					id: 1,
 263 | 					dependencies: [],
 264 | 					subtasks: [
 265 | 						{ id: 1, dependencies: ['1.3'] },
 266 | 						{ id: 2, dependencies: ['1.1'] },
 267 | 						{ id: 3, dependencies: ['1.2'] }
 268 | 					]
 269 | 				}
 270 | 			];
 271 | 
 272 | 			// This creates a circular dependency: 1.1 -> 1.3 -> 1.2 -> 1.1
 273 | 			mockTaskExists.mockImplementation(() => true);
 274 | 			const result = isCircularDependency(tasks, '1.2', ['1.1']);
 275 | 			expect(result).toBe(true);
 276 | 		});
 277 | 	});
 278 | 
 279 | 	describe('validateTaskDependencies function', () => {
 280 | 		test('should detect missing dependencies', () => {
 281 | 			const tasks = [
 282 | 				{ id: 1, dependencies: [99] }, // 99 doesn't exist
 283 | 				{ id: 2, dependencies: [1] }
 284 | 			];
 285 | 
 286 | 			const result = validateTaskDependencies(tasks);
 287 | 
 288 | 			expect(result.valid).toBe(false);
 289 | 			expect(result.issues.length).toBeGreaterThan(0);
 290 | 			expect(result.issues[0].type).toBe('missing');
 291 | 			expect(result.issues[0].taskId).toBe(1);
 292 | 			expect(result.issues[0].dependencyId).toBe(99);
 293 | 		});
 294 | 
 295 | 		test('should detect circular dependencies', () => {
 296 | 			const tasks = [
 297 | 				{ id: 1, dependencies: [2] },
 298 | 				{ id: 2, dependencies: [1] }
 299 | 			];
 300 | 
 301 | 			const result = validateTaskDependencies(tasks);
 302 | 
 303 | 			expect(result.valid).toBe(false);
 304 | 			expect(result.issues.some((issue) => issue.type === 'circular')).toBe(
 305 | 				true
 306 | 			);
 307 | 		});
 308 | 
 309 | 		test('should detect self-dependencies', () => {
 310 | 			const tasks = [{ id: 1, dependencies: [1] }];
 311 | 
 312 | 			const result = validateTaskDependencies(tasks);
 313 | 
 314 | 			expect(result.valid).toBe(false);
 315 | 			expect(
 316 | 				result.issues.some(
 317 | 					(issue) => issue.type === 'self' && issue.taskId === 1
 318 | 				)
 319 | 			).toBe(true);
 320 | 		});
 321 | 
 322 | 		test('should return valid for correct dependencies', () => {
 323 | 			const tasks = [
 324 | 				{ id: 1, dependencies: [] },
 325 | 				{ id: 2, dependencies: [1] },
 326 | 				{ id: 3, dependencies: [1, 2] }
 327 | 			];
 328 | 
 329 | 			const result = validateTaskDependencies(tasks);
 330 | 
 331 | 			expect(result.valid).toBe(true);
 332 | 			expect(result.issues.length).toBe(0);
 333 | 		});
 334 | 
 335 | 		test('should handle tasks with no dependencies property', () => {
 336 | 			const tasks = [
 337 | 				{ id: 1 }, // Missing dependencies property
 338 | 				{ id: 2, dependencies: [1] }
 339 | 			];
 340 | 
 341 | 			const result = validateTaskDependencies(tasks);
 342 | 
 343 | 			// Should be valid since a missing dependencies property is interpreted as an empty array
 344 | 			expect(result.valid).toBe(true);
 345 | 		});
 346 | 
 347 | 		test('should handle subtask dependencies correctly', () => {
 348 | 			const tasks = [
 349 | 				{
 350 | 					id: 1,
 351 | 					dependencies: [],
 352 | 					subtasks: [
 353 | 						{ id: 1, dependencies: [] },
 354 | 						{ id: 2, dependencies: ['1.1'] }, // Valid - depends on another subtask
 355 | 						{ id: 3, dependencies: ['1.2'] } // Valid - depends on another subtask
 356 | 					]
 357 | 				},
 358 | 				{
 359 | 					id: 2,
 360 | 					dependencies: ['1.3'], // Valid - depends on a subtask from task 1
 361 | 					subtasks: []
 362 | 				}
 363 | 			];
 364 | 
 365 | 			// Set up mock to handle subtask validation
 366 | 			mockTaskExists.mockImplementation((tasks, id) => {
 367 | 				if (typeof id === 'string' && id.includes('.')) {
 368 | 					const [taskId, subtaskId] = id.split('.').map(Number);
 369 | 					const task = tasks.find((t) => t.id === taskId);
 370 | 					return (
 371 | 						task &&
 372 | 						task.subtasks &&
 373 | 						task.subtasks.some((st) => st.id === subtaskId)
 374 | 					);
 375 | 				}
 376 | 				return tasks.some((task) => task.id === parseInt(id, 10));
 377 | 			});
 378 | 
 379 | 			const result = validateTaskDependencies(tasks);
 380 | 
 381 | 			expect(result.valid).toBe(true);
 382 | 			expect(result.issues.length).toBe(0);
 383 | 		});
 384 | 
 385 | 		test('should detect missing subtask dependencies', () => {
 386 | 			const tasks = [
 387 | 				{
 388 | 					id: 1,
 389 | 					dependencies: [],
 390 | 					subtasks: [
 391 | 						{ id: 1, dependencies: ['1.4'] }, // Invalid - subtask 4 doesn't exist
 392 | 						{ id: 2, dependencies: ['2.1'] } // Invalid - task 2 has no subtasks
 393 | 					]
 394 | 				},
 395 | 				{
 396 | 					id: 2,
 397 | 					dependencies: [],
 398 | 					subtasks: []
 399 | 				}
 400 | 			];
 401 | 
 402 | 			// Mock taskExists to correctly identify missing subtasks
 403 | 			mockTaskExists.mockImplementation((taskArray, depId) => {
 404 | 				if (typeof depId === 'string' && depId === '1.4') {
 405 | 					return false; // Subtask 1.4 doesn't exist
 406 | 				}
 407 | 				if (typeof depId === 'string' && depId === '2.1') {
 408 | 					return false; // Subtask 2.1 doesn't exist
 409 | 				}
 410 | 				return true; // All other dependencies exist
 411 | 			});
 412 | 
 413 | 			const result = validateTaskDependencies(tasks);
 414 | 
 415 | 			expect(result.valid).toBe(false);
 416 | 			expect(result.issues.length).toBeGreaterThan(0);
 417 | 			// Should detect missing subtask dependencies
 418 | 			expect(
 419 | 				result.issues.some(
 420 | 					(issue) =>
 421 | 						issue.type === 'missing' &&
 422 | 						String(issue.taskId) === '1.1' &&
 423 | 						String(issue.dependencyId) === '1.4'
 424 | 				)
 425 | 			).toBe(true);
 426 | 		});
 427 | 
 428 | 		test('should detect circular dependencies between subtasks', () => {
 429 | 			const tasks = [
 430 | 				{
 431 | 					id: 1,
 432 | 					dependencies: [],
 433 | 					subtasks: [
 434 | 						{ id: 1, dependencies: ['1.2'] },
 435 | 						{ id: 2, dependencies: ['1.1'] } // Creates a circular dependency with 1.1
 436 | 					]
 437 | 				}
 438 | 			];
 439 | 
 440 | 			// Mock isCircularDependency for subtasks
 441 | 			mockFindCycles.mockReturnValue(true);
 442 | 
 443 | 			const result = validateTaskDependencies(tasks);
 444 | 
 445 | 			expect(result.valid).toBe(false);
 446 | 			expect(result.issues.some((issue) => issue.type === 'circular')).toBe(
 447 | 				true
 448 | 			);
 449 | 		});
 450 | 
 451 | 		test('should properly validate dependencies between subtasks of the same parent', () => {
 452 | 			const tasks = [
 453 | 				{
 454 | 					id: 23,
 455 | 					dependencies: [],
 456 | 					subtasks: [
 457 | 						{ id: 8, dependencies: ['23.13'] },
 458 | 						{ id: 10, dependencies: ['23.8'] },
 459 | 						{ id: 13, dependencies: [] }
 460 | 					]
 461 | 				}
 462 | 			];
 463 | 
 464 | 			// Mock taskExists to validate the subtask dependencies
 465 | 			mockTaskExists.mockImplementation((taskArray, id) => {
 466 | 				if (typeof id === 'string') {
 467 | 					if (id === '23.8' || id === '23.10' || id === '23.13') {
 468 | 						return true;
 469 | 					}
 470 | 				}
 471 | 				return false;
 472 | 			});
 473 | 
 474 | 			const result = validateTaskDependencies(tasks);
 475 | 
 476 | 			expect(result.valid).toBe(true);
 477 | 			expect(result.issues.length).toBe(0);
 478 | 		});
 479 | 	});
 480 | 
 481 | 	describe('removeDuplicateDependencies function', () => {
 482 | 		test('should remove duplicate dependencies from tasks', () => {
 483 | 			const tasksData = {
 484 | 				tasks: [
 485 | 					{ id: 1, dependencies: [2, 2, 3, 3, 3] },
 486 | 					{ id: 2, dependencies: [3] },
 487 | 					{ id: 3, dependencies: [] }
 488 | 				]
 489 | 			};
 490 | 
 491 | 			const result = removeDuplicateDependencies(tasksData);
 492 | 
 493 | 			expect(result.tasks[0].dependencies).toEqual([2, 3]);
 494 | 			expect(result.tasks[1].dependencies).toEqual([3]);
 495 | 			expect(result.tasks[2].dependencies).toEqual([]);
 496 | 		});
 497 | 
 498 | 		test('should handle empty dependencies array', () => {
 499 | 			const tasksData = {
 500 | 				tasks: [
 501 | 					{ id: 1, dependencies: [] },
 502 | 					{ id: 2, dependencies: [1] }
 503 | 				]
 504 | 			};
 505 | 
 506 | 			const result = removeDuplicateDependencies(tasksData);
 507 | 
 508 | 			expect(result.tasks[0].dependencies).toEqual([]);
 509 | 			expect(result.tasks[1].dependencies).toEqual([1]);
 510 | 		});
 511 | 
 512 | 		test('should handle tasks with no dependencies property', () => {
 513 | 			const tasksData = {
 514 | 				tasks: [
 515 | 					{ id: 1 }, // No dependencies property
 516 | 					{ id: 2, dependencies: [1] }
 517 | 				]
 518 | 			};
 519 | 
 520 | 			const result = removeDuplicateDependencies(tasksData);
 521 | 
 522 | 			expect(result.tasks[0]).not.toHaveProperty('dependencies');
 523 | 			expect(result.tasks[1].dependencies).toEqual([1]);
 524 | 		});
 525 | 	});
 526 | 
 527 | 	describe('cleanupSubtaskDependencies function', () => {
 528 | 		test('should remove dependencies to non-existent subtasks', () => {
 529 | 			const tasksData = {
 530 | 				tasks: [
 531 | 					{
 532 | 						id: 1,
 533 | 						dependencies: [],
 534 | 						subtasks: [
 535 | 							{ id: 1, dependencies: [] },
 536 | 							{ id: 2, dependencies: [3] } // Dependency 3 doesn't exist
 537 | 						]
 538 | 					},
 539 | 					{
 540 | 						id: 2,
 541 | 						dependencies: ['1.2'], // Valid subtask dependency
 542 | 						subtasks: [
 543 | 							{ id: 1, dependencies: ['1.1'] } // Valid subtask dependency
 544 | 						]
 545 | 					}
 546 | 				]
 547 | 			};
 548 | 
 549 | 			const result = cleanupSubtaskDependencies(tasksData);
 550 | 
 551 | 			// Should remove the invalid dependency to subtask 3
 552 | 			expect(result.tasks[0].subtasks[1].dependencies).toEqual([]);
 553 | 			// Should keep valid dependencies
 554 | 			expect(result.tasks[1].dependencies).toEqual(['1.2']);
 555 | 			expect(result.tasks[1].subtasks[0].dependencies).toEqual(['1.1']);
 556 | 		});
 557 | 
 558 | 		test('should handle tasks without subtasks', () => {
 559 | 			const tasksData = {
 560 | 				tasks: [
 561 | 					{ id: 1, dependencies: [] },
 562 | 					{ id: 2, dependencies: [1] }
 563 | 				]
 564 | 			};
 565 | 
 566 | 			const result = cleanupSubtaskDependencies(tasksData);
 567 | 
 568 | 			// Should return the original data unchanged
 569 | 			expect(result).toEqual(tasksData);
 570 | 		});
 571 | 	});
 572 | 
 573 | 	describe('ensureAtLeastOneIndependentSubtask function', () => {
 574 | 		test('should clear dependencies of first subtask if none are independent', () => {
 575 | 			const tasksData = {
 576 | 				tasks: [
 577 | 					{
 578 | 						id: 1,
 579 | 						subtasks: [
 580 | 							{ id: 1, dependencies: [2] },
 581 | 							{ id: 2, dependencies: [1] }
 582 | 						]
 583 | 					}
 584 | 				]
 585 | 			};
 586 | 
 587 | 			const result = ensureAtLeastOneIndependentSubtask(tasksData);
 588 | 
 589 | 			expect(result).toBe(true);
 590 | 			expect(tasksData.tasks[0].subtasks[0].dependencies).toEqual([]);
 591 | 			expect(tasksData.tasks[0].subtasks[1].dependencies).toEqual([1]);
 592 | 		});
 593 | 
 594 | 		test('should not modify tasks if at least one subtask is independent', () => {
 595 | 			const tasksData = {
 596 | 				tasks: [
 597 | 					{
 598 | 						id: 1,
 599 | 						subtasks: [
 600 | 							{ id: 1, dependencies: [] },
 601 | 							{ id: 2, dependencies: [1] }
 602 | 						]
 603 | 					}
 604 | 				]
 605 | 			};
 606 | 
 607 | 			const result = ensureAtLeastOneIndependentSubtask(tasksData);
 608 | 
 609 | 			expect(result).toBe(false);
 610 | 			expect(tasksData.tasks[0].subtasks[0].dependencies).toEqual([]);
 611 | 			expect(tasksData.tasks[0].subtasks[1].dependencies).toEqual([1]);
 612 | 		});
 613 | 
 614 | 		test('should handle tasks without subtasks', () => {
 615 | 			const tasksData = {
 616 | 				tasks: [{ id: 1 }, { id: 2, dependencies: [1] }]
 617 | 			};
 618 | 
 619 | 			const result = ensureAtLeastOneIndependentSubtask(tasksData);
 620 | 
 621 | 			expect(result).toBe(false);
 622 | 			expect(tasksData).toEqual({
 623 | 				tasks: [{ id: 1 }, { id: 2, dependencies: [1] }]
 624 | 			});
 625 | 		});
 626 | 
 627 | 		test('should handle empty subtasks array', () => {
 628 | 			const tasksData = {
 629 | 				tasks: [{ id: 1, subtasks: [] }]
 630 | 			};
 631 | 
 632 | 			const result = ensureAtLeastOneIndependentSubtask(tasksData);
 633 | 
 634 | 			expect(result).toBe(false);
 635 | 			expect(tasksData).toEqual({
 636 | 				tasks: [{ id: 1, subtasks: [] }]
 637 | 			});
 638 | 		});
 639 | 	});
 640 | 
 641 | 	describe('validateAndFixDependencies function', () => {
 642 | 		test('should fix multiple dependency issues and return true if changes made', () => {
 643 | 			const tasksData = {
 644 | 				tasks: [
 645 | 					{
 646 | 						id: 1,
 647 | 						dependencies: [1, 1, 99], // Self-dependency and duplicate and invalid dependency
 648 | 						subtasks: [
 649 | 							{ id: 1, dependencies: [2, 2] }, // Duplicate dependencies
 650 | 							{ id: 2, dependencies: [1] }
 651 | 						]
 652 | 					},
 653 | 					{
 654 | 						id: 2,
 655 | 						dependencies: [1],
 656 | 						subtasks: [
 657 | 							{ id: 1, dependencies: [99] } // Invalid dependency
 658 | 						]
 659 | 					}
 660 | 				]
 661 | 			};
 662 | 
 663 | 			// Mock taskExists for validating dependencies
 664 | 			mockTaskExists.mockImplementation((tasks, id) => {
 665 | 				// Convert id to string for comparison
 666 | 				const idStr = String(id);
 667 | 
 668 | 				// Handle subtask references (e.g., "1.2")
 669 | 				if (idStr.includes('.')) {
 670 | 					const [parentId, subtaskId] = idStr.split('.').map(Number);
 671 | 					const task = tasks.find((t) => t.id === parentId);
 672 | 					return (
 673 | 						task &&
 674 | 						task.subtasks &&
 675 | 						task.subtasks.some((st) => st.id === subtaskId)
 676 | 					);
 677 | 				}
 678 | 
 679 | 				// Handle regular task references
 680 | 				const taskId = parseInt(idStr, 10);
 681 | 				return taskId === 1 || taskId === 2; // Only tasks 1 and 2 exist
 682 | 			});
 683 | 
 684 | 			// Make a copy for verification that original is modified
 685 | 			const originalData = JSON.parse(JSON.stringify(tasksData));
 686 | 
 687 | 			const result = validateAndFixDependencies(tasksData);
 688 | 
 689 | 			expect(result).toBe(true);
 690 | 			// Check that data has been modified
 691 | 			expect(tasksData).not.toEqual(originalData);
 692 | 
 693 | 			// Check specific changes
 694 | 			// 1. Self-dependency removed
 695 | 			expect(tasksData.tasks[0].dependencies).not.toContain(1);
 696 | 			// 2. Invalid dependency removed
 697 | 			expect(tasksData.tasks[0].dependencies).not.toContain(99);
 698 | 			// 3. Dependencies have been deduplicated
 699 | 			if (tasksData.tasks[0].subtasks[0].dependencies.length > 0) {
 700 | 				expect(tasksData.tasks[0].subtasks[0].dependencies).toEqual(
 701 | 					expect.arrayContaining([])
 702 | 				);
 703 | 			}
 704 | 			// 4. Invalid subtask dependency removed
 705 | 			expect(tasksData.tasks[1].subtasks[0].dependencies).toEqual([]);
 706 | 
 707 | 			// IMPORTANT: Verify no calls to writeJSON with actual tasks.json
 708 | 			expect(mockWriteJSON).not.toHaveBeenCalledWith(
 709 | 				'tasks/tasks.json',
 710 | 				expect.anything(),
 711 | 				expect.anything(),
 712 | 				expect.anything()
 713 | 			);
 714 | 		});
 715 | 
 716 | 		test('should return false if no changes needed', () => {
 717 | 			const tasksData = {
 718 | 				tasks: [
 719 | 					{
 720 | 						id: 1,
 721 | 						dependencies: [],
 722 | 						subtasks: [
 723 | 							{ id: 1, dependencies: [] }, // Already has an independent subtask
 724 | 							{ id: 2, dependencies: ['1.1'] }
 725 | 						]
 726 | 					},
 727 | 					{
 728 | 						id: 2,
 729 | 						dependencies: [1]
 730 | 					}
 731 | 				]
 732 | 			};
 733 | 
 734 | 			// Mock taskExists to validate all dependencies as valid
 735 | 			mockTaskExists.mockImplementation((tasks, id) => {
 736 | 				// Convert id to string for comparison
 737 | 				const idStr = String(id);
 738 | 
 739 | 				// Handle subtask references
 740 | 				if (idStr.includes('.')) {
 741 | 					const [parentId, subtaskId] = idStr.split('.').map(Number);
 742 | 					const task = tasks.find((t) => t.id === parentId);
 743 | 					return (
 744 | 						task &&
 745 | 						task.subtasks &&
 746 | 						task.subtasks.some((st) => st.id === subtaskId)
 747 | 					);
 748 | 				}
 749 | 
 750 | 				// Handle regular task references
 751 | 				const taskId = parseInt(idStr, 10);
 752 | 				return taskId === 1 || taskId === 2;
 753 | 			});
 754 | 
 755 | 			const originalData = JSON.parse(JSON.stringify(tasksData));
 756 | 			const result = validateAndFixDependencies(tasksData);
 757 | 
 758 | 			expect(result).toBe(false);
 759 | 			// Verify data is unchanged
 760 | 			expect(tasksData).toEqual(originalData);
 761 | 
 762 | 			// IMPORTANT: Verify no calls to writeJSON with actual tasks.json
 763 | 			expect(mockWriteJSON).not.toHaveBeenCalledWith(
 764 | 				'tasks/tasks.json',
 765 | 				expect.anything(),
 766 | 				expect.anything(),
 767 | 				expect.anything()
 768 | 			);
 769 | 		});
 770 | 
 771 | 		test('should handle invalid input', () => {
 772 | 			expect(validateAndFixDependencies(null)).toBe(false);
 773 | 			expect(validateAndFixDependencies({})).toBe(false);
 774 | 			expect(validateAndFixDependencies({ tasks: null })).toBe(false);
 775 | 			expect(validateAndFixDependencies({ tasks: 'not an array' })).toBe(false);
 776 | 
 777 | 			// IMPORTANT: Verify no calls to writeJSON with actual tasks.json
 778 | 			expect(mockWriteJSON).not.toHaveBeenCalledWith(
 779 | 				'tasks/tasks.json',
 780 | 				expect.anything(),
 781 | 				expect.anything(),
 782 | 				expect.anything()
 783 | 			);
 784 | 		});
 785 | 
 786 | 		test('should save changes when tasksPath is provided', () => {
 787 | 			const tasksData = {
 788 | 				tasks: [
 789 | 					{
 790 | 						id: 1,
 791 | 						dependencies: [1, 1], // Self-dependency and duplicate
 792 | 						subtasks: [
 793 | 							{ id: 1, dependencies: [99] } // Invalid dependency
 794 | 						]
 795 | 					}
 796 | 				]
 797 | 			};
 798 | 
 799 | 			// Mock taskExists for this specific test
 800 | 			mockTaskExists.mockImplementation((tasks, id) => {
 801 | 				// Convert id to string for comparison
 802 | 				const idStr = String(id);
 803 | 
 804 | 				// Handle subtask references
 805 | 				if (idStr.includes('.')) {
 806 | 					const [parentId, subtaskId] = idStr.split('.').map(Number);
 807 | 					const task = tasks.find((t) => t.id === parentId);
 808 | 					return (
 809 | 						task &&
 810 | 						task.subtasks &&
 811 | 						task.subtasks.some((st) => st.id === subtaskId)
 812 | 					);
 813 | 				}
 814 | 
 815 | 				// Handle regular task references
 816 | 				const taskId = parseInt(idStr, 10);
 817 | 				return taskId === 1; // Only task 1 exists
 818 | 			});
 819 | 
 820 | 			// Copy the original data to verify changes
 821 | 			const originalData = JSON.parse(JSON.stringify(tasksData));
 822 | 
 823 | 			// Call the function with our test path instead of the actual tasks.json
 824 | 			const result = validateAndFixDependencies(tasksData, TEST_TASKS_PATH);
 825 | 
 826 | 			// First verify that the result is true (changes were made)
 827 | 			expect(result).toBe(true);
 828 | 
 829 | 			// Verify the data was modified
 830 | 			expect(tasksData).not.toEqual(originalData);
 831 | 
 832 | 			// IMPORTANT: Verify no calls to writeJSON with actual tasks.json
 833 | 			expect(mockWriteJSON).not.toHaveBeenCalledWith(
 834 | 				'tasks/tasks.json',
 835 | 				expect.anything(),
 836 | 				expect.anything(),
 837 | 				expect.anything()
 838 | 			);
 839 | 		});
 840 | 	});
 841 | 
 842 | 	describe('canMoveWithDependencies', () => {
 843 | 		it('should return canMove: false when conflicts exist', () => {
 844 | 			const allTasks = [
 845 | 				{
 846 | 					id: 1,
 847 | 					tag: 'source',
 848 | 					dependencies: [2],
 849 | 					title: 'Task 1'
 850 | 				},
 851 | 				{
 852 | 					id: 2,
 853 | 					tag: 'other',
 854 | 					dependencies: [],
 855 | 					title: 'Task 2'
 856 | 				}
 857 | 			];
 858 | 
 859 | 			const result = canMoveWithDependencies('1', 'source', 'target', allTasks);
 860 | 
 861 | 			expect(result.canMove).toBe(false);
 862 | 			expect(result.conflicts).toBeDefined();
 863 | 			expect(result.conflicts.length).toBeGreaterThan(0);
 864 | 			expect(result.dependentTaskIds).toBeDefined();
 865 | 		});
 866 | 
 867 | 		it('should return canMove: true when no conflicts exist', () => {
 868 | 			const allTasks = [
 869 | 				{
 870 | 					id: 1,
 871 | 					tag: 'source',
 872 | 					dependencies: [],
 873 | 					title: 'Task 1'
 874 | 				},
 875 | 				{
 876 | 					id: 2,
 877 | 					tag: 'target',
 878 | 					dependencies: [],
 879 | 					title: 'Task 2'
 880 | 				}
 881 | 			];
 882 | 
 883 | 			const result = canMoveWithDependencies('1', 'source', 'target', allTasks);
 884 | 
 885 | 			expect(result.canMove).toBe(true);
 886 | 			expect(result.conflicts).toBeDefined();
 887 | 			expect(result.conflicts.length).toBe(0);
 888 | 			expect(result.dependentTaskIds).toBeDefined();
 889 | 			expect(result.dependentTaskIds.length).toBe(0);
 890 | 		});
 891 | 
 892 | 		it('should handle subtask lookup correctly', () => {
 893 | 			const allTasks = [
 894 | 				{
 895 | 					id: 1,
 896 | 					tag: 'source',
 897 | 					dependencies: [],
 898 | 					title: 'Parent Task',
 899 | 					subtasks: [
 900 | 						{
 901 | 							id: 1,
 902 | 							dependencies: [2],
 903 | 							title: 'Subtask 1'
 904 | 						}
 905 | 					]
 906 | 				},
 907 | 				{
 908 | 					id: 2,
 909 | 					tag: 'other',
 910 | 					dependencies: [],
 911 | 					title: 'Task 2'
 912 | 				}
 913 | 			];
 914 | 
 915 | 			const result = canMoveWithDependencies(
 916 | 				'1.1',
 917 | 				'source',
 918 | 				'target',
 919 | 				allTasks
 920 | 			);
 921 | 
 922 | 			expect(result.canMove).toBe(false);
 923 | 			expect(result.conflicts).toBeDefined();
 924 | 			expect(result.conflicts.length).toBeGreaterThan(0);
 925 | 		});
 926 | 
 927 | 		it('should return error when task not found', () => {
 928 | 			const allTasks = [
 929 | 				{
 930 | 					id: 1,
 931 | 					tag: 'source',
 932 | 					dependencies: [],
 933 | 					title: 'Task 1'
 934 | 				}
 935 | 			];
 936 | 
 937 | 			const result = canMoveWithDependencies(
 938 | 				'999',
 939 | 				'source',
 940 | 				'target',
 941 | 				allTasks
 942 | 			);
 943 | 
 944 | 			expect(result.canMove).toBe(false);
 945 | 			expect(result.error).toBe('Task not found');
 946 | 			expect(result.dependentTaskIds).toEqual([]);
 947 | 			expect(result.conflicts).toEqual([]);
 948 | 		});
 949 | 	});
 950 | 
 951 | 	describe('Cross-level dependency tests (Issue #542)', () => {
 952 | 		let originalExit;
 953 | 
 954 | 		beforeEach(async () => {
 955 | 			// Ensure a fresh module instance so ESM mocks apply to dynamic imports
 956 | 			jest.resetModules();
 957 | 			originalExit = process.exit;
 958 | 			process.exit = jest.fn();
 959 | 
 960 | 			// For ESM dynamic imports, use the same pattern
 961 | 			await jest.unstable_mockModule('../../scripts/modules/utils.js', () => ({
 962 | 				log: mockLog,
 963 | 				readJSON: mockReadJSON,
 964 | 				writeJSON: mockWriteJSON,
 965 | 				taskExists: mockTaskExists,
 966 | 				formatTaskId: mockFormatTaskId,
 967 | 				findCycles: mockFindCycles,
 968 | 				traverseDependencies: jest.fn(() => []),
 969 | 				isSilentMode: jest.fn(() => true),
 970 | 				findProjectRoot: jest.fn(() => '/test'),
 971 | 				resolveEnvVariable: jest.fn(() => undefined),
 972 | 				isEmpty: jest.fn((v) =>
 973 | 					v == null
 974 | 						? true
 975 | 						: Array.isArray(v)
 976 | 							? v.length === 0
 977 | 							: typeof v === 'object'
 978 | 								? Object.keys(v).length === 0
 979 | 								: false
 980 | 				),
 981 | 				enableSilentMode: jest.fn(),
 982 | 				disableSilentMode: jest.fn(),
 983 | 				getTaskManager: jest.fn(async () => ({})),
 984 | 				getTagAwareFilePath: jest.fn(
 985 | 					(basePath, _tag, projectRoot = '.') => basePath
 986 | 				),
 987 | 				readComplexityReport: jest.fn(() => null)
 988 | 			}));
 989 | 
 990 | 			// Also mock transitive imports to keep dependency surface minimal
 991 | 			await jest.unstable_mockModule('../../scripts/modules/ui.js', () => ({
 992 | 				displayBanner: jest.fn()
 993 | 			}));
 994 | 			await jest.unstable_mockModule(
 995 | 				'../../scripts/modules/task-manager/generate-task-files.js',
 996 | 				() => ({ default: jest.fn() })
 997 | 			);
 998 | 			// Set up test data that matches the issue report
 999 | 			// Clone fixture data before each test to prevent mutation issues
1000 | 			mockReadJSON.mockImplementation(() =>
1001 | 				structuredClone(crossLevelDependencyTasks)
1002 | 			);
1003 | 
1004 | 			// Configure mockTaskExists to properly validate cross-level dependencies
1005 | 			mockTaskExists.mockImplementation((tasks, taskId) => {
1006 | 				if (typeof taskId === 'string' && taskId.includes('.')) {
1007 | 					const [parentId, subtaskId] = taskId.split('.').map(Number);
1008 | 					const task = tasks.find((t) => t.id === parentId);
1009 | 					return (
1010 | 						task &&
1011 | 						task.subtasks &&
1012 | 						task.subtasks.some((st) => st.id === subtaskId)
1013 | 					);
1014 | 				}
1015 | 
1016 | 				const numericId =
1017 | 					typeof taskId === 'string' ? parseInt(taskId, 10) : taskId;
1018 | 				return tasks.some((task) => task.id === numericId);
1019 | 			});
1020 | 
1021 | 			mockFormatTaskId.mockImplementation((id) => {
1022 | 				if (typeof id === 'string' && id.includes('.')) return id; // keep dot notation
1023 | 				return parseInt(id, 10); // normalize top-level task IDs to number
1024 | 			});
1025 | 		});
1026 | 
1027 | 		afterEach(() => {
1028 | 			process.exit = originalExit;
1029 | 		});
1030 | 
1031 | 		test('should allow subtask to depend on top-level task', async () => {
1032 | 			const { addDependency } = await import(
1033 | 				'../../scripts/modules/dependency-manager.js'
1034 | 			);
1035 | 
1036 | 			// Test the specific scenario from Issue #542: subtask 2.2 depending on task 11
1037 | 			await addDependency(TEST_TASKS_PATH, '2.2', 11, { projectRoot: '/test' });
1038 | 
1039 | 			// Verify we wrote to the test path (and not the real tasks.json)
1040 | 			expect(mockWriteJSON).toHaveBeenCalledWith(
1041 | 				TEST_TASKS_PATH,
1042 | 				expect.anything(),
1043 | 				'/test',
1044 | 				undefined
1045 | 			);
1046 | 			expect(mockWriteJSON).not.toHaveBeenCalledWith(
1047 | 				'tasks/tasks.json',
1048 | 				expect.anything(),
1049 | 				expect.anything(),
1050 | 				expect.anything()
1051 | 			);
1052 | 			// Get the specific write call for TEST_TASKS_PATH
1053 | 			const writeCall = mockWriteJSON.mock.calls.find(
1054 | 				([p]) => p === TEST_TASKS_PATH
1055 | 			);
1056 | 			expect(writeCall).toBeDefined();
1057 | 			const savedData = writeCall[1];
1058 | 			const parent2 = savedData.tasks.find((t) => t.id === 2);
1059 | 			const subtask22 = parent2.subtasks.find((st) => st.id === 2);
1060 | 
1061 | 			// Verify the dependency was actually added to subtask 2.2
1062 | 			expect(subtask22.dependencies).toContain(11);
1063 | 			// Also verify a success log was emitted
1064 | 			const successCall = mockLog.mock.calls.find(
1065 | 				([level]) => level === 'success'
1066 | 			);
1067 | 			expect(successCall).toBeDefined();
1068 | 			expect(successCall[1]).toContain('2.2');
1069 | 			expect(successCall[1]).toContain('11');
1070 | 		});
1071 | 
1072 | 		test('should allow top-level task to depend on subtask', async () => {
1073 | 			const { addDependency } = await import(
1074 | 				'../../scripts/modules/dependency-manager.js'
1075 | 			);
1076 | 
1077 | 			// Test reverse scenario: task 11 depending on subtask 2.1
1078 | 			await addDependency(TEST_TASKS_PATH, 11, '2.1', { projectRoot: '/test' });
1079 | 
1080 | 			// Stronger assertions for writeJSON call and locating the correct task
1081 | 			expect(mockWriteJSON).toHaveBeenCalledWith(
1082 | 				TEST_TASKS_PATH,
1083 | 				expect.anything(),
1084 | 				'/test',
1085 | 				undefined
1086 | 			);
1087 | 			expect(mockWriteJSON).not.toHaveBeenCalledWith(
1088 | 				'tasks/tasks.json',
1089 | 				expect.anything(),
1090 | 				expect.anything(),
1091 | 				expect.anything()
1092 | 			);
1093 | 			const writeCall = mockWriteJSON.mock.calls.find(
1094 | 				([p]) => p === TEST_TASKS_PATH
1095 | 			);
1096 | 			expect(writeCall).toBeDefined();
1097 | 			const savedData = writeCall[1];
1098 | 			const task11 = savedData.tasks.find((t) => t.id === 11);
1099 | 
1100 | 			// Verify the dependency was actually added to task 11
1101 | 			expect(task11.dependencies).toContain('2.1');
1102 | 			// Verify a success log was emitted mentioning both task 11 and subtask 2.1
1103 | 			const successCall = mockLog.mock.calls.find(
1104 | 				([level]) => level === 'success'
1105 | 			);
1106 | 			expect(successCall).toBeDefined();
1107 | 			expect(successCall[1]).toContain('11');
1108 | 			expect(successCall[1]).toContain('2.1');
1109 | 		});
1110 | 
1111 | 		test('should properly validate cross-level dependencies exist', async () => {
1112 | 			// Test that validation correctly identifies when a cross-level dependency target doesn't exist
1113 | 			mockTaskExists.mockImplementation((tasks, taskId) => {
1114 | 				// Simulate task 99 not existing
1115 | 				if (taskId === '99' || taskId === 99) {
1116 | 					return false;
1117 | 				}
1118 | 
1119 | 				if (typeof taskId === 'string' && taskId.includes('.')) {
1120 | 					const [parentId, subtaskId] = taskId.split('.').map(Number);
1121 | 					const task = tasks.find((t) => t.id === parentId);
1122 | 					return (
1123 | 						task &&
1124 | 						task.subtasks &&
1125 | 						task.subtasks.some((st) => st.id === subtaskId)
1126 | 					);
1127 | 				}
1128 | 
1129 | 				const numericId =
1130 | 					typeof taskId === 'string' ? parseInt(taskId, 10) : taskId;
1131 | 				return tasks.some((task) => task.id === numericId);
1132 | 			});
1133 | 
1134 | 			const { addDependency } = await import(
1135 | 				'../../scripts/modules/dependency-manager.js'
1136 | 			);
1137 | 
1138 | 			const exitError = new Error('process.exit invoked');
1139 | 			process.exit.mockImplementation(() => {
1140 | 				throw exitError;
1141 | 			});
1142 | 
1143 | 			await expect(
1144 | 				addDependency(TEST_TASKS_PATH, '2.2', 99, { projectRoot: '/test' })
1145 | 			).rejects.toBe(exitError);
1146 | 
1147 | 			expect(process.exit).toHaveBeenCalledWith(1);
1148 | 			expect(mockWriteJSON).not.toHaveBeenCalled();
1149 | 			// Verify that an error was reported to the user
1150 | 			expect(mockLog).toHaveBeenCalled();
1151 | 		});
1152 | 
1153 | 		test('should remove top-level task dependency from a subtask', async () => {
1154 | 			const { addDependency, removeDependency } = await import(
1155 | 				'../../scripts/modules/dependency-manager.js'
1156 | 			);
1157 | 
1158 | 			// Start with cloned data and add 11 to 2.2
1159 | 			await addDependency(TEST_TASKS_PATH, '2.2', 11, { projectRoot: '/test' });
1160 | 
1161 | 			// Get the saved data from the add operation
1162 | 			const addWriteCall = mockWriteJSON.mock.calls.find(
1163 | 				([p]) => p === TEST_TASKS_PATH
1164 | 			);
1165 | 			expect(addWriteCall).toBeDefined();
1166 | 			const dataWithDep = addWriteCall[1];
1167 | 
1168 | 			// Verify the dependency was added
1169 | 			const subtask22AfterAdd = dataWithDep.tasks
1170 | 				.find((t) => t.id === 2)
1171 | 				.subtasks.find((st) => st.id === 2);
1172 | 			expect(subtask22AfterAdd.dependencies).toContain(11);
1173 | 
1174 | 			// Clear mocks and re-setup mockReadJSON with the modified data
1175 | 			jest.clearAllMocks();
1176 | 			mockReadJSON.mockImplementation(() => structuredClone(dataWithDep));
1177 | 
1178 | 			await removeDependency(TEST_TASKS_PATH, '2.2', 11, {
1179 | 				projectRoot: '/test'
1180 | 			});
1181 | 
1182 | 			const writeCall = mockWriteJSON.mock.calls.find(
1183 | 				([p]) => p === TEST_TASKS_PATH
1184 | 			);
1185 | 			expect(writeCall).toBeDefined();
1186 | 			const saved = writeCall[1];
1187 | 			const subtask22 = saved.tasks
1188 | 				.find((t) => t.id === 2)
1189 | 				.subtasks.find((st) => st.id === 2);
1190 | 			expect(subtask22.dependencies).not.toContain(11);
1191 | 			// Verify success log was emitted
1192 | 			const successCall = mockLog.mock.calls.find(
1193 | 				([level]) => level === 'success'
1194 | 			);
1195 | 			expect(successCall).toBeDefined();
1196 | 			expect(successCall[1]).toContain('2.2');
1197 | 			expect(successCall[1]).toContain('11');
1198 | 		});
1199 | 
1200 | 		test('should remove subtask dependency from a top-level task', async () => {
1201 | 			const { addDependency, removeDependency } = await import(
1202 | 				'../../scripts/modules/dependency-manager.js'
1203 | 			);
1204 | 
1205 | 			// Add subtask dependency to task 11
1206 | 			await addDependency(TEST_TASKS_PATH, 11, '2.1', { projectRoot: '/test' });
1207 | 
1208 | 			// Get the saved data from the add operation
1209 | 			const addWriteCall = mockWriteJSON.mock.calls.find(
1210 | 				([p]) => p === TEST_TASKS_PATH
1211 | 			);
1212 | 			expect(addWriteCall).toBeDefined();
1213 | 			const dataWithDep = addWriteCall[1];
1214 | 
1215 | 			// Verify the dependency was added
1216 | 			const task11AfterAdd = dataWithDep.tasks.find((t) => t.id === 11);
1217 | 			expect(task11AfterAdd.dependencies).toContain('2.1');
1218 | 
1219 | 			// Clear mocks and re-setup mockReadJSON with the modified data
1220 | 			jest.clearAllMocks();
1221 | 			mockReadJSON.mockImplementation(() => structuredClone(dataWithDep));
1222 | 
1223 | 			await removeDependency(TEST_TASKS_PATH, 11, '2.1', {
1224 | 				projectRoot: '/test'
1225 | 			});
1226 | 
1227 | 			const writeCall = mockWriteJSON.mock.calls.find(
1228 | 				([p]) => p === TEST_TASKS_PATH
1229 | 			);
1230 | 			expect(writeCall).toBeDefined();
1231 | 			const saved = writeCall[1];
1232 | 			const task11 = saved.tasks.find((t) => t.id === 11);
1233 | 			expect(task11.dependencies).not.toContain('2.1');
1234 | 			// Verify success log was emitted
1235 | 			const successCall = mockLog.mock.calls.find(
1236 | 				([level]) => level === 'success'
1237 | 			);
1238 | 			expect(successCall).toBeDefined();
1239 | 			expect(successCall[1]).toContain('11');
1240 | 			expect(successCall[1]).toContain('2.1');
1241 | 		});
1242 | 	});
1243 | });
1244 | 
```
Page 53/69FirstPrevNextLast