#
tokens: 48981/50000 9/975 files (page 29/50)
lines: off (toggle) GitHub
raw markdown copy
This is page 29 of 50. Use http://codebase.md/eyaltoledano/claude-task-master?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

--------------------------------------------------------------------------------
/tests/unit/scripts/modules/ui/cross-tag-error-display.test.js:
--------------------------------------------------------------------------------

```javascript
import { jest } from '@jest/globals';
import {
	displayCrossTagDependencyError,
	displaySubtaskMoveError,
	displayInvalidTagCombinationError,
	displayDependencyValidationHints,
	formatTaskIdForDisplay
} from '../../../../../scripts/modules/ui.js';

// Mock console.log to capture output
const originalConsoleLog = console.log;
const mockConsoleLog = jest.fn();
global.console.log = mockConsoleLog;

// Add afterAll hook to restore
afterAll(() => {
	global.console.log = originalConsoleLog;
});

describe('Cross-Tag Error Display Functions', () => {
	beforeEach(() => {
		mockConsoleLog.mockClear();
	});

	describe('displayCrossTagDependencyError', () => {
		it('should display cross-tag dependency error with conflicts', () => {
			const conflicts = [
				{
					taskId: 1,
					dependencyId: 2,
					dependencyTag: 'backlog',
					message: 'Task 1 depends on 2 (in backlog)'
				},
				{
					taskId: 3,
					dependencyId: 4,
					dependencyTag: 'done',
					message: 'Task 3 depends on 4 (in done)'
				}
			];

			displayCrossTagDependencyError(conflicts, 'in-progress', 'done', '1,3');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move tasks from "in-progress" to "done"'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Cross-tag dependency conflicts detected:')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Task 1 depends on 2 (in backlog)')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Task 3 depends on 4 (in done)')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Resolution options:')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('--with-dependencies')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('--ignore-dependencies')
			);
		});

		it('should handle empty conflicts array', () => {
			displayCrossTagDependencyError([], 'backlog', 'done', '1');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('❌ Cannot move tasks from "backlog" to "done"')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Cross-tag dependency conflicts detected:')
			);
		});
	});

	describe('displaySubtaskMoveError', () => {
		it('should display subtask movement restriction error', () => {
			displaySubtaskMoveError('5.2', 'backlog', 'in-progress');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask 5.2 directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Subtask movement restriction:')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'• Subtasks cannot be moved directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Resolution options:')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('remove-subtask --id=5.2 --convert')
			);
		});

		it('should handle nested subtask IDs (three levels)', () => {
			displaySubtaskMoveError('5.2.1', 'feature-auth', 'production');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask 5.2.1 directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('remove-subtask --id=5.2.1 --convert')
			);
		});

		it('should handle deeply nested subtask IDs (four levels)', () => {
			displaySubtaskMoveError('10.3.2.1', 'development', 'testing');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask 10.3.2.1 directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('remove-subtask --id=10.3.2.1 --convert')
			);
		});

		it('should handle single-level subtask IDs', () => {
			displaySubtaskMoveError('15.1', 'master', 'feature-branch');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask 15.1 directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('remove-subtask --id=15.1 --convert')
			);
		});

		it('should handle invalid subtask ID format gracefully', () => {
			displaySubtaskMoveError('invalid-id', 'tag1', 'tag2');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask invalid-id directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('remove-subtask --id=invalid-id --convert')
			);
		});

		it('should handle empty subtask ID', () => {
			displaySubtaskMoveError('', 'source', 'target');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					`❌ Cannot move subtask ${formatTaskIdForDisplay('')} directly between tags`
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					`remove-subtask --id=${formatTaskIdForDisplay('')} --convert`
				)
			);
		});

		it('should handle null subtask ID', () => {
			displaySubtaskMoveError(null, 'source', 'target');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask null directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('remove-subtask --id=null --convert')
			);
		});

		it('should handle undefined subtask ID', () => {
			displaySubtaskMoveError(undefined, 'source', 'target');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask undefined directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('remove-subtask --id=undefined --convert')
			);
		});

		it('should handle special characters in subtask ID', () => {
			displaySubtaskMoveError('5.2@test', 'dev', 'prod');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask 5.2@test directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('remove-subtask --id=5.2@test --convert')
			);
		});

		it('should handle numeric subtask IDs', () => {
			displaySubtaskMoveError('123.456', 'alpha', 'beta');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask 123.456 directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('remove-subtask --id=123.456 --convert')
			);
		});

		it('should handle identical source and target tags', () => {
			displaySubtaskMoveError('7.3', 'same-tag', 'same-tag');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask 7.3 directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Source tag: "same-tag"')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Target tag: "same-tag"')
			);
		});

		it('should handle empty tag names', () => {
			displaySubtaskMoveError('9.1', '', '');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask 9.1 directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Source tag: ""')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Target tag: ""')
			);
		});

		it('should handle null tag names', () => {
			displaySubtaskMoveError('12.4', null, null);

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask 12.4 directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Source tag: "null"')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Target tag: "null"')
			);
		});

		it('should handle complex tag names with special characters', () => {
			displaySubtaskMoveError(
				'3.2.1',
				'feature/[email protected]',
				'production@stable'
			);

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask 3.2.1 directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Source tag: "feature/[email protected]"')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Target tag: "production@stable"')
			);
		});

		it('should handle very long subtask IDs', () => {
			const longId = '1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20';
			displaySubtaskMoveError(longId, 'short', 'long');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					`❌ Cannot move subtask ${longId} directly between tags`
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(`remove-subtask --id=${longId} --convert`)
			);
		});

		it('should handle whitespace in subtask ID', () => {
			displaySubtaskMoveError(' 5.2 ', 'clean', 'dirty');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'❌ Cannot move subtask  5.2  directly between tags'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('remove-subtask --id= 5.2  --convert')
			);
		});
	});

	describe('displayInvalidTagCombinationError', () => {
		it('should display invalid tag combination error', () => {
			displayInvalidTagCombinationError(
				'backlog',
				'backlog',
				'Source and target tags are identical'
			);

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('❌ Invalid tag combination')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Error details:')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Source tag: "backlog"')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('• Target tag: "backlog"')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'• Reason: Source and target tags are identical'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Resolution options:')
			);
		});
	});

	describe('displayDependencyValidationHints', () => {
		it('should display general hints by default', () => {
			displayDependencyValidationHints();

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Helpful hints:')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('💡 Use "task-master validate-dependencies"')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('💡 Use "task-master fix-dependencies"')
			);
		});

		it('should display before-move hints', () => {
			displayDependencyValidationHints('before-move');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Helpful hints:')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'💡 Tip: Run "task-master validate-dependencies"'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('💡 Tip: Use "task-master fix-dependencies"')
			);
		});

		it('should display after-error hints', () => {
			displayDependencyValidationHints('after-error');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Helpful hints:')
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'🔧 Quick fix: Run "task-master validate-dependencies"'
				)
			);
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining(
					'🔧 Quick fix: Use "task-master fix-dependencies"'
				)
			);
		});

		it('should handle unknown context gracefully', () => {
			displayDependencyValidationHints('unknown-context');

			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('Helpful hints:')
			);
			// Should fall back to general hints
			expect(mockConsoleLog).toHaveBeenCalledWith(
				expect.stringContaining('💡 Use "task-master validate-dependencies"')
			);
		});
	});
});

/**
 * Test for ID type consistency in dependency comparisons
 * This test verifies that the fix for mixed string/number ID comparison issues works correctly
 */

describe('ID Type Consistency in Dependency Comparisons', () => {
	test('should handle mixed string/number ID comparisons correctly', () => {
		// Test the pattern that was fixed in the move-task tests
		const sourceTasks = [
			{ id: 1, title: 'Task 1' },
			{ id: 2, title: 'Task 2' },
			{ id: '3.1', title: 'Subtask 3.1' }
		];

		const allTasks = [
			{ id: 1, title: 'Task 1', dependencies: [2, '3.1'] },
			{ id: 2, title: 'Task 2', dependencies: ['1'] },
			{
				id: 3,
				title: 'Task 3',
				subtasks: [{ id: 1, title: 'Subtask 3.1', dependencies: [1] }]
			}
		];

		// Test the fixed pattern: normalize source IDs and compare with string conversion
		const sourceIds = sourceTasks.map((t) => t.id);
		const normalizedSourceIds = sourceIds.map((id) => String(id));

		// Test that dependencies are correctly identified regardless of type
		const result = [];
		allTasks.forEach((task) => {
			if (task.dependencies && Array.isArray(task.dependencies)) {
				const hasDependency = task.dependencies.some((depId) =>
					normalizedSourceIds.includes(String(depId))
				);
				if (hasDependency) {
					result.push(task.id);
				}
			}
		});

		// Verify that the comparison works correctly
		expect(result).toContain(1); // Task 1 has dependency on 2 and '3.1'
		expect(result).toContain(2); // Task 2 has dependency on '1'

		// Test edge cases
		const mixedDependencies = [
			{ id: 1, dependencies: [1, 2, '3.1', '4.2'] },
			{ id: 2, dependencies: ['1', 3, '5.1'] }
		];

		const testSourceIds = [1, '3.1', 4];
		const normalizedTestSourceIds = testSourceIds.map((id) => String(id));

		mixedDependencies.forEach((task) => {
			const hasMatch = task.dependencies.some((depId) =>
				normalizedTestSourceIds.includes(String(depId))
			);
			expect(typeof hasMatch).toBe('boolean');
			expect(hasMatch).toBe(true); // Should find matches in both tasks
		});
	});

	test('should handle edge cases in ID normalization', () => {
		// Test various ID formats
		const testCases = [
			{ source: 1, dependency: '1', expected: true },
			{ source: '1', dependency: 1, expected: true },
			{ source: '3.1', dependency: '3.1', expected: true },
			{ source: 3, dependency: '3.1', expected: false }, // Different formats
			{ source: '3.1', dependency: 3, expected: false }, // Different formats
			{ source: 1, dependency: 2, expected: false }, // No match
			{ source: '1.2', dependency: '1.2', expected: true },
			{ source: 1, dependency: null, expected: false }, // Handle null
			{ source: 1, dependency: undefined, expected: false } // Handle undefined
		];

		testCases.forEach(({ source, dependency, expected }) => {
			const normalizedSourceIds = [String(source)];
			const hasMatch = normalizedSourceIds.includes(String(dependency));
			expect(hasMatch).toBe(expected);
		});
	});
});

```

--------------------------------------------------------------------------------
/src/utils/path-utils.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Path utility functions for Task Master
 * Provides centralized path resolution logic for both CLI and MCP use cases
 *
 * NOTE: This file is a legacy wrapper around @tm/core utilities.
 * New code should import directly from @tm/core instead.
 * This file exists for backward compatibility during the migration period.
 */

import path from 'path';
import fs from 'fs';
import {
	TASKMASTER_TASKS_FILE,
	LEGACY_TASKS_FILE,
	TASKMASTER_DOCS_DIR,
	TASKMASTER_REPORTS_DIR,
	COMPLEXITY_REPORT_FILE,
	TASKMASTER_CONFIG_FILE,
	LEGACY_CONFIG_FILE
} from '../constants/paths.js';
import { getLoggerOrDefault } from './logger-utils.js';
import {
	findProjectRoot as findProjectRootCore,
	normalizeProjectRoot as normalizeProjectRootCore
} from '@tm/core';

/**
 * Normalize project root to ensure it doesn't end with .taskmaster
 * This prevents double .taskmaster paths when using constants that include .taskmaster
 *
 * @deprecated Use the TypeScript implementation from @tm/core instead
 * @param {string} projectRoot - The project root path to normalize
 * @returns {string} - Normalized project root path
 */
export function normalizeProjectRoot(projectRoot) {
	return normalizeProjectRootCore(projectRoot);
}

/**
 * Find the project root directory by looking for project markers
 * Traverses upwards from startDir until a project marker is found or filesystem root is reached
 * Limited to 50 parent directory levels to prevent excessive traversal
 *
 * Strategy: First searches ALL parent directories for .taskmaster (highest priority).
 * If not found, then searches for other project markers starting from current directory.
 * This ensures .taskmaster in parent directories takes precedence over other markers in subdirectories.
 *
 * @deprecated Use the TypeScript implementation from @tm/core instead
 * @param {string} startDir - Directory to start searching from (defaults to process.cwd())
 * @returns {string} - Project root path (falls back to current directory if no markers found)
 */
export function findProjectRoot(startDir = process.cwd()) {
	return findProjectRootCore(startDir);
}

/**
 * Find the tasks.json file path with fallback logic
 * @param {string|null} explicitPath - Explicit path provided by user (highest priority)
 * @param {Object|null} args - Args object from MCP args (optional)
 * @param {Object|null} log - Logger object (optional)
 * @returns {string|null} - Resolved tasks.json path or null if not found
 */
export function findTasksPath(explicitPath = null, args = null, log = null) {
	// Use the passed logger if available, otherwise use the default logger
	const logger = getLoggerOrDefault(log);

	// 1. First determine project root to use as base for all path resolution
	const rawProjectRoot = args?.projectRoot || findProjectRoot();

	if (!rawProjectRoot) {
		logger.warn?.('Could not determine project root directory');
		return null;
	}

	// 2. Normalize project root to prevent double .taskmaster paths
	const projectRoot = normalizeProjectRoot(rawProjectRoot);

	// 3. If explicit path is provided, resolve it relative to project root (highest priority)
	if (explicitPath) {
		const resolvedPath = path.isAbsolute(explicitPath)
			? explicitPath
			: path.resolve(projectRoot, explicitPath);

		if (fs.existsSync(resolvedPath)) {
			logger.info?.(`Using explicit tasks path: ${resolvedPath}`);
			return resolvedPath;
		} else {
			logger.warn?.(
				`Explicit tasks path not found: ${resolvedPath}, trying fallbacks`
			);
		}
	}

	// 4. Check possible locations in order of preference
	const possiblePaths = [
		path.join(projectRoot, TASKMASTER_TASKS_FILE), // .taskmaster/tasks/tasks.json (NEW)
		path.join(projectRoot, LEGACY_TASKS_FILE) // tasks/tasks.json (LEGACY)
	];

	for (const tasksPath of possiblePaths) {
		if (fs.existsSync(tasksPath)) {
			logger.info?.(`Found tasks file at: ${tasksPath}`);

			// Issue deprecation warning for legacy paths
			if (
				tasksPath.includes('tasks/tasks.json') &&
				!tasksPath.includes('.taskmaster')
			) {
				logger.warn?.(
					`⚠️  DEPRECATION WARNING: Found tasks.json in legacy location '${tasksPath}'. Please migrate to the new .taskmaster directory structure. Run 'task-master migrate' to automatically migrate your project.`
				);
			} else if (
				tasksPath.endsWith('tasks.json') &&
				!tasksPath.includes('.taskmaster') &&
				!tasksPath.includes('tasks/')
			) {
				logger.warn?.(
					`⚠️  DEPRECATION WARNING: Found tasks.json in legacy root location '${tasksPath}'. Please migrate to the new .taskmaster directory structure. Run 'task-master migrate' to automatically migrate your project.`
				);
			}

			return tasksPath;
		}
	}

	logger.warn?.(`No tasks.json found in project: ${projectRoot}`);
	return null;
}

/**
 * Find the PRD document file path with fallback logic
 * @param {string|null} explicitPath - Explicit path provided by user (highest priority)
 * @param {Object|null} args - Args object for MCP context (optional)
 * @param {Object|null} log - Logger object (optional)
 * @returns {string|null} - Resolved PRD document path or null if not found
 */
export function findPRDPath(explicitPath = null, args = null, log = null) {
	const logger = getLoggerOrDefault(log);

	// 1. If explicit path is provided, use it (highest priority)
	if (explicitPath) {
		// Use original cwd if available (set by dev.js), otherwise current cwd
		// This ensures relative paths are resolved from where the user invoked the command
		const cwdForResolution =
			process.env.TASKMASTER_ORIGINAL_CWD || process.cwd();

		const resolvedPath = path.isAbsolute(explicitPath)
			? explicitPath
			: path.resolve(cwdForResolution, explicitPath);

		if (fs.existsSync(resolvedPath)) {
			logger.info?.(`Using explicit PRD path: ${resolvedPath}`);
			return resolvedPath;
		} else {
			logger.warn?.(
				`Explicit PRD path not found: ${resolvedPath}, trying fallbacks`
			);
		}
	}

	// 2. Try to get project root from args (MCP) or find it
	const rawProjectRoot = args?.projectRoot || findProjectRoot();

	if (!rawProjectRoot) {
		logger.warn?.('Could not determine project root directory');
		return null;
	}

	// 3. Normalize project root to prevent double .taskmaster paths
	const projectRoot = normalizeProjectRoot(rawProjectRoot);

	// 4. Check possible locations in order of preference
	const locations = [
		TASKMASTER_DOCS_DIR, // .taskmaster/docs/ (NEW)
		'scripts/', // Legacy location
		'' // Project root
	];

	const fileNames = ['PRD.md', 'prd.md', 'PRD.txt', 'prd.txt'];

	for (const location of locations) {
		for (const fileName of fileNames) {
			const prdPath = path.join(projectRoot, location, fileName);
			if (fs.existsSync(prdPath)) {
				logger.info?.(`Found PRD document at: ${prdPath}`);

				// Issue deprecation warning for legacy paths
				if (location === 'scripts/' || location === '') {
					logger.warn?.(
						`⚠️  DEPRECATION WARNING: Found PRD file in legacy location '${prdPath}'. Please migrate to .taskmaster/docs/ directory. Run 'task-master migrate' to automatically migrate your project.`
					);
				}

				return prdPath;
			}
		}
	}

	logger.warn?.(`No PRD document found in project: ${projectRoot}`);
	return null;
}

/**
 * Find the complexity report file path with fallback logic
 * @param {string|null} explicitPath - Explicit path provided by user (highest priority)
 * @param {Object|null} args - Args object for MCP context (optional)
 * @param {Object|null} log - Logger object (optional)
 * @returns {string|null} - Resolved complexity report path or null if not found
 */
export function findComplexityReportPath(
	explicitPath = null,
	args = null,
	log = null
) {
	const logger = getLoggerOrDefault(log);

	// 1. If explicit path is provided, use it (highest priority)
	if (explicitPath) {
		// Use original cwd if available (set by dev.js), otherwise current cwd
		const cwdForResolution =
			process.env.TASKMASTER_ORIGINAL_CWD || process.cwd();

		const resolvedPath = path.isAbsolute(explicitPath)
			? explicitPath
			: path.resolve(cwdForResolution, explicitPath);

		if (fs.existsSync(resolvedPath)) {
			logger.info?.(`Using explicit complexity report path: ${resolvedPath}`);
			return resolvedPath;
		} else {
			logger.warn?.(
				`Explicit complexity report path not found: ${resolvedPath}, trying fallbacks`
			);
		}
	}

	// 2. Try to get project root from args (MCP) or find it
	const rawProjectRoot = args?.projectRoot || findProjectRoot();

	if (!rawProjectRoot) {
		logger.warn?.('Could not determine project root directory');
		return null;
	}

	// 3. Normalize project root to prevent double .taskmaster paths
	const projectRoot = normalizeProjectRoot(rawProjectRoot);

	// 4. Check possible locations in order of preference
	const locations = [
		TASKMASTER_REPORTS_DIR, // .taskmaster/reports/ (NEW)
		'scripts/', // Legacy location
		'' // Project root
	];

	const fileNames = [
		'task-complexity-report',
		'task-complexity',
		'complexity-report'
	].map((fileName) => {
		if (args?.tag && args?.tag !== 'master') {
			return `${fileName}_${args.tag}.json`;
		}
		return `${fileName}.json`;
	});

	for (const location of locations) {
		for (const fileName of fileNames) {
			const reportPath = path.join(projectRoot, location, fileName);
			if (fs.existsSync(reportPath)) {
				logger.info?.(`Found complexity report at: ${reportPath}`);

				// Issue deprecation warning for legacy paths
				if (location === 'scripts/' || location === '') {
					logger.warn?.(
						`⚠️  DEPRECATION WARNING: Found complexity report in legacy location '${reportPath}'. Please migrate to .taskmaster/reports/ directory. Run 'task-master migrate' to automatically migrate your project.`
					);
				}

				return reportPath;
			}
		}
	}

	logger.warn?.(`No complexity report found in project: ${projectRoot}`);
	return null;
}

/**
 * Resolve output path for tasks.json (create if needed)
 * @param {string|null} explicitPath - Explicit output path provided by user
 * @param {Object|null} args - Args object for MCP context (optional)
 * @param {Object|null} log - Logger object (optional)
 * @returns {string} - Resolved output path for tasks.json
 */
export function resolveTasksOutputPath(
	explicitPath = null,
	args = null,
	log = null
) {
	const logger = getLoggerOrDefault(log);

	// 1. If explicit path is provided, use it
	if (explicitPath) {
		// Use original cwd if available (set by dev.js), otherwise current cwd
		const cwdForResolution =
			process.env.TASKMASTER_ORIGINAL_CWD || process.cwd();

		const resolvedPath = path.isAbsolute(explicitPath)
			? explicitPath
			: path.resolve(cwdForResolution, explicitPath);

		logger.info?.(`Using explicit output path: ${resolvedPath}`);
		return resolvedPath;
	}

	// 2. Try to get project root from args (MCP) or find it
	const rawProjectRoot =
		args?.projectRoot || findProjectRoot() || process.cwd();

	// 3. Normalize project root to prevent double .taskmaster paths
	const projectRoot = normalizeProjectRoot(rawProjectRoot);

	// 4. Use new .taskmaster structure by default
	const defaultPath = path.join(projectRoot, TASKMASTER_TASKS_FILE);
	logger.info?.(`Using default output path: ${defaultPath}`);

	// Ensure the directory exists
	const outputDir = path.dirname(defaultPath);
	if (!fs.existsSync(outputDir)) {
		logger.info?.(`Creating tasks directory: ${outputDir}`);
		fs.mkdirSync(outputDir, { recursive: true });
	}

	return defaultPath;
}

/**
 * Resolve output path for complexity report (create if needed)
 * @param {string|null} explicitPath - Explicit output path provided by user
 * @param {Object|null} args - Args object for MCP context (optional)
 * @param {Object|null} log - Logger object (optional)
 * @returns {string} - Resolved output path for complexity report
 */
export function resolveComplexityReportOutputPath(
	explicitPath = null,
	args = null,
	log = null
) {
	const logger = getLoggerOrDefault(log);
	const tag = args?.tag;

	// 1. If explicit path is provided, use it
	if (explicitPath) {
		// Use original cwd if available (set by dev.js), otherwise current cwd
		const cwdForResolution =
			process.env.TASKMASTER_ORIGINAL_CWD || process.cwd();

		const resolvedPath = path.isAbsolute(explicitPath)
			? explicitPath
			: path.resolve(cwdForResolution, explicitPath);

		logger.info?.(
			`Using explicit complexity report output path: ${resolvedPath}`
		);
		return resolvedPath;
	}

	// 2. Try to get project root from args (MCP) or find it
	const rawProjectRoot =
		args?.projectRoot || findProjectRoot() || process.cwd();
	const projectRoot = normalizeProjectRoot(rawProjectRoot);

	// 3. Use tag-aware filename
	let filename = 'task-complexity-report.json';
	if (tag && tag !== 'master') {
		filename = `task-complexity-report_${tag}.json`;
	}

	// 4. Use new .taskmaster structure by default
	const defaultPath = path.join(projectRoot, '.taskmaster/reports', filename);
	logger.info?.(
		`Using tag-aware complexity report output path: ${defaultPath}`
	);

	// Ensure the directory exists
	const outputDir = path.dirname(defaultPath);
	if (!fs.existsSync(outputDir)) {
		logger.info?.(`Creating reports directory: ${outputDir}`);
		fs.mkdirSync(outputDir, { recursive: true });
	}

	return defaultPath;
}

/**
 * Find the configuration file path with fallback logic
 * @param {string|null} explicitPath - Explicit path provided by user (highest priority)
 * @param {Object|null} args - Args object for MCP context (optional)
 * @param {Object|null} log - Logger object (optional)
 * @returns {string|null} - Resolved config file path or null if not found
 */
export function findConfigPath(explicitPath = null, args = null, log = null) {
	const logger = getLoggerOrDefault(log);

	// 1. If explicit path is provided, use it (highest priority)
	if (explicitPath) {
		// Use original cwd if available (set by dev.js), otherwise current cwd
		const cwdForResolution =
			process.env.TASKMASTER_ORIGINAL_CWD || process.cwd();

		const resolvedPath = path.isAbsolute(explicitPath)
			? explicitPath
			: path.resolve(cwdForResolution, explicitPath);

		if (fs.existsSync(resolvedPath)) {
			logger.info?.(`Using explicit config path: ${resolvedPath}`);
			return resolvedPath;
		} else {
			logger.warn?.(
				`Explicit config path not found: ${resolvedPath}, trying fallbacks`
			);
		}
	}

	// 2. Try to get project root from args (MCP) or find it
	const rawProjectRoot = args?.projectRoot || findProjectRoot();

	if (!rawProjectRoot) {
		logger.warn?.('Could not determine project root directory');
		return null;
	}

	// 3. Normalize project root to prevent double .taskmaster paths
	const projectRoot = normalizeProjectRoot(rawProjectRoot);

	// 4. Check possible locations in order of preference
	const possiblePaths = [
		path.join(projectRoot, TASKMASTER_CONFIG_FILE), // NEW location
		path.join(projectRoot, LEGACY_CONFIG_FILE) // LEGACY location
	];

	for (const configPath of possiblePaths) {
		if (fs.existsSync(configPath)) {
			// Issue deprecation warning for legacy paths
			if (configPath?.endsWith(LEGACY_CONFIG_FILE)) {
				logger.warn?.(
					`⚠️  DEPRECATION WARNING: Found configuration in legacy location '${configPath}'. Please migrate to .taskmaster/config.json. Run 'task-master migrate' to automatically migrate your project.`
				);
			}

			return configPath;
		}
	}

	// Only warn once per command execution to prevent spam during init
	const warningKey = `config_warning_${projectRoot}`;

	if (!global._tmConfigWarningsThisRun) {
		global._tmConfigWarningsThisRun = new Set();
	}

	if (!global._tmConfigWarningsThisRun.has(warningKey)) {
		global._tmConfigWarningsThisRun.add(warningKey);
		logger.warn?.(`No configuration file found in project: ${projectRoot}`);
	}

	return null;
}

```

--------------------------------------------------------------------------------
/tests/unit/commands.test.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Commands module tests - Focus on CLI setup and integration
 */

import { jest } from '@jest/globals';

// Mock modules first
jest.mock('fs', () => ({
	existsSync: jest.fn(),
	readFileSync: jest.fn()
}));

jest.mock('path', () => ({
	join: jest.fn((dir, file) => `${dir}/${file}`)
}));

jest.mock('chalk', () => ({
	red: jest.fn((text) => text),
	blue: jest.fn((text) => text),
	green: jest.fn((text) => text),
	yellow: jest.fn((text) => text),
	white: jest.fn((text) => ({
		bold: jest.fn((text) => text)
	})),
	reset: jest.fn((text) => text)
}));

// Mock config-manager to prevent file system discovery issues
jest.mock('../../scripts/modules/config-manager.js', () => ({
	getLogLevel: jest.fn(() => 'info'),
	getDebugFlag: jest.fn(() => false),
	getConfig: jest.fn(() => ({})), // Return empty config to prevent real loading
	getGlobalConfig: jest.fn(() => ({}))
}));

// Mock path-utils to prevent file system discovery issues
jest.mock('../../src/utils/path-utils.js', () => ({
	__esModule: true,
	findProjectRoot: jest.fn(() => '/mock/project'),
	findConfigPath: jest.fn(() => null),
	findTasksPath: jest.fn(() => '/mock/tasks.json'),
	findComplexityReportPath: jest.fn(() => null),
	resolveTasksOutputPath: jest.fn(() => '/mock/tasks.json'),
	resolveComplexityReportOutputPath: jest.fn(() => '/mock/report.json')
}));

jest.mock('../../scripts/modules/ui.js', () => ({
	displayBanner: jest.fn(),
	displayHelp: jest.fn()
}));

// Add utility functions for testing
const toKebabCase = (str) => {
	return str
		.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
		.toLowerCase()
		.replace(/^-/, '');
};

function detectCamelCaseFlags(args) {
	const camelCaseFlags = [];
	for (const arg of args) {
		if (arg.startsWith('--')) {
			const flagName = arg.split('=')[0].slice(2);

			if (!flagName.includes('-')) {
				if (/[a-z][A-Z]/.test(flagName)) {
					const kebabVersion = toKebabCase(flagName);
					if (kebabVersion !== flagName) {
						camelCaseFlags.push({
							original: flagName,
							kebabCase: kebabVersion
						});
					}
				}
			}
		}
	}
	return camelCaseFlags;
}

jest.mock('../../scripts/modules/utils.js', () => ({
	CONFIG: {
		projectVersion: '1.5.0'
	},
	log: jest.fn(() => {}), // Prevent any real logging that could trigger config discovery
	toKebabCase: toKebabCase,
	detectCamelCaseFlags: detectCamelCaseFlags
}));

// Import all modules after mocking
import fs from 'fs';
import path from 'path';
import { setupCLI } from '../../scripts/modules/commands.js';
import {
	RULES_SETUP_ACTION,
	RULES_ACTIONS
} from '../../src/constants/rules-actions.js';
import { compareVersions } from '@tm/cli';

describe('Commands Module - CLI Setup and Integration', () => {
	const mockExistsSync = jest.spyOn(fs, 'existsSync');

	beforeEach(() => {
		jest.clearAllMocks();
		mockExistsSync.mockReturnValue(true);
	});

	afterAll(() => {
		jest.restoreAllMocks();
	});

	describe('setupCLI function', () => {
		test('should return Commander program instance', () => {
			const program = setupCLI();
			expect(program).toBeDefined();
			expect(program.name()).toBe('task-master');
		});

		test('should return version that matches package.json when TM_PUBLIC_VERSION is set', () => {
			// Read actual version from package.json
			const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
			const expectedVersion = packageJson.version;

			// Set environment variable to match package.json
			const originalEnv = process.env.TM_PUBLIC_VERSION;
			process.env.TM_PUBLIC_VERSION = expectedVersion;

			const program = setupCLI();
			const version = program.version();
			expect(version).toBe(expectedVersion);

			// Restore original environment
			if (originalEnv !== undefined) {
				process.env.TM_PUBLIC_VERSION = originalEnv;
			} else {
				delete process.env.TM_PUBLIC_VERSION;
			}
		});

		test('should use default version when TM_PUBLIC_VERSION is not available', () => {
			const originalEnv = process.env.TM_PUBLIC_VERSION;
			delete process.env.TM_PUBLIC_VERSION;

			const program = setupCLI();
			const version = program.version();
			expect(version).toBe('unknown');

			// Restore original environment
			if (originalEnv !== undefined) {
				process.env.TM_PUBLIC_VERSION = originalEnv;
			}
		});
	});

	describe('CLI Flag Format Validation', () => {
		test('should detect camelCase flags correctly', () => {
			const args = ['node', 'task-master', '--camelCase', '--kebab-case'];
			const camelCaseFlags = args.filter(
				(arg) =>
					arg.startsWith('--') && /[A-Z]/.test(arg) && !arg.includes('-[A-Z]')
			);
			expect(camelCaseFlags).toContain('--camelCase');
			expect(camelCaseFlags).not.toContain('--kebab-case');
		});

		test('should accept kebab-case flags correctly', () => {
			const args = ['node', 'task-master', '--kebab-case'];
			const camelCaseFlags = args.filter(
				(arg) =>
					arg.startsWith('--') && /[A-Z]/.test(arg) && !arg.includes('-[A-Z]')
			);
			expect(camelCaseFlags).toHaveLength(0);
		});

		test('toKebabCase should convert camelCase to kebab-case', () => {
			expect(toKebabCase('promptText')).toBe('prompt-text');
			expect(toKebabCase('userID')).toBe('user-id');
			expect(toKebabCase('numTasks')).toBe('num-tasks');
			expect(toKebabCase('alreadyKebabCase')).toBe('already-kebab-case');
		});

		test('detectCamelCaseFlags should identify camelCase flags', () => {
			const args = [
				'node',
				'task-master',
				'add-task',
				'--promptText=test',
				'--userID=123'
			];
			const flags = detectCamelCaseFlags(args);

			expect(flags).toHaveLength(2);
			expect(flags).toContainEqual({
				original: 'promptText',
				kebabCase: 'prompt-text'
			});
			expect(flags).toContainEqual({
				original: 'userID',
				kebabCase: 'user-id'
			});
		});

		test('detectCamelCaseFlags should not flag kebab-case flags', () => {
			const args = [
				'node',
				'task-master',
				'add-task',
				'--prompt-text=test',
				'--user-id=123'
			];
			const flags = detectCamelCaseFlags(args);

			expect(flags).toHaveLength(0);
		});

		test('detectCamelCaseFlags should respect single-word flags', () => {
			const args = [
				'node',
				'task-master',
				'add-task',
				'--prompt=test',
				'--file=test.json',
				'--priority=high',
				'--promptText=test'
			];
			const flags = detectCamelCaseFlags(args);

			expect(flags).toHaveLength(1);
			expect(flags).toContainEqual({
				original: 'promptText',
				kebabCase: 'prompt-text'
			});
		});
	});

	describe('Command Validation Logic', () => {
		test('should validate task ID parameter correctly', () => {
			// Test valid task IDs
			const validId = '5';
			const taskId = parseInt(validId, 10);
			expect(Number.isNaN(taskId) || taskId <= 0).toBe(false);

			// Test invalid task IDs
			const invalidId = 'not-a-number';
			const invalidTaskId = parseInt(invalidId, 10);
			expect(Number.isNaN(invalidTaskId) || invalidTaskId <= 0).toBe(true);

			// Test zero or negative IDs
			const zeroId = '0';
			const zeroTaskId = parseInt(zeroId, 10);
			expect(Number.isNaN(zeroTaskId) || zeroTaskId <= 0).toBe(true);
		});

		test('should handle environment variable cleanup correctly', () => {
			// Instead of using delete operator, test setting to undefined
			const testEnv = { PERPLEXITY_API_KEY: 'test-key' };
			testEnv.PERPLEXITY_API_KEY = undefined;
			expect(testEnv.PERPLEXITY_API_KEY).toBeUndefined();
		});
	});
});

// Test utility functions that commands rely on
describe('Version comparison utility', () => {
	test('compareVersions correctly compares semantic versions', () => {
		expect(compareVersions('1.0.0', '1.0.0')).toBe(0);
		expect(compareVersions('1.0.0', '1.0.1')).toBe(-1);
		expect(compareVersions('1.0.1', '1.0.0')).toBe(1);
		expect(compareVersions('1.0.0', '1.1.0')).toBe(-1);
		expect(compareVersions('1.1.0', '1.0.0')).toBe(1);
		expect(compareVersions('1.0.0', '2.0.0')).toBe(-1);
		expect(compareVersions('2.0.0', '1.0.0')).toBe(1);
		expect(compareVersions('1.0', '1.0.0')).toBe(0);
		expect(compareVersions('1.0.0.0', '1.0.0')).toBe(0);
		expect(compareVersions('1.0.0', '1.0.0.1')).toBe(-1);
	});
});

describe('Update check functionality', () => {
	let displayUpgradeNotification;
	let parseChangelogHighlights;
	let consoleLogSpy;

	beforeAll(async () => {
		// Import from @tm/cli instead of commands.js
		const cliModule = await import('../../apps/cli/src/utils/auto-update.js');
		displayUpgradeNotification = cliModule.displayUpgradeNotification;
		parseChangelogHighlights = cliModule.parseChangelogHighlights;
	});

	beforeEach(() => {
		consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
	});

	afterEach(() => {
		consoleLogSpy.mockRestore();
	});

	test('displays upgrade notification when newer version is available', () => {
		displayUpgradeNotification('1.0.0', '1.1.0');
		expect(consoleLogSpy).toHaveBeenCalled();
		expect(consoleLogSpy.mock.calls[0][0]).toContain('Update Available!');
		expect(consoleLogSpy.mock.calls[0][0]).toContain('1.0.0');
		expect(consoleLogSpy.mock.calls[0][0]).toContain('1.1.0');
	});

	test('displays upgrade notification with highlights when provided', () => {
		const highlights = [
			'Add Codex CLI provider with OAuth authentication',
			'Cursor IDE custom slash command support',
			'Move to AI SDK v5'
		];
		displayUpgradeNotification('1.0.0', '1.1.0', highlights);
		expect(consoleLogSpy).toHaveBeenCalled();
		const output = consoleLogSpy.mock.calls[0][0];
		expect(output).toContain('Update Available!');
		expect(output).toContain('1.0.0');
		expect(output).toContain('1.1.0');
		expect(output).toContain("What's New:");
		expect(output).toContain(
			'Add Codex CLI provider with OAuth authentication'
		);
		expect(output).toContain('Cursor IDE custom slash command support');
		expect(output).toContain('Move to AI SDK v5');
	});

	test('displays upgrade notification without highlights section when empty array', () => {
		displayUpgradeNotification('1.0.0', '1.1.0', []);
		expect(consoleLogSpy).toHaveBeenCalled();
		const output = consoleLogSpy.mock.calls[0][0];
		expect(output).toContain('Update Available!');
		expect(output).not.toContain("What's New:");
		expect(output).toContain(
			'Auto-updating to the latest version with new features and bug fixes'
		);
	});

	test('parseChangelogHighlights validates version format to prevent ReDoS', () => {
		const mockChangelog = `
## 1.0.0

### Minor Changes

- [#123](https://example.com) Thanks [@user](https://example.com)! - Test feature
		`;

		// Valid versions should work
		expect(parseChangelogHighlights(mockChangelog, '1.0.0')).toEqual([
			'Test feature'
		]);
		expect(parseChangelogHighlights(mockChangelog, '1.0.0-rc.1')).toEqual([]);

		// Invalid versions should return empty array (ReDoS protection)
		expect(parseChangelogHighlights(mockChangelog, 'invalid')).toEqual([]);
		expect(parseChangelogHighlights(mockChangelog, '1.0')).toEqual([]);
		expect(parseChangelogHighlights(mockChangelog, 'a.b.c')).toEqual([]);
		expect(
			parseChangelogHighlights(mockChangelog, '((((((((((((((((((((((((((((((a')
		).toEqual([]);
	});
});

// -----------------------------------------------------------------------------
// Rules command tests (add/remove)
// -----------------------------------------------------------------------------
describe('rules command', () => {
	let program;
	let mockConsoleLog;
	let mockConsoleError;
	let mockExit;

	beforeEach(() => {
		jest.clearAllMocks();
		program = setupCLI();
		mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => {});
		mockConsoleError = jest
			.spyOn(console, 'error')
			.mockImplementation(() => {});
		mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
	});

	test('should handle rules add <profile> command', async () => {
		// Simulate: task-master rules add roo
		await program.parseAsync(['rules', RULES_ACTIONS.ADD, 'roo'], {
			from: 'user'
		});
		// Expect some log output indicating success
		expect(mockConsoleLog).toHaveBeenCalledWith(
			expect.stringMatching(/adding rules for profile: roo/i)
		);
		expect(mockConsoleLog).toHaveBeenCalledWith(
			expect.stringMatching(/completed adding rules for profile: roo/i)
		);
		// Should not exit with error
		expect(mockExit).not.toHaveBeenCalledWith(1);
	});

	test('should handle rules remove <profile> command', async () => {
		// Simulate: task-master rules remove roo --force
		await program.parseAsync(
			['rules', RULES_ACTIONS.REMOVE, 'roo', '--force'],
			{
				from: 'user'
			}
		);
		// Expect some log output indicating removal
		expect(mockConsoleLog).toHaveBeenCalledWith(
			expect.stringMatching(/removing rules for profile: roo/i)
		);
		expect(mockConsoleLog).toHaveBeenCalledWith(
			expect.stringMatching(/Summary for roo: Rule profile removed/i)
		);
		// Should not exit with error
		expect(mockExit).not.toHaveBeenCalledWith(1);
	});

	test(`should handle rules --${RULES_SETUP_ACTION} command`, async () => {
		// For this test, we'll verify that the command doesn't crash and exits gracefully
		// Since mocking ES modules is complex, we'll test the command structure instead

		// Create a spy on console.log to capture any output
		const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});

		// Mock process.exit to prevent actual exit and capture the call
		const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {});

		try {
			// The command should be recognized and not throw an error about invalid action
			// We expect it to attempt to run the interactive setup, but since we can't easily
			// mock the ES module, we'll just verify the command structure is correct

			// This test verifies that:
			// 1. The --setup flag is recognized as a valid option
			// 2. The command doesn't exit with error code 1 due to invalid action
			// 3. The command structure is properly set up

			// Note: In a real scenario, this would call runInteractiveProfilesSetup()
			// but for testing purposes, we're focusing on command structure validation

			expect(() => {
				// Test that the command option is properly configured
				const command = program.commands.find((cmd) => cmd.name() === 'rules');
				expect(command).toBeDefined();

				// Check that the --setup option exists
				const setupOption = command.options.find(
					(opt) => opt.long === `--${RULES_SETUP_ACTION}`
				);
				expect(setupOption).toBeDefined();
				expect(setupOption.description).toContain('interactive setup');
			}).not.toThrow();

			// Verify the command structure is valid
			expect(mockExit).not.toHaveBeenCalledWith(1);
		} finally {
			consoleSpy.mockRestore();
			exitSpy.mockRestore();
		}
	});

	test('should show error for invalid action', async () => {
		// Simulate: task-master rules invalid-action
		await program.parseAsync(['rules', 'invalid-action'], { from: 'user' });

		// Should show error for invalid action
		expect(mockConsoleError).toHaveBeenCalledWith(
			expect.stringMatching(/Error: Invalid or missing action/i)
		);
		expect(mockConsoleError).toHaveBeenCalledWith(
			expect.stringMatching(
				new RegExp(
					`For interactive setup, use: task-master rules --${RULES_SETUP_ACTION}`,
					'i'
				)
			)
		);
		expect(mockExit).toHaveBeenCalledWith(1);
	});

	test('should show error when no action provided', async () => {
		// Simulate: task-master rules (no action)
		await program.parseAsync(['rules'], { from: 'user' });

		// Should show error for missing action
		expect(mockConsoleError).toHaveBeenCalledWith(
			expect.stringMatching(/Error: Invalid or missing action 'none'/i)
		);
		expect(mockConsoleError).toHaveBeenCalledWith(
			expect.stringMatching(
				new RegExp(
					`For interactive setup, use: task-master rules --${RULES_SETUP_ACTION}`,
					'i'
				)
			)
		);
		expect(mockExit).toHaveBeenCalledWith(1);
	});
});

```

--------------------------------------------------------------------------------
/src/utils/rule-transformer.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Rule Transformer Module
 * Handles conversion of Cursor rules to profile rules
 *
 * This module procedurally generates .{profile}/rules files from assets/rules files,
 * eliminating the need to maintain both sets of files manually.
 */
import fs from 'fs';
import path from 'path';
import { log } from '../../scripts/modules/utils.js';

// Import asset resolver
import { assetExists, readAsset, getAssetsDir } from './asset-resolver.js';

// Import the shared MCP configuration helper
import {
	setupMCPConfiguration,
	removeTaskMasterMCPConfiguration
} from './create-mcp-config.js';

// Import profile constants (single source of truth)
import { RULE_PROFILES } from '../constants/profiles.js';

// --- Profile Imports ---
import * as profilesModule from '../profiles/index.js';

export function isValidProfile(profile) {
	return RULE_PROFILES.includes(profile);
}

/**
 * Get rule profile by name
 * @param {string} name - Profile name
 * @returns {Object|null} Profile object or null if not found
 */
export function getRulesProfile(name) {
	if (!isValidProfile(name)) {
		return null;
	}

	// Get the profile from the imported profiles module
	const profileKey = `${name}Profile`;
	const profile = profilesModule[profileKey];

	if (!profile) {
		throw new Error(
			`Profile not found: static import missing for '${name}'. Valid profiles: ${RULE_PROFILES.join(', ')}`
		);
	}

	return profile;
}

/**
 * Replace basic Cursor terms with profile equivalents
 */
function replaceBasicTerms(content, conversionConfig) {
	let result = content;

	// Apply profile term replacements
	conversionConfig.profileTerms.forEach((pattern) => {
		if (typeof pattern.to === 'function') {
			result = result.replace(pattern.from, pattern.to);
		} else {
			result = result.replace(pattern.from, pattern.to);
		}
	});

	// Apply file extension replacements
	conversionConfig.fileExtensions.forEach((pattern) => {
		result = result.replace(pattern.from, pattern.to);
	});

	return result;
}

/**
 * Replace Cursor tool references with profile tool equivalents
 */
function replaceToolReferences(content, conversionConfig) {
	let result = content;

	// Basic pattern for direct tool name replacements
	const toolNames = conversionConfig.toolNames;
	const toolReferencePattern = new RegExp(
		`\\b(${Object.keys(toolNames).join('|')})\\b`,
		'g'
	);

	// Apply direct tool name replacements
	result = result.replace(toolReferencePattern, (match, toolName) => {
		return toolNames[toolName] || toolName;
	});

	// Apply contextual tool replacements
	conversionConfig.toolContexts.forEach((pattern) => {
		result = result.replace(pattern.from, pattern.to);
	});

	// Apply tool group replacements
	conversionConfig.toolGroups.forEach((pattern) => {
		result = result.replace(pattern.from, pattern.to);
	});

	return result;
}

/**
 * Update documentation URLs to point to profile documentation
 */
function updateDocReferences(content, conversionConfig) {
	let result = content;

	// Apply documentation URL replacements
	conversionConfig.docUrls.forEach((pattern) => {
		if (typeof pattern.to === 'function') {
			result = result.replace(pattern.from, pattern.to);
		} else {
			result = result.replace(pattern.from, pattern.to);
		}
	});

	return result;
}

/**
 * Update file references in markdown links
 */
function updateFileReferences(content, conversionConfig) {
	const { pathPattern, replacement } = conversionConfig.fileReferences;
	return content.replace(pathPattern, replacement);
}

/**
 * Transform rule content to profile-specific rules
 * @param {string} content - The content to transform
 * @param {Object} conversionConfig - The conversion configuration
 * @param {Object} globalReplacements - Global text replacements
 * @returns {string} - The transformed content
 */
function transformRuleContent(content, conversionConfig, globalReplacements) {
	let result = content;

	// Apply all transformations in appropriate order
	result = updateFileReferences(result, conversionConfig);
	result = replaceBasicTerms(result, conversionConfig);
	result = replaceToolReferences(result, conversionConfig);
	result = updateDocReferences(result, conversionConfig);

	// Apply any global/catch-all replacements from the profile
	// Super aggressive failsafe pass to catch any variations we might have missed
	// This ensures critical transformations are applied even in contexts we didn't anticipate
	globalReplacements.forEach((pattern) => {
		if (typeof pattern.to === 'function') {
			result = result.replace(pattern.from, pattern.to);
		} else {
			result = result.replace(pattern.from, pattern.to);
		}
	});

	return result;
}

/**
 * Convert a Cursor rule file to a profile-specific rule file
 * @param {string} sourcePath - Path to the source .mdc file
 * @param {string} targetPath - Path to the target file
 * @param {Object} profile - The profile configuration
 * @returns {boolean} - Success status
 */
export function convertRuleToProfileRule(sourcePath, targetPath, profile) {
	const { conversionConfig, globalReplacements } = profile;
	try {
		// Read source content
		const content = fs.readFileSync(sourcePath, 'utf8');

		// Transform content
		const transformedContent = transformRuleContent(
			content,
			conversionConfig,
			globalReplacements
		);

		// Ensure target directory exists
		const targetDir = path.dirname(targetPath);
		if (!fs.existsSync(targetDir)) {
			fs.mkdirSync(targetDir, { recursive: true });
		}

		// Write transformed content
		fs.writeFileSync(targetPath, transformedContent);

		return true;
	} catch (error) {
		console.error(`Error converting rule file: ${error.message}`);
		return false;
	}
}

/**
 * Convert all Cursor rules to profile rules for a specific profile
 */
export function convertAllRulesToProfileRules(projectRoot, profile) {
	const targetDir = path.join(projectRoot, profile.rulesDir);

	let success = 0;
	let failed = 0;

	// 1. Call onAddRulesProfile first (for pre-processing like copying assets)
	if (typeof profile.onAddRulesProfile === 'function') {
		try {
			const assetsDir = getAssetsDir();
			profile.onAddRulesProfile(targetDir, assetsDir);
			log(
				'debug',
				`[Rule Transformer] Called onAddRulesProfile for ${profile.profileName}`
			);
		} catch (error) {
			log(
				'error',
				`[Rule Transformer] onAddRulesProfile failed for ${profile.profileName}: ${error.message}`
			);
			failed++;
		}
	}

	// 2. Handle fileMap-based rule conversion (if any)
	const sourceFiles = Object.keys(profile.fileMap);
	if (sourceFiles.length > 0) {
		// Only create rules directory if we have files to copy
		if (!fs.existsSync(targetDir)) {
			fs.mkdirSync(targetDir, { recursive: true });
		}

		for (const sourceFile of sourceFiles) {
			// Determine if this is an asset file (not a rule file)
			const isAssetFile = !sourceFile.startsWith('rules/');

			try {
				// Check if source file exists using asset resolver
				if (!assetExists(sourceFile)) {
					log(
						'warn',
						`[Rule Transformer] Source file not found: ${sourceFile}, skipping`
					);
					continue;
				}

				const targetFilename = profile.fileMap[sourceFile];
				const targetPath = path.join(targetDir, targetFilename);

				// Ensure target subdirectory exists (for rules like taskmaster/dev_workflow.md)
				const targetFileDir = path.dirname(targetPath);
				if (!fs.existsSync(targetFileDir)) {
					fs.mkdirSync(targetFileDir, { recursive: true });
				}

				// Read source content using asset resolver
				let content = readAsset(sourceFile, 'utf8');

				// Apply transformations (only if this is a rule file, not an asset file)
				if (!isAssetFile) {
					content = transformRuleContent(
						content,
						profile.conversionConfig,
						profile.globalReplacements
					);
				}

				// Write to target
				fs.writeFileSync(targetPath, content, 'utf8');
				success++;

				log(
					'debug',
					`[Rule Transformer] ${isAssetFile ? 'Copied' : 'Converted'} ${sourceFile} -> ${targetFilename} for ${profile.profileName}`
				);
			} catch (error) {
				failed++;
				log(
					'error',
					`[Rule Transformer] Failed to ${isAssetFile ? 'copy' : 'convert'} ${sourceFile} for ${profile.profileName}: ${error.message}`
				);
			}
		}
	}

	// 3. Setup MCP configuration (if enabled)
	if (profile.mcpConfig !== false) {
		try {
			setupMCPConfiguration(projectRoot, profile.mcpConfigPath);
			log(
				'debug',
				`[Rule Transformer] Setup MCP configuration for ${profile.profileName}`
			);
		} catch (error) {
			log(
				'error',
				`[Rule Transformer] MCP setup failed for ${profile.profileName}: ${error.message}`
			);
		}
	}

	// 4. Call post-conversion hook (for finalization)
	if (typeof profile.onPostConvertRulesProfile === 'function') {
		try {
			const assetsDir = getAssetsDir();
			profile.onPostConvertRulesProfile(targetDir, assetsDir);
			log(
				'debug',
				`[Rule Transformer] Called onPostConvertRulesProfile for ${profile.profileName}`
			);
		} catch (error) {
			log(
				'error',
				`[Rule Transformer] onPostConvertRulesProfile failed for ${profile.profileName}: ${error.message}`
			);
		}
	}

	// Ensure we return at least 1 success for profiles that only use lifecycle functions
	return { success: Math.max(success, 1), failed };
}

/**
 * Remove only Task Master specific files from a profile, leaving other existing rules intact
 * @param {string} projectRoot - Target project directory
 * @param {Object} profile - Profile configuration
 * @returns {Object} Result object
 */
export function removeProfileRules(projectRoot, profile) {
	const targetDir = path.join(projectRoot, profile.rulesDir);
	const profileDir = path.join(projectRoot, profile.profileDir);

	const result = {
		profileName: profile.profileName,
		success: false,
		skipped: false,
		error: null,
		filesRemoved: [],
		mcpResult: null,
		profileDirRemoved: false,
		notice: null
	};

	try {
		// 1. Call onRemoveRulesProfile first (for custom cleanup like removing assets)
		if (typeof profile.onRemoveRulesProfile === 'function') {
			try {
				profile.onRemoveRulesProfile(targetDir);
				log(
					'debug',
					`[Rule Transformer] Called onRemoveRulesProfile for ${profile.profileName}`
				);
			} catch (error) {
				log(
					'error',
					`[Rule Transformer] onRemoveRulesProfile failed for ${profile.profileName}: ${error.message}`
				);
			}
		}

		// 2. Remove fileMap-based files (if any)
		const sourceFiles = Object.keys(profile.fileMap);
		if (sourceFiles.length > 0) {
			// Check if profile directory exists at all (for full profiles)
			if (!fs.existsSync(profileDir)) {
				result.success = true;
				result.skipped = true;
				log(
					'debug',
					`[Rule Transformer] Profile directory does not exist: ${profileDir}`
				);
				return result;
			}

			let hasOtherRulesFiles = false;

			if (fs.existsSync(targetDir)) {
				// Get list of files we're responsible for
				const taskMasterFiles = sourceFiles.map(
					(sourceFile) => profile.fileMap[sourceFile]
				);

				// Get all files in the rules directory
				// For root-level directories, we need to be careful to avoid circular symlinks
				let allFiles = [];
				if (targetDir === projectRoot || profile.rulesDir === '.') {
					// For root directory, manually read without recursion into problematic directories
					const items = fs.readdirSync(targetDir);
					for (const item of items) {
						// Skip directories that can cause issues or are irrelevant
						if (item === 'node_modules' || item === '.git' || item === 'dist') {
							continue;
						}
						const itemPath = path.join(targetDir, item);
						try {
							const stats = fs.lstatSync(itemPath);
							if (stats.isFile()) {
								allFiles.push(item);
							} else if (stats.isDirectory() && !stats.isSymbolicLink()) {
								// Only recurse into safe directories
								const subFiles = fs.readdirSync(itemPath, { recursive: true });
								subFiles.forEach((subFile) => {
									allFiles.push(path.join(item, subFile.toString()));
								});
							}
						} catch (err) {
							// Silently skip files we can't access
						}
					}
				} else {
					// For non-root directories, use normal recursive read
					allFiles = fs.readdirSync(targetDir, { recursive: true });
				}

				const allFilePaths = allFiles
					.filter((file) => {
						const fullPath = path.join(targetDir, file);
						try {
							const stats = fs.statSync(fullPath);
							return stats.isFile();
						} catch (err) {
							return false;
						}
					})
					.map((file) => file.toString()); // Ensure it's a string

				// Remove only Task Master files
				for (const taskMasterFile of taskMasterFiles) {
					const filePath = path.join(targetDir, taskMasterFile);
					if (fs.existsSync(filePath)) {
						try {
							fs.rmSync(filePath, { force: true });
							result.filesRemoved.push(taskMasterFile);
							log(
								'debug',
								`[Rule Transformer] Removed Task Master file: ${taskMasterFile}`
							);
						} catch (error) {
							log(
								'error',
								`[Rule Transformer] Failed to remove ${taskMasterFile}: ${error.message}`
							);
						}
					}
				}

				// Check for other (non-Task Master) files
				const remainingFiles = allFilePaths.filter(
					(file) => !taskMasterFiles.includes(file)
				);

				hasOtherRulesFiles = remainingFiles.length > 0;

				// Remove empty directories or note preserved files
				if (remainingFiles.length === 0) {
					fs.rmSync(targetDir, { recursive: true, force: true });
					log(
						'debug',
						`[Rule Transformer] Removed empty rules directory: ${targetDir}`
					);
				} else if (hasOtherRulesFiles) {
					result.notice = `Preserved ${remainingFiles.length} existing rule files in ${profile.rulesDir}`;
					log('info', `[Rule Transformer] ${result.notice}`);
				}
			}
		}

		// 3. Handle MCP configuration - only remove Task Master, preserve other servers
		if (profile.mcpConfig !== false) {
			try {
				result.mcpResult = removeTaskMasterMCPConfiguration(
					projectRoot,
					profile.mcpConfigPath
				);
				if (result.mcpResult.hasOtherServers) {
					if (!result.notice) {
						result.notice = 'Preserved other MCP server configurations';
					} else {
						result.notice += '; preserved other MCP server configurations';
					}
				}
				log(
					'debug',
					`[Rule Transformer] Processed MCP configuration for ${profile.profileName}`
				);
			} catch (error) {
				log(
					'error',
					`[Rule Transformer] MCP cleanup failed for ${profile.profileName}: ${error.message}`
				);
			}
		}

		// 4. Check if we should remove the entire profile directory
		if (fs.existsSync(profileDir)) {
			const remainingContents = fs.readdirSync(profileDir);
			if (remainingContents.length === 0 && profile.profileDir !== '.') {
				// Only remove profile directory if it's empty and not root directory
				try {
					fs.rmSync(profileDir, { recursive: true, force: true });
					result.profileDirRemoved = true;
					log(
						'debug',
						`[Rule Transformer] Removed empty profile directory: ${profileDir}`
					);
				} catch (error) {
					log(
						'error',
						`[Rule Transformer] Failed to remove profile directory ${profileDir}: ${error.message}`
					);
				}
			} else if (remainingContents.length > 0) {
				// Profile directory has remaining files/folders, add notice
				const preservedNotice = `Preserved ${remainingContents.length} existing files/folders in ${profile.profileDir}`;
				if (!result.notice) {
					result.notice = preservedNotice;
				} else {
					result.notice += `; ${preservedNotice.toLowerCase()}`;
				}
				log('info', `[Rule Transformer] ${preservedNotice}`);
			}
		}

		result.success = true;
		log(
			'debug',
			`[Rule Transformer] Successfully removed ${profile.profileName} Task Master files from ${projectRoot}`
		);
	} catch (error) {
		result.error = error.message;
		log(
			'error',
			`[Rule Transformer] Failed to remove ${profile.profileName} rules: ${error.message}`
		);
	}

	return result;
}

```

--------------------------------------------------------------------------------
/apps/cli/src/commands/auth.command.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Auth command using Commander's native class pattern
 * Extends Commander.Command for better integration with the framework
 */

import {
	type AuthCredentials,
	AuthManager,
	AuthenticationError
} from '@tm/core';
import chalk from 'chalk';
import { Command } from 'commander';
import inquirer from 'inquirer';
import open from 'open';
import ora, { type Ora } from 'ora';
import { displayError } from '../utils/error-handler.js';
import * as ui from '../utils/ui.js';
import { ContextCommand } from './context.command.js';

/**
 * Result type from auth command
 */
export interface AuthResult {
	success: boolean;
	action: 'login' | 'logout' | 'status' | 'refresh';
	credentials?: AuthCredentials;
	message?: string;
}

/**
 * AuthCommand extending Commander's Command class
 * This is a thin presentation layer over @tm/core's AuthManager
 */
export class AuthCommand extends Command {
	private authManager: AuthManager;
	private lastResult?: AuthResult;

	constructor(name?: string) {
		super(name || 'auth');

		// Initialize auth manager
		this.authManager = AuthManager.getInstance();

		// Configure the command with subcommands
		this.description('Manage authentication with tryhamster.com');

		// Add subcommands
		this.addLoginCommand();
		this.addLogoutCommand();
		this.addStatusCommand();
		this.addRefreshCommand();

		// Default action shows help
		this.action(() => {
			this.help();
		});
	}

	/**
	 * Add login subcommand
	 */
	private addLoginCommand(): void {
		this.command('login')
			.description('Authenticate with tryhamster.com')
			.argument(
				'[token]',
				'Authentication token (optional, for SSH/remote environments)'
			)
			.option('-y, --yes', 'Skip interactive prompts')
			.addHelpText(
				'after',
				`
Examples:
  $ tm auth login         # Browser-based OAuth flow (interactive)
  $ tm auth login <token> # Token-based authentication
  $ tm auth login <token> -y # Non-interactive token auth (for scripts)
`
			)
			.action(async (token?: string, options?: { yes?: boolean }) => {
				await this.executeLogin(token, options?.yes);
			});
	}

	/**
	 * Add logout subcommand
	 */
	private addLogoutCommand(): void {
		this.command('logout')
			.description('Logout and clear credentials')
			.action(async () => {
				await this.executeLogout();
			});
	}

	/**
	 * Add status subcommand
	 */
	private addStatusCommand(): void {
		this.command('status')
			.description('Display authentication status')
			.action(async () => {
				await this.executeStatus();
			});
	}

	/**
	 * Add refresh subcommand
	 */
	private addRefreshCommand(): void {
		this.command('refresh')
			.description('Refresh authentication token')
			.action(async () => {
				await this.executeRefresh();
			});
	}

	/**
	 * Execute login command
	 */
	private async executeLogin(token?: string, yes?: boolean): Promise<void> {
		try {
			const result = token
				? await this.performTokenAuth(token, yes)
				: await this.performInteractiveAuth(yes);
			this.setLastResult(result);

			if (!result.success) {
				process.exit(1);
			}

			// Exit cleanly after successful authentication
			// Small delay to ensure all output is flushed
			setTimeout(() => {
				process.exit(0);
			}, 100);
		} catch (error: any) {
			displayError(error);
		}
	}

	/**
	 * Execute logout command
	 */
	private async executeLogout(): Promise<void> {
		try {
			const result = await this.performLogout();
			this.setLastResult(result);

			if (!result.success) {
				process.exit(1);
			}
		} catch (error: any) {
			displayError(error);
		}
	}

	/**
	 * Execute status command
	 */
	private async executeStatus(): Promise<void> {
		try {
			const result = await this.displayStatus();
			this.setLastResult(result);
		} catch (error: any) {
			displayError(error);
		}
	}

	/**
	 * Execute refresh command
	 */
	private async executeRefresh(): Promise<void> {
		try {
			const result = await this.refreshToken();
			this.setLastResult(result);

			if (!result.success) {
				process.exit(1);
			}
		} catch (error: any) {
			displayError(error);
		}
	}

	/**
	 * Display authentication status
	 */
	private async displayStatus(): Promise<AuthResult> {
		console.log(chalk.cyan('\n🔐 Authentication Status\n'));

		// Check if user has valid session
		const hasSession = await this.authManager.hasValidSession();

		if (hasSession) {
			// Get session from Supabase (has tokens and expiry)
			const session = await this.authManager.getSession();

			// Get user context (has email, userId, org/brief selection)
			const context = this.authManager.getContext();
			const contextStore = this.authManager.getStoredContext();

			console.log(chalk.green('✓ Authenticated'));
			console.log(chalk.gray(`  Email: ${contextStore?.email || 'N/A'}`));
			console.log(chalk.gray(`  User ID: ${contextStore?.userId || 'N/A'}`));
			console.log(chalk.gray(`  Token Type: standard`));

			// Display expiration info
			if (session?.expires_at) {
				const expiresAt = new Date(session.expires_at * 1000);
				const now = new Date();
				const timeRemaining = expiresAt.getTime() - now.getTime();
				const hoursRemaining = Math.floor(timeRemaining / (1000 * 60 * 60));
				const minutesRemaining = Math.floor(timeRemaining / (1000 * 60));

				if (timeRemaining > 0) {
					// Token is still valid
					if (hoursRemaining > 0) {
						console.log(
							chalk.gray(
								`  Expires at: ${expiresAt.toLocaleString()} (${hoursRemaining} hours remaining)`
							)
						);
					} else {
						console.log(
							chalk.gray(
								`  Expires at: ${expiresAt.toLocaleString()} (${minutesRemaining} minutes remaining)`
							)
						);
					}
				} else {
					// Token has expired
					console.log(
						chalk.yellow(`  Expired at: ${expiresAt.toLocaleString()}`)
					);
				}
			}

			// Display context if available
			if (context) {
				console.log(chalk.gray('\n  Context:'));
				if (context.orgName) {
					console.log(chalk.gray(`    Organization: ${context.orgName}`));
				}
				if (context.briefName) {
					console.log(chalk.gray(`    Brief: ${context.briefName}`));
				}
			}

			// Build credentials for backward compatibility
			const credentials = {
				token: session?.access_token || '',
				refreshToken: session?.refresh_token,
				userId: contextStore?.userId || '',
				email: contextStore?.email,
				expiresAt: session?.expires_at
					? new Date(session.expires_at * 1000).toISOString()
					: undefined,
				tokenType: 'standard' as const,
				savedAt: contextStore?.lastUpdated || new Date().toISOString(),
				selectedContext: context || undefined
			};

			return {
				success: true,
				action: 'status',
				credentials,
				message: 'Authenticated'
			};
		} else {
			console.log(chalk.yellow('✗ Not authenticated'));
			console.log(
				chalk.gray('\n  Run "task-master auth login" to authenticate')
			);

			return {
				success: false,
				action: 'status',
				message: 'Not authenticated'
			};
		}
	}

	/**
	 * Perform logout
	 */
	private async performLogout(): Promise<AuthResult> {
		try {
			await this.authManager.logout();
			ui.displaySuccess('Successfully logged out');

			return {
				success: true,
				action: 'logout',
				message: 'Successfully logged out'
			};
		} catch (error) {
			const message = `Failed to logout: ${(error as Error).message}`;
			ui.displayError(message);

			return {
				success: false,
				action: 'logout',
				message
			};
		}
	}

	/**
	 * Refresh authentication token
	 */
	private async refreshToken(): Promise<AuthResult> {
		const spinner = ora('Refreshing authentication token...').start();

		try {
			const credentials = await this.authManager.refreshToken();
			spinner.succeed('Token refreshed successfully');

			console.log(
				chalk.gray(
					`  New expiration: ${credentials.expiresAt ? new Date(credentials.expiresAt).toLocaleString() : 'Never'}`
				)
			);

			return {
				success: true,
				action: 'refresh',
				credentials,
				message: 'Token refreshed successfully'
			};
		} catch (error) {
			spinner.fail('Failed to refresh token');

			if ((error as AuthenticationError).code === 'NO_REFRESH_TOKEN') {
				ui.displayWarning(
					'No refresh token available. Please re-authenticate.'
				);
			} else {
				ui.displayError(`Refresh failed: ${(error as Error).message}`);
			}

			return {
				success: false,
				action: 'refresh',
				message: `Failed to refresh: ${(error as Error).message}`
			};
		}
	}

	/**
	 * Perform interactive authentication
	 */
	private async performInteractiveAuth(yes?: boolean): Promise<AuthResult> {
		ui.displayBanner('Task Master Authentication');
		const isAuthenticated = await this.authManager.hasValidSession();

		// Check if already authenticated (skip if --yes is used)
		if (isAuthenticated && !yes) {
			const { continueAuth } = await inquirer.prompt([
				{
					type: 'confirm',
					name: 'continueAuth',
					message:
						'You are already authenticated. Do you want to re-authenticate?',
					default: false
				}
			]);

			if (!continueAuth) {
				const credentials = await this.authManager.getAuthCredentials();
				ui.displaySuccess('Using existing authentication');

				if (credentials) {
					console.log(chalk.gray(`  Email: ${credentials.email || 'N/A'}`));
					console.log(chalk.gray(`  User ID: ${credentials.userId}`));
				}

				return {
					success: true,
					action: 'login',
					credentials: credentials || undefined,
					message: 'Using existing authentication'
				};
			}
		}

		try {
			// Direct browser authentication - no menu needed
			const credentials = await this.authenticateWithBrowser();

			ui.displaySuccess('Authentication successful!');
			console.log(
				chalk.gray(`  Logged in as: ${credentials.email || credentials.userId}`)
			);

			// Post-auth: Set up workspace context (skip if --yes flag is used)
			if (!yes) {
				console.log(); // Add spacing
				try {
					const contextCommand = new ContextCommand();
					const contextResult = await contextCommand.setupContextInteractive();
					if (contextResult.success) {
						if (contextResult.orgSelected && contextResult.briefSelected) {
							console.log(
								chalk.green('✓ Workspace context configured successfully')
							);
						} else if (contextResult.orgSelected) {
							console.log(chalk.green('✓ Organization selected'));
						}
					} else {
						console.log(
							chalk.yellow('⚠️ Context setup was skipped or encountered issues')
						);
						console.log(
							chalk.gray('  You can set up context later with "tm context"')
						);
					}
				} catch (contextError) {
					console.log(chalk.yellow('⚠️ Context setup encountered an error'));
					console.log(
						chalk.gray('  You can set up context later with "tm context"')
					);
					if (process.env.DEBUG) {
						console.error(chalk.gray((contextError as Error).message));
					}
				}
			} else {
				console.log(
					chalk.gray(
						'\n  Skipped interactive setup. Use "tm context" to configure later.'
					)
				);
			}

			return {
				success: true,
				action: 'login',
				credentials,
				message: 'Authentication successful'
			};
		} catch (error) {
			displayError(error, { skipExit: true });

			return {
				success: false,
				action: 'login',
				message: `Authentication failed: ${(error as Error).message}`
			};
		}
	}

	/**
	 * Authenticate with browser using OAuth 2.0 with PKCE
	 */
	private async authenticateWithBrowser(): Promise<AuthCredentials> {
		let authSpinner: Ora | null = null;

		try {
			// Use AuthManager's new unified OAuth flow method with callbacks
			const credentials = await this.authManager.authenticateWithOAuth({
				// Callback to handle browser opening
				openBrowser: async (authUrl) => {
					await open(authUrl);
				},
				timeout: 5 * 60 * 1000, // 5 minutes

				// Callback when auth URL is ready
				onAuthUrl: (authUrl) => {
					// Display authentication instructions
					console.log(chalk.blue.bold('\n🔐 Browser Authentication\n'));
					console.log(chalk.white('  Opening your browser to authenticate...'));
					console.log(chalk.gray("  If the browser doesn't open, visit:"));
					console.log(chalk.cyan.underline(`  ${authUrl}\n`));
				},

				// Callback when waiting for authentication
				onWaitingForAuth: () => {
					authSpinner = ora({
						text: 'Waiting for authentication...',
						spinner: 'dots'
					}).start();
				},

				// Callback on success
				onSuccess: () => {
					if (authSpinner) {
						authSpinner.succeed('Authentication successful!');
					}
				},

				// Callback on error
				onError: () => {
					if (authSpinner) {
						authSpinner.fail('Authentication failed');
					}
				}
			});

			return credentials;
		} catch (error) {
			throw error;
		}
	}

	/**
	 * Authenticate with token
	 */
	private async authenticateWithToken(token: string): Promise<AuthCredentials> {
		const spinner = ora('Verifying authentication token...').start();

		try {
			const credentials = await this.authManager.authenticateWithCode(token);
			spinner.succeed('Successfully authenticated!');
			return credentials;
		} catch (error) {
			spinner.fail('Authentication failed');
			throw error;
		}
	}

	/**
	 * Perform token-based authentication flow
	 */
	private async performTokenAuth(
		token: string,
		yes?: boolean
	): Promise<AuthResult> {
		ui.displayBanner('Task Master Authentication');

		try {
			// Authenticate with the token
			const credentials = await this.authenticateWithToken(token);

			ui.displaySuccess('Authentication successful!');
			console.log(
				chalk.gray(`  Logged in as: ${credentials.email || credentials.userId}`)
			);

			// Post-auth: Set up workspace context (skip if --yes flag is used)
			if (!yes) {
				console.log(); // Add spacing
				try {
					const contextCommand = new ContextCommand();
					const contextResult = await contextCommand.setupContextInteractive();
					if (contextResult.success) {
						if (contextResult.orgSelected && contextResult.briefSelected) {
							console.log(
								chalk.green('✓ Workspace context configured successfully')
							);
						} else if (contextResult.orgSelected) {
							console.log(chalk.green('✓ Organization selected'));
						}
					} else {
						console.log(
							chalk.yellow('⚠️ Context setup was skipped or encountered issues')
						);
						console.log(
							chalk.gray('  You can set up context later with "tm context"')
						);
					}
				} catch (contextError) {
					console.log(chalk.yellow('⚠️ Context setup encountered an error'));
					console.log(
						chalk.gray('  You can set up context later with "tm context"')
					);
					if (process.env.DEBUG) {
						console.error(chalk.gray((contextError as Error).message));
					}
				}
			} else {
				console.log(
					chalk.gray(
						'\n  Skipped interactive setup. Use "tm context" to configure later.'
					)
				);
			}

			return {
				success: true,
				action: 'login',
				credentials,
				message: 'Authentication successful'
			};
		} catch (error) {
			displayError(error, { skipExit: true });

			return {
				success: false,
				action: 'login',
				message: `Authentication failed: ${(error as Error).message}`
			};
		}
	}

	/**
	 * Set the last result for programmatic access
	 */
	private setLastResult(result: AuthResult): void {
		this.lastResult = result;
	}

	/**
	 * Get the last result (for programmatic usage)
	 */
	getLastResult(): AuthResult | undefined {
		return this.lastResult;
	}

	/**
	 * Get current credentials (for programmatic usage)
	 */
	async getCredentials(): Promise<AuthCredentials | null> {
		return this.authManager.getAuthCredentials();
	}

	/**
	 * Clean up resources
	 */
	async cleanup(): Promise<void> {
		// No resources to clean up for auth command
		// But keeping method for consistency with other commands
	}

	/**
	 * Register this command on an existing program
	 */
	static register(program: Command, name?: string): AuthCommand {
		const authCommand = new AuthCommand(name);
		program.addCommand(authCommand);
		return authCommand;
	}
}

```

--------------------------------------------------------------------------------
/docs/mcp-provider-guide.md:
--------------------------------------------------------------------------------

```markdown
# MCP Provider Integration Guide

## Overview

Task Master provides a **unified MCP provider** for AI operations:

**MCP Provider** (`mcp`) - Modern AI SDK-compatible provider with full structured object generation support

The MCP provider enables Task Master to act as an MCP client, using MCP servers as AI providers alongside traditional API-based providers. This integration follows the existing provider pattern and supports all standard AI operations including structured object generation for PRD parsing and task creation.

## MCP Provider Features

The **MCP Provider** (`mcp`) provides:

✅ **Full AI SDK Compatibility** - Complete LanguageModelV1 interface implementation  
✅ **Structured Object Generation** - Schema-driven outputs for PRD parsing and task creation  
✅ **Enhanced Error Handling** - Robust JSON extraction and validation  
✅ **Session Management** - Automatic session detection and context handling  
✅ **Schema Validation** - Type-safe object generation with Zod validation  

### Quick Setup

```bash
# Set MCP provider for main role  
task-master models set-main --provider mcp --model claude-3-5-sonnet-20241022
```

For detailed information, see [MCP Provider Documentation](mcp-provider.md).

## What is MCP Provider?

The MCP provider allows Task Master to:
- Connect to MCP servers/tools as AI providers
- Use session-based authentication instead of API keys
- Map AI operations to MCP tool calls
- Integrate with existing role-based provider assignment
- Maintain compatibility with fallback chains
- Support structured object generation for schema-driven features

## Configuration

### MCP Provider Setup

Add MCP provider to your `.taskmaster/config.json`:

```json
{
  "models": {
    "main": {
      "provider": "mcp",
      "modelId": "claude-3-5-sonnet-20241022",
      "maxTokens": 50000,
      "temperature": 0.2
    },
    "research": {
      "provider": "mcp", 
      "modelId": "claude-3-5-sonnet-20241022",
      "maxTokens": 8700,
      "temperature": 0.1
    },
    "fallback": {
      "provider": "anthropic",
      "modelId": "claude-3-5-sonnet-20241022"
    }
  }
}
```

### Available Models

**MCP Provider Models:**

- **`claude-3-5-sonnet-20241022`** - High-performance model for general tasks
  - **SWE Score**: 0.49
  - **Features**: Text + Object generation

- **`claude-3-opus-20240229`** - Enhanced reasoning model for complex tasks  
  - **SWE Score**: 0.725
  - **Features**: Text + Object generation

- **`mcp-sampling`** - General text generation using MCP client sampling
  - **SWE Score**: null
  - **Roles**: Supports main, research, and fallback roles
  - **SWE Score**: 0.49
  - **Cost**: $0 (session-based)
  - **Max Tokens**: 200,000
  - **Supported Roles**: main, research, fallback
  - **Features**: Text + Object generation

- **`claude-3-opus-20240229`** - Enhanced reasoning model for complex tasks  
  - **SWE Score**: 0.725
  - **Cost**: $0 (session-based)
  - **Max Tokens**: 200,000
  - **Supported Roles**: main, research, fallback
  - **Features**: Text + Object generation

**Basic MCP Provider Models:**

- **`mcp-sampling`** - General text generation using MCP client sampling
- **`mcp-sampling`** - General text generation using MCP client sampling
  - **SWE Score**: null
  - **Roles**: Supports main, research, and fallback roles

### Model ID Format

MCP model IDs use a simple format:

- **`claude-3-5-sonnet-20241022`** - Uses Claude 3.5 Sonnet via MCP sampling
- **`claude-3-opus-20240229`** - Uses Claude 3 Opus via MCP sampling  
- **`mcp-sampling`** - Uses MCP client's sampling capability for text generation

## Session Requirements

The MCP provider requires an active MCP session with sampling capabilities:

```javascript
session: {
  clientCapabilities: {
    sampling: {} // Client supports sampling requests
  }
}
```

## Usage Examples

### Basic Text Generation

```javascript
import { generateTextService } from './scripts/modules/ai-services-unified.js';

const result = await generateTextService({
  role: 'main',
  session: mcpSession, // Required for MCP provider
  prompt: 'Explain MCP integration',
  systemPrompt: 'You are a helpful AI assistant'
});

console.log(result.text);
```

### Structured Object Generation

```javascript
import { generateObjectService } from './scripts/modules/ai-services-unified.js';

const result = await generateObjectService({
  role: 'main',
  session: mcpSession,
  prompt: 'Create a task breakdown',
  schema: {
    type: 'object',
    properties: {
      tasks: {
        type: 'array',
        items: { type: 'string' }
      }
    }
  }
});

console.log(result.object);
```

### Research Operations

```javascript
const research = await generateTextService({
  role: 'research',
  session: mcpSession,
  prompt: 'Research the latest developments in AI',
  systemPrompt: 'You are a research assistant'
});
```

## CLI Integration

The MCP provider works seamlessly with Task Master CLI commands when running in an MCP context:

```bash
# Generate tasks using MCP provider (if configured as main)
task-master add-task "Implement user authentication"

# Research using MCP provider (if configured as research)
task-master research "OAuth 2.0 best practices"

# Parse PRD using MCP provider
task-master parse-prd requirements.txt
```

## Architecture Details

### Provider Architecture
**MCPProvider** (`mcp-server/src/providers/mcp-provider.js`)
   - Modern AI SDK-compliant provider for Task Master's MCP server
   - Auto-registers when MCP sessions connect to Task Master
   - Enables Task Master to use MCP sessions for AI operations
   - Supports both text generation and structured object generation

### Auto-Registration Process

When running as an MCP server, Task Master automatically:

```javascript
// On MCP session connect
server.on("connect", (event) => {
  // Check session capabilities
  if (session.clientCapabilities?.sampling) {
    // Create and register MCP provider
    const mcpProvider = new MCPProvider();
    mcpProvider.setSession(session);
    
    // Auto-register with provider registry
    providerRegistry.registerProvider('mcp', mcpProvider);
  }
});
```

This enables seamless self-referential AI operations within MCP contexts.

### Provider Pattern Integration

The MCP provider follows the same pattern as other providers:

```javascript
class MCPProvider extends BaseAIProvider {
  // Implements generateText, generateObject
  // Uses session context instead of API keys
  // Maps operations to MCP sampling requests
}
```

### Session Detection

The provider automatically detects MCP sampling capability when sessions connect:

```javascript
// On MCP session connect
if (session.clientCapabilities?.sampling) {
  // Auto-register MCP provider for use
  const mcpProvider = new MCPProvider();
  mcpProvider.setSession(session);
}
```

### Sampling Integration

AI operations use MCP sampling with different levels of support:

- `generateText()` → MCP `requestSampling()` with messages (2-minute timeout) ✅ **Full Support**
- `streamText()` → **Limited/No Support** ⚠️ See streaming limitations below
- `generateObject()` → MCP `requestSampling()` with JSON schema instructions (2-minute timeout) ✅ **Full Support**

**Timeout Configuration**: All MCP sampling requests use a 2-minute (120,000ms) timeout to accommodate complex AI operations.

#### Streaming Text Limitations ⚠️

**Important**: The MCP provider has **no support** for text streaming:

**MCPProvider**:
- **❌ No Streaming Support**: Throws error "MCP Provider does not support streaming text, use generateText instead"  
- **Solution**: Always use `generateText()` instead of `streamText()` with this provider

**Recommendation**: For streaming functionality, configure a non-MCP fallback provider (like Anthropic or OpenAI) in your fallback role.

### Error Handling

The MCP provider includes comprehensive error handling:

- Session validation errors (checks for `clientCapabilities.sampling`)
- MCP sampling request failures
- JSON parsing errors (for structured output)
- Automatic fallback to other providers

### Best Practices

### 1. Configure Fallbacks

Always configure a non-MCP fallback provider, especially for streaming operations:

```json
{
  "models": {
    "main": {
      "provider": "mcp",
      "modelId": "mcp-sampling"
    },
    "fallback": {
      "provider": "anthropic",
      "modelId": "claude-3-5-sonnet-20241022"
    }
  }
}
```

### 2. Avoid Streaming with MCP

**Do not use `streamTextService()` with MCP provider**. Use `generateTextService()` instead:

```javascript
// ❌ Don't do this with MCP provider
const result = await streamTextService({
  role: 'main', // MCP provider
  session: mcpSession,
  prompt: 'Generate content'
});

// ✅ Do this instead
const result = await generateTextService({
  role: 'main', // MCP provider
  session: mcpSession,
  prompt: 'Generate content'
});
```

### 3. Session Management

Ensure your MCP session remains active throughout Task Master operations:

```javascript
// Check session health before operations
if (!session || !session.capabilities) {
  throw new Error('MCP session not available');
}
```

### 4. Tool Availability

Verify required capabilities are available in your MCP session:

```javascript
// Check session health and capabilities
if (session && session.clientCapabilities && session.clientCapabilities.sampling) {
  console.log('MCP sampling available');
} else {
  console.log('MCP sampling not available');
}
```

### 5. Error Recovery

Handle MCP-specific errors gracefully:

```javascript
try {
  const result = await generateTextService({
    role: 'main',
    session: mcpSession,
    prompt: 'Generate content'
  });
} catch (error) {
  if (error.message.includes('MCP')) {
    // Handle MCP-specific error
    console.log('MCP error, falling back to alternate provider');
  }
}
```

## Troubleshooting

### Common Issues

1. **"MCP provider requires session context"**
   - Ensure `session` parameter is passed to service calls
   - Verify session has proper structure
   - Check that you're running in an MCP environment

2. **"MCP session must have client sampling capabilities"**
   - Check that `session.clientCapabilities.sampling` exists
   - Verify session has `requestSampling()` method
   - Ensure MCP client supports sampling feature

3. **"MCP Provider does not support streaming text, use generateText instead"**
   - **Common Error**: Occurs when calling `streamTextService()` with MCP provider
   - **Solution**: Use `generateTextService()` instead of `streamTextService()`
   - **Alternative**: Configure a non-MCP fallback provider for streaming operations

4. **"MCP sampling failed"** or **Timeout errors**
   - Check MCP client is responding to sampling requests
   - Verify session is still active and connected
   - Consider if request complexity requires longer processing time
   - Check for network connectivity issues

5. **"Model ID is required for MCP Remote Provider"**
   - Ensure `modelId` is specified in configuration
   - Use `mcp-sampling` as the standard model ID
   - Verify provider configuration is properly loaded

6. **Auto-registration failures**
   - Check that MCP session has required sampling capabilities
   - Verify server event listeners are properly configured
   - Look for provider registry initialization issues

### Streaming-Related Issues

**Error**: `streamTextService()` calls fail with MCP provider
**Cause**: MCP provider has no streaming support
**Solutions**:
- Use `generateTextService()` for all MCP-based text generation
- Configure non-MCP fallback providers for streaming requirements
- Check your provider configuration to ensure fallback chain includes streaming-capable providers

### Debug Mode

Enable debug logging to see MCP provider operations:

```javascript
// Set debug flag in config or environment
process.env.DEBUG = 'true';

// Or in .taskmasterconfig
{
  "debug": true,
  "models": { /* ... */ }
}
```

### Testing MCP Integration

Test MCP provider functionality:

```javascript
// Check if MCP provider is properly registered
import { MCPProvider } from './mcp-server/src/providers/mcp-provider.js';

// Test session capabilities
if (session && session.clientCapabilities && session.clientCapabilities.sampling) {
  console.log('MCP sampling available');
  
  // Test provider creation
  const provider = new MCPProvider();
  provider.setSession(session);
  console.log('MCP provider created successfully');
} else {
  console.log('MCP session lacks required capabilities');
}
```

## Integration with Development Tools

### VS Code with MCP Extension

When using Task Master in VS Code with MCP support:

1. Configure Task Master MCP server in your `.vscode/mcp.json`
2. Set MCP provider as main/research in `.taskmaster/config.json`
3. Benefit from integrated AI assistance within your development workflow
4. Use Task Master tools directly from VS Code's MCP interface

**Example VS Code MCP Configuration:**
```json
{
  "servers": {
    "task-master-dev": {
      "command": "npx",
      "args": ["-y", "task-master-ai"],
      "cwd": "/path/to/your/task-master-project",
      "env": {
        "NODE_ENV": "development",
        "ANTHROPIC_API_KEY": "${env:ANTHROPIC_API_KEY}",
        "TASK_MASTER_PROJECT_ROOT": "/path/to/your/project"
      }
    }
  }
}
```

### Claude Desktop

When using Task Master through Claude Desktop's MCP integration:

1. Configure Task Master as MCP provider in Claude Desktop
2. Use MCP provider for AI operations within Task Master
3. Benefit from nested MCP tool calling capabilities

### Cursor and Other MCP Clients

The MCP provider works with any MCP-compatible development environment:

1. Ensure your IDE has MCP client capabilities
2. Configure Task Master MCP server endpoint
3. Use MCP provider for enhanced AI-driven development

## Advanced Configuration

### Custom Tool Mapping

Advanced users can use MCP sampling for all roles:

```javascript
// MCP sampling for all roles
{
  "models": {
    "main": {
      "provider": "mcp",
      "modelId": "mcp-sampling"
    }
  }
}
```

### Role-Specific Configuration

Configure MCP sampling for different roles:

```json
{
  "models": {
    "main": {
      "provider": "mcp",
      "modelId": "mcp-sampling"
    },
    "research": {
      "provider": "mcp", 
      "modelId": "mcp-sampling"
    },
    "fallback": {
      "provider": "mcp",
      "modelId": "backup-server:simple-generation"
    }
  }
}
```

### API Reference

### MCPProvider Methods

- `generateText(params)` - Generate text using MCP sampling ✅ **Supported**
- `streamText(params)` - Stream text ❌ **Not supported** (throws error)
- `generateObject(params)` - Generate structured objects ✅ **Supported**
- `setSession(session)` - Update provider session
- `validateAuth(params)` - Validate session capabilities
- `getClient()` - Returns null (not applicable for MCP)

### Required Parameters

All MCP operations require:
- `session` - Active MCP session object (auto-provided when registered)
- `modelId` - MCP model identifier (typically "mcp-sampling")
- `messages` - Array of message objects

### Optional Parameters

- `temperature` - Creativity control (if supported by MCP client)
- `maxTokens` - Maximum response length (if supported)
- `schema` - JSON schema for structured output (generateObject only)

## Security Considerations

1. **Session Security**: MCP sessions should be properly authenticated
2. **Server Validation**: Only connect to trusted MCP servers
3. **Data Privacy**: Ensure MCP clients handle data according to your privacy requirements
4. **Error Exposure**: Be careful not to expose sensitive session information in error messages

## Future Enhancements

Planned improvements for MCP provider:

1. **Native Streaming Support** - True streaming for compatible MCP clients (requires MCP protocol updates)
2. **Enhanced Session Monitoring** - Automatic session validation and recovery
3. **Performance Optimization** - Caching and connection pooling
4. **Advanced Error Recovery** - Intelligent retry and fallback strategies

**Note**: True streaming support depends on future MCP protocol enhancements. Current implementation provides text generation without streaming capabilities.

```

--------------------------------------------------------------------------------
/.taskmaster/reports/task-complexity-report_autonomous-tdd-git-workflow.json:
--------------------------------------------------------------------------------

```json
{
	"meta": {
		"generatedAt": "2025-10-07T09:46:06.248Z",
		"tasksAnalyzed": 23,
		"totalTasks": 23,
		"analysisCount": 23,
		"thresholdScore": 5,
		"projectName": "Taskmaster",
		"usedResearch": false
	},
	"complexityAnalysis": [
		{
			"taskId": 31,
			"taskTitle": "Create WorkflowOrchestrator service foundation",
			"complexityScore": 7,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Break down the WorkflowOrchestrator foundation into its core architectural components: phase management system, event emitter infrastructure, state management interfaces, service integration, and lifecycle control methods. Each subtask should focus on a specific architectural concern with clear interfaces and testable units.",
			"reasoning": "This is a foundational service requiring state machine implementation, event-driven architecture, and integration with existing services. The complexity is high due to the need for robust phase management, error handling, and service orchestration patterns."
		},
		{
			"taskId": 32,
			"taskTitle": "Implement GitAdapter for repository operations",
			"complexityScore": 6,
			"recommendedSubtasks": 4,
			"expansionPrompt": "Decompose the GitAdapter implementation into: TypeScript wrapper creation around existing git-utils.js, core git operation methods with comprehensive error handling, branch naming pattern system with token replacement, and confirmation gates for destructive operations. Focus on type safety and existing code integration.",
			"reasoning": "Moderate-high complexity due to TypeScript integration over existing JavaScript utilities, branch pattern implementation, and safety mechanisms. The existing git-utils.js provides a solid foundation, reducing complexity."
		},
		{
			"taskId": 33,
			"taskTitle": "Create TestRunnerAdapter for framework detection and execution",
			"complexityScore": 8,
			"recommendedSubtasks": 6,
			"expansionPrompt": "Break down TestRunnerAdapter into framework detection logic, test execution engine with process management, Jest-specific result parsing, Vitest-specific result parsing, unified result interfaces, and final integration. Each framework parser should be separate to handle their unique output formats.",
			"reasoning": "High complexity due to multiple framework support (Jest, Vitest), child process management, result parsing from different formats, coverage reporting, and timeout handling. Each framework has unique output formats requiring specialized parsers."
		},
		{
			"taskId": 34,
			"taskTitle": "Implement autopilot CLI command structure",
			"complexityScore": 5,
			"recommendedSubtasks": 4,
			"expansionPrompt": "Structure the autopilot command into: basic command setup with Commander.js integration, comprehensive flag handling and validation system, preflight check validation with environment validation, and WorkflowOrchestrator integration with dry-run execution planning. Follow existing CLI patterns from the codebase.",
			"reasoning": "Moderate complexity involving CLI structure, flag handling, and integration with WorkflowOrchestrator. The existing CLI patterns and Commander.js usage in the codebase provide good guidance, reducing implementation complexity."
		},
		{
			"taskId": 35,
			"taskTitle": "Integrate surgical test generator with WorkflowOrchestrator",
			"complexityScore": 6,
			"recommendedSubtasks": 4,
			"expansionPrompt": "Decompose the test generation integration into: TaskExecutionService enhancement for test generation mode, TestGenerationService creation using executor framework, prompt composition system for rule integration, and framework-specific test pattern support. Leverage existing executor patterns from the codebase.",
			"reasoning": "Moderate-high complexity due to integration with existing services, prompt composition system, and framework-specific test generation. The existing executor framework and TaskExecutionService provide good integration points."
		},
		{
			"taskId": 36,
			"taskTitle": "Implement subtask TDD loop execution",
			"complexityScore": 9,
			"recommendedSubtasks": 7,
			"expansionPrompt": "Break down the TDD loop into: SubtaskExecutor class architecture, RED phase test generation, GREEN phase code generation, COMMIT phase with conventional commits, retry mechanism for GREEN phase, timeout and backoff policies, and TaskService integration. Each phase should be independently testable.",
			"reasoning": "Very high complexity due to implementing the complete TDD red-green-commit cycle with AI integration, retry logic, timeout handling, and git operations. This is the core autonomous workflow requiring robust error handling and state management."
		},
		{
			"taskId": 37,
			"taskTitle": "Add configuration schema for autopilot settings",
			"complexityScore": 4,
			"recommendedSubtasks": 3,
			"expansionPrompt": "Expand configuration support into: extending configuration interfaces with autopilot settings, updating ConfigManager validation logic, and implementing default configuration values. Build on existing configuration patterns and maintain backward compatibility.",
			"reasoning": "Low-moderate complexity involving schema extension and validation logic. The existing configuration system provides clear patterns to follow, making this primarily an extension task rather than new architecture."
		},
		{
			"taskId": 38,
			"taskTitle": "Implement run state persistence and logging",
			"complexityScore": 6,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Structure run state management into: RunStateManager service class creation, run directory structure and manifest creation, JSONL event logging system, test result and commit tracking storage, and state checkpointing with resume functionality. Focus on data integrity and structured logging.",
			"reasoning": "Moderate-high complexity due to file system operations, structured logging, state serialization, and resume functionality. Requires careful design of data formats and error handling for persistence operations."
		},
		{
			"taskId": 39,
			"taskTitle": "Add GitHub PR creation with run reports",
			"complexityScore": 5,
			"recommendedSubtasks": 4,
			"expansionPrompt": "Decompose PR creation into: PRAdapter service foundation with interfaces, GitHub CLI integration and command execution, PR body generation from run data and test results, and custom PR template system with configuration support. Leverage existing git-utils.js patterns for CLI integration.",
			"reasoning": "Moderate complexity involving GitHub CLI integration, report generation, and template systems. The existing git-utils.js provides patterns for CLI tool integration, reducing implementation complexity."
		},
		{
			"taskId": 40,
			"taskTitle": "Implement task dependency resolution for subtask ordering",
			"complexityScore": 6,
			"recommendedSubtasks": 4,
			"expansionPrompt": "Break down dependency resolution into: dependency resolution algorithm with cycle detection, topological sorting for subtask ordering, task eligibility checking system, and TaskService integration. Implement graph algorithms for dependency management with proper error handling.",
			"reasoning": "Moderate-high complexity due to graph algorithm implementation, cycle detection, and integration with existing task management. Requires careful design of dependency resolution logic and edge case handling."
		},
		{
			"taskId": 41,
			"taskTitle": "Create resume functionality for interrupted runs",
			"complexityScore": 7,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Structure resume functionality into: checkpoint creation in RunStateManager, state restoration logic with validation, state validation for safe resume operations, CLI flag implementation for resume command, and partial phase resume functionality. Focus on data integrity and workflow consistency.",
			"reasoning": "High complexity due to state serialization/deserialization, workflow restoration, validation logic, and CLI integration. Requires robust error handling and state consistency checks for reliable resume operations."
		},
		{
			"taskId": 42,
			"taskTitle": "Add coverage threshold enforcement",
			"complexityScore": 5,
			"recommendedSubtasks": 4,
			"expansionPrompt": "Decompose coverage enforcement into: coverage report parsing from Jest/Vitest, configurable threshold validation logic, coverage gates integration in workflow phases, and detailed coverage failure reporting system. Build on existing TestRunnerAdapter patterns.",
			"reasoning": "Moderate complexity involving coverage report parsing, validation logic, and workflow integration. The existing TestRunnerAdapter provides good foundation for extending coverage capabilities."
		},
		{
			"taskId": 43,
			"taskTitle": "Implement tmux-based TUI navigator",
			"complexityScore": 8,
			"recommendedSubtasks": 6,
			"expansionPrompt": "Break down TUI implementation into: framework selection and basic structure setup, left pane interface layout with status indicators, tmux integration and terminal coordination, navigation system with keybindings, real-time status updates system, and comprehensive event handling with UX polish. Each component should be independently testable.",
			"reasoning": "High complexity due to terminal UI framework integration, tmux session management, real-time updates, keyboard event handling, and terminal interface design. Requires expertise in terminal UI libraries and tmux integration."
		},
		{
			"taskId": 44,
			"taskTitle": "Add prompt composition system for context-aware test generation",
			"complexityScore": 6,
			"recommendedSubtasks": 4,
			"expansionPrompt": "Structure prompt composition into: PromptComposer service foundation, template processing engine with token replacement, rule loading system with precedence handling, and context injection with phase-specific prompt generation. Focus on flexible template system and rule management.",
			"reasoning": "Moderate-high complexity due to template processing, rule precedence systems, and context injection logic. Requires careful design of template syntax and rule loading mechanisms."
		},
		{
			"taskId": 45,
			"taskTitle": "Implement tag-branch mapping and automatic tag switching",
			"complexityScore": 5,
			"recommendedSubtasks": 3,
			"expansionPrompt": "Decompose tag-branch mapping into: GitAdapter enhancement with branch-to-tag extraction logic, automatic tag switching workflow integration, and branch-to-tag mapping persistence with validation. Build on existing git-utils.js and tag management functionality.",
			"reasoning": "Moderate complexity involving pattern matching, tag management integration, and workflow automation. The existing git-utils.js and tag management systems provide good foundation for implementation."
		},
		{
			"taskId": 46,
			"taskTitle": "Add comprehensive error handling and recovery",
			"complexityScore": 7,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Structure error handling into: error classification system with specific error types, recovery suggestion engine with actionable recommendations, error context management and preservation, force flag implementation with selective bypass, and logging/reporting system integration. Focus on actionable error messages and automated recovery where possible.",
			"reasoning": "High complexity due to comprehensive error taxonomy, recovery automation, context preservation, and integration across all workflow components. Requires deep understanding of failure modes and recovery strategies."
		},
		{
			"taskId": 47,
			"taskTitle": "Implement conventional commit message generation",
			"complexityScore": 4,
			"recommendedSubtasks": 3,
			"expansionPrompt": "Break down commit message generation into: template system creation with variable substitution, commit type auto-detection based on task content and file changes, and validation with GitAdapter integration. Follow conventional commit standards and integrate with existing git operations.",
			"reasoning": "Low-moderate complexity involving template processing, pattern matching for commit type detection, and validation logic. Well-defined conventional commit standards provide clear implementation guidance."
		},
		{
			"taskId": 48,
			"taskTitle": "Add multi-framework test execution support",
			"complexityScore": 7,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Expand test framework support into: framework detection system for multiple languages, common adapter interface design, Python pytest adapter implementation, Go and Rust adapter implementations, and integration with existing TestRunnerAdapter. Each language adapter should follow the unified interface pattern.",
			"reasoning": "High complexity due to multi-language support, framework detection across different ecosystems, and adapter pattern implementation. Each language has unique testing conventions and output formats."
		},
		{
			"taskId": 49,
			"taskTitle": "Implement workflow event streaming for real-time monitoring",
			"complexityScore": 6,
			"recommendedSubtasks": 4,
			"expansionPrompt": "Structure event streaming into: WorkflowOrchestrator EventEmitter enhancement, structured event format with metadata, event persistence to run logs, and optional WebSocket streaming for external monitoring. Focus on event consistency and real-time delivery.",
			"reasoning": "Moderate-high complexity due to event-driven architecture, structured event formats, persistence integration, and WebSocket implementation. Requires careful design of event schemas and delivery mechanisms."
		},
		{
			"taskId": 50,
			"taskTitle": "Add intelligent test targeting for faster feedback",
			"complexityScore": 7,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Decompose test targeting into: file change detection system, test dependency analysis engine, framework-specific targeting adapters, test impact calculation algorithm, and fallback integration with TestRunnerAdapter. Focus on accuracy and performance optimization.",
			"reasoning": "High complexity due to dependency analysis, impact calculation algorithms, framework-specific targeting, and integration with existing test execution. Requires sophisticated analysis of code relationships and test dependencies."
		},
		{
			"taskId": 51,
			"taskTitle": "Implement dry-run visualization with execution timeline",
			"complexityScore": 6,
			"recommendedSubtasks": 4,
			"expansionPrompt": "Structure dry-run visualization into: timeline calculation engine with duration estimates, estimation algorithms based on task complexity, ASCII art progress visualization with formatting, and resource validation with preflight checks. Focus on accurate planning and clear visual presentation.",
			"reasoning": "Moderate-high complexity due to timeline calculation, estimation algorithms, ASCII visualization, and resource validation. Requires understanding of workflow timing and visual formatting for terminal output."
		},
		{
			"taskId": 52,
			"taskTitle": "Add autopilot workflow integration tests",
			"complexityScore": 8,
			"recommendedSubtasks": 6,
			"expansionPrompt": "Structure integration testing into: isolated test environment infrastructure, mock integrations and service stubs, end-to-end workflow test scenarios, performance benchmarking and resource monitoring, test isolation and parallelization strategies, and comprehensive result validation and reporting. Focus on realistic test scenarios and reliable automation.",
			"reasoning": "High complexity due to end-to-end testing requirements, mock service integration, performance testing, isolation mechanisms, and comprehensive validation. Requires sophisticated test infrastructure and scenario design."
		},
		{
			"taskId": 53,
			"taskTitle": "Finalize autopilot documentation and examples",
			"complexityScore": 3,
			"recommendedSubtasks": 4,
			"expansionPrompt": "Structure documentation into: comprehensive autopilot documentation covering setup and usage, example PRD files and templates for different project types, troubleshooting guide for common issues and solutions, and demo materials with workflow visualization. Focus on clarity and practical examples.",
			"reasoning": "Low complexity involving documentation writing, example creation, and demo material production. The main challenge is ensuring accuracy and completeness rather than technical implementation."
		}
	]
}

```

--------------------------------------------------------------------------------
/apps/cli/src/ui/components/dashboard.component.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Dashboard components for Task Master CLI
 * Displays project statistics and dependency information
 */

import type { Task, TaskPriority, TaskStatus } from '@tm/core';
import boxen from 'boxen';
import chalk from 'chalk';
import { isTaskComplete } from '../../utils/task-status.js';
import { getComplexityWithColor } from '../../utils/ui.js';

/**
 * Statistics for task collection
 */
export interface TaskStatistics {
	total: number;
	done: number;
	inProgress: number;
	pending: number;
	blocked: number;
	deferred: number;
	cancelled: number;
	review?: number;
	completionPercentage: number;
	/** Count of all terminal complete tasks (done + completed + cancelled) */
	completedCount: number;
}

/**
 * Statistics for dependencies
 */
export interface DependencyStatistics {
	tasksWithNoDeps: number;
	tasksReadyToWork: number;
	tasksBlockedByDeps: number;
	mostDependedOnTaskId?: number;
	mostDependedOnCount?: number;
	avgDependenciesPerTask: number;
}

/**
 * Next task information
 */
export interface NextTaskInfo {
	id: string | number;
	title: string;
	priority?: TaskPriority;
	dependencies?: (string | number)[];
	complexity?: number | string;
}

/**
 * Status breakdown for progress bars
 */
export interface StatusBreakdown {
	done?: number;
	'in-progress'?: number;
	pending?: number;
	blocked?: number;
	deferred?: number;
	cancelled?: number;
	review?: number;
}

/**
 * Create a progress bar with color-coded status segments
 */
function createProgressBar(
	completionPercentage: number,
	width: number = 30,
	statusBreakdown?: StatusBreakdown
): string {
	// If no breakdown provided, use simple green bar
	if (!statusBreakdown) {
		const filled = Math.round((completionPercentage / 100) * width);
		const empty = width - filled;
		return chalk.green('█').repeat(filled) + chalk.gray('░').repeat(empty);
	}

	// Build the bar with different colored sections
	// Order matches the status display: Done, Cancelled, Deferred, In Progress, Review, Pending, Blocked
	let bar = '';
	let charsUsed = 0;

	// 1. Green filled blocks for done tasks only
	// Note: completionPercentage includes cancelled, but we show them separately in the bar
	if (statusBreakdown.done && statusBreakdown.done > 0) {
		const doneChars = Math.round((statusBreakdown.done / 100) * width);
		if (doneChars > 0) {
			bar += chalk.green('█').repeat(doneChars);
			charsUsed += doneChars;
		}
	}

	// 2. Gray filled blocks for cancelled (terminal complete, but visually distinct)
	if (statusBreakdown.cancelled && charsUsed < width) {
		const cancelledChars = Math.round(
			(statusBreakdown.cancelled / 100) * width
		);
		const actualChars = Math.min(cancelledChars, width - charsUsed);
		if (actualChars > 0) {
			bar += chalk.gray('█').repeat(actualChars);
			charsUsed += actualChars;
		}
	}

	// 3. Gray filled blocks for deferred (won't be done now)
	if (statusBreakdown.deferred && charsUsed < width) {
		const deferredChars = Math.round((statusBreakdown.deferred / 100) * width);
		const actualChars = Math.min(deferredChars, width - charsUsed);
		if (actualChars > 0) {
			bar += chalk.gray('█').repeat(actualChars);
			charsUsed += actualChars;
		}
	}

	// 4. Blue filled blocks for in-progress (actively working)
	if (statusBreakdown['in-progress'] && charsUsed < width) {
		const inProgressChars = Math.round(
			(statusBreakdown['in-progress'] / 100) * width
		);
		const actualChars = Math.min(inProgressChars, width - charsUsed);
		if (actualChars > 0) {
			bar += chalk.blue('█').repeat(actualChars);
			charsUsed += actualChars;
		}
	}

	// 5. Magenta empty blocks for review (almost done)
	if (statusBreakdown.review && charsUsed < width) {
		const reviewChars = Math.round((statusBreakdown.review / 100) * width);
		const actualChars = Math.min(reviewChars, width - charsUsed);
		if (actualChars > 0) {
			bar += chalk.magenta('░').repeat(actualChars);
			charsUsed += actualChars;
		}
	}

	// 6. Yellow empty blocks for pending (ready to start)
	if (statusBreakdown.pending && charsUsed < width) {
		const pendingChars = Math.round((statusBreakdown.pending / 100) * width);
		const actualChars = Math.min(pendingChars, width - charsUsed);
		if (actualChars > 0) {
			bar += chalk.yellow('░').repeat(actualChars);
			charsUsed += actualChars;
		}
	}

	// 7. Red empty blocks for blocked (can't start yet)
	if (statusBreakdown.blocked && charsUsed < width) {
		const blockedChars = Math.round((statusBreakdown.blocked / 100) * width);
		const actualChars = Math.min(blockedChars, width - charsUsed);
		if (actualChars > 0) {
			bar += chalk.red('░').repeat(actualChars);
			charsUsed += actualChars;
		}
	}

	// Fill any remaining space with gray empty yellow blocks
	if (charsUsed < width) {
		bar += chalk.yellow('░').repeat(width - charsUsed);
	}

	return bar;
}

/**
 * Calculate task statistics from a list of tasks
 */
export function calculateTaskStatistics(tasks: Task[]): TaskStatistics {
	const stats: TaskStatistics = {
		total: tasks.length,
		done: 0,
		inProgress: 0,
		pending: 0,
		blocked: 0,
		deferred: 0,
		cancelled: 0,
		review: 0,
		completionPercentage: 0,
		completedCount: 0
	};

	tasks.forEach((task) => {
		switch (task.status) {
			case 'done':
				stats.done++;
				break;
			case 'in-progress':
				stats.inProgress++;
				break;
			case 'pending':
				stats.pending++;
				break;
			case 'blocked':
				stats.blocked++;
				break;
			case 'deferred':
				stats.deferred++;
				break;
			case 'cancelled':
				stats.cancelled++;
				break;
			case 'review':
				stats.review = (stats.review || 0) + 1;
				break;
		}
	});

	// Count terminal complete tasks for percentage calculation and display
	stats.completedCount = tasks.filter((t) => isTaskComplete(t.status)).length;
	stats.completionPercentage =
		stats.total > 0
			? Math.round((stats.completedCount / stats.total) * 100)
			: 0;

	return stats;
}

/**
 * Calculate subtask statistics from tasks
 */
export function calculateSubtaskStatistics(tasks: Task[]): TaskStatistics {
	const stats: TaskStatistics = {
		total: 0,
		done: 0,
		inProgress: 0,
		pending: 0,
		blocked: 0,
		deferred: 0,
		cancelled: 0,
		review: 0,
		completionPercentage: 0,
		completedCount: 0
	};

	const allSubtasks: Array<{ status: string }> = [];
	tasks.forEach((task) => {
		if (task.subtasks && task.subtasks.length > 0) {
			task.subtasks.forEach((subtask) => {
				stats.total++;
				allSubtasks.push(subtask);
				switch (subtask.status) {
					case 'done':
						stats.done++;
						break;
					case 'in-progress':
						stats.inProgress++;
						break;
					case 'pending':
						stats.pending++;
						break;
					case 'blocked':
						stats.blocked++;
						break;
					case 'deferred':
						stats.deferred++;
						break;
					case 'cancelled':
						stats.cancelled++;
						break;
					case 'review':
						stats.review = (stats.review || 0) + 1;
						break;
				}
			});
		}
	});

	// Count terminal complete subtasks for percentage calculation and display
	stats.completedCount = allSubtasks.filter((st) =>
		isTaskComplete(st.status as TaskStatus)
	).length;
	stats.completionPercentage =
		stats.total > 0
			? Math.round((stats.completedCount / stats.total) * 100)
			: 0;

	return stats;
}

/**
 * Calculate dependency statistics
 */
export function calculateDependencyStatistics(
	tasks: Task[]
): DependencyStatistics {
	// Get all terminal complete task IDs - these satisfy dependencies
	const completedTaskIds = new Set(
		tasks.filter((t) => isTaskComplete(t.status)).map((t) => t.id)
	);

	const tasksWithNoDeps = tasks.filter(
		(t) =>
			!isTaskComplete(t.status) &&
			(!t.dependencies || t.dependencies.length === 0)
	).length;

	const tasksWithAllDepsSatisfied = tasks.filter(
		(t) =>
			!isTaskComplete(t.status) &&
			t.dependencies &&
			t.dependencies.length > 0 &&
			t.dependencies.every((depId) => completedTaskIds.has(depId))
	).length;

	const tasksBlockedByDeps = tasks.filter(
		(t) =>
			!isTaskComplete(t.status) &&
			t.dependencies &&
			t.dependencies.length > 0 &&
			!t.dependencies.every((depId) => completedTaskIds.has(depId))
	).length;

	// Calculate most depended-on task
	const dependencyCount: Record<string, number> = {};
	tasks.forEach((task) => {
		if (task.dependencies && task.dependencies.length > 0) {
			task.dependencies.forEach((depId) => {
				const key = String(depId);
				dependencyCount[key] = (dependencyCount[key] || 0) + 1;
			});
		}
	});

	let mostDependedOnTaskId: number | undefined;
	let mostDependedOnCount = 0;

	for (const [taskId, count] of Object.entries(dependencyCount)) {
		if (count > mostDependedOnCount) {
			mostDependedOnCount = count;
			mostDependedOnTaskId = parseInt(taskId);
		}
	}

	// Calculate average dependencies
	const totalDependencies = tasks.reduce(
		(sum, task) => sum + (task.dependencies ? task.dependencies.length : 0),
		0
	);
	const avgDependenciesPerTask =
		tasks.length > 0 ? totalDependencies / tasks.length : 0;

	return {
		tasksWithNoDeps,
		tasksReadyToWork: tasksWithNoDeps + tasksWithAllDepsSatisfied,
		tasksBlockedByDeps,
		mostDependedOnTaskId,
		mostDependedOnCount,
		avgDependenciesPerTask
	};
}

/**
 * Get priority counts
 */
export function getPriorityBreakdown(
	tasks: Task[]
): Record<TaskPriority, number> {
	const breakdown: Record<TaskPriority, number> = {
		critical: 0,
		high: 0,
		medium: 0,
		low: 0
	};

	tasks.forEach((task) => {
		const priority = task.priority || 'medium';
		breakdown[priority]++;
	});

	return breakdown;
}

/**
 * Calculate status breakdown as percentages
 */
function calculateStatusBreakdown(stats: TaskStatistics): StatusBreakdown {
	if (stats.total === 0) return {};

	return {
		done: (stats.done / stats.total) * 100,
		'in-progress': (stats.inProgress / stats.total) * 100,
		pending: (stats.pending / stats.total) * 100,
		blocked: (stats.blocked / stats.total) * 100,
		deferred: (stats.deferred / stats.total) * 100,
		cancelled: (stats.cancelled / stats.total) * 100,
		review: ((stats.review || 0) / stats.total) * 100
	};
}

/**
 * Format status counts in the correct order with colors
 * @param stats - The statistics object containing counts
 * @param isSubtask - Whether this is for subtasks (affects "Done" vs "Completed" label)
 */
function formatStatusLine(
	stats: TaskStatistics,
	isSubtask: boolean = false
): string {
	const parts: string[] = [];

	// Order: Done, Cancelled, Deferred, In Progress, Review, Pending, Blocked
	if (isSubtask) {
		parts.push(
			`Completed: ${chalk.green(`${stats.completedCount}/${stats.total}`)}`
		);
	} else {
		parts.push(`Done: ${chalk.green(stats.done)}`);
	}

	parts.push(`Cancelled: ${chalk.gray(stats.cancelled)}`);
	parts.push(`Deferred: ${chalk.gray(stats.deferred)}`);

	// Add line break for second row
	const firstLine = parts.join('  ');
	parts.length = 0;

	parts.push(`In Progress: ${chalk.blue(stats.inProgress)}`);
	parts.push(`Review: ${chalk.magenta(stats.review || 0)}`);
	parts.push(`Pending: ${chalk.yellow(stats.pending)}`);
	parts.push(`Blocked: ${chalk.red(stats.blocked)}`);

	const secondLine = parts.join('  ');

	return firstLine + '\n' + secondLine;
}

/**
 * Display the project dashboard box
 */
export function displayProjectDashboard(
	taskStats: TaskStatistics,
	subtaskStats: TaskStatistics,
	priorityBreakdown: Record<TaskPriority, number>
): string {
	// Calculate status breakdowns using the helper function
	const taskStatusBreakdown = calculateStatusBreakdown(taskStats);
	const subtaskStatusBreakdown = calculateStatusBreakdown(subtaskStats);

	// Create progress bars with the breakdowns
	const taskProgressBar = createProgressBar(
		taskStats.completionPercentage,
		30,
		taskStatusBreakdown
	);
	const subtaskProgressBar = createProgressBar(
		subtaskStats.completionPercentage,
		30,
		subtaskStatusBreakdown
	);

	const taskPercentage = `${taskStats.completionPercentage}% ${taskStats.completedCount}/${taskStats.total}`;
	const subtaskPercentage = `${subtaskStats.completionPercentage}% ${subtaskStats.completedCount}/${subtaskStats.total}`;

	const content =
		chalk.white.bold('Project Dashboard') +
		'\n' +
		`Tasks Progress: ${taskProgressBar} ${chalk.yellow(taskPercentage)}\n` +
		formatStatusLine(taskStats, false) +
		'\n\n' +
		`Subtasks Progress: ${subtaskProgressBar} ${chalk.cyan(subtaskPercentage)}\n` +
		formatStatusLine(subtaskStats, true) +
		'\n\n' +
		chalk.cyan.bold('Priority Breakdown:') +
		'\n' +
		`${chalk.red('•')} ${chalk.white('High priority:')} ${priorityBreakdown.high}\n` +
		`${chalk.yellow('•')} ${chalk.white('Medium priority:')} ${priorityBreakdown.medium}\n` +
		`${chalk.green('•')} ${chalk.white('Low priority:')} ${priorityBreakdown.low}`;

	return content;
}

/**
 * Display the dependency dashboard box
 */
export function displayDependencyDashboard(
	depStats: DependencyStatistics,
	nextTask?: NextTaskInfo
): string {
	const content =
		chalk.white.bold('Dependency Status & Next Task') +
		'\n' +
		chalk.cyan.bold('Dependency Metrics:') +
		'\n' +
		`${chalk.green('•')} ${chalk.white('Tasks with no dependencies:')} ${depStats.tasksWithNoDeps}\n` +
		`${chalk.green('•')} ${chalk.white('Tasks ready to work on:')} ${depStats.tasksReadyToWork}\n` +
		`${chalk.yellow('•')} ${chalk.white('Tasks blocked by dependencies:')} ${depStats.tasksBlockedByDeps}\n` +
		`${chalk.magenta('•')} ${chalk.white('Most depended-on task:')} ${
			depStats.mostDependedOnTaskId
				? chalk.cyan(
						`#${depStats.mostDependedOnTaskId} (${depStats.mostDependedOnCount} dependents)`
					)
				: chalk.gray('None')
		}\n` +
		`${chalk.blue('•')} ${chalk.white('Avg dependencies per task:')} ${depStats.avgDependenciesPerTask.toFixed(1)}\n\n` +
		chalk.cyan.bold('Next Task to Work On:') +
		'\n' +
		`ID: ${nextTask ? chalk.cyan(String(nextTask.id)) : chalk.gray('N/A')} - ${
			nextTask
				? chalk.white.bold(nextTask.title)
				: chalk.yellow('No task available')
		}\n` +
		`Priority: ${nextTask?.priority || chalk.gray('N/A')}  Dependencies: ${
			nextTask?.dependencies?.length
				? chalk.cyan(nextTask.dependencies.join(', '))
				: chalk.gray('None')
		}\n` +
		`Complexity: ${nextTask?.complexity !== undefined ? getComplexityWithColor(nextTask.complexity) : chalk.gray('N/A')}`;

	return content;
}

/**
 * Display dashboard boxes side by side or stacked
 */
export function displayDashboards(
	taskStats: TaskStatistics,
	subtaskStats: TaskStatistics,
	priorityBreakdown: Record<TaskPriority, number>,
	depStats: DependencyStatistics,
	nextTask?: NextTaskInfo
): void {
	const projectDashboardContent = displayProjectDashboard(
		taskStats,
		subtaskStats,
		priorityBreakdown
	);
	const dependencyDashboardContent = displayDependencyDashboard(
		depStats,
		nextTask
	);

	// Get terminal width
	const terminalWidth = process.stdout.columns || 80;
	const minDashboardWidth = 50;
	const minDependencyWidth = 50;
	const totalMinWidth = minDashboardWidth + minDependencyWidth + 4;

	// If terminal is wide enough, show side by side
	if (terminalWidth >= totalMinWidth) {
		const halfWidth = Math.floor(terminalWidth / 2);
		const boxContentWidth = halfWidth - 4;

		const dashboardBox = boxen(projectDashboardContent, {
			padding: 1,
			borderColor: 'blue',
			borderStyle: 'round',
			width: boxContentWidth,
			dimBorder: false
		});

		const dependencyBox = boxen(dependencyDashboardContent, {
			padding: 1,
			borderColor: 'magenta',
			borderStyle: 'round',
			width: boxContentWidth,
			dimBorder: false
		});

		// Create side-by-side layout
		const dashboardLines = dashboardBox.split('\n');
		const dependencyLines = dependencyBox.split('\n');
		const maxHeight = Math.max(dashboardLines.length, dependencyLines.length);

		const combinedLines = [];
		for (let i = 0; i < maxHeight; i++) {
			const dashLine = i < dashboardLines.length ? dashboardLines[i] : '';
			const depLine = i < dependencyLines.length ? dependencyLines[i] : '';
			const paddedDashLine = dashLine.padEnd(halfWidth, ' ');
			combinedLines.push(paddedDashLine + depLine);
		}

		console.log(combinedLines.join('\n'));
	} else {
		// Show stacked vertically
		const dashboardBox = boxen(projectDashboardContent, {
			padding: 1,
			borderColor: 'blue',
			borderStyle: 'round',
			margin: { top: 0, bottom: 1 }
		});

		const dependencyBox = boxen(dependencyDashboardContent, {
			padding: 1,
			borderColor: 'magenta',
			borderStyle: 'round',
			margin: { top: 0, bottom: 1 }
		});

		console.log(dashboardBox);
		console.log(dependencyBox);
	}
}

```

--------------------------------------------------------------------------------
/docs/tutorial.md:
--------------------------------------------------------------------------------

```markdown
# Task Master Tutorial

This tutorial will guide you through setting up and using Task Master for AI-driven development.

## Initial Setup

There are two ways to set up Task Master: using MCP (recommended) or via npm installation.

### Option 1: Using MCP (Recommended)

MCP (Model Control Protocol) provides the easiest way to get started with Task Master directly in your editor.

1. **Install the package**

```bash
npm i -g task-master-ai
```

2. **Add the MCP config to your IDE/MCP Client** (Cursor is recommended, but it works with other clients):

```json
{
  "mcpServers": {
    "taskmaster-ai": {
      "command": "npx",
      "args": ["-y", "task-master-ai"],
      "env": {
        "ANTHROPIC_API_KEY": "YOUR_ANTHROPIC_API_KEY_HERE",
        "PERPLEXITY_API_KEY": "YOUR_PERPLEXITY_API_KEY_HERE",
        "OPENAI_API_KEY": "YOUR_OPENAI_KEY_HERE",
        "GOOGLE_API_KEY": "YOUR_GOOGLE_KEY_HERE",
        "MISTRAL_API_KEY": "YOUR_MISTRAL_KEY_HERE",
        "OPENROUTER_API_KEY": "YOUR_OPENROUTER_KEY_HERE",
        "XAI_API_KEY": "YOUR_XAI_KEY_HERE",
        "AZURE_OPENAI_API_KEY": "YOUR_AZURE_KEY_HERE"
      }
    }
  }
}
```

**IMPORTANT:** An API key is _required_ for each AI provider you plan on using. Run the `task-master models` command to see your selected models and the status of your API keys across .env and mcp.json

**To use AI commands in CLI** you MUST have API keys in the .env file
**To use AI commands in MCP** you MUST have API keys in the .mcp.json file (or MCP config equivalent)

We recommend having keys in both places and adding mcp.json to your gitignore so your API keys aren't checked into git.

3. **Enable the MCP** in your editor settings

4. **Prompt the AI** to initialize Task Master:

```
Can you please initialize taskmaster-ai into my project?
```

The AI will:

- Create necessary project structure
- Set up initial configuration files
- Guide you through the rest of the process

5. Place your PRD document in the `.taskmaster/docs/` directory (e.g., `.taskmaster/docs/prd.txt`)

6. **Use natural language commands** to interact with Task Master:

```
Can you parse my PRD at .taskmaster/docs/prd.txt?
What's the next task I should work on?
Can you help me implement task 3?
```

### Option 2: Manual Installation

If you prefer to use the command line interface directly:

```bash
# Install globally
npm install -g task-master-ai

# OR install locally within your project
npm install task-master-ai
```

Initialize a new project:

```bash
# If installed globally
task-master init

# If installed locally
npx task-master init
```

This will prompt you for project details and set up a new project with the necessary files and structure.

## Common Commands

After setting up Task Master, you can use these commands (either via AI prompts or CLI):

```bash
# Parse a PRD and generate tasks
task-master parse-prd your-prd.txt

# List all tasks
task-master list

# Show the next task to work on
task-master next

# Generate task files
task-master generate
```

## Setting up Cursor AI Integration

Task Master is designed to work seamlessly with [Cursor AI](https://www.cursor.so/), providing a structured workflow for AI-driven development.

### Using Cursor with MCP (Recommended)

If you've already set up Task Master with MCP in Cursor, the integration is automatic. You can simply use natural language to interact with Task Master:

```
What tasks are available to work on next?
Can you analyze the complexity of our tasks?
I'd like to implement task 4. What does it involve?
```

### Manual Cursor Setup

If you're not using MCP, you can still set up Cursor integration:

1. After initializing your project, open it in Cursor
2. The `.cursor/rules/dev_workflow.mdc` file is automatically loaded by Cursor, providing the AI with knowledge about the task management system
3. Place your PRD document in the `.taskmaster/docs/` directory (e.g., `.taskmaster/docs/prd.txt`)
4. Open Cursor's AI chat and switch to Agent mode

### Alternative MCP Setup in Cursor

You can also set up the MCP server in Cursor settings:

1. Go to Cursor settings
2. Navigate to the MCP section
3. Click on "Add New MCP Server"
4. Configure with the following details:
   - Name: "Task Master"
   - Type: "Command"
   - Command: "npx -y task-master-ai"
5. Save the settings

Once configured, you can interact with Task Master's task management commands directly through Cursor's interface, providing a more integrated experience.

## Initial Task Generation

In Cursor's AI chat, instruct the agent to generate tasks from your PRD:

```
Please use the task-master parse-prd command to generate tasks from my PRD. The PRD is located at .taskmaster/docs/prd.txt.
```

The agent will execute:

```bash
task-master parse-prd .taskmaster/docs/prd.txt
```

This will:

- Parse your PRD document
- Generate a structured `tasks.json` file with tasks, dependencies, priorities, and test strategies
- The agent will understand this process due to the Cursor rules

### Generate Individual Task Files

Next, ask the agent to generate individual task files:

```
Please generate individual task files from tasks.json
```

The agent will execute:

```bash
task-master generate
```

This creates individual task files in the `tasks/` directory (e.g., `task_001.txt`, `task_002.txt`), making it easier to reference specific tasks.

## AI-Driven Development Workflow

The Cursor agent is pre-configured (via the rules file) to follow this workflow:

### 1. Task Discovery and Selection

Ask the agent to list available tasks:

```
What tasks are available to work on next?
```

```
Can you show me tasks 1, 3, and 5 to understand their current status?
```

The agent will:

- Run `task-master list` to see all tasks
- Run `task-master next` to determine the next task to work on
- Run `task-master show 1,3,5` to display multiple tasks with interactive options
- Analyze dependencies to determine which tasks are ready to be worked on
- Prioritize tasks based on priority level and ID order
- Suggest the next task(s) to implement

### 2. Task Implementation

When implementing a task, the agent will:

- Reference the task's details section for implementation specifics
- Consider dependencies on previous tasks
- Follow the project's coding standards
- Create appropriate tests based on the task's testStrategy

You can ask:

```
Let's implement task 3. What does it involve?
```

### 2.1. Viewing Multiple Tasks

For efficient context gathering and batch operations:

```
Show me tasks 5, 7, and 9 so I can plan my implementation approach.
```

The agent will:

- Run `task-master show 5,7,9` to display a compact summary table
- Show task status, priority, and progress indicators
- Provide an interactive action menu with batch operations
- Allow you to perform group actions like marking multiple tasks as in-progress

### 3. Task Verification

Before marking a task as complete, verify it according to:

- The task's specified testStrategy
- Any automated tests in the codebase
- Manual verification if required

### 4. Task Completion

When a task is completed, tell the agent:

```
Task 3 is now complete. Please update its status.
```

The agent will execute:

```bash
task-master set-status --id=3 --status=done
```

### 5. Handling Implementation Drift

If during implementation, you discover that:

- The current approach differs significantly from what was planned
- Future tasks need to be modified due to current implementation choices
- New dependencies or requirements have emerged

Tell the agent:

```
We've decided to use MongoDB instead of PostgreSQL. Can you update all future tasks (from ID 4) to reflect this change?
```

The agent will execute:

```bash
task-master update --from=4 --prompt="Now we are using MongoDB instead of PostgreSQL."

# OR, if research is needed to find best practices for MongoDB:
task-master update --from=4 --prompt="Update to use MongoDB, researching best practices" --research
```

This will rewrite or re-scope subsequent tasks in tasks.json while preserving completed work.

### 6. Reorganizing Tasks

If you need to reorganize your task structure:

```
I think subtask 5.2 would fit better as part of task 7 instead. Can you move it there?
```

The agent will execute:

```bash
task-master move --from=5.2 --to=7.3
```

You can reorganize tasks in various ways:

- Moving a standalone task to become a subtask: `--from=5 --to=7`
- Moving a subtask to become a standalone task: `--from=5.2 --to=7`
- Moving a subtask to a different parent: `--from=5.2 --to=7.3`
- Reordering subtasks within the same parent: `--from=5.2 --to=5.4`
- Moving a task to a new ID position: `--from=5 --to=25` (even if task 25 doesn't exist yet)
- Moving multiple tasks at once: `--from=10,11,12 --to=16,17,18` (must have same number of IDs, Taskmaster will look through each position)

When moving tasks to new IDs:

- The system automatically creates placeholder tasks for non-existent destination IDs
- This prevents accidental data loss during reorganization
- Any tasks that depend on moved tasks will have their dependencies updated
- When moving a parent task, all its subtasks are automatically moved with it and renumbered

This is particularly useful as your project understanding evolves and you need to refine your task structure.

### 7. Resolving Merge Conflicts with Tasks

When working with a team, you might encounter merge conflicts in your tasks.json file if multiple team members create tasks on different branches. The move command makes resolving these conflicts straightforward:

```
I just merged the main branch and there's a conflict with tasks.json. My teammates created tasks 10-15 while I created tasks 10-12 on my branch. Can you help me resolve this?
```

The agent will help you:

1. Keep your teammates' tasks (10-15)
2. Move your tasks to new positions to avoid conflicts:

```bash
# Move your tasks to new positions (e.g., 16-18)
task-master move --from=10 --to=16
task-master move --from=11 --to=17
task-master move --from=12 --to=18
```

This approach preserves everyone's work while maintaining a clean task structure, making it much easier to handle task conflicts than trying to manually merge JSON files.

### 8. Breaking Down Complex Tasks

For complex tasks that need more granularity:

```
Task 5 seems complex. Can you break it down into subtasks?
```

The agent will execute:

```bash
task-master expand --id=5 --num=3
```

You can provide additional context:

```
Please break down task 5 with a focus on security considerations.
```

The agent will execute:

```bash
task-master expand --id=5 --prompt="Focus on security aspects"
```

You can also expand all pending tasks:

```
Please break down all pending tasks into subtasks.
```

The agent will execute:

```bash
task-master expand --all
```

For research-backed subtask generation using the configured research model:

```
Please break down task 5 using research-backed generation.
```

The agent will execute:

```bash
task-master expand --id=5 --research
```

## Example Cursor AI Interactions

### Starting a new project

```
I've just initialized a new project with Claude Task Master. I have a PRD at .taskmaster/docs/prd.txt.
Can you help me parse it and set up the initial tasks?
```

### Working on tasks

```
What's the next task I should work on? Please consider dependencies and priorities.
```

### Implementing a specific task

```
I'd like to implement task 4. Can you help me understand what needs to be done and how to approach it?
```

### Managing subtasks

```
I need to regenerate the subtasks for task 3 with a different approach. Can you help me clear and regenerate them?
```

### Handling changes

```
We've decided to use MongoDB instead of PostgreSQL. Can you update all future tasks to reflect this change?
```

### Completing work

```
I've finished implementing the authentication system described in task 2. All tests are passing.
Please mark it as complete and tell me what I should work on next.
```

### Analyzing complexity

```
Can you analyze the complexity of our tasks to help me understand which ones need to be broken down further?
```

### Viewing complexity report

```
Can you show me the complexity report in a more readable format?
```

### Research-Driven Development

Task Master includes a powerful research tool that provides fresh, up-to-date information beyond the AI's knowledge cutoff. This is particularly valuable for:

#### Getting Current Best Practices

```
Before implementing task 5 (authentication), research the latest JWT security recommendations.
```

The agent will execute:

```bash
task-master research "Latest JWT security recommendations 2024" --id=5
```

#### Research with Project Context

```
Research React Query v5 migration strategies for our current API implementation.
```

The agent will execute:

```bash
task-master research "React Query v5 migration strategies" --files=src/api.js,src/hooks.js
```

#### Research and Update Pattern

A powerful workflow is to research first, then update tasks with findings:

```
Research the latest Node.js performance optimization techniques and update task 12 with the findings.
```

The agent will:

1. Run research: `task-master research "Node.js performance optimization 2024" --id=12`
2. Update the task: `task-master update-subtask --id=12.2 --prompt="Updated with latest performance findings: [research results]"`

#### When to Use Research

- **Before implementing any new technology**
- **When encountering security-related tasks**
- **For performance optimization tasks**
- **When debugging complex issues**
- **Before making architectural decisions**
- **When updating dependencies**

The research tool automatically includes relevant project context and provides fresh information that can significantly improve implementation quality.

## Git Integration and Tag Management

Task Master supports tagged task lists for multi-context development, which is particularly useful when working with git branches or different project phases.

### Working with Tags

Tags provide isolated task contexts, allowing you to maintain separate task lists for different features, branches, or experiments:

```
I'm starting work on a new feature branch. Can you create a new tag for this work?
```

The agent will execute:

```bash
# Create a tag based on your current git branch
task-master add-tag --from-branch
```

Or you can create a tag with a specific name:

```
Create a new tag called 'user-auth' for authentication-related tasks.
```

The agent will execute:

```bash
task-master add-tag user-auth --description="User authentication feature tasks"
```

### Switching Between Contexts

When working on different features or branches:

```
Switch to the 'user-auth' tag context so I can work on authentication tasks.
```

The agent will execute:

```bash
task-master use-tag user-auth
```

### Copying Tasks Between Tags

When you need to duplicate work across contexts:

```
Copy all tasks from the current tag to a new 'testing' tag for QA work.
```

The agent will execute:

```bash
task-master add-tag testing --copy-from-current --description="QA and testing tasks"
```

### Tag Management

View and manage your tag contexts:

```
Show me all available tags and their current status.
```

The agent will execute:

```bash
task-master tags --show-metadata
```

### Benefits of Tagged Task Lists

- **Branch Isolation**: Each git branch can have its own task context
- **Merge Conflict Prevention**: Tasks in different tags don't interfere with each other
- **Parallel Development**: Multiple team members can work on separate contexts
- **Context Switching**: Easily switch between different project phases or features
- **Experimentation**: Create experimental task lists without affecting main work

### Git Workflow Integration

A typical git workflow with Task Master tags:

1. **Create feature branch**: `git checkout -b feature/user-auth`
2. **Create matching tag**: Ask agent to run `task-master add-tag --from-branch`
3. **Work in isolated context**: All task operations work within the new tag
4. **Switch contexts as needed**: Use `task-master use-tag <name>` to switch between different work streams
5. **Merge and cleanup**: After merging the branch, optionally delete the tag with `task-master delete-tag <name>`

This workflow ensures your task management stays organized and conflicts are minimized when working with teams or multiple features simultaneously.

```
Page 29/50FirstPrevNextLast