This is page 7 of 69. Use http://codebase.md/eyaltoledano/claude-task-master?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ └── README.md
├── .claude
│ ├── commands
│ │ └── dedupe.md
│ └── TM_COMMANDS_GUIDE.md
├── .claude-plugin
│ └── marketplace.json
├── .coderabbit.yaml
├── .cursor
│ ├── mcp.json
│ └── rules
│ ├── ai_providers.mdc
│ ├── ai_services.mdc
│ ├── architecture.mdc
│ ├── changeset.mdc
│ ├── commands.mdc
│ ├── context_gathering.mdc
│ ├── cursor_rules.mdc
│ ├── dependencies.mdc
│ ├── dev_workflow.mdc
│ ├── git_workflow.mdc
│ ├── glossary.mdc
│ ├── mcp.mdc
│ ├── new_features.mdc
│ ├── self_improve.mdc
│ ├── tags.mdc
│ ├── taskmaster.mdc
│ ├── tasks.mdc
│ ├── telemetry.mdc
│ ├── test_workflow.mdc
│ ├── tests.mdc
│ ├── ui.mdc
│ └── utilities.mdc
├── .cursorignore
├── .env.example
├── .github
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ ├── enhancements---feature-requests.md
│ │ └── feedback.md
│ ├── PULL_REQUEST_TEMPLATE
│ │ ├── bugfix.md
│ │ ├── config.yml
│ │ ├── feature.md
│ │ └── integration.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── scripts
│ │ ├── auto-close-duplicates.mjs
│ │ ├── backfill-duplicate-comments.mjs
│ │ ├── check-pre-release-mode.mjs
│ │ ├── parse-metrics.mjs
│ │ ├── release.mjs
│ │ ├── tag-extension.mjs
│ │ ├── utils.mjs
│ │ └── validate-changesets.mjs
│ └── workflows
│ ├── auto-close-duplicates.yml
│ ├── backfill-duplicate-comments.yml
│ ├── ci.yml
│ ├── claude-dedupe-issues.yml
│ ├── claude-docs-trigger.yml
│ ├── claude-docs-updater.yml
│ ├── claude-issue-triage.yml
│ ├── claude.yml
│ ├── extension-ci.yml
│ ├── extension-release.yml
│ ├── log-issue-events.yml
│ ├── pre-release.yml
│ ├── release-check.yml
│ ├── release.yml
│ ├── update-models-md.yml
│ └── weekly-metrics-discord.yml
├── .gitignore
├── .kiro
│ ├── hooks
│ │ ├── tm-code-change-task-tracker.kiro.hook
│ │ ├── tm-complexity-analyzer.kiro.hook
│ │ ├── tm-daily-standup-assistant.kiro.hook
│ │ ├── tm-git-commit-task-linker.kiro.hook
│ │ ├── tm-pr-readiness-checker.kiro.hook
│ │ ├── tm-task-dependency-auto-progression.kiro.hook
│ │ └── tm-test-success-task-completer.kiro.hook
│ ├── settings
│ │ └── mcp.json
│ └── steering
│ ├── dev_workflow.md
│ ├── kiro_rules.md
│ ├── self_improve.md
│ ├── taskmaster_hooks_workflow.md
│ └── taskmaster.md
├── .manypkg.json
├── .mcp.json
├── .npmignore
├── .nvmrc
├── .taskmaster
│ ├── CLAUDE.md
│ ├── config.json
│ ├── docs
│ │ ├── autonomous-tdd-git-workflow.md
│ │ ├── MIGRATION-ROADMAP.md
│ │ ├── prd-tm-start.txt
│ │ ├── prd.txt
│ │ ├── README.md
│ │ ├── research
│ │ │ ├── 2025-06-14_how-can-i-improve-the-scope-up-and-scope-down-comm.md
│ │ │ ├── 2025-06-14_should-i-be-using-any-specific-libraries-for-this.md
│ │ │ ├── 2025-06-14_test-save-functionality.md
│ │ │ ├── 2025-06-14_test-the-fix-for-duplicate-saves-final-test.md
│ │ │ └── 2025-08-01_do-we-need-to-add-new-commands-or-can-we-just-weap.md
│ │ ├── task-template-importing-prd.txt
│ │ ├── tdd-workflow-phase-0-spike.md
│ │ ├── tdd-workflow-phase-1-core-rails.md
│ │ ├── tdd-workflow-phase-1-orchestrator.md
│ │ ├── tdd-workflow-phase-2-pr-resumability.md
│ │ ├── tdd-workflow-phase-3-extensibility-guardrails.md
│ │ ├── test-prd.txt
│ │ └── tm-core-phase-1.txt
│ ├── reports
│ │ ├── task-complexity-report_autonomous-tdd-git-workflow.json
│ │ ├── task-complexity-report_cc-kiro-hooks.json
│ │ ├── task-complexity-report_tdd-phase-1-core-rails.json
│ │ ├── task-complexity-report_tdd-workflow-phase-0.json
│ │ ├── task-complexity-report_test-prd-tag.json
│ │ ├── task-complexity-report_tm-core-phase-1.json
│ │ ├── task-complexity-report.json
│ │ └── tm-core-complexity.json
│ ├── state.json
│ ├── tasks
│ │ ├── task_001_tm-start.txt
│ │ ├── task_002_tm-start.txt
│ │ ├── task_003_tm-start.txt
│ │ ├── task_004_tm-start.txt
│ │ ├── task_007_tm-start.txt
│ │ └── tasks.json
│ └── templates
│ ├── example_prd_rpg.md
│ └── example_prd.md
├── .vscode
│ ├── extensions.json
│ └── settings.json
├── apps
│ ├── cli
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── command-registry.ts
│ │ │ ├── commands
│ │ │ │ ├── auth.command.ts
│ │ │ │ ├── autopilot
│ │ │ │ │ ├── abort.command.ts
│ │ │ │ │ ├── commit.command.ts
│ │ │ │ │ ├── complete.command.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── next.command.ts
│ │ │ │ │ ├── resume.command.ts
│ │ │ │ │ ├── shared.ts
│ │ │ │ │ ├── start.command.ts
│ │ │ │ │ └── status.command.ts
│ │ │ │ ├── briefs.command.ts
│ │ │ │ ├── context.command.ts
│ │ │ │ ├── export.command.ts
│ │ │ │ ├── list.command.ts
│ │ │ │ ├── models
│ │ │ │ │ ├── custom-providers.ts
│ │ │ │ │ ├── fetchers.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── prompts.ts
│ │ │ │ │ ├── setup.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── next.command.ts
│ │ │ │ ├── set-status.command.ts
│ │ │ │ ├── show.command.ts
│ │ │ │ ├── start.command.ts
│ │ │ │ └── tags.command.ts
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ └── model-management.ts
│ │ │ ├── types
│ │ │ │ └── tag-management.d.ts
│ │ │ ├── ui
│ │ │ │ ├── components
│ │ │ │ │ ├── cardBox.component.ts
│ │ │ │ │ ├── dashboard.component.ts
│ │ │ │ │ ├── header.component.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── next-task.component.ts
│ │ │ │ │ ├── suggested-steps.component.ts
│ │ │ │ │ └── task-detail.component.ts
│ │ │ │ ├── display
│ │ │ │ │ ├── messages.ts
│ │ │ │ │ └── tables.ts
│ │ │ │ ├── formatters
│ │ │ │ │ ├── complexity-formatters.ts
│ │ │ │ │ ├── dependency-formatters.ts
│ │ │ │ │ ├── priority-formatters.ts
│ │ │ │ │ ├── status-formatters.spec.ts
│ │ │ │ │ └── status-formatters.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── layout
│ │ │ │ ├── helpers.spec.ts
│ │ │ │ └── helpers.ts
│ │ │ └── utils
│ │ │ ├── auth-helpers.ts
│ │ │ ├── auto-update.ts
│ │ │ ├── brief-selection.ts
│ │ │ ├── display-helpers.ts
│ │ │ ├── error-handler.ts
│ │ │ ├── index.ts
│ │ │ ├── project-root.ts
│ │ │ ├── task-status.ts
│ │ │ ├── ui.spec.ts
│ │ │ └── ui.ts
│ │ ├── tests
│ │ │ ├── integration
│ │ │ │ └── commands
│ │ │ │ └── autopilot
│ │ │ │ └── workflow.test.ts
│ │ │ └── unit
│ │ │ ├── commands
│ │ │ │ ├── autopilot
│ │ │ │ │ └── shared.test.ts
│ │ │ │ ├── list.command.spec.ts
│ │ │ │ └── show.command.spec.ts
│ │ │ └── ui
│ │ │ └── dashboard.component.spec.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ ├── docs
│ │ ├── archive
│ │ │ ├── ai-client-utils-example.mdx
│ │ │ ├── ai-development-workflow.mdx
│ │ │ ├── command-reference.mdx
│ │ │ ├── configuration.mdx
│ │ │ ├── cursor-setup.mdx
│ │ │ ├── examples.mdx
│ │ │ └── Installation.mdx
│ │ ├── best-practices
│ │ │ ├── advanced-tasks.mdx
│ │ │ ├── configuration-advanced.mdx
│ │ │ └── index.mdx
│ │ ├── capabilities
│ │ │ ├── cli-root-commands.mdx
│ │ │ ├── index.mdx
│ │ │ ├── mcp.mdx
│ │ │ ├── rpg-method.mdx
│ │ │ └── task-structure.mdx
│ │ ├── CHANGELOG.md
│ │ ├── command-reference.mdx
│ │ ├── configuration.mdx
│ │ ├── docs.json
│ │ ├── favicon.svg
│ │ ├── getting-started
│ │ │ ├── api-keys.mdx
│ │ │ ├── contribute.mdx
│ │ │ ├── faq.mdx
│ │ │ └── quick-start
│ │ │ ├── configuration-quick.mdx
│ │ │ ├── execute-quick.mdx
│ │ │ ├── installation.mdx
│ │ │ ├── moving-forward.mdx
│ │ │ ├── prd-quick.mdx
│ │ │ ├── quick-start.mdx
│ │ │ ├── requirements.mdx
│ │ │ ├── rules-quick.mdx
│ │ │ └── tasks-quick.mdx
│ │ ├── introduction.mdx
│ │ ├── licensing.md
│ │ ├── logo
│ │ │ ├── dark.svg
│ │ │ ├── light.svg
│ │ │ └── task-master-logo.png
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── style.css
│ │ ├── tdd-workflow
│ │ │ ├── ai-agent-integration.mdx
│ │ │ └── quickstart.mdx
│ │ ├── vercel.json
│ │ └── whats-new.mdx
│ ├── extension
│ │ ├── .vscodeignore
│ │ ├── assets
│ │ │ ├── banner.png
│ │ │ ├── icon-dark.svg
│ │ │ ├── icon-light.svg
│ │ │ ├── icon.png
│ │ │ ├── screenshots
│ │ │ │ ├── kanban-board.png
│ │ │ │ └── task-details.png
│ │ │ └── sidebar-icon.svg
│ │ ├── CHANGELOG.md
│ │ ├── components.json
│ │ ├── docs
│ │ │ ├── extension-CI-setup.md
│ │ │ └── extension-development-guide.md
│ │ ├── esbuild.js
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── package.mjs
│ │ ├── package.publish.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── components
│ │ │ │ ├── ConfigView.tsx
│ │ │ │ ├── constants.ts
│ │ │ │ ├── TaskDetails
│ │ │ │ │ ├── AIActionsSection.tsx
│ │ │ │ │ ├── DetailsSection.tsx
│ │ │ │ │ ├── PriorityBadge.tsx
│ │ │ │ │ ├── SubtasksSection.tsx
│ │ │ │ │ ├── TaskMetadataSidebar.tsx
│ │ │ │ │ └── useTaskDetails.ts
│ │ │ │ ├── TaskDetailsView.tsx
│ │ │ │ ├── TaskMasterLogo.tsx
│ │ │ │ └── ui
│ │ │ │ ├── badge.tsx
│ │ │ │ ├── breadcrumb.tsx
│ │ │ │ ├── button.tsx
│ │ │ │ ├── card.tsx
│ │ │ │ ├── collapsible.tsx
│ │ │ │ ├── CollapsibleSection.tsx
│ │ │ │ ├── dropdown-menu.tsx
│ │ │ │ ├── label.tsx
│ │ │ │ ├── scroll-area.tsx
│ │ │ │ ├── separator.tsx
│ │ │ │ ├── shadcn-io
│ │ │ │ │ └── kanban
│ │ │ │ │ └── index.tsx
│ │ │ │ └── textarea.tsx
│ │ │ ├── extension.ts
│ │ │ ├── index.ts
│ │ │ ├── lib
│ │ │ │ └── utils.ts
│ │ │ ├── services
│ │ │ │ ├── config-service.ts
│ │ │ │ ├── error-handler.ts
│ │ │ │ ├── notification-preferences.ts
│ │ │ │ ├── polling-service.ts
│ │ │ │ ├── polling-strategies.ts
│ │ │ │ ├── sidebar-webview-manager.ts
│ │ │ │ ├── task-repository.ts
│ │ │ │ ├── terminal-manager.ts
│ │ │ │ └── webview-manager.ts
│ │ │ ├── test
│ │ │ │ └── extension.test.ts
│ │ │ ├── utils
│ │ │ │ ├── configManager.ts
│ │ │ │ ├── connectionManager.ts
│ │ │ │ ├── errorHandler.ts
│ │ │ │ ├── event-emitter.ts
│ │ │ │ ├── logger.ts
│ │ │ │ ├── mcpClient.ts
│ │ │ │ ├── notificationPreferences.ts
│ │ │ │ └── task-master-api
│ │ │ │ ├── cache
│ │ │ │ │ └── cache-manager.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── mcp-client.ts
│ │ │ │ ├── transformers
│ │ │ │ │ └── task-transformer.ts
│ │ │ │ └── types
│ │ │ │ └── index.ts
│ │ │ └── webview
│ │ │ ├── App.tsx
│ │ │ ├── components
│ │ │ │ ├── AppContent.tsx
│ │ │ │ ├── EmptyState.tsx
│ │ │ │ ├── ErrorBoundary.tsx
│ │ │ │ ├── PollingStatus.tsx
│ │ │ │ ├── PriorityBadge.tsx
│ │ │ │ ├── SidebarView.tsx
│ │ │ │ ├── TagDropdown.tsx
│ │ │ │ ├── TaskCard.tsx
│ │ │ │ ├── TaskEditModal.tsx
│ │ │ │ ├── TaskMasterKanban.tsx
│ │ │ │ ├── ToastContainer.tsx
│ │ │ │ └── ToastNotification.tsx
│ │ │ ├── constants
│ │ │ │ └── index.ts
│ │ │ ├── contexts
│ │ │ │ └── VSCodeContext.tsx
│ │ │ ├── hooks
│ │ │ │ ├── useTaskQueries.ts
│ │ │ │ ├── useVSCodeMessages.ts
│ │ │ │ └── useWebviewHeight.ts
│ │ │ ├── index.css
│ │ │ ├── index.tsx
│ │ │ ├── providers
│ │ │ │ └── QueryProvider.tsx
│ │ │ ├── reducers
│ │ │ │ └── appReducer.ts
│ │ │ ├── sidebar.tsx
│ │ │ ├── types
│ │ │ │ └── index.ts
│ │ │ └── utils
│ │ │ ├── logger.ts
│ │ │ └── toast.ts
│ │ └── tsconfig.json
│ └── mcp
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── shared
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ └── tools
│ │ ├── autopilot
│ │ │ ├── abort.tool.ts
│ │ │ ├── commit.tool.ts
│ │ │ ├── complete.tool.ts
│ │ │ ├── finalize.tool.ts
│ │ │ ├── index.ts
│ │ │ ├── next.tool.ts
│ │ │ ├── resume.tool.ts
│ │ │ ├── start.tool.ts
│ │ │ └── status.tool.ts
│ │ ├── README-ZOD-V3.md
│ │ └── tasks
│ │ ├── get-task.tool.ts
│ │ ├── get-tasks.tool.ts
│ │ └── index.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── assets
│ ├── .windsurfrules
│ ├── AGENTS.md
│ ├── claude
│ │ └── TM_COMMANDS_GUIDE.md
│ ├── config.json
│ ├── env.example
│ ├── example_prd_rpg.txt
│ ├── example_prd.txt
│ ├── GEMINI.md
│ ├── gitignore
│ ├── kiro-hooks
│ │ ├── tm-code-change-task-tracker.kiro.hook
│ │ ├── tm-complexity-analyzer.kiro.hook
│ │ ├── tm-daily-standup-assistant.kiro.hook
│ │ ├── tm-git-commit-task-linker.kiro.hook
│ │ ├── tm-pr-readiness-checker.kiro.hook
│ │ ├── tm-task-dependency-auto-progression.kiro.hook
│ │ └── tm-test-success-task-completer.kiro.hook
│ ├── roocode
│ │ ├── .roo
│ │ │ ├── rules-architect
│ │ │ │ └── architect-rules
│ │ │ ├── rules-ask
│ │ │ │ └── ask-rules
│ │ │ ├── rules-code
│ │ │ │ └── code-rules
│ │ │ ├── rules-debug
│ │ │ │ └── debug-rules
│ │ │ ├── rules-orchestrator
│ │ │ │ └── orchestrator-rules
│ │ │ └── rules-test
│ │ │ └── test-rules
│ │ └── .roomodes
│ ├── rules
│ │ ├── cursor_rules.mdc
│ │ ├── dev_workflow.mdc
│ │ ├── self_improve.mdc
│ │ ├── taskmaster_hooks_workflow.mdc
│ │ └── taskmaster.mdc
│ └── scripts_README.md
├── bin
│ └── task-master.js
├── biome.json
├── CHANGELOG.md
├── CLAUDE_CODE_PLUGIN.md
├── CLAUDE.md
├── context
│ ├── chats
│ │ ├── add-task-dependencies-1.md
│ │ └── max-min-tokens.txt.md
│ ├── fastmcp-core.txt
│ ├── fastmcp-docs.txt
│ ├── MCP_INTEGRATION.md
│ ├── mcp-js-sdk-docs.txt
│ ├── mcp-protocol-repo.txt
│ ├── mcp-protocol-schema-03262025.json
│ └── mcp-protocol-spec.txt
├── CONTRIBUTING.md
├── docs
│ ├── claude-code-integration.md
│ ├── CLI-COMMANDER-PATTERN.md
│ ├── command-reference.md
│ ├── configuration.md
│ ├── contributor-docs
│ │ ├── testing-roo-integration.md
│ │ └── worktree-setup.md
│ ├── cross-tag-task-movement.md
│ ├── examples
│ │ ├── claude-code-usage.md
│ │ └── codex-cli-usage.md
│ ├── examples.md
│ ├── licensing.md
│ ├── mcp-provider-guide.md
│ ├── mcp-provider.md
│ ├── migration-guide.md
│ ├── models.md
│ ├── providers
│ │ ├── codex-cli.md
│ │ └── gemini-cli.md
│ ├── README.md
│ ├── scripts
│ │ └── models-json-to-markdown.js
│ ├── task-structure.md
│ └── tutorial.md
├── images
│ ├── hamster-hiring.png
│ └── logo.png
├── index.js
├── jest.config.js
├── jest.resolver.cjs
├── LICENSE
├── llms-install.md
├── mcp-server
│ ├── server.js
│ └── src
│ ├── core
│ │ ├── __tests__
│ │ │ └── context-manager.test.js
│ │ ├── context-manager.js
│ │ ├── direct-functions
│ │ │ ├── add-dependency.js
│ │ │ ├── add-subtask.js
│ │ │ ├── add-tag.js
│ │ │ ├── add-task.js
│ │ │ ├── analyze-task-complexity.js
│ │ │ ├── cache-stats.js
│ │ │ ├── clear-subtasks.js
│ │ │ ├── complexity-report.js
│ │ │ ├── copy-tag.js
│ │ │ ├── create-tag-from-branch.js
│ │ │ ├── delete-tag.js
│ │ │ ├── expand-all-tasks.js
│ │ │ ├── expand-task.js
│ │ │ ├── fix-dependencies.js
│ │ │ ├── generate-task-files.js
│ │ │ ├── initialize-project.js
│ │ │ ├── list-tags.js
│ │ │ ├── models.js
│ │ │ ├── move-task-cross-tag.js
│ │ │ ├── move-task.js
│ │ │ ├── next-task.js
│ │ │ ├── parse-prd.js
│ │ │ ├── remove-dependency.js
│ │ │ ├── remove-subtask.js
│ │ │ ├── remove-task.js
│ │ │ ├── rename-tag.js
│ │ │ ├── research.js
│ │ │ ├── response-language.js
│ │ │ ├── rules.js
│ │ │ ├── scope-down.js
│ │ │ ├── scope-up.js
│ │ │ ├── set-task-status.js
│ │ │ ├── update-subtask-by-id.js
│ │ │ ├── update-task-by-id.js
│ │ │ ├── update-tasks.js
│ │ │ ├── use-tag.js
│ │ │ └── validate-dependencies.js
│ │ ├── task-master-core.js
│ │ └── utils
│ │ ├── env-utils.js
│ │ └── path-utils.js
│ ├── custom-sdk
│ │ ├── errors.js
│ │ ├── index.js
│ │ ├── json-extractor.js
│ │ ├── language-model.js
│ │ ├── message-converter.js
│ │ └── schema-converter.js
│ ├── index.js
│ ├── logger.js
│ ├── providers
│ │ └── mcp-provider.js
│ └── tools
│ ├── add-dependency.js
│ ├── add-subtask.js
│ ├── add-tag.js
│ ├── add-task.js
│ ├── analyze.js
│ ├── clear-subtasks.js
│ ├── complexity-report.js
│ ├── copy-tag.js
│ ├── delete-tag.js
│ ├── expand-all.js
│ ├── expand-task.js
│ ├── fix-dependencies.js
│ ├── generate.js
│ ├── get-operation-status.js
│ ├── index.js
│ ├── initialize-project.js
│ ├── list-tags.js
│ ├── models.js
│ ├── move-task.js
│ ├── next-task.js
│ ├── parse-prd.js
│ ├── README-ZOD-V3.md
│ ├── remove-dependency.js
│ ├── remove-subtask.js
│ ├── remove-task.js
│ ├── rename-tag.js
│ ├── research.js
│ ├── response-language.js
│ ├── rules.js
│ ├── scope-down.js
│ ├── scope-up.js
│ ├── set-task-status.js
│ ├── tool-registry.js
│ ├── update-subtask.js
│ ├── update-task.js
│ ├── update.js
│ ├── use-tag.js
│ ├── utils.js
│ └── validate-dependencies.js
├── mcp-test.js
├── output.json
├── package-lock.json
├── package.json
├── packages
│ ├── ai-sdk-provider-grok-cli
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── errors.test.ts
│ │ │ ├── errors.ts
│ │ │ ├── grok-cli-language-model.ts
│ │ │ ├── grok-cli-provider.test.ts
│ │ │ ├── grok-cli-provider.ts
│ │ │ ├── index.ts
│ │ │ ├── json-extractor.test.ts
│ │ │ ├── json-extractor.ts
│ │ │ ├── message-converter.test.ts
│ │ │ ├── message-converter.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── build-config
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src
│ │ │ └── tsdown.base.ts
│ │ └── tsconfig.json
│ ├── claude-code-plugin
│ │ ├── .claude-plugin
│ │ │ └── plugin.json
│ │ ├── .gitignore
│ │ ├── agents
│ │ │ ├── task-checker.md
│ │ │ ├── task-executor.md
│ │ │ └── task-orchestrator.md
│ │ ├── CHANGELOG.md
│ │ ├── commands
│ │ │ ├── add-dependency.md
│ │ │ ├── add-subtask.md
│ │ │ ├── add-task.md
│ │ │ ├── analyze-complexity.md
│ │ │ ├── analyze-project.md
│ │ │ ├── auto-implement-tasks.md
│ │ │ ├── command-pipeline.md
│ │ │ ├── complexity-report.md
│ │ │ ├── convert-task-to-subtask.md
│ │ │ ├── expand-all-tasks.md
│ │ │ ├── expand-task.md
│ │ │ ├── fix-dependencies.md
│ │ │ ├── generate-tasks.md
│ │ │ ├── help.md
│ │ │ ├── init-project-quick.md
│ │ │ ├── init-project.md
│ │ │ ├── install-taskmaster.md
│ │ │ ├── learn.md
│ │ │ ├── list-tasks-by-status.md
│ │ │ ├── list-tasks-with-subtasks.md
│ │ │ ├── list-tasks.md
│ │ │ ├── next-task.md
│ │ │ ├── parse-prd-with-research.md
│ │ │ ├── parse-prd.md
│ │ │ ├── project-status.md
│ │ │ ├── quick-install-taskmaster.md
│ │ │ ├── remove-all-subtasks.md
│ │ │ ├── remove-dependency.md
│ │ │ ├── remove-subtask.md
│ │ │ ├── remove-subtasks.md
│ │ │ ├── remove-task.md
│ │ │ ├── setup-models.md
│ │ │ ├── show-task.md
│ │ │ ├── smart-workflow.md
│ │ │ ├── sync-readme.md
│ │ │ ├── tm-main.md
│ │ │ ├── to-cancelled.md
│ │ │ ├── to-deferred.md
│ │ │ ├── to-done.md
│ │ │ ├── to-in-progress.md
│ │ │ ├── to-pending.md
│ │ │ ├── to-review.md
│ │ │ ├── update-single-task.md
│ │ │ ├── update-task.md
│ │ │ ├── update-tasks-from-id.md
│ │ │ ├── validate-dependencies.md
│ │ │ └── view-models.md
│ │ ├── mcp.json
│ │ └── package.json
│ ├── tm-bridge
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── add-tag-bridge.ts
│ │ │ ├── bridge-types.ts
│ │ │ ├── bridge-utils.ts
│ │ │ ├── expand-bridge.ts
│ │ │ ├── index.ts
│ │ │ ├── tags-bridge.ts
│ │ │ ├── update-bridge.ts
│ │ │ └── use-tag-bridge.ts
│ │ └── tsconfig.json
│ └── tm-core
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── docs
│ │ └── listTasks-architecture.md
│ ├── package.json
│ ├── POC-STATUS.md
│ ├── README.md
│ ├── src
│ │ ├── common
│ │ │ ├── constants
│ │ │ │ ├── index.ts
│ │ │ │ ├── paths.ts
│ │ │ │ └── providers.ts
│ │ │ ├── errors
│ │ │ │ ├── index.ts
│ │ │ │ └── task-master-error.ts
│ │ │ ├── interfaces
│ │ │ │ ├── configuration.interface.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── storage.interface.ts
│ │ │ ├── logger
│ │ │ │ ├── factory.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── logger.spec.ts
│ │ │ │ └── logger.ts
│ │ │ ├── mappers
│ │ │ │ ├── TaskMapper.test.ts
│ │ │ │ └── TaskMapper.ts
│ │ │ ├── types
│ │ │ │ ├── database.types.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── legacy.ts
│ │ │ │ └── repository-types.ts
│ │ │ └── utils
│ │ │ ├── git-utils.ts
│ │ │ ├── id-generator.ts
│ │ │ ├── index.ts
│ │ │ ├── path-helpers.ts
│ │ │ ├── path-normalizer.spec.ts
│ │ │ ├── path-normalizer.ts
│ │ │ ├── project-root-finder.spec.ts
│ │ │ ├── project-root-finder.ts
│ │ │ ├── run-id-generator.spec.ts
│ │ │ └── run-id-generator.ts
│ │ ├── index.ts
│ │ ├── modules
│ │ │ ├── ai
│ │ │ │ ├── index.ts
│ │ │ │ ├── interfaces
│ │ │ │ │ └── ai-provider.interface.ts
│ │ │ │ └── providers
│ │ │ │ ├── base-provider.ts
│ │ │ │ └── index.ts
│ │ │ ├── auth
│ │ │ │ ├── auth-domain.spec.ts
│ │ │ │ ├── auth-domain.ts
│ │ │ │ ├── config.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── managers
│ │ │ │ │ ├── auth-manager.spec.ts
│ │ │ │ │ └── auth-manager.ts
│ │ │ │ ├── services
│ │ │ │ │ ├── context-store.ts
│ │ │ │ │ ├── oauth-service.ts
│ │ │ │ │ ├── organization.service.ts
│ │ │ │ │ ├── supabase-session-storage.spec.ts
│ │ │ │ │ └── supabase-session-storage.ts
│ │ │ │ └── types.ts
│ │ │ ├── briefs
│ │ │ │ ├── briefs-domain.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── services
│ │ │ │ │ └── brief-service.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils
│ │ │ │ └── url-parser.ts
│ │ │ ├── commands
│ │ │ │ └── index.ts
│ │ │ ├── config
│ │ │ │ ├── config-domain.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── managers
│ │ │ │ │ ├── config-manager.spec.ts
│ │ │ │ │ └── config-manager.ts
│ │ │ │ └── services
│ │ │ │ ├── config-loader.service.spec.ts
│ │ │ │ ├── config-loader.service.ts
│ │ │ │ ├── config-merger.service.spec.ts
│ │ │ │ ├── config-merger.service.ts
│ │ │ │ ├── config-persistence.service.spec.ts
│ │ │ │ ├── config-persistence.service.ts
│ │ │ │ ├── environment-config-provider.service.spec.ts
│ │ │ │ ├── environment-config-provider.service.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── runtime-state-manager.service.spec.ts
│ │ │ │ └── runtime-state-manager.service.ts
│ │ │ ├── dependencies
│ │ │ │ └── index.ts
│ │ │ ├── execution
│ │ │ │ ├── executors
│ │ │ │ │ ├── base-executor.ts
│ │ │ │ │ ├── claude-executor.ts
│ │ │ │ │ └── executor-factory.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── services
│ │ │ │ │ └── executor-service.ts
│ │ │ │ └── types.ts
│ │ │ ├── git
│ │ │ │ ├── adapters
│ │ │ │ │ ├── git-adapter.test.ts
│ │ │ │ │ └── git-adapter.ts
│ │ │ │ ├── git-domain.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── services
│ │ │ │ ├── branch-name-generator.spec.ts
│ │ │ │ ├── branch-name-generator.ts
│ │ │ │ ├── commit-message-generator.test.ts
│ │ │ │ ├── commit-message-generator.ts
│ │ │ │ ├── scope-detector.test.ts
│ │ │ │ ├── scope-detector.ts
│ │ │ │ ├── template-engine.test.ts
│ │ │ │ └── template-engine.ts
│ │ │ ├── integration
│ │ │ │ ├── clients
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── supabase-client.ts
│ │ │ │ ├── integration-domain.ts
│ │ │ │ └── services
│ │ │ │ ├── export.service.ts
│ │ │ │ ├── task-expansion.service.ts
│ │ │ │ └── task-retrieval.service.ts
│ │ │ ├── reports
│ │ │ │ ├── index.ts
│ │ │ │ ├── managers
│ │ │ │ │ └── complexity-report-manager.ts
│ │ │ │ └── types.ts
│ │ │ ├── storage
│ │ │ │ ├── adapters
│ │ │ │ │ ├── activity-logger.ts
│ │ │ │ │ ├── api-storage.ts
│ │ │ │ │ └── file-storage
│ │ │ │ │ ├── file-operations.ts
│ │ │ │ │ ├── file-storage.ts
│ │ │ │ │ ├── format-handler.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── path-resolver.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── services
│ │ │ │ │ └── storage-factory.ts
│ │ │ │ └── utils
│ │ │ │ └── api-client.ts
│ │ │ ├── tasks
│ │ │ │ ├── entities
│ │ │ │ │ └── task.entity.ts
│ │ │ │ ├── parser
│ │ │ │ │ └── index.ts
│ │ │ │ ├── repositories
│ │ │ │ │ ├── supabase
│ │ │ │ │ │ ├── dependency-fetcher.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── supabase-repository.ts
│ │ │ │ │ └── task-repository.interface.ts
│ │ │ │ ├── services
│ │ │ │ │ ├── preflight-checker.service.ts
│ │ │ │ │ ├── tag.service.ts
│ │ │ │ │ ├── task-execution-service.ts
│ │ │ │ │ ├── task-loader.service.ts
│ │ │ │ │ └── task-service.ts
│ │ │ │ └── tasks-domain.ts
│ │ │ ├── ui
│ │ │ │ └── index.ts
│ │ │ └── workflow
│ │ │ ├── managers
│ │ │ │ ├── workflow-state-manager.spec.ts
│ │ │ │ └── workflow-state-manager.ts
│ │ │ ├── orchestrators
│ │ │ │ ├── workflow-orchestrator.test.ts
│ │ │ │ └── workflow-orchestrator.ts
│ │ │ ├── services
│ │ │ │ ├── test-result-validator.test.ts
│ │ │ │ ├── test-result-validator.ts
│ │ │ │ ├── test-result-validator.types.ts
│ │ │ │ ├── workflow-activity-logger.ts
│ │ │ │ └── workflow.service.ts
│ │ │ ├── types.ts
│ │ │ └── workflow-domain.ts
│ │ ├── subpath-exports.test.ts
│ │ ├── tm-core.ts
│ │ └── utils
│ │ └── time.utils.ts
│ ├── tests
│ │ ├── auth
│ │ │ └── auth-refresh.test.ts
│ │ ├── integration
│ │ │ ├── auth-token-refresh.test.ts
│ │ │ ├── list-tasks.test.ts
│ │ │ └── storage
│ │ │ └── activity-logger.test.ts
│ │ ├── mocks
│ │ │ └── mock-provider.ts
│ │ ├── setup.ts
│ │ └── unit
│ │ ├── base-provider.test.ts
│ │ ├── executor.test.ts
│ │ └── smoke.test.ts
│ ├── tsconfig.json
│ └── vitest.config.ts
├── README-task-master.md
├── README.md
├── scripts
│ ├── create-worktree.sh
│ ├── dev.js
│ ├── init.js
│ ├── list-worktrees.sh
│ ├── modules
│ │ ├── ai-services-unified.js
│ │ ├── bridge-utils.js
│ │ ├── commands.js
│ │ ├── config-manager.js
│ │ ├── dependency-manager.js
│ │ ├── index.js
│ │ ├── prompt-manager.js
│ │ ├── supported-models.json
│ │ ├── sync-readme.js
│ │ ├── task-manager
│ │ │ ├── add-subtask.js
│ │ │ ├── add-task.js
│ │ │ ├── analyze-task-complexity.js
│ │ │ ├── clear-subtasks.js
│ │ │ ├── expand-all-tasks.js
│ │ │ ├── expand-task.js
│ │ │ ├── find-next-task.js
│ │ │ ├── generate-task-files.js
│ │ │ ├── is-task-dependent.js
│ │ │ ├── list-tasks.js
│ │ │ ├── migrate.js
│ │ │ ├── models.js
│ │ │ ├── move-task.js
│ │ │ ├── parse-prd
│ │ │ │ ├── index.js
│ │ │ │ ├── parse-prd-config.js
│ │ │ │ ├── parse-prd-helpers.js
│ │ │ │ ├── parse-prd-non-streaming.js
│ │ │ │ ├── parse-prd-streaming.js
│ │ │ │ └── parse-prd.js
│ │ │ ├── remove-subtask.js
│ │ │ ├── remove-task.js
│ │ │ ├── research.js
│ │ │ ├── response-language.js
│ │ │ ├── scope-adjustment.js
│ │ │ ├── set-task-status.js
│ │ │ ├── tag-management.js
│ │ │ ├── task-exists.js
│ │ │ ├── update-single-task-status.js
│ │ │ ├── update-subtask-by-id.js
│ │ │ ├── update-task-by-id.js
│ │ │ └── update-tasks.js
│ │ ├── task-manager.js
│ │ ├── ui.js
│ │ ├── update-config-tokens.js
│ │ ├── utils
│ │ │ ├── contextGatherer.js
│ │ │ ├── fuzzyTaskSearch.js
│ │ │ └── git-utils.js
│ │ └── utils.js
│ ├── task-complexity-report.json
│ ├── test-claude-errors.js
│ └── test-claude.js
├── sonar-project.properties
├── src
│ ├── ai-providers
│ │ ├── anthropic.js
│ │ ├── azure.js
│ │ ├── base-provider.js
│ │ ├── bedrock.js
│ │ ├── claude-code.js
│ │ ├── codex-cli.js
│ │ ├── gemini-cli.js
│ │ ├── google-vertex.js
│ │ ├── google.js
│ │ ├── grok-cli.js
│ │ ├── groq.js
│ │ ├── index.js
│ │ ├── lmstudio.js
│ │ ├── ollama.js
│ │ ├── openai-compatible.js
│ │ ├── openai.js
│ │ ├── openrouter.js
│ │ ├── perplexity.js
│ │ ├── xai.js
│ │ ├── zai-coding.js
│ │ └── zai.js
│ ├── constants
│ │ ├── commands.js
│ │ ├── paths.js
│ │ ├── profiles.js
│ │ ├── rules-actions.js
│ │ ├── task-priority.js
│ │ └── task-status.js
│ ├── profiles
│ │ ├── amp.js
│ │ ├── base-profile.js
│ │ ├── claude.js
│ │ ├── cline.js
│ │ ├── codex.js
│ │ ├── cursor.js
│ │ ├── gemini.js
│ │ ├── index.js
│ │ ├── kilo.js
│ │ ├── kiro.js
│ │ ├── opencode.js
│ │ ├── roo.js
│ │ ├── trae.js
│ │ ├── vscode.js
│ │ ├── windsurf.js
│ │ └── zed.js
│ ├── progress
│ │ ├── base-progress-tracker.js
│ │ ├── cli-progress-factory.js
│ │ ├── parse-prd-tracker.js
│ │ ├── progress-tracker-builder.js
│ │ └── tracker-ui.js
│ ├── prompts
│ │ ├── add-task.json
│ │ ├── analyze-complexity.json
│ │ ├── expand-task.json
│ │ ├── parse-prd.json
│ │ ├── README.md
│ │ ├── research.json
│ │ ├── schemas
│ │ │ ├── parameter.schema.json
│ │ │ ├── prompt-template.schema.json
│ │ │ ├── README.md
│ │ │ └── variant.schema.json
│ │ ├── update-subtask.json
│ │ ├── update-task.json
│ │ └── update-tasks.json
│ ├── provider-registry
│ │ └── index.js
│ ├── schemas
│ │ ├── add-task.js
│ │ ├── analyze-complexity.js
│ │ ├── base-schemas.js
│ │ ├── expand-task.js
│ │ ├── parse-prd.js
│ │ ├── registry.js
│ │ ├── update-subtask.js
│ │ ├── update-task.js
│ │ └── update-tasks.js
│ ├── task-master.js
│ ├── ui
│ │ ├── confirm.js
│ │ ├── indicators.js
│ │ └── parse-prd.js
│ └── utils
│ ├── asset-resolver.js
│ ├── create-mcp-config.js
│ ├── format.js
│ ├── getVersion.js
│ ├── logger-utils.js
│ ├── manage-gitignore.js
│ ├── path-utils.js
│ ├── profiles.js
│ ├── rule-transformer.js
│ ├── stream-parser.js
│ └── timeout-manager.js
├── test-clean-tags.js
├── test-config-manager.js
├── test-prd.txt
├── test-tag-functions.js
├── test-version-check-full.js
├── test-version-check.js
├── tests
│ ├── e2e
│ │ ├── e2e_helpers.sh
│ │ ├── parse_llm_output.cjs
│ │ ├── run_e2e.sh
│ │ ├── run_fallback_verification.sh
│ │ └── test_llm_analysis.sh
│ ├── fixtures
│ │ ├── .taskmasterconfig
│ │ ├── sample-claude-response.js
│ │ ├── sample-prd.txt
│ │ └── sample-tasks.js
│ ├── helpers
│ │ └── tool-counts.js
│ ├── integration
│ │ ├── claude-code-error-handling.test.js
│ │ ├── claude-code-optional.test.js
│ │ ├── cli
│ │ │ ├── commands.test.js
│ │ │ ├── complex-cross-tag-scenarios.test.js
│ │ │ └── move-cross-tag.test.js
│ │ ├── manage-gitignore.test.js
│ │ ├── mcp-server
│ │ │ └── direct-functions.test.js
│ │ ├── move-task-cross-tag.integration.test.js
│ │ ├── move-task-simple.integration.test.js
│ │ ├── profiles
│ │ │ ├── amp-init-functionality.test.js
│ │ │ ├── claude-init-functionality.test.js
│ │ │ ├── cline-init-functionality.test.js
│ │ │ ├── codex-init-functionality.test.js
│ │ │ ├── cursor-init-functionality.test.js
│ │ │ ├── gemini-init-functionality.test.js
│ │ │ ├── opencode-init-functionality.test.js
│ │ │ ├── roo-files-inclusion.test.js
│ │ │ ├── roo-init-functionality.test.js
│ │ │ ├── rules-files-inclusion.test.js
│ │ │ ├── trae-init-functionality.test.js
│ │ │ ├── vscode-init-functionality.test.js
│ │ │ └── windsurf-init-functionality.test.js
│ │ └── providers
│ │ └── temperature-support.test.js
│ ├── manual
│ │ ├── progress
│ │ │ ├── parse-prd-analysis.js
│ │ │ ├── test-parse-prd.js
│ │ │ └── TESTING_GUIDE.md
│ │ └── prompts
│ │ ├── prompt-test.js
│ │ └── README.md
│ ├── README.md
│ ├── setup.js
│ └── unit
│ ├── ai-providers
│ │ ├── base-provider.test.js
│ │ ├── claude-code.test.js
│ │ ├── codex-cli.test.js
│ │ ├── gemini-cli.test.js
│ │ ├── lmstudio.test.js
│ │ ├── mcp-components.test.js
│ │ ├── openai-compatible.test.js
│ │ ├── openai.test.js
│ │ ├── provider-registry.test.js
│ │ ├── zai-coding.test.js
│ │ ├── zai-provider.test.js
│ │ ├── zai-schema-introspection.test.js
│ │ └── zai.test.js
│ ├── ai-services-unified.test.js
│ ├── commands.test.js
│ ├── config-manager.test.js
│ ├── config-manager.test.mjs
│ ├── dependency-manager.test.js
│ ├── init.test.js
│ ├── initialize-project.test.js
│ ├── kebab-case-validation.test.js
│ ├── manage-gitignore.test.js
│ ├── mcp
│ │ └── tools
│ │ ├── __mocks__
│ │ │ └── move-task.js
│ │ ├── add-task.test.js
│ │ ├── analyze-complexity.test.js
│ │ ├── expand-all.test.js
│ │ ├── get-tasks.test.js
│ │ ├── initialize-project.test.js
│ │ ├── move-task-cross-tag-options.test.js
│ │ ├── move-task-cross-tag.test.js
│ │ ├── remove-task.test.js
│ │ └── tool-registration.test.js
│ ├── mcp-providers
│ │ ├── mcp-components.test.js
│ │ └── mcp-provider.test.js
│ ├── parse-prd.test.js
│ ├── profiles
│ │ ├── amp-integration.test.js
│ │ ├── claude-integration.test.js
│ │ ├── cline-integration.test.js
│ │ ├── codex-integration.test.js
│ │ ├── cursor-integration.test.js
│ │ ├── gemini-integration.test.js
│ │ ├── kilo-integration.test.js
│ │ ├── kiro-integration.test.js
│ │ ├── mcp-config-validation.test.js
│ │ ├── opencode-integration.test.js
│ │ ├── profile-safety-check.test.js
│ │ ├── roo-integration.test.js
│ │ ├── rule-transformer-cline.test.js
│ │ ├── rule-transformer-cursor.test.js
│ │ ├── rule-transformer-gemini.test.js
│ │ ├── rule-transformer-kilo.test.js
│ │ ├── rule-transformer-kiro.test.js
│ │ ├── rule-transformer-opencode.test.js
│ │ ├── rule-transformer-roo.test.js
│ │ ├── rule-transformer-trae.test.js
│ │ ├── rule-transformer-vscode.test.js
│ │ ├── rule-transformer-windsurf.test.js
│ │ ├── rule-transformer-zed.test.js
│ │ ├── rule-transformer.test.js
│ │ ├── selective-profile-removal.test.js
│ │ ├── subdirectory-support.test.js
│ │ ├── trae-integration.test.js
│ │ ├── vscode-integration.test.js
│ │ ├── windsurf-integration.test.js
│ │ └── zed-integration.test.js
│ ├── progress
│ │ └── base-progress-tracker.test.js
│ ├── prompt-manager.test.js
│ ├── prompts
│ │ ├── expand-task-prompt.test.js
│ │ └── prompt-migration.test.js
│ ├── scripts
│ │ └── modules
│ │ ├── commands
│ │ │ ├── move-cross-tag.test.js
│ │ │ └── README.md
│ │ ├── dependency-manager
│ │ │ ├── circular-dependencies.test.js
│ │ │ ├── cross-tag-dependencies.test.js
│ │ │ └── fix-dependencies-command.test.js
│ │ ├── task-manager
│ │ │ ├── add-subtask.test.js
│ │ │ ├── add-task.test.js
│ │ │ ├── analyze-task-complexity.test.js
│ │ │ ├── clear-subtasks.test.js
│ │ │ ├── complexity-report-tag-isolation.test.js
│ │ │ ├── expand-all-tasks.test.js
│ │ │ ├── expand-task.test.js
│ │ │ ├── find-next-task.test.js
│ │ │ ├── generate-task-files.test.js
│ │ │ ├── list-tasks.test.js
│ │ │ ├── models-baseurl.test.js
│ │ │ ├── move-task-cross-tag.test.js
│ │ │ ├── move-task.test.js
│ │ │ ├── parse-prd-schema.test.js
│ │ │ ├── parse-prd.test.js
│ │ │ ├── remove-subtask.test.js
│ │ │ ├── remove-task.test.js
│ │ │ ├── research.test.js
│ │ │ ├── scope-adjustment.test.js
│ │ │ ├── set-task-status.test.js
│ │ │ ├── setup.js
│ │ │ ├── update-single-task-status.test.js
│ │ │ ├── update-subtask-by-id.test.js
│ │ │ ├── update-task-by-id.test.js
│ │ │ └── update-tasks.test.js
│ │ ├── ui
│ │ │ └── cross-tag-error-display.test.js
│ │ └── utils-tag-aware-paths.test.js
│ ├── task-finder.test.js
│ ├── task-manager
│ │ ├── clear-subtasks.test.js
│ │ ├── move-task.test.js
│ │ ├── tag-boundary.test.js
│ │ └── tag-management.test.js
│ ├── task-master.test.js
│ ├── ui
│ │ └── indicators.test.js
│ ├── ui.test.js
│ ├── utils-strip-ansi.test.js
│ └── utils.test.js
├── tsconfig.json
├── tsdown.config.ts
├── turbo.json
└── update-task-migration-plan.md
```
# Files
--------------------------------------------------------------------------------
/scripts/modules/task-manager/parse-prd/parse-prd-non-streaming.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * Non-streaming handler for PRD parsing
3 | */
4 |
5 | import ora from 'ora';
6 | import { generateObjectService } from '../../ai-services-unified.js';
7 | import { LoggingConfig, prdResponseSchema } from './parse-prd-config.js';
8 | import { estimateTokens } from './parse-prd-helpers.js';
9 |
10 | /**
11 | * Handle non-streaming AI service call
12 | * @param {Object} config - Configuration object
13 | * @param {Object} prompts - System and user prompts
14 | * @returns {Promise<Object>} Generated tasks and telemetry
15 | */
16 | export async function handleNonStreamingService(config, prompts) {
17 | const logger = new LoggingConfig(config.mcpLog, config.reportProgress);
18 | const { systemPrompt, userPrompt } = prompts;
19 | const estimatedInputTokens = estimateTokens(systemPrompt + userPrompt);
20 |
21 | // Initialize spinner for CLI
22 | let spinner = null;
23 | if (config.outputFormat === 'text' && !config.isMCP) {
24 | spinner = ora('Parsing PRD and generating tasks...\n').start();
25 | }
26 |
27 | try {
28 | // Call AI service
29 | logger.report(
30 | `Calling AI service to generate tasks from PRD${config.research ? ' with research-backed analysis' : ''}...`,
31 | 'info'
32 | );
33 |
34 | const aiServiceResponse = await generateObjectService({
35 | role: config.research ? 'research' : 'main',
36 | session: config.session,
37 | projectRoot: config.projectRoot,
38 | schema: prdResponseSchema,
39 | objectName: 'tasks_data',
40 | systemPrompt,
41 | prompt: userPrompt,
42 | commandName: 'parse-prd',
43 | outputType: config.isMCP ? 'mcp' : 'cli'
44 | });
45 |
46 | // Extract generated data
47 | let generatedData = null;
48 | if (aiServiceResponse?.mainResult) {
49 | if (
50 | typeof aiServiceResponse.mainResult === 'object' &&
51 | aiServiceResponse.mainResult !== null &&
52 | 'tasks' in aiServiceResponse.mainResult
53 | ) {
54 | generatedData = aiServiceResponse.mainResult;
55 | } else if (
56 | typeof aiServiceResponse.mainResult.object === 'object' &&
57 | aiServiceResponse.mainResult.object !== null &&
58 | 'tasks' in aiServiceResponse.mainResult.object
59 | ) {
60 | generatedData = aiServiceResponse.mainResult.object;
61 | }
62 | }
63 |
64 | if (!generatedData || !Array.isArray(generatedData.tasks)) {
65 | throw new Error(
66 | 'AI service returned unexpected data structure after validation.'
67 | );
68 | }
69 |
70 | if (spinner) {
71 | spinner.succeed('Tasks generated successfully!');
72 | }
73 |
74 | return {
75 | parsedTasks: generatedData.tasks,
76 | aiServiceResponse,
77 | estimatedInputTokens
78 | };
79 | } catch (error) {
80 | if (spinner) {
81 | spinner.fail(`Error parsing PRD: ${error.message}`);
82 | }
83 | throw error;
84 | }
85 | }
86 |
```
--------------------------------------------------------------------------------
/.github/scripts/utils.mjs:
--------------------------------------------------------------------------------
```
1 | #!/usr/bin/env node
2 | import { spawnSync } from 'node:child_process';
3 | import { readFileSync } from 'node:fs';
4 | import { join, dirname, resolve } from 'node:path';
5 |
6 | // Find the root directory by looking for package.json with task-master-ai
7 | export function findRootDir(startDir) {
8 | let currentDir = resolve(startDir);
9 | while (currentDir !== '/') {
10 | const pkgPath = join(currentDir, 'package.json');
11 | try {
12 | const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
13 | if (pkg.name === 'task-master-ai' || pkg.repository) {
14 | return currentDir;
15 | }
16 | } catch {}
17 | currentDir = dirname(currentDir);
18 | }
19 | throw new Error('Could not find root directory');
20 | }
21 |
22 | // Run a command with proper error handling
23 | export function runCommand(command, args = [], options = {}) {
24 | console.log(`Running: ${command} ${args.join(' ')}`);
25 | const result = spawnSync(command, args, {
26 | encoding: 'utf8',
27 | stdio: 'inherit',
28 | ...options
29 | });
30 |
31 | if (result.status !== 0) {
32 | console.error(`Command failed with exit code ${result.status}`);
33 | process.exit(result.status);
34 | }
35 |
36 | return result;
37 | }
38 |
39 | // Get package version from a package.json file
40 | export function getPackageVersion(packagePath) {
41 | try {
42 | const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
43 | return pkg.version;
44 | } catch (error) {
45 | console.error(
46 | `Failed to read package version from ${packagePath}:`,
47 | error.message
48 | );
49 | process.exit(1);
50 | }
51 | }
52 |
53 | // Check if a git tag exists on remote
54 | export function tagExistsOnRemote(tag, remote = 'origin') {
55 | const result = spawnSync('git', ['ls-remote', remote, tag], {
56 | encoding: 'utf8'
57 | });
58 |
59 | return result.status === 0 && result.stdout.trim() !== '';
60 | }
61 |
62 | // Create and push a git tag if it doesn't exist
63 | export function createAndPushTag(tag, remote = 'origin') {
64 | // Check if tag already exists
65 | if (tagExistsOnRemote(tag, remote)) {
66 | console.log(`Tag ${tag} already exists on remote, skipping`);
67 | return false;
68 | }
69 |
70 | console.log(`Creating new tag: ${tag}`);
71 |
72 | // Create the tag locally
73 | const tagResult = spawnSync('git', ['tag', tag]);
74 | if (tagResult.status !== 0) {
75 | console.error('Failed to create tag:', tagResult.error || tagResult.stderr);
76 | process.exit(1);
77 | }
78 |
79 | // Push the tag to remote
80 | const pushResult = spawnSync('git', ['push', remote, tag]);
81 | if (pushResult.status !== 0) {
82 | console.error('Failed to push tag:', pushResult.error || pushResult.stderr);
83 | process.exit(1);
84 | }
85 |
86 | console.log(`✅ Successfully created and pushed tag: ${tag}`);
87 | return true;
88 | }
89 |
```
--------------------------------------------------------------------------------
/packages/claude-code-plugin/commands/complexity-report.md:
--------------------------------------------------------------------------------
```markdown
1 | Display the task complexity analysis report.
2 |
3 | Arguments: $ARGUMENTS
4 |
5 | View the detailed complexity analysis generated by analyze-complexity command.
6 |
7 | ## Viewing Complexity Report
8 |
9 | Shows comprehensive task complexity analysis with actionable insights.
10 |
11 | ## Execution
12 |
13 | ```bash
14 | task-master complexity-report [--file=<path>]
15 | ```
16 |
17 | ## Report Location
18 |
19 | Default: `.taskmaster/reports/complexity-analysis.md`
20 | Custom: Specify with --file parameter
21 |
22 | ## Report Contents
23 |
24 | ### 1. **Executive Summary**
25 | ```
26 | Complexity Analysis Summary
27 | ━━━━━━━━━━━━━━━━━━━━━━━━
28 | Analysis Date: 2024-01-15
29 | Tasks Analyzed: 32
30 | High Complexity: 5 (16%)
31 | Medium Complexity: 12 (37%)
32 | Low Complexity: 15 (47%)
33 |
34 | Critical Findings:
35 | - 5 tasks need immediate expansion
36 | - 3 tasks have high technical risk
37 | - 2 tasks block critical path
38 | ```
39 |
40 | ### 2. **Detailed Task Analysis**
41 | For each complex task:
42 | - Complexity score breakdown
43 | - Contributing factors
44 | - Specific risks identified
45 | - Expansion recommendations
46 | - Similar completed tasks
47 |
48 | ### 3. **Risk Matrix**
49 | Visual representation:
50 | ```
51 | Risk vs Complexity Matrix
52 | ━━━━━━━━━━━━━━━━━━━━━━━
53 | High Risk | #5(9) #12(8) | #23(6)
54 | Med Risk | #34(7) | #45(5) #67(5)
55 | Low Risk | #78(8) | [15 tasks]
56 | | High Complex | Med Complex
57 | ```
58 |
59 | ### 4. **Recommendations**
60 |
61 | **Immediate Actions:**
62 | 1. Expand task #5 - Critical path + high complexity
63 | 2. Expand task #12 - High risk + dependencies
64 | 3. Review task #34 - Consider splitting
65 |
66 | **Sprint Planning:**
67 | - Don't schedule multiple high-complexity tasks together
68 | - Ensure expertise available for complex tasks
69 | - Build in buffer time for unknowns
70 |
71 | ## Interactive Features
72 |
73 | When viewing report:
74 | 1. **Quick Actions**
75 | - Press 'e' to expand a task
76 | - Press 'd' for task details
77 | - Press 'r' to refresh analysis
78 |
79 | 2. **Filtering**
80 | - View by complexity level
81 | - Filter by risk factors
82 | - Show only actionable items
83 |
84 | 3. **Export Options**
85 | - Markdown format
86 | - CSV for spreadsheets
87 | - JSON for tools
88 |
89 | ## Report Intelligence
90 |
91 | - Compares with historical data
92 | - Shows complexity trends
93 | - Identifies patterns
94 | - Suggests process improvements
95 |
96 | ## Integration
97 |
98 | Use report for:
99 | - Sprint planning sessions
100 | - Resource allocation
101 | - Risk assessment
102 | - Team discussions
103 | - Client updates
104 |
105 | ## Example Usage
106 |
107 | ```
108 | /taskmaster:complexity-report
109 | → Opens latest analysis
110 |
111 | /taskmaster:complexity-report --file=archived/2024-01-01.md
112 | → View historical analysis
113 |
114 | After viewing:
115 | /taskmaster:expand 5
116 | → Expand high-complexity task
117 | ```
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/initialize-project.js:
--------------------------------------------------------------------------------
```javascript
1 | import { z } from 'zod';
2 | import {
3 | createErrorResponse,
4 | handleApiResult,
5 | withNormalizedProjectRoot
6 | } from './utils.js';
7 | import { initializeProjectDirect } from '../core/task-master-core.js';
8 | import { RULE_PROFILES } from '../../../src/constants/profiles.js';
9 |
10 | export function registerInitializeProjectTool(server) {
11 | server.addTool({
12 | name: 'initialize_project',
13 | description:
14 | 'Initializes a new Task Master project structure by calling the core initialization logic. Creates necessary folders and configuration files for Task Master in the current directory.',
15 | parameters: z.object({
16 | skipInstall: z
17 | .boolean()
18 | .optional()
19 | .default(false)
20 | .describe(
21 | 'Skip installing dependencies automatically. Never do this unless you are sure the project is already installed.'
22 | ),
23 | addAliases: z
24 | .boolean()
25 | .optional()
26 | .default(true)
27 | .describe('Add shell aliases (tm, taskmaster) to shell config file.'),
28 | initGit: z
29 | .boolean()
30 | .optional()
31 | .default(true)
32 | .describe('Initialize Git repository in project root.'),
33 | storeTasksInGit: z
34 | .boolean()
35 | .optional()
36 | .default(true)
37 | .describe('Store tasks in Git (tasks.json and tasks/ directory).'),
38 | yes: z
39 | .boolean()
40 | .optional()
41 | .default(true)
42 | .describe(
43 | 'Skip prompts and use default values. Always set to true for MCP tools.'
44 | ),
45 | projectRoot: z
46 | .string()
47 | .describe(
48 | 'The root directory for the project. ALWAYS SET THIS TO THE PROJECT ROOT DIRECTORY. IF NOT SET, THE TOOL WILL NOT WORK.'
49 | ),
50 | rules: z
51 | .array(z.enum(RULE_PROFILES))
52 | .optional()
53 | .describe(
54 | `List of rule profiles to include at initialization. If omitted, defaults to Cursor profile only. Available options: ${RULE_PROFILES.join(', ')}`
55 | )
56 | }),
57 | execute: withNormalizedProjectRoot(async (args, context) => {
58 | const { log } = context;
59 | const session = context.session;
60 |
61 | try {
62 | log.info(
63 | `Executing initialize_project tool with args: ${JSON.stringify(args)}`
64 | );
65 |
66 | const result = await initializeProjectDirect(args, log, { session });
67 |
68 | return handleApiResult(
69 | result,
70 | log,
71 | 'Initialization failed',
72 | undefined,
73 | args.projectRoot
74 | );
75 | } catch (error) {
76 | const errorMessage = `Project initialization tool failed: ${error.message || 'Unknown error'}`;
77 | log.error(errorMessage, error);
78 | return createErrorResponse(errorMessage, { details: error.stack });
79 | }
80 | })
81 | });
82 | }
83 |
```
--------------------------------------------------------------------------------
/packages/tm-core/src/modules/auth/types.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Authentication types and interfaces
3 | */
4 |
5 | export interface AuthCredentials {
6 | token: string;
7 | refreshToken?: string;
8 | userId: string;
9 | email?: string;
10 | expiresAt?: string | number;
11 | tokenType?: 'standard';
12 | savedAt: string;
13 | selectedContext?: UserContext;
14 | }
15 |
16 | export interface UserContext {
17 | orgId?: string;
18 | orgName?: string;
19 | orgSlug?: string;
20 | briefId?: string;
21 | briefName?: string;
22 | briefStatus?: string;
23 | briefUpdatedAt?: string;
24 | updatedAt: string;
25 | }
26 |
27 | /**
28 | * User context with a guaranteed briefId
29 | */
30 | export type UserContextWithBrief = UserContext & { briefId: string };
31 |
32 | export interface OAuthFlowOptions {
33 | /** Callback to open the browser with the auth URL. If not provided, browser won't be opened */
34 | openBrowser?: (url: string) => Promise<void>;
35 | /** Timeout for the OAuth flow in milliseconds. Default: 300000 (5 minutes) */
36 | timeout?: number;
37 | /** Callback to be invoked with the authorization URL */
38 | onAuthUrl?: (url: string) => void;
39 | /** Callback to be invoked when waiting for authentication */
40 | onWaitingForAuth?: () => void;
41 | /** Callback to be invoked on successful authentication */
42 | onSuccess?: (credentials: AuthCredentials) => void;
43 | /** Callback to be invoked on authentication error */
44 | onError?: (error: AuthenticationError) => void;
45 | }
46 |
47 | export interface AuthConfig {
48 | baseUrl: string;
49 | configDir: string;
50 | configFile: string;
51 | }
52 |
53 | export interface CliData {
54 | callback: string;
55 | state: string;
56 | name: string;
57 | version: string;
58 | device?: string;
59 | user?: string;
60 | platform?: string;
61 | timestamp?: number;
62 | }
63 |
64 | /**
65 | * Authentication error codes
66 | */
67 | export type AuthErrorCode =
68 | | 'AUTH_TIMEOUT'
69 | | 'AUTH_EXPIRED'
70 | | 'OAUTH_FAILED'
71 | | 'OAUTH_ERROR'
72 | | 'OAUTH_CANCELED'
73 | | 'URL_GENERATION_FAILED'
74 | | 'INVALID_STATE'
75 | | 'NO_TOKEN'
76 | | 'TOKEN_EXCHANGE_FAILED'
77 | | 'INVALID_CREDENTIALS'
78 | | 'NO_REFRESH_TOKEN'
79 | | 'NOT_AUTHENTICATED'
80 | | 'NETWORK_ERROR'
81 | | 'CONFIG_MISSING'
82 | | 'SAVE_FAILED'
83 | | 'CLEAR_FAILED'
84 | | 'STORAGE_ERROR'
85 | | 'NOT_SUPPORTED'
86 | | 'REFRESH_FAILED'
87 | | 'INVALID_RESPONSE'
88 | | 'PKCE_INIT_FAILED'
89 | | 'PKCE_FAILED'
90 | | 'CODE_EXCHANGE_FAILED'
91 | | 'SESSION_SET_FAILED'
92 | | 'CODE_AUTH_FAILED'
93 | | 'INVALID_CODE';
94 |
95 | /**
96 | * Authentication error class
97 | */
98 | export class AuthenticationError extends Error {
99 | constructor(
100 | message: string,
101 | public code: AuthErrorCode,
102 | public cause?: unknown
103 | ) {
104 | super(message);
105 | this.name = 'AuthenticationError';
106 | if (cause && cause instanceof Error) {
107 | this.stack = `${this.stack}\nCaused by: ${cause.stack}`;
108 | }
109 | }
110 | }
111 |
```
--------------------------------------------------------------------------------
/packages/claude-code-plugin/commands/update-single-task.md:
--------------------------------------------------------------------------------
```markdown
1 | Update a single specific task with new information.
2 |
3 | Arguments: $ARGUMENTS
4 |
5 | Parse task ID and update details.
6 |
7 | ## Single Task Update
8 |
9 | Precisely update one task with AI assistance to maintain consistency.
10 |
11 | ## Argument Parsing
12 |
13 | Natural language updates:
14 | - "5: add caching requirement"
15 | - "update 5 to include error handling"
16 | - "task 5 needs rate limiting"
17 | - "5 change priority to high"
18 |
19 | ## Execution
20 |
21 | ```bash
22 | task-master update-task --id=<id> --prompt="<context>"
23 | ```
24 |
25 | ## Update Types
26 |
27 | ### 1. **Content Updates**
28 | - Enhance description
29 | - Add requirements
30 | - Clarify details
31 | - Update acceptance criteria
32 |
33 | ### 2. **Metadata Updates**
34 | - Change priority
35 | - Adjust time estimates
36 | - Update complexity
37 | - Modify dependencies
38 |
39 | ### 3. **Strategic Updates**
40 | - Revise approach
41 | - Change test strategy
42 | - Update implementation notes
43 | - Adjust subtask needs
44 |
45 | ## AI-Powered Updates
46 |
47 | The AI:
48 | 1. **Understands Context**
49 | - Reads current task state
50 | - Identifies update intent
51 | - Maintains consistency
52 | - Preserves important info
53 |
54 | 2. **Applies Changes**
55 | - Updates relevant fields
56 | - Keeps style consistent
57 | - Adds without removing
58 | - Enhances clarity
59 |
60 | 3. **Validates Results**
61 | - Checks coherence
62 | - Verifies completeness
63 | - Maintains relationships
64 | - Suggests related updates
65 |
66 | ## Example Updates
67 |
68 | ```
69 | /taskmaster:update/single 5: add rate limiting
70 | → Updating Task #5: "Implement API endpoints"
71 |
72 | Current: Basic CRUD endpoints
73 | Adding: Rate limiting requirements
74 |
75 | Updated sections:
76 | ✓ Description: Added rate limiting mention
77 | ✓ Details: Added specific limits (100/min)
78 | ✓ Test Strategy: Added rate limit tests
79 | ✓ Complexity: Increased from 5 to 6
80 | ✓ Time Estimate: Increased by 2 hours
81 |
82 | Suggestion: Also update task #6 (API Gateway) for consistency?
83 | ```
84 |
85 | ## Smart Features
86 |
87 | 1. **Incremental Updates**
88 | - Adds without overwriting
89 | - Preserves work history
90 | - Tracks what changed
91 | - Shows diff view
92 |
93 | 2. **Consistency Checks**
94 | - Related task alignment
95 | - Subtask compatibility
96 | - Dependency validity
97 | - Timeline impact
98 |
99 | 3. **Update History**
100 | - Timestamp changes
101 | - Track who/what updated
102 | - Reason for update
103 | - Previous versions
104 |
105 | ## Field-Specific Updates
106 |
107 | Quick syntax for specific fields:
108 | - "5 priority:high" → Update priority only
109 | - "5 add-time:4h" → Add to time estimate
110 | - "5 status:review" → Change status
111 | - "5 depends:3,4" → Add dependencies
112 |
113 | ## Post-Update
114 |
115 | - Show updated task
116 | - Highlight changes
117 | - Check related tasks
118 | - Update suggestions
119 | - Timeline adjustments
```
--------------------------------------------------------------------------------
/packages/tm-core/CHANGELOG.md:
--------------------------------------------------------------------------------
```markdown
1 | # Changelog
2 |
3 | ## null
4 |
5 | ## null
6 |
7 | ## null
8 |
9 | ## null
10 |
11 | ## null
12 |
13 | ## null
14 |
15 | ## null
16 |
17 | ## 0.26.1
18 |
19 | All notable changes to the @task-master/tm-core package will be documented in this file.
20 |
21 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
22 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
23 |
24 | ## [Unreleased]
25 |
26 | ### Added
27 |
28 | - Initial package structure and configuration
29 | - TypeScript support with strict mode
30 | - Dual ESM/CJS build system with tsup
31 | - Jest testing framework with TypeScript support
32 | - ESLint and Prettier for code quality
33 | - Modular architecture with barrel exports
34 | - Placeholder implementations for all modules
35 | - Comprehensive documentation and README
36 |
37 | ### Development Infrastructure
38 |
39 | - tsup configuration for dual format builds
40 | - Jest configuration with ESM support
41 | - ESLint configuration with TypeScript rules
42 | - Prettier configuration for consistent formatting
43 | - Complete package.json with all required fields
44 | - TypeScript configuration with strict settings
45 | - .gitignore for development files
46 |
47 | ### Package Structure
48 |
49 | - `src/types/` - TypeScript type definitions (placeholder)
50 | - `src/providers/` - AI provider implementations (placeholder)
51 | - `src/storage/` - Storage layer abstractions (placeholder)
52 | - `src/parser/` - Task parsing utilities (placeholder)
53 | - `src/utils/` - Common utility functions (placeholder)
54 | - `src/errors/` - Custom error classes (placeholder)
55 | - `tests/` - Test directories and setup
56 |
57 | ## [1.0.0] - TBD
58 |
59 | ### Planned Features
60 |
61 | - Complete TypeScript type system
62 | - AI provider implementations
63 | - Storage adapters
64 | - Task parsing capabilities
65 | - Comprehensive utility functions
66 | - Custom error handling
67 | - Full test coverage
68 | - Complete documentation
69 |
70 | ---
71 |
72 | ## Release Notes
73 |
74 | ### Version 1.0.0 (Coming Soon)
75 |
76 | This will be the first stable release of tm-core with complete implementations of all modules. Currently, all modules contain placeholder implementations to establish the package structure and enable development of dependent packages.
77 |
78 | ### Development Status
79 |
80 | - ✅ Package structure and configuration
81 | - ✅ Build and test infrastructure
82 | - ✅ Development tooling setup
83 | - 🚧 TypeScript types implementation (Task 116)
84 | - 🚧 AI provider system (Task 117)
85 | - 🚧 Storage layer (Task 118)
86 | - 🚧 Task parser (Task 119)
87 | - 🚧 Utility functions (Task 120)
88 | - 🚧 Error handling (Task 121)
89 | - 🚧 Configuration system (Task 122)
90 | - 🚧 Testing infrastructure (Task 123)
91 | - 🚧 Documentation (Task 124)
92 | - 🚧 Package finalization (Task 125)
93 |
```
--------------------------------------------------------------------------------
/tests/manual/progress/TESTING_GUIDE.md:
--------------------------------------------------------------------------------
```markdown
1 | # Task Master Progress Testing Guide
2 |
3 | Quick reference for testing streaming/non-streaming functionality with token tracking.
4 |
5 | ## 🎯 Test Modes
6 |
7 | 1. **MCP Streaming** - Has `reportProgress` + `mcpLog`, shows emoji indicators (🔴🟠🟢)
8 | 2. **CLI Streaming** - No `reportProgress`, shows terminal progress bars
9 | 3. **Non-Streaming** - No progress reporting, single response
10 |
11 | ## 🚀 Quick Commands
12 |
13 | ```bash
14 | # Test Scripts (accept: mcp-streaming, cli-streaming, non-streaming, both, all)
15 | node test-parse-prd.js [mode]
16 | node test-analyze-complexity.js [mode]
17 | node test-expand.js [mode] [num_subtasks]
18 | node test-expand-all.js [mode] [num_subtasks]
19 | node parse-prd-analysis.js [accuracy|complexity|all]
20 |
21 | # CLI Commands
22 | node scripts/dev.js parse-prd test.txt # Local dev (streaming)
23 | node scripts/dev.js analyze-complexity --research
24 | node scripts/dev.js expand --id=1 --force
25 | node scripts/dev.js expand --all --force
26 |
27 | task-master [command] # Global CLI (non-streaming)
28 | ```
29 |
30 | ## ✅ Success Indicators
31 |
32 | ### Indicators
33 | - **Priority**: 🔴🔴🔴 (high), 🟠🟠⚪ (medium), 🟢⚪⚪ (low)
34 | - **Complexity**: ●●● (7-10), ●●○ (4-6), ●○○ (1-3)
35 |
36 | ### Token Format
37 | `Tokens (I/O): 2,150/1,847 ($0.0423)` (~4 chars per token)
38 |
39 | ### Progress Bars
40 | ```
41 | Single: Generating subtasks... |████████░░| 80% (4/5)
42 | Dual: Expanding 3 tasks | Task 2/3 |████████░░| 66%
43 | Generating 5 subtasks... |██████░░░░| 60%
44 | ```
45 |
46 | ### Fractional Progress
47 | `(completedTasks + currentSubtask/totalSubtasks) / totalTasks`
48 | Example: 33% → 46% → 60% → 66% → 80% → 93% → 100%
49 |
50 | ## 🐛 Quick Fixes
51 |
52 | | Issue | Fix |
53 | |-------|-----|
54 | | No streaming | Check `reportProgress` is passed |
55 | | NaN% progress | Filter duplicate `subtask_progress` events |
56 | | Missing tokens | Check `.env` has API keys |
57 | | Broken bars | Terminal width > 80 |
58 | | projectRoot.split | Use `projectRoot` not `session` |
59 |
60 | ```bash
61 | # Debug
62 | TASKMASTER_DEBUG=true node test-expand.js
63 | npm run lint
64 | ```
65 |
66 | ## 📊 Benchmarks
67 | - Single task: 10-20s (5 subtasks)
68 | - Expand all: 30-45s (3 tasks)
69 | - Streaming: ~10-20% faster
70 | - Updates: Every 2-5s
71 |
72 | ## 🔄 Test Workflow
73 |
74 | ```bash
75 | # Quick check
76 | node test-parse-prd.js both && npm test
77 |
78 | # Full suite (before release)
79 | for test in parse-prd analyze-complexity expand expand-all; do
80 | node test-$test.js all
81 | done
82 | node parse-prd-analysis.js all
83 | npm test
84 | ```
85 |
86 | ## 🎯 MCP Tool Example
87 |
88 | ```javascript
89 | {
90 | "tool": "parse_prd",
91 | "args": {
92 | "input": "prd.txt",
93 | "numTasks": "8",
94 | "force": true,
95 | "projectRoot": "/path/to/project"
96 | }
97 | }
98 |
```
--------------------------------------------------------------------------------
/tests/unit/profiles/zed-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('Zed 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('settings.json')) {
33 | return JSON.stringify({ context_servers: {} }, null, 2);
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 createProjectStructure behavior for Zed files
51 | function mockCreateZedStructure() {
52 | // Create main .zed directory
53 | fs.mkdirSync(path.join(tempDir, '.zed'), { recursive: true });
54 |
55 | // Create MCP config file (settings.json)
56 | fs.writeFileSync(
57 | path.join(tempDir, '.zed', 'settings.json'),
58 | JSON.stringify({ context_servers: {} }, null, 2)
59 | );
60 |
61 | // Create AGENTS.md in project root
62 | fs.writeFileSync(
63 | path.join(tempDir, 'AGENTS.md'),
64 | '# Task Master Instructions\n\nThis is the Task Master agents file.'
65 | );
66 | }
67 |
68 | test('creates all required .zed directories', () => {
69 | // Act
70 | mockCreateZedStructure();
71 |
72 | // Assert
73 | expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(tempDir, '.zed'), {
74 | recursive: true
75 | });
76 | });
77 |
78 | test('creates Zed settings.json with context_servers format', () => {
79 | // Act
80 | mockCreateZedStructure();
81 |
82 | // Assert
83 | expect(fs.writeFileSync).toHaveBeenCalledWith(
84 | path.join(tempDir, '.zed', 'settings.json'),
85 | JSON.stringify({ context_servers: {} }, null, 2)
86 | );
87 | });
88 |
89 | test('creates AGENTS.md in project root', () => {
90 | // Act
91 | mockCreateZedStructure();
92 |
93 | // Assert
94 | expect(fs.writeFileSync).toHaveBeenCalledWith(
95 | path.join(tempDir, 'AGENTS.md'),
96 | '# Task Master Instructions\n\nThis is the Task Master agents file.'
97 | );
98 | });
99 | });
100 |
```
--------------------------------------------------------------------------------
/tests/unit/scripts/modules/task-manager/move-task.test.js:
--------------------------------------------------------------------------------
```javascript
1 | import { jest } from '@jest/globals';
2 |
3 | // --- Mocks ---
4 | // Only mock the specific functions that move-task actually uses
5 | jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
6 | readJSON: jest.fn(),
7 | writeJSON: jest.fn(),
8 | log: jest.fn(),
9 | setTasksForTag: jest.fn(),
10 | traverseDependencies: jest.fn(() => [])
11 | }));
12 |
13 | jest.unstable_mockModule(
14 | '../../../../../scripts/modules/task-manager/generate-task-files.js',
15 | () => ({
16 | default: jest.fn().mockResolvedValue()
17 | })
18 | );
19 |
20 | jest.unstable_mockModule(
21 | '../../../../../scripts/modules/task-manager/is-task-dependent.js',
22 | () => ({
23 | default: jest.fn(() => false)
24 | })
25 | );
26 |
27 | jest.unstable_mockModule(
28 | '../../../../../scripts/modules/dependency-manager.js',
29 | () => ({
30 | findCrossTagDependencies: jest.fn(() => []),
31 | getDependentTaskIds: jest.fn(() => []),
32 | validateSubtaskMove: jest.fn()
33 | })
34 | );
35 |
36 | const { readJSON, writeJSON, log } = await import(
37 | '../../../../../scripts/modules/utils.js'
38 | );
39 | const generateTaskFiles = (
40 | await import(
41 | '../../../../../scripts/modules/task-manager/generate-task-files.js'
42 | )
43 | ).default;
44 |
45 | const { default: moveTask } = await import(
46 | '../../../../../scripts/modules/task-manager/move-task.js'
47 | );
48 |
49 | const sampleTagged = () => ({
50 | master: {
51 | tasks: [
52 | { id: 1, title: 'A' },
53 | { id: 2, title: 'B', subtasks: [{ id: 1, title: 'B.1' }] }
54 | ],
55 | metadata: {}
56 | },
57 | feature: {
58 | tasks: [{ id: 10, title: 'X' }],
59 | metadata: {}
60 | }
61 | });
62 |
63 | const clone = () => JSON.parse(JSON.stringify(sampleTagged()));
64 |
65 | describe('moveTask (unit)', () => {
66 | beforeEach(() => {
67 | jest.clearAllMocks();
68 | readJSON.mockImplementation((path, projectRoot, tag) => {
69 | const data = clone();
70 | return { ...data[tag], tag, _rawTaggedData: data };
71 | });
72 | writeJSON.mockResolvedValue();
73 | log.mockImplementation(() => {});
74 | });
75 |
76 | test('moves task to new ID in same tag', async () => {
77 | await moveTask('tasks.json', '1', '3', false, { tag: 'master' });
78 | expect(writeJSON).toHaveBeenCalled();
79 | const written = writeJSON.mock.calls[0][1];
80 | const ids = written.master.tasks.map((t) => t.id);
81 | expect(ids).toEqual(expect.arrayContaining([2, 3]));
82 | expect(ids).not.toContain(1);
83 | });
84 |
85 | test('throws when counts of source and dest mismatch', async () => {
86 | await expect(
87 | moveTask('tasks.json', '1,2', '3', {}, { tag: 'master' })
88 | ).rejects.toThrow(/Number of source IDs/);
89 | });
90 |
91 | test('error when tag invalid', async () => {
92 | await expect(
93 | moveTask('tasks.json', '1', '2', false, { tag: 'ghost' })
94 | ).rejects.toThrow(/tag "ghost" not found/);
95 | });
96 | });
97 |
```
--------------------------------------------------------------------------------
/apps/mcp/src/tools/autopilot/resume.tool.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview autopilot-resume MCP tool
3 | * Resume a previously started TDD workflow from saved state
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 ResumeWorkflowSchema = z.object({
16 | projectRoot: z
17 | .string()
18 | .describe('Absolute path to the project root directory')
19 | });
20 |
21 | type ResumeWorkflowArgs = z.infer<typeof ResumeWorkflowSchema>;
22 |
23 | /**
24 | * Register the autopilot_resume tool with the MCP server
25 | */
26 | export function registerAutopilotResumeTool(server: FastMCP) {
27 | server.addTool({
28 | name: 'autopilot_resume',
29 | description:
30 | 'Resume a previously started TDD workflow from saved state. Restores the workflow state machine and continues from where it left off.',
31 | parameters: ResumeWorkflowSchema,
32 | execute: withNormalizedProjectRoot(
33 | async (args: ResumeWorkflowArgs, context: MCPContext) => {
34 | const { projectRoot } = args;
35 |
36 | try {
37 | context.log.info(`Resuming autopilot 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 workflow state found. Start a new workflow with autopilot_start'
49 | }
50 | },
51 | log: context.log,
52 | projectRoot
53 | });
54 | }
55 |
56 | // Resume workflow
57 | const status = await workflowService.resumeWorkflow();
58 | const nextAction = workflowService.getNextAction();
59 |
60 | context.log.info(
61 | `Workflow resumed successfully for task ${status.taskId}`
62 | );
63 |
64 | return handleApiResult({
65 | result: {
66 | success: true,
67 | data: {
68 | message: 'Workflow resumed',
69 | ...status,
70 | nextAction: nextAction.action,
71 | actionDescription: nextAction.description,
72 | nextSteps: nextAction.nextSteps
73 | }
74 | },
75 | log: context.log,
76 | projectRoot
77 | });
78 | } catch (error: any) {
79 | context.log.error(`Error in autopilot-resume: ${error.message}`);
80 | if (error.stack) {
81 | context.log.debug(error.stack);
82 | }
83 | return handleApiResult({
84 | result: {
85 | success: false,
86 | error: { message: `Failed to resume workflow: ${error.message}` }
87 | },
88 | log: context.log,
89 | projectRoot
90 | });
91 | }
92 | }
93 | )
94 | });
95 | }
96 |
```
--------------------------------------------------------------------------------
/apps/extension/src/services/sidebar-webview-manager.ts:
--------------------------------------------------------------------------------
```typescript
1 | import * as vscode from 'vscode';
2 | import type { TaskMasterApi } from '../utils/task-master-api';
3 |
4 | export class SidebarWebviewManager implements vscode.WebviewViewProvider {
5 | private webviewView?: vscode.WebviewView;
6 | private api?: TaskMasterApi;
7 |
8 | constructor(private readonly extensionUri: vscode.Uri) {}
9 |
10 | setApi(api: TaskMasterApi): void {
11 | this.api = api;
12 | // Update connection status if webview exists
13 | if (this.webviewView) {
14 | this.updateConnectionStatus();
15 | }
16 | }
17 |
18 | resolveWebviewView(
19 | webviewView: vscode.WebviewView,
20 | context: vscode.WebviewViewResolveContext,
21 | token: vscode.CancellationToken
22 | ): void {
23 | this.webviewView = webviewView;
24 |
25 | webviewView.webview.options = {
26 | enableScripts: true,
27 | localResourceRoots: [
28 | vscode.Uri.joinPath(this.extensionUri, 'dist'),
29 | vscode.Uri.joinPath(this.extensionUri, 'assets')
30 | ]
31 | };
32 |
33 | webviewView.webview.html = this.getHtmlContent(webviewView.webview);
34 |
35 | // Handle messages from the webview
36 | webviewView.webview.onDidReceiveMessage((message) => {
37 | if (message.command === 'openBoard') {
38 | vscode.commands.executeCommand('tm.showKanbanBoard');
39 | }
40 | });
41 |
42 | // Update connection status on load
43 | this.updateConnectionStatus();
44 | }
45 |
46 | updateConnectionStatus(): void {
47 | if (!this.webviewView || !this.api) return;
48 |
49 | const status = this.api.getConnectionStatus();
50 | this.webviewView.webview.postMessage({
51 | type: 'connectionStatus',
52 | data: status
53 | });
54 | }
55 |
56 | private getHtmlContent(webview: vscode.Webview): string {
57 | const scriptUri = webview.asWebviewUri(
58 | vscode.Uri.joinPath(this.extensionUri, 'dist', 'sidebar.js')
59 | );
60 | const styleUri = webview.asWebviewUri(
61 | vscode.Uri.joinPath(this.extensionUri, 'dist', 'index.css')
62 | );
63 | const nonce = this.getNonce();
64 |
65 | return `<!DOCTYPE html>
66 | <html lang="en">
67 | <head>
68 | <meta charset="UTF-8">
69 | <meta name="viewport" content="width=device-width, initial-scale=1.0">
70 | <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}'; style-src ${webview.cspSource} 'unsafe-inline';">
71 | <link href="${styleUri}" rel="stylesheet">
72 | <title>TaskMaster</title>
73 | </head>
74 | <body>
75 | <div id="root"></div>
76 | <script nonce="${nonce}" src="${scriptUri}"></script>
77 | </body>
78 | </html>`;
79 | }
80 |
81 | private getNonce(): string {
82 | let text = '';
83 | const possible =
84 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
85 | for (let i = 0; i < 32; i++) {
86 | text += possible.charAt(Math.floor(Math.random() * possible.length));
87 | }
88 | return text;
89 | }
90 | }
91 |
```
--------------------------------------------------------------------------------
/packages/claude-code-plugin/commands/install-taskmaster.md:
--------------------------------------------------------------------------------
```markdown
1 | Check if Task Master is installed and install it if needed.
2 |
3 | This command helps you get Task Master set up globally on your system.
4 |
5 | ## Detection and Installation Process
6 |
7 | 1. **Check Current Installation**
8 | ```bash
9 | # Check if task-master command exists
10 | which task-master || echo "Task Master not found"
11 |
12 | # Check npm global packages
13 | npm list -g task-master-ai
14 | ```
15 |
16 | 2. **System Requirements Check**
17 | ```bash
18 | # Verify Node.js is installed
19 | node --version
20 |
21 | # Verify npm is installed
22 | npm --version
23 |
24 | # Check Node version (need 16+)
25 | ```
26 |
27 | 3. **Install Task Master Globally**
28 | If not installed, run:
29 | ```bash
30 | npm install -g task-master-ai
31 | ```
32 |
33 | 4. **Verify Installation**
34 | ```bash
35 | # Check version
36 | task-master --version
37 |
38 | # Verify command is available
39 | which task-master
40 | ```
41 |
42 | 5. **Initial Setup**
43 | ```bash
44 | # Initialize in current directory
45 | task-master init
46 | ```
47 |
48 | 6. **Configure AI Provider**
49 | Ensure you have at least one AI provider API key set:
50 | ```bash
51 | # Check current configuration
52 | task-master models --status
53 |
54 | # If no API keys found, guide setup
55 | echo "You'll need at least one API key:"
56 | echo "- ANTHROPIC_API_KEY for Claude"
57 | echo "- OPENAI_API_KEY for GPT models"
58 | echo "- PERPLEXITY_API_KEY for research"
59 | echo ""
60 | echo "Set them in your shell profile or .env file"
61 | ```
62 |
63 | 7. **Quick Test**
64 | ```bash
65 | # Create a test PRD
66 | echo "Build a simple hello world API" > test-prd.txt
67 |
68 | # Try parsing it
69 | task-master parse-prd test-prd.txt -n 3
70 | ```
71 |
72 | ## Troubleshooting
73 |
74 | If installation fails:
75 |
76 | **Permission Errors:**
77 | ```bash
78 | # Try with sudo (macOS/Linux)
79 | sudo npm install -g task-master-ai
80 |
81 | # Or fix npm permissions
82 | npm config set prefix ~/.npm-global
83 | export PATH=~/.npm-global/bin:$PATH
84 | ```
85 |
86 | **Network Issues:**
87 | ```bash
88 | # Use different registry
89 | npm install -g task-master-ai --registry https://registry.npmjs.org/
90 | ```
91 |
92 | **Node Version Issues:**
93 | ```bash
94 | # Install Node 18+ via nvm
95 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
96 | nvm install 18
97 | nvm use 18
98 | ```
99 |
100 | ## Success Confirmation
101 |
102 | Once installed, you should see:
103 | ```
104 | ✅ Task Master v0.16.2 (or higher) installed
105 | ✅ Command 'task-master' available globally
106 | ✅ AI provider configured
107 | ✅ Ready to use slash commands!
108 |
109 | Try: /project:task-master:init your-prd.md
110 | ```
111 |
112 | ## Next Steps
113 |
114 | After installation:
115 | 1. Run `/project:utils:check-health` to verify setup
116 | 2. Configure AI providers with `/project:task-master:models`
117 | 3. Start using Task Master commands!
```
--------------------------------------------------------------------------------
/apps/extension/src/utils/task-master-api/mcp-client.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * MCP Client Wrapper
3 | * Handles MCP tool calls with retry logic
4 | */
5 |
6 | import type { ExtensionLogger } from '../logger';
7 | import type { MCPClientManager } from '../mcpClient';
8 |
9 | export class MCPClient {
10 | constructor(
11 | private mcpClient: MCPClientManager,
12 | private logger: ExtensionLogger,
13 | private config: { timeout: number; retryAttempts: number }
14 | ) {}
15 |
16 | /**
17 | * Call MCP tool with retry logic
18 | */
19 | async callTool(
20 | toolName: string,
21 | args: Record<string, unknown>
22 | ): Promise<any> {
23 | let lastError: Error | null = null;
24 |
25 | for (let attempt = 1; attempt <= this.config.retryAttempts; attempt++) {
26 | try {
27 | const rawResponse = await this.mcpClient.callTool(toolName, args);
28 | this.logger.debug(
29 | `Raw MCP response for ${toolName}:`,
30 | JSON.stringify(rawResponse, null, 2)
31 | );
32 |
33 | // Parse MCP response format
34 | if (
35 | rawResponse &&
36 | rawResponse.content &&
37 | Array.isArray(rawResponse.content) &&
38 | rawResponse.content[0]
39 | ) {
40 | const contentItem = rawResponse.content[0];
41 | if (contentItem.type === 'text' && contentItem.text) {
42 | try {
43 | const parsedData = JSON.parse(contentItem.text);
44 | this.logger.debug(`Parsed MCP data for ${toolName}:`, parsedData);
45 | return parsedData;
46 | } catch (parseError) {
47 | this.logger.error(
48 | `Failed to parse MCP response text for ${toolName}:`,
49 | parseError
50 | );
51 | this.logger.error(`Raw text was:`, contentItem.text);
52 | return rawResponse; // Fall back to original response
53 | }
54 | }
55 | }
56 |
57 | // If not in expected format, return as-is
58 | this.logger.warn(
59 | `Unexpected MCP response format for ${toolName}, returning raw response`
60 | );
61 | return rawResponse;
62 | } catch (error) {
63 | lastError = error instanceof Error ? error : new Error('Unknown error');
64 | this.logger.warn(
65 | `Attempt ${attempt}/${this.config.retryAttempts} failed for ${toolName}:`,
66 | lastError.message
67 | );
68 |
69 | if (attempt < this.config.retryAttempts) {
70 | // Exponential backoff
71 | const delay = Math.min(1000 * 2 ** (attempt - 1), 5000);
72 | await new Promise((resolve) => setTimeout(resolve, delay));
73 | }
74 | }
75 | }
76 |
77 | throw (
78 | lastError ||
79 | new Error(
80 | `Failed to call ${toolName} after ${this.config.retryAttempts} attempts`
81 | )
82 | );
83 | }
84 |
85 | /**
86 | * Get connection status
87 | */
88 | getStatus(): { isRunning: boolean; error?: string } {
89 | return this.mcpClient.getStatus();
90 | }
91 |
92 | /**
93 | * Test connection
94 | */
95 | async testConnection(): Promise<boolean> {
96 | return this.mcpClient.testConnection();
97 | }
98 | }
99 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/add-tag.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/add-tag.js
3 | * Tool to create a new tag
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | createErrorResponse,
9 | handleApiResult,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { addTagDirect } from '../core/task-master-core.js';
13 | import { findTasksPath } from '../core/utils/path-utils.js';
14 |
15 | /**
16 | * Register the addTag tool with the MCP server
17 | * @param {Object} server - FastMCP server instance
18 | */
19 | export function registerAddTagTool(server) {
20 | server.addTool({
21 | name: 'add_tag',
22 | description: 'Create a new tag for organizing tasks in different contexts',
23 | parameters: z.object({
24 | name: z.string().describe('Name of the new tag to create'),
25 | copyFromCurrent: z
26 | .boolean()
27 | .optional()
28 | .describe(
29 | 'Whether to copy tasks from the current tag (default: false)'
30 | ),
31 | copyFromTag: z
32 | .string()
33 | .optional()
34 | .describe('Specific tag to copy tasks from'),
35 | fromBranch: z
36 | .boolean()
37 | .optional()
38 | .describe(
39 | 'Create tag name from current git branch (ignores name parameter)'
40 | ),
41 | description: z
42 | .string()
43 | .optional()
44 | .describe('Optional description for the tag'),
45 | file: z
46 | .string()
47 | .optional()
48 | .describe('Path to the tasks file (default: tasks/tasks.json)'),
49 | projectRoot: z
50 | .string()
51 | .describe('The directory of the project. Must be an absolute path.')
52 | }),
53 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
54 | try {
55 | log.info(`Starting add-tag with args: ${JSON.stringify(args)}`);
56 |
57 | // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
58 | let tasksJsonPath;
59 | try {
60 | tasksJsonPath = findTasksPath(
61 | { projectRoot: args.projectRoot, file: args.file },
62 | log
63 | );
64 | } catch (error) {
65 | log.error(`Error finding tasks.json: ${error.message}`);
66 | return createErrorResponse(
67 | `Failed to find tasks.json: ${error.message}`
68 | );
69 | }
70 |
71 | // Call the direct function
72 | const result = await addTagDirect(
73 | {
74 | tasksJsonPath: tasksJsonPath,
75 | name: args.name,
76 | copyFromCurrent: args.copyFromCurrent,
77 | copyFromTag: args.copyFromTag,
78 | fromBranch: args.fromBranch,
79 | description: args.description,
80 | projectRoot: args.projectRoot
81 | },
82 | log,
83 | { session }
84 | );
85 |
86 | return handleApiResult(
87 | result,
88 | log,
89 | 'Error creating tag',
90 | undefined,
91 | args.projectRoot
92 | );
93 | } catch (error) {
94 | log.error(`Error in add-tag tool: ${error.message}`);
95 | return createErrorResponse(error.message);
96 | }
97 | })
98 | });
99 | }
100 |
```
--------------------------------------------------------------------------------
/apps/mcp/src/tools/autopilot/next.tool.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview autopilot-next MCP tool
3 | * Get the next action to perform in the TDD workflow
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 NextActionSchema = z.object({
16 | projectRoot: z
17 | .string()
18 | .describe('Absolute path to the project root directory')
19 | });
20 |
21 | type NextActionArgs = z.infer<typeof NextActionSchema>;
22 |
23 | /**
24 | * Register the autopilot_next tool with the MCP server
25 | */
26 | export function registerAutopilotNextTool(server: FastMCP) {
27 | server.addTool({
28 | name: 'autopilot_next',
29 | description:
30 | 'Get the next action to perform in the TDD workflow. Returns detailed context about what needs to be done next, including the current phase, subtask, and expected actions.',
31 | parameters: NextActionSchema,
32 | execute: withNormalizedProjectRoot(
33 | async (args: NextActionArgs, context: MCPContext) => {
34 | const { projectRoot } = args;
35 |
36 | try {
37 | context.log.info(
38 | `Getting next action for workflow in ${projectRoot}`
39 | );
40 |
41 | const workflowService = new WorkflowService(projectRoot);
42 |
43 | // Check if workflow exists
44 | if (!(await workflowService.hasWorkflow())) {
45 | return handleApiResult({
46 | result: {
47 | success: false,
48 | error: {
49 | message:
50 | 'No active workflow found. Start a workflow with autopilot_start'
51 | }
52 | },
53 | log: context.log,
54 | projectRoot
55 | });
56 | }
57 |
58 | // Resume to load state
59 | await workflowService.resumeWorkflow();
60 |
61 | // Get next action
62 | const nextAction = workflowService.getNextAction();
63 | const status = workflowService.getStatus();
64 |
65 | context.log.info(`Next action determined: ${nextAction.action}`);
66 |
67 | return handleApiResult({
68 | result: {
69 | success: true,
70 | data: {
71 | action: nextAction.action,
72 | actionDescription: nextAction.description,
73 | ...status,
74 | nextSteps: nextAction.nextSteps
75 | }
76 | },
77 | log: context.log,
78 | projectRoot
79 | });
80 | } catch (error: any) {
81 | context.log.error(`Error in autopilot-next: ${error.message}`);
82 | if (error.stack) {
83 | context.log.debug(error.stack);
84 | }
85 | return handleApiResult({
86 | result: {
87 | success: false,
88 | error: {
89 | message: `Failed to get next action: ${error.message}`
90 | }
91 | },
92 | log: context.log,
93 | projectRoot
94 | });
95 | }
96 | }
97 | )
98 | });
99 | }
100 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/remove-dependency.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/remove-dependency.js
3 | * Tool for removing a dependency from a task
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | handleApiResult,
9 | createErrorResponse,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { removeDependencyDirect } 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 removeDependency tool with the MCP server
18 | * @param {Object} server - FastMCP server instance
19 | */
20 | export function registerRemoveDependencyTool(server) {
21 | server.addTool({
22 | name: 'remove_dependency',
23 | description: 'Remove a dependency from a task',
24 | parameters: z.object({
25 | id: z.string().describe('Task ID to remove dependency from'),
26 | dependsOn: z.string().describe('Task ID to remove as a dependency'),
27 | file: z
28 | .string()
29 | .optional()
30 | .describe(
31 | 'Absolute path to the tasks file (default: tasks/tasks.json)'
32 | ),
33 | projectRoot: z
34 | .string()
35 | .describe('The directory of the project. Must be an absolute path.'),
36 | tag: z.string().optional().describe('Tag context to operate on')
37 | }),
38 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
39 | try {
40 | const resolvedTag = resolveTag({
41 | projectRoot: args.projectRoot,
42 | tag: args.tag
43 | });
44 | log.info(
45 | `Removing dependency for task ${args.id} from ${args.dependsOn} with args: ${JSON.stringify(args)}`
46 | );
47 |
48 | // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
49 | let tasksJsonPath;
50 | try {
51 | tasksJsonPath = findTasksPath(
52 | { projectRoot: args.projectRoot, file: args.file },
53 | log
54 | );
55 | } catch (error) {
56 | log.error(`Error finding tasks.json: ${error.message}`);
57 | return createErrorResponse(
58 | `Failed to find tasks.json: ${error.message}`
59 | );
60 | }
61 |
62 | const result = await removeDependencyDirect(
63 | {
64 | tasksJsonPath: tasksJsonPath,
65 | id: args.id,
66 | dependsOn: args.dependsOn,
67 | projectRoot: args.projectRoot,
68 | tag: resolvedTag
69 | },
70 | log
71 | );
72 |
73 | if (result.success) {
74 | log.info(`Successfully removed dependency: ${result.data.message}`);
75 | } else {
76 | log.error(`Failed to remove dependency: ${result.error.message}`);
77 | }
78 |
79 | return handleApiResult(
80 | result,
81 | log,
82 | 'Error removing dependency',
83 | undefined,
84 | args.projectRoot
85 | );
86 | } catch (error) {
87 | log.error(`Error in removeDependency tool: ${error.message}`);
88 | return createErrorResponse(error.message);
89 | }
90 | })
91 | });
92 | }
93 |
```
--------------------------------------------------------------------------------
/packages/tm-bridge/src/add-tag-bridge.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { ui } from '@tm/cli';
2 | import type { BaseBridgeParams } from './bridge-types.js';
3 | import { checkStorageType } from './bridge-utils.js';
4 |
5 | /**
6 | * Parameters for the add-tag bridge function
7 | */
8 | export interface AddTagBridgeParams extends BaseBridgeParams {
9 | /** Tag name to create */
10 | tagName: string;
11 | }
12 |
13 | /**
14 | * Result returned when API storage redirects to web UI
15 | */
16 | export interface RemoteAddTagResult {
17 | success: boolean;
18 | message: string;
19 | redirectUrl: string;
20 | }
21 |
22 | /**
23 | * Shared bridge function for add-tag command.
24 | * Checks if using API storage and redirects to web UI if so.
25 | *
26 | * For API storage, tags are called "briefs" and must be created
27 | * through the Hamster web interface.
28 | *
29 | * @param params - Bridge parameters
30 | * @returns Result object if API storage handled it, null if should fall through to file storage
31 | */
32 | export async function tryAddTagViaRemote(
33 | params: AddTagBridgeParams
34 | ): Promise<RemoteAddTagResult | null> {
35 | const {
36 | tagName,
37 | projectRoot,
38 | isMCP = false,
39 | outputFormat = 'text',
40 | report
41 | } = params;
42 |
43 | // Check storage type using shared utility
44 | const { isApiStorage, tmCore } = await checkStorageType(
45 | projectRoot,
46 | report,
47 | 'falling back to file-based tag creation'
48 | );
49 |
50 | if (!isApiStorage || !tmCore) {
51 | // Not API storage - signal caller to fall through to file-based logic
52 | return null;
53 | }
54 |
55 | // Get the brief creation URL from tmCore
56 | const redirectUrl = tmCore.auth.getBriefCreationUrl();
57 |
58 | if (!redirectUrl) {
59 | report(
60 | 'error',
61 | 'Could not generate brief creation URL. Please ensure you have selected an organization using "tm context org"'
62 | );
63 | return {
64 | success: false,
65 | message:
66 | 'Failed to generate brief creation URL. Please ensure an organization is selected.',
67 | redirectUrl: ''
68 | };
69 | }
70 |
71 | // Show CLI output if not MCP
72 | if (!isMCP && outputFormat === 'text') {
73 | console.log(
74 | ui.displayCardBox({
75 | header: '# Create a Brief in Hamster Studio',
76 | body: [
77 | 'Your tags are separate task lists. When connected to Hamster,\ntask lists are attached to briefs.',
78 | 'Create a new brief and its task list will automatically be\navailable when generated.'
79 | ],
80 | callToAction: {
81 | label: 'Visit:',
82 | action: redirectUrl
83 | },
84 | footer:
85 | 'To access tasks for a specific brief, use:\n' +
86 | ' • tm briefs select <brief-name>\n' +
87 | ' • tm briefs select <brief-id>\n' +
88 | ' • tm briefs select (interactive)'
89 | })
90 | );
91 | }
92 |
93 | // Return success result with redirect URL
94 | return {
95 | success: true,
96 | message: `API storage detected. Please create tag "${tagName}" at: ${redirectUrl}`,
97 | redirectUrl
98 | };
99 | }
100 |
```
--------------------------------------------------------------------------------
/apps/mcp/src/tools/autopilot/abort.tool.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview autopilot-abort MCP tool
3 | * Abort a running TDD workflow and clean up state
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 AbortSchema = z.object({
16 | projectRoot: z
17 | .string()
18 | .describe('Absolute path to the project root directory')
19 | });
20 |
21 | type AbortArgs = z.infer<typeof AbortSchema>;
22 |
23 | /**
24 | * Register the autopilot_abort tool with the MCP server
25 | */
26 | export function registerAutopilotAbortTool(server: FastMCP) {
27 | server.addTool({
28 | name: 'autopilot_abort',
29 | description:
30 | 'Abort the current TDD workflow and clean up workflow state. This will remove the workflow state file but will NOT delete the git branch or any code changes.',
31 | parameters: AbortSchema,
32 | execute: withNormalizedProjectRoot(
33 | async (args: AbortArgs, context: MCPContext) => {
34 | const { projectRoot } = args;
35 |
36 | try {
37 | context.log.info(`Aborting autopilot workflow in ${projectRoot}`);
38 |
39 | const workflowService = new WorkflowService(projectRoot);
40 |
41 | // Check if workflow exists
42 | const hasWorkflow = await workflowService.hasWorkflow();
43 |
44 | if (!hasWorkflow) {
45 | context.log.warn('No active workflow to abort');
46 | return handleApiResult({
47 | result: {
48 | success: true,
49 | data: {
50 | message: 'No active workflow to abort',
51 | hadWorkflow: false
52 | }
53 | },
54 | log: context.log,
55 | projectRoot
56 | });
57 | }
58 |
59 | // Get info before aborting
60 | await workflowService.resumeWorkflow();
61 | const status = workflowService.getStatus();
62 |
63 | // Abort workflow
64 | await workflowService.abortWorkflow();
65 |
66 | context.log.info('Workflow state deleted');
67 |
68 | return handleApiResult({
69 | result: {
70 | success: true,
71 | data: {
72 | message: 'Workflow aborted',
73 | hadWorkflow: true,
74 | taskId: status.taskId,
75 | branchName: status.branchName,
76 | note: 'Git branch and code changes were preserved. You can manually clean them up if needed.'
77 | }
78 | },
79 | log: context.log,
80 | projectRoot
81 | });
82 | } catch (error: any) {
83 | context.log.error(`Error in autopilot-abort: ${error.message}`);
84 | if (error.stack) {
85 | context.log.debug(error.stack);
86 | }
87 | return handleApiResult({
88 | result: {
89 | success: false,
90 | error: { message: `Failed to abort workflow: ${error.message}` }
91 | },
92 | log: context.log,
93 | projectRoot
94 | });
95 | }
96 | }
97 | )
98 | });
99 | }
100 |
```
--------------------------------------------------------------------------------
/tests/integration/profiles/claude-init-functionality.test.js:
--------------------------------------------------------------------------------
```javascript
1 | import fs from 'fs';
2 | import path from 'path';
3 | import { claudeProfile } from '../../../src/profiles/claude.js';
4 |
5 | describe('Claude Profile Initialization Functionality', () => {
6 | let claudeProfileContent;
7 |
8 | beforeAll(() => {
9 | const claudeJsPath = path.join(
10 | process.cwd(),
11 | 'src',
12 | 'profiles',
13 | 'claude.js'
14 | );
15 | claudeProfileContent = fs.readFileSync(claudeJsPath, 'utf8');
16 | });
17 |
18 | test('claude.js has correct asset-only profile configuration', () => {
19 | // Check for explicit, non-default values in the source file
20 | expect(claudeProfileContent).toContain("name: 'claude'");
21 | expect(claudeProfileContent).toContain("displayName: 'Claude Code'");
22 | expect(claudeProfileContent).toContain("profileDir: '.'"); // non-default
23 | expect(claudeProfileContent).toContain("rulesDir: '.'"); // non-default
24 | expect(claudeProfileContent).toContain("mcpConfigName: '.mcp.json'"); // non-default
25 | expect(claudeProfileContent).toContain('includeDefaultRules: false'); // non-default
26 | expect(claudeProfileContent).toContain(
27 | "'AGENTS.md': '.taskmaster/CLAUDE.md'"
28 | );
29 |
30 | // Check the final computed properties on the profile object
31 | expect(claudeProfile.profileName).toBe('claude');
32 | expect(claudeProfile.displayName).toBe('Claude Code');
33 | expect(claudeProfile.profileDir).toBe('.');
34 | expect(claudeProfile.rulesDir).toBe('.');
35 | expect(claudeProfile.mcpConfig).toBe(true); // default from base profile
36 | expect(claudeProfile.mcpConfigName).toBe('.mcp.json'); // explicitly set
37 | expect(claudeProfile.mcpConfigPath).toBe('.mcp.json'); // computed
38 | expect(claudeProfile.includeDefaultRules).toBe(false);
39 | expect(claudeProfile.fileMap['AGENTS.md']).toBe('.taskmaster/CLAUDE.md');
40 | });
41 |
42 | test('claude.js has lifecycle functions for file management', () => {
43 | expect(claudeProfileContent).toContain('function onAddRulesProfile');
44 | expect(claudeProfileContent).toContain('function onRemoveRulesProfile');
45 | expect(claudeProfileContent).toContain(
46 | 'function onPostConvertRulesProfile'
47 | );
48 | });
49 |
50 | test('claude.js handles .claude directory and .taskmaster/CLAUDE.md import in lifecycle functions', () => {
51 | expect(claudeProfileContent).toContain('.claude');
52 | expect(claudeProfileContent).toContain('copyRecursiveSync');
53 | expect(claudeProfileContent).toContain('.taskmaster/CLAUDE.md');
54 | expect(claudeProfileContent).toContain('@./.taskmaster/CLAUDE.md');
55 | });
56 |
57 | test('claude.js has proper error handling in lifecycle functions', () => {
58 | expect(claudeProfileContent).toContain('try {');
59 | expect(claudeProfileContent).toContain('} catch (err) {');
60 | expect(claudeProfileContent).toContain("log('error'");
61 | });
62 | });
63 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/generate.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/generate.js
3 | * Tool to generate individual task files from tasks.json
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | handleApiResult,
9 | createErrorResponse,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { generateTaskFilesDirect } from '../core/task-master-core.js';
13 | import { findTasksPath } from '../core/utils/path-utils.js';
14 | import { resolveTag } from '../../../scripts/modules/utils.js';
15 | import path from 'path';
16 |
17 | /**
18 | * Register the generate tool with the MCP server
19 | * @param {Object} server - FastMCP server instance
20 | */
21 | export function registerGenerateTool(server) {
22 | server.addTool({
23 | name: 'generate',
24 | description:
25 | 'Generates individual task files in tasks/ directory based on tasks.json',
26 | parameters: z.object({
27 | file: z.string().optional().describe('Absolute path to the tasks file'),
28 | output: z
29 | .string()
30 | .optional()
31 | .describe('Output directory (default: same directory as tasks file)'),
32 | projectRoot: z
33 | .string()
34 | .describe('The directory of the project. Must be an absolute path.'),
35 | tag: z.string().optional().describe('Tag context to operate on')
36 | }),
37 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
38 | try {
39 | log.info(`Generating task files with args: ${JSON.stringify(args)}`);
40 |
41 | const resolvedTag = resolveTag({
42 | projectRoot: args.projectRoot,
43 | tag: args.tag
44 | });
45 | // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
46 | let tasksJsonPath;
47 | try {
48 | tasksJsonPath = findTasksPath(
49 | { projectRoot: args.projectRoot, file: args.file },
50 | log
51 | );
52 | } catch (error) {
53 | log.error(`Error finding tasks.json: ${error.message}`);
54 | return createErrorResponse(
55 | `Failed to find tasks.json: ${error.message}`
56 | );
57 | }
58 |
59 | const outputDir = args.output
60 | ? path.resolve(args.projectRoot, args.output)
61 | : path.dirname(tasksJsonPath);
62 |
63 | const result = await generateTaskFilesDirect(
64 | {
65 | tasksJsonPath: tasksJsonPath,
66 | outputDir: outputDir,
67 | projectRoot: args.projectRoot,
68 | tag: resolvedTag
69 | },
70 | log,
71 | { session }
72 | );
73 |
74 | if (result.success) {
75 | log.info(`Successfully generated task files: ${result.data.message}`);
76 | } else {
77 | log.error(
78 | `Failed to generate task files: ${result.error?.message || 'Unknown error'}`
79 | );
80 | }
81 |
82 | return handleApiResult(
83 | result,
84 | log,
85 | 'Error generating task files',
86 | undefined,
87 | args.projectRoot
88 | );
89 | } catch (error) {
90 | log.error(`Error in generate tool: ${error.message}`);
91 | return createErrorResponse(error.message);
92 | }
93 | })
94 | });
95 | }
96 |
```
--------------------------------------------------------------------------------
/packages/claude-code-plugin/commands/learn.md:
--------------------------------------------------------------------------------
```markdown
1 | Learn about Task Master capabilities through interactive exploration.
2 |
3 | Arguments: $ARGUMENTS
4 |
5 | ## Interactive Task Master Learning
6 |
7 | Based on your input, I'll help you discover capabilities:
8 |
9 | ### 1. **What are you trying to do?**
10 |
11 | If $ARGUMENTS contains:
12 | - "start" / "begin" → Show project initialization workflows
13 | - "manage" / "organize" → Show task management commands
14 | - "automate" / "auto" → Show automation workflows
15 | - "analyze" / "report" → Show analysis tools
16 | - "fix" / "problem" → Show troubleshooting commands
17 | - "fast" / "quick" → Show efficiency shortcuts
18 |
19 | ### 2. **Intelligent Suggestions**
20 |
21 | Based on your project state:
22 |
23 | **No tasks yet?**
24 | ```
25 | You'll want to start with:
26 | 1. /project:task-master:init <prd-file>
27 | → Creates tasks from requirements
28 |
29 | 2. /project:task-master:parse-prd <file>
30 | → Alternative task generation
31 |
32 | Try: /project:task-master:init demo-prd.md
33 | ```
34 |
35 | **Have tasks?**
36 | Let me analyze what you might need...
37 | - Many pending tasks? → Learn sprint planning
38 | - Complex tasks? → Learn task expansion
39 | - Daily work? → Learn workflow automation
40 |
41 | ### 3. **Command Discovery**
42 |
43 | **By Category:**
44 | - 📋 Task Management: list, show, add, update, complete
45 | - 🔄 Workflows: auto-implement, sprint-plan, daily-standup
46 | - 🛠️ Utilities: check-health, complexity-report, sync-memory
47 | - 🔍 Analysis: validate-deps, show dependencies
48 |
49 | **By Scenario:**
50 | - "I want to see what to work on" → `/project:task-master:next`
51 | - "I need to break this down" → `/project:task-master:expand <id>`
52 | - "Show me everything" → `/project:task-master:status`
53 | - "Just do it for me" → `/project:workflows:auto-implement`
54 |
55 | ### 4. **Power User Patterns**
56 |
57 | **Command Chaining:**
58 | ```
59 | /project:task-master:next
60 | /project:task-master:start <id>
61 | /project:workflows:auto-implement
62 | ```
63 |
64 | **Smart Filters:**
65 | ```
66 | /project:task-master:list pending high
67 | /project:task-master:list blocked
68 | /project:task-master:list 1-5 tree
69 | ```
70 |
71 | **Automation:**
72 | ```
73 | /project:workflows:pipeline init → expand-all → sprint-plan
74 | ```
75 |
76 | ### 5. **Learning Path**
77 |
78 | Based on your experience level:
79 |
80 | **Beginner Path:**
81 | 1. init → Create project
82 | 2. status → Understand state
83 | 3. next → Find work
84 | 4. complete → Finish task
85 |
86 | **Intermediate Path:**
87 | 1. expand → Break down complex tasks
88 | 2. sprint-plan → Organize work
89 | 3. complexity-report → Understand difficulty
90 | 4. validate-deps → Ensure consistency
91 |
92 | **Advanced Path:**
93 | 1. pipeline → Chain operations
94 | 2. smart-flow → Context-aware automation
95 | 3. Custom commands → Extend the system
96 |
97 | ### 6. **Try This Now**
98 |
99 | Based on what you asked about, try:
100 | [Specific command suggestion based on $ARGUMENTS]
101 |
102 | Want to learn more about a specific command?
103 | Type: /project:help <command-name>
```
--------------------------------------------------------------------------------
/tests/unit/ai-providers/lmstudio.test.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * Tests for LMStudioProvider
3 | */
4 |
5 | import { LMStudioProvider } from '../../../src/ai-providers/lmstudio.js';
6 |
7 | describe('LMStudioProvider', () => {
8 | let provider;
9 |
10 | beforeEach(() => {
11 | provider = new LMStudioProvider();
12 | });
13 |
14 | describe('constructor', () => {
15 | it('should initialize with correct name', () => {
16 | expect(provider.name).toBe('LM Studio');
17 | });
18 |
19 | it('should not require API key', () => {
20 | expect(provider.requiresApiKey).toBe(false);
21 | });
22 |
23 | it('should have default localhost baseURL', () => {
24 | expect(provider.defaultBaseURL).toBe('http://localhost:1234/v1');
25 | });
26 |
27 | it('should disable structured outputs (LM Studio only supports json_schema mode)', () => {
28 | expect(provider.supportsStructuredOutputs).toBe(true);
29 | });
30 |
31 | it('should inherit from OpenAICompatibleProvider', () => {
32 | expect(provider).toHaveProperty('generateText');
33 | expect(provider).toHaveProperty('streamText');
34 | expect(provider).toHaveProperty('generateObject');
35 | });
36 | });
37 |
38 | describe('getRequiredApiKeyName', () => {
39 | it('should return environment variable name', () => {
40 | expect(provider.getRequiredApiKeyName()).toBe('LMSTUDIO_API_KEY');
41 | });
42 | });
43 |
44 | describe('isRequiredApiKey', () => {
45 | it('should return false as local server does not require API key', () => {
46 | expect(provider.isRequiredApiKey()).toBe(false);
47 | });
48 | });
49 |
50 | describe('getClient', () => {
51 | it('should create client without API key', () => {
52 | const client = provider.getClient({});
53 | expect(client).toBeDefined();
54 | });
55 |
56 | it('should create client with custom baseURL', () => {
57 | const params = {
58 | baseURL: 'http://custom-host:8080/v1'
59 | };
60 | const client = provider.getClient(params);
61 | expect(client).toBeDefined();
62 | });
63 |
64 | it('should not throw error when API key is missing', () => {
65 | expect(() => {
66 | provider.getClient({});
67 | }).not.toThrow();
68 | });
69 | });
70 |
71 | describe('validateAuth', () => {
72 | it('should not require API key validation', () => {
73 | expect(() => {
74 | provider.validateAuth({});
75 | }).not.toThrow();
76 | });
77 |
78 | it('should pass with or without API key', () => {
79 | expect(() => {
80 | provider.validateAuth({ apiKey: 'test-key' });
81 | }).not.toThrow();
82 |
83 | expect(() => {
84 | provider.validateAuth({});
85 | }).not.toThrow();
86 | });
87 | });
88 |
89 | describe('getBaseURL', () => {
90 | it('should return default localhost URL', () => {
91 | const baseURL = provider.getBaseURL({});
92 | expect(baseURL).toBe('http://localhost:1234/v1');
93 | });
94 |
95 | it('should return custom baseURL when provided', () => {
96 | const baseURL = provider.getBaseURL({
97 | baseURL: 'http://192.168.1.100:1234/v1'
98 | });
99 | expect(baseURL).toBe('http://192.168.1.100:1234/v1');
100 | });
101 | });
102 | });
103 |
```
--------------------------------------------------------------------------------
/test-version-check-full.js:
--------------------------------------------------------------------------------
```javascript
1 | import {
2 | checkForUpdate,
3 | displayUpgradeNotification,
4 | compareVersions
5 | } from './scripts/modules/commands.js';
6 | import fs from 'fs';
7 | import path from 'path';
8 |
9 | // Force our current version for testing
10 | process.env.FORCE_VERSION = '0.9.30';
11 |
12 | // Create a mock package.json in memory for testing
13 | const mockPackageJson = {
14 | name: 'task-master-ai',
15 | version: '0.9.30'
16 | };
17 |
18 | // Modified version of checkForUpdate that doesn't use HTTP for testing
19 | async function testCheckForUpdate(simulatedLatestVersion) {
20 | // Get current version - use our forced version
21 | const currentVersion = process.env.FORCE_VERSION || '0.9.30';
22 |
23 | console.log(`Using simulated current version: ${currentVersion}`);
24 | console.log(`Using simulated latest version: ${simulatedLatestVersion}`);
25 |
26 | // Compare versions
27 | const needsUpdate =
28 | compareVersions(currentVersion, simulatedLatestVersion) < 0;
29 |
30 | return {
31 | currentVersion,
32 | latestVersion: simulatedLatestVersion,
33 | needsUpdate
34 | };
35 | }
36 |
37 | // Test with current version older than latest (should show update notice)
38 | async function runTest() {
39 | console.log('=== Testing version check scenarios ===\n');
40 |
41 | // Scenario 1: Update available
42 | console.log(
43 | '\n--- Scenario 1: Update available (Current: 0.9.30, Latest: 1.0.0) ---'
44 | );
45 | const updateInfo1 = await testCheckForUpdate('1.0.0');
46 | console.log('Update check results:');
47 | console.log(`- Current version: ${updateInfo1.currentVersion}`);
48 | console.log(`- Latest version: ${updateInfo1.latestVersion}`);
49 | console.log(`- Update needed: ${updateInfo1.needsUpdate}`);
50 |
51 | if (updateInfo1.needsUpdate) {
52 | console.log('\nDisplaying upgrade notification:');
53 | displayUpgradeNotification(
54 | updateInfo1.currentVersion,
55 | updateInfo1.latestVersion
56 | );
57 | }
58 |
59 | // Scenario 2: No update needed (versions equal)
60 | console.log(
61 | '\n--- Scenario 2: No update needed (Current: 0.9.30, Latest: 0.9.30) ---'
62 | );
63 | const updateInfo2 = await testCheckForUpdate('0.9.30');
64 | console.log('Update check results:');
65 | console.log(`- Current version: ${updateInfo2.currentVersion}`);
66 | console.log(`- Latest version: ${updateInfo2.latestVersion}`);
67 | console.log(`- Update needed: ${updateInfo2.needsUpdate}`);
68 |
69 | // Scenario 3: Development version (current newer than latest)
70 | console.log(
71 | '\n--- Scenario 3: Development version (Current: 0.9.30, Latest: 0.9.0) ---'
72 | );
73 | const updateInfo3 = await testCheckForUpdate('0.9.0');
74 | console.log('Update check results:');
75 | console.log(`- Current version: ${updateInfo3.currentVersion}`);
76 | console.log(`- Latest version: ${updateInfo3.latestVersion}`);
77 | console.log(`- Update needed: ${updateInfo3.needsUpdate}`);
78 |
79 | console.log('\n=== Test complete ===');
80 | }
81 |
82 | // Run all tests
83 | runTest();
84 |
```
--------------------------------------------------------------------------------
/apps/extension/src/components/TaskMasterLogo.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import React from 'react';
2 |
3 | interface TaskMasterLogoProps {
4 | className?: string;
5 | }
6 |
7 | export const TaskMasterLogo: React.FC<TaskMasterLogoProps> = ({
8 | className = ''
9 | }) => {
10 | return (
11 | <svg
12 | className={className}
13 | viewBox="0 0 224 291"
14 | fill="none"
15 | xmlns="http://www.w3.org/2000/svg"
16 | >
17 | <path
18 | d="M101.635 286.568L71.4839 256.414C65.6092 250.539 65.6092 241.03 71.4839 235.155L142.52 164.11C144.474 162.156 147.643 162.156 149.61 164.11L176.216 190.719C178.17 192.673 181.339 192.673 183.305 190.719L189.719 184.305C191.673 182.35 191.673 179.181 189.719 177.214L163.113 150.605C161.159 148.651 161.159 145.481 163.113 143.514L191.26 115.365C193.214 113.41 193.214 110.241 191.26 108.274L182.316 99.3291C180.362 97.3748 177.193 97.3748 175.226 99.3291L55.8638 218.706C49.989 224.581 40.4816 224.581 34.6068 218.706L4.4061 188.501C-1.4687 182.626 -1.4687 173.117 4.4061 167.242L23.8342 147.811C25.7883 145.857 25.7883 142.688 23.8342 140.721L4.78187 121.666C-1.09293 115.791 -1.09293 106.282 4.78187 100.406L34.7195 70.4527C40.5943 64.5772 50.1017 64.5772 55.9765 70.4527L75.555 90.0335C77.5091 91.9879 80.6782 91.9879 82.6448 90.0335L124.144 48.5292C126.098 46.5749 126.098 43.4054 124.144 41.4385L115.463 32.7568C113.509 30.8025 110.34 30.8025 108.374 32.7568L99.8683 41.2632C97.9143 43.2175 94.7451 43.2175 92.7785 41.2632L82.1438 30.6271C80.1897 28.6728 80.1897 25.5033 82.1438 23.5364L101.271 4.40662C107.146 -1.46887 116.653 -1.46887 122.528 4.40662L152.478 34.3604C158.353 40.2359 158.353 49.7444 152.478 55.6199L82.6323 125.474C80.6782 127.429 77.5091 127.429 75.5425 125.474L48.8741 98.8029C46.9201 96.8486 43.7509 96.8486 41.7843 98.8029L33.1036 107.485C31.1496 109.439 31.1496 112.608 33.1036 114.575L59.2458 140.721C61.1999 142.675 61.1999 145.844 59.2458 147.811L32.7404 174.32C30.7863 176.274 30.7863 179.444 32.7404 181.411L41.6841 190.355C43.6382 192.31 46.8073 192.31 48.7739 190.355L168.136 70.9789C174.011 65.1034 183.518 65.1034 189.393 70.9789L219.594 101.183C225.469 107.059 225.469 116.567 219.594 122.443L198.537 143.502C196.583 145.456 196.583 148.626 198.537 150.592L218.053 170.111C223.928 175.986 223.928 185.495 218.053 191.37L190.37 219.056C184.495 224.932 174.988 224.932 169.113 219.056L149.597 199.538C147.643 197.584 144.474 197.584 142.508 199.538L99.8057 242.245C97.8516 244.2 97.8516 247.369 99.8057 249.336L108.699 258.231C110.653 260.185 113.823 260.185 115.789 258.231L122.954 251.065C124.908 249.11 128.077 249.11 130.044 251.065L140.679 261.701C142.633 263.655 142.633 266.825 140.679 268.791L122.879 286.593C117.004 292.469 107.497 292.469 101.622 286.593L101.635 286.568Z"
19 | fill="currentColor"
20 | />
21 | </svg>
22 | );
23 | };
24 |
```
--------------------------------------------------------------------------------
/tests/integration/profiles/cline-init-functionality.test.js:
--------------------------------------------------------------------------------
```javascript
1 | import fs from 'fs';
2 | import path from 'path';
3 | import { clineProfile } from '../../../src/profiles/cline.js';
4 |
5 | describe('Cline Profile Initialization Functionality', () => {
6 | let clineProfileContent;
7 |
8 | beforeAll(() => {
9 | const clineJsPath = path.join(process.cwd(), 'src', 'profiles', 'cline.js');
10 | clineProfileContent = fs.readFileSync(clineJsPath, 'utf8');
11 | });
12 |
13 | test('cline.js uses factory pattern with correct configuration', () => {
14 | // Check for explicit, non-default values in the source file
15 | expect(clineProfileContent).toContain("name: 'cline'");
16 | expect(clineProfileContent).toContain("displayName: 'Cline'");
17 | expect(clineProfileContent).toContain("profileDir: '.clinerules'"); // non-default
18 | expect(clineProfileContent).toContain("rulesDir: '.clinerules'"); // non-default
19 | expect(clineProfileContent).toContain('mcpConfig: false'); // non-default
20 |
21 | // Check the final computed properties on the profile object
22 | expect(clineProfile.profileName).toBe('cline');
23 | expect(clineProfile.displayName).toBe('Cline');
24 | expect(clineProfile.profileDir).toBe('.clinerules');
25 | expect(clineProfile.rulesDir).toBe('.clinerules');
26 | expect(clineProfile.mcpConfig).toBe(false);
27 | expect(clineProfile.mcpConfigName).toBe(null);
28 | });
29 |
30 | test('cline.js configures .mdc to .md extension mapping', () => {
31 | // Check that the profile object has the correct file mapping behavior (cline converts to .md)
32 | expect(clineProfile.fileMap['rules/cursor_rules.mdc']).toBe(
33 | 'cline_rules.md'
34 | );
35 | });
36 |
37 | test('cline.js uses standard tool mappings', () => {
38 | // Check that the profile uses default tool mappings (equivalent to COMMON_TOOL_MAPPINGS.STANDARD)
39 | // This verifies the architectural pattern: no custom toolMappings = standard tool names
40 | expect(clineProfileContent).not.toContain('toolMappings:');
41 | expect(clineProfileContent).not.toContain('apply_diff');
42 | expect(clineProfileContent).not.toContain('search_files');
43 |
44 | // Verify the result: default mappings means tools keep their original names
45 | expect(clineProfile.conversionConfig.toolNames.edit_file).toBe('edit_file');
46 | expect(clineProfile.conversionConfig.toolNames.search).toBe('search');
47 | });
48 |
49 | test('cline.js has custom file mapping for cursor_rules.mdc', () => {
50 | // Check actual behavior - cline gets default rule files
51 | expect(Object.keys(clineProfile.fileMap)).toContain(
52 | 'rules/cursor_rules.mdc'
53 | );
54 | expect(clineProfile.fileMap['rules/cursor_rules.mdc']).toBe(
55 | 'cline_rules.md'
56 | );
57 | });
58 |
59 | test('cline.js uses createProfile factory function', () => {
60 | expect(clineProfileContent).toContain('createProfile');
61 | expect(clineProfileContent).toContain('export const clineProfile');
62 | });
63 | });
64 |
```
--------------------------------------------------------------------------------
/apps/docs/getting-started/quick-start/tasks-quick.mdx:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Tasks Setup
3 | sidebarTitle: "Tasks Setup"
4 | ---
5 | Now that your tasks are generated you can review the plan and prepare for execution.
6 |
7 | <Tip>
8 | Not all of the setup steps are required but they are recommended in order to ensure your coding agents work on accurate tasks.
9 | </Tip>
10 |
11 | ## Expand Tasks
12 | Used to add detail to tasks and create subtasks. We recommend expanding all tasks using the MCP request below:
13 | ```
14 | Expand all tasks into subtasks.
15 | ```
16 | The agent will execute
17 | ```bash
18 | task-master expand --all
19 | ```
20 | ## List/Show Tasks
21 |
22 | Used to view task details. It is important to review the plan and ensure it makes sense in your project. Check for correct folder structures, dependencies, out of scope subtasks, etc.
23 |
24 | To see a list of tasks and descriptions use the following command:
25 |
26 | ```
27 | List all pending tasks so I can review.
28 | ```
29 | To see all tasks in the CLI you can use:
30 | ```bash
31 | task-master list
32 | ```
33 |
34 | To see all implementation details of an individual task, including subtasks and testing strategy, you can use Show Task:
35 |
36 | ```
37 | Show task 2 so I can review.
38 | ```
39 |
40 | ```bash
41 | task-master show --id=<##>
42 | ```
43 |
44 | ## Update Tasks
45 |
46 | If the task details need to be edited you can update the task using this request:
47 |
48 | ```
49 | Update Task 2 to use Postgres instead of MongoDB and remove the sharding subtask
50 | ```
51 | Or this CLI command:
52 |
53 | ```bash
54 | task-master update-task --id=2 --prompt="use Postgres instead of MongoDB and remove the sharding subtask"
55 | ```
56 | ## Analyze complexity
57 |
58 | Task Master can provide a complexity report which can be helpful to read before you begin. If you didn't already expand all your tasks, it could help identify which could be broken down further with subtasks.
59 |
60 | ```
61 | Can you analyze the complexity of our tasks to help me understand which ones need to be broken down further?
62 | ```
63 |
64 | The agent will use the `analyze_project_complexity` MCP tool, or you can run it directly with the CLI command:
65 | ```bash
66 | task-master analyze-complexity
67 | ```
68 |
69 | For more comprehensive analysis using your configured research model, you can use:
70 | ```bash
71 | task-master analyze-complexity --research
72 | ```
73 |
74 | <Tip>
75 | The `--research` flag uses whatever research model you have configured in `.taskmaster/config.json` (configurable via `task-master models --setup`) for research-backed complexity analysis, providing more informed recommendations.
76 | </Tip>
77 |
78 | You can view the report in a friendly table using:
79 | ```
80 | Can you show me the complexity report in a more readable format?
81 | ```
82 |
83 | For more detailed CLI options, see the [Analyze Task Complexity](/capabilities/cli-root-commands#analyze-task-complexity) section.
84 |
85 | <Check>Now you are ready to begin [executing tasks](/getting-started/quick-start/execute-quick)</Check>
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/remove-task.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/remove-task.js
3 | * Tool to remove a task by ID
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | handleApiResult,
9 | createErrorResponse,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { removeTaskDirect } 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 remove-task tool with the MCP server
18 | * @param {Object} server - FastMCP server instance
19 | */
20 | export function registerRemoveTaskTool(server) {
21 | server.addTool({
22 | name: 'remove_task',
23 | description: 'Remove a task or subtask permanently from the tasks list',
24 | parameters: z.object({
25 | id: z
26 | .string()
27 | .describe(
28 | "ID of the task or subtask to remove (e.g., '5' or '5.2'). Can be comma-separated to update multiple tasks/subtasks at once."
29 | ),
30 | file: z.string().optional().describe('Absolute path to the tasks file'),
31 | projectRoot: z
32 | .string()
33 | .describe('The directory of the project. Must be an absolute path.'),
34 | confirm: z
35 | .boolean()
36 | .optional()
37 | .describe('Whether to skip confirmation prompt (default: false)'),
38 | tag: z
39 | .string()
40 | .optional()
41 | .describe(
42 | 'Specify which tag context to operate on. Defaults to the current active tag.'
43 | )
44 | }),
45 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
46 | try {
47 | log.info(`Removing task(s) with ID(s): ${args.id}`);
48 |
49 | const resolvedTag = resolveTag({
50 | projectRoot: args.projectRoot,
51 | tag: args.tag
52 | });
53 |
54 | // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
55 | let tasksJsonPath;
56 | try {
57 | tasksJsonPath = findTasksPath(
58 | { projectRoot: args.projectRoot, file: args.file },
59 | log
60 | );
61 | } catch (error) {
62 | log.error(`Error finding tasks.json: ${error.message}`);
63 | return createErrorResponse(
64 | `Failed to find tasks.json: ${error.message}`
65 | );
66 | }
67 |
68 | log.info(`Using tasks file path: ${tasksJsonPath}`);
69 |
70 | const result = await removeTaskDirect(
71 | {
72 | tasksJsonPath: tasksJsonPath,
73 | id: args.id,
74 | projectRoot: args.projectRoot,
75 | tag: resolvedTag
76 | },
77 | log,
78 | { session }
79 | );
80 |
81 | if (result.success) {
82 | log.info(`Successfully removed task: ${args.id}`);
83 | } else {
84 | log.error(`Failed to remove task: ${result.error.message}`);
85 | }
86 |
87 | return handleApiResult(
88 | result,
89 | log,
90 | 'Error removing task',
91 | undefined,
92 | args.projectRoot
93 | );
94 | } catch (error) {
95 | log.error(`Error in remove-task tool: ${error.message}`);
96 | return createErrorResponse(`Failed to remove task: ${error.message}`);
97 | }
98 | })
99 | });
100 | }
101 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/scope-up.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/scope-up.js
3 | * Tool to scope up task complexity
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | createErrorResponse,
9 | handleApiResult,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { scopeUpDirect } 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 scopeUp tool with the MCP server
18 | * @param {Object} server - FastMCP server instance
19 | */
20 | export function registerScopeUpTool(server) {
21 | server.addTool({
22 | name: 'scope_up_task',
23 | description: 'Increase the complexity of one or more tasks using AI',
24 | parameters: z.object({
25 | id: z
26 | .string()
27 | .describe(
28 | 'Comma-separated list of task IDs to scope up (e.g., "1,3,5")'
29 | ),
30 | strength: z
31 | .string()
32 | .optional()
33 | .describe(
34 | 'Strength level: light, regular, or heavy (default: regular)'
35 | ),
36 | prompt: z
37 | .string()
38 | .optional()
39 | .describe('Custom prompt for specific scoping adjustments'),
40 | file: z
41 | .string()
42 | .optional()
43 | .describe('Path to the tasks file (default: tasks/tasks.json)'),
44 | projectRoot: z
45 | .string()
46 | .describe('The directory of the project. Must be an absolute path.'),
47 | tag: z.string().optional().describe('Tag context to operate on'),
48 | research: z
49 | .boolean()
50 | .optional()
51 | .describe('Whether to use research capabilities for scoping')
52 | }),
53 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
54 | try {
55 | log.info(`Starting scope-up with args: ${JSON.stringify(args)}`);
56 |
57 | const resolvedTag = resolveTag({
58 | projectRoot: args.projectRoot,
59 | tag: args.tag
60 | });
61 |
62 | // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
63 | let tasksJsonPath;
64 | try {
65 | tasksJsonPath = findTasksPath(
66 | { projectRoot: args.projectRoot, file: args.file },
67 | log
68 | );
69 | } catch (error) {
70 | log.error(`Error finding tasks.json: ${error.message}`);
71 | return createErrorResponse(
72 | `Failed to find tasks.json: ${error.message}`
73 | );
74 | }
75 |
76 | // Call the direct function
77 | const result = await scopeUpDirect(
78 | {
79 | tasksJsonPath: tasksJsonPath,
80 | id: args.id,
81 | strength: args.strength,
82 | prompt: args.prompt,
83 | research: args.research,
84 | projectRoot: args.projectRoot,
85 | tag: resolvedTag
86 | },
87 | log,
88 | { session }
89 | );
90 |
91 | return handleApiResult(
92 | result,
93 | log,
94 | 'Error scoping up task',
95 | undefined,
96 | args.projectRoot
97 | );
98 | } catch (error) {
99 | log.error(`Error in scope-up tool: ${error.message}`);
100 | return createErrorResponse(error.message);
101 | }
102 | })
103 | });
104 | }
105 |
```
--------------------------------------------------------------------------------
/mcp-server/src/custom-sdk/message-converter.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * src/ai-providers/custom-sdk/mcp/message-converter.js
3 | *
4 | * Message conversion utilities for converting between AI SDK prompt format
5 | * and MCP sampling format.
6 | */
7 |
8 | /**
9 | * Convert AI SDK prompt format to MCP sampling format
10 | * @param {Array} prompt - AI SDK prompt array
11 | * @returns {object} MCP format with messages and systemPrompt
12 | */
13 | export function convertToMCPFormat(prompt) {
14 | const messages = [];
15 | let systemPrompt = '';
16 |
17 | for (const message of prompt) {
18 | if (message.role === 'system') {
19 | // Extract system prompt
20 | systemPrompt = extractTextContent(message.content);
21 | } else if (message.role === 'user' || message.role === 'assistant') {
22 | // Convert user/assistant messages
23 | messages.push({
24 | role: message.role,
25 | content: {
26 | type: 'text',
27 | text: extractTextContent(message.content)
28 | }
29 | });
30 | }
31 | }
32 |
33 | return {
34 | messages,
35 | systemPrompt
36 | };
37 | }
38 |
39 | /**
40 | * Convert MCP response format to AI SDK format
41 | * @param {object} response - MCP sampling response
42 | * @returns {object} AI SDK compatible result
43 | */
44 | export function convertFromMCPFormat(response) {
45 | // Handle different possible response formats
46 | let text = '';
47 | let usage = null;
48 | let finishReason = 'stop';
49 | let warnings = [];
50 |
51 | if (typeof response === 'string') {
52 | text = response;
53 | } else if (response.content) {
54 | text = extractTextContent(response.content);
55 | usage = response.usage;
56 | finishReason = response.finishReason || 'stop';
57 | } else if (response.text) {
58 | text = response.text;
59 | usage = response.usage;
60 | finishReason = response.finishReason || 'stop';
61 | } else {
62 | // Fallback: try to extract text from response
63 | text = JSON.stringify(response);
64 | warnings.push('Unexpected MCP response format, used JSON fallback');
65 | }
66 |
67 | return {
68 | text,
69 | usage,
70 | finishReason,
71 | warnings
72 | };
73 | }
74 |
75 | /**
76 | * Extract text content from various content formats
77 | * @param {string|Array|object} content - Content in various formats
78 | * @returns {string} Extracted text
79 | */
80 | function extractTextContent(content) {
81 | if (typeof content === 'string') {
82 | return content;
83 | }
84 |
85 | if (Array.isArray(content)) {
86 | // Handle array of content parts
87 | return content
88 | .map((part) => {
89 | if (typeof part === 'string') {
90 | return part;
91 | }
92 | if (part.type === 'text' && part.text) {
93 | return part.text;
94 | }
95 | if (part.text) {
96 | return part.text;
97 | }
98 | // Skip non-text content (images, etc.)
99 | return '';
100 | })
101 | .filter((text) => text.length > 0)
102 | .join(' ');
103 | }
104 |
105 | if (content && typeof content === 'object') {
106 | if (content.type === 'text' && content.text) {
107 | return content.text;
108 | }
109 | if (content.text) {
110 | return content.text;
111 | }
112 | }
113 |
114 | // Fallback
115 | return String(content || '');
116 | }
117 |
```
--------------------------------------------------------------------------------
/src/ai-providers/grok-cli.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * grok-cli.js
3 | * AI provider implementation for Grok models using Grok CLI.
4 | */
5 |
6 | import { createGrokCli } from '@tm/ai-sdk-provider-grok-cli';
7 | import { BaseAIProvider } from './base-provider.js';
8 | import { getGrokCliSettingsForCommand } from '../../scripts/modules/config-manager.js';
9 |
10 | export class GrokCliProvider extends BaseAIProvider {
11 | constructor() {
12 | super();
13 | this.name = 'Grok CLI';
14 | // Grok CLI requires explicit JSON schema mode
15 | this.needsExplicitJsonSchema = true;
16 | // Grok CLI does not support temperature parameter
17 | this.supportsTemperature = false;
18 | }
19 |
20 | /**
21 | * Returns the environment variable name required for this provider's API key.
22 | * @returns {string} The environment variable name for the Grok API key
23 | */
24 | getRequiredApiKeyName() {
25 | return 'GROK_CLI_API_KEY';
26 | }
27 |
28 | /**
29 | * Override to indicate that API key is optional since Grok CLI can be configured separately
30 | * @returns {boolean} False since Grok CLI can use its own config
31 | */
32 | isRequiredApiKey() {
33 | return false; // Grok CLI can use its own config file
34 | }
35 |
36 | /**
37 | * Override validateAuth to be more flexible with API key validation
38 | * @param {object} params - Parameters to validate
39 | */
40 | validateAuth(params) {
41 | // Grok CLI can work with:
42 | // 1. API key passed in params
43 | // 2. Environment variable GROK_CLI_API_KEY
44 | // 3. Grok CLI's own config file (~/.grok/user-settings.json)
45 | // So we don't enforce API key requirement here
46 | // Suppress unused parameter warning
47 | void params;
48 | }
49 |
50 | /**
51 | * Creates and returns a Grok CLI client instance.
52 | * @param {object} params - Parameters for client initialization
53 | * @param {string} [params.apiKey] - Grok CLI API key (optional if configured in CLI)
54 | * @param {string} [params.baseURL] - Optional custom API endpoint
55 | * @param {string} [params.workingDirectory] - Working directory for CLI commands
56 | * @param {number} [params.timeout] - Timeout for CLI commands in milliseconds
57 | * @param {string} [params.commandName] - Name of the command invoking the service
58 | * @returns {Function} Grok CLI client function
59 | * @throws {Error} If initialization fails
60 | */
61 | getClient(params) {
62 | try {
63 | const { apiKey, baseURL, workingDirectory, timeout, commandName } =
64 | params;
65 |
66 | // Get Grok CLI settings from config
67 | const grokCliSettings = getGrokCliSettingsForCommand(commandName);
68 |
69 | return createGrokCli({
70 | defaultSettings: {
71 | apiKey,
72 | baseURL,
73 | workingDirectory:
74 | workingDirectory || grokCliSettings.workingDirectory,
75 | timeout: timeout || grokCliSettings.timeout,
76 | defaultModel: grokCliSettings.defaultModel
77 | }
78 | });
79 | } catch (error) {
80 | this.handleError('client initialization', error);
81 | }
82 | }
83 | }
84 |
```
--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/use-tag.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * use-tag.js
3 | * Direct function implementation for switching to a tag
4 | */
5 |
6 | import { useTag } 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 switching to a tag with error handling.
15 | *
16 | * @param {Object} args - Command arguments
17 | * @param {string} args.name - Name of the tag to switch to
18 | * @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
19 | * @param {string} [args.projectRoot] - Project root path
20 | * @param {Object} log - Logger object
21 | * @param {Object} context - Additional context (session)
22 | * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
23 | */
24 | export async function useTagDirect(args, log, context = {}) {
25 | // Destructure expected args
26 | const { tasksJsonPath, name, projectRoot } = args;
27 | const { session } = context;
28 |
29 | // Enable silent mode to prevent console logs from interfering with JSON response
30 | enableSilentMode();
31 |
32 | // Create logger wrapper using the utility
33 | const mcpLog = createLogWrapper(log);
34 |
35 | try {
36 | // Check if tasksJsonPath was provided
37 | if (!tasksJsonPath) {
38 | log.error('useTagDirect called without tasksJsonPath');
39 | disableSilentMode();
40 | return {
41 | success: false,
42 | error: {
43 | code: 'MISSING_ARGUMENT',
44 | message: 'tasksJsonPath is required'
45 | }
46 | };
47 | }
48 |
49 | // Check required parameters
50 | if (!name || typeof name !== 'string') {
51 | log.error('Missing required parameter: name');
52 | disableSilentMode();
53 | return {
54 | success: false,
55 | error: {
56 | code: 'MISSING_PARAMETER',
57 | message: 'Tag name is required and must be a string'
58 | }
59 | };
60 | }
61 |
62 | log.info(`Switching to tag: ${name}`);
63 |
64 | // Call the useTag function
65 | const result = await useTag(
66 | tasksJsonPath,
67 | name,
68 | {}, // options (empty for now)
69 | {
70 | session,
71 | mcpLog,
72 | projectRoot
73 | },
74 | 'json' // outputFormat - use 'json' to suppress CLI UI
75 | );
76 |
77 | // Restore normal logging
78 | disableSilentMode();
79 |
80 | return {
81 | success: true,
82 | data: {
83 | tagName: result.currentTag,
84 | switched: result.switched,
85 | previousTag: result.previousTag,
86 | taskCount: result.taskCount,
87 | message: `Successfully switched to tag "${result.currentTag}"`
88 | }
89 | };
90 | } catch (error) {
91 | // Make sure to restore normal logging even if there's an error
92 | disableSilentMode();
93 |
94 | log.error(`Error in useTagDirect: ${error.message}`);
95 | return {
96 | success: false,
97 | error: {
98 | code: error.code || 'USE_TAG_ERROR',
99 | message: error.message
100 | }
101 | };
102 | }
103 | }
104 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/add-dependency.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/add-dependency.js
3 | * Tool for adding a dependency to a task
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | handleApiResult,
9 | createErrorResponse,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { addDependencyDirect } 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 addDependency tool with the MCP server
18 | * @param {Object} server - FastMCP server instance
19 | */
20 | export function registerAddDependencyTool(server) {
21 | server.addTool({
22 | name: 'add_dependency',
23 | description: 'Add a dependency relationship between two tasks',
24 | parameters: z.object({
25 | id: z.string().describe('ID of task that will depend on another task'),
26 | dependsOn: z
27 | .string()
28 | .describe('ID of task that will become a dependency'),
29 | file: z
30 | .string()
31 | .optional()
32 | .describe(
33 | 'Absolute path to the tasks file (default: tasks/tasks.json)'
34 | ),
35 | projectRoot: z
36 | .string()
37 | .describe('The directory of the project. Must be an absolute path.'),
38 | tag: z.string().optional().describe('Tag context to operate on')
39 | }),
40 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
41 | try {
42 | log.info(
43 | `Adding dependency for task ${args.id} to depend on ${args.dependsOn}`
44 | );
45 | const resolvedTag = resolveTag({
46 | projectRoot: args.projectRoot,
47 | tag: args.tag
48 | });
49 | let tasksJsonPath;
50 | try {
51 | tasksJsonPath = findTasksPath(
52 | { projectRoot: args.projectRoot, file: args.file },
53 | log
54 | );
55 | } catch (error) {
56 | log.error(`Error finding tasks.json: ${error.message}`);
57 | return createErrorResponse(
58 | `Failed to find tasks.json: ${error.message}`
59 | );
60 | }
61 |
62 | // Call the direct function with the resolved path
63 | const result = await addDependencyDirect(
64 | {
65 | // Pass the explicitly resolved path
66 | tasksJsonPath: tasksJsonPath,
67 | // Pass other relevant args
68 | id: args.id,
69 | dependsOn: args.dependsOn,
70 | projectRoot: args.projectRoot,
71 | tag: resolvedTag
72 | },
73 | log
74 | // Remove context object
75 | );
76 |
77 | // Log result
78 | if (result.success) {
79 | log.info(`Successfully added dependency: ${result.data.message}`);
80 | } else {
81 | log.error(`Failed to add dependency: ${result.error.message}`);
82 | }
83 |
84 | // Use handleApiResult to format the response
85 | return handleApiResult(
86 | result,
87 | log,
88 | 'Error adding dependency',
89 | undefined,
90 | args.projectRoot
91 | );
92 | } catch (error) {
93 | log.error(`Error in addDependency tool: ${error.message}`);
94 | return createErrorResponse(error.message);
95 | }
96 | })
97 | });
98 | }
99 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/clear-subtasks.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/clear-subtasks.js
3 | * Tool for clearing subtasks from parent tasks
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | handleApiResult,
9 | createErrorResponse,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { clearSubtasksDirect } 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 clearSubtasks tool with the MCP server
18 | * @param {Object} server - FastMCP server instance
19 | */
20 | export function registerClearSubtasksTool(server) {
21 | server.addTool({
22 | name: 'clear_subtasks',
23 | description: 'Clear subtasks from specified tasks',
24 | parameters: z
25 | .object({
26 | id: z
27 | .string()
28 | .optional()
29 | .describe('Task IDs (comma-separated) to clear subtasks from'),
30 | all: z.boolean().optional().describe('Clear subtasks from all tasks'),
31 | file: z
32 | .string()
33 | .optional()
34 | .describe(
35 | 'Absolute path to the tasks file (default: tasks/tasks.json)'
36 | ),
37 | projectRoot: z
38 | .string()
39 | .describe('The directory of the project. Must be an absolute path.'),
40 | tag: z.string().optional().describe('Tag context to operate on')
41 | })
42 | .refine((data) => data.id || data.all, {
43 | message: "Either 'id' or 'all' parameter must be provided",
44 | path: ['id', 'all']
45 | }),
46 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
47 | try {
48 | log.info(`Clearing subtasks with args: ${JSON.stringify(args)}`);
49 |
50 | const resolvedTag = resolveTag({
51 | projectRoot: args.projectRoot,
52 | tag: args.tag
53 | });
54 |
55 | // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
56 | let tasksJsonPath;
57 | try {
58 | tasksJsonPath = findTasksPath(
59 | { projectRoot: args.projectRoot, file: args.file },
60 | log
61 | );
62 | } catch (error) {
63 | log.error(`Error finding tasks.json: ${error.message}`);
64 | return createErrorResponse(
65 | `Failed to find tasks.json: ${error.message}`
66 | );
67 | }
68 |
69 | const result = await clearSubtasksDirect(
70 | {
71 | tasksJsonPath: tasksJsonPath,
72 | id: args.id,
73 | all: args.all,
74 |
75 | projectRoot: args.projectRoot,
76 | tag: resolvedTag
77 | },
78 | log,
79 | { session }
80 | );
81 |
82 | if (result.success) {
83 | log.info(`Subtasks cleared successfully: ${result.data.message}`);
84 | } else {
85 | log.error(`Failed to clear subtasks: ${result.error.message}`);
86 | }
87 |
88 | return handleApiResult(
89 | result,
90 | log,
91 | 'Error clearing subtasks',
92 | undefined,
93 | args.projectRoot
94 | );
95 | } catch (error) {
96 | log.error(`Error in clearSubtasks tool: ${error.message}`);
97 | return createErrorResponse(error.message);
98 | }
99 | })
100 | });
101 | }
102 |
```
--------------------------------------------------------------------------------
/tests/unit/scripts/modules/utils-tag-aware-paths.test.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * Test for getTagAwareFilePath utility function
3 | * Tests the fix for Issue #850
4 | */
5 |
6 | import { getTagAwareFilePath } from '../../../../scripts/modules/utils.js';
7 | import path from 'path';
8 |
9 | describe('getTagAwareFilePath utility function', () => {
10 | const projectRoot = '/test/project';
11 | const basePath = '.taskmaster/reports/task-complexity-report.json';
12 |
13 | it('should return base path for master tag', () => {
14 | const result = getTagAwareFilePath(basePath, 'master', projectRoot);
15 | const expected = path.join(projectRoot, basePath);
16 | expect(result).toBe(expected);
17 | });
18 |
19 | it('should return base path for null tag', () => {
20 | const result = getTagAwareFilePath(basePath, null, projectRoot);
21 | const expected = path.join(projectRoot, basePath);
22 | expect(result).toBe(expected);
23 | });
24 |
25 | it('should return base path for undefined tag', () => {
26 | const result = getTagAwareFilePath(basePath, undefined, projectRoot);
27 | const expected = path.join(projectRoot, basePath);
28 | expect(result).toBe(expected);
29 | });
30 |
31 | it('should return tag-specific path for non-master tag', () => {
32 | const tag = 'feature-branch';
33 | const result = getTagAwareFilePath(basePath, tag, projectRoot);
34 | const expected = path.join(
35 | projectRoot,
36 | '.taskmaster/reports/task-complexity-report_feature-branch.json'
37 | );
38 | expect(result).toBe(expected);
39 | });
40 |
41 | it('should handle different file extensions', () => {
42 | const csvBasePath = '.taskmaster/reports/export.csv';
43 | const tag = 'dev-branch';
44 | const result = getTagAwareFilePath(csvBasePath, tag, projectRoot);
45 | const expected = path.join(
46 | projectRoot,
47 | '.taskmaster/reports/export_dev-branch.csv'
48 | );
49 | expect(result).toBe(expected);
50 | });
51 |
52 | it('should handle paths without extensions', () => {
53 | const noExtPath = '.taskmaster/reports/summary';
54 | const tag = 'test-tag';
55 | const result = getTagAwareFilePath(noExtPath, tag, projectRoot);
56 | // Since there's no extension, it should append the tag
57 | const expected = path.join(
58 | projectRoot,
59 | '.taskmaster/reports/summary_test-tag'
60 | );
61 | expect(result).toBe(expected);
62 | });
63 |
64 | it('should use default project root when not provided', () => {
65 | const tag = 'feature-tag';
66 | const result = getTagAwareFilePath(basePath, tag);
67 | const expected = path.join(
68 | '.',
69 | '.taskmaster/reports/task-complexity-report_feature-tag.json'
70 | );
71 | expect(result).toBe(expected);
72 | });
73 |
74 | it('should handle complex tag names with special characters', () => {
75 | const tag = 'feature-user-auth-v2';
76 | const result = getTagAwareFilePath(basePath, tag, projectRoot);
77 | const expected = path.join(
78 | projectRoot,
79 | '.taskmaster/reports/task-complexity-report_feature-user-auth-v2.json'
80 | );
81 | expect(result).toBe(expected);
82 | });
83 | });
84 |
```
--------------------------------------------------------------------------------
/mcp-server/src/tools/scope-down.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * tools/scope-down.js
3 | * Tool to scope down task complexity
4 | */
5 |
6 | import { z } from 'zod';
7 | import {
8 | createErrorResponse,
9 | handleApiResult,
10 | withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { scopeDownDirect } 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 scopeDown tool with the MCP server
18 | * @param {Object} server - FastMCP server instance
19 | */
20 | export function registerScopeDownTool(server) {
21 | server.addTool({
22 | name: 'scope_down_task',
23 | description: 'Decrease the complexity of one or more tasks using AI',
24 | parameters: z.object({
25 | id: z
26 | .string()
27 | .describe(
28 | 'Comma-separated list of task IDs to scope down (e.g., "1,3,5")'
29 | ),
30 | strength: z
31 | .string()
32 | .optional()
33 | .describe(
34 | 'Strength level: light, regular, or heavy (default: regular)'
35 | ),
36 | prompt: z
37 | .string()
38 | .optional()
39 | .describe('Custom prompt for specific scoping adjustments'),
40 | file: z
41 | .string()
42 | .optional()
43 | .describe('Path to the tasks file (default: tasks/tasks.json)'),
44 | projectRoot: z
45 | .string()
46 | .describe('The directory of the project. Must be an absolute path.'),
47 | tag: z.string().optional().describe('Tag context to operate on'),
48 | research: z
49 | .boolean()
50 | .optional()
51 | .describe('Whether to use research capabilities for scoping')
52 | }),
53 | execute: withNormalizedProjectRoot(async (args, { log, session }) => {
54 | try {
55 | log.info(`Starting scope-down with args: ${JSON.stringify(args)}`);
56 |
57 | const resolvedTag = resolveTag({
58 | projectRoot: args.projectRoot,
59 | tag: args.tag
60 | });
61 |
62 | // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
63 | let tasksJsonPath;
64 | try {
65 | tasksJsonPath = findTasksPath(
66 | { projectRoot: args.projectRoot, file: args.file },
67 | log
68 | );
69 | } catch (error) {
70 | log.error(`Error finding tasks.json: ${error.message}`);
71 | return createErrorResponse(
72 | `Failed to find tasks.json: ${error.message}`
73 | );
74 | }
75 |
76 | // Call the direct function
77 | const result = await scopeDownDirect(
78 | {
79 | tasksJsonPath: tasksJsonPath,
80 | id: args.id,
81 | strength: args.strength,
82 | prompt: args.prompt,
83 | research: args.research,
84 | projectRoot: args.projectRoot,
85 | tag: resolvedTag
86 | },
87 | log,
88 | { session }
89 | );
90 |
91 | return handleApiResult(
92 | result,
93 | log,
94 | 'Error scoping down task',
95 | undefined,
96 | args.projectRoot
97 | );
98 | } catch (error) {
99 | log.error(`Error in scope-down tool: ${error.message}`);
100 | return createErrorResponse(error.message);
101 | }
102 | })
103 | });
104 | }
105 |
```
--------------------------------------------------------------------------------
/packages/tm-core/src/modules/execution/services/executor-service.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Service for managing task execution
3 | */
4 |
5 | import { getLogger } from '../../../common/logger/index.js';
6 | import type { Task } from '../../../common/types/index.js';
7 | import { ExecutorFactory } from '../executors/executor-factory.js';
8 | import type {
9 | ExecutionResult,
10 | ExecutorOptions,
11 | ExecutorType,
12 | ITaskExecutor
13 | } from '../types.js';
14 |
15 | export interface ExecutorServiceOptions {
16 | projectRoot: string;
17 | defaultExecutor?: ExecutorType;
18 | executorConfig?: Record<string, any>;
19 | }
20 |
21 | export class ExecutorService {
22 | private logger = getLogger('ExecutorService');
23 | private projectRoot: string;
24 | private defaultExecutor?: ExecutorType;
25 | private executorConfig: Record<string, any>;
26 | private currentExecutor?: ITaskExecutor;
27 |
28 | constructor(options: ExecutorServiceOptions) {
29 | this.projectRoot = options.projectRoot;
30 | this.defaultExecutor = options.defaultExecutor;
31 | this.executorConfig = options.executorConfig || {};
32 | }
33 |
34 | /**
35 | * Execute a task
36 | */
37 | async executeTask(
38 | task: Task,
39 | executorType?: ExecutorType
40 | ): Promise<ExecutionResult> {
41 | try {
42 | // Determine executor type
43 | const type =
44 | executorType ||
45 | this.defaultExecutor ||
46 | (await ExecutorFactory.getDefaultExecutor(this.projectRoot));
47 | if (!type) {
48 | return {
49 | success: false,
50 | taskId: task.id,
51 | executorType: 'claude',
52 | error:
53 | 'No executor available. Please install Claude CLI or specify an executor type.',
54 | startTime: new Date().toISOString()
55 | };
56 | }
57 |
58 | // Create executor
59 | const executorOptions: ExecutorOptions = {
60 | type,
61 | projectRoot: this.projectRoot,
62 | config: this.executorConfig
63 | };
64 |
65 | this.currentExecutor = ExecutorFactory.create(executorOptions);
66 |
67 | // Check if executor is available
68 | const isAvailable = await this.currentExecutor.isAvailable();
69 | if (!isAvailable) {
70 | return {
71 | success: false,
72 | taskId: task.id,
73 | executorType: type,
74 | error: `Executor ${type} is not available or not configured properly`,
75 | startTime: new Date().toISOString()
76 | };
77 | }
78 |
79 | // Execute the task
80 | this.logger.info(`Starting task ${task.id} with ${type} executor`);
81 | const result = await this.currentExecutor.execute(task);
82 |
83 | return result;
84 | } catch (error: any) {
85 | this.logger.error(`Failed to execute task ${task.id}:`, error);
86 | return {
87 | success: false,
88 | taskId: task.id,
89 | executorType: executorType || 'claude',
90 | error: error.message || 'Unknown error occurred',
91 | startTime: new Date().toISOString()
92 | };
93 | }
94 | }
95 |
96 | /**
97 | * Stop the current task execution
98 | */
99 | async stopCurrentTask(): Promise<void> {
100 | if (this.currentExecutor && this.currentExecutor.stop) {
101 | await this.currentExecutor.stop();
102 | this.currentExecutor = undefined;
103 | }
104 | }
105 | }
106 |
```