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

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/scripts/modules/commands.js:
--------------------------------------------------------------------------------

```javascript
   1 | /**
   2 |  * commands.js
   3 |  * Command-line interface for the Task Master CLI
   4 |  */
   5 | 
   6 | import { Command } from 'commander';
   7 | import path from 'path';
   8 | import chalk from 'chalk';
   9 | import boxen from 'boxen';
  10 | import fs from 'fs';
  11 | import inquirer from 'inquirer';
  12 | 
  13 | import { log, readJSON } from './utils.js';
  14 | // Import command registry and utilities from @tm/cli
  15 | import {
  16 | 	registerAllCommands,
  17 | 	checkForUpdate,
  18 | 	performAutoUpdate,
  19 | 	displayUpgradeNotification,
  20 | 	restartWithNewVersion,
  21 | 	displayError,
  22 | 	runInteractiveSetup
  23 | } from '@tm/cli';
  24 | 
  25 | import {
  26 | 	parsePRD,
  27 | 	updateTasks,
  28 | 	generateTaskFiles,
  29 | 	expandTask,
  30 | 	expandAllTasks,
  31 | 	clearSubtasks,
  32 | 	addTask,
  33 | 	addSubtask,
  34 | 	removeSubtask,
  35 | 	analyzeTaskComplexity,
  36 | 	updateTaskById,
  37 | 	updateSubtaskById,
  38 | 	removeTask,
  39 | 	findTaskById,
  40 | 	taskExists,
  41 | 	moveTask,
  42 | 	migrateProject,
  43 | 	setResponseLanguage,
  44 | 	scopeUpTask,
  45 | 	scopeDownTask,
  46 | 	validateStrength
  47 | } from './task-manager.js';
  48 | 
  49 | import { moveTasksBetweenTags } from './task-manager/move-task.js';
  50 | 
  51 | import {
  52 | 	createTag,
  53 | 	deleteTag,
  54 | 	tags,
  55 | 	useTag,
  56 | 	renameTag,
  57 | 	copyTag
  58 | } from './task-manager/tag-management.js';
  59 | 
  60 | import {
  61 | 	addDependency,
  62 | 	removeDependency,
  63 | 	validateDependenciesCommand,
  64 | 	fixDependenciesCommand
  65 | } from './dependency-manager.js';
  66 | 
  67 | import {
  68 | 	isApiKeySet,
  69 | 	getDebugFlag,
  70 | 	ConfigurationError,
  71 | 	isConfigFilePresent,
  72 | 	getDefaultNumTasks
  73 | } from './config-manager.js';
  74 | 
  75 | import { CUSTOM_PROVIDERS } from '@tm/core';
  76 | 
  77 | import {
  78 | 	COMPLEXITY_REPORT_FILE,
  79 | 	TASKMASTER_TASKS_FILE,
  80 | 	TASKMASTER_DOCS_DIR
  81 | } from '../../src/constants/paths.js';
  82 | 
  83 | import { initTaskMaster } from '../../src/task-master.js';
  84 | 
  85 | import {
  86 | 	displayBanner,
  87 | 	displayHelp,
  88 | 	displayComplexityReport,
  89 | 	getStatusWithColor,
  90 | 	confirmTaskOverwrite,
  91 | 	startLoadingIndicator,
  92 | 	stopLoadingIndicator,
  93 | 	displayModelConfiguration,
  94 | 	displayAvailableModels,
  95 | 	displayApiKeyStatus,
  96 | 	displayTaggedTasksFYI,
  97 | 	displayCurrentTagIndicator,
  98 | 	displayCrossTagDependencyError,
  99 | 	displaySubtaskMoveError,
 100 | 	displayInvalidTagCombinationError,
 101 | 	displayDependencyValidationHints
 102 | } from './ui.js';
 103 | import {
 104 | 	confirmProfilesRemove,
 105 | 	confirmRemoveAllRemainingProfiles
 106 | } from '../../src/ui/confirm.js';
 107 | import {
 108 | 	wouldRemovalLeaveNoProfiles,
 109 | 	getInstalledProfiles
 110 | } from '../../src/utils/profiles.js';
 111 | 
 112 | import { initializeProject } from '../init.js';
 113 | import {
 114 | 	getModelConfiguration,
 115 | 	getAvailableModelsList,
 116 | 	setModel,
 117 | 	getApiKeyStatusReport
 118 | } from './task-manager/models.js';
 119 | import {
 120 | 	isValidRulesAction,
 121 | 	RULES_ACTIONS,
 122 | 	RULES_SETUP_ACTION
 123 | } from '../../src/constants/rules-actions.js';
 124 | import { getTaskMasterVersion } from '../../src/utils/getVersion.js';
 125 | import { syncTasksToReadme } from './sync-readme.js';
 126 | import { RULE_PROFILES } from '../../src/constants/profiles.js';
 127 | import {
 128 | 	convertAllRulesToProfileRules,
 129 | 	removeProfileRules,
 130 | 	isValidProfile,
 131 | 	getRulesProfile
 132 | } from '../../src/utils/rule-transformer.js';
 133 | import {
 134 | 	runInteractiveProfilesSetup,
 135 | 	generateProfileSummary,
 136 | 	categorizeProfileResults,
 137 | 	generateProfileRemovalSummary,
 138 | 	categorizeRemovalResults
 139 | } from '../../src/utils/profiles.js';
 140 | 
 141 | /**
 142 |  * Configure and register CLI commands
 143 |  * @param {Object} program - Commander program instance
 144 |  */
 145 | function registerCommands(programInstance) {
 146 | 	// Add global error handler for unknown options
 147 | 	programInstance.on('option:unknown', function (unknownOption) {
 148 | 		const commandName = this._name || 'unknown';
 149 | 		console.error(chalk.red(`Error: Unknown option '${unknownOption}'`));
 150 | 		console.error(
 151 | 			chalk.yellow(
 152 | 				`Run 'task-master ${commandName} --help' to see available options`
 153 | 			)
 154 | 		);
 155 | 		process.exit(1);
 156 | 	});
 157 | 
 158 | 	// parse-prd command
 159 | 	programInstance
 160 | 		.command('parse-prd')
 161 | 		.description('Parse a PRD file and generate tasks')
 162 | 		.argument('[file]', 'Path to the PRD file')
 163 | 		.option(
 164 | 			'-i, --input <file>',
 165 | 			'Path to the PRD file (alternative to positional argument)'
 166 | 		)
 167 | 		.option('-o, --output <file>', 'Output file path')
 168 | 		.option(
 169 | 			'-n, --num-tasks <number>',
 170 | 			'Number of tasks to generate',
 171 | 			getDefaultNumTasks()
 172 | 		)
 173 | 		.option('-f, --force', 'Skip confirmation when overwriting existing tasks')
 174 | 		.option(
 175 | 			'--append',
 176 | 			'Append new tasks to existing tasks.json instead of overwriting'
 177 | 		)
 178 | 		.option(
 179 | 			'-r, --research',
 180 | 			'Use Perplexity AI for research-backed task generation, providing more comprehensive and accurate task breakdown'
 181 | 		)
 182 | 		.option('--tag <tag>', 'Specify tag context for task operations')
 183 | 		.action(async (file, options) => {
 184 | 			// Initialize TaskMaster
 185 | 			let taskMaster;
 186 | 			try {
 187 | 				const initOptions = {
 188 | 					prdPath: file || options.input || true,
 189 | 					tag: options.tag
 190 | 				};
 191 | 				// Only include tasksPath if output is explicitly specified
 192 | 				if (options.output) {
 193 | 					initOptions.tasksPath = options.output;
 194 | 				}
 195 | 				taskMaster = initTaskMaster(initOptions);
 196 | 			} catch (error) {
 197 | 				console.log(
 198 | 					boxen(
 199 | 						`${chalk.white.bold('Parse PRD Help')}\n\n${chalk.cyan('Usage:')}\n  task-master parse-prd <prd-file.txt> [options]\n\n${chalk.cyan('Options:')}\n  -i, --input <file>       Path to the PRD file (alternative to positional argument)\n  -o, --output <file>      Output file path (default: .taskmaster/tasks/tasks.json)\n  -n, --num-tasks <number> Number of tasks to generate (default: 10)\n  -f, --force              Skip confirmation when overwriting existing tasks\n  --append                 Append new tasks to existing tasks.json instead of overwriting\n  -r, --research           Use Perplexity AI for research-backed task generation\n\n${chalk.cyan('Example:')}\n  task-master parse-prd requirements.txt --num-tasks 15\n  task-master parse-prd --input=requirements.txt\n  task-master parse-prd --force\n  task-master parse-prd requirements_v2.txt --append\n  task-master parse-prd requirements.txt --research\n\n${chalk.yellow('Note: This command will:')}\n  1. Look for a PRD file at ${TASKMASTER_DOCS_DIR}/PRD.md by default\n  2. Use the file specified by --input or positional argument if provided\n  3. Generate tasks from the PRD and either:\n     - Overwrite any existing tasks.json file (default)\n     - Append to existing tasks.json if --append is used`,
 200 | 						{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
 201 | 					)
 202 | 				);
 203 | 				console.error(chalk.red(`\nError: ${error.message}`));
 204 | 				process.exit(1);
 205 | 			}
 206 | 
 207 | 			const numTasks = parseInt(options.numTasks, 10);
 208 | 			const force = options.force || false;
 209 | 			const append = options.append || false;
 210 | 			const research = options.research || false;
 211 | 			let useForce = force;
 212 | 			const useAppend = append;
 213 | 
 214 | 			// Resolve tag using standard pattern
 215 | 			const tag = taskMaster.getCurrentTag();
 216 | 
 217 | 			// Show current tag context
 218 | 			displayCurrentTagIndicator(tag);
 219 | 
 220 | 			// Helper function to check if there are existing tasks in the target tag and confirm overwrite
 221 | 			async function confirmOverwriteIfNeeded() {
 222 | 				// Check if there are existing tasks in the target tag
 223 | 				let hasExistingTasksInTag = false;
 224 | 				const tasksPath = taskMaster.getTasksPath();
 225 | 				if (fs.existsSync(tasksPath)) {
 226 | 					try {
 227 | 						// Read the entire file to check if the tag exists
 228 | 						const existingFileContent = fs.readFileSync(tasksPath, 'utf8');
 229 | 						const allData = JSON.parse(existingFileContent);
 230 | 
 231 | 						// Check if the target tag exists and has tasks
 232 | 						if (
 233 | 							allData[tag] &&
 234 | 							Array.isArray(allData[tag].tasks) &&
 235 | 							allData[tag].tasks.length > 0
 236 | 						) {
 237 | 							hasExistingTasksInTag = true;
 238 | 						}
 239 | 					} catch (error) {
 240 | 						// If we can't read the file or parse it, assume no existing tasks in this tag
 241 | 						hasExistingTasksInTag = false;
 242 | 					}
 243 | 				}
 244 | 
 245 | 				// Only show confirmation if there are existing tasks in the target tag
 246 | 				if (hasExistingTasksInTag && !useForce && !useAppend) {
 247 | 					const overwrite = await confirmTaskOverwrite(tasksPath);
 248 | 					if (!overwrite) {
 249 | 						log('info', 'Operation cancelled.');
 250 | 						return false;
 251 | 					}
 252 | 					// If user confirms 'y', we should set useForce = true for the parsePRD call
 253 | 					// Only overwrite if not appending
 254 | 					useForce = true;
 255 | 				}
 256 | 				return true;
 257 | 			}
 258 | 
 259 | 			try {
 260 | 				if (!(await confirmOverwriteIfNeeded())) return;
 261 | 
 262 | 				console.log(chalk.blue(`Parsing PRD file: ${taskMaster.getPrdPath()}`));
 263 | 				console.log(chalk.blue(`Generating ${numTasks} tasks...`));
 264 | 				if (append) {
 265 | 					console.log(chalk.blue('Appending to existing tasks...'));
 266 | 				}
 267 | 				if (research) {
 268 | 					console.log(
 269 | 						chalk.blue(
 270 | 							'Using Perplexity AI for research-backed task generation'
 271 | 						)
 272 | 					);
 273 | 				}
 274 | 
 275 | 				// Handle case where getTasksPath() returns null
 276 | 				const outputPath =
 277 | 					taskMaster.getTasksPath() ||
 278 | 					path.join(taskMaster.getProjectRoot(), TASKMASTER_TASKS_FILE);
 279 | 				await parsePRD(taskMaster.getPrdPath(), outputPath, numTasks, {
 280 | 					append: useAppend,
 281 | 					force: useForce,
 282 | 					research: research,
 283 | 					projectRoot: taskMaster.getProjectRoot(),
 284 | 					tag: tag
 285 | 				});
 286 | 			} catch (error) {
 287 | 				console.error(chalk.red(`Error parsing PRD: ${error.message}`));
 288 | 				process.exit(1);
 289 | 			}
 290 | 		});
 291 | 
 292 | 	// update command
 293 | 	programInstance
 294 | 		.command('update')
 295 | 		.description(
 296 | 			'Update multiple tasks with ID >= "from" based on new information or implementation changes'
 297 | 		)
 298 | 		.option(
 299 | 			'-f, --file <file>',
 300 | 			'Path to the tasks file',
 301 | 			TASKMASTER_TASKS_FILE
 302 | 		)
 303 | 		.option(
 304 | 			'--from <id>',
 305 | 			'Task ID to start updating from (tasks with ID >= this value will be updated)',
 306 | 			'1'
 307 | 		)
 308 | 		.option(
 309 | 			'-p, --prompt <text>',
 310 | 			'Prompt explaining the changes or new context (required)'
 311 | 		)
 312 | 		.option(
 313 | 			'-r, --research',
 314 | 			'Use Perplexity AI for research-backed task updates'
 315 | 		)
 316 | 		.option('--tag <tag>', 'Specify tag context for task operations')
 317 | 		.action(async (options) => {
 318 | 			// Initialize TaskMaster
 319 | 			const taskMaster = initTaskMaster({
 320 | 				tasksPath: options.file || true,
 321 | 				tag: options.tag
 322 | 			});
 323 | 
 324 | 			const fromId = parseInt(options.from, 10); // Validation happens here
 325 | 			const prompt = options.prompt;
 326 | 			const useResearch = options.research || false;
 327 | 
 328 | 			const tasksPath = taskMaster.getTasksPath();
 329 | 
 330 | 			// Resolve tag using standard pattern
 331 | 			const tag = taskMaster.getCurrentTag();
 332 | 
 333 | 			// Show current tag context
 334 | 			displayCurrentTagIndicator(tag);
 335 | 
 336 | 			// Check if there's an 'id' option which is a common mistake (instead of 'from')
 337 | 			if (
 338 | 				process.argv.includes('--id') ||
 339 | 				process.argv.some((arg) => arg.startsWith('--id='))
 340 | 			) {
 341 | 				console.error(
 342 | 					chalk.red('Error: The update command uses --from=<id>, not --id=<id>')
 343 | 				);
 344 | 				console.log(chalk.yellow('\nTo update multiple tasks:'));
 345 | 				console.log(
 346 | 					`  task-master update --from=${fromId} --prompt="Your prompt here"`
 347 | 				);
 348 | 				console.log(
 349 | 					chalk.yellow(
 350 | 						'\nTo update a single specific task, use the update-task command instead:'
 351 | 					)
 352 | 				);
 353 | 				console.log(
 354 | 					`  task-master update-task --id=<id> --prompt="Your prompt here"`
 355 | 				);
 356 | 				process.exit(1);
 357 | 			}
 358 | 
 359 | 			if (!prompt) {
 360 | 				console.error(
 361 | 					chalk.red(
 362 | 						'Error: --prompt parameter is required. Please provide information about the changes.'
 363 | 					)
 364 | 				);
 365 | 				process.exit(1);
 366 | 			}
 367 | 
 368 | 			console.log(
 369 | 				chalk.blue(
 370 | 					`Updating tasks from ID >= ${fromId} with prompt: "${prompt}"`
 371 | 				)
 372 | 			);
 373 | 			console.log(chalk.blue(`Tasks file: ${tasksPath}`));
 374 | 
 375 | 			if (useResearch) {
 376 | 				console.log(
 377 | 					chalk.blue('Using Perplexity AI for research-backed task updates')
 378 | 				);
 379 | 			}
 380 | 
 381 | 			// Call core updateTasks, passing context for CLI
 382 | 			await updateTasks(
 383 | 				taskMaster.getTasksPath(),
 384 | 				fromId,
 385 | 				prompt,
 386 | 				useResearch,
 387 | 				{ projectRoot: taskMaster.getProjectRoot(), tag } // Pass context with projectRoot and tag
 388 | 			);
 389 | 		});
 390 | 
 391 | 	// update-task command
 392 | 	programInstance
 393 | 		.command('update-task')
 394 | 		.description(
 395 | 			'Update a single specific task by ID with new information (use --id parameter)'
 396 | 		)
 397 | 		.option(
 398 | 			'-f, --file <file>',
 399 | 			'Path to the tasks file',
 400 | 			TASKMASTER_TASKS_FILE
 401 | 		)
 402 | 		.option('-i, --id <id>', 'Task ID to update (required)')
 403 | 		.option(
 404 | 			'-p, --prompt <text>',
 405 | 			'Prompt explaining the changes or new context (required)'
 406 | 		)
 407 | 		.option(
 408 | 			'-r, --research',
 409 | 			'Use Perplexity AI for research-backed task updates'
 410 | 		)
 411 | 		.option(
 412 | 			'--append',
 413 | 			'Append timestamped information to task details instead of full update'
 414 | 		)
 415 | 		.option('--tag <tag>', 'Specify tag context for task operations')
 416 | 		.action(async (options) => {
 417 | 			try {
 418 | 				// Initialize TaskMaster
 419 | 				const taskMaster = initTaskMaster({
 420 | 					tasksPath: options.file || true,
 421 | 					tag: options.tag
 422 | 				});
 423 | 				const tasksPath = taskMaster.getTasksPath();
 424 | 
 425 | 				// Resolve tag using standard pattern
 426 | 				const tag = taskMaster.getCurrentTag();
 427 | 
 428 | 				// Show current tag context
 429 | 				displayCurrentTagIndicator(tag);
 430 | 
 431 | 				// Validate required parameters
 432 | 				if (!options.id) {
 433 | 					console.error(chalk.red('Error: --id parameter is required'));
 434 | 					console.log(
 435 | 						chalk.yellow(
 436 | 							'Usage example: task-master update-task --id=23 --prompt="Update with new information"'
 437 | 						)
 438 | 					);
 439 | 					process.exit(1);
 440 | 				}
 441 | 
 442 | 				// Parse the task ID and validate it's a number or a string like ham-123 or tas-456
 443 | 				// Accept valid task IDs:
 444 | 				// - positive integers (e.g. 1,2,3)
 445 | 				// - strings like ham-123, ham-1, tas-456, etc
 446 | 				// Disallow decimals and invalid formats
 447 | 				const validId =
 448 | 					/^\d+$/.test(options.id) || // plain positive integer
 449 | 					/^[a-z]+-\d+$/i.test(options.id); // label-number format (e.g., ham-123)
 450 | 
 451 | 				if (!validId) {
 452 | 					console.error(
 453 | 						chalk.red(
 454 | 							`Error: Invalid task ID: ${options.id}. Task ID must be a positive integer or in the form "ham-123".`
 455 | 						)
 456 | 					);
 457 | 					console.log(
 458 | 						chalk.yellow(
 459 | 							'Usage example: task-master update-task --id=23 --prompt="Update with new information"'
 460 | 						)
 461 | 					);
 462 | 					process.exit(1);
 463 | 				}
 464 | 
 465 | 				const taskId = options.id;
 466 | 
 467 | 				if (!options.prompt) {
 468 | 					console.error(
 469 | 						chalk.red(
 470 | 							'Error: --prompt parameter is required. Please provide information about the changes.'
 471 | 						)
 472 | 					);
 473 | 					console.log(
 474 | 						chalk.yellow(
 475 | 							'Usage example: task-master update-task --id=23 --prompt="Update with new information"'
 476 | 						)
 477 | 					);
 478 | 					process.exit(1);
 479 | 				}
 480 | 
 481 | 				const prompt = options.prompt;
 482 | 				const useResearch = options.research || false;
 483 | 
 484 | 				// Validate tasks file exists
 485 | 				if (!fs.existsSync(tasksPath)) {
 486 | 					console.error(
 487 | 						chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
 488 | 					);
 489 | 					if (tasksPath === TASKMASTER_TASKS_FILE) {
 490 | 						console.log(
 491 | 							chalk.yellow(
 492 | 								'Hint: Run task-master init or task-master parse-prd to create tasks.json first'
 493 | 							)
 494 | 						);
 495 | 					} else {
 496 | 						console.log(
 497 | 							chalk.yellow(
 498 | 								`Hint: Check if the file path is correct: ${tasksPath}`
 499 | 							)
 500 | 						);
 501 | 					}
 502 | 					process.exit(1);
 503 | 				}
 504 | 
 505 | 				console.log(
 506 | 					chalk.blue(`Updating task ${taskId} with prompt: "${prompt}"`)
 507 | 				);
 508 | 				console.log(chalk.blue(`Tasks file: ${tasksPath}`));
 509 | 
 510 | 				if (useResearch) {
 511 | 					// Verify Perplexity API key exists if using research
 512 | 					if (!isApiKeySet('perplexity')) {
 513 | 						console.log(
 514 | 							chalk.yellow(
 515 | 								'Warning: PERPLEXITY_API_KEY environment variable is missing. Research-backed updates will not be available.'
 516 | 							)
 517 | 						);
 518 | 						console.log(
 519 | 							chalk.yellow('Falling back to Claude AI for task update.')
 520 | 						);
 521 | 					} else {
 522 | 						console.log(
 523 | 							chalk.blue('Using Perplexity AI for research-backed task update')
 524 | 						);
 525 | 					}
 526 | 				}
 527 | 
 528 | 				const result = await updateTaskById(
 529 | 					taskMaster.getTasksPath(),
 530 | 					taskId,
 531 | 					prompt,
 532 | 					useResearch,
 533 | 					{ projectRoot: taskMaster.getProjectRoot(), tag },
 534 | 					'text',
 535 | 					options.append || false
 536 | 				);
 537 | 
 538 | 				// If the task wasn't updated (e.g., if it was already marked as done)
 539 | 				if (!result) {
 540 | 					console.log(
 541 | 						chalk.yellow(
 542 | 							'\nTask update was not completed. Review the messages above for details.'
 543 | 						)
 544 | 					);
 545 | 				}
 546 | 			} catch (error) {
 547 | 				console.error(chalk.red(`Error: ${error.message}`));
 548 | 
 549 | 				// Provide more helpful error messages for common issues
 550 | 				if (
 551 | 					error.message.includes('task') &&
 552 | 					error.message.includes('not found')
 553 | 				) {
 554 | 					console.log(chalk.yellow('\nTo fix this issue:'));
 555 | 					console.log(
 556 | 						'  1. Run task-master list to see all available task IDs'
 557 | 					);
 558 | 					console.log('  2. Use a valid task ID with the --id parameter');
 559 | 				} else if (error.message.includes('API key')) {
 560 | 					console.log(
 561 | 						chalk.yellow(
 562 | 							'\nThis error is related to API keys. Check your environment variables.'
 563 | 						)
 564 | 					);
 565 | 				}
 566 | 
 567 | 				// Use getDebugFlag getter instead of CONFIG.debug
 568 | 				if (getDebugFlag()) {
 569 | 					console.error(error);
 570 | 				}
 571 | 
 572 | 				process.exit(1);
 573 | 			}
 574 | 		});
 575 | 
 576 | 	// update-subtask command
 577 | 	programInstance
 578 | 		.command('update-subtask')
 579 | 		.description(
 580 | 			'Update a subtask by appending additional timestamped information'
 581 | 		)
 582 | 		.option(
 583 | 			'-f, --file <file>',
 584 | 			'Path to the tasks file',
 585 | 			TASKMASTER_TASKS_FILE
 586 | 		)
 587 | 		.option(
 588 | 			'-i, --id <id>',
 589 | 			'Subtask ID to update in format "parentId.subtaskId" (required)'
 590 | 		)
 591 | 		.option(
 592 | 			'-p, --prompt <text>',
 593 | 			'Prompt explaining what information to add (required)'
 594 | 		)
 595 | 		.option('-r, --research', 'Use Perplexity AI for research-backed updates')
 596 | 		.option('--tag <tag>', 'Specify tag context for task operations')
 597 | 		.action(async (options) => {
 598 | 			try {
 599 | 				// Initialize TaskMaster
 600 | 				const taskMaster = initTaskMaster({
 601 | 					tasksPath: options.file || true,
 602 | 					tag: options.tag
 603 | 				});
 604 | 				const tasksPath = taskMaster.getTasksPath();
 605 | 
 606 | 				// Resolve tag using standard pattern
 607 | 				const tag = taskMaster.getCurrentTag();
 608 | 
 609 | 				// Show current tag context
 610 | 				displayCurrentTagIndicator(tag);
 611 | 
 612 | 				// Validate required parameters
 613 | 				if (!options.id) {
 614 | 					console.error(chalk.red('Error: --id parameter is required'));
 615 | 					console.log(
 616 | 						chalk.yellow(
 617 | 							'Usage example: task-master update-subtask --id=5.2 --prompt="Add more details about the API endpoint"'
 618 | 						)
 619 | 					);
 620 | 					process.exit(1);
 621 | 				}
 622 | 
 623 | 				// Validate subtask ID format (should contain a dot)
 624 | 				const subtaskId = options.id;
 625 | 				if (!subtaskId.includes('.')) {
 626 | 					console.error(
 627 | 						chalk.red(
 628 | 							`Error: Invalid subtask ID format: ${subtaskId}. Subtask ID must be in format "parentId.subtaskId"`
 629 | 						)
 630 | 					);
 631 | 					console.log(
 632 | 						chalk.yellow(
 633 | 							'Usage example: task-master update-subtask --id=5.2 --prompt="Add more details about the API endpoint"'
 634 | 						)
 635 | 					);
 636 | 					process.exit(1);
 637 | 				}
 638 | 
 639 | 				if (!options.prompt) {
 640 | 					console.error(
 641 | 						chalk.red(
 642 | 							'Error: --prompt parameter is required. Please provide information to add to the subtask.'
 643 | 						)
 644 | 					);
 645 | 					console.log(
 646 | 						chalk.yellow(
 647 | 							'Usage example: task-master update-subtask --id=5.2 --prompt="Add more details about the API endpoint"'
 648 | 						)
 649 | 					);
 650 | 					process.exit(1);
 651 | 				}
 652 | 
 653 | 				const prompt = options.prompt;
 654 | 				const useResearch = options.research || false;
 655 | 
 656 | 				// Validate tasks file exists
 657 | 				if (!fs.existsSync(tasksPath)) {
 658 | 					console.error(
 659 | 						chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
 660 | 					);
 661 | 					if (tasksPath === TASKMASTER_TASKS_FILE) {
 662 | 						console.log(
 663 | 							chalk.yellow(
 664 | 								'Hint: Run task-master init or task-master parse-prd to create tasks.json first'
 665 | 							)
 666 | 						);
 667 | 					} else {
 668 | 						console.log(
 669 | 							chalk.yellow(
 670 | 								`Hint: Check if the file path is correct: ${tasksPath}`
 671 | 							)
 672 | 						);
 673 | 					}
 674 | 					process.exit(1);
 675 | 				}
 676 | 
 677 | 				console.log(
 678 | 					chalk.blue(`Updating subtask ${subtaskId} with prompt: "${prompt}"`)
 679 | 				);
 680 | 				console.log(chalk.blue(`Tasks file: ${tasksPath}`));
 681 | 
 682 | 				if (useResearch) {
 683 | 					// Verify Perplexity API key exists if using research
 684 | 					if (!isApiKeySet('perplexity')) {
 685 | 						console.log(
 686 | 							chalk.yellow(
 687 | 								'Warning: PERPLEXITY_API_KEY environment variable is missing. Research-backed updates will not be available.'
 688 | 							)
 689 | 						);
 690 | 						console.log(
 691 | 							chalk.yellow('Falling back to Claude AI for subtask update.')
 692 | 						);
 693 | 					} else {
 694 | 						console.log(
 695 | 							chalk.blue(
 696 | 								'Using Perplexity AI for research-backed subtask update'
 697 | 							)
 698 | 						);
 699 | 					}
 700 | 				}
 701 | 
 702 | 				const result = await updateSubtaskById(
 703 | 					taskMaster.getTasksPath(),
 704 | 					subtaskId,
 705 | 					prompt,
 706 | 					useResearch,
 707 | 					{ projectRoot: taskMaster.getProjectRoot(), tag }
 708 | 				);
 709 | 
 710 | 				if (!result) {
 711 | 					console.log(
 712 | 						chalk.yellow(
 713 | 							'\nSubtask update was not completed. Review the messages above for details.'
 714 | 						)
 715 | 					);
 716 | 				}
 717 | 			} catch (error) {
 718 | 				console.error(chalk.red(`Error: ${error.message}`));
 719 | 
 720 | 				// Provide more helpful error messages for common issues
 721 | 				if (
 722 | 					error.message.includes('subtask') &&
 723 | 					error.message.includes('not found')
 724 | 				) {
 725 | 					console.log(chalk.yellow('\nTo fix this issue:'));
 726 | 					console.log(
 727 | 						'  1. Run task-master list --with-subtasks to see all available subtask IDs'
 728 | 					);
 729 | 					console.log(
 730 | 						'  2. Use a valid subtask ID with the --id parameter in format "parentId.subtaskId"'
 731 | 					);
 732 | 				} else if (error.message.includes('API key')) {
 733 | 					console.log(
 734 | 						chalk.yellow(
 735 | 							'\nThis error is related to API keys. Check your environment variables.'
 736 | 						)
 737 | 					);
 738 | 				}
 739 | 
 740 | 				// Use getDebugFlag getter instead of CONFIG.debug
 741 | 				if (getDebugFlag()) {
 742 | 					console.error(error);
 743 | 				}
 744 | 
 745 | 				process.exit(1);
 746 | 			}
 747 | 		});
 748 | 
 749 | 	// scope-up command
 750 | 	programInstance
 751 | 		.command('scope-up')
 752 | 		.description('Increase task complexity with AI assistance')
 753 | 		.option(
 754 | 			'-f, --file <file>',
 755 | 			'Path to the tasks file',
 756 | 			TASKMASTER_TASKS_FILE
 757 | 		)
 758 | 		.option(
 759 | 			'-i, --id <ids>',
 760 | 			'Comma-separated task/subtask IDs to scope up (required)'
 761 | 		)
 762 | 		.option(
 763 | 			'-s, --strength <level>',
 764 | 			'Complexity increase strength: light, regular, heavy',
 765 | 			'regular'
 766 | 		)
 767 | 		.option(
 768 | 			'-p, --prompt <text>',
 769 | 			'Custom instructions for targeted scope adjustments'
 770 | 		)
 771 | 		.option('-r, --research', 'Use research AI for more informed adjustments')
 772 | 		.option('--tag <tag>', 'Specify tag context for task operations')
 773 | 		.action(async (options) => {
 774 | 			try {
 775 | 				// Initialize TaskMaster
 776 | 				const taskMaster = initTaskMaster({
 777 | 					tasksPath: options.file || true,
 778 | 					tag: options.tag
 779 | 				});
 780 | 				const tasksPath = taskMaster.getTasksPath();
 781 | 				const tag = taskMaster.getCurrentTag();
 782 | 
 783 | 				// Show current tag context
 784 | 				displayCurrentTagIndicator(tag);
 785 | 
 786 | 				// Validate required parameters
 787 | 				if (!options.id) {
 788 | 					console.error(chalk.red('Error: --id parameter is required'));
 789 | 					console.log(
 790 | 						chalk.yellow(
 791 | 							'Usage example: task-master scope-up --id=1,2,3 --strength=regular'
 792 | 						)
 793 | 					);
 794 | 					process.exit(1);
 795 | 				}
 796 | 
 797 | 				// Parse and validate task IDs
 798 | 				const taskIds = options.id.split(',').map((id) => {
 799 | 					const parsed = parseInt(id.trim(), 10);
 800 | 					if (Number.isNaN(parsed) || parsed <= 0) {
 801 | 						console.error(chalk.red(`Error: Invalid task ID: ${id.trim()}`));
 802 | 						process.exit(1);
 803 | 					}
 804 | 					return parsed;
 805 | 				});
 806 | 
 807 | 				// Validate strength level
 808 | 				if (!validateStrength(options.strength)) {
 809 | 					console.error(
 810 | 						chalk.red(
 811 | 							`Error: Invalid strength level: ${options.strength}. Must be one of: light, regular, heavy`
 812 | 						)
 813 | 					);
 814 | 					process.exit(1);
 815 | 				}
 816 | 
 817 | 				// Validate tasks file exists
 818 | 				if (!fs.existsSync(tasksPath)) {
 819 | 					console.error(
 820 | 						chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
 821 | 					);
 822 | 					process.exit(1);
 823 | 				}
 824 | 
 825 | 				console.log(
 826 | 					chalk.blue(
 827 | 						`Scoping up ${taskIds.length} task(s): ${taskIds.join(', ')}`
 828 | 					)
 829 | 				);
 830 | 				console.log(chalk.blue(`Strength level: ${options.strength}`));
 831 | 				if (options.prompt) {
 832 | 					console.log(chalk.blue(`Custom instructions: ${options.prompt}`));
 833 | 				}
 834 | 
 835 | 				const context = {
 836 | 					projectRoot: taskMaster.getProjectRoot(),
 837 | 					tag,
 838 | 					commandName: 'scope-up',
 839 | 					outputType: 'cli',
 840 | 					research: options.research || false
 841 | 				};
 842 | 
 843 | 				const result = await scopeUpTask(
 844 | 					tasksPath,
 845 | 					taskIds,
 846 | 					options.strength,
 847 | 					options.prompt || null,
 848 | 					context,
 849 | 					'text'
 850 | 				);
 851 | 
 852 | 				console.log(
 853 | 					chalk.green(
 854 | 						`✅ Successfully scoped up ${result.updatedTasks.length} task(s)`
 855 | 					)
 856 | 				);
 857 | 			} catch (error) {
 858 | 				console.error(chalk.red(`Error: ${error.message}`));
 859 | 
 860 | 				if (error.message.includes('not found')) {
 861 | 					console.log(chalk.yellow('\nTo fix this issue:'));
 862 | 					console.log(
 863 | 						'  1. Run task-master list to see all available task IDs'
 864 | 					);
 865 | 					console.log('  2. Use valid task IDs with the --id parameter');
 866 | 				}
 867 | 
 868 | 				if (getDebugFlag()) {
 869 | 					console.error(error);
 870 | 				}
 871 | 
 872 | 				process.exit(1);
 873 | 			}
 874 | 		});
 875 | 
 876 | 	// scope-down command
 877 | 	programInstance
 878 | 		.command('scope-down')
 879 | 		.description('Decrease task complexity with AI assistance')
 880 | 		.option(
 881 | 			'-f, --file <file>',
 882 | 			'Path to the tasks file',
 883 | 			TASKMASTER_TASKS_FILE
 884 | 		)
 885 | 		.option(
 886 | 			'-i, --id <ids>',
 887 | 			'Comma-separated task/subtask IDs to scope down (required)'
 888 | 		)
 889 | 		.option(
 890 | 			'-s, --strength <level>',
 891 | 			'Complexity decrease strength: light, regular, heavy',
 892 | 			'regular'
 893 | 		)
 894 | 		.option(
 895 | 			'-p, --prompt <text>',
 896 | 			'Custom instructions for targeted scope adjustments'
 897 | 		)
 898 | 		.option('-r, --research', 'Use research AI for more informed adjustments')
 899 | 		.option('--tag <tag>', 'Specify tag context for task operations')
 900 | 		.action(async (options) => {
 901 | 			try {
 902 | 				// Initialize TaskMaster
 903 | 				const taskMaster = initTaskMaster({
 904 | 					tasksPath: options.file || true,
 905 | 					tag: options.tag
 906 | 				});
 907 | 				const tasksPath = taskMaster.getTasksPath();
 908 | 				const tag = taskMaster.getCurrentTag();
 909 | 
 910 | 				// Show current tag context
 911 | 				displayCurrentTagIndicator(tag);
 912 | 
 913 | 				// Validate required parameters
 914 | 				if (!options.id) {
 915 | 					console.error(chalk.red('Error: --id parameter is required'));
 916 | 					console.log(
 917 | 						chalk.yellow(
 918 | 							'Usage example: task-master scope-down --id=1,2,3 --strength=regular'
 919 | 						)
 920 | 					);
 921 | 					process.exit(1);
 922 | 				}
 923 | 
 924 | 				// Parse and validate task IDs
 925 | 				const taskIds = options.id.split(',').map((id) => {
 926 | 					const parsed = parseInt(id.trim(), 10);
 927 | 					if (Number.isNaN(parsed) || parsed <= 0) {
 928 | 						console.error(chalk.red(`Error: Invalid task ID: ${id.trim()}`));
 929 | 						process.exit(1);
 930 | 					}
 931 | 					return parsed;
 932 | 				});
 933 | 
 934 | 				// Validate strength level
 935 | 				if (!validateStrength(options.strength)) {
 936 | 					console.error(
 937 | 						chalk.red(
 938 | 							`Error: Invalid strength level: ${options.strength}. Must be one of: light, regular, heavy`
 939 | 						)
 940 | 					);
 941 | 					process.exit(1);
 942 | 				}
 943 | 
 944 | 				// Validate tasks file exists
 945 | 				if (!fs.existsSync(tasksPath)) {
 946 | 					console.error(
 947 | 						chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
 948 | 					);
 949 | 					process.exit(1);
 950 | 				}
 951 | 
 952 | 				console.log(
 953 | 					chalk.blue(
 954 | 						`Scoping down ${taskIds.length} task(s): ${taskIds.join(', ')}`
 955 | 					)
 956 | 				);
 957 | 				console.log(chalk.blue(`Strength level: ${options.strength}`));
 958 | 				if (options.prompt) {
 959 | 					console.log(chalk.blue(`Custom instructions: ${options.prompt}`));
 960 | 				}
 961 | 
 962 | 				const context = {
 963 | 					projectRoot: taskMaster.getProjectRoot(),
 964 | 					tag,
 965 | 					commandName: 'scope-down',
 966 | 					outputType: 'cli',
 967 | 					research: options.research || false
 968 | 				};
 969 | 
 970 | 				const result = await scopeDownTask(
 971 | 					tasksPath,
 972 | 					taskIds,
 973 | 					options.strength,
 974 | 					options.prompt || null,
 975 | 					context,
 976 | 					'text'
 977 | 				);
 978 | 
 979 | 				console.log(
 980 | 					chalk.green(
 981 | 						`✅ Successfully scoped down ${result.updatedTasks.length} task(s)`
 982 | 					)
 983 | 				);
 984 | 			} catch (error) {
 985 | 				console.error(chalk.red(`Error: ${error.message}`));
 986 | 
 987 | 				if (error.message.includes('not found')) {
 988 | 					console.log(chalk.yellow('\nTo fix this issue:'));
 989 | 					console.log(
 990 | 						'  1. Run task-master list to see all available task IDs'
 991 | 					);
 992 | 					console.log('  2. Use valid task IDs with the --id parameter');
 993 | 				}
 994 | 
 995 | 				if (getDebugFlag()) {
 996 | 					console.error(error);
 997 | 				}
 998 | 
 999 | 				process.exit(1);
1000 | 			}
1001 | 		});
1002 | 
1003 | 	// generate command
1004 | 	programInstance
1005 | 		.command('generate')
1006 | 		.description('Generate task files from tasks.json')
1007 | 		.option(
1008 | 			'-f, --file <file>',
1009 | 			'Path to the tasks file',
1010 | 			TASKMASTER_TASKS_FILE
1011 | 		)
1012 | 		.option(
1013 | 			'-o, --output <dir>',
1014 | 			'Output directory',
1015 | 			path.dirname(TASKMASTER_TASKS_FILE)
1016 | 		)
1017 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1018 | 		.action(async (options) => {
1019 | 			// Initialize TaskMaster
1020 | 			const taskMaster = initTaskMaster({
1021 | 				tasksPath: options.file || true,
1022 | 				tag: options.tag
1023 | 			});
1024 | 
1025 | 			const outputDir = options.output;
1026 | 			const tag = taskMaster.getCurrentTag();
1027 | 
1028 | 			console.log(
1029 | 				chalk.blue(`Generating task files from: ${taskMaster.getTasksPath()}`)
1030 | 			);
1031 | 			console.log(chalk.blue(`Output directory: ${outputDir}`));
1032 | 
1033 | 			await generateTaskFiles(taskMaster.getTasksPath(), outputDir, {
1034 | 				projectRoot: taskMaster.getProjectRoot(),
1035 | 				tag
1036 | 			});
1037 | 		});
1038 | 
1039 | 	// ========================================
1040 | 	// Register All Commands from @tm/cli
1041 | 	// ========================================
1042 | 	// Use the centralized command registry to register all CLI commands
1043 | 	// This replaces individual command registrations and reduces duplication
1044 | 	registerAllCommands(programInstance);
1045 | 
1046 | 	// expand command
1047 | 	programInstance
1048 | 		.command('expand')
1049 | 		.description('Expand a task into subtasks using AI')
1050 | 		.option('-i, --id <id>', 'ID of the task to expand')
1051 | 		.option(
1052 | 			'-a, --all',
1053 | 			'Expand all pending tasks based on complexity analysis'
1054 | 		)
1055 | 		.option(
1056 | 			'-n, --num <number>',
1057 | 			'Number of subtasks to generate (uses complexity analysis by default if available)'
1058 | 		)
1059 | 		.option(
1060 | 			'-r, --research',
1061 | 			'Enable research-backed generation (e.g., using Perplexity)',
1062 | 			false
1063 | 		)
1064 | 		.option('-p, --prompt <text>', 'Additional context for subtask generation')
1065 | 		.option('-f, --force', 'Force expansion even if subtasks exist', false) // Ensure force option exists
1066 | 		.option(
1067 | 			'--file <file>',
1068 | 			'Path to the tasks file (relative to project root)',
1069 | 			TASKMASTER_TASKS_FILE // Allow file override
1070 | 		) // Allow file override
1071 | 		.option(
1072 | 			'-cr, --complexity-report <file>',
1073 | 			'Path to the complexity report file (use this to specify the complexity report, not --file)'
1074 | 			// Removed default value to allow tag-specific auto-detection
1075 | 		)
1076 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1077 | 		.action(async (options) => {
1078 | 			// Initialize TaskMaster
1079 | 			const initOptions = {
1080 | 				tasksPath: options.file || true,
1081 | 				tag: options.tag
1082 | 			};
1083 | 
1084 | 			if (options.complexityReport) {
1085 | 				initOptions.complexityReportPath = options.complexityReport;
1086 | 			}
1087 | 
1088 | 			const taskMaster = initTaskMaster(initOptions);
1089 | 
1090 | 			const tag = taskMaster.getCurrentTag();
1091 | 
1092 | 			// Show current tag context
1093 | 			displayCurrentTagIndicator(tag);
1094 | 
1095 | 			if (options.all) {
1096 | 				// --- Handle expand --all ---
1097 | 				console.log(chalk.blue('Expanding all pending tasks...'));
1098 | 				// Updated call to the refactored expandAllTasks
1099 | 				try {
1100 | 					const result = await expandAllTasks(
1101 | 						taskMaster.getTasksPath(),
1102 | 						options.num, // Pass num
1103 | 						options.research, // Pass research flag
1104 | 						options.prompt, // Pass additional context
1105 | 						options.force, // Pass force flag
1106 | 						{
1107 | 							projectRoot: taskMaster.getProjectRoot(),
1108 | 							tag,
1109 | 							complexityReportPath: taskMaster.getComplexityReportPath()
1110 | 						} // Pass context with projectRoot and tag
1111 | 						// outputFormat defaults to 'text' in expandAllTasks for CLI
1112 | 					);
1113 | 				} catch (error) {
1114 | 					console.error(
1115 | 						chalk.red(`Error expanding all tasks: ${error.message}`)
1116 | 					);
1117 | 					process.exit(1);
1118 | 				}
1119 | 			} else if (options.id) {
1120 | 				// --- Handle expand --id <id> (Should be correct from previous refactor) ---
1121 | 				if (!options.id) {
1122 | 					console.error(
1123 | 						chalk.red('Error: Task ID is required unless using --all.')
1124 | 					);
1125 | 					process.exit(1);
1126 | 				}
1127 | 
1128 | 				console.log(chalk.blue(`Expanding task ${options.id}...`));
1129 | 				try {
1130 | 					// Call the refactored expandTask function
1131 | 					await expandTask(
1132 | 						taskMaster.getTasksPath(),
1133 | 						options.id,
1134 | 						options.num,
1135 | 						options.research,
1136 | 						options.prompt,
1137 | 						{
1138 | 							projectRoot: taskMaster.getProjectRoot(),
1139 | 							tag,
1140 | 							complexityReportPath: taskMaster.getComplexityReportPath()
1141 | 						}, // Pass context with projectRoot and tag
1142 | 						options.force // Pass the force flag down
1143 | 					);
1144 | 					// expandTask logs its own success/failure for single task
1145 | 				} catch (error) {
1146 | 					console.error(
1147 | 						chalk.red(`Error expanding task ${options.id}: ${error.message}`)
1148 | 					);
1149 | 					process.exit(1);
1150 | 				}
1151 | 			} else {
1152 | 				console.error(
1153 | 					chalk.red('Error: You must specify either a task ID (--id) or --all.')
1154 | 				);
1155 | 				programInstance.help(); // Show help
1156 | 			}
1157 | 		});
1158 | 
1159 | 	// analyze-complexity command
1160 | 	programInstance
1161 | 		.command('analyze-complexity')
1162 | 		.description(
1163 | 			`Analyze tasks and generate expansion recommendations${chalk.reset('')}`
1164 | 		)
1165 | 		.option('-o, --output <file>', 'Output file path for the report')
1166 | 		.option(
1167 | 			'-m, --model <model>',
1168 | 			'LLM model to use for analysis (defaults to configured model)'
1169 | 		)
1170 | 		.option(
1171 | 			'-t, --threshold <number>',
1172 | 			'Minimum complexity score to recommend expansion (1-10)',
1173 | 			'5'
1174 | 		)
1175 | 		.option(
1176 | 			'-f, --file <file>',
1177 | 			'Path to the tasks file',
1178 | 			TASKMASTER_TASKS_FILE
1179 | 		)
1180 | 		.option(
1181 | 			'-r, --research',
1182 | 			'Use configured research model for research-backed complexity analysis'
1183 | 		)
1184 | 		.option(
1185 | 			'-i, --id <ids>',
1186 | 			'Comma-separated list of specific task IDs to analyze (e.g., "1,3,5")'
1187 | 		)
1188 | 		.option('--from <id>', 'Starting task ID in a range to analyze')
1189 | 		.option('--to <id>', 'Ending task ID in a range to analyze')
1190 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1191 | 		.action(async (options) => {
1192 | 			// Initialize TaskMaster
1193 | 			const initOptions = {
1194 | 				tasksPath: options.file || true, // Tasks file is required to analyze
1195 | 				tag: options.tag
1196 | 			};
1197 | 			// Only include complexityReportPath if output is explicitly specified
1198 | 			if (options.output) {
1199 | 				initOptions.complexityReportPath = options.output;
1200 | 			}
1201 | 
1202 | 			const taskMaster = initTaskMaster(initOptions);
1203 | 
1204 | 			const modelOverride = options.model;
1205 | 			const thresholdScore = parseFloat(options.threshold);
1206 | 			const useResearch = options.research || false;
1207 | 
1208 | 			// Use the provided tag, or the current active tag, or default to 'master'
1209 | 			const targetTag = taskMaster.getCurrentTag();
1210 | 
1211 | 			// Show current tag context
1212 | 			displayCurrentTagIndicator(targetTag);
1213 | 
1214 | 			// Use user's explicit output path if provided, otherwise use tag-aware default
1215 | 			const outputPath = taskMaster.getComplexityReportPath();
1216 | 
1217 | 			console.log(
1218 | 				chalk.blue(
1219 | 					`Analyzing task complexity from: ${taskMaster.getTasksPath()}`
1220 | 				)
1221 | 			);
1222 | 			console.log(chalk.blue(`Output report will be saved to: ${outputPath}`));
1223 | 
1224 | 			if (options.id) {
1225 | 				console.log(chalk.blue(`Analyzing specific task IDs: ${options.id}`));
1226 | 			} else if (options.from || options.to) {
1227 | 				const fromStr = options.from ? options.from : 'first';
1228 | 				const toStr = options.to ? options.to : 'last';
1229 | 				console.log(
1230 | 					chalk.blue(`Analyzing tasks in range: ${fromStr} to ${toStr}`)
1231 | 				);
1232 | 			}
1233 | 
1234 | 			if (useResearch) {
1235 | 				console.log(
1236 | 					chalk.blue(
1237 | 						'Using Perplexity AI for research-backed complexity analysis'
1238 | 					)
1239 | 				);
1240 | 			}
1241 | 
1242 | 			// Update options with tag-aware output path and context
1243 | 			const updatedOptions = {
1244 | 				...options,
1245 | 				output: outputPath,
1246 | 				tag: targetTag,
1247 | 				projectRoot: taskMaster.getProjectRoot(),
1248 | 				file: taskMaster.getTasksPath()
1249 | 			};
1250 | 
1251 | 			await analyzeTaskComplexity(updatedOptions);
1252 | 		});
1253 | 
1254 | 	// research command
1255 | 	programInstance
1256 | 		.command('research')
1257 | 		.description('Perform AI-powered research queries with project context')
1258 | 		.argument('[prompt]', 'Research prompt to investigate')
1259 | 		.option('--file <file>', 'Path to the tasks file')
1260 | 		.option(
1261 | 			'-i, --id <ids>',
1262 | 			'Comma-separated task/subtask IDs to include as context (e.g., "15,16.2")'
1263 | 		)
1264 | 		.option(
1265 | 			'-f, --files <paths>',
1266 | 			'Comma-separated file paths to include as context'
1267 | 		)
1268 | 		.option(
1269 | 			'-c, --context <text>',
1270 | 			'Additional custom context to include in the research prompt'
1271 | 		)
1272 | 		.option(
1273 | 			'-t, --tree',
1274 | 			'Include project file tree structure in the research context'
1275 | 		)
1276 | 		.option(
1277 | 			'-s, --save <file>',
1278 | 			'Save research results to the specified task/subtask(s)'
1279 | 		)
1280 | 		.option(
1281 | 			'-d, --detail <level>',
1282 | 			'Output detail level: low, medium, high',
1283 | 			'medium'
1284 | 		)
1285 | 		.option(
1286 | 			'--save-to <id>',
1287 | 			'Automatically save research results to specified task/subtask ID (e.g., "15" or "15.2")'
1288 | 		)
1289 | 		.option(
1290 | 			'--save-file',
1291 | 			'Save research results to .taskmaster/docs/research/ directory'
1292 | 		)
1293 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1294 | 		.action(async (prompt, options) => {
1295 | 			// Initialize TaskMaster
1296 | 			const initOptions = {
1297 | 				tasksPath: options.file || true,
1298 | 				tag: options.tag
1299 | 			};
1300 | 
1301 | 			const taskMaster = initTaskMaster(initOptions);
1302 | 
1303 | 			// Parameter validation
1304 | 			if (!prompt || typeof prompt !== 'string' || prompt.trim().length === 0) {
1305 | 				console.error(
1306 | 					chalk.red('Error: Research prompt is required and cannot be empty')
1307 | 				);
1308 | 				showResearchHelp();
1309 | 				process.exit(1);
1310 | 			}
1311 | 
1312 | 			// Validate detail level
1313 | 			const validDetailLevels = ['low', 'medium', 'high'];
1314 | 			if (
1315 | 				options.detail &&
1316 | 				!validDetailLevels.includes(options.detail.toLowerCase())
1317 | 			) {
1318 | 				console.error(
1319 | 					chalk.red(
1320 | 						`Error: Detail level must be one of: ${validDetailLevels.join(', ')}`
1321 | 					)
1322 | 				);
1323 | 				process.exit(1);
1324 | 			}
1325 | 
1326 | 			// Validate and parse task IDs if provided
1327 | 			let taskIds = [];
1328 | 			if (options.id) {
1329 | 				try {
1330 | 					taskIds = options.id.split(',').map((id) => {
1331 | 						const trimmedId = id.trim();
1332 | 						// Support both task IDs (e.g., "15") and subtask IDs (e.g., "15.2")
1333 | 						if (!/^\d+(\.\d+)?$/.test(trimmedId)) {
1334 | 							throw new Error(
1335 | 								`Invalid task ID format: "${trimmedId}". Expected format: "15" or "15.2"`
1336 | 							);
1337 | 						}
1338 | 						return trimmedId;
1339 | 					});
1340 | 				} catch (error) {
1341 | 					console.error(chalk.red(`Error parsing task IDs: ${error.message}`));
1342 | 					process.exit(1);
1343 | 				}
1344 | 			}
1345 | 
1346 | 			// Validate and parse file paths if provided
1347 | 			let filePaths = [];
1348 | 			if (options.files) {
1349 | 				try {
1350 | 					filePaths = options.files.split(',').map((filePath) => {
1351 | 						const trimmedPath = filePath.trim();
1352 | 						if (trimmedPath.length === 0) {
1353 | 							throw new Error('Empty file path provided');
1354 | 						}
1355 | 						return trimmedPath;
1356 | 					});
1357 | 				} catch (error) {
1358 | 					console.error(
1359 | 						chalk.red(`Error parsing file paths: ${error.message}`)
1360 | 					);
1361 | 					process.exit(1);
1362 | 				}
1363 | 			}
1364 | 
1365 | 			// Validate save-to option if provided
1366 | 			if (options.saveTo) {
1367 | 				const saveToId = options.saveTo.trim();
1368 | 				if (saveToId.length === 0) {
1369 | 					console.error(chalk.red('Error: Save-to ID cannot be empty'));
1370 | 					process.exit(1);
1371 | 				}
1372 | 				// Validate ID format: number or number.number
1373 | 				if (!/^\d+(\.\d+)?$/.test(saveToId)) {
1374 | 					console.error(
1375 | 						chalk.red(
1376 | 							'Error: Save-to ID must be in format "15" for task or "15.2" for subtask'
1377 | 						)
1378 | 					);
1379 | 					process.exit(1);
1380 | 				}
1381 | 			}
1382 | 
1383 | 			// Validate save option if provided (legacy file save)
1384 | 			if (options.save) {
1385 | 				const saveTarget = options.save.trim();
1386 | 				if (saveTarget.length === 0) {
1387 | 					console.error(chalk.red('Error: Save target cannot be empty'));
1388 | 					process.exit(1);
1389 | 				}
1390 | 				// Check if it's a valid file path (basic validation)
1391 | 				if (saveTarget.includes('..') || saveTarget.startsWith('/')) {
1392 | 					console.error(
1393 | 						chalk.red(
1394 | 							'Error: Save path must be relative and cannot contain ".."'
1395 | 						)
1396 | 					);
1397 | 					process.exit(1);
1398 | 				}
1399 | 			}
1400 | 
1401 | 			const tag = taskMaster.getCurrentTag();
1402 | 
1403 | 			// Show current tag context
1404 | 			displayCurrentTagIndicator(tag);
1405 | 
1406 | 			// Validate tasks file exists if task IDs are specified
1407 | 			if (taskIds.length > 0) {
1408 | 				try {
1409 | 					const tasksData = readJSON(
1410 | 						taskMaster.getTasksPath(),
1411 | 						taskMaster.getProjectRoot(),
1412 | 						tag
1413 | 					);
1414 | 					if (!tasksData || !tasksData.tasks) {
1415 | 						console.error(
1416 | 							chalk.red(
1417 | 								`Error: No valid tasks found in ${taskMaster.getTasksPath()} for tag '${tag}'`
1418 | 							)
1419 | 						);
1420 | 						process.exit(1);
1421 | 					}
1422 | 				} catch (error) {
1423 | 					console.error(
1424 | 						chalk.red(`Error reading tasks file: ${error.message}`)
1425 | 					);
1426 | 					process.exit(1);
1427 | 				}
1428 | 			}
1429 | 
1430 | 			// Validate file paths exist if specified
1431 | 			if (filePaths.length > 0) {
1432 | 				for (const filePath of filePaths) {
1433 | 					const fullPath = path.isAbsolute(filePath)
1434 | 						? filePath
1435 | 						: path.join(taskMaster.getProjectRoot(), filePath);
1436 | 					if (!fs.existsSync(fullPath)) {
1437 | 						console.error(chalk.red(`Error: File not found: ${filePath}`));
1438 | 						process.exit(1);
1439 | 					}
1440 | 				}
1441 | 			}
1442 | 
1443 | 			// Create validated parameters object
1444 | 			const validatedParams = {
1445 | 				prompt: prompt.trim(),
1446 | 				taskIds: taskIds,
1447 | 				filePaths: filePaths,
1448 | 				customContext: options.context ? options.context.trim() : null,
1449 | 				includeProjectTree: !!options.tree,
1450 | 				saveTarget: options.save ? options.save.trim() : null,
1451 | 				saveToId: options.saveTo ? options.saveTo.trim() : null,
1452 | 				allowFollowUp: true, // Always allow follow-up in CLI
1453 | 				detailLevel: options.detail ? options.detail.toLowerCase() : 'medium',
1454 | 				tasksPath: taskMaster.getTasksPath(),
1455 | 				projectRoot: taskMaster.getProjectRoot()
1456 | 			};
1457 | 
1458 | 			// Display what we're about to do
1459 | 			console.log(chalk.blue(`Researching: "${validatedParams.prompt}"`));
1460 | 
1461 | 			if (validatedParams.taskIds.length > 0) {
1462 | 				console.log(
1463 | 					chalk.gray(`Task context: ${validatedParams.taskIds.join(', ')}`)
1464 | 				);
1465 | 			}
1466 | 
1467 | 			if (validatedParams.filePaths.length > 0) {
1468 | 				console.log(
1469 | 					chalk.gray(`File context: ${validatedParams.filePaths.join(', ')}`)
1470 | 				);
1471 | 			}
1472 | 
1473 | 			if (validatedParams.customContext) {
1474 | 				console.log(
1475 | 					chalk.gray(
1476 | 						`Custom context: ${validatedParams.customContext.substring(0, 50)}${validatedParams.customContext.length > 50 ? '...' : ''}`
1477 | 					)
1478 | 				);
1479 | 			}
1480 | 
1481 | 			if (validatedParams.includeProjectTree) {
1482 | 				console.log(chalk.gray('Including project file tree'));
1483 | 			}
1484 | 
1485 | 			console.log(chalk.gray(`Detail level: ${validatedParams.detailLevel}`));
1486 | 
1487 | 			try {
1488 | 				// Import the research function
1489 | 				const { performResearch } = await import('./task-manager/research.js');
1490 | 
1491 | 				// Prepare research options
1492 | 				const researchOptions = {
1493 | 					taskIds: validatedParams.taskIds,
1494 | 					filePaths: validatedParams.filePaths,
1495 | 					customContext: validatedParams.customContext || '',
1496 | 					includeProjectTree: validatedParams.includeProjectTree,
1497 | 					detailLevel: validatedParams.detailLevel,
1498 | 					projectRoot: validatedParams.projectRoot,
1499 | 					saveToFile: !!options.saveFile,
1500 | 					tag: tag
1501 | 				};
1502 | 
1503 | 				// Execute research
1504 | 				const result = await performResearch(
1505 | 					validatedParams.prompt,
1506 | 					researchOptions,
1507 | 					{
1508 | 						commandName: 'research',
1509 | 						outputType: 'cli',
1510 | 						tag: tag
1511 | 					},
1512 | 					'text',
1513 | 					validatedParams.allowFollowUp // Pass follow-up flag
1514 | 				);
1515 | 
1516 | 				// Auto-save to task/subtask if requested and no interactive save occurred
1517 | 				if (validatedParams.saveToId && !result.interactiveSaveOccurred) {
1518 | 					try {
1519 | 						const isSubtask = validatedParams.saveToId.includes('.');
1520 | 
1521 | 						// Format research content for saving
1522 | 						const researchContent = `## Research Query: ${validatedParams.prompt}
1523 | 
1524 | **Detail Level:** ${result.detailLevel}
1525 | **Context Size:** ${result.contextSize} characters
1526 | **Timestamp:** ${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}
1527 | 
1528 | ### Results
1529 | 
1530 | ${result.result}`;
1531 | 
1532 | 						if (isSubtask) {
1533 | 							// Save to subtask
1534 | 							const { updateSubtaskById } = await import(
1535 | 								'./task-manager/update-subtask-by-id.js'
1536 | 							);
1537 | 
1538 | 							await updateSubtaskById(
1539 | 								validatedParams.tasksPath,
1540 | 								validatedParams.saveToId,
1541 | 								researchContent,
1542 | 								false, // useResearch = false for simple append
1543 | 								{
1544 | 									commandName: 'research-save',
1545 | 									outputType: 'cli',
1546 | 									projectRoot: validatedParams.projectRoot,
1547 | 									tag: tag
1548 | 								},
1549 | 								'text'
1550 | 							);
1551 | 
1552 | 							console.log(
1553 | 								chalk.green(
1554 | 									`✅ Research saved to subtask ${validatedParams.saveToId}`
1555 | 								)
1556 | 							);
1557 | 						} else {
1558 | 							// Save to task
1559 | 							const updateTaskById = (
1560 | 								await import('./task-manager/update-task-by-id.js')
1561 | 							).default;
1562 | 
1563 | 							const taskIdNum = parseInt(validatedParams.saveToId, 10);
1564 | 							await updateTaskById(
1565 | 								validatedParams.tasksPath,
1566 | 								taskIdNum,
1567 | 								researchContent,
1568 | 								false, // useResearch = false for simple append
1569 | 								{
1570 | 									commandName: 'research-save',
1571 | 									outputType: 'cli',
1572 | 									projectRoot: validatedParams.projectRoot,
1573 | 									tag: tag
1574 | 								},
1575 | 								'text',
1576 | 								true // appendMode = true
1577 | 							);
1578 | 
1579 | 							console.log(
1580 | 								chalk.green(
1581 | 									`✅ Research saved to task ${validatedParams.saveToId}`
1582 | 								)
1583 | 							);
1584 | 						}
1585 | 					} catch (saveError) {
1586 | 						console.log(
1587 | 							chalk.red(`❌ Error saving to task/subtask: ${saveError.message}`)
1588 | 						);
1589 | 					}
1590 | 				}
1591 | 
1592 | 				// Save results to file if requested (legacy)
1593 | 				if (validatedParams.saveTarget) {
1594 | 					const saveContent = `# Research Query: ${validatedParams.prompt}
1595 | 
1596 | **Detail Level:** ${result.detailLevel}
1597 | **Context Size:** ${result.contextSize} characters
1598 | **Timestamp:** ${new Date().toISOString()}
1599 | 
1600 | ## Results
1601 | 
1602 | ${result.result}
1603 | `;
1604 | 
1605 | 					fs.writeFileSync(validatedParams.saveTarget, saveContent, 'utf-8');
1606 | 					console.log(
1607 | 						chalk.green(`\n💾 Results saved to: ${validatedParams.saveTarget}`)
1608 | 					);
1609 | 				}
1610 | 			} catch (error) {
1611 | 				console.error(chalk.red(`\n❌ Research failed: ${error.message}`));
1612 | 				process.exit(1);
1613 | 			}
1614 | 		});
1615 | 
1616 | 	// clear-subtasks command
1617 | 	programInstance
1618 | 		.command('clear-subtasks')
1619 | 		.description('Clear subtasks from specified tasks')
1620 | 		.option(
1621 | 			'-f, --file <file>',
1622 | 			'Path to the tasks file',
1623 | 			TASKMASTER_TASKS_FILE
1624 | 		)
1625 | 		.option(
1626 | 			'-i, --id <ids>',
1627 | 			'Task IDs (comma-separated) to clear subtasks from'
1628 | 		)
1629 | 		.option('--all', 'Clear subtasks from all tasks')
1630 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1631 | 		.action(async (options) => {
1632 | 			const taskIds = options.id;
1633 | 			const all = options.all;
1634 | 
1635 | 			// Initialize TaskMaster
1636 | 			const taskMaster = initTaskMaster({
1637 | 				tasksPath: options.file || true,
1638 | 				tag: options.tag
1639 | 			});
1640 | 
1641 | 			const tag = taskMaster.getCurrentTag();
1642 | 
1643 | 			// Show current tag context
1644 | 			displayCurrentTagIndicator(tag);
1645 | 
1646 | 			if (!taskIds && !all) {
1647 | 				console.error(
1648 | 					chalk.red(
1649 | 						'Error: Please specify task IDs with --id=<ids> or use --all to clear all tasks'
1650 | 					)
1651 | 				);
1652 | 				process.exit(1);
1653 | 			}
1654 | 
1655 | 			if (all) {
1656 | 				// If --all is specified, get all task IDs
1657 | 				const data = readJSON(
1658 | 					taskMaster.getTasksPath(),
1659 | 					taskMaster.getProjectRoot(),
1660 | 					tag
1661 | 				);
1662 | 				if (!data || !data.tasks) {
1663 | 					console.error(chalk.red('Error: No valid tasks found'));
1664 | 					process.exit(1);
1665 | 				}
1666 | 				const allIds = data.tasks.map((t) => t.id).join(',');
1667 | 				clearSubtasks(taskMaster.getTasksPath(), allIds, {
1668 | 					projectRoot: taskMaster.getProjectRoot(),
1669 | 					tag
1670 | 				});
1671 | 			} else {
1672 | 				clearSubtasks(taskMaster.getTasksPath(), taskIds, {
1673 | 					projectRoot: taskMaster.getProjectRoot(),
1674 | 					tag
1675 | 				});
1676 | 			}
1677 | 		});
1678 | 
1679 | 	// add-task command
1680 | 	programInstance
1681 | 		.command('add-task')
1682 | 		.description('Add a new task using AI, optionally providing manual details')
1683 | 		.option(
1684 | 			'-f, --file <file>',
1685 | 			'Path to the tasks file',
1686 | 			TASKMASTER_TASKS_FILE
1687 | 		)
1688 | 		.option(
1689 | 			'-p, --prompt <prompt>',
1690 | 			'Description of the task to add (required if not using manual fields)'
1691 | 		)
1692 | 		.option('-t, --title <title>', 'Task title (for manual task creation)')
1693 | 		.option(
1694 | 			'-d, --description <description>',
1695 | 			'Task description (for manual task creation)'
1696 | 		)
1697 | 		.option(
1698 | 			'--details <details>',
1699 | 			'Implementation details (for manual task creation)'
1700 | 		)
1701 | 		.option(
1702 | 			'--dependencies <dependencies>',
1703 | 			'Comma-separated list of task IDs this task depends on'
1704 | 		)
1705 | 		.option(
1706 | 			'--priority <priority>',
1707 | 			'Task priority (high, medium, low)',
1708 | 			'medium'
1709 | 		)
1710 | 		.option(
1711 | 			'-r, --research',
1712 | 			'Whether to use research capabilities for task creation'
1713 | 		)
1714 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1715 | 		.action(async (options) => {
1716 | 			const isManualCreation = options.title && options.description;
1717 | 
1718 | 			// Validate that either prompt or title+description are provided
1719 | 			if (!options.prompt && !isManualCreation) {
1720 | 				console.error(
1721 | 					chalk.red(
1722 | 						'Error: Either --prompt or both --title and --description must be provided'
1723 | 					)
1724 | 				);
1725 | 				process.exit(1);
1726 | 			}
1727 | 
1728 | 			const tasksPath = options.file || TASKMASTER_TASKS_FILE;
1729 | 
1730 | 			if (!fs.existsSync(tasksPath)) {
1731 | 				console.error(
1732 | 					`❌ No tasks.json file found. Please run "task-master init" or create a tasks.json file at ${TASKMASTER_TASKS_FILE}`
1733 | 				);
1734 | 				process.exit(1);
1735 | 			}
1736 | 
1737 | 			// Correctly determine projectRoot
1738 | 			// Initialize TaskMaster
1739 | 			const taskMaster = initTaskMaster({
1740 | 				tasksPath: options.file || true,
1741 | 				tag: options.tag
1742 | 			});
1743 | 
1744 | 			const projectRoot = taskMaster.getProjectRoot();
1745 | 
1746 | 			const tag = taskMaster.getCurrentTag();
1747 | 
1748 | 			// Show current tag context
1749 | 			displayCurrentTagIndicator(tag);
1750 | 
1751 | 			let manualTaskData = null;
1752 | 			if (isManualCreation) {
1753 | 				manualTaskData = {
1754 | 					title: options.title,
1755 | 					description: options.description,
1756 | 					details: options.details || '',
1757 | 					testStrategy: options.testStrategy || ''
1758 | 				};
1759 | 				// Restore specific logging for manual creation
1760 | 				console.log(
1761 | 					chalk.blue(`Creating task manually with title: "${options.title}"`)
1762 | 				);
1763 | 			} else {
1764 | 				// Restore specific logging for AI creation
1765 | 				console.log(
1766 | 					chalk.blue(`Creating task with AI using prompt: "${options.prompt}"`)
1767 | 				);
1768 | 			}
1769 | 
1770 | 			// Log dependencies and priority if provided (restored)
1771 | 			const dependenciesArray = options.dependencies
1772 | 				? options.dependencies.split(',').map((id) => id.trim())
1773 | 				: [];
1774 | 			if (dependenciesArray.length > 0) {
1775 | 				console.log(
1776 | 					chalk.blue(`Dependencies: [${dependenciesArray.join(', ')}]`)
1777 | 				);
1778 | 			}
1779 | 			if (options.priority) {
1780 | 				console.log(chalk.blue(`Priority: ${options.priority}`));
1781 | 			}
1782 | 
1783 | 			const context = {
1784 | 				projectRoot,
1785 | 				tag,
1786 | 				commandName: 'add-task',
1787 | 				outputType: 'cli'
1788 | 			};
1789 | 
1790 | 			try {
1791 | 				const { newTaskId, telemetryData } = await addTask(
1792 | 					taskMaster.getTasksPath(),
1793 | 					options.prompt,
1794 | 					dependenciesArray,
1795 | 					options.priority,
1796 | 					context,
1797 | 					'text',
1798 | 					manualTaskData,
1799 | 					options.research
1800 | 				);
1801 | 
1802 | 				// addTask handles detailed CLI success logging AND telemetry display when outputFormat is 'text'
1803 | 				// No need to call displayAiUsageSummary here anymore.
1804 | 			} catch (error) {
1805 | 				console.error(chalk.red(`Error adding task: ${error.message}`));
1806 | 				if (error.details) {
1807 | 					console.error(chalk.red(error.details));
1808 | 				}
1809 | 				process.exit(1);
1810 | 			}
1811 | 		});
1812 | 
1813 | 	// add-dependency command
1814 | 	programInstance
1815 | 		.command('add-dependency')
1816 | 		.description('Add a dependency to a task')
1817 | 		.option('-i, --id <id>', 'Task ID to add dependency to')
1818 | 		.option('-d, --depends-on <id>', 'Task ID that will become a dependency')
1819 | 		.option(
1820 | 			'-f, --file <file>',
1821 | 			'Path to the tasks file',
1822 | 			TASKMASTER_TASKS_FILE
1823 | 		)
1824 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1825 | 		.action(async (options) => {
1826 | 			const initOptions = {
1827 | 				tasksPath: options.file || true,
1828 | 				tag: options.tag
1829 | 			};
1830 | 
1831 | 			// Initialize TaskMaster
1832 | 			const taskMaster = initTaskMaster(initOptions);
1833 | 
1834 | 			const taskId = options.id;
1835 | 			const dependencyId = options.dependsOn;
1836 | 
1837 | 			// Resolve tag using standard pattern
1838 | 			const tag = taskMaster.getCurrentTag();
1839 | 
1840 | 			// Show current tag context
1841 | 			displayCurrentTagIndicator(tag);
1842 | 
1843 | 			if (!taskId || !dependencyId) {
1844 | 				console.error(
1845 | 					chalk.red('Error: Both --id and --depends-on are required')
1846 | 				);
1847 | 				process.exit(1);
1848 | 			}
1849 | 
1850 | 			// Handle subtask IDs correctly by preserving the string format for IDs containing dots
1851 | 			// Only use parseInt for simple numeric IDs
1852 | 			const formattedTaskId = taskId.includes('.')
1853 | 				? taskId
1854 | 				: parseInt(taskId, 10);
1855 | 			const formattedDependencyId = dependencyId.includes('.')
1856 | 				? dependencyId
1857 | 				: parseInt(dependencyId, 10);
1858 | 
1859 | 			await addDependency(
1860 | 				taskMaster.getTasksPath(),
1861 | 				formattedTaskId,
1862 | 				formattedDependencyId,
1863 | 				{
1864 | 					projectRoot: taskMaster.getProjectRoot(),
1865 | 					tag
1866 | 				}
1867 | 			);
1868 | 		});
1869 | 
1870 | 	// remove-dependency command
1871 | 	programInstance
1872 | 		.command('remove-dependency')
1873 | 		.description('Remove a dependency from a task')
1874 | 		.option('-i, --id <id>', 'Task ID to remove dependency from')
1875 | 		.option('-d, --depends-on <id>', 'Task ID to remove as a dependency')
1876 | 		.option(
1877 | 			'-f, --file <file>',
1878 | 			'Path to the tasks file',
1879 | 			TASKMASTER_TASKS_FILE
1880 | 		)
1881 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1882 | 		.action(async (options) => {
1883 | 			const initOptions = {
1884 | 				tasksPath: options.file || true,
1885 | 				tag: options.tag
1886 | 			};
1887 | 
1888 | 			// Initialize TaskMaster
1889 | 			const taskMaster = initTaskMaster(initOptions);
1890 | 
1891 | 			const taskId = options.id;
1892 | 			const dependencyId = options.dependsOn;
1893 | 
1894 | 			// Resolve tag using standard pattern
1895 | 			const tag = taskMaster.getCurrentTag();
1896 | 
1897 | 			// Show current tag context
1898 | 			displayCurrentTagIndicator(tag);
1899 | 
1900 | 			if (!taskId || !dependencyId) {
1901 | 				console.error(
1902 | 					chalk.red('Error: Both --id and --depends-on are required')
1903 | 				);
1904 | 				process.exit(1);
1905 | 			}
1906 | 
1907 | 			// Handle subtask IDs correctly by preserving the string format for IDs containing dots
1908 | 			// Only use parseInt for simple numeric IDs
1909 | 			const formattedTaskId = taskId.includes('.')
1910 | 				? taskId
1911 | 				: parseInt(taskId, 10);
1912 | 			const formattedDependencyId = dependencyId.includes('.')
1913 | 				? dependencyId
1914 | 				: parseInt(dependencyId, 10);
1915 | 
1916 | 			await removeDependency(
1917 | 				taskMaster.getTasksPath(),
1918 | 				formattedTaskId,
1919 | 				formattedDependencyId,
1920 | 				{
1921 | 					projectRoot: taskMaster.getProjectRoot(),
1922 | 					tag
1923 | 				}
1924 | 			);
1925 | 		});
1926 | 
1927 | 	// validate-dependencies command
1928 | 	programInstance
1929 | 		.command('validate-dependencies')
1930 | 		.description(
1931 | 			`Identify invalid dependencies without fixing them${chalk.reset('')}`
1932 | 		)
1933 | 		.option(
1934 | 			'-f, --file <file>',
1935 | 			'Path to the tasks file',
1936 | 			TASKMASTER_TASKS_FILE
1937 | 		)
1938 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1939 | 		.action(async (options) => {
1940 | 			const initOptions = {
1941 | 				tasksPath: options.file || true,
1942 | 				tag: options.tag
1943 | 			};
1944 | 
1945 | 			// Initialize TaskMaster
1946 | 			const taskMaster = initTaskMaster(initOptions);
1947 | 
1948 | 			// Resolve tag using standard pattern
1949 | 			const tag = taskMaster.getCurrentTag();
1950 | 
1951 | 			// Show current tag context
1952 | 			displayCurrentTagIndicator(tag);
1953 | 
1954 | 			await validateDependenciesCommand(taskMaster.getTasksPath(), {
1955 | 				context: { projectRoot: taskMaster.getProjectRoot(), tag }
1956 | 			});
1957 | 		});
1958 | 
1959 | 	// fix-dependencies command
1960 | 	programInstance
1961 | 		.command('fix-dependencies')
1962 | 		.description(`Fix invalid dependencies automatically${chalk.reset('')}`)
1963 | 		.option(
1964 | 			'-f, --file <file>',
1965 | 			'Path to the tasks file',
1966 | 			TASKMASTER_TASKS_FILE
1967 | 		)
1968 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1969 | 		.action(async (options) => {
1970 | 			const initOptions = {
1971 | 				tasksPath: options.file || true,
1972 | 				tag: options.tag
1973 | 			};
1974 | 
1975 | 			// Initialize TaskMaster
1976 | 			const taskMaster = initTaskMaster(initOptions);
1977 | 
1978 | 			// Resolve tag using standard pattern
1979 | 			const tag = taskMaster.getCurrentTag();
1980 | 
1981 | 			// Show current tag context
1982 | 			displayCurrentTagIndicator(tag);
1983 | 
1984 | 			await fixDependenciesCommand(taskMaster.getTasksPath(), {
1985 | 				context: { projectRoot: taskMaster.getProjectRoot(), tag }
1986 | 			});
1987 | 		});
1988 | 
1989 | 	// complexity-report command
1990 | 	programInstance
1991 | 		.command('complexity-report')
1992 | 		.description(`Display the complexity analysis report${chalk.reset('')}`)
1993 | 		.option(
1994 | 			'-f, --file <file>',
1995 | 			'Path to the report file',
1996 | 			COMPLEXITY_REPORT_FILE
1997 | 		)
1998 | 		.option('--tag <tag>', 'Specify tag context for task operations')
1999 | 		.action(async (options) => {
2000 | 			const initOptions = {
2001 | 				tag: options.tag
2002 | 			};
2003 | 
2004 | 			if (options.file && options.file !== COMPLEXITY_REPORT_FILE) {
2005 | 				initOptions.complexityReportPath = options.file;
2006 | 			}
2007 | 
2008 | 			// Initialize TaskMaster
2009 | 			const taskMaster = initTaskMaster(initOptions);
2010 | 
2011 | 			// Show current tag context
2012 | 			displayCurrentTagIndicator(taskMaster.getCurrentTag());
2013 | 
2014 | 			await displayComplexityReport(taskMaster.getComplexityReportPath());
2015 | 		});
2016 | 
2017 | 	// add-subtask command
2018 | 	programInstance
2019 | 		.command('add-subtask')
2020 | 		.description('Add a subtask to an existing task')
2021 | 		.option(
2022 | 			'-f, --file <file>',
2023 | 			'Path to the tasks file',
2024 | 			TASKMASTER_TASKS_FILE
2025 | 		)
2026 | 		.option('-p, --parent <id>', 'Parent task ID (required)')
2027 | 		.option('-i, --task-id <id>', 'Existing task ID to convert to subtask')
2028 | 		.option(
2029 | 			'-t, --title <title>',
2030 | 			'Title for the new subtask (when creating a new subtask)'
2031 | 		)
2032 | 		.option('-d, --description <text>', 'Description for the new subtask')
2033 | 		.option('--details <text>', 'Implementation details for the new subtask')
2034 | 		.option(
2035 | 			'--dependencies <ids>',
2036 | 			'Comma-separated list of dependency IDs for the new subtask'
2037 | 		)
2038 | 		.option('-s, --status <status>', 'Status for the new subtask', 'pending')
2039 | 		.option('--generate', 'Regenerate task files after adding subtask')
2040 | 		.option('--tag <tag>', 'Specify tag context for task operations')
2041 | 		.action(async (options) => {
2042 | 			// Initialize TaskMaster
2043 | 			const taskMaster = initTaskMaster({
2044 | 				tasksPath: options.file || true,
2045 | 				tag: options.tag
2046 | 			});
2047 | 
2048 | 			const parentId = options.parent;
2049 | 			const existingTaskId = options.taskId;
2050 | 			const generateFiles = options.generate || false;
2051 | 
2052 | 			// Resolve tag using standard pattern
2053 | 			const tag = taskMaster.getCurrentTag();
2054 | 
2055 | 			// Show current tag context
2056 | 			displayCurrentTagIndicator(tag);
2057 | 
2058 | 			if (!parentId) {
2059 | 				console.error(
2060 | 					chalk.red(
2061 | 						'Error: --parent parameter is required. Please provide a parent task ID.'
2062 | 					)
2063 | 				);
2064 | 				showAddSubtaskHelp();
2065 | 				process.exit(1);
2066 | 			}
2067 | 
2068 | 			// Parse dependencies if provided
2069 | 			let dependencies = [];
2070 | 			if (options.dependencies) {
2071 | 				dependencies = options.dependencies.split(',').map((id) => {
2072 | 					// Handle both regular IDs and dot notation
2073 | 					return id.includes('.') ? id.trim() : parseInt(id.trim(), 10);
2074 | 				});
2075 | 			}
2076 | 
2077 | 			try {
2078 | 				if (existingTaskId) {
2079 | 					// Convert existing task to subtask
2080 | 					console.log(
2081 | 						chalk.blue(
2082 | 							`Converting task ${existingTaskId} to a subtask of ${parentId}...`
2083 | 						)
2084 | 					);
2085 | 					await addSubtask(
2086 | 						taskMaster.getTasksPath(),
2087 | 						parentId,
2088 | 						existingTaskId,
2089 | 						null,
2090 | 						generateFiles,
2091 | 						{ projectRoot: taskMaster.getProjectRoot(), tag }
2092 | 					);
2093 | 					console.log(
2094 | 						chalk.green(
2095 | 							`✓ Task ${existingTaskId} successfully converted to a subtask of task ${parentId}`
2096 | 						)
2097 | 					);
2098 | 				} else if (options.title) {
2099 | 					// Create new subtask with provided data
2100 | 					console.log(
2101 | 						chalk.blue(`Creating new subtask for parent task ${parentId}...`)
2102 | 					);
2103 | 
2104 | 					const newSubtaskData = {
2105 | 						title: options.title,
2106 | 						description: options.description || '',
2107 | 						details: options.details || '',
2108 | 						status: options.status || 'pending',
2109 | 						dependencies: dependencies
2110 | 					};
2111 | 
2112 | 					const subtask = await addSubtask(
2113 | 						taskMaster.getTasksPath(),
2114 | 						parentId,
2115 | 						null,
2116 | 						newSubtaskData,
2117 | 						generateFiles,
2118 | 						{ projectRoot: taskMaster.getProjectRoot(), tag }
2119 | 					);
2120 | 					console.log(
2121 | 						chalk.green(
2122 | 							`✓ New subtask ${parentId}.${subtask.id} successfully created`
2123 | 						)
2124 | 					);
2125 | 
2126 | 					// Display success message and suggested next steps
2127 | 					console.log(
2128 | 						boxen(
2129 | 							chalk.white.bold(
2130 | 								`Subtask ${parentId}.${subtask.id} Added Successfully`
2131 | 							) +
2132 | 								'\n\n' +
2133 | 								chalk.white(`Title: ${subtask.title}`) +
2134 | 								'\n' +
2135 | 								chalk.white(`Status: ${getStatusWithColor(subtask.status)}`) +
2136 | 								'\n' +
2137 | 								(dependencies.length > 0
2138 | 									? chalk.white(`Dependencies: ${dependencies.join(', ')}`) +
2139 | 										'\n'
2140 | 									: '') +
2141 | 								'\n' +
2142 | 								chalk.white.bold('Next Steps:') +
2143 | 								'\n' +
2144 | 								chalk.cyan(
2145 | 									`1. Run ${chalk.yellow(`task-master show ${parentId}`)} to see the parent task with all subtasks`
2146 | 								) +
2147 | 								'\n' +
2148 | 								chalk.cyan(
2149 | 									`2. Run ${chalk.yellow(`task-master set-status --id=${parentId}.${subtask.id} --status=in-progress`)} to start working on it`
2150 | 								),
2151 | 							{
2152 | 								padding: 1,
2153 | 								borderColor: 'green',
2154 | 								borderStyle: 'round',
2155 | 								margin: { top: 1 }
2156 | 							}
2157 | 						)
2158 | 					);
2159 | 				} else {
2160 | 					console.error(
2161 | 						chalk.red('Error: Either --task-id or --title must be provided.')
2162 | 					);
2163 | 					console.log(
2164 | 						boxen(
2165 | 							chalk.white.bold('Usage Examples:') +
2166 | 								'\n\n' +
2167 | 								chalk.white('Convert existing task to subtask:') +
2168 | 								'\n' +
2169 | 								chalk.yellow(
2170 | 									`  task-master add-subtask --parent=5 --task-id=8`
2171 | 								) +
2172 | 								'\n\n' +
2173 | 								chalk.white('Create new subtask:') +
2174 | 								'\n' +
2175 | 								chalk.yellow(
2176 | 									`  task-master add-subtask --parent=5 --title="Implement login UI" --description="Create the login form"`
2177 | 								) +
2178 | 								'\n\n',
2179 | 							{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
2180 | 						)
2181 | 					);
2182 | 					process.exit(1);
2183 | 				}
2184 | 			} catch (error) {
2185 | 				console.error(chalk.red(`Error: ${error.message}`));
2186 | 				showAddSubtaskHelp();
2187 | 				process.exit(1);
2188 | 			}
2189 | 		})
2190 | 		.on('error', function (err) {
2191 | 			console.error(chalk.red(`Error: ${err.message}`));
2192 | 			showAddSubtaskHelp();
2193 | 			process.exit(1);
2194 | 		});
2195 | 
2196 | 	// Helper function to show add-subtask command help
2197 | 	function showAddSubtaskHelp() {
2198 | 		console.log(
2199 | 			boxen(
2200 | 				`${chalk.white.bold('Add Subtask Command Help')}\n\n${chalk.cyan('Usage:')}\n  task-master add-subtask --parent=<id> [options]\n\n${chalk.cyan('Options:')}\n  -p, --parent <id>         Parent task ID (required)\n  -i, --task-id <id>        Existing task ID to convert to subtask\n  -t, --title <title>       Title for the new subtask\n  -d, --description <text>  Description for the new subtask\n  --details <text>          Implementation details for the new subtask\n  --dependencies <ids>      Comma-separated list of dependency IDs\n  -s, --status <status>     Status for the new subtask (default: "pending")\n  -f, --file <file>         Path to the tasks file (default: "${TASKMASTER_TASKS_FILE}")\n  --generate                Regenerate task files after adding subtask\n\n${chalk.cyan('Examples:')}\n  task-master add-subtask --parent=5 --task-id=8\n  task-master add-subtask -p 5 -t "Implement login UI" -d "Create the login form" --generate`,
2201 | 				{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
2202 | 			)
2203 | 		);
2204 | 	}
2205 | 
2206 | 	// remove-subtask command
2207 | 	programInstance
2208 | 		.command('remove-subtask')
2209 | 		.description('Remove a subtask from its parent task')
2210 | 		.option(
2211 | 			'-f, --file <file>',
2212 | 			'Path to the tasks file',
2213 | 			TASKMASTER_TASKS_FILE
2214 | 		)
2215 | 		.option(
2216 | 			'-i, --id <id>',
2217 | 			'Subtask ID(s) to remove in format "parentId.subtaskId" (can be comma-separated for multiple subtasks)'
2218 | 		)
2219 | 		.option(
2220 | 			'-c, --convert',
2221 | 			'Convert the subtask to a standalone task instead of deleting it'
2222 | 		)
2223 | 		.option('--generate', 'Regenerate task files after removing subtask')
2224 | 		.option('--tag <tag>', 'Specify tag context for task operations')
2225 | 		.action(async (options) => {
2226 | 			// Initialize TaskMaster
2227 | 			const taskMaster = initTaskMaster({
2228 | 				tasksPath: options.file || true,
2229 | 				tag: options.tag
2230 | 			});
2231 | 
2232 | 			const subtaskIds = options.id;
2233 | 			const convertToTask = options.convert || false;
2234 | 			const generateFiles = options.generate || false;
2235 | 			const tag = taskMaster.getCurrentTag();
2236 | 
2237 | 			if (!subtaskIds) {
2238 | 				console.error(
2239 | 					chalk.red(
2240 | 						'Error: --id parameter is required. Please provide subtask ID(s) in format "parentId.subtaskId".'
2241 | 					)
2242 | 				);
2243 | 				showRemoveSubtaskHelp();
2244 | 				process.exit(1);
2245 | 			}
2246 | 
2247 | 			try {
2248 | 				// Split by comma to support multiple subtask IDs
2249 | 				const subtaskIdArray = subtaskIds.split(',').map((id) => id.trim());
2250 | 
2251 | 				for (const subtaskId of subtaskIdArray) {
2252 | 					// Validate subtask ID format
2253 | 					if (!subtaskId.includes('.')) {
2254 | 						console.error(
2255 | 							chalk.red(
2256 | 								`Error: Subtask ID "${subtaskId}" must be in format "parentId.subtaskId"`
2257 | 							)
2258 | 						);
2259 | 						showRemoveSubtaskHelp();
2260 | 						process.exit(1);
2261 | 					}
2262 | 
2263 | 					console.log(chalk.blue(`Removing subtask ${subtaskId}...`));
2264 | 					if (convertToTask) {
2265 | 						console.log(
2266 | 							chalk.blue('The subtask will be converted to a standalone task')
2267 | 						);
2268 | 					}
2269 | 
2270 | 					const result = await removeSubtask(
2271 | 						taskMaster.getTasksPath(),
2272 | 						subtaskId,
2273 | 						convertToTask,
2274 | 						generateFiles,
2275 | 						{ projectRoot: taskMaster.getProjectRoot(), tag }
2276 | 					);
2277 | 
2278 | 					if (convertToTask && result) {
2279 | 						// Display success message and next steps for converted task
2280 | 						console.log(
2281 | 							boxen(
2282 | 								chalk.white.bold(
2283 | 									`Subtask ${subtaskId} Converted to Task #${result.id}`
2284 | 								) +
2285 | 									'\n\n' +
2286 | 									chalk.white(`Title: ${result.title}`) +
2287 | 									'\n' +
2288 | 									chalk.white(`Status: ${getStatusWithColor(result.status)}`) +
2289 | 									'\n' +
2290 | 									chalk.white(
2291 | 										`Dependencies: ${result.dependencies.join(', ')}`
2292 | 									) +
2293 | 									'\n\n' +
2294 | 									chalk.white.bold('Next Steps:') +
2295 | 									'\n' +
2296 | 									chalk.cyan(
2297 | 										`1. Run ${chalk.yellow(`task-master show ${result.id}`)} to see details of the new task`
2298 | 									) +
2299 | 									'\n' +
2300 | 									chalk.cyan(
2301 | 										`2. Run ${chalk.yellow(`task-master set-status --id=${result.id} --status=in-progress`)} to start working on it`
2302 | 									),
2303 | 								{
2304 | 									padding: 1,
2305 | 									borderColor: 'green',
2306 | 									borderStyle: 'round',
2307 | 									margin: { top: 1 }
2308 | 								}
2309 | 							)
2310 | 						);
2311 | 					} else {
2312 | 						// Display success message for deleted subtask
2313 | 						console.log(
2314 | 							boxen(
2315 | 								chalk.white.bold(`Subtask ${subtaskId} Removed`) +
2316 | 									'\n\n' +
2317 | 									chalk.white('The subtask has been successfully deleted.'),
2318 | 								{
2319 | 									padding: 1,
2320 | 									borderColor: 'green',
2321 | 									borderStyle: 'round',
2322 | 									margin: { top: 1 }
2323 | 								}
2324 | 							)
2325 | 						);
2326 | 					}
2327 | 				}
2328 | 			} catch (error) {
2329 | 				console.error(chalk.red(`Error: ${error.message}`));
2330 | 				showRemoveSubtaskHelp();
2331 | 				process.exit(1);
2332 | 			}
2333 | 		})
2334 | 		.on('error', function (err) {
2335 | 			console.error(chalk.red(`Error: ${err.message}`));
2336 | 			showRemoveSubtaskHelp();
2337 | 			process.exit(1);
2338 | 		});
2339 | 
2340 | 	// Helper function to show remove-subtask command help
2341 | 	function showRemoveSubtaskHelp() {
2342 | 		console.log(
2343 | 			boxen(
2344 | 				chalk.white.bold('Remove Subtask Command Help') +
2345 | 					'\n\n' +
2346 | 					chalk.cyan('Usage:') +
2347 | 					'\n' +
2348 | 					`  task-master remove-subtask --id=<parentId.subtaskId> [options]\n\n` +
2349 | 					chalk.cyan('Options:') +
2350 | 					'\n' +
2351 | 					'  -i, --id <id>       Subtask ID(s) to remove in format "parentId.subtaskId" (can be comma-separated, required)\n' +
2352 | 					'  -c, --convert       Convert the subtask to a standalone task instead of deleting it\n' +
2353 | 					'  -f, --file <file>   Path to the tasks file (default: "' +
2354 | 					TASKMASTER_TASKS_FILE +
2355 | 					'")\n' +
2356 | 					'  --skip-generate     Skip regenerating task files\n\n' +
2357 | 					chalk.cyan('Examples:') +
2358 | 					'\n' +
2359 | 					'  task-master remove-subtask --id=5.2\n' +
2360 | 					'  task-master remove-subtask --id=5.2,6.3,7.1\n' +
2361 | 					'  task-master remove-subtask --id=5.2 --convert',
2362 | 				{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
2363 | 			)
2364 | 		);
2365 | 	}
2366 | 
2367 | 	// Helper function to show tags command help
2368 | 	function showTagsHelp() {
2369 | 		console.log(
2370 | 			boxen(
2371 | 				chalk.white.bold('Tags Command Help') +
2372 | 					'\n\n' +
2373 | 					chalk.cyan('Usage:') +
2374 | 					'\n' +
2375 | 					`  task-master tags [options]\n\n` +
2376 | 					chalk.cyan('Options:') +
2377 | 					'\n' +
2378 | 					'  -f, --file <file>   Path to the tasks file (default: "' +
2379 | 					TASKMASTER_TASKS_FILE +
2380 | 					'")\n' +
2381 | 					'  --show-metadata     Show detailed metadata for each tag\n\n' +
2382 | 					chalk.cyan('Examples:') +
2383 | 					'\n' +
2384 | 					'  task-master tags\n' +
2385 | 					'  task-master tags --show-metadata\n\n' +
2386 | 					chalk.cyan('Related Commands:') +
2387 | 					'\n' +
2388 | 					'  task-master add-tag <name>      Create a new tag\n' +
2389 | 					'  task-master use-tag <name>      Switch to a tag\n' +
2390 | 					'  task-master delete-tag <name>   Delete a tag',
2391 | 				{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
2392 | 			)
2393 | 		);
2394 | 	}
2395 | 
2396 | 	// Helper function to show add-tag command help
2397 | 	function showAddTagHelp() {
2398 | 		console.log(
2399 | 			boxen(
2400 | 				chalk.white.bold('Add Tag Command Help') +
2401 | 					'\n\n' +
2402 | 					chalk.cyan('Usage:') +
2403 | 					'\n' +
2404 | 					`  task-master add-tag <tagName> [options]\n\n` +
2405 | 					chalk.cyan('Options:') +
2406 | 					'\n' +
2407 | 					'  -f, --file <file>        Path to the tasks file (default: "' +
2408 | 					TASKMASTER_TASKS_FILE +
2409 | 					'")\n' +
2410 | 					'  --copy-from-current      Copy tasks from the current tag to the new tag\n' +
2411 | 					'  --copy-from <tag>        Copy tasks from the specified tag to the new tag\n' +
2412 | 					'  -d, --description <text> Optional description for the tag\n\n' +
2413 | 					chalk.cyan('Examples:') +
2414 | 					'\n' +
2415 | 					'  task-master add-tag feature-xyz\n' +
2416 | 					'  task-master add-tag feature-xyz --copy-from-current\n' +
2417 | 					'  task-master add-tag feature-xyz --copy-from master\n' +
2418 | 					'  task-master add-tag feature-xyz -d "Feature XYZ development"',
2419 | 				{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
2420 | 			)
2421 | 		);
2422 | 	}
2423 | 
2424 | 	// Helper function to show delete-tag command help
2425 | 	function showDeleteTagHelp() {
2426 | 		console.log(
2427 | 			boxen(
2428 | 				chalk.white.bold('Delete Tag Command Help') +
2429 | 					'\n\n' +
2430 | 					chalk.cyan('Usage:') +
2431 | 					'\n' +
2432 | 					`  task-master delete-tag <tagName> [options]\n\n` +
2433 | 					chalk.cyan('Options:') +
2434 | 					'\n' +
2435 | 					'  -f, --file <file>   Path to the tasks file (default: "' +
2436 | 					TASKMASTER_TASKS_FILE +
2437 | 					'")\n' +
2438 | 					'  -y, --yes           Skip confirmation prompts\n\n' +
2439 | 					chalk.cyan('Examples:') +
2440 | 					'\n' +
2441 | 					'  task-master delete-tag feature-xyz\n' +
2442 | 					'  task-master delete-tag feature-xyz --yes\n\n' +
2443 | 					chalk.yellow('Warning:') +
2444 | 					'\n' +
2445 | 					'  This will permanently delete the tag and all its tasks!',
2446 | 				{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
2447 | 			)
2448 | 		);
2449 | 	}
2450 | 
2451 | 	// Helper function to show use-tag command help
2452 | 	function showUseTagHelp() {
2453 | 		console.log(
2454 | 			boxen(
2455 | 				chalk.white.bold('Use Tag Command Help') +
2456 | 					'\n\n' +
2457 | 					chalk.cyan('Usage:') +
2458 | 					'\n' +
2459 | 					`  task-master use-tag <tagName> [options]\n\n` +
2460 | 					chalk.cyan('Options:') +
2461 | 					'\n' +
2462 | 					'  -f, --file <file>   Path to the tasks file (default: "' +
2463 | 					TASKMASTER_TASKS_FILE +
2464 | 					'")\n\n' +
2465 | 					chalk.cyan('Examples:') +
2466 | 					'\n' +
2467 | 					'  task-master use-tag feature-xyz\n' +
2468 | 					'  task-master use-tag master\n\n' +
2469 | 					chalk.cyan('Related Commands:') +
2470 | 					'\n' +
2471 | 					'  task-master tags                 List all available tags\n' +
2472 | 					'  task-master add-tag <name>       Create a new tag',
2473 | 				{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
2474 | 			)
2475 | 		);
2476 | 	}
2477 | 
2478 | 	// Helper function to show research command help
2479 | 	function showResearchHelp() {
2480 | 		console.log(
2481 | 			boxen(
2482 | 				chalk.white.bold('Research Command Help') +
2483 | 					'\n\n' +
2484 | 					chalk.cyan('Usage:') +
2485 | 					'\n' +
2486 | 					`  task-master research "<query>" [options]\n\n` +
2487 | 					chalk.cyan('Required:') +
2488 | 					'\n' +
2489 | 					'  <query>             Research question or prompt (required)\n\n' +
2490 | 					chalk.cyan('Context Options:') +
2491 | 					'\n' +
2492 | 					'  -i, --id <ids>      Comma-separated task/subtask IDs for context (e.g., "15,23.2")\n' +
2493 | 					'  -f, --files <paths> Comma-separated file paths for context\n' +
2494 | 					'  -c, --context <text> Additional custom context text\n' +
2495 | 					'  --tree              Include project file tree structure\n\n' +
2496 | 					chalk.cyan('Output Options:') +
2497 | 					'\n' +
2498 | 					'  -d, --detail <level> Detail level: low, medium, high (default: medium)\n' +
2499 | 					'  --save-to <id>      Auto-save results to task/subtask ID (e.g., "15" or "15.2")\n' +
2500 | 					'  --tag <tag>         Specify tag context for task operations\n\n' +
2501 | 					chalk.cyan('Examples:') +
2502 | 					'\n' +
2503 | 					'  task-master research "How should I implement user authentication?"\n' +
2504 | 					'  task-master research "What\'s the best approach?" --id=15,23.2\n' +
2505 | 					'  task-master research "How does auth work?" --files=src/auth.js --tree\n' +
2506 | 					'  task-master research "Implementation steps?" --save-to=15.2 --detail=high',
2507 | 				{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
2508 | 			)
2509 | 		);
2510 | 	}
2511 | 
2512 | 	// remove-task command
2513 | 	programInstance
2514 | 		.command('remove-task')
2515 | 		.description('Remove one or more tasks or subtasks permanently')
2516 | 		.option(
2517 | 			'-i, --id <ids>',
2518 | 			'ID(s) of the task(s) or subtask(s) to remove (e.g., "5", "5.2", or "5,6.1,7")'
2519 | 		)
2520 | 		.option(
2521 | 			'-f, --file <file>',
2522 | 			'Path to the tasks file',
2523 | 			TASKMASTER_TASKS_FILE
2524 | 		)
2525 | 		.option('-y, --yes', 'Skip confirmation prompt', false)
2526 | 		.option('--tag <tag>', 'Specify tag context for task operations')
2527 | 		.action(async (options) => {
2528 | 			// Initialize TaskMaster
2529 | 			const taskMaster = initTaskMaster({
2530 | 				tasksPath: options.file || true,
2531 | 				tag: options.tag
2532 | 			});
2533 | 
2534 | 			const taskIdsString = options.id;
2535 | 
2536 | 			// Resolve tag using standard pattern
2537 | 			const tag = taskMaster.getCurrentTag();
2538 | 
2539 | 			// Show current tag context
2540 | 			displayCurrentTagIndicator(tag);
2541 | 
2542 | 			if (!taskIdsString) {
2543 | 				console.error(chalk.red('Error: Task ID(s) are required'));
2544 | 				console.error(
2545 | 					chalk.yellow(
2546 | 						'Usage: task-master remove-task --id=<taskId1,taskId2...>'
2547 | 					)
2548 | 				);
2549 | 				process.exit(1);
2550 | 			}
2551 | 
2552 | 			const taskIdsToRemove = taskIdsString
2553 | 				.split(',')
2554 | 				.map((id) => id.trim())
2555 | 				.filter(Boolean);
2556 | 
2557 | 			if (taskIdsToRemove.length === 0) {
2558 | 				console.error(chalk.red('Error: No valid task IDs provided.'));
2559 | 				process.exit(1);
2560 | 			}
2561 | 
2562 | 			try {
2563 | 				// Read data once for checks and confirmation
2564 | 				const data = readJSON(
2565 | 					taskMaster.getTasksPath(),
2566 | 					taskMaster.getProjectRoot(),
2567 | 					tag
2568 | 				);
2569 | 				if (!data || !data.tasks) {
2570 | 					console.error(
2571 | 						chalk.red(`Error: No valid tasks found in ${tasksPath}`)
2572 | 					);
2573 | 					process.exit(1);
2574 | 				}
2575 | 
2576 | 				const existingTasksToRemove = [];
2577 | 				const nonExistentIds = [];
2578 | 				let totalSubtasksToDelete = 0;
2579 | 				const dependentTaskMessages = [];
2580 | 
2581 | 				for (const taskId of taskIdsToRemove) {
2582 | 					if (!taskExists(data.tasks, taskId)) {
2583 | 						nonExistentIds.push(taskId);
2584 | 					} else {
2585 | 						// Correctly extract the task object from the result of findTaskById
2586 | 						const findResult = findTaskById(data.tasks, taskId);
2587 | 						const taskObject = findResult.task; // Get the actual task/subtask object
2588 | 
2589 | 						if (taskObject) {
2590 | 							existingTasksToRemove.push({ id: taskId, task: taskObject }); // Push the actual task object
2591 | 
2592 | 							// If it's a main task, count its subtasks and check dependents
2593 | 							if (!taskObject.isSubtask) {
2594 | 								// Check the actual task object
2595 | 								if (taskObject.subtasks && taskObject.subtasks.length > 0) {
2596 | 									totalSubtasksToDelete += taskObject.subtasks.length;
2597 | 								}
2598 | 								const dependentTasks = data.tasks.filter(
2599 | 									(t) =>
2600 | 										t.dependencies &&
2601 | 										t.dependencies.includes(parseInt(taskId, 10))
2602 | 								);
2603 | 								if (dependentTasks.length > 0) {
2604 | 									dependentTaskMessages.push(
2605 | 										`  - Task ${taskId}: ${dependentTasks.length} dependent tasks (${dependentTasks.map((t) => t.id).join(', ')})`
2606 | 									);
2607 | 								}
2608 | 							}
2609 | 						} else {
2610 | 							// Handle case where findTaskById returned null for the task property (should be rare)
2611 | 							nonExistentIds.push(`${taskId} (error finding details)`);
2612 | 						}
2613 | 					}
2614 | 				}
2615 | 
2616 | 				if (nonExistentIds.length > 0) {
2617 | 					console.warn(
2618 | 						chalk.yellow(
2619 | 							`Warning: The following task IDs were not found: ${nonExistentIds.join(', ')}`
2620 | 						)
2621 | 					);
2622 | 				}
2623 | 
2624 | 				if (existingTasksToRemove.length === 0) {
2625 | 					console.log(chalk.blue('No existing tasks found to remove.'));
2626 | 					process.exit(0);
2627 | 				}
2628 | 
2629 | 				// Skip confirmation if --yes flag is provided
2630 | 				if (!options.yes) {
2631 | 					console.log();
2632 | 					console.log(
2633 | 						chalk.red.bold(
2634 | 							`⚠️ WARNING: This will permanently delete the following ${existingTasksToRemove.length} item(s):`
2635 | 						)
2636 | 					);
2637 | 					console.log();
2638 | 
2639 | 					existingTasksToRemove.forEach(({ id, task }) => {
2640 | 						if (!task) return; // Should not happen due to taskExists check, but safeguard
2641 | 						if (task.isSubtask) {
2642 | 							// Subtask - title is directly on the task object
2643 | 							console.log(
2644 | 								chalk.white(`  Subtask ${id}: ${task.title || '(no title)'}`)
2645 | 							);
2646 | 							// Optionally show parent context if available
2647 | 							if (task.parentTask) {
2648 | 								console.log(
2649 | 									chalk.gray(
2650 | 										`    (Parent: ${task.parentTask.id} - ${task.parentTask.title || '(no title)'})`
2651 | 									)
2652 | 								);
2653 | 							}
2654 | 						} else {
2655 | 							// Main task - title is directly on the task object
2656 | 							console.log(
2657 | 								chalk.white.bold(`  Task ${id}: ${task.title || '(no title)'}`)
2658 | 							);
2659 | 						}
2660 | 					});
2661 | 
2662 | 					if (totalSubtasksToDelete > 0) {
2663 | 						console.log(
2664 | 							chalk.yellow(
2665 | 								`⚠️ This will also delete ${totalSubtasksToDelete} subtasks associated with the selected main tasks!`
2666 | 							)
2667 | 						);
2668 | 					}
2669 | 
2670 | 					if (dependentTaskMessages.length > 0) {
2671 | 						console.log(
2672 | 							chalk.yellow(
2673 | 								'⚠️ Warning: Dependencies on the following tasks will be removed:'
2674 | 							)
2675 | 						);
2676 | 						dependentTaskMessages.forEach((msg) =>
2677 | 							console.log(chalk.yellow(msg))
2678 | 						);
2679 | 					}
2680 | 
2681 | 					console.log();
2682 | 
2683 | 					const { confirm } = await inquirer.prompt([
2684 | 						{
2685 | 							type: 'confirm',
2686 | 							name: 'confirm',
2687 | 							message: chalk.red.bold(
2688 | 								`Are you sure you want to permanently delete these ${existingTasksToRemove.length} item(s)?`
2689 | 							),
2690 | 							default: false
2691 | 						}
2692 | 					]);
2693 | 
2694 | 					if (!confirm) {
2695 | 						console.log(chalk.blue('Task deletion cancelled.'));
2696 | 						process.exit(0);
2697 | 					}
2698 | 				}
2699 | 
2700 | 				const indicator = startLoadingIndicator(
2701 | 					`Removing ${existingTasksToRemove.length} task(s)/subtask(s)...`
2702 | 				);
2703 | 
2704 | 				// Use the string of existing IDs for the core function
2705 | 				const existingIdsString = existingTasksToRemove
2706 | 					.map(({ id }) => id)
2707 | 					.join(',');
2708 | 				const result = await removeTask(
2709 | 					taskMaster.getTasksPath(),
2710 | 					existingIdsString,
2711 | 					{
2712 | 						projectRoot: taskMaster.getProjectRoot(),
2713 | 						tag
2714 | 					}
2715 | 				);
2716 | 
2717 | 				stopLoadingIndicator(indicator);
2718 | 
2719 | 				if (result.success) {
2720 | 					console.log(
2721 | 						boxen(
2722 | 							chalk.green(
2723 | 								`Successfully removed ${result.removedTasks.length} task(s)/subtask(s).`
2724 | 							) +
2725 | 								(result.message ? `\n\nDetails:\n${result.message}` : '') +
2726 | 								(result.error
2727 | 									? `\n\nWarnings:\n${chalk.yellow(result.error)}`
2728 | 									: ''),
2729 | 							{ padding: 1, borderColor: 'green', borderStyle: 'round' }
2730 | 						)
2731 | 					);
2732 | 				} else {
2733 | 					console.error(
2734 | 						boxen(
2735 | 							chalk.red(
2736 | 								`Operation completed with errors. Removed ${result.removedTasks.length} task(s)/subtask(s).`
2737 | 							) +
2738 | 								(result.message ? `\n\nDetails:\n${result.message}` : '') +
2739 | 								(result.error ? `\n\nErrors:\n${chalk.red(result.error)}` : ''),
2740 | 							{
2741 | 								padding: 1,
2742 | 								borderColor: 'red',
2743 | 								borderStyle: 'round'
2744 | 							}
2745 | 						)
2746 | 					);
2747 | 					process.exit(1); // Exit with error code if any part failed
2748 | 				}
2749 | 
2750 | 				// Log any initially non-existent IDs again for clarity
2751 | 				if (nonExistentIds.length > 0) {
2752 | 					console.warn(
2753 | 						chalk.yellow(
2754 | 							`Note: The following IDs were not found initially and were skipped: ${nonExistentIds.join(', ')}`
2755 | 						)
2756 | 					);
2757 | 
2758 | 					// Exit with error if any removals failed
2759 | 					if (result.removedTasks.length === 0) {
2760 | 						process.exit(1);
2761 | 					}
2762 | 				}
2763 | 			} catch (error) {
2764 | 				console.error(
2765 | 					chalk.red(`Error: ${error.message || 'An unknown error occurred'}`)
2766 | 				);
2767 | 				process.exit(1);
2768 | 			}
2769 | 		});
2770 | 
2771 | 	// init command (Directly calls the implementation from init.js)
2772 | 	programInstance
2773 | 		.command('init')
2774 | 		.description('Initialize a new project with Task Master structure')
2775 | 		.option('-y, --yes', 'Skip prompts and use default values')
2776 | 		.option('-n, --name <name>', 'Project name')
2777 | 		.option('-d, --description <description>', 'Project description')
2778 | 		.option('-v, --version <version>', 'Project version', '0.1.0') // Set default here
2779 | 		.option('-a, --author <author>', 'Author name')
2780 | 		.option(
2781 | 			'-r, --rules <rules...>',
2782 | 			'List of rules to add (roo, windsurf, cursor, ...). Accepts comma or space separated values.'
2783 | 		)
2784 | 		.option('--skip-install', 'Skip installing dependencies')
2785 | 		.option('--dry-run', 'Show what would be done without making changes')
2786 | 		.option('--aliases', 'Add shell aliases (tm, taskmaster)')
2787 | 		.option('--no-aliases', 'Skip shell aliases (tm, taskmaster)')
2788 | 		.option('--git', 'Initialize Git repository')
2789 | 		.option('--no-git', 'Skip Git repository initialization')
2790 | 		.option('--git-tasks', 'Store tasks in Git')
2791 | 		.option('--no-git-tasks', 'No Git storage of tasks')
2792 | 		.action(async (cmdOptions) => {
2793 | 			// cmdOptions contains parsed arguments
2794 | 			// Parse rules: accept space or comma separated, default to all available rules
2795 | 			let selectedProfiles = RULE_PROFILES;
2796 | 			let rulesExplicitlyProvided = false;
2797 | 
2798 | 			if (cmdOptions.rules && Array.isArray(cmdOptions.rules)) {
2799 | 				const userSpecifiedProfiles = cmdOptions.rules
2800 | 					.flatMap((r) => r.split(','))
2801 | 					.map((r) => r.trim())
2802 | 					.filter(Boolean);
2803 | 				// Only override defaults if user specified valid rules
2804 | 				if (userSpecifiedProfiles.length > 0) {
2805 | 					selectedProfiles = userSpecifiedProfiles;
2806 | 					rulesExplicitlyProvided = true;
2807 | 				}
2808 | 			}
2809 | 
2810 | 			cmdOptions.rules = selectedProfiles;
2811 | 			cmdOptions.rulesExplicitlyProvided = rulesExplicitlyProvided;
2812 | 
2813 | 			try {
2814 | 				// Directly call the initializeProject function, passing the parsed options
2815 | 				await initializeProject(cmdOptions);
2816 | 				// initializeProject handles its own flow, including potential process.exit()
2817 | 			} catch (error) {
2818 | 				console.error(
2819 | 					chalk.red(`Error during initialization: ${error.message}`)
2820 | 				);
2821 | 				process.exit(1);
2822 | 			}
2823 | 		});
2824 | 
2825 | 	// models command
2826 | 	programInstance
2827 | 		.command('models')
2828 | 		.description('Manage AI model configurations')
2829 | 		.option(
2830 | 			'--set-main <model_id>',
2831 | 			'Set the primary model for task generation/updates'
2832 | 		)
2833 | 		.option(
2834 | 			'--set-research <model_id>',
2835 | 			'Set the model for research-backed operations'
2836 | 		)
2837 | 		.option(
2838 | 			'--set-fallback <model_id>',
2839 | 			'Set the model to use if the primary fails'
2840 | 		)
2841 | 		.option('--setup', 'Run interactive setup to configure models')
2842 | 		.option(
2843 | 			'--openrouter',
2844 | 			'Allow setting a custom OpenRouter model ID (use with --set-*) '
2845 | 		)
2846 | 		.option(
2847 | 			'--ollama',
2848 | 			'Allow setting a custom Ollama model ID (use with --set-*) '
2849 | 		)
2850 | 		.option(
2851 | 			'--bedrock',
2852 | 			'Allow setting a custom Bedrock model ID (use with --set-*) '
2853 | 		)
2854 | 		.option(
2855 | 			'--claude-code',
2856 | 			'Allow setting a Claude Code model ID (use with --set-*)'
2857 | 		)
2858 | 		.option(
2859 | 			'--azure',
2860 | 			'Allow setting a custom Azure OpenAI model ID (use with --set-*) '
2861 | 		)
2862 | 		.option(
2863 | 			'--vertex',
2864 | 			'Allow setting a custom Vertex AI model ID (use with --set-*) '
2865 | 		)
2866 | 		.option(
2867 | 			'--gemini-cli',
2868 | 			'Allow setting a Gemini CLI model ID (use with --set-*)'
2869 | 		)
2870 | 		.option(
2871 | 			'--codex-cli',
2872 | 			'Allow setting a Codex CLI model ID (use with --set-*)'
2873 | 		)
2874 | 		.option(
2875 | 			'--lmstudio',
2876 | 			'Allow setting a custom LM Studio model ID (use with --set-*)'
2877 | 		)
2878 | 		.option(
2879 | 			'--openai-compatible',
2880 | 			'Allow setting a custom OpenAI-compatible model ID (use with --set-*)'
2881 | 		)
2882 | 		.option(
2883 | 			'--baseURL <url>',
2884 | 			'Custom base URL for openai-compatible, lmstudio, or ollama providers (e.g., http://localhost:8000/v1)'
2885 | 		)
2886 | 		.addHelpText(
2887 | 			'after',
2888 | 			`
2889 | Examples:
2890 |   $ task-master models                              # View current configuration
2891 |   $ task-master models --set-main gpt-4o             # Set main model (provider inferred)
2892 |   $ task-master models --set-research sonar-pro       # Set research model
2893 |   $ task-master models --set-fallback claude-3-5-sonnet-20241022 # Set fallback
2894 |   $ task-master models --set-main my-custom-model --ollama  # Set custom Ollama model for main role
2895 |   $ task-master models --set-main anthropic.claude-3-sonnet-20240229-v1:0 --bedrock # Set custom Bedrock model for main role
2896 |   $ task-master models --set-main some/other-model --openrouter # Set custom OpenRouter model for main role
2897 |   $ task-master models --set-main sonnet --claude-code           # Set Claude Code model for main role
2898 |   $ task-master models --set-main gpt-4o --azure # Set custom Azure OpenAI model for main role
2899 |   $ task-master models --set-main claude-3-5-sonnet@20241022 --vertex # Set custom Vertex AI model for main role
2900 |   $ task-master models --set-main gemini-2.5-pro --gemini-cli # Set Gemini CLI model for main role
2901 |   $ task-master models --set-main gpt-5-codex --codex-cli     # Set Codex CLI model for main role
2902 |   $ task-master models --set-main qwen3-vl-4b --lmstudio      # Set LM Studio model for main role (defaults to http://localhost:1234/v1)
2903 |   $ task-master models --set-main qwen3-vl-4b --lmstudio --baseURL http://localhost:8000/v1 # Set LM Studio model with custom base URL
2904 |   $ task-master models --set-main my-model --openai-compatible --baseURL http://localhost:8000/v1 # Set custom OpenAI-compatible model with custom endpoint
2905 |   $ task-master models --setup                            # Run interactive setup`
2906 | 		)
2907 | 		.action(async (options) => {
2908 | 			// Initialize TaskMaster
2909 | 			const taskMaster = initTaskMaster({
2910 | 				tasksPath: options.file || false
2911 | 			});
2912 | 
2913 | 			const projectRoot = taskMaster.getProjectRoot();
2914 | 
2915 | 			// Validate flags: cannot use multiple provider flags simultaneously
2916 | 			const providerFlags = [
2917 | 				options.openrouter,
2918 | 				options.ollama,
2919 | 				options.bedrock,
2920 | 				options.claudeCode,
2921 | 				options.geminiCli,
2922 | 				options.codexCli,
2923 | 				options.lmstudio,
2924 | 				options.openaiCompatible
2925 | 			].filter(Boolean).length;
2926 | 			if (providerFlags > 1) {
2927 | 				console.error(
2928 | 					chalk.red(
2929 | 						'Error: Cannot use multiple provider flags (--openrouter, --ollama, --bedrock, --claude-code, --gemini-cli, --codex-cli, --lmstudio, --openai-compatible) simultaneously.'
2930 | 					)
2931 | 				);
2932 | 				process.exit(1);
2933 | 			}
2934 | 
2935 | 			// Determine the primary action based on flags
2936 | 			const isSetup = options.setup;
2937 | 			const isSetOperation =
2938 | 				options.setMain || options.setResearch || options.setFallback;
2939 | 
2940 | 			// --- Execute Action ---
2941 | 
2942 | 			if (isSetup) {
2943 | 				// Action 1: Run Interactive Setup
2944 | 				console.log(chalk.blue('Starting interactive model setup...')); // Added feedback
2945 | 				try {
2946 | 					await runInteractiveSetup(taskMaster.getProjectRoot());
2947 | 					// runInteractiveSetup logs its own completion/error messages
2948 | 				} catch (setupError) {
2949 | 					console.error(
2950 | 						chalk.red('\\nInteractive setup failed unexpectedly:'),
2951 | 						setupError.message
2952 | 					);
2953 | 				}
2954 | 				// --- IMPORTANT: Exit after setup ---
2955 | 				return; // Stop execution here
2956 | 			}
2957 | 
2958 | 			if (isSetOperation) {
2959 | 				// Action 2: Perform Direct Set Operations
2960 | 				let updateOccurred = false; // Track if any update actually happened
2961 | 
2962 | 				if (options.setMain) {
2963 | 					const result = await setModel('main', options.setMain, {
2964 | 						projectRoot,
2965 | 						providerHint: options.openrouter
2966 | 							? 'openrouter'
2967 | 							: options.ollama
2968 | 								? 'ollama'
2969 | 								: options.bedrock
2970 | 									? 'bedrock'
2971 | 									: options.claudeCode
2972 | 										? 'claude-code'
2973 | 										: options.geminiCli
2974 | 											? 'gemini-cli'
2975 | 											: options.codexCli
2976 | 												? 'codex-cli'
2977 | 												: options.lmstudio
2978 | 													? 'lmstudio'
2979 | 													: options.openaiCompatible
2980 | 														? 'openai-compatible'
2981 | 														: undefined,
2982 | 						baseURL: options.baseURL
2983 | 					});
2984 | 					if (result.success) {
2985 | 						console.log(chalk.green(`✅ ${result.data.message}`));
2986 | 						if (result.data.warning)
2987 | 							console.log(chalk.yellow(result.data.warning));
2988 | 						updateOccurred = true;
2989 | 					} else {
2990 | 						console.error(
2991 | 							chalk.red(`❌ Error setting main model: ${result.error.message}`)
2992 | 						);
2993 | 					}
2994 | 				}
2995 | 				if (options.setResearch) {
2996 | 					const result = await setModel('research', options.setResearch, {
2997 | 						projectRoot,
2998 | 						providerHint: options.openrouter
2999 | 							? 'openrouter'
3000 | 							: options.ollama
3001 | 								? 'ollama'
3002 | 								: options.bedrock
3003 | 									? 'bedrock'
3004 | 									: options.claudeCode
3005 | 										? 'claude-code'
3006 | 										: options.geminiCli
3007 | 											? 'gemini-cli'
3008 | 											: options.codexCli
3009 | 												? 'codex-cli'
3010 | 												: options.lmstudio
3011 | 													? 'lmstudio'
3012 | 													: options.openaiCompatible
3013 | 														? 'openai-compatible'
3014 | 														: undefined,
3015 | 						baseURL: options.baseURL
3016 | 					});
3017 | 					if (result.success) {
3018 | 						console.log(chalk.green(`✅ ${result.data.message}`));
3019 | 						if (result.data.warning)
3020 | 							console.log(chalk.yellow(result.data.warning));
3021 | 						updateOccurred = true;
3022 | 					} else {
3023 | 						console.error(
3024 | 							chalk.red(
3025 | 								`❌ Error setting research model: ${result.error.message}`
3026 | 							)
3027 | 						);
3028 | 					}
3029 | 				}
3030 | 				if (options.setFallback) {
3031 | 					const result = await setModel('fallback', options.setFallback, {
3032 | 						projectRoot,
3033 | 						providerHint: options.openrouter
3034 | 							? 'openrouter'
3035 | 							: options.ollama
3036 | 								? 'ollama'
3037 | 								: options.bedrock
3038 | 									? 'bedrock'
3039 | 									: options.claudeCode
3040 | 										? 'claude-code'
3041 | 										: options.geminiCli
3042 | 											? 'gemini-cli'
3043 | 											: options.codexCli
3044 | 												? 'codex-cli'
3045 | 												: options.lmstudio
3046 | 													? 'lmstudio'
3047 | 													: options.openaiCompatible
3048 | 														? 'openai-compatible'
3049 | 														: undefined,
3050 | 						baseURL: options.baseURL
3051 | 					});
3052 | 					if (result.success) {
3053 | 						console.log(chalk.green(`✅ ${result.data.message}`));
3054 | 						if (result.data.warning)
3055 | 							console.log(chalk.yellow(result.data.warning));
3056 | 						updateOccurred = true;
3057 | 					} else {
3058 | 						console.error(
3059 | 							chalk.red(
3060 | 								`❌ Error setting fallback model: ${result.error.message}`
3061 | 							)
3062 | 						);
3063 | 					}
3064 | 				}
3065 | 
3066 | 				// Optional: Add a final confirmation if any update occurred
3067 | 				if (updateOccurred) {
3068 | 					console.log(chalk.blue('\nModel configuration updated.'));
3069 | 				} else {
3070 | 					console.log(
3071 | 						chalk.yellow(
3072 | 							'\nNo model configuration changes were made (or errors occurred).'
3073 | 						)
3074 | 					);
3075 | 				}
3076 | 
3077 | 				// --- IMPORTANT: Exit after set operations ---
3078 | 				return; // Stop execution here
3079 | 			}
3080 | 
3081 | 			// Action 3: Display Full Status (Only runs if no setup and no set flags)
3082 | 			console.log(chalk.blue('Fetching current model configuration...')); // Added feedback
3083 | 			const configResult = await getModelConfiguration({ projectRoot });
3084 | 			const availableResult = await getAvailableModelsList({ projectRoot });
3085 | 			const apiKeyStatusResult = await getApiKeyStatusReport({ projectRoot });
3086 | 
3087 | 			// 1. Display Active Models
3088 | 			if (!configResult.success) {
3089 | 				console.error(
3090 | 					chalk.red(
3091 | 						`❌ Error fetching configuration: ${configResult.error.message}`
3092 | 					)
3093 | 				);
3094 | 			} else {
3095 | 				displayModelConfiguration(
3096 | 					configResult.data,
3097 | 					availableResult.data?.models || []
3098 | 				);
3099 | 			}
3100 | 
3101 | 			// 2. Display API Key Status
3102 | 			if (apiKeyStatusResult.success) {
3103 | 				displayApiKeyStatus(apiKeyStatusResult.data.report);
3104 | 			} else {
3105 | 				console.error(
3106 | 					chalk.yellow(
3107 | 						`⚠️ Warning: Could not display API Key status: ${apiKeyStatusResult.error.message}`
3108 | 					)
3109 | 				);
3110 | 			}
3111 | 
3112 | 			// 3. Display Other Available Models (Filtered)
3113 | 			if (availableResult.success) {
3114 | 				const activeIds = configResult.success
3115 | 					? [
3116 | 							configResult.data.activeModels.main.modelId,
3117 | 							configResult.data.activeModels.research.modelId,
3118 | 							configResult.data.activeModels.fallback?.modelId
3119 | 						].filter(Boolean)
3120 | 					: [];
3121 | 				const displayableAvailable = availableResult.data.models.filter(
3122 | 					(m) => !activeIds.includes(m.modelId) && !m.modelId.startsWith('[')
3123 | 				);
3124 | 				displayAvailableModels(displayableAvailable);
3125 | 			} else {
3126 | 				console.error(
3127 | 					chalk.yellow(
3128 | 						`⚠️ Warning: Could not display available models: ${availableResult.error.message}`
3129 | 					)
3130 | 				);
3131 | 			}
3132 | 
3133 | 			// 4. Conditional Hint if Config File is Missing
3134 | 			const configExists = isConfigFilePresent(projectRoot);
3135 | 			if (!configExists) {
3136 | 				console.log(
3137 | 					chalk.yellow(
3138 | 						"\\nHint: Run 'task-master models --setup' to create or update your configuration."
3139 | 					)
3140 | 				);
3141 | 			}
3142 | 			// --- IMPORTANT: Exit after displaying status ---
3143 | 			return; // Stop execution here
3144 | 		});
3145 | 
3146 | 	// response-language command
3147 | 	programInstance
3148 | 		.command('lang')
3149 | 		.description('Manage response language settings')
3150 | 		.option('--response <response_language>', 'Set the response language')
3151 | 		.option('--setup', 'Run interactive setup to configure response language')
3152 | 		.action(async (options) => {
3153 | 			const taskMaster = initTaskMaster({});
3154 | 			const projectRoot = taskMaster.getProjectRoot(); // Find project root for context
3155 | 			const { response, setup } = options;
3156 | 			let responseLanguage = response !== undefined ? response : 'English';
3157 | 			if (setup) {
3158 | 				console.log(
3159 | 					chalk.blue('Starting interactive response language setup...')
3160 | 				);
3161 | 				try {
3162 | 					const userResponse = await inquirer.prompt([
3163 | 						{
3164 | 							type: 'input',
3165 | 							name: 'responseLanguage',
3166 | 							message: 'Input your preferred response language',
3167 | 							default: 'English'
3168 | 						}
3169 | 					]);
3170 | 
3171 | 					console.log(
3172 | 						chalk.blue(
3173 | 							'Response language set to:',
3174 | 							userResponse.responseLanguage
3175 | 						)
3176 | 					);
3177 | 					responseLanguage = userResponse.responseLanguage;
3178 | 				} catch (setupError) {
3179 | 					console.error(
3180 | 						chalk.red('\\nInteractive setup failed unexpectedly:'),
3181 | 						setupError.message
3182 | 					);
3183 | 				}
3184 | 			}
3185 | 
3186 | 			const result = setResponseLanguage(responseLanguage, {
3187 | 				projectRoot
3188 | 			});
3189 | 
3190 | 			if (result.success) {
3191 | 				console.log(chalk.green(`✅ ${result.data.message}`));
3192 | 			} else {
3193 | 				console.error(
3194 | 					chalk.red(
3195 | 						`❌ Error setting response language: ${result.error.message}`
3196 | 					)
3197 | 				);
3198 | 				process.exit(1);
3199 | 			}
3200 | 		});
3201 | 
3202 | 	// move-task command
3203 | 	programInstance
3204 | 		.command('move')
3205 | 		.description(
3206 | 			'Move tasks between tags or reorder within tags. Supports cross-tag moves with dependency resolution options.'
3207 | 		)
3208 | 		.option(
3209 | 			'-f, --file <file>',
3210 | 			'Path to the tasks file',
3211 | 			TASKMASTER_TASKS_FILE
3212 | 		)
3213 | 		.option(
3214 | 			'--from <id>',
3215 | 			'ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated to move multiple tasks (e.g., "5,6,7")'
3216 | 		)
3217 | 		.option(
3218 | 			'--to <id>',
3219 | 			'ID of the destination (e.g., "7" or "7.3"). Must match the number of source IDs if comma-separated'
3220 | 		)
3221 | 		.option('--tag <tag>', 'Specify tag context for task operations')
3222 | 		.option('--from-tag <tag>', 'Source tag for cross-tag moves')
3223 | 		.option('--to-tag <tag>', 'Target tag for cross-tag moves')
3224 | 		.option('--with-dependencies', 'Move dependent tasks along with main task')
3225 | 		.option('--ignore-dependencies', 'Break cross-tag dependencies during move')
3226 | 		.action(async (options) => {
3227 | 			// Helper function to show move command help - defined in scope for proper encapsulation
3228 | 			function showMoveHelp() {
3229 | 				console.log(
3230 | 					chalk.white.bold('Move Command Help') +
3231 | 						'\n\n' +
3232 | 						chalk.cyan('Move tasks between tags or reorder within tags.') +
3233 | 						'\n\n' +
3234 | 						chalk.yellow.bold('Within-Tag Moves:') +
3235 | 						'\n' +
3236 | 						chalk.white('  task-master move --from=5 --to=7') +
3237 | 						'\n' +
3238 | 						chalk.white('  task-master move --from=5.2 --to=7.3') +
3239 | 						'\n' +
3240 | 						chalk.white('  task-master move --from=5,6,7 --to=10,11,12') +
3241 | 						'\n\n' +
3242 | 						chalk.yellow.bold('Cross-Tag Moves:') +
3243 | 						'\n' +
3244 | 						chalk.white(
3245 | 							'  task-master move --from=5 --from-tag=backlog --to-tag=in-progress'
3246 | 						) +
3247 | 						'\n' +
3248 | 						chalk.white(
3249 | 							'  task-master move --from=5,6 --from-tag=backlog --to-tag=done'
3250 | 						) +
3251 | 						'\n\n' +
3252 | 						chalk.yellow.bold('Dependency Resolution:') +
3253 | 						'\n' +
3254 | 						chalk.white('  # Move with dependencies') +
3255 | 						'\n' +
3256 | 						chalk.white(
3257 | 							'  task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies'
3258 | 						) +
3259 | 						'\n\n' +
3260 | 						chalk.white('  # Break dependencies') +
3261 | 						'\n' +
3262 | 						chalk.white(
3263 | 							'  task-master move --from=5 --from-tag=backlog --to-tag=in-progress --ignore-dependencies'
3264 | 						) +
3265 | 						'\n\n' +
3266 | 						'\n' +
3267 | 						chalk.yellow.bold('Best Practices:') +
3268 | 						'\n' +
3269 | 						chalk.white(
3270 | 							'  • Use --with-dependencies to move dependent tasks together'
3271 | 						) +
3272 | 						'\n' +
3273 | 						chalk.white(
3274 | 							'  • Use --ignore-dependencies to break cross-tag dependencies'
3275 | 						) +
3276 | 						'\n' +
3277 | 						chalk.white(
3278 | 							'  • Check dependencies first: task-master validate-dependencies'
3279 | 						) +
3280 | 						'\n' +
3281 | 						chalk.white(
3282 | 							'  • Fix dependency issues: task-master fix-dependencies'
3283 | 						) +
3284 | 						'\n\n' +
3285 | 						chalk.yellow.bold('Error Resolution:') +
3286 | 						'\n' +
3287 | 						chalk.white(
3288 | 							'  • Cross-tag dependency conflicts: Use --with-dependencies or --ignore-dependencies'
3289 | 						) +
3290 | 						'\n' +
3291 | 						chalk.white(
3292 | 							'  • Subtask movement: Promote subtask first with remove-subtask --convert'
3293 | 						) +
3294 | 						'\n' +
3295 | 						chalk.white(
3296 | 							'  • Invalid tags: Check available tags with task-master tags'
3297 | 						) +
3298 | 						'\n\n' +
3299 | 						chalk.gray('For more help, run: task-master move --help')
3300 | 				);
3301 | 			}
3302 | 
3303 | 			// Helper function to handle cross-tag move logic
3304 | 			async function handleCrossTagMove(moveContext, options) {
3305 | 				const { sourceId, sourceTag, toTag, taskMaster } = moveContext;
3306 | 
3307 | 				if (!sourceId) {
3308 | 					console.error(
3309 | 						chalk.red('Error: --from parameter is required for cross-tag moves')
3310 | 					);
3311 | 					showMoveHelp();
3312 | 					process.exit(1);
3313 | 				}
3314 | 
3315 | 				const sourceIds = sourceId.split(',').map((id) => id.trim());
3316 | 				const moveOptions = {
3317 | 					withDependencies: options.withDependencies || false,
3318 | 					ignoreDependencies: options.ignoreDependencies || false
3319 | 				};
3320 | 
3321 | 				console.log(
3322 | 					chalk.blue(
3323 | 						`Moving tasks ${sourceIds.join(', ')} from "${sourceTag}" to "${toTag}"...`
3324 | 					)
3325 | 				);
3326 | 
3327 | 				const result = await moveTasksBetweenTags(
3328 | 					taskMaster.getTasksPath(),
3329 | 					sourceIds,
3330 | 					sourceTag,
3331 | 					toTag,
3332 | 					moveOptions,
3333 | 					{ projectRoot: taskMaster.getProjectRoot() }
3334 | 				);
3335 | 
3336 | 				console.log(chalk.green(`✓ ${result.message}`));
3337 | 
3338 | 				// Print any tips returned from the move operation (e.g., after ignoring dependencies)
3339 | 				if (Array.isArray(result.tips) && result.tips.length > 0) {
3340 | 					console.log('\n' + chalk.yellow.bold('Next Steps:'));
3341 | 					result.tips.forEach((t) => console.log(chalk.white(`  • ${t}`)));
3342 | 				}
3343 | 
3344 | 				// Check if source tag still contains tasks before regenerating files
3345 | 				const tasksData = readJSON(
3346 | 					taskMaster.getTasksPath(),
3347 | 					taskMaster.getProjectRoot(),
3348 | 					sourceTag
3349 | 				);
3350 | 				const sourceTagHasTasks =
3351 | 					tasksData &&
3352 | 					Array.isArray(tasksData.tasks) &&
3353 | 					tasksData.tasks.length > 0;
3354 | 
3355 | 				// Generate task files for the affected tags
3356 | 				await generateTaskFiles(
3357 | 					taskMaster.getTasksPath(),
3358 | 					path.dirname(taskMaster.getTasksPath()),
3359 | 					{ tag: toTag, projectRoot: taskMaster.getProjectRoot() }
3360 | 				);
3361 | 
3362 | 				// Only regenerate source tag files if it still contains tasks
3363 | 				if (sourceTagHasTasks) {
3364 | 					await generateTaskFiles(
3365 | 						taskMaster.getTasksPath(),
3366 | 						path.dirname(taskMaster.getTasksPath()),
3367 | 						{ tag: sourceTag, projectRoot: taskMaster.getProjectRoot() }
3368 | 					);
3369 | 				}
3370 | 			}
3371 | 
3372 | 			// Helper function to handle within-tag move logic
3373 | 			async function handleWithinTagMove(moveContext) {
3374 | 				const { sourceId, destinationId, tag, taskMaster } = moveContext;
3375 | 
3376 | 				if (!sourceId || !destinationId) {
3377 | 					console.error(
3378 | 						chalk.red(
3379 | 							'Error: Both --from and --to parameters are required for within-tag moves'
3380 | 						)
3381 | 					);
3382 | 					console.log(
3383 | 						chalk.yellow(
3384 | 							'Usage: task-master move --from=<sourceId> --to=<destinationId>'
3385 | 						)
3386 | 					);
3387 | 					process.exit(1);
3388 | 				}
3389 | 
3390 | 				// Check if we're moving multiple tasks (comma-separated IDs)
3391 | 				const sourceIds = sourceId.split(',').map((id) => id.trim());
3392 | 				const destinationIds = destinationId.split(',').map((id) => id.trim());
3393 | 
3394 | 				// Validate that the number of source and destination IDs match
3395 | 				if (sourceIds.length !== destinationIds.length) {
3396 | 					console.error(
3397 | 						chalk.red(
3398 | 							'Error: The number of source and destination IDs must match'
3399 | 						)
3400 | 					);
3401 | 					console.log(
3402 | 						chalk.yellow('Example: task-master move --from=5,6,7 --to=10,11,12')
3403 | 					);
3404 | 					process.exit(1);
3405 | 				}
3406 | 
3407 | 				// If moving multiple tasks
3408 | 				if (sourceIds.length > 1) {
3409 | 					console.log(
3410 | 						chalk.blue(
3411 | 							`Moving multiple tasks: ${sourceIds.join(', ')} to ${destinationIds.join(', ')}...`
3412 | 						)
3413 | 					);
3414 | 
3415 | 					// Read tasks data once to validate destination IDs
3416 | 					const tasksData = readJSON(
3417 | 						taskMaster.getTasksPath(),
3418 | 						taskMaster.getProjectRoot(),
3419 | 						tag
3420 | 					);
3421 | 					if (!tasksData || !tasksData.tasks) {
3422 | 						console.error(
3423 | 							chalk.red(
3424 | 								`Error: Invalid or missing tasks file at ${taskMaster.getTasksPath()}`
3425 | 							)
3426 | 						);
3427 | 						process.exit(1);
3428 | 					}
3429 | 
3430 | 					// Collect errors during move attempts
3431 | 					const moveErrors = [];
3432 | 					const successfulMoves = [];
3433 | 
3434 | 					// Move tasks one by one
3435 | 					for (let i = 0; i < sourceIds.length; i++) {
3436 | 						const fromId = sourceIds[i];
3437 | 						const toId = destinationIds[i];
3438 | 
3439 | 						// Skip if source and destination are the same
3440 | 						if (fromId === toId) {
3441 | 							console.log(
3442 | 								chalk.yellow(`Skipping ${fromId} -> ${toId} (same ID)`)
3443 | 							);
3444 | 							continue;
3445 | 						}
3446 | 
3447 | 						console.log(
3448 | 							chalk.blue(`Moving task/subtask ${fromId} to ${toId}...`)
3449 | 						);
3450 | 						try {
3451 | 							await moveTask(
3452 | 								taskMaster.getTasksPath(),
3453 | 								fromId,
3454 | 								toId,
3455 | 								i === sourceIds.length - 1,
3456 | 								{ projectRoot: taskMaster.getProjectRoot(), tag }
3457 | 							);
3458 | 							console.log(
3459 | 								chalk.green(
3460 | 									`✓ Successfully moved task/subtask ${fromId} to ${toId}`
3461 | 								)
3462 | 							);
3463 | 							successfulMoves.push({ fromId, toId });
3464 | 						} catch (error) {
3465 | 							const errorInfo = {
3466 | 								fromId,
3467 | 								toId,
3468 | 								error: error.message
3469 | 							};
3470 | 							moveErrors.push(errorInfo);
3471 | 							console.error(
3472 | 								chalk.red(`Error moving ${fromId} to ${toId}: ${error.message}`)
3473 | 							);
3474 | 							// Continue with the next task rather than exiting
3475 | 						}
3476 | 					}
3477 | 
3478 | 					// Display summary after all moves are attempted
3479 | 					if (moveErrors.length > 0) {
3480 | 						console.log(chalk.yellow('\n--- Move Operation Summary ---'));
3481 | 						console.log(
3482 | 							chalk.green(
3483 | 								`✓ Successfully moved: ${successfulMoves.length} tasks`
3484 | 							)
3485 | 						);
3486 | 						console.log(
3487 | 							chalk.red(`✗ Failed to move: ${moveErrors.length} tasks`)
3488 | 						);
3489 | 
3490 | 						if (successfulMoves.length > 0) {
3491 | 							console.log(chalk.cyan('\nSuccessful moves:'));
3492 | 							successfulMoves.forEach(({ fromId, toId }) => {
3493 | 								console.log(chalk.cyan(`  ${fromId} → ${toId}`));
3494 | 							});
3495 | 						}
3496 | 
3497 | 						console.log(chalk.red('\nFailed moves:'));
3498 | 						moveErrors.forEach(({ fromId, toId, error }) => {
3499 | 							console.log(chalk.red(`  ${fromId} → ${toId}: ${error}`));
3500 | 						});
3501 | 
3502 | 						console.log(
3503 | 							chalk.yellow(
3504 | 								'\nNote: Some tasks were moved successfully. Check the errors above for failed moves.'
3505 | 							)
3506 | 						);
3507 | 					} else {
3508 | 						console.log(chalk.green('\n✓ All tasks moved successfully!'));
3509 | 					}
3510 | 				} else {
3511 | 					// Moving a single task (existing logic)
3512 | 					console.log(
3513 | 						chalk.blue(`Moving task/subtask ${sourceId} to ${destinationId}...`)
3514 | 					);
3515 | 
3516 | 					const result = await moveTask(
3517 | 						taskMaster.getTasksPath(),
3518 | 						sourceId,
3519 | 						destinationId,
3520 | 						true,
3521 | 						{ projectRoot: taskMaster.getProjectRoot(), tag }
3522 | 					);
3523 | 					console.log(
3524 | 						chalk.green(
3525 | 							`✓ Successfully moved task/subtask ${sourceId} to ${destinationId}`
3526 | 						)
3527 | 					);
3528 | 				}
3529 | 			}
3530 | 
3531 | 			// Helper function to handle move errors
3532 | 			function handleMoveError(error, moveContext) {
3533 | 				console.error(chalk.red(`Error: ${error.message}`));
3534 | 
3535 | 				// Enhanced error handling with structured error objects
3536 | 				if (error.code === 'CROSS_TAG_DEPENDENCY_CONFLICTS') {
3537 | 					// Use structured error data
3538 | 					const conflicts = error.data.conflicts || [];
3539 | 					const taskIds = error.data.taskIds || [];
3540 | 					displayCrossTagDependencyError(
3541 | 						conflicts,
3542 | 						moveContext.sourceTag,
3543 | 						moveContext.toTag,
3544 | 						taskIds.join(', ')
3545 | 					);
3546 | 				} else if (error.code === 'CANNOT_MOVE_SUBTASK') {
3547 | 					// Use structured error data
3548 | 					const taskId =
3549 | 						error.data.taskId || moveContext.sourceId?.split(',')[0];
3550 | 					displaySubtaskMoveError(
3551 | 						taskId,
3552 | 						moveContext.sourceTag,
3553 | 						moveContext.toTag
3554 | 					);
3555 | 				} else if (
3556 | 					error.code === 'SOURCE_TARGET_TAGS_SAME' ||
3557 | 					error.code === 'SAME_SOURCE_TARGET_TAG'
3558 | 				) {
3559 | 					displayInvalidTagCombinationError(
3560 | 						moveContext.sourceTag,
3561 | 						moveContext.toTag,
3562 | 						'Source and target tags are identical'
3563 | 					);
3564 | 				} else {
3565 | 					// General error - show dependency validation hints
3566 | 					displayDependencyValidationHints('after-error');
3567 | 				}
3568 | 
3569 | 				process.exit(1);
3570 | 			}
3571 | 
3572 | 			// Initialize TaskMaster
3573 | 			const taskMaster = initTaskMaster({
3574 | 				tasksPath: options.file || true,
3575 | 				tag: options.tag
3576 | 			});
3577 | 
3578 | 			const sourceId = options.from;
3579 | 			const destinationId = options.to;
3580 | 			const fromTag = options.fromTag;
3581 | 			const toTag = options.toTag;
3582 | 
3583 | 			const tag = taskMaster.getCurrentTag();
3584 | 
3585 | 			// Get the source tag - fallback to current tag if not provided
3586 | 			const sourceTag = fromTag || taskMaster.getCurrentTag();
3587 | 
3588 | 			// Check if this is a cross-tag move (different tags)
3589 | 			const isCrossTagMove = sourceTag && toTag && sourceTag !== toTag;
3590 | 
3591 | 			// Initialize move context with all relevant data
3592 | 			const moveContext = {
3593 | 				sourceId,
3594 | 				destinationId,
3595 | 				sourceTag,
3596 | 				toTag,
3597 | 				tag,
3598 | 				taskMaster
3599 | 			};
3600 | 
3601 | 			try {
3602 | 				if (isCrossTagMove) {
3603 | 					// Cross-tag move logic
3604 | 					await handleCrossTagMove(moveContext, options);
3605 | 				} else {
3606 | 					// Within-tag move logic
3607 | 					await handleWithinTagMove(moveContext);
3608 | 				}
3609 | 			} catch (error) {
3610 | 				const errMsg = String(error && (error.message || error));
3611 | 				if (errMsg.includes('already exists in target tag')) {
3612 | 					console.error(chalk.red(`Error: ${errMsg}`));
3613 | 					console.log(
3614 | 						'\n' +
3615 | 							chalk.yellow.bold('Conflict: ID already exists in target tag') +
3616 | 							'\n' +
3617 | 							chalk.white(
3618 | 								'  • Choose a different target tag without conflicting IDs'
3619 | 							) +
3620 | 							'\n' +
3621 | 							chalk.white(
3622 | 								'  • Move a different set of IDs (avoid existing ones)'
3623 | 							) +
3624 | 							'\n' +
3625 | 							chalk.white(
3626 | 								'  • If needed, move within-tag to a new ID first, then cross-tag move'
3627 | 							)
3628 | 					);
3629 | 					process.exit(1);
3630 | 				}
3631 | 				handleMoveError(error, moveContext);
3632 | 			}
3633 | 		});
3634 | 
3635 | 	// Add/remove profile rules command
3636 | 	programInstance
3637 | 		.command('rules [action] [profiles...]')
3638 | 		.description(
3639 | 			`Add or remove rules for one or more profiles. Valid actions: ${Object.values(RULES_ACTIONS).join(', ')} (e.g., task-master rules ${RULES_ACTIONS.ADD} windsurf roo)`
3640 | 		)
3641 | 		.option(
3642 | 			'-f, --force',
3643 | 			'Skip confirmation prompt when removing rules (dangerous)'
3644 | 		)
3645 | 		.option(
3646 | 			`--${RULES_SETUP_ACTION}`,
3647 | 			'Run interactive setup to select rule profiles to add'
3648 | 		)
3649 | 		.addHelpText(
3650 | 			'after',
3651 | 			`
3652 | 		Examples:
3653 | 		$ task-master rules ${RULES_ACTIONS.ADD} windsurf roo          # Add Windsurf and Roo rule sets
3654 | 		$ task-master rules ${RULES_ACTIONS.REMOVE} windsurf          # Remove Windsurf rule set
3655 | 		$ task-master rules --${RULES_SETUP_ACTION}                  # Interactive setup to select rule profiles`
3656 | 		)
3657 | 		.action(async (action, profiles, options) => {
3658 | 			const taskMaster = initTaskMaster({});
3659 | 			const projectRoot = taskMaster.getProjectRoot();
3660 | 			if (!projectRoot) {
3661 | 				console.error(chalk.red('Error: Could not find project root.'));
3662 | 				process.exit(1);
3663 | 			}
3664 | 
3665 | 			/**
3666 | 			 * 'task-master rules --setup' action:
3667 | 			 *
3668 | 			 * Launches an interactive prompt to select which rule profiles to add to the current project.
3669 | 			 * This does NOT perform project initialization or ask about shell aliases—only rules selection.
3670 | 			 *
3671 | 			 * Example usage:
3672 | 			 *   $ task-master rules --setup
3673 | 			 *
3674 | 			 * Useful for adding rules after project creation.
3675 | 			 *
3676 | 			 * The list of profiles is always up-to-date with the available profiles.
3677 | 			 */
3678 | 			if (options[RULES_SETUP_ACTION]) {
3679 | 				// Run interactive rules setup ONLY (no project init)
3680 | 				const selectedRuleProfiles = await runInteractiveProfilesSetup();
3681 | 
3682 | 				if (!selectedRuleProfiles || selectedRuleProfiles.length === 0) {
3683 | 					console.log(chalk.yellow('No profiles selected. Exiting.'));
3684 | 					return;
3685 | 				}
3686 | 
3687 | 				console.log(
3688 | 					chalk.blue(
3689 | 						`Installing ${selectedRuleProfiles.length} selected profile(s)...`
3690 | 					)
3691 | 				);
3692 | 
3693 | 				for (let i = 0; i < selectedRuleProfiles.length; i++) {
3694 | 					const profile = selectedRuleProfiles[i];
3695 | 					console.log(
3696 | 						chalk.blue(
3697 | 							`Processing profile ${i + 1}/${selectedRuleProfiles.length}: ${profile}...`
3698 | 						)
3699 | 					);
3700 | 
3701 | 					if (!isValidProfile(profile)) {
3702 | 						console.warn(
3703 | 							`Rule profile for "${profile}" not found. Valid profiles: ${RULE_PROFILES.join(', ')}. Skipping.`
3704 | 						);
3705 | 						continue;
3706 | 					}
3707 | 					const profileConfig = getRulesProfile(profile);
3708 | 
3709 | 					const addResult = convertAllRulesToProfileRules(
3710 | 						projectRoot,
3711 | 						profileConfig
3712 | 					);
3713 | 
3714 | 					console.log(chalk.green(generateProfileSummary(profile, addResult)));
3715 | 				}
3716 | 
3717 | 				console.log(
3718 | 					chalk.green(
3719 | 						`\nCompleted installation of all ${selectedRuleProfiles.length} profile(s).`
3720 | 					)
3721 | 				);
3722 | 				return;
3723 | 			}
3724 | 
3725 | 			// Validate action for non-setup mode
3726 | 			if (!action || !isValidRulesAction(action)) {
3727 | 				console.error(
3728 | 					chalk.red(
3729 | 						`Error: Invalid or missing action '${action || 'none'}'. Valid actions are: ${Object.values(RULES_ACTIONS).join(', ')}`
3730 | 					)
3731 | 				);
3732 | 				console.error(
3733 | 					chalk.yellow(
3734 | 						`For interactive setup, use: task-master rules --${RULES_SETUP_ACTION}`
3735 | 					)
3736 | 				);
3737 | 				process.exit(1);
3738 | 			}
3739 | 
3740 | 			if (!profiles || profiles.length === 0) {
3741 | 				console.error(
3742 | 					'Please specify at least one rule profile (e.g., windsurf, roo).'
3743 | 				);
3744 | 				process.exit(1);
3745 | 			}
3746 | 
3747 | 			// Support both space- and comma-separated profile lists
3748 | 			const expandedProfiles = profiles
3749 | 				.flatMap((b) => b.split(',').map((s) => s.trim()))
3750 | 				.filter(Boolean);
3751 | 
3752 | 			if (action === RULES_ACTIONS.REMOVE) {
3753 | 				let confirmed = true;
3754 | 				if (!options.force) {
3755 | 					// Check if this removal would leave no profiles remaining
3756 | 					if (wouldRemovalLeaveNoProfiles(projectRoot, expandedProfiles)) {
3757 | 						const installedProfiles = getInstalledProfiles(projectRoot);
3758 | 						confirmed = await confirmRemoveAllRemainingProfiles(
3759 | 							expandedProfiles,
3760 | 							installedProfiles
3761 | 						);
3762 | 					} else {
3763 | 						confirmed = await confirmProfilesRemove(expandedProfiles);
3764 | 					}
3765 | 				}
3766 | 				if (!confirmed) {
3767 | 					console.log(chalk.yellow('Aborted: No rules were removed.'));
3768 | 					return;
3769 | 				}
3770 | 			}
3771 | 
3772 | 			const removalResults = [];
3773 | 			const addResults = [];
3774 | 
3775 | 			for (const profile of expandedProfiles) {
3776 | 				if (!isValidProfile(profile)) {
3777 | 					console.warn(
3778 | 						`Rule profile for "${profile}" not found. Valid profiles: ${RULE_PROFILES.join(', ')}. Skipping.`
3779 | 					);
3780 | 					continue;
3781 | 				}
3782 | 				const profileConfig = getRulesProfile(profile);
3783 | 
3784 | 				if (action === RULES_ACTIONS.ADD) {
3785 | 					console.log(chalk.blue(`Adding rules for profile: ${profile}...`));
3786 | 					const addResult = convertAllRulesToProfileRules(
3787 | 						projectRoot,
3788 | 						profileConfig
3789 | 					);
3790 | 					console.log(
3791 | 						chalk.blue(`Completed adding rules for profile: ${profile}`)
3792 | 					);
3793 | 
3794 | 					// Store result with profile name for summary
3795 | 					addResults.push({
3796 | 						profileName: profile,
3797 | 						success: addResult.success,
3798 | 						failed: addResult.failed
3799 | 					});
3800 | 
3801 | 					console.log(chalk.green(generateProfileSummary(profile, addResult)));
3802 | 				} else if (action === RULES_ACTIONS.REMOVE) {
3803 | 					console.log(chalk.blue(`Removing rules for profile: ${profile}...`));
3804 | 					const result = removeProfileRules(projectRoot, profileConfig);
3805 | 					removalResults.push(result);
3806 | 					console.log(
3807 | 						chalk.green(generateProfileRemovalSummary(profile, result))
3808 | 					);
3809 | 				} else {
3810 | 					console.error(
3811 | 						`Unknown action. Use "${RULES_ACTIONS.ADD}" or "${RULES_ACTIONS.REMOVE}".`
3812 | 					);
3813 | 					process.exit(1);
3814 | 				}
3815 | 			}
3816 | 
3817 | 			// Print summary for additions
3818 | 			if (action === RULES_ACTIONS.ADD && addResults.length > 0) {
3819 | 				const { allSuccessfulProfiles, totalSuccess, totalFailed } =
3820 | 					categorizeProfileResults(addResults);
3821 | 
3822 | 				if (allSuccessfulProfiles.length > 0) {
3823 | 					console.log(
3824 | 						chalk.green(
3825 | 							`\nSuccessfully processed profiles: ${allSuccessfulProfiles.join(', ')}`
3826 | 						)
3827 | 					);
3828 | 
3829 | 					// Create a descriptive summary
3830 | 					if (totalSuccess > 0) {
3831 | 						console.log(
3832 | 							chalk.green(
3833 | 								`Total: ${totalSuccess} files processed, ${totalFailed} failed.`
3834 | 							)
3835 | 						);
3836 | 					} else {
3837 | 						console.log(
3838 | 							chalk.green(
3839 | 								`Total: ${allSuccessfulProfiles.length} profile(s) set up successfully.`
3840 | 							)
3841 | 						);
3842 | 					}
3843 | 				}
3844 | 			}
3845 | 
3846 | 			// Print summary for removals
3847 | 			if (action === RULES_ACTIONS.REMOVE && removalResults.length > 0) {
3848 | 				const {
3849 | 					successfulRemovals,
3850 | 					skippedRemovals,
3851 | 					failedRemovals,
3852 | 					removalsWithNotices
3853 | 				} = categorizeRemovalResults(removalResults);
3854 | 
3855 | 				if (successfulRemovals.length > 0) {
3856 | 					console.log(
3857 | 						chalk.green(
3858 | 							`\nSuccessfully removed profiles for: ${successfulRemovals.join(', ')}`
3859 | 						)
3860 | 					);
3861 | 				}
3862 | 				if (skippedRemovals.length > 0) {
3863 | 					console.log(
3864 | 						chalk.yellow(
3865 | 							`Skipped (default or protected): ${skippedRemovals.join(', ')}`
3866 | 						)
3867 | 					);
3868 | 				}
3869 | 				if (failedRemovals.length > 0) {
3870 | 					console.log(chalk.red('\nErrors occurred:'));
3871 | 					failedRemovals.forEach((r) => {
3872 | 						console.log(chalk.red(`  ${r.profileName}: ${r.error}`));
3873 | 					});
3874 | 				}
3875 | 				// Display notices about preserved files/configurations
3876 | 				if (removalsWithNotices.length > 0) {
3877 | 					console.log(chalk.cyan('\nNotices:'));
3878 | 					removalsWithNotices.forEach((r) => {
3879 | 						console.log(chalk.cyan(`  ${r.profileName}: ${r.notice}`));
3880 | 					});
3881 | 				}
3882 | 
3883 | 				// Overall summary
3884 | 				const totalProcessed = removalResults.length;
3885 | 				const totalSuccessful = successfulRemovals.length;
3886 | 				const totalSkipped = skippedRemovals.length;
3887 | 				const totalFailed = failedRemovals.length;
3888 | 
3889 | 				console.log(
3890 | 					chalk.blue(
3891 | 						`\nTotal: ${totalProcessed} profile(s) processed - ${totalSuccessful} removed, ${totalSkipped} skipped, ${totalFailed} failed.`
3892 | 					)
3893 | 				);
3894 | 			}
3895 | 		});
3896 | 
3897 | 	programInstance
3898 | 		.command('migrate')
3899 | 		.description(
3900 | 			'Migrate existing project to use the new .taskmaster directory structure'
3901 | 		)
3902 | 		.option(
3903 | 			'-f, --force',
3904 | 			'Force migration even if .taskmaster directory already exists'
3905 | 		)
3906 | 		.option(
3907 | 			'--backup',
3908 | 			'Create backup of old files before migration (default: false)',
3909 | 			false
3910 | 		)
3911 | 		.option(
3912 | 			'--cleanup',
3913 | 			'Remove old files after successful migration (default: true)',
3914 | 			true
3915 | 		)
3916 | 		.option('-y, --yes', 'Skip confirmation prompts')
3917 | 		.option(
3918 | 			'--dry-run',
3919 | 			'Show what would be migrated without actually moving files'
3920 | 		)
3921 | 		.action(async (options) => {
3922 | 			try {
3923 | 				await migrateProject(options);
3924 | 			} catch (error) {
3925 | 				console.error(chalk.red('Error during migration:'), error.message);
3926 | 				process.exit(1);
3927 | 			}
3928 | 		});
3929 | 
3930 | 	// sync-readme command
3931 | 	programInstance
3932 | 		.command('sync-readme')
3933 | 		.description('Sync the current task list to README.md in the project root')
3934 | 		.option(
3935 | 			'-f, --file <file>',
3936 | 			'Path to the tasks file',
3937 | 			TASKMASTER_TASKS_FILE
3938 | 		)
3939 | 		.option('--with-subtasks', 'Include subtasks in the README output')
3940 | 		.option(
3941 | 			'-s, --status <status>',
3942 | 			'Show only tasks matching this status (e.g., pending, done)'
3943 | 		)
3944 | 		.option('-t, --tag <tag>', 'Tag to use for the task list (default: master)')
3945 | 		.action(async (options) => {
3946 | 			// Initialize TaskMaster
3947 | 			const taskMaster = initTaskMaster({
3948 | 				tasksPath: options.file || true,
3949 | 				tag: options.tag
3950 | 			});
3951 | 
3952 | 			const withSubtasks = options.withSubtasks || false;
3953 | 			const status = options.status || null;
3954 | 
3955 | 			const tag = taskMaster.getCurrentTag();
3956 | 
3957 | 			console.log(
3958 | 				chalk.blue(
3959 | 					`📝 Syncing tasks to README.md${withSubtasks ? ' (with subtasks)' : ''}${status ? ` (status: ${status})` : ''}...`
3960 | 				)
3961 | 			);
3962 | 
3963 | 			const success = await syncTasksToReadme(taskMaster.getProjectRoot(), {
3964 | 				withSubtasks,
3965 | 				status,
3966 | 				tasksPath: taskMaster.getTasksPath(),
3967 | 				tag
3968 | 			});
3969 | 
3970 | 			if (!success) {
3971 | 				console.error(chalk.red('❌ Failed to sync tasks to README.md'));
3972 | 				process.exit(1);
3973 | 			}
3974 | 		});
3975 | 
3976 | 	// ===== TAG MANAGEMENT COMMANDS =====
3977 | 
3978 | 	// add-tag command (DEPRECATED - use `tm tags add` instead)
3979 | 	programInstance
3980 | 		.command('add-tag')
3981 | 		.description(
3982 | 			'[DEPRECATED] Create a new tag context for organizing tasks (use "tm tags add" instead)'
3983 | 		)
3984 | 		.argument(
3985 | 			'[tagName]',
3986 | 			'Name of the new tag to create (optional when using --from-branch)'
3987 | 		)
3988 | 		.option(
3989 | 			'-f, --file <file>',
3990 | 			'Path to the tasks file',
3991 | 			TASKMASTER_TASKS_FILE
3992 | 		)
3993 | 		.option(
3994 | 			'--copy-from-current',
3995 | 			'Copy tasks from the current tag to the new tag'
3996 | 		)
3997 | 		.option(
3998 | 			'--copy-from <tag>',
3999 | 			'Copy tasks from the specified tag to the new tag'
4000 | 		)
4001 | 		.option(
4002 | 			'--from-branch',
4003 | 			'Create tag name from current git branch (ignores tagName argument)'
4004 | 		)
4005 | 		.option('-d, --description <text>', 'Optional description for the tag')
4006 | 		.action(async (tagName, options) => {
4007 | 			// Show deprecation warning
4008 | 			console.warn(
4009 | 				chalk.yellow(
4010 | 					'⚠ Warning: "tm add-tag" is deprecated. Use "tm tags add" instead.'
4011 | 				)
4012 | 			);
4013 | 			console.log(
4014 | 				chalk.gray('  This command will be removed in a future version.\n')
4015 | 			);
4016 | 
4017 | 			try {
4018 | 				// Initialize TaskMaster
4019 | 				const taskMaster = initTaskMaster({
4020 | 					tasksPath: options.file || true
4021 | 				});
4022 | 				const tasksPath = taskMaster.getTasksPath();
4023 | 
4024 | 				// Validate tasks file exists
4025 | 				if (!fs.existsSync(tasksPath)) {
4026 | 					console.error(
4027 | 						chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
4028 | 					);
4029 | 					console.log(
4030 | 						chalk.yellow(
4031 | 							'Hint: Run task-master init or task-master parse-prd to create tasks.json first'
4032 | 						)
4033 | 					);
4034 | 					process.exit(1);
4035 | 				}
4036 | 
4037 | 				// Validate that either tagName is provided or --from-branch is used
4038 | 				if (!tagName && !options.fromBranch) {
4039 | 					console.error(
4040 | 						chalk.red(
4041 | 							'Error: Either tagName argument or --from-branch option is required.'
4042 | 						)
4043 | 					);
4044 | 					console.log(chalk.yellow('Usage examples:'));
4045 | 					console.log(chalk.cyan('  task-master add-tag my-tag'));
4046 | 					console.log(chalk.cyan('  task-master add-tag --from-branch'));
4047 | 					process.exit(1);
4048 | 				}
4049 | 
4050 | 				const context = {
4051 | 					projectRoot: taskMaster.getProjectRoot(),
4052 | 					commandName: 'add-tag',
4053 | 					outputType: 'cli'
4054 | 				};
4055 | 
4056 | 				// Handle --from-branch option
4057 | 				if (options.fromBranch) {
4058 | 					const { createTagFromBranch } = await import(
4059 | 						'./task-manager/tag-management.js'
4060 | 					);
4061 | 					const gitUtils = await import('./utils/git-utils.js');
4062 | 
4063 | 					// Check if we're in a git repository
4064 | 					if (!(await gitUtils.isGitRepository(context.projectRoot))) {
4065 | 						console.error(
4066 | 							chalk.red(
4067 | 								'Error: Not in a git repository. Cannot use --from-branch option.'
4068 | 							)
4069 | 						);
4070 | 						process.exit(1);
4071 | 					}
4072 | 
4073 | 					// Get current git branch
4074 | 					const currentBranch = await gitUtils.getCurrentBranch(
4075 | 						context.projectRoot
4076 | 					);
4077 | 					if (!currentBranch) {
4078 | 						console.error(
4079 | 							chalk.red('Error: Could not determine current git branch.')
4080 | 						);
4081 | 						process.exit(1);
4082 | 					}
4083 | 
4084 | 					// Create tag from branch
4085 | 					const branchOptions = {
4086 | 						copyFromCurrent: options.copyFromCurrent || false,
4087 | 						copyFromTag: options.copyFrom,
4088 | 						description:
4089 | 							options.description ||
4090 | 							`Tag created from git branch "${currentBranch}"`
4091 | 					};
4092 | 
4093 | 					await createTagFromBranch(
4094 | 						taskMaster.getTasksPath(),
4095 | 						currentBranch,
4096 | 						branchOptions,
4097 | 						context,
4098 | 						'text'
4099 | 					);
4100 | 				} else {
4101 | 					// Regular tag creation
4102 | 					const createOptions = {
4103 | 						copyFromCurrent: options.copyFromCurrent || false,
4104 | 						copyFromTag: options.copyFrom,
4105 | 						description: options.description
4106 | 					};
4107 | 
4108 | 					await createTag(
4109 | 						taskMaster.getTasksPath(),
4110 | 						tagName,
4111 | 						createOptions,
4112 | 						context,
4113 | 						'text'
4114 | 					);
4115 | 				}
4116 | 
4117 | 				// Handle auto-switch if requested
4118 | 				if (options.autoSwitch) {
4119 | 					const { useTag } = await import('./task-manager/tag-management.js');
4120 | 					const finalTagName = options.fromBranch
4121 | 						? (await import('./utils/git-utils.js')).sanitizeBranchNameForTag(
4122 | 								await (await import('./utils/git-utils.js')).getCurrentBranch(
4123 | 									projectRoot
4124 | 								)
4125 | 							)
4126 | 						: tagName;
4127 | 					await useTag(
4128 | 						taskMaster.getTasksPath(),
4129 | 						finalTagName,
4130 | 						{},
4131 | 						context,
4132 | 						'text'
4133 | 					);
4134 | 				}
4135 | 			} catch (error) {
4136 | 				console.error(chalk.red(`Error creating tag: ${error.message}`));
4137 | 				showAddTagHelp();
4138 | 				process.exit(1);
4139 | 			}
4140 | 		})
4141 | 		.on('error', function (err) {
4142 | 			console.error(chalk.red(`Error: ${err.message}`));
4143 | 			showAddTagHelp();
4144 | 			process.exit(1);
4145 | 		});
4146 | 
4147 | 	// delete-tag command (DEPRECATED - use `tm tags remove` instead)
4148 | 	programInstance
4149 | 		.command('delete-tag')
4150 | 		.description(
4151 | 			'[DEPRECATED] Delete an existing tag and all its tasks (use "tm tags remove" instead)'
4152 | 		)
4153 | 		.argument('<tagName>', 'Name of the tag to delete')
4154 | 		.option(
4155 | 			'-f, --file <file>',
4156 | 			'Path to the tasks file',
4157 | 			TASKMASTER_TASKS_FILE
4158 | 		)
4159 | 		.option('-y, --yes', 'Skip confirmation prompts')
4160 | 		.action(async (tagName, options) => {
4161 | 			// Show deprecation warning
4162 | 			console.warn(
4163 | 				chalk.yellow(
4164 | 					'⚠ Warning: "tm delete-tag" is deprecated. Use "tm tags remove" instead.'
4165 | 				)
4166 | 			);
4167 | 			console.log(
4168 | 				chalk.gray('  This command will be removed in a future version.\n')
4169 | 			);
4170 | 
4171 | 			try {
4172 | 				// Initialize TaskMaster
4173 | 				const taskMaster = initTaskMaster({
4174 | 					tasksPath: options.file || true
4175 | 				});
4176 | 				const tasksPath = taskMaster.getTasksPath();
4177 | 
4178 | 				// Validate tasks file exists
4179 | 				if (!fs.existsSync(tasksPath)) {
4180 | 					console.error(
4181 | 						chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
4182 | 					);
4183 | 					process.exit(1);
4184 | 				}
4185 | 
4186 | 				const deleteOptions = {
4187 | 					yes: options.yes || false
4188 | 				};
4189 | 
4190 | 				const context = {
4191 | 					projectRoot: taskMaster.getProjectRoot(),
4192 | 					commandName: 'delete-tag',
4193 | 					outputType: 'cli'
4194 | 				};
4195 | 
4196 | 				await deleteTag(
4197 | 					taskMaster.getTasksPath(),
4198 | 					tagName,
4199 | 					deleteOptions,
4200 | 					context,
4201 | 					'text'
4202 | 				);
4203 | 			} catch (error) {
4204 | 				console.error(chalk.red(`Error deleting tag: ${error.message}`));
4205 | 				showDeleteTagHelp();
4206 | 				process.exit(1);
4207 | 			}
4208 | 		})
4209 | 		.on('error', function (err) {
4210 | 			console.error(chalk.red(`Error: ${err.message}`));
4211 | 			showDeleteTagHelp();
4212 | 			process.exit(1);
4213 | 		});
4214 | 
4215 | 	// tags command - REMOVED
4216 | 	// This command has been replaced by the new CommandRegistry-based TagsCommand
4217 | 	// in apps/cli/src/commands/tags.command.ts
4218 | 	// The old implementation is no longer needed
4219 | 
4220 | 	// use-tag command (DEPRECATED - use `tm tags use` instead)
4221 | 	programInstance
4222 | 		.command('use-tag')
4223 | 		.description(
4224 | 			'[DEPRECATED] Switch to a different tag context (use "tm tags use" instead)'
4225 | 		)
4226 | 		.argument('<tagName>', 'Name of the tag to switch to')
4227 | 		.option(
4228 | 			'-f, --file <file>',
4229 | 			'Path to the tasks file',
4230 | 			TASKMASTER_TASKS_FILE
4231 | 		)
4232 | 		.action(async (tagName, options) => {
4233 | 			// Show deprecation warning
4234 | 			console.warn(
4235 | 				chalk.yellow(
4236 | 					'⚠ Warning: "tm use-tag" is deprecated. Use "tm tags use" instead.'
4237 | 				)
4238 | 			);
4239 | 			console.log(
4240 | 				chalk.gray('  This command will be removed in a future version.\n')
4241 | 			);
4242 | 
4243 | 			try {
4244 | 				// Initialize TaskMaster
4245 | 				const taskMaster = initTaskMaster({
4246 | 					tasksPath: options.file || true
4247 | 				});
4248 | 				const tasksPath = taskMaster.getTasksPath();
4249 | 
4250 | 				// Validate tasks file exists
4251 | 				if (!fs.existsSync(tasksPath)) {
4252 | 					console.error(
4253 | 						chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
4254 | 					);
4255 | 					process.exit(1);
4256 | 				}
4257 | 
4258 | 				const context = {
4259 | 					projectRoot: taskMaster.getProjectRoot(),
4260 | 					commandName: 'use-tag',
4261 | 					outputType: 'cli'
4262 | 				};
4263 | 
4264 | 				await useTag(taskMaster.getTasksPath(), tagName, {}, context, 'text');
4265 | 			} catch (error) {
4266 | 				console.error(chalk.red(`Error switching tag: ${error.message}`));
4267 | 				showUseTagHelp();
4268 | 				process.exit(1);
4269 | 			}
4270 | 		})
4271 | 		.on('error', function (err) {
4272 | 			console.error(chalk.red(`Error: ${err.message}`));
4273 | 			showUseTagHelp();
4274 | 			process.exit(1);
4275 | 		});
4276 | 
4277 | 	// rename-tag command (DEPRECATED - use `tm tags rename` instead)
4278 | 	programInstance
4279 | 		.command('rename-tag')
4280 | 		.description(
4281 | 			'[DEPRECATED] Rename an existing tag (use "tm tags rename" instead)'
4282 | 		)
4283 | 		.argument('<oldName>', 'Current name of the tag')
4284 | 		.argument('<newName>', 'New name for the tag')
4285 | 		.option(
4286 | 			'-f, --file <file>',
4287 | 			'Path to the tasks file',
4288 | 			TASKMASTER_TASKS_FILE
4289 | 		)
4290 | 		.action(async (oldName, newName, options) => {
4291 | 			// Show deprecation warning
4292 | 			console.warn(
4293 | 				chalk.yellow(
4294 | 					'⚠ Warning: "tm rename-tag" is deprecated. Use "tm tags rename" instead.'
4295 | 				)
4296 | 			);
4297 | 			console.log(
4298 | 				chalk.gray('  This command will be removed in a future version.\n')
4299 | 			);
4300 | 
4301 | 			try {
4302 | 				// Initialize TaskMaster
4303 | 				const taskMaster = initTaskMaster({
4304 | 					tasksPath: options.file || true
4305 | 				});
4306 | 				const tasksPath = taskMaster.getTasksPath();
4307 | 
4308 | 				// Validate tasks file exists
4309 | 				if (!fs.existsSync(tasksPath)) {
4310 | 					console.error(
4311 | 						chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
4312 | 					);
4313 | 					process.exit(1);
4314 | 				}
4315 | 
4316 | 				const context = {
4317 | 					projectRoot: taskMaster.getProjectRoot(),
4318 | 					commandName: 'rename-tag',
4319 | 					outputType: 'cli'
4320 | 				};
4321 | 
4322 | 				await renameTag(
4323 | 					taskMaster.getTasksPath(),
4324 | 					oldName,
4325 | 					newName,
4326 | 					{},
4327 | 					context,
4328 | 					'text'
4329 | 				);
4330 | 			} catch (error) {
4331 | 				console.error(chalk.red(`Error renaming tag: ${error.message}`));
4332 | 				process.exit(1);
4333 | 			}
4334 | 		})
4335 | 		.on('error', function (err) {
4336 | 			console.error(chalk.red(`Error: ${err.message}`));
4337 | 			process.exit(1);
4338 | 		});
4339 | 
4340 | 	// copy-tag command (DEPRECATED - use `tm tags copy` instead)
4341 | 	programInstance
4342 | 		.command('copy-tag')
4343 | 		.description(
4344 | 			'[DEPRECATED] Copy an existing tag to create a new tag with the same tasks (use "tm tags copy" instead)'
4345 | 		)
4346 | 		.argument('<sourceName>', 'Name of the source tag to copy from')
4347 | 		.argument('<targetName>', 'Name of the new tag to create')
4348 | 		.option(
4349 | 			'-f, --file <file>',
4350 | 			'Path to the tasks file',
4351 | 			TASKMASTER_TASKS_FILE
4352 | 		)
4353 | 		.option('-d, --description <text>', 'Optional description for the new tag')
4354 | 		.action(async (sourceName, targetName, options) => {
4355 | 			// Show deprecation warning
4356 | 			console.warn(
4357 | 				chalk.yellow(
4358 | 					'⚠ Warning: "tm copy-tag" is deprecated. Use "tm tags copy" instead.'
4359 | 				)
4360 | 			);
4361 | 			console.log(
4362 | 				chalk.gray('  This command will be removed in a future version.\n')
4363 | 			);
4364 | 
4365 | 			try {
4366 | 				// Initialize TaskMaster
4367 | 				const taskMaster = initTaskMaster({
4368 | 					tasksPath: options.file || true
4369 | 				});
4370 | 				const tasksPath = taskMaster.getTasksPath();
4371 | 
4372 | 				// Validate tasks file exists
4373 | 				if (!fs.existsSync(tasksPath)) {
4374 | 					console.error(
4375 | 						chalk.red(`Error: Tasks file not found at path: ${tasksPath}`)
4376 | 					);
4377 | 					process.exit(1);
4378 | 				}
4379 | 
4380 | 				const copyOptions = {
4381 | 					description: options.description
4382 | 				};
4383 | 
4384 | 				const context = {
4385 | 					projectRoot: taskMaster.getProjectRoot(),
4386 | 					commandName: 'copy-tag',
4387 | 					outputType: 'cli'
4388 | 				};
4389 | 
4390 | 				await copyTag(
4391 | 					tasksPath,
4392 | 					sourceName,
4393 | 					targetName,
4394 | 					copyOptions,
4395 | 					context,
4396 | 					'text'
4397 | 				);
4398 | 			} catch (error) {
4399 | 				console.error(chalk.red(`Error copying tag: ${error.message}`));
4400 | 				process.exit(1);
4401 | 			}
4402 | 		})
4403 | 		.on('error', function (err) {
4404 | 			console.error(chalk.red(`Error: ${err.message}`));
4405 | 			process.exit(1);
4406 | 		});
4407 | 
4408 | 	return programInstance;
4409 | }
4410 | 
4411 | /**
4412 |  * Setup the CLI application
4413 |  * @returns {Object} Configured Commander program
4414 |  */
4415 | function setupCLI() {
4416 | 	// Create a new program instance
4417 | 	const programInstance = new Command()
4418 | 		.name('task-master')
4419 | 		.description('AI-driven development task management')
4420 | 		.version(process.env.TM_PUBLIC_VERSION || 'unknown')
4421 | 		.helpOption('-h, --help', 'Display help')
4422 | 		.addHelpCommand(false); // Disable default help command
4423 | 
4424 | 	// Only override help for the main program, not for individual commands
4425 | 	const originalHelpInformation =
4426 | 		programInstance.helpInformation.bind(programInstance);
4427 | 	programInstance.helpInformation = function () {
4428 | 		// If this is being called for a subcommand, use the default Commander.js help
4429 | 		if (this.parent && this.parent !== programInstance) {
4430 | 			return originalHelpInformation();
4431 | 		}
4432 | 		// If this is the main program help, use our custom display
4433 | 		displayHelp();
4434 | 		return '';
4435 | 	};
4436 | 
4437 | 	// Register commands
4438 | 	registerCommands(programInstance);
4439 | 
4440 | 	return programInstance;
4441 | }
4442 | 
4443 | /**
4444 |  * Parse arguments and run the CLI
4445 |  * @param {Array} argv - Command-line arguments
4446 |  */
4447 | async function runCLI(argv = process.argv) {
4448 | 	try {
4449 | 		// Display banner if not in a pipe (except for init command which has its own banner)
4450 | 		const isInitCommand = argv.includes('init');
4451 | 		if (process.stdout.isTTY && !isInitCommand) {
4452 | 			displayBanner();
4453 | 		}
4454 | 
4455 | 		// If no arguments provided, show help
4456 | 		if (argv.length <= 2) {
4457 | 			displayHelp();
4458 | 			process.exit(0);
4459 | 		}
4460 | 
4461 | 		// Check for updates BEFORE executing the command
4462 | 		const currentVersion = getTaskMasterVersion();
4463 | 		const updateInfo = await checkForUpdate(currentVersion);
4464 | 
4465 | 		if (updateInfo.needsUpdate) {
4466 | 			// Display the upgrade notification first
4467 | 			displayUpgradeNotification(
4468 | 				updateInfo.currentVersion,
4469 | 				updateInfo.latestVersion,
4470 | 				updateInfo.highlights
4471 | 			);
4472 | 
4473 | 			// Automatically perform the update
4474 | 			const updateSuccess = await performAutoUpdate(updateInfo.latestVersion);
4475 | 			if (updateSuccess) {
4476 | 				// Restart with the new version - this will execute the user's command
4477 | 				restartWithNewVersion(argv);
4478 | 				return; // Never reached, but for clarity
4479 | 			}
4480 | 			// If update fails, continue with current version
4481 | 		}
4482 | 
4483 | 		// Setup and parse
4484 | 		// NOTE: getConfig() might be called during setupCLI->registerCommands if commands need config
4485 | 		// This means the ConfigurationError might be thrown here if configuration file is missing.
4486 | 		const programInstance = setupCLI();
4487 | 		await programInstance.parseAsync(argv);
4488 | 
4489 | 		// Check if migration has occurred and show FYI notice once
4490 | 		try {
4491 | 			// Use initTaskMaster with no required fields - will only fail if no project root
4492 | 			const taskMaster = initTaskMaster({});
4493 | 
4494 | 			const tasksPath = taskMaster.getTasksPath();
4495 | 			const statePath = taskMaster.getStatePath();
4496 | 
4497 | 			if (tasksPath && fs.existsSync(tasksPath)) {
4498 | 				// Read raw file to check if it has master key (bypassing tag resolution)
4499 | 				const rawData = fs.readFileSync(tasksPath, 'utf8');
4500 | 				const parsedData = JSON.parse(rawData);
4501 | 
4502 | 				if (parsedData && parsedData.master) {
4503 | 					// Migration has occurred, check if we've shown the notice
4504 | 					let stateData = { migrationNoticeShown: false };
4505 | 					if (statePath && fs.existsSync(statePath)) {
4506 | 						// Read state.json directly without tag resolution since it's not a tagged file
4507 | 						const rawStateData = fs.readFileSync(statePath, 'utf8');
4508 | 						stateData = JSON.parse(rawStateData) || stateData;
4509 | 					}
4510 | 
4511 | 					if (!stateData.migrationNoticeShown) {
4512 | 						displayTaggedTasksFYI({ _migrationHappened: true });
4513 | 
4514 | 						// Mark as shown
4515 | 						stateData.migrationNoticeShown = true;
4516 | 						// Write state.json directly without tag resolution since it's not a tagged file
4517 | 						if (statePath) {
4518 | 							fs.writeFileSync(statePath, JSON.stringify(stateData, null, 2));
4519 | 						}
4520 | 					}
4521 | 				}
4522 | 			}
4523 | 		} catch (error) {
4524 | 			// Silently ignore errors checking for migration notice
4525 | 		}
4526 | 	} catch (error) {
4527 | 		// ** Specific catch block for missing configuration file **
4528 | 		if (error instanceof ConfigurationError) {
4529 | 			console.error(
4530 | 				boxen(
4531 | 					chalk.red.bold('Configuration Update Required!') +
4532 | 						'\n\n' +
4533 | 						chalk.white('Taskmaster now uses a ') +
4534 | 						chalk.yellow.bold('configuration file') +
4535 | 						chalk.white(
4536 | 							' in your project for AI model choices and settings.\n\n' +
4537 | 								'This file appears to be '
4538 | 						) +
4539 | 						chalk.red.bold('missing') +
4540 | 						chalk.white('. No worries though.\n\n') +
4541 | 						chalk.cyan.bold('To create this file, run the interactive setup:') +
4542 | 						'\n' +
4543 | 						chalk.green('   task-master models --setup') +
4544 | 						'\n\n' +
4545 | 						chalk.white.bold('Key Points:') +
4546 | 						'\n' +
4547 | 						chalk.white('*   ') +
4548 | 						chalk.yellow.bold('Configuration file') +
4549 | 						chalk.white(
4550 | 							': Stores your AI model settings (do not manually edit)\n'
4551 | 						) +
4552 | 						chalk.white('*   ') +
4553 | 						chalk.yellow.bold('.env & .mcp.json') +
4554 | 						chalk.white(': Still used ') +
4555 | 						chalk.red.bold('only') +
4556 | 						chalk.white(' for your AI provider API keys.\n\n') +
4557 | 						chalk.cyan(
4558 | 							'`task-master models` to check your config & available models\n'
4559 | 						) +
4560 | 						chalk.cyan(
4561 | 							'`task-master models --setup` to adjust the AI models used by Taskmaster'
4562 | 						),
4563 | 					{
4564 | 						padding: 1,
4565 | 						margin: { top: 1 },
4566 | 						borderColor: 'red',
4567 | 						borderStyle: 'round'
4568 | 					}
4569 | 				)
4570 | 			);
4571 | 		} else {
4572 | 			// Generic error handling for other errors
4573 | 			displayError(error);
4574 | 		}
4575 | 
4576 | 		process.exit(1);
4577 | 	}
4578 | }
4579 | 
4580 | /**
4581 |  * Resolve the final complexity-report path.
4582 |  * Rules:
4583 |  *  1. If caller passes --output, always respect it.
4584 |  *  2. If no explicit output AND tag === 'master' → default report file
4585 |  *  3. If no explicit output AND tag !== 'master' → append _<tag>.json
4586 |  *
4587 |  * @param {string|undefined} outputOpt  --output value from CLI (may be undefined)
4588 |  * @param {string} targetTag            resolved tag (defaults to 'master')
4589 |  * @param {string} projectRoot          absolute project root
4590 |  * @returns {string} absolute path for the report
4591 |  */
4592 | export function resolveComplexityReportPath({
4593 | 	projectRoot,
4594 | 	tag = 'master',
4595 | 	output // may be undefined
4596 | }) {
4597 | 	// 1. user knows best
4598 | 	if (output) {
4599 | 		return path.isAbsolute(output) ? output : path.join(projectRoot, output);
4600 | 	}
4601 | 
4602 | 	// 2. default naming
4603 | 	const base = path.join(projectRoot, COMPLEXITY_REPORT_FILE);
4604 | 	return tag !== 'master' ? base.replace('.json', `_${tag}.json`) : base;
4605 | }
4606 | 
4607 | export { registerCommands, setupCLI, runCLI };
4608 | 
```
Page 67/69FirstPrevNextLast