This is page 54 of 69. Use http://codebase.md/eyaltoledano/claude-task-master?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ └── README.md
├── .claude
│ ├── commands
│ │ └── dedupe.md
│ └── TM_COMMANDS_GUIDE.md
├── .claude-plugin
│ └── marketplace.json
├── .coderabbit.yaml
├── .cursor
│ ├── mcp.json
│ └── rules
│ ├── ai_providers.mdc
│ ├── ai_services.mdc
│ ├── architecture.mdc
│ ├── changeset.mdc
│ ├── commands.mdc
│ ├── context_gathering.mdc
│ ├── cursor_rules.mdc
│ ├── dependencies.mdc
│ ├── dev_workflow.mdc
│ ├── git_workflow.mdc
│ ├── glossary.mdc
│ ├── mcp.mdc
│ ├── new_features.mdc
│ ├── self_improve.mdc
│ ├── tags.mdc
│ ├── taskmaster.mdc
│ ├── tasks.mdc
│ ├── telemetry.mdc
│ ├── test_workflow.mdc
│ ├── tests.mdc
│ ├── ui.mdc
│ └── utilities.mdc
├── .cursorignore
├── .env.example
├── .github
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ ├── enhancements---feature-requests.md
│ │ └── feedback.md
│ ├── PULL_REQUEST_TEMPLATE
│ │ ├── bugfix.md
│ │ ├── config.yml
│ │ ├── feature.md
│ │ └── integration.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── scripts
│ │ ├── auto-close-duplicates.mjs
│ │ ├── backfill-duplicate-comments.mjs
│ │ ├── check-pre-release-mode.mjs
│ │ ├── parse-metrics.mjs
│ │ ├── release.mjs
│ │ ├── tag-extension.mjs
│ │ ├── utils.mjs
│ │ └── validate-changesets.mjs
│ └── workflows
│ ├── auto-close-duplicates.yml
│ ├── backfill-duplicate-comments.yml
│ ├── ci.yml
│ ├── claude-dedupe-issues.yml
│ ├── claude-docs-trigger.yml
│ ├── claude-docs-updater.yml
│ ├── claude-issue-triage.yml
│ ├── claude.yml
│ ├── extension-ci.yml
│ ├── extension-release.yml
│ ├── log-issue-events.yml
│ ├── pre-release.yml
│ ├── release-check.yml
│ ├── release.yml
│ ├── update-models-md.yml
│ └── weekly-metrics-discord.yml
├── .gitignore
├── .kiro
│ ├── hooks
│ │ ├── tm-code-change-task-tracker.kiro.hook
│ │ ├── tm-complexity-analyzer.kiro.hook
│ │ ├── tm-daily-standup-assistant.kiro.hook
│ │ ├── tm-git-commit-task-linker.kiro.hook
│ │ ├── tm-pr-readiness-checker.kiro.hook
│ │ ├── tm-task-dependency-auto-progression.kiro.hook
│ │ └── tm-test-success-task-completer.kiro.hook
│ ├── settings
│ │ └── mcp.json
│ └── steering
│ ├── dev_workflow.md
│ ├── kiro_rules.md
│ ├── self_improve.md
│ ├── taskmaster_hooks_workflow.md
│ └── taskmaster.md
├── .manypkg.json
├── .mcp.json
├── .npmignore
├── .nvmrc
├── .taskmaster
│ ├── CLAUDE.md
│ ├── config.json
│ ├── docs
│ │ ├── autonomous-tdd-git-workflow.md
│ │ ├── MIGRATION-ROADMAP.md
│ │ ├── prd-tm-start.txt
│ │ ├── prd.txt
│ │ ├── README.md
│ │ ├── research
│ │ │ ├── 2025-06-14_how-can-i-improve-the-scope-up-and-scope-down-comm.md
│ │ │ ├── 2025-06-14_should-i-be-using-any-specific-libraries-for-this.md
│ │ │ ├── 2025-06-14_test-save-functionality.md
│ │ │ ├── 2025-06-14_test-the-fix-for-duplicate-saves-final-test.md
│ │ │ └── 2025-08-01_do-we-need-to-add-new-commands-or-can-we-just-weap.md
│ │ ├── task-template-importing-prd.txt
│ │ ├── tdd-workflow-phase-0-spike.md
│ │ ├── tdd-workflow-phase-1-core-rails.md
│ │ ├── tdd-workflow-phase-1-orchestrator.md
│ │ ├── tdd-workflow-phase-2-pr-resumability.md
│ │ ├── tdd-workflow-phase-3-extensibility-guardrails.md
│ │ ├── test-prd.txt
│ │ └── tm-core-phase-1.txt
│ ├── reports
│ │ ├── task-complexity-report_autonomous-tdd-git-workflow.json
│ │ ├── task-complexity-report_cc-kiro-hooks.json
│ │ ├── task-complexity-report_tdd-phase-1-core-rails.json
│ │ ├── task-complexity-report_tdd-workflow-phase-0.json
│ │ ├── task-complexity-report_test-prd-tag.json
│ │ ├── task-complexity-report_tm-core-phase-1.json
│ │ ├── task-complexity-report.json
│ │ └── tm-core-complexity.json
│ ├── state.json
│ ├── tasks
│ │ ├── task_001_tm-start.txt
│ │ ├── task_002_tm-start.txt
│ │ ├── task_003_tm-start.txt
│ │ ├── task_004_tm-start.txt
│ │ ├── task_007_tm-start.txt
│ │ └── tasks.json
│ └── templates
│ ├── example_prd_rpg.md
│ └── example_prd.md
├── .vscode
│ ├── extensions.json
│ └── settings.json
├── apps
│ ├── cli
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── command-registry.ts
│ │ │ ├── commands
│ │ │ │ ├── auth.command.ts
│ │ │ │ ├── autopilot
│ │ │ │ │ ├── abort.command.ts
│ │ │ │ │ ├── commit.command.ts
│ │ │ │ │ ├── complete.command.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── next.command.ts
│ │ │ │ │ ├── resume.command.ts
│ │ │ │ │ ├── shared.ts
│ │ │ │ │ ├── start.command.ts
│ │ │ │ │ └── status.command.ts
│ │ │ │ ├── briefs.command.ts
│ │ │ │ ├── context.command.ts
│ │ │ │ ├── export.command.ts
│ │ │ │ ├── list.command.ts
│ │ │ │ ├── models
│ │ │ │ │ ├── custom-providers.ts
│ │ │ │ │ ├── fetchers.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── prompts.ts
│ │ │ │ │ ├── setup.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── next.command.ts
│ │ │ │ ├── set-status.command.ts
│ │ │ │ ├── show.command.ts
│ │ │ │ ├── start.command.ts
│ │ │ │ └── tags.command.ts
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ └── model-management.ts
│ │ │ ├── types
│ │ │ │ └── tag-management.d.ts
│ │ │ ├── ui
│ │ │ │ ├── components
│ │ │ │ │ ├── cardBox.component.ts
│ │ │ │ │ ├── dashboard.component.ts
│ │ │ │ │ ├── header.component.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── next-task.component.ts
│ │ │ │ │ ├── suggested-steps.component.ts
│ │ │ │ │ └── task-detail.component.ts
│ │ │ │ ├── display
│ │ │ │ │ ├── messages.ts
│ │ │ │ │ └── tables.ts
│ │ │ │ ├── formatters
│ │ │ │ │ ├── complexity-formatters.ts
│ │ │ │ │ ├── dependency-formatters.ts
│ │ │ │ │ ├── priority-formatters.ts
│ │ │ │ │ ├── status-formatters.spec.ts
│ │ │ │ │ └── status-formatters.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── layout
│ │ │ │ ├── helpers.spec.ts
│ │ │ │ └── helpers.ts
│ │ │ └── utils
│ │ │ ├── auth-helpers.ts
│ │ │ ├── auto-update.ts
│ │ │ ├── brief-selection.ts
│ │ │ ├── display-helpers.ts
│ │ │ ├── error-handler.ts
│ │ │ ├── index.ts
│ │ │ ├── project-root.ts
│ │ │ ├── task-status.ts
│ │ │ ├── ui.spec.ts
│ │ │ └── ui.ts
│ │ ├── tests
│ │ │ ├── integration
│ │ │ │ └── commands
│ │ │ │ └── autopilot
│ │ │ │ └── workflow.test.ts
│ │ │ └── unit
│ │ │ ├── commands
│ │ │ │ ├── autopilot
│ │ │ │ │ └── shared.test.ts
│ │ │ │ ├── list.command.spec.ts
│ │ │ │ └── show.command.spec.ts
│ │ │ └── ui
│ │ │ └── dashboard.component.spec.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── docs
│ │ ├── archive
│ │ │ ├── ai-client-utils-example.mdx
│ │ │ ├── ai-development-workflow.mdx
│ │ │ ├── command-reference.mdx
│ │ │ ├── configuration.mdx
│ │ │ ├── cursor-setup.mdx
│ │ │ ├── examples.mdx
│ │ │ └── Installation.mdx
│ │ ├── best-practices
│ │ │ ├── advanced-tasks.mdx
│ │ │ ├── configuration-advanced.mdx
│ │ │ └── index.mdx
│ │ ├── capabilities
│ │ │ ├── cli-root-commands.mdx
│ │ │ ├── index.mdx
│ │ │ ├── mcp.mdx
│ │ │ ├── rpg-method.mdx
│ │ │ └── task-structure.mdx
│ │ ├── CHANGELOG.md
│ │ ├── command-reference.mdx
│ │ ├── configuration.mdx
│ │ ├── docs.json
│ │ ├── favicon.svg
│ │ ├── getting-started
│ │ │ ├── api-keys.mdx
│ │ │ ├── contribute.mdx
│ │ │ ├── faq.mdx
│ │ │ └── quick-start
│ │ │ ├── configuration-quick.mdx
│ │ │ ├── execute-quick.mdx
│ │ │ ├── installation.mdx
│ │ │ ├── moving-forward.mdx
│ │ │ ├── prd-quick.mdx
│ │ │ ├── quick-start.mdx
│ │ │ ├── requirements.mdx
│ │ │ ├── rules-quick.mdx
│ │ │ └── tasks-quick.mdx
│ │ ├── introduction.mdx
│ │ ├── licensing.md
│ │ ├── logo
│ │ │ ├── dark.svg
│ │ │ ├── light.svg
│ │ │ └── task-master-logo.png
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── style.css
│ │ ├── tdd-workflow
│ │ │ ├── ai-agent-integration.mdx
│ │ │ └── quickstart.mdx
│ │ ├── vercel.json
│ │ └── whats-new.mdx
│ ├── extension
│ │ ├── .vscodeignore
│ │ ├── assets
│ │ │ ├── banner.png
│ │ │ ├── icon-dark.svg
│ │ │ ├── icon-light.svg
│ │ │ ├── icon.png
│ │ │ ├── screenshots
│ │ │ │ ├── kanban-board.png
│ │ │ │ └── task-details.png
│ │ │ └── sidebar-icon.svg
│ │ ├── CHANGELOG.md
│ │ ├── components.json
│ │ ├── docs
│ │ │ ├── extension-CI-setup.md
│ │ │ └── extension-development-guide.md
│ │ ├── esbuild.js
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── package.mjs
│ │ ├── package.publish.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── components
│ │ │ │ ├── ConfigView.tsx
│ │ │ │ ├── constants.ts
│ │ │ │ ├── TaskDetails
│ │ │ │ │ ├── AIActionsSection.tsx
│ │ │ │ │ ├── DetailsSection.tsx
│ │ │ │ │ ├── PriorityBadge.tsx
│ │ │ │ │ ├── SubtasksSection.tsx
│ │ │ │ │ ├── TaskMetadataSidebar.tsx
│ │ │ │ │ └── useTaskDetails.ts
│ │ │ │ ├── TaskDetailsView.tsx
│ │ │ │ ├── TaskMasterLogo.tsx
│ │ │ │ └── ui
│ │ │ │ ├── badge.tsx
│ │ │ │ ├── breadcrumb.tsx
│ │ │ │ ├── button.tsx
│ │ │ │ ├── card.tsx
│ │ │ │ ├── collapsible.tsx
│ │ │ │ ├── CollapsibleSection.tsx
│ │ │ │ ├── dropdown-menu.tsx
│ │ │ │ ├── label.tsx
│ │ │ │ ├── scroll-area.tsx
│ │ │ │ ├── separator.tsx
│ │ │ │ ├── shadcn-io
│ │ │ │ │ └── kanban
│ │ │ │ │ └── index.tsx
│ │ │ │ └── textarea.tsx
│ │ │ ├── extension.ts
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ └── utils.ts
│ │ │ ├── services
│ │ │ │ ├── config-service.ts
│ │ │ │ ├── error-handler.ts
│ │ │ │ ├── notification-preferences.ts
│ │ │ │ ├── polling-service.ts
│ │ │ │ ├── polling-strategies.ts
│ │ │ │ ├── sidebar-webview-manager.ts
│ │ │ │ ├── task-repository.ts
│ │ │ │ ├── terminal-manager.ts
│ │ │ │ └── webview-manager.ts
│ │ │ ├── test
│ │ │ │ └── extension.test.ts
│ │ │ ├── utils
│ │ │ │ ├── configManager.ts
│ │ │ │ ├── connectionManager.ts
│ │ │ │ ├── errorHandler.ts
│ │ │ │ ├── event-emitter.ts
│ │ │ │ ├── logger.ts
│ │ │ │ ├── mcpClient.ts
│ │ │ │ ├── notificationPreferences.ts
│ │ │ │ └── task-master-api
│ │ │ │ ├── cache
│ │ │ │ │ └── cache-manager.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── mcp-client.ts
│ │ │ │ ├── transformers
│ │ │ │ │ └── task-transformer.ts
│ │ │ │ └── types
│ │ │ │ └── index.ts
│ │ │ └── webview
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ ├── AppContent.tsx
│ │ │ │ ├── EmptyState.tsx
│ │ │ │ ├── ErrorBoundary.tsx
│ │ │ │ ├── PollingStatus.tsx
│ │ │ │ ├── PriorityBadge.tsx
│ │ │ │ ├── SidebarView.tsx
│ │ │ │ ├── TagDropdown.tsx
│ │ │ │ ├── TaskCard.tsx
│ │ │ │ ├── TaskEditModal.tsx
│ │ │ │ ├── TaskMasterKanban.tsx
│ │ │ │ ├── ToastContainer.tsx
│ │ │ │ └── ToastNotification.tsx
│ │ │ ├── constants
│ │ │ │ └── index.ts
│ │ │ ├── contexts
│ │ │ │ └── VSCodeContext.tsx
│ │ │ ├── hooks
│ │ │ │ ├── useTaskQueries.ts
│ │ │ │ ├── useVSCodeMessages.ts
│ │ │ │ └── useWebviewHeight.ts
│ │ │ ├── index.css
│ │ │ ├── index.tsx
│ │ │ ├── providers
│ │ │ │ └── QueryProvider.tsx
│ │ │ ├── reducers
│ │ │ │ └── appReducer.ts
│ │ │ ├── sidebar.tsx
│ │ │ ├── types
│ │ │ │ └── index.ts
│ │ │ └── utils
│ │ │ ├── logger.ts
│ │ │ └── toast.ts
│ │ └── tsconfig.json
│ └── mcp
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── shared
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ └── tools
│ │ ├── autopilot
│ │ │ ├── abort.tool.ts
│ │ │ ├── commit.tool.ts
│ │ │ ├── complete.tool.ts
│ │ │ ├── finalize.tool.ts
│ │ │ ├── index.ts
│ │ │ ├── next.tool.ts
│ │ │ ├── resume.tool.ts
│ │ │ ├── start.tool.ts
│ │ │ └── status.tool.ts
│ │ ├── README-ZOD-V3.md
│ │ └── tasks
│ │ ├── get-task.tool.ts
│ │ ├── get-tasks.tool.ts
│ │ └── index.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── assets
│ ├── .windsurfrules
│ ├── AGENTS.md
│ ├── claude
│ │ └── TM_COMMANDS_GUIDE.md
│ ├── config.json
│ ├── env.example
│ ├── example_prd_rpg.txt
│ ├── example_prd.txt
│ ├── GEMINI.md
│ ├── gitignore
│ ├── kiro-hooks
│ │ ├── tm-code-change-task-tracker.kiro.hook
│ │ ├── tm-complexity-analyzer.kiro.hook
│ │ ├── tm-daily-standup-assistant.kiro.hook
│ │ ├── tm-git-commit-task-linker.kiro.hook
│ │ ├── tm-pr-readiness-checker.kiro.hook
│ │ ├── tm-task-dependency-auto-progression.kiro.hook
│ │ └── tm-test-success-task-completer.kiro.hook
│ ├── roocode
│ │ ├── .roo
│ │ │ ├── rules-architect
│ │ │ │ └── architect-rules
│ │ │ ├── rules-ask
│ │ │ │ └── ask-rules
│ │ │ ├── rules-code
│ │ │ │ └── code-rules
│ │ │ ├── rules-debug
│ │ │ │ └── debug-rules
│ │ │ ├── rules-orchestrator
│ │ │ │ └── orchestrator-rules
│ │ │ └── rules-test
│ │ │ └── test-rules
│ │ └── .roomodes
│ ├── rules
│ │ ├── cursor_rules.mdc
│ │ ├── dev_workflow.mdc
│ │ ├── self_improve.mdc
│ │ ├── taskmaster_hooks_workflow.mdc
│ │ └── taskmaster.mdc
│ └── scripts_README.md
├── bin
│ └── task-master.js
├── biome.json
├── CHANGELOG.md
├── CLAUDE_CODE_PLUGIN.md
├── CLAUDE.md
├── context
│ ├── chats
│ │ ├── add-task-dependencies-1.md
│ │ └── max-min-tokens.txt.md
│ ├── fastmcp-core.txt
│ ├── fastmcp-docs.txt
│ ├── MCP_INTEGRATION.md
│ ├── mcp-js-sdk-docs.txt
│ ├── mcp-protocol-repo.txt
│ ├── mcp-protocol-schema-03262025.json
│ └── mcp-protocol-spec.txt
├── CONTRIBUTING.md
├── docs
│ ├── claude-code-integration.md
│ ├── CLI-COMMANDER-PATTERN.md
│ ├── command-reference.md
│ ├── configuration.md
│ ├── contributor-docs
│ │ ├── testing-roo-integration.md
│ │ └── worktree-setup.md
│ ├── cross-tag-task-movement.md
│ ├── examples
│ │ ├── claude-code-usage.md
│ │ └── codex-cli-usage.md
│ ├── examples.md
│ ├── licensing.md
│ ├── mcp-provider-guide.md
│ ├── mcp-provider.md
│ ├── migration-guide.md
│ ├── models.md
│ ├── providers
│ │ ├── codex-cli.md
│ │ └── gemini-cli.md
│ ├── README.md
│ ├── scripts
│ │ └── models-json-to-markdown.js
│ ├── task-structure.md
│ └── tutorial.md
├── images
│ ├── hamster-hiring.png
│ └── logo.png
├── index.js
├── jest.config.js
├── jest.resolver.cjs
├── LICENSE
├── llms-install.md
├── mcp-server
│ ├── server.js
│ └── src
│ ├── core
│ │ ├── __tests__
│ │ │ └── context-manager.test.js
│ │ ├── context-manager.js
│ │ ├── direct-functions
│ │ │ ├── add-dependency.js
│ │ │ ├── add-subtask.js
│ │ │ ├── add-tag.js
│ │ │ ├── add-task.js
│ │ │ ├── analyze-task-complexity.js
│ │ │ ├── cache-stats.js
│ │ │ ├── clear-subtasks.js
│ │ │ ├── complexity-report.js
│ │ │ ├── copy-tag.js
│ │ │ ├── create-tag-from-branch.js
│ │ │ ├── delete-tag.js
│ │ │ ├── expand-all-tasks.js
│ │ │ ├── expand-task.js
│ │ │ ├── fix-dependencies.js
│ │ │ ├── generate-task-files.js
│ │ │ ├── initialize-project.js
│ │ │ ├── list-tags.js
│ │ │ ├── models.js
│ │ │ ├── move-task-cross-tag.js
│ │ │ ├── move-task.js
│ │ │ ├── next-task.js
│ │ │ ├── parse-prd.js
│ │ │ ├── remove-dependency.js
│ │ │ ├── remove-subtask.js
│ │ │ ├── remove-task.js
│ │ │ ├── rename-tag.js
│ │ │ ├── research.js
│ │ │ ├── response-language.js
│ │ │ ├── rules.js
│ │ │ ├── scope-down.js
│ │ │ ├── scope-up.js
│ │ │ ├── set-task-status.js
│ │ │ ├── update-subtask-by-id.js
│ │ │ ├── update-task-by-id.js
│ │ │ ├── update-tasks.js
│ │ │ ├── use-tag.js
│ │ │ └── validate-dependencies.js
│ │ ├── task-master-core.js
│ │ └── utils
│ │ ├── env-utils.js
│ │ └── path-utils.js
│ ├── custom-sdk
│ │ ├── errors.js
│ │ ├── index.js
│ │ ├── json-extractor.js
│ │ ├── language-model.js
│ │ ├── message-converter.js
│ │ └── schema-converter.js
│ ├── index.js
│ ├── logger.js
│ ├── providers
│ │ └── mcp-provider.js
│ └── tools
│ ├── add-dependency.js
│ ├── add-subtask.js
│ ├── add-tag.js
│ ├── add-task.js
│ ├── analyze.js
│ ├── clear-subtasks.js
│ ├── complexity-report.js
│ ├── copy-tag.js
│ ├── delete-tag.js
│ ├── expand-all.js
│ ├── expand-task.js
│ ├── fix-dependencies.js
│ ├── generate.js
│ ├── get-operation-status.js
│ ├── index.js
│ ├── initialize-project.js
│ ├── list-tags.js
│ ├── models.js
│ ├── move-task.js
│ ├── next-task.js
│ ├── parse-prd.js
│ ├── README-ZOD-V3.md
│ ├── remove-dependency.js
│ ├── remove-subtask.js
│ ├── remove-task.js
│ ├── rename-tag.js
│ ├── research.js
│ ├── response-language.js
│ ├── rules.js
│ ├── scope-down.js
│ ├── scope-up.js
│ ├── set-task-status.js
│ ├── tool-registry.js
│ ├── update-subtask.js
│ ├── update-task.js
│ ├── update.js
│ ├── use-tag.js
│ ├── utils.js
│ └── validate-dependencies.js
├── mcp-test.js
├── output.json
├── package-lock.json
├── package.json
├── packages
│ ├── ai-sdk-provider-grok-cli
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── errors.test.ts
│ │ │ ├── errors.ts
│ │ │ ├── grok-cli-language-model.ts
│ │ │ ├── grok-cli-provider.test.ts
│ │ │ ├── grok-cli-provider.ts
│ │ │ ├── index.ts
│ │ │ ├── json-extractor.test.ts
│ │ │ ├── json-extractor.ts
│ │ │ ├── message-converter.test.ts
│ │ │ ├── message-converter.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── build-config
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src
│ │ │ └── tsdown.base.ts
│ │ └── tsconfig.json
│ ├── claude-code-plugin
│ │ ├── .claude-plugin
│ │ │ └── plugin.json
│ │ ├── .gitignore
│ │ ├── agents
│ │ │ ├── task-checker.md
│ │ │ ├── task-executor.md
│ │ │ └── task-orchestrator.md
│ │ ├── CHANGELOG.md
│ │ ├── commands
│ │ │ ├── add-dependency.md
│ │ │ ├── add-subtask.md
│ │ │ ├── add-task.md
│ │ │ ├── analyze-complexity.md
│ │ │ ├── analyze-project.md
│ │ │ ├── auto-implement-tasks.md
│ │ │ ├── command-pipeline.md
│ │ │ ├── complexity-report.md
│ │ │ ├── convert-task-to-subtask.md
│ │ │ ├── expand-all-tasks.md
│ │ │ ├── expand-task.md
│ │ │ ├── fix-dependencies.md
│ │ │ ├── generate-tasks.md
│ │ │ ├── help.md
│ │ │ ├── init-project-quick.md
│ │ │ ├── init-project.md
│ │ │ ├── install-taskmaster.md
│ │ │ ├── learn.md
│ │ │ ├── list-tasks-by-status.md
│ │ │ ├── list-tasks-with-subtasks.md
│ │ │ ├── list-tasks.md
│ │ │ ├── next-task.md
│ │ │ ├── parse-prd-with-research.md
│ │ │ ├── parse-prd.md
│ │ │ ├── project-status.md
│ │ │ ├── quick-install-taskmaster.md
│ │ │ ├── remove-all-subtasks.md
│ │ │ ├── remove-dependency.md
│ │ │ ├── remove-subtask.md
│ │ │ ├── remove-subtasks.md
│ │ │ ├── remove-task.md
│ │ │ ├── setup-models.md
│ │ │ ├── show-task.md
│ │ │ ├── smart-workflow.md
│ │ │ ├── sync-readme.md
│ │ │ ├── tm-main.md
│ │ │ ├── to-cancelled.md
│ │ │ ├── to-deferred.md
│ │ │ ├── to-done.md
│ │ │ ├── to-in-progress.md
│ │ │ ├── to-pending.md
│ │ │ ├── to-review.md
│ │ │ ├── update-single-task.md
│ │ │ ├── update-task.md
│ │ │ ├── update-tasks-from-id.md
│ │ │ ├── validate-dependencies.md
│ │ │ └── view-models.md
│ │ ├── mcp.json
│ │ └── package.json
│ ├── tm-bridge
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── add-tag-bridge.ts
│ │ │ ├── bridge-types.ts
│ │ │ ├── bridge-utils.ts
│ │ │ ├── expand-bridge.ts
│ │ │ ├── index.ts
│ │ │ ├── tags-bridge.ts
│ │ │ ├── update-bridge.ts
│ │ │ └── use-tag-bridge.ts
│ │ └── tsconfig.json
│ └── tm-core
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── docs
│ │ └── listTasks-architecture.md
│ ├── package.json
│ ├── POC-STATUS.md
│ ├── README.md
│ ├── src
│ │ ├── common
│ │ │ ├── constants
│ │ │ │ ├── index.ts
│ │ │ │ ├── paths.ts
│ │ │ │ └── providers.ts
│ │ │ ├── errors
│ │ │ │ ├── index.ts
│ │ │ │ └── task-master-error.ts
│ │ │ ├── interfaces
│ │ │ │ ├── configuration.interface.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── storage.interface.ts
│ │ │ ├── logger
│ │ │ │ ├── factory.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── logger.spec.ts
│ │ │ │ └── logger.ts
│ │ │ ├── mappers
│ │ │ │ ├── TaskMapper.test.ts
│ │ │ │ └── TaskMapper.ts
│ │ │ ├── types
│ │ │ │ ├── database.types.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── legacy.ts
│ │ │ │ └── repository-types.ts
│ │ │ └── utils
│ │ │ ├── git-utils.ts
│ │ │ ├── id-generator.ts
│ │ │ ├── index.ts
│ │ │ ├── path-helpers.ts
│ │ │ ├── path-normalizer.spec.ts
│ │ │ ├── path-normalizer.ts
│ │ │ ├── project-root-finder.spec.ts
│ │ │ ├── project-root-finder.ts
│ │ │ ├── run-id-generator.spec.ts
│ │ │ └── run-id-generator.ts
│ │ ├── index.ts
│ │ ├── modules
│ │ │ ├── ai
│ │ │ │ ├── index.ts
│ │ │ │ ├── interfaces
│ │ │ │ │ └── ai-provider.interface.ts
│ │ │ │ └── providers
│ │ │ │ ├── base-provider.ts
│ │ │ │ └── index.ts
│ │ │ ├── auth
│ │ │ │ ├── auth-domain.spec.ts
│ │ │ │ ├── auth-domain.ts
│ │ │ │ ├── config.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── managers
│ │ │ │ │ ├── auth-manager.spec.ts
│ │ │ │ │ └── auth-manager.ts
│ │ │ │ ├── services
│ │ │ │ │ ├── context-store.ts
│ │ │ │ │ ├── oauth-service.ts
│ │ │ │ │ ├── organization.service.ts
│ │ │ │ │ ├── supabase-session-storage.spec.ts
│ │ │ │ │ └── supabase-session-storage.ts
│ │ │ │ └── types.ts
│ │ │ ├── briefs
│ │ │ │ ├── briefs-domain.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── services
│ │ │ │ │ └── brief-service.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils
│ │ │ │ └── url-parser.ts
│ │ │ ├── commands
│ │ │ │ └── index.ts
│ │ │ ├── config
│ │ │ │ ├── config-domain.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── managers
│ │ │ │ │ ├── config-manager.spec.ts
│ │ │ │ │ └── config-manager.ts
│ │ │ │ └── services
│ │ │ │ ├── config-loader.service.spec.ts
│ │ │ │ ├── config-loader.service.ts
│ │ │ │ ├── config-merger.service.spec.ts
│ │ │ │ ├── config-merger.service.ts
│ │ │ │ ├── config-persistence.service.spec.ts
│ │ │ │ ├── config-persistence.service.ts
│ │ │ │ ├── environment-config-provider.service.spec.ts
│ │ │ │ ├── environment-config-provider.service.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── runtime-state-manager.service.spec.ts
│ │ │ │ └── runtime-state-manager.service.ts
│ │ │ ├── dependencies
│ │ │ │ └── index.ts
│ │ │ ├── execution
│ │ │ │ ├── executors
│ │ │ │ │ ├── base-executor.ts
│ │ │ │ │ ├── claude-executor.ts
│ │ │ │ │ └── executor-factory.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── services
│ │ │ │ │ └── executor-service.ts
│ │ │ │ └── types.ts
│ │ │ ├── git
│ │ │ │ ├── adapters
│ │ │ │ │ ├── git-adapter.test.ts
│ │ │ │ │ └── git-adapter.ts
│ │ │ │ ├── git-domain.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── services
│ │ │ │ ├── branch-name-generator.spec.ts
│ │ │ │ ├── branch-name-generator.ts
│ │ │ │ ├── commit-message-generator.test.ts
│ │ │ │ ├── commit-message-generator.ts
│ │ │ │ ├── scope-detector.test.ts
│ │ │ │ ├── scope-detector.ts
│ │ │ │ ├── template-engine.test.ts
│ │ │ │ └── template-engine.ts
│ │ │ ├── integration
│ │ │ │ ├── clients
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── supabase-client.ts
│ │ │ │ ├── integration-domain.ts
│ │ │ │ └── services
│ │ │ │ ├── export.service.ts
│ │ │ │ ├── task-expansion.service.ts
│ │ │ │ └── task-retrieval.service.ts
│ │ │ ├── reports
│ │ │ │ ├── index.ts
│ │ │ │ ├── managers
│ │ │ │ │ └── complexity-report-manager.ts
│ │ │ │ └── types.ts
│ │ │ ├── storage
│ │ │ │ ├── adapters
│ │ │ │ │ ├── activity-logger.ts
│ │ │ │ │ ├── api-storage.ts
│ │ │ │ │ └── file-storage
│ │ │ │ │ ├── file-operations.ts
│ │ │ │ │ ├── file-storage.ts
│ │ │ │ │ ├── format-handler.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── path-resolver.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── services
│ │ │ │ │ └── storage-factory.ts
│ │ │ │ └── utils
│ │ │ │ └── api-client.ts
│ │ │ ├── tasks
│ │ │ │ ├── entities
│ │ │ │ │ └── task.entity.ts
│ │ │ │ ├── parser
│ │ │ │ │ └── index.ts
│ │ │ │ ├── repositories
│ │ │ │ │ ├── supabase
│ │ │ │ │ │ ├── dependency-fetcher.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── supabase-repository.ts
│ │ │ │ │ └── task-repository.interface.ts
│ │ │ │ ├── services
│ │ │ │ │ ├── preflight-checker.service.ts
│ │ │ │ │ ├── tag.service.ts
│ │ │ │ │ ├── task-execution-service.ts
│ │ │ │ │ ├── task-loader.service.ts
│ │ │ │ │ └── task-service.ts
│ │ │ │ └── tasks-domain.ts
│ │ │ ├── ui
│ │ │ │ └── index.ts
│ │ │ └── workflow
│ │ │ ├── managers
│ │ │ │ ├── workflow-state-manager.spec.ts
│ │ │ │ └── workflow-state-manager.ts
│ │ │ ├── orchestrators
│ │ │ │ ├── workflow-orchestrator.test.ts
│ │ │ │ └── workflow-orchestrator.ts
│ │ │ ├── services
│ │ │ │ ├── test-result-validator.test.ts
│ │ │ │ ├── test-result-validator.ts
│ │ │ │ ├── test-result-validator.types.ts
│ │ │ │ ├── workflow-activity-logger.ts
│ │ │ │ └── workflow.service.ts
│ │ │ ├── types.ts
│ │ │ └── workflow-domain.ts
│ │ ├── subpath-exports.test.ts
│ │ ├── tm-core.ts
│ │ └── utils
│ │ └── time.utils.ts
│ ├── tests
│ │ ├── auth
│ │ │ └── auth-refresh.test.ts
│ │ ├── integration
│ │ │ ├── auth-token-refresh.test.ts
│ │ │ ├── list-tasks.test.ts
│ │ │ └── storage
│ │ │ └── activity-logger.test.ts
│ │ ├── mocks
│ │ │ └── mock-provider.ts
│ │ ├── setup.ts
│ │ └── unit
│ │ ├── base-provider.test.ts
│ │ ├── executor.test.ts
│ │ └── smoke.test.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── README-task-master.md
├── README.md
├── scripts
│ ├── create-worktree.sh
│ ├── dev.js
│ ├── init.js
│ ├── list-worktrees.sh
│ ├── modules
│ │ ├── ai-services-unified.js
│ │ ├── bridge-utils.js
│ │ ├── commands.js
│ │ ├── config-manager.js
│ │ ├── dependency-manager.js
│ │ ├── index.js
│ │ ├── prompt-manager.js
│ │ ├── supported-models.json
│ │ ├── sync-readme.js
│ │ ├── task-manager
│ │ │ ├── add-subtask.js
│ │ │ ├── add-task.js
│ │ │ ├── analyze-task-complexity.js
│ │ │ ├── clear-subtasks.js
│ │ │ ├── expand-all-tasks.js
│ │ │ ├── expand-task.js
│ │ │ ├── find-next-task.js
│ │ │ ├── generate-task-files.js
│ │ │ ├── is-task-dependent.js
│ │ │ ├── list-tasks.js
│ │ │ ├── migrate.js
│ │ │ ├── models.js
│ │ │ ├── move-task.js
│ │ │ ├── parse-prd
│ │ │ │ ├── index.js
│ │ │ │ ├── parse-prd-config.js
│ │ │ │ ├── parse-prd-helpers.js
│ │ │ │ ├── parse-prd-non-streaming.js
│ │ │ │ ├── parse-prd-streaming.js
│ │ │ │ └── parse-prd.js
│ │ │ ├── remove-subtask.js
│ │ │ ├── remove-task.js
│ │ │ ├── research.js
│ │ │ ├── response-language.js
│ │ │ ├── scope-adjustment.js
│ │ │ ├── set-task-status.js
│ │ │ ├── tag-management.js
│ │ │ ├── task-exists.js
│ │ │ ├── update-single-task-status.js
│ │ │ ├── update-subtask-by-id.js
│ │ │ ├── update-task-by-id.js
│ │ │ └── update-tasks.js
│ │ ├── task-manager.js
│ │ ├── ui.js
│ │ ├── update-config-tokens.js
│ │ ├── utils
│ │ │ ├── contextGatherer.js
│ │ │ ├── fuzzyTaskSearch.js
│ │ │ └── git-utils.js
│ │ └── utils.js
│ ├── task-complexity-report.json
│ ├── test-claude-errors.js
│ └── test-claude.js
├── sonar-project.properties
├── src
│ ├── ai-providers
│ │ ├── anthropic.js
│ │ ├── azure.js
│ │ ├── base-provider.js
│ │ ├── bedrock.js
│ │ ├── claude-code.js
│ │ ├── codex-cli.js
│ │ ├── gemini-cli.js
│ │ ├── google-vertex.js
│ │ ├── google.js
│ │ ├── grok-cli.js
│ │ ├── groq.js
│ │ ├── index.js
│ │ ├── lmstudio.js
│ │ ├── ollama.js
│ │ ├── openai-compatible.js
│ │ ├── openai.js
│ │ ├── openrouter.js
│ │ ├── perplexity.js
│ │ ├── xai.js
│ │ ├── zai-coding.js
│ │ └── zai.js
│ ├── constants
│ │ ├── commands.js
│ │ ├── paths.js
│ │ ├── profiles.js
│ │ ├── rules-actions.js
│ │ ├── task-priority.js
│ │ └── task-status.js
│ ├── profiles
│ │ ├── amp.js
│ │ ├── base-profile.js
│ │ ├── claude.js
│ │ ├── cline.js
│ │ ├── codex.js
│ │ ├── cursor.js
│ │ ├── gemini.js
│ │ ├── index.js
│ │ ├── kilo.js
│ │ ├── kiro.js
│ │ ├── opencode.js
│ │ ├── roo.js
│ │ ├── trae.js
│ │ ├── vscode.js
│ │ ├── windsurf.js
│ │ └── zed.js
│ ├── progress
│ │ ├── base-progress-tracker.js
│ │ ├── cli-progress-factory.js
│ │ ├── parse-prd-tracker.js
│ │ ├── progress-tracker-builder.js
│ │ └── tracker-ui.js
│ ├── prompts
│ │ ├── add-task.json
│ │ ├── analyze-complexity.json
│ │ ├── expand-task.json
│ │ ├── parse-prd.json
│ │ ├── README.md
│ │ ├── research.json
│ │ ├── schemas
│ │ │ ├── parameter.schema.json
│ │ │ ├── prompt-template.schema.json
│ │ │ ├── README.md
│ │ │ └── variant.schema.json
│ │ ├── update-subtask.json
│ │ ├── update-task.json
│ │ └── update-tasks.json
│ ├── provider-registry
│ │ └── index.js
│ ├── schemas
│ │ ├── add-task.js
│ │ ├── analyze-complexity.js
│ │ ├── base-schemas.js
│ │ ├── expand-task.js
│ │ ├── parse-prd.js
│ │ ├── registry.js
│ │ ├── update-subtask.js
│ │ ├── update-task.js
│ │ └── update-tasks.js
│ ├── task-master.js
│ ├── ui
│ │ ├── confirm.js
│ │ ├── indicators.js
│ │ └── parse-prd.js
│ └── utils
│ ├── asset-resolver.js
│ ├── create-mcp-config.js
│ ├── format.js
│ ├── getVersion.js
│ ├── logger-utils.js
│ ├── manage-gitignore.js
│ ├── path-utils.js
│ ├── profiles.js
│ ├── rule-transformer.js
│ ├── stream-parser.js
│ └── timeout-manager.js
├── test-clean-tags.js
├── test-config-manager.js
├── test-prd.txt
├── test-tag-functions.js
├── test-version-check-full.js
├── test-version-check.js
├── tests
│ ├── e2e
│ │ ├── e2e_helpers.sh
│ │ ├── parse_llm_output.cjs
│ │ ├── run_e2e.sh
│ │ ├── run_fallback_verification.sh
│ │ └── test_llm_analysis.sh
│ ├── fixtures
│ │ ├── .taskmasterconfig
│ │ ├── sample-claude-response.js
│ │ ├── sample-prd.txt
│ │ └── sample-tasks.js
│ ├── helpers
│ │ └── tool-counts.js
│ ├── integration
│ │ ├── claude-code-error-handling.test.js
│ │ ├── claude-code-optional.test.js
│ │ ├── cli
│ │ │ ├── commands.test.js
│ │ │ ├── complex-cross-tag-scenarios.test.js
│ │ │ └── move-cross-tag.test.js
│ │ ├── manage-gitignore.test.js
│ │ ├── mcp-server
│ │ │ └── direct-functions.test.js
│ │ ├── move-task-cross-tag.integration.test.js
│ │ ├── move-task-simple.integration.test.js
│ │ ├── profiles
│ │ │ ├── amp-init-functionality.test.js
│ │ │ ├── claude-init-functionality.test.js
│ │ │ ├── cline-init-functionality.test.js
│ │ │ ├── codex-init-functionality.test.js
│ │ │ ├── cursor-init-functionality.test.js
│ │ │ ├── gemini-init-functionality.test.js
│ │ │ ├── opencode-init-functionality.test.js
│ │ │ ├── roo-files-inclusion.test.js
│ │ │ ├── roo-init-functionality.test.js
│ │ │ ├── rules-files-inclusion.test.js
│ │ │ ├── trae-init-functionality.test.js
│ │ │ ├── vscode-init-functionality.test.js
│ │ │ └── windsurf-init-functionality.test.js
│ │ └── providers
│ │ └── temperature-support.test.js
│ ├── manual
│ │ ├── progress
│ │ │ ├── parse-prd-analysis.js
│ │ │ ├── test-parse-prd.js
│ │ │ └── TESTING_GUIDE.md
│ │ └── prompts
│ │ ├── prompt-test.js
│ │ └── README.md
│ ├── README.md
│ ├── setup.js
│ └── unit
│ ├── ai-providers
│ │ ├── base-provider.test.js
│ │ ├── claude-code.test.js
│ │ ├── codex-cli.test.js
│ │ ├── gemini-cli.test.js
│ │ ├── lmstudio.test.js
│ │ ├── mcp-components.test.js
│ │ ├── openai-compatible.test.js
│ │ ├── openai.test.js
│ │ ├── provider-registry.test.js
│ │ ├── zai-coding.test.js
│ │ ├── zai-provider.test.js
│ │ ├── zai-schema-introspection.test.js
│ │ └── zai.test.js
│ ├── ai-services-unified.test.js
│ ├── commands.test.js
│ ├── config-manager.test.js
│ ├── config-manager.test.mjs
│ ├── dependency-manager.test.js
│ ├── init.test.js
│ ├── initialize-project.test.js
│ ├── kebab-case-validation.test.js
│ ├── manage-gitignore.test.js
│ ├── mcp
│ │ └── tools
│ │ ├── __mocks__
│ │ │ └── move-task.js
│ │ ├── add-task.test.js
│ │ ├── analyze-complexity.test.js
│ │ ├── expand-all.test.js
│ │ ├── get-tasks.test.js
│ │ ├── initialize-project.test.js
│ │ ├── move-task-cross-tag-options.test.js
│ │ ├── move-task-cross-tag.test.js
│ │ ├── remove-task.test.js
│ │ └── tool-registration.test.js
│ ├── mcp-providers
│ │ ├── mcp-components.test.js
│ │ └── mcp-provider.test.js
│ ├── parse-prd.test.js
│ ├── profiles
│ │ ├── amp-integration.test.js
│ │ ├── claude-integration.test.js
│ │ ├── cline-integration.test.js
│ │ ├── codex-integration.test.js
│ │ ├── cursor-integration.test.js
│ │ ├── gemini-integration.test.js
│ │ ├── kilo-integration.test.js
│ │ ├── kiro-integration.test.js
│ │ ├── mcp-config-validation.test.js
│ │ ├── opencode-integration.test.js
│ │ ├── profile-safety-check.test.js
│ │ ├── roo-integration.test.js
│ │ ├── rule-transformer-cline.test.js
│ │ ├── rule-transformer-cursor.test.js
│ │ ├── rule-transformer-gemini.test.js
│ │ ├── rule-transformer-kilo.test.js
│ │ ├── rule-transformer-kiro.test.js
│ │ ├── rule-transformer-opencode.test.js
│ │ ├── rule-transformer-roo.test.js
│ │ ├── rule-transformer-trae.test.js
│ │ ├── rule-transformer-vscode.test.js
│ │ ├── rule-transformer-windsurf.test.js
│ │ ├── rule-transformer-zed.test.js
│ │ ├── rule-transformer.test.js
│ │ ├── selective-profile-removal.test.js
│ │ ├── subdirectory-support.test.js
│ │ ├── trae-integration.test.js
│ │ ├── vscode-integration.test.js
│ │ ├── windsurf-integration.test.js
│ │ └── zed-integration.test.js
│ ├── progress
│ │ └── base-progress-tracker.test.js
│ ├── prompt-manager.test.js
│ ├── prompts
│ │ ├── expand-task-prompt.test.js
│ │ └── prompt-migration.test.js
│ ├── scripts
│ │ └── modules
│ │ ├── commands
│ │ │ ├── move-cross-tag.test.js
│ │ │ └── README.md
│ │ ├── dependency-manager
│ │ │ ├── circular-dependencies.test.js
│ │ │ ├── cross-tag-dependencies.test.js
│ │ │ └── fix-dependencies-command.test.js
│ │ ├── task-manager
│ │ │ ├── add-subtask.test.js
│ │ │ ├── add-task.test.js
│ │ │ ├── analyze-task-complexity.test.js
│ │ │ ├── clear-subtasks.test.js
│ │ │ ├── complexity-report-tag-isolation.test.js
│ │ │ ├── expand-all-tasks.test.js
│ │ │ ├── expand-task.test.js
│ │ │ ├── find-next-task.test.js
│ │ │ ├── generate-task-files.test.js
│ │ │ ├── list-tasks.test.js
│ │ │ ├── models-baseurl.test.js
│ │ │ ├── move-task-cross-tag.test.js
│ │ │ ├── move-task.test.js
│ │ │ ├── parse-prd-schema.test.js
│ │ │ ├── parse-prd.test.js
│ │ │ ├── remove-subtask.test.js
│ │ │ ├── remove-task.test.js
│ │ │ ├── research.test.js
│ │ │ ├── scope-adjustment.test.js
│ │ │ ├── set-task-status.test.js
│ │ │ ├── setup.js
│ │ │ ├── update-single-task-status.test.js
│ │ │ ├── update-subtask-by-id.test.js
│ │ │ ├── update-task-by-id.test.js
│ │ │ └── update-tasks.test.js
│ │ ├── ui
│ │ │ └── cross-tag-error-display.test.js
│ │ └── utils-tag-aware-paths.test.js
│ ├── task-finder.test.js
│ ├── task-manager
│ │ ├── clear-subtasks.test.js
│ │ ├── move-task.test.js
│ │ ├── tag-boundary.test.js
│ │ └── tag-management.test.js
│ ├── task-master.test.js
│ ├── ui
│ │ └── indicators.test.js
│ ├── ui.test.js
│ ├── utils-strip-ansi.test.js
│ └── utils.test.js
├── tsconfig.json
├── tsdown.config.ts
├── turbo.json
└── update-task-migration-plan.md
```
# Files
--------------------------------------------------------------------------------
/scripts/modules/task-manager/list-tasks.js:
--------------------------------------------------------------------------------
```javascript
1 | import chalk from 'chalk';
2 | import boxen from 'boxen';
3 | import Table from 'cli-table3';
4 |
5 | import {
6 | log,
7 | readJSON,
8 | truncate,
9 | readComplexityReport,
10 | addComplexityToTask
11 | } from '../utils.js';
12 | import findNextTask from './find-next-task.js';
13 |
14 | import {
15 | displayBanner,
16 | getStatusWithColor,
17 | formatDependenciesWithStatus,
18 | getComplexityWithColor,
19 | createProgressBar
20 | } from '../ui.js';
21 | import { createTmCore } from '@tm/core';
22 |
23 | /**
24 | * List all tasks
25 | * @param {string} tasksPath - Path to the tasks.json file
26 | * @param {string} statusFilter - Filter by status (single status or comma-separated list, e.g., 'pending' or 'blocked,deferred')
27 | * @param {string} reportPath - Path to the complexity report
28 | * @param {boolean} withSubtasks - Whether to show subtasks
29 | * @param {string} outputFormat - Output format (text or json)
30 | * @param {Object} context - Context object (required)
31 | * @param {string} context.projectRoot - Project root path
32 | * @param {string} context.tag - Tag for the task
33 | * @returns {Object} - Task list result for json format
34 | */
35 | async function listTasks(
36 | tasksPath,
37 | statusFilter,
38 | reportPath = null,
39 | withSubtasks = false,
40 | outputFormat = 'text',
41 | context = {}
42 | ) {
43 | const { projectRoot, tag } = context;
44 | try {
45 | // BRIDGE: Initialize tm-core for unified task access
46 | let data;
47 | try {
48 | const tmCore = await createTmCore({
49 | projectPath: projectRoot || process.cwd()
50 | });
51 |
52 | // Load tasks via tm-core tasks domain (supports both file and API storage)
53 | const result = await tmCore.tasks.list({ tag });
54 | data = { tasks: result.tasks };
55 |
56 | log(
57 | 'debug',
58 | `Loaded ${result.tasks.length} tasks via tm-core (${result.storageType} storage)`
59 | );
60 | } catch (storageError) {
61 | log(
62 | 'warn',
63 | `TmCore failed, falling back to legacy readJSON: ${storageError.message}`
64 | );
65 | // Fallback to old readJSON if tm-core fails
66 | data = readJSON(tasksPath, projectRoot, tag);
67 | }
68 |
69 | if (!data || !data.tasks) {
70 | throw new Error(`No valid tasks found in ${tasksPath}`);
71 | }
72 |
73 | // Add complexity scores to tasks if report exists
74 | // `reportPath` is already tag-aware (resolved at the CLI boundary).
75 | const complexityReport = readComplexityReport(reportPath);
76 | // Apply complexity scores to tasks
77 | if (complexityReport && complexityReport.complexityAnalysis) {
78 | data.tasks.forEach((task) => addComplexityToTask(task, complexityReport));
79 | }
80 |
81 | // Filter tasks by status if specified - now supports comma-separated statuses
82 | let filteredTasks;
83 | if (statusFilter && statusFilter.toLowerCase() !== 'all') {
84 | // Handle comma-separated statuses
85 | const allowedStatuses = statusFilter
86 | .split(',')
87 | .map((s) => s.trim().toLowerCase())
88 | .filter((s) => s.length > 0); // Remove empty strings
89 |
90 | filteredTasks = data.tasks.filter(
91 | (task) =>
92 | task.status && allowedStatuses.includes(task.status.toLowerCase())
93 | );
94 | } else {
95 | // Default to all tasks if no filter or filter is 'all'
96 | filteredTasks = data.tasks;
97 | }
98 |
99 | // Calculate completion statistics
100 | const totalTasks = data.tasks.length;
101 | const completedTasks = data.tasks.filter(
102 | (task) => task.status === 'done' || task.status === 'completed'
103 | ).length;
104 | const completionPercentage =
105 | totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0;
106 |
107 | // Count statuses for tasks
108 | const doneCount = completedTasks;
109 | const inProgressCount = data.tasks.filter(
110 | (task) => task.status === 'in-progress'
111 | ).length;
112 | const pendingCount = data.tasks.filter(
113 | (task) => task.status === 'pending'
114 | ).length;
115 | const blockedCount = data.tasks.filter(
116 | (task) => task.status === 'blocked'
117 | ).length;
118 | const deferredCount = data.tasks.filter(
119 | (task) => task.status === 'deferred'
120 | ).length;
121 | const cancelledCount = data.tasks.filter(
122 | (task) => task.status === 'cancelled'
123 | ).length;
124 | const reviewCount = data.tasks.filter(
125 | (task) => task.status === 'review'
126 | ).length;
127 |
128 | // Count subtasks and their statuses
129 | let totalSubtasks = 0;
130 | let completedSubtasks = 0;
131 | let inProgressSubtasks = 0;
132 | let pendingSubtasks = 0;
133 | let blockedSubtasks = 0;
134 | let deferredSubtasks = 0;
135 | let cancelledSubtasks = 0;
136 | let reviewSubtasks = 0;
137 |
138 | data.tasks.forEach((task) => {
139 | if (task.subtasks && task.subtasks.length > 0) {
140 | totalSubtasks += task.subtasks.length;
141 | completedSubtasks += task.subtasks.filter(
142 | (st) => st.status === 'done' || st.status === 'completed'
143 | ).length;
144 | inProgressSubtasks += task.subtasks.filter(
145 | (st) => st.status === 'in-progress'
146 | ).length;
147 | pendingSubtasks += task.subtasks.filter(
148 | (st) => st.status === 'pending'
149 | ).length;
150 | blockedSubtasks += task.subtasks.filter(
151 | (st) => st.status === 'blocked'
152 | ).length;
153 | deferredSubtasks += task.subtasks.filter(
154 | (st) => st.status === 'deferred'
155 | ).length;
156 | cancelledSubtasks += task.subtasks.filter(
157 | (st) => st.status === 'cancelled'
158 | ).length;
159 | reviewSubtasks += task.subtasks.filter(
160 | (st) => st.status === 'review'
161 | ).length;
162 | }
163 | });
164 |
165 | const subtaskCompletionPercentage =
166 | totalSubtasks > 0 ? (completedSubtasks / totalSubtasks) * 100 : 0;
167 |
168 | // Calculate dependency statistics (moved up to be available for all output formats)
169 | const completedTaskIds = new Set(
170 | data.tasks
171 | .filter((t) => t.status === 'done' || t.status === 'completed')
172 | .map((t) => t.id)
173 | );
174 |
175 | const tasksWithNoDeps = data.tasks.filter(
176 | (t) =>
177 | t.status !== 'done' &&
178 | t.status !== 'completed' &&
179 | (!t.dependencies || t.dependencies.length === 0)
180 | ).length;
181 |
182 | const tasksWithAllDepsSatisfied = data.tasks.filter(
183 | (t) =>
184 | t.status !== 'done' &&
185 | t.status !== 'completed' &&
186 | t.dependencies &&
187 | t.dependencies.length > 0 &&
188 | t.dependencies.every((depId) => completedTaskIds.has(depId))
189 | ).length;
190 |
191 | const tasksWithUnsatisfiedDeps = data.tasks.filter(
192 | (t) =>
193 | t.status !== 'done' &&
194 | t.status !== 'completed' &&
195 | t.dependencies &&
196 | t.dependencies.length > 0 &&
197 | !t.dependencies.every((depId) => completedTaskIds.has(depId))
198 | ).length;
199 |
200 | // Calculate total tasks ready to work on (no deps + satisfied deps)
201 | const tasksReadyToWork = tasksWithNoDeps + tasksWithAllDepsSatisfied;
202 |
203 | // Calculate most depended-on tasks
204 | const dependencyCount = {};
205 | data.tasks.forEach((task) => {
206 | if (task.dependencies && task.dependencies.length > 0) {
207 | task.dependencies.forEach((depId) => {
208 | dependencyCount[depId] = (dependencyCount[depId] || 0) + 1;
209 | });
210 | }
211 | });
212 |
213 | // Find the most depended-on task
214 | let mostDependedOnTaskId = null;
215 | let maxDependents = 0;
216 |
217 | for (const [taskId, count] of Object.entries(dependencyCount)) {
218 | if (count > maxDependents) {
219 | maxDependents = count;
220 | mostDependedOnTaskId = parseInt(taskId);
221 | }
222 | }
223 |
224 | // Get the most depended-on task
225 | const mostDependedOnTask =
226 | mostDependedOnTaskId !== null
227 | ? data.tasks.find((t) => t.id === mostDependedOnTaskId)
228 | : null;
229 |
230 | // Calculate average dependencies per task
231 | const totalDependencies = data.tasks.reduce(
232 | (sum, task) => sum + (task.dependencies ? task.dependencies.length : 0),
233 | 0
234 | );
235 | const avgDependenciesPerTask = totalDependencies / data.tasks.length;
236 |
237 | // Find next task to work on, passing the complexity report
238 | const nextItem = findNextTask(data.tasks, complexityReport);
239 |
240 | // For JSON output, return structured data
241 | if (outputFormat === 'json') {
242 | // *** Modification: Remove 'details' field for JSON output ***
243 | const tasksWithoutDetails = filteredTasks.map((task) => {
244 | // <-- USES filteredTasks!
245 | // Omit 'details' from the parent task
246 | const { details, ...taskRest } = task;
247 |
248 | // If subtasks exist, omit 'details' from them too
249 | if (taskRest.subtasks && Array.isArray(taskRest.subtasks)) {
250 | taskRest.subtasks = taskRest.subtasks.map((subtask) => {
251 | const { details: subtaskDetails, ...subtaskRest } = subtask;
252 | return subtaskRest;
253 | });
254 | }
255 | return taskRest;
256 | });
257 | // *** End of Modification ***
258 |
259 | return {
260 | tasks: tasksWithoutDetails, // <--- THIS IS THE ARRAY BEING RETURNED
261 | filter: statusFilter || 'all', // Return the actual filter used
262 | stats: {
263 | total: totalTasks,
264 | completed: doneCount,
265 | inProgress: inProgressCount,
266 | pending: pendingCount,
267 | blocked: blockedCount,
268 | deferred: deferredCount,
269 | cancelled: cancelledCount,
270 | review: reviewCount,
271 | completionPercentage,
272 | subtasks: {
273 | total: totalSubtasks,
274 | completed: completedSubtasks,
275 | inProgress: inProgressSubtasks,
276 | pending: pendingSubtasks,
277 | blocked: blockedSubtasks,
278 | deferred: deferredSubtasks,
279 | cancelled: cancelledSubtasks,
280 | completionPercentage: subtaskCompletionPercentage
281 | }
282 | }
283 | };
284 | }
285 |
286 | // For markdown-readme output, return formatted markdown
287 | if (outputFormat === 'markdown-readme') {
288 | return generateMarkdownOutput(data, filteredTasks, {
289 | totalTasks,
290 | completedTasks,
291 | completionPercentage,
292 | doneCount,
293 | inProgressCount,
294 | pendingCount,
295 | blockedCount,
296 | deferredCount,
297 | cancelledCount,
298 | totalSubtasks,
299 | completedSubtasks,
300 | subtaskCompletionPercentage,
301 | inProgressSubtasks,
302 | pendingSubtasks,
303 | blockedSubtasks,
304 | deferredSubtasks,
305 | cancelledSubtasks,
306 | reviewSubtasks,
307 | tasksWithNoDeps,
308 | tasksReadyToWork,
309 | tasksWithUnsatisfiedDeps,
310 | mostDependedOnTask,
311 | mostDependedOnTaskId,
312 | maxDependents,
313 | avgDependenciesPerTask,
314 | complexityReport,
315 | withSubtasks,
316 | nextItem
317 | });
318 | }
319 |
320 | // For compact output, return minimal one-line format
321 | if (outputFormat === 'compact') {
322 | return renderCompactOutput(filteredTasks, withSubtasks);
323 | }
324 |
325 | // ... existing code for text output ...
326 |
327 | // Calculate status breakdowns as percentages of total
328 | const taskStatusBreakdown = {
329 | 'in-progress': totalTasks > 0 ? (inProgressCount / totalTasks) * 100 : 0,
330 | pending: totalTasks > 0 ? (pendingCount / totalTasks) * 100 : 0,
331 | blocked: totalTasks > 0 ? (blockedCount / totalTasks) * 100 : 0,
332 | deferred: totalTasks > 0 ? (deferredCount / totalTasks) * 100 : 0,
333 | cancelled: totalTasks > 0 ? (cancelledCount / totalTasks) * 100 : 0,
334 | review: totalTasks > 0 ? (reviewCount / totalTasks) * 100 : 0
335 | };
336 |
337 | const subtaskStatusBreakdown = {
338 | 'in-progress':
339 | totalSubtasks > 0 ? (inProgressSubtasks / totalSubtasks) * 100 : 0,
340 | pending: totalSubtasks > 0 ? (pendingSubtasks / totalSubtasks) * 100 : 0,
341 | blocked: totalSubtasks > 0 ? (blockedSubtasks / totalSubtasks) * 100 : 0,
342 | deferred:
343 | totalSubtasks > 0 ? (deferredSubtasks / totalSubtasks) * 100 : 0,
344 | cancelled:
345 | totalSubtasks > 0 ? (cancelledSubtasks / totalSubtasks) * 100 : 0,
346 | review: totalSubtasks > 0 ? (reviewSubtasks / totalSubtasks) * 100 : 0
347 | };
348 |
349 | // Create progress bars with status breakdowns
350 | const taskProgressBar = createProgressBar(
351 | completionPercentage,
352 | 30,
353 | taskStatusBreakdown
354 | );
355 | const subtaskProgressBar = createProgressBar(
356 | subtaskCompletionPercentage,
357 | 30,
358 | subtaskStatusBreakdown
359 | );
360 |
361 | // Get terminal width - more reliable method
362 | let terminalWidth;
363 | try {
364 | // Try to get the actual terminal columns
365 | terminalWidth = process.stdout.columns;
366 | } catch (e) {
367 | // Fallback if columns cannot be determined
368 | log('debug', 'Could not determine terminal width, using default');
369 | }
370 | // Ensure we have a reasonable default if detection fails
371 | terminalWidth = terminalWidth || 80;
372 |
373 | // Ensure terminal width is at least a minimum value to prevent layout issues
374 | terminalWidth = Math.max(terminalWidth, 80);
375 |
376 | // Create dashboard content
377 | const projectDashboardContent =
378 | chalk.white.bold('Project Dashboard') +
379 | '\n' +
380 | `Tasks Progress: ${chalk.greenBright(taskProgressBar)} ${completionPercentage.toFixed(0)}%\n` +
381 | `Done: ${chalk.green(doneCount)} In Progress: ${chalk.blue(inProgressCount)} Pending: ${chalk.yellow(pendingCount)} Blocked: ${chalk.red(blockedCount)} Deferred: ${chalk.gray(deferredCount)} Cancelled: ${chalk.gray(cancelledCount)}\n\n` +
382 | `Subtasks Progress: ${chalk.cyan(subtaskProgressBar)} ${subtaskCompletionPercentage.toFixed(0)}%\n` +
383 | `Completed: ${chalk.green(completedSubtasks)}/${totalSubtasks} In Progress: ${chalk.blue(inProgressSubtasks)} Pending: ${chalk.yellow(pendingSubtasks)} Blocked: ${chalk.red(blockedSubtasks)} Deferred: ${chalk.gray(deferredSubtasks)} Cancelled: ${chalk.gray(cancelledSubtasks)}\n\n` +
384 | chalk.cyan.bold('Priority Breakdown:') +
385 | '\n' +
386 | `${chalk.red('•')} ${chalk.white('High priority:')} ${data.tasks.filter((t) => t.priority === 'high').length}\n` +
387 | `${chalk.yellow('•')} ${chalk.white('Medium priority:')} ${data.tasks.filter((t) => t.priority === 'medium').length}\n` +
388 | `${chalk.green('•')} ${chalk.white('Low priority:')} ${data.tasks.filter((t) => t.priority === 'low').length}`;
389 |
390 | const dependencyDashboardContent =
391 | chalk.white.bold('Dependency Status & Next Task') +
392 | '\n' +
393 | chalk.cyan.bold('Dependency Metrics:') +
394 | '\n' +
395 | `${chalk.green('•')} ${chalk.white('Tasks with no dependencies:')} ${tasksWithNoDeps}\n` +
396 | `${chalk.green('•')} ${chalk.white('Tasks ready to work on:')} ${tasksReadyToWork}\n` +
397 | `${chalk.yellow('•')} ${chalk.white('Tasks blocked by dependencies:')} ${tasksWithUnsatisfiedDeps}\n` +
398 | `${chalk.magenta('•')} ${chalk.white('Most depended-on task:')} ${mostDependedOnTask ? chalk.cyan(`#${mostDependedOnTaskId} (${maxDependents} dependents)`) : chalk.gray('None')}\n` +
399 | `${chalk.blue('•')} ${chalk.white('Avg dependencies per task:')} ${avgDependenciesPerTask.toFixed(1)}\n\n` +
400 | chalk.cyan.bold('Next Task to Work On:') +
401 | '\n' +
402 | `ID: ${chalk.cyan(nextItem ? nextItem.id : 'N/A')} - ${nextItem ? chalk.white.bold(truncate(nextItem.title, 40)) : chalk.yellow('No task available')}
403 | ` +
404 | `Priority: ${nextItem ? chalk.white(nextItem.priority || 'medium') : ''} Dependencies: ${nextItem ? formatDependenciesWithStatus(nextItem.dependencies, data.tasks, true, complexityReport) : ''}
405 | ` +
406 | `Complexity: ${nextItem && nextItem.complexityScore ? getComplexityWithColor(nextItem.complexityScore) : chalk.gray('N/A')}`;
407 |
408 | // Calculate width for side-by-side display
409 | // Box borders, padding take approximately 4 chars on each side
410 | const minDashboardWidth = 50; // Minimum width for dashboard
411 | const minDependencyWidth = 50; // Minimum width for dependency dashboard
412 | const totalMinWidth = minDashboardWidth + minDependencyWidth + 4; // Extra 4 chars for spacing
413 |
414 | // If terminal is wide enough, show boxes side by side with responsive widths
415 | if (terminalWidth >= totalMinWidth) {
416 | // Calculate widths proportionally for each box - use exact 50% width each
417 | const availableWidth = terminalWidth;
418 | const halfWidth = Math.floor(availableWidth / 2);
419 |
420 | // Account for border characters (2 chars on each side)
421 | const boxContentWidth = halfWidth - 4;
422 |
423 | // Create boxen options with precise widths
424 | const dashboardBox = boxen(projectDashboardContent, {
425 | padding: 1,
426 | borderColor: 'blue',
427 | borderStyle: 'round',
428 | width: boxContentWidth,
429 | dimBorder: false
430 | });
431 |
432 | const dependencyBox = boxen(dependencyDashboardContent, {
433 | padding: 1,
434 | borderColor: 'magenta',
435 | borderStyle: 'round',
436 | width: boxContentWidth,
437 | dimBorder: false
438 | });
439 |
440 | // Create a better side-by-side layout with exact spacing
441 | const dashboardLines = dashboardBox.split('\n');
442 | const dependencyLines = dependencyBox.split('\n');
443 |
444 | // Make sure both boxes have the same height
445 | const maxHeight = Math.max(dashboardLines.length, dependencyLines.length);
446 |
447 | // For each line of output, pad the dashboard line to exactly halfWidth chars
448 | // This ensures the dependency box starts at exactly the right position
449 | const combinedLines = [];
450 | for (let i = 0; i < maxHeight; i++) {
451 | // Get the dashboard line (or empty string if we've run out of lines)
452 | const dashLine = i < dashboardLines.length ? dashboardLines[i] : '';
453 | // Get the dependency line (or empty string if we've run out of lines)
454 | const depLine = i < dependencyLines.length ? dependencyLines[i] : '';
455 |
456 | // Remove any trailing spaces from dashLine before padding to exact width
457 | const trimmedDashLine = dashLine.trimEnd();
458 | // Pad the dashboard line to exactly halfWidth chars with no extra spaces
459 | const paddedDashLine = trimmedDashLine.padEnd(halfWidth, ' ');
460 |
461 | // Join the lines with no space in between
462 | combinedLines.push(paddedDashLine + depLine);
463 | }
464 |
465 | // Join all lines and output
466 | console.log(combinedLines.join('\n'));
467 | } else {
468 | // Terminal too narrow, show boxes stacked vertically
469 | const dashboardBox = boxen(projectDashboardContent, {
470 | padding: 1,
471 | borderColor: 'blue',
472 | borderStyle: 'round',
473 | margin: { top: 0, bottom: 1 }
474 | });
475 |
476 | const dependencyBox = boxen(dependencyDashboardContent, {
477 | padding: 1,
478 | borderColor: 'magenta',
479 | borderStyle: 'round',
480 | margin: { top: 0, bottom: 1 }
481 | });
482 |
483 | // Display stacked vertically
484 | console.log(dashboardBox);
485 | console.log(dependencyBox);
486 | }
487 |
488 | if (filteredTasks.length === 0) {
489 | console.log(
490 | boxen(
491 | statusFilter
492 | ? chalk.yellow(`No tasks with status '${statusFilter}' found`)
493 | : chalk.yellow('No tasks found'),
494 | { padding: 1, borderColor: 'yellow', borderStyle: 'round' }
495 | )
496 | );
497 | return;
498 | }
499 |
500 | // COMPLETELY REVISED TABLE APPROACH
501 | // Define percentage-based column widths and calculate actual widths
502 | // Adjust percentages based on content type and user requirements
503 |
504 | // Adjust ID width if showing subtasks (subtask IDs are longer: e.g., "1.2")
505 | const idWidthPct = withSubtasks ? 10 : 7;
506 |
507 | // Calculate max status length to accommodate "in-progress"
508 | const statusWidthPct = 15;
509 |
510 | // Increase priority column width as requested
511 | const priorityWidthPct = 12;
512 |
513 | // Make dependencies column smaller as requested (-20%)
514 | const depsWidthPct = 20;
515 |
516 | const complexityWidthPct = 10;
517 |
518 | // Calculate title/description width as remaining space (+20% from dependencies reduction)
519 | const titleWidthPct =
520 | 100 -
521 | idWidthPct -
522 | statusWidthPct -
523 | priorityWidthPct -
524 | depsWidthPct -
525 | complexityWidthPct;
526 |
527 | // Allow 10 characters for borders and padding
528 | const availableWidth = terminalWidth - 10;
529 |
530 | // Calculate actual column widths based on percentages
531 | const idWidth = Math.floor(availableWidth * (idWidthPct / 100));
532 | const statusWidth = Math.floor(availableWidth * (statusWidthPct / 100));
533 | const priorityWidth = Math.floor(availableWidth * (priorityWidthPct / 100));
534 | const depsWidth = Math.floor(availableWidth * (depsWidthPct / 100));
535 | const complexityWidth = Math.floor(
536 | availableWidth * (complexityWidthPct / 100)
537 | );
538 | const titleWidth = Math.floor(availableWidth * (titleWidthPct / 100));
539 |
540 | // Create a table with correct borders and spacing
541 | const table = new Table({
542 | head: [
543 | chalk.cyan.bold('ID'),
544 | chalk.cyan.bold('Title'),
545 | chalk.cyan.bold('Status'),
546 | chalk.cyan.bold('Priority'),
547 | chalk.cyan.bold('Dependencies'),
548 | chalk.cyan.bold('Complexity')
549 | ],
550 | colWidths: [
551 | idWidth,
552 | titleWidth,
553 | statusWidth,
554 | priorityWidth,
555 | depsWidth,
556 | complexityWidth // Added complexity column width
557 | ],
558 | style: {
559 | head: [], // No special styling for header
560 | border: [], // No special styling for border
561 | compact: false // Use default spacing
562 | },
563 | wordWrap: true,
564 | wrapOnWordBoundary: true
565 | });
566 |
567 | // Process tasks for the table
568 | filteredTasks.forEach((task) => {
569 | // Format dependencies with status indicators (colored)
570 | let depText = 'None';
571 | if (task.dependencies && task.dependencies.length > 0) {
572 | // Use the proper formatDependenciesWithStatus function for colored status
573 | depText = formatDependenciesWithStatus(
574 | task.dependencies,
575 | data.tasks,
576 | true,
577 | complexityReport
578 | );
579 | } else {
580 | depText = chalk.gray('None');
581 | }
582 |
583 | // Clean up any ANSI codes or confusing characters
584 | const cleanTitle = task.title.replace(/\n/g, ' ');
585 |
586 | // Get priority color
587 | const priorityColor =
588 | {
589 | high: chalk.red,
590 | medium: chalk.yellow,
591 | low: chalk.gray
592 | }[task.priority || 'medium'] || chalk.white;
593 |
594 | // Format status
595 | const status = getStatusWithColor(task.status, true);
596 |
597 | // Add the row without truncating dependencies
598 | table.push([
599 | task.id.toString(),
600 | truncate(cleanTitle, titleWidth - 3),
601 | status,
602 | priorityColor(truncate(task.priority || 'medium', priorityWidth - 2)),
603 | depText,
604 | task.complexityScore
605 | ? getComplexityWithColor(task.complexityScore)
606 | : chalk.gray('N/A')
607 | ]);
608 |
609 | // Add subtasks if requested
610 | if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
611 | task.subtasks.forEach((subtask) => {
612 | // Format subtask dependencies with status indicators
613 | let subtaskDepText = 'None';
614 | if (subtask.dependencies && subtask.dependencies.length > 0) {
615 | // Handle both subtask-to-subtask and subtask-to-task dependencies
616 | const formattedDeps = subtask.dependencies
617 | .map((depId) => {
618 | // Check if it's a dependency on another subtask
619 | if (typeof depId === 'number' && depId < 100) {
620 | const foundSubtask = task.subtasks.find(
621 | (st) => st.id === depId
622 | );
623 | if (foundSubtask) {
624 | const isDone =
625 | foundSubtask.status === 'done' ||
626 | foundSubtask.status === 'completed';
627 | const isInProgress = foundSubtask.status === 'in-progress';
628 |
629 | // Use consistent color formatting instead of emojis
630 | if (isDone) {
631 | return chalk.green.bold(`${task.id}.${depId}`);
632 | } else if (isInProgress) {
633 | return chalk.hex('#FFA500').bold(`${task.id}.${depId}`);
634 | } else {
635 | return chalk.red.bold(`${task.id}.${depId}`);
636 | }
637 | }
638 | }
639 | // Default to regular task dependency
640 | const depTask = data.tasks.find((t) => t.id === depId);
641 | if (depTask) {
642 | // Add complexity to depTask before checking status
643 | addComplexityToTask(depTask, complexityReport);
644 | const isDone =
645 | depTask.status === 'done' || depTask.status === 'completed';
646 | const isInProgress = depTask.status === 'in-progress';
647 | // Use the same color scheme as in formatDependenciesWithStatus
648 | if (isDone) {
649 | return chalk.green.bold(`${depId}`);
650 | } else if (isInProgress) {
651 | return chalk.hex('#FFA500').bold(`${depId}`);
652 | } else {
653 | return chalk.red.bold(`${depId}`);
654 | }
655 | }
656 | return chalk.cyan(depId.toString());
657 | })
658 | .join(', ');
659 |
660 | subtaskDepText = formattedDeps || chalk.gray('None');
661 | }
662 |
663 | // Add the subtask row without truncating dependencies
664 | table.push([
665 | `${task.id}.${subtask.id}`,
666 | chalk.dim(`└─ ${truncate(subtask.title, titleWidth - 5)}`),
667 | getStatusWithColor(subtask.status, true),
668 | chalk.dim('-'),
669 | subtaskDepText,
670 | subtask.complexityScore
671 | ? chalk.gray(`${subtask.complexityScore}`)
672 | : chalk.gray('N/A')
673 | ]);
674 | });
675 | }
676 | });
677 |
678 | // Ensure we output the table even if it had to wrap
679 | try {
680 | console.log(table.toString());
681 | } catch (err) {
682 | log('error', `Error rendering table: ${err.message}`);
683 |
684 | // Fall back to simpler output
685 | console.log(
686 | chalk.yellow(
687 | '\nFalling back to simple task list due to terminal width constraints:'
688 | )
689 | );
690 | filteredTasks.forEach((task) => {
691 | console.log(
692 | `${chalk.cyan(task.id)}: ${chalk.white(task.title)} - ${getStatusWithColor(task.status)}`
693 | );
694 | });
695 | }
696 |
697 | // Show filter info if applied
698 | if (statusFilter) {
699 | console.log(chalk.yellow(`\nFiltered by status: ${statusFilter}`));
700 | console.log(
701 | chalk.yellow(`Showing ${filteredTasks.length} of ${totalTasks} tasks`)
702 | );
703 | }
704 |
705 | // Define priority colors
706 | const priorityColors = {
707 | high: chalk.red.bold,
708 | medium: chalk.yellow,
709 | low: chalk.gray
710 | };
711 |
712 | // Show next task box in a prominent color
713 | if (nextItem) {
714 | // Prepare subtasks section if they exist (Only tasks have .subtasks property)
715 | let subtasksSection = '';
716 | // Check if the nextItem is a top-level task before looking for subtasks
717 | const parentTaskForSubtasks = data.tasks.find(
718 | (t) => String(t.id) === String(nextItem.id)
719 | ); // Find the original task object
720 | if (
721 | parentTaskForSubtasks &&
722 | parentTaskForSubtasks.subtasks &&
723 | parentTaskForSubtasks.subtasks.length > 0
724 | ) {
725 | subtasksSection = `\n\n${chalk.white.bold('Subtasks:')}\n`;
726 | subtasksSection += parentTaskForSubtasks.subtasks
727 | .map((subtask) => {
728 | // Add complexity to subtask before display
729 | addComplexityToTask(subtask, complexityReport);
730 | // Using a more simplified format for subtask status display
731 | const status = subtask.status || 'pending';
732 | const statusColors = {
733 | done: chalk.green,
734 | completed: chalk.green,
735 | pending: chalk.yellow,
736 | 'in-progress': chalk.blue,
737 | deferred: chalk.gray,
738 | blocked: chalk.red,
739 | cancelled: chalk.gray
740 | };
741 | const statusColor =
742 | statusColors[status.toLowerCase()] || chalk.white;
743 | // Ensure subtask ID is displayed correctly using parent ID from the original task object
744 | return `${chalk.cyan(`${parentTaskForSubtasks.id}.${subtask.id}`)} [${statusColor(status)}] ${subtask.title}`;
745 | })
746 | .join('\n');
747 | }
748 |
749 | console.log(
750 | boxen(
751 | chalk.hex('#FF8800').bold(
752 | // Use nextItem.id and nextItem.title
753 | `🔥 Next Task to Work On: #${nextItem.id} - ${nextItem.title}`
754 | ) +
755 | '\n\n' +
756 | // Use nextItem.priority, nextItem.status, nextItem.dependencies
757 | `${chalk.white('Priority:')} ${priorityColors[nextItem.priority || 'medium'](nextItem.priority || 'medium')} ${chalk.white('Status:')} ${getStatusWithColor(nextItem.status, true)}\n` +
758 | `${chalk.white('Dependencies:')} ${nextItem.dependencies && nextItem.dependencies.length > 0 ? formatDependenciesWithStatus(nextItem.dependencies, data.tasks, true, complexityReport) : chalk.gray('None')}\n\n` +
759 | // Use nextTask.description (Note: findNextTask doesn't return description, need to fetch original task/subtask for this)
760 | // *** Fetching original item for description and details ***
761 | `${chalk.white('Description:')} ${getWorkItemDescription(nextItem, data.tasks)}` +
762 | subtasksSection + // <-- Subtasks are handled above now
763 | '\n\n' +
764 | // Use nextItem.id
765 | `${chalk.cyan('Start working:')} ${chalk.yellow(`task-master set-status --id=${nextItem.id} --status=in-progress`)}\n` +
766 | // Use nextItem.id
767 | `${chalk.cyan('View details:')} ${chalk.yellow(`task-master show ${nextItem.id}`)}`,
768 | {
769 | padding: { left: 2, right: 2, top: 1, bottom: 1 },
770 | borderColor: '#FF8800',
771 | borderStyle: 'round',
772 | margin: { top: 1, bottom: 1 },
773 | title: '⚡ RECOMMENDED NEXT TASK ⚡',
774 | titleAlignment: 'center',
775 | width: terminalWidth - 4,
776 | fullscreen: false
777 | }
778 | )
779 | );
780 | } else {
781 | console.log(
782 | boxen(
783 | chalk.hex('#FF8800').bold('No eligible next task found') +
784 | '\n\n' +
785 | 'All pending tasks have dependencies that are not yet completed, or all tasks are done.',
786 | {
787 | padding: 1,
788 | borderColor: '#FF8800',
789 | borderStyle: 'round',
790 | margin: { top: 1, bottom: 1 },
791 | title: '⚡ NEXT TASK ⚡',
792 | titleAlignment: 'center',
793 | width: terminalWidth - 4 // Use full terminal width minus a small margin
794 | }
795 | )
796 | );
797 | }
798 |
799 | // Show next steps
800 | console.log(
801 | boxen(
802 | chalk.white.bold('Suggested Next Steps:') +
803 | '\n\n' +
804 | `${chalk.cyan('1.')} Run ${chalk.yellow('task-master next')} to see what to work on next\n` +
805 | `${chalk.cyan('2.')} Run ${chalk.yellow('task-master expand --id=<id>')} to break down a task into subtasks\n` +
806 | `${chalk.cyan('3.')} Run ${chalk.yellow('task-master set-status --id=<id> --status=done')} to mark a task as complete`,
807 | {
808 | padding: 1,
809 | borderColor: 'gray',
810 | borderStyle: 'round',
811 | margin: { top: 1 }
812 | }
813 | )
814 | );
815 | } catch (error) {
816 | log('error', `Error listing tasks: ${error.message}`);
817 |
818 | if (outputFormat === 'json') {
819 | // Return structured error for JSON output
820 | throw {
821 | code: 'TASK_LIST_ERROR',
822 | message: error.message,
823 | details: error.stack
824 | };
825 | }
826 |
827 | console.error(chalk.red(`Error: ${error.message}`));
828 | process.exit(1);
829 | }
830 | }
831 |
832 | // *** Helper function to get description for task or subtask ***
833 | function getWorkItemDescription(item, allTasks) {
834 | if (!item) return 'N/A';
835 | if (item.parentId) {
836 | // It's a subtask
837 | const parent = allTasks.find((t) => t.id === item.parentId);
838 | const subtask = parent?.subtasks?.find(
839 | (st) => `${parent.id}.${st.id}` === item.id
840 | );
841 | return subtask?.description || 'No description available.';
842 | } else {
843 | // It's a top-level task
844 | const task = allTasks.find((t) => String(t.id) === String(item.id));
845 | return task?.description || 'No description available.';
846 | }
847 | }
848 |
849 | /**
850 | * Generate markdown-formatted output for README files
851 | * @param {Object} data - Full tasks data
852 | * @param {Array} filteredTasks - Filtered tasks array
853 | * @param {Object} stats - Statistics object
854 | * @returns {string} - Formatted markdown string
855 | */
856 | function generateMarkdownOutput(data, filteredTasks, stats) {
857 | const {
858 | totalTasks,
859 | completedTasks,
860 | completionPercentage,
861 | doneCount,
862 | inProgressCount,
863 | pendingCount,
864 | blockedCount,
865 | deferredCount,
866 | cancelledCount,
867 | totalSubtasks,
868 | completedSubtasks,
869 | subtaskCompletionPercentage,
870 | inProgressSubtasks,
871 | pendingSubtasks,
872 | blockedSubtasks,
873 | deferredSubtasks,
874 | cancelledSubtasks,
875 | tasksWithNoDeps,
876 | tasksReadyToWork,
877 | tasksWithUnsatisfiedDeps,
878 | mostDependedOnTask,
879 | mostDependedOnTaskId,
880 | maxDependents,
881 | avgDependenciesPerTask,
882 | complexityReport,
883 | withSubtasks,
884 | nextItem
885 | } = stats;
886 |
887 | let markdown = '';
888 |
889 | // Create progress bars for markdown (using Unicode block characters)
890 | const createMarkdownProgressBar = (percentage, width = 20) => {
891 | const filled = Math.round((percentage / 100) * width);
892 | const empty = width - filled;
893 | return '█'.repeat(filled) + '░'.repeat(empty);
894 | };
895 |
896 | const taskProgressBar = createMarkdownProgressBar(completionPercentage, 20);
897 | const subtaskProgressBar = createMarkdownProgressBar(
898 | subtaskCompletionPercentage,
899 | 20
900 | );
901 |
902 | // Dashboard section
903 | // markdown += '```\n';
904 | markdown += '| Project Dashboard | |\n';
905 | markdown += '| :- |:-|\n';
906 | markdown += `| Task Progress | ${taskProgressBar} ${Math.round(completionPercentage)}% |\n`;
907 | markdown += `| Done | ${doneCount} |\n`;
908 | markdown += `| In Progress | ${inProgressCount} |\n`;
909 | markdown += `| Pending | ${pendingCount} |\n`;
910 | markdown += `| Deferred | ${deferredCount} |\n`;
911 | markdown += `| Cancelled | ${cancelledCount} |\n`;
912 | markdown += `|-|-|\n`;
913 | markdown += `| Subtask Progress | ${subtaskProgressBar} ${Math.round(subtaskCompletionPercentage)}% |\n`;
914 | markdown += `| Completed | ${completedSubtasks} |\n`;
915 | markdown += `| In Progress | ${inProgressSubtasks} |\n`;
916 | markdown += `| Pending | ${pendingSubtasks} |\n`;
917 |
918 | markdown += '\n\n';
919 |
920 | // Tasks table
921 | markdown +=
922 | '| ID | Title | Status | Priority | Dependencies | Complexity |\n';
923 | markdown +=
924 | '| :- | :- | :- | :- | :- | :- |\n';
925 |
926 | // Helper function to format status with symbols
927 | const getStatusSymbol = (status) => {
928 | switch (status) {
929 | case 'done':
930 | case 'completed':
931 | return '✓ done';
932 | case 'in-progress':
933 | return '► in-progress';
934 | case 'pending':
935 | return '○ pending';
936 | case 'blocked':
937 | return '⭕ blocked';
938 | case 'deferred':
939 | return 'x deferred';
940 | case 'cancelled':
941 | return 'x cancelled';
942 | case 'review':
943 | return '? review';
944 | default:
945 | return status || 'pending';
946 | }
947 | };
948 |
949 | // Helper function to format dependencies without color codes
950 | const formatDependenciesForMarkdown = (deps, allTasks) => {
951 | if (!deps || deps.length === 0) return 'None';
952 | return deps
953 | .map((depId) => {
954 | const depTask = allTasks.find((t) => t.id === depId);
955 | return depTask ? depId.toString() : depId.toString();
956 | })
957 | .join(', ');
958 | };
959 |
960 | // Process all tasks
961 | filteredTasks.forEach((task) => {
962 | const taskTitle = task.title; // No truncation for README
963 | const statusSymbol = getStatusSymbol(task.status);
964 | const priority = task.priority || 'medium';
965 | const deps = formatDependenciesForMarkdown(task.dependencies, data.tasks);
966 | const complexity = task.complexityScore
967 | ? `● ${task.complexityScore}`
968 | : 'N/A';
969 |
970 | markdown += `| ${task.id} | ${taskTitle} | ${statusSymbol} | ${priority} | ${deps} | ${complexity} |\n`;
971 |
972 | // Add subtasks if requested
973 | if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
974 | task.subtasks.forEach((subtask) => {
975 | const subtaskTitle = `${subtask.title}`; // No truncation
976 | const subtaskStatus = getStatusSymbol(subtask.status);
977 | const subtaskDeps = formatDependenciesForMarkdown(
978 | subtask.dependencies,
979 | data.tasks
980 | );
981 | const subtaskComplexity = subtask.complexityScore
982 | ? subtask.complexityScore.toString()
983 | : 'N/A';
984 |
985 | markdown += `| ${task.id}.${subtask.id} | ${subtaskTitle} | ${subtaskStatus} | - | ${subtaskDeps} | ${subtaskComplexity} |\n`;
986 | });
987 | }
988 | });
989 |
990 | return markdown;
991 | }
992 |
993 | /**
994 | * Format dependencies for compact output with truncation and coloring
995 | * @param {Array} dependencies - Array of dependency IDs
996 | * @returns {string} - Formatted dependency string with arrow prefix
997 | */
998 | function formatCompactDependencies(dependencies) {
999 | if (!dependencies || dependencies.length === 0) {
1000 | return '';
1001 | }
1002 |
1003 | if (dependencies.length > 5) {
1004 | const visible = dependencies.slice(0, 5).join(',');
1005 | const remaining = dependencies.length - 5;
1006 | return ` → ${chalk.cyan(visible)}${chalk.gray('... (+' + remaining + ' more)')}`;
1007 | } else {
1008 | return ` → ${chalk.cyan(dependencies.join(','))}`;
1009 | }
1010 | }
1011 |
1012 | /**
1013 | * Format a single task in compact one-line format
1014 | * @param {Object} task - Task object
1015 | * @param {number} maxTitleLength - Maximum title length before truncation
1016 | * @returns {string} - Formatted task line
1017 | */
1018 | function formatCompactTask(task, maxTitleLength = 50) {
1019 | const status = task.status || 'pending';
1020 | const priority = task.priority || 'medium';
1021 | const title = truncate(task.title || 'Untitled', maxTitleLength);
1022 |
1023 | // Use colored status from existing function
1024 | const coloredStatus = getStatusWithColor(status, true);
1025 |
1026 | // Color priority based on level
1027 | const priorityColors = {
1028 | high: chalk.red,
1029 | medium: chalk.yellow,
1030 | low: chalk.gray
1031 | };
1032 | const priorityColor = priorityColors[priority] || chalk.white;
1033 |
1034 | // Format dependencies using shared helper
1035 | const depsText = formatCompactDependencies(task.dependencies);
1036 |
1037 | return `${chalk.cyan(task.id)} ${coloredStatus} ${chalk.white(title)} ${priorityColor('(' + priority + ')')}${depsText}`;
1038 | }
1039 |
1040 | /**
1041 | * Format a subtask in compact format with indentation
1042 | * @param {Object} subtask - Subtask object
1043 | * @param {string|number} parentId - Parent task ID
1044 | * @param {number} maxTitleLength - Maximum title length before truncation
1045 | * @returns {string} - Formatted subtask line
1046 | */
1047 | function formatCompactSubtask(subtask, parentId, maxTitleLength = 47) {
1048 | const status = subtask.status || 'pending';
1049 | const title = truncate(subtask.title || 'Untitled', maxTitleLength);
1050 |
1051 | // Use colored status from existing function
1052 | const coloredStatus = getStatusWithColor(status, true);
1053 |
1054 | // Format dependencies using shared helper
1055 | const depsText = formatCompactDependencies(subtask.dependencies);
1056 |
1057 | return ` ${chalk.cyan(parentId + '.' + subtask.id)} ${coloredStatus} ${chalk.dim(title)}${depsText}`;
1058 | }
1059 |
1060 | /**
1061 | * Render complete compact output
1062 | * @param {Array} filteredTasks - Tasks to display
1063 | * @param {boolean} withSubtasks - Whether to include subtasks
1064 | * @returns {void} - Outputs directly to console
1065 | */
1066 | function renderCompactOutput(filteredTasks, withSubtasks) {
1067 | if (filteredTasks.length === 0) {
1068 | console.log('No tasks found');
1069 | return;
1070 | }
1071 |
1072 | const output = [];
1073 |
1074 | filteredTasks.forEach((task) => {
1075 | output.push(formatCompactTask(task));
1076 |
1077 | if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
1078 | task.subtasks.forEach((subtask) => {
1079 | output.push(formatCompactSubtask(subtask, task.id));
1080 | });
1081 | }
1082 | });
1083 |
1084 | console.log(output.join('\n'));
1085 | }
1086 |
1087 | export default listTasks;
1088 |
```
--------------------------------------------------------------------------------
/.taskmaster/docs/tdd-workflow-phase-1-core-rails.md:
--------------------------------------------------------------------------------
```markdown
1 | # Phase 1: Core Rails - Autonomous TDD Workflow
2 |
3 | ## Objective
4 | Implement the core autonomous TDD workflow with safe git operations, test generation/execution, and commit gating.
5 |
6 | ## Scope
7 | - WorkflowOrchestrator with event stream
8 | - GitAdapter and TestResultValidator
9 | - Subtask loop (RED → GREEN → COMMIT)
10 | - CLI commands for AI agent orchestration
11 | - MCP tools for AI agent orchestration
12 | - Test result validation (AI reports, TaskMaster validates)
13 | - Commit creation with enhanced metadata
14 | - Branch/tag mapping
15 | - Global storage for state and activity logs
16 | - Framework-agnostic design (AI runs tests, not TaskMaster)
17 | - Run report persistence
18 |
19 | ## Key Design Decisions
20 |
21 | ### Global Storage (`~/.taskmaster/`)
22 | - **Why:** Keeps project directory clean, client-friendly, no tooling evidence in PRs
23 | - **What:** All runtime state, logs, and throwaway artifacts
24 | - **Where:** `~/.taskmaster/projects/<project-path>/runs/<run-id>/`
25 |
26 | ### Dual System: State + Activity Log
27 | - **State (`state.json`):** For orchestration, tells AI what to do next, mutable
28 | - **Activity Log (`activity.jsonl`):** For debugging/audit, append-only event stream
29 | - **Separation:** Optimizes for different use cases (fast reads vs. complete history)
30 |
31 | ### Enhanced Commit Messages
32 | - **Why:** Enables future task-checker bot validation without external dependencies
33 | - **What:** Embeds task ID, phase, tag, test counts, coverage in commit body
34 | - **Benefit:** PR contains full context for review and automated validation
35 |
36 | ### Worktree Support
37 | - **Why:** Enables parallel autonomous agents on different branches
38 | - **How:** Each worktree has independent global state directory
39 | - **Isolation:** No conflicts, complete separation
40 |
41 | ### Framework-Agnostic Test Execution
42 | - **AI runs tests:** AI agent knows project context and test framework (npm test, pytest, go test)
43 | - **TaskMaster validates:** Only checks that RED fails and GREEN passes
44 | - **No framework detection:** TaskMaster doesn't need to know Jest vs Vitest vs pytest
45 | - **Trust but verify:** AI reports results, TaskMaster validates they make sense
46 | - **Language agnostic:** Works with any language/framework without TaskMaster changes
47 |
48 | ### AI Agent Orchestration Model
49 | - **Who executes:** User's AI agent (Claude Code, Cursor, Windsurf, etc.) - not TaskMaster
50 | - **TaskMaster's role:** Workflow orchestration, validation, commit creation
51 | - **AI agent's role:** Code generation, test execution, result reporting
52 | - **Communication:** Via CLI commands or MCP tools
53 | - **State-driven:** AI agent reads `state.json` to know what to do next
54 |
55 | **Separation of Concerns:**
56 |
57 | | TaskMaster Responsibilities | AI Agent Responsibilities |
58 | |----------------------------|---------------------------|
59 | | Workflow state machine | Generate tests |
60 | | Validate phase transitions | Run tests (knows test framework) |
61 | | Create commits with metadata | Implement code |
62 | | Store activity logs | Report test results |
63 | | Manage git operations | Understand project context |
64 | | Track progress | Choose appropriate test commands |
65 |
66 | **Flow:**
67 | ```
68 | AI Agent TaskMaster
69 | │ │
70 | ├──► tm autopilot start 1 │
71 | │ ├──► Creates state, branch
72 | │ ├──► Returns: "next action: RED phase for 1.1"
73 | │ │
74 | ├──► tm autopilot next │
75 | │ ├──► Reads state.json
76 | │ ├──► Returns: { phase: "red", subtask: "1.1", context: {...} }
77 | │ │
78 | │ Generate tests (AI does this) │
79 | │ npm test (AI runs this) │
80 | │ Results: 3 failed, 0 passed │
81 | │ │
82 | ├──► tm autopilot complete red 1.1 \ │
83 | │ --results="failed:3,passed:0" │
84 | │ ├──► Validates: tests failed ✓
85 | │ ├──► Updates state to GREEN
86 | │ ├──► Returns: "next action: GREEN phase"
87 | │ │
88 | ├──► tm autopilot next │
89 | │ ├──► Returns: { phase: "green", subtask: "1.1" }
90 | │ │
91 | │ Implement code (AI does this) │
92 | │ npm test (AI runs this) │
93 | │ Results: 3 passed, 0 failed │
94 | │ │
95 | ├──► tm autopilot complete green 1.1 \ │
96 | │ --results="passed:3,failed:0" │
97 | │ ├──► Validates: tests passed ✓
98 | │ ├──► Updates state to COMMIT
99 | │ ├──► Returns: "next action: COMMIT phase"
100 | │ │
101 | ├──► tm autopilot commit 1.1 │
102 | │ ├──► Detects changed files (git status)
103 | │ ├──► Stages files
104 | │ ├──► Creates commit with metadata
105 | │ ├──► Updates state to next subtask
106 | │ ├──► Returns: { sha: "a1b2c3d", nextAction: {...} }
107 | │ │
108 | └──► Loop continues... │
109 | ```
110 |
111 | **Key principle:** AI agent is the domain expert (knows the codebase, frameworks, tools). TaskMaster is the workflow expert (knows TDD process, state management, git operations).
112 |
113 | ## Deliverables
114 |
115 | ### 1. WorkflowOrchestrator (`packages/tm-core/src/services/workflow-orchestrator.ts`)
116 |
117 | **Responsibilities:**
118 | - State machine driving phases: Preflight → Branch/Tag → SubtaskIter → Finalize
119 | - Event emission for progress tracking
120 | - Coordination of Git, Test, and Executor adapters
121 | - Run state persistence
122 |
123 | **API:**
124 | ```typescript
125 | class WorkflowOrchestrator {
126 | async executeTask(taskId: string, options: AutopilotOptions): Promise<RunResult>
127 | async resume(runId: string): Promise<RunResult>
128 | on(event: string, handler: (data: any) => void): void
129 |
130 | // Events emitted:
131 | // - 'phase:start' { phase, timestamp }
132 | // - 'phase:complete' { phase, status, timestamp }
133 | // - 'subtask:start' { subtaskId, phase }
134 | // - 'subtask:complete' { subtaskId, phase, status }
135 | // - 'test:run' { subtaskId, phase, results }
136 | // - 'commit:created' { subtaskId, sha, message }
137 | // - 'error' { phase, error, recoverable }
138 | }
139 | ```
140 |
141 | **State Machine Phases:**
142 | 1. Preflight - validate environment
143 | 2. BranchSetup - create branch, set tag
144 | 3. SubtaskLoop - for each subtask: RED → GREEN → COMMIT
145 | 4. Finalize - full test suite, coverage check
146 | 5. Complete - run report, cleanup
147 |
148 | ### 2. GitAdapter (`packages/tm-core/src/services/git-adapter.ts`)
149 |
150 | **Responsibilities:**
151 | - All git operations with safety checks
152 | - Branch name generation from tag/task
153 | - Confirmation gates for destructive operations
154 |
155 | **API:**
156 | ```typescript
157 | class GitAdapter {
158 | async isWorkingTreeClean(): Promise<boolean>
159 | async getCurrentBranch(): Promise<string>
160 | async getDefaultBranch(): Promise<string>
161 | async createBranch(name: string): Promise<void>
162 | async checkoutBranch(name: string): Promise<void>
163 | async commit(message: string, files?: string[]): Promise<string>
164 | async push(branch: string, remote?: string): Promise<void>
165 |
166 | // Safety checks
167 | async assertNotOnDefaultBranch(): Promise<void>
168 | async assertCleanOrConfirm(): Promise<void>
169 |
170 | // Branch naming
171 | generateBranchName(tag: string, taskId: string, slug: string): string
172 | }
173 | ```
174 |
175 | **Guardrails:**
176 | - Never allow commits on default branch
177 | - Always check working tree before branch creation
178 | - Confirm destructive operations unless `--no-confirm` flag
179 |
180 | ### 3. Test Result Validator (`packages/tm-core/src/services/test-result-validator.ts`)
181 |
182 | **Responsibilities:**
183 | - Validate test results reported by AI agent
184 | - Ensure RED phase has failing tests
185 | - Ensure GREEN phase has passing tests
186 | - Enforce coverage thresholds (if provided)
187 |
188 | **API:**
189 | ```typescript
190 | class TestResultValidator {
191 | async validateRedPhase(results: TestResults): Promise<ValidationResult>
192 | async validateGreenPhase(results: TestResults, coverage?: number): Promise<ValidationResult>
193 | async meetsThresholds(coverage: number): Promise<boolean>
194 | }
195 |
196 | interface TestResults {
197 | passed: number
198 | failed: number
199 | skipped?: number
200 | total: number
201 | }
202 |
203 | interface ValidationResult {
204 | valid: boolean
205 | message: string
206 | suggestion?: string
207 | }
208 | ```
209 |
210 | **Validation Logic:**
211 | ```typescript
212 | async function validateRedPhase(results: TestResults): ValidationResult {
213 | if (results.failed === 0) {
214 | return {
215 | valid: false,
216 | message: "RED phase requires failing tests. All tests passed.",
217 | suggestion: "Verify tests are checking expected behavior. Tests should fail before implementation."
218 | }
219 | }
220 |
221 | if (results.passed > 0) {
222 | return {
223 | valid: true,
224 | message: `RED phase valid: ${results.failed} failing, ${results.passed} passing (existing tests)`,
225 | warning: "Some tests passing - ensure new tests are failing"
226 | }
227 | }
228 |
229 | return {
230 | valid: true,
231 | message: `RED phase complete: ${results.failed} tests failing as expected`
232 | }
233 | }
234 |
235 | async function validateGreenPhase(results: TestResults): ValidationResult {
236 | if (results.failed > 0) {
237 | return {
238 | valid: false,
239 | message: `GREEN phase incomplete: ${results.failed} tests still failing`,
240 | suggestion: "Continue implementing until all tests pass or retry GREEN phase"
241 | }
242 | }
243 |
244 | return {
245 | valid: true,
246 | message: `GREEN phase complete: ${results.passed} tests passing`
247 | }
248 | }
249 | ```
250 |
251 | **Note:** AI agent is responsible for:
252 | - Running test commands (knows npm test vs pytest vs go test)
253 | - Parsing test output
254 | - Reporting results to TaskMaster
255 |
256 | TaskMaster only validates the reported numbers make sense for the phase.
257 |
258 | ### 4. Test Generation Integration
259 |
260 | **Use Surgical Test Generator:**
261 | - Load prompt from `.claude/agents/surgical-test-generator.md`
262 | - Compose with task/subtask context
263 | - Generate tests via executor (Claude)
264 | - Write test files to detected locations
265 |
266 | **Prompt Composition:**
267 | ```typescript
268 | async function composeRedPrompt(subtask: Subtask, context: ProjectContext): Promise<string> {
269 | const systemPrompts = [
270 | loadFile('.cursor/rules/git_workflow.mdc'),
271 | loadFile('.cursor/rules/test_workflow.mdc'),
272 | loadFile('.claude/agents/surgical-test-generator.md')
273 | ]
274 |
275 | const taskContext = formatTaskContext(subtask)
276 | const instruction = formatRedInstruction(subtask, context)
277 |
278 | return [
279 | ...systemPrompts,
280 | '<TASK CONTEXT>',
281 | taskContext,
282 | '<INSTRUCTION>',
283 | instruction
284 | ].join('\n\n')
285 | }
286 | ```
287 |
288 | ### 5. Subtask Loop Implementation
289 |
290 | **RED Phase:**
291 | 1. TaskMaster returns RED action with subtask context
292 | 2. AI agent generates tests (TaskMaster not involved)
293 | 3. AI agent writes test files (TaskMaster not involved)
294 | 4. AI agent runs tests using project's test command (e.g., npm test)
295 | 5. AI agent reports results: `tm autopilot complete red <id> --results="failed:3,passed:0"`
296 | 6. TaskMaster validates: tests should have failures
297 | 7. If validation fails (tests passed), return error with suggestion
298 | 8. If validation passes, update state to GREEN, store results in activity log
299 | 9. Return next action (GREEN phase)
300 |
301 | **GREEN Phase:**
302 | 1. TaskMaster returns GREEN action with subtask context
303 | 2. AI agent implements code (TaskMaster not involved)
304 | 3. AI agent runs tests using project's test command
305 | 4. AI agent reports results: `tm autopilot complete green <id> --results="passed:5,failed:0" --coverage="85"`
306 | 5. TaskMaster validates: all tests should pass
307 | 6. If validation fails (tests still failing):
308 | - Increment attempt counter
309 | - If under max attempts: return GREEN action again with attempt number
310 | - If max attempts reached: save state, emit pause event, return resumable checkpoint
311 | 7. If validation passes: update state to COMMIT, store results in activity log
312 | 8. Return next action (COMMIT phase)
313 |
314 | **COMMIT Phase:**
315 | 1. TaskMaster receives commit command: `tm autopilot commit <id>`
316 | 2. Detect changed files: `git status --porcelain`
317 | 3. Validate coverage meets thresholds (if provided and threshold configured)
318 | 4. Generate conventional commit message with task metadata
319 | 5. Stage files: `git add <files>`
320 | 6. Create commit: `git commit -m "<message>"`
321 | 7. Update subtask status to 'done' in tasks.json
322 | 8. Log commit event to activity.jsonl
323 | 9. Update state to next subtask's RED phase
324 | 10. Return next action
325 |
326 | **Key changes from original design:**
327 | - AI agent runs all test commands (framework agnostic)
328 | - TaskMaster only validates reported results
329 | - No test framework detection needed
330 | - No test execution by TaskMaster
331 | - AI agent is trusted to report accurate results
332 |
333 | ### 6. Branch & Tag Management
334 |
335 | **Integration with existing tag system:**
336 | - Use `scripts/modules/task-manager/tag-management.js`
337 | - Explicit tag switching when branch created
338 | - Store branch ↔ tag mapping in run state
339 |
340 | **Branch Naming:**
341 | - Pattern from config: `{tag}/task-{id}-{slug}`
342 | - Default: `analytics/task-42-user-metrics`
343 | - Sanitize: lowercase, replace spaces with hyphens
344 |
345 | ### 7. Global Storage & State Management
346 |
347 | **Philosophy:**
348 | - All runtime state, logs, and throwaway artifacts stored globally in `~/.taskmaster/`
349 | - Project directory stays clean - only code changes and tasks.json versioned
350 | - Enables single-player autonomous mode without polluting PRs
351 | - Client-friendly: no evidence of tooling in source code
352 |
353 | **Global directory structure:**
354 | ```
355 | ~/.taskmaster/
356 | ├── projects/
357 | │ └── <project-path-normalized>/
358 | │ ├── runs/
359 | │ │ └── <tag>__task-<id>__<timestamp>/
360 | │ │ ├── manifest.json # run metadata
361 | │ │ ├── activity.jsonl # event stream (debugging)
362 | │ │ ├── state.json # resumable checkpoint
363 | │ │ ├── commits.txt # commit SHAs
364 | │ │ └── test-results/
365 | │ │ ├── subtask-1.1-red.json
366 | │ │ ├── subtask-1.1-green.json
367 | │ │ └── final-suite.json
368 | │ └── tags/
369 | │ └── <tag-name>/
370 | │ └── current-run.json # active run pointer
371 | └── cache/
372 | └── templates/ # shared templates
373 | ```
374 |
375 | **Project path normalization:**
376 | ```typescript
377 | function getProjectStoragePath(projectRoot: string): string {
378 | const normalized = projectRoot
379 | .replace(/\//g, '-')
380 | .replace(/^-/, '')
381 |
382 | return path.join(os.homedir(), '.taskmaster', 'projects', normalized)
383 | // Example: ~/.taskmaster/projects/-Volumes-Workspace-contrib-task-master-claude-task-master
384 | }
385 | ```
386 |
387 | **Run ID generation:**
388 | ```typescript
389 | function generateRunId(tag: string, taskId: string): string {
390 | const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
391 | return `${tag}__task-${taskId}__${timestamp}`
392 | // Example: tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z
393 | }
394 | ```
395 |
396 | **manifest.json:**
397 | ```json
398 | {
399 | "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z",
400 | "projectRoot": "/Volumes/Workspace/contrib/task-master/claude-task-master",
401 | "taskId": "1",
402 | "tag": "tdd-workflow-phase-0",
403 | "branch": "tdd-phase-0-implementation",
404 | "startTime": "2025-10-07T14:30:00Z",
405 | "endTime": null,
406 | "status": "in-progress",
407 | "currentPhase": "subtask-loop",
408 | "currentSubtask": "1.2",
409 | "subtasksCompleted": ["1.1"],
410 | "subtasksFailed": [],
411 | "totalCommits": 1
412 | }
413 | ```
414 |
415 | **state.json** (orchestration state):
416 | ```json
417 | {
418 | "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z",
419 | "taskId": "1",
420 | "tag": "tdd-workflow-phase-0",
421 | "branch": "tdd-phase-0-implementation",
422 | "currentSubtask": "1.2",
423 | "currentPhase": "green",
424 | "attemptNumber": 1,
425 | "maxAttempts": 3,
426 | "completedSubtasks": ["1.1"],
427 | "pendingSubtasks": ["1.2", "1.3", "1.4"],
428 | "nextAction": {
429 | "type": "implement",
430 | "subtask": "1.2",
431 | "phase": "green",
432 | "context": {
433 | "testFile": "src/__tests__/preflight.test.ts",
434 | "failingTests": [
435 | "should detect test runner from package.json",
436 | "should validate git working tree"
437 | ],
438 | "implementationFiles": ["src/services/preflight-checker.ts"]
439 | }
440 | },
441 | "lastUpdated": "2025-10-07T14:31:45Z",
442 | "canResume": true
443 | }
444 | ```
445 |
446 | **activity.jsonl** (append-only event log):
447 | ```jsonl
448 | {"ts":"2025-10-07T14:30:00Z","event":"phase:start","phase":"preflight","status":"ok"}
449 | {"ts":"2025-10-07T14:30:15Z","event":"phase:complete","phase":"preflight","checks":{"git":true,"test":true,"tools":true}}
450 | {"ts":"2025-10-07T14:30:20Z","event":"branch:created","branch":"tdd-phase-0-implementation"}
451 | {"ts":"2025-10-07T14:30:22Z","event":"tag:switched","from":"master","to":"tdd-workflow-phase-0"}
452 | {"ts":"2025-10-07T14:30:25Z","event":"subtask:start","subtaskId":"1.1","phase":"red"}
453 | {"ts":"2025-10-07T14:31:10Z","event":"test:generated","files":["src/__tests__/autopilot.test.ts"],"testCount":3}
454 | {"ts":"2025-10-07T14:31:15Z","event":"test:run","subtaskId":"1.1","phase":"red","passed":0,"failed":3,"status":"expected"}
455 | {"ts":"2025-10-07T14:31:20Z","event":"phase:transition","from":"red","to":"green"}
456 | {"ts":"2025-10-07T14:32:45Z","event":"code:modified","files":["src/commands/autopilot.ts"],"linesChanged":"+58,-0"}
457 | {"ts":"2025-10-07T14:33:00Z","event":"test:run","subtaskId":"1.1","phase":"green","attempt":1,"passed":3,"failed":0,"status":"success"}
458 | {"ts":"2025-10-07T14:33:15Z","event":"commit:created","subtaskId":"1.1","sha":"a1b2c3d","message":"feat(cli): add autopilot command skeleton (task 1.1)"}
459 | {"ts":"2025-10-07T14:33:20Z","event":"subtask:complete","subtaskId":"1.1","duration":"180s"}
460 | ```
461 |
462 | **current-run.json** (active run pointer):
463 | ```json
464 | {
465 | "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z",
466 | "taskId": "1",
467 | "tag": "tdd-workflow-phase-0",
468 | "startTime": "2025-10-07T14:30:00Z",
469 | "status": "in-progress"
470 | }
471 | ```
472 |
473 | **What stays in project (versioned):**
474 | ```
475 | <project>/
476 | ├── .taskmaster/
477 | │ ├── tasks/
478 | │ │ └── tasks.json # ✅ Versioned (task definitions)
479 | │ └── config.json # ✅ Versioned (shared config)
480 | └── .gitignore # Add: .taskmaster/state/, .taskmaster/reports/
481 | ```
482 |
483 | **State vs Activity Log:**
484 |
485 | | State File (state.json) | Activity Log (activity.jsonl) |
486 | |------------------------|-------------------------------|
487 | | Current position | Full history |
488 | | What to do next | What happened |
489 | | Mutable (updated) | Immutable (append-only) |
490 | | For orchestration | For debugging/audit |
491 | | Single JSON object | Line-delimited JSON |
492 | | Small (~2KB) | Can grow large |
493 |
494 | **Resume logic:**
495 | ```typescript
496 | async function resumeWorkflow(): Promise<void> {
497 | // 1. Find active run
498 | const currentRun = await loadJSON('~/.taskmaster/projects/<project>/tags/<tag>/current-run.json')
499 |
500 | // 2. Load state from that run
501 | const state = await loadJSON(`~/.taskmaster/projects/<project>/runs/${currentRun.runId}/state.json`)
502 |
503 | // 3. Continue from checkpoint
504 | return orchestrator.resumeFrom(state)
505 | }
506 | ```
507 |
508 | ### 8. Enhanced Commit Message Format
509 |
510 | **Purpose:**
511 | - Embed task context in commits for future validation
512 | - Enable task-checker bot to verify alignment
513 | - Provide audit trail without needing external logs in PR
514 |
515 | **Commit message template:**
516 | ```
517 | {type}({scope}): {summary} (task {taskId})
518 |
519 | {detailed description}
520 |
521 | Task: #{taskId} - {taskTitle}
522 | Phase: {phaseName}
523 | Tag: {tagName}
524 |
525 | Tests: {testCount} passing
526 | Coverage: {coveragePercent}% lines
527 | ```
528 |
529 | **Example commit:**
530 | ```
531 | feat(cli): add autopilot command skeleton (task 1.1)
532 |
533 | Implements AutopilotCommand class with Commander.js integration.
534 | Adds argument parsing for task ID and dry-run flag. Includes basic
535 | command registration and help text following existing CLI patterns.
536 |
537 | Task: #1.1 - Create command structure
538 | Phase: Phase 0 - Spike
539 | Tag: tdd-workflow-phase-0
540 |
541 | Tests: 3 passing
542 | Coverage: 92% lines
543 | ```
544 |
545 | **Conventional commit types:**
546 | - `feat` - New feature or capability
547 | - `fix` - Bug fix
548 | - `test` - Test-only changes
549 | - `refactor` - Code restructuring without behavior change
550 | - `docs` - Documentation updates
551 | - `chore` - Build/tooling changes
552 |
553 | **Scope determination:**
554 | ```typescript
555 | function determineScope(files: string[]): string {
556 | // Extract common scope from changed files
557 | const scopes = files.map(f => {
558 | if (f.startsWith('apps/cli/')) return 'cli'
559 | if (f.startsWith('packages/tm-core/')) return 'core'
560 | if (f.startsWith('packages/tm-mcp/')) return 'mcp'
561 | return 'misc'
562 | })
563 |
564 | // Use most common scope
565 | return mode(scopes)
566 | }
567 | ```
568 |
569 | **Commit validation (future task-checker bot):**
570 | ```typescript
571 | async function validateCommit(commit: Commit, task: Task): Promise<ValidationResult> {
572 | const taskId = extractTaskId(commit.message) // "1.1"
573 | const task = await loadTask(taskId)
574 |
575 | return aiChecker.validate({
576 | commitDiff: commit.diff,
577 | commitMessage: commit.message,
578 | taskDescription: task.description,
579 | acceptanceCriteria: task.acceptanceCriteria,
580 | testStrategy: task.testStrategy
581 | })
582 | }
583 | ```
584 |
585 | ### 9. CLI Commands for AI Agent Orchestration
586 |
587 | **New CLI commands** (all under `tm autopilot` namespace):
588 |
589 | ```bash
590 | # Start workflow - creates branch, initializes state
591 | tm autopilot start <taskId> [options]
592 | --branch <name> # Override branch name
593 | --no-confirm # Skip confirmations
594 | --max-attempts <n> # Override max GREEN attempts
595 |
596 | # Get next action from state
597 | tm autopilot next [options]
598 | --json # Output as JSON for parsing
599 |
600 | # Complete a phase and report test results
601 | tm autopilot complete <phase> <subtaskId> --results="<passed:n,failed:n>" [options]
602 | # phase: red | green
603 | --results <passed:n,failed:n> # Required: test results from AI
604 | --coverage <percentage> # Optional: coverage percentage
605 | --files <file1,file2> # Optional: files changed (auto-detected if omitted)
606 |
607 | # Create commit (called by AI after GREEN passes)
608 | tm autopilot commit <subtaskId> [options]
609 | --message <msg> # Override commit message
610 |
611 | # Resume from interrupted run
612 | tm autopilot resume [options]
613 | --run-id <id> # Specific run to resume
614 |
615 | # Get current status
616 | tm autopilot status
617 | --json # Output as JSON
618 |
619 | # Watch activity log in real-time
620 | tm autopilot watch
621 |
622 | # Abort current run
623 | tm autopilot abort [options]
624 | --cleanup # Delete branch and state
625 | ```
626 |
627 | **Command details:**
628 |
629 | **`tm autopilot start <taskId>`**
630 | - Creates global state directory
631 | - Creates feature branch
632 | - Switches tag
633 | - Initializes state.json with first subtask
634 | - Returns next action (RED phase for first subtask)
635 |
636 | **`tm autopilot next`**
637 | - Reads `~/.taskmaster/projects/<project>/tags/<tag>/current-run.json`
638 | - Reads `~/.taskmaster/projects/<project>/runs/<run-id>/state.json`
639 | - Returns next action with full context
640 |
641 | Output:
642 | ```json
643 | {
644 | "action": "red",
645 | "subtask": {
646 | "id": "1.1",
647 | "title": "Create command structure",
648 | "description": "...",
649 | "testStrategy": "..."
650 | },
651 | "context": {
652 | "projectRoot": "/path/to/project",
653 | "testPattern": "**/*.test.ts",
654 | "existingTests": []
655 | },
656 | "instructions": "Generate tests for this subtask. Tests should fail initially."
657 | }
658 | ```
659 |
660 | **`tm autopilot complete <phase> <subtaskId>`**
661 | - Receives test results from AI agent
662 | - Validates phase completion:
663 | - **RED**: Ensures reported results show failures
664 | - **GREEN**: Ensures reported results show all tests passing
665 | - Updates state to next phase
666 | - Logs event to activity.jsonl with test results
667 | - Returns next action
668 |
669 | **Examples:**
670 | ```bash
671 | # After AI generates tests and runs them
672 | tm autopilot complete red 1.1 --results="failed:3,passed:0"
673 |
674 | # After AI implements code and runs tests
675 | tm autopilot complete green 1.1 --results="passed:3,failed:0" --coverage="92"
676 |
677 | # With existing passing tests
678 | tm autopilot complete red 1.1 --results="failed:3,passed:12"
679 | ```
680 |
681 | **`tm autopilot commit <subtaskId>`**
682 | - Generates commit message from template
683 | - Stages files
684 | - Creates commit with enhanced message
685 | - Updates subtask status to 'done'
686 | - Updates state to next subtask
687 | - Returns next action
688 |
689 | **`tm autopilot status`**
690 | ```json
691 | {
692 | "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z",
693 | "taskId": "1",
694 | "currentSubtask": "1.2",
695 | "currentPhase": "green",
696 | "attemptNumber": 1,
697 | "progress": {
698 | "completed": ["1.1"],
699 | "current": "1.2",
700 | "remaining": ["1.3", "1.4"]
701 | },
702 | "commits": 1,
703 | "startTime": "2025-10-07T14:30:00Z",
704 | "duration": "5m 30s"
705 | }
706 | ```
707 |
708 | ### 10. MCP Tools for AI Agent Orchestration
709 |
710 | **New MCP tools** (add to `packages/tm-mcp/src/tools/`):
711 |
712 | ```typescript
713 | // autopilot_start
714 | {
715 | name: "autopilot_start",
716 | description: "Start autonomous TDD workflow for a task",
717 | parameters: {
718 | taskId: string,
719 | options?: {
720 | branch?: string,
721 | maxAttempts?: number
722 | }
723 | },
724 | returns: {
725 | runId: string,
726 | branch: string,
727 | nextAction: NextAction
728 | }
729 | }
730 |
731 | // autopilot_next
732 | {
733 | name: "autopilot_next",
734 | description: "Get next action from workflow state",
735 | parameters: {
736 | projectRoot?: string // defaults to current
737 | },
738 | returns: {
739 | action: "red" | "green" | "commit" | "complete",
740 | subtask: Subtask,
741 | context: Context,
742 | instructions: string
743 | }
744 | }
745 |
746 | // autopilot_complete_phase
747 | {
748 | name: "autopilot_complete_phase",
749 | description: "Report test results and validate phase completion",
750 | parameters: {
751 | phase: "red" | "green",
752 | subtaskId: string,
753 | testResults: {
754 | passed: number,
755 | failed: number,
756 | skipped?: number
757 | },
758 | coverage?: number, // Optional coverage percentage
759 | files?: string[] // Optional, auto-detected if not provided
760 | },
761 | returns: {
762 | validated: boolean,
763 | message: string,
764 | suggestion?: string,
765 | nextAction: NextAction
766 | }
767 | }
768 |
769 | // autopilot_commit
770 | {
771 | name: "autopilot_commit",
772 | description: "Create commit for completed subtask",
773 | parameters: {
774 | subtaskId: string,
775 | files?: string[],
776 | message?: string // Override
777 | },
778 | returns: {
779 | commitSha: string,
780 | message: string,
781 | nextAction: NextAction
782 | }
783 | }
784 |
785 | // autopilot_status
786 | {
787 | name: "autopilot_status",
788 | description: "Get current workflow status",
789 | parameters: {
790 | projectRoot?: string
791 | },
792 | returns: {
793 | runId: string,
794 | taskId: string,
795 | currentSubtask: string,
796 | currentPhase: string,
797 | progress: Progress,
798 | commits: number
799 | }
800 | }
801 |
802 | // autopilot_resume
803 | {
804 | name: "autopilot_resume",
805 | description: "Resume interrupted workflow",
806 | parameters: {
807 | runId?: string // defaults to current
808 | },
809 | returns: {
810 | resumed: boolean,
811 | nextAction: NextAction
812 | }
813 | }
814 | ```
815 |
816 | **MCP tool usage example (Claude Code session):**
817 |
818 | ```javascript
819 | // AI agent calls MCP tools
820 | const { runId, nextAction } = await mcp.autopilot_start({ taskId: "1" })
821 |
822 | while (nextAction.action !== "complete") {
823 | const action = await mcp.autopilot_next()
824 |
825 | if (action.action === "red") {
826 | // AI generates tests
827 | const tests = await generateTests(action.subtask, action.context)
828 | await writeFiles(tests)
829 |
830 | // AI runs tests (using project's test command)
831 | const testOutput = await runCommand("npm test") // or pytest, go test, etc.
832 | const results = parseTestOutput(testOutput)
833 |
834 | // Report results to TaskMaster
835 | const validation = await mcp.autopilot_complete_phase({
836 | phase: "red",
837 | subtaskId: action.subtask.id,
838 | testResults: {
839 | passed: results.passed,
840 | failed: results.failed,
841 | skipped: results.skipped
842 | }
843 | })
844 |
845 | if (!validation.validated) {
846 | console.error(validation.message)
847 | // Handle validation failure
848 | }
849 | }
850 |
851 | if (action.action === "green") {
852 | // AI implements code
853 | const impl = await implementCode(action.subtask, action.context)
854 | await writeFiles(impl)
855 |
856 | // AI runs tests again
857 | const testOutput = await runCommand("npm test")
858 | const results = parseTestOutput(testOutput)
859 | const coverage = parseCoverage(testOutput)
860 |
861 | // Report results to TaskMaster
862 | const validation = await mcp.autopilot_complete_phase({
863 | phase: "green",
864 | subtaskId: action.subtask.id,
865 | testResults: {
866 | passed: results.passed,
867 | failed: results.failed
868 | },
869 | coverage: coverage.lines
870 | })
871 |
872 | if (!validation.validated) {
873 | console.log(validation.message, validation.suggestion)
874 | // Retry or handle failure
875 | }
876 | }
877 |
878 | if (action.action === "commit") {
879 | // TaskMaster creates the commit
880 | const { commitSha, nextAction: next } = await mcp.autopilot_commit({
881 | subtaskId: action.subtask.id
882 | })
883 |
884 | nextAction = next
885 | }
886 | }
887 | ```
888 |
889 | ### 11. AI Agent Instructions (CLAUDE.md integration)
890 |
891 | Add to `.claude/CLAUDE.md` or `.cursor/rules/`:
892 |
893 | ````markdown
894 | ## TaskMaster Autonomous Workflow
895 |
896 | When working on tasks with `tm autopilot`:
897 |
898 | 1. **Start workflow:**
899 | ```bash
900 | tm autopilot start <taskId>
901 | ```
902 |
903 | 2. **Loop until complete:**
904 | ```bash
905 | # Get next action
906 | NEXT=$(tm autopilot next --json)
907 | ACTION=$(echo $NEXT | jq -r '.action')
908 | SUBTASK=$(echo $NEXT | jq -r '.subtask.id')
909 |
910 | case $ACTION in
911 | red)
912 | # 1. Generate tests based on instructions
913 | # 2. Write test files
914 | # 3. Run tests yourself (you know the test command)
915 | npm test # or pytest, go test, cargo test, etc.
916 |
917 | # 4. Report results to TaskMaster
918 | tm autopilot complete red $SUBTASK --results="failed:3,passed:0"
919 | ;;
920 |
921 | green)
922 | # 1. Implement code to pass tests
923 | # 2. Write implementation files
924 | # 3. Run tests yourself
925 | npm test
926 |
927 | # 4. Report results to TaskMaster (include coverage if available)
928 | tm autopilot complete green $SUBTASK --results="passed:3,failed:0" --coverage="92"
929 | ;;
930 |
931 | commit)
932 | # TaskMaster handles git operations
933 | tm autopilot commit $SUBTASK
934 | ;;
935 |
936 | complete)
937 | echo "Workflow complete!"
938 | ;;
939 | esac
940 | ```
941 |
942 | 3. **State is preserved** - you can stop/resume anytime with `tm autopilot resume`
943 |
944 | **Important:** You are responsible for:
945 | - Running test commands (TaskMaster doesn't know your test framework)
946 | - Parsing test output (passed/failed counts)
947 | - Reporting accurate results
948 |
949 | **Via MCP:**
950 | Use `autopilot_*` tools for the same workflow with better integration.
951 | ````
952 |
953 | **Example AI agent prompt:**
954 |
955 | ```markdown
956 | You are working autonomously on Task Master tasks using the autopilot workflow.
957 |
958 | Instructions:
959 | 1. Call `tm autopilot next --json` to get your next action
960 | 2. Read the action type and context
961 | 3. Execute the action:
962 | - **RED**:
963 | * Generate tests that fail initially
964 | * Run tests: `npm test` (or appropriate test command)
965 | * Report: `tm autopilot complete red <id> --results="failed:n,passed:n"`
966 | - **GREEN**:
967 | * Implement code to pass the tests
968 | * Run tests: `npm test`
969 | * Report: `tm autopilot complete green <id> --results="passed:n,failed:n" --coverage="nn"`
970 | - **COMMIT**:
971 | * Call: `tm autopilot commit <id>` (TaskMaster handles git)
972 | 4. Repeat until action is "complete"
973 |
974 | Always:
975 | - Follow TDD principles (RED → GREEN → COMMIT)
976 | - YOU run the tests (TaskMaster doesn't know test frameworks)
977 | - Report accurate test results (passed/failed counts)
978 | - Write minimal code to pass tests
979 | - Check `tm autopilot status` if unsure of current state
980 |
981 | You are responsible for:
982 | - Knowing which test command to run (npm test, pytest, go test, etc.)
983 | - Parsing test output to get pass/fail counts
984 | - Understanding the project's testing framework
985 | - Running tests after generating/implementing code
986 |
987 | TaskMaster is responsible for:
988 | - Validating your reported results make sense (RED should fail, GREEN should pass)
989 | - Creating properly formatted git commits
990 | - Managing workflow state and transitions
991 | ```
992 |
993 | ## Success Criteria
994 | - Can execute a simple task end-to-end without manual intervention
995 | - All commits made on feature branch, never on default branch
996 | - Tests are generated before implementation (RED → GREEN order enforced)
997 | - Only commits when tests pass and coverage meets threshold
998 | - Run state is persisted and can be inspected post-run
999 | - Clear error messages when things go wrong
1000 | - Orchestrator events allow CLI to show live progress
1001 |
1002 | ## Configuration
1003 |
1004 | **Add to `.taskmaster/config.json` (versioned):**
1005 | ```json
1006 | {
1007 | "autopilot": {
1008 | "enabled": true,
1009 | "requireCleanWorkingTree": true,
1010 | "commitTemplate": "{type}({scope}): {msg} (task {taskId})",
1011 | "defaultCommitType": "feat",
1012 | "maxGreenAttempts": 3,
1013 | "testTimeout": 300000,
1014 | "storage": {
1015 | "location": "global",
1016 | "basePath": "~/.taskmaster"
1017 | }
1018 | },
1019 | "test": {
1020 | "runner": "auto",
1021 | "coverageThresholds": {
1022 | "lines": 80,
1023 | "branches": 80,
1024 | "functions": 80,
1025 | "statements": 80
1026 | },
1027 | "targetedRunPattern": "**/*.test.js"
1028 | },
1029 | "git": {
1030 | "branchPattern": "{tag}/task-{id}-{slug}",
1031 | "defaultRemote": "origin"
1032 | }
1033 | }
1034 | ```
1035 |
1036 | **Update `.gitignore` (keep project clean):**
1037 | ```gitignore
1038 | # TaskMaster runtime artifacts (stored globally, not needed in repo)
1039 | .taskmaster/state/
1040 | .taskmaster/reports/
1041 |
1042 | # Keep these versioned
1043 | !.taskmaster/tasks/
1044 | !.taskmaster/config.json
1045 | !.taskmaster/docs/
1046 | !.taskmaster/templates/
1047 | ```
1048 |
1049 | ## Out of Scope (defer to Phase 2)
1050 | - PR creation (gh integration)
1051 | - Resume functionality (`--resume` flag)
1052 | - Lint/format step
1053 | - Multiple executor support (only Claude)
1054 |
1055 | ## Implementation Order
1056 |
1057 | ### Phase 1A: Infrastructure (Week 1)
1058 | 1. Global storage utilities (path normalization, run ID generation)
1059 | 2. Activity log writer (append-only JSONL)
1060 | 3. State manager (load/save/update state.json)
1061 | 4. GitAdapter with safety checks
1062 | 5. TestResultValidator (validate RED/GREEN phase results)
1063 |
1064 | ### Phase 1B: Orchestration Core (Week 1-2)
1065 | 6. WorkflowOrchestrator state machine skeleton
1066 | 7. State transitions (Preflight → BranchSetup → SubtaskLoop → Finalize)
1067 | 8. Event emitter for activity logging
1068 | 9. Enhanced commit message generator
1069 |
1070 | ### Phase 1C: TDD Loop (Week 2)
1071 | 10. RED phase validator (ensure tests fail)
1072 | 11. GREEN phase validator (ensure tests pass)
1073 | 12. COMMIT phase implementation (staging, committing)
1074 | 13. Subtask progression logic
1075 |
1076 | ### Phase 1D: CLI Interface (Week 2-3)
1077 | 14. `tm autopilot start` command
1078 | 15. `tm autopilot next` command
1079 | 16. `tm autopilot complete` command
1080 | 17. `tm autopilot commit` command
1081 | 18. `tm autopilot status` command
1082 | 19. `tm autopilot resume` command
1083 |
1084 | ### Phase 1E: MCP Interface (Week 3)
1085 | 20. `autopilot_start` tool
1086 | 21. `autopilot_next` tool
1087 | 22. `autopilot_complete_phase` tool
1088 | 23. `autopilot_commit` tool
1089 | 24. `autopilot_status` tool
1090 | 25. `autopilot_resume` tool
1091 |
1092 | ### Phase 1F: Integration (Week 3)
1093 | 26. AI agent instruction templates
1094 | 27. Error handling and recovery
1095 | 28. Integration tests
1096 | 29. Documentation
1097 |
1098 | ## Testing Strategy
1099 | - Unit tests for global storage (path normalization, state management)
1100 | - Unit tests for activity log (JSONL append, parsing)
1101 | - Unit tests for each adapter (mock git/test commands)
1102 | - Integration tests with real git repo (temporary directory)
1103 | - End-to-end test with sample task in test project
1104 | - Verify no commits on default branch (security test)
1105 | - Verify commit gating works (force test failure, ensure no commit)
1106 | - Verify enhanced commit messages include task context
1107 | - Test resume from state checkpoint
1108 | - Verify project directory stays clean (no runtime artifacts)
1109 |
1110 | ## Dependencies
1111 | - Phase 0 completed (CLI skeleton, preflight checks)
1112 | - Existing TaskService and executor infrastructure
1113 | - Surgical Test Generator prompt file exists
1114 |
1115 | ## Estimated Effort
1116 | 2-3 weeks
1117 |
1118 | ## Risks & Mitigations
1119 | - **Risk:** Test generation produces invalid/wrong tests
1120 | - **Mitigation:** Use Surgical Test Generator prompt, add manual review step in early iterations
1121 |
1122 | - **Risk:** Implementation attempts timeout/fail repeatedly
1123 | - **Mitigation:** Max attempts with pause/resume; store state for manual intervention
1124 |
1125 | - **Risk:** Coverage parsing fails on different test frameworks
1126 | - **Mitigation:** Start with one framework (vitest), add parsers incrementally
1127 |
1128 | - **Risk:** Git operations fail (conflicts, permissions)
1129 | - **Mitigation:** Detailed error messages, save state before destructive ops
1130 |
1131 | ## Validation
1132 | Test with:
1133 | - Simple task (1 subtask, clear requirements)
1134 | - Medium task (3 subtasks with dependencies)
1135 | - Task requiring multiple GREEN attempts
1136 | - Task with dirty working tree (should error)
1137 | - Task on default branch (should error)
1138 | - Project without test command (should error with helpful message)
1139 | - Verify global storage created in `~/.taskmaster/projects/<project>/`
1140 | - Verify activity log is valid JSONL and streamable
1141 | - Verify state.json allows resumption
1142 | - Verify commit messages include task metadata
1143 | - Verify project directory contains no runtime artifacts after run
1144 | - Test with multiple worktrees (independent state per worktree)
1145 |
```