#
tokens: 44367/50000 6/975 files (page 40/69)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 40 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

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

```javascript
  1 | /**
  2 |  * Path utility functions for Task Master
  3 |  * Provides centralized path resolution logic for both CLI and MCP use cases
  4 |  *
  5 |  * NOTE: This file is a legacy wrapper around @tm/core utilities.
  6 |  * New code should import directly from @tm/core instead.
  7 |  * This file exists for backward compatibility during the migration period.
  8 |  */
  9 | 
 10 | import path from 'path';
 11 | import fs from 'fs';
 12 | import {
 13 | 	TASKMASTER_TASKS_FILE,
 14 | 	LEGACY_TASKS_FILE,
 15 | 	TASKMASTER_DOCS_DIR,
 16 | 	TASKMASTER_REPORTS_DIR,
 17 | 	COMPLEXITY_REPORT_FILE,
 18 | 	TASKMASTER_CONFIG_FILE,
 19 | 	LEGACY_CONFIG_FILE
 20 | } from '../constants/paths.js';
 21 | import { getLoggerOrDefault } from './logger-utils.js';
 22 | import {
 23 | 	findProjectRoot as findProjectRootCore,
 24 | 	normalizeProjectRoot as normalizeProjectRootCore
 25 | } from '@tm/core';
 26 | 
 27 | /**
 28 |  * Normalize project root to ensure it doesn't end with .taskmaster
 29 |  * This prevents double .taskmaster paths when using constants that include .taskmaster
 30 |  *
 31 |  * @deprecated Use the TypeScript implementation from @tm/core instead
 32 |  * @param {string} projectRoot - The project root path to normalize
 33 |  * @returns {string} - Normalized project root path
 34 |  */
 35 | export function normalizeProjectRoot(projectRoot) {
 36 | 	return normalizeProjectRootCore(projectRoot);
 37 | }
 38 | 
 39 | /**
 40 |  * Find the project root directory by looking for project markers
 41 |  * Traverses upwards from startDir until a project marker is found or filesystem root is reached
 42 |  * Limited to 50 parent directory levels to prevent excessive traversal
 43 |  *
 44 |  * Strategy: First searches ALL parent directories for .taskmaster (highest priority).
 45 |  * If not found, then searches for other project markers starting from current directory.
 46 |  * This ensures .taskmaster in parent directories takes precedence over other markers in subdirectories.
 47 |  *
 48 |  * @deprecated Use the TypeScript implementation from @tm/core instead
 49 |  * @param {string} startDir - Directory to start searching from (defaults to process.cwd())
 50 |  * @returns {string} - Project root path (falls back to current directory if no markers found)
 51 |  */
 52 | export function findProjectRoot(startDir = process.cwd()) {
 53 | 	return findProjectRootCore(startDir);
 54 | }
 55 | 
 56 | /**
 57 |  * Find the tasks.json file path with fallback logic
 58 |  * @param {string|null} explicitPath - Explicit path provided by user (highest priority)
 59 |  * @param {Object|null} args - Args object from MCP args (optional)
 60 |  * @param {Object|null} log - Logger object (optional)
 61 |  * @returns {string|null} - Resolved tasks.json path or null if not found
 62 |  */
 63 | export function findTasksPath(explicitPath = null, args = null, log = null) {
 64 | 	// Use the passed logger if available, otherwise use the default logger
 65 | 	const logger = getLoggerOrDefault(log);
 66 | 
 67 | 	// 1. First determine project root to use as base for all path resolution
 68 | 	const rawProjectRoot = args?.projectRoot || findProjectRoot();
 69 | 
 70 | 	if (!rawProjectRoot) {
 71 | 		logger.warn?.('Could not determine project root directory');
 72 | 		return null;
 73 | 	}
 74 | 
 75 | 	// 2. Normalize project root to prevent double .taskmaster paths
 76 | 	const projectRoot = normalizeProjectRoot(rawProjectRoot);
 77 | 
 78 | 	// 3. If explicit path is provided, resolve it relative to project root (highest priority)
 79 | 	if (explicitPath) {
 80 | 		const resolvedPath = path.isAbsolute(explicitPath)
 81 | 			? explicitPath
 82 | 			: path.resolve(projectRoot, explicitPath);
 83 | 
 84 | 		if (fs.existsSync(resolvedPath)) {
 85 | 			logger.info?.(`Using explicit tasks path: ${resolvedPath}`);
 86 | 			return resolvedPath;
 87 | 		} else {
 88 | 			logger.warn?.(
 89 | 				`Explicit tasks path not found: ${resolvedPath}, trying fallbacks`
 90 | 			);
 91 | 		}
 92 | 	}
 93 | 
 94 | 	// 4. Check possible locations in order of preference
 95 | 	const possiblePaths = [
 96 | 		path.join(projectRoot, TASKMASTER_TASKS_FILE), // .taskmaster/tasks/tasks.json (NEW)
 97 | 		path.join(projectRoot, LEGACY_TASKS_FILE) // tasks/tasks.json (LEGACY)
 98 | 	];
 99 | 
100 | 	for (const tasksPath of possiblePaths) {
101 | 		if (fs.existsSync(tasksPath)) {
102 | 			logger.info?.(`Found tasks file at: ${tasksPath}`);
103 | 
104 | 			// Issue deprecation warning for legacy paths
105 | 			if (
106 | 				tasksPath.includes('tasks/tasks.json') &&
107 | 				!tasksPath.includes('.taskmaster')
108 | 			) {
109 | 				logger.warn?.(
110 | 					`⚠️  DEPRECATION WARNING: Found tasks.json in legacy location '${tasksPath}'. Please migrate to the new .taskmaster directory structure. Run 'task-master migrate' to automatically migrate your project.`
111 | 				);
112 | 			} else if (
113 | 				tasksPath.endsWith('tasks.json') &&
114 | 				!tasksPath.includes('.taskmaster') &&
115 | 				!tasksPath.includes('tasks/')
116 | 			) {
117 | 				logger.warn?.(
118 | 					`⚠️  DEPRECATION WARNING: Found tasks.json in legacy root location '${tasksPath}'. Please migrate to the new .taskmaster directory structure. Run 'task-master migrate' to automatically migrate your project.`
119 | 				);
120 | 			}
121 | 
122 | 			return tasksPath;
123 | 		}
124 | 	}
125 | 
126 | 	logger.warn?.(`No tasks.json found in project: ${projectRoot}`);
127 | 	return null;
128 | }
129 | 
130 | /**
131 |  * Find the PRD document file path with fallback logic
132 |  * @param {string|null} explicitPath - Explicit path provided by user (highest priority)
133 |  * @param {Object|null} args - Args object for MCP context (optional)
134 |  * @param {Object|null} log - Logger object (optional)
135 |  * @returns {string|null} - Resolved PRD document path or null if not found
136 |  */
137 | export function findPRDPath(explicitPath = null, args = null, log = null) {
138 | 	const logger = getLoggerOrDefault(log);
139 | 
140 | 	// 1. If explicit path is provided, use it (highest priority)
141 | 	if (explicitPath) {
142 | 		// Use original cwd if available (set by dev.js), otherwise current cwd
143 | 		// This ensures relative paths are resolved from where the user invoked the command
144 | 		const cwdForResolution =
145 | 			process.env.TASKMASTER_ORIGINAL_CWD || process.cwd();
146 | 
147 | 		const resolvedPath = path.isAbsolute(explicitPath)
148 | 			? explicitPath
149 | 			: path.resolve(cwdForResolution, explicitPath);
150 | 
151 | 		if (fs.existsSync(resolvedPath)) {
152 | 			logger.info?.(`Using explicit PRD path: ${resolvedPath}`);
153 | 			return resolvedPath;
154 | 		} else {
155 | 			logger.warn?.(
156 | 				`Explicit PRD path not found: ${resolvedPath}, trying fallbacks`
157 | 			);
158 | 		}
159 | 	}
160 | 
161 | 	// 2. Try to get project root from args (MCP) or find it
162 | 	const rawProjectRoot = args?.projectRoot || findProjectRoot();
163 | 
164 | 	if (!rawProjectRoot) {
165 | 		logger.warn?.('Could not determine project root directory');
166 | 		return null;
167 | 	}
168 | 
169 | 	// 3. Normalize project root to prevent double .taskmaster paths
170 | 	const projectRoot = normalizeProjectRoot(rawProjectRoot);
171 | 
172 | 	// 4. Check possible locations in order of preference
173 | 	const locations = [
174 | 		TASKMASTER_DOCS_DIR, // .taskmaster/docs/ (NEW)
175 | 		'scripts/', // Legacy location
176 | 		'' // Project root
177 | 	];
178 | 
179 | 	const fileNames = ['PRD.md', 'prd.md', 'PRD.txt', 'prd.txt'];
180 | 
181 | 	for (const location of locations) {
182 | 		for (const fileName of fileNames) {
183 | 			const prdPath = path.join(projectRoot, location, fileName);
184 | 			if (fs.existsSync(prdPath)) {
185 | 				logger.info?.(`Found PRD document at: ${prdPath}`);
186 | 
187 | 				// Issue deprecation warning for legacy paths
188 | 				if (location === 'scripts/' || location === '') {
189 | 					logger.warn?.(
190 | 						`⚠️  DEPRECATION WARNING: Found PRD file in legacy location '${prdPath}'. Please migrate to .taskmaster/docs/ directory. Run 'task-master migrate' to automatically migrate your project.`
191 | 					);
192 | 				}
193 | 
194 | 				return prdPath;
195 | 			}
196 | 		}
197 | 	}
198 | 
199 | 	logger.warn?.(`No PRD document found in project: ${projectRoot}`);
200 | 	return null;
201 | }
202 | 
203 | /**
204 |  * Find the complexity report file path with fallback logic
205 |  * @param {string|null} explicitPath - Explicit path provided by user (highest priority)
206 |  * @param {Object|null} args - Args object for MCP context (optional)
207 |  * @param {Object|null} log - Logger object (optional)
208 |  * @returns {string|null} - Resolved complexity report path or null if not found
209 |  */
210 | export function findComplexityReportPath(
211 | 	explicitPath = null,
212 | 	args = null,
213 | 	log = null
214 | ) {
215 | 	const logger = getLoggerOrDefault(log);
216 | 
217 | 	// 1. If explicit path is provided, use it (highest priority)
218 | 	if (explicitPath) {
219 | 		// Use original cwd if available (set by dev.js), otherwise current cwd
220 | 		const cwdForResolution =
221 | 			process.env.TASKMASTER_ORIGINAL_CWD || process.cwd();
222 | 
223 | 		const resolvedPath = path.isAbsolute(explicitPath)
224 | 			? explicitPath
225 | 			: path.resolve(cwdForResolution, explicitPath);
226 | 
227 | 		if (fs.existsSync(resolvedPath)) {
228 | 			logger.info?.(`Using explicit complexity report path: ${resolvedPath}`);
229 | 			return resolvedPath;
230 | 		} else {
231 | 			logger.warn?.(
232 | 				`Explicit complexity report path not found: ${resolvedPath}, trying fallbacks`
233 | 			);
234 | 		}
235 | 	}
236 | 
237 | 	// 2. Try to get project root from args (MCP) or find it
238 | 	const rawProjectRoot = args?.projectRoot || findProjectRoot();
239 | 
240 | 	if (!rawProjectRoot) {
241 | 		logger.warn?.('Could not determine project root directory');
242 | 		return null;
243 | 	}
244 | 
245 | 	// 3. Normalize project root to prevent double .taskmaster paths
246 | 	const projectRoot = normalizeProjectRoot(rawProjectRoot);
247 | 
248 | 	// 4. Check possible locations in order of preference
249 | 	const locations = [
250 | 		TASKMASTER_REPORTS_DIR, // .taskmaster/reports/ (NEW)
251 | 		'scripts/', // Legacy location
252 | 		'' // Project root
253 | 	];
254 | 
255 | 	const fileNames = [
256 | 		'task-complexity-report',
257 | 		'task-complexity',
258 | 		'complexity-report'
259 | 	].map((fileName) => {
260 | 		if (args?.tag && args?.tag !== 'master') {
261 | 			return `${fileName}_${args.tag}.json`;
262 | 		}
263 | 		return `${fileName}.json`;
264 | 	});
265 | 
266 | 	for (const location of locations) {
267 | 		for (const fileName of fileNames) {
268 | 			const reportPath = path.join(projectRoot, location, fileName);
269 | 			if (fs.existsSync(reportPath)) {
270 | 				logger.info?.(`Found complexity report at: ${reportPath}`);
271 | 
272 | 				// Issue deprecation warning for legacy paths
273 | 				if (location === 'scripts/' || location === '') {
274 | 					logger.warn?.(
275 | 						`⚠️  DEPRECATION WARNING: Found complexity report in legacy location '${reportPath}'. Please migrate to .taskmaster/reports/ directory. Run 'task-master migrate' to automatically migrate your project.`
276 | 					);
277 | 				}
278 | 
279 | 				return reportPath;
280 | 			}
281 | 		}
282 | 	}
283 | 
284 | 	logger.warn?.(`No complexity report found in project: ${projectRoot}`);
285 | 	return null;
286 | }
287 | 
288 | /**
289 |  * Resolve output path for tasks.json (create if needed)
290 |  * @param {string|null} explicitPath - Explicit output path provided by user
291 |  * @param {Object|null} args - Args object for MCP context (optional)
292 |  * @param {Object|null} log - Logger object (optional)
293 |  * @returns {string} - Resolved output path for tasks.json
294 |  */
295 | export function resolveTasksOutputPath(
296 | 	explicitPath = null,
297 | 	args = null,
298 | 	log = null
299 | ) {
300 | 	const logger = getLoggerOrDefault(log);
301 | 
302 | 	// 1. If explicit path is provided, use it
303 | 	if (explicitPath) {
304 | 		// Use original cwd if available (set by dev.js), otherwise current cwd
305 | 		const cwdForResolution =
306 | 			process.env.TASKMASTER_ORIGINAL_CWD || process.cwd();
307 | 
308 | 		const resolvedPath = path.isAbsolute(explicitPath)
309 | 			? explicitPath
310 | 			: path.resolve(cwdForResolution, explicitPath);
311 | 
312 | 		logger.info?.(`Using explicit output path: ${resolvedPath}`);
313 | 		return resolvedPath;
314 | 	}
315 | 
316 | 	// 2. Try to get project root from args (MCP) or find it
317 | 	const rawProjectRoot =
318 | 		args?.projectRoot || findProjectRoot() || process.cwd();
319 | 
320 | 	// 3. Normalize project root to prevent double .taskmaster paths
321 | 	const projectRoot = normalizeProjectRoot(rawProjectRoot);
322 | 
323 | 	// 4. Use new .taskmaster structure by default
324 | 	const defaultPath = path.join(projectRoot, TASKMASTER_TASKS_FILE);
325 | 	logger.info?.(`Using default output path: ${defaultPath}`);
326 | 
327 | 	// Ensure the directory exists
328 | 	const outputDir = path.dirname(defaultPath);
329 | 	if (!fs.existsSync(outputDir)) {
330 | 		logger.info?.(`Creating tasks directory: ${outputDir}`);
331 | 		fs.mkdirSync(outputDir, { recursive: true });
332 | 	}
333 | 
334 | 	return defaultPath;
335 | }
336 | 
337 | /**
338 |  * Resolve output path for complexity report (create if needed)
339 |  * @param {string|null} explicitPath - Explicit output path provided by user
340 |  * @param {Object|null} args - Args object for MCP context (optional)
341 |  * @param {Object|null} log - Logger object (optional)
342 |  * @returns {string} - Resolved output path for complexity report
343 |  */
344 | export function resolveComplexityReportOutputPath(
345 | 	explicitPath = null,
346 | 	args = null,
347 | 	log = null
348 | ) {
349 | 	const logger = getLoggerOrDefault(log);
350 | 	const tag = args?.tag;
351 | 
352 | 	// 1. If explicit path is provided, use it
353 | 	if (explicitPath) {
354 | 		// Use original cwd if available (set by dev.js), otherwise current cwd
355 | 		const cwdForResolution =
356 | 			process.env.TASKMASTER_ORIGINAL_CWD || process.cwd();
357 | 
358 | 		const resolvedPath = path.isAbsolute(explicitPath)
359 | 			? explicitPath
360 | 			: path.resolve(cwdForResolution, explicitPath);
361 | 
362 | 		logger.info?.(
363 | 			`Using explicit complexity report output path: ${resolvedPath}`
364 | 		);
365 | 		return resolvedPath;
366 | 	}
367 | 
368 | 	// 2. Try to get project root from args (MCP) or find it
369 | 	const rawProjectRoot =
370 | 		args?.projectRoot || findProjectRoot() || process.cwd();
371 | 	const projectRoot = normalizeProjectRoot(rawProjectRoot);
372 | 
373 | 	// 3. Use tag-aware filename
374 | 	let filename = 'task-complexity-report.json';
375 | 	if (tag && tag !== 'master') {
376 | 		filename = `task-complexity-report_${tag}.json`;
377 | 	}
378 | 
379 | 	// 4. Use new .taskmaster structure by default
380 | 	const defaultPath = path.join(projectRoot, '.taskmaster/reports', filename);
381 | 	logger.info?.(
382 | 		`Using tag-aware complexity report output path: ${defaultPath}`
383 | 	);
384 | 
385 | 	// Ensure the directory exists
386 | 	const outputDir = path.dirname(defaultPath);
387 | 	if (!fs.existsSync(outputDir)) {
388 | 		logger.info?.(`Creating reports directory: ${outputDir}`);
389 | 		fs.mkdirSync(outputDir, { recursive: true });
390 | 	}
391 | 
392 | 	return defaultPath;
393 | }
394 | 
395 | /**
396 |  * Find the configuration file path with fallback logic
397 |  * @param {string|null} explicitPath - Explicit path provided by user (highest priority)
398 |  * @param {Object|null} args - Args object for MCP context (optional)
399 |  * @param {Object|null} log - Logger object (optional)
400 |  * @returns {string|null} - Resolved config file path or null if not found
401 |  */
402 | export function findConfigPath(explicitPath = null, args = null, log = null) {
403 | 	const logger = getLoggerOrDefault(log);
404 | 
405 | 	// 1. If explicit path is provided, use it (highest priority)
406 | 	if (explicitPath) {
407 | 		// Use original cwd if available (set by dev.js), otherwise current cwd
408 | 		const cwdForResolution =
409 | 			process.env.TASKMASTER_ORIGINAL_CWD || process.cwd();
410 | 
411 | 		const resolvedPath = path.isAbsolute(explicitPath)
412 | 			? explicitPath
413 | 			: path.resolve(cwdForResolution, explicitPath);
414 | 
415 | 		if (fs.existsSync(resolvedPath)) {
416 | 			logger.info?.(`Using explicit config path: ${resolvedPath}`);
417 | 			return resolvedPath;
418 | 		} else {
419 | 			logger.warn?.(
420 | 				`Explicit config path not found: ${resolvedPath}, trying fallbacks`
421 | 			);
422 | 		}
423 | 	}
424 | 
425 | 	// 2. Try to get project root from args (MCP) or find it
426 | 	const rawProjectRoot = args?.projectRoot || findProjectRoot();
427 | 
428 | 	if (!rawProjectRoot) {
429 | 		logger.warn?.('Could not determine project root directory');
430 | 		return null;
431 | 	}
432 | 
433 | 	// 3. Normalize project root to prevent double .taskmaster paths
434 | 	const projectRoot = normalizeProjectRoot(rawProjectRoot);
435 | 
436 | 	// 4. Check possible locations in order of preference
437 | 	const possiblePaths = [
438 | 		path.join(projectRoot, TASKMASTER_CONFIG_FILE), // NEW location
439 | 		path.join(projectRoot, LEGACY_CONFIG_FILE) // LEGACY location
440 | 	];
441 | 
442 | 	for (const configPath of possiblePaths) {
443 | 		if (fs.existsSync(configPath)) {
444 | 			// Issue deprecation warning for legacy paths
445 | 			if (configPath?.endsWith(LEGACY_CONFIG_FILE)) {
446 | 				logger.warn?.(
447 | 					`⚠️  DEPRECATION WARNING: Found configuration in legacy location '${configPath}'. Please migrate to .taskmaster/config.json. Run 'task-master migrate' to automatically migrate your project.`
448 | 				);
449 | 			}
450 | 
451 | 			return configPath;
452 | 		}
453 | 	}
454 | 
455 | 	// Only warn once per command execution to prevent spam during init
456 | 	const warningKey = `config_warning_${projectRoot}`;
457 | 
458 | 	if (!global._tmConfigWarningsThisRun) {
459 | 		global._tmConfigWarningsThisRun = new Set();
460 | 	}
461 | 
462 | 	if (!global._tmConfigWarningsThisRun.has(warningKey)) {
463 | 		global._tmConfigWarningsThisRun.add(warningKey);
464 | 		logger.warn?.(`No configuration file found in project: ${projectRoot}`);
465 | 	}
466 | 
467 | 	return null;
468 | }
469 | 
```

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

```javascript
  1 | /**
  2 |  * Commands module tests - Focus on CLI setup and integration
  3 |  */
  4 | 
  5 | import { jest } from '@jest/globals';
  6 | 
  7 | // Mock modules first
  8 | jest.mock('fs', () => ({
  9 | 	existsSync: jest.fn(),
 10 | 	readFileSync: jest.fn()
 11 | }));
 12 | 
 13 | jest.mock('path', () => ({
 14 | 	join: jest.fn((dir, file) => `${dir}/${file}`)
 15 | }));
 16 | 
 17 | jest.mock('chalk', () => ({
 18 | 	red: jest.fn((text) => text),
 19 | 	blue: jest.fn((text) => text),
 20 | 	green: jest.fn((text) => text),
 21 | 	yellow: jest.fn((text) => text),
 22 | 	white: jest.fn((text) => ({
 23 | 		bold: jest.fn((text) => text)
 24 | 	})),
 25 | 	reset: jest.fn((text) => text)
 26 | }));
 27 | 
 28 | // Mock config-manager to prevent file system discovery issues
 29 | jest.mock('../../scripts/modules/config-manager.js', () => ({
 30 | 	getLogLevel: jest.fn(() => 'info'),
 31 | 	getDebugFlag: jest.fn(() => false),
 32 | 	getConfig: jest.fn(() => ({})), // Return empty config to prevent real loading
 33 | 	getGlobalConfig: jest.fn(() => ({}))
 34 | }));
 35 | 
 36 | // Mock path-utils to prevent file system discovery issues
 37 | jest.mock('../../src/utils/path-utils.js', () => ({
 38 | 	__esModule: true,
 39 | 	findProjectRoot: jest.fn(() => '/mock/project'),
 40 | 	findConfigPath: jest.fn(() => null),
 41 | 	findTasksPath: jest.fn(() => '/mock/tasks.json'),
 42 | 	findComplexityReportPath: jest.fn(() => null),
 43 | 	resolveTasksOutputPath: jest.fn(() => '/mock/tasks.json'),
 44 | 	resolveComplexityReportOutputPath: jest.fn(() => '/mock/report.json')
 45 | }));
 46 | 
 47 | jest.mock('../../scripts/modules/ui.js', () => ({
 48 | 	displayBanner: jest.fn(),
 49 | 	displayHelp: jest.fn()
 50 | }));
 51 | 
 52 | // Add utility functions for testing
 53 | const toKebabCase = (str) => {
 54 | 	return str
 55 | 		.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
 56 | 		.toLowerCase()
 57 | 		.replace(/^-/, '');
 58 | };
 59 | 
 60 | function detectCamelCaseFlags(args) {
 61 | 	const camelCaseFlags = [];
 62 | 	for (const arg of args) {
 63 | 		if (arg.startsWith('--')) {
 64 | 			const flagName = arg.split('=')[0].slice(2);
 65 | 
 66 | 			if (!flagName.includes('-')) {
 67 | 				if (/[a-z][A-Z]/.test(flagName)) {
 68 | 					const kebabVersion = toKebabCase(flagName);
 69 | 					if (kebabVersion !== flagName) {
 70 | 						camelCaseFlags.push({
 71 | 							original: flagName,
 72 | 							kebabCase: kebabVersion
 73 | 						});
 74 | 					}
 75 | 				}
 76 | 			}
 77 | 		}
 78 | 	}
 79 | 	return camelCaseFlags;
 80 | }
 81 | 
 82 | jest.mock('../../scripts/modules/utils.js', () => ({
 83 | 	CONFIG: {
 84 | 		projectVersion: '1.5.0'
 85 | 	},
 86 | 	log: jest.fn(() => {}), // Prevent any real logging that could trigger config discovery
 87 | 	toKebabCase: toKebabCase,
 88 | 	detectCamelCaseFlags: detectCamelCaseFlags
 89 | }));
 90 | 
 91 | // Import all modules after mocking
 92 | import fs from 'fs';
 93 | import path from 'path';
 94 | import { setupCLI } from '../../scripts/modules/commands.js';
 95 | import {
 96 | 	RULES_SETUP_ACTION,
 97 | 	RULES_ACTIONS
 98 | } from '../../src/constants/rules-actions.js';
 99 | import { compareVersions } from '@tm/cli';
100 | 
101 | describe('Commands Module - CLI Setup and Integration', () => {
102 | 	const mockExistsSync = jest.spyOn(fs, 'existsSync');
103 | 
104 | 	beforeEach(() => {
105 | 		jest.clearAllMocks();
106 | 		mockExistsSync.mockReturnValue(true);
107 | 	});
108 | 
109 | 	afterAll(() => {
110 | 		jest.restoreAllMocks();
111 | 	});
112 | 
113 | 	describe('setupCLI function', () => {
114 | 		test('should return Commander program instance', () => {
115 | 			const program = setupCLI();
116 | 			expect(program).toBeDefined();
117 | 			expect(program.name()).toBe('task-master');
118 | 		});
119 | 
120 | 		test('should return version that matches package.json when TM_PUBLIC_VERSION is set', () => {
121 | 			// Read actual version from package.json
122 | 			const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
123 | 			const expectedVersion = packageJson.version;
124 | 
125 | 			// Set environment variable to match package.json
126 | 			const originalEnv = process.env.TM_PUBLIC_VERSION;
127 | 			process.env.TM_PUBLIC_VERSION = expectedVersion;
128 | 
129 | 			const program = setupCLI();
130 | 			const version = program.version();
131 | 			expect(version).toBe(expectedVersion);
132 | 
133 | 			// Restore original environment
134 | 			if (originalEnv !== undefined) {
135 | 				process.env.TM_PUBLIC_VERSION = originalEnv;
136 | 			} else {
137 | 				delete process.env.TM_PUBLIC_VERSION;
138 | 			}
139 | 		});
140 | 
141 | 		test('should use default version when TM_PUBLIC_VERSION is not available', () => {
142 | 			const originalEnv = process.env.TM_PUBLIC_VERSION;
143 | 			delete process.env.TM_PUBLIC_VERSION;
144 | 
145 | 			const program = setupCLI();
146 | 			const version = program.version();
147 | 			expect(version).toBe('unknown');
148 | 
149 | 			// Restore original environment
150 | 			if (originalEnv !== undefined) {
151 | 				process.env.TM_PUBLIC_VERSION = originalEnv;
152 | 			}
153 | 		});
154 | 	});
155 | 
156 | 	describe('CLI Flag Format Validation', () => {
157 | 		test('should detect camelCase flags correctly', () => {
158 | 			const args = ['node', 'task-master', '--camelCase', '--kebab-case'];
159 | 			const camelCaseFlags = args.filter(
160 | 				(arg) =>
161 | 					arg.startsWith('--') && /[A-Z]/.test(arg) && !arg.includes('-[A-Z]')
162 | 			);
163 | 			expect(camelCaseFlags).toContain('--camelCase');
164 | 			expect(camelCaseFlags).not.toContain('--kebab-case');
165 | 		});
166 | 
167 | 		test('should accept kebab-case flags correctly', () => {
168 | 			const args = ['node', 'task-master', '--kebab-case'];
169 | 			const camelCaseFlags = args.filter(
170 | 				(arg) =>
171 | 					arg.startsWith('--') && /[A-Z]/.test(arg) && !arg.includes('-[A-Z]')
172 | 			);
173 | 			expect(camelCaseFlags).toHaveLength(0);
174 | 		});
175 | 
176 | 		test('toKebabCase should convert camelCase to kebab-case', () => {
177 | 			expect(toKebabCase('promptText')).toBe('prompt-text');
178 | 			expect(toKebabCase('userID')).toBe('user-id');
179 | 			expect(toKebabCase('numTasks')).toBe('num-tasks');
180 | 			expect(toKebabCase('alreadyKebabCase')).toBe('already-kebab-case');
181 | 		});
182 | 
183 | 		test('detectCamelCaseFlags should identify camelCase flags', () => {
184 | 			const args = [
185 | 				'node',
186 | 				'task-master',
187 | 				'add-task',
188 | 				'--promptText=test',
189 | 				'--userID=123'
190 | 			];
191 | 			const flags = detectCamelCaseFlags(args);
192 | 
193 | 			expect(flags).toHaveLength(2);
194 | 			expect(flags).toContainEqual({
195 | 				original: 'promptText',
196 | 				kebabCase: 'prompt-text'
197 | 			});
198 | 			expect(flags).toContainEqual({
199 | 				original: 'userID',
200 | 				kebabCase: 'user-id'
201 | 			});
202 | 		});
203 | 
204 | 		test('detectCamelCaseFlags should not flag kebab-case flags', () => {
205 | 			const args = [
206 | 				'node',
207 | 				'task-master',
208 | 				'add-task',
209 | 				'--prompt-text=test',
210 | 				'--user-id=123'
211 | 			];
212 | 			const flags = detectCamelCaseFlags(args);
213 | 
214 | 			expect(flags).toHaveLength(0);
215 | 		});
216 | 
217 | 		test('detectCamelCaseFlags should respect single-word flags', () => {
218 | 			const args = [
219 | 				'node',
220 | 				'task-master',
221 | 				'add-task',
222 | 				'--prompt=test',
223 | 				'--file=test.json',
224 | 				'--priority=high',
225 | 				'--promptText=test'
226 | 			];
227 | 			const flags = detectCamelCaseFlags(args);
228 | 
229 | 			expect(flags).toHaveLength(1);
230 | 			expect(flags).toContainEqual({
231 | 				original: 'promptText',
232 | 				kebabCase: 'prompt-text'
233 | 			});
234 | 		});
235 | 	});
236 | 
237 | 	describe('Command Validation Logic', () => {
238 | 		test('should validate task ID parameter correctly', () => {
239 | 			// Test valid task IDs
240 | 			const validId = '5';
241 | 			const taskId = parseInt(validId, 10);
242 | 			expect(Number.isNaN(taskId) || taskId <= 0).toBe(false);
243 | 
244 | 			// Test invalid task IDs
245 | 			const invalidId = 'not-a-number';
246 | 			const invalidTaskId = parseInt(invalidId, 10);
247 | 			expect(Number.isNaN(invalidTaskId) || invalidTaskId <= 0).toBe(true);
248 | 
249 | 			// Test zero or negative IDs
250 | 			const zeroId = '0';
251 | 			const zeroTaskId = parseInt(zeroId, 10);
252 | 			expect(Number.isNaN(zeroTaskId) || zeroTaskId <= 0).toBe(true);
253 | 		});
254 | 
255 | 		test('should handle environment variable cleanup correctly', () => {
256 | 			// Instead of using delete operator, test setting to undefined
257 | 			const testEnv = { PERPLEXITY_API_KEY: 'test-key' };
258 | 			testEnv.PERPLEXITY_API_KEY = undefined;
259 | 			expect(testEnv.PERPLEXITY_API_KEY).toBeUndefined();
260 | 		});
261 | 	});
262 | });
263 | 
264 | // Test utility functions that commands rely on
265 | describe('Version comparison utility', () => {
266 | 	test('compareVersions correctly compares semantic versions', () => {
267 | 		expect(compareVersions('1.0.0', '1.0.0')).toBe(0);
268 | 		expect(compareVersions('1.0.0', '1.0.1')).toBe(-1);
269 | 		expect(compareVersions('1.0.1', '1.0.0')).toBe(1);
270 | 		expect(compareVersions('1.0.0', '1.1.0')).toBe(-1);
271 | 		expect(compareVersions('1.1.0', '1.0.0')).toBe(1);
272 | 		expect(compareVersions('1.0.0', '2.0.0')).toBe(-1);
273 | 		expect(compareVersions('2.0.0', '1.0.0')).toBe(1);
274 | 		expect(compareVersions('1.0', '1.0.0')).toBe(0);
275 | 		expect(compareVersions('1.0.0.0', '1.0.0')).toBe(0);
276 | 		expect(compareVersions('1.0.0', '1.0.0.1')).toBe(-1);
277 | 	});
278 | });
279 | 
280 | describe('Update check functionality', () => {
281 | 	let displayUpgradeNotification;
282 | 	let parseChangelogHighlights;
283 | 	let consoleLogSpy;
284 | 
285 | 	beforeAll(async () => {
286 | 		// Import from @tm/cli instead of commands.js
287 | 		const cliModule = await import('../../apps/cli/src/utils/auto-update.js');
288 | 		displayUpgradeNotification = cliModule.displayUpgradeNotification;
289 | 		parseChangelogHighlights = cliModule.parseChangelogHighlights;
290 | 	});
291 | 
292 | 	beforeEach(() => {
293 | 		consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
294 | 	});
295 | 
296 | 	afterEach(() => {
297 | 		consoleLogSpy.mockRestore();
298 | 	});
299 | 
300 | 	test('displays upgrade notification when newer version is available', () => {
301 | 		displayUpgradeNotification('1.0.0', '1.1.0');
302 | 		expect(consoleLogSpy).toHaveBeenCalled();
303 | 		expect(consoleLogSpy.mock.calls[0][0]).toContain('Update Available!');
304 | 		expect(consoleLogSpy.mock.calls[0][0]).toContain('1.0.0');
305 | 		expect(consoleLogSpy.mock.calls[0][0]).toContain('1.1.0');
306 | 	});
307 | 
308 | 	test('displays upgrade notification with highlights when provided', () => {
309 | 		const highlights = [
310 | 			'Add Codex CLI provider with OAuth authentication',
311 | 			'Cursor IDE custom slash command support',
312 | 			'Move to AI SDK v5'
313 | 		];
314 | 		displayUpgradeNotification('1.0.0', '1.1.0', highlights);
315 | 		expect(consoleLogSpy).toHaveBeenCalled();
316 | 		const output = consoleLogSpy.mock.calls[0][0];
317 | 		expect(output).toContain('Update Available!');
318 | 		expect(output).toContain('1.0.0');
319 | 		expect(output).toContain('1.1.0');
320 | 		expect(output).toContain("What's New:");
321 | 		expect(output).toContain(
322 | 			'Add Codex CLI provider with OAuth authentication'
323 | 		);
324 | 		expect(output).toContain('Cursor IDE custom slash command support');
325 | 		expect(output).toContain('Move to AI SDK v5');
326 | 	});
327 | 
328 | 	test('displays upgrade notification without highlights section when empty array', () => {
329 | 		displayUpgradeNotification('1.0.0', '1.1.0', []);
330 | 		expect(consoleLogSpy).toHaveBeenCalled();
331 | 		const output = consoleLogSpy.mock.calls[0][0];
332 | 		expect(output).toContain('Update Available!');
333 | 		expect(output).not.toContain("What's New:");
334 | 		expect(output).toContain(
335 | 			'Auto-updating to the latest version with new features and bug fixes'
336 | 		);
337 | 	});
338 | 
339 | 	test('parseChangelogHighlights validates version format to prevent ReDoS', () => {
340 | 		const mockChangelog = `
341 | ## 1.0.0
342 | 
343 | ### Minor Changes
344 | 
345 | - [#123](https://example.com) Thanks [@user](https://example.com)! - Test feature
346 | 		`;
347 | 
348 | 		// Valid versions should work
349 | 		expect(parseChangelogHighlights(mockChangelog, '1.0.0')).toEqual([
350 | 			'Test feature'
351 | 		]);
352 | 		expect(parseChangelogHighlights(mockChangelog, '1.0.0-rc.1')).toEqual([]);
353 | 
354 | 		// Invalid versions should return empty array (ReDoS protection)
355 | 		expect(parseChangelogHighlights(mockChangelog, 'invalid')).toEqual([]);
356 | 		expect(parseChangelogHighlights(mockChangelog, '1.0')).toEqual([]);
357 | 		expect(parseChangelogHighlights(mockChangelog, 'a.b.c')).toEqual([]);
358 | 		expect(
359 | 			parseChangelogHighlights(mockChangelog, '((((((((((((((((((((((((((((((a')
360 | 		).toEqual([]);
361 | 	});
362 | });
363 | 
364 | // -----------------------------------------------------------------------------
365 | // Rules command tests (add/remove)
366 | // -----------------------------------------------------------------------------
367 | describe('rules command', () => {
368 | 	let program;
369 | 	let mockConsoleLog;
370 | 	let mockConsoleError;
371 | 	let mockExit;
372 | 
373 | 	beforeEach(() => {
374 | 		jest.clearAllMocks();
375 | 		program = setupCLI();
376 | 		mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => {});
377 | 		mockConsoleError = jest
378 | 			.spyOn(console, 'error')
379 | 			.mockImplementation(() => {});
380 | 		mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
381 | 	});
382 | 
383 | 	test('should handle rules add <profile> command', async () => {
384 | 		// Simulate: task-master rules add roo
385 | 		await program.parseAsync(['rules', RULES_ACTIONS.ADD, 'roo'], {
386 | 			from: 'user'
387 | 		});
388 | 		// Expect some log output indicating success
389 | 		expect(mockConsoleLog).toHaveBeenCalledWith(
390 | 			expect.stringMatching(/adding rules for profile: roo/i)
391 | 		);
392 | 		expect(mockConsoleLog).toHaveBeenCalledWith(
393 | 			expect.stringMatching(/completed adding rules for profile: roo/i)
394 | 		);
395 | 		// Should not exit with error
396 | 		expect(mockExit).not.toHaveBeenCalledWith(1);
397 | 	});
398 | 
399 | 	test('should handle rules remove <profile> command', async () => {
400 | 		// Simulate: task-master rules remove roo --force
401 | 		await program.parseAsync(
402 | 			['rules', RULES_ACTIONS.REMOVE, 'roo', '--force'],
403 | 			{
404 | 				from: 'user'
405 | 			}
406 | 		);
407 | 		// Expect some log output indicating removal
408 | 		expect(mockConsoleLog).toHaveBeenCalledWith(
409 | 			expect.stringMatching(/removing rules for profile: roo/i)
410 | 		);
411 | 		expect(mockConsoleLog).toHaveBeenCalledWith(
412 | 			expect.stringMatching(/Summary for roo: Rule profile removed/i)
413 | 		);
414 | 		// Should not exit with error
415 | 		expect(mockExit).not.toHaveBeenCalledWith(1);
416 | 	});
417 | 
418 | 	test(`should handle rules --${RULES_SETUP_ACTION} command`, async () => {
419 | 		// For this test, we'll verify that the command doesn't crash and exits gracefully
420 | 		// Since mocking ES modules is complex, we'll test the command structure instead
421 | 
422 | 		// Create a spy on console.log to capture any output
423 | 		const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
424 | 
425 | 		// Mock process.exit to prevent actual exit and capture the call
426 | 		const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {});
427 | 
428 | 		try {
429 | 			// The command should be recognized and not throw an error about invalid action
430 | 			// We expect it to attempt to run the interactive setup, but since we can't easily
431 | 			// mock the ES module, we'll just verify the command structure is correct
432 | 
433 | 			// This test verifies that:
434 | 			// 1. The --setup flag is recognized as a valid option
435 | 			// 2. The command doesn't exit with error code 1 due to invalid action
436 | 			// 3. The command structure is properly set up
437 | 
438 | 			// Note: In a real scenario, this would call runInteractiveProfilesSetup()
439 | 			// but for testing purposes, we're focusing on command structure validation
440 | 
441 | 			expect(() => {
442 | 				// Test that the command option is properly configured
443 | 				const command = program.commands.find((cmd) => cmd.name() === 'rules');
444 | 				expect(command).toBeDefined();
445 | 
446 | 				// Check that the --setup option exists
447 | 				const setupOption = command.options.find(
448 | 					(opt) => opt.long === `--${RULES_SETUP_ACTION}`
449 | 				);
450 | 				expect(setupOption).toBeDefined();
451 | 				expect(setupOption.description).toContain('interactive setup');
452 | 			}).not.toThrow();
453 | 
454 | 			// Verify the command structure is valid
455 | 			expect(mockExit).not.toHaveBeenCalledWith(1);
456 | 		} finally {
457 | 			consoleSpy.mockRestore();
458 | 			exitSpy.mockRestore();
459 | 		}
460 | 	});
461 | 
462 | 	test('should show error for invalid action', async () => {
463 | 		// Simulate: task-master rules invalid-action
464 | 		await program.parseAsync(['rules', 'invalid-action'], { from: 'user' });
465 | 
466 | 		// Should show error for invalid action
467 | 		expect(mockConsoleError).toHaveBeenCalledWith(
468 | 			expect.stringMatching(/Error: Invalid or missing action/i)
469 | 		);
470 | 		expect(mockConsoleError).toHaveBeenCalledWith(
471 | 			expect.stringMatching(
472 | 				new RegExp(
473 | 					`For interactive setup, use: task-master rules --${RULES_SETUP_ACTION}`,
474 | 					'i'
475 | 				)
476 | 			)
477 | 		);
478 | 		expect(mockExit).toHaveBeenCalledWith(1);
479 | 	});
480 | 
481 | 	test('should show error when no action provided', async () => {
482 | 		// Simulate: task-master rules (no action)
483 | 		await program.parseAsync(['rules'], { from: 'user' });
484 | 
485 | 		// Should show error for missing action
486 | 		expect(mockConsoleError).toHaveBeenCalledWith(
487 | 			expect.stringMatching(/Error: Invalid or missing action 'none'/i)
488 | 		);
489 | 		expect(mockConsoleError).toHaveBeenCalledWith(
490 | 			expect.stringMatching(
491 | 				new RegExp(
492 | 					`For interactive setup, use: task-master rules --${RULES_SETUP_ACTION}`,
493 | 					'i'
494 | 				)
495 | 			)
496 | 		);
497 | 		expect(mockExit).toHaveBeenCalledWith(1);
498 | 	});
499 | });
500 | 
```

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

```javascript
  1 | /**
  2 |  * Rule Transformer Module
  3 |  * Handles conversion of Cursor rules to profile rules
  4 |  *
  5 |  * This module procedurally generates .{profile}/rules files from assets/rules files,
  6 |  * eliminating the need to maintain both sets of files manually.
  7 |  */
  8 | import fs from 'fs';
  9 | import path from 'path';
 10 | import { log } from '../../scripts/modules/utils.js';
 11 | 
 12 | // Import asset resolver
 13 | import { assetExists, readAsset, getAssetsDir } from './asset-resolver.js';
 14 | 
 15 | // Import the shared MCP configuration helper
 16 | import {
 17 | 	setupMCPConfiguration,
 18 | 	removeTaskMasterMCPConfiguration
 19 | } from './create-mcp-config.js';
 20 | 
 21 | // Import profile constants (single source of truth)
 22 | import { RULE_PROFILES } from '../constants/profiles.js';
 23 | 
 24 | // --- Profile Imports ---
 25 | import * as profilesModule from '../profiles/index.js';
 26 | 
 27 | export function isValidProfile(profile) {
 28 | 	return RULE_PROFILES.includes(profile);
 29 | }
 30 | 
 31 | /**
 32 |  * Get rule profile by name
 33 |  * @param {string} name - Profile name
 34 |  * @returns {Object|null} Profile object or null if not found
 35 |  */
 36 | export function getRulesProfile(name) {
 37 | 	if (!isValidProfile(name)) {
 38 | 		return null;
 39 | 	}
 40 | 
 41 | 	// Get the profile from the imported profiles module
 42 | 	const profileKey = `${name}Profile`;
 43 | 	const profile = profilesModule[profileKey];
 44 | 
 45 | 	if (!profile) {
 46 | 		throw new Error(
 47 | 			`Profile not found: static import missing for '${name}'. Valid profiles: ${RULE_PROFILES.join(', ')}`
 48 | 		);
 49 | 	}
 50 | 
 51 | 	return profile;
 52 | }
 53 | 
 54 | /**
 55 |  * Replace basic Cursor terms with profile equivalents
 56 |  */
 57 | function replaceBasicTerms(content, conversionConfig) {
 58 | 	let result = content;
 59 | 
 60 | 	// Apply profile term replacements
 61 | 	conversionConfig.profileTerms.forEach((pattern) => {
 62 | 		if (typeof pattern.to === 'function') {
 63 | 			result = result.replace(pattern.from, pattern.to);
 64 | 		} else {
 65 | 			result = result.replace(pattern.from, pattern.to);
 66 | 		}
 67 | 	});
 68 | 
 69 | 	// Apply file extension replacements
 70 | 	conversionConfig.fileExtensions.forEach((pattern) => {
 71 | 		result = result.replace(pattern.from, pattern.to);
 72 | 	});
 73 | 
 74 | 	return result;
 75 | }
 76 | 
 77 | /**
 78 |  * Replace Cursor tool references with profile tool equivalents
 79 |  */
 80 | function replaceToolReferences(content, conversionConfig) {
 81 | 	let result = content;
 82 | 
 83 | 	// Basic pattern for direct tool name replacements
 84 | 	const toolNames = conversionConfig.toolNames;
 85 | 	const toolReferencePattern = new RegExp(
 86 | 		`\\b(${Object.keys(toolNames).join('|')})\\b`,
 87 | 		'g'
 88 | 	);
 89 | 
 90 | 	// Apply direct tool name replacements
 91 | 	result = result.replace(toolReferencePattern, (match, toolName) => {
 92 | 		return toolNames[toolName] || toolName;
 93 | 	});
 94 | 
 95 | 	// Apply contextual tool replacements
 96 | 	conversionConfig.toolContexts.forEach((pattern) => {
 97 | 		result = result.replace(pattern.from, pattern.to);
 98 | 	});
 99 | 
100 | 	// Apply tool group replacements
101 | 	conversionConfig.toolGroups.forEach((pattern) => {
102 | 		result = result.replace(pattern.from, pattern.to);
103 | 	});
104 | 
105 | 	return result;
106 | }
107 | 
108 | /**
109 |  * Update documentation URLs to point to profile documentation
110 |  */
111 | function updateDocReferences(content, conversionConfig) {
112 | 	let result = content;
113 | 
114 | 	// Apply documentation URL replacements
115 | 	conversionConfig.docUrls.forEach((pattern) => {
116 | 		if (typeof pattern.to === 'function') {
117 | 			result = result.replace(pattern.from, pattern.to);
118 | 		} else {
119 | 			result = result.replace(pattern.from, pattern.to);
120 | 		}
121 | 	});
122 | 
123 | 	return result;
124 | }
125 | 
126 | /**
127 |  * Update file references in markdown links
128 |  */
129 | function updateFileReferences(content, conversionConfig) {
130 | 	const { pathPattern, replacement } = conversionConfig.fileReferences;
131 | 	return content.replace(pathPattern, replacement);
132 | }
133 | 
134 | /**
135 |  * Transform rule content to profile-specific rules
136 |  * @param {string} content - The content to transform
137 |  * @param {Object} conversionConfig - The conversion configuration
138 |  * @param {Object} globalReplacements - Global text replacements
139 |  * @returns {string} - The transformed content
140 |  */
141 | function transformRuleContent(content, conversionConfig, globalReplacements) {
142 | 	let result = content;
143 | 
144 | 	// Apply all transformations in appropriate order
145 | 	result = updateFileReferences(result, conversionConfig);
146 | 	result = replaceBasicTerms(result, conversionConfig);
147 | 	result = replaceToolReferences(result, conversionConfig);
148 | 	result = updateDocReferences(result, conversionConfig);
149 | 
150 | 	// Apply any global/catch-all replacements from the profile
151 | 	// Super aggressive failsafe pass to catch any variations we might have missed
152 | 	// This ensures critical transformations are applied even in contexts we didn't anticipate
153 | 	globalReplacements.forEach((pattern) => {
154 | 		if (typeof pattern.to === 'function') {
155 | 			result = result.replace(pattern.from, pattern.to);
156 | 		} else {
157 | 			result = result.replace(pattern.from, pattern.to);
158 | 		}
159 | 	});
160 | 
161 | 	return result;
162 | }
163 | 
164 | /**
165 |  * Convert a Cursor rule file to a profile-specific rule file
166 |  * @param {string} sourcePath - Path to the source .mdc file
167 |  * @param {string} targetPath - Path to the target file
168 |  * @param {Object} profile - The profile configuration
169 |  * @returns {boolean} - Success status
170 |  */
171 | export function convertRuleToProfileRule(sourcePath, targetPath, profile) {
172 | 	const { conversionConfig, globalReplacements } = profile;
173 | 	try {
174 | 		// Read source content
175 | 		const content = fs.readFileSync(sourcePath, 'utf8');
176 | 
177 | 		// Transform content
178 | 		const transformedContent = transformRuleContent(
179 | 			content,
180 | 			conversionConfig,
181 | 			globalReplacements
182 | 		);
183 | 
184 | 		// Ensure target directory exists
185 | 		const targetDir = path.dirname(targetPath);
186 | 		if (!fs.existsSync(targetDir)) {
187 | 			fs.mkdirSync(targetDir, { recursive: true });
188 | 		}
189 | 
190 | 		// Write transformed content
191 | 		fs.writeFileSync(targetPath, transformedContent);
192 | 
193 | 		return true;
194 | 	} catch (error) {
195 | 		console.error(`Error converting rule file: ${error.message}`);
196 | 		return false;
197 | 	}
198 | }
199 | 
200 | /**
201 |  * Convert all Cursor rules to profile rules for a specific profile
202 |  */
203 | export function convertAllRulesToProfileRules(projectRoot, profile) {
204 | 	const targetDir = path.join(projectRoot, profile.rulesDir);
205 | 
206 | 	let success = 0;
207 | 	let failed = 0;
208 | 
209 | 	// 1. Call onAddRulesProfile first (for pre-processing like copying assets)
210 | 	if (typeof profile.onAddRulesProfile === 'function') {
211 | 		try {
212 | 			const assetsDir = getAssetsDir();
213 | 			profile.onAddRulesProfile(targetDir, assetsDir);
214 | 			log(
215 | 				'debug',
216 | 				`[Rule Transformer] Called onAddRulesProfile for ${profile.profileName}`
217 | 			);
218 | 		} catch (error) {
219 | 			log(
220 | 				'error',
221 | 				`[Rule Transformer] onAddRulesProfile failed for ${profile.profileName}: ${error.message}`
222 | 			);
223 | 			failed++;
224 | 		}
225 | 	}
226 | 
227 | 	// 2. Handle fileMap-based rule conversion (if any)
228 | 	const sourceFiles = Object.keys(profile.fileMap);
229 | 	if (sourceFiles.length > 0) {
230 | 		// Only create rules directory if we have files to copy
231 | 		if (!fs.existsSync(targetDir)) {
232 | 			fs.mkdirSync(targetDir, { recursive: true });
233 | 		}
234 | 
235 | 		for (const sourceFile of sourceFiles) {
236 | 			// Determine if this is an asset file (not a rule file)
237 | 			const isAssetFile = !sourceFile.startsWith('rules/');
238 | 
239 | 			try {
240 | 				// Check if source file exists using asset resolver
241 | 				if (!assetExists(sourceFile)) {
242 | 					log(
243 | 						'warn',
244 | 						`[Rule Transformer] Source file not found: ${sourceFile}, skipping`
245 | 					);
246 | 					continue;
247 | 				}
248 | 
249 | 				const targetFilename = profile.fileMap[sourceFile];
250 | 				const targetPath = path.join(targetDir, targetFilename);
251 | 
252 | 				// Ensure target subdirectory exists (for rules like taskmaster/dev_workflow.md)
253 | 				const targetFileDir = path.dirname(targetPath);
254 | 				if (!fs.existsSync(targetFileDir)) {
255 | 					fs.mkdirSync(targetFileDir, { recursive: true });
256 | 				}
257 | 
258 | 				// Read source content using asset resolver
259 | 				let content = readAsset(sourceFile, 'utf8');
260 | 
261 | 				// Apply transformations (only if this is a rule file, not an asset file)
262 | 				if (!isAssetFile) {
263 | 					content = transformRuleContent(
264 | 						content,
265 | 						profile.conversionConfig,
266 | 						profile.globalReplacements
267 | 					);
268 | 				}
269 | 
270 | 				// Write to target
271 | 				fs.writeFileSync(targetPath, content, 'utf8');
272 | 				success++;
273 | 
274 | 				log(
275 | 					'debug',
276 | 					`[Rule Transformer] ${isAssetFile ? 'Copied' : 'Converted'} ${sourceFile} -> ${targetFilename} for ${profile.profileName}`
277 | 				);
278 | 			} catch (error) {
279 | 				failed++;
280 | 				log(
281 | 					'error',
282 | 					`[Rule Transformer] Failed to ${isAssetFile ? 'copy' : 'convert'} ${sourceFile} for ${profile.profileName}: ${error.message}`
283 | 				);
284 | 			}
285 | 		}
286 | 	}
287 | 
288 | 	// 3. Setup MCP configuration (if enabled)
289 | 	if (profile.mcpConfig !== false) {
290 | 		try {
291 | 			setupMCPConfiguration(projectRoot, profile.mcpConfigPath);
292 | 			log(
293 | 				'debug',
294 | 				`[Rule Transformer] Setup MCP configuration for ${profile.profileName}`
295 | 			);
296 | 		} catch (error) {
297 | 			log(
298 | 				'error',
299 | 				`[Rule Transformer] MCP setup failed for ${profile.profileName}: ${error.message}`
300 | 			);
301 | 		}
302 | 	}
303 | 
304 | 	// 4. Call post-conversion hook (for finalization)
305 | 	if (typeof profile.onPostConvertRulesProfile === 'function') {
306 | 		try {
307 | 			const assetsDir = getAssetsDir();
308 | 			profile.onPostConvertRulesProfile(targetDir, assetsDir);
309 | 			log(
310 | 				'debug',
311 | 				`[Rule Transformer] Called onPostConvertRulesProfile for ${profile.profileName}`
312 | 			);
313 | 		} catch (error) {
314 | 			log(
315 | 				'error',
316 | 				`[Rule Transformer] onPostConvertRulesProfile failed for ${profile.profileName}: ${error.message}`
317 | 			);
318 | 		}
319 | 	}
320 | 
321 | 	// Ensure we return at least 1 success for profiles that only use lifecycle functions
322 | 	return { success: Math.max(success, 1), failed };
323 | }
324 | 
325 | /**
326 |  * Remove only Task Master specific files from a profile, leaving other existing rules intact
327 |  * @param {string} projectRoot - Target project directory
328 |  * @param {Object} profile - Profile configuration
329 |  * @returns {Object} Result object
330 |  */
331 | export function removeProfileRules(projectRoot, profile) {
332 | 	const targetDir = path.join(projectRoot, profile.rulesDir);
333 | 	const profileDir = path.join(projectRoot, profile.profileDir);
334 | 
335 | 	const result = {
336 | 		profileName: profile.profileName,
337 | 		success: false,
338 | 		skipped: false,
339 | 		error: null,
340 | 		filesRemoved: [],
341 | 		mcpResult: null,
342 | 		profileDirRemoved: false,
343 | 		notice: null
344 | 	};
345 | 
346 | 	try {
347 | 		// 1. Call onRemoveRulesProfile first (for custom cleanup like removing assets)
348 | 		if (typeof profile.onRemoveRulesProfile === 'function') {
349 | 			try {
350 | 				profile.onRemoveRulesProfile(targetDir);
351 | 				log(
352 | 					'debug',
353 | 					`[Rule Transformer] Called onRemoveRulesProfile for ${profile.profileName}`
354 | 				);
355 | 			} catch (error) {
356 | 				log(
357 | 					'error',
358 | 					`[Rule Transformer] onRemoveRulesProfile failed for ${profile.profileName}: ${error.message}`
359 | 				);
360 | 			}
361 | 		}
362 | 
363 | 		// 2. Remove fileMap-based files (if any)
364 | 		const sourceFiles = Object.keys(profile.fileMap);
365 | 		if (sourceFiles.length > 0) {
366 | 			// Check if profile directory exists at all (for full profiles)
367 | 			if (!fs.existsSync(profileDir)) {
368 | 				result.success = true;
369 | 				result.skipped = true;
370 | 				log(
371 | 					'debug',
372 | 					`[Rule Transformer] Profile directory does not exist: ${profileDir}`
373 | 				);
374 | 				return result;
375 | 			}
376 | 
377 | 			let hasOtherRulesFiles = false;
378 | 
379 | 			if (fs.existsSync(targetDir)) {
380 | 				// Get list of files we're responsible for
381 | 				const taskMasterFiles = sourceFiles.map(
382 | 					(sourceFile) => profile.fileMap[sourceFile]
383 | 				);
384 | 
385 | 				// Get all files in the rules directory
386 | 				// For root-level directories, we need to be careful to avoid circular symlinks
387 | 				let allFiles = [];
388 | 				if (targetDir === projectRoot || profile.rulesDir === '.') {
389 | 					// For root directory, manually read without recursion into problematic directories
390 | 					const items = fs.readdirSync(targetDir);
391 | 					for (const item of items) {
392 | 						// Skip directories that can cause issues or are irrelevant
393 | 						if (item === 'node_modules' || item === '.git' || item === 'dist') {
394 | 							continue;
395 | 						}
396 | 						const itemPath = path.join(targetDir, item);
397 | 						try {
398 | 							const stats = fs.lstatSync(itemPath);
399 | 							if (stats.isFile()) {
400 | 								allFiles.push(item);
401 | 							} else if (stats.isDirectory() && !stats.isSymbolicLink()) {
402 | 								// Only recurse into safe directories
403 | 								const subFiles = fs.readdirSync(itemPath, { recursive: true });
404 | 								subFiles.forEach((subFile) => {
405 | 									allFiles.push(path.join(item, subFile.toString()));
406 | 								});
407 | 							}
408 | 						} catch (err) {
409 | 							// Silently skip files we can't access
410 | 						}
411 | 					}
412 | 				} else {
413 | 					// For non-root directories, use normal recursive read
414 | 					allFiles = fs.readdirSync(targetDir, { recursive: true });
415 | 				}
416 | 
417 | 				const allFilePaths = allFiles
418 | 					.filter((file) => {
419 | 						const fullPath = path.join(targetDir, file);
420 | 						try {
421 | 							const stats = fs.statSync(fullPath);
422 | 							return stats.isFile();
423 | 						} catch (err) {
424 | 							return false;
425 | 						}
426 | 					})
427 | 					.map((file) => file.toString()); // Ensure it's a string
428 | 
429 | 				// Remove only Task Master files
430 | 				for (const taskMasterFile of taskMasterFiles) {
431 | 					const filePath = path.join(targetDir, taskMasterFile);
432 | 					if (fs.existsSync(filePath)) {
433 | 						try {
434 | 							fs.rmSync(filePath, { force: true });
435 | 							result.filesRemoved.push(taskMasterFile);
436 | 							log(
437 | 								'debug',
438 | 								`[Rule Transformer] Removed Task Master file: ${taskMasterFile}`
439 | 							);
440 | 						} catch (error) {
441 | 							log(
442 | 								'error',
443 | 								`[Rule Transformer] Failed to remove ${taskMasterFile}: ${error.message}`
444 | 							);
445 | 						}
446 | 					}
447 | 				}
448 | 
449 | 				// Check for other (non-Task Master) files
450 | 				const remainingFiles = allFilePaths.filter(
451 | 					(file) => !taskMasterFiles.includes(file)
452 | 				);
453 | 
454 | 				hasOtherRulesFiles = remainingFiles.length > 0;
455 | 
456 | 				// Remove empty directories or note preserved files
457 | 				if (remainingFiles.length === 0) {
458 | 					fs.rmSync(targetDir, { recursive: true, force: true });
459 | 					log(
460 | 						'debug',
461 | 						`[Rule Transformer] Removed empty rules directory: ${targetDir}`
462 | 					);
463 | 				} else if (hasOtherRulesFiles) {
464 | 					result.notice = `Preserved ${remainingFiles.length} existing rule files in ${profile.rulesDir}`;
465 | 					log('info', `[Rule Transformer] ${result.notice}`);
466 | 				}
467 | 			}
468 | 		}
469 | 
470 | 		// 3. Handle MCP configuration - only remove Task Master, preserve other servers
471 | 		if (profile.mcpConfig !== false) {
472 | 			try {
473 | 				result.mcpResult = removeTaskMasterMCPConfiguration(
474 | 					projectRoot,
475 | 					profile.mcpConfigPath
476 | 				);
477 | 				if (result.mcpResult.hasOtherServers) {
478 | 					if (!result.notice) {
479 | 						result.notice = 'Preserved other MCP server configurations';
480 | 					} else {
481 | 						result.notice += '; preserved other MCP server configurations';
482 | 					}
483 | 				}
484 | 				log(
485 | 					'debug',
486 | 					`[Rule Transformer] Processed MCP configuration for ${profile.profileName}`
487 | 				);
488 | 			} catch (error) {
489 | 				log(
490 | 					'error',
491 | 					`[Rule Transformer] MCP cleanup failed for ${profile.profileName}: ${error.message}`
492 | 				);
493 | 			}
494 | 		}
495 | 
496 | 		// 4. Check if we should remove the entire profile directory
497 | 		if (fs.existsSync(profileDir)) {
498 | 			const remainingContents = fs.readdirSync(profileDir);
499 | 			if (remainingContents.length === 0 && profile.profileDir !== '.') {
500 | 				// Only remove profile directory if it's empty and not root directory
501 | 				try {
502 | 					fs.rmSync(profileDir, { recursive: true, force: true });
503 | 					result.profileDirRemoved = true;
504 | 					log(
505 | 						'debug',
506 | 						`[Rule Transformer] Removed empty profile directory: ${profileDir}`
507 | 					);
508 | 				} catch (error) {
509 | 					log(
510 | 						'error',
511 | 						`[Rule Transformer] Failed to remove profile directory ${profileDir}: ${error.message}`
512 | 					);
513 | 				}
514 | 			} else if (remainingContents.length > 0) {
515 | 				// Profile directory has remaining files/folders, add notice
516 | 				const preservedNotice = `Preserved ${remainingContents.length} existing files/folders in ${profile.profileDir}`;
517 | 				if (!result.notice) {
518 | 					result.notice = preservedNotice;
519 | 				} else {
520 | 					result.notice += `; ${preservedNotice.toLowerCase()}`;
521 | 				}
522 | 				log('info', `[Rule Transformer] ${preservedNotice}`);
523 | 			}
524 | 		}
525 | 
526 | 		result.success = true;
527 | 		log(
528 | 			'debug',
529 | 			`[Rule Transformer] Successfully removed ${profile.profileName} Task Master files from ${projectRoot}`
530 | 		);
531 | 	} catch (error) {
532 | 		result.error = error.message;
533 | 		log(
534 | 			'error',
535 | 			`[Rule Transformer] Failed to remove ${profile.profileName} rules: ${error.message}`
536 | 		);
537 | 	}
538 | 
539 | 	return result;
540 | }
541 | 
```

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

```typescript
  1 | /**
  2 |  * @fileoverview Auth command using Commander's native class pattern
  3 |  * Extends Commander.Command for better integration with the framework
  4 |  */
  5 | 
  6 | import {
  7 | 	type AuthCredentials,
  8 | 	AuthManager,
  9 | 	AuthenticationError
 10 | } from '@tm/core';
 11 | import chalk from 'chalk';
 12 | import { Command } from 'commander';
 13 | import inquirer from 'inquirer';
 14 | import open from 'open';
 15 | import ora, { type Ora } from 'ora';
 16 | import { displayError } from '../utils/error-handler.js';
 17 | import * as ui from '../utils/ui.js';
 18 | import { ContextCommand } from './context.command.js';
 19 | 
 20 | /**
 21 |  * Result type from auth command
 22 |  */
 23 | export interface AuthResult {
 24 | 	success: boolean;
 25 | 	action: 'login' | 'logout' | 'status' | 'refresh';
 26 | 	credentials?: AuthCredentials;
 27 | 	message?: string;
 28 | }
 29 | 
 30 | /**
 31 |  * AuthCommand extending Commander's Command class
 32 |  * This is a thin presentation layer over @tm/core's AuthManager
 33 |  */
 34 | export class AuthCommand extends Command {
 35 | 	private authManager: AuthManager;
 36 | 	private lastResult?: AuthResult;
 37 | 
 38 | 	constructor(name?: string) {
 39 | 		super(name || 'auth');
 40 | 
 41 | 		// Initialize auth manager
 42 | 		this.authManager = AuthManager.getInstance();
 43 | 
 44 | 		// Configure the command with subcommands
 45 | 		this.description('Manage authentication with tryhamster.com');
 46 | 
 47 | 		// Add subcommands
 48 | 		this.addLoginCommand();
 49 | 		this.addLogoutCommand();
 50 | 		this.addStatusCommand();
 51 | 		this.addRefreshCommand();
 52 | 
 53 | 		// Default action shows help
 54 | 		this.action(() => {
 55 | 			this.help();
 56 | 		});
 57 | 	}
 58 | 
 59 | 	/**
 60 | 	 * Add login subcommand
 61 | 	 */
 62 | 	private addLoginCommand(): void {
 63 | 		this.command('login')
 64 | 			.description('Authenticate with tryhamster.com')
 65 | 			.argument(
 66 | 				'[token]',
 67 | 				'Authentication token (optional, for SSH/remote environments)'
 68 | 			)
 69 | 			.option('-y, --yes', 'Skip interactive prompts')
 70 | 			.addHelpText(
 71 | 				'after',
 72 | 				`
 73 | Examples:
 74 |   $ tm auth login         # Browser-based OAuth flow (interactive)
 75 |   $ tm auth login <token> # Token-based authentication
 76 |   $ tm auth login <token> -y # Non-interactive token auth (for scripts)
 77 | `
 78 | 			)
 79 | 			.action(async (token?: string, options?: { yes?: boolean }) => {
 80 | 				await this.executeLogin(token, options?.yes);
 81 | 			});
 82 | 	}
 83 | 
 84 | 	/**
 85 | 	 * Add logout subcommand
 86 | 	 */
 87 | 	private addLogoutCommand(): void {
 88 | 		this.command('logout')
 89 | 			.description('Logout and clear credentials')
 90 | 			.action(async () => {
 91 | 				await this.executeLogout();
 92 | 			});
 93 | 	}
 94 | 
 95 | 	/**
 96 | 	 * Add status subcommand
 97 | 	 */
 98 | 	private addStatusCommand(): void {
 99 | 		this.command('status')
100 | 			.description('Display authentication status')
101 | 			.action(async () => {
102 | 				await this.executeStatus();
103 | 			});
104 | 	}
105 | 
106 | 	/**
107 | 	 * Add refresh subcommand
108 | 	 */
109 | 	private addRefreshCommand(): void {
110 | 		this.command('refresh')
111 | 			.description('Refresh authentication token')
112 | 			.action(async () => {
113 | 				await this.executeRefresh();
114 | 			});
115 | 	}
116 | 
117 | 	/**
118 | 	 * Execute login command
119 | 	 */
120 | 	private async executeLogin(token?: string, yes?: boolean): Promise<void> {
121 | 		try {
122 | 			const result = token
123 | 				? await this.performTokenAuth(token, yes)
124 | 				: await this.performInteractiveAuth(yes);
125 | 			this.setLastResult(result);
126 | 
127 | 			if (!result.success) {
128 | 				process.exit(1);
129 | 			}
130 | 
131 | 			// Exit cleanly after successful authentication
132 | 			// Small delay to ensure all output is flushed
133 | 			setTimeout(() => {
134 | 				process.exit(0);
135 | 			}, 100);
136 | 		} catch (error: any) {
137 | 			displayError(error);
138 | 		}
139 | 	}
140 | 
141 | 	/**
142 | 	 * Execute logout command
143 | 	 */
144 | 	private async executeLogout(): Promise<void> {
145 | 		try {
146 | 			const result = await this.performLogout();
147 | 			this.setLastResult(result);
148 | 
149 | 			if (!result.success) {
150 | 				process.exit(1);
151 | 			}
152 | 		} catch (error: any) {
153 | 			displayError(error);
154 | 		}
155 | 	}
156 | 
157 | 	/**
158 | 	 * Execute status command
159 | 	 */
160 | 	private async executeStatus(): Promise<void> {
161 | 		try {
162 | 			const result = await this.displayStatus();
163 | 			this.setLastResult(result);
164 | 		} catch (error: any) {
165 | 			displayError(error);
166 | 		}
167 | 	}
168 | 
169 | 	/**
170 | 	 * Execute refresh command
171 | 	 */
172 | 	private async executeRefresh(): Promise<void> {
173 | 		try {
174 | 			const result = await this.refreshToken();
175 | 			this.setLastResult(result);
176 | 
177 | 			if (!result.success) {
178 | 				process.exit(1);
179 | 			}
180 | 		} catch (error: any) {
181 | 			displayError(error);
182 | 		}
183 | 	}
184 | 
185 | 	/**
186 | 	 * Display authentication status
187 | 	 */
188 | 	private async displayStatus(): Promise<AuthResult> {
189 | 		console.log(chalk.cyan('\n🔐 Authentication Status\n'));
190 | 
191 | 		// Check if user has valid session
192 | 		const hasSession = await this.authManager.hasValidSession();
193 | 
194 | 		if (hasSession) {
195 | 			// Get session from Supabase (has tokens and expiry)
196 | 			const session = await this.authManager.getSession();
197 | 
198 | 			// Get user context (has email, userId, org/brief selection)
199 | 			const context = this.authManager.getContext();
200 | 			const contextStore = this.authManager.getStoredContext();
201 | 
202 | 			console.log(chalk.green('✓ Authenticated'));
203 | 			console.log(chalk.gray(`  Email: ${contextStore?.email || 'N/A'}`));
204 | 			console.log(chalk.gray(`  User ID: ${contextStore?.userId || 'N/A'}`));
205 | 			console.log(chalk.gray(`  Token Type: standard`));
206 | 
207 | 			// Display expiration info
208 | 			if (session?.expires_at) {
209 | 				const expiresAt = new Date(session.expires_at * 1000);
210 | 				const now = new Date();
211 | 				const timeRemaining = expiresAt.getTime() - now.getTime();
212 | 				const hoursRemaining = Math.floor(timeRemaining / (1000 * 60 * 60));
213 | 				const minutesRemaining = Math.floor(timeRemaining / (1000 * 60));
214 | 
215 | 				if (timeRemaining > 0) {
216 | 					// Token is still valid
217 | 					if (hoursRemaining > 0) {
218 | 						console.log(
219 | 							chalk.gray(
220 | 								`  Expires at: ${expiresAt.toLocaleString()} (${hoursRemaining} hours remaining)`
221 | 							)
222 | 						);
223 | 					} else {
224 | 						console.log(
225 | 							chalk.gray(
226 | 								`  Expires at: ${expiresAt.toLocaleString()} (${minutesRemaining} minutes remaining)`
227 | 							)
228 | 						);
229 | 					}
230 | 				} else {
231 | 					// Token has expired
232 | 					console.log(
233 | 						chalk.yellow(`  Expired at: ${expiresAt.toLocaleString()}`)
234 | 					);
235 | 				}
236 | 			}
237 | 
238 | 			// Display context if available
239 | 			if (context) {
240 | 				console.log(chalk.gray('\n  Context:'));
241 | 				if (context.orgName) {
242 | 					console.log(chalk.gray(`    Organization: ${context.orgName}`));
243 | 				}
244 | 				if (context.briefName) {
245 | 					console.log(chalk.gray(`    Brief: ${context.briefName}`));
246 | 				}
247 | 			}
248 | 
249 | 			// Build credentials for backward compatibility
250 | 			const credentials = {
251 | 				token: session?.access_token || '',
252 | 				refreshToken: session?.refresh_token,
253 | 				userId: contextStore?.userId || '',
254 | 				email: contextStore?.email,
255 | 				expiresAt: session?.expires_at
256 | 					? new Date(session.expires_at * 1000).toISOString()
257 | 					: undefined,
258 | 				tokenType: 'standard' as const,
259 | 				savedAt: contextStore?.lastUpdated || new Date().toISOString(),
260 | 				selectedContext: context || undefined
261 | 			};
262 | 
263 | 			return {
264 | 				success: true,
265 | 				action: 'status',
266 | 				credentials,
267 | 				message: 'Authenticated'
268 | 			};
269 | 		} else {
270 | 			console.log(chalk.yellow('✗ Not authenticated'));
271 | 			console.log(
272 | 				chalk.gray('\n  Run "task-master auth login" to authenticate')
273 | 			);
274 | 
275 | 			return {
276 | 				success: false,
277 | 				action: 'status',
278 | 				message: 'Not authenticated'
279 | 			};
280 | 		}
281 | 	}
282 | 
283 | 	/**
284 | 	 * Perform logout
285 | 	 */
286 | 	private async performLogout(): Promise<AuthResult> {
287 | 		try {
288 | 			await this.authManager.logout();
289 | 			ui.displaySuccess('Successfully logged out');
290 | 
291 | 			return {
292 | 				success: true,
293 | 				action: 'logout',
294 | 				message: 'Successfully logged out'
295 | 			};
296 | 		} catch (error) {
297 | 			const message = `Failed to logout: ${(error as Error).message}`;
298 | 			ui.displayError(message);
299 | 
300 | 			return {
301 | 				success: false,
302 | 				action: 'logout',
303 | 				message
304 | 			};
305 | 		}
306 | 	}
307 | 
308 | 	/**
309 | 	 * Refresh authentication token
310 | 	 */
311 | 	private async refreshToken(): Promise<AuthResult> {
312 | 		const spinner = ora('Refreshing authentication token...').start();
313 | 
314 | 		try {
315 | 			const credentials = await this.authManager.refreshToken();
316 | 			spinner.succeed('Token refreshed successfully');
317 | 
318 | 			console.log(
319 | 				chalk.gray(
320 | 					`  New expiration: ${credentials.expiresAt ? new Date(credentials.expiresAt).toLocaleString() : 'Never'}`
321 | 				)
322 | 			);
323 | 
324 | 			return {
325 | 				success: true,
326 | 				action: 'refresh',
327 | 				credentials,
328 | 				message: 'Token refreshed successfully'
329 | 			};
330 | 		} catch (error) {
331 | 			spinner.fail('Failed to refresh token');
332 | 
333 | 			if ((error as AuthenticationError).code === 'NO_REFRESH_TOKEN') {
334 | 				ui.displayWarning(
335 | 					'No refresh token available. Please re-authenticate.'
336 | 				);
337 | 			} else {
338 | 				ui.displayError(`Refresh failed: ${(error as Error).message}`);
339 | 			}
340 | 
341 | 			return {
342 | 				success: false,
343 | 				action: 'refresh',
344 | 				message: `Failed to refresh: ${(error as Error).message}`
345 | 			};
346 | 		}
347 | 	}
348 | 
349 | 	/**
350 | 	 * Perform interactive authentication
351 | 	 */
352 | 	private async performInteractiveAuth(yes?: boolean): Promise<AuthResult> {
353 | 		ui.displayBanner('Task Master Authentication');
354 | 		const isAuthenticated = await this.authManager.hasValidSession();
355 | 
356 | 		// Check if already authenticated (skip if --yes is used)
357 | 		if (isAuthenticated && !yes) {
358 | 			const { continueAuth } = await inquirer.prompt([
359 | 				{
360 | 					type: 'confirm',
361 | 					name: 'continueAuth',
362 | 					message:
363 | 						'You are already authenticated. Do you want to re-authenticate?',
364 | 					default: false
365 | 				}
366 | 			]);
367 | 
368 | 			if (!continueAuth) {
369 | 				const credentials = await this.authManager.getAuthCredentials();
370 | 				ui.displaySuccess('Using existing authentication');
371 | 
372 | 				if (credentials) {
373 | 					console.log(chalk.gray(`  Email: ${credentials.email || 'N/A'}`));
374 | 					console.log(chalk.gray(`  User ID: ${credentials.userId}`));
375 | 				}
376 | 
377 | 				return {
378 | 					success: true,
379 | 					action: 'login',
380 | 					credentials: credentials || undefined,
381 | 					message: 'Using existing authentication'
382 | 				};
383 | 			}
384 | 		}
385 | 
386 | 		try {
387 | 			// Direct browser authentication - no menu needed
388 | 			const credentials = await this.authenticateWithBrowser();
389 | 
390 | 			ui.displaySuccess('Authentication successful!');
391 | 			console.log(
392 | 				chalk.gray(`  Logged in as: ${credentials.email || credentials.userId}`)
393 | 			);
394 | 
395 | 			// Post-auth: Set up workspace context (skip if --yes flag is used)
396 | 			if (!yes) {
397 | 				console.log(); // Add spacing
398 | 				try {
399 | 					const contextCommand = new ContextCommand();
400 | 					const contextResult = await contextCommand.setupContextInteractive();
401 | 					if (contextResult.success) {
402 | 						if (contextResult.orgSelected && contextResult.briefSelected) {
403 | 							console.log(
404 | 								chalk.green('✓ Workspace context configured successfully')
405 | 							);
406 | 						} else if (contextResult.orgSelected) {
407 | 							console.log(chalk.green('✓ Organization selected'));
408 | 						}
409 | 					} else {
410 | 						console.log(
411 | 							chalk.yellow('⚠️ Context setup was skipped or encountered issues')
412 | 						);
413 | 						console.log(
414 | 							chalk.gray('  You can set up context later with "tm context"')
415 | 						);
416 | 					}
417 | 				} catch (contextError) {
418 | 					console.log(chalk.yellow('⚠️ Context setup encountered an error'));
419 | 					console.log(
420 | 						chalk.gray('  You can set up context later with "tm context"')
421 | 					);
422 | 					if (process.env.DEBUG) {
423 | 						console.error(chalk.gray((contextError as Error).message));
424 | 					}
425 | 				}
426 | 			} else {
427 | 				console.log(
428 | 					chalk.gray(
429 | 						'\n  Skipped interactive setup. Use "tm context" to configure later.'
430 | 					)
431 | 				);
432 | 			}
433 | 
434 | 			return {
435 | 				success: true,
436 | 				action: 'login',
437 | 				credentials,
438 | 				message: 'Authentication successful'
439 | 			};
440 | 		} catch (error) {
441 | 			displayError(error, { skipExit: true });
442 | 
443 | 			return {
444 | 				success: false,
445 | 				action: 'login',
446 | 				message: `Authentication failed: ${(error as Error).message}`
447 | 			};
448 | 		}
449 | 	}
450 | 
451 | 	/**
452 | 	 * Authenticate with browser using OAuth 2.0 with PKCE
453 | 	 */
454 | 	private async authenticateWithBrowser(): Promise<AuthCredentials> {
455 | 		let authSpinner: Ora | null = null;
456 | 
457 | 		try {
458 | 			// Use AuthManager's new unified OAuth flow method with callbacks
459 | 			const credentials = await this.authManager.authenticateWithOAuth({
460 | 				// Callback to handle browser opening
461 | 				openBrowser: async (authUrl) => {
462 | 					await open(authUrl);
463 | 				},
464 | 				timeout: 5 * 60 * 1000, // 5 minutes
465 | 
466 | 				// Callback when auth URL is ready
467 | 				onAuthUrl: (authUrl) => {
468 | 					// Display authentication instructions
469 | 					console.log(chalk.blue.bold('\n🔐 Browser Authentication\n'));
470 | 					console.log(chalk.white('  Opening your browser to authenticate...'));
471 | 					console.log(chalk.gray("  If the browser doesn't open, visit:"));
472 | 					console.log(chalk.cyan.underline(`  ${authUrl}\n`));
473 | 				},
474 | 
475 | 				// Callback when waiting for authentication
476 | 				onWaitingForAuth: () => {
477 | 					authSpinner = ora({
478 | 						text: 'Waiting for authentication...',
479 | 						spinner: 'dots'
480 | 					}).start();
481 | 				},
482 | 
483 | 				// Callback on success
484 | 				onSuccess: () => {
485 | 					if (authSpinner) {
486 | 						authSpinner.succeed('Authentication successful!');
487 | 					}
488 | 				},
489 | 
490 | 				// Callback on error
491 | 				onError: () => {
492 | 					if (authSpinner) {
493 | 						authSpinner.fail('Authentication failed');
494 | 					}
495 | 				}
496 | 			});
497 | 
498 | 			return credentials;
499 | 		} catch (error) {
500 | 			throw error;
501 | 		}
502 | 	}
503 | 
504 | 	/**
505 | 	 * Authenticate with token
506 | 	 */
507 | 	private async authenticateWithToken(token: string): Promise<AuthCredentials> {
508 | 		const spinner = ora('Verifying authentication token...').start();
509 | 
510 | 		try {
511 | 			const credentials = await this.authManager.authenticateWithCode(token);
512 | 			spinner.succeed('Successfully authenticated!');
513 | 			return credentials;
514 | 		} catch (error) {
515 | 			spinner.fail('Authentication failed');
516 | 			throw error;
517 | 		}
518 | 	}
519 | 
520 | 	/**
521 | 	 * Perform token-based authentication flow
522 | 	 */
523 | 	private async performTokenAuth(
524 | 		token: string,
525 | 		yes?: boolean
526 | 	): Promise<AuthResult> {
527 | 		ui.displayBanner('Task Master Authentication');
528 | 
529 | 		try {
530 | 			// Authenticate with the token
531 | 			const credentials = await this.authenticateWithToken(token);
532 | 
533 | 			ui.displaySuccess('Authentication successful!');
534 | 			console.log(
535 | 				chalk.gray(`  Logged in as: ${credentials.email || credentials.userId}`)
536 | 			);
537 | 
538 | 			// Post-auth: Set up workspace context (skip if --yes flag is used)
539 | 			if (!yes) {
540 | 				console.log(); // Add spacing
541 | 				try {
542 | 					const contextCommand = new ContextCommand();
543 | 					const contextResult = await contextCommand.setupContextInteractive();
544 | 					if (contextResult.success) {
545 | 						if (contextResult.orgSelected && contextResult.briefSelected) {
546 | 							console.log(
547 | 								chalk.green('✓ Workspace context configured successfully')
548 | 							);
549 | 						} else if (contextResult.orgSelected) {
550 | 							console.log(chalk.green('✓ Organization selected'));
551 | 						}
552 | 					} else {
553 | 						console.log(
554 | 							chalk.yellow('⚠️ Context setup was skipped or encountered issues')
555 | 						);
556 | 						console.log(
557 | 							chalk.gray('  You can set up context later with "tm context"')
558 | 						);
559 | 					}
560 | 				} catch (contextError) {
561 | 					console.log(chalk.yellow('⚠️ Context setup encountered an error'));
562 | 					console.log(
563 | 						chalk.gray('  You can set up context later with "tm context"')
564 | 					);
565 | 					if (process.env.DEBUG) {
566 | 						console.error(chalk.gray((contextError as Error).message));
567 | 					}
568 | 				}
569 | 			} else {
570 | 				console.log(
571 | 					chalk.gray(
572 | 						'\n  Skipped interactive setup. Use "tm context" to configure later.'
573 | 					)
574 | 				);
575 | 			}
576 | 
577 | 			return {
578 | 				success: true,
579 | 				action: 'login',
580 | 				credentials,
581 | 				message: 'Authentication successful'
582 | 			};
583 | 		} catch (error) {
584 | 			displayError(error, { skipExit: true });
585 | 
586 | 			return {
587 | 				success: false,
588 | 				action: 'login',
589 | 				message: `Authentication failed: ${(error as Error).message}`
590 | 			};
591 | 		}
592 | 	}
593 | 
594 | 	/**
595 | 	 * Set the last result for programmatic access
596 | 	 */
597 | 	private setLastResult(result: AuthResult): void {
598 | 		this.lastResult = result;
599 | 	}
600 | 
601 | 	/**
602 | 	 * Get the last result (for programmatic usage)
603 | 	 */
604 | 	getLastResult(): AuthResult | undefined {
605 | 		return this.lastResult;
606 | 	}
607 | 
608 | 	/**
609 | 	 * Get current credentials (for programmatic usage)
610 | 	 */
611 | 	async getCredentials(): Promise<AuthCredentials | null> {
612 | 		return this.authManager.getAuthCredentials();
613 | 	}
614 | 
615 | 	/**
616 | 	 * Clean up resources
617 | 	 */
618 | 	async cleanup(): Promise<void> {
619 | 		// No resources to clean up for auth command
620 | 		// But keeping method for consistency with other commands
621 | 	}
622 | 
623 | 	/**
624 | 	 * Register this command on an existing program
625 | 	 */
626 | 	static register(program: Command, name?: string): AuthCommand {
627 | 		const authCommand = new AuthCommand(name);
628 | 		program.addCommand(authCommand);
629 | 		return authCommand;
630 | 	}
631 | }
632 | 
```

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

```markdown
  1 | # MCP Provider Integration Guide
  2 | 
  3 | ## Overview
  4 | 
  5 | Task Master provides a **unified MCP provider** for AI operations:
  6 | 
  7 | **MCP Provider** (`mcp`) - Modern AI SDK-compatible provider with full structured object generation support
  8 | 
  9 | The MCP provider enables Task Master to act as an MCP client, using MCP servers as AI providers alongside traditional API-based providers. This integration follows the existing provider pattern and supports all standard AI operations including structured object generation for PRD parsing and task creation.
 10 | 
 11 | ## MCP Provider Features
 12 | 
 13 | The **MCP Provider** (`mcp`) provides:
 14 | 
 15 | ✅ **Full AI SDK Compatibility** - Complete LanguageModelV1 interface implementation  
 16 | ✅ **Structured Object Generation** - Schema-driven outputs for PRD parsing and task creation  
 17 | ✅ **Enhanced Error Handling** - Robust JSON extraction and validation  
 18 | ✅ **Session Management** - Automatic session detection and context handling  
 19 | ✅ **Schema Validation** - Type-safe object generation with Zod validation  
 20 | 
 21 | ### Quick Setup
 22 | 
 23 | ```bash
 24 | # Set MCP provider for main role  
 25 | task-master models set-main --provider mcp --model claude-3-5-sonnet-20241022
 26 | ```
 27 | 
 28 | For detailed information, see [MCP Provider Documentation](mcp-provider.md).
 29 | 
 30 | ## What is MCP Provider?
 31 | 
 32 | The MCP provider allows Task Master to:
 33 | - Connect to MCP servers/tools as AI providers
 34 | - Use session-based authentication instead of API keys
 35 | - Map AI operations to MCP tool calls
 36 | - Integrate with existing role-based provider assignment
 37 | - Maintain compatibility with fallback chains
 38 | - Support structured object generation for schema-driven features
 39 | 
 40 | ## Configuration
 41 | 
 42 | ### MCP Provider Setup
 43 | 
 44 | Add MCP provider to your `.taskmaster/config.json`:
 45 | 
 46 | ```json
 47 | {
 48 |   "models": {
 49 |     "main": {
 50 |       "provider": "mcp",
 51 |       "modelId": "claude-3-5-sonnet-20241022",
 52 |       "maxTokens": 50000,
 53 |       "temperature": 0.2
 54 |     },
 55 |     "research": {
 56 |       "provider": "mcp", 
 57 |       "modelId": "claude-3-5-sonnet-20241022",
 58 |       "maxTokens": 8700,
 59 |       "temperature": 0.1
 60 |     },
 61 |     "fallback": {
 62 |       "provider": "anthropic",
 63 |       "modelId": "claude-3-5-sonnet-20241022"
 64 |     }
 65 |   }
 66 | }
 67 | ```
 68 | 
 69 | ### Available Models
 70 | 
 71 | **MCP Provider Models:**
 72 | 
 73 | - **`claude-3-5-sonnet-20241022`** - High-performance model for general tasks
 74 |   - **SWE Score**: 0.49
 75 |   - **Features**: Text + Object generation
 76 | 
 77 | - **`claude-3-opus-20240229`** - Enhanced reasoning model for complex tasks  
 78 |   - **SWE Score**: 0.725
 79 |   - **Features**: Text + Object generation
 80 | 
 81 | - **`mcp-sampling`** - General text generation using MCP client sampling
 82 |   - **SWE Score**: null
 83 |   - **Roles**: Supports main, research, and fallback roles
 84 |   - **SWE Score**: 0.49
 85 |   - **Cost**: $0 (session-based)
 86 |   - **Max Tokens**: 200,000
 87 |   - **Supported Roles**: main, research, fallback
 88 |   - **Features**: Text + Object generation
 89 | 
 90 | - **`claude-3-opus-20240229`** - Enhanced reasoning model for complex tasks  
 91 |   - **SWE Score**: 0.725
 92 |   - **Cost**: $0 (session-based)
 93 |   - **Max Tokens**: 200,000
 94 |   - **Supported Roles**: main, research, fallback
 95 |   - **Features**: Text + Object generation
 96 | 
 97 | **Basic MCP Provider Models:**
 98 | 
 99 | - **`mcp-sampling`** - General text generation using MCP client sampling
100 | - **`mcp-sampling`** - General text generation using MCP client sampling
101 |   - **SWE Score**: null
102 |   - **Roles**: Supports main, research, and fallback roles
103 | 
104 | ### Model ID Format
105 | 
106 | MCP model IDs use a simple format:
107 | 
108 | - **`claude-3-5-sonnet-20241022`** - Uses Claude 3.5 Sonnet via MCP sampling
109 | - **`claude-3-opus-20240229`** - Uses Claude 3 Opus via MCP sampling  
110 | - **`mcp-sampling`** - Uses MCP client's sampling capability for text generation
111 | 
112 | ## Session Requirements
113 | 
114 | The MCP provider requires an active MCP session with sampling capabilities:
115 | 
116 | ```javascript
117 | session: {
118 |   clientCapabilities: {
119 |     sampling: {} // Client supports sampling requests
120 |   }
121 | }
122 | ```
123 | 
124 | ## Usage Examples
125 | 
126 | ### Basic Text Generation
127 | 
128 | ```javascript
129 | import { generateTextService } from './scripts/modules/ai-services-unified.js';
130 | 
131 | const result = await generateTextService({
132 |   role: 'main',
133 |   session: mcpSession, // Required for MCP provider
134 |   prompt: 'Explain MCP integration',
135 |   systemPrompt: 'You are a helpful AI assistant'
136 | });
137 | 
138 | console.log(result.text);
139 | ```
140 | 
141 | ### Structured Object Generation
142 | 
143 | ```javascript
144 | import { generateObjectService } from './scripts/modules/ai-services-unified.js';
145 | 
146 | const result = await generateObjectService({
147 |   role: 'main',
148 |   session: mcpSession,
149 |   prompt: 'Create a task breakdown',
150 |   schema: {
151 |     type: 'object',
152 |     properties: {
153 |       tasks: {
154 |         type: 'array',
155 |         items: { type: 'string' }
156 |       }
157 |     }
158 |   }
159 | });
160 | 
161 | console.log(result.object);
162 | ```
163 | 
164 | ### Research Operations
165 | 
166 | ```javascript
167 | const research = await generateTextService({
168 |   role: 'research',
169 |   session: mcpSession,
170 |   prompt: 'Research the latest developments in AI',
171 |   systemPrompt: 'You are a research assistant'
172 | });
173 | ```
174 | 
175 | ## CLI Integration
176 | 
177 | The MCP provider works seamlessly with Task Master CLI commands when running in an MCP context:
178 | 
179 | ```bash
180 | # Generate tasks using MCP provider (if configured as main)
181 | task-master add-task "Implement user authentication"
182 | 
183 | # Research using MCP provider (if configured as research)
184 | task-master research "OAuth 2.0 best practices"
185 | 
186 | # Parse PRD using MCP provider
187 | task-master parse-prd requirements.txt
188 | ```
189 | 
190 | ## Architecture Details
191 | 
192 | ### Provider Architecture
193 | **MCPProvider** (`mcp-server/src/providers/mcp-provider.js`)
194 |    - Modern AI SDK-compliant provider for Task Master's MCP server
195 |    - Auto-registers when MCP sessions connect to Task Master
196 |    - Enables Task Master to use MCP sessions for AI operations
197 |    - Supports both text generation and structured object generation
198 | 
199 | ### Auto-Registration Process
200 | 
201 | When running as an MCP server, Task Master automatically:
202 | 
203 | ```javascript
204 | // On MCP session connect
205 | server.on("connect", (event) => {
206 |   // Check session capabilities
207 |   if (session.clientCapabilities?.sampling) {
208 |     // Create and register MCP provider
209 |     const mcpProvider = new MCPProvider();
210 |     mcpProvider.setSession(session);
211 |     
212 |     // Auto-register with provider registry
213 |     providerRegistry.registerProvider('mcp', mcpProvider);
214 |   }
215 | });
216 | ```
217 | 
218 | This enables seamless self-referential AI operations within MCP contexts.
219 | 
220 | ### Provider Pattern Integration
221 | 
222 | The MCP provider follows the same pattern as other providers:
223 | 
224 | ```javascript
225 | class MCPProvider extends BaseAIProvider {
226 |   // Implements generateText, generateObject
227 |   // Uses session context instead of API keys
228 |   // Maps operations to MCP sampling requests
229 | }
230 | ```
231 | 
232 | ### Session Detection
233 | 
234 | The provider automatically detects MCP sampling capability when sessions connect:
235 | 
236 | ```javascript
237 | // On MCP session connect
238 | if (session.clientCapabilities?.sampling) {
239 |   // Auto-register MCP provider for use
240 |   const mcpProvider = new MCPProvider();
241 |   mcpProvider.setSession(session);
242 | }
243 | ```
244 | 
245 | ### Sampling Integration
246 | 
247 | AI operations use MCP sampling with different levels of support:
248 | 
249 | - `generateText()` → MCP `requestSampling()` with messages (2-minute timeout) ✅ **Full Support**
250 | - `streamText()` → **Limited/No Support** ⚠️ See streaming limitations below
251 | - `generateObject()` → MCP `requestSampling()` with JSON schema instructions (2-minute timeout) ✅ **Full Support**
252 | 
253 | **Timeout Configuration**: All MCP sampling requests use a 2-minute (120,000ms) timeout to accommodate complex AI operations.
254 | 
255 | #### Streaming Text Limitations ⚠️
256 | 
257 | **Important**: The MCP provider has **no support** for text streaming:
258 | 
259 | **MCPProvider**:
260 | - **❌ No Streaming Support**: Throws error "MCP Provider does not support streaming text, use generateText instead"  
261 | - **Solution**: Always use `generateText()` instead of `streamText()` with this provider
262 | 
263 | **Recommendation**: For streaming functionality, configure a non-MCP fallback provider (like Anthropic or OpenAI) in your fallback role.
264 | 
265 | ### Error Handling
266 | 
267 | The MCP provider includes comprehensive error handling:
268 | 
269 | - Session validation errors (checks for `clientCapabilities.sampling`)
270 | - MCP sampling request failures
271 | - JSON parsing errors (for structured output)
272 | - Automatic fallback to other providers
273 | 
274 | ### Best Practices
275 | 
276 | ### 1. Configure Fallbacks
277 | 
278 | Always configure a non-MCP fallback provider, especially for streaming operations:
279 | 
280 | ```json
281 | {
282 |   "models": {
283 |     "main": {
284 |       "provider": "mcp",
285 |       "modelId": "mcp-sampling"
286 |     },
287 |     "fallback": {
288 |       "provider": "anthropic",
289 |       "modelId": "claude-3-5-sonnet-20241022"
290 |     }
291 |   }
292 | }
293 | ```
294 | 
295 | ### 2. Avoid Streaming with MCP
296 | 
297 | **Do not use `streamTextService()` with MCP provider**. Use `generateTextService()` instead:
298 | 
299 | ```javascript
300 | // ❌ Don't do this with MCP provider
301 | const result = await streamTextService({
302 |   role: 'main', // MCP provider
303 |   session: mcpSession,
304 |   prompt: 'Generate content'
305 | });
306 | 
307 | // ✅ Do this instead
308 | const result = await generateTextService({
309 |   role: 'main', // MCP provider
310 |   session: mcpSession,
311 |   prompt: 'Generate content'
312 | });
313 | ```
314 | 
315 | ### 3. Session Management
316 | 
317 | Ensure your MCP session remains active throughout Task Master operations:
318 | 
319 | ```javascript
320 | // Check session health before operations
321 | if (!session || !session.capabilities) {
322 |   throw new Error('MCP session not available');
323 | }
324 | ```
325 | 
326 | ### 4. Tool Availability
327 | 
328 | Verify required capabilities are available in your MCP session:
329 | 
330 | ```javascript
331 | // Check session health and capabilities
332 | if (session && session.clientCapabilities && session.clientCapabilities.sampling) {
333 |   console.log('MCP sampling available');
334 | } else {
335 |   console.log('MCP sampling not available');
336 | }
337 | ```
338 | 
339 | ### 5. Error Recovery
340 | 
341 | Handle MCP-specific errors gracefully:
342 | 
343 | ```javascript
344 | try {
345 |   const result = await generateTextService({
346 |     role: 'main',
347 |     session: mcpSession,
348 |     prompt: 'Generate content'
349 |   });
350 | } catch (error) {
351 |   if (error.message.includes('MCP')) {
352 |     // Handle MCP-specific error
353 |     console.log('MCP error, falling back to alternate provider');
354 |   }
355 | }
356 | ```
357 | 
358 | ## Troubleshooting
359 | 
360 | ### Common Issues
361 | 
362 | 1. **"MCP provider requires session context"**
363 |    - Ensure `session` parameter is passed to service calls
364 |    - Verify session has proper structure
365 |    - Check that you're running in an MCP environment
366 | 
367 | 2. **"MCP session must have client sampling capabilities"**
368 |    - Check that `session.clientCapabilities.sampling` exists
369 |    - Verify session has `requestSampling()` method
370 |    - Ensure MCP client supports sampling feature
371 | 
372 | 3. **"MCP Provider does not support streaming text, use generateText instead"**
373 |    - **Common Error**: Occurs when calling `streamTextService()` with MCP provider
374 |    - **Solution**: Use `generateTextService()` instead of `streamTextService()`
375 |    - **Alternative**: Configure a non-MCP fallback provider for streaming operations
376 | 
377 | 4. **"MCP sampling failed"** or **Timeout errors**
378 |    - Check MCP client is responding to sampling requests
379 |    - Verify session is still active and connected
380 |    - Consider if request complexity requires longer processing time
381 |    - Check for network connectivity issues
382 | 
383 | 5. **"Model ID is required for MCP Remote Provider"**
384 |    - Ensure `modelId` is specified in configuration
385 |    - Use `mcp-sampling` as the standard model ID
386 |    - Verify provider configuration is properly loaded
387 | 
388 | 6. **Auto-registration failures**
389 |    - Check that MCP session has required sampling capabilities
390 |    - Verify server event listeners are properly configured
391 |    - Look for provider registry initialization issues
392 | 
393 | ### Streaming-Related Issues
394 | 
395 | **Error**: `streamTextService()` calls fail with MCP provider
396 | **Cause**: MCP provider has no streaming support
397 | **Solutions**:
398 | - Use `generateTextService()` for all MCP-based text generation
399 | - Configure non-MCP fallback providers for streaming requirements
400 | - Check your provider configuration to ensure fallback chain includes streaming-capable providers
401 | 
402 | ### Debug Mode
403 | 
404 | Enable debug logging to see MCP provider operations:
405 | 
406 | ```javascript
407 | // Set debug flag in config or environment
408 | process.env.DEBUG = 'true';
409 | 
410 | // Or in .taskmasterconfig
411 | {
412 |   "debug": true,
413 |   "models": { /* ... */ }
414 | }
415 | ```
416 | 
417 | ### Testing MCP Integration
418 | 
419 | Test MCP provider functionality:
420 | 
421 | ```javascript
422 | // Check if MCP provider is properly registered
423 | import { MCPProvider } from './mcp-server/src/providers/mcp-provider.js';
424 | 
425 | // Test session capabilities
426 | if (session && session.clientCapabilities && session.clientCapabilities.sampling) {
427 |   console.log('MCP sampling available');
428 |   
429 |   // Test provider creation
430 |   const provider = new MCPProvider();
431 |   provider.setSession(session);
432 |   console.log('MCP provider created successfully');
433 | } else {
434 |   console.log('MCP session lacks required capabilities');
435 | }
436 | ```
437 | 
438 | ## Integration with Development Tools
439 | 
440 | ### VS Code with MCP Extension
441 | 
442 | When using Task Master in VS Code with MCP support:
443 | 
444 | 1. Configure Task Master MCP server in your `.vscode/mcp.json`
445 | 2. Set MCP provider as main/research in `.taskmaster/config.json`
446 | 3. Benefit from integrated AI assistance within your development workflow
447 | 4. Use Task Master tools directly from VS Code's MCP interface
448 | 
449 | **Example VS Code MCP Configuration:**
450 | ```json
451 | {
452 |   "servers": {
453 |     "task-master-dev": {
454 |       "command": "npx",
455 |       "args": ["-y", "task-master-ai"],
456 |       "cwd": "/path/to/your/task-master-project",
457 |       "env": {
458 |         "NODE_ENV": "development",
459 |         "ANTHROPIC_API_KEY": "${env:ANTHROPIC_API_KEY}",
460 |         "TASK_MASTER_PROJECT_ROOT": "/path/to/your/project"
461 |       }
462 |     }
463 |   }
464 | }
465 | ```
466 | 
467 | ### Claude Desktop
468 | 
469 | When using Task Master through Claude Desktop's MCP integration:
470 | 
471 | 1. Configure Task Master as MCP provider in Claude Desktop
472 | 2. Use MCP provider for AI operations within Task Master
473 | 3. Benefit from nested MCP tool calling capabilities
474 | 
475 | ### Cursor and Other MCP Clients
476 | 
477 | The MCP provider works with any MCP-compatible development environment:
478 | 
479 | 1. Ensure your IDE has MCP client capabilities
480 | 2. Configure Task Master MCP server endpoint
481 | 3. Use MCP provider for enhanced AI-driven development
482 | 
483 | ## Advanced Configuration
484 | 
485 | ### Custom Tool Mapping
486 | 
487 | Advanced users can use MCP sampling for all roles:
488 | 
489 | ```javascript
490 | // MCP sampling for all roles
491 | {
492 |   "models": {
493 |     "main": {
494 |       "provider": "mcp",
495 |       "modelId": "mcp-sampling"
496 |     }
497 |   }
498 | }
499 | ```
500 | 
501 | ### Role-Specific Configuration
502 | 
503 | Configure MCP sampling for different roles:
504 | 
505 | ```json
506 | {
507 |   "models": {
508 |     "main": {
509 |       "provider": "mcp",
510 |       "modelId": "mcp-sampling"
511 |     },
512 |     "research": {
513 |       "provider": "mcp", 
514 |       "modelId": "mcp-sampling"
515 |     },
516 |     "fallback": {
517 |       "provider": "mcp",
518 |       "modelId": "backup-server:simple-generation"
519 |     }
520 |   }
521 | }
522 | ```
523 | 
524 | ### API Reference
525 | 
526 | ### MCPProvider Methods
527 | 
528 | - `generateText(params)` - Generate text using MCP sampling ✅ **Supported**
529 | - `streamText(params)` - Stream text ❌ **Not supported** (throws error)
530 | - `generateObject(params)` - Generate structured objects ✅ **Supported**
531 | - `setSession(session)` - Update provider session
532 | - `validateAuth(params)` - Validate session capabilities
533 | - `getClient()` - Returns null (not applicable for MCP)
534 | 
535 | ### Required Parameters
536 | 
537 | All MCP operations require:
538 | - `session` - Active MCP session object (auto-provided when registered)
539 | - `modelId` - MCP model identifier (typically "mcp-sampling")
540 | - `messages` - Array of message objects
541 | 
542 | ### Optional Parameters
543 | 
544 | - `temperature` - Creativity control (if supported by MCP client)
545 | - `maxTokens` - Maximum response length (if supported)
546 | - `schema` - JSON schema for structured output (generateObject only)
547 | 
548 | ## Security Considerations
549 | 
550 | 1. **Session Security**: MCP sessions should be properly authenticated
551 | 2. **Server Validation**: Only connect to trusted MCP servers
552 | 3. **Data Privacy**: Ensure MCP clients handle data according to your privacy requirements
553 | 4. **Error Exposure**: Be careful not to expose sensitive session information in error messages
554 | 
555 | ## Future Enhancements
556 | 
557 | Planned improvements for MCP provider:
558 | 
559 | 1. **Native Streaming Support** - True streaming for compatible MCP clients (requires MCP protocol updates)
560 | 2. **Enhanced Session Monitoring** - Automatic session validation and recovery
561 | 3. **Performance Optimization** - Caching and connection pooling
562 | 4. **Advanced Error Recovery** - Intelligent retry and fallback strategies
563 | 
564 | **Note**: True streaming support depends on future MCP protocol enhancements. Current implementation provides text generation without streaming capabilities.
565 | 
```

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

```json
  1 | {
  2 | 	"meta": {
  3 | 		"generatedAt": "2025-10-07T09:46:06.248Z",
  4 | 		"tasksAnalyzed": 23,
  5 | 		"totalTasks": 23,
  6 | 		"analysisCount": 23,
  7 | 		"thresholdScore": 5,
  8 | 		"projectName": "Taskmaster",
  9 | 		"usedResearch": false
 10 | 	},
 11 | 	"complexityAnalysis": [
 12 | 		{
 13 | 			"taskId": 31,
 14 | 			"taskTitle": "Create WorkflowOrchestrator service foundation",
 15 | 			"complexityScore": 7,
 16 | 			"recommendedSubtasks": 5,
 17 | 			"expansionPrompt": "Break down the WorkflowOrchestrator foundation into its core architectural components: phase management system, event emitter infrastructure, state management interfaces, service integration, and lifecycle control methods. Each subtask should focus on a specific architectural concern with clear interfaces and testable units.",
 18 | 			"reasoning": "This is a foundational service requiring state machine implementation, event-driven architecture, and integration with existing services. The complexity is high due to the need for robust phase management, error handling, and service orchestration patterns."
 19 | 		},
 20 | 		{
 21 | 			"taskId": 32,
 22 | 			"taskTitle": "Implement GitAdapter for repository operations",
 23 | 			"complexityScore": 6,
 24 | 			"recommendedSubtasks": 4,
 25 | 			"expansionPrompt": "Decompose the GitAdapter implementation into: TypeScript wrapper creation around existing git-utils.js, core git operation methods with comprehensive error handling, branch naming pattern system with token replacement, and confirmation gates for destructive operations. Focus on type safety and existing code integration.",
 26 | 			"reasoning": "Moderate-high complexity due to TypeScript integration over existing JavaScript utilities, branch pattern implementation, and safety mechanisms. The existing git-utils.js provides a solid foundation, reducing complexity."
 27 | 		},
 28 | 		{
 29 | 			"taskId": 33,
 30 | 			"taskTitle": "Create TestRunnerAdapter for framework detection and execution",
 31 | 			"complexityScore": 8,
 32 | 			"recommendedSubtasks": 6,
 33 | 			"expansionPrompt": "Break down TestRunnerAdapter into framework detection logic, test execution engine with process management, Jest-specific result parsing, Vitest-specific result parsing, unified result interfaces, and final integration. Each framework parser should be separate to handle their unique output formats.",
 34 | 			"reasoning": "High complexity due to multiple framework support (Jest, Vitest), child process management, result parsing from different formats, coverage reporting, and timeout handling. Each framework has unique output formats requiring specialized parsers."
 35 | 		},
 36 | 		{
 37 | 			"taskId": 34,
 38 | 			"taskTitle": "Implement autopilot CLI command structure",
 39 | 			"complexityScore": 5,
 40 | 			"recommendedSubtasks": 4,
 41 | 			"expansionPrompt": "Structure the autopilot command into: basic command setup with Commander.js integration, comprehensive flag handling and validation system, preflight check validation with environment validation, and WorkflowOrchestrator integration with dry-run execution planning. Follow existing CLI patterns from the codebase.",
 42 | 			"reasoning": "Moderate complexity involving CLI structure, flag handling, and integration with WorkflowOrchestrator. The existing CLI patterns and Commander.js usage in the codebase provide good guidance, reducing implementation complexity."
 43 | 		},
 44 | 		{
 45 | 			"taskId": 35,
 46 | 			"taskTitle": "Integrate surgical test generator with WorkflowOrchestrator",
 47 | 			"complexityScore": 6,
 48 | 			"recommendedSubtasks": 4,
 49 | 			"expansionPrompt": "Decompose the test generation integration into: TaskExecutionService enhancement for test generation mode, TestGenerationService creation using executor framework, prompt composition system for rule integration, and framework-specific test pattern support. Leverage existing executor patterns from the codebase.",
 50 | 			"reasoning": "Moderate-high complexity due to integration with existing services, prompt composition system, and framework-specific test generation. The existing executor framework and TaskExecutionService provide good integration points."
 51 | 		},
 52 | 		{
 53 | 			"taskId": 36,
 54 | 			"taskTitle": "Implement subtask TDD loop execution",
 55 | 			"complexityScore": 9,
 56 | 			"recommendedSubtasks": 7,
 57 | 			"expansionPrompt": "Break down the TDD loop into: SubtaskExecutor class architecture, RED phase test generation, GREEN phase code generation, COMMIT phase with conventional commits, retry mechanism for GREEN phase, timeout and backoff policies, and TaskService integration. Each phase should be independently testable.",
 58 | 			"reasoning": "Very high complexity due to implementing the complete TDD red-green-commit cycle with AI integration, retry logic, timeout handling, and git operations. This is the core autonomous workflow requiring robust error handling and state management."
 59 | 		},
 60 | 		{
 61 | 			"taskId": 37,
 62 | 			"taskTitle": "Add configuration schema for autopilot settings",
 63 | 			"complexityScore": 4,
 64 | 			"recommendedSubtasks": 3,
 65 | 			"expansionPrompt": "Expand configuration support into: extending configuration interfaces with autopilot settings, updating ConfigManager validation logic, and implementing default configuration values. Build on existing configuration patterns and maintain backward compatibility.",
 66 | 			"reasoning": "Low-moderate complexity involving schema extension and validation logic. The existing configuration system provides clear patterns to follow, making this primarily an extension task rather than new architecture."
 67 | 		},
 68 | 		{
 69 | 			"taskId": 38,
 70 | 			"taskTitle": "Implement run state persistence and logging",
 71 | 			"complexityScore": 6,
 72 | 			"recommendedSubtasks": 5,
 73 | 			"expansionPrompt": "Structure run state management into: RunStateManager service class creation, run directory structure and manifest creation, JSONL event logging system, test result and commit tracking storage, and state checkpointing with resume functionality. Focus on data integrity and structured logging.",
 74 | 			"reasoning": "Moderate-high complexity due to file system operations, structured logging, state serialization, and resume functionality. Requires careful design of data formats and error handling for persistence operations."
 75 | 		},
 76 | 		{
 77 | 			"taskId": 39,
 78 | 			"taskTitle": "Add GitHub PR creation with run reports",
 79 | 			"complexityScore": 5,
 80 | 			"recommendedSubtasks": 4,
 81 | 			"expansionPrompt": "Decompose PR creation into: PRAdapter service foundation with interfaces, GitHub CLI integration and command execution, PR body generation from run data and test results, and custom PR template system with configuration support. Leverage existing git-utils.js patterns for CLI integration.",
 82 | 			"reasoning": "Moderate complexity involving GitHub CLI integration, report generation, and template systems. The existing git-utils.js provides patterns for CLI tool integration, reducing implementation complexity."
 83 | 		},
 84 | 		{
 85 | 			"taskId": 40,
 86 | 			"taskTitle": "Implement task dependency resolution for subtask ordering",
 87 | 			"complexityScore": 6,
 88 | 			"recommendedSubtasks": 4,
 89 | 			"expansionPrompt": "Break down dependency resolution into: dependency resolution algorithm with cycle detection, topological sorting for subtask ordering, task eligibility checking system, and TaskService integration. Implement graph algorithms for dependency management with proper error handling.",
 90 | 			"reasoning": "Moderate-high complexity due to graph algorithm implementation, cycle detection, and integration with existing task management. Requires careful design of dependency resolution logic and edge case handling."
 91 | 		},
 92 | 		{
 93 | 			"taskId": 41,
 94 | 			"taskTitle": "Create resume functionality for interrupted runs",
 95 | 			"complexityScore": 7,
 96 | 			"recommendedSubtasks": 5,
 97 | 			"expansionPrompt": "Structure resume functionality into: checkpoint creation in RunStateManager, state restoration logic with validation, state validation for safe resume operations, CLI flag implementation for resume command, and partial phase resume functionality. Focus on data integrity and workflow consistency.",
 98 | 			"reasoning": "High complexity due to state serialization/deserialization, workflow restoration, validation logic, and CLI integration. Requires robust error handling and state consistency checks for reliable resume operations."
 99 | 		},
100 | 		{
101 | 			"taskId": 42,
102 | 			"taskTitle": "Add coverage threshold enforcement",
103 | 			"complexityScore": 5,
104 | 			"recommendedSubtasks": 4,
105 | 			"expansionPrompt": "Decompose coverage enforcement into: coverage report parsing from Jest/Vitest, configurable threshold validation logic, coverage gates integration in workflow phases, and detailed coverage failure reporting system. Build on existing TestRunnerAdapter patterns.",
106 | 			"reasoning": "Moderate complexity involving coverage report parsing, validation logic, and workflow integration. The existing TestRunnerAdapter provides good foundation for extending coverage capabilities."
107 | 		},
108 | 		{
109 | 			"taskId": 43,
110 | 			"taskTitle": "Implement tmux-based TUI navigator",
111 | 			"complexityScore": 8,
112 | 			"recommendedSubtasks": 6,
113 | 			"expansionPrompt": "Break down TUI implementation into: framework selection and basic structure setup, left pane interface layout with status indicators, tmux integration and terminal coordination, navigation system with keybindings, real-time status updates system, and comprehensive event handling with UX polish. Each component should be independently testable.",
114 | 			"reasoning": "High complexity due to terminal UI framework integration, tmux session management, real-time updates, keyboard event handling, and terminal interface design. Requires expertise in terminal UI libraries and tmux integration."
115 | 		},
116 | 		{
117 | 			"taskId": 44,
118 | 			"taskTitle": "Add prompt composition system for context-aware test generation",
119 | 			"complexityScore": 6,
120 | 			"recommendedSubtasks": 4,
121 | 			"expansionPrompt": "Structure prompt composition into: PromptComposer service foundation, template processing engine with token replacement, rule loading system with precedence handling, and context injection with phase-specific prompt generation. Focus on flexible template system and rule management.",
122 | 			"reasoning": "Moderate-high complexity due to template processing, rule precedence systems, and context injection logic. Requires careful design of template syntax and rule loading mechanisms."
123 | 		},
124 | 		{
125 | 			"taskId": 45,
126 | 			"taskTitle": "Implement tag-branch mapping and automatic tag switching",
127 | 			"complexityScore": 5,
128 | 			"recommendedSubtasks": 3,
129 | 			"expansionPrompt": "Decompose tag-branch mapping into: GitAdapter enhancement with branch-to-tag extraction logic, automatic tag switching workflow integration, and branch-to-tag mapping persistence with validation. Build on existing git-utils.js and tag management functionality.",
130 | 			"reasoning": "Moderate complexity involving pattern matching, tag management integration, and workflow automation. The existing git-utils.js and tag management systems provide good foundation for implementation."
131 | 		},
132 | 		{
133 | 			"taskId": 46,
134 | 			"taskTitle": "Add comprehensive error handling and recovery",
135 | 			"complexityScore": 7,
136 | 			"recommendedSubtasks": 5,
137 | 			"expansionPrompt": "Structure error handling into: error classification system with specific error types, recovery suggestion engine with actionable recommendations, error context management and preservation, force flag implementation with selective bypass, and logging/reporting system integration. Focus on actionable error messages and automated recovery where possible.",
138 | 			"reasoning": "High complexity due to comprehensive error taxonomy, recovery automation, context preservation, and integration across all workflow components. Requires deep understanding of failure modes and recovery strategies."
139 | 		},
140 | 		{
141 | 			"taskId": 47,
142 | 			"taskTitle": "Implement conventional commit message generation",
143 | 			"complexityScore": 4,
144 | 			"recommendedSubtasks": 3,
145 | 			"expansionPrompt": "Break down commit message generation into: template system creation with variable substitution, commit type auto-detection based on task content and file changes, and validation with GitAdapter integration. Follow conventional commit standards and integrate with existing git operations.",
146 | 			"reasoning": "Low-moderate complexity involving template processing, pattern matching for commit type detection, and validation logic. Well-defined conventional commit standards provide clear implementation guidance."
147 | 		},
148 | 		{
149 | 			"taskId": 48,
150 | 			"taskTitle": "Add multi-framework test execution support",
151 | 			"complexityScore": 7,
152 | 			"recommendedSubtasks": 5,
153 | 			"expansionPrompt": "Expand test framework support into: framework detection system for multiple languages, common adapter interface design, Python pytest adapter implementation, Go and Rust adapter implementations, and integration with existing TestRunnerAdapter. Each language adapter should follow the unified interface pattern.",
154 | 			"reasoning": "High complexity due to multi-language support, framework detection across different ecosystems, and adapter pattern implementation. Each language has unique testing conventions and output formats."
155 | 		},
156 | 		{
157 | 			"taskId": 49,
158 | 			"taskTitle": "Implement workflow event streaming for real-time monitoring",
159 | 			"complexityScore": 6,
160 | 			"recommendedSubtasks": 4,
161 | 			"expansionPrompt": "Structure event streaming into: WorkflowOrchestrator EventEmitter enhancement, structured event format with metadata, event persistence to run logs, and optional WebSocket streaming for external monitoring. Focus on event consistency and real-time delivery.",
162 | 			"reasoning": "Moderate-high complexity due to event-driven architecture, structured event formats, persistence integration, and WebSocket implementation. Requires careful design of event schemas and delivery mechanisms."
163 | 		},
164 | 		{
165 | 			"taskId": 50,
166 | 			"taskTitle": "Add intelligent test targeting for faster feedback",
167 | 			"complexityScore": 7,
168 | 			"recommendedSubtasks": 5,
169 | 			"expansionPrompt": "Decompose test targeting into: file change detection system, test dependency analysis engine, framework-specific targeting adapters, test impact calculation algorithm, and fallback integration with TestRunnerAdapter. Focus on accuracy and performance optimization.",
170 | 			"reasoning": "High complexity due to dependency analysis, impact calculation algorithms, framework-specific targeting, and integration with existing test execution. Requires sophisticated analysis of code relationships and test dependencies."
171 | 		},
172 | 		{
173 | 			"taskId": 51,
174 | 			"taskTitle": "Implement dry-run visualization with execution timeline",
175 | 			"complexityScore": 6,
176 | 			"recommendedSubtasks": 4,
177 | 			"expansionPrompt": "Structure dry-run visualization into: timeline calculation engine with duration estimates, estimation algorithms based on task complexity, ASCII art progress visualization with formatting, and resource validation with preflight checks. Focus on accurate planning and clear visual presentation.",
178 | 			"reasoning": "Moderate-high complexity due to timeline calculation, estimation algorithms, ASCII visualization, and resource validation. Requires understanding of workflow timing and visual formatting for terminal output."
179 | 		},
180 | 		{
181 | 			"taskId": 52,
182 | 			"taskTitle": "Add autopilot workflow integration tests",
183 | 			"complexityScore": 8,
184 | 			"recommendedSubtasks": 6,
185 | 			"expansionPrompt": "Structure integration testing into: isolated test environment infrastructure, mock integrations and service stubs, end-to-end workflow test scenarios, performance benchmarking and resource monitoring, test isolation and parallelization strategies, and comprehensive result validation and reporting. Focus on realistic test scenarios and reliable automation.",
186 | 			"reasoning": "High complexity due to end-to-end testing requirements, mock service integration, performance testing, isolation mechanisms, and comprehensive validation. Requires sophisticated test infrastructure and scenario design."
187 | 		},
188 | 		{
189 | 			"taskId": 53,
190 | 			"taskTitle": "Finalize autopilot documentation and examples",
191 | 			"complexityScore": 3,
192 | 			"recommendedSubtasks": 4,
193 | 			"expansionPrompt": "Structure documentation into: comprehensive autopilot documentation covering setup and usage, example PRD files and templates for different project types, troubleshooting guide for common issues and solutions, and demo materials with workflow visualization. Focus on clarity and practical examples.",
194 | 			"reasoning": "Low complexity involving documentation writing, example creation, and demo material production. The main challenge is ensuring accuracy and completeness rather than technical implementation."
195 | 		}
196 | 	]
197 | }
198 | 
```
Page 40/69FirstPrevNextLast