This is page 9 of 69. Use http://codebase.md/eyaltoledano/claude-task-master?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ └── README.md
├── .claude
│ ├── commands
│ │ └── dedupe.md
│ └── TM_COMMANDS_GUIDE.md
├── .claude-plugin
│ └── marketplace.json
├── .coderabbit.yaml
├── .cursor
│ ├── mcp.json
│ └── rules
│ ├── ai_providers.mdc
│ ├── ai_services.mdc
│ ├── architecture.mdc
│ ├── changeset.mdc
│ ├── commands.mdc
│ ├── context_gathering.mdc
│ ├── cursor_rules.mdc
│ ├── dependencies.mdc
│ ├── dev_workflow.mdc
│ ├── git_workflow.mdc
│ ├── glossary.mdc
│ ├── mcp.mdc
│ ├── new_features.mdc
│ ├── self_improve.mdc
│ ├── tags.mdc
│ ├── taskmaster.mdc
│ ├── tasks.mdc
│ ├── telemetry.mdc
│ ├── test_workflow.mdc
│ ├── tests.mdc
│ ├── ui.mdc
│ └── utilities.mdc
├── .cursorignore
├── .env.example
├── .github
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ ├── enhancements---feature-requests.md
│ │ └── feedback.md
│ ├── PULL_REQUEST_TEMPLATE
│ │ ├── bugfix.md
│ │ ├── config.yml
│ │ ├── feature.md
│ │ └── integration.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── scripts
│ │ ├── auto-close-duplicates.mjs
│ │ ├── backfill-duplicate-comments.mjs
│ │ ├── check-pre-release-mode.mjs
│ │ ├── parse-metrics.mjs
│ │ ├── release.mjs
│ │ ├── tag-extension.mjs
│ │ ├── utils.mjs
│ │ └── validate-changesets.mjs
│ └── workflows
│ ├── auto-close-duplicates.yml
│ ├── backfill-duplicate-comments.yml
│ ├── ci.yml
│ ├── claude-dedupe-issues.yml
│ ├── claude-docs-trigger.yml
│ ├── claude-docs-updater.yml
│ ├── claude-issue-triage.yml
│ ├── claude.yml
│ ├── extension-ci.yml
│ ├── extension-release.yml
│ ├── log-issue-events.yml
│ ├── pre-release.yml
│ ├── release-check.yml
│ ├── release.yml
│ ├── update-models-md.yml
│ └── weekly-metrics-discord.yml
├── .gitignore
├── .kiro
│ ├── hooks
│ │ ├── tm-code-change-task-tracker.kiro.hook
│ │ ├── tm-complexity-analyzer.kiro.hook
│ │ ├── tm-daily-standup-assistant.kiro.hook
│ │ ├── tm-git-commit-task-linker.kiro.hook
│ │ ├── tm-pr-readiness-checker.kiro.hook
│ │ ├── tm-task-dependency-auto-progression.kiro.hook
│ │ └── tm-test-success-task-completer.kiro.hook
│ ├── settings
│ │ └── mcp.json
│ └── steering
│ ├── dev_workflow.md
│ ├── kiro_rules.md
│ ├── self_improve.md
│ ├── taskmaster_hooks_workflow.md
│ └── taskmaster.md
├── .manypkg.json
├── .mcp.json
├── .npmignore
├── .nvmrc
├── .taskmaster
│ ├── CLAUDE.md
│ ├── config.json
│ ├── docs
│ │ ├── autonomous-tdd-git-workflow.md
│ │ ├── MIGRATION-ROADMAP.md
│ │ ├── prd-tm-start.txt
│ │ ├── prd.txt
│ │ ├── README.md
│ │ ├── research
│ │ │ ├── 2025-06-14_how-can-i-improve-the-scope-up-and-scope-down-comm.md
│ │ │ ├── 2025-06-14_should-i-be-using-any-specific-libraries-for-this.md
│ │ │ ├── 2025-06-14_test-save-functionality.md
│ │ │ ├── 2025-06-14_test-the-fix-for-duplicate-saves-final-test.md
│ │ │ └── 2025-08-01_do-we-need-to-add-new-commands-or-can-we-just-weap.md
│ │ ├── task-template-importing-prd.txt
│ │ ├── tdd-workflow-phase-0-spike.md
│ │ ├── tdd-workflow-phase-1-core-rails.md
│ │ ├── tdd-workflow-phase-1-orchestrator.md
│ │ ├── tdd-workflow-phase-2-pr-resumability.md
│ │ ├── tdd-workflow-phase-3-extensibility-guardrails.md
│ │ ├── test-prd.txt
│ │ └── tm-core-phase-1.txt
│ ├── reports
│ │ ├── task-complexity-report_autonomous-tdd-git-workflow.json
│ │ ├── task-complexity-report_cc-kiro-hooks.json
│ │ ├── task-complexity-report_tdd-phase-1-core-rails.json
│ │ ├── task-complexity-report_tdd-workflow-phase-0.json
│ │ ├── task-complexity-report_test-prd-tag.json
│ │ ├── task-complexity-report_tm-core-phase-1.json
│ │ ├── task-complexity-report.json
│ │ └── tm-core-complexity.json
│ ├── state.json
│ ├── tasks
│ │ ├── task_001_tm-start.txt
│ │ ├── task_002_tm-start.txt
│ │ ├── task_003_tm-start.txt
│ │ ├── task_004_tm-start.txt
│ │ ├── task_007_tm-start.txt
│ │ └── tasks.json
│ └── templates
│ ├── example_prd_rpg.md
│ └── example_prd.md
├── .vscode
│ ├── extensions.json
│ └── settings.json
├── apps
│ ├── cli
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── command-registry.ts
│ │ │ ├── commands
│ │ │ │ ├── auth.command.ts
│ │ │ │ ├── autopilot
│ │ │ │ │ ├── abort.command.ts
│ │ │ │ │ ├── commit.command.ts
│ │ │ │ │ ├── complete.command.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── next.command.ts
│ │ │ │ │ ├── resume.command.ts
│ │ │ │ │ ├── shared.ts
│ │ │ │ │ ├── start.command.ts
│ │ │ │ │ └── status.command.ts
│ │ │ │ ├── briefs.command.ts
│ │ │ │ ├── context.command.ts
│ │ │ │ ├── export.command.ts
│ │ │ │ ├── list.command.ts
│ │ │ │ ├── models
│ │ │ │ │ ├── custom-providers.ts
│ │ │ │ │ ├── fetchers.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── prompts.ts
│ │ │ │ │ ├── setup.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── next.command.ts
│ │ │ │ ├── set-status.command.ts
│ │ │ │ ├── show.command.ts
│ │ │ │ ├── start.command.ts
│ │ │ │ └── tags.command.ts
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ └── model-management.ts
│ │ │ ├── types
│ │ │ │ └── tag-management.d.ts
│ │ │ ├── ui
│ │ │ │ ├── components
│ │ │ │ │ ├── cardBox.component.ts
│ │ │ │ │ ├── dashboard.component.ts
│ │ │ │ │ ├── header.component.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── next-task.component.ts
│ │ │ │ │ ├── suggested-steps.component.ts
│ │ │ │ │ └── task-detail.component.ts
│ │ │ │ ├── display
│ │ │ │ │ ├── messages.ts
│ │ │ │ │ └── tables.ts
│ │ │ │ ├── formatters
│ │ │ │ │ ├── complexity-formatters.ts
│ │ │ │ │ ├── dependency-formatters.ts
│ │ │ │ │ ├── priority-formatters.ts
│ │ │ │ │ ├── status-formatters.spec.ts
│ │ │ │ │ └── status-formatters.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── layout
│ │ │ │ ├── helpers.spec.ts
│ │ │ │ └── helpers.ts
│ │ │ └── utils
│ │ │ ├── auth-helpers.ts
│ │ │ ├── auto-update.ts
│ │ │ ├── brief-selection.ts
│ │ │ ├── display-helpers.ts
│ │ │ ├── error-handler.ts
│ │ │ ├── index.ts
│ │ │ ├── project-root.ts
│ │ │ ├── task-status.ts
│ │ │ ├── ui.spec.ts
│ │ │ └── ui.ts
│ │ ├── tests
│ │ │ ├── integration
│ │ │ │ └── commands
│ │ │ │ └── autopilot
│ │ │ │ └── workflow.test.ts
│ │ │ └── unit
│ │ │ ├── commands
│ │ │ │ ├── autopilot
│ │ │ │ │ └── shared.test.ts
│ │ │ │ ├── list.command.spec.ts
│ │ │ │ └── show.command.spec.ts
│ │ │ └── ui
│ │ │ └── dashboard.component.spec.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── docs
│ │ ├── archive
│ │ │ ├── ai-client-utils-example.mdx
│ │ │ ├── ai-development-workflow.mdx
│ │ │ ├── command-reference.mdx
│ │ │ ├── configuration.mdx
│ │ │ ├── cursor-setup.mdx
│ │ │ ├── examples.mdx
│ │ │ └── Installation.mdx
│ │ ├── best-practices
│ │ │ ├── advanced-tasks.mdx
│ │ │ ├── configuration-advanced.mdx
│ │ │ └── index.mdx
│ │ ├── capabilities
│ │ │ ├── cli-root-commands.mdx
│ │ │ ├── index.mdx
│ │ │ ├── mcp.mdx
│ │ │ ├── rpg-method.mdx
│ │ │ └── task-structure.mdx
│ │ ├── CHANGELOG.md
│ │ ├── command-reference.mdx
│ │ ├── configuration.mdx
│ │ ├── docs.json
│ │ ├── favicon.svg
│ │ ├── getting-started
│ │ │ ├── api-keys.mdx
│ │ │ ├── contribute.mdx
│ │ │ ├── faq.mdx
│ │ │ └── quick-start
│ │ │ ├── configuration-quick.mdx
│ │ │ ├── execute-quick.mdx
│ │ │ ├── installation.mdx
│ │ │ ├── moving-forward.mdx
│ │ │ ├── prd-quick.mdx
│ │ │ ├── quick-start.mdx
│ │ │ ├── requirements.mdx
│ │ │ ├── rules-quick.mdx
│ │ │ └── tasks-quick.mdx
│ │ ├── introduction.mdx
│ │ ├── licensing.md
│ │ ├── logo
│ │ │ ├── dark.svg
│ │ │ ├── light.svg
│ │ │ └── task-master-logo.png
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── style.css
│ │ ├── tdd-workflow
│ │ │ ├── ai-agent-integration.mdx
│ │ │ └── quickstart.mdx
│ │ ├── vercel.json
│ │ └── whats-new.mdx
│ ├── extension
│ │ ├── .vscodeignore
│ │ ├── assets
│ │ │ ├── banner.png
│ │ │ ├── icon-dark.svg
│ │ │ ├── icon-light.svg
│ │ │ ├── icon.png
│ │ │ ├── screenshots
│ │ │ │ ├── kanban-board.png
│ │ │ │ └── task-details.png
│ │ │ └── sidebar-icon.svg
│ │ ├── CHANGELOG.md
│ │ ├── components.json
│ │ ├── docs
│ │ │ ├── extension-CI-setup.md
│ │ │ └── extension-development-guide.md
│ │ ├── esbuild.js
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── package.mjs
│ │ ├── package.publish.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── components
│ │ │ │ ├── ConfigView.tsx
│ │ │ │ ├── constants.ts
│ │ │ │ ├── TaskDetails
│ │ │ │ │ ├── AIActionsSection.tsx
│ │ │ │ │ ├── DetailsSection.tsx
│ │ │ │ │ ├── PriorityBadge.tsx
│ │ │ │ │ ├── SubtasksSection.tsx
│ │ │ │ │ ├── TaskMetadataSidebar.tsx
│ │ │ │ │ └── useTaskDetails.ts
│ │ │ │ ├── TaskDetailsView.tsx
│ │ │ │ ├── TaskMasterLogo.tsx
│ │ │ │ └── ui
│ │ │ │ ├── badge.tsx
│ │ │ │ ├── breadcrumb.tsx
│ │ │ │ ├── button.tsx
│ │ │ │ ├── card.tsx
│ │ │ │ ├── collapsible.tsx
│ │ │ │ ├── CollapsibleSection.tsx
│ │ │ │ ├── dropdown-menu.tsx
│ │ │ │ ├── label.tsx
│ │ │ │ ├── scroll-area.tsx
│ │ │ │ ├── separator.tsx
│ │ │ │ ├── shadcn-io
│ │ │ │ │ └── kanban
│ │ │ │ │ └── index.tsx
│ │ │ │ └── textarea.tsx
│ │ │ ├── extension.ts
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ └── utils.ts
│ │ │ ├── services
│ │ │ │ ├── config-service.ts
│ │ │ │ ├── error-handler.ts
│ │ │ │ ├── notification-preferences.ts
│ │ │ │ ├── polling-service.ts
│ │ │ │ ├── polling-strategies.ts
│ │ │ │ ├── sidebar-webview-manager.ts
│ │ │ │ ├── task-repository.ts
│ │ │ │ ├── terminal-manager.ts
│ │ │ │ └── webview-manager.ts
│ │ │ ├── test
│ │ │ │ └── extension.test.ts
│ │ │ ├── utils
│ │ │ │ ├── configManager.ts
│ │ │ │ ├── connectionManager.ts
│ │ │ │ ├── errorHandler.ts
│ │ │ │ ├── event-emitter.ts
│ │ │ │ ├── logger.ts
│ │ │ │ ├── mcpClient.ts
│ │ │ │ ├── notificationPreferences.ts
│ │ │ │ └── task-master-api
│ │ │ │ ├── cache
│ │ │ │ │ └── cache-manager.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── mcp-client.ts
│ │ │ │ ├── transformers
│ │ │ │ │ └── task-transformer.ts
│ │ │ │ └── types
│ │ │ │ └── index.ts
│ │ │ └── webview
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ ├── AppContent.tsx
│ │ │ │ ├── EmptyState.tsx
│ │ │ │ ├── ErrorBoundary.tsx
│ │ │ │ ├── PollingStatus.tsx
│ │ │ │ ├── PriorityBadge.tsx
│ │ │ │ ├── SidebarView.tsx
│ │ │ │ ├── TagDropdown.tsx
│ │ │ │ ├── TaskCard.tsx
│ │ │ │ ├── TaskEditModal.tsx
│ │ │ │ ├── TaskMasterKanban.tsx
│ │ │ │ ├── ToastContainer.tsx
│ │ │ │ └── ToastNotification.tsx
│ │ │ ├── constants
│ │ │ │ └── index.ts
│ │ │ ├── contexts
│ │ │ │ └── VSCodeContext.tsx
│ │ │ ├── hooks
│ │ │ │ ├── useTaskQueries.ts
│ │ │ │ ├── useVSCodeMessages.ts
│ │ │ │ └── useWebviewHeight.ts
│ │ │ ├── index.css
│ │ │ ├── index.tsx
│ │ │ ├── providers
│ │ │ │ └── QueryProvider.tsx
│ │ │ ├── reducers
│ │ │ │ └── appReducer.ts
│ │ │ ├── sidebar.tsx
│ │ │ ├── types
│ │ │ │ └── index.ts
│ │ │ └── utils
│ │ │ ├── logger.ts
│ │ │ └── toast.ts
│ │ └── tsconfig.json
│ └── mcp
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── shared
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ └── tools
│ │ ├── autopilot
│ │ │ ├── abort.tool.ts
│ │ │ ├── commit.tool.ts
│ │ │ ├── complete.tool.ts
│ │ │ ├── finalize.tool.ts
│ │ │ ├── index.ts
│ │ │ ├── next.tool.ts
│ │ │ ├── resume.tool.ts
│ │ │ ├── start.tool.ts
│ │ │ └── status.tool.ts
│ │ ├── README-ZOD-V3.md
│ │ └── tasks
│ │ ├── get-task.tool.ts
│ │ ├── get-tasks.tool.ts
│ │ └── index.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── assets
│ ├── .windsurfrules
│ ├── AGENTS.md
│ ├── claude
│ │ └── TM_COMMANDS_GUIDE.md
│ ├── config.json
│ ├── env.example
│ ├── example_prd_rpg.txt
│ ├── example_prd.txt
│ ├── GEMINI.md
│ ├── gitignore
│ ├── kiro-hooks
│ │ ├── tm-code-change-task-tracker.kiro.hook
│ │ ├── tm-complexity-analyzer.kiro.hook
│ │ ├── tm-daily-standup-assistant.kiro.hook
│ │ ├── tm-git-commit-task-linker.kiro.hook
│ │ ├── tm-pr-readiness-checker.kiro.hook
│ │ ├── tm-task-dependency-auto-progression.kiro.hook
│ │ └── tm-test-success-task-completer.kiro.hook
│ ├── roocode
│ │ ├── .roo
│ │ │ ├── rules-architect
│ │ │ │ └── architect-rules
│ │ │ ├── rules-ask
│ │ │ │ └── ask-rules
│ │ │ ├── rules-code
│ │ │ │ └── code-rules
│ │ │ ├── rules-debug
│ │ │ │ └── debug-rules
│ │ │ ├── rules-orchestrator
│ │ │ │ └── orchestrator-rules
│ │ │ └── rules-test
│ │ │ └── test-rules
│ │ └── .roomodes
│ ├── rules
│ │ ├── cursor_rules.mdc
│ │ ├── dev_workflow.mdc
│ │ ├── self_improve.mdc
│ │ ├── taskmaster_hooks_workflow.mdc
│ │ └── taskmaster.mdc
│ └── scripts_README.md
├── bin
│ └── task-master.js
├── biome.json
├── CHANGELOG.md
├── CLAUDE_CODE_PLUGIN.md
├── CLAUDE.md
├── context
│ ├── chats
│ │ ├── add-task-dependencies-1.md
│ │ └── max-min-tokens.txt.md
│ ├── fastmcp-core.txt
│ ├── fastmcp-docs.txt
│ ├── MCP_INTEGRATION.md
│ ├── mcp-js-sdk-docs.txt
│ ├── mcp-protocol-repo.txt
│ ├── mcp-protocol-schema-03262025.json
│ └── mcp-protocol-spec.txt
├── CONTRIBUTING.md
├── docs
│ ├── claude-code-integration.md
│ ├── CLI-COMMANDER-PATTERN.md
│ ├── command-reference.md
│ ├── configuration.md
│ ├── contributor-docs
│ │ ├── testing-roo-integration.md
│ │ └── worktree-setup.md
│ ├── cross-tag-task-movement.md
│ ├── examples
│ │ ├── claude-code-usage.md
│ │ └── codex-cli-usage.md
│ ├── examples.md
│ ├── licensing.md
│ ├── mcp-provider-guide.md
│ ├── mcp-provider.md
│ ├── migration-guide.md
│ ├── models.md
│ ├── providers
│ │ ├── codex-cli.md
│ │ └── gemini-cli.md
│ ├── README.md
│ ├── scripts
│ │ └── models-json-to-markdown.js
│ ├── task-structure.md
│ └── tutorial.md
├── images
│ ├── hamster-hiring.png
│ └── logo.png
├── index.js
├── jest.config.js
├── jest.resolver.cjs
├── LICENSE
├── llms-install.md
├── mcp-server
│ ├── server.js
│ └── src
│ ├── core
│ │ ├── __tests__
│ │ │ └── context-manager.test.js
│ │ ├── context-manager.js
│ │ ├── direct-functions
│ │ │ ├── add-dependency.js
│ │ │ ├── add-subtask.js
│ │ │ ├── add-tag.js
│ │ │ ├── add-task.js
│ │ │ ├── analyze-task-complexity.js
│ │ │ ├── cache-stats.js
│ │ │ ├── clear-subtasks.js
│ │ │ ├── complexity-report.js
│ │ │ ├── copy-tag.js
│ │ │ ├── create-tag-from-branch.js
│ │ │ ├── delete-tag.js
│ │ │ ├── expand-all-tasks.js
│ │ │ ├── expand-task.js
│ │ │ ├── fix-dependencies.js
│ │ │ ├── generate-task-files.js
│ │ │ ├── initialize-project.js
│ │ │ ├── list-tags.js
│ │ │ ├── models.js
│ │ │ ├── move-task-cross-tag.js
│ │ │ ├── move-task.js
│ │ │ ├── next-task.js
│ │ │ ├── parse-prd.js
│ │ │ ├── remove-dependency.js
│ │ │ ├── remove-subtask.js
│ │ │ ├── remove-task.js
│ │ │ ├── rename-tag.js
│ │ │ ├── research.js
│ │ │ ├── response-language.js
│ │ │ ├── rules.js
│ │ │ ├── scope-down.js
│ │ │ ├── scope-up.js
│ │ │ ├── set-task-status.js
│ │ │ ├── update-subtask-by-id.js
│ │ │ ├── update-task-by-id.js
│ │ │ ├── update-tasks.js
│ │ │ ├── use-tag.js
│ │ │ └── validate-dependencies.js
│ │ ├── task-master-core.js
│ │ └── utils
│ │ ├── env-utils.js
│ │ └── path-utils.js
│ ├── custom-sdk
│ │ ├── errors.js
│ │ ├── index.js
│ │ ├── json-extractor.js
│ │ ├── language-model.js
│ │ ├── message-converter.js
│ │ └── schema-converter.js
│ ├── index.js
│ ├── logger.js
│ ├── providers
│ │ └── mcp-provider.js
│ └── tools
│ ├── add-dependency.js
│ ├── add-subtask.js
│ ├── add-tag.js
│ ├── add-task.js
│ ├── analyze.js
│ ├── clear-subtasks.js
│ ├── complexity-report.js
│ ├── copy-tag.js
│ ├── delete-tag.js
│ ├── expand-all.js
│ ├── expand-task.js
│ ├── fix-dependencies.js
│ ├── generate.js
│ ├── get-operation-status.js
│ ├── index.js
│ ├── initialize-project.js
│ ├── list-tags.js
│ ├── models.js
│ ├── move-task.js
│ ├── next-task.js
│ ├── parse-prd.js
│ ├── README-ZOD-V3.md
│ ├── remove-dependency.js
│ ├── remove-subtask.js
│ ├── remove-task.js
│ ├── rename-tag.js
│ ├── research.js
│ ├── response-language.js
│ ├── rules.js
│ ├── scope-down.js
│ ├── scope-up.js
│ ├── set-task-status.js
│ ├── tool-registry.js
│ ├── update-subtask.js
│ ├── update-task.js
│ ├── update.js
│ ├── use-tag.js
│ ├── utils.js
│ └── validate-dependencies.js
├── mcp-test.js
├── output.json
├── package-lock.json
├── package.json
├── packages
│ ├── ai-sdk-provider-grok-cli
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── errors.test.ts
│ │ │ ├── errors.ts
│ │ │ ├── grok-cli-language-model.ts
│ │ │ ├── grok-cli-provider.test.ts
│ │ │ ├── grok-cli-provider.ts
│ │ │ ├── index.ts
│ │ │ ├── json-extractor.test.ts
│ │ │ ├── json-extractor.ts
│ │ │ ├── message-converter.test.ts
│ │ │ ├── message-converter.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── build-config
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src
│ │ │ └── tsdown.base.ts
│ │ └── tsconfig.json
│ ├── claude-code-plugin
│ │ ├── .claude-plugin
│ │ │ └── plugin.json
│ │ ├── .gitignore
│ │ ├── agents
│ │ │ ├── task-checker.md
│ │ │ ├── task-executor.md
│ │ │ └── task-orchestrator.md
│ │ ├── CHANGELOG.md
│ │ ├── commands
│ │ │ ├── add-dependency.md
│ │ │ ├── add-subtask.md
│ │ │ ├── add-task.md
│ │ │ ├── analyze-complexity.md
│ │ │ ├── analyze-project.md
│ │ │ ├── auto-implement-tasks.md
│ │ │ ├── command-pipeline.md
│ │ │ ├── complexity-report.md
│ │ │ ├── convert-task-to-subtask.md
│ │ │ ├── expand-all-tasks.md
│ │ │ ├── expand-task.md
│ │ │ ├── fix-dependencies.md
│ │ │ ├── generate-tasks.md
│ │ │ ├── help.md
│ │ │ ├── init-project-quick.md
│ │ │ ├── init-project.md
│ │ │ ├── install-taskmaster.md
│ │ │ ├── learn.md
│ │ │ ├── list-tasks-by-status.md
│ │ │ ├── list-tasks-with-subtasks.md
│ │ │ ├── list-tasks.md
│ │ │ ├── next-task.md
│ │ │ ├── parse-prd-with-research.md
│ │ │ ├── parse-prd.md
│ │ │ ├── project-status.md
│ │ │ ├── quick-install-taskmaster.md
│ │ │ ├── remove-all-subtasks.md
│ │ │ ├── remove-dependency.md
│ │ │ ├── remove-subtask.md
│ │ │ ├── remove-subtasks.md
│ │ │ ├── remove-task.md
│ │ │ ├── setup-models.md
│ │ │ ├── show-task.md
│ │ │ ├── smart-workflow.md
│ │ │ ├── sync-readme.md
│ │ │ ├── tm-main.md
│ │ │ ├── to-cancelled.md
│ │ │ ├── to-deferred.md
│ │ │ ├── to-done.md
│ │ │ ├── to-in-progress.md
│ │ │ ├── to-pending.md
│ │ │ ├── to-review.md
│ │ │ ├── update-single-task.md
│ │ │ ├── update-task.md
│ │ │ ├── update-tasks-from-id.md
│ │ │ ├── validate-dependencies.md
│ │ │ └── view-models.md
│ │ ├── mcp.json
│ │ └── package.json
│ ├── tm-bridge
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── add-tag-bridge.ts
│ │ │ ├── bridge-types.ts
│ │ │ ├── bridge-utils.ts
│ │ │ ├── expand-bridge.ts
│ │ │ ├── index.ts
│ │ │ ├── tags-bridge.ts
│ │ │ ├── update-bridge.ts
│ │ │ └── use-tag-bridge.ts
│ │ └── tsconfig.json
│ └── tm-core
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── docs
│ │ └── listTasks-architecture.md
│ ├── package.json
│ ├── POC-STATUS.md
│ ├── README.md
│ ├── src
│ │ ├── common
│ │ │ ├── constants
│ │ │ │ ├── index.ts
│ │ │ │ ├── paths.ts
│ │ │ │ └── providers.ts
│ │ │ ├── errors
│ │ │ │ ├── index.ts
│ │ │ │ └── task-master-error.ts
│ │ │ ├── interfaces
│ │ │ │ ├── configuration.interface.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── storage.interface.ts
│ │ │ ├── logger
│ │ │ │ ├── factory.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── logger.spec.ts
│ │ │ │ └── logger.ts
│ │ │ ├── mappers
│ │ │ │ ├── TaskMapper.test.ts
│ │ │ │ └── TaskMapper.ts
│ │ │ ├── types
│ │ │ │ ├── database.types.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── legacy.ts
│ │ │ │ └── repository-types.ts
│ │ │ └── utils
│ │ │ ├── git-utils.ts
│ │ │ ├── id-generator.ts
│ │ │ ├── index.ts
│ │ │ ├── path-helpers.ts
│ │ │ ├── path-normalizer.spec.ts
│ │ │ ├── path-normalizer.ts
│ │ │ ├── project-root-finder.spec.ts
│ │ │ ├── project-root-finder.ts
│ │ │ ├── run-id-generator.spec.ts
│ │ │ └── run-id-generator.ts
│ │ ├── index.ts
│ │ ├── modules
│ │ │ ├── ai
│ │ │ │ ├── index.ts
│ │ │ │ ├── interfaces
│ │ │ │ │ └── ai-provider.interface.ts
│ │ │ │ └── providers
│ │ │ │ ├── base-provider.ts
│ │ │ │ └── index.ts
│ │ │ ├── auth
│ │ │ │ ├── auth-domain.spec.ts
│ │ │ │ ├── auth-domain.ts
│ │ │ │ ├── config.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── managers
│ │ │ │ │ ├── auth-manager.spec.ts
│ │ │ │ │ └── auth-manager.ts
│ │ │ │ ├── services
│ │ │ │ │ ├── context-store.ts
│ │ │ │ │ ├── oauth-service.ts
│ │ │ │ │ ├── organization.service.ts
│ │ │ │ │ ├── supabase-session-storage.spec.ts
│ │ │ │ │ └── supabase-session-storage.ts
│ │ │ │ └── types.ts
│ │ │ ├── briefs
│ │ │ │ ├── briefs-domain.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── services
│ │ │ │ │ └── brief-service.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils
│ │ │ │ └── url-parser.ts
│ │ │ ├── commands
│ │ │ │ └── index.ts
│ │ │ ├── config
│ │ │ │ ├── config-domain.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── managers
│ │ │ │ │ ├── config-manager.spec.ts
│ │ │ │ │ └── config-manager.ts
│ │ │ │ └── services
│ │ │ │ ├── config-loader.service.spec.ts
│ │ │ │ ├── config-loader.service.ts
│ │ │ │ ├── config-merger.service.spec.ts
│ │ │ │ ├── config-merger.service.ts
│ │ │ │ ├── config-persistence.service.spec.ts
│ │ │ │ ├── config-persistence.service.ts
│ │ │ │ ├── environment-config-provider.service.spec.ts
│ │ │ │ ├── environment-config-provider.service.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── runtime-state-manager.service.spec.ts
│ │ │ │ └── runtime-state-manager.service.ts
│ │ │ ├── dependencies
│ │ │ │ └── index.ts
│ │ │ ├── execution
│ │ │ │ ├── executors
│ │ │ │ │ ├── base-executor.ts
│ │ │ │ │ ├── claude-executor.ts
│ │ │ │ │ └── executor-factory.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── services
│ │ │ │ │ └── executor-service.ts
│ │ │ │ └── types.ts
│ │ │ ├── git
│ │ │ │ ├── adapters
│ │ │ │ │ ├── git-adapter.test.ts
│ │ │ │ │ └── git-adapter.ts
│ │ │ │ ├── git-domain.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── services
│ │ │ │ ├── branch-name-generator.spec.ts
│ │ │ │ ├── branch-name-generator.ts
│ │ │ │ ├── commit-message-generator.test.ts
│ │ │ │ ├── commit-message-generator.ts
│ │ │ │ ├── scope-detector.test.ts
│ │ │ │ ├── scope-detector.ts
│ │ │ │ ├── template-engine.test.ts
│ │ │ │ └── template-engine.ts
│ │ │ ├── integration
│ │ │ │ ├── clients
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── supabase-client.ts
│ │ │ │ ├── integration-domain.ts
│ │ │ │ └── services
│ │ │ │ ├── export.service.ts
│ │ │ │ ├── task-expansion.service.ts
│ │ │ │ └── task-retrieval.service.ts
│ │ │ ├── reports
│ │ │ │ ├── index.ts
│ │ │ │ ├── managers
│ │ │ │ │ └── complexity-report-manager.ts
│ │ │ │ └── types.ts
│ │ │ ├── storage
│ │ │ │ ├── adapters
│ │ │ │ │ ├── activity-logger.ts
│ │ │ │ │ ├── api-storage.ts
│ │ │ │ │ └── file-storage
│ │ │ │ │ ├── file-operations.ts
│ │ │ │ │ ├── file-storage.ts
│ │ │ │ │ ├── format-handler.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── path-resolver.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── services
│ │ │ │ │ └── storage-factory.ts
│ │ │ │ └── utils
│ │ │ │ └── api-client.ts
│ │ │ ├── tasks
│ │ │ │ ├── entities
│ │ │ │ │ └── task.entity.ts
│ │ │ │ ├── parser
│ │ │ │ │ └── index.ts
│ │ │ │ ├── repositories
│ │ │ │ │ ├── supabase
│ │ │ │ │ │ ├── dependency-fetcher.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── supabase-repository.ts
│ │ │ │ │ └── task-repository.interface.ts
│ │ │ │ ├── services
│ │ │ │ │ ├── preflight-checker.service.ts
│ │ │ │ │ ├── tag.service.ts
│ │ │ │ │ ├── task-execution-service.ts
│ │ │ │ │ ├── task-loader.service.ts
│ │ │ │ │ └── task-service.ts
│ │ │ │ └── tasks-domain.ts
│ │ │ ├── ui
│ │ │ │ └── index.ts
│ │ │ └── workflow
│ │ │ ├── managers
│ │ │ │ ├── workflow-state-manager.spec.ts
│ │ │ │ └── workflow-state-manager.ts
│ │ │ ├── orchestrators
│ │ │ │ ├── workflow-orchestrator.test.ts
│ │ │ │ └── workflow-orchestrator.ts
│ │ │ ├── services
│ │ │ │ ├── test-result-validator.test.ts
│ │ │ │ ├── test-result-validator.ts
│ │ │ │ ├── test-result-validator.types.ts
│ │ │ │ ├── workflow-activity-logger.ts
│ │ │ │ └── workflow.service.ts
│ │ │ ├── types.ts
│ │ │ └── workflow-domain.ts
│ │ ├── subpath-exports.test.ts
│ │ ├── tm-core.ts
│ │ └── utils
│ │ └── time.utils.ts
│ ├── tests
│ │ ├── auth
│ │ │ └── auth-refresh.test.ts
│ │ ├── integration
│ │ │ ├── auth-token-refresh.test.ts
│ │ │ ├── list-tasks.test.ts
│ │ │ └── storage
│ │ │ └── activity-logger.test.ts
│ │ ├── mocks
│ │ │ └── mock-provider.ts
│ │ ├── setup.ts
│ │ └── unit
│ │ ├── base-provider.test.ts
│ │ ├── executor.test.ts
│ │ └── smoke.test.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── README-task-master.md
├── README.md
├── scripts
│ ├── create-worktree.sh
│ ├── dev.js
│ ├── init.js
│ ├── list-worktrees.sh
│ ├── modules
│ │ ├── ai-services-unified.js
│ │ ├── bridge-utils.js
│ │ ├── commands.js
│ │ ├── config-manager.js
│ │ ├── dependency-manager.js
│ │ ├── index.js
│ │ ├── prompt-manager.js
│ │ ├── supported-models.json
│ │ ├── sync-readme.js
│ │ ├── task-manager
│ │ │ ├── add-subtask.js
│ │ │ ├── add-task.js
│ │ │ ├── analyze-task-complexity.js
│ │ │ ├── clear-subtasks.js
│ │ │ ├── expand-all-tasks.js
│ │ │ ├── expand-task.js
│ │ │ ├── find-next-task.js
│ │ │ ├── generate-task-files.js
│ │ │ ├── is-task-dependent.js
│ │ │ ├── list-tasks.js
│ │ │ ├── migrate.js
│ │ │ ├── models.js
│ │ │ ├── move-task.js
│ │ │ ├── parse-prd
│ │ │ │ ├── index.js
│ │ │ │ ├── parse-prd-config.js
│ │ │ │ ├── parse-prd-helpers.js
│ │ │ │ ├── parse-prd-non-streaming.js
│ │ │ │ ├── parse-prd-streaming.js
│ │ │ │ └── parse-prd.js
│ │ │ ├── remove-subtask.js
│ │ │ ├── remove-task.js
│ │ │ ├── research.js
│ │ │ ├── response-language.js
│ │ │ ├── scope-adjustment.js
│ │ │ ├── set-task-status.js
│ │ │ ├── tag-management.js
│ │ │ ├── task-exists.js
│ │ │ ├── update-single-task-status.js
│ │ │ ├── update-subtask-by-id.js
│ │ │ ├── update-task-by-id.js
│ │ │ └── update-tasks.js
│ │ ├── task-manager.js
│ │ ├── ui.js
│ │ ├── update-config-tokens.js
│ │ ├── utils
│ │ │ ├── contextGatherer.js
│ │ │ ├── fuzzyTaskSearch.js
│ │ │ └── git-utils.js
│ │ └── utils.js
│ ├── task-complexity-report.json
│ ├── test-claude-errors.js
│ └── test-claude.js
├── sonar-project.properties
├── src
│ ├── ai-providers
│ │ ├── anthropic.js
│ │ ├── azure.js
│ │ ├── base-provider.js
│ │ ├── bedrock.js
│ │ ├── claude-code.js
│ │ ├── codex-cli.js
│ │ ├── gemini-cli.js
│ │ ├── google-vertex.js
│ │ ├── google.js
│ │ ├── grok-cli.js
│ │ ├── groq.js
│ │ ├── index.js
│ │ ├── lmstudio.js
│ │ ├── ollama.js
│ │ ├── openai-compatible.js
│ │ ├── openai.js
│ │ ├── openrouter.js
│ │ ├── perplexity.js
│ │ ├── xai.js
│ │ ├── zai-coding.js
│ │ └── zai.js
│ ├── constants
│ │ ├── commands.js
│ │ ├── paths.js
│ │ ├── profiles.js
│ │ ├── rules-actions.js
│ │ ├── task-priority.js
│ │ └── task-status.js
│ ├── profiles
│ │ ├── amp.js
│ │ ├── base-profile.js
│ │ ├── claude.js
│ │ ├── cline.js
│ │ ├── codex.js
│ │ ├── cursor.js
│ │ ├── gemini.js
│ │ ├── index.js
│ │ ├── kilo.js
│ │ ├── kiro.js
│ │ ├── opencode.js
│ │ ├── roo.js
│ │ ├── trae.js
│ │ ├── vscode.js
│ │ ├── windsurf.js
│ │ └── zed.js
│ ├── progress
│ │ ├── base-progress-tracker.js
│ │ ├── cli-progress-factory.js
│ │ ├── parse-prd-tracker.js
│ │ ├── progress-tracker-builder.js
│ │ └── tracker-ui.js
│ ├── prompts
│ │ ├── add-task.json
│ │ ├── analyze-complexity.json
│ │ ├── expand-task.json
│ │ ├── parse-prd.json
│ │ ├── README.md
│ │ ├── research.json
│ │ ├── schemas
│ │ │ ├── parameter.schema.json
│ │ │ ├── prompt-template.schema.json
│ │ │ ├── README.md
│ │ │ └── variant.schema.json
│ │ ├── update-subtask.json
│ │ ├── update-task.json
│ │ └── update-tasks.json
│ ├── provider-registry
│ │ └── index.js
│ ├── schemas
│ │ ├── add-task.js
│ │ ├── analyze-complexity.js
│ │ ├── base-schemas.js
│ │ ├── expand-task.js
│ │ ├── parse-prd.js
│ │ ├── registry.js
│ │ ├── update-subtask.js
│ │ ├── update-task.js
│ │ └── update-tasks.js
│ ├── task-master.js
│ ├── ui
│ │ ├── confirm.js
│ │ ├── indicators.js
│ │ └── parse-prd.js
│ └── utils
│ ├── asset-resolver.js
│ ├── create-mcp-config.js
│ ├── format.js
│ ├── getVersion.js
│ ├── logger-utils.js
│ ├── manage-gitignore.js
│ ├── path-utils.js
│ ├── profiles.js
│ ├── rule-transformer.js
│ ├── stream-parser.js
│ └── timeout-manager.js
├── test-clean-tags.js
├── test-config-manager.js
├── test-prd.txt
├── test-tag-functions.js
├── test-version-check-full.js
├── test-version-check.js
├── tests
│ ├── e2e
│ │ ├── e2e_helpers.sh
│ │ ├── parse_llm_output.cjs
│ │ ├── run_e2e.sh
│ │ ├── run_fallback_verification.sh
│ │ └── test_llm_analysis.sh
│ ├── fixtures
│ │ ├── .taskmasterconfig
│ │ ├── sample-claude-response.js
│ │ ├── sample-prd.txt
│ │ └── sample-tasks.js
│ ├── helpers
│ │ └── tool-counts.js
│ ├── integration
│ │ ├── claude-code-error-handling.test.js
│ │ ├── claude-code-optional.test.js
│ │ ├── cli
│ │ │ ├── commands.test.js
│ │ │ ├── complex-cross-tag-scenarios.test.js
│ │ │ └── move-cross-tag.test.js
│ │ ├── manage-gitignore.test.js
│ │ ├── mcp-server
│ │ │ └── direct-functions.test.js
│ │ ├── move-task-cross-tag.integration.test.js
│ │ ├── move-task-simple.integration.test.js
│ │ ├── profiles
│ │ │ ├── amp-init-functionality.test.js
│ │ │ ├── claude-init-functionality.test.js
│ │ │ ├── cline-init-functionality.test.js
│ │ │ ├── codex-init-functionality.test.js
│ │ │ ├── cursor-init-functionality.test.js
│ │ │ ├── gemini-init-functionality.test.js
│ │ │ ├── opencode-init-functionality.test.js
│ │ │ ├── roo-files-inclusion.test.js
│ │ │ ├── roo-init-functionality.test.js
│ │ │ ├── rules-files-inclusion.test.js
│ │ │ ├── trae-init-functionality.test.js
│ │ │ ├── vscode-init-functionality.test.js
│ │ │ └── windsurf-init-functionality.test.js
│ │ └── providers
│ │ └── temperature-support.test.js
│ ├── manual
│ │ ├── progress
│ │ │ ├── parse-prd-analysis.js
│ │ │ ├── test-parse-prd.js
│ │ │ └── TESTING_GUIDE.md
│ │ └── prompts
│ │ ├── prompt-test.js
│ │ └── README.md
│ ├── README.md
│ ├── setup.js
│ └── unit
│ ├── ai-providers
│ │ ├── base-provider.test.js
│ │ ├── claude-code.test.js
│ │ ├── codex-cli.test.js
│ │ ├── gemini-cli.test.js
│ │ ├── lmstudio.test.js
│ │ ├── mcp-components.test.js
│ │ ├── openai-compatible.test.js
│ │ ├── openai.test.js
│ │ ├── provider-registry.test.js
│ │ ├── zai-coding.test.js
│ │ ├── zai-provider.test.js
│ │ ├── zai-schema-introspection.test.js
│ │ └── zai.test.js
│ ├── ai-services-unified.test.js
│ ├── commands.test.js
│ ├── config-manager.test.js
│ ├── config-manager.test.mjs
│ ├── dependency-manager.test.js
│ ├── init.test.js
│ ├── initialize-project.test.js
│ ├── kebab-case-validation.test.js
│ ├── manage-gitignore.test.js
│ ├── mcp
│ │ └── tools
│ │ ├── __mocks__
│ │ │ └── move-task.js
│ │ ├── add-task.test.js
│ │ ├── analyze-complexity.test.js
│ │ ├── expand-all.test.js
│ │ ├── get-tasks.test.js
│ │ ├── initialize-project.test.js
│ │ ├── move-task-cross-tag-options.test.js
│ │ ├── move-task-cross-tag.test.js
│ │ ├── remove-task.test.js
│ │ └── tool-registration.test.js
│ ├── mcp-providers
│ │ ├── mcp-components.test.js
│ │ └── mcp-provider.test.js
│ ├── parse-prd.test.js
│ ├── profiles
│ │ ├── amp-integration.test.js
│ │ ├── claude-integration.test.js
│ │ ├── cline-integration.test.js
│ │ ├── codex-integration.test.js
│ │ ├── cursor-integration.test.js
│ │ ├── gemini-integration.test.js
│ │ ├── kilo-integration.test.js
│ │ ├── kiro-integration.test.js
│ │ ├── mcp-config-validation.test.js
│ │ ├── opencode-integration.test.js
│ │ ├── profile-safety-check.test.js
│ │ ├── roo-integration.test.js
│ │ ├── rule-transformer-cline.test.js
│ │ ├── rule-transformer-cursor.test.js
│ │ ├── rule-transformer-gemini.test.js
│ │ ├── rule-transformer-kilo.test.js
│ │ ├── rule-transformer-kiro.test.js
│ │ ├── rule-transformer-opencode.test.js
│ │ ├── rule-transformer-roo.test.js
│ │ ├── rule-transformer-trae.test.js
│ │ ├── rule-transformer-vscode.test.js
│ │ ├── rule-transformer-windsurf.test.js
│ │ ├── rule-transformer-zed.test.js
│ │ ├── rule-transformer.test.js
│ │ ├── selective-profile-removal.test.js
│ │ ├── subdirectory-support.test.js
│ │ ├── trae-integration.test.js
│ │ ├── vscode-integration.test.js
│ │ ├── windsurf-integration.test.js
│ │ └── zed-integration.test.js
│ ├── progress
│ │ └── base-progress-tracker.test.js
│ ├── prompt-manager.test.js
│ ├── prompts
│ │ ├── expand-task-prompt.test.js
│ │ └── prompt-migration.test.js
│ ├── scripts
│ │ └── modules
│ │ ├── commands
│ │ │ ├── move-cross-tag.test.js
│ │ │ └── README.md
│ │ ├── dependency-manager
│ │ │ ├── circular-dependencies.test.js
│ │ │ ├── cross-tag-dependencies.test.js
│ │ │ └── fix-dependencies-command.test.js
│ │ ├── task-manager
│ │ │ ├── add-subtask.test.js
│ │ │ ├── add-task.test.js
│ │ │ ├── analyze-task-complexity.test.js
│ │ │ ├── clear-subtasks.test.js
│ │ │ ├── complexity-report-tag-isolation.test.js
│ │ │ ├── expand-all-tasks.test.js
│ │ │ ├── expand-task.test.js
│ │ │ ├── find-next-task.test.js
│ │ │ ├── generate-task-files.test.js
│ │ │ ├── list-tasks.test.js
│ │ │ ├── models-baseurl.test.js
│ │ │ ├── move-task-cross-tag.test.js
│ │ │ ├── move-task.test.js
│ │ │ ├── parse-prd-schema.test.js
│ │ │ ├── parse-prd.test.js
│ │ │ ├── remove-subtask.test.js
│ │ │ ├── remove-task.test.js
│ │ │ ├── research.test.js
│ │ │ ├── scope-adjustment.test.js
│ │ │ ├── set-task-status.test.js
│ │ │ ├── setup.js
│ │ │ ├── update-single-task-status.test.js
│ │ │ ├── update-subtask-by-id.test.js
│ │ │ ├── update-task-by-id.test.js
│ │ │ └── update-tasks.test.js
│ │ ├── ui
│ │ │ └── cross-tag-error-display.test.js
│ │ └── utils-tag-aware-paths.test.js
│ ├── task-finder.test.js
│ ├── task-manager
│ │ ├── clear-subtasks.test.js
│ │ ├── move-task.test.js
│ │ ├── tag-boundary.test.js
│ │ └── tag-management.test.js
│ ├── task-master.test.js
│ ├── ui
│ │ └── indicators.test.js
│ ├── ui.test.js
│ ├── utils-strip-ansi.test.js
│ └── utils.test.js
├── tsconfig.json
├── tsdown.config.ts
├── turbo.json
└── update-task-migration-plan.md
```
# Files
--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/add-dependency.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * add-dependency.js
3 | * Direct function implementation for adding a dependency to a task
4 | */
5 |
6 | import { addDependency } from '../../../../scripts/modules/dependency-manager.js';
7 | import {
8 | enableSilentMode,
9 | disableSilentMode
10 | } from '../../../../scripts/modules/utils.js';
11 |
12 | /**
13 | * Direct function wrapper for addDependency with error handling.
14 | *
15 | * @param {Object} args - Command arguments
16 | * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
17 | * @param {string|number} args.id - Task ID to add dependency to
18 | * @param {string|number} args.dependsOn - Task ID that will become a dependency
19 | * @param {string} args.tag - Tag for the task (optional)
20 | * @param {string} args.projectRoot - Project root path (for MCP/env fallback)
21 | * @param {Object} log - Logger object
22 | * @returns {Promise<Object>} - Result object with success status and data/error information
23 | */
24 | export async function addDependencyDirect(args, log) {
25 | // Destructure expected args
26 | const { tasksJsonPath, id, dependsOn, tag, projectRoot } = args;
27 | try {
28 | log.info(`Adding dependency with args: ${JSON.stringify(args)}`);
29 |
30 | // Check if tasksJsonPath was provided
31 | if (!tasksJsonPath) {
32 | log.error('addDependencyDirect called without tasksJsonPath');
33 | return {
34 | success: false,
35 | error: {
36 | code: 'MISSING_ARGUMENT',
37 | message: 'tasksJsonPath is required'
38 | }
39 | };
40 | }
41 |
42 | // Validate required parameters
43 | if (!id) {
44 | return {
45 | success: false,
46 | error: {
47 | code: 'INPUT_VALIDATION_ERROR',
48 | message: 'Task ID (id) is required'
49 | }
50 | };
51 | }
52 |
53 | if (!dependsOn) {
54 | return {
55 | success: false,
56 | error: {
57 | code: 'INPUT_VALIDATION_ERROR',
58 | message: 'Dependency ID (dependsOn) is required'
59 | }
60 | };
61 | }
62 |
63 | // Use provided path
64 | const tasksPath = tasksJsonPath;
65 |
66 | // Format IDs for the core function
67 | const taskId =
68 | id && id.includes && id.includes('.') ? id : parseInt(id, 10);
69 | const dependencyId =
70 | dependsOn && dependsOn.includes && dependsOn.includes('.')
71 | ? dependsOn
72 | : parseInt(dependsOn, 10);
73 |
74 | log.info(
75 | `Adding dependency: task ${taskId} will depend on ${dependencyId}`
76 | );
77 |
78 | // Enable silent mode to prevent console logs from interfering with JSON response
79 | enableSilentMode();
80 |
81 | // Create context object
82 | const context = { projectRoot, tag };
83 |
84 | // Call the core function using the provided path
85 | await addDependency(tasksPath, taskId, dependencyId, context);
86 |
87 | // Restore normal logging
88 | disableSilentMode();
89 |
90 | return {
91 | success: true,
92 | data: {
93 | message: `Successfully added dependency: Task ${taskId} now depends on ${dependencyId}`,
94 | taskId: taskId,
95 | dependencyId: dependencyId
96 | }
97 | };
98 | } catch (error) {
99 | // Make sure to restore normal logging even if there's an error
100 | disableSilentMode();
101 |
102 | log.error(`Error in addDependencyDirect: ${error.message}`);
103 | return {
104 | success: false,
105 | error: {
106 | code: 'CORE_FUNCTION_ERROR',
107 | message: error.message
108 | }
109 | };
110 | }
111 | }
112 |
```
--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/complexity-report.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * complexity-report.js
3 | * Direct function implementation for displaying complexity analysis report
4 | */
5 |
6 | import {
7 | readComplexityReport,
8 | enableSilentMode,
9 | disableSilentMode
10 | } from '../../../../scripts/modules/utils.js';
11 |
12 | /**
13 | * Direct function wrapper for displaying the complexity report with error handling and caching.
14 | *
15 | * @param {Object} args - Command arguments containing reportPath.
16 | * @param {string} args.reportPath - Explicit path to the complexity report file.
17 | * @param {Object} log - Logger object
18 | * @returns {Promise<Object>} - Result object with success status and data/error information
19 | */
20 | export async function complexityReportDirect(args, log) {
21 | // Destructure expected args
22 | const { reportPath } = args;
23 | try {
24 | log.info(`Getting complexity report with args: ${JSON.stringify(args)}`);
25 |
26 | // Check if reportPath was provided
27 | if (!reportPath) {
28 | log.error('complexityReportDirect called without reportPath');
29 | return {
30 | success: false,
31 | error: { code: 'MISSING_ARGUMENT', message: 'reportPath is required' }
32 | };
33 | }
34 |
35 | // Use the provided report path
36 | log.info(`Looking for complexity report at: ${reportPath}`);
37 |
38 | // Generate cache key based on report path
39 | const cacheKey = `complexityReport:${reportPath}`;
40 |
41 | // Define the core action function to read the report
42 | const coreActionFn = async () => {
43 | try {
44 | // Enable silent mode to prevent console logs from interfering with JSON response
45 | enableSilentMode();
46 |
47 | const report = readComplexityReport(reportPath);
48 |
49 | // Restore normal logging
50 | disableSilentMode();
51 |
52 | if (!report) {
53 | log.warn(`No complexity report found at ${reportPath}`);
54 | return {
55 | success: false,
56 | error: {
57 | code: 'FILE_NOT_FOUND_ERROR',
58 | message: `No complexity report found at ${reportPath}. Run 'analyze-complexity' first.`
59 | }
60 | };
61 | }
62 |
63 | return {
64 | success: true,
65 | data: {
66 | report,
67 | reportPath
68 | }
69 | };
70 | } catch (error) {
71 | // Make sure to restore normal logging even if there's an error
72 | disableSilentMode();
73 |
74 | log.error(`Error reading complexity report: ${error.message}`);
75 | return {
76 | success: false,
77 | error: {
78 | code: 'READ_ERROR',
79 | message: error.message
80 | }
81 | };
82 | }
83 | };
84 |
85 | // Use the caching utility
86 | try {
87 | const result = await coreActionFn();
88 | log.info('complexityReportDirect completed');
89 | return result;
90 | } catch (error) {
91 | // Ensure silent mode is disabled
92 | disableSilentMode();
93 |
94 | log.error(`Unexpected error during complexityReport: ${error.message}`);
95 | return {
96 | success: false,
97 | error: {
98 | code: 'UNEXPECTED_ERROR',
99 | message: error.message
100 | }
101 | };
102 | }
103 | } catch (error) {
104 | // Ensure silent mode is disabled if an outer error occurs
105 | disableSilentMode();
106 |
107 | log.error(`Error in complexityReportDirect: ${error.message}`);
108 | return {
109 | success: false,
110 | error: {
111 | code: 'UNEXPECTED_ERROR',
112 | message: error.message
113 | }
114 | };
115 | }
116 | }
117 |
```
--------------------------------------------------------------------------------
/mcp-server/src/logger.js:
--------------------------------------------------------------------------------
```javascript
1 | import chalk from 'chalk';
2 | import { isSilentMode } from '../../scripts/modules/utils.js';
3 | import { getLogLevel } from '../../scripts/modules/config-manager.js';
4 |
5 | // Define log levels
6 | const LOG_LEVELS = {
7 | debug: 0,
8 | info: 1,
9 | warn: 2,
10 | error: 3,
11 | success: 4
12 | };
13 |
14 | // Get log level from config manager or default to info
15 | const LOG_LEVEL = LOG_LEVELS[getLogLevel().toLowerCase()] ?? LOG_LEVELS.info;
16 |
17 | /**
18 | * Logs a message with the specified level
19 | * @param {string} level - The log level (debug, info, warn, error, success)
20 | * @param {...any} args - Arguments to log
21 | */
22 | function log(level, ...args) {
23 | // Skip logging if silent mode is enabled
24 | if (isSilentMode()) {
25 | return;
26 | }
27 |
28 | // Use text prefixes instead of emojis
29 | const prefixes = {
30 | debug: chalk.gray('[DEBUG]'),
31 | info: chalk.blue('[INFO]'),
32 | warn: chalk.yellow('[WARN]'),
33 | error: chalk.red('[ERROR]'),
34 | success: chalk.green('[SUCCESS]')
35 | };
36 |
37 | if (LOG_LEVELS[level] !== undefined && LOG_LEVELS[level] >= LOG_LEVEL) {
38 | const prefix = prefixes[level] || '';
39 | let coloredArgs = args;
40 |
41 | try {
42 | switch (level) {
43 | case 'error':
44 | coloredArgs = args.map((arg) =>
45 | typeof arg === 'string' ? chalk.red(arg) : arg
46 | );
47 | break;
48 | case 'warn':
49 | coloredArgs = args.map((arg) =>
50 | typeof arg === 'string' ? chalk.yellow(arg) : arg
51 | );
52 | break;
53 | case 'success':
54 | coloredArgs = args.map((arg) =>
55 | typeof arg === 'string' ? chalk.green(arg) : arg
56 | );
57 | break;
58 | case 'info':
59 | coloredArgs = args.map((arg) =>
60 | typeof arg === 'string' ? chalk.blue(arg) : arg
61 | );
62 | break;
63 | case 'debug':
64 | coloredArgs = args.map((arg) =>
65 | typeof arg === 'string' ? chalk.gray(arg) : arg
66 | );
67 | break;
68 | // default: use original args (no color)
69 | }
70 | } catch (colorError) {
71 | // Fallback if chalk fails on an argument
72 | // Use console.error here for internal logger errors, separate from normal logging
73 | console.error('Internal Logger Error applying chalk color:', colorError);
74 | coloredArgs = args;
75 | }
76 |
77 | // Revert to console.log - FastMCP's context logger (context.log)
78 | // is responsible for directing logs correctly (e.g., to stderr)
79 | // during tool execution without upsetting the client connection.
80 | // Logs outside of tool execution (like startup) will go to stdout.
81 | console.error(prefix, ...coloredArgs);
82 | }
83 | }
84 |
85 | /**
86 | * Create a logger object with methods for different log levels
87 | * @returns {Object} Logger object with info, error, debug, warn, and success methods
88 | */
89 | export function createLogger() {
90 | const createLogMethod =
91 | (level) =>
92 | (...args) =>
93 | log(level, ...args);
94 |
95 | return {
96 | debug: createLogMethod('debug'),
97 | info: createLogMethod('info'),
98 | warn: createLogMethod('warn'),
99 | error: createLogMethod('error'),
100 | success: createLogMethod('success'),
101 | log: log // Also expose the raw log function
102 | };
103 | }
104 |
105 | // Export a default logger instance
106 | const logger = createLogger();
107 |
108 | export default logger;
109 | export { log, LOG_LEVELS };
110 |
```
--------------------------------------------------------------------------------
/apps/extension/src/webview/App.tsx:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Main App Component
3 | */
4 |
5 | import React, { useReducer, useState, useEffect, useRef } from 'react';
6 | import { VSCodeContext } from './contexts/VSCodeContext';
7 | import { QueryProvider } from './providers/QueryProvider';
8 | import { AppContent } from './components/AppContent';
9 | import { ToastContainer } from './components/ToastContainer';
10 | import { ErrorBoundary } from './components/ErrorBoundary';
11 | import { appReducer, initialState } from './reducers/appReducer';
12 | import { useWebviewHeight } from './hooks/useWebviewHeight';
13 | import { useVSCodeMessages } from './hooks/useVSCodeMessages';
14 | import {
15 | showSuccessToast,
16 | showInfoToast,
17 | showWarningToast,
18 | showErrorToast,
19 | createToast
20 | } from './utils/toast';
21 |
22 | export const App: React.FC = () => {
23 | const [state, dispatch] = useReducer(appReducer, initialState);
24 | const [vscode] = useState(() => window.acquireVsCodeApi?.());
25 | const availableHeight = useWebviewHeight();
26 | const { sendMessage } = useVSCodeMessages(vscode, state, dispatch);
27 | const hasInitialized = useRef(false);
28 |
29 | // Initialize the webview
30 | useEffect(() => {
31 | if (hasInitialized.current) return;
32 | hasInitialized.current = true;
33 |
34 | if (!vscode) {
35 | console.warn('⚠️ VS Code API not available - running in standalone mode');
36 | dispatch({
37 | type: 'SET_CONNECTION_STATUS',
38 | payload: { isConnected: false, status: 'Standalone Mode' }
39 | });
40 | return;
41 | }
42 |
43 | console.log('🔄 Initializing webview...');
44 |
45 | // Notify extension that webview is ready
46 | vscode.postMessage({ type: 'ready' });
47 |
48 | // React Query will handle task fetching, so we only need to load tags data
49 | sendMessage({ type: 'getTags' })
50 | .then((tagsData) => {
51 | if (tagsData?.tags && tagsData?.currentTag) {
52 | const tagNames = tagsData.tags.map((tag: any) => tag.name || tag);
53 | dispatch({
54 | type: 'SET_TAG_DATA',
55 | payload: {
56 | currentTag: tagsData.currentTag,
57 | availableTags: tagNames
58 | }
59 | });
60 | }
61 | })
62 | .catch((error) => {
63 | console.error('❌ Failed to load tags:', error);
64 | });
65 | }, [vscode, sendMessage, dispatch]);
66 |
67 | const contextValue = {
68 | vscode,
69 | state,
70 | dispatch,
71 | sendMessage,
72 | availableHeight,
73 | // Toast notification functions
74 | showSuccessToast: showSuccessToast(dispatch),
75 | showInfoToast: showInfoToast(dispatch),
76 | showWarningToast: showWarningToast(dispatch),
77 | showErrorToast: showErrorToast(dispatch)
78 | };
79 |
80 | return (
81 | <QueryProvider>
82 | <VSCodeContext.Provider value={contextValue}>
83 | <ErrorBoundary
84 | onError={(error) => {
85 | // Handle React errors and show appropriate toast
86 | dispatch({
87 | type: 'ADD_TOAST',
88 | payload: createToast(
89 | 'error',
90 | 'Component Error',
91 | `A React component crashed: ${error.message}`,
92 | 10000
93 | )
94 | });
95 | }}
96 | >
97 | <AppContent />
98 | <ToastContainer
99 | notifications={state.toastNotifications}
100 | onDismiss={(id) => dispatch({ type: 'REMOVE_TOAST', payload: id })}
101 | />
102 | </ErrorBoundary>
103 | </VSCodeContext.Provider>
104 | </QueryProvider>
105 | );
106 | };
107 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/research.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/research.js
3 | * Tool to perform AI-powered research queries with project context
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | createErrorResponse,
9 | handleApiResult,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { researchDirect } from '../core/task-master-core.js';
13 | import { resolveTag } from '../../../scripts/modules/utils.js';
14 |
15 | /**
16 | * Register the research tool with the MCP server
17 | * @param {Object} server - FastMCP server instance
18 | */
19 | export function registerResearchTool(server) {
20 | server.addTool({
21 | name: 'research',
22 | description: 'Perform AI-powered research queries with project context',
23 |
24 | parameters: z.object({
25 | query: z.string().describe('Research query/prompt (required)'),
26 | taskIds: z
27 | .string()
28 | .optional()
29 | .describe(
30 | 'Comma-separated list of task/subtask IDs for context (e.g., "15,16.2,17")'
31 | ),
32 | filePaths: z
33 | .string()
34 | .optional()
35 | .describe(
36 | 'Comma-separated list of file paths for context (e.g., "src/api.js,docs/readme.md")'
37 | ),
38 | customContext: z
39 | .string()
40 | .optional()
41 | .describe('Additional custom context text to include in the research'),
42 | includeProjectTree: z
43 | .boolean()
44 | .optional()
45 | .describe(
46 | 'Include project file tree structure in context (default: false)'
47 | ),
48 | detailLevel: z
49 | .enum(['low', 'medium', 'high'])
50 | .optional()
51 | .describe('Detail level for the research response (default: medium)'),
52 | saveTo: z
53 | .string()
54 | .optional()
55 | .describe(
56 | 'Automatically save research results to specified task/subtask ID (e.g., "15" or "15.2")'
57 | ),
58 | saveToFile: z
59 | .boolean()
60 | .optional()
61 | .describe(
62 | 'Save research results to .taskmaster/docs/research/ directory (default: false)'
63 | ),
64 | projectRoot: z
65 | .string()
66 | .describe('The directory of the project. Must be an absolute path.'),
67 | tag: z.string().optional().describe('Tag context to operate on')
68 | }),
69 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
70 | try {
71 | const resolvedTag = resolveTag({
72 | projectRoot: args.projectRoot,
73 | tag: args.tag
74 | });
75 | log.info(
76 | `Starting research with query: "${args.query.substring(0, 100)}${args.query.length > 100 ? '...' : ''}"`
77 | );
78 |
79 | // Call the direct function
80 | const result = await researchDirect(
81 | {
82 | query: args.query,
83 | taskIds: args.taskIds,
84 | filePaths: args.filePaths,
85 | customContext: args.customContext,
86 | includeProjectTree: args.includeProjectTree || false,
87 | detailLevel: args.detailLevel || 'medium',
88 | saveTo: args.saveTo,
89 | saveToFile: args.saveToFile || false,
90 | projectRoot: args.projectRoot,
91 | tag: resolvedTag
92 | },
93 | log,
94 | { session }
95 | );
96 |
97 | return handleApiResult(
98 | result,
99 | log,
100 | 'Error performing research',
101 | undefined,
102 | args.projectRoot
103 | );
104 | } catch (error) {
105 | log.error(`Error in research tool: ${error.message}`);
106 | return createErrorResponse(error.message);
107 | }
108 | })
109 | });
110 | }
111 |
```
--------------------------------------------------------------------------------
/apps/mcp/src/tools/autopilot/finalize.tool.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview autopilot-finalize MCP tool
3 | * Finalize and complete the workflow with working tree validation
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | handleApiResult,
9 | withNormalizedProjectRoot
10 | } from '../../shared/utils.js';
11 | import type { MCPContext } from '../../shared/types.js';
12 | import { WorkflowService } from '@tm/core';
13 | import type { FastMCP } from 'fastmcp';
14 |
15 | const FinalizeSchema = z.object({
16 | projectRoot: z
17 | .string()
18 | .describe('Absolute path to the project root directory')
19 | });
20 |
21 | type FinalizeArgs = z.infer<typeof FinalizeSchema>;
22 |
23 | /**
24 | * Register the autopilot_finalize tool with the MCP server
25 | */
26 | export function registerAutopilotFinalizeTool(server: FastMCP) {
27 | server.addTool({
28 | name: 'autopilot_finalize',
29 | description:
30 | 'Finalize and complete the workflow. Validates that all changes are committed and working tree is clean before marking workflow as complete.',
31 | parameters: FinalizeSchema,
32 | execute: withNormalizedProjectRoot(
33 | async (args: FinalizeArgs, context: MCPContext) => {
34 | const { projectRoot } = args;
35 |
36 | try {
37 | context.log.info(`Finalizing workflow in ${projectRoot}`);
38 |
39 | const workflowService = new WorkflowService(projectRoot);
40 |
41 | // Check if workflow exists
42 | if (!(await workflowService.hasWorkflow())) {
43 | return handleApiResult({
44 | result: {
45 | success: false,
46 | error: {
47 | message:
48 | 'No active workflow found. Start a workflow with autopilot_start'
49 | }
50 | },
51 | log: context.log,
52 | projectRoot
53 | });
54 | }
55 |
56 | // Resume workflow
57 | await workflowService.resumeWorkflow();
58 | const currentStatus = workflowService.getStatus();
59 |
60 | // Verify we're in FINALIZE phase
61 | if (currentStatus.phase !== 'FINALIZE') {
62 | return handleApiResult({
63 | result: {
64 | success: false,
65 | error: {
66 | message: `Cannot finalize: workflow is in ${currentStatus.phase} phase. Complete all subtasks first.`
67 | }
68 | },
69 | log: context.log,
70 | projectRoot
71 | });
72 | }
73 |
74 | // Finalize workflow (validates clean working tree)
75 | const newStatus = await workflowService.finalizeWorkflow();
76 |
77 | context.log.info('Workflow finalized successfully');
78 |
79 | // Get next action
80 | const nextAction = workflowService.getNextAction();
81 |
82 | return handleApiResult({
83 | result: {
84 | success: true,
85 | data: {
86 | message: 'Workflow completed successfully',
87 | ...newStatus,
88 | nextAction: nextAction.action,
89 | nextSteps: nextAction.nextSteps
90 | }
91 | },
92 | log: context.log,
93 | projectRoot
94 | });
95 | } catch (error: any) {
96 | context.log.error(`Error in autopilot-finalize: ${error.message}`);
97 | if (error.stack) {
98 | context.log.debug(error.stack);
99 | }
100 | return handleApiResult({
101 | result: {
102 | success: false,
103 | error: {
104 | message: `Failed to finalize workflow: ${error.message}`
105 | }
106 | },
107 | log: context.log,
108 | projectRoot
109 | });
110 | }
111 | }
112 | )
113 | });
114 | }
115 |
```
--------------------------------------------------------------------------------
/apps/docs/archive/Installation.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: "Installation(2)"
3 | description: "This guide walks you through setting up Task Master in your development environment."
4 | ---
5 |
6 | ## Initial Setup
7 |
8 | <Tip>
9 | MCP (Model Control Protocol) provides the easiest way to get started with Task Master directly in your editor.
10 | </Tip>
11 |
12 | <AccordionGroup>
13 | <Accordion title="Option 1: Using MCP (Recommended)" icon="sparkles">
14 | <Steps>
15 | <Step title="Add the MCP config to your editor">
16 | <Link href="https://cursor.sh">Cursor</Link> recommended, but it works with other text editors
17 | ```json
18 | {
19 | "mcpServers": {
20 | "taskmaster-ai": {
21 | "command": "npx",
22 | "args": ["-y", "task-master-ai"],
23 | "env": {
24 | "ANTHROPIC_API_KEY": "YOUR_ANTHROPIC_API_KEY_HERE",
25 | "PERPLEXITY_API_KEY": "YOUR_PERPLEXITY_API_KEY_HERE",
26 | "MODEL": "claude-3-7-sonnet-20250219",
27 | "PERPLEXITY_MODEL": "sonar-pro",
28 | "MAX_TOKENS": 128000,
29 | "TEMPERATURE": 0.2,
30 | "DEFAULT_SUBTASKS": 5,
31 | "DEFAULT_PRIORITY": "medium"
32 | }
33 | }
34 | }
35 | }
36 | ```
37 | </Step>
38 | <Step title="Enable the MCP in your editor settings">
39 |
40 | </Step>
41 | <Step title="Prompt the AI to initialize Task Master">
42 | > "Can you please initialize taskmaster-ai into my project?"
43 |
44 | **The AI will:**
45 |
46 | 1. Create necessary project structure
47 | 2. Set up initial configuration files
48 | 3. Guide you through the rest of the process
49 | 4. Place your PRD document in the `scripts/` directory (e.g., `scripts/prd.txt`)
50 | 5. **Use natural language commands** to interact with Task Master:
51 |
52 | > "Can you parse my PRD at scripts/prd.txt?"
53 | >
54 | > "What's the next task I should work on?"
55 | >
56 | > "Can you help me implement task 3?"
57 | </Step>
58 | </Steps>
59 | </Accordion>
60 | <Accordion title="Option 2: Manual Installation">
61 | If you prefer to use the command line interface directly:
62 |
63 | <Steps>
64 | <Step title="Install">
65 | <CodeGroup>
66 |
67 | ```bash Global
68 | npm install -g task-master-ai
69 | ```
70 |
71 |
72 | ```bash Local
73 | npm install task-master-ai
74 | ```
75 |
76 | </CodeGroup>
77 | </Step>
78 | <Step title="Initialize a new project">
79 | <CodeGroup>
80 |
81 | ```bash Global
82 | task-master init
83 | ```
84 |
85 |
86 | ```bash Local
87 | npx task-master-init
88 | ```
89 |
90 | </CodeGroup>
91 | </Step>
92 | </Steps>
93 | This will prompt you for project details and set up a new project with the necessary files and structure.
94 | </Accordion>
95 | </AccordionGroup>
96 |
97 | ## Common Commands
98 |
99 | <Tip>
100 | After setting up Task Master, you can use these commands (either via AI prompts or CLI)
101 | </Tip>
102 |
103 | ```bash
104 | # Parse a PRD and generate tasks
105 | task-master parse-prd your-prd.txt
106 |
107 | # List all tasks
108 | task-master list
109 |
110 | # Show the next task to work on
111 | task-master next
112 |
113 | # Generate task files
114 | task-master generate
115 |
```
--------------------------------------------------------------------------------
/packages/tm-core/src/common/constants/paths.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview Path constants for Task Master Core
3 | * Defines all file paths and directory structure constants
4 | */
5 |
6 | // .taskmaster directory structure paths
7 | export const TASKMASTER_DIR = '.taskmaster';
8 | export const TASKMASTER_TASKS_DIR = '.taskmaster/tasks';
9 | export const TASKMASTER_DOCS_DIR = '.taskmaster/docs';
10 | export const TASKMASTER_REPORTS_DIR = '.taskmaster/reports';
11 | export const TASKMASTER_TEMPLATES_DIR = '.taskmaster/templates';
12 |
13 | // Task Master configuration files
14 | export const TASKMASTER_CONFIG_FILE = '.taskmaster/config.json';
15 | export const TASKMASTER_STATE_FILE = '.taskmaster/state.json';
16 | export const LEGACY_CONFIG_FILE = '.taskmasterconfig';
17 |
18 | // Task Master report files
19 | export const COMPLEXITY_REPORT_FILE =
20 | '.taskmaster/reports/task-complexity-report.json';
21 | export const LEGACY_COMPLEXITY_REPORT_FILE =
22 | 'scripts/task-complexity-report.json';
23 |
24 | // Task Master PRD file paths
25 | export const PRD_FILE = '.taskmaster/docs/prd.txt';
26 | export const LEGACY_PRD_FILE = 'scripts/prd.txt';
27 |
28 | // Task Master template files
29 | export const EXAMPLE_PRD_FILE = '.taskmaster/templates/example_prd.txt';
30 | export const LEGACY_EXAMPLE_PRD_FILE = 'scripts/example_prd.txt';
31 |
32 | // Task Master task file paths
33 | export const TASKMASTER_TASKS_FILE = '.taskmaster/tasks/tasks.json';
34 | export const LEGACY_TASKS_FILE = 'tasks/tasks.json';
35 |
36 | // General project files (not Task Master specific but commonly used)
37 | export const ENV_EXAMPLE_FILE = '.env.example';
38 | export const GITIGNORE_FILE = '.gitignore';
39 |
40 | // Task file naming pattern
41 | export const TASK_FILE_PREFIX = 'task_';
42 | export const TASK_FILE_EXTENSION = '.txt';
43 |
44 | /**
45 | * Task Master specific markers (absolute highest priority)
46 | * ONLY truly Task Master-specific markers that uniquely identify a Task Master project
47 | */
48 | export const TASKMASTER_PROJECT_MARKERS = [
49 | '.taskmaster', // Task Master directory
50 | TASKMASTER_CONFIG_FILE, // .taskmaster/config.json
51 | TASKMASTER_TASKS_FILE, // .taskmaster/tasks/tasks.json
52 | LEGACY_CONFIG_FILE // .taskmasterconfig (legacy but still Task Master-specific)
53 | ] as const;
54 |
55 | /**
56 | * Other project markers (only checked if no Task Master markers found)
57 | * Includes generic task files that could belong to any task runner/build system
58 | */
59 | export const OTHER_PROJECT_MARKERS = [
60 | LEGACY_TASKS_FILE, // tasks/tasks.json (NOT Task Master-specific)
61 | 'tasks.json', // Generic tasks file (NOT Task Master-specific)
62 | '.git', // Git repository
63 | '.svn', // SVN repository
64 | 'package.json', // Node.js project
65 | 'yarn.lock', // Yarn project
66 | 'package-lock.json', // npm project
67 | 'pnpm-lock.yaml', // pnpm project
68 | 'Cargo.toml', // Rust project
69 | 'go.mod', // Go project
70 | 'pyproject.toml', // Python project
71 | 'requirements.txt', // Python project
72 | 'Gemfile', // Ruby project
73 | 'composer.json' // PHP project
74 | ] as const;
75 |
76 | /**
77 | * All project markers combined (for backward compatibility)
78 | * @deprecated Use TASKMASTER_PROJECT_MARKERS and OTHER_PROJECT_MARKERS separately
79 | */
80 | export const PROJECT_MARKERS = [
81 | ...TASKMASTER_PROJECT_MARKERS,
82 | ...OTHER_PROJECT_MARKERS
83 | ] as const;
84 |
```
--------------------------------------------------------------------------------
/packages/tm-core/src/common/constants/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview Constants for Task Master Core
3 | * Single source of truth for all constant values
4 | */
5 |
6 | import type {
7 | TaskComplexity,
8 | TaskPriority,
9 | TaskStatus
10 | } from '../types/index.js';
11 |
12 | // Import from root package.json (monorepo root) for version info
13 | import packageJson from '../../../../../package.json' with { type: 'json' };
14 |
15 | /**
16 | * Task Master version from root package.json
17 | * Centralized to avoid fragile relative paths throughout the codebase
18 | */
19 | export const TASKMASTER_VERSION = packageJson.version || 'unknown';
20 |
21 | /**
22 | * Package name from root package.json
23 | */
24 | export const PACKAGE_NAME = packageJson.name || 'task-master-ai';
25 |
26 | /**
27 | * Valid task status values
28 | */
29 | export const TASK_STATUSES: readonly TaskStatus[] = [
30 | 'pending',
31 | 'in-progress',
32 | 'done',
33 | 'deferred',
34 | 'cancelled',
35 | 'blocked',
36 | 'review'
37 | ] as const;
38 |
39 | /**
40 | * Terminal complete statuses - tasks that are finished and satisfy dependencies
41 | * These statuses indicate a task is in a final state and:
42 | * - Should count toward completion percentage
43 | * - Should be considered satisfied for dependency resolution
44 | * - Should not be selected as "next task"
45 | *
46 | * Note: 'completed' is a workflow-specific alias for 'done' used in some contexts
47 | */
48 | export const TERMINAL_COMPLETE_STATUSES: readonly TaskStatus[] = [
49 | 'done',
50 | 'completed',
51 | 'cancelled'
52 | ] as const;
53 |
54 | /**
55 | * Check if a task status represents a terminal complete state
56 | *
57 | * @param status - The task status to check
58 | * @returns true if the status represents a completed/terminal task
59 | *
60 | * @example
61 | * ```typescript
62 | * isTaskComplete('done') // true
63 | * isTaskComplete('completed') // true
64 | * isTaskComplete('cancelled') // true
65 | * isTaskComplete('pending') // false
66 | * ```
67 | */
68 | export function isTaskComplete(status: TaskStatus): boolean {
69 | return TERMINAL_COMPLETE_STATUSES.includes(status);
70 | }
71 |
72 | /**
73 | * Valid task priority values
74 | */
75 | export const TASK_PRIORITIES: readonly TaskPriority[] = [
76 | 'low',
77 | 'medium',
78 | 'high',
79 | 'critical'
80 | ] as const;
81 |
82 | /**
83 | * Valid task complexity values
84 | */
85 | export const TASK_COMPLEXITIES: readonly TaskComplexity[] = [
86 | 'simple',
87 | 'moderate',
88 | 'complex',
89 | 'very-complex'
90 | ] as const;
91 |
92 | /**
93 | * Valid output formats for task display
94 | */
95 | export const OUTPUT_FORMATS = ['text', 'json', 'compact'] as const;
96 | export type OutputFormat = (typeof OUTPUT_FORMATS)[number];
97 |
98 | /**
99 | * Status icons for display
100 | */
101 | export const STATUS_ICONS: Record<TaskStatus, string> = {
102 | done: '✓',
103 | completed: '✓',
104 | 'in-progress': '►',
105 | blocked: '⭕',
106 | pending: '○',
107 | deferred: '⏸',
108 | cancelled: '✗',
109 | review: '👁'
110 | } as const;
111 |
112 | /**
113 | * Status colors for display (using chalk color names)
114 | */
115 | export const STATUS_COLORS: Record<TaskStatus, string> = {
116 | pending: 'yellow',
117 | 'in-progress': 'blue',
118 | done: 'green',
119 | deferred: 'gray',
120 | cancelled: 'red',
121 | blocked: 'magenta',
122 | review: 'cyan',
123 | completed: 'green'
124 | } as const;
125 |
126 | /**
127 | * Provider constants - AI model providers
128 | */
129 | export * from './providers.js';
130 |
131 | /**
132 | * Path constants - file paths and directory structure
133 | */
134 | export * from './paths.js';
135 |
```
--------------------------------------------------------------------------------
/packages/tm-core/src/modules/workflow/types.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Workflow phase definitions
3 | */
4 | export type WorkflowPhase =
5 | | 'PREFLIGHT'
6 | | 'BRANCH_SETUP'
7 | | 'SUBTASK_LOOP'
8 | | 'FINALIZE'
9 | | 'COMPLETE';
10 |
11 | /**
12 | * TDD cycle phases within subtask loop
13 | */
14 | export type TDDPhase = 'RED' | 'GREEN' | 'COMMIT';
15 |
16 | /**
17 | * Workflow state context
18 | */
19 | export interface WorkflowContext {
20 | taskId: string;
21 | subtasks: SubtaskInfo[];
22 | currentSubtaskIndex: number;
23 | currentTDDPhase?: TDDPhase;
24 | branchName?: string;
25 | errors: WorkflowError[];
26 | metadata: Record<string, unknown>;
27 | lastTestResults?: TestResult;
28 | }
29 |
30 | /**
31 | * Test result from test execution
32 | */
33 | export interface TestResult {
34 | total: number;
35 | passed: number;
36 | failed: number;
37 | skipped: number;
38 | phase: 'RED' | 'GREEN';
39 | }
40 |
41 | /**
42 | * Subtask information
43 | */
44 | export interface SubtaskInfo {
45 | id: string;
46 | title: string;
47 | status: 'pending' | 'in-progress' | 'completed' | 'failed';
48 | attempts: number;
49 | maxAttempts?: number;
50 | }
51 |
52 | /**
53 | * Workflow error information
54 | */
55 | export interface WorkflowError {
56 | phase: WorkflowPhase;
57 | message: string;
58 | timestamp: Date;
59 | recoverable: boolean;
60 | }
61 |
62 | /**
63 | * State machine state
64 | */
65 | export interface WorkflowState {
66 | phase: WorkflowPhase;
67 | context: WorkflowContext;
68 | }
69 |
70 | /**
71 | * State transition event types
72 | */
73 | export type WorkflowEvent =
74 | | { type: 'PREFLIGHT_COMPLETE' }
75 | | { type: 'BRANCH_CREATED'; branchName: string }
76 | | { type: 'SUBTASK_START'; subtaskId: string }
77 | | { type: 'RED_PHASE_COMPLETE'; testResults?: TestResult }
78 | | { type: 'GREEN_PHASE_COMPLETE'; testResults?: TestResult }
79 | | { type: 'COMMIT_COMPLETE' }
80 | | { type: 'SUBTASK_COMPLETE' }
81 | | { type: 'ALL_SUBTASKS_COMPLETE' }
82 | | { type: 'FINALIZE_COMPLETE' }
83 | | { type: 'ERROR'; error: WorkflowError }
84 | | { type: 'RETRY' }
85 | | { type: 'ABORT' };
86 |
87 | /**
88 | * State transition definition
89 | */
90 | export interface StateTransition {
91 | from: WorkflowPhase;
92 | to: WorkflowPhase;
93 | event: WorkflowEvent['type'];
94 | guard?: (context: WorkflowContext) => boolean;
95 | }
96 |
97 | /**
98 | * State machine configuration
99 | */
100 | export interface StateMachineConfig {
101 | initialPhase: WorkflowPhase;
102 | transitions: StateTransition[];
103 | }
104 |
105 | /**
106 | * Workflow event listener
107 | */
108 | export type WorkflowEventListener = (event: WorkflowEventData) => void;
109 |
110 | /**
111 | * Comprehensive event data for workflow events
112 | */
113 | export interface WorkflowEventData {
114 | type: WorkflowEventType;
115 | timestamp: Date;
116 | phase: WorkflowPhase;
117 | tddPhase?: TDDPhase;
118 | subtaskId?: string;
119 | data?: Record<string, unknown>;
120 | }
121 |
122 | /**
123 | * All possible workflow event types
124 | */
125 | export type WorkflowEventType =
126 | | 'workflow:started'
127 | | 'workflow:completed'
128 | | 'workflow:error'
129 | | 'workflow:resumed'
130 | | 'phase:entered'
131 | | 'phase:exited'
132 | | 'tdd:feature-already-implemented'
133 | | 'tdd:red:started'
134 | | 'tdd:red:completed'
135 | | 'tdd:green:started'
136 | | 'tdd:green:completed'
137 | | 'tdd:commit:started'
138 | | 'tdd:commit:completed'
139 | | 'subtask:started'
140 | | 'subtask:completed'
141 | | 'subtask:failed'
142 | | 'test:run'
143 | | 'test:passed'
144 | | 'test:failed'
145 | | 'git:branch:created'
146 | | 'git:commit:created'
147 | | 'error:occurred'
148 | | 'state:persisted'
149 | | 'progress:updated'
150 | | 'adapter:configured';
151 |
```
--------------------------------------------------------------------------------
/.github/workflows/extension-release.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Extension Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - "extension@*"
7 |
8 | permissions:
9 | contents: write
10 |
11 | concurrency: extension-release-${{ github.ref }}
12 |
13 | jobs:
14 | publish-extension:
15 | runs-on: ubuntu-latest
16 | environment: extension-release
17 | steps:
18 | - uses: actions/checkout@v4
19 |
20 | - uses: actions/setup-node@v4
21 | with:
22 | node-version: 20
23 |
24 | - name: Cache node_modules
25 | uses: actions/cache@v4
26 | with:
27 | path: |
28 | node_modules
29 | */*/node_modules
30 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
31 | restore-keys: |
32 | ${{ runner.os }}-node-
33 |
34 | - name: Install Monorepo Dependencies
35 | run: npm ci
36 | timeout-minutes: 5
37 |
38 | - name: Type Check Extension
39 | working-directory: apps/extension
40 | run: npm run typecheck
41 | env:
42 | FORCE_COLOR: 1
43 |
44 | - name: Build Extension
45 | working-directory: apps/extension
46 | run: npm run build
47 | env:
48 | FORCE_COLOR: 1
49 |
50 | - name: Package Extension
51 | working-directory: apps/extension
52 | run: npm run package
53 | env:
54 | FORCE_COLOR: 1
55 |
56 | - name: Create VSIX Package
57 | working-directory: apps/extension/vsix-build
58 | run: npx vsce package --no-dependencies
59 | env:
60 | FORCE_COLOR: 1
61 |
62 | - name: Get VSIX filename
63 | id: vsix-info
64 | working-directory: apps/extension/vsix-build
65 | run: |
66 | VSIX_FILE=$(find . -maxdepth 1 -name "*.vsix" -type f | head -n1 | xargs basename)
67 | if [ -z "$VSIX_FILE" ]; then
68 | echo "Error: No VSIX file found"
69 | exit 1
70 | fi
71 | echo "vsix-filename=$VSIX_FILE" >> "$GITHUB_OUTPUT"
72 | echo "Found VSIX: $VSIX_FILE"
73 |
74 | - name: Publish to VS Code Marketplace
75 | working-directory: apps/extension/vsix-build
76 | run: npx vsce publish --packagePath "${{ steps.vsix-info.outputs.vsix-filename }}"
77 | env:
78 | VSCE_PAT: ${{ secrets.VSCE_PAT }}
79 | FORCE_COLOR: 1
80 |
81 | - name: Install Open VSX CLI
82 | run: npm install -g ovsx
83 |
84 | - name: Publish to Open VSX Registry
85 | working-directory: apps/extension/vsix-build
86 | run: ovsx publish "${{ steps.vsix-info.outputs.vsix-filename }}"
87 | env:
88 | OVSX_PAT: ${{ secrets.OVSX_PAT }}
89 | FORCE_COLOR: 1
90 |
91 | - name: Upload Build Artifacts
92 | uses: actions/upload-artifact@v4
93 | with:
94 | name: extension-release-${{ github.ref_name }}
95 | path: |
96 | apps/extension/vsix-build/*.vsix
97 | apps/extension/dist/
98 | retention-days: 90
99 |
100 | notify-success:
101 | needs: publish-extension
102 | if: success()
103 | runs-on: ubuntu-latest
104 | steps:
105 | - name: Success Notification
106 | run: |
107 | echo "🎉 Extension ${{ github.ref_name }} successfully published!"
108 | echo "📦 Available on VS Code Marketplace"
109 | echo "🌍 Available on Open VSX Registry"
110 | echo "🏷️ GitHub release created: ${{ github.ref_name }}"
111 |
```
--------------------------------------------------------------------------------
/apps/cli/src/commands/autopilot/status.command.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview Status Command - Show workflow progress
3 | */
4 |
5 | import { WorkflowOrchestrator } from '@tm/core';
6 | import { Command } from 'commander';
7 | import {
8 | AutopilotBaseOptions,
9 | OutputFormatter,
10 | hasWorkflowState,
11 | loadWorkflowState
12 | } from './shared.js';
13 | import { getProjectRoot } from '../../utils/project-root.js';
14 |
15 | type StatusOptions = AutopilotBaseOptions;
16 |
17 | /**
18 | * Status Command - Show current workflow status
19 | */
20 | export class StatusCommand extends Command {
21 | constructor() {
22 | super('status');
23 |
24 | this.description('Show current TDD workflow status and progress').action(
25 | async (options: StatusOptions) => {
26 | await this.execute(options);
27 | }
28 | );
29 | }
30 |
31 | private async execute(options: StatusOptions): Promise<void> {
32 | // Inherit parent options
33 | const parentOpts = this.parent?.opts() as AutopilotBaseOptions;
34 | const mergedOptions: StatusOptions = {
35 | ...parentOpts,
36 | ...options,
37 | projectRoot: getProjectRoot(
38 | options.projectRoot || parentOpts?.projectRoot
39 | )
40 | };
41 |
42 | const formatter = new OutputFormatter(mergedOptions.json || false);
43 |
44 | try {
45 | // Check for workflow state
46 | const hasState = await hasWorkflowState(mergedOptions.projectRoot!);
47 | if (!hasState) {
48 | formatter.error('No active workflow', {
49 | suggestion: 'Start a workflow with: autopilot start <taskId>'
50 | });
51 | process.exit(1);
52 | }
53 |
54 | // Load state
55 | const state = await loadWorkflowState(mergedOptions.projectRoot!);
56 | if (!state) {
57 | formatter.error('Failed to load workflow state');
58 | process.exit(1);
59 | }
60 |
61 | // Restore orchestrator
62 | const orchestrator = new WorkflowOrchestrator(state.context);
63 | orchestrator.restoreState(state);
64 |
65 | // Get status information
66 | const phase = orchestrator.getCurrentPhase();
67 | const tddPhase = orchestrator.getCurrentTDDPhase();
68 | const progress = orchestrator.getProgress();
69 | const currentSubtask = orchestrator.getCurrentSubtask();
70 | const errors = state.context.errors ?? [];
71 |
72 | // Build status output
73 | const status = {
74 | taskId: state.context.taskId,
75 | phase,
76 | tddPhase,
77 | branchName: state.context.branchName,
78 | progress: {
79 | completed: progress.completed,
80 | total: progress.total,
81 | current: progress.current,
82 | percentage: progress.percentage
83 | },
84 | currentSubtask: currentSubtask
85 | ? {
86 | id: currentSubtask.id,
87 | title: currentSubtask.title,
88 | status: currentSubtask.status,
89 | attempts: currentSubtask.attempts,
90 | maxAttempts: currentSubtask.maxAttempts
91 | }
92 | : null,
93 | subtasks: state.context.subtasks.map((st) => ({
94 | id: st.id,
95 | title: st.title,
96 | status: st.status,
97 | attempts: st.attempts
98 | })),
99 | errors: errors.length > 0 ? errors : undefined,
100 | metadata: state.context.metadata
101 | };
102 |
103 | if (mergedOptions.json) {
104 | formatter.output(status);
105 | } else {
106 | formatter.success('Workflow status', status);
107 | }
108 | } catch (error) {
109 | formatter.error((error as Error).message);
110 | if (mergedOptions.verbose) {
111 | console.error((error as Error).stack);
112 | }
113 | process.exit(1);
114 | }
115 | }
116 | }
117 |
```
--------------------------------------------------------------------------------
/apps/cli/src/commands/autopilot/resume.command.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview Resume Command - Restore and resume TDD workflow
3 | */
4 |
5 | import { WorkflowOrchestrator } from '@tm/core';
6 | import { Command } from 'commander';
7 | import {
8 | AutopilotBaseOptions,
9 | OutputFormatter,
10 | hasWorkflowState,
11 | loadWorkflowState
12 | } from './shared.js';
13 | import { getProjectRoot } from '../../utils/project-root.js';
14 |
15 | type ResumeOptions = AutopilotBaseOptions;
16 |
17 | /**
18 | * Resume Command - Restore workflow from saved state
19 | */
20 | export class ResumeCommand extends Command {
21 | constructor() {
22 | super('resume');
23 |
24 | this.description('Resume a previously started TDD workflow').action(
25 | async (options: ResumeOptions) => {
26 | await this.execute(options);
27 | }
28 | );
29 | }
30 |
31 | private async execute(options: ResumeOptions): Promise<void> {
32 | // Inherit parent options (autopilot command)
33 | const parentOpts = this.parent?.opts() as AutopilotBaseOptions;
34 | const mergedOptions: ResumeOptions = {
35 | ...parentOpts,
36 | ...options,
37 | projectRoot: getProjectRoot(
38 | options.projectRoot || parentOpts?.projectRoot
39 | )
40 | };
41 |
42 | const formatter = new OutputFormatter(mergedOptions.json || false);
43 |
44 | try {
45 | // Check for workflow state
46 | const hasState = await hasWorkflowState(mergedOptions.projectRoot!);
47 | if (!hasState) {
48 | formatter.error('No workflow state found', {
49 | suggestion: 'Start a new workflow with: autopilot start <taskId>'
50 | });
51 | process.exit(1);
52 | }
53 |
54 | // Load state
55 | formatter.info('Loading workflow state...');
56 | const state = await loadWorkflowState(mergedOptions.projectRoot!);
57 |
58 | if (!state) {
59 | formatter.error('Failed to load workflow state');
60 | process.exit(1);
61 | }
62 |
63 | // Validate state can be resumed
64 | const orchestrator = new WorkflowOrchestrator(state.context);
65 | if (!orchestrator.canResumeFromState(state)) {
66 | formatter.error('Invalid workflow state', {
67 | suggestion:
68 | 'State file may be corrupted. Consider starting a new workflow.'
69 | });
70 | process.exit(1);
71 | }
72 |
73 | // Restore state
74 | orchestrator.restoreState(state);
75 |
76 | // Re-enable auto-persistence
77 | const { saveWorkflowState } = await import('./shared.js');
78 | orchestrator.enableAutoPersist(async (newState) => {
79 | await saveWorkflowState(mergedOptions.projectRoot!, newState);
80 | });
81 |
82 | // Get progress
83 | const progress = orchestrator.getProgress();
84 | const currentSubtask = orchestrator.getCurrentSubtask();
85 |
86 | // Output success
87 | formatter.success('Workflow resumed', {
88 | taskId: state.context.taskId,
89 | phase: orchestrator.getCurrentPhase(),
90 | tddPhase: orchestrator.getCurrentTDDPhase(),
91 | branchName: state.context.branchName,
92 | progress: {
93 | completed: progress.completed,
94 | total: progress.total,
95 | percentage: progress.percentage
96 | },
97 | currentSubtask: currentSubtask
98 | ? {
99 | id: currentSubtask.id,
100 | title: currentSubtask.title,
101 | attempts: currentSubtask.attempts
102 | }
103 | : null
104 | });
105 | } catch (error) {
106 | formatter.error((error as Error).message);
107 | if (mergedOptions.verbose) {
108 | console.error((error as Error).stack);
109 | }
110 | process.exit(1);
111 | }
112 | }
113 | }
114 |
```
--------------------------------------------------------------------------------
/tests/integration/providers/temperature-support.test.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * Integration Tests for Provider Temperature Support
3 | *
4 | * This test suite verifies that all providers correctly declare their
5 | * temperature support capabilities. CLI providers should have
6 | * supportsTemperature = false, while standard API providers should
7 | * have supportsTemperature = true.
8 | *
9 | * These tests are separated from unit tests to avoid coupling
10 | * base provider tests with concrete provider implementations.
11 | */
12 |
13 | import { ClaudeCodeProvider } from '../../../src/ai-providers/claude-code.js';
14 | import { CodexCliProvider } from '../../../src/ai-providers/codex-cli.js';
15 | import { GeminiCliProvider } from '../../../src/ai-providers/gemini-cli.js';
16 | import { GrokCliProvider } from '../../../src/ai-providers/grok-cli.js';
17 | import { AnthropicAIProvider } from '../../../src/ai-providers/anthropic.js';
18 | import { OpenAIProvider } from '../../../src/ai-providers/openai.js';
19 | import { GoogleAIProvider } from '../../../src/ai-providers/google.js';
20 | import { PerplexityAIProvider } from '../../../src/ai-providers/perplexity.js';
21 | import { XAIProvider } from '../../../src/ai-providers/xai.js';
22 | import { GroqProvider } from '../../../src/ai-providers/groq.js';
23 | import { OpenRouterAIProvider } from '../../../src/ai-providers/openrouter.js';
24 | import { OllamaAIProvider } from '../../../src/ai-providers/ollama.js';
25 | import { BedrockAIProvider } from '../../../src/ai-providers/bedrock.js';
26 | import { AzureProvider } from '../../../src/ai-providers/azure.js';
27 | import { VertexAIProvider } from '../../../src/ai-providers/google-vertex.js';
28 |
29 | describe('Provider Temperature Support', () => {
30 | describe('CLI Providers', () => {
31 | it('should verify CLI providers have supportsTemperature = false', () => {
32 | expect(new ClaudeCodeProvider().supportsTemperature).toBe(false);
33 | expect(new CodexCliProvider().supportsTemperature).toBe(false);
34 | expect(new GeminiCliProvider().supportsTemperature).toBe(false);
35 | expect(new GrokCliProvider().supportsTemperature).toBe(false);
36 | });
37 | });
38 |
39 | describe('Standard API Providers', () => {
40 | it('should verify standard providers have supportsTemperature = true', () => {
41 | expect(new AnthropicAIProvider().supportsTemperature).toBe(true);
42 | expect(new OpenAIProvider().supportsTemperature).toBe(true);
43 | expect(new GoogleAIProvider().supportsTemperature).toBe(true);
44 | expect(new PerplexityAIProvider().supportsTemperature).toBe(true);
45 | expect(new XAIProvider().supportsTemperature).toBe(true);
46 | expect(new GroqProvider().supportsTemperature).toBe(true);
47 | expect(new OpenRouterAIProvider().supportsTemperature).toBe(true);
48 | });
49 | });
50 |
51 | describe('Special Case Providers', () => {
52 | it('should verify Ollama provider has supportsTemperature = true', () => {
53 | expect(new OllamaAIProvider().supportsTemperature).toBe(true);
54 | });
55 |
56 | it('should verify cloud providers have supportsTemperature = true', () => {
57 | expect(new BedrockAIProvider().supportsTemperature).toBe(true);
58 | expect(new AzureProvider().supportsTemperature).toBe(true);
59 | expect(new VertexAIProvider().supportsTemperature).toBe(true);
60 | });
61 | });
62 | });
63 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/update-task.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/update-task.js
3 | * Tool to update a single task by ID with new information
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | handleApiResult,
9 | createErrorResponse,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { updateTaskByIdDirect } from '../core/task-master-core.js';
13 | import { findTasksPath } from '../core/utils/path-utils.js';
14 | import { resolveTag } from '../../../scripts/modules/utils.js';
15 |
16 | /**
17 | * Register the update-task tool with the MCP server
18 | * @param {Object} server - FastMCP server instance
19 | */
20 | export function registerUpdateTaskTool(server) {
21 | server.addTool({
22 | name: 'update_task',
23 | description:
24 | 'Updates a single task by ID with new information or context provided in the prompt.',
25 | parameters: z.object({
26 | id: z
27 | .string() // ID can be number or string like "1.2"
28 | .describe(
29 | "ID of the task (e.g., '15') to update. Subtasks are supported using the update-subtask tool."
30 | ),
31 | prompt: z
32 | .string()
33 | .describe('New information or context to incorporate into the task'),
34 | research: z
35 | .boolean()
36 | .optional()
37 | .describe('Use Perplexity AI for research-backed updates'),
38 | append: z
39 | .boolean()
40 | .optional()
41 | .describe(
42 | 'Append timestamped information to task details instead of full update'
43 | ),
44 | file: z.string().optional().describe('Absolute path to the tasks file'),
45 | projectRoot: z
46 | .string()
47 | .describe('The directory of the project. Must be an absolute path.'),
48 | tag: z.string().optional().describe('Tag context to operate on')
49 | }),
50 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
51 | const toolName = 'update_task';
52 | try {
53 | const resolvedTag = resolveTag({
54 | projectRoot: args.projectRoot,
55 | tag: args.tag
56 | });
57 | log.info(
58 | `Executing ${toolName} tool with args: ${JSON.stringify(args)}`
59 | );
60 |
61 | let tasksJsonPath;
62 | try {
63 | tasksJsonPath = findTasksPath(
64 | { projectRoot: args.projectRoot, file: args.file },
65 | log
66 | );
67 | log.info(`${toolName}: Resolved tasks path: ${tasksJsonPath}`);
68 | } catch (error) {
69 | log.error(`${toolName}: Error finding tasks.json: ${error.message}`);
70 | return createErrorResponse(
71 | `Failed to find tasks.json: ${error.message}`
72 | );
73 | }
74 |
75 | // 3. Call Direct Function - Include projectRoot
76 | const result = await updateTaskByIdDirect(
77 | {
78 | tasksJsonPath: tasksJsonPath,
79 | id: args.id,
80 | prompt: args.prompt,
81 | research: args.research,
82 | append: args.append,
83 | projectRoot: args.projectRoot,
84 | tag: resolvedTag
85 | },
86 | log,
87 | { session }
88 | );
89 |
90 | // 4. Handle Result
91 | log.info(
92 | `${toolName}: Direct function result: success=${result.success}`
93 | );
94 | return handleApiResult(
95 | result,
96 | log,
97 | 'Error updating task',
98 | undefined,
99 | args.projectRoot
100 | );
101 | } catch (error) {
102 | log.error(
103 | `Critical error in ${toolName} tool execute: ${error.message}`
104 | );
105 | return createErrorResponse(
106 | `Internal tool error (${toolName}): ${error.message}`
107 | );
108 | }
109 | })
110 | });
111 | }
112 |
```
--------------------------------------------------------------------------------
/src/prompts/schemas/prompt-template.schema.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "$schema": "http://json-schema.org/draft-07/schema#",
3 | "$id": "https://github.com/eyaltoledano/claude-task-master/blob/main/src/prompts/schemas/prompt-template.schema.json",
4 | "version": "1.0.0",
5 | "title": "Task Master Prompt Template",
6 | "description": "Schema for Task Master AI prompt template files",
7 | "type": "object",
8 | "required": ["id", "version", "description", "prompts"],
9 | "properties": {
10 | "id": {
11 | "type": "string",
12 | "pattern": "^[a-z0-9-]+$",
13 | "description": "Unique identifier for the prompt template"
14 | },
15 | "version": {
16 | "type": "string",
17 | "pattern": "^\\d+\\.\\d+\\.\\d+$",
18 | "description": "Semantic version of the prompt template"
19 | },
20 | "description": {
21 | "type": "string",
22 | "minLength": 1,
23 | "description": "Brief description of what this prompt does"
24 | },
25 | "metadata": {
26 | "$ref": "#/definitions/metadata"
27 | },
28 | "parameters": {
29 | "type": "object",
30 | "additionalProperties": {
31 | "$ref": "#/definitions/parameter"
32 | }
33 | },
34 | "prompts": {
35 | "type": "object",
36 | "properties": {
37 | "default": {
38 | "$ref": "#/definitions/promptVariant"
39 | }
40 | },
41 | "additionalProperties": {
42 | "$ref": "#/definitions/conditionalPromptVariant"
43 | }
44 | }
45 | },
46 | "definitions": {
47 | "parameter": {
48 | "type": "object",
49 | "required": ["type", "description"],
50 | "properties": {
51 | "type": {
52 | "type": "string",
53 | "enum": ["string", "number", "boolean", "array", "object"]
54 | },
55 | "description": {
56 | "type": "string",
57 | "minLength": 1
58 | },
59 | "required": {
60 | "type": "boolean",
61 | "default": false
62 | },
63 | "default": {
64 | "description": "Default value for optional parameters"
65 | },
66 | "enum": {
67 | "type": "array",
68 | "description": "Valid values for string parameters"
69 | },
70 | "pattern": {
71 | "type": "string",
72 | "description": "Regular expression pattern for string validation"
73 | },
74 | "minimum": {
75 | "type": "number",
76 | "description": "Minimum value for number parameters"
77 | },
78 | "maximum": {
79 | "type": "number",
80 | "description": "Maximum value for number parameters"
81 | }
82 | }
83 | },
84 | "promptVariant": {
85 | "type": "object",
86 | "required": ["system", "user"],
87 | "properties": {
88 | "system": {
89 | "type": "string",
90 | "minLength": 1
91 | },
92 | "user": {
93 | "type": "string",
94 | "minLength": 1
95 | }
96 | }
97 | },
98 | "conditionalPromptVariant": {
99 | "allOf": [
100 | { "$ref": "#/definitions/promptVariant" },
101 | {
102 | "type": "object",
103 | "properties": {
104 | "condition": {
105 | "type": "string",
106 | "description": "JavaScript expression for variant selection"
107 | }
108 | }
109 | }
110 | ]
111 | },
112 | "metadata": {
113 | "type": "object",
114 | "properties": {
115 | "author": { "type": "string" },
116 | "created": { "type": "string", "format": "date-time" },
117 | "updated": { "type": "string", "format": "date-time" },
118 | "tags": {
119 | "type": "array",
120 | "items": { "type": "string" }
121 | },
122 | "category": {
123 | "type": "string",
124 | "enum": [
125 | "task",
126 | "analysis",
127 | "research",
128 | "parsing",
129 | "update",
130 | "expansion"
131 | ]
132 | }
133 | }
134 | }
135 | }
136 | }
137 |
```
--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/generate-task-files.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * generate-task-files.js
3 | * Direct function implementation for generating task files from tasks.json
4 | */
5 |
6 | import { generateTaskFiles } from '../../../../scripts/modules/task-manager.js';
7 | import {
8 | enableSilentMode,
9 | disableSilentMode
10 | } from '../../../../scripts/modules/utils.js';
11 |
12 | /**
13 | * Direct function wrapper for generateTaskFiles with error handling.
14 | *
15 | * @param {Object} args - Command arguments containing tasksJsonPath and outputDir.
16 | * @param {string} args.tasksJsonPath - Path to the tasks.json file.
17 | * @param {string} args.outputDir - Path to the output directory.
18 | * @param {string} args.projectRoot - Project root path (for MCP/env fallback)
19 | * @param {string} args.tag - Tag for the task (optional)
20 | * @param {Object} log - Logger object.
21 | * @returns {Promise<Object>} - Result object with success status and data/error information.
22 | */
23 | export async function generateTaskFilesDirect(args, log) {
24 | // Destructure expected args
25 | const { tasksJsonPath, outputDir, projectRoot, tag } = args;
26 | try {
27 | log.info(`Generating task files with args: ${JSON.stringify(args)}`);
28 |
29 | // Check if paths were provided
30 | if (!tasksJsonPath) {
31 | const errorMessage = 'tasksJsonPath is required but was not provided.';
32 | log.error(errorMessage);
33 | return {
34 | success: false,
35 | error: { code: 'MISSING_ARGUMENT', message: errorMessage }
36 | };
37 | }
38 | if (!outputDir) {
39 | const errorMessage = 'outputDir is required but was not provided.';
40 | log.error(errorMessage);
41 | return {
42 | success: false,
43 | error: { code: 'MISSING_ARGUMENT', message: errorMessage }
44 | };
45 | }
46 |
47 | // Use the provided paths
48 | const tasksPath = tasksJsonPath;
49 | const resolvedOutputDir = outputDir;
50 |
51 | log.info(`Generating task files from ${tasksPath} to ${resolvedOutputDir}`);
52 |
53 | // Execute core generateTaskFiles function in a separate try/catch
54 | try {
55 | // Enable silent mode to prevent logs from being written to stdout
56 | enableSilentMode();
57 |
58 | // Pass projectRoot and tag so the core respects context
59 | generateTaskFiles(tasksPath, resolvedOutputDir, {
60 | projectRoot,
61 | tag,
62 | mcpLog: log
63 | });
64 |
65 | // Restore normal logging after task generation
66 | disableSilentMode();
67 | } catch (genError) {
68 | // Make sure to restore normal logging even if there's an error
69 | disableSilentMode();
70 |
71 | log.error(`Error in generateTaskFiles: ${genError.message}`);
72 | return {
73 | success: false,
74 | error: { code: 'GENERATE_FILES_ERROR', message: genError.message }
75 | };
76 | }
77 |
78 | // Return success with file paths
79 | return {
80 | success: true,
81 | data: {
82 | message: `Successfully generated task files`,
83 | tasksPath: tasksPath,
84 | outputDir: resolvedOutputDir,
85 | taskFiles:
86 | 'Individual task files have been generated in the output directory'
87 | }
88 | };
89 | } catch (error) {
90 | // Make sure to restore normal logging if an outer error occurs
91 | disableSilentMode();
92 |
93 | log.error(`Error generating task files: ${error.message}`);
94 | return {
95 | success: false,
96 | error: {
97 | code: 'GENERATE_TASKS_ERROR',
98 | message: error.message || 'Unknown error generating task files'
99 | }
100 | };
101 | }
102 | }
103 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/models.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * models.js
3 | * MCP tool for managing AI model configurations
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | handleApiResult,
9 | createErrorResponse,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { modelsDirect } from '../core/task-master-core.js';
13 |
14 | /**
15 | * Register the models tool with the MCP server
16 | * @param {Object} server - FastMCP server instance
17 | */
18 | export function registerModelsTool(server) {
19 | server.addTool({
20 | name: 'models',
21 | description:
22 | 'Get information about available AI models or set model configurations. Run without arguments to get the current model configuration and API key status for the selected model providers.',
23 | parameters: z.object({
24 | setMain: z
25 | .string()
26 | .optional()
27 | .describe(
28 | 'Set the primary model for task generation/updates. Model provider API key is required in the MCP config ENV.'
29 | ),
30 | setResearch: z
31 | .string()
32 | .optional()
33 | .describe(
34 | 'Set the model for research-backed operations. Model provider API key is required in the MCP config ENV.'
35 | ),
36 | setFallback: z
37 | .string()
38 | .optional()
39 | .describe(
40 | 'Set the model to use if the primary fails. Model provider API key is required in the MCP config ENV.'
41 | ),
42 | listAvailableModels: z
43 | .boolean()
44 | .optional()
45 | .describe(
46 | 'List all available models not currently in use. Input/output costs values are in dollars (3 is $3.00).'
47 | ),
48 | projectRoot: z
49 | .string()
50 | .describe('The directory of the project. Must be an absolute path.'),
51 | openrouter: z
52 | .boolean()
53 | .optional()
54 | .describe('Indicates the set model ID is a custom OpenRouter model.'),
55 | ollama: z
56 | .boolean()
57 | .optional()
58 | .describe('Indicates the set model ID is a custom Ollama model.'),
59 | bedrock: z
60 | .boolean()
61 | .optional()
62 | .describe('Indicates the set model ID is a custom AWS Bedrock model.'),
63 | azure: z
64 | .boolean()
65 | .optional()
66 | .describe('Indicates the set model ID is a custom Azure OpenAI model.'),
67 | vertex: z
68 | .boolean()
69 | .optional()
70 | .describe(
71 | 'Indicates the set model ID is a custom Google Vertex AI model.'
72 | ),
73 | 'openai-compatible': z
74 | .boolean()
75 | .optional()
76 | .describe(
77 | 'Indicates the set model ID is a custom OpenAI-compatible model. Requires baseURL parameter.'
78 | ),
79 | baseURL: z
80 | .string()
81 | .optional()
82 | .describe(
83 | 'Custom base URL for providers that support it (e.g., https://api.example.com/v1).'
84 | )
85 | }),
86 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
87 | try {
88 | log.info(`Starting models tool with args: ${JSON.stringify(args)}`);
89 |
90 | // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
91 | const result = await modelsDirect(
92 | { ...args, projectRoot: args.projectRoot },
93 | log,
94 | { session }
95 | );
96 |
97 | return handleApiResult(
98 | result,
99 | log,
100 | 'Error managing models',
101 | undefined,
102 | args.projectRoot
103 | );
104 | } catch (error) {
105 | log.error(`Error in models tool: ${error.message}`);
106 | return createErrorResponse(error.message);
107 | }
108 | })
109 | });
110 | }
111 |
```
--------------------------------------------------------------------------------
/tests/unit/mcp/tools/__mocks__/move-task.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * Mock for move-task module
3 | * Provides mock implementations for testing scenarios
4 | */
5 |
6 | // Mock the moveTask function from the core module
7 | const mockMoveTask = jest
8 | .fn()
9 | .mockImplementation(
10 | async (tasksPath, sourceId, destinationId, generateFiles, options) => {
11 | // Simulate successful move operation
12 | return {
13 | success: true,
14 | sourceId,
15 | destinationId,
16 | message: `Successfully moved task ${sourceId} to ${destinationId}`,
17 | ...options
18 | };
19 | }
20 | );
21 |
22 | // Mock the moveTaskDirect function
23 | const mockMoveTaskDirect = jest
24 | .fn()
25 | .mockImplementation(async (args, log, context = {}) => {
26 | // Validate required parameters
27 | if (!args.sourceId) {
28 | return {
29 | success: false,
30 | error: {
31 | message: 'Source ID is required',
32 | code: 'MISSING_SOURCE_ID'
33 | }
34 | };
35 | }
36 |
37 | if (!args.destinationId) {
38 | return {
39 | success: false,
40 | error: {
41 | message: 'Destination ID is required',
42 | code: 'MISSING_DESTINATION_ID'
43 | }
44 | };
45 | }
46 |
47 | // Simulate successful move
48 | return {
49 | success: true,
50 | data: {
51 | sourceId: args.sourceId,
52 | destinationId: args.destinationId,
53 | message: `Successfully moved task/subtask ${args.sourceId} to ${args.destinationId}`,
54 | tag: args.tag,
55 | projectRoot: args.projectRoot
56 | }
57 | };
58 | });
59 |
60 | // Mock the moveTaskCrossTagDirect function
61 | const mockMoveTaskCrossTagDirect = jest
62 | .fn()
63 | .mockImplementation(async (args, log, context = {}) => {
64 | // Validate required parameters
65 | if (!args.sourceIds) {
66 | return {
67 | success: false,
68 | error: {
69 | message: 'Source IDs are required',
70 | code: 'MISSING_SOURCE_IDS'
71 | }
72 | };
73 | }
74 |
75 | if (!args.sourceTag) {
76 | return {
77 | success: false,
78 | error: {
79 | message: 'Source tag is required for cross-tag moves',
80 | code: 'MISSING_SOURCE_TAG'
81 | }
82 | };
83 | }
84 |
85 | if (!args.targetTag) {
86 | return {
87 | success: false,
88 | error: {
89 | message: 'Target tag is required for cross-tag moves',
90 | code: 'MISSING_TARGET_TAG'
91 | }
92 | };
93 | }
94 |
95 | if (args.sourceTag === args.targetTag) {
96 | return {
97 | success: false,
98 | error: {
99 | message: `Source and target tags are the same ("${args.sourceTag}")`,
100 | code: 'SAME_SOURCE_TARGET_TAG'
101 | }
102 | };
103 | }
104 |
105 | // Simulate successful cross-tag move
106 | return {
107 | success: true,
108 | data: {
109 | sourceIds: args.sourceIds,
110 | sourceTag: args.sourceTag,
111 | targetTag: args.targetTag,
112 | message: `Successfully moved tasks ${args.sourceIds} from ${args.sourceTag} to ${args.targetTag}`,
113 | withDependencies: args.withDependencies || false,
114 | ignoreDependencies: args.ignoreDependencies || false
115 | }
116 | };
117 | });
118 |
119 | // Mock the registerMoveTaskTool function
120 | const mockRegisterMoveTaskTool = jest.fn().mockImplementation((server) => {
121 | // Simulate tool registration
122 | server.addTool({
123 | name: 'move_task',
124 | description: 'Move a task or subtask to a new position',
125 | parameters: {},
126 | execute: jest.fn()
127 | });
128 | });
129 |
130 | // Export the mock functions
131 | export {
132 | mockMoveTask,
133 | mockMoveTaskDirect,
134 | mockMoveTaskCrossTagDirect,
135 | mockRegisterMoveTaskTool
136 | };
137 |
138 | // Default export for the main moveTask function
139 | export default mockMoveTask;
140 |
```
--------------------------------------------------------------------------------
/assets/GEMINI.md:
--------------------------------------------------------------------------------
```markdown
1 | # Gemini CLI-Specific Instructions
2 |
3 | > **Note:** This file works alongside `AGENTS.md` (generic AI agent instructions). AGENTS.md contains the core Task Master commands and workflows for all AI agents. This file contains only Gemini CLI-specific features and integrations.
4 |
5 | ## MCP Configuration for Gemini CLI
6 |
7 | Configure Task Master MCP server in `~/.gemini/settings.json`:
8 |
9 | ```json
10 | {
11 | "mcpServers": {
12 | "task-master-ai": {
13 | "command": "npx",
14 | "args": ["-y", "task-master-ai"]
15 | }
16 | }
17 | }
18 | ```
19 |
20 | **Note:** API keys are configured via `task-master models --setup`, not in MCP configuration.
21 |
22 | ## Gemini CLI-Specific Features
23 |
24 | ### Session Management
25 |
26 | Built-in session commands:
27 |
28 | - `/chat` - Start new conversation while keeping context
29 | - `/checkpoint save <name>` - Save session state
30 | - `/checkpoint load <name>` - Resume saved session
31 | - `/memory show` - View loaded context
32 |
33 | Both `AGENTS.md` and `GEMINI.md` are auto-loaded on every Gemini CLI session.
34 |
35 | ### Headless Mode for Automation
36 |
37 | Non-interactive mode for scripts:
38 |
39 | ```bash
40 | # Simple text response
41 | gemini -p "What's the next task?"
42 |
43 | # JSON output for parsing
44 | gemini -p "List all pending tasks" --output-format json
45 |
46 | # Stream events for long operations
47 | gemini -p "Expand all tasks" --output-format stream-json
48 | ```
49 |
50 | ### Token Usage Monitoring
51 |
52 | ```bash
53 | # In Gemini CLI session
54 | /stats
55 |
56 | # Shows: token usage, API costs, request counts
57 | ```
58 |
59 | ### Google Search Grounding
60 |
61 | Leverage built-in Google Search as an alternative to Perplexity research mode:
62 | - Best practices research
63 | - Library documentation
64 | - Security vulnerability checks
65 | - Implementation patterns
66 |
67 | ## Important Differences from Other Agents
68 |
69 | ### No Slash Commands
70 | Gemini CLI does not support custom slash commands (unlike Claude Code). Use natural language instead.
71 |
72 | ### No Tool Allowlist
73 | Security is managed at the MCP level, not via agent configuration.
74 |
75 | ### Session Persistence
76 | Use `/checkpoint` instead of git worktrees for managing multiple work contexts.
77 |
78 | ### Configuration Files
79 | - Global: `~/.gemini/settings.json`
80 | - Project: `.gemini/settings.json`
81 | - **Not**: `.mcp.json` (that's for Claude Code)
82 |
83 | ## Recommended Model Configuration
84 |
85 | For Gemini CLI users:
86 |
87 | ```bash
88 | # Set Gemini as primary model
89 | task-master models --set-main gemini-2.0-flash-exp
90 | task-master models --set-fallback gemini-1.5-flash
91 |
92 | # Optional: Use Perplexity for research (or rely on Google Search)
93 | task-master models --set-research perplexity-llama-3.1-sonar-large-128k-online
94 | ```
95 |
96 | ## Your Role with Gemini CLI
97 |
98 | As a Gemini CLI assistant with Task Master:
99 |
100 | 1. **Use MCP tools naturally** - They integrate transparently in conversation
101 | 2. **Reference files with @** - Leverage Gemini's file inclusion
102 | 3. **Save checkpoints** - Offer to save state after significant progress
103 | 4. **Monitor usage** - Remind users about `/stats` for long sessions
104 | 5. **Use Google Search** - Leverage search grounding for research
105 |
106 | **Key Principle:** Focus on natural conversation. Task Master MCP tools work seamlessly with Gemini CLI's interface.
107 |
108 | ---
109 |
110 | *See AGENTS.md for complete Task Master commands, workflows, and best practices.*
111 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/parse-prd.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/parsePRD.js
3 | * Tool to parse PRD document and generate tasks
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | handleApiResult,
9 | withNormalizedProjectRoot,
10 | createErrorResponse,
11 | checkProgressCapability
12 | } from './utils.js';
13 | import { parsePRDDirect } from '../core/task-master-core.js';
14 | import {
15 | PRD_FILE,
16 | TASKMASTER_DOCS_DIR,
17 | TASKMASTER_TASKS_FILE
18 | } from '../../../src/constants/paths.js';
19 | import { resolveTag } from '../../../scripts/modules/utils.js';
20 |
21 | /**
22 | * Register the parse_prd tool
23 | * @param {Object} server - FastMCP server instance
24 | */
25 | export function registerParsePRDTool(server) {
26 | server.addTool({
27 | name: 'parse_prd',
28 | description: `Parse a Product Requirements Document (PRD) text file to automatically generate initial tasks. Reinitializing the project is not necessary to run this tool. It is recommended to run parse-prd after initializing the project and creating/importing a prd.txt file in the project root's ${TASKMASTER_DOCS_DIR} directory.`,
29 |
30 | parameters: z.object({
31 | input: z
32 | .string()
33 | .optional()
34 | .default(PRD_FILE)
35 | .describe('Absolute path to the PRD document file (.txt, .md, etc.)'),
36 | projectRoot: z
37 | .string()
38 | .describe('The directory of the project. Must be an absolute path.'),
39 | tag: z.string().optional().describe('Tag context to operate on'),
40 | output: z
41 | .string()
42 | .optional()
43 | .describe(
44 | `Output path for tasks.json file (default: ${TASKMASTER_TASKS_FILE})`
45 | ),
46 | numTasks: z
47 | .string()
48 | .optional()
49 | .describe(
50 | 'Approximate number of top-level tasks to generate (default: 10). As the agent, if you have enough information, ensure to enter a number of tasks that would logically scale with project complexity. Setting to 0 will allow Taskmaster to determine the appropriate number of tasks based on the complexity of the PRD. Avoid entering numbers above 50 due to context window limitations.'
51 | ),
52 | force: z
53 | .boolean()
54 | .optional()
55 | .default(false)
56 | .describe('Overwrite existing output file without prompting.'),
57 | research: z
58 | .boolean()
59 | .optional()
60 | .describe(
61 | 'Enable Taskmaster to use the research role for potentially more informed task generation. Requires appropriate API key.'
62 | ),
63 | append: z
64 | .boolean()
65 | .optional()
66 | .describe('Append generated tasks to existing file.')
67 | }),
68 | execute: withNormalizedProjectRoot(
69 | async (args, { log, session, reportProgress }) => {
70 | try {
71 | const resolvedTag = resolveTag({
72 | projectRoot: args.projectRoot,
73 | tag: args.tag
74 | });
75 | const progressCapability = checkProgressCapability(
76 | reportProgress,
77 | log
78 | );
79 | const result = await parsePRDDirect(
80 | {
81 | ...args,
82 | tag: resolvedTag
83 | },
84 | log,
85 | { session, reportProgress: progressCapability }
86 | );
87 | return handleApiResult(
88 | result,
89 | log,
90 | 'Error parsing PRD',
91 | undefined,
92 | args.projectRoot
93 | );
94 | } catch (error) {
95 | log.error(`Error in parse_prd: ${error.message}`);
96 | return createErrorResponse(`Failed to parse PRD: ${error.message}`);
97 | }
98 | }
99 | )
100 | });
101 | }
102 |
```
--------------------------------------------------------------------------------
/.github/workflows/pre-release.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Pre-Release (RC)
2 |
3 | on:
4 | workflow_dispatch: # Allows manual triggering from GitHub UI/API
5 |
6 | concurrency: pre-release-${{ github.ref_name }}
7 | jobs:
8 | rc:
9 | runs-on: ubuntu-latest
10 | # Only allow pre-releases on non-main branches
11 | if: github.ref != 'refs/heads/main'
12 | environment: extension-release
13 | steps:
14 | - uses: actions/checkout@v4
15 | with:
16 | fetch-depth: 0
17 |
18 | - uses: actions/setup-node@v4
19 | with:
20 | node-version: 20
21 | cache: "npm"
22 |
23 | - name: Cache node_modules
24 | uses: actions/cache@v4
25 | with:
26 | path: |
27 | node_modules
28 | */*/node_modules
29 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
30 | restore-keys: |
31 | ${{ runner.os }}-node-
32 |
33 | - name: Install dependencies
34 | run: npm ci
35 | timeout-minutes: 2
36 |
37 | - name: Enter RC mode (if not already in RC mode)
38 | run: |
39 | # Check if we're in pre-release mode with the "rc" tag
40 | if [ -f .changeset/pre.json ]; then
41 | MODE=$(jq -r '.mode' .changeset/pre.json 2>/dev/null || echo '')
42 | TAG=$(jq -r '.tag' .changeset/pre.json 2>/dev/null || echo '')
43 |
44 | if [ "$MODE" = "exit" ]; then
45 | echo "Pre-release mode is in 'exit' state, re-entering RC mode..."
46 | npx changeset pre enter rc
47 | elif [ "$MODE" = "pre" ] && [ "$TAG" != "rc" ]; then
48 | echo "In pre-release mode but with wrong tag ($TAG), switching to RC..."
49 | npx changeset pre exit
50 | npx changeset pre enter rc
51 | elif [ "$MODE" = "pre" ] && [ "$TAG" = "rc" ]; then
52 | echo "Already in RC pre-release mode"
53 | else
54 | echo "Unknown mode state: $MODE, entering RC mode..."
55 | npx changeset pre enter rc
56 | fi
57 | else
58 | echo "No pre.json found, entering RC mode..."
59 | npx changeset pre enter rc
60 | fi
61 |
62 | - name: Version RC packages
63 | run: npx changeset version
64 | env:
65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
67 |
68 | - name: Run format
69 | run: npm run format
70 | env:
71 | FORCE_COLOR: 1
72 |
73 | - name: Build packages
74 | run: npm run turbo:build
75 | env:
76 | NODE_ENV: production
77 | FORCE_COLOR: 1
78 | TM_PUBLIC_BASE_DOMAIN: ${{ secrets.TM_PUBLIC_BASE_DOMAIN }}
79 | TM_PUBLIC_SUPABASE_URL: ${{ secrets.TM_PUBLIC_SUPABASE_URL }}
80 | TM_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.TM_PUBLIC_SUPABASE_ANON_KEY }}
81 |
82 | - name: Create Release Candidate Pull Request or Publish Release Candidate to npm
83 | uses: changesets/action@v1
84 | with:
85 | publish: npx changeset publish
86 | env:
87 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
88 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
89 |
90 | - name: Commit & Push changes
91 | uses: actions-js/push@master
92 | with:
93 | github_token: ${{ secrets.GITHUB_TOKEN }}
94 | branch: ${{ github.ref }}
95 | message: "chore: rc version bump"
96 |
```
--------------------------------------------------------------------------------
/apps/extension/src/webview/components/ErrorBoundary.tsx:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Error Boundary Component
3 | */
4 |
5 | import React from 'react';
6 |
7 | interface ErrorBoundaryState {
8 | hasError: boolean;
9 | error?: Error;
10 | errorInfo?: React.ErrorInfo;
11 | }
12 |
13 | interface ErrorBoundaryProps {
14 | children: React.ReactNode;
15 | onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
16 | }
17 |
18 | export class ErrorBoundary extends React.Component<
19 | ErrorBoundaryProps,
20 | ErrorBoundaryState
21 | > {
22 | constructor(props: ErrorBoundaryProps) {
23 | super(props);
24 | this.state = { hasError: false };
25 | }
26 |
27 | static getDerivedStateFromError(error: Error): ErrorBoundaryState {
28 | return { hasError: true, error };
29 | }
30 |
31 | componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
32 | console.error('React Error Boundary caught:', error, errorInfo);
33 |
34 | // Log to extension
35 | if (this.props.onError) {
36 | this.props.onError(error, errorInfo);
37 | }
38 |
39 | // Send error to extension for centralized handling
40 | if (window.acquireVsCodeApi) {
41 | const vscode = window.acquireVsCodeApi();
42 | vscode.postMessage({
43 | type: 'reactError',
44 | data: {
45 | message: error.message,
46 | stack: error.stack,
47 | componentStack: errorInfo.componentStack,
48 | timestamp: Date.now()
49 | }
50 | });
51 | }
52 | }
53 |
54 | render() {
55 | if (this.state.hasError) {
56 | return (
57 | <div className="min-h-screen flex items-center justify-center bg-vscode-background">
58 | <div className="max-w-md mx-auto text-center p-6">
59 | <div className="w-16 h-16 mx-auto mb-4 text-red-400">
60 | <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
61 | <path
62 | strokeLinecap="round"
63 | strokeLinejoin="round"
64 | strokeWidth={2}
65 | d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.962-.833-2.732 0L3.732 19c-.77.833.192 2.5 1.732 2.5z"
66 | />
67 | </svg>
68 | </div>
69 | <h2 className="text-xl font-semibold text-vscode-foreground mb-2">
70 | Something went wrong
71 | </h2>
72 | <p className="text-vscode-foreground/70 mb-4">
73 | The Task Master Kanban board encountered an unexpected error.
74 | </p>
75 | <div className="space-y-2">
76 | <button
77 | onClick={() =>
78 | this.setState({
79 | hasError: false,
80 | error: undefined,
81 | errorInfo: undefined
82 | })
83 | }
84 | className="w-full px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-md transition-colors"
85 | >
86 | Try Again
87 | </button>
88 | <button
89 | onClick={() => window.location.reload()}
90 | className="w-full px-4 py-2 bg-gray-500 hover:bg-gray-600 text-white rounded-md transition-colors"
91 | >
92 | Reload Extension
93 | </button>
94 | </div>
95 | {this.state.error && (
96 | <details className="mt-4 text-left">
97 | <summary className="text-sm text-vscode-foreground/50 cursor-pointer">
98 | Error Details
99 | </summary>
100 | <pre className="mt-2 text-xs text-vscode-foreground/70 bg-vscode-input/30 p-2 rounded overflow-auto max-h-32">
101 | {this.state.error.message}
102 | {this.state.error.stack && `\n\n${this.state.error.stack}`}
103 | </pre>
104 | </details>
105 | )}
106 | </div>
107 | </div>
108 | );
109 | }
110 |
111 | return this.props.children;
112 | }
113 | }
114 |
```
--------------------------------------------------------------------------------
/packages/tm-core/src/modules/auth/auth-domain.spec.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Auth Domain tests
3 | */
4 |
5 | import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
6 | import { AuthDomain } from './auth-domain.js';
7 |
8 | describe('AuthDomain', () => {
9 | let authDomain: AuthDomain;
10 | let originalEnv: NodeJS.ProcessEnv;
11 |
12 | beforeEach(() => {
13 | // Save original environment
14 | originalEnv = { ...process.env };
15 | authDomain = new AuthDomain();
16 | });
17 |
18 | afterEach(() => {
19 | // Restore original environment
20 | process.env = originalEnv;
21 | vi.clearAllMocks();
22 | });
23 |
24 | describe('getBriefCreationUrl', () => {
25 | it('should return null if no base domain is configured', () => {
26 | // Clear environment variables
27 | delete process.env.TM_BASE_DOMAIN;
28 | delete process.env.TM_PUBLIC_BASE_DOMAIN;
29 |
30 | // Create fresh instance with cleared env
31 | const domain = new AuthDomain();
32 | const url = domain.getBriefCreationUrl();
33 |
34 | expect(url).toBeNull();
35 | });
36 |
37 | it('should return null if org slug is not available in context', () => {
38 | // Set base domain but context will have no orgSlug
39 | process.env.TM_BASE_DOMAIN = 'localhost:8080';
40 |
41 | const domain = new AuthDomain();
42 | // Mock getContext to return null (no context set)
43 | vi.spyOn(domain, 'getContext').mockReturnValue(null);
44 |
45 | const url = domain.getBriefCreationUrl();
46 |
47 | expect(url).toBeNull();
48 | });
49 |
50 | it('should construct URL with http protocol for localhost', () => {
51 | process.env.TM_BASE_DOMAIN = 'localhost:8080';
52 |
53 | // Mock getContext to return a context with orgSlug
54 | const domain = new AuthDomain();
55 | vi.spyOn(domain, 'getContext').mockReturnValue({
56 | orgSlug: 'test-org',
57 | updatedAt: new Date().toISOString()
58 | });
59 |
60 | const url = domain.getBriefCreationUrl();
61 |
62 | expect(url).toBe('http://localhost:8080/home/test-org/briefs/create');
63 | });
64 |
65 | it('should construct URL with https protocol for production domain', () => {
66 | process.env.TM_BASE_DOMAIN = 'tryhamster.com';
67 |
68 | const domain = new AuthDomain();
69 | vi.spyOn(domain, 'getContext').mockReturnValue({
70 | orgSlug: 'acme-corp',
71 | updatedAt: new Date().toISOString()
72 | });
73 |
74 | const url = domain.getBriefCreationUrl();
75 |
76 | expect(url).toBe('https://tryhamster.com/home/acme-corp/briefs/create');
77 | });
78 |
79 | it('should use existing protocol if base domain includes it', () => {
80 | process.env.TM_BASE_DOMAIN = 'https://staging.hamster.dev';
81 |
82 | const domain = new AuthDomain();
83 | vi.spyOn(domain, 'getContext').mockReturnValue({
84 | orgSlug: 'staging-org',
85 | updatedAt: new Date().toISOString()
86 | });
87 |
88 | const url = domain.getBriefCreationUrl();
89 |
90 | expect(url).toBe(
91 | 'https://staging.hamster.dev/home/staging-org/briefs/create'
92 | );
93 | });
94 |
95 | it('should prefer TM_BASE_DOMAIN over TM_PUBLIC_BASE_DOMAIN', () => {
96 | process.env.TM_BASE_DOMAIN = 'localhost:8080';
97 | process.env.TM_PUBLIC_BASE_DOMAIN = 'tryhamster.com';
98 |
99 | const domain = new AuthDomain();
100 | vi.spyOn(domain, 'getContext').mockReturnValue({
101 | orgSlug: 'my-org',
102 | updatedAt: new Date().toISOString()
103 | });
104 |
105 | const url = domain.getBriefCreationUrl();
106 |
107 | // Should use TM_BASE_DOMAIN (localhost), not TM_PUBLIC_BASE_DOMAIN
108 | expect(url).toBe('http://localhost:8080/home/my-org/briefs/create');
109 | });
110 | });
111 | });
112 |
```
--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/rename-tag.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * rename-tag.js
3 | * Direct function implementation for renaming a tag
4 | */
5 |
6 | import { renameTag } from '../../../../scripts/modules/task-manager/tag-management.js';
7 | import {
8 | enableSilentMode,
9 | disableSilentMode
10 | } from '../../../../scripts/modules/utils.js';
11 | import { createLogWrapper } from '../../tools/utils.js';
12 |
13 | /**
14 | * Direct function wrapper for renaming a tag with error handling.
15 | *
16 | * @param {Object} args - Command arguments
17 | * @param {string} args.oldName - Current name of the tag to rename
18 | * @param {string} args.newName - New name for the tag
19 | * @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
20 | * @param {string} [args.projectRoot] - Project root path
21 | * @param {Object} log - Logger object
22 | * @param {Object} context - Additional context (session)
23 | * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
24 | */
25 | export async function renameTagDirect(args, log, context = {}) {
26 | // Destructure expected args
27 | const { tasksJsonPath, oldName, newName, projectRoot } = args;
28 | const { session } = context;
29 |
30 | // Enable silent mode to prevent console logs from interfering with JSON response
31 | enableSilentMode();
32 |
33 | // Create logger wrapper using the utility
34 | const mcpLog = createLogWrapper(log);
35 |
36 | try {
37 | // Check if tasksJsonPath was provided
38 | if (!tasksJsonPath) {
39 | log.error('renameTagDirect called without tasksJsonPath');
40 | disableSilentMode();
41 | return {
42 | success: false,
43 | error: {
44 | code: 'MISSING_ARGUMENT',
45 | message: 'tasksJsonPath is required'
46 | }
47 | };
48 | }
49 |
50 | // Check required parameters
51 | if (!oldName || typeof oldName !== 'string') {
52 | log.error('Missing required parameter: oldName');
53 | disableSilentMode();
54 | return {
55 | success: false,
56 | error: {
57 | code: 'MISSING_PARAMETER',
58 | message: 'Old tag name is required and must be a string'
59 | }
60 | };
61 | }
62 |
63 | if (!newName || typeof newName !== 'string') {
64 | log.error('Missing required parameter: newName');
65 | disableSilentMode();
66 | return {
67 | success: false,
68 | error: {
69 | code: 'MISSING_PARAMETER',
70 | message: 'New tag name is required and must be a string'
71 | }
72 | };
73 | }
74 |
75 | log.info(`Renaming tag from "${oldName}" to "${newName}"`);
76 |
77 | // Call the renameTag function
78 | const result = await renameTag(
79 | tasksJsonPath,
80 | oldName,
81 | newName,
82 | {}, // options (empty for now)
83 | {
84 | session,
85 | mcpLog,
86 | projectRoot
87 | },
88 | 'json' // outputFormat - use 'json' to suppress CLI UI
89 | );
90 |
91 | // Restore normal logging
92 | disableSilentMode();
93 |
94 | return {
95 | success: true,
96 | data: {
97 | oldName: result.oldName,
98 | newName: result.newName,
99 | renamed: result.renamed,
100 | taskCount: result.taskCount,
101 | wasCurrentTag: result.wasCurrentTag,
102 | message: `Successfully renamed tag from "${result.oldName}" to "${result.newName}"`
103 | }
104 | };
105 | } catch (error) {
106 | // Make sure to restore normal logging even if there's an error
107 | disableSilentMode();
108 |
109 | log.error(`Error in renameTagDirect: ${error.message}`);
110 | return {
111 | success: false,
112 | error: {
113 | code: error.code || 'RENAME_TAG_ERROR',
114 | message: error.message
115 | }
116 | };
117 | }
118 | }
119 |
```
--------------------------------------------------------------------------------
/apps/extension/src/webview/types/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Shared types for the webview application
3 | */
4 |
5 | export interface TaskMasterTask {
6 | id: string;
7 | title: string;
8 | description: string;
9 | status: 'pending' | 'in-progress' | 'done' | 'deferred' | 'review';
10 | priority: 'high' | 'medium' | 'low';
11 | dependencies?: string[];
12 | details?: string;
13 | testStrategy?: string;
14 | subtasks?: TaskMasterTask[];
15 | complexityScore?: number;
16 | }
17 |
18 | export interface TaskUpdates {
19 | title?: string;
20 | description?: string;
21 | details?: string;
22 | priority?: TaskMasterTask['priority'];
23 | testStrategy?: string;
24 | dependencies?: string[];
25 | }
26 |
27 | export interface WebviewMessage {
28 | type: string;
29 | requestId?: string;
30 | data?: any;
31 | success?: boolean;
32 | [key: string]: any;
33 | }
34 |
35 | export interface ToastNotification {
36 | id: string;
37 | type: 'success' | 'info' | 'warning' | 'error';
38 | title: string;
39 | message: string;
40 | duration?: number;
41 | }
42 |
43 | export interface AppState {
44 | tasks: TaskMasterTask[];
45 | loading: boolean;
46 | error?: string;
47 | requestId: number;
48 | isConnected: boolean;
49 | connectionStatus: string;
50 | editingTask?: { taskId: string | null; editData?: TaskMasterTask };
51 | polling: {
52 | isActive: boolean;
53 | errorCount: number;
54 | lastUpdate?: number;
55 | isUserInteracting: boolean;
56 | isOfflineMode: boolean;
57 | reconnectAttempts: number;
58 | maxReconnectAttempts: number;
59 | lastSuccessfulConnection?: number;
60 | connectionStatus: 'online' | 'offline' | 'reconnecting';
61 | };
62 | toastNotifications: ToastNotification[];
63 | currentView: 'kanban' | 'task-details' | 'config';
64 | selectedTaskId?: string;
65 | // Tag-related state
66 | currentTag: string;
67 | availableTags: string[];
68 | }
69 |
70 | export type AppAction =
71 | | { type: 'SET_TASKS'; payload: TaskMasterTask[] }
72 | | { type: 'SET_LOADING'; payload: boolean }
73 | | { type: 'SET_ERROR'; payload: string }
74 | | { type: 'CLEAR_ERROR' }
75 | | { type: 'INCREMENT_REQUEST_ID' }
76 | | {
77 | type: 'UPDATE_TASK_STATUS';
78 | payload: { taskId: string; newStatus: TaskMasterTask['status'] };
79 | }
80 | | {
81 | type: 'UPDATE_TASK_CONTENT';
82 | payload: { taskId: string; updates: TaskUpdates };
83 | }
84 | | {
85 | type: 'SET_CONNECTION_STATUS';
86 | payload: { isConnected: boolean; status: string };
87 | }
88 | | {
89 | type: 'SET_EDITING_TASK';
90 | payload: { taskId: string | null; editData?: TaskMasterTask };
91 | }
92 | | {
93 | type: 'SET_POLLING_STATUS';
94 | payload: { isActive: boolean; errorCount?: number };
95 | }
96 | | { type: 'SET_USER_INTERACTING'; payload: boolean }
97 | | { type: 'TASKS_UPDATED_FROM_POLLING'; payload: TaskMasterTask[] }
98 | | {
99 | type: 'SET_NETWORK_STATUS';
100 | payload: {
101 | isOfflineMode: boolean;
102 | connectionStatus: 'online' | 'offline' | 'reconnecting';
103 | reconnectAttempts?: number;
104 | maxReconnectAttempts?: number;
105 | lastSuccessfulConnection?: number;
106 | };
107 | }
108 | | { type: 'LOAD_CACHED_TASKS'; payload: TaskMasterTask[] }
109 | | { type: 'ADD_TOAST'; payload: ToastNotification }
110 | | { type: 'REMOVE_TOAST'; payload: string }
111 | | { type: 'CLEAR_ALL_TOASTS' }
112 | | { type: 'NAVIGATE_TO_TASK'; payload: string }
113 | | { type: 'NAVIGATE_TO_KANBAN' }
114 | | { type: 'NAVIGATE_TO_CONFIG' }
115 | | { type: 'SET_CURRENT_TAG'; payload: string }
116 | | { type: 'SET_AVAILABLE_TAGS'; payload: string[] }
117 | | {
118 | type: 'SET_TAG_DATA';
119 | payload: { currentTag: string; availableTags: string[] };
120 | };
121 |
```
--------------------------------------------------------------------------------
/.github/workflows/extension-ci.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Extension CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - next
8 | paths:
9 | - "apps/extension/**"
10 | - ".github/workflows/extension-ci.yml"
11 | pull_request:
12 | branches:
13 | - main
14 | - next
15 | paths:
16 | - "apps/extension/**"
17 | - ".github/workflows/extension-ci.yml"
18 |
19 | permissions:
20 | contents: read
21 |
22 | jobs:
23 | setup:
24 | runs-on: ubuntu-latest
25 | steps:
26 | - uses: actions/checkout@v4
27 | with:
28 | fetch-depth: 0
29 |
30 | - uses: actions/setup-node@v4
31 | with:
32 | node-version: 20
33 |
34 | - name: Cache node_modules
35 | uses: actions/cache@v4
36 | with:
37 | path: |
38 | node_modules
39 | */*/node_modules
40 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
41 | restore-keys: |
42 | ${{ runner.os }}-node-
43 |
44 | - name: Install Monorepo Dependencies
45 | run: npm ci
46 | timeout-minutes: 5
47 |
48 | typecheck:
49 | needs: setup
50 | runs-on: ubuntu-latest
51 | steps:
52 | - uses: actions/checkout@v4
53 |
54 | - uses: actions/setup-node@v4
55 | with:
56 | node-version: 20
57 |
58 | - name: Restore node_modules
59 | uses: actions/cache@v4
60 | with:
61 | path: |
62 | node_modules
63 | */*/node_modules
64 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
65 | restore-keys: |
66 | ${{ runner.os }}-node-
67 |
68 | - name: Install if cache miss
69 | run: npm ci
70 | timeout-minutes: 3
71 |
72 | - name: Type Check Extension
73 | working-directory: apps/extension
74 | run: npm run typecheck
75 | env:
76 | FORCE_COLOR: 1
77 |
78 | build:
79 | needs: setup
80 | runs-on: ubuntu-latest
81 | steps:
82 | - uses: actions/checkout@v4
83 |
84 | - uses: actions/setup-node@v4
85 | with:
86 | node-version: 20
87 |
88 | - name: Restore node_modules
89 | uses: actions/cache@v4
90 | with:
91 | path: |
92 | node_modules
93 | */*/node_modules
94 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
95 | restore-keys: |
96 | ${{ runner.os }}-node-
97 |
98 | - name: Install if cache miss
99 | run: npm ci
100 | timeout-minutes: 3
101 |
102 | - name: Build Extension
103 | working-directory: apps/extension
104 | run: npm run build
105 | env:
106 | FORCE_COLOR: 1
107 |
108 | - name: Package Extension
109 | working-directory: apps/extension
110 | run: npm run package
111 | env:
112 | FORCE_COLOR: 1
113 |
114 | - name: Verify Package Contents
115 | working-directory: apps/extension
116 | run: |
117 | echo "Checking vsix-build contents..."
118 | ls -la vsix-build/
119 | echo "Checking dist contents..."
120 | ls -la vsix-build/dist/
121 | echo "Checking package.json exists..."
122 | test -f vsix-build/package.json
123 |
124 | - name: Create VSIX Package (Test)
125 | working-directory: apps/extension/vsix-build
126 | run: npx vsce package --no-dependencies
127 | env:
128 | FORCE_COLOR: 1
129 |
130 | - name: Upload Extension Artifact
131 | uses: actions/upload-artifact@v4
132 | with:
133 | name: extension-package
134 | path: |
135 | apps/extension/vsix-build/*.vsix
136 | apps/extension/dist/
137 | retention-days: 30
138 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/add-task.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/add-task.js
3 | * Tool to add a new task using AI
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | createErrorResponse,
9 | handleApiResult,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { addTaskDirect } from '../core/task-master-core.js';
13 | import { findTasksPath } from '../core/utils/path-utils.js';
14 | import { resolveTag } from '../../../scripts/modules/utils.js';
15 |
16 | /**
17 | * Register the addTask tool with the MCP server
18 | * @param {Object} server - FastMCP server instance
19 | */
20 | export function registerAddTaskTool(server) {
21 | server.addTool({
22 | name: 'add_task',
23 | description: 'Add a new task using AI',
24 | parameters: z.object({
25 | prompt: z
26 | .string()
27 | .optional()
28 | .describe(
29 | 'Description of the task to add (required if not using manual fields)'
30 | ),
31 | title: z
32 | .string()
33 | .optional()
34 | .describe('Task title (for manual task creation)'),
35 | description: z
36 | .string()
37 | .optional()
38 | .describe('Task description (for manual task creation)'),
39 | details: z
40 | .string()
41 | .optional()
42 | .describe('Implementation details (for manual task creation)'),
43 | testStrategy: z
44 | .string()
45 | .optional()
46 | .describe('Test strategy (for manual task creation)'),
47 | dependencies: z
48 | .string()
49 | .optional()
50 | .describe('Comma-separated list of task IDs this task depends on'),
51 | priority: z
52 | .string()
53 | .optional()
54 | .describe('Task priority (high, medium, low)'),
55 | file: z
56 | .string()
57 | .optional()
58 | .describe('Path to the tasks file (default: tasks/tasks.json)'),
59 | projectRoot: z
60 | .string()
61 | .describe('The directory of the project. Must be an absolute path.'),
62 | tag: z.string().optional().describe('Tag context to operate on'),
63 | research: z
64 | .boolean()
65 | .optional()
66 | .describe('Whether to use research capabilities for task creation')
67 | }),
68 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
69 | try {
70 | log.info(`Starting add-task with args: ${JSON.stringify(args)}`);
71 |
72 | const resolvedTag = resolveTag({
73 | projectRoot: args.projectRoot,
74 | tag: args.tag
75 | });
76 |
77 | // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
78 | let tasksJsonPath;
79 | try {
80 | tasksJsonPath = findTasksPath(
81 | { projectRoot: args.projectRoot, file: args.file },
82 | log
83 | );
84 | } catch (error) {
85 | log.error(`Error finding tasks.json: ${error.message}`);
86 | return createErrorResponse(
87 | `Failed to find tasks.json: ${error.message}`
88 | );
89 | }
90 |
91 | // Call the direct functionP
92 | const result = await addTaskDirect(
93 | {
94 | tasksJsonPath: tasksJsonPath,
95 | prompt: args.prompt,
96 | title: args.title,
97 | description: args.description,
98 | details: args.details,
99 | testStrategy: args.testStrategy,
100 | dependencies: args.dependencies,
101 | priority: args.priority,
102 | research: args.research,
103 | projectRoot: args.projectRoot,
104 | tag: resolvedTag
105 | },
106 | log,
107 | { session }
108 | );
109 |
110 | return handleApiResult(
111 | result,
112 | log,
113 | 'Error adding task',
114 | undefined,
115 | args.projectRoot
116 | );
117 | } catch (error) {
118 | log.error(`Error in add-task tool: ${error.message}`);
119 | return createErrorResponse(error.message);
120 | }
121 | })
122 | });
123 | }
124 |
```
--------------------------------------------------------------------------------
/tests/integration/profiles/gemini-init-functionality.test.js:
--------------------------------------------------------------------------------
```javascript
1 | import fs from 'fs';
2 | import path from 'path';
3 | import { geminiProfile } from '../../../src/profiles/gemini.js';
4 |
5 | describe('Gemini Profile Initialization Functionality', () => {
6 | let geminiProfileContent;
7 |
8 | beforeAll(() => {
9 | const geminiJsPath = path.join(
10 | process.cwd(),
11 | 'src',
12 | 'profiles',
13 | 'gemini.js'
14 | );
15 | geminiProfileContent = fs.readFileSync(geminiJsPath, 'utf8');
16 | });
17 |
18 | test('gemini.js has correct profile configuration', () => {
19 | // Check for explicit, non-default values in the source file
20 | expect(geminiProfileContent).toContain("name: 'gemini'");
21 | expect(geminiProfileContent).toContain("displayName: 'Gemini'");
22 | expect(geminiProfileContent).toContain("url: 'codeassist.google'");
23 | expect(geminiProfileContent).toContain(
24 | "docsUrl: 'github.com/google-gemini/gemini-cli'"
25 | );
26 | expect(geminiProfileContent).toContain("profileDir: '.gemini'");
27 | expect(geminiProfileContent).toContain("rulesDir: '.'"); // non-default
28 | expect(geminiProfileContent).toContain("mcpConfigName: 'settings.json'"); // non-default
29 | expect(geminiProfileContent).toContain('includeDefaultRules: false'); // non-default
30 | expect(geminiProfileContent).toContain("'AGENT.md': 'AGENTS.md'");
31 | expect(geminiProfileContent).toContain("'GEMINI.md': 'GEMINI.md'");
32 |
33 | // Check the final computed properties on the profile object
34 | expect(geminiProfile.profileName).toBe('gemini');
35 | expect(geminiProfile.displayName).toBe('Gemini');
36 | expect(geminiProfile.profileDir).toBe('.gemini');
37 | expect(geminiProfile.rulesDir).toBe('.');
38 | expect(geminiProfile.mcpConfig).toBe(true); // computed from mcpConfigName
39 | expect(geminiProfile.mcpConfigName).toBe('settings.json');
40 | expect(geminiProfile.mcpConfigPath).toBe('.gemini/settings.json'); // computed
41 | expect(geminiProfile.includeDefaultRules).toBe(false);
42 | expect(geminiProfile.fileMap['AGENT.md']).toBe('AGENTS.md');
43 | expect(geminiProfile.fileMap['GEMINI.md']).toBe('GEMINI.md');
44 | });
45 |
46 | test('gemini.js has no lifecycle functions', () => {
47 | // Gemini profile should not have any lifecycle functions
48 | expect(geminiProfileContent).not.toContain('function onAddRulesProfile');
49 | expect(geminiProfileContent).not.toContain('function onRemoveRulesProfile');
50 | expect(geminiProfileContent).not.toContain(
51 | 'function onPostConvertRulesProfile'
52 | );
53 | expect(geminiProfileContent).not.toContain('onAddRulesProfile:');
54 | expect(geminiProfileContent).not.toContain('onRemoveRulesProfile:');
55 | expect(geminiProfileContent).not.toContain('onPostConvertRulesProfile:');
56 | });
57 |
58 | test('gemini.js uses custom MCP config name', () => {
59 | // Gemini uses settings.json instead of mcp.json
60 | expect(geminiProfileContent).toContain("mcpConfigName: 'settings.json'");
61 | // Should not contain mcp.json as a config value (comments are OK)
62 | expect(geminiProfileContent).not.toMatch(
63 | /mcpConfigName:\s*['"]mcp\.json['"]/
64 | );
65 | });
66 |
67 | test('gemini.js has minimal implementation', () => {
68 | // Verify the profile is minimal (no extra functions or logic)
69 | const lines = geminiProfileContent.split('\n');
70 | const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
71 | // Should be around 16 lines (import, export, and profile definition)
72 | expect(nonEmptyLines.length).toBeLessThan(20);
73 | });
74 | });
75 |
```
--------------------------------------------------------------------------------
/tests/integration/profiles/rules-files-inclusion.test.js:
--------------------------------------------------------------------------------
```javascript
1 | import { jest } from '@jest/globals';
2 | import fs from 'fs';
3 | import path from 'path';
4 | import os from 'os';
5 | import { execSync } from 'child_process';
6 |
7 | describe('Rules Files Inclusion in Package', () => {
8 | // This test verifies that the required rules files are included in the final package
9 |
10 | test('package.json includes dist/** in the "files" array for bundled files', () => {
11 | // Read the package.json file
12 | const packageJsonPath = path.join(process.cwd(), 'package.json');
13 | const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
14 |
15 | // Check if dist/** is included in the files array (which contains bundled output including assets)
16 | expect(packageJson.files).toContain('dist/**');
17 | });
18 |
19 | test('source rules files exist in assets/rules directory', () => {
20 | // Verify that the actual rules files exist
21 | const rulesDir = path.join(process.cwd(), 'assets', 'rules');
22 | expect(fs.existsSync(rulesDir)).toBe(true);
23 |
24 | // Check for the 4 files that currently exist
25 | const expectedFiles = [
26 | 'dev_workflow.mdc',
27 | 'taskmaster.mdc',
28 | 'self_improve.mdc',
29 | 'cursor_rules.mdc'
30 | ];
31 |
32 | expectedFiles.forEach((file) => {
33 | const filePath = path.join(rulesDir, file);
34 | expect(fs.existsSync(filePath)).toBe(true);
35 | });
36 | });
37 |
38 | test('roo.js profile contains logic for Roo directory creation and file copying', () => {
39 | // Read the roo.js profile file
40 | const rooJsPath = path.join(process.cwd(), 'src', 'profiles', 'roo.js');
41 | const rooJsContent = fs.readFileSync(rooJsPath, 'utf8');
42 |
43 | // Check for the main handler function
44 | expect(
45 | rooJsContent.includes('onAddRulesProfile(targetDir, assetsDir)')
46 | ).toBe(true);
47 |
48 | // Check for general recursive copy of assets/roocode
49 | expect(
50 | rooJsContent.includes('copyRecursiveSync(sourceDir, targetDir)')
51 | ).toBe(true);
52 |
53 | // Check for updated path handling
54 | expect(rooJsContent.includes("path.join(assetsDir, 'roocode')")).toBe(true);
55 |
56 | // Check for .roomodes file copying logic (source and destination paths)
57 | expect(rooJsContent.includes("path.join(sourceDir, '.roomodes')")).toBe(
58 | true
59 | );
60 | expect(rooJsContent.includes("path.join(targetDir, '.roomodes')")).toBe(
61 | true
62 | );
63 |
64 | // Check for mode-specific rule file copying logic
65 | expect(rooJsContent.includes('for (const mode of ROO_MODES)')).toBe(true);
66 | expect(
67 | rooJsContent.includes(
68 | 'path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`)'
69 | )
70 | ).toBe(true);
71 | expect(
72 | rooJsContent.includes(
73 | "path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`)"
74 | )
75 | ).toBe(true);
76 |
77 | // Check for import of ROO_MODES from profiles.js
78 | expect(
79 | rooJsContent.includes(
80 | "import { ROO_MODES } from '../constants/profiles.js'"
81 | )
82 | ).toBe(true);
83 |
84 | // Verify mode variable is used in the template strings (this confirms modes are being processed)
85 | expect(rooJsContent.includes('rules-${mode}')).toBe(true);
86 | expect(rooJsContent.includes('${mode}-rules')).toBe(true);
87 | });
88 |
89 | test('source Roo files exist in assets directory', () => {
90 | // Verify that the source files for Roo integration exist
91 | expect(
92 | fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roo'))
93 | ).toBe(true);
94 | expect(
95 | fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roomodes'))
96 | ).toBe(true);
97 | });
98 | });
99 |
```
--------------------------------------------------------------------------------
/tests/unit/profiles/codex-integration.test.js:
--------------------------------------------------------------------------------
```javascript
1 | import { jest } from '@jest/globals';
2 | import fs from 'fs';
3 | import path from 'path';
4 | import os from 'os';
5 |
6 | // Mock external modules
7 | jest.mock('child_process', () => ({
8 | execSync: jest.fn()
9 | }));
10 |
11 | // Mock console methods
12 | jest.mock('console', () => ({
13 | log: jest.fn(),
14 | info: jest.fn(),
15 | warn: jest.fn(),
16 | error: jest.fn(),
17 | clear: jest.fn()
18 | }));
19 |
20 | describe('Codex Profile Integration', () => {
21 | let tempDir;
22 |
23 | beforeEach(() => {
24 | jest.clearAllMocks();
25 |
26 | // Create a temporary directory for testing
27 | tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-master-test-'));
28 |
29 | // Spy on fs methods
30 | jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
31 | jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
32 | if (filePath.toString().includes('AGENTS.md')) {
33 | return 'Sample AGENTS.md content for Codex integration';
34 | }
35 | return '{}';
36 | });
37 | jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
38 | jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
39 | });
40 |
41 | afterEach(() => {
42 | // Clean up the temporary directory
43 | try {
44 | fs.rmSync(tempDir, { recursive: true, force: true });
45 | } catch (err) {
46 | console.error(`Error cleaning up: ${err.message}`);
47 | }
48 | });
49 |
50 | // Test function that simulates the Codex profile file copying behavior
51 | function mockCreateCodexStructure() {
52 | // Codex profile copies AGENTS.md to AGENTS.md in project root (same name)
53 | const sourceContent = 'Sample AGENTS.md content for Codex integration';
54 | fs.writeFileSync(path.join(tempDir, 'AGENTS.md'), sourceContent);
55 | }
56 |
57 | test('creates AGENTS.md file in project root', () => {
58 | // Act
59 | mockCreateCodexStructure();
60 |
61 | // Assert
62 | expect(fs.writeFileSync).toHaveBeenCalledWith(
63 | path.join(tempDir, 'AGENTS.md'),
64 | 'Sample AGENTS.md content for Codex integration'
65 | );
66 | });
67 |
68 | test('does not create any profile directories', () => {
69 | // Act
70 | mockCreateCodexStructure();
71 |
72 | // Assert - Codex profile should not create any directories
73 | // Only the temp directory creation calls should exist
74 | const mkdirCalls = fs.mkdirSync.mock.calls.filter(
75 | (call) => !call[0].includes('task-master-test-')
76 | );
77 | expect(mkdirCalls).toHaveLength(0);
78 | });
79 |
80 | test('does not create MCP configuration files', () => {
81 | // Act
82 | mockCreateCodexStructure();
83 |
84 | // Assert - Codex profile should not create any MCP config files
85 | const writeFileCalls = fs.writeFileSync.mock.calls;
86 | const mcpConfigCalls = writeFileCalls.filter(
87 | (call) =>
88 | call[0].toString().includes('mcp.json') ||
89 | call[0].toString().includes('mcp_settings.json')
90 | );
91 | expect(mcpConfigCalls).toHaveLength(0);
92 | });
93 |
94 | test('only creates the target integration guide file', () => {
95 | // Act
96 | mockCreateCodexStructure();
97 |
98 | // Assert - Should only create AGENTS.md
99 | const writeFileCalls = fs.writeFileSync.mock.calls;
100 | expect(writeFileCalls).toHaveLength(1);
101 | expect(writeFileCalls[0][0]).toBe(path.join(tempDir, 'AGENTS.md'));
102 | });
103 |
104 | test('uses the same filename as source (AGENTS.md)', () => {
105 | // Act
106 | mockCreateCodexStructure();
107 |
108 | // Assert - Codex should keep the same filename unlike Claude which renames it
109 | const writeFileCalls = fs.writeFileSync.mock.calls;
110 | expect(writeFileCalls[0][0]).toContain('AGENTS.md');
111 | expect(writeFileCalls[0][0]).not.toContain('CLAUDE.md');
112 | });
113 | });
114 |
```