This is page 21 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
--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/move-task-cross-tag.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * Direct function wrapper for cross-tag task moves
3 | */
4 |
5 | import { moveTasksBetweenTags } from '../../../../scripts/modules/task-manager/move-task.js';
6 | import { findTasksPath } from '../utils/path-utils.js';
7 |
8 | import {
9 | enableSilentMode,
10 | disableSilentMode
11 | } from '../../../../scripts/modules/utils.js';
12 |
13 | /**
14 | * Move tasks between tags
15 | * @param {Object} args - Function arguments
16 | * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file
17 | * @param {string} args.sourceIds - Comma-separated IDs of tasks to move
18 | * @param {string} args.sourceTag - Source tag name
19 | * @param {string} args.targetTag - Target tag name
20 | * @param {boolean} args.withDependencies - Move dependent tasks along with main task
21 | * @param {boolean} args.ignoreDependencies - Break cross-tag dependencies during move
22 | * @param {string} args.file - Alternative path to the tasks.json file
23 | * @param {string} args.projectRoot - Project root directory
24 | * @param {Object} log - Logger object
25 | * @returns {Promise<{success: boolean, data?: Object, error?: Object}>}
26 | */
27 | export async function moveTaskCrossTagDirect(args, log, context = {}) {
28 | const { session } = context;
29 | const { projectRoot } = args;
30 |
31 | log.info(`moveTaskCrossTagDirect called with args: ${JSON.stringify(args)}`);
32 |
33 | // Validate required parameters
34 | if (!args.sourceIds) {
35 | return {
36 | success: false,
37 | error: {
38 | message: 'Source IDs are required',
39 | code: 'MISSING_SOURCE_IDS'
40 | }
41 | };
42 | }
43 |
44 | if (!args.sourceTag) {
45 | return {
46 | success: false,
47 | error: {
48 | message: 'Source tag is required for cross-tag moves',
49 | code: 'MISSING_SOURCE_TAG'
50 | }
51 | };
52 | }
53 |
54 | if (!args.targetTag) {
55 | return {
56 | success: false,
57 | error: {
58 | message: 'Target tag is required for cross-tag moves',
59 | code: 'MISSING_TARGET_TAG'
60 | }
61 | };
62 | }
63 |
64 | // Validate that source and target tags are different
65 | if (args.sourceTag === args.targetTag) {
66 | return {
67 | success: false,
68 | error: {
69 | message: `Source and target tags are the same ("${args.sourceTag}")`,
70 | code: 'SAME_SOURCE_TARGET_TAG',
71 | suggestions: [
72 | 'Use different tags for cross-tag moves',
73 | 'Use within-tag move: task-master move --from=<id> --to=<id> --tag=<tag>',
74 | 'Check available tags: task-master tags'
75 | ]
76 | }
77 | };
78 | }
79 |
80 | try {
81 | // Find tasks.json path if not provided
82 | let tasksPath = args.tasksJsonPath || args.file;
83 | if (!tasksPath) {
84 | if (!args.projectRoot) {
85 | return {
86 | success: false,
87 | error: {
88 | message:
89 | 'Project root is required if tasksJsonPath is not provided',
90 | code: 'MISSING_PROJECT_ROOT'
91 | }
92 | };
93 | }
94 | tasksPath = findTasksPath(args, log);
95 | }
96 |
97 | // Enable silent mode to prevent console output during MCP operation
98 | enableSilentMode();
99 |
100 | try {
101 | // Parse source IDs
102 | const sourceIds = args.sourceIds.split(',').map((id) => id.trim());
103 |
104 | // Prepare move options
105 | const moveOptions = {
106 | withDependencies: args.withDependencies || false,
107 | ignoreDependencies: args.ignoreDependencies || false
108 | };
109 |
110 | // Call the core moveTasksBetweenTags function
111 | const result = await moveTasksBetweenTags(
112 | tasksPath,
113 | sourceIds,
114 | args.sourceTag,
115 | args.targetTag,
116 | moveOptions,
117 | { projectRoot }
118 | );
119 |
120 | return {
121 | success: true,
122 | data: {
123 | ...result,
124 | message: `Successfully moved ${sourceIds.length} task(s) from "${args.sourceTag}" to "${args.targetTag}"`,
125 | moveOptions,
126 | sourceTag: args.sourceTag,
127 | targetTag: args.targetTag
128 | }
129 | };
130 | } finally {
131 | // Restore console output - always executed regardless of success or error
132 | disableSilentMode();
133 | }
134 | } catch (error) {
135 | log.error(`Failed to move tasks between tags: ${error.message}`);
136 | log.error(`Error code: ${error.code}, Error name: ${error.name}`);
137 |
138 | // Enhanced error handling with structured error objects
139 | let errorCode = 'MOVE_TASK_CROSS_TAG_ERROR';
140 | let suggestions = [];
141 |
142 | // Handle structured errors first
143 | if (error.code === 'CROSS_TAG_DEPENDENCY_CONFLICTS') {
144 | errorCode = 'CROSS_TAG_DEPENDENCY_CONFLICT';
145 | suggestions = [
146 | 'Use --with-dependencies to move dependent tasks together',
147 | 'Use --ignore-dependencies to break cross-tag dependencies',
148 | 'Run task-master validate-dependencies to check for issues',
149 | 'Move dependencies first, then move the main task'
150 | ];
151 | } else if (error.code === 'CANNOT_MOVE_SUBTASK') {
152 | errorCode = 'SUBTASK_MOVE_RESTRICTION';
153 | suggestions = [
154 | 'Promote subtask to full task first: task-master remove-subtask --id=<subtaskId> --convert',
155 | 'Move the parent task with all subtasks using --with-dependencies'
156 | ];
157 | } else if (
158 | error.code === 'TASK_NOT_FOUND' ||
159 | error.code === 'INVALID_SOURCE_TAG' ||
160 | error.code === 'INVALID_TARGET_TAG'
161 | ) {
162 | errorCode = 'TAG_OR_TASK_NOT_FOUND';
163 | suggestions = [
164 | 'Check available tags: task-master tags',
165 | 'Verify task IDs exist: task-master list',
166 | 'Check task details: task-master show <id>'
167 | ];
168 | } else if (error.message.includes('cross-tag dependency conflicts')) {
169 | // Fallback for legacy error messages
170 | errorCode = 'CROSS_TAG_DEPENDENCY_CONFLICT';
171 | suggestions = [
172 | 'Use --with-dependencies to move dependent tasks together',
173 | 'Use --ignore-dependencies to break cross-tag dependencies',
174 | 'Run task-master validate-dependencies to check for issues',
175 | 'Move dependencies first, then move the main task'
176 | ];
177 | } else if (error.message.includes('Cannot move subtask')) {
178 | // Fallback for legacy error messages
179 | errorCode = 'SUBTASK_MOVE_RESTRICTION';
180 | suggestions = [
181 | 'Promote subtask to full task first: task-master remove-subtask --id=<subtaskId> --convert',
182 | 'Move the parent task with all subtasks using --with-dependencies'
183 | ];
184 | } else if (error.message.includes('not found')) {
185 | // Fallback for legacy error messages
186 | errorCode = 'TAG_OR_TASK_NOT_FOUND';
187 | suggestions = [
188 | 'Check available tags: task-master tags',
189 | 'Verify task IDs exist: task-master list',
190 | 'Check task details: task-master show <id>'
191 | ];
192 | } else if (
193 | error.code === 'TASK_ALREADY_EXISTS' ||
194 | error.message?.includes('already exists in target tag')
195 | ) {
196 | // Target tag has an ID collision
197 | errorCode = 'TASK_ALREADY_EXISTS';
198 | suggestions = [
199 | 'Choose a different target tag without conflicting IDs',
200 | 'Move a different set of IDs (avoid existing ones)',
201 | 'If needed, move within-tag to a new ID first, then cross-tag move'
202 | ];
203 | }
204 |
205 | return {
206 | success: false,
207 | error: {
208 | message: error.message,
209 | code: errorCode,
210 | suggestions
211 | }
212 | };
213 | }
214 | }
215 |
```
--------------------------------------------------------------------------------
/scripts/modules/task-manager/generate-task-files.js:
--------------------------------------------------------------------------------
```javascript
1 | import path from 'path';
2 | import fs from 'fs';
3 | import chalk from 'chalk';
4 |
5 | import { log, readJSON } from '../utils.js';
6 | import { formatDependenciesWithStatus } from '../ui.js';
7 | import { validateAndFixDependencies } from '../dependency-manager.js';
8 | import { getDebugFlag } from '../config-manager.js';
9 |
10 | /**
11 | * Generate individual task files from tasks.json
12 | * @param {string} tasksPath - Path to the tasks.json file
13 | * @param {string} outputDir - Output directory for task files
14 | * @param {Object} options - Additional options (mcpLog for MCP mode, projectRoot, tag)
15 | * @param {string} [options.projectRoot] - Project root path
16 | * @param {string} [options.tag] - Tag for the task
17 | * @param {Object} [options.mcpLog] - MCP logger object
18 | * @returns {Object|undefined} Result object in MCP mode, undefined in CLI mode
19 | */
20 | function generateTaskFiles(tasksPath, outputDir, options = {}) {
21 | try {
22 | const isMcpMode = !!options?.mcpLog;
23 | const { projectRoot, tag } = options;
24 |
25 | // 1. Read the raw data structure, ensuring we have all tags.
26 | // We call readJSON without a specific tag to get the resolved default view,
27 | // which correctly contains the full structure in `_rawTaggedData`.
28 | const resolvedData = readJSON(tasksPath, projectRoot, tag);
29 | if (!resolvedData) {
30 | throw new Error(`Could not read or parse tasks file: ${tasksPath}`);
31 | }
32 | // Prioritize the _rawTaggedData if it exists, otherwise use the data as is.
33 | const rawData = resolvedData._rawTaggedData || resolvedData;
34 |
35 | // 2. Determine the target tag we need to generate files for.
36 | const tagData = rawData[tag];
37 |
38 | if (!tagData || !tagData.tasks) {
39 | throw new Error(`Tag '${tag}' not found or has no tasks in the data.`);
40 | }
41 | const tasksForGeneration = tagData.tasks;
42 |
43 | // Create the output directory if it doesn't exist
44 | if (!fs.existsSync(outputDir)) {
45 | fs.mkdirSync(outputDir, { recursive: true });
46 | }
47 |
48 | log(
49 | 'info',
50 | `Preparing to regenerate ${tasksForGeneration.length} task files for tag '${tag}'`
51 | );
52 |
53 | // 3. Validate dependencies using the FULL, raw data structure to prevent data loss.
54 | validateAndFixDependencies(
55 | rawData, // Pass the entire object with all tags
56 | tasksPath,
57 | projectRoot,
58 | tag // Provide the current tag context for the operation
59 | );
60 |
61 | const allTasksInTag = tagData.tasks;
62 | const validTaskIds = allTasksInTag.map((task) => task.id);
63 |
64 | // Cleanup orphaned task files
65 | log('info', 'Checking for orphaned task files to clean up...');
66 | try {
67 | const files = fs.readdirSync(outputDir);
68 | // Tag-aware file patterns: master -> task_001.txt, other tags -> task_001_tagname.txt
69 | const masterFilePattern = /^task_(\d+)\.txt$/;
70 | const taggedFilePattern = new RegExp(`^task_(\\d+)_${tag}\\.txt$`);
71 |
72 | const orphanedFiles = files.filter((file) => {
73 | let match = null;
74 | let fileTaskId = null;
75 |
76 | // Check if file belongs to current tag
77 | if (tag === 'master') {
78 | match = file.match(masterFilePattern);
79 | if (match) {
80 | fileTaskId = parseInt(match[1], 10);
81 | // Only clean up master files when processing master tag
82 | return !validTaskIds.includes(fileTaskId);
83 | }
84 | } else {
85 | match = file.match(taggedFilePattern);
86 | if (match) {
87 | fileTaskId = parseInt(match[1], 10);
88 | // Only clean up files for the current tag
89 | return !validTaskIds.includes(fileTaskId);
90 | }
91 | }
92 | return false;
93 | });
94 |
95 | if (orphanedFiles.length > 0) {
96 | log(
97 | 'info',
98 | `Found ${orphanedFiles.length} orphaned task files to remove for tag '${tag}'`
99 | );
100 | orphanedFiles.forEach((file) => {
101 | const filePath = path.join(outputDir, file);
102 | fs.unlinkSync(filePath);
103 | });
104 | } else {
105 | log('info', 'No orphaned task files found.');
106 | }
107 | } catch (err) {
108 | log('warn', `Error cleaning up orphaned task files: ${err.message}`);
109 | }
110 |
111 | // Generate task files for the target tag
112 | log('info', `Generating individual task files for tag '${tag}'...`);
113 | tasksForGeneration.forEach((task) => {
114 | // Tag-aware file naming: master -> task_001.txt, other tags -> task_001_tagname.txt
115 | const taskFileName =
116 | tag === 'master'
117 | ? `task_${task.id.toString().padStart(3, '0')}.txt`
118 | : `task_${task.id.toString().padStart(3, '0')}_${tag}.txt`;
119 |
120 | const taskPath = path.join(outputDir, taskFileName);
121 |
122 | let content = `# Task ID: ${task.id}\n`;
123 | content += `# Title: ${task.title}\n`;
124 | content += `# Status: ${task.status || 'pending'}\n`;
125 |
126 | if (task.dependencies && task.dependencies.length > 0) {
127 | content += `# Dependencies: ${formatDependenciesWithStatus(task.dependencies, allTasksInTag, false)}\n`;
128 | } else {
129 | content += '# Dependencies: None\n';
130 | }
131 |
132 | content += `# Priority: ${task.priority || 'medium'}\n`;
133 | content += `# Description: ${task.description || ''}\n`;
134 | content += '# Details:\n';
135 | content += (task.details || '')
136 | .split('\n')
137 | .map((line) => line)
138 | .join('\n');
139 | content += '\n\n';
140 | content += '# Test Strategy:\n';
141 | content += (task.testStrategy || '')
142 | .split('\n')
143 | .map((line) => line)
144 | .join('\n');
145 | content += '\n';
146 |
147 | if (task.subtasks && task.subtasks.length > 0) {
148 | content += '\n# Subtasks:\n';
149 | task.subtasks.forEach((subtask) => {
150 | content += `## ${subtask.id}. ${subtask.title} [${subtask.status || 'pending'}]\n`;
151 | if (subtask.dependencies && subtask.dependencies.length > 0) {
152 | const subtaskDeps = subtask.dependencies
153 | .map((depId) =>
154 | typeof depId === 'number'
155 | ? `${task.id}.${depId}`
156 | : depId.toString()
157 | )
158 | .join(', ');
159 | content += `### Dependencies: ${subtaskDeps}\n`;
160 | } else {
161 | content += '### Dependencies: None\n';
162 | }
163 | content += `### Description: ${subtask.description || ''}\n`;
164 | content += '### Details:\n';
165 | content += (subtask.details || '')
166 | .split('\n')
167 | .map((line) => line)
168 | .join('\n');
169 | content += '\n\n';
170 | });
171 | }
172 |
173 | fs.writeFileSync(taskPath, content);
174 | });
175 |
176 | log(
177 | 'success',
178 | `All ${tasksForGeneration.length} tasks for tag '${tag}' have been generated into '${outputDir}'.`
179 | );
180 |
181 | if (isMcpMode) {
182 | return {
183 | success: true,
184 | count: tasksForGeneration.length,
185 | directory: outputDir
186 | };
187 | }
188 | } catch (error) {
189 | log('error', `Error generating task files: ${error.message}`);
190 | if (!options?.mcpLog) {
191 | console.error(chalk.red(`Error generating task files: ${error.message}`));
192 | if (getDebugFlag()) {
193 | console.error(error);
194 | }
195 | process.exit(1);
196 | } else {
197 | throw error;
198 | }
199 | }
200 | }
201 |
202 | export default generateTaskFiles;
203 |
```
--------------------------------------------------------------------------------
/apps/docs/tdd-workflow/quickstart.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: "TDD Workflow Quick Start"
3 | description: "Get started with TaskMaster's autonomous TDD workflow in 5 minutes"
4 | ---
5 |
6 | Get started with TaskMaster's autonomous TDD workflow in 5 minutes.
7 |
8 | ## Prerequisites
9 |
10 | - TaskMaster initialized project (`tm init`)
11 | - Tasks with subtasks created (`tm parse-prd` or `tm expand`)
12 | - Git repository with clean working tree
13 | - Test framework installed (vitest, jest, mocha, etc.)
14 |
15 | ## 1. Start a Workflow
16 |
17 | ```bash
18 | tm autopilot start <taskId>
19 | ```
20 |
21 | Example:
22 | ```bash
23 | $ tm autopilot start 7
24 |
25 | ✓ Workflow started for task 7
26 | ✓ Created branch: task-7
27 | ✓ Current phase: RED
28 | ✓ Subtask 1/5: Implement start command
29 | → Next action: Write a failing test
30 | ```
31 |
32 | ## 2. The TDD Cycle
33 |
34 | ### RED Phase: Write Failing Test
35 |
36 | ```bash
37 | # Check what to do next
38 | $ tm autopilot next --json
39 | {
40 | "action": "generate_test",
41 | "currentSubtask": {
42 | "id": "1",
43 | "title": "Implement start command"
44 | }
45 | }
46 | ```
47 |
48 | Write a test that fails:
49 | ```typescript
50 | // tests/start.test.ts
51 | import { describe, it, expect } from 'vitest';
52 | import { StartCommand } from '../src/commands/start';
53 |
54 | describe('StartCommand', () => {
55 | it('should initialize workflow', async () => {
56 | const command = new StartCommand();
57 | const result = await command.execute({ taskId: '7' });
58 | expect(result.success).toBe(true);
59 | });
60 | });
61 | ```
62 |
63 | Run tests:
64 | ```bash
65 | $ npm test
66 | # ✗ 1 test failed
67 | ```
68 |
69 | Complete RED phase:
70 | ```bash
71 | $ tm autopilot complete --results '{"total":1,"passed":0,"failed":1,"skipped":0}'
72 |
73 | ✓ RED phase complete
74 | ✓ Current phase: GREEN
75 | → Next action: Implement code to pass tests
76 | ```
77 |
78 | ### GREEN Phase: Implement Feature
79 |
80 | Write minimal code to pass:
81 | ```typescript
82 | // src/commands/start.ts
83 | export class StartCommand {
84 | async execute(options: { taskId: string }) {
85 | return { success: true };
86 | }
87 | }
88 | ```
89 |
90 | Run tests:
91 | ```bash
92 | $ npm test
93 | # ✓ 1 test passed
94 | ```
95 |
96 | Complete GREEN phase:
97 | ```bash
98 | $ tm autopilot complete --results '{"total":1,"passed":1,"failed":0,"skipped":0}'
99 |
100 | ✓ GREEN phase complete
101 | ✓ Current phase: COMMIT
102 | → Next action: Commit changes
103 | ```
104 |
105 | ### COMMIT Phase: Save Progress
106 |
107 | ```bash
108 | $ tm autopilot commit
109 |
110 | ✓ Created commit: abc123
111 | ✓ Message: feat(autopilot): implement start command (Task 7.1)
112 | ✓ Advanced to subtask 2/5
113 | ✓ Current phase: RED
114 | → Next action: Write a failing test
115 | ```
116 |
117 | ## 3. Continue for All Subtasks
118 |
119 | Repeat the RED-GREEN-COMMIT cycle for each subtask until complete.
120 |
121 | ```bash
122 | # Check progress anytime
123 | $ tm autopilot status --json
124 | {
125 | "taskId": "7",
126 | "progress": {
127 | "completed": 1,
128 | "total": 5,
129 | "percentage": 20
130 | },
131 | "currentSubtask": {
132 | "id": "2",
133 | "title": "Implement resume command"
134 | }
135 | }
136 | ```
137 |
138 | ## 4. Complete the Workflow
139 |
140 | When all subtasks are done:
141 |
142 | ```bash
143 | $ tm autopilot status --json
144 | {
145 | "phase": "COMPLETE",
146 | "progress": {
147 | "completed": 5,
148 | "total": 5,
149 | "percentage": 100
150 | }
151 | }
152 | ```
153 |
154 | Your branch `task-7` is ready for review/merge!
155 |
156 | ## Common Patterns
157 |
158 | ### Parse Test Output
159 |
160 | Your test runner outputs human-readable format - convert to JSON:
161 |
162 | **Vitest:**
163 | ```
164 | Tests 2 failed | 8 passed | 10 total
165 | ```
166 | → `{"total":10,"passed":8,"failed":2,"skipped":0}`
167 |
168 | **Jest:**
169 | ```
170 | Tests: 2 failed, 8 passed, 10 total
171 | ```
172 | → `{"total":10,"passed":8,"failed":2,"skipped":0}`
173 |
174 | ### Handle Errors
175 |
176 | **Problem:** RED phase won't complete - "no test failures"
177 |
178 | **Solution:** Your test isn't testing new behavior. Make sure it fails:
179 | ```typescript
180 | // Bad - test passes immediately
181 | it('should exist', () => {
182 | expect(StartCommand).toBeDefined(); // Always passes
183 | });
184 |
185 | // Good - test fails until feature exists
186 | it('should initialize workflow', async () => {
187 | const result = await new StartCommand().execute({ taskId: '7' });
188 | expect(result.success).toBe(true); // Fails until execute() is implemented
189 | });
190 | ```
191 |
192 | **Problem:** GREEN phase won't complete - "tests still failing"
193 |
194 | **Solution:** Fix your implementation until all tests pass:
195 | ```bash
196 | # Run tests to see what's failing
197 | $ npm test
198 |
199 | # Fix the issue
200 | $ vim src/commands/start.ts
201 |
202 | # Verify tests pass
203 | $ npm test
204 |
205 | # Try again
206 | $ tm autopilot complete --results '{"total":1,"passed":1,"failed":0,"skipped":0}'
207 | ```
208 |
209 | ### Resume Interrupted Work
210 |
211 | ```bash
212 | # If you interrupted the workflow
213 | $ tm autopilot resume
214 |
215 | ✓ Workflow resumed
216 | ✓ Task 7 - subtask 3/5
217 | ✓ Current phase: GREEN
218 | → Continue from where you left off
219 | ```
220 |
221 | ## JSON Output Mode
222 |
223 | All commands support `--json` for programmatic use:
224 |
225 | ```bash
226 | $ tm autopilot start 7 --json | jq .
227 | {
228 | "success": true,
229 | "taskId": "7",
230 | "branchName": "task-7",
231 | "phase": "SUBTASK_LOOP",
232 | "tddPhase": "RED",
233 | "progress": { ... },
234 | "currentSubtask": { ... },
235 | "nextAction": "generate_test"
236 | }
237 | ```
238 |
239 | Perfect for:
240 | - CI/CD integration
241 | - Custom tooling
242 | - Automated workflows
243 | - Progress monitoring
244 |
245 | ## MCP Integration
246 |
247 | For AI agents (Claude Code, etc.), use MCP tools:
248 |
249 | ```typescript
250 | // Start workflow
251 | await mcp.call('autopilot_start', {
252 | taskId: '7',
253 | projectRoot: '/path/to/project'
254 | });
255 |
256 | // Get next action
257 | const next = await mcp.call('autopilot_next', {
258 | projectRoot: '/path/to/project'
259 | });
260 |
261 | // Complete phase
262 | await mcp.call('autopilot_complete_phase', {
263 | projectRoot: '/path/to/project',
264 | testResults: { total: 1, passed: 0, failed: 1, skipped: 0 }
265 | });
266 |
267 | // Commit
268 | await mcp.call('autopilot_commit', {
269 | projectRoot: '/path/to/project'
270 | });
271 | ```
272 |
273 | See [AI Agent Integration Guide](./ai-agent-integration.mdx) for details.
274 |
275 | ## Cheat Sheet
276 |
277 | ```bash
278 | # Start
279 | tm autopilot start <taskId> # Initialize workflow
280 |
281 | # Workflow Control
282 | tm autopilot next # What's next?
283 | tm autopilot status # Current state
284 | tm autopilot resume # Continue interrupted work
285 | tm autopilot abort # Cancel and cleanup
286 |
287 | # TDD Cycle
288 | tm autopilot complete --results '{...}' # Advance phase
289 | tm autopilot commit # Save progress
290 |
291 | # Options
292 | --json # Machine-readable output
293 | --project-root <path> # Specify project location
294 | --force # Override safety checks
295 | ```
296 |
297 | ## Next Steps
298 |
299 | - Read [AI Agent Integration Guide](./ai-agent-integration.mdx) for complete documentation
300 | - Check [Command Reference](/command-reference) for all options
301 |
302 | ## Tips
303 |
304 | 1. **Always let tests fail first** - That's the RED phase
305 | 2. **Write minimal code** - Just enough to pass
306 | 3. **Commit frequently** - After each subtask
307 | 4. **Use --json** - Better for programmatic use
308 | 5. **Check status often** - Know where you are
309 | 6. **Trust the workflow** - It enforces TDD rules
310 |
311 | ---
312 |
313 | **Ready to start?** Run `tm autopilot start <taskId>` and begin your TDD journey!
314 |
```
--------------------------------------------------------------------------------
/src/profiles/vscode.js:
--------------------------------------------------------------------------------
```javascript
1 | // VS Code conversion profile for rule-transformer
2 | import path from 'path';
3 | import fs from 'fs';
4 | import { log } from '../../scripts/modules/utils.js';
5 | import { createProfile, COMMON_TOOL_MAPPINGS } from './base-profile.js';
6 |
7 | /**
8 | * Transform standard MCP config format to VS Code format
9 | * @param {Object} mcpConfig - Standard MCP configuration object
10 | * @returns {Object} - Transformed VS Code configuration object
11 | */
12 | function transformToVSCodeFormat(mcpConfig) {
13 | const vscodeConfig = {};
14 |
15 | // Transform mcpServers to servers
16 | if (mcpConfig.mcpServers) {
17 | vscodeConfig.servers = {};
18 |
19 | for (const [serverName, serverConfig] of Object.entries(
20 | mcpConfig.mcpServers
21 | )) {
22 | // Transform server configuration
23 | const transformedServer = {
24 | ...serverConfig
25 | };
26 |
27 | // Add type: "stdio" after the env block
28 | if (transformedServer.env) {
29 | // Reorder properties: keep command, args, env, then add type
30 | const reorderedServer = {};
31 | if (transformedServer.command)
32 | reorderedServer.command = transformedServer.command;
33 | if (transformedServer.args)
34 | reorderedServer.args = transformedServer.args;
35 | if (transformedServer.env) reorderedServer.env = transformedServer.env;
36 | reorderedServer.type = 'stdio';
37 |
38 | // Add any other properties that might exist
39 | Object.keys(transformedServer).forEach((key) => {
40 | if (!['command', 'args', 'env', 'type'].includes(key)) {
41 | reorderedServer[key] = transformedServer[key];
42 | }
43 | });
44 |
45 | vscodeConfig.servers[serverName] = reorderedServer;
46 | } else {
47 | // If no env block, just add type at the end
48 | transformedServer.type = 'stdio';
49 | vscodeConfig.servers[serverName] = transformedServer;
50 | }
51 | }
52 | }
53 |
54 | return vscodeConfig;
55 | }
56 |
57 | /**
58 | * Lifecycle function called after MCP config generation to transform to VS Code format
59 | * @param {string} targetDir - Target project directory
60 | * @param {string} assetsDir - Assets directory (unused for VS Code)
61 | */
62 | function onPostConvertRulesProfile(targetDir, assetsDir) {
63 | const vscodeConfigPath = path.join(targetDir, '.vscode', 'mcp.json');
64 |
65 | if (!fs.existsSync(vscodeConfigPath)) {
66 | log('debug', '[VS Code] No .vscode/mcp.json found to transform');
67 | return;
68 | }
69 |
70 | try {
71 | // Read the generated standard MCP config
72 | const mcpConfigContent = fs.readFileSync(vscodeConfigPath, 'utf8');
73 | const mcpConfig = JSON.parse(mcpConfigContent);
74 |
75 | // Check if it's already in VS Code format (has servers instead of mcpServers)
76 | if (mcpConfig.servers) {
77 | log(
78 | 'info',
79 | '[VS Code] mcp.json already in VS Code format, skipping transformation'
80 | );
81 | return;
82 | }
83 |
84 | // Transform to VS Code format
85 | const vscodeConfig = transformToVSCodeFormat(mcpConfig);
86 |
87 | // Write back the transformed config with proper formatting
88 | fs.writeFileSync(
89 | vscodeConfigPath,
90 | JSON.stringify(vscodeConfig, null, 2) + '\n'
91 | );
92 |
93 | log('info', '[VS Code] Transformed mcp.json to VS Code format');
94 | log('debug', `[VS Code] Renamed mcpServers->servers, added type: "stdio"`);
95 | } catch (error) {
96 | log('error', `[VS Code] Failed to transform mcp.json: ${error.message}`);
97 | }
98 | }
99 |
100 | /**
101 | * Lifecycle function called when removing VS Code profile
102 | * @param {string} targetDir - Target project directory
103 | */
104 | function onRemoveRulesProfile(targetDir) {
105 | const vscodeConfigPath = path.join(targetDir, '.vscode', 'mcp.json');
106 |
107 | if (!fs.existsSync(vscodeConfigPath)) {
108 | log('debug', '[VS Code] No .vscode/mcp.json found to clean up');
109 | return;
110 | }
111 |
112 | try {
113 | // Read the current config
114 | const configContent = fs.readFileSync(vscodeConfigPath, 'utf8');
115 | const config = JSON.parse(configContent);
116 |
117 | // Check if it has the servers section and task-master-ai server
118 | if (config.servers && config.servers['task-master-ai']) {
119 | // Remove task-master-ai server
120 | delete config.servers['task-master-ai'];
121 |
122 | // Check if there are other MCP servers
123 | const remainingServers = Object.keys(config.servers);
124 |
125 | if (remainingServers.length === 0) {
126 | // No other servers, remove entire file
127 | fs.rmSync(vscodeConfigPath, { force: true });
128 | log('info', '[VS Code] Removed empty mcp.json file');
129 |
130 | // Also remove .vscode directory if it's empty
131 | const vscodeDir = path.dirname(vscodeConfigPath);
132 | try {
133 | const dirContents = fs.readdirSync(vscodeDir);
134 | if (dirContents.length === 0) {
135 | fs.rmSync(vscodeDir, { recursive: true, force: true });
136 | log('debug', '[VS Code] Removed empty .vscode directory');
137 | }
138 | } catch (err) {
139 | // Directory might not be empty or might not exist, that's fine
140 | }
141 | } else {
142 | // Write back the modified config
143 | fs.writeFileSync(
144 | vscodeConfigPath,
145 | JSON.stringify(config, null, 2) + '\n'
146 | );
147 | log(
148 | 'info',
149 | '[VS Code] Removed TaskMaster from mcp.json, preserved other configurations'
150 | );
151 | }
152 | } else {
153 | log('debug', '[VS Code] TaskMaster not found in mcp.json');
154 | }
155 | } catch (error) {
156 | log('error', `[VS Code] Failed to clean up mcp.json: ${error.message}`);
157 | }
158 | }
159 |
160 | // Create and export vscode profile using the base factory
161 | export const vscodeProfile = createProfile({
162 | name: 'vscode',
163 | displayName: 'VS Code',
164 | url: 'code.visualstudio.com',
165 | docsUrl: 'code.visualstudio.com/docs',
166 | rulesDir: '.github/instructions', // VS Code instructions location
167 | profileDir: '.vscode', // VS Code configuration directory
168 | mcpConfigName: 'mcp.json', // VS Code uses mcp.json in .vscode directory
169 | targetExtension: '.instructions.md',
170 | customReplacements: [
171 | // Core VS Code directory structure changes
172 | { from: /\.cursor\/rules/g, to: '.github/instructions' },
173 | { from: /\.cursor\/mcp\.json/g, to: '.vscode/mcp.json' },
174 |
175 | // Fix any remaining vscode/rules references that might be created during transformation
176 | { from: /\.vscode\/rules/g, to: '.github/instructions' },
177 |
178 | // VS Code custom instructions format - use applyTo with quoted patterns instead of globs
179 | { from: /^globs:\s*(.+)$/gm, to: 'applyTo: "$1"' },
180 |
181 | // Remove unsupported property - alwaysApply
182 | { from: /^alwaysApply:\s*(true|false)\s*\n?/gm, to: '' },
183 |
184 | // Essential markdown link transformations for VS Code structure
185 | {
186 | from: /\[(.+?)\]\(mdc:\.cursor\/rules\/(.+?)\.mdc\)/g,
187 | to: '[$1](.github/instructions/$2.instructions.md)'
188 | },
189 |
190 | // VS Code specific terminology
191 | { from: /rules directory/g, to: 'instructions directory' },
192 | { from: /cursor rules/gi, to: 'VS Code instructions' }
193 | ],
194 | onPostConvert: onPostConvertRulesProfile,
195 | onRemove: onRemoveRulesProfile
196 | });
197 |
198 | // Export lifecycle functions separately to avoid naming conflicts
199 | export { onPostConvertRulesProfile, onRemoveRulesProfile };
200 |
```
--------------------------------------------------------------------------------
/packages/tm-core/src/modules/workflow/services/test-result-validator.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from 'zod';
2 | import type {
3 | CoverageThresholds,
4 | PhaseValidationOptions,
5 | TestResult,
6 | ValidationResult
7 | } from './test-result-validator.types.js';
8 |
9 | /**
10 | * Schema for coverage metrics validation
11 | */
12 | const coverageSchema = z.object({
13 | line: z.number().min(0).max(100),
14 | branch: z.number().min(0).max(100),
15 | function: z.number().min(0).max(100),
16 | statement: z.number().min(0).max(100)
17 | });
18 |
19 | /**
20 | * Schema for test result validation
21 | */
22 | const testResultSchema = z.object({
23 | total: z.number().int().nonnegative(),
24 | passed: z.number().int().nonnegative(),
25 | failed: z.number().int().nonnegative(),
26 | skipped: z.number().int().nonnegative(),
27 | phase: z.enum(['RED', 'GREEN', 'REFACTOR']),
28 | coverage: coverageSchema.optional()
29 | });
30 |
31 | /**
32 | * Validates test results according to TDD phase semantics
33 | */
34 | export class TestResultValidator {
35 | /**
36 | * Validates a test result object
37 | */
38 | validate(testResult: TestResult): ValidationResult {
39 | const errors: string[] = [];
40 | const warnings: string[] = [];
41 | const suggestions: string[] = [];
42 |
43 | // Schema validation
44 | const parseResult = testResultSchema.safeParse(testResult);
45 | if (!parseResult.success) {
46 | const zodIssues = parseResult.error.issues || [];
47 | errors.push(
48 | ...zodIssues.map((e) => {
49 | const path = e.path.length > 0 ? `${e.path.join('.')}: ` : '';
50 | return `${path}${e.message}`;
51 | })
52 | );
53 | return { valid: false, errors, warnings, suggestions };
54 | }
55 |
56 | // Total validation
57 | const sum = testResult.passed + testResult.failed + testResult.skipped;
58 | if (sum !== testResult.total) {
59 | errors.push('Total tests must equal passed + failed + skipped');
60 | }
61 |
62 | // If there are validation errors, return early
63 | if (errors.length > 0) {
64 | return { valid: false, errors, warnings, suggestions };
65 | }
66 |
67 | return { valid: true, errors, warnings, suggestions };
68 | }
69 |
70 | /**
71 | * Validates RED phase test results
72 | * RED phase must have at least one failing test
73 | */
74 | validateRedPhase(testResult: TestResult): ValidationResult {
75 | const baseValidation = this.validate(testResult);
76 | if (!baseValidation.valid) {
77 | return baseValidation;
78 | }
79 |
80 | const errors: string[] = [];
81 | const suggestions: string[] = [];
82 |
83 | // RED phase must have failures
84 | if (testResult.failed === 0) {
85 | errors.push('RED phase must have at least one failing test');
86 | suggestions.push('Write failing tests first to follow TDD workflow');
87 | }
88 |
89 | // Must have at least one test
90 | if (testResult.total === 0) {
91 | errors.push('Cannot validate empty test suite');
92 | suggestions.push('Add at least one test to begin TDD cycle');
93 | }
94 |
95 | return {
96 | valid: errors.length === 0,
97 | errors,
98 | suggestions
99 | };
100 | }
101 |
102 | /**
103 | * Validates GREEN phase test results
104 | * GREEN phase must have zero failures
105 | */
106 | validateGreenPhase(
107 | testResult: TestResult,
108 | previousTestCount?: number
109 | ): ValidationResult {
110 | const baseValidation = this.validate(testResult);
111 | if (!baseValidation.valid) {
112 | return baseValidation;
113 | }
114 |
115 | const errors: string[] = [];
116 | const warnings: string[] = [];
117 | const suggestions: string[] = [];
118 |
119 | // GREEN phase must have zero failures
120 | if (testResult.failed > 0) {
121 | errors.push('GREEN phase must have zero failures');
122 | suggestions.push('Fix implementation to make all tests pass');
123 | }
124 |
125 | // Must have at least one passing test
126 | if (testResult.passed === 0) {
127 | errors.push('GREEN phase must have at least one passing test');
128 | suggestions.push('Ensure tests exist and implementation makes them pass');
129 | }
130 |
131 | // Check for test count regression
132 | if (
133 | previousTestCount !== undefined &&
134 | testResult.total < previousTestCount
135 | ) {
136 | warnings.push(
137 | `Test count decreased from ${previousTestCount} to ${testResult.total}`
138 | );
139 | suggestions.push('Verify that no tests were accidentally removed');
140 | }
141 |
142 | return {
143 | valid: errors.length === 0,
144 | errors,
145 | warnings,
146 | suggestions
147 | };
148 | }
149 |
150 | /**
151 | * Validates coverage thresholds if provided
152 | */
153 | validateCoverage(
154 | testResult: TestResult,
155 | thresholds: CoverageThresholds
156 | ): ValidationResult {
157 | const baseValidation = this.validate(testResult);
158 | if (!baseValidation.valid) {
159 | return baseValidation;
160 | }
161 |
162 | const errors: string[] = [];
163 | const suggestions: string[] = [];
164 |
165 | // Skip validation if no coverage data
166 | if (!testResult.coverage) {
167 | return { valid: true, errors: [], suggestions: [] };
168 | }
169 |
170 | const coverage = testResult.coverage;
171 | const gaps: string[] = [];
172 |
173 | // Check each coverage type against threshold
174 | if (thresholds.line !== undefined && coverage.line < thresholds.line) {
175 | gaps.push(`line coverage (${coverage.line}% < ${thresholds.line}%)`);
176 | }
177 |
178 | if (
179 | thresholds.branch !== undefined &&
180 | coverage.branch < thresholds.branch
181 | ) {
182 | gaps.push(
183 | `branch coverage (${coverage.branch}% < ${thresholds.branch}%)`
184 | );
185 | }
186 |
187 | if (
188 | thresholds.function !== undefined &&
189 | coverage.function < thresholds.function
190 | ) {
191 | gaps.push(
192 | `function coverage (${coverage.function}% < ${thresholds.function}%)`
193 | );
194 | }
195 |
196 | if (
197 | thresholds.statement !== undefined &&
198 | coverage.statement < thresholds.statement
199 | ) {
200 | gaps.push(
201 | `statement coverage (${coverage.statement}% < ${thresholds.statement}%)`
202 | );
203 | }
204 |
205 | if (gaps.length > 0) {
206 | errors.push(`Coverage thresholds not met: ${gaps.join(', ')}`);
207 | suggestions.push('Add more tests to improve code coverage');
208 | }
209 |
210 | return {
211 | valid: errors.length === 0,
212 | errors,
213 | suggestions
214 | };
215 | }
216 |
217 | /**
218 | * Validates test results based on TDD phase
219 | */
220 | validatePhase(
221 | testResult: TestResult,
222 | options?: PhaseValidationOptions
223 | ): ValidationResult {
224 | const phase = options?.phase ?? testResult.phase;
225 |
226 | // Phase-specific validation
227 | let phaseResult: ValidationResult;
228 | if (phase === 'RED') {
229 | phaseResult = this.validateRedPhase(testResult);
230 | } else if (phase === 'GREEN') {
231 | phaseResult = this.validateGreenPhase(
232 | testResult,
233 | options?.previousTestCount
234 | );
235 | } else {
236 | // REFACTOR phase uses same rules as GREEN
237 | phaseResult = this.validateGreenPhase(
238 | testResult,
239 | options?.previousTestCount
240 | );
241 | }
242 |
243 | if (!phaseResult.valid) {
244 | return phaseResult;
245 | }
246 |
247 | // Coverage validation if thresholds provided
248 | if (options?.coverageThresholds) {
249 | const coverageResult = this.validateCoverage(
250 | testResult,
251 | options.coverageThresholds
252 | );
253 |
254 | // Merge results
255 | return {
256 | valid: coverageResult.valid,
257 | errors: [...(phaseResult.errors || []), ...coverageResult.errors],
258 | warnings: phaseResult.warnings,
259 | suggestions: [
260 | ...(phaseResult.suggestions || []),
261 | ...(coverageResult.suggestions || [])
262 | ]
263 | };
264 | }
265 |
266 | return phaseResult;
267 | }
268 | }
269 |
```
--------------------------------------------------------------------------------
/apps/docs/capabilities/task-structure.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: "Task Structure"
3 | sidebarTitle: "Task Structure"
4 | description: "Tasks in Task Master follow a specific format designed to provide comprehensive information for both humans and AI assistants."
5 | ---
6 |
7 | ## Task Fields in tasks.json
8 |
9 | Tasks in tasks.json have the following structure:
10 |
11 | | Field | Description | Example |
12 | | -------------- | ---------------------------------------------- | ------------------------------------------------------ |
13 | | `id` | Unique identifier for the task. | `1` |
14 | | `title` | Brief, descriptive title. | `"Initialize Repo"` |
15 | | `description` | What the task involves. | `"Create a new repository, set up initial structure."` |
16 | | `status` | Current state. | `"pending"`, `"done"`, `"deferred"` |
17 | | `dependencies` | Prerequisite task IDs. ✅ Completed, ⏱️ Pending | `[1, 2]` |
18 | | `priority` | Task importance. | `"high"`, `"medium"`, `"low"` |
19 | | `details` | Implementation instructions. | `"Use GitHub client ID/secret, handle callback..."` |
20 | | `testStrategy` | How to verify success. | `"Deploy and confirm 'Hello World' response."` |
21 | | `subtasks` | Nested subtasks related to the main task. | `[{"id": 1, "title": "Configure OAuth", ...}]` |
22 |
23 | ## Task File Format
24 |
25 | Individual task files follow this format:
26 |
27 | ```
28 | # Task ID: <id>
29 | # Title: <title>
30 | # Status: <status>
31 | # Dependencies: <comma-separated list of dependency IDs>
32 | # Priority: <priority>
33 | # Description: <brief description>
34 | # Details:
35 | <detailed implementation notes>
36 |
37 | # Test Strategy:
38 | <verification approach>
39 | ```
40 |
41 | ## Features in Detail
42 |
43 | <AccordionGroup>
44 | <Accordion title="Analyzing Task Complexity">
45 | The `analyze-complexity` command:
46 |
47 | - Analyzes each task using AI to assess its complexity on a scale of 1-10
48 | - Recommends optimal number of subtasks based on configured DEFAULT_SUBTASKS
49 | - Generates tailored prompts for expanding each task
50 | - Creates a comprehensive JSON report with ready-to-use commands
51 | - Saves the report to scripts/task-complexity-report.json by default
52 |
53 | The generated report contains:
54 |
55 | - Complexity analysis for each task (scored 1-10)
56 | - Recommended number of subtasks based on complexity
57 | - AI-generated expansion prompts customized for each task
58 | - Ready-to-run expansion commands directly within each task analysis
59 | </Accordion>
60 |
61 | <Accordion title="Viewing Complexity Report">
62 | The `complexity-report` command:
63 |
64 | - Displays a formatted, easy-to-read version of the complexity analysis report
65 | - Shows tasks organized by complexity score (highest to lowest)
66 | - Provides complexity distribution statistics (low, medium, high)
67 | - Highlights tasks recommended for expansion based on threshold score
68 | - Includes ready-to-use expansion commands for each complex task
69 | - If no report exists, offers to generate one on the spot
70 | </Accordion>
71 |
72 | <Accordion title="Smart Task Expansion">
73 | The `expand` command automatically checks for and uses the complexity report:
74 |
75 | When a complexity report exists:
76 |
77 | - Tasks are automatically expanded using the recommended subtask count and prompts
78 | - When expanding all tasks, they're processed in order of complexity (highest first)
79 | - Research-backed generation is preserved from the complexity analysis
80 | - You can still override recommendations with explicit command-line options
81 |
82 | Example workflow:
83 |
84 | ```bash
85 | # Generate the complexity analysis report with research capabilities
86 | task-master analyze-complexity --research
87 |
88 | # Review the report in a readable format
89 | task-master complexity-report
90 |
91 | # Expand tasks using the optimized recommendations
92 | task-master expand --id=8
93 | # or expand all tasks
94 | task-master expand --all
95 | ```
96 | </Accordion>
97 |
98 | <Accordion title="Finding the Next Task">
99 | The `next` command:
100 |
101 | - Identifies tasks that are pending/in-progress and have all dependencies satisfied
102 | - Prioritizes tasks by priority level, dependency count, and task ID
103 | - Displays comprehensive information about the selected task:
104 | - Basic task details (ID, title, priority, dependencies)
105 | - Implementation details
106 | - Subtasks (if they exist)
107 | - Provides contextual suggested actions:
108 | - Command to mark the task as in-progress
109 | - Command to mark the task as done
110 | - Commands for working with subtasks
111 | </Accordion>
112 |
113 | <Accordion title="Viewing Specific Task Details">
114 | The `show` command:
115 |
116 | - Displays comprehensive details about a specific task or subtask
117 | - Shows task status, priority, dependencies, and detailed implementation notes
118 | - For parent tasks, displays all subtasks and their status
119 | - For subtasks, shows parent task relationship
120 | - Provides contextual action suggestions based on the task's state
121 | - Works with both regular tasks and subtasks (using the format taskId.subtaskId)
122 | </Accordion>
123 | </AccordionGroup>
124 |
125 | ## Best Practices for AI-Driven Development
126 |
127 | <CardGroup cols={2}>
128 | <Card title="📝 Detailed PRD" icon="lightbulb">
129 | The more detailed your PRD, the better the generated tasks will be.
130 | </Card>
131 |
132 | <Card title="👀 Review Tasks" icon="magnifying-glass">
133 | After parsing the PRD, review the tasks to ensure they make sense and have appropriate dependencies.
134 | </Card>
135 |
136 | <Card title="📊 Analyze Complexity" icon="chart-line">
137 | Use the complexity analysis feature to identify which tasks should be broken down further.
138 | </Card>
139 |
140 | <Card title="⛓️ Follow Dependencies" icon="link">
141 | Always respect task dependencies - the Cursor agent will help with this.
142 | </Card>
143 |
144 | <Card title="🔄 Update As You Go" icon="arrows-rotate">
145 | If your implementation diverges from the plan, use the update command to keep future tasks aligned.
146 | </Card>
147 |
148 | <Card title="📦 Break Down Tasks" icon="boxes-stacked">
149 | Use the expand command to break down complex tasks into manageable subtasks.
150 | </Card>
151 |
152 | <Card title="🔄 Regenerate Files" icon="file-arrow-up">
153 | After any updates to tasks.json, regenerate the task files to keep them in sync.
154 | </Card>
155 |
156 | <Card title="💬 Provide Context" icon="comment">
157 | When asking the Cursor agent to help with a task, provide context about what you're trying to achieve.
158 | </Card>
159 |
160 | <Card title="✅ Validate Dependencies" icon="circle-check">
161 | Periodically run the validate-dependencies command to check for invalid or circular dependencies.
162 | </Card>
163 | </CardGroup>
164 |
```
--------------------------------------------------------------------------------
/tests/unit/task-finder.test.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * Task finder tests
3 | */
4 |
5 | // Import after mocks are set up - No mocks needed for readComplexityReport anymore
6 | import { findTaskById } from '../../scripts/modules/utils.js';
7 | import { emptySampleTasks, sampleTasks } from '../fixtures/sample-tasks.js';
8 |
9 | describe('Task Finder', () => {
10 | describe('findTaskById function', () => {
11 | test('should find a task by numeric ID', () => {
12 | const result = findTaskById(sampleTasks.tasks, 2);
13 | expect(result.task).toBeDefined();
14 | expect(result.task.id).toBe(2);
15 | expect(result.task.title).toBe('Create Core Functionality');
16 | expect(result.originalSubtaskCount).toBeNull();
17 | });
18 |
19 | test('should find a task by string ID', () => {
20 | const result = findTaskById(sampleTasks.tasks, '2');
21 | expect(result.task).toBeDefined();
22 | expect(result.task.id).toBe(2);
23 | expect(result.originalSubtaskCount).toBeNull();
24 | });
25 |
26 | test('should find tasks when JSON contains string IDs (normalized to numbers)', () => {
27 | // Simulate tasks loaded from JSON with string IDs and mixed subtask notations
28 | const tasksWithStringIds = [
29 | { id: '1', title: 'First Task' },
30 | {
31 | id: '2',
32 | title: 'Second Task',
33 | subtasks: [
34 | { id: '1', title: 'Subtask One' },
35 | { id: '2.2', title: 'Subtask Two (with dotted notation)' } // Testing dotted notation
36 | ]
37 | },
38 | {
39 | id: '5',
40 | title: 'Fifth Task',
41 | subtasks: [
42 | { id: '5.1', title: 'Subtask with dotted ID' }, // Should normalize to 1
43 | { id: '3', title: 'Subtask with simple ID' } // Should stay as 3
44 | ]
45 | }
46 | ];
47 |
48 | // The readJSON function should normalize these IDs to numbers
49 | // For this test, we'll manually normalize them to simulate what happens
50 | tasksWithStringIds.forEach((task) => {
51 | task.id = parseInt(task.id, 10);
52 | if (task.subtasks) {
53 | task.subtasks.forEach((subtask) => {
54 | // Handle dotted notation like "5.1" -> extract the subtask part
55 | if (typeof subtask.id === 'string' && subtask.id.includes('.')) {
56 | const parts = subtask.id.split('.');
57 | subtask.id = parseInt(parts[parts.length - 1], 10);
58 | } else {
59 | subtask.id = parseInt(subtask.id, 10);
60 | }
61 | });
62 | }
63 | });
64 |
65 | // Test finding tasks by numeric ID
66 | const result1 = findTaskById(tasksWithStringIds, 5);
67 | expect(result1.task).toBeDefined();
68 | expect(result1.task.id).toBe(5);
69 | expect(result1.task.title).toBe('Fifth Task');
70 |
71 | // Test finding tasks by string ID
72 | const result2 = findTaskById(tasksWithStringIds, '5');
73 | expect(result2.task).toBeDefined();
74 | expect(result2.task.id).toBe(5);
75 |
76 | // Test finding subtasks with normalized IDs
77 | const result3 = findTaskById(tasksWithStringIds, '2.1');
78 | expect(result3.task).toBeDefined();
79 | expect(result3.task.id).toBe(1);
80 | expect(result3.task.title).toBe('Subtask One');
81 | expect(result3.task.isSubtask).toBe(true);
82 |
83 | // Test subtask that was originally "2.2" (should be normalized to 2)
84 | const result4 = findTaskById(tasksWithStringIds, '2.2');
85 | expect(result4.task).toBeDefined();
86 | expect(result4.task.id).toBe(2);
87 | expect(result4.task.title).toBe('Subtask Two (with dotted notation)');
88 |
89 | // Test subtask that was originally "5.1" (should be normalized to 1)
90 | const result5 = findTaskById(tasksWithStringIds, '5.1');
91 | expect(result5.task).toBeDefined();
92 | expect(result5.task.id).toBe(1);
93 | expect(result5.task.title).toBe('Subtask with dotted ID');
94 |
95 | // Test subtask that was originally "3" (should stay as 3)
96 | const result6 = findTaskById(tasksWithStringIds, '5.3');
97 | expect(result6.task).toBeDefined();
98 | expect(result6.task.id).toBe(3);
99 | expect(result6.task.title).toBe('Subtask with simple ID');
100 | });
101 |
102 | test('should find a subtask using dot notation', () => {
103 | const result = findTaskById(sampleTasks.tasks, '3.1');
104 | expect(result.task).toBeDefined();
105 | expect(result.task.id).toBe(1);
106 | expect(result.task.title).toBe('Create Header Component');
107 | expect(result.task.isSubtask).toBe(true);
108 | expect(result.task.parentTask.id).toBe(3);
109 | expect(result.originalSubtaskCount).toBeNull();
110 | });
111 |
112 | test('should return null for non-existent task ID', () => {
113 | const result = findTaskById(sampleTasks.tasks, 99);
114 | expect(result.task).toBeNull();
115 | expect(result.originalSubtaskCount).toBeNull();
116 | });
117 |
118 | test('should return null for non-existent subtask ID', () => {
119 | const result = findTaskById(sampleTasks.tasks, '3.99');
120 | expect(result.task).toBeNull();
121 | expect(result.originalSubtaskCount).toBeNull();
122 | });
123 |
124 | test('should return null for non-existent parent task ID in subtask notation', () => {
125 | const result = findTaskById(sampleTasks.tasks, '99.1');
126 | expect(result.task).toBeNull();
127 | expect(result.originalSubtaskCount).toBeNull();
128 | });
129 |
130 | test('should return null when tasks array is empty', () => {
131 | const result = findTaskById(emptySampleTasks.tasks, 1);
132 | expect(result.task).toBeNull();
133 | expect(result.originalSubtaskCount).toBeNull();
134 | });
135 | test('should work correctly when no complexity report is provided', () => {
136 | // Pass null as the complexity report
137 | const result = findTaskById(sampleTasks.tasks, 2, null);
138 |
139 | expect(result.task).toBeDefined();
140 | expect(result.task.id).toBe(2);
141 | expect(result.task.complexityScore).toBeUndefined();
142 | });
143 | test('should work correctly when task has no complexity data in the provided report', () => {
144 | // Define a complexity report that doesn't include task 2
145 | const complexityReport = {
146 | complexityAnalysis: [{ taskId: 999, complexityScore: 5 }]
147 | };
148 |
149 | const result = findTaskById(sampleTasks.tasks, 2, complexityReport);
150 |
151 | expect(result.task).toBeDefined();
152 | expect(result.task.id).toBe(2);
153 | expect(result.task.complexityScore).toBeUndefined();
154 | });
155 |
156 | test('should include complexity score when report is provided', () => {
157 | // Define the complexity report for this test
158 | const complexityReport = {
159 | meta: {
160 | generatedAt: '2023-01-01T00:00:00.000Z',
161 | tasksAnalyzed: 3,
162 | thresholdScore: 5
163 | },
164 | complexityAnalysis: [
165 | {
166 | taskId: 1,
167 | taskTitle: 'Initialize Project',
168 | complexityScore: 3,
169 | recommendedSubtasks: 2
170 | },
171 | {
172 | taskId: 2,
173 | taskTitle: 'Create Core Functionality',
174 | complexityScore: 8,
175 | recommendedSubtasks: 5
176 | },
177 | {
178 | taskId: 3,
179 | taskTitle: 'Implement UI Components',
180 | complexityScore: 6,
181 | recommendedSubtasks: 4
182 | }
183 | ]
184 | };
185 |
186 | const result = findTaskById(sampleTasks.tasks, 2, complexityReport);
187 |
188 | expect(result.task).toBeDefined();
189 | expect(result.task.id).toBe(2);
190 | expect(result.task.complexityScore).toBe(8);
191 | });
192 | });
193 | });
194 |
```
--------------------------------------------------------------------------------
/.taskmaster/reports/task-complexity-report_cc-kiro-hooks.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "meta": {
3 | "generatedAt": "2025-07-22T09:41:10.517Z",
4 | "tasksAnalyzed": 10,
5 | "totalTasks": 10,
6 | "analysisCount": 10,
7 | "thresholdScore": 5,
8 | "projectName": "Taskmaster",
9 | "usedResearch": false
10 | },
11 | "complexityAnalysis": [
12 | {
13 | "taskId": 1,
14 | "taskTitle": "Implement Task Integration Layer (TIL) Core",
15 | "complexityScore": 8,
16 | "recommendedSubtasks": 5,
17 | "expansionPrompt": "Break down the TIL Core implementation into distinct components: hook registration system, task lifecycle management, event coordination, state persistence layer, and configuration validation. Each subtask should focus on a specific architectural component with clear interfaces and testable boundaries.",
18 | "reasoning": "This is a foundational component with multiple complex subsystems including event-driven architecture, API integration, state management, and configuration validation. The existing 5 subtasks are well-structured and appropriately sized."
19 | },
20 | {
21 | "taskId": 2,
22 | "taskTitle": "Develop Dependency Monitor with Taskmaster MCP Integration",
23 | "complexityScore": 7,
24 | "recommendedSubtasks": 4,
25 | "expansionPrompt": "Divide the dependency monitor into: dependency graph data structure implementation, circular dependency detection algorithm, Taskmaster MCP integration layer, and real-time notification system. Focus on performance optimization for large graphs and efficient caching strategies.",
26 | "reasoning": "Complex graph algorithms and real-time monitoring require careful implementation. The task involves sophisticated data structures, algorithm design, and API integration with performance constraints."
27 | },
28 | {
29 | "taskId": 3,
30 | "taskTitle": "Build Execution Manager with Priority Queue and Parallel Execution",
31 | "complexityScore": 8,
32 | "recommendedSubtasks": 5,
33 | "expansionPrompt": "Structure the execution manager into: priority queue implementation, resource conflict detection system, parallel execution coordinator, timeout and cancellation handler, and execution history persistence layer. Each component should handle specific aspects of concurrent task management.",
34 | "reasoning": "Managing concurrent execution with resource conflicts, priority scheduling, and persistence is highly complex. Requires careful synchronization, error handling, and performance optimization."
35 | },
36 | {
37 | "taskId": 4,
38 | "taskTitle": "Implement Safety Manager with Configurable Constraints and Emergency Controls",
39 | "complexityScore": 7,
40 | "recommendedSubtasks": 4,
41 | "expansionPrompt": "Break down into: constraint validation engine, emergency control system (stop/pause), user approval workflow implementation, and safety monitoring/audit logging. Each subtask should address specific safety aspects with fail-safe mechanisms.",
42 | "reasoning": "Safety systems require careful design with multiple fail-safes. The task involves validation logic, real-time controls, workflow management, and comprehensive logging."
43 | },
44 | {
45 | "taskId": 5,
46 | "taskTitle": "Develop Event-Based Hook Processor",
47 | "complexityScore": 6,
48 | "recommendedSubtasks": 4,
49 | "expansionPrompt": "Organize into: file system event integration, Git/VCS event listeners, build system event connectors, and event filtering/debouncing mechanism. Focus on modular event source integration with configurable processing pipelines.",
50 | "reasoning": "While conceptually straightforward, integrating multiple event sources with proper filtering and performance optimization requires careful implementation. Each event source has unique characteristics."
51 | },
52 | {
53 | "taskId": 6,
54 | "taskTitle": "Implement Prompt-Based Hook Processor with AI Integration",
55 | "complexityScore": 7,
56 | "recommendedSubtasks": 4,
57 | "expansionPrompt": "Divide into: prompt interception mechanism, NLP-based task suggestion engine, context injection system, and conversation-based status updater. Each component should handle specific aspects of AI conversation integration.",
58 | "reasoning": "AI integration with prompt analysis and dynamic context injection is complex. Requires understanding of conversation flow, relevance scoring, and seamless integration with existing systems."
59 | },
60 | {
61 | "taskId": 7,
62 | "taskTitle": "Create Update-Based Hook Processor for Automatic Progress Tracking",
63 | "complexityScore": 6,
64 | "recommendedSubtasks": 4,
65 | "expansionPrompt": "Structure as: code change monitor, acceptance criteria validator, dependency update propagator, and conflict detection/resolution system. Focus on accurate progress tracking and automated validation logic.",
66 | "reasoning": "Automatic progress tracking requires integration with version control and intelligent analysis of code changes. Conflict detection and dependency propagation add complexity."
67 | },
68 | {
69 | "taskId": 8,
70 | "taskTitle": "Develop Real-Time Automation Dashboard and User Controls",
71 | "complexityScore": 7,
72 | "recommendedSubtasks": 5,
73 | "expansionPrompt": "Break down into: WebSocket real-time communication layer, interactive dependency graph visualization, task queue and status displays, user control interfaces, and analytics/charting components. Each UI component should be modular and reusable.",
74 | "reasoning": "Building a responsive real-time dashboard with complex visualizations and interactive controls is challenging. Requires careful state management, performance optimization, and user experience design."
75 | },
76 | {
77 | "taskId": 9,
78 | "taskTitle": "Integrate Kiro IDE and Taskmaster MCP with Core Services",
79 | "complexityScore": 8,
80 | "recommendedSubtasks": 4,
81 | "expansionPrompt": "Organize into: KiroHookAdapter implementation, TaskmasterMCPAdapter development, error handling and retry logic layer, and IDE UI component integration. Focus on robust adapter patterns and comprehensive error recovery.",
82 | "reasoning": "End-to-end integration of multiple systems with different architectures is highly complex. Requires careful adapter design, extensive error handling, and thorough testing across all integration points."
83 | },
84 | {
85 | "taskId": 10,
86 | "taskTitle": "Implement Configuration Management and Safety Profiles",
87 | "complexityScore": 6,
88 | "recommendedSubtasks": 4,
89 | "expansionPrompt": "Divide into: visual configuration editor UI, JSON Schema validation engine, import/export functionality, and version control integration. Each component should provide intuitive configuration management with robust validation.",
90 | "reasoning": "While technically less complex than core systems, building an intuitive configuration editor with validation, versioning, and import/export requires careful UI/UX design and robust data handling."
91 | }
92 | ]
93 | }
94 |
```
--------------------------------------------------------------------------------
/apps/extension/src/components/TaskDetails/SubtasksSection.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import type React from 'react';
2 | import { useState } from 'react';
3 | import { Button } from '@/components/ui/button';
4 | import { Label } from '@/components/ui/label';
5 | import { Textarea } from '@/components/ui/textarea';
6 | import { Badge } from '@/components/ui/badge';
7 | import { CollapsibleSection } from '@/components/ui/CollapsibleSection';
8 | import { Plus, Loader2 } from 'lucide-react';
9 | import type { TaskMasterTask } from '../../webview/types';
10 | import { getStatusDotColor } from '../constants';
11 |
12 | interface SubtasksSectionProps {
13 | currentTask: TaskMasterTask;
14 | isSubtask: boolean;
15 | sendMessage: (message: any) => Promise<any>;
16 | onNavigateToTask: (taskId: string) => void;
17 | }
18 |
19 | export const SubtasksSection: React.FC<SubtasksSectionProps> = ({
20 | currentTask,
21 | isSubtask,
22 | sendMessage,
23 | onNavigateToTask
24 | }) => {
25 | const [isAddingSubtask, setIsAddingSubtask] = useState(false);
26 | const [newSubtaskTitle, setNewSubtaskTitle] = useState('');
27 | const [newSubtaskDescription, setNewSubtaskDescription] = useState('');
28 | const [isSubmittingSubtask, setIsSubmittingSubtask] = useState(false);
29 |
30 | const handleAddSubtask = async () => {
31 | if (!currentTask || !newSubtaskTitle.trim() || isSubtask) {
32 | return;
33 | }
34 |
35 | setIsSubmittingSubtask(true);
36 | try {
37 | await sendMessage({
38 | type: 'addSubtask',
39 | data: {
40 | parentTaskId: currentTask.id,
41 | subtaskData: {
42 | title: newSubtaskTitle.trim(),
43 | description: newSubtaskDescription.trim() || undefined,
44 | status: 'pending'
45 | }
46 | }
47 | });
48 |
49 | // Reset form and close
50 | setNewSubtaskTitle('');
51 | setNewSubtaskDescription('');
52 | setIsAddingSubtask(false);
53 | } catch (error) {
54 | console.error('❌ TaskDetailsView: Failed to add subtask:', error);
55 | } finally {
56 | setIsSubmittingSubtask(false);
57 | }
58 | };
59 |
60 | const handleCancelAddSubtask = () => {
61 | setIsAddingSubtask(false);
62 | setNewSubtaskTitle('');
63 | setNewSubtaskDescription('');
64 | };
65 |
66 | if (
67 | !((currentTask.subtasks && currentTask.subtasks.length > 0) || !isSubtask)
68 | ) {
69 | return null;
70 | }
71 |
72 | const rightElement = (
73 | <>
74 | {currentTask.subtasks && currentTask.subtasks.length > 0 && (
75 | <span className="text-sm text-vscode-foreground/50">
76 | {currentTask.subtasks?.filter((st) => st.status === 'done').length}/
77 | {currentTask.subtasks?.length}
78 | </span>
79 | )}
80 | {!isSubtask && (
81 | <Button
82 | variant="ghost"
83 | size="sm"
84 | className="ml-auto p-1 h-6 w-6 hover:bg-vscode-button-hoverBackground"
85 | onClick={() => setIsAddingSubtask(true)}
86 | title="Add subtask"
87 | >
88 | <Plus className="w-4 h-4" />
89 | </Button>
90 | )}
91 | </>
92 | );
93 |
94 | return (
95 | <CollapsibleSection
96 | title="Sub-issues"
97 | defaultExpanded={true}
98 | rightElement={rightElement}
99 | >
100 | <div className="space-y-3">
101 | {/* Add Subtask Form */}
102 | {isAddingSubtask && (
103 | <div className="bg-widget-background rounded-lg p-4 border border-widget-border">
104 | <h4 className="text-sm font-medium text-vscode-foreground mb-3">
105 | Add New Subtask
106 | </h4>
107 | <div className="space-y-3">
108 | <div>
109 | <Label
110 | htmlFor="subtask-title"
111 | className="block text-sm text-vscode-foreground/80 mb-1"
112 | >
113 | Title*
114 | </Label>
115 | <input
116 | id="subtask-title"
117 | type="text"
118 | placeholder="Enter subtask title..."
119 | value={newSubtaskTitle}
120 | onChange={(e) => setNewSubtaskTitle(e.target.value)}
121 | className="w-full px-3 py-2 text-sm bg-vscode-input-background border border-vscode-input-border text-vscode-input-foreground placeholder-vscode-input-foreground/50 rounded focus:border-vscode-focusBorder focus:ring-1 focus:ring-vscode-focusBorder"
122 | disabled={isSubmittingSubtask}
123 | />
124 | </div>
125 |
126 | <div>
127 | <Label
128 | htmlFor="subtask-description"
129 | className="block text-sm text-vscode-foreground/80 mb-1"
130 | >
131 | Description (Optional)
132 | </Label>
133 | <Textarea
134 | id="subtask-description"
135 | placeholder="Enter subtask description..."
136 | value={newSubtaskDescription}
137 | onChange={(e) => setNewSubtaskDescription(e.target.value)}
138 | className="min-h-[80px] bg-vscode-input-background border-vscode-input-border text-vscode-input-foreground placeholder-vscode-input-foreground/50 focus:border-vscode-focusBorder focus:ring-vscode-focusBorder"
139 | disabled={isSubmittingSubtask}
140 | />
141 | </div>
142 |
143 | <div className="flex gap-3 pt-2">
144 | <Button
145 | onClick={handleAddSubtask}
146 | disabled={!newSubtaskTitle.trim() || isSubmittingSubtask}
147 | className="bg-primary text-primary-foreground hover:bg-primary/90"
148 | >
149 | {isSubmittingSubtask ? (
150 | <>
151 | <Loader2 className="w-4 h-4 mr-2 animate-spin" />
152 | Adding...
153 | </>
154 | ) : (
155 | <>
156 | <Plus className="w-4 h-4 mr-2" />
157 | Add Subtask
158 | </>
159 | )}
160 | </Button>
161 | <Button
162 | onClick={handleCancelAddSubtask}
163 | variant="outline"
164 | disabled={isSubmittingSubtask}
165 | className="border-widget-border"
166 | >
167 | Cancel
168 | </Button>
169 | </div>
170 | </div>
171 | </div>
172 | )}
173 |
174 | {/* Subtasks List */}
175 | {currentTask.subtasks && currentTask.subtasks.length > 0 && (
176 | <div className="space-y-2">
177 | {currentTask.subtasks.map((subtask, index) => {
178 | const subtaskId = `${currentTask.id}.${index + 1}`;
179 |
180 | return (
181 | <div
182 | key={subtask.id}
183 | className="flex items-center gap-3 p-3 rounded-md border border-textSeparator-foreground hover:border-vscode-border/70 transition-colors cursor-pointer"
184 | onClick={() => onNavigateToTask(subtaskId)}
185 | >
186 | <div
187 | className="w-4 h-4 rounded-full flex items-center justify-center"
188 | style={{
189 | backgroundColor: getStatusDotColor(subtask.status)
190 | }}
191 | />
192 | <div className="flex-1 min-w-0">
193 | <p className="text-sm text-vscode-foreground truncate">
194 | {subtask.title}
195 | </p>
196 | {subtask.description && (
197 | <p className="text-xs text-vscode-foreground/60 truncate mt-0.5">
198 | {subtask.description}
199 | </p>
200 | )}
201 | </div>
202 | <div className="flex items-center gap-2 flex-shrink-0">
203 | <Badge
204 | variant="secondary"
205 | className="text-xs bg-secondary/20 border-secondary/30 text-secondary-foreground px-2 py-0.5"
206 | >
207 | {subtask.status === 'pending' ? 'todo' : subtask.status}
208 | </Badge>
209 | </div>
210 | </div>
211 | );
212 | })}
213 | </div>
214 | )}
215 | </div>
216 | </CollapsibleSection>
217 | );
218 | };
219 |
```
--------------------------------------------------------------------------------
/mcp-server/src/custom-sdk/language-model.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * src/ai-providers/custom-sdk/mcp/language-model.js
3 | *
4 | * MCP Language Model implementation following AI SDK LanguageModelV1 interface.
5 | * Uses MCP session.requestSampling() for AI operations.
6 | */
7 |
8 | import {
9 | convertToMCPFormat,
10 | convertFromMCPFormat
11 | } from './message-converter.js';
12 | import { MCPError, mapMCPError } from './errors.js';
13 | import { extractJson } from './json-extractor.js';
14 | import {
15 | convertSchemaToInstructions,
16 | enhancePromptForJSON
17 | } from './schema-converter.js';
18 |
19 | /**
20 | * MCP Language Model implementing AI SDK LanguageModelV1 interface
21 | */
22 | export class MCPLanguageModel {
23 | specificationVersion = 'v1';
24 | defaultObjectGenerationMode = 'json';
25 | supportsImageUrls = false;
26 | supportsStructuredOutputs = true;
27 |
28 | constructor(options) {
29 | this.session = options.session; // MCP session object
30 | this.modelId = options.modelId;
31 | this.settings = options.settings || {};
32 | this.provider = 'mcp-ai-sdk';
33 | this.maxTokens = this.settings.maxTokens;
34 | this.temperature = this.settings.temperature;
35 |
36 | this.validateSession();
37 | }
38 |
39 | /**
40 | * Validate that the MCP session has required capabilities
41 | */
42 | validateSession() {
43 | if (!this.session?.clientCapabilities?.sampling) {
44 | throw new MCPError('MCP session must have client sampling capabilities');
45 | }
46 | }
47 |
48 | /**
49 | * Generate text using MCP session sampling
50 | * @param {object} options - Generation options
51 | * @param {Array} options.prompt - AI SDK prompt format
52 | * @param {AbortSignal} options.abortSignal - Abort signal
53 | * @returns {Promise<object>} Generation result in AI SDK format
54 | */
55 | async doGenerate(options) {
56 | try {
57 | // Convert AI SDK prompt to MCP format
58 | const { messages, systemPrompt } = convertToMCPFormat(options.prompt);
59 |
60 | // Use MCP session.requestSampling (same as MCPRemoteProvider)
61 | const response = await this.session.requestSampling(
62 | {
63 | messages,
64 | systemPrompt,
65 | temperature: this.settings.temperature,
66 | maxTokens: this.settings.maxTokens,
67 | includeContext: 'thisServer'
68 | },
69 | {
70 | // signal: options.abortSignal,
71 | timeout: 240000 // 4 minutes timeout
72 | }
73 | );
74 |
75 | // Convert MCP response back to AI SDK format
76 | const result = convertFromMCPFormat(response);
77 |
78 | return {
79 | text: result.text,
80 | finishReason: result.finishReason || 'stop',
81 | usage: {
82 | promptTokens: result.usage?.inputTokens || 0,
83 | completionTokens: result.usage?.outputTokens || 0,
84 | totalTokens:
85 | (result.usage?.inputTokens || 0) + (result.usage?.outputTokens || 0)
86 | },
87 | rawResponse: response,
88 | warnings: result.warnings
89 | };
90 | } catch (error) {
91 | throw mapMCPError(error);
92 | }
93 | }
94 |
95 | /**
96 | * Generate structured object using MCP session sampling
97 | * @param {object} options - Generation options
98 | * @param {Array} options.prompt - AI SDK prompt format
99 | * @param {import('zod').ZodSchema} options.schema - Zod schema for validation
100 | * @param {string} [options.mode='json'] - Generation mode ('json' or 'tool')
101 | * @param {AbortSignal} options.abortSignal - Abort signal
102 | * @returns {Promise<object>} Generation result with structured object
103 | */
104 | async doGenerateObject(options) {
105 | try {
106 | const { schema, mode = 'json', ...restOptions } = options;
107 |
108 | if (!schema) {
109 | throw new MCPError('Schema is required for object generation');
110 | }
111 |
112 | // Convert schema to JSON instructions
113 | const objectName = restOptions.objectName || 'generated_object';
114 | const jsonInstructions = convertSchemaToInstructions(schema, objectName);
115 |
116 | // Enhance prompt with JSON generation instructions
117 | const enhancedPrompt = enhancePromptForJSON(
118 | options.prompt,
119 | jsonInstructions
120 | );
121 |
122 | // Convert enhanced prompt to MCP format
123 | const { messages, systemPrompt } = convertToMCPFormat(enhancedPrompt);
124 |
125 | // Use MCP session.requestSampling with enhanced prompt
126 | const response = await this.session.requestSampling(
127 | {
128 | messages,
129 | systemPrompt,
130 | temperature: this.settings.temperature,
131 | maxTokens: this.settings.maxTokens,
132 | includeContext: 'thisServer'
133 | },
134 | {
135 | timeout: 240000 // 4 minutes timeout
136 | }
137 | );
138 |
139 | // Convert MCP response back to AI SDK format
140 | const result = convertFromMCPFormat(response);
141 |
142 | // Extract JSON from the response text
143 | const jsonText = extractJson(result.text);
144 |
145 | // Parse and validate JSON
146 | let parsedObject;
147 | try {
148 | parsedObject = JSON.parse(jsonText);
149 | } catch (parseError) {
150 | throw new MCPError(
151 | `Failed to parse JSON response: ${parseError.message}. Response: ${result.text.substring(0, 200)}...`
152 | );
153 | }
154 |
155 | // Validate against schema
156 | try {
157 | const validatedObject = schema.parse(parsedObject);
158 |
159 | return {
160 | object: validatedObject,
161 | finishReason: result.finishReason || 'stop',
162 | usage: {
163 | promptTokens: result.usage?.inputTokens || 0,
164 | completionTokens: result.usage?.outputTokens || 0,
165 | totalTokens:
166 | (result.usage?.inputTokens || 0) +
167 | (result.usage?.outputTokens || 0)
168 | },
169 | rawResponse: response,
170 | warnings: result.warnings
171 | };
172 | } catch (validationError) {
173 | throw new MCPError(
174 | `Generated object does not match schema: ${validationError.message}. Generated: ${JSON.stringify(parsedObject, null, 2)}`
175 | );
176 | }
177 | } catch (error) {
178 | throw mapMCPError(error);
179 | }
180 | }
181 |
182 | /**
183 | * Stream text generation using MCP session sampling
184 | * Note: MCP may not support native streaming, so this may simulate streaming
185 | * @param {object} options - Generation options
186 | * @returns {AsyncIterable} Stream of generation chunks
187 | */
188 | async doStream(options) {
189 | try {
190 | // For now, simulate streaming by chunking the complete response
191 | // TODO: Implement native streaming if MCP supports it
192 | const result = await this.doGenerate(options);
193 |
194 | // Create async generator that yields chunks
195 | return this.simulateStreaming(result);
196 | } catch (error) {
197 | throw mapMCPError(error);
198 | }
199 | }
200 |
201 | /**
202 | * Simulate streaming by chunking a complete response
203 | * @param {object} result - Complete generation result
204 | * @returns {AsyncIterable} Simulated stream chunks
205 | */
206 | async *simulateStreaming(result) {
207 | const text = result.text;
208 | const chunkSize = Math.max(1, Math.floor(text.length / 10)); // 10 chunks
209 |
210 | for (let i = 0; i < text.length; i += chunkSize) {
211 | const chunk = text.slice(i, i + chunkSize);
212 | const isLast = i + chunkSize >= text.length;
213 |
214 | yield {
215 | type: 'text-delta',
216 | textDelta: chunk
217 | };
218 |
219 | // Small delay to simulate streaming
220 | await new Promise((resolve) => setTimeout(resolve, 50));
221 | }
222 |
223 | // Final chunk with finish reason and usage
224 | yield {
225 | type: 'finish',
226 | finishReason: result.finishReason,
227 | usage: result.usage
228 | };
229 | }
230 | }
231 |
```
--------------------------------------------------------------------------------
/scripts/test-claude-errors.js:
--------------------------------------------------------------------------------
```javascript
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * test-claude-errors.js
5 | *
6 | * A test script to verify the error handling and retry logic in the callClaude function.
7 | * This script creates a modified version of dev.js that simulates different error scenarios.
8 | */
9 |
10 | import fs from 'fs';
11 | import path from 'path';
12 | import dotenv from 'dotenv';
13 | import { fileURLToPath } from 'url';
14 | import { dirname } from 'path';
15 | import { execSync, spawn } from 'child_process';
16 |
17 | const __filename = fileURLToPath(import.meta.url);
18 | const __dirname = dirname(__filename);
19 |
20 | // Load environment variables from .env file
21 | dotenv.config();
22 |
23 | // Create a simple PRD for testing
24 | const createTestPRD = () => {
25 | return `# Test PRD for Error Handling
26 |
27 | ## Overview
28 | This is a simple test PRD to verify the error handling in the callClaude function.
29 |
30 | ## Requirements
31 | 1. Create a simple web application
32 | 2. Implement user authentication
33 | 3. Add a dashboard for users
34 | `;
35 | };
36 |
37 | // Create a modified version of dev.js that simulates errors
38 | function createErrorSimulationScript(errorType, failureCount = 2) {
39 | // Read the original dev.js file
40 | const devJsPath = path.join(__dirname, 'dev.js');
41 | const devJsContent = fs.readFileSync(devJsPath, 'utf8');
42 |
43 | // Create a modified version that simulates errors
44 | let modifiedContent = devJsContent;
45 |
46 | // Find the anthropic.messages.create call and replace it with our mock
47 | const anthropicCallRegex =
48 | /const response = await anthropic\.messages\.create\(/;
49 |
50 | let mockCode = '';
51 |
52 | switch (errorType) {
53 | case 'network':
54 | mockCode = `
55 | // Mock for network error simulation
56 | let currentAttempt = 0;
57 | const failureCount = ${failureCount};
58 |
59 | // Simulate network error for the first few attempts
60 | currentAttempt++;
61 | console.log(\`[Mock] API call attempt \${currentAttempt}\`);
62 |
63 | if (currentAttempt <= failureCount) {
64 | console.log(\`[Mock] Simulating network error (attempt \${currentAttempt}/\${failureCount})\`);
65 | throw new Error('Network error: Connection refused');
66 | }
67 |
68 | const response = await anthropic.messages.create(`;
69 | break;
70 |
71 | case 'timeout':
72 | mockCode = `
73 | // Mock for timeout error simulation
74 | let currentAttempt = 0;
75 | const failureCount = ${failureCount};
76 |
77 | // Simulate timeout error for the first few attempts
78 | currentAttempt++;
79 | console.log(\`[Mock] API call attempt \${currentAttempt}\`);
80 |
81 | if (currentAttempt <= failureCount) {
82 | console.log(\`[Mock] Simulating timeout error (attempt \${currentAttempt}/\${failureCount})\`);
83 | throw new Error('Request timed out after 60000ms');
84 | }
85 |
86 | const response = await anthropic.messages.create(`;
87 | break;
88 |
89 | case 'invalid-json':
90 | mockCode = `
91 | // Mock for invalid JSON response
92 | let currentAttempt = 0;
93 | const failureCount = ${failureCount};
94 |
95 | // Simulate invalid JSON for the first few attempts
96 | currentAttempt++;
97 | console.log(\`[Mock] API call attempt \${currentAttempt}\`);
98 |
99 | if (currentAttempt <= failureCount) {
100 | console.log(\`[Mock] Simulating invalid JSON response (attempt \${currentAttempt}/\${failureCount})\`);
101 | return {
102 | content: [
103 | {
104 | text: \`\`\`json\\n{"meta": {"projectName": "Test Project"}, "tasks": [{"id": 1, "title": "Task 1"\`
105 | }
106 | ]
107 | };
108 | }
109 |
110 | const response = await anthropic.messages.create(`;
111 | break;
112 |
113 | case 'empty-tasks':
114 | mockCode = `
115 | // Mock for empty tasks array
116 | let currentAttempt = 0;
117 | const failureCount = ${failureCount};
118 |
119 | // Simulate empty tasks array for the first few attempts
120 | currentAttempt++;
121 | console.log(\`[Mock] API call attempt \${currentAttempt}\`);
122 |
123 | if (currentAttempt <= failureCount) {
124 | console.log(\`[Mock] Simulating empty tasks array (attempt \${currentAttempt}/\${failureCount})\`);
125 | return {
126 | content: [
127 | {
128 | text: \`\`\`json\\n{"meta": {"projectName": "Test Project"}, "tasks": []}\\n\`\`\`
129 | }
130 | ]
131 | };
132 | }
133 |
134 | const response = await anthropic.messages.create(`;
135 | break;
136 |
137 | default:
138 | // No modification
139 | mockCode = `const response = await anthropic.messages.create(`;
140 | }
141 |
142 | // Replace the anthropic call with our mock
143 | modifiedContent = modifiedContent.replace(anthropicCallRegex, mockCode);
144 |
145 | // Write the modified script to a temporary file
146 | const tempScriptPath = path.join(__dirname, `temp-dev-${errorType}.js`);
147 | fs.writeFileSync(tempScriptPath, modifiedContent, 'utf8');
148 |
149 | return tempScriptPath;
150 | }
151 |
152 | // Function to run a test with a specific error type
153 | async function runErrorTest(errorType, numTasks = 5, failureCount = 2) {
154 | console.log(`\n=== Test: ${errorType.toUpperCase()} Error Simulation ===`);
155 |
156 | // Create a test PRD
157 | const testPRD = createTestPRD();
158 | const testPRDPath = path.join(__dirname, `test-prd-${errorType}.txt`);
159 | fs.writeFileSync(testPRDPath, testPRD, 'utf8');
160 |
161 | // Create a modified dev.js that simulates the specified error
162 | const tempScriptPath = createErrorSimulationScript(errorType, failureCount);
163 |
164 | console.log(`Created test PRD at ${testPRDPath}`);
165 | console.log(`Created error simulation script at ${tempScriptPath}`);
166 | console.log(
167 | `Running with error type: ${errorType}, failure count: ${failureCount}, tasks: ${numTasks}`
168 | );
169 |
170 | try {
171 | // Run the modified script
172 | execSync(
173 | `node ${tempScriptPath} parse-prd --input=${testPRDPath} --tasks=${numTasks}`,
174 | {
175 | stdio: 'inherit'
176 | }
177 | );
178 | console.log(`${errorType} error test completed successfully`);
179 | } catch (error) {
180 | console.error(`${errorType} error test failed:`, error.message);
181 | } finally {
182 | // Clean up temporary files
183 | if (fs.existsSync(tempScriptPath)) {
184 | fs.unlinkSync(tempScriptPath);
185 | }
186 | if (fs.existsSync(testPRDPath)) {
187 | fs.unlinkSync(testPRDPath);
188 | }
189 | }
190 | }
191 |
192 | // Function to run all error tests
193 | async function runAllErrorTests() {
194 | console.log('Starting error handling tests for callClaude function...');
195 |
196 | // Test 1: Network error with automatic retry
197 | await runErrorTest('network', 5, 2);
198 |
199 | // Test 2: Timeout error with automatic retry
200 | await runErrorTest('timeout', 5, 2);
201 |
202 | // Test 3: Invalid JSON response with task reduction
203 | await runErrorTest('invalid-json', 10, 2);
204 |
205 | // Test 4: Empty tasks array with task reduction
206 | await runErrorTest('empty-tasks', 15, 2);
207 |
208 | // Test 5: Exhausted retries (more failures than MAX_RETRIES)
209 | await runErrorTest('network', 5, 4);
210 |
211 | console.log('\nAll error tests completed!');
212 | }
213 |
214 | // Run the tests
215 | runAllErrorTests().catch((error) => {
216 | console.error('Error running tests:', error);
217 | process.exit(1);
218 | });
219 |
```
--------------------------------------------------------------------------------
/apps/docs/command-reference.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: "Task Master Commands"
3 | description: "A comprehensive reference of all available Task Master commands"
4 | ---
5 |
6 | <AccordionGroup>
7 | <Accordion title="Parse PRD">
8 | ```bash
9 | # Parse a PRD file and generate tasks
10 | task-master parse-prd <prd-file.txt>
11 |
12 | # Limit the number of tasks generated
13 | task-master parse-prd <prd-file.txt> --num-tasks=10
14 | ```
15 | </Accordion>
16 |
17 | <Accordion title="List Tasks">
18 | ```bash
19 | # List all tasks
20 | task-master list
21 |
22 | # List tasks with a specific status
23 | task-master list --status=<status>
24 |
25 | # List tasks with subtasks
26 | task-master list --with-subtasks
27 |
28 | # List tasks with a specific status and include subtasks
29 | task-master list --status=<status> --with-subtasks
30 | ```
31 | </Accordion>
32 |
33 | <Accordion title="Show Next Task">
34 | ```bash
35 | # Show the next task to work on based on dependencies and status
36 | task-master next
37 | ```
38 | </Accordion>
39 |
40 | <Accordion title="Show Specific Task">
41 | ```bash
42 | # Show details of a specific task
43 | task-master show <id>
44 | # or
45 | task-master show --id=<id>
46 |
47 | # View a specific subtask (e.g., subtask 2 of task 1)
48 | task-master show 1.2
49 | ```
50 | </Accordion>
51 |
52 | <Accordion title="Update Tasks">
53 | ```bash
54 | # Update tasks from a specific ID and provide context
55 | task-master update --from=<id> --prompt="<prompt>"
56 | ```
57 | </Accordion>
58 |
59 | <Accordion title="Update a Specific Task">
60 | ```bash
61 | # Update a single task by ID with new information
62 | task-master update-task --id=<id> --prompt="<prompt>"
63 |
64 | # Use research-backed updates with Perplexity AI
65 | task-master update-task --id=<id> --prompt="<prompt>" --research
66 | ```
67 | </Accordion>
68 |
69 | <Accordion title="Update a Subtask">
70 | ```bash
71 | # Append additional information to a specific subtask
72 | task-master update-subtask --id=<parentId.subtaskId> --prompt="<prompt>"
73 |
74 | # Example: Add details about API rate limiting to subtask 2 of task 5
75 | task-master update-subtask --id=5.2 --prompt="Add rate limiting of 100 requests per minute"
76 |
77 | # Use research-backed updates with Perplexity AI
78 | task-master update-subtask --id=<parentId.subtaskId> --prompt="<prompt>" --research
79 | ```
80 |
81 | Unlike the `update-task` command which replaces task information, the `update-subtask` command _appends_ new information to the existing subtask details, marking it with a timestamp. This is useful for iteratively enhancing subtasks while preserving the original content.
82 | </Accordion>
83 |
84 | <Accordion title="Generate Task Files">
85 | ```bash
86 | # Generate individual task files from tasks.json
87 | task-master generate
88 | ```
89 | </Accordion>
90 |
91 | <Accordion title="Set Task Status">
92 | ```bash
93 | # Set status of a single task
94 | task-master set-status --id=<id> --status=<status>
95 |
96 | # Set status for multiple tasks
97 | task-master set-status --id=1,2,3 --status=<status>
98 |
99 | # Set status for subtasks
100 | task-master set-status --id=1.1,1.2 --status=<status>
101 | ```
102 |
103 | When marking a task as "done", all of its subtasks will automatically be marked as "done" as well.
104 | </Accordion>
105 |
106 | <Accordion title="Expand Tasks">
107 | ```bash
108 | # Expand a specific task with subtasks
109 | task-master expand --id=<id> --num=<number>
110 |
111 | # Expand with additional context
112 | task-master expand --id=<id> --prompt="<context>"
113 |
114 | # Expand all pending tasks
115 | task-master expand --all
116 |
117 | # Force regeneration of subtasks for tasks that already have them
118 | task-master expand --all --force
119 |
120 | # Research-backed subtask generation for a specific task
121 | task-master expand --id=<id> --research
122 |
123 | # Research-backed generation for all tasks
124 | task-master expand --all --research
125 | ```
126 | </Accordion>
127 |
128 | <Accordion title="Clear Subtasks">
129 | ```bash
130 | # Clear subtasks from a specific task
131 | task-master clear-subtasks --id=<id>
132 |
133 | # Clear subtasks from multiple tasks
134 | task-master clear-subtasks --id=1,2,3
135 |
136 | # Clear subtasks from all tasks
137 | task-master clear-subtasks --all
138 | ```
139 | </Accordion>
140 |
141 | <Accordion title="Analyze Task Complexity">
142 | ```bash
143 | # Analyze complexity of all tasks
144 | task-master analyze-complexity
145 |
146 | # Save report to a custom location
147 | task-master analyze-complexity --output=my-report.json
148 |
149 | # Use a specific LLM model
150 | task-master analyze-complexity --model=claude-3-opus-20240229
151 |
152 | # Set a custom complexity threshold (1-10)
153 | task-master analyze-complexity --threshold=6
154 |
155 | # Use an alternative tasks file
156 | task-master analyze-complexity --file=custom-tasks.json
157 |
158 | # Use Perplexity AI for research-backed complexity analysis
159 | task-master analyze-complexity --research
160 | ```
161 | </Accordion>
162 |
163 | <Accordion title="View Complexity Report">
164 | ```bash
165 | # Display the task complexity analysis report
166 | task-master complexity-report
167 |
168 | # View a report at a custom location
169 | task-master complexity-report --file=my-report.json
170 | ```
171 | </Accordion>
172 |
173 | <Accordion title="Managing Task Dependencies">
174 | ```bash
175 | # Add a dependency to a task
176 | task-master add-dependency --id=<id> --depends-on=<id>
177 |
178 | # Remove a dependency from a task
179 | task-master remove-dependency --id=<id> --depends-on=<id>
180 |
181 | # Validate dependencies without fixing them
182 | task-master validate-dependencies
183 |
184 | # Find and fix invalid dependencies automatically
185 | task-master fix-dependencies
186 | ```
187 | </Accordion>
188 |
189 | <Accordion title="Add a New Task">
190 | ```bash
191 | # Add a new task using AI
192 | task-master add-task --prompt="Description of the new task"
193 |
194 | # Add a task with dependencies
195 | task-master add-task --prompt="Description" --dependencies=1,2,3
196 |
197 | # Add a task with priority
198 | task-master add-task --prompt="Description" --priority=high
199 | ```
200 | </Accordion>
201 |
202 | <Accordion title="Initialize a Project">
203 | ```bash
204 | # Initialize a new project with Task Master structure
205 | task-master init
206 | ```
207 | </Accordion>
208 |
209 | <Accordion title="TDD Workflow (Autopilot)">
210 | ```bash
211 | # Start autonomous TDD workflow for a task
212 | task-master autopilot start <taskId>
213 |
214 | # Get next action with context
215 | task-master autopilot next
216 |
217 | # Complete phase with test results
218 | task-master autopilot complete --results '{"total":N,"passed":N,"failed":N}'
219 |
220 | # Commit changes
221 | task-master autopilot commit
222 |
223 | # Check workflow status
224 | task-master autopilot status
225 |
226 | # Resume interrupted workflow
227 | task-master autopilot resume
228 |
229 | # Abort workflow
230 | task-master autopilot abort
231 | ```
232 |
233 | The TDD workflow enforces RED → GREEN → COMMIT cycles for each subtask. See [AI Agent Integration](/tdd-workflow/ai-agent-integration) for details.
234 | </Accordion>
235 | </AccordionGroup>
236 |
```
--------------------------------------------------------------------------------
/.github/workflows/log-issue-events.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Log GitHub Issue Events
2 |
3 | on:
4 | issues:
5 | types: [opened, closed]
6 |
7 | jobs:
8 | log-issue-created:
9 | if: github.event.action == 'opened'
10 | runs-on: ubuntu-latest
11 | timeout-minutes: 5
12 | permissions:
13 | contents: read
14 | issues: read
15 |
16 | steps:
17 | - name: Log issue creation to Statsig
18 | env:
19 | STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}
20 | run: |
21 | ISSUE_NUMBER=${{ github.event.issue.number }}
22 | REPO=${{ github.repository }}
23 | ISSUE_TITLE=$(echo '${{ github.event.issue.title }}' | sed "s/'/'\\\\''/g")
24 | AUTHOR="${{ github.event.issue.user.login }}"
25 | CREATED_AT="${{ github.event.issue.created_at }}"
26 |
27 | if [ -z "$STATSIG_API_KEY" ]; then
28 | echo "STATSIG_API_KEY not found, skipping Statsig logging"
29 | exit 0
30 | fi
31 |
32 | # Prepare the event payload
33 | EVENT_PAYLOAD=$(jq -n \
34 | --arg issue_number "$ISSUE_NUMBER" \
35 | --arg repo "$REPO" \
36 | --arg title "$ISSUE_TITLE" \
37 | --arg author "$AUTHOR" \
38 | --arg created_at "$CREATED_AT" \
39 | '{
40 | events: [{
41 | eventName: "github_issue_created",
42 | value: 1,
43 | metadata: {
44 | repository: $repo,
45 | issue_number: ($issue_number | tonumber),
46 | issue_title: $title,
47 | issue_author: $author,
48 | created_at: $created_at
49 | },
50 | time: (now | floor | tostring)
51 | }]
52 | }')
53 |
54 | # Send to Statsig API
55 | echo "Logging issue creation to Statsig for issue #${ISSUE_NUMBER}"
56 |
57 | RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://events.statsigapi.net/v1/log_event \
58 | -H "Content-Type: application/json" \
59 | -H "STATSIG-API-KEY: ${STATSIG_API_KEY}" \
60 | -d "$EVENT_PAYLOAD")
61 |
62 | HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
63 | BODY=$(echo "$RESPONSE" | head -n-1)
64 |
65 | if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 202 ]; then
66 | echo "Successfully logged issue creation for issue #${ISSUE_NUMBER}"
67 | else
68 | echo "Failed to log issue creation for issue #${ISSUE_NUMBER}. HTTP ${HTTP_CODE}: ${BODY}"
69 | fi
70 |
71 | log-issue-closed:
72 | if: github.event.action == 'closed'
73 | runs-on: ubuntu-latest
74 | timeout-minutes: 5
75 | permissions:
76 | contents: read
77 | issues: read
78 |
79 | steps:
80 | - name: Log issue closure to Statsig
81 | env:
82 | STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}
83 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
84 | run: |
85 | ISSUE_NUMBER=${{ github.event.issue.number }}
86 | REPO=${{ github.repository }}
87 | ISSUE_TITLE=$(echo '${{ github.event.issue.title }}' | sed "s/'/'\\\\''/g")
88 | CLOSED_BY="${{ github.event.issue.closed_by.login }}"
89 | CLOSED_AT="${{ github.event.issue.closed_at }}"
90 | STATE_REASON="${{ github.event.issue.state_reason }}"
91 |
92 | if [ -z "$STATSIG_API_KEY" ]; then
93 | echo "STATSIG_API_KEY not found, skipping Statsig logging"
94 | exit 0
95 | fi
96 |
97 | # Get additional issue data via GitHub API
98 | echo "Fetching additional issue data for #${ISSUE_NUMBER}"
99 | ISSUE_DATA=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
100 | -H "Accept: application/vnd.github.v3+json" \
101 | "https://api.github.com/repos/${REPO}/issues/${ISSUE_NUMBER}")
102 |
103 | COMMENTS_COUNT=$(echo "$ISSUE_DATA" | jq -r '.comments')
104 |
105 | # Get reactions data
106 | REACTIONS_DATA=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
107 | -H "Accept: application/vnd.github.v3+json" \
108 | "https://api.github.com/repos/${REPO}/issues/${ISSUE_NUMBER}/reactions")
109 |
110 | REACTIONS_COUNT=$(echo "$REACTIONS_DATA" | jq '. | length')
111 |
112 | # Check if issue was closed automatically (by checking if closed_by is a bot)
113 | CLOSED_AUTOMATICALLY="false"
114 | if [[ "$CLOSED_BY" == *"[bot]"* ]]; then
115 | CLOSED_AUTOMATICALLY="true"
116 | fi
117 |
118 | # Check if closed as duplicate by state_reason
119 | CLOSED_AS_DUPLICATE="false"
120 | if [ "$STATE_REASON" = "duplicate" ]; then
121 | CLOSED_AS_DUPLICATE="true"
122 | fi
123 |
124 | # Prepare the event payload
125 | EVENT_PAYLOAD=$(jq -n \
126 | --arg issue_number "$ISSUE_NUMBER" \
127 | --arg repo "$REPO" \
128 | --arg title "$ISSUE_TITLE" \
129 | --arg closed_by "$CLOSED_BY" \
130 | --arg closed_at "$CLOSED_AT" \
131 | --arg state_reason "$STATE_REASON" \
132 | --arg comments_count "$COMMENTS_COUNT" \
133 | --arg reactions_count "$REACTIONS_COUNT" \
134 | --arg closed_automatically "$CLOSED_AUTOMATICALLY" \
135 | --arg closed_as_duplicate "$CLOSED_AS_DUPLICATE" \
136 | '{
137 | events: [{
138 | eventName: "github_issue_closed",
139 | value: 1,
140 | metadata: {
141 | repository: $repo,
142 | issue_number: ($issue_number | tonumber),
143 | issue_title: $title,
144 | closed_by: $closed_by,
145 | closed_at: $closed_at,
146 | state_reason: $state_reason,
147 | comments_count: ($comments_count | tonumber),
148 | reactions_count: ($reactions_count | tonumber),
149 | closed_automatically: ($closed_automatically | test("true")),
150 | closed_as_duplicate: ($closed_as_duplicate | test("true"))
151 | },
152 | time: (now | floor | tostring)
153 | }]
154 | }')
155 |
156 | # Send to Statsig API
157 | echo "Logging issue closure to Statsig for issue #${ISSUE_NUMBER}"
158 |
159 | RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://events.statsigapi.net/v1/log_event \
160 | -H "Content-Type: application/json" \
161 | -H "STATSIG-API-KEY: ${STATSIG_API_KEY}" \
162 | -d "$EVENT_PAYLOAD")
163 |
164 | HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
165 | BODY=$(echo "$RESPONSE" | head -n-1)
166 |
167 | if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 202 ]; then
168 | echo "Successfully logged issue closure for issue #${ISSUE_NUMBER}"
169 | echo "Closed by: $CLOSED_BY"
170 | echo "Comments: $COMMENTS_COUNT"
171 | echo "Reactions: $REACTIONS_COUNT"
172 | echo "Closed automatically: $CLOSED_AUTOMATICALLY"
173 | echo "Closed as duplicate: $CLOSED_AS_DUPLICATE"
174 | else
175 | echo "Failed to log issue closure for issue #${ISSUE_NUMBER}. HTTP ${HTTP_CODE}: ${BODY}"
176 | fi
177 |
```
--------------------------------------------------------------------------------
/docs/examples.md:
--------------------------------------------------------------------------------
```markdown
1 | # Example Cursor AI Interactions
2 |
3 | Here are some common interactions with Cursor AI when using Task Master:
4 |
5 | ## Starting a new project
6 |
7 | ```
8 | I've just initialized a new project with Claude Task Master. I have a PRD at .taskmaster/docs/prd.txt.
9 | Can you help me parse it and set up the initial tasks?
10 | ```
11 |
12 | ## Working on tasks
13 |
14 | ```
15 | What's the next task I should work on? Please consider dependencies and priorities.
16 | ```
17 |
18 | ## Implementing a specific task
19 |
20 | ```
21 | I'd like to implement task 4. Can you help me understand what needs to be done and how to approach it?
22 | ```
23 |
24 | ## Viewing multiple tasks
25 |
26 | ```
27 | Can you show me tasks 1, 3, and 5 so I can understand their relationship?
28 | ```
29 |
30 | ```
31 | I need to see the status of tasks 44, 55, and their subtasks. Can you show me those?
32 | ```
33 |
34 | ```
35 | Show me tasks 10, 12, and 15 and give me some batch actions I can perform on them.
36 | ```
37 |
38 | ## Managing subtasks
39 |
40 | ```
41 | I need to regenerate the subtasks for task 3 with a different approach. Can you help me clear and regenerate them?
42 | ```
43 |
44 | ## Handling changes
45 |
46 | ```
47 | I've decided to use MongoDB instead of PostgreSQL. Can you update all future tasks to reflect this change?
48 | ```
49 |
50 | ## Completing work
51 |
52 | ```
53 | I've finished implementing the authentication system described in task 2. All tests are passing.
54 | Please mark it as complete and tell me what I should work on next.
55 | ```
56 |
57 | ## Reorganizing tasks
58 |
59 | ```
60 | I think subtask 5.2 would fit better as part of task 7. Can you move it there?
61 | ```
62 |
63 | (Agent runs: `task-master move --from=5.2 --to=7.3`)
64 |
65 | ```
66 | Task 8 should actually be a subtask of task 4. Can you reorganize this?
67 | ```
68 |
69 | (Agent runs: `task-master move --from=8 --to=4.1`)
70 |
71 | ```
72 | I just merged the main branch and there's a conflict in tasks.json. My teammates created tasks 10-15 on their branch while I created tasks 10-12 on my branch. Can you help me resolve this by moving my tasks?
73 | ```
74 |
75 | (Agent runs:
76 |
77 | ```bash
78 | task-master move --from=10 --to=16
79 | task-master move --from=11 --to=17
80 | task-master move --from=12 --to=18
81 | ```
82 |
83 | )
84 |
85 | ## Analyzing complexity
86 |
87 | ```
88 | Can you analyze the complexity of our tasks to help me understand which ones need to be broken down further?
89 | ```
90 |
91 | ## Viewing complexity report
92 |
93 | ```
94 | Can you show me the complexity report in a more readable format?
95 | ```
96 |
97 | ### Breaking Down Complex Tasks
98 |
99 | ```
100 | Task 5 seems complex. Can you break it down into subtasks?
101 | ```
102 |
103 | (Agent runs: `task-master expand --id=5`)
104 |
105 | ```
106 | Please break down task 5 using research-backed generation.
107 | ```
108 |
109 | (Agent runs: `task-master expand --id=5 --research`)
110 |
111 | ### Updating Tasks with Research
112 |
113 | ```
114 | We need to update task 15 based on the latest React Query v5 changes. Can you research this and update the task?
115 | ```
116 |
117 | (Agent runs: `task-master update-task --id=15 --prompt="Update based on React Query v5 changes" --research`)
118 |
119 | ### Adding Tasks with Research
120 |
121 | ```
122 | Please add a new task to implement user profile image uploads using Cloudinary, research the best approach.
123 | ```
124 |
125 | (Agent runs: `task-master add-task --prompt="Implement user profile image uploads using Cloudinary" --research`)
126 |
127 | ## Research-Driven Development
128 |
129 | ### Getting Fresh Information
130 |
131 | ```
132 | Research the latest best practices for implementing JWT authentication in Node.js applications.
133 | ```
134 |
135 | (Agent runs: `task-master research "Latest best practices for JWT authentication in Node.js"`)
136 |
137 | ### Research with Project Context
138 |
139 | ```
140 | I'm working on task 15 which involves API optimization. Can you research current best practices for our specific implementation?
141 | ```
142 |
143 | (Agent runs: `task-master research "API optimization best practices" --id=15 --files=src/api.js`)
144 |
145 | ### Research Before Implementation
146 |
147 | ```
148 | Before I implement task 8 (React Query integration), can you research the latest React Query v5 patterns and any breaking changes?
149 | ```
150 |
151 | (Agent runs: `task-master research "React Query v5 patterns and breaking changes" --id=8`)
152 |
153 | ### Research and Update Pattern
154 |
155 | ```
156 | Research the latest security recommendations for Express.js applications and update our authentication task with the findings.
157 | ```
158 |
159 | (Agent runs:
160 |
161 | 1. `task-master research "Latest Express.js security recommendations" --id=12`
162 | 2. `task-master update-subtask --id=12.3 --prompt="Updated with latest security findings: [research results]"`)
163 |
164 | ### Research for Debugging
165 |
166 | ```
167 | I'm having issues with our WebSocket implementation in task 20. Can you research common WebSocket problems and solutions?
168 | ```
169 |
170 | (Agent runs: `task-master research "Common WebSocket implementation problems and solutions" --id=20 --files=src/websocket.js`)
171 |
172 | ### Research Technology Comparisons
173 |
174 | ```
175 | We need to choose between Redis and Memcached for caching. Can you research the current recommendations for our use case?
176 | ```
177 |
178 | (Agent runs: `task-master research "Redis vs Memcached 2024 comparison for session caching" --tree`)
179 |
180 | ## Git Integration and Tag Management
181 |
182 | ### Creating Tags for Feature Branches
183 |
184 | ```
185 | I'm starting work on a new feature branch for user authentication. Can you create a matching task tag?
186 | ```
187 |
188 | (Agent runs: `task-master add-tag --from-branch`)
189 |
190 | ### Creating Named Tags
191 |
192 | ```
193 | Create a new tag called 'api-v2' for our API redesign work.
194 | ```
195 |
196 | (Agent runs: `task-master add-tag api-v2 --description="API v2 redesign tasks"`)
197 |
198 | ### Switching Tag Contexts
199 |
200 | ```
201 | Switch to the 'testing' tag so I can work on QA tasks.
202 | ```
203 |
204 | (Agent runs: `task-master use-tag testing`)
205 |
206 | ### Copying Tasks Between Tags
207 |
208 | ```
209 | I need to copy the current tasks to a new 'hotfix' tag for urgent fixes.
210 | ```
211 |
212 | (Agent runs: `task-master add-tag hotfix --copy-from-current --description="Urgent hotfix tasks"`)
213 |
214 | ### Managing Multiple Contexts
215 |
216 | ```
217 | Show me all available tags and their current status.
218 | ```
219 |
220 | (Agent runs: `task-master tags --show-metadata`)
221 |
222 | ### Tag Cleanup
223 |
224 | ```
225 | I've finished the 'user-auth' feature and merged the branch. Can you clean up the tag?
226 | ```
227 |
228 | (Agent runs: `task-master delete-tag user-auth`)
229 |
230 | ### Working with Tag-Specific Tasks
231 |
232 | ```
233 | List all tasks in the 'api-v2' tag context.
234 | ```
235 |
236 | (Agent runs: `task-master use-tag api-v2` then `task-master list`)
237 |
238 | ### Branch-Based Development Workflow
239 |
240 | ```
241 | I'm switching to work on the 'feature/payments' branch. Can you set up the task context for this?
242 | ```
243 |
244 | (Agent runs:
245 | 1. `git checkout feature/payments`
246 | 2. `task-master add-tag --from-branch --description="Payment system implementation"`
247 | 3. `task-master list` to show tasks in the new context)
248 |
249 | ### Parallel Feature Development
250 |
251 | ```
252 | I need to work on both authentication and payment features simultaneously. How should I organize the tasks?
253 | ```
254 |
255 | (Agent suggests and runs:
256 | 1. `task-master add-tag auth --description="Authentication feature tasks"`
257 | 2. `task-master add-tag payments --description="Payment system tasks"`
258 | 3. `task-master use-tag auth` to start with authentication work)
259 |
```
--------------------------------------------------------------------------------
/apps/mcp/src/tools/autopilot/commit.tool.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview autopilot-commit MCP tool
3 | * Create a git commit with automatic staging and message generation
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | handleApiResult,
9 | withNormalizedProjectRoot
10 | } from '../../shared/utils.js';
11 | import type { MCPContext } from '../../shared/types.js';
12 | import { WorkflowService, GitAdapter, CommitMessageGenerator } from '@tm/core';
13 | import type { FastMCP } from 'fastmcp';
14 |
15 | const CommitSchema = z.object({
16 | projectRoot: z
17 | .string()
18 | .describe('Absolute path to the project root directory'),
19 | files: z
20 | .array(z.string())
21 | .optional()
22 | .describe(
23 | 'Specific files to stage (relative to project root). If not provided, stages all changes.'
24 | ),
25 | customMessage: z
26 | .string()
27 | .optional()
28 | .describe('Custom commit message to use instead of auto-generated message')
29 | });
30 |
31 | type CommitArgs = z.infer<typeof CommitSchema>;
32 |
33 | /**
34 | * Register the autopilot_commit tool with the MCP server
35 | */
36 | export function registerAutopilotCommitTool(server: FastMCP) {
37 | server.addTool({
38 | name: 'autopilot_commit',
39 | description:
40 | 'Create a git commit with automatic staging, message generation, and metadata embedding. Generates appropriate commit messages based on subtask context and TDD phase.',
41 | parameters: CommitSchema,
42 | execute: withNormalizedProjectRoot(
43 | async (args: CommitArgs, context: MCPContext) => {
44 | const { projectRoot, files, customMessage } = args;
45 |
46 | try {
47 | context.log.info(`Creating commit for workflow in ${projectRoot}`);
48 |
49 | const workflowService = new WorkflowService(projectRoot);
50 |
51 | // Check if workflow exists
52 | if (!(await workflowService.hasWorkflow())) {
53 | return handleApiResult({
54 | result: {
55 | success: false,
56 | error: {
57 | message:
58 | 'No active workflow found. Start a workflow with autopilot_start'
59 | }
60 | },
61 | log: context.log,
62 | projectRoot
63 | });
64 | }
65 |
66 | // Resume workflow
67 | await workflowService.resumeWorkflow();
68 | const status = workflowService.getStatus();
69 | const workflowContext = workflowService.getContext();
70 |
71 | // Verify we're in COMMIT phase
72 | if (status.tddPhase !== 'COMMIT') {
73 | context.log.warn(
74 | `Not in COMMIT phase (currently in ${status.tddPhase})`
75 | );
76 | return handleApiResult({
77 | result: {
78 | success: false,
79 | error: {
80 | message: `Cannot commit: currently in ${status.tddPhase} phase. Complete the ${status.tddPhase} phase first using autopilot_complete_phase`
81 | }
82 | },
83 | log: context.log,
84 | projectRoot
85 | });
86 | }
87 |
88 | // Verify there's an active subtask
89 | if (!status.currentSubtask) {
90 | return handleApiResult({
91 | result: {
92 | success: false,
93 | error: { message: 'No active subtask to commit' }
94 | },
95 | log: context.log,
96 | projectRoot
97 | });
98 | }
99 |
100 | // Initialize git adapter
101 | const gitAdapter = new GitAdapter(projectRoot);
102 |
103 | // Stage files
104 | try {
105 | if (files && files.length > 0) {
106 | await gitAdapter.stageFiles(files);
107 | context.log.info(`Staged ${files.length} files`);
108 | } else {
109 | await gitAdapter.stageFiles(['.']);
110 | context.log.info('Staged all changes');
111 | }
112 | } catch (error: any) {
113 | context.log.error(`Failed to stage files: ${error.message}`);
114 | return handleApiResult({
115 | result: {
116 | success: false,
117 | error: { message: `Failed to stage files: ${error.message}` }
118 | },
119 | log: context.log,
120 | projectRoot
121 | });
122 | }
123 |
124 | // Check if there are staged changes
125 | const hasStagedChanges = await gitAdapter.hasStagedChanges();
126 | if (!hasStagedChanges) {
127 | context.log.warn('No staged changes to commit');
128 | return handleApiResult({
129 | result: {
130 | success: false,
131 | error: {
132 | message:
133 | 'No staged changes to commit. Make code changes before committing'
134 | }
135 | },
136 | log: context.log,
137 | projectRoot
138 | });
139 | }
140 |
141 | // Get git status for message generation
142 | const gitStatus = await gitAdapter.getStatus();
143 |
144 | // Generate commit message
145 | let commitMessage: string;
146 | if (customMessage) {
147 | commitMessage = customMessage;
148 | context.log.info('Using custom commit message');
149 | } else {
150 | const messageGenerator = new CommitMessageGenerator();
151 |
152 | // Determine commit type based on phase and subtask
153 | // RED phase = test files, GREEN phase = implementation
154 | const type = status.tddPhase === 'COMMIT' ? 'feat' : 'test';
155 |
156 | // Use subtask title as description
157 | const description = status.currentSubtask.title;
158 |
159 | // Construct proper CommitMessageOptions
160 | const options = {
161 | type,
162 | description,
163 | changedFiles: gitStatus.staged,
164 | taskId: status.taskId,
165 | phase: status.tddPhase,
166 | testsPassing: workflowContext.lastTestResults?.passed,
167 | testsFailing: workflowContext.lastTestResults?.failed
168 | };
169 |
170 | commitMessage = messageGenerator.generateMessage(options);
171 | context.log.info('Generated commit message automatically');
172 | }
173 |
174 | // Create commit
175 | try {
176 | await gitAdapter.createCommit(commitMessage);
177 | context.log.info('Commit created successfully');
178 | } catch (error: any) {
179 | context.log.error(`Failed to create commit: ${error.message}`);
180 | return handleApiResult({
181 | result: {
182 | success: false,
183 | error: { message: `Failed to create commit: ${error.message}` }
184 | },
185 | log: context.log,
186 | projectRoot
187 | });
188 | }
189 |
190 | // Get last commit info
191 | const lastCommit = await gitAdapter.getLastCommit();
192 |
193 | // Complete COMMIT phase and advance workflow
194 | const newStatus = await workflowService.commit();
195 |
196 | context.log.info(
197 | `Commit completed. Current phase: ${newStatus.tddPhase || newStatus.phase}`
198 | );
199 |
200 | const isComplete = newStatus.phase === 'COMPLETE';
201 |
202 | // Get next action with guidance
203 | const nextAction = workflowService.getNextAction();
204 |
205 | return handleApiResult({
206 | result: {
207 | success: true,
208 | data: {
209 | message: isComplete
210 | ? 'Workflow completed successfully'
211 | : 'Commit created and workflow advanced',
212 | commitSha: lastCommit.sha,
213 | commitMessage,
214 | ...newStatus,
215 | isComplete,
216 | nextAction: nextAction.action,
217 | nextSteps: nextAction.nextSteps
218 | }
219 | },
220 | log: context.log,
221 | projectRoot
222 | });
223 | } catch (error: any) {
224 | context.log.error(`Error in autopilot-commit: ${error.message}`);
225 | if (error.stack) {
226 | context.log.debug(error.stack);
227 | }
228 | return handleApiResult({
229 | result: {
230 | success: false,
231 | error: { message: `Failed to commit: ${error.message}` }
232 | },
233 | log: context.log,
234 | projectRoot
235 | });
236 | }
237 | }
238 | )
239 | });
240 | }
241 |
```