This is page 23 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
--------------------------------------------------------------------------------
/.taskmaster/reports/task-complexity-report_tdd-workflow-phase-0.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "meta": {
3 | "generatedAt": "2025-10-07T14:16:40.283Z",
4 | "tasksAnalyzed": 10,
5 | "totalTasks": 10,
6 | "analysisCount": 10,
7 | "thresholdScore": 5,
8 | "projectName": "Taskmaster",
9 | "usedResearch": false
10 | },
11 | "complexityAnalysis": [
12 | {
13 | "taskId": 1,
14 | "taskTitle": "Create autopilot command CLI skeleton",
15 | "complexityScore": 4,
16 | "recommendedSubtasks": 3,
17 | "expansionPrompt": "Break down the autopilot command creation into: 1) Create AutopilotCommand class extending Commander.Command with proper argument parsing and options, 2) Implement command structure with help text and validation following existing patterns, 3) Add basic registration method and placeholder action handler",
18 | "reasoning": "Medium complexity due to following established patterns in the codebase. The command-registry.ts and start.command.ts provide clear templates for implementation. Main complexity is argument parsing and option validation."
19 | },
20 | {
21 | "taskId": 2,
22 | "taskTitle": "Implement preflight detection system",
23 | "complexityScore": 7,
24 | "recommendedSubtasks": 5,
25 | "expansionPrompt": "Create PreflightChecker with these subtasks: 1) Package.json test script detection and validation, 2) Git working tree status checking using system commands, 3) Tool availability validation (git, gh, node/npm), 4) Default branch detection via git commands, 5) Structured result reporting with success/failure indicators and error messages",
26 | "reasoning": "High complexity due to system integration requirements. Needs to interact with multiple external tools (git, npm, gh), parse various file formats, and handle different system configurations. Error handling for missing tools adds complexity."
27 | },
28 | {
29 | "taskId": 3,
30 | "taskTitle": "Implement task loading and validation",
31 | "complexityScore": 5,
32 | "recommendedSubtasks": 3,
33 | "expansionPrompt": "Implement task loading: 1) Use existing TaskService from @tm/core to load tasks by ID with proper error handling, 2) Validate task structure including subtask existence and dependency validation, 3) Provide user-friendly error messages for missing tasks or need to expand subtasks first",
34 | "reasoning": "Medium-high complexity. While leveraging existing TaskService reduces implementation effort, the validation logic for subtasks and dependencies requires careful handling of edge cases. Task structure validation adds complexity."
35 | },
36 | {
37 | "taskId": 4,
38 | "taskTitle": "Create execution plan display logic",
39 | "complexityScore": 6,
40 | "recommendedSubtasks": 4,
41 | "expansionPrompt": "Build ExecutionPlanDisplay: 1) Create display formatter using boxen and chalk for consistent CLI styling, 2) Format preflight check results with color-coded status indicators, 3) Display subtask execution order with RED/GREEN/COMMIT phase visualization, 4) Show branch/tag info and finalization steps with duration estimates",
42 | "reasoning": "Moderate-high complexity due to complex formatting requirements and dependency on multiple other components. The display needs to coordinate information from preflight, task validation, and execution planning. CLI styling consistency adds complexity."
43 | },
44 | {
45 | "taskId": 5,
46 | "taskTitle": "Implement branch and tag planning",
47 | "complexityScore": 3,
48 | "recommendedSubtasks": 2,
49 | "expansionPrompt": "Create BranchPlanner: 1) Implement branch name generation using pattern <tag>/task-<id>-<slug> with kebab-case conversion and special character handling, 2) Add TaskMaster config integration to determine active tag and handle existing branch conflicts",
50 | "reasoning": "Low-medium complexity. String manipulation and naming convention implementation is straightforward. The main complexity is handling edge cases with special characters and existing branch conflicts."
51 | },
52 | {
53 | "taskId": 6,
54 | "taskTitle": "Create subtask execution order calculation",
55 | "complexityScore": 8,
56 | "recommendedSubtasks": 4,
57 | "expansionPrompt": "Implement dependency resolution: 1) Build dependency graph from subtask data with proper parsing, 2) Implement topological sort algorithm for execution order, 3) Add circular dependency detection with clear error reporting, 4) Create parallel execution grouping for independent subtasks",
58 | "reasoning": "High complexity due to graph algorithms and dependency resolution. Topological sorting, circular dependency detection, and parallel grouping require algorithmic sophistication. Edge cases in dependency chains add significant complexity."
59 | },
60 | {
61 | "taskId": 7,
62 | "taskTitle": "Implement TDD phase planning for subtasks",
63 | "complexityScore": 6,
64 | "recommendedSubtasks": 4,
65 | "expansionPrompt": "Create TDDPhasePlanner: 1) Implement test file path detection for common project structures (src/, tests/, __tests__), 2) Parse implementation files from subtask details and descriptions, 3) Generate conventional commit messages for RED/GREEN/COMMIT phases, 4) Add implementation complexity estimation based on subtask content",
66 | "reasoning": "Moderate-high complexity due to project structure detection and file path inference. Conventional commit message generation and complexity estimation require understanding of different project layouts and parsing subtask content effectively."
67 | },
68 | {
69 | "taskId": 8,
70 | "taskTitle": "Add finalization steps planning",
71 | "complexityScore": 4,
72 | "recommendedSubtasks": 3,
73 | "expansionPrompt": "Create FinalizationPlanner: 1) Implement test suite execution planning with coverage threshold detection from package.json, 2) Add git operations planning (branch push, PR creation) using existing git patterns, 3) Create duration estimation algorithm based on subtask count and complexity metrics",
74 | "reasoning": "Medium complexity. Building on existing git utilities and test command detection reduces complexity. Main challenges are coverage threshold parsing and duration estimation algorithms."
75 | },
76 | {
77 | "taskId": 9,
78 | "taskTitle": "Integrate command with existing CLI infrastructure",
79 | "complexityScore": 3,
80 | "recommendedSubtasks": 2,
81 | "expansionPrompt": "Complete CLI integration: 1) Add AutopilotCommand to command-registry.ts following existing patterns and update command metadata, 2) Test command registration and help system integration with proper cleanup and error handling",
82 | "reasoning": "Low-medium complexity. The command-registry.ts provides a clear pattern to follow. Main work is registration and ensuring proper integration with existing CLI infrastructure. Well-established patterns reduce complexity."
83 | },
84 | {
85 | "taskId": 10,
86 | "taskTitle": "Add comprehensive error handling and edge cases",
87 | "complexityScore": 7,
88 | "recommendedSubtasks": 5,
89 | "expansionPrompt": "Implement error handling: 1) Add missing task and invalid task structure error handling with helpful messages, 2) Handle git state errors (dirty working tree, missing tools), 3) Add dependency validation errors (circular, invalid references), 4) Implement missing tool detection with installation guidance, 5) Create user-friendly error messages following existing CLI patterns",
90 | "reasoning": "High complexity due to comprehensive error scenarios. Each component (preflight, task loading, dependency resolution) has multiple failure modes that need proper handling. Providing helpful error messages and recovery suggestions adds complexity."
91 | }
92 | ]
93 | }
94 |
```
--------------------------------------------------------------------------------
/src/progress/base-progress-tracker.js:
--------------------------------------------------------------------------------
```javascript
1 | import { newMultiBar } from './cli-progress-factory.js';
2 |
3 | /**
4 | * Base class for progress trackers, handling common logic for time, tokens, estimation, and multibar management.
5 | */
6 | export class BaseProgressTracker {
7 | constructor(options = {}) {
8 | this.numUnits = options.numUnits || 1;
9 | this.unitName = options.unitName || 'unit'; // e.g., 'task', 'subtask'
10 | this.startTime = null;
11 | this.completedUnits = 0;
12 | this.tokensIn = 0;
13 | this.tokensOut = 0;
14 | this.isEstimate = true; // For token display
15 |
16 | // Time estimation properties
17 | this.bestAvgTimePerUnit = null;
18 | this.lastEstimateTime = null;
19 | this.lastEstimateSeconds = 0;
20 |
21 | // UI components
22 | this.multibar = null;
23 | this.timeTokensBar = null;
24 | this.progressBar = null;
25 | this._timerInterval = null;
26 |
27 | // State flags
28 | this.isStarted = false;
29 | this.isFinished = false;
30 |
31 | // Allow subclasses to define custom properties
32 | this._initializeCustomProperties(options);
33 | }
34 |
35 | /**
36 | * Protected method for subclasses to initialize custom properties.
37 | * @protected
38 | */
39 | _initializeCustomProperties(options) {
40 | // Subclasses can override this
41 | }
42 |
43 | /**
44 | * Get the pluralized form of the unit name for safe property keys.
45 | * @returns {string} Pluralized unit name
46 | */
47 | get unitNamePlural() {
48 | return `${this.unitName}s`;
49 | }
50 |
51 | start() {
52 | if (this.isStarted || this.isFinished) return;
53 |
54 | this.isStarted = true;
55 | this.startTime = Date.now();
56 |
57 | this.multibar = newMultiBar();
58 |
59 | // Create time/tokens bar using subclass-provided format
60 | this.timeTokensBar = this.multibar.create(
61 | 1,
62 | 0,
63 | {},
64 | {
65 | format: this._getTimeTokensBarFormat(),
66 | barsize: 1,
67 | hideCursor: true,
68 | clearOnComplete: false
69 | }
70 | );
71 |
72 | // Create main progress bar using subclass-provided format
73 | this.progressBar = this.multibar.create(
74 | this.numUnits,
75 | 0,
76 | {},
77 | {
78 | format: this._getProgressBarFormat(),
79 | barCompleteChar: '\u2588',
80 | barIncompleteChar: '\u2591'
81 | }
82 | );
83 |
84 | this._updateTimeTokensBar();
85 | this.progressBar.update(0, { [this.unitNamePlural]: `0/${this.numUnits}` });
86 |
87 | // Start timer
88 | this._timerInterval = setInterval(() => this._updateTimeTokensBar(), 1000);
89 |
90 | // Allow subclasses to add custom bars or setup
91 | this._setupCustomUI();
92 | }
93 |
94 | /**
95 | * Protected method for subclasses to add custom UI elements after start.
96 | * @protected
97 | */
98 | _setupCustomUI() {
99 | // Subclasses can override this
100 | }
101 |
102 | /**
103 | * Protected method to get the format for the time/tokens bar.
104 | * @protected
105 | * @returns {string} Format string for the time/tokens bar.
106 | */
107 | _getTimeTokensBarFormat() {
108 | return `{clock} {elapsed} | Tokens (I/O): {in}/{out} | Est: {remaining}`;
109 | }
110 |
111 | /**
112 | * Protected method to get the format for the main progress bar.
113 | * @protected
114 | * @returns {string} Format string for the progress bar.
115 | */
116 | _getProgressBarFormat() {
117 | return `${this.unitName.charAt(0).toUpperCase() + this.unitName.slice(1)}s {${this.unitNamePlural}} |{bar}| {percentage}%`;
118 | }
119 |
120 | updateTokens(tokensIn, tokensOut, isEstimate = false) {
121 | this.tokensIn = tokensIn || 0;
122 | this.tokensOut = tokensOut || 0;
123 | this.isEstimate = isEstimate;
124 | this._updateTimeTokensBar();
125 | }
126 |
127 | _updateTimeTokensBar() {
128 | if (!this.timeTokensBar || this.isFinished) return;
129 |
130 | const elapsed = this._formatElapsedTime();
131 | const remaining = this._estimateRemainingTime();
132 | const tokensLabel = this.isEstimate ? '~ Tokens (I/O)' : 'Tokens (I/O)';
133 |
134 | this.timeTokensBar.update(1, {
135 | clock: '⏱️',
136 | elapsed,
137 | in: this.tokensIn,
138 | out: this.tokensOut,
139 | remaining,
140 | tokensLabel,
141 | // Subclasses can add more payload here via override
142 | ...this._getCustomTimeTokensPayload()
143 | });
144 | }
145 |
146 | /**
147 | * Protected method for subclasses to provide custom payload for time/tokens bar.
148 | * @protected
149 | * @returns {Object} Custom payload object.
150 | */
151 | _getCustomTimeTokensPayload() {
152 | return {};
153 | }
154 |
155 | _formatElapsedTime() {
156 | if (!this.startTime) return '0m 00s';
157 | const seconds = Math.floor((Date.now() - this.startTime) / 1000);
158 | const minutes = Math.floor(seconds / 60);
159 | const remainingSeconds = seconds % 60;
160 | return `${minutes}m ${remainingSeconds.toString().padStart(2, '0')}s`;
161 | }
162 |
163 | _estimateRemainingTime() {
164 | const progress = this._getProgressFraction();
165 | if (progress >= 1) return '~0s';
166 |
167 | const now = Date.now();
168 | const elapsed = (now - this.startTime) / 1000;
169 |
170 | if (progress === 0) return '~calculating...';
171 |
172 | const avgTimePerUnit = elapsed / progress;
173 |
174 | if (
175 | this.bestAvgTimePerUnit === null ||
176 | avgTimePerUnit < this.bestAvgTimePerUnit
177 | ) {
178 | this.bestAvgTimePerUnit = avgTimePerUnit;
179 | }
180 |
181 | const remainingUnits = this.numUnits * (1 - progress);
182 | let estimatedSeconds = Math.ceil(remainingUnits * this.bestAvgTimePerUnit);
183 |
184 | // Stabilization logic
185 | if (this.lastEstimateTime) {
186 | const elapsedSinceEstimate = Math.floor(
187 | (now - this.lastEstimateTime) / 1000
188 | );
189 | const countdownSeconds = Math.max(
190 | 0,
191 | this.lastEstimateSeconds - elapsedSinceEstimate
192 | );
193 | if (countdownSeconds === 0) return '~0s';
194 | estimatedSeconds = Math.min(estimatedSeconds, countdownSeconds);
195 | }
196 |
197 | this.lastEstimateTime = now;
198 | this.lastEstimateSeconds = estimatedSeconds;
199 |
200 | return `~${this._formatDuration(estimatedSeconds)}`;
201 | }
202 |
203 | /**
204 | * Protected method for subclasses to calculate current progress fraction (0-1).
205 | * Defaults to simple completedUnits / numUnits.
206 | * @protected
207 | * @returns {number} Progress fraction (can be fractional for subtasks).
208 | */
209 | _getProgressFraction() {
210 | return this.completedUnits / this.numUnits;
211 | }
212 |
213 | _formatDuration(seconds) {
214 | if (seconds < 60) return `${seconds}s`;
215 | const minutes = Math.floor(seconds / 60);
216 | const remainingSeconds = seconds % 60;
217 | if (minutes < 60) {
218 | return remainingSeconds > 0
219 | ? `${minutes}m ${remainingSeconds}s`
220 | : `${minutes}m`;
221 | }
222 | const hours = Math.floor(minutes / 60);
223 | const remainingMinutes = minutes % 60;
224 | return `${hours}h ${remainingMinutes}m`;
225 | }
226 |
227 | getElapsedTime() {
228 | return this.startTime ? Date.now() - this.startTime : 0;
229 | }
230 |
231 | stop() {
232 | if (this.isFinished) return;
233 |
234 | this.isFinished = true;
235 |
236 | if (this._timerInterval) {
237 | clearInterval(this._timerInterval);
238 | this._timerInterval = null;
239 | }
240 |
241 | if (this.multibar) {
242 | this._updateTimeTokensBar();
243 | this.multibar.stop();
244 | }
245 |
246 | // Ensure cleanup is called to prevent memory leaks
247 | this.cleanup();
248 | }
249 |
250 | getSummary() {
251 | return {
252 | completedUnits: this.completedUnits,
253 | elapsedTime: this.getElapsedTime()
254 | // Subclasses should extend this
255 | };
256 | }
257 |
258 | /**
259 | * Cleanup method to ensure proper resource disposal and prevent memory leaks.
260 | * Should be called when the progress tracker is no longer needed.
261 | */
262 | cleanup() {
263 | // Stop any active timers
264 | if (this._timerInterval) {
265 | clearInterval(this._timerInterval);
266 | this._timerInterval = null;
267 | }
268 |
269 | // Stop and clear multibar
270 | if (this.multibar) {
271 | try {
272 | this.multibar.stop();
273 | } catch (error) {
274 | // Ignore errors during cleanup
275 | }
276 | this.multibar = null;
277 | }
278 |
279 | // Clear progress bar references
280 | this.timeTokensBar = null;
281 | this.progressBar = null;
282 |
283 | // Reset state
284 | this.isStarted = false;
285 | this.isFinished = true;
286 |
287 | // Allow subclasses to perform custom cleanup
288 | this._performCustomCleanup();
289 | }
290 |
291 | /**
292 | * Protected method for subclasses to perform custom cleanup.
293 | * @protected
294 | */
295 | _performCustomCleanup() {
296 | // Subclasses can override this
297 | }
298 | }
299 |
```
--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/expand-task.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * expand-task.js
3 | * Direct function implementation for expanding a task into subtasks
4 | */
5 |
6 | import expandTask from '../../../../scripts/modules/task-manager/expand-task.js';
7 | import {
8 | readJSON,
9 | writeJSON,
10 | enableSilentMode,
11 | disableSilentMode,
12 | isSilentMode
13 | } from '../../../../scripts/modules/utils.js';
14 | import path from 'path';
15 | import fs from 'fs';
16 | import { createLogWrapper } from '../../tools/utils.js';
17 |
18 | /**
19 | * Direct function wrapper for expanding a task into subtasks with error handling.
20 | *
21 | * @param {Object} args - Command arguments
22 | * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
23 | * @param {string} args.id - The ID of the task to expand.
24 | * @param {number|string} [args.num] - Number of subtasks to generate.
25 | * @param {boolean} [args.research] - Enable research role for subtask generation.
26 | * @param {string} [args.prompt] - Additional context to guide subtask generation.
27 | * @param {boolean} [args.force] - Force expansion even if subtasks exist.
28 | * @param {string} [args.projectRoot] - Project root directory.
29 | * @param {string} [args.tag] - Tag for the task
30 | * @param {Object} log - Logger object
31 | * @param {Object} context - Context object containing session
32 | * @param {Object} [context.session] - MCP Session object
33 | * @returns {Promise<Object>} - Task expansion result { success: boolean, data?: any, error?: { code: string, message: string } }
34 | */
35 | export async function expandTaskDirect(args, log, context = {}) {
36 | const { session } = context; // Extract session
37 | // Destructure expected args, including projectRoot
38 | const {
39 | tasksJsonPath,
40 | id,
41 | num,
42 | research,
43 | prompt,
44 | force,
45 | projectRoot,
46 | tag,
47 | complexityReportPath
48 | } = args;
49 |
50 | // Log session root data for debugging
51 | log.info(
52 | `Session data in expandTaskDirect: ${JSON.stringify({
53 | hasSession: !!session,
54 | sessionKeys: session ? Object.keys(session) : [],
55 | roots: session?.roots,
56 | rootsStr: JSON.stringify(session?.roots)
57 | })}`
58 | );
59 |
60 | // Check if tasksJsonPath was provided
61 | if (!tasksJsonPath) {
62 | log.error('expandTaskDirect called without tasksJsonPath');
63 | return {
64 | success: false,
65 | error: {
66 | code: 'MISSING_ARGUMENT',
67 | message: 'tasksJsonPath is required'
68 | }
69 | };
70 | }
71 |
72 | // Use provided path
73 | const tasksPath = tasksJsonPath;
74 |
75 | log.info(`[expandTaskDirect] Using tasksPath: ${tasksPath}`);
76 |
77 | // Validate task ID
78 | const taskId = id ? parseInt(id, 10) : null;
79 | if (!taskId) {
80 | log.error('Task ID is required');
81 | return {
82 | success: false,
83 | error: {
84 | code: 'INPUT_VALIDATION_ERROR',
85 | message: 'Task ID is required'
86 | }
87 | };
88 | }
89 |
90 | // Process other parameters
91 | const numSubtasks = num ? parseInt(num, 10) : undefined;
92 | const useResearch = research === true;
93 | const additionalContext = prompt || '';
94 | const forceFlag = force === true;
95 |
96 | try {
97 | log.info(
98 | `[expandTaskDirect] Expanding task ${taskId} into ${numSubtasks || 'default'} subtasks. Research: ${useResearch}, Force: ${forceFlag}`
99 | );
100 |
101 | // Read tasks data
102 | log.info(`[expandTaskDirect] Attempting to read JSON from: ${tasksPath}`);
103 | const data = readJSON(tasksPath, projectRoot);
104 | log.info(
105 | `[expandTaskDirect] Result of readJSON: ${data ? 'Data read successfully' : 'readJSON returned null or undefined'}`
106 | );
107 |
108 | if (!data || !data.tasks) {
109 | log.error(
110 | `[expandTaskDirect] readJSON failed or returned invalid data for path: ${tasksPath}`
111 | );
112 | return {
113 | success: false,
114 | error: {
115 | code: 'INVALID_TASKS_FILE',
116 | message: `No valid tasks found in ${tasksPath}. readJSON returned: ${JSON.stringify(data)}`
117 | }
118 | };
119 | }
120 |
121 | // Find the specific task
122 | log.info(`[expandTaskDirect] Searching for task ID ${taskId} in data`);
123 | const task = data.tasks.find((t) => t.id === taskId);
124 | log.info(`[expandTaskDirect] Task found: ${task ? 'Yes' : 'No'}`);
125 |
126 | if (!task) {
127 | return {
128 | success: false,
129 | error: {
130 | code: 'TASK_NOT_FOUND',
131 | message: `Task with ID ${taskId} not found`
132 | }
133 | };
134 | }
135 |
136 | // Check if task is completed
137 | if (task.status === 'done' || task.status === 'completed') {
138 | return {
139 | success: false,
140 | error: {
141 | code: 'TASK_COMPLETED',
142 | message: `Task ${taskId} is already marked as ${task.status} and cannot be expanded`
143 | }
144 | };
145 | }
146 |
147 | // Check for existing subtasks and force flag
148 | const hasExistingSubtasks = task.subtasks && task.subtasks.length > 0;
149 | if (hasExistingSubtasks && !forceFlag) {
150 | log.info(
151 | `Task ${taskId} already has ${task.subtasks.length} subtasks. Use --force to overwrite.`
152 | );
153 | return {
154 | success: true,
155 | data: {
156 | message: `Task ${taskId} already has subtasks. Expansion skipped.`,
157 | task,
158 | subtasksAdded: 0,
159 | hasExistingSubtasks
160 | }
161 | };
162 | }
163 |
164 | // If force flag is set, clear existing subtasks
165 | if (hasExistingSubtasks && forceFlag) {
166 | log.info(
167 | `Force flag set. Clearing existing subtasks for task ${taskId}.`
168 | );
169 | task.subtasks = [];
170 | }
171 |
172 | // Keep a copy of the task before modification
173 | const originalTask = JSON.parse(JSON.stringify(task));
174 |
175 | // Tracking subtasks count before expansion
176 | const subtasksCountBefore = task.subtasks ? task.subtasks.length : 0;
177 |
178 | // Directly modify the data instead of calling the CLI function
179 | if (!task.subtasks) {
180 | task.subtasks = [];
181 | }
182 |
183 | // Save tasks.json with potentially empty subtasks array and proper context
184 | writeJSON(tasksPath, data, projectRoot, tag);
185 |
186 | // Create logger wrapper using the utility
187 | const mcpLog = createLogWrapper(log);
188 |
189 | let wasSilent; // Declare wasSilent outside the try block
190 | // Process the request
191 | try {
192 | // Enable silent mode to prevent console logs from interfering with JSON response
193 | wasSilent = isSilentMode(); // Assign inside the try block
194 | if (!wasSilent) enableSilentMode();
195 |
196 | // Call the core expandTask function with the wrapped logger and projectRoot
197 | const coreResult = await expandTask(
198 | tasksPath,
199 | taskId,
200 | numSubtasks,
201 | useResearch,
202 | additionalContext,
203 | {
204 | complexityReportPath,
205 | mcpLog,
206 | session,
207 | projectRoot,
208 | commandName: 'expand-task',
209 | outputType: 'mcp',
210 | tag
211 | },
212 | forceFlag
213 | );
214 |
215 | // Restore normal logging
216 | if (!wasSilent && isSilentMode()) disableSilentMode();
217 |
218 | // Read the updated data
219 | const updatedData = readJSON(tasksPath, projectRoot);
220 | const updatedTask = updatedData.tasks.find((t) => t.id === taskId);
221 |
222 | // Calculate how many subtasks were added
223 | const subtasksAdded = updatedTask.subtasks
224 | ? updatedTask.subtasks.length - subtasksCountBefore
225 | : 0;
226 |
227 | // Return the result, including telemetryData
228 | log.info(
229 | `Successfully expanded task ${taskId} with ${subtasksAdded} new subtasks`
230 | );
231 | return {
232 | success: true,
233 | data: {
234 | task: coreResult.task,
235 | subtasksAdded,
236 | hasExistingSubtasks,
237 | telemetryData: coreResult.telemetryData,
238 | tagInfo: coreResult.tagInfo
239 | }
240 | };
241 | } catch (error) {
242 | // Make sure to restore normal logging even if there's an error
243 | if (!wasSilent && isSilentMode()) disableSilentMode();
244 |
245 | log.error(`Error expanding task: ${error.message}`);
246 | return {
247 | success: false,
248 | error: {
249 | code: 'CORE_FUNCTION_ERROR',
250 | message: error.message || 'Failed to expand task'
251 | }
252 | };
253 | }
254 | } catch (error) {
255 | log.error(`Error expanding task: ${error.message}`);
256 | return {
257 | success: false,
258 | error: {
259 | code: 'CORE_FUNCTION_ERROR',
260 | message: error.message || 'Failed to expand task'
261 | }
262 | };
263 | }
264 | }
265 |
```
--------------------------------------------------------------------------------
/apps/extension/src/webview/index.css:
--------------------------------------------------------------------------------
```css
1 | @import "tailwindcss";
2 |
3 | /* shadcn/ui CSS variables */
4 | @theme {
5 | /* VS Code CSS variables will be injected here */
6 | /* color-scheme: var(--vscode-theme-kind, light); */
7 |
8 | /* shadcn/ui variables - adapted for VS Code */
9 | --color-background: var(--vscode-editor-background);
10 | --color-sidebar-background: var(--vscode-sideBar-background);
11 | --color-foreground: var(--vscode-foreground);
12 | --color-card: var(--vscode-editor-background);
13 | --color-card-foreground: var(--vscode-foreground);
14 | --color-popover: var(--vscode-editor-background);
15 | --color-popover-foreground: var(--vscode-foreground);
16 | --color-primary: var(--vscode-button-background);
17 | --color-primary-foreground: var(--vscode-button-foreground);
18 | --color-secondary: var(--vscode-button-secondaryBackground);
19 | --color-secondary-foreground: var(--vscode-button-secondaryForeground);
20 | --color-widget-background: var(--vscode-editorWidget-background);
21 | --color-widget-border: var(--vscode-editorWidget-border);
22 | --color-code-snippet-background: var(--vscode-textPreformat-background);
23 | --color-code-snippet-text: var(--vscode-textPreformat-foreground);
24 | --font-editor-font: var(--vscode-editor-font-family);
25 | --font-editor-size: var(--vscode-editor-font-size);
26 | --color-input-background: var(--vscode-input-background);
27 | --color-input-foreground: var(--vscode-input-foreground);
28 | --color-accent: var(--vscode-focusBorder);
29 | --color-accent-foreground: var(--vscode-foreground);
30 | --color-destructive: var(--vscode-errorForeground);
31 | --color-destructive-foreground: var(--vscode-foreground);
32 | --color-border: var(--vscode-panel-border);
33 | --color-ring: var(--vscode-focusBorder);
34 | --color-link: var(--vscode-editorLink-foreground);
35 | --color-link-hover: var(--vscode-editorLink-activeForeground);
36 | --color-textSeparator-foreground: var(--vscode-textSeparator-foreground);
37 | --radius: 0.5rem;
38 |
39 | /* VS Code specific color mappings for Tailwind utilities */
40 | --color-vscode-foreground: var(--vscode-foreground);
41 | --color-vscode-button-background: var(--vscode-button-background);
42 | --color-vscode-button-foreground: var(--vscode-button-foreground);
43 | --color-vscode-button-hoverBackground: var(--vscode-button-hoverBackground);
44 | --color-vscode-editor-background: var(--vscode-editor-background);
45 | --color-vscode-input-background: var(--vscode-input-background);
46 | --color-vscode-input-foreground: var(--vscode-input-foreground);
47 | --color-vscode-dropdown-background: var(--vscode-dropdown-background);
48 | --color-vscode-dropdown-foreground: var(--vscode-dropdown-foreground);
49 | --color-vscode-dropdown-border: var(--vscode-dropdown-border);
50 | --color-vscode-focusBorder: var(--vscode-focusBorder);
51 | --color-vscode-panel-border: var(--vscode-panel-border);
52 | --color-vscode-sideBar-background: var(--vscode-sideBar-background);
53 | --color-vscode-sideBar-foreground: var(--vscode-sideBar-foreground);
54 | --color-vscode-sideBarTitle-foreground: var(--vscode-sideBarTitle-foreground);
55 | --color-vscode-testing-iconPassed: var(--vscode-testing-iconPassed);
56 | --color-vscode-testing-iconFailed: var(--vscode-testing-iconFailed);
57 | --color-vscode-errorForeground: var(--vscode-errorForeground);
58 | --color-vscode-editorWidget-background: var(--vscode-editorWidget-background);
59 | --color-vscode-editorWidget-border: var(--vscode-editorWidget-border);
60 | --color-vscode-list-hoverBackground: var(--vscode-list-hoverBackground);
61 | --color-vscode-list-activeSelectionBackground: var(
62 | --vscode-list-activeSelectionBackground
63 | );
64 | --color-vscode-list-activeSelectionForeground: var(
65 | --vscode-list-activeSelectionForeground
66 | );
67 | --color-vscode-badge-background: var(--vscode-badge-background);
68 | --color-vscode-badge-foreground: var(--vscode-badge-foreground);
69 | --color-vscode-textLink-foreground: var(--vscode-textLink-foreground);
70 | --color-vscode-textLink-activeForeground: var(
71 | --vscode-textLink-activeForeground
72 | );
73 | --color-vscode-icon-foreground: var(--vscode-icon-foreground);
74 | --color-vscode-descriptionForeground: var(--vscode-descriptionForeground);
75 | --color-vscode-disabledForeground: var(--vscode-disabledForeground);
76 | }
77 |
78 | /* Reset body to match VS Code styles instead of Tailwind defaults */
79 | @layer base {
80 | html,
81 | body {
82 | height: 100%;
83 | margin: 0 !important;
84 | padding: 0 !important;
85 | overflow: hidden;
86 | }
87 |
88 | body {
89 | background-color: var(--vscode-editor-background) !important;
90 | color: var(--vscode-foreground) !important;
91 | font-family: var(--vscode-font-family) !important;
92 | font-size: var(--vscode-font-size) !important;
93 | font-weight: var(--vscode-font-weight) !important;
94 | line-height: 1.4 !important;
95 | }
96 |
97 | /* Ensure root container takes full space */
98 | #root {
99 | height: 100vh;
100 | width: 100vw;
101 | display: flex;
102 | flex-direction: column;
103 | overflow: hidden;
104 | }
105 |
106 | /* Override any conflicting Tailwind defaults for VS Code integration */
107 | * {
108 | box-sizing: border-box;
109 | }
110 |
111 | /* Ensure buttons and inputs use VS Code styling */
112 | button,
113 | input,
114 | select,
115 | textarea {
116 | font-family: inherit;
117 | }
118 | }
119 |
120 | /* Enhanced scrollbar styling for Kanban board */
121 | ::-webkit-scrollbar {
122 | width: 8px;
123 | height: 8px;
124 | }
125 |
126 | ::-webkit-scrollbar-track {
127 | background: var(--vscode-scrollbarSlider-background, rgba(255, 255, 255, 0.1));
128 | border-radius: 4px;
129 | }
130 |
131 | ::-webkit-scrollbar-thumb {
132 | background: var(
133 | --vscode-scrollbarSlider-hoverBackground,
134 | rgba(255, 255, 255, 0.2)
135 | );
136 | border-radius: 4px;
137 | border: 1px solid transparent;
138 | background-clip: padding-box;
139 | }
140 |
141 | ::-webkit-scrollbar-thumb:hover {
142 | background: var(
143 | --vscode-scrollbarSlider-activeBackground,
144 | rgba(255, 255, 255, 0.3)
145 | );
146 | }
147 |
148 | ::-webkit-scrollbar-corner {
149 | background: var(--vscode-scrollbarSlider-background, rgba(255, 255, 255, 0.1));
150 | }
151 |
152 | /* Kanban specific styles */
153 | @layer components {
154 | .kanban-container {
155 | scrollbar-gutter: stable;
156 | }
157 |
158 | /* Smooth scrolling for better UX */
159 | .kanban-container {
160 | scroll-behavior: smooth;
161 | }
162 |
163 | /* Ensure proper touch scrolling on mobile */
164 | .kanban-container {
165 | -webkit-overflow-scrolling: touch;
166 | }
167 |
168 | /* Add subtle shadow for depth */
169 | .kanban-column {
170 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
171 | }
172 |
173 | /* Enhanced scrolling for column content areas */
174 | .kanban-column > div[style*="overflow-y"] {
175 | scrollbar-width: thin;
176 | scrollbar-color: var(
177 | --vscode-scrollbarSlider-hoverBackground,
178 | rgba(255, 255, 255, 0.2)
179 | )
180 | var(--vscode-scrollbarSlider-background, rgba(255, 255, 255, 0.1));
181 | }
182 |
183 | /* Card hover effects */
184 | .kanban-card {
185 | transition: all 0.2s ease-in-out;
186 | }
187 |
188 | .kanban-card:hover {
189 | transform: translateY(-1px);
190 | }
191 |
192 | /* Focus indicators for accessibility */
193 | .kanban-card:focus-visible {
194 | outline: 2px solid var(--vscode-focusBorder);
195 | outline-offset: 2px;
196 | }
197 | }
198 |
199 | /* Line clamp utility for text truncation */
200 | @layer utilities {
201 | .line-clamp-2 {
202 | overflow: hidden;
203 | display: -webkit-box;
204 | -webkit-box-orient: vertical;
205 | -webkit-line-clamp: 2;
206 | }
207 |
208 | .line-clamp-3 {
209 | overflow: hidden;
210 | display: -webkit-box;
211 | -webkit-box-orient: vertical;
212 | -webkit-line-clamp: 3;
213 | }
214 |
215 | /* Custom scrollbar utilities */
216 | .scrollbar-thin {
217 | scrollbar-width: thin;
218 | }
219 |
220 | .scrollbar-track-transparent {
221 | scrollbar-color: var(
222 | --vscode-scrollbarSlider-hoverBackground,
223 | rgba(255, 255, 255, 0.2)
224 | )
225 | transparent;
226 | }
227 | }
228 |
229 | /* Dark mode adjustments */
230 | @media (prefers-color-scheme: dark) {
231 | ::-webkit-scrollbar-track {
232 | background: rgba(255, 255, 255, 0.05);
233 | }
234 |
235 | ::-webkit-scrollbar-thumb {
236 | background: rgba(255, 255, 255, 0.15);
237 | }
238 |
239 | ::-webkit-scrollbar-thumb:hover {
240 | background: rgba(255, 255, 255, 0.25);
241 | }
242 | }
243 |
```
--------------------------------------------------------------------------------
/apps/cli/src/command-registry.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview Centralized Command Registry
3 | * Provides a single location for registering all CLI commands
4 | */
5 |
6 | import type { Command } from 'commander';
7 |
8 | import { AuthCommand } from './commands/auth.command.js';
9 | import { AutopilotCommand } from './commands/autopilot/index.js';
10 | import { BriefsCommand } from './commands/briefs.command.js';
11 | import { ContextCommand } from './commands/context.command.js';
12 | import { ExportCommand } from './commands/export.command.js';
13 | // Import all commands
14 | import { ListTasksCommand } from './commands/list.command.js';
15 | import { NextCommand } from './commands/next.command.js';
16 | import { SetStatusCommand } from './commands/set-status.command.js';
17 | import { ShowCommand } from './commands/show.command.js';
18 | import { StartCommand } from './commands/start.command.js';
19 | import { TagsCommand } from './commands/tags.command.js';
20 |
21 | /**
22 | * Command metadata for registration
23 | */
24 | export interface CommandMetadata {
25 | name: string;
26 | description: string;
27 | commandClass: typeof Command;
28 | category?: 'task' | 'auth' | 'utility' | 'development';
29 | }
30 |
31 | /**
32 | * Registry of all available commands
33 | */
34 | export class CommandRegistry {
35 | /**
36 | * All available commands with their metadata
37 | */
38 | private static commands: CommandMetadata[] = [
39 | // Task Management Commands
40 | {
41 | name: 'list',
42 | description: 'List all tasks with filtering and status overview',
43 | commandClass: ListTasksCommand as any,
44 | category: 'task'
45 | },
46 | {
47 | name: 'show',
48 | description: 'Display detailed information about a specific task',
49 | commandClass: ShowCommand as any,
50 | category: 'task'
51 | },
52 | {
53 | name: 'next',
54 | description: 'Find the next available task to work on',
55 | commandClass: NextCommand as any,
56 | category: 'task'
57 | },
58 | {
59 | name: 'start',
60 | description: 'Start working on a task with claude-code',
61 | commandClass: StartCommand as any,
62 | category: 'task'
63 | },
64 | {
65 | name: 'set-status',
66 | description: 'Update the status of one or more tasks',
67 | commandClass: SetStatusCommand as any,
68 | category: 'task'
69 | },
70 | {
71 | name: 'export',
72 | description: 'Export tasks to external systems',
73 | commandClass: ExportCommand as any,
74 | category: 'task'
75 | },
76 | {
77 | name: 'autopilot',
78 | description:
79 | 'AI agent orchestration for TDD workflow (start, resume, next, complete, commit, status, abort)',
80 | commandClass: AutopilotCommand as any,
81 | category: 'development'
82 | },
83 |
84 | // Authentication & Context Commands
85 | {
86 | name: 'auth',
87 | description: 'Manage authentication with tryhamster.com',
88 | commandClass: AuthCommand as any,
89 | category: 'auth'
90 | },
91 | {
92 | name: 'context',
93 | description: 'Manage workspace context (organization/brief)',
94 | commandClass: ContextCommand as any,
95 | category: 'auth'
96 | },
97 | {
98 | name: 'tags',
99 | description: 'Manage tags for task organization',
100 | commandClass: TagsCommand as any,
101 | category: 'task'
102 | },
103 | {
104 | name: 'briefs',
105 | description: 'Manage briefs (Hamster only)',
106 | commandClass: BriefsCommand as any,
107 | category: 'task'
108 | }
109 | ];
110 |
111 | /**
112 | * Register all commands on a program instance
113 | * @param program - Commander program to register commands on
114 | */
115 | static registerAll(program: Command): void {
116 | for (const cmd of this.commands) {
117 | this.registerCommand(program, cmd);
118 | }
119 | }
120 |
121 | /**
122 | * Register specific commands by category
123 | * @param program - Commander program to register commands on
124 | * @param category - Category of commands to register
125 | */
126 | static registerByCategory(
127 | program: Command,
128 | category: 'task' | 'auth' | 'utility' | 'development'
129 | ): void {
130 | const categoryCommands = this.commands.filter(
131 | (cmd) => cmd.category === category
132 | );
133 |
134 | for (const cmd of categoryCommands) {
135 | this.registerCommand(program, cmd);
136 | }
137 | }
138 |
139 | /**
140 | * Register a single command by name
141 | * @param program - Commander program to register the command on
142 | * @param name - Name of the command to register
143 | */
144 | static registerByName(program: Command, name: string): void {
145 | const cmd = this.commands.find((c) => c.name === name);
146 | if (cmd) {
147 | this.registerCommand(program, cmd);
148 | } else {
149 | throw new Error(`Command '${name}' not found in registry`);
150 | }
151 | }
152 |
153 | /**
154 | * Register a single command
155 | * @param program - Commander program to register the command on
156 | * @param metadata - Command metadata
157 | */
158 | private static registerCommand(
159 | program: Command,
160 | metadata: CommandMetadata
161 | ): void {
162 | const CommandClass = metadata.commandClass as any;
163 |
164 | // Use the static registration method that all commands have
165 | if (CommandClass.registerOn) {
166 | CommandClass.registerOn(program);
167 | } else if (CommandClass.register) {
168 | CommandClass.register(program);
169 | } else {
170 | // Fallback to creating instance and adding
171 | const instance = new CommandClass();
172 | program.addCommand(instance);
173 | }
174 | }
175 |
176 | /**
177 | * Get all registered command names
178 | */
179 | static getCommandNames(): string[] {
180 | return this.commands.map((cmd) => cmd.name);
181 | }
182 |
183 | /**
184 | * Get commands by category
185 | */
186 | static getCommandsByCategory(
187 | category: 'task' | 'auth' | 'utility' | 'development'
188 | ): CommandMetadata[] {
189 | return this.commands.filter((cmd) => cmd.category === category);
190 | }
191 |
192 | /**
193 | * Add a new command to the registry
194 | * @param metadata - Command metadata to add
195 | */
196 | static addCommand(metadata: CommandMetadata): void {
197 | // Check if command already exists
198 | if (this.commands.some((cmd) => cmd.name === metadata.name)) {
199 | throw new Error(`Command '${metadata.name}' already exists in registry`);
200 | }
201 |
202 | this.commands.push(metadata);
203 | }
204 |
205 | /**
206 | * Remove a command from the registry
207 | * @param name - Name of the command to remove
208 | */
209 | static removeCommand(name: string): boolean {
210 | const index = this.commands.findIndex((cmd) => cmd.name === name);
211 | if (index >= 0) {
212 | this.commands.splice(index, 1);
213 | return true;
214 | }
215 | return false;
216 | }
217 |
218 | /**
219 | * Get command metadata by name
220 | * @param name - Name of the command
221 | */
222 | static getCommand(name: string): CommandMetadata | undefined {
223 | return this.commands.find((cmd) => cmd.name === name);
224 | }
225 |
226 | /**
227 | * Check if a command exists
228 | * @param name - Name of the command
229 | */
230 | static hasCommand(name: string): boolean {
231 | return this.commands.some((cmd) => cmd.name === name);
232 | }
233 |
234 | /**
235 | * Get a formatted list of all commands for display
236 | */
237 | static getFormattedCommandList(): string {
238 | const categories = {
239 | task: 'Task Management',
240 | auth: 'Authentication & Context',
241 | utility: 'Utilities',
242 | development: 'Development'
243 | };
244 |
245 | let output = '';
246 |
247 | for (const [category, title] of Object.entries(categories)) {
248 | const cmds = this.getCommandsByCategory(
249 | category as keyof typeof categories
250 | );
251 | if (cmds.length > 0) {
252 | output += `\n${title}:\n`;
253 | for (const cmd of cmds) {
254 | output += ` ${cmd.name.padEnd(20)} ${cmd.description}\n`;
255 | }
256 | }
257 | }
258 |
259 | return output;
260 | }
261 | }
262 |
263 | /**
264 | * Convenience function to register all CLI commands
265 | * @param program - Commander program instance
266 | */
267 | export function registerAllCommands(program: Command): void {
268 | CommandRegistry.registerAll(program);
269 | }
270 |
271 | /**
272 | * Convenience function to register commands by category
273 | * @param program - Commander program instance
274 | * @param category - Category to register
275 | */
276 | export function registerCommandsByCategory(
277 | program: Command,
278 | category: 'task' | 'auth' | 'utility' | 'development'
279 | ): void {
280 | CommandRegistry.registerByCategory(program, category);
281 | }
282 |
283 | // Export the registry for direct access if needed
284 | export default CommandRegistry;
285 |
```
--------------------------------------------------------------------------------
/src/ui/indicators.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * indicators.js
3 | * UI functions for displaying priority and complexity indicators in different contexts
4 | */
5 |
6 | import chalk from 'chalk';
7 | import { TASK_PRIORITY_OPTIONS } from '../constants/task-priority.js';
8 |
9 | // Extract priority values for cleaner object keys
10 | const [HIGH, MEDIUM, LOW] = TASK_PRIORITY_OPTIONS;
11 |
12 | // Cache for generated indicators
13 | const INDICATOR_CACHE = new Map();
14 |
15 | /**
16 | * Base configuration for indicator systems
17 | */
18 | class IndicatorConfig {
19 | constructor(name, levels, colors, thresholds = null) {
20 | this.name = name;
21 | this.levels = levels;
22 | this.colors = colors;
23 | this.thresholds = thresholds;
24 | }
25 |
26 | getColor(level) {
27 | return this.colors[level] || chalk.gray;
28 | }
29 |
30 | getLevelFromScore(score) {
31 | if (!this.thresholds) {
32 | throw new Error(`${this.name} does not support score-based levels`);
33 | }
34 |
35 | if (score >= 7) return this.levels[0]; // high
36 | if (score <= 3) return this.levels[2]; // low
37 | return this.levels[1]; // medium
38 | }
39 | }
40 |
41 | /**
42 | * Visual style definitions
43 | */
44 | const VISUAL_STYLES = {
45 | cli: {
46 | filled: '●', // ●
47 | empty: '○' // ○
48 | },
49 | statusBar: {
50 | high: '⋮', // ⋮
51 | medium: ':', // :
52 | low: '.' // .
53 | },
54 | mcp: {
55 | high: '🔴', // 🔴
56 | medium: '🟠', // 🟠
57 | low: '🟢' // 🟢
58 | }
59 | };
60 |
61 | /**
62 | * Priority configuration
63 | */
64 | const PRIORITY_CONFIG = new IndicatorConfig('priority', [HIGH, MEDIUM, LOW], {
65 | [HIGH]: chalk.hex('#CC0000'),
66 | [MEDIUM]: chalk.hex('#FF8800'),
67 | [LOW]: chalk.yellow
68 | });
69 |
70 | /**
71 | * Generates CLI indicator with intensity
72 | */
73 | function generateCliIndicator(intensity, color) {
74 | const filled = VISUAL_STYLES.cli.filled;
75 | const empty = VISUAL_STYLES.cli.empty;
76 |
77 | let indicator = '';
78 | for (let i = 0; i < 3; i++) {
79 | if (i < intensity) {
80 | indicator += color(filled);
81 | } else {
82 | indicator += chalk.white(empty);
83 | }
84 | }
85 | return indicator;
86 | }
87 |
88 | /**
89 | * Get intensity level from priority/complexity level
90 | */
91 | function getIntensityFromLevel(level, levels) {
92 | const index = levels.indexOf(level);
93 | return 3 - index; // high=3, medium=2, low=1
94 | }
95 |
96 | /**
97 | * Generic cached indicator getter
98 | * @param {string} cacheKey - Cache key for the indicators
99 | * @param {Function} generator - Function to generate the indicators
100 | * @returns {Object} Cached or newly generated indicators
101 | */
102 | function getCachedIndicators(cacheKey, generator) {
103 | if (INDICATOR_CACHE.has(cacheKey)) {
104 | return INDICATOR_CACHE.get(cacheKey);
105 | }
106 |
107 | const indicators = generator();
108 | INDICATOR_CACHE.set(cacheKey, indicators);
109 | return indicators;
110 | }
111 |
112 | /**
113 | * Get priority indicators for MCP context (single emojis)
114 | * @returns {Object} Priority to emoji mapping
115 | */
116 | export function getMcpPriorityIndicators() {
117 | return getCachedIndicators('mcp-priority-all', () => ({
118 | [HIGH]: VISUAL_STYLES.mcp.high,
119 | [MEDIUM]: VISUAL_STYLES.mcp.medium,
120 | [LOW]: VISUAL_STYLES.mcp.low
121 | }));
122 | }
123 |
124 | /**
125 | * Get priority indicators for CLI context (colored dots with visual hierarchy)
126 | * @returns {Object} Priority to colored dot string mapping
127 | */
128 | export function getCliPriorityIndicators() {
129 | return getCachedIndicators('cli-priority-all', () => {
130 | const indicators = {};
131 | PRIORITY_CONFIG.levels.forEach((level) => {
132 | const intensity = getIntensityFromLevel(level, PRIORITY_CONFIG.levels);
133 | const color = PRIORITY_CONFIG.getColor(level);
134 | indicators[level] = generateCliIndicator(intensity, color);
135 | });
136 | return indicators;
137 | });
138 | }
139 |
140 | /**
141 | * Get priority indicators for status bars (simplified single character versions)
142 | * @returns {Object} Priority to single character indicator mapping
143 | */
144 | export function getStatusBarPriorityIndicators() {
145 | return getCachedIndicators('statusbar-priority-all', () => {
146 | const indicators = {};
147 | PRIORITY_CONFIG.levels.forEach((level, index) => {
148 | const style =
149 | index === 0
150 | ? VISUAL_STYLES.statusBar.high
151 | : index === 1
152 | ? VISUAL_STYLES.statusBar.medium
153 | : VISUAL_STYLES.statusBar.low;
154 | const color = PRIORITY_CONFIG.getColor(level);
155 | indicators[level] = color(style);
156 | });
157 | return indicators;
158 | });
159 | }
160 |
161 | /**
162 | * Get priority colors for consistent styling
163 | * @returns {Object} Priority to chalk color function mapping
164 | */
165 | export function getPriorityColors() {
166 | return {
167 | [HIGH]: PRIORITY_CONFIG.colors[HIGH],
168 | [MEDIUM]: PRIORITY_CONFIG.colors[MEDIUM],
169 | [LOW]: PRIORITY_CONFIG.colors[LOW]
170 | };
171 | }
172 |
173 | /**
174 | * Get priority indicators based on context
175 | * @param {boolean} isMcp - Whether this is for MCP context (true) or CLI context (false)
176 | * @returns {Object} Priority to indicator mapping
177 | */
178 | export function getPriorityIndicators(isMcp = false) {
179 | return isMcp ? getMcpPriorityIndicators() : getCliPriorityIndicators();
180 | }
181 |
182 | /**
183 | * Get a specific priority indicator
184 | * @param {string} priority - The priority level ('high', 'medium', 'low')
185 | * @param {boolean} isMcp - Whether this is for MCP context
186 | * @returns {string} The indicator string for the priority
187 | */
188 | export function getPriorityIndicator(priority, isMcp = false) {
189 | const indicators = getPriorityIndicators(isMcp);
190 | return indicators[priority] || indicators[MEDIUM];
191 | }
192 |
193 | // ============================================================================
194 | // Complexity Indicators
195 | // ============================================================================
196 |
197 | /**
198 | * Complexity configuration
199 | */
200 | const COMPLEXITY_CONFIG = new IndicatorConfig(
201 | 'complexity',
202 | ['high', 'medium', 'low'],
203 | {
204 | high: chalk.hex('#CC0000'),
205 | medium: chalk.hex('#FF8800'),
206 | low: chalk.green
207 | },
208 | {
209 | high: (score) => score >= 7,
210 | medium: (score) => score >= 4 && score <= 6,
211 | low: (score) => score <= 3
212 | }
213 | );
214 |
215 | /**
216 | * Get complexity indicators for CLI context (colored dots with visual hierarchy)
217 | * Complexity scores: 1-3 (low), 4-6 (medium), 7-10 (high)
218 | * @returns {Object} Complexity level to colored dot string mapping
219 | */
220 | export function getCliComplexityIndicators() {
221 | return getCachedIndicators('cli-complexity-all', () => {
222 | const indicators = {};
223 | COMPLEXITY_CONFIG.levels.forEach((level) => {
224 | const intensity = getIntensityFromLevel(level, COMPLEXITY_CONFIG.levels);
225 | const color = COMPLEXITY_CONFIG.getColor(level);
226 | indicators[level] = generateCliIndicator(intensity, color);
227 | });
228 | return indicators;
229 | });
230 | }
231 |
232 | /**
233 | * Get complexity indicators for status bars (simplified single character versions)
234 | * @returns {Object} Complexity level to single character indicator mapping
235 | */
236 | export function getStatusBarComplexityIndicators() {
237 | return getCachedIndicators('statusbar-complexity-all', () => {
238 | const indicators = {};
239 | COMPLEXITY_CONFIG.levels.forEach((level, index) => {
240 | const style =
241 | index === 0
242 | ? VISUAL_STYLES.statusBar.high
243 | : index === 1
244 | ? VISUAL_STYLES.statusBar.medium
245 | : VISUAL_STYLES.statusBar.low;
246 | const color = COMPLEXITY_CONFIG.getColor(level);
247 | indicators[level] = color(style);
248 | });
249 | return indicators;
250 | });
251 | }
252 |
253 | /**
254 | * Get complexity colors for consistent styling
255 | * @returns {Object} Complexity level to chalk color function mapping
256 | */
257 | export function getComplexityColors() {
258 | return { ...COMPLEXITY_CONFIG.colors };
259 | }
260 |
261 | /**
262 | * Get a specific complexity indicator based on score
263 | * @param {number} score - The complexity score (1-10)
264 | * @param {boolean} statusBar - Whether to return status bar version (single char)
265 | * @returns {string} The indicator string for the complexity level
266 | */
267 | export function getComplexityIndicator(score, statusBar = false) {
268 | const level = COMPLEXITY_CONFIG.getLevelFromScore(score);
269 | const indicators = statusBar
270 | ? getStatusBarComplexityIndicators()
271 | : getCliComplexityIndicators();
272 | return indicators[level];
273 | }
274 |
```
--------------------------------------------------------------------------------
/apps/cli/src/commands/set-status.command.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview SetStatusCommand using Commander's native class pattern
3 | * Extends Commander.Command for better integration with the framework
4 | */
5 |
6 | import { type TaskStatus, type TmCore, createTmCore } from '@tm/core';
7 | import type { StorageType } from '@tm/core';
8 | import boxen from 'boxen';
9 | import chalk from 'chalk';
10 | import { Command } from 'commander';
11 | import { displayError } from '../utils/error-handler.js';
12 | import { getProjectRoot } from '../utils/project-root.js';
13 |
14 | /**
15 | * Valid task status values for validation
16 | */
17 | const VALID_TASK_STATUSES: TaskStatus[] = [
18 | 'pending',
19 | 'in-progress',
20 | 'done',
21 | 'deferred',
22 | 'cancelled',
23 | 'blocked',
24 | 'review'
25 | ];
26 |
27 | /**
28 | * Options interface for the set-status command
29 | */
30 | export interface SetStatusCommandOptions {
31 | id?: string;
32 | status?: TaskStatus;
33 | format?: 'text' | 'json';
34 | silent?: boolean;
35 | project?: string;
36 | }
37 |
38 | /**
39 | * Result type from set-status command
40 | */
41 | export interface SetStatusResult {
42 | success: boolean;
43 | updatedTasks: Array<{
44 | taskId: string;
45 | oldStatus: TaskStatus;
46 | newStatus: TaskStatus;
47 | }>;
48 | storageType: Exclude<StorageType, 'auto'>;
49 | }
50 |
51 | /**
52 | * SetStatusCommand extending Commander's Command class
53 | * This is a thin presentation layer over @tm/core
54 | */
55 | export class SetStatusCommand extends Command {
56 | private tmCore?: TmCore;
57 | private lastResult?: SetStatusResult;
58 |
59 | constructor(name?: string) {
60 | super(name || 'set-status');
61 |
62 | // Configure the command
63 | this.description('Update the status of one or more tasks')
64 | .requiredOption(
65 | '-i, --id <id>',
66 | 'Task ID(s) to update (comma-separated for multiple, supports subtasks like 5.2)'
67 | )
68 | .requiredOption(
69 | '-s, --status <status>',
70 | `New status (${VALID_TASK_STATUSES.join(', ')})`
71 | )
72 | .option('-f, --format <format>', 'Output format (text, json)', 'text')
73 | .option('--silent', 'Suppress output (useful for programmatic usage)')
74 | .option(
75 | '-p, --project <path>',
76 | 'Project root directory (auto-detected if not provided)'
77 | )
78 | .action(async (options: SetStatusCommandOptions) => {
79 | await this.executeCommand(options);
80 | });
81 | }
82 |
83 | /**
84 | * Execute the set-status command
85 | */
86 | private async executeCommand(
87 | options: SetStatusCommandOptions
88 | ): Promise<void> {
89 | let hasError = false;
90 | try {
91 | // Validate required options
92 | if (!options.id) {
93 | console.error(chalk.red('Error: Task ID is required. Use -i or --id'));
94 | process.exit(1);
95 | }
96 |
97 | if (!options.status) {
98 | console.error(
99 | chalk.red('Error: Status is required. Use -s or --status')
100 | );
101 | process.exit(1);
102 | }
103 |
104 | // Validate status
105 | if (!VALID_TASK_STATUSES.includes(options.status)) {
106 | console.error(
107 | chalk.red(
108 | `Error: Invalid status "${options.status}". Valid options: ${VALID_TASK_STATUSES.join(', ')}`
109 | )
110 | );
111 | process.exit(1);
112 | }
113 |
114 | // Initialize TaskMaster core
115 | this.tmCore = await createTmCore({
116 | projectPath: getProjectRoot(options.project)
117 | });
118 |
119 | // Parse task IDs (handle comma-separated values)
120 | const taskIds = options.id.split(',').map((id) => id.trim());
121 |
122 | // Update each task
123 | const updatedTasks: Array<{
124 | taskId: string;
125 | oldStatus: TaskStatus;
126 | newStatus: TaskStatus;
127 | }> = [];
128 |
129 | for (const taskId of taskIds) {
130 | try {
131 | const result = await this.tmCore.tasks.updateStatus(
132 | taskId,
133 | options.status
134 | );
135 | updatedTasks.push({
136 | taskId: result.taskId,
137 | oldStatus: result.oldStatus,
138 | newStatus: result.newStatus
139 | });
140 | } catch (error: any) {
141 | hasError = true;
142 | if (options.format === 'json') {
143 | const errorMessage = error?.getSanitizedDetails
144 | ? error.getSanitizedDetails().message
145 | : error instanceof Error
146 | ? error.message
147 | : String(error);
148 |
149 | console.log(
150 | JSON.stringify({
151 | success: false,
152 | error: errorMessage,
153 | taskId,
154 | timestamp: new Date().toISOString()
155 | })
156 | );
157 | } else if (!options.silent) {
158 | // Show which task failed with context
159 | console.error(chalk.red(`\nFailed to update task ${taskId}:`));
160 | displayError(error, { skipExit: true });
161 | }
162 | // Don't exit here - let finally block clean up first
163 | break;
164 | }
165 | }
166 |
167 | // Store result for potential reuse
168 | this.lastResult = {
169 | success: true,
170 | updatedTasks,
171 | storageType: this.tmCore.tasks.getStorageType()
172 | };
173 |
174 | // Display results
175 | this.displayResults(this.lastResult, options);
176 | } catch (error: any) {
177 | hasError = true;
178 | if (options.format === 'json') {
179 | const errorMessage =
180 | error instanceof Error ? error.message : 'Unknown error occurred';
181 | console.log(JSON.stringify({ success: false, error: errorMessage }));
182 | } else if (!options.silent) {
183 | displayError(error, { skipExit: true });
184 | }
185 | } finally {
186 | // Clean up resources
187 | if (this.tmCore) {
188 | }
189 | }
190 |
191 | // Exit after cleanup completes
192 | if (hasError) {
193 | process.exit(1);
194 | }
195 | }
196 |
197 | /**
198 | * Display results based on format
199 | */
200 | private displayResults(
201 | result: SetStatusResult,
202 | options: SetStatusCommandOptions
203 | ): void {
204 | const format = options.format || 'text';
205 |
206 | switch (format) {
207 | case 'json':
208 | console.log(JSON.stringify(result, null, 2));
209 | break;
210 |
211 | case 'text':
212 | default:
213 | if (!options.silent) {
214 | this.displayTextResults(result);
215 | }
216 | break;
217 | }
218 | }
219 |
220 | /**
221 | * Display results in text format
222 | */
223 | private displayTextResults(result: SetStatusResult): void {
224 | if (result.updatedTasks.length === 1) {
225 | // Single task update
226 | const update = result.updatedTasks[0];
227 | console.log(
228 | boxen(
229 | chalk.white.bold(`✅ Successfully updated task ${update.taskId}`) +
230 | '\n\n' +
231 | `${chalk.blue('From:')} ${this.getStatusDisplay(update.oldStatus)}\n` +
232 | `${chalk.blue('To:')} ${this.getStatusDisplay(update.newStatus)}`,
233 | {
234 | padding: 1,
235 | borderColor: 'green',
236 | borderStyle: 'round',
237 | margin: { top: 1 }
238 | }
239 | )
240 | );
241 | } else {
242 | // Multiple task updates
243 | console.log(
244 | boxen(
245 | chalk.white.bold(
246 | `✅ Successfully updated ${result.updatedTasks.length} tasks`
247 | ) +
248 | '\n\n' +
249 | result.updatedTasks
250 | .map(
251 | (update) =>
252 | `${chalk.cyan(update.taskId)}: ${this.getStatusDisplay(update.oldStatus)} → ${this.getStatusDisplay(update.newStatus)}`
253 | )
254 | .join('\n'),
255 | {
256 | padding: 1,
257 | borderColor: 'green',
258 | borderStyle: 'round',
259 | margin: { top: 1 }
260 | }
261 | )
262 | );
263 | }
264 | }
265 |
266 | /**
267 | * Get colored status display
268 | */
269 | private getStatusDisplay(status: TaskStatus): string {
270 | const statusColors: Record<TaskStatus, (text: string) => string> = {
271 | pending: chalk.yellow,
272 | 'in-progress': chalk.blue,
273 | done: chalk.green,
274 | deferred: chalk.gray,
275 | cancelled: chalk.red,
276 | blocked: chalk.red,
277 | review: chalk.magenta,
278 | completed: chalk.green
279 | };
280 |
281 | const colorFn = statusColors[status] || chalk.white;
282 | return colorFn(status);
283 | }
284 |
285 | /**
286 | * Get the last command result (useful for testing or chaining)
287 | */
288 | getLastResult(): SetStatusResult | undefined {
289 | return this.lastResult;
290 | }
291 |
292 | /**
293 | * Register this command on an existing program
294 | */
295 | static register(program: Command, name?: string): SetStatusCommand {
296 | const setStatusCommand = new SetStatusCommand(name);
297 | program.addCommand(setStatusCommand);
298 | return setStatusCommand;
299 | }
300 | }
301 |
302 | /**
303 | * Factory function to create and configure the set-status command
304 | */
305 | export function createSetStatusCommand(): SetStatusCommand {
306 | return new SetStatusCommand();
307 | }
308 |
```
--------------------------------------------------------------------------------
/scripts/modules/task-manager/expand-all-tasks.js:
--------------------------------------------------------------------------------
```javascript
1 | import { log, readJSON, isSilentMode, findProjectRoot } from '../utils.js';
2 | import {
3 | startLoadingIndicator,
4 | stopLoadingIndicator,
5 | displayAiUsageSummary
6 | } from '../ui.js';
7 | import expandTask from './expand-task.js';
8 | import { getDebugFlag } from '../config-manager.js';
9 | import { aggregateTelemetry } from '../utils.js';
10 | import chalk from 'chalk';
11 | import boxen from 'boxen';
12 |
13 | /**
14 | * Expand all eligible pending or in-progress tasks using the expandTask function.
15 | * @param {string} tasksPath - Path to the tasks.json file
16 | * @param {number} [numSubtasks] - Optional: Target number of subtasks per task.
17 | * @param {boolean} [useResearch=false] - Whether to use the research AI role.
18 | * @param {string} [additionalContext=''] - Optional additional context.
19 | * @param {boolean} [force=false] - Force expansion even if tasks already have subtasks.
20 | * @param {Object} context - Context object containing session and mcpLog.
21 | * @param {Object} [context.session] - Session object from MCP.
22 | * @param {Object} [context.mcpLog] - MCP logger object.
23 | * @param {string} [context.projectRoot] - Project root path
24 | * @param {string} [context.tag] - Tag for the task
25 | * @param {string} [context.complexityReportPath] - Path to the complexity report file
26 | * @param {string} [outputFormat='text'] - Output format ('text' or 'json'). MCP calls should use 'json'.
27 | * @returns {Promise<{success: boolean, expandedCount: number, failedCount: number, skippedCount: number, tasksToExpand: number, telemetryData: Array<Object>}>} - Result summary.
28 | */
29 | async function expandAllTasks(
30 | tasksPath,
31 | numSubtasks, // Keep this signature, expandTask handles defaults
32 | useResearch = false,
33 | additionalContext = '',
34 | force = false, // Keep force here for the filter logic
35 | context = {},
36 | outputFormat = 'text' // Assume text default for CLI
37 | ) {
38 | const {
39 | session,
40 | mcpLog,
41 | projectRoot: providedProjectRoot,
42 | tag,
43 | complexityReportPath
44 | } = context;
45 | const isMCPCall = !!mcpLog; // Determine if called from MCP
46 |
47 | const projectRoot = providedProjectRoot || findProjectRoot();
48 | if (!projectRoot) {
49 | throw new Error('Could not determine project root directory');
50 | }
51 |
52 | // Use mcpLog if available, otherwise use the default console log wrapper respecting silent mode
53 | const logger =
54 | mcpLog ||
55 | (outputFormat === 'json'
56 | ? {
57 | // Basic logger for JSON output mode
58 | info: (msg) => {},
59 | warn: (msg) => {},
60 | error: (msg) => console.error(`ERROR: ${msg}`), // Still log errors
61 | debug: (msg) => {}
62 | }
63 | : {
64 | // CLI logger respecting silent mode
65 | info: (msg) => !isSilentMode() && log('info', msg),
66 | warn: (msg) => !isSilentMode() && log('warn', msg),
67 | error: (msg) => !isSilentMode() && log('error', msg),
68 | debug: (msg) =>
69 | !isSilentMode() && getDebugFlag(session) && log('debug', msg)
70 | });
71 |
72 | let loadingIndicator = null;
73 | let expandedCount = 0;
74 | let failedCount = 0;
75 | let tasksToExpandCount = 0;
76 | const allTelemetryData = []; // Still collect individual data first
77 |
78 | if (!isMCPCall && outputFormat === 'text') {
79 | loadingIndicator = startLoadingIndicator(
80 | 'Analyzing tasks for expansion...'
81 | );
82 | }
83 |
84 | try {
85 | logger.info(`Reading tasks from ${tasksPath}`);
86 | const data = readJSON(tasksPath, projectRoot, tag);
87 | if (!data || !data.tasks) {
88 | throw new Error(`Invalid tasks data in ${tasksPath}`);
89 | }
90 |
91 | // --- Restore Original Filtering Logic ---
92 | const tasksToExpand = data.tasks.filter(
93 | (task) =>
94 | (task.status === 'pending' || task.status === 'in-progress') && // Include 'in-progress'
95 | (!task.subtasks || task.subtasks.length === 0 || force) // Check subtasks/force here
96 | );
97 | tasksToExpandCount = tasksToExpand.length; // Get the count from the filtered array
98 | logger.info(`Found ${tasksToExpandCount} tasks eligible for expansion.`);
99 | // --- End Restored Filtering Logic ---
100 |
101 | if (loadingIndicator) {
102 | stopLoadingIndicator(loadingIndicator, 'Analysis complete.');
103 | }
104 |
105 | if (tasksToExpandCount === 0) {
106 | logger.info('No tasks eligible for expansion.');
107 | // --- Fix: Restore success: true and add message ---
108 | return {
109 | success: true, // Indicate overall success despite no action
110 | expandedCount: 0,
111 | failedCount: 0,
112 | skippedCount: 0,
113 | tasksToExpand: 0,
114 | telemetryData: allTelemetryData,
115 | message: 'No tasks eligible for expansion.'
116 | };
117 | // --- End Fix ---
118 | }
119 |
120 | // Iterate over the already filtered tasks
121 | for (const task of tasksToExpand) {
122 | // Start indicator for individual task expansion in CLI mode
123 | let taskIndicator = null;
124 | if (!isMCPCall && outputFormat === 'text') {
125 | taskIndicator = startLoadingIndicator(`Expanding task ${task.id}...`);
126 | }
127 |
128 | try {
129 | // Call the refactored expandTask function AND capture result
130 | const result = await expandTask(
131 | tasksPath,
132 | task.id,
133 | numSubtasks,
134 | useResearch,
135 | additionalContext,
136 | {
137 | ...context,
138 | projectRoot,
139 | tag: data.tag || tag,
140 | complexityReportPath
141 | }, // Pass the whole context object with projectRoot and resolved tag
142 | force
143 | );
144 | expandedCount++;
145 |
146 | // Collect individual telemetry data
147 | if (result && result.telemetryData) {
148 | allTelemetryData.push(result.telemetryData);
149 | }
150 |
151 | if (taskIndicator) {
152 | stopLoadingIndicator(taskIndicator, `Task ${task.id} expanded.`);
153 | }
154 | logger.info(`Successfully expanded task ${task.id}.`);
155 | } catch (error) {
156 | failedCount++;
157 | if (taskIndicator) {
158 | stopLoadingIndicator(
159 | taskIndicator,
160 | `Failed to expand task ${task.id}.`,
161 | false
162 | );
163 | }
164 | logger.error(`Failed to expand task ${task.id}: ${error.message}`);
165 | // Continue to the next task
166 | }
167 | }
168 |
169 | // --- AGGREGATION AND DISPLAY ---
170 | logger.info(
171 | `Expansion complete: ${expandedCount} expanded, ${failedCount} failed.`
172 | );
173 |
174 | // Aggregate the collected telemetry data
175 | const aggregatedTelemetryData = aggregateTelemetry(
176 | allTelemetryData,
177 | 'expand-all-tasks'
178 | );
179 |
180 | if (outputFormat === 'text') {
181 | const summaryContent =
182 | `${chalk.white.bold('Expansion Summary:')}\n\n` +
183 | `${chalk.cyan('-')} Attempted: ${chalk.bold(tasksToExpandCount)}\n` +
184 | `${chalk.green('-')} Expanded: ${chalk.bold(expandedCount)}\n` +
185 | // Skipped count is always 0 now due to pre-filtering
186 | `${chalk.gray('-')} Skipped: ${chalk.bold(0)}\n` +
187 | `${chalk.red('-')} Failed: ${chalk.bold(failedCount)}`;
188 |
189 | console.log(
190 | boxen(summaryContent, {
191 | padding: 1,
192 | margin: { top: 1 },
193 | borderColor: failedCount > 0 ? 'red' : 'green', // Red if failures, green otherwise
194 | borderStyle: 'round'
195 | })
196 | );
197 | }
198 |
199 | if (outputFormat === 'text' && aggregatedTelemetryData) {
200 | displayAiUsageSummary(aggregatedTelemetryData, 'cli');
201 | }
202 |
203 | // Return summary including the AGGREGATED telemetry data
204 | return {
205 | success: true,
206 | expandedCount,
207 | failedCount,
208 | skippedCount: 0,
209 | tasksToExpand: tasksToExpandCount,
210 | telemetryData: aggregatedTelemetryData
211 | };
212 | } catch (error) {
213 | if (loadingIndicator)
214 | stopLoadingIndicator(loadingIndicator, 'Error.', false);
215 | logger.error(`Error during expand all operation: ${error.message}`);
216 | if (!isMCPCall && getDebugFlag(session)) {
217 | console.error(error); // Log full stack in debug CLI mode
218 | }
219 | // Re-throw error for the caller to handle, the direct function will format it
220 | throw error; // Let direct function wrapper handle formatting
221 | /* Original re-throw:
222 | throw new Error(`Failed to expand all tasks: ${error.message}`);
223 | */
224 | }
225 | }
226 |
227 | export default expandAllTasks;
228 |
```
--------------------------------------------------------------------------------
/apps/cli/src/commands/models/custom-providers.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview Custom provider handlers for model setup
3 | */
4 |
5 | import { CUSTOM_PROVIDERS } from '@tm/core';
6 | import chalk from 'chalk';
7 | import inquirer from 'inquirer';
8 | import { validateOllamaModel, validateOpenRouterModel } from './fetchers.js';
9 | import { CUSTOM_PROVIDER_IDS } from './types.js';
10 | import type {
11 | CustomProviderConfig,
12 | CustomProviderId,
13 | ModelRole
14 | } from './types.js';
15 |
16 | /**
17 | * Configuration for all custom providers
18 | */
19 | export const customProviderConfigs: Record<
20 | keyof typeof CUSTOM_PROVIDER_IDS,
21 | CustomProviderConfig
22 | > = {
23 | OPENROUTER: {
24 | id: '__CUSTOM_OPENROUTER__',
25 | name: '* Custom OpenRouter model',
26 | provider: CUSTOM_PROVIDERS.OPENROUTER,
27 | promptMessage: (role) =>
28 | `Enter the custom OpenRouter Model ID for the ${role} role:`,
29 | validate: async (modelId) => {
30 | const isValid = await validateOpenRouterModel(modelId);
31 | if (!isValid) {
32 | console.error(
33 | chalk.red(
34 | `Error: Model ID "${modelId}" not found in the live OpenRouter model list. Please check the ID.`
35 | )
36 | );
37 | }
38 | return isValid;
39 | }
40 | },
41 | OLLAMA: {
42 | id: '__CUSTOM_OLLAMA__',
43 | name: '* Custom Ollama model',
44 | provider: CUSTOM_PROVIDERS.OLLAMA,
45 | requiresBaseURL: true,
46 | defaultBaseURL: 'http://localhost:11434/api',
47 | promptMessage: (role) =>
48 | `Enter the custom Ollama Model ID for the ${role} role:`,
49 | validate: async (modelId, baseURL) => {
50 | const urlToCheck = baseURL || 'http://localhost:11434/api';
51 | const isValid = await validateOllamaModel(modelId, urlToCheck);
52 | if (!isValid) {
53 | console.error(
54 | chalk.red(
55 | `Error: Model ID "${modelId}" not found in the Ollama instance. Please verify the model is pulled and available.`
56 | )
57 | );
58 | console.log(
59 | chalk.yellow(
60 | `You can check available models with: curl ${urlToCheck}/tags`
61 | )
62 | );
63 | }
64 | return isValid;
65 | }
66 | },
67 | BEDROCK: {
68 | id: '__CUSTOM_BEDROCK__',
69 | name: '* Custom Bedrock model',
70 | provider: CUSTOM_PROVIDERS.BEDROCK,
71 | promptMessage: (role) =>
72 | `Enter the custom Bedrock Model ID for the ${role} role (e.g., anthropic.claude-3-sonnet-20240229-v1:0):`,
73 | checkEnvVars: () => {
74 | if (
75 | !process.env.AWS_ACCESS_KEY_ID ||
76 | !process.env.AWS_SECRET_ACCESS_KEY
77 | ) {
78 | console.warn(
79 | chalk.yellow(
80 | 'Warning: AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY environment variables are missing. Will fallback to system configuration (ex: aws config files or ec2 instance profiles).'
81 | )
82 | );
83 | }
84 | return true;
85 | }
86 | },
87 | AZURE: {
88 | id: '__CUSTOM_AZURE__',
89 | name: '* Custom Azure model',
90 | provider: CUSTOM_PROVIDERS.AZURE,
91 | promptMessage: (role) =>
92 | `Enter the custom Azure OpenAI Model ID for the ${role} role (e.g., gpt-4o):`,
93 | checkEnvVars: () => {
94 | if (
95 | !process.env.AZURE_OPENAI_API_KEY ||
96 | !process.env.AZURE_OPENAI_ENDPOINT
97 | ) {
98 | console.error(
99 | chalk.red(
100 | 'Error: AZURE_OPENAI_API_KEY and/or AZURE_OPENAI_ENDPOINT environment variables are missing. Please set them before using custom Azure models.'
101 | )
102 | );
103 | return false;
104 | }
105 | return true;
106 | }
107 | },
108 | VERTEX: {
109 | id: '__CUSTOM_VERTEX__',
110 | name: '* Custom Vertex model',
111 | provider: CUSTOM_PROVIDERS.VERTEX,
112 | promptMessage: (role) =>
113 | `Enter the custom Vertex AI Model ID for the ${role} role (e.g., gemini-1.5-pro-002):`,
114 | checkEnvVars: () => {
115 | if (
116 | !process.env.GOOGLE_API_KEY &&
117 | !process.env.GOOGLE_APPLICATION_CREDENTIALS
118 | ) {
119 | console.error(
120 | chalk.red(
121 | 'Error: Either GOOGLE_API_KEY or GOOGLE_APPLICATION_CREDENTIALS environment variable is required. Please set one before using custom Vertex models.'
122 | )
123 | );
124 | return false;
125 | }
126 | return true;
127 | }
128 | },
129 | LMSTUDIO: {
130 | id: '__CUSTOM_LMSTUDIO__',
131 | name: '* Custom LMStudio model',
132 | provider: CUSTOM_PROVIDERS.LMSTUDIO,
133 | requiresBaseURL: true,
134 | defaultBaseURL: 'http://localhost:1234/v1',
135 | promptMessage: (role) =>
136 | `Enter the custom LM Studio Model ID for the ${role} role:`,
137 | checkEnvVars: () => {
138 | console.log(
139 | chalk.blue(
140 | 'Note: LM Studio runs locally. Make sure the LM Studio server is running.'
141 | )
142 | );
143 | return true;
144 | }
145 | },
146 | OPENAI_COMPATIBLE: {
147 | id: '__CUSTOM_OPENAI_COMPATIBLE__',
148 | name: '* Custom OpenAI-compatible model',
149 | provider: CUSTOM_PROVIDERS.OPENAI_COMPATIBLE,
150 | promptMessage: (role) =>
151 | `Enter the custom OpenAI-compatible Model ID for the ${role} role:`,
152 | requiresBaseURL: true,
153 | checkEnvVars: () => {
154 | console.log(
155 | chalk.blue(
156 | 'Note: This will configure a generic OpenAI-compatible provider. Make sure your API endpoint is accessible.'
157 | )
158 | );
159 | return true;
160 | }
161 | }
162 | };
163 |
164 | /**
165 | * Handle custom provider selection
166 | */
167 | export async function handleCustomProvider(
168 | providerId: CustomProviderId,
169 | role: ModelRole,
170 | currentModel: {
171 | modelId?: string | null;
172 | provider?: string | null;
173 | baseURL?: string | null;
174 | } | null = null
175 | ): Promise<{
176 | modelId: string | null;
177 | provider: string | null;
178 | baseURL?: string | null;
179 | success: boolean;
180 | }> {
181 | // Find the matching config
182 | const configEntry = Object.entries(customProviderConfigs).find(
183 | ([_, config]) => config.id === providerId
184 | );
185 |
186 | if (!configEntry) {
187 | console.error(chalk.red(`Unknown custom provider: ${providerId}`));
188 | return { modelId: null, provider: null, success: false };
189 | }
190 |
191 | const config = configEntry[1];
192 |
193 | // Check environment variables if needed
194 | if (config.checkEnvVars && !config.checkEnvVars()) {
195 | return { modelId: null, provider: null, success: false };
196 | }
197 |
198 | // Prompt for baseURL if required
199 | let baseURL: string | null = null;
200 | if (config.requiresBaseURL) {
201 | // Determine the appropriate default baseURL
202 | let defaultBaseURL: string;
203 | if (currentModel?.provider === config.provider && currentModel?.baseURL) {
204 | // Already using this provider - preserve existing baseURL
205 | defaultBaseURL = currentModel.baseURL;
206 | } else {
207 | // Switching providers or no existing baseURL - use fallback default
208 | defaultBaseURL = config.defaultBaseURL || '';
209 | }
210 |
211 | const baseURLAnswer = await inquirer.prompt([
212 | {
213 | type: 'input',
214 | name: 'baseURL',
215 | message: `Enter the base URL for the ${role} role:`,
216 | default: defaultBaseURL,
217 | validate: (input: string) => {
218 | if (!input || input.trim() === '') {
219 | return `Base URL is required for ${config.provider} providers`;
220 | }
221 | try {
222 | new URL(input);
223 | return true;
224 | } catch {
225 | return 'Please enter a valid URL';
226 | }
227 | }
228 | }
229 | ]);
230 | baseURL = baseURLAnswer.baseURL;
231 | }
232 |
233 | // Prompt for custom ID
234 | const { customId } = await inquirer.prompt([
235 | {
236 | type: 'input',
237 | name: 'customId',
238 | message: config.promptMessage(role)
239 | }
240 | ]);
241 |
242 | if (!customId) {
243 | console.log(chalk.yellow('No custom ID entered. Skipping role.'));
244 | return { modelId: null, provider: null, success: true };
245 | }
246 |
247 | // Validate if validation function exists
248 | if (config.validate) {
249 | const isValid = await config.validate(customId, baseURL || undefined);
250 | if (!isValid) {
251 | return { modelId: null, provider: null, success: false };
252 | }
253 | } else {
254 | console.log(
255 | chalk.blue(
256 | `Custom ${config.provider} model "${customId}" will be used. No validation performed.`
257 | )
258 | );
259 | }
260 |
261 | return {
262 | modelId: customId,
263 | provider: config.provider,
264 | baseURL: baseURL,
265 | success: true
266 | };
267 | }
268 |
269 | /**
270 | * Get all custom provider options for display
271 | */
272 | export function getCustomProviderOptions(): Array<{
273 | name: string;
274 | value: CustomProviderId;
275 | short: string;
276 | }> {
277 | return Object.values(customProviderConfigs).map((config) => ({
278 | name: config.name,
279 | value: config.id,
280 | short: config.name
281 | }));
282 | }
283 |
```
--------------------------------------------------------------------------------
/docs/cross-tag-task-movement.md:
--------------------------------------------------------------------------------
```markdown
1 | # Cross-Tag Task Movement
2 |
3 | Task Master now supports moving tasks between different tag contexts, allowing you to organize your work across multiple project contexts, feature branches, or development phases.
4 |
5 | ## Overview
6 |
7 | Cross-tag task movement enables you to:
8 | - Move tasks between different tag contexts (e.g., from "backlog" to "in-progress")
9 | - Handle cross-tag dependencies intelligently
10 | - Maintain task relationships across different contexts
11 | - Organize work across multiple project phases
12 |
13 | ## Basic Usage
14 |
15 | ### Within-Tag Moves
16 |
17 | Move tasks within the same tag context:
18 |
19 | ```bash
20 | # Move a single task
21 | task-master move --from=5 --to=7
22 |
23 | # Move a subtask
24 | task-master move --from=5.2 --to=7.3
25 |
26 | # Move multiple tasks
27 | task-master move --from=5,6,7 --to=10,11,12
28 | ```
29 |
30 | ### Cross-Tag Moves
31 |
32 | Move tasks between different tag contexts:
33 |
34 | ```bash
35 | # Basic cross-tag move
36 | task-master move --from=5 --from-tag=backlog --to-tag=in-progress
37 |
38 | # Move multiple tasks
39 | task-master move --from=5,6,7 --from-tag=backlog --to-tag=done
40 | ```
41 |
42 | ## Dependency Resolution
43 |
44 | When moving tasks between tags, you may encounter cross-tag dependencies. Task Master provides several options to handle these:
45 |
46 | ### Move with Dependencies
47 |
48 | Move the main task along with all its dependent tasks:
49 |
50 | ```bash
51 | task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies
52 | ```
53 |
54 | This ensures that all dependent tasks are moved together, maintaining the task relationships.
55 |
56 | ### Break Dependencies
57 |
58 | Break cross-tag dependencies and move only the specified task:
59 |
60 | ```bash
61 | task-master move --from=5 --from-tag=backlog --to-tag=in-progress --ignore-dependencies
62 | ```
63 |
64 | This removes the dependency relationships and moves only the specified task.
65 |
66 | ### Force Move
67 |
68 | Note: Force moves are no longer supported. Instead, use one of these options:
69 |
70 | - `--with-dependencies` — move dependents together
71 | - `--ignore-dependencies` — break cross-tag dependencies
72 |
73 | ⚠️ **Warning**: This may break dependency relationships and should be used with caution.
74 |
75 | ## Error Handling
76 |
77 | Task Master provides enhanced error messages with specific resolution suggestions:
78 |
79 | ### Cross-Tag Dependency Conflicts
80 |
81 | When you encounter dependency conflicts, you'll see:
82 |
83 | ```text
84 | ❌ Cannot move tasks from "backlog" to "in-progress"
85 |
86 | Cross-tag dependency conflicts detected:
87 | • Task 5 depends on 2 (in backlog)
88 | • Task 6 depends on 3 (in done)
89 |
90 | Resolution options:
91 | 1. Move with dependencies: task-master move --from=5,6 --from-tag=backlog --to-tag=in-progress --with-dependencies
92 | 2. Break dependencies: task-master move --from=5,6 --from-tag=backlog --to-tag=in-progress --ignore-dependencies
93 | 3. Validate and fix dependencies: task-master validate-dependencies && task-master fix-dependencies
94 | 4. Move dependencies first: task-master move --from=2,3 --from-tag=backlog --to-tag=in-progress
95 | 5. After deciding, re-run the move with either --with-dependencies or --ignore-dependencies
96 | ```
97 |
98 | ### Subtask Movement Restrictions
99 |
100 | Subtasks cannot be moved directly between tags:
101 |
102 | ```text
103 | ❌ Cannot move subtask 5.2 directly between tags
104 |
105 | Subtask movement restriction:
106 | • Subtasks cannot be moved directly between tags
107 | • They must be promoted to full tasks first
108 |
109 | Resolution options:
110 | 1. Promote subtask to full task: task-master remove-subtask --id=5.2 --convert
111 | 2. Then move the promoted task: task-master move --from=5 --from-tag=backlog --to-tag=in-progress
112 | 3. Or move the parent task with all subtasks: task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies
113 | ```
114 |
115 | ### Invalid Tag Combinations
116 |
117 | When source and target tags are the same:
118 |
119 | ```text
120 | ❌ Invalid tag combination
121 |
122 | Error details:
123 | • Source tag: "backlog"
124 | • Target tag: "backlog"
125 | • Reason: Source and target tags are identical
126 |
127 | Resolution options:
128 | 1. Use different tags for cross-tag moves
129 | 2. Use within-tag move: task-master move --from=<id> --to=<id> --tag=backlog
130 | 3. Check available tags: task-master tags
131 | ```
132 |
133 | ## Best Practices
134 |
135 | ### 1. Check Dependencies First
136 |
137 | Before moving tasks, validate your dependencies:
138 |
139 | ```bash
140 | # Check for dependency issues
141 | task-master validate-dependencies
142 |
143 | # Fix common dependency problems
144 | task-master fix-dependencies
145 | ```
146 |
147 | ### 2. Use Appropriate Flags
148 |
149 | - **`--with-dependencies`**: When you want to maintain task relationships
150 | - **`--ignore-dependencies`**: When you want to break cross-tag dependencies
151 |
152 | ### 3. Organize by Context
153 |
154 | Use tags to organize work by:
155 | - **Development phases**: `backlog`, `in-progress`, `review`, `done`
156 | - **Feature branches**: `feature-auth`, `feature-dashboard`
157 | - **Team members**: `alice-tasks`, `bob-tasks`
158 | - **Project versions**: `v1.0`, `v2.0`
159 |
160 | ### 4. Handle Subtasks Properly
161 |
162 | For subtasks, either:
163 | 1. Promote the subtask to a full task first
164 | 2. Move the parent task with all subtasks using `--with-dependencies`
165 |
166 | ## Advanced Usage
167 |
168 | ### Multiple Task Movement
169 |
170 | Move multiple tasks at once:
171 |
172 | ```bash
173 | # Move multiple tasks with dependencies
174 | task-master move --from=5,6,7 --from-tag=backlog --to-tag=in-progress --with-dependencies
175 |
176 | # Move multiple tasks, breaking dependencies
177 | task-master move --from=5,6,7 --from-tag=backlog --to-tag=in-progress --ignore-dependencies
178 | ```
179 |
180 | ### Tag Creation
181 |
182 | Target tags are created automatically if they don't exist:
183 |
184 | ```bash
185 | # This will create the "new-feature" tag if it doesn't exist
186 | task-master move --from=5 --from-tag=backlog --to-tag=new-feature
187 | ```
188 |
189 | ### Current Tag Fallback
190 |
191 | If `--from-tag` is not provided, the current tag is used:
192 |
193 | ```bash
194 | # Uses current tag as source
195 | task-master move --from=5 --to-tag=in-progress
196 | ```
197 |
198 | ## MCP Integration
199 |
200 | The cross-tag move functionality is also available through MCP tools:
201 |
202 | ```javascript
203 | // Move task with dependencies
204 | await moveTask({
205 | from: "5",
206 | fromTag: "backlog",
207 | toTag: "in-progress",
208 | withDependencies: true
209 | });
210 |
211 | // Break dependencies
212 | await moveTask({
213 | from: "5",
214 | fromTag: "backlog",
215 | toTag: "in-progress",
216 | ignoreDependencies: true
217 | });
218 | ```
219 |
220 | ## Troubleshooting
221 |
222 | ### Common Issues
223 |
224 | 1. **"Source tag not found"**: Check available tags with `task-master tags`
225 | 2. **"Task not found"**: Verify task IDs with `task-master list`
226 | 3. **"Cross-tag dependency conflicts"**: Use dependency resolution flags
227 | 4. **"Cannot move subtask"**: Promote subtask first or move parent task
228 |
229 | ### Getting Help
230 |
231 | ```bash
232 | # Show move command help
233 | task-master move --help
234 |
235 | # Check available tags
236 | task-master tags
237 |
238 | # Validate dependencies
239 | task-master validate-dependencies
240 |
241 | # Fix dependency issues
242 | task-master fix-dependencies
243 | ```
244 |
245 | ## Examples
246 |
247 | ### Scenario 1: Moving from Backlog to In-Progress
248 |
249 | ```bash
250 | # Check for dependencies first
251 | task-master validate-dependencies
252 |
253 | # Move with dependencies
254 | task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies
255 | ```
256 |
257 | ### Scenario 2: Breaking Dependencies
258 |
259 | ```bash
260 | # Move task, breaking cross-tag dependencies
261 | task-master move --from=5 --from-tag=backlog --to-tag=done --ignore-dependencies
262 | ```
263 |
264 | ### Scenario 3: Force Move
265 |
266 | Choose one of these options explicitly:
267 |
268 | ```bash
269 | # Move together with dependencies
270 | task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies
271 |
272 | # Or break dependencies
273 | task-master move --from=5 --from-tag=backlog --to-tag=in-progress --ignore-dependencies
274 | ```
275 |
276 | ### Scenario 4: Moving Subtasks
277 |
278 | ```bash
279 | # Option 1: Promote subtask first
280 | task-master remove-subtask --id=5.2 --convert
281 | task-master move --from=5 --from-tag=backlog --to-tag=in-progress
282 |
283 | # Option 2: Move parent with all subtasks
284 | task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies
285 | ```
286 |
```
--------------------------------------------------------------------------------
/src/prompts/parse-prd.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "id": "parse-prd",
3 | "version": "1.0.0",
4 | "description": "Parse a Product Requirements Document into structured tasks",
5 | "metadata": {
6 | "author": "system",
7 | "created": "2024-01-01T00:00:00Z",
8 | "updated": "2024-01-01T00:00:00Z",
9 | "tags": ["prd", "parsing", "initialization"]
10 | },
11 | "parameters": {
12 | "numTasks": {
13 | "type": "number",
14 | "required": true,
15 | "description": "Target number of tasks to generate"
16 | },
17 | "nextId": {
18 | "type": "number",
19 | "required": true,
20 | "description": "Starting ID for tasks"
21 | },
22 | "research": {
23 | "type": "boolean",
24 | "default": false,
25 | "description": "Enable research mode for latest best practices"
26 | },
27 | "prdContent": {
28 | "type": "string",
29 | "required": true,
30 | "description": "Content of the PRD file"
31 | },
32 | "prdPath": {
33 | "type": "string",
34 | "required": true,
35 | "description": "Path to the PRD file"
36 | },
37 | "defaultTaskPriority": {
38 | "type": "string",
39 | "required": false,
40 | "default": "medium",
41 | "enum": ["high", "medium", "low"],
42 | "description": "Default priority for generated tasks"
43 | },
44 | "hasCodebaseAnalysis": {
45 | "type": "boolean",
46 | "required": false,
47 | "default": false,
48 | "description": "Whether codebase analysis is available"
49 | },
50 | "projectRoot": {
51 | "type": "string",
52 | "required": false,
53 | "default": "",
54 | "description": "Project root path for context"
55 | }
56 | },
57 | "prompts": {
58 | "default": {
59 | "system": "You are an AI assistant specialized in analyzing Product Requirements Documents (PRDs) and generating a structured, logically ordered, dependency-aware and sequenced list of development tasks in JSON format.{{#if research}}\nBefore breaking down the PRD into tasks, you will:\n1. Research and analyze the latest technologies, libraries, frameworks, and best practices that would be appropriate for this project\n2. Identify any potential technical challenges, security concerns, or scalability issues not explicitly mentioned in the PRD without discarding any explicit requirements or going overboard with complexity -- always aim to provide the most direct path to implementation, avoiding over-engineering or roundabout approaches\n3. Consider current industry standards and evolving trends relevant to this project (this step aims to solve LLM hallucinations and out of date information due to training data cutoff dates)\n4. Evaluate alternative implementation approaches and recommend the most efficient path\n5. Include specific library versions, helpful APIs, and concrete implementation guidance based on your research\n6. Always aim to provide the most direct path to implementation, avoiding over-engineering or roundabout approaches\n\nYour task breakdown should incorporate this research, resulting in more detailed implementation guidance, more accurate dependency mapping, and more precise technology recommendations than would be possible from the PRD text alone, while maintaining all explicit requirements and best practices and all details and nuances of the PRD.{{/if}}\n\nAnalyze the provided PRD content and generate {{#if (gt numTasks 0)}}approximately {{numTasks}}{{else}}an appropriate number of{{/if}} top-level development tasks. If the complexity or the level of detail of the PRD is high, generate more tasks relative to the complexity of the PRD\nEach task should represent a logical unit of work needed to implement the requirements and focus on the most direct and effective way to implement the requirements without unnecessary complexity or overengineering. Include pseudo-code, implementation details, and test strategy for each task. Find the most up to date information to implement each task.\nAssign sequential IDs starting from {{nextId}}. Infer title, description, details, and test strategy for each task based *only* on the PRD content.\nSet status to 'pending', dependencies to an empty array [], and priority to '{{defaultTaskPriority}}' initially for all tasks.\nGenerate a response containing a single key \"tasks\", where the value is an array of task objects adhering to the provided schema.\n\nEach task should follow this JSON structure:\n{\n\t\"id\": number,\n\t\"title\": string,\n\t\"description\": string,\n\t\"status\": \"pending\",\n\t\"dependencies\": number[] (IDs of tasks this depends on),\n\t\"priority\": \"high\" | \"medium\" | \"low\",\n\t\"details\": string (implementation details),\n\t\"testStrategy\": string (validation approach)\n}\n\nGuidelines:\n1. {{#if (gt numTasks 0)}}Unless complexity warrants otherwise{{else}}Depending on the complexity{{/if}}, create {{#if (gt numTasks 0)}}exactly {{numTasks}}{{else}}an appropriate number of{{/if}} tasks, numbered sequentially starting from {{nextId}}\n2. Each task should be atomic and focused on a single responsibility following the most up to date best practices and standards\n3. Order tasks logically - consider dependencies and implementation sequence\n4. Early tasks should focus on setup, core functionality first, then advanced features\n5. Include clear validation/testing approach for each task\n6. Set appropriate dependency IDs (a task can only depend on tasks with lower IDs, potentially including existing tasks with IDs less than {{nextId}} if applicable)\n7. Assign priority (high/medium/low) based on criticality and dependency order\n8. Include detailed implementation guidance in the \"details\" field{{#if research}}, with specific libraries and version recommendations based on your research{{/if}}\n9. If the PRD contains specific requirements for libraries, database schemas, frameworks, tech stacks, or any other implementation details, STRICTLY ADHERE to these requirements in your task breakdown and do not discard them under any circumstance\n10. Focus on filling in any gaps left by the PRD or areas that aren't fully specified, while preserving all explicit requirements\n11. Always aim to provide the most direct path to implementation, avoiding over-engineering or roundabout approaches{{#if research}}\n12. For each task, include specific, actionable guidance based on current industry standards and best practices discovered through research{{/if}}",
60 | "user": "{{#if hasCodebaseAnalysis}}## IMPORTANT: Codebase Analysis Required\n\nYou have access to powerful codebase analysis tools. Before generating tasks:\n\n1. Use the Glob tool to explore the project structure (e.g., \"**/*.js\", \"**/*.json\", \"**/README.md\")\n2. Use the Grep tool to search for existing implementations, patterns, and technologies\n3. Use the Read tool to examine key files like package.json, README.md, and main entry points\n4. Analyze the current state of implementation to understand what already exists\n\nBased on your analysis:\n- Identify what components/features are already implemented\n- Understand the technology stack, frameworks, and patterns in use\n- Generate tasks that build upon the existing codebase rather than duplicating work\n- Ensure tasks align with the project's current architecture and conventions\n\nProject Root: {{projectRoot}}\n\n{{/if}}Here's the Product Requirements Document (PRD) to break down into {{#if (gt numTasks 0)}}approximately {{numTasks}}{{else}}an appropriate number of{{/if}} tasks, starting IDs from {{nextId}}:{{#if research}}\n\nRemember to thoroughly research current best practices and technologies before task breakdown to provide specific, actionable implementation details.{{/if}}\n\n{{prdContent}}\n\nIMPORTANT: Your response must be a JSON object with a \"tasks\" property containing an array of task objects. You may optionally include a \"metadata\" object. Do not include any other properties."
61 | }
62 | }
63 | }
64 |
```
--------------------------------------------------------------------------------
/scripts/modules/task-manager/remove-task.js:
--------------------------------------------------------------------------------
```javascript
1 | import path from 'path';
2 | import * as fs from 'fs';
3 | import { readJSON, writeJSON, log, findTaskById } from '../utils.js';
4 | import generateTaskFiles from './generate-task-files.js';
5 | import taskExists from './task-exists.js';
6 |
7 | /**
8 | * Removes one or more tasks or subtasks from the tasks file
9 | * @param {string} tasksPath - Path to the tasks file
10 | * @param {string} taskIds - Comma-separated string of task/subtask IDs to remove (e.g., '5,6.1,7')
11 | * @param {Object} context - Context object containing projectRoot and tag information
12 | * @param {string} [context.projectRoot] - Project root path
13 | * @param {string} [context.tag] - Tag for the task
14 | * @returns {Object} Result object with success status, messages, and removed task info
15 | */
16 | async function removeTask(tasksPath, taskIds, context = {}) {
17 | const { projectRoot, tag } = context;
18 | const results = {
19 | success: true,
20 | messages: [],
21 | errors: [],
22 | removedTasks: []
23 | };
24 | const taskIdsToRemove = taskIds
25 | .split(',')
26 | .map((id) => id.trim())
27 | .filter(Boolean); // Remove empty strings if any
28 |
29 | if (taskIdsToRemove.length === 0) {
30 | results.success = false;
31 | results.errors.push('No valid task IDs provided.');
32 | return results;
33 | }
34 |
35 | try {
36 | // Read the tasks file ONCE before the loop, preserving the full tagged structure
37 | const rawData = readJSON(tasksPath, projectRoot, tag); // Read raw data
38 | if (!rawData) {
39 | throw new Error(`Could not read tasks file at ${tasksPath}`);
40 | }
41 |
42 | // Use the full tagged data if available, otherwise use the data as is
43 | const fullTaggedData = rawData._rawTaggedData || rawData;
44 |
45 | if (!fullTaggedData[tag] || !fullTaggedData[tag].tasks) {
46 | throw new Error(`Tag '${tag}' not found or has no tasks.`);
47 | }
48 |
49 | const tasks = fullTaggedData[tag].tasks; // Work with tasks from the correct tag
50 |
51 | const tasksToDeleteFiles = []; // Collect IDs of main tasks whose files should be deleted
52 |
53 | for (const taskId of taskIdsToRemove) {
54 | // Check if the task ID exists *before* attempting removal
55 | if (!taskExists(tasks, taskId)) {
56 | const errorMsg = `Task with ID ${taskId} in tag '${tag}' not found or already removed.`;
57 | results.errors.push(errorMsg);
58 | results.success = false; // Mark overall success as false if any error occurs
59 | continue; // Skip to the next ID
60 | }
61 |
62 | try {
63 | // Handle subtask removal (e.g., '5.2')
64 | if (typeof taskId === 'string' && taskId.includes('.')) {
65 | const [parentTaskId, subtaskId] = taskId
66 | .split('.')
67 | .map((id) => parseInt(id, 10));
68 |
69 | // Find the parent task
70 | const parentTask = tasks.find((t) => t.id === parentTaskId);
71 | if (!parentTask || !parentTask.subtasks) {
72 | throw new Error(
73 | `Parent task ${parentTaskId} or its subtasks not found for subtask ${taskId}`
74 | );
75 | }
76 |
77 | // Find the subtask to remove
78 | const subtaskIndex = parentTask.subtasks.findIndex(
79 | (st) => st.id === subtaskId
80 | );
81 | if (subtaskIndex === -1) {
82 | throw new Error(
83 | `Subtask ${subtaskId} not found in parent task ${parentTaskId}`
84 | );
85 | }
86 |
87 | // Store the subtask info before removal
88 | const removedSubtask = {
89 | ...parentTask.subtasks[subtaskIndex],
90 | parentTaskId: parentTaskId
91 | };
92 | results.removedTasks.push(removedSubtask);
93 |
94 | // Remove the subtask from the parent
95 | parentTask.subtasks.splice(subtaskIndex, 1);
96 |
97 | results.messages.push(
98 | `Successfully removed subtask ${taskId} from tag '${tag}'`
99 | );
100 | }
101 | // Handle main task removal
102 | else {
103 | const taskIdNum = parseInt(taskId, 10);
104 | const taskIndex = tasks.findIndex((t) => t.id === taskIdNum);
105 | if (taskIndex === -1) {
106 | throw new Error(`Task with ID ${taskId} not found in tag '${tag}'`);
107 | }
108 |
109 | // Store the task info before removal
110 | const removedTask = tasks[taskIndex];
111 | results.removedTasks.push(removedTask);
112 | tasksToDeleteFiles.push(taskIdNum); // Add to list for file deletion
113 |
114 | // Remove the task from the main array
115 | tasks.splice(taskIndex, 1);
116 |
117 | results.messages.push(
118 | `Successfully removed task ${taskId} from tag '${tag}'`
119 | );
120 | }
121 | } catch (innerError) {
122 | // Catch errors specific to processing *this* ID
123 | const errorMsg = `Error processing ID ${taskId}: ${innerError.message}`;
124 | results.errors.push(errorMsg);
125 | results.success = false;
126 | log('warn', errorMsg); // Log as warning and continue with next ID
127 | }
128 | } // End of loop through taskIdsToRemove
129 |
130 | // --- Post-Loop Operations ---
131 |
132 | // Only proceed with cleanup and saving if at least one task was potentially removed
133 | if (results.removedTasks.length > 0) {
134 | const allRemovedIds = new Set(
135 | taskIdsToRemove.map((id) =>
136 | typeof id === 'string' && id.includes('.') ? id : parseInt(id, 10)
137 | )
138 | );
139 |
140 | // Update the tasks in the current tag of the full data structure
141 | fullTaggedData[tag].tasks = tasks;
142 |
143 | // Remove dependencies from all tags
144 | for (const tagName in fullTaggedData) {
145 | if (
146 | Object.prototype.hasOwnProperty.call(fullTaggedData, tagName) &&
147 | fullTaggedData[tagName] &&
148 | fullTaggedData[tagName].tasks
149 | ) {
150 | const currentTagTasks = fullTaggedData[tagName].tasks;
151 | currentTagTasks.forEach((task) => {
152 | if (task.dependencies) {
153 | task.dependencies = task.dependencies.filter(
154 | (depId) => !allRemovedIds.has(depId)
155 | );
156 | }
157 | if (task.subtasks) {
158 | task.subtasks.forEach((subtask) => {
159 | if (subtask.dependencies) {
160 | subtask.dependencies = subtask.dependencies.filter(
161 | (depId) =>
162 | !allRemovedIds.has(`${task.id}.${depId}`) &&
163 | !allRemovedIds.has(depId)
164 | );
165 | }
166 | });
167 | }
168 | });
169 | }
170 | }
171 |
172 | // Save the updated raw data structure
173 | writeJSON(tasksPath, fullTaggedData, projectRoot, tag);
174 |
175 | // Delete task files AFTER saving tasks.json
176 | for (const taskIdNum of tasksToDeleteFiles) {
177 | const taskFileName = path.join(
178 | path.dirname(tasksPath),
179 | `task_${taskIdNum.toString().padStart(3, '0')}.txt`
180 | );
181 | if (fs.existsSync(taskFileName)) {
182 | try {
183 | fs.unlinkSync(taskFileName);
184 | results.messages.push(`Deleted task file: ${taskFileName}`);
185 | } catch (unlinkError) {
186 | const unlinkMsg = `Failed to delete task file ${taskFileName}: ${unlinkError.message}`;
187 | results.errors.push(unlinkMsg);
188 | results.success = false;
189 | log('warn', unlinkMsg);
190 | }
191 | }
192 | }
193 |
194 | // Generate updated task files ONCE, with context
195 | // try {
196 | // await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
197 | // projectRoot,
198 | // tag
199 | // });
200 | // results.messages.push('Task files regenerated successfully.');
201 | // } catch (genError) {
202 | // const genErrMsg = `Failed to regenerate task files: ${genError.message}`;
203 | // results.errors.push(genErrMsg);
204 | // results.success = false;
205 | // log('warn', genErrMsg);
206 | // }
207 | } else if (results.errors.length === 0) {
208 | results.messages.push('No tasks found matching the provided IDs.');
209 | }
210 |
211 | // Consolidate messages for final output
212 | const finalMessage = results.messages.join('\n');
213 | const finalError = results.errors.join('\n');
214 |
215 | return {
216 | success: results.success,
217 | message: finalMessage || 'No tasks were removed.',
218 | error: finalError || null,
219 | removedTasks: results.removedTasks
220 | };
221 | } catch (error) {
222 | // Catch errors from reading file or other initial setup
223 | log('error', `Error removing tasks: ${error.message}`);
224 | return {
225 | success: false,
226 | message: '',
227 | error: `Operation failed: ${error.message}`,
228 | removedTasks: []
229 | };
230 | }
231 | }
232 |
233 | export default removeTask;
234 |
```
--------------------------------------------------------------------------------
/apps/docs/getting-started/contribute.mdx:
--------------------------------------------------------------------------------
```markdown
1 | # Contributing to Task Master
2 |
3 | Thank you for your interest in contributing to Task Master! We're excited to work with you and appreciate your help in making this project better. 🚀
4 |
5 | ## 🤝 Our Collaborative Approach
6 |
7 | We're a **PR-friendly team** that values collaboration:
8 |
9 | - ✅ **We review PRs quickly** - Usually within hours, not days
10 | - ✅ **We're super reactive** - Expect fast feedback and engagement
11 | - ✅ **We sometimes take over PRs** - If your contribution is valuable but needs cleanup, we might jump in to help finish it
12 | - ✅ **We're open to all contributions** - From bug fixes to major features
13 |
14 | **We don't mind AI-generated code**, but we do expect you to:
15 |
16 | - ✅ **Review and understand** what the AI generated
17 | - ✅ **Test the code thoroughly** before submitting
18 | - ✅ **Ensure it's well-written** and follows our patterns
19 | - ❌ **Don't submit "AI slop"** - untested, unreviewed AI output
20 |
21 | > **Why this matters**: We spend significant time reviewing PRs. Help us help you by submitting quality contributions that save everyone time!
22 |
23 | ## 🚀 Quick Start for Contributors
24 |
25 | ### 1. Fork and Clone
26 |
27 | ```bash
28 | git clone https://github.com/YOUR_USERNAME/claude-task-master.git
29 | cd claude-task-master
30 | npm install
31 | ```
32 |
33 | ### 2. Create a Feature Branch
34 |
35 | **Important**: Always target the `next` branch, not `main`:
36 |
37 | ```bash
38 | git checkout next
39 | git pull origin next
40 | git checkout -b feature/your-feature-name
41 | ```
42 |
43 | ### 3. Make Your Changes
44 |
45 | Follow our development guidelines below.
46 |
47 | ### 4. Test Everything Yourself
48 |
49 | **Before submitting your PR**, ensure:
50 |
51 | ```bash
52 | # Run all tests
53 | npm test
54 |
55 | # Check formatting
56 | npm run format-check
57 |
58 | # Fix formatting if needed
59 | npm run format
60 | ```
61 |
62 | ### 5. Create a Changeset
63 |
64 | **Required for most changes**:
65 |
66 | ```bash
67 | npm run changeset
68 | ```
69 |
70 | See the [Changeset Guidelines](#changeset-guidelines) below for details.
71 |
72 | ### 6. Submit Your PR
73 |
74 | - Target the `next` branch
75 | - Write a clear description
76 | - Reference any related issues
77 |
78 | ## 📋 Development Guidelines
79 |
80 | ### Branch Strategy
81 |
82 | - **`main`**: Production-ready code
83 | - **`next`**: Development branch - **target this for PRs**
84 | - **Feature branches**: `feature/description` or `fix/description`
85 |
86 | ### Code Quality Standards
87 |
88 | 1. **Write tests** for new functionality
89 | 2. **Follow existing patterns** in the codebase
90 | 3. **Add JSDoc comments** for functions
91 | 4. **Keep functions focused** and single-purpose
92 |
93 | ### Testing Requirements
94 |
95 | Your PR **must pass all CI checks**:
96 |
97 | - ✅ **Unit tests**: `npm test`
98 | - ✅ **Format check**: `npm run format-check`
99 |
100 | **Test your changes locally first** - this saves review time and shows you care about quality.
101 |
102 | ## 📦 Changeset Guidelines
103 |
104 | We use [Changesets](https://github.com/changesets/changesets) to manage versioning and generate changelogs.
105 |
106 | ### When to Create a Changeset
107 |
108 | **Always create a changeset for**:
109 |
110 | - ✅ New features
111 | - ✅ Bug fixes
112 | - ✅ Breaking changes
113 | - ✅ Performance improvements
114 | - ✅ User-facing documentation updates
115 | - ✅ Dependency updates that affect functionality
116 |
117 | **Skip changesets for**:
118 |
119 | - ❌ Internal documentation only
120 | - ❌ Test-only changes
121 | - ❌ Code formatting/linting
122 | - ❌ Development tooling that doesn't affect users
123 |
124 | ### How to Create a Changeset
125 |
126 | 1. **After making your changes**:
127 |
128 | ```bash
129 | npm run changeset
130 | ```
131 |
132 | 2. **Choose the bump type**:
133 |
134 | - **Major**: Breaking changes
135 | - **Minor**: New features
136 | - **Patch**: Bug fixes, docs, performance improvements
137 |
138 | 3. **Write a clear summary**:
139 |
140 | ```
141 | Add support for custom AI models in MCP configuration
142 | ```
143 |
144 | 4. **Commit the changeset file** with your changes:
145 | ```bash
146 | git add .changeset/*.md
147 | git commit -m "feat: add custom AI model support"
148 | ```
149 |
150 | ### Changeset vs Git Commit Messages
151 |
152 | - **Changeset summary**: User-facing, goes in CHANGELOG.md
153 | - **Git commit**: Developer-facing, explains the technical change
154 |
155 | Example:
156 |
157 | ```bash
158 | # Changeset summary (user-facing)
159 | "Add support for custom Ollama models"
160 |
161 | # Git commit message (developer-facing)
162 | "feat(models): implement custom Ollama model validation
163 |
164 | - Add model validation for custom Ollama endpoints
165 | - Update configuration schema to support custom models
166 | - Add tests for new validation logic"
167 | ```
168 |
169 | ## 🔧 Development Setup
170 |
171 | ### Prerequisites
172 |
173 | - Node.js 18+
174 | - npm or yarn
175 |
176 | ### Environment Setup
177 |
178 | 1. **Copy environment template**:
179 |
180 | ```bash
181 | cp .env.example .env
182 | ```
183 |
184 | 2. **Add your API keys** (for testing AI features):
185 | ```bash
186 | ANTHROPIC_API_KEY=your_key_here
187 | OPENAI_API_KEY=your_key_here
188 | # Add others as needed
189 | ```
190 |
191 | ### Running Tests
192 |
193 | ```bash
194 | # Run all tests
195 | npm test
196 |
197 | # Run tests in watch mode
198 | npm run test:watch
199 |
200 | # Run with coverage
201 | npm run test:coverage
202 |
203 | # Run E2E tests
204 | npm run test:e2e
205 | ```
206 |
207 | ### Code Formatting
208 |
209 | We use Prettier for consistent formatting:
210 |
211 | ```bash
212 | # Check formatting
213 | npm run format-check
214 |
215 | # Fix formatting
216 | npm run format
217 | ```
218 |
219 | ## 📝 PR Guidelines
220 |
221 | ### Before Submitting
222 |
223 | - [ ] **Target the `next` branch**
224 | - [ ] **Test everything locally**
225 | - [ ] **Run the full test suite**
226 | - [ ] **Check code formatting**
227 | - [ ] **Create a changeset** (if needed)
228 | - [ ] **Re-read your changes** - ensure they're clean and well-thought-out
229 |
230 | ### PR Description Template
231 |
232 | ```markdown
233 | ## Description
234 |
235 | Brief description of what this PR does.
236 |
237 | ## Type of Change
238 |
239 | - [ ] Bug fix
240 | - [ ] New feature
241 | - [ ] Breaking change
242 | - [ ] Documentation update
243 |
244 | ## Testing
245 |
246 | - [ ] I have tested this locally
247 | - [ ] All existing tests pass
248 | - [ ] I have added tests for new functionality
249 |
250 | ## Changeset
251 |
252 | - [ ] I have created a changeset (or this change doesn't need one)
253 |
254 | ## Additional Notes
255 |
256 | Any additional context or notes for reviewers.
257 | ```
258 |
259 | ### What We Look For
260 |
261 | ✅ **Good PRs**:
262 |
263 | - Clear, focused changes
264 | - Comprehensive testing
265 | - Good commit messages
266 | - Proper changeset (when needed)
267 | - Self-reviewed code
268 |
269 | ❌ **Avoid**:
270 |
271 | - Massive PRs that change everything
272 | - Untested code
273 | - Formatting issues
274 | - Missing changesets for user-facing changes
275 | - AI-generated code that wasn't reviewed
276 |
277 | ## 🏗️ Project Structure
278 |
279 | ```
280 | claude-task-master/
281 | ├── bin/ # CLI executables
282 | ├── mcp-server/ # MCP server implementation
283 | ├── scripts/ # Core task management logic
284 | ├── src/ # Shared utilities and providers and well refactored code (we are slowly moving everything here)
285 | ├── tests/ # Test files
286 | ├── docs/ # Documentation
287 | └── .cursor/ # Cursor IDE rules and configuration
288 | └── assets/ # Assets like rules and configuration for all IDEs
289 | ```
290 |
291 | ### Key Areas for Contribution
292 |
293 | - **CLI Commands**: `scripts/modules/commands.js`
294 | - **MCP Tools**: `mcp-server/src/tools/`
295 | - **Core Logic**: `scripts/modules/task-manager/`
296 | - **AI Providers**: `src/ai-providers/`
297 | - **Tests**: `tests/`
298 |
299 | ## 🐛 Reporting Issues
300 |
301 | ### Bug Reports
302 |
303 | Include:
304 |
305 | - Task Master version
306 | - Node.js version
307 | - Operating system
308 | - Steps to reproduce
309 | - Expected vs actual behavior
310 | - Error messages/logs
311 |
312 | ### Feature Requests
313 |
314 | Include:
315 |
316 | - Clear description of the feature
317 | - Use case/motivation
318 | - Proposed implementation (if you have ideas)
319 | - Willingness to contribute
320 |
321 | ## 💬 Getting Help
322 |
323 | - **Discord**: [Join our community](https://discord.gg/taskmasterai)
324 | - **Issues**: [GitHub Issues](https://github.com/eyaltoledano/claude-task-master/issues)
325 | - **Discussions**: [GitHub Discussions](https://github.com/eyaltoledano/claude-task-master/discussions)
326 |
327 | ## 📄 License
328 |
329 | By contributing, you agree that your contributions will be licensed under the same license as the project (MIT with Commons Clause).
330 |
331 | ---
332 |
333 | **Thank you for contributing to Task Master!** 🎉
334 |
335 | Your contributions help make AI-driven development more accessible and efficient for everyone.
```