This is page 41 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
--------------------------------------------------------------------------------
/apps/cli/src/ui/components/dashboard.component.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview Dashboard components for Task Master CLI
3 | * Displays project statistics and dependency information
4 | */
5 |
6 | import type { Task, TaskPriority, TaskStatus } from '@tm/core';
7 | import boxen from 'boxen';
8 | import chalk from 'chalk';
9 | import { isTaskComplete } from '../../utils/task-status.js';
10 | import { getComplexityWithColor } from '../../utils/ui.js';
11 |
12 | /**
13 | * Statistics for task collection
14 | */
15 | export interface TaskStatistics {
16 | total: number;
17 | done: number;
18 | inProgress: number;
19 | pending: number;
20 | blocked: number;
21 | deferred: number;
22 | cancelled: number;
23 | review?: number;
24 | completionPercentage: number;
25 | /** Count of all terminal complete tasks (done + completed + cancelled) */
26 | completedCount: number;
27 | }
28 |
29 | /**
30 | * Statistics for dependencies
31 | */
32 | export interface DependencyStatistics {
33 | tasksWithNoDeps: number;
34 | tasksReadyToWork: number;
35 | tasksBlockedByDeps: number;
36 | mostDependedOnTaskId?: number;
37 | mostDependedOnCount?: number;
38 | avgDependenciesPerTask: number;
39 | }
40 |
41 | /**
42 | * Next task information
43 | */
44 | export interface NextTaskInfo {
45 | id: string | number;
46 | title: string;
47 | priority?: TaskPriority;
48 | dependencies?: (string | number)[];
49 | complexity?: number | string;
50 | }
51 |
52 | /**
53 | * Status breakdown for progress bars
54 | */
55 | export interface StatusBreakdown {
56 | done?: number;
57 | 'in-progress'?: number;
58 | pending?: number;
59 | blocked?: number;
60 | deferred?: number;
61 | cancelled?: number;
62 | review?: number;
63 | }
64 |
65 | /**
66 | * Create a progress bar with color-coded status segments
67 | */
68 | function createProgressBar(
69 | completionPercentage: number,
70 | width: number = 30,
71 | statusBreakdown?: StatusBreakdown
72 | ): string {
73 | // If no breakdown provided, use simple green bar
74 | if (!statusBreakdown) {
75 | const filled = Math.round((completionPercentage / 100) * width);
76 | const empty = width - filled;
77 | return chalk.green('█').repeat(filled) + chalk.gray('░').repeat(empty);
78 | }
79 |
80 | // Build the bar with different colored sections
81 | // Order matches the status display: Done, Cancelled, Deferred, In Progress, Review, Pending, Blocked
82 | let bar = '';
83 | let charsUsed = 0;
84 |
85 | // 1. Green filled blocks for done tasks only
86 | // Note: completionPercentage includes cancelled, but we show them separately in the bar
87 | if (statusBreakdown.done && statusBreakdown.done > 0) {
88 | const doneChars = Math.round((statusBreakdown.done / 100) * width);
89 | if (doneChars > 0) {
90 | bar += chalk.green('█').repeat(doneChars);
91 | charsUsed += doneChars;
92 | }
93 | }
94 |
95 | // 2. Gray filled blocks for cancelled (terminal complete, but visually distinct)
96 | if (statusBreakdown.cancelled && charsUsed < width) {
97 | const cancelledChars = Math.round(
98 | (statusBreakdown.cancelled / 100) * width
99 | );
100 | const actualChars = Math.min(cancelledChars, width - charsUsed);
101 | if (actualChars > 0) {
102 | bar += chalk.gray('█').repeat(actualChars);
103 | charsUsed += actualChars;
104 | }
105 | }
106 |
107 | // 3. Gray filled blocks for deferred (won't be done now)
108 | if (statusBreakdown.deferred && charsUsed < width) {
109 | const deferredChars = Math.round((statusBreakdown.deferred / 100) * width);
110 | const actualChars = Math.min(deferredChars, width - charsUsed);
111 | if (actualChars > 0) {
112 | bar += chalk.gray('█').repeat(actualChars);
113 | charsUsed += actualChars;
114 | }
115 | }
116 |
117 | // 4. Blue filled blocks for in-progress (actively working)
118 | if (statusBreakdown['in-progress'] && charsUsed < width) {
119 | const inProgressChars = Math.round(
120 | (statusBreakdown['in-progress'] / 100) * width
121 | );
122 | const actualChars = Math.min(inProgressChars, width - charsUsed);
123 | if (actualChars > 0) {
124 | bar += chalk.blue('█').repeat(actualChars);
125 | charsUsed += actualChars;
126 | }
127 | }
128 |
129 | // 5. Magenta empty blocks for review (almost done)
130 | if (statusBreakdown.review && charsUsed < width) {
131 | const reviewChars = Math.round((statusBreakdown.review / 100) * width);
132 | const actualChars = Math.min(reviewChars, width - charsUsed);
133 | if (actualChars > 0) {
134 | bar += chalk.magenta('░').repeat(actualChars);
135 | charsUsed += actualChars;
136 | }
137 | }
138 |
139 | // 6. Yellow empty blocks for pending (ready to start)
140 | if (statusBreakdown.pending && charsUsed < width) {
141 | const pendingChars = Math.round((statusBreakdown.pending / 100) * width);
142 | const actualChars = Math.min(pendingChars, width - charsUsed);
143 | if (actualChars > 0) {
144 | bar += chalk.yellow('░').repeat(actualChars);
145 | charsUsed += actualChars;
146 | }
147 | }
148 |
149 | // 7. Red empty blocks for blocked (can't start yet)
150 | if (statusBreakdown.blocked && charsUsed < width) {
151 | const blockedChars = Math.round((statusBreakdown.blocked / 100) * width);
152 | const actualChars = Math.min(blockedChars, width - charsUsed);
153 | if (actualChars > 0) {
154 | bar += chalk.red('░').repeat(actualChars);
155 | charsUsed += actualChars;
156 | }
157 | }
158 |
159 | // Fill any remaining space with gray empty yellow blocks
160 | if (charsUsed < width) {
161 | bar += chalk.yellow('░').repeat(width - charsUsed);
162 | }
163 |
164 | return bar;
165 | }
166 |
167 | /**
168 | * Calculate task statistics from a list of tasks
169 | */
170 | export function calculateTaskStatistics(tasks: Task[]): TaskStatistics {
171 | const stats: TaskStatistics = {
172 | total: tasks.length,
173 | done: 0,
174 | inProgress: 0,
175 | pending: 0,
176 | blocked: 0,
177 | deferred: 0,
178 | cancelled: 0,
179 | review: 0,
180 | completionPercentage: 0,
181 | completedCount: 0
182 | };
183 |
184 | tasks.forEach((task) => {
185 | switch (task.status) {
186 | case 'done':
187 | stats.done++;
188 | break;
189 | case 'in-progress':
190 | stats.inProgress++;
191 | break;
192 | case 'pending':
193 | stats.pending++;
194 | break;
195 | case 'blocked':
196 | stats.blocked++;
197 | break;
198 | case 'deferred':
199 | stats.deferred++;
200 | break;
201 | case 'cancelled':
202 | stats.cancelled++;
203 | break;
204 | case 'review':
205 | stats.review = (stats.review || 0) + 1;
206 | break;
207 | }
208 | });
209 |
210 | // Count terminal complete tasks for percentage calculation and display
211 | stats.completedCount = tasks.filter((t) => isTaskComplete(t.status)).length;
212 | stats.completionPercentage =
213 | stats.total > 0
214 | ? Math.round((stats.completedCount / stats.total) * 100)
215 | : 0;
216 |
217 | return stats;
218 | }
219 |
220 | /**
221 | * Calculate subtask statistics from tasks
222 | */
223 | export function calculateSubtaskStatistics(tasks: Task[]): TaskStatistics {
224 | const stats: TaskStatistics = {
225 | total: 0,
226 | done: 0,
227 | inProgress: 0,
228 | pending: 0,
229 | blocked: 0,
230 | deferred: 0,
231 | cancelled: 0,
232 | review: 0,
233 | completionPercentage: 0,
234 | completedCount: 0
235 | };
236 |
237 | const allSubtasks: Array<{ status: string }> = [];
238 | tasks.forEach((task) => {
239 | if (task.subtasks && task.subtasks.length > 0) {
240 | task.subtasks.forEach((subtask) => {
241 | stats.total++;
242 | allSubtasks.push(subtask);
243 | switch (subtask.status) {
244 | case 'done':
245 | stats.done++;
246 | break;
247 | case 'in-progress':
248 | stats.inProgress++;
249 | break;
250 | case 'pending':
251 | stats.pending++;
252 | break;
253 | case 'blocked':
254 | stats.blocked++;
255 | break;
256 | case 'deferred':
257 | stats.deferred++;
258 | break;
259 | case 'cancelled':
260 | stats.cancelled++;
261 | break;
262 | case 'review':
263 | stats.review = (stats.review || 0) + 1;
264 | break;
265 | }
266 | });
267 | }
268 | });
269 |
270 | // Count terminal complete subtasks for percentage calculation and display
271 | stats.completedCount = allSubtasks.filter((st) =>
272 | isTaskComplete(st.status as TaskStatus)
273 | ).length;
274 | stats.completionPercentage =
275 | stats.total > 0
276 | ? Math.round((stats.completedCount / stats.total) * 100)
277 | : 0;
278 |
279 | return stats;
280 | }
281 |
282 | /**
283 | * Calculate dependency statistics
284 | */
285 | export function calculateDependencyStatistics(
286 | tasks: Task[]
287 | ): DependencyStatistics {
288 | // Get all terminal complete task IDs - these satisfy dependencies
289 | const completedTaskIds = new Set(
290 | tasks.filter((t) => isTaskComplete(t.status)).map((t) => t.id)
291 | );
292 |
293 | const tasksWithNoDeps = tasks.filter(
294 | (t) =>
295 | !isTaskComplete(t.status) &&
296 | (!t.dependencies || t.dependencies.length === 0)
297 | ).length;
298 |
299 | const tasksWithAllDepsSatisfied = tasks.filter(
300 | (t) =>
301 | !isTaskComplete(t.status) &&
302 | t.dependencies &&
303 | t.dependencies.length > 0 &&
304 | t.dependencies.every((depId) => completedTaskIds.has(depId))
305 | ).length;
306 |
307 | const tasksBlockedByDeps = tasks.filter(
308 | (t) =>
309 | !isTaskComplete(t.status) &&
310 | t.dependencies &&
311 | t.dependencies.length > 0 &&
312 | !t.dependencies.every((depId) => completedTaskIds.has(depId))
313 | ).length;
314 |
315 | // Calculate most depended-on task
316 | const dependencyCount: Record<string, number> = {};
317 | tasks.forEach((task) => {
318 | if (task.dependencies && task.dependencies.length > 0) {
319 | task.dependencies.forEach((depId) => {
320 | const key = String(depId);
321 | dependencyCount[key] = (dependencyCount[key] || 0) + 1;
322 | });
323 | }
324 | });
325 |
326 | let mostDependedOnTaskId: number | undefined;
327 | let mostDependedOnCount = 0;
328 |
329 | for (const [taskId, count] of Object.entries(dependencyCount)) {
330 | if (count > mostDependedOnCount) {
331 | mostDependedOnCount = count;
332 | mostDependedOnTaskId = parseInt(taskId);
333 | }
334 | }
335 |
336 | // Calculate average dependencies
337 | const totalDependencies = tasks.reduce(
338 | (sum, task) => sum + (task.dependencies ? task.dependencies.length : 0),
339 | 0
340 | );
341 | const avgDependenciesPerTask =
342 | tasks.length > 0 ? totalDependencies / tasks.length : 0;
343 |
344 | return {
345 | tasksWithNoDeps,
346 | tasksReadyToWork: tasksWithNoDeps + tasksWithAllDepsSatisfied,
347 | tasksBlockedByDeps,
348 | mostDependedOnTaskId,
349 | mostDependedOnCount,
350 | avgDependenciesPerTask
351 | };
352 | }
353 |
354 | /**
355 | * Get priority counts
356 | */
357 | export function getPriorityBreakdown(
358 | tasks: Task[]
359 | ): Record<TaskPriority, number> {
360 | const breakdown: Record<TaskPriority, number> = {
361 | critical: 0,
362 | high: 0,
363 | medium: 0,
364 | low: 0
365 | };
366 |
367 | tasks.forEach((task) => {
368 | const priority = task.priority || 'medium';
369 | breakdown[priority]++;
370 | });
371 |
372 | return breakdown;
373 | }
374 |
375 | /**
376 | * Calculate status breakdown as percentages
377 | */
378 | function calculateStatusBreakdown(stats: TaskStatistics): StatusBreakdown {
379 | if (stats.total === 0) return {};
380 |
381 | return {
382 | done: (stats.done / stats.total) * 100,
383 | 'in-progress': (stats.inProgress / stats.total) * 100,
384 | pending: (stats.pending / stats.total) * 100,
385 | blocked: (stats.blocked / stats.total) * 100,
386 | deferred: (stats.deferred / stats.total) * 100,
387 | cancelled: (stats.cancelled / stats.total) * 100,
388 | review: ((stats.review || 0) / stats.total) * 100
389 | };
390 | }
391 |
392 | /**
393 | * Format status counts in the correct order with colors
394 | * @param stats - The statistics object containing counts
395 | * @param isSubtask - Whether this is for subtasks (affects "Done" vs "Completed" label)
396 | */
397 | function formatStatusLine(
398 | stats: TaskStatistics,
399 | isSubtask: boolean = false
400 | ): string {
401 | const parts: string[] = [];
402 |
403 | // Order: Done, Cancelled, Deferred, In Progress, Review, Pending, Blocked
404 | if (isSubtask) {
405 | parts.push(
406 | `Completed: ${chalk.green(`${stats.completedCount}/${stats.total}`)}`
407 | );
408 | } else {
409 | parts.push(`Done: ${chalk.green(stats.done)}`);
410 | }
411 |
412 | parts.push(`Cancelled: ${chalk.gray(stats.cancelled)}`);
413 | parts.push(`Deferred: ${chalk.gray(stats.deferred)}`);
414 |
415 | // Add line break for second row
416 | const firstLine = parts.join(' ');
417 | parts.length = 0;
418 |
419 | parts.push(`In Progress: ${chalk.blue(stats.inProgress)}`);
420 | parts.push(`Review: ${chalk.magenta(stats.review || 0)}`);
421 | parts.push(`Pending: ${chalk.yellow(stats.pending)}`);
422 | parts.push(`Blocked: ${chalk.red(stats.blocked)}`);
423 |
424 | const secondLine = parts.join(' ');
425 |
426 | return firstLine + '\n' + secondLine;
427 | }
428 |
429 | /**
430 | * Display the project dashboard box
431 | */
432 | export function displayProjectDashboard(
433 | taskStats: TaskStatistics,
434 | subtaskStats: TaskStatistics,
435 | priorityBreakdown: Record<TaskPriority, number>
436 | ): string {
437 | // Calculate status breakdowns using the helper function
438 | const taskStatusBreakdown = calculateStatusBreakdown(taskStats);
439 | const subtaskStatusBreakdown = calculateStatusBreakdown(subtaskStats);
440 |
441 | // Create progress bars with the breakdowns
442 | const taskProgressBar = createProgressBar(
443 | taskStats.completionPercentage,
444 | 30,
445 | taskStatusBreakdown
446 | );
447 | const subtaskProgressBar = createProgressBar(
448 | subtaskStats.completionPercentage,
449 | 30,
450 | subtaskStatusBreakdown
451 | );
452 |
453 | const taskPercentage = `${taskStats.completionPercentage}% ${taskStats.completedCount}/${taskStats.total}`;
454 | const subtaskPercentage = `${subtaskStats.completionPercentage}% ${subtaskStats.completedCount}/${subtaskStats.total}`;
455 |
456 | const content =
457 | chalk.white.bold('Project Dashboard') +
458 | '\n' +
459 | `Tasks Progress: ${taskProgressBar} ${chalk.yellow(taskPercentage)}\n` +
460 | formatStatusLine(taskStats, false) +
461 | '\n\n' +
462 | `Subtasks Progress: ${subtaskProgressBar} ${chalk.cyan(subtaskPercentage)}\n` +
463 | formatStatusLine(subtaskStats, true) +
464 | '\n\n' +
465 | chalk.cyan.bold('Priority Breakdown:') +
466 | '\n' +
467 | `${chalk.red('•')} ${chalk.white('High priority:')} ${priorityBreakdown.high}\n` +
468 | `${chalk.yellow('•')} ${chalk.white('Medium priority:')} ${priorityBreakdown.medium}\n` +
469 | `${chalk.green('•')} ${chalk.white('Low priority:')} ${priorityBreakdown.low}`;
470 |
471 | return content;
472 | }
473 |
474 | /**
475 | * Display the dependency dashboard box
476 | */
477 | export function displayDependencyDashboard(
478 | depStats: DependencyStatistics,
479 | nextTask?: NextTaskInfo
480 | ): string {
481 | const content =
482 | chalk.white.bold('Dependency Status & Next Task') +
483 | '\n' +
484 | chalk.cyan.bold('Dependency Metrics:') +
485 | '\n' +
486 | `${chalk.green('•')} ${chalk.white('Tasks with no dependencies:')} ${depStats.tasksWithNoDeps}\n` +
487 | `${chalk.green('•')} ${chalk.white('Tasks ready to work on:')} ${depStats.tasksReadyToWork}\n` +
488 | `${chalk.yellow('•')} ${chalk.white('Tasks blocked by dependencies:')} ${depStats.tasksBlockedByDeps}\n` +
489 | `${chalk.magenta('•')} ${chalk.white('Most depended-on task:')} ${
490 | depStats.mostDependedOnTaskId
491 | ? chalk.cyan(
492 | `#${depStats.mostDependedOnTaskId} (${depStats.mostDependedOnCount} dependents)`
493 | )
494 | : chalk.gray('None')
495 | }\n` +
496 | `${chalk.blue('•')} ${chalk.white('Avg dependencies per task:')} ${depStats.avgDependenciesPerTask.toFixed(1)}\n\n` +
497 | chalk.cyan.bold('Next Task to Work On:') +
498 | '\n' +
499 | `ID: ${nextTask ? chalk.cyan(String(nextTask.id)) : chalk.gray('N/A')} - ${
500 | nextTask
501 | ? chalk.white.bold(nextTask.title)
502 | : chalk.yellow('No task available')
503 | }\n` +
504 | `Priority: ${nextTask?.priority || chalk.gray('N/A')} Dependencies: ${
505 | nextTask?.dependencies?.length
506 | ? chalk.cyan(nextTask.dependencies.join(', '))
507 | : chalk.gray('None')
508 | }\n` +
509 | `Complexity: ${nextTask?.complexity !== undefined ? getComplexityWithColor(nextTask.complexity) : chalk.gray('N/A')}`;
510 |
511 | return content;
512 | }
513 |
514 | /**
515 | * Display dashboard boxes side by side or stacked
516 | */
517 | export function displayDashboards(
518 | taskStats: TaskStatistics,
519 | subtaskStats: TaskStatistics,
520 | priorityBreakdown: Record<TaskPriority, number>,
521 | depStats: DependencyStatistics,
522 | nextTask?: NextTaskInfo
523 | ): void {
524 | const projectDashboardContent = displayProjectDashboard(
525 | taskStats,
526 | subtaskStats,
527 | priorityBreakdown
528 | );
529 | const dependencyDashboardContent = displayDependencyDashboard(
530 | depStats,
531 | nextTask
532 | );
533 |
534 | // Get terminal width
535 | const terminalWidth = process.stdout.columns || 80;
536 | const minDashboardWidth = 50;
537 | const minDependencyWidth = 50;
538 | const totalMinWidth = minDashboardWidth + minDependencyWidth + 4;
539 |
540 | // If terminal is wide enough, show side by side
541 | if (terminalWidth >= totalMinWidth) {
542 | const halfWidth = Math.floor(terminalWidth / 2);
543 | const boxContentWidth = halfWidth - 4;
544 |
545 | const dashboardBox = boxen(projectDashboardContent, {
546 | padding: 1,
547 | borderColor: 'blue',
548 | borderStyle: 'round',
549 | width: boxContentWidth,
550 | dimBorder: false
551 | });
552 |
553 | const dependencyBox = boxen(dependencyDashboardContent, {
554 | padding: 1,
555 | borderColor: 'magenta',
556 | borderStyle: 'round',
557 | width: boxContentWidth,
558 | dimBorder: false
559 | });
560 |
561 | // Create side-by-side layout
562 | const dashboardLines = dashboardBox.split('\n');
563 | const dependencyLines = dependencyBox.split('\n');
564 | const maxHeight = Math.max(dashboardLines.length, dependencyLines.length);
565 |
566 | const combinedLines = [];
567 | for (let i = 0; i < maxHeight; i++) {
568 | const dashLine = i < dashboardLines.length ? dashboardLines[i] : '';
569 | const depLine = i < dependencyLines.length ? dependencyLines[i] : '';
570 | const paddedDashLine = dashLine.padEnd(halfWidth, ' ');
571 | combinedLines.push(paddedDashLine + depLine);
572 | }
573 |
574 | console.log(combinedLines.join('\n'));
575 | } else {
576 | // Show stacked vertically
577 | const dashboardBox = boxen(projectDashboardContent, {
578 | padding: 1,
579 | borderColor: 'blue',
580 | borderStyle: 'round',
581 | margin: { top: 0, bottom: 1 }
582 | });
583 |
584 | const dependencyBox = boxen(dependencyDashboardContent, {
585 | padding: 1,
586 | borderColor: 'magenta',
587 | borderStyle: 'round',
588 | margin: { top: 0, bottom: 1 }
589 | });
590 |
591 | console.log(dashboardBox);
592 | console.log(dependencyBox);
593 | }
594 | }
595 |
```
--------------------------------------------------------------------------------
/docs/tutorial.md:
--------------------------------------------------------------------------------
```markdown
1 | # Task Master Tutorial
2 |
3 | This tutorial will guide you through setting up and using Task Master for AI-driven development.
4 |
5 | ## Initial Setup
6 |
7 | There are two ways to set up Task Master: using MCP (recommended) or via npm installation.
8 |
9 | ### Option 1: Using MCP (Recommended)
10 |
11 | MCP (Model Control Protocol) provides the easiest way to get started with Task Master directly in your editor.
12 |
13 | 1. **Install the package**
14 |
15 | ```bash
16 | npm i -g task-master-ai
17 | ```
18 |
19 | 2. **Add the MCP config to your IDE/MCP Client** (Cursor is recommended, but it works with other clients):
20 |
21 | ```json
22 | {
23 | "mcpServers": {
24 | "taskmaster-ai": {
25 | "command": "npx",
26 | "args": ["-y", "task-master-ai"],
27 | "env": {
28 | "ANTHROPIC_API_KEY": "YOUR_ANTHROPIC_API_KEY_HERE",
29 | "PERPLEXITY_API_KEY": "YOUR_PERPLEXITY_API_KEY_HERE",
30 | "OPENAI_API_KEY": "YOUR_OPENAI_KEY_HERE",
31 | "GOOGLE_API_KEY": "YOUR_GOOGLE_KEY_HERE",
32 | "MISTRAL_API_KEY": "YOUR_MISTRAL_KEY_HERE",
33 | "OPENROUTER_API_KEY": "YOUR_OPENROUTER_KEY_HERE",
34 | "XAI_API_KEY": "YOUR_XAI_KEY_HERE",
35 | "AZURE_OPENAI_API_KEY": "YOUR_AZURE_KEY_HERE"
36 | }
37 | }
38 | }
39 | }
40 | ```
41 |
42 | **IMPORTANT:** An API key is _required_ for each AI provider you plan on using. Run the `task-master models` command to see your selected models and the status of your API keys across .env and mcp.json
43 |
44 | **To use AI commands in CLI** you MUST have API keys in the .env file
45 | **To use AI commands in MCP** you MUST have API keys in the .mcp.json file (or MCP config equivalent)
46 |
47 | We recommend having keys in both places and adding mcp.json to your gitignore so your API keys aren't checked into git.
48 |
49 | 3. **Enable the MCP** in your editor settings
50 |
51 | 4. **Prompt the AI** to initialize Task Master:
52 |
53 | ```
54 | Can you please initialize taskmaster-ai into my project?
55 | ```
56 |
57 | The AI will:
58 |
59 | - Create necessary project structure
60 | - Set up initial configuration files
61 | - Guide you through the rest of the process
62 |
63 | 5. Place your PRD document in the `.taskmaster/docs/` directory (e.g., `.taskmaster/docs/prd.txt`)
64 |
65 | 6. **Use natural language commands** to interact with Task Master:
66 |
67 | ```
68 | Can you parse my PRD at .taskmaster/docs/prd.txt?
69 | What's the next task I should work on?
70 | Can you help me implement task 3?
71 | ```
72 |
73 | ### Option 2: Manual Installation
74 |
75 | If you prefer to use the command line interface directly:
76 |
77 | ```bash
78 | # Install globally
79 | npm install -g task-master-ai
80 |
81 | # OR install locally within your project
82 | npm install task-master-ai
83 | ```
84 |
85 | Initialize a new project:
86 |
87 | ```bash
88 | # If installed globally
89 | task-master init
90 |
91 | # If installed locally
92 | npx task-master init
93 | ```
94 |
95 | This will prompt you for project details and set up a new project with the necessary files and structure.
96 |
97 | ## Common Commands
98 |
99 | After setting up Task Master, you can use these commands (either via AI prompts or CLI):
100 |
101 | ```bash
102 | # Parse a PRD and generate tasks
103 | task-master parse-prd your-prd.txt
104 |
105 | # List all tasks
106 | task-master list
107 |
108 | # Show the next task to work on
109 | task-master next
110 |
111 | # Generate task files
112 | task-master generate
113 | ```
114 |
115 | ## Setting up Cursor AI Integration
116 |
117 | Task Master is designed to work seamlessly with [Cursor AI](https://www.cursor.so/), providing a structured workflow for AI-driven development.
118 |
119 | ### Using Cursor with MCP (Recommended)
120 |
121 | If you've already set up Task Master with MCP in Cursor, the integration is automatic. You can simply use natural language to interact with Task Master:
122 |
123 | ```
124 | What tasks are available to work on next?
125 | Can you analyze the complexity of our tasks?
126 | I'd like to implement task 4. What does it involve?
127 | ```
128 |
129 | ### Manual Cursor Setup
130 |
131 | If you're not using MCP, you can still set up Cursor integration:
132 |
133 | 1. After initializing your project, open it in Cursor
134 | 2. The `.cursor/rules/dev_workflow.mdc` file is automatically loaded by Cursor, providing the AI with knowledge about the task management system
135 | 3. Place your PRD document in the `.taskmaster/docs/` directory (e.g., `.taskmaster/docs/prd.txt`)
136 | 4. Open Cursor's AI chat and switch to Agent mode
137 |
138 | ### Alternative MCP Setup in Cursor
139 |
140 | You can also set up the MCP server in Cursor settings:
141 |
142 | 1. Go to Cursor settings
143 | 2. Navigate to the MCP section
144 | 3. Click on "Add New MCP Server"
145 | 4. Configure with the following details:
146 | - Name: "Task Master"
147 | - Type: "Command"
148 | - Command: "npx -y task-master-ai"
149 | 5. Save the settings
150 |
151 | Once configured, you can interact with Task Master's task management commands directly through Cursor's interface, providing a more integrated experience.
152 |
153 | ## Initial Task Generation
154 |
155 | In Cursor's AI chat, instruct the agent to generate tasks from your PRD:
156 |
157 | ```
158 | Please use the task-master parse-prd command to generate tasks from my PRD. The PRD is located at .taskmaster/docs/prd.txt.
159 | ```
160 |
161 | The agent will execute:
162 |
163 | ```bash
164 | task-master parse-prd .taskmaster/docs/prd.txt
165 | ```
166 |
167 | This will:
168 |
169 | - Parse your PRD document
170 | - Generate a structured `tasks.json` file with tasks, dependencies, priorities, and test strategies
171 | - The agent will understand this process due to the Cursor rules
172 |
173 | ### Generate Individual Task Files
174 |
175 | Next, ask the agent to generate individual task files:
176 |
177 | ```
178 | Please generate individual task files from tasks.json
179 | ```
180 |
181 | The agent will execute:
182 |
183 | ```bash
184 | task-master generate
185 | ```
186 |
187 | This creates individual task files in the `tasks/` directory (e.g., `task_001.txt`, `task_002.txt`), making it easier to reference specific tasks.
188 |
189 | ## AI-Driven Development Workflow
190 |
191 | The Cursor agent is pre-configured (via the rules file) to follow this workflow:
192 |
193 | ### 1. Task Discovery and Selection
194 |
195 | Ask the agent to list available tasks:
196 |
197 | ```
198 | What tasks are available to work on next?
199 | ```
200 |
201 | ```
202 | Can you show me tasks 1, 3, and 5 to understand their current status?
203 | ```
204 |
205 | The agent will:
206 |
207 | - Run `task-master list` to see all tasks
208 | - Run `task-master next` to determine the next task to work on
209 | - Run `task-master show 1,3,5` to display multiple tasks with interactive options
210 | - Analyze dependencies to determine which tasks are ready to be worked on
211 | - Prioritize tasks based on priority level and ID order
212 | - Suggest the next task(s) to implement
213 |
214 | ### 2. Task Implementation
215 |
216 | When implementing a task, the agent will:
217 |
218 | - Reference the task's details section for implementation specifics
219 | - Consider dependencies on previous tasks
220 | - Follow the project's coding standards
221 | - Create appropriate tests based on the task's testStrategy
222 |
223 | You can ask:
224 |
225 | ```
226 | Let's implement task 3. What does it involve?
227 | ```
228 |
229 | ### 2.1. Viewing Multiple Tasks
230 |
231 | For efficient context gathering and batch operations:
232 |
233 | ```
234 | Show me tasks 5, 7, and 9 so I can plan my implementation approach.
235 | ```
236 |
237 | The agent will:
238 |
239 | - Run `task-master show 5,7,9` to display a compact summary table
240 | - Show task status, priority, and progress indicators
241 | - Provide an interactive action menu with batch operations
242 | - Allow you to perform group actions like marking multiple tasks as in-progress
243 |
244 | ### 3. Task Verification
245 |
246 | Before marking a task as complete, verify it according to:
247 |
248 | - The task's specified testStrategy
249 | - Any automated tests in the codebase
250 | - Manual verification if required
251 |
252 | ### 4. Task Completion
253 |
254 | When a task is completed, tell the agent:
255 |
256 | ```
257 | Task 3 is now complete. Please update its status.
258 | ```
259 |
260 | The agent will execute:
261 |
262 | ```bash
263 | task-master set-status --id=3 --status=done
264 | ```
265 |
266 | ### 5. Handling Implementation Drift
267 |
268 | If during implementation, you discover that:
269 |
270 | - The current approach differs significantly from what was planned
271 | - Future tasks need to be modified due to current implementation choices
272 | - New dependencies or requirements have emerged
273 |
274 | Tell the agent:
275 |
276 | ```
277 | We've decided to use MongoDB instead of PostgreSQL. Can you update all future tasks (from ID 4) to reflect this change?
278 | ```
279 |
280 | The agent will execute:
281 |
282 | ```bash
283 | task-master update --from=4 --prompt="Now we are using MongoDB instead of PostgreSQL."
284 |
285 | # OR, if research is needed to find best practices for MongoDB:
286 | task-master update --from=4 --prompt="Update to use MongoDB, researching best practices" --research
287 | ```
288 |
289 | This will rewrite or re-scope subsequent tasks in tasks.json while preserving completed work.
290 |
291 | ### 6. Reorganizing Tasks
292 |
293 | If you need to reorganize your task structure:
294 |
295 | ```
296 | I think subtask 5.2 would fit better as part of task 7 instead. Can you move it there?
297 | ```
298 |
299 | The agent will execute:
300 |
301 | ```bash
302 | task-master move --from=5.2 --to=7.3
303 | ```
304 |
305 | You can reorganize tasks in various ways:
306 |
307 | - Moving a standalone task to become a subtask: `--from=5 --to=7`
308 | - Moving a subtask to become a standalone task: `--from=5.2 --to=7`
309 | - Moving a subtask to a different parent: `--from=5.2 --to=7.3`
310 | - Reordering subtasks within the same parent: `--from=5.2 --to=5.4`
311 | - Moving a task to a new ID position: `--from=5 --to=25` (even if task 25 doesn't exist yet)
312 | - Moving multiple tasks at once: `--from=10,11,12 --to=16,17,18` (must have same number of IDs, Taskmaster will look through each position)
313 |
314 | When moving tasks to new IDs:
315 |
316 | - The system automatically creates placeholder tasks for non-existent destination IDs
317 | - This prevents accidental data loss during reorganization
318 | - Any tasks that depend on moved tasks will have their dependencies updated
319 | - When moving a parent task, all its subtasks are automatically moved with it and renumbered
320 |
321 | This is particularly useful as your project understanding evolves and you need to refine your task structure.
322 |
323 | ### 7. Resolving Merge Conflicts with Tasks
324 |
325 | When working with a team, you might encounter merge conflicts in your tasks.json file if multiple team members create tasks on different branches. The move command makes resolving these conflicts straightforward:
326 |
327 | ```
328 | I just merged the main branch and there's a conflict with tasks.json. My teammates created tasks 10-15 while I created tasks 10-12 on my branch. Can you help me resolve this?
329 | ```
330 |
331 | The agent will help you:
332 |
333 | 1. Keep your teammates' tasks (10-15)
334 | 2. Move your tasks to new positions to avoid conflicts:
335 |
336 | ```bash
337 | # Move your tasks to new positions (e.g., 16-18)
338 | task-master move --from=10 --to=16
339 | task-master move --from=11 --to=17
340 | task-master move --from=12 --to=18
341 | ```
342 |
343 | This approach preserves everyone's work while maintaining a clean task structure, making it much easier to handle task conflicts than trying to manually merge JSON files.
344 |
345 | ### 8. Breaking Down Complex Tasks
346 |
347 | For complex tasks that need more granularity:
348 |
349 | ```
350 | Task 5 seems complex. Can you break it down into subtasks?
351 | ```
352 |
353 | The agent will execute:
354 |
355 | ```bash
356 | task-master expand --id=5 --num=3
357 | ```
358 |
359 | You can provide additional context:
360 |
361 | ```
362 | Please break down task 5 with a focus on security considerations.
363 | ```
364 |
365 | The agent will execute:
366 |
367 | ```bash
368 | task-master expand --id=5 --prompt="Focus on security aspects"
369 | ```
370 |
371 | You can also expand all pending tasks:
372 |
373 | ```
374 | Please break down all pending tasks into subtasks.
375 | ```
376 |
377 | The agent will execute:
378 |
379 | ```bash
380 | task-master expand --all
381 | ```
382 |
383 | For research-backed subtask generation using the configured research model:
384 |
385 | ```
386 | Please break down task 5 using research-backed generation.
387 | ```
388 |
389 | The agent will execute:
390 |
391 | ```bash
392 | task-master expand --id=5 --research
393 | ```
394 |
395 | ## Example Cursor AI Interactions
396 |
397 | ### Starting a new project
398 |
399 | ```
400 | I've just initialized a new project with Claude Task Master. I have a PRD at .taskmaster/docs/prd.txt.
401 | Can you help me parse it and set up the initial tasks?
402 | ```
403 |
404 | ### Working on tasks
405 |
406 | ```
407 | What's the next task I should work on? Please consider dependencies and priorities.
408 | ```
409 |
410 | ### Implementing a specific task
411 |
412 | ```
413 | I'd like to implement task 4. Can you help me understand what needs to be done and how to approach it?
414 | ```
415 |
416 | ### Managing subtasks
417 |
418 | ```
419 | I need to regenerate the subtasks for task 3 with a different approach. Can you help me clear and regenerate them?
420 | ```
421 |
422 | ### Handling changes
423 |
424 | ```
425 | We've decided to use MongoDB instead of PostgreSQL. Can you update all future tasks to reflect this change?
426 | ```
427 |
428 | ### Completing work
429 |
430 | ```
431 | I've finished implementing the authentication system described in task 2. All tests are passing.
432 | Please mark it as complete and tell me what I should work on next.
433 | ```
434 |
435 | ### Analyzing complexity
436 |
437 | ```
438 | Can you analyze the complexity of our tasks to help me understand which ones need to be broken down further?
439 | ```
440 |
441 | ### Viewing complexity report
442 |
443 | ```
444 | Can you show me the complexity report in a more readable format?
445 | ```
446 |
447 | ### Research-Driven Development
448 |
449 | Task Master includes a powerful research tool that provides fresh, up-to-date information beyond the AI's knowledge cutoff. This is particularly valuable for:
450 |
451 | #### Getting Current Best Practices
452 |
453 | ```
454 | Before implementing task 5 (authentication), research the latest JWT security recommendations.
455 | ```
456 |
457 | The agent will execute:
458 |
459 | ```bash
460 | task-master research "Latest JWT security recommendations 2024" --id=5
461 | ```
462 |
463 | #### Research with Project Context
464 |
465 | ```
466 | Research React Query v5 migration strategies for our current API implementation.
467 | ```
468 |
469 | The agent will execute:
470 |
471 | ```bash
472 | task-master research "React Query v5 migration strategies" --files=src/api.js,src/hooks.js
473 | ```
474 |
475 | #### Research and Update Pattern
476 |
477 | A powerful workflow is to research first, then update tasks with findings:
478 |
479 | ```
480 | Research the latest Node.js performance optimization techniques and update task 12 with the findings.
481 | ```
482 |
483 | The agent will:
484 |
485 | 1. Run research: `task-master research "Node.js performance optimization 2024" --id=12`
486 | 2. Update the task: `task-master update-subtask --id=12.2 --prompt="Updated with latest performance findings: [research results]"`
487 |
488 | #### When to Use Research
489 |
490 | - **Before implementing any new technology**
491 | - **When encountering security-related tasks**
492 | - **For performance optimization tasks**
493 | - **When debugging complex issues**
494 | - **Before making architectural decisions**
495 | - **When updating dependencies**
496 |
497 | The research tool automatically includes relevant project context and provides fresh information that can significantly improve implementation quality.
498 |
499 | ## Git Integration and Tag Management
500 |
501 | Task Master supports tagged task lists for multi-context development, which is particularly useful when working with git branches or different project phases.
502 |
503 | ### Working with Tags
504 |
505 | Tags provide isolated task contexts, allowing you to maintain separate task lists for different features, branches, or experiments:
506 |
507 | ```
508 | I'm starting work on a new feature branch. Can you create a new tag for this work?
509 | ```
510 |
511 | The agent will execute:
512 |
513 | ```bash
514 | # Create a tag based on your current git branch
515 | task-master add-tag --from-branch
516 | ```
517 |
518 | Or you can create a tag with a specific name:
519 |
520 | ```
521 | Create a new tag called 'user-auth' for authentication-related tasks.
522 | ```
523 |
524 | The agent will execute:
525 |
526 | ```bash
527 | task-master add-tag user-auth --description="User authentication feature tasks"
528 | ```
529 |
530 | ### Switching Between Contexts
531 |
532 | When working on different features or branches:
533 |
534 | ```
535 | Switch to the 'user-auth' tag context so I can work on authentication tasks.
536 | ```
537 |
538 | The agent will execute:
539 |
540 | ```bash
541 | task-master use-tag user-auth
542 | ```
543 |
544 | ### Copying Tasks Between Tags
545 |
546 | When you need to duplicate work across contexts:
547 |
548 | ```
549 | Copy all tasks from the current tag to a new 'testing' tag for QA work.
550 | ```
551 |
552 | The agent will execute:
553 |
554 | ```bash
555 | task-master add-tag testing --copy-from-current --description="QA and testing tasks"
556 | ```
557 |
558 | ### Tag Management
559 |
560 | View and manage your tag contexts:
561 |
562 | ```
563 | Show me all available tags and their current status.
564 | ```
565 |
566 | The agent will execute:
567 |
568 | ```bash
569 | task-master tags --show-metadata
570 | ```
571 |
572 | ### Benefits of Tagged Task Lists
573 |
574 | - **Branch Isolation**: Each git branch can have its own task context
575 | - **Merge Conflict Prevention**: Tasks in different tags don't interfere with each other
576 | - **Parallel Development**: Multiple team members can work on separate contexts
577 | - **Context Switching**: Easily switch between different project phases or features
578 | - **Experimentation**: Create experimental task lists without affecting main work
579 |
580 | ### Git Workflow Integration
581 |
582 | A typical git workflow with Task Master tags:
583 |
584 | 1. **Create feature branch**: `git checkout -b feature/user-auth`
585 | 2. **Create matching tag**: Ask agent to run `task-master add-tag --from-branch`
586 | 3. **Work in isolated context**: All task operations work within the new tag
587 | 4. **Switch contexts as needed**: Use `task-master use-tag <name>` to switch between different work streams
588 | 5. **Merge and cleanup**: After merging the branch, optionally delete the tag with `task-master delete-tag <name>`
589 |
590 | This workflow ensures your task management stays organized and conflicts are minimized when working with teams or multiple features simultaneously.
591 |
```
--------------------------------------------------------------------------------
/apps/cli/src/commands/context.command.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * @fileoverview Context command for managing org/brief selection
3 | * Provides a clean interface for workspace context management
4 | */
5 |
6 | import { Command } from 'commander';
7 | import chalk from 'chalk';
8 | import inquirer from 'inquirer';
9 | import ora from 'ora';
10 | import {
11 | AuthManager,
12 | createTmCore,
13 | type UserContext,
14 | type TmCore
15 | } from '@tm/core';
16 | import * as ui from '../utils/ui.js';
17 | import { checkAuthentication } from '../utils/auth-helpers.js';
18 | import { getBriefStatusWithColor } from '../ui/formatters/status-formatters.js';
19 | import {
20 | selectBriefInteractive,
21 | selectBriefFromInput
22 | } from '../utils/brief-selection.js';
23 |
24 | /**
25 | * Result type from context command
26 | */
27 | export interface ContextResult {
28 | success: boolean;
29 | action: 'show' | 'select-org' | 'select-brief' | 'clear' | 'set';
30 | context?: UserContext;
31 | message?: string;
32 | }
33 |
34 | /**
35 | * ContextCommand extending Commander's Command class
36 | * Manages user's workspace context (org/brief selection)
37 | */
38 | export class ContextCommand extends Command {
39 | private authManager: AuthManager;
40 | private tmCore?: TmCore;
41 | private lastResult?: ContextResult;
42 |
43 | constructor(name?: string) {
44 | super(name || 'context');
45 |
46 | // Initialize auth manager
47 | this.authManager = AuthManager.getInstance();
48 |
49 | // Configure the command
50 | this.description(
51 | 'Manage workspace context (organization and brief selection)'
52 | );
53 |
54 | // Add subcommands
55 | this.addOrgCommand();
56 | this.addBriefCommand();
57 | this.addClearCommand();
58 | this.addSetCommand();
59 |
60 | // Accept optional positional argument for brief ID or Hamster URL
61 | this.argument('[briefOrUrl]', 'Brief ID or Hamster brief URL');
62 |
63 | // Default action: if an argument is provided, resolve and set context; else show
64 | this.action(async (briefOrUrl?: string) => {
65 | if (briefOrUrl && briefOrUrl.trim().length > 0) {
66 | await this.executeSetFromBriefInput(briefOrUrl.trim());
67 | return;
68 | }
69 | await this.executeShow();
70 | });
71 | }
72 |
73 | /**
74 | * Add org selection subcommand
75 | */
76 | private addOrgCommand(): void {
77 | this.command('org')
78 | .description('Select an organization')
79 | .argument('[orgId]', 'Organization ID or slug to select directly')
80 | .action(async (orgId?: string) => {
81 | await this.executeSelectOrg(orgId);
82 | });
83 | }
84 |
85 | /**
86 | * Add brief selection subcommand
87 | */
88 | private addBriefCommand(): void {
89 | this.command('brief')
90 | .description('Select a brief within the current organization')
91 | .argument('[briefIdOrUrl]', 'Brief ID or Hamster URL to select directly')
92 | .action(async (briefIdOrUrl?: string) => {
93 | await this.executeSelectBrief(briefIdOrUrl);
94 | });
95 | }
96 |
97 | /**
98 | * Add clear subcommand
99 | */
100 | private addClearCommand(): void {
101 | this.command('clear')
102 | .description('Clear all context selections')
103 | .action(async () => {
104 | await this.executeClear();
105 | });
106 | }
107 |
108 | /**
109 | * Add set subcommand for direct context setting
110 | */
111 | private addSetCommand(): void {
112 | this.command('set')
113 | .description('Set context directly')
114 | .option('--org <id>', 'Organization ID')
115 | .option('--org-name <name>', 'Organization name')
116 | .option('--brief <id>', 'Brief ID')
117 | .option('--brief-name <name>', 'Brief name')
118 | .action(async (options) => {
119 | await this.executeSet(options);
120 | });
121 | }
122 |
123 | /**
124 | * Execute show current context
125 | */
126 | private async executeShow(): Promise<void> {
127 | try {
128 | const result = await this.displayContext();
129 | this.setLastResult(result);
130 | } catch (error: any) {
131 | ui.displayError(`Failed to show context: ${(error as Error).message}`);
132 | process.exit(1);
133 | }
134 | }
135 |
136 | /**
137 | * Display current context
138 | */
139 | private async displayContext(): Promise<ContextResult> {
140 | // Check authentication first
141 | const isAuthenticated = await checkAuthentication(this.authManager, {
142 | message:
143 | 'The "context" command requires you to be logged in to your Hamster account.'
144 | });
145 |
146 | if (!isAuthenticated) {
147 | return {
148 | success: false,
149 | action: 'show',
150 | message: 'Not authenticated'
151 | };
152 | }
153 |
154 | const context = this.authManager.getContext();
155 |
156 | console.log(chalk.cyan('\n🌍 Workspace Context\n'));
157 |
158 | if (context && (context.orgId || context.briefId)) {
159 | if (context.orgName || context.orgId) {
160 | console.log(chalk.green('✓ Organization'));
161 | if (context.orgName) {
162 | console.log(chalk.white(` ${context.orgName}`));
163 | }
164 | if (context.orgId) {
165 | console.log(chalk.gray(` ID: ${context.orgId}`));
166 | }
167 | }
168 |
169 | if (context.briefName || context.briefId) {
170 | console.log(chalk.green('\n✓ Brief'));
171 | if (context.briefName && context.briefId) {
172 | const shortId = context.briefId.slice(-8);
173 | console.log(
174 | chalk.white(` ${context.briefName} `) + chalk.gray(`(${shortId})`)
175 | );
176 | } else if (context.briefName) {
177 | console.log(chalk.white(` ${context.briefName}`));
178 | } else if (context.briefId) {
179 | console.log(chalk.gray(` ID: ${context.briefId}`));
180 | }
181 |
182 | // Show brief status if available
183 | if (context.briefStatus) {
184 | const statusDisplay = getBriefStatusWithColor(context.briefStatus);
185 | console.log(chalk.gray(` Status: `) + statusDisplay);
186 | }
187 |
188 | // Show brief updated date if available
189 | if (context.briefUpdatedAt) {
190 | const updatedDate = new Date(
191 | context.briefUpdatedAt
192 | ).toLocaleDateString('en-US', {
193 | month: 'short',
194 | day: 'numeric',
195 | year: 'numeric',
196 | hour: '2-digit',
197 | minute: '2-digit'
198 | });
199 | console.log(chalk.gray(` Updated: ${updatedDate}`));
200 | }
201 | }
202 |
203 | if (context.updatedAt) {
204 | console.log(
205 | chalk.gray(
206 | `\n Last updated: ${new Date(context.updatedAt).toLocaleString()}`
207 | )
208 | );
209 | }
210 |
211 | return {
212 | success: true,
213 | action: 'show',
214 | context,
215 | message: 'Context loaded'
216 | };
217 | } else {
218 | console.log(chalk.yellow('✗ No context selected'));
219 | console.log(
220 | chalk.gray('\n Run "tm context org" to select an organization')
221 | );
222 | console.log(chalk.gray(' Run "tm context brief" to select a brief'));
223 |
224 | return {
225 | success: true,
226 | action: 'show',
227 | message: 'No context selected'
228 | };
229 | }
230 | }
231 |
232 | /**
233 | * Execute org selection
234 | */
235 | private async executeSelectOrg(orgId?: string): Promise<void> {
236 | try {
237 | // Check authentication
238 | if (!(await checkAuthentication(this.authManager))) {
239 | process.exit(1);
240 | }
241 |
242 | const result = await this.selectOrganization(orgId);
243 | this.setLastResult(result);
244 |
245 | if (!result.success) {
246 | process.exit(1);
247 | }
248 | } catch (error: any) {
249 | ui.displayError(
250 | `Failed to select organization: ${(error as Error).message}`
251 | );
252 | process.exit(1);
253 | }
254 | }
255 |
256 | /**
257 | * Select an organization interactively or by ID/slug/name
258 | */
259 | private async selectOrganization(orgId?: string): Promise<ContextResult> {
260 | const spinner = ora('Fetching organizations...').start();
261 |
262 | try {
263 | // Fetch organizations from API
264 | const organizations = await this.authManager.getOrganizations();
265 | spinner.stop();
266 |
267 | if (organizations.length === 0) {
268 | ui.displayWarning('No organizations available');
269 | return {
270 | success: false,
271 | action: 'select-org',
272 | message: 'No organizations available'
273 | };
274 | }
275 |
276 | let selectedOrg;
277 |
278 | // If orgId provided, find matching org by ID, slug or name
279 | const trimmedOrgId = orgId?.trim();
280 | if (trimmedOrgId) {
281 | const normalizedInput = trimmedOrgId.toLowerCase();
282 | selectedOrg = organizations.find(
283 | (org) =>
284 | org.id === trimmedOrgId ||
285 | org.slug?.toLowerCase() === normalizedInput ||
286 | org.name.toLowerCase() === normalizedInput
287 | );
288 |
289 | if (!selectedOrg) {
290 | const totalCount = organizations.length;
291 | const displayLimit = 5;
292 | const orgList = organizations
293 | .slice(0, displayLimit)
294 | .map((o) => o.name)
295 | .join(', ');
296 |
297 | let errorMessage = `Organization not found: ${trimmedOrgId}\n`;
298 | if (totalCount <= displayLimit) {
299 | errorMessage += `Available organizations: ${orgList}`;
300 | } else {
301 | errorMessage += `Available organizations (showing ${displayLimit} of ${totalCount}): ${orgList}`;
302 | errorMessage += `\nRun "tm context org" to see all organizations and select interactively`;
303 | }
304 |
305 | ui.displayError(errorMessage);
306 | return {
307 | success: false,
308 | action: 'select-org',
309 | message: `Organization not found: ${trimmedOrgId}`
310 | };
311 | }
312 | } else {
313 | // Interactive selection
314 | const response = await inquirer.prompt([
315 | {
316 | type: 'list',
317 | name: 'selectedOrg',
318 | message: 'Select an organization:',
319 | choices: organizations.map((org) => ({
320 | name: org.name,
321 | value: org
322 | }))
323 | }
324 | ]);
325 | selectedOrg = response.selectedOrg;
326 | }
327 |
328 | // Update context
329 | await this.authManager.updateContext({
330 | orgId: selectedOrg.id,
331 | orgName: selectedOrg.name,
332 | orgSlug: selectedOrg.slug,
333 | // Clear brief when changing org
334 | briefId: undefined,
335 | briefName: undefined
336 | });
337 |
338 | ui.displaySuccess(`Selected organization: ${selectedOrg.name}`);
339 |
340 | return {
341 | success: true,
342 | action: 'select-org',
343 | context: this.authManager.getContext() || undefined,
344 | message: `Selected organization: ${selectedOrg.name}`
345 | };
346 | } catch (error) {
347 | spinner.fail('Failed to fetch organizations');
348 | throw error;
349 | }
350 | }
351 |
352 | /**
353 | * Execute brief selection
354 | */
355 | private async executeSelectBrief(briefIdOrUrl?: string): Promise<void> {
356 | try {
357 | // Check authentication
358 | if (!(await checkAuthentication(this.authManager))) {
359 | process.exit(1);
360 | }
361 |
362 | // If briefIdOrUrl provided, use direct selection
363 | if (briefIdOrUrl && briefIdOrUrl.trim().length > 0) {
364 | await this.selectBriefDirectly(briefIdOrUrl.trim(), 'select-brief');
365 | return;
366 | }
367 |
368 | // Interactive selection
369 | const context = this.authManager.getContext();
370 | if (!context?.orgId) {
371 | ui.displayError(
372 | 'No organization selected. Run "tm context org" first.'
373 | );
374 | process.exit(1);
375 | }
376 |
377 | // Use shared utility for interactive selection
378 | const result = await selectBriefInteractive(
379 | this.authManager,
380 | context.orgId
381 | );
382 |
383 | this.setLastResult({
384 | success: result.success,
385 | action: 'select-brief',
386 | context: this.authManager.getContext() || undefined,
387 | message: result.message
388 | });
389 |
390 | if (!result.success) {
391 | process.exit(1);
392 | }
393 | } catch (error: any) {
394 | ui.displayError(`Failed to select brief: ${(error as Error).message}`);
395 | process.exit(1);
396 | }
397 | }
398 |
399 | /**
400 | * Execute clear context
401 | */
402 | private async executeClear(): Promise<void> {
403 | try {
404 | // Check authentication
405 | if (!(await checkAuthentication(this.authManager))) {
406 | process.exit(1);
407 | }
408 |
409 | const result = await this.clearContext();
410 | this.setLastResult(result);
411 |
412 | if (!result.success) {
413 | process.exit(1);
414 | }
415 | } catch (error: any) {
416 | ui.displayError(`Failed to clear context: ${(error as Error).message}`);
417 | process.exit(1);
418 | }
419 | }
420 |
421 | /**
422 | * Clear all context selections
423 | */
424 | private async clearContext(): Promise<ContextResult> {
425 | try {
426 | await this.authManager.clearContext();
427 | ui.displaySuccess('Context cleared');
428 |
429 | return {
430 | success: true,
431 | action: 'clear',
432 | message: 'Context cleared'
433 | };
434 | } catch (error) {
435 | ui.displayError(`Failed to clear context: ${(error as Error).message}`);
436 |
437 | return {
438 | success: false,
439 | action: 'clear',
440 | message: `Failed to clear context: ${(error as Error).message}`
441 | };
442 | }
443 | }
444 |
445 | /**
446 | * Execute set context with options
447 | */
448 | private async executeSet(options: any): Promise<void> {
449 | try {
450 | // Check authentication
451 | if (!(await checkAuthentication(this.authManager))) {
452 | process.exit(1);
453 | }
454 |
455 | const result = await this.setContext(options);
456 | this.setLastResult(result);
457 |
458 | if (!result.success) {
459 | process.exit(1);
460 | }
461 | } catch (error: any) {
462 | ui.displayError(`Failed to set context: ${(error as Error).message}`);
463 | process.exit(1);
464 | }
465 | }
466 |
467 | /**
468 | * Initialize TmCore if not already initialized
469 | */
470 | private async initTmCore(): Promise<void> {
471 | if (!this.tmCore) {
472 | this.tmCore = await createTmCore({
473 | projectPath: process.cwd()
474 | });
475 | }
476 | }
477 |
478 | /**
479 | * Helper method to select brief directly from input (URL or ID)
480 | * Used by both executeSelectBrief and executeSetFromBriefInput
481 | */
482 | private async selectBriefDirectly(
483 | input: string,
484 | action: 'select-brief' | 'set'
485 | ): Promise<void> {
486 | await this.initTmCore();
487 |
488 | const result = await selectBriefFromInput(
489 | this.authManager,
490 | input,
491 | this.tmCore
492 | );
493 |
494 | this.setLastResult({
495 | success: result.success,
496 | action,
497 | context: this.authManager.getContext() || undefined,
498 | message: result.message
499 | });
500 |
501 | if (!result.success) {
502 | process.exit(1);
503 | }
504 | }
505 |
506 | /**
507 | * Execute setting context from a brief ID or Hamster URL
508 | * All parsing logic is in tm-core
509 | */
510 | private async executeSetFromBriefInput(input: string): Promise<void> {
511 | try {
512 | // Check authentication
513 | if (!(await checkAuthentication(this.authManager))) {
514 | process.exit(1);
515 | }
516 |
517 | await this.selectBriefDirectly(input, 'set');
518 | } catch (error: any) {
519 | ui.displayError(
520 | `Failed to set context from brief: ${(error as Error).message}`
521 | );
522 | process.exit(1);
523 | }
524 | }
525 |
526 | /**
527 | * Set context directly from options
528 | */
529 | private async setContext(options: any): Promise<ContextResult> {
530 | try {
531 | const context: Partial<UserContext> = {};
532 |
533 | if (options.org) {
534 | context.orgId = options.org;
535 | }
536 | if (options.orgName) {
537 | context.orgName = options.orgName;
538 | }
539 | if (options.brief) {
540 | context.briefId = options.brief;
541 | }
542 | if (options.briefName) {
543 | context.briefName = options.briefName;
544 | }
545 |
546 | if (Object.keys(context).length === 0) {
547 | ui.displayWarning('No context options provided');
548 | return {
549 | success: false,
550 | action: 'set',
551 | message: 'No context options provided'
552 | };
553 | }
554 |
555 | await this.authManager.updateContext(context);
556 | ui.displaySuccess('Context updated');
557 |
558 | // Display what was set
559 | if (context.orgName || context.orgId) {
560 | console.log(
561 | chalk.gray(` Organization: ${context.orgName || context.orgId}`)
562 | );
563 | }
564 | if (context.briefName || context.briefId) {
565 | console.log(
566 | chalk.gray(` Brief: ${context.briefName || context.briefId}`)
567 | );
568 | }
569 |
570 | return {
571 | success: true,
572 | action: 'set',
573 | context: this.authManager.getContext() || undefined,
574 | message: 'Context updated'
575 | };
576 | } catch (error) {
577 | ui.displayError(`Failed to set context: ${(error as Error).message}`);
578 |
579 | return {
580 | success: false,
581 | action: 'set',
582 | message: `Failed to set context: ${(error as Error).message}`
583 | };
584 | }
585 | }
586 |
587 | /**
588 | * Set the last result for programmatic access
589 | */
590 | private setLastResult(result: ContextResult): void {
591 | this.lastResult = result;
592 | }
593 |
594 | /**
595 | * Get the last result (for programmatic usage)
596 | */
597 | getLastResult(): ContextResult | undefined {
598 | return this.lastResult;
599 | }
600 |
601 | /**
602 | * Get current context (for programmatic usage)
603 | */
604 | getContext(): UserContext | null {
605 | return this.authManager.getContext();
606 | }
607 |
608 | /**
609 | * Interactive context setup (for post-auth flow)
610 | * Prompts user to select org and brief
611 | */
612 | async setupContextInteractive(): Promise<{
613 | success: boolean;
614 | orgSelected: boolean;
615 | briefSelected: boolean;
616 | }> {
617 | try {
618 | // Ask if user wants to set up workspace context
619 | const { setupContext } = await inquirer.prompt([
620 | {
621 | type: 'confirm',
622 | name: 'setupContext',
623 | message: 'Would you like to set up your workspace context now?',
624 | default: true
625 | }
626 | ]);
627 |
628 | if (!setupContext) {
629 | return { success: true, orgSelected: false, briefSelected: false };
630 | }
631 |
632 | // Select organization
633 | const orgResult = await this.selectOrganization();
634 | if (!orgResult.success || !orgResult.context?.orgId) {
635 | return { success: false, orgSelected: false, briefSelected: false };
636 | }
637 |
638 | // Select brief using shared utility
639 | const briefResult = await selectBriefInteractive(
640 | this.authManager,
641 | orgResult.context.orgId
642 | );
643 | return {
644 | success: true,
645 | orgSelected: true,
646 | briefSelected: briefResult.success
647 | };
648 | } catch (error) {
649 | console.error(
650 | chalk.yellow(
651 | '\nContext setup skipped due to error. You can set it up later with "tm context"'
652 | )
653 | );
654 | return { success: false, orgSelected: false, briefSelected: false };
655 | }
656 | }
657 |
658 | /**
659 | * Clean up resources
660 | */
661 | async cleanup(): Promise<void> {
662 | // No resources to clean up for context command
663 | }
664 |
665 | /**
666 | * Register this command on an existing program
667 | */
668 | static register(program: Command, name?: string): ContextCommand {
669 | const contextCommand = new ContextCommand(name);
670 | program.addCommand(contextCommand);
671 | return contextCommand;
672 | }
673 | }
674 |
```
--------------------------------------------------------------------------------
/.taskmaster/docs/prd.txt:
--------------------------------------------------------------------------------
```
1 |
2 | # Claude Task Master - Product Requirements Document
3 |
4 | <PRD>
5 | # Technical Architecture
6 |
7 | ## System Components
8 | 1. **Task Management Core**
9 | - Tasks.json file structure (single source of truth)
10 | - Task model with dependencies, priorities, and metadata
11 | - Task state management system
12 | - Task file generation subsystem
13 |
14 | 2. **AI Integration Layer**
15 | - Anthropic Claude API integration
16 | - Perplexity API integration (optional)
17 | - Prompt engineering components
18 | - Response parsing and processing
19 |
20 | 3. **Command Line Interface**
21 | - Command parsing and execution
22 | - Interactive user input handling
23 | - Display and formatting utilities
24 | - Status reporting and feedback system
25 |
26 | 4. **Cursor AI Integration**
27 | - Cursor rules documentation
28 | - Agent interaction patterns
29 | - Workflow guideline specifications
30 |
31 | ## Data Models
32 |
33 | ### Task Model
34 | ```json
35 | {
36 | "id": 1,
37 | "title": "Task Title",
38 | "description": "Brief task description",
39 | "status": "pending|done|deferred",
40 | "dependencies": [0],
41 | "priority": "high|medium|low",
42 | "details": "Detailed implementation instructions",
43 | "testStrategy": "Verification approach details",
44 | "subtasks": [
45 | {
46 | "id": 1,
47 | "title": "Subtask Title",
48 | "description": "Subtask description",
49 | "status": "pending|done|deferred",
50 | "dependencies": [],
51 | "acceptanceCriteria": "Verification criteria"
52 | }
53 | ]
54 | }
55 | ```
56 |
57 | ### Tasks Collection Model
58 | ```json
59 | {
60 | "meta": {
61 | "projectName": "Project Name",
62 | "version": "1.0.0",
63 | "prdSource": "path/to/prd.txt",
64 | "createdAt": "ISO-8601 timestamp",
65 | "updatedAt": "ISO-8601 timestamp"
66 | },
67 | "tasks": [
68 | // Array of Task objects
69 | ]
70 | }
71 | ```
72 |
73 | ### Task File Format
74 | ```
75 | # Task ID: <id>
76 | # Title: <title>
77 | # Status: <status>
78 | # Dependencies: <comma-separated list of dependency IDs>
79 | # Priority: <priority>
80 | # Description: <brief description>
81 | # Details:
82 | <detailed implementation notes>
83 |
84 | # Test Strategy:
85 | <verification approach>
86 |
87 | # Subtasks:
88 | 1. <subtask title> - <subtask description>
89 | ```
90 |
91 | ## APIs and Integrations
92 | 1. **Anthropic Claude API**
93 | - Authentication via API key
94 | - Prompt construction and streaming
95 | - Response parsing and extraction
96 | - Error handling and retries
97 |
98 | 2. **Perplexity API (via OpenAI client)**
99 | - Authentication via API key
100 | - Research-oriented prompt construction
101 | - Enhanced contextual response handling
102 | - Fallback mechanisms to Claude
103 |
104 | 3. **File System API**
105 | - Reading/writing tasks.json
106 | - Managing individual task files
107 | - Command execution logging
108 | - Debug logging system
109 |
110 | ## Infrastructure Requirements
111 | 1. **Node.js Runtime**
112 | - Version 14.0.0 or higher
113 | - ES Module support
114 | - File system access rights
115 | - Command execution capabilities
116 |
117 | 2. **Configuration Management**
118 | - Environment variable handling
119 | - .env file support
120 | - Configuration validation
121 | - Sensible defaults with overrides
122 |
123 | 3. **Development Environment**
124 | - Git repository
125 | - NPM package management
126 | - Cursor editor integration
127 | - Command-line terminal access
128 |
129 | # Development Roadmap
130 |
131 | ## Phase 1: Core Task Management System
132 | 1. **Task Data Structure**
133 | - Design and implement the tasks.json structure
134 | - Create task model validation
135 | - Implement basic task operations (create, read, update)
136 | - Develop file system interactions
137 |
138 | 2. **Command Line Interface Foundation**
139 | - Implement command parsing with Commander.js
140 | - Create help documentation
141 | - Implement colorized console output
142 | - Add logging system with configurable levels
143 |
144 | 3. **Basic Task Operations**
145 | - Implement task listing functionality
146 | - Create task status update capability
147 | - Add dependency tracking
148 | - Implement priority management
149 |
150 | 4. **Task File Generation**
151 | - Create task file templates
152 | - Implement generation from tasks.json
153 | - Add bi-directional synchronization
154 | - Implement proper file naming and organization
155 |
156 | ## Phase 2: AI Integration
157 | 1. **Claude API Integration**
158 | - Implement API authentication
159 | - Create prompt templates for PRD parsing
160 | - Design response handlers
161 | - Add error management and retries
162 |
163 | 2. **PRD Parsing System**
164 | - Implement PRD file reading
165 | - Create PRD to task conversion logic
166 | - Add intelligent dependency inference
167 | - Implement priority assignment logic
168 |
169 | 3. **Task Expansion With Claude**
170 | - Create subtask generation prompts
171 | - Implement subtask creation workflow
172 | - Add context-aware expansion capabilities
173 | - Implement parent-child relationship management
174 |
175 | 4. **Implementation Drift Handling**
176 | - Add capability to update future tasks
177 | - Implement task rewriting based on new context
178 | - Create dependency chain updates
179 | - Preserve completed work while updating future tasks
180 |
181 | ## Phase 3: Advanced Features
182 | 1. **Perplexity Integration**
183 | - Implement Perplexity API authentication
184 | - Create research-oriented prompts
185 | - Add fallback to Claude when unavailable
186 | - Implement response quality comparison logic
187 |
188 | 2. **Research-Backed Subtask Generation**
189 | - Create specialized research prompts
190 | - Implement context enrichment
191 | - Add domain-specific knowledge incorporation
192 | - Create more detailed subtask generation
193 |
194 | 3. **Batch Operations**
195 | - Implement multi-task status updates
196 | - Add bulk subtask generation
197 | - Create task filtering and querying
198 | - Implement advanced dependency management
199 |
200 | 4. **Project Initialization**
201 | - Create project templating system
202 | - Implement interactive setup
203 | - Add environment configuration
204 | - Create documentation generation
205 |
206 | ## Phase 4: Cursor AI Integration
207 | 1. **Cursor Rules Implementation**
208 | - Create dev_workflow.mdc documentation
209 | - Implement cursor_rules.mdc
210 | - Add self_improve.mdc
211 | - Design rule integration documentation
212 |
213 | 2. **Agent Workflow Guidelines**
214 | - Document task discovery workflow
215 | - Create task selection guidelines
216 | - Implement implementation guidance
217 | - Add verification procedures
218 |
219 | 3. **Agent Command Integration**
220 | - Document command syntax for agents
221 | - Create example interactions
222 | - Implement agent response patterns
223 | - Add context management for agents
224 |
225 | 4. **User Documentation**
226 | - Create detailed README
227 | - Add scripts documentation
228 | - Implement example workflows
229 | - Create troubleshooting guides
230 |
231 | # Logical Dependency Chain
232 |
233 | ## Foundation Layer
234 | 1. **Task Data Structure**
235 | - Must be implemented first as all other functionality depends on this
236 | - Defines the core data model for the entire system
237 | - Establishes the single source of truth concept
238 |
239 | 2. **Command Line Interface**
240 | - Built on top of the task data structure
241 | - Provides the primary user interaction mechanism
242 | - Required for all subsequent operations to be accessible
243 |
244 | 3. **Basic Task Operations**
245 | - Depends on both task data structure and CLI
246 | - Provides the fundamental operations for task management
247 | - Enables the minimal viable workflow
248 |
249 | ## Functional Layer
250 | 4. **Task File Generation**
251 | - Depends on task data structure and basic operations
252 | - Creates the individual task files for reference
253 | - Enables the file-based workflow complementing tasks.json
254 |
255 | 5. **Claude API Integration**
256 | - Independent of most previous components but needs the task data structure
257 | - Provides the AI capabilities that enhance the system
258 | - Gateway to advanced task generation features
259 |
260 | 6. **PRD Parsing System**
261 | - Depends on Claude API integration and task data structure
262 | - Enables the initial task generation workflow
263 | - Creates the starting point for new projects
264 |
265 | ## Enhancement Layer
266 | 7. **Task Expansion With Claude**
267 | - Depends on Claude API integration and basic task operations
268 | - Enhances existing tasks with more detailed subtasks
269 | - Improves the implementation guidance
270 |
271 | 8. **Implementation Drift Handling**
272 | - Depends on Claude API integration and task operations
273 | - Addresses a key challenge in AI-driven development
274 | - Maintains the relevance of task planning as implementation evolves
275 |
276 | 9. **Perplexity Integration**
277 | - Can be developed in parallel with other features after Claude integration
278 | - Enhances the quality of generated content
279 | - Provides research-backed improvements
280 |
281 | ## Advanced Layer
282 | 10. **Research-Backed Subtask Generation**
283 | - Depends on Perplexity integration and task expansion
284 | - Provides higher quality, more contextual subtasks
285 | - Enhances the value of the task breakdown
286 |
287 | 11. **Batch Operations**
288 | - Depends on basic task operations
289 | - Improves efficiency for managing multiple tasks
290 | - Quality-of-life enhancement for larger projects
291 |
292 | 12. **Project Initialization**
293 | - Depends on most previous components being stable
294 | - Provides a smooth onboarding experience
295 | - Creates a complete project setup in one step
296 |
297 | ## Integration Layer
298 | 13. **Cursor Rules Implementation**
299 | - Can be developed in parallel after basic functionality
300 | - Provides the guidance for Cursor AI agent
301 | - Enhances the AI-driven workflow
302 |
303 | 14. **Agent Workflow Guidelines**
304 | - Depends on Cursor rules implementation
305 | - Structures how the agent interacts with the system
306 | - Ensures consistent agent behavior
307 |
308 | 15. **Agent Command Integration**
309 | - Depends on agent workflow guidelines
310 | - Provides specific command patterns for the agent
311 | - Optimizes the agent-user interaction
312 |
313 | 16. **User Documentation**
314 | - Should be developed alongside all features
315 | - Must be completed before release
316 | - Ensures users can effectively use the system
317 |
318 | # Risks and Mitigations
319 |
320 | ## Technical Challenges
321 |
322 | ### API Reliability
323 | **Risk**: Anthropic or Perplexity API could have downtime, rate limiting, or breaking changes.
324 | **Mitigation**:
325 | - Implement robust error handling with exponential backoff
326 | - Add fallback mechanisms (Claude fallback for Perplexity)
327 | - Cache important responses to reduce API dependency
328 | - Support offline mode for critical functions
329 |
330 | ### Model Output Variability
331 | **Risk**: AI models may produce inconsistent or unexpected outputs.
332 | **Mitigation**:
333 | - Design robust prompt templates with strict output formatting requirements
334 | - Implement response validation and error detection
335 | - Add self-correction mechanisms and retries with improved prompts
336 | - Allow manual editing of generated content
337 |
338 | ### Node.js Version Compatibility
339 | **Risk**: Differences in Node.js versions could cause unexpected behavior.
340 | **Mitigation**:
341 | - Clearly document minimum Node.js version requirements
342 | - Use transpilers if needed for compatibility
343 | - Test across multiple Node.js versions
344 | - Handle version-specific features gracefully
345 |
346 | ## MVP Definition
347 |
348 | ### Feature Prioritization
349 | **Risk**: Including too many features in the MVP could delay release and adoption.
350 | **Mitigation**:
351 | - Define MVP as core task management + basic Claude integration
352 | - Ensure each phase delivers a complete, usable product
353 | - Implement feature flags for easy enabling/disabling of features
354 | - Get early user feedback to validate feature importance
355 |
356 | ### Scope Creep
357 | **Risk**: The project could expand beyond its original intent, becoming too complex.
358 | **Mitigation**:
359 | - Maintain a strict definition of what the tool is and isn't
360 | - Focus on task management for AI-driven development
361 | - Evaluate new features against core value proposition
362 | - Implement extensibility rather than building every feature
363 |
364 | ### User Expectations
365 | **Risk**: Users might expect a full project management solution rather than a task tracking system.
366 | **Mitigation**:
367 | - Clearly communicate the tool's purpose and limitations
368 | - Provide integration points with existing project management tools
369 | - Focus on the unique value of AI-driven development
370 | - Document specific use cases and example workflows
371 |
372 | ## Resource Constraints
373 |
374 | ### Development Capacity
375 | **Risk**: Limited development resources could delay implementation.
376 | **Mitigation**:
377 | - Phase implementation to deliver value incrementally
378 | - Focus on core functionality first
379 | - Leverage open source libraries where possible
380 | - Design for extensibility to allow community contributions
381 |
382 | ### AI Cost Management
383 | **Risk**: Excessive API usage could lead to high costs.
384 | **Mitigation**:
385 | - Implement token usage tracking and reporting
386 | - Add configurable limits to prevent unexpected costs
387 | - Cache responses where appropriate
388 | - Optimize prompts for token efficiency
389 | - Support local LLM options in the future
390 |
391 | ### Documentation Overhead
392 | **Risk**: Complexity of the system requires extensive documentation that is time-consuming to maintain.
393 | **Mitigation**:
394 | - Use AI to help generate and maintain documentation
395 | - Create self-documenting commands and features
396 | - Implement progressive documentation (basic to advanced)
397 | - Build help directly into the CLI
398 |
399 | # Appendix
400 |
401 | ## AI Prompt Engineering Specifications
402 |
403 | ### PRD Parsing Prompt Structure
404 | ```
405 | You are assisting with transforming a Product Requirements Document (PRD) into a structured set of development tasks.
406 |
407 | Given the following PRD, create a comprehensive list of development tasks that would be needed to implement the described product.
408 |
409 | For each task:
410 | 1. Assign a short, descriptive title
411 | 2. Write a concise description
412 | 3. Identify dependencies (which tasks must be completed before this one)
413 | 4. Assign a priority (high, medium, low)
414 | 5. Include detailed implementation notes
415 | 6. Describe a test strategy to verify completion
416 |
417 | Structure the tasks in a logical order of implementation.
418 |
419 | PRD:
420 | {prd_content}
421 | ```
422 |
423 | ### Task Expansion Prompt Structure
424 | ```
425 | You are helping to break down a development task into more manageable subtasks.
426 |
427 | Main task:
428 | Title: {task_title}
429 | Description: {task_description}
430 | Details: {task_details}
431 |
432 | Please create {num_subtasks} specific subtasks that together would accomplish this main task.
433 |
434 | For each subtask, provide:
435 | 1. A clear, actionable title
436 | 2. A concise description
437 | 3. Any dependencies on other subtasks
438 | 4. Specific acceptance criteria to verify completion
439 |
440 | Additional context:
441 | {additional_context}
442 | ```
443 |
444 | ### Research-Backed Expansion Prompt Structure
445 | ```
446 | You are a technical researcher and developer helping to break down a software development task into detailed, well-researched subtasks.
447 |
448 | Main task:
449 | Title: {task_title}
450 | Description: {task_description}
451 | Details: {task_details}
452 |
453 | Research the latest best practices, technologies, and implementation patterns for this type of task. Then create {num_subtasks} specific, actionable subtasks that together would accomplish the main task.
454 |
455 | For each subtask:
456 | 1. Provide a clear, specific title
457 | 2. Write a detailed description including technical approach
458 | 3. Identify dependencies on other subtasks
459 | 4. Include specific acceptance criteria
460 | 5. Reference any relevant libraries, tools, or resources that should be used
461 |
462 | Consider security, performance, maintainability, and user experience in your recommendations.
463 | ```
464 |
465 | ## Task File System Specification
466 |
467 | ### Directory Structure
468 | ```
469 | /
470 | ├── .cursor/
471 | │ └── rules/
472 | │ ├── dev_workflow.mdc
473 | │ ├── cursor_rules.mdc
474 | │ └── self_improve.mdc
475 | ├── scripts/
476 | │ ├── dev.js
477 | │ └── README.md
478 | ├── tasks/
479 | │ ├── task_001.txt
480 | │ ├── task_002.txt
481 | │ └── ...
482 | ├── .env
483 | ├── .env.example
484 | ├── .gitignore
485 | ├── package.json
486 | ├── README.md
487 | └── tasks.json
488 | ```
489 |
490 | ### Task ID Specification
491 | - Main tasks: Sequential integers (1, 2, 3, ...)
492 | - Subtasks: Parent ID + dot + sequential integer (1.1, 1.2, 2.1, ...)
493 | - ID references: Used in dependencies, command parameters
494 | - ID ordering: Implies suggested implementation order
495 |
496 | ## Command-Line Interface Specification
497 |
498 | ### Global Options
499 | - `--help`: Display help information
500 | - `--version`: Display version information
501 | - `--file=<file>`: Specify an alternative tasks.json file
502 | - `--quiet`: Reduce output verbosity
503 | - `--debug`: Increase output verbosity
504 | - `--json`: Output in JSON format (for programmatic use)
505 |
506 | ### Command Structure
507 | - `node scripts/dev.js <command> [options]`
508 | - All commands operate on tasks.json by default
509 | - Commands follow consistent parameter naming
510 | - Common parameter styles: `--id=<id>`, `--status=<status>`, `--prompt="<text>"`
511 | - Boolean flags: `--all`, `--force`, `--with-subtasks`
512 |
513 | ## API Integration Specifications
514 |
515 | ### Anthropic API Configuration
516 | - Authentication: ANTHROPIC_API_KEY environment variable
517 | - Model selection: MODEL environment variable
518 | - Default model: claude-3-7-sonnet-20250219
519 | - Maximum tokens: MAX_TOKENS environment variable (default: 4000)
520 | - Temperature: TEMPERATURE environment variable (default: 0.7)
521 |
522 | ### Perplexity API Configuration
523 | - Authentication: PERPLEXITY_API_KEY environment variable
524 | - Model selection: PERPLEXITY_MODEL environment variable
525 | - Default model: sonar-medium-online
526 | - Connection: Via OpenAI client
527 | - Fallback: Use Claude if Perplexity unavailable
528 | </PRD>
529 |
```
--------------------------------------------------------------------------------
/assets/scripts_README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Meta-Development Script
2 |
3 | This folder contains a **meta-development script** (`dev.js`) and related utilities that manage tasks for an AI-driven or traditional software development workflow. The script revolves around a `tasks.json` file, which holds an up-to-date list of development tasks.
4 |
5 | ## Overview
6 |
7 | In an AI-driven development process—particularly with tools like [Cursor](https://www.cursor.so/)—it's beneficial to have a **single source of truth** for tasks. This script allows you to:
8 |
9 | 1. **Parse** a PRD or requirements document (`.txt`) to initialize a set of tasks (`tasks.json`).
10 | 2. **List** all existing tasks (IDs, statuses, titles).
11 | 3. **Update** tasks to accommodate new prompts or architecture changes (useful if you discover "implementation drift").
12 | 4. **Generate** individual task files (e.g., `task_001.txt`) for easy reference or to feed into an AI coding workflow.
13 | 5. **Set task status**—mark tasks as `done`, `pending`, or `deferred` based on progress.
14 | 6. **Expand** tasks with subtasks—break down complex tasks into smaller, more manageable subtasks.
15 | 7. **Research-backed subtask generation**—use Perplexity AI to generate more informed and contextually relevant subtasks.
16 | 8. **Clear subtasks**—remove subtasks from specified tasks to allow regeneration or restructuring.
17 | 9. **Show task details**—display detailed information about a specific task and its subtasks.
18 |
19 | ## Configuration (Updated)
20 |
21 | Task Master configuration is now managed through two primary methods:
22 |
23 | 1. **`.taskmaster/config.json` File (Project Root - Primary)**
24 |
25 | - Stores AI model selections (`main`, `research`, `fallback`), model parameters (`maxTokens`, `temperature`), `logLevel`, `defaultSubtasks`, `defaultPriority`, `projectName`, etc.
26 | - Managed using the `task-master models --setup` command or the `models` MCP tool.
27 | - This is the main configuration file for most settings.
28 |
29 | 2. **Environment Variables (`.env` File - API Keys Only)**
30 | - Used **only** for sensitive **API Keys** (e.g., `ANTHROPIC_API_KEY`, `PERPLEXITY_API_KEY`).
31 | - Create a `.env` file in your project root for CLI usage.
32 | - See `assets/env.example` for required key names.
33 |
34 | **Important:** Settings like `MODEL`, `MAX_TOKENS`, `TEMPERATURE`, `TASKMASTER_LOG_LEVEL`, etc., are **no longer set via `.env`**. Use `task-master models --setup` instead.
35 |
36 | ## How It Works
37 |
38 | 1. **`tasks.json`**:
39 |
40 | - A JSON file at the project root containing an array of tasks (each with `id`, `title`, `description`, `status`, etc.).
41 | - The `meta` field can store additional info like the project's name, version, or reference to the PRD.
42 | - Tasks can have `subtasks` for more detailed implementation steps.
43 | - Dependencies are displayed with status indicators (✅ for completed, ⏱️ for pending) to easily track progress.
44 |
45 | 2. **CLI Commands**
46 | You can run the commands via:
47 |
48 | ```bash
49 | # If installed globally
50 | task-master [command] [options]
51 |
52 | # If using locally within the project
53 | node scripts/dev.js [command] [options]
54 | ```
55 |
56 | Available commands:
57 |
58 | - `init`: Initialize a new project
59 | - `parse-prd`: Generate tasks from a PRD document
60 | - `list`: Display all tasks with their status
61 | - `update`: Update tasks based on new information
62 | - `generate`: Create individual task files
63 | - `set-status`: Change a task's status
64 | - `expand`: Add subtasks to a task or all tasks
65 | - `clear-subtasks`: Remove subtasks from specified tasks
66 | - `next`: Determine the next task to work on based on dependencies
67 | - `show`: Display detailed information about a specific task
68 | - `analyze-complexity`: Analyze task complexity and generate recommendations
69 | - `complexity-report`: Display the complexity analysis in a readable format
70 | - `add-dependency`: Add a dependency between tasks
71 | - `remove-dependency`: Remove a dependency from a task
72 | - `validate-dependencies`: Check for invalid dependencies
73 | - `fix-dependencies`: Fix invalid dependencies automatically
74 | - `add-task`: Add a new task using AI
75 |
76 | Run `task-master --help` or `node scripts/dev.js --help` to see detailed usage information.
77 |
78 | ## Listing Tasks
79 |
80 | The `list` command allows you to view all tasks and their status:
81 |
82 | ```bash
83 | # List all tasks
84 | task-master list
85 |
86 | # List tasks with a specific status
87 | task-master list --status=pending
88 |
89 | # List tasks and include their subtasks
90 | task-master list --with-subtasks
91 |
92 | # List tasks with a specific status and include their subtasks
93 | task-master list --status=pending --with-subtasks
94 | ```
95 |
96 | ## Updating Tasks
97 |
98 | The `update` command allows you to update tasks based on new information or implementation changes:
99 |
100 | ```bash
101 | # Update tasks starting from ID 4 with a new prompt
102 | task-master update --from=4 --prompt="Refactor tasks from ID 4 onward to use Express instead of Fastify"
103 |
104 | # Update all tasks (default from=1)
105 | task-master update --prompt="Add authentication to all relevant tasks"
106 |
107 | # Specify a different tasks file
108 | task-master update --file=custom-tasks.json --from=5 --prompt="Change database from MongoDB to PostgreSQL"
109 | ```
110 |
111 | Notes:
112 |
113 | - The `--prompt` parameter is required and should explain the changes or new context
114 | - Only tasks that aren't marked as 'done' will be updated
115 | - Tasks with ID >= the specified --from value will be updated
116 |
117 | ## Setting Task Status
118 |
119 | The `set-status` command allows you to change a task's status:
120 |
121 | ```bash
122 | # Mark a task as done
123 | task-master set-status --id=3 --status=done
124 |
125 | # Mark a task as pending
126 | task-master set-status --id=4 --status=pending
127 |
128 | # Mark a specific subtask as done
129 | task-master set-status --id=3.1 --status=done
130 |
131 | # Mark multiple tasks at once
132 | task-master set-status --id=1,2,3 --status=done
133 | ```
134 |
135 | Notes:
136 |
137 | - When marking a parent task as "done", all of its subtasks will automatically be marked as "done" as well
138 | - Common status values are 'done', 'pending', and 'deferred', but any string is accepted
139 | - You can specify multiple task IDs by separating them with commas
140 | - Subtask IDs are specified using the format `parentId.subtaskId` (e.g., `3.1`)
141 | - Dependencies are updated to show completion status (✅ for completed, ⏱️ for pending) throughout the system
142 |
143 | ## Expanding Tasks
144 |
145 | The `expand` command allows you to break down tasks into subtasks for more detailed implementation:
146 |
147 | ```bash
148 | # Expand a specific task with 3 subtasks (default)
149 | task-master expand --id=3
150 |
151 | # Expand a specific task with 5 subtasks
152 | task-master expand --id=3 --num=5
153 |
154 | # Expand a task with additional context
155 | task-master expand --id=3 --prompt="Focus on security aspects"
156 |
157 | # Expand all pending tasks that don't have subtasks
158 | task-master expand --all
159 |
160 | # Force regeneration of subtasks for all pending tasks
161 | task-master expand --all --force
162 |
163 | # Use Perplexity AI for research-backed subtask generation
164 | task-master expand --id=3 --research
165 |
166 | # Use Perplexity AI for research-backed generation on all pending tasks
167 | task-master expand --all --research
168 | ```
169 |
170 | ## Clearing Subtasks
171 |
172 | The `clear-subtasks` command allows you to remove subtasks from specified tasks:
173 |
174 | ```bash
175 | # Clear subtasks from a specific task
176 | task-master clear-subtasks --id=3
177 |
178 | # Clear subtasks from multiple tasks
179 | task-master clear-subtasks --id=1,2,3
180 |
181 | # Clear subtasks from all tasks
182 | task-master clear-subtasks --all
183 | ```
184 |
185 | Notes:
186 |
187 | - After clearing subtasks, task files are automatically regenerated
188 | - This is useful when you want to regenerate subtasks with a different approach
189 | - Can be combined with the `expand` command to immediately generate new subtasks
190 | - Works with both parent tasks and individual subtasks
191 |
192 | ## AI Integration (Updated)
193 |
194 | - The script now uses a unified AI service layer (`ai-services-unified.js`).
195 | - Model selection (e.g., Claude vs. Perplexity for `--research`) is determined by the configuration in `.taskmaster/config.json` based on the requested `role` (`main` or `research`).
196 | - API keys are automatically resolved from your `.env` file (for CLI) or MCP session environment.
197 | - To use the research capabilities (e.g., `expand --research`), ensure you have:
198 | 1. Configured a model for the `research` role using `task-master models --setup` (Perplexity models are recommended).
199 | 2. Added the corresponding API key (e.g., `PERPLEXITY_API_KEY`) to your `.env` file.
200 |
201 | ## Logging
202 |
203 | The script supports different logging levels controlled by the `TASKMASTER_LOG_LEVEL` environment variable:
204 |
205 | - `debug`: Detailed information, typically useful for troubleshooting
206 | - `info`: Confirmation that things are working as expected (default)
207 | - `warn`: Warning messages that don't prevent execution
208 | - `error`: Error messages that might prevent execution
209 |
210 | When `DEBUG=true` is set, debug logs are also written to a `dev-debug.log` file in the project root.
211 |
212 | ## Managing Task Dependencies
213 |
214 | The `add-dependency` and `remove-dependency` commands allow you to manage task dependencies:
215 |
216 | ```bash
217 | # Add a dependency to a task
218 | task-master add-dependency --id=<id> --depends-on=<id>
219 |
220 | # Remove a dependency from a task
221 | task-master remove-dependency --id=<id> --depends-on=<id>
222 | ```
223 |
224 | These commands:
225 |
226 | 1. **Allow precise dependency management**:
227 |
228 | - Add dependencies between tasks with automatic validation
229 | - Remove dependencies when they're no longer needed
230 | - Update task files automatically after changes
231 |
232 | 2. **Include validation checks**:
233 |
234 | - Prevent circular dependencies (a task depending on itself)
235 | - Prevent duplicate dependencies
236 | - Verify that both tasks exist before adding/removing dependencies
237 | - Check if dependencies exist before attempting to remove them
238 |
239 | 3. **Provide clear feedback**:
240 |
241 | - Success messages confirm when dependencies are added/removed
242 | - Error messages explain why operations failed (if applicable)
243 |
244 | 4. **Automatically update task files**:
245 | - Regenerates task files to reflect dependency changes
246 | - Ensures tasks and their files stay synchronized
247 |
248 | ## Dependency Validation and Fixing
249 |
250 | The script provides two specialized commands to ensure task dependencies remain valid and properly maintained:
251 |
252 | ### Validating Dependencies
253 |
254 | The `validate-dependencies` command allows you to check for invalid dependencies without making changes:
255 |
256 | ```bash
257 | # Check for invalid dependencies in tasks.json
258 | task-master validate-dependencies
259 |
260 | # Specify a different tasks file
261 | task-master validate-dependencies --file=custom-tasks.json
262 | ```
263 |
264 | This command:
265 |
266 | - Scans all tasks and subtasks for non-existent dependencies
267 | - Identifies potential self-dependencies (tasks referencing themselves)
268 | - Reports all found issues without modifying files
269 | - Provides a comprehensive summary of dependency state
270 | - Gives detailed statistics on task dependencies
271 |
272 | Use this command to audit your task structure before applying fixes.
273 |
274 | ### Fixing Dependencies
275 |
276 | The `fix-dependencies` command proactively finds and fixes all invalid dependencies:
277 |
278 | ```bash
279 | # Find and fix all invalid dependencies
280 | task-master fix-dependencies
281 |
282 | # Specify a different tasks file
283 | task-master fix-dependencies --file=custom-tasks.json
284 | ```
285 |
286 | This command:
287 |
288 | 1. **Validates all dependencies** across tasks and subtasks
289 | 2. **Automatically removes**:
290 | - References to non-existent tasks and subtasks
291 | - Self-dependencies (tasks depending on themselves)
292 | 3. **Fixes issues in both**:
293 | - The tasks.json data structure
294 | - Individual task files during regeneration
295 | 4. **Provides a detailed report**:
296 | - Types of issues fixed (non-existent vs. self-dependencies)
297 | - Number of tasks affected (tasks vs. subtasks)
298 | - Where fixes were applied (tasks.json vs. task files)
299 | - List of all individual fixes made
300 |
301 | This is especially useful when tasks have been deleted or IDs have changed, potentially breaking dependency chains.
302 |
303 | ## Analyzing Task Complexity
304 |
305 | The `analyze-complexity` command allows you to automatically assess task complexity and generate expansion recommendations:
306 |
307 | ```bash
308 | # Analyze all tasks and generate expansion recommendations
309 | task-master analyze-complexity
310 |
311 | # Specify a custom output file
312 | task-master analyze-complexity --output=custom-report.json
313 |
314 | # Override the model used for analysis
315 | task-master analyze-complexity --model=claude-3-opus-20240229
316 |
317 | # Set a custom complexity threshold (1-10)
318 | task-master analyze-complexity --threshold=6
319 |
320 | # Use Perplexity AI for research-backed complexity analysis
321 | task-master analyze-complexity --research
322 | ```
323 |
324 | Notes:
325 |
326 | - The command uses Claude to analyze each task's complexity (or Perplexity with --research flag)
327 | - Tasks are scored on a scale of 1-10
328 | - Each task receives a recommended number of subtasks based on DEFAULT_SUBTASKS configuration
329 | - The default output path is `scripts/task-complexity-report.json`
330 | - Each task in the analysis includes a ready-to-use `expansionCommand` that can be copied directly to the terminal or executed programmatically
331 | - Tasks with complexity scores below the threshold (default: 5) may not need expansion
332 | - The research flag provides more contextual and informed complexity assessments
333 |
334 | ### Integration with Expand Command
335 |
336 | The `expand` command automatically checks for and uses complexity analysis if available:
337 |
338 | ```bash
339 | # Expand a task, using complexity report recommendations if available
340 | task-master expand --id=8
341 |
342 | # Expand all tasks, prioritizing by complexity score if a report exists
343 | task-master expand --all
344 |
345 | # Override recommendations with explicit values
346 | task-master expand --id=8 --num=5 --prompt="Custom prompt"
347 | ```
348 |
349 | When a complexity report exists:
350 |
351 | - The `expand` command will use the recommended subtask count from the report (unless overridden)
352 | - It will use the tailored expansion prompt from the report (unless a custom prompt is provided)
353 | - When using `--all`, tasks are sorted by complexity score (highest first)
354 | - The `--research` flag is preserved from the complexity analysis to expansion
355 |
356 | The output report structure is:
357 |
358 | ```json
359 | {
360 | "meta": {
361 | "generatedAt": "2023-06-15T12:34:56.789Z",
362 | "tasksAnalyzed": 20,
363 | "thresholdScore": 5,
364 | "projectName": "Your Project Name",
365 | "usedResearch": true
366 | },
367 | "complexityAnalysis": [
368 | {
369 | "taskId": 8,
370 | "taskTitle": "Develop Implementation Drift Handling",
371 | "complexityScore": 9.5,
372 | "recommendedSubtasks": 6,
373 | "expansionPrompt": "Create subtasks that handle detecting...",
374 | "reasoning": "This task requires sophisticated logic...",
375 | "expansionCommand": "task-master expand --id=8 --num=6 --prompt=\"Create subtasks...\" --research"
376 | }
377 | // More tasks sorted by complexity score (highest first)
378 | ]
379 | }
380 | ```
381 |
382 | ## Finding the Next Task
383 |
384 | The `next` command helps you determine which task to work on next based on dependencies and status:
385 |
386 | ```bash
387 | # Show the next task to work on
388 | task-master next
389 |
390 | # Specify a different tasks file
391 | task-master next --file=custom-tasks.json
392 | ```
393 |
394 | This command:
395 |
396 | 1. Identifies all **eligible tasks** - pending or in-progress tasks whose dependencies are all satisfied (marked as done)
397 | 2. **Prioritizes** these eligible tasks by:
398 | - Priority level (high > medium > low)
399 | - Number of dependencies (fewer dependencies first)
400 | - Task ID (lower ID first)
401 | 3. **Displays** comprehensive information about the selected task:
402 | - Basic task details (ID, title, priority, dependencies)
403 | - Detailed description and implementation details
404 | - Subtasks if they exist
405 | 4. Provides **contextual suggested actions**:
406 | - Command to mark the task as in-progress
407 | - Command to mark the task as done when completed
408 | - Commands for working with subtasks (update status or expand)
409 |
410 | This feature ensures you're always working on the most appropriate task based on your project's current state and dependency structure.
411 |
412 | ## Showing Task Details
413 |
414 | The `show` command allows you to view detailed information about a specific task:
415 |
416 | ```bash
417 | # Show details for a specific task
418 | task-master show 1
419 |
420 | # Alternative syntax with --id option
421 | task-master show --id=1
422 |
423 | # Show details for a subtask
424 | task-master show --id=1.2
425 |
426 | # Specify a different tasks file
427 | task-master show 3 --file=custom-tasks.json
428 | ```
429 |
430 | This command:
431 |
432 | 1. **Displays comprehensive information** about the specified task:
433 | - Basic task details (ID, title, priority, dependencies, status)
434 | - Full description and implementation details
435 | - Test strategy information
436 | - Subtasks if they exist
437 | 2. **Handles both regular tasks and subtasks**:
438 | - For regular tasks, shows all subtasks and their status
439 | - For subtasks, shows the parent task relationship
440 | 3. **Provides contextual suggested actions**:
441 | - Commands to update the task status
442 | - Commands for working with subtasks
443 | - For subtasks, provides a link to view the parent task
444 |
445 | This command is particularly useful when you need to examine a specific task in detail before implementing it or when you want to check the status and details of a particular task.
446 |
```
--------------------------------------------------------------------------------
/scripts/modules/task-manager/parse-prd/parse-prd-streaming.js:
--------------------------------------------------------------------------------
```javascript
1 | /**
2 | * Streaming handler for PRD parsing
3 | */
4 |
5 | import { createParsePrdTracker } from '../../../../src/progress/parse-prd-tracker.js';
6 | import { displayParsePrdStart } from '../../../../src/ui/parse-prd.js';
7 | import { getPriorityIndicators } from '../../../../src/ui/indicators.js';
8 | import { TimeoutManager } from '../../../../src/utils/timeout-manager.js';
9 | import {
10 | streamObjectService,
11 | generateObjectService
12 | } from '../../ai-services-unified.js';
13 | import {
14 | getMainModelId,
15 | getParametersForRole,
16 | getResearchModelId,
17 | getDefaultPriority
18 | } from '../../config-manager.js';
19 | import { LoggingConfig, prdResponseSchema } from './parse-prd-config.js';
20 | import { estimateTokens, reportTaskProgress } from './parse-prd-helpers.js';
21 |
22 | /**
23 | * Extract a readable stream from various stream result formats
24 | * @param {any} streamResult - The stream result object from AI service
25 | * @returns {AsyncIterable|ReadableStream} The extracted stream
26 | * @throws {StreamingError} If no valid stream can be extracted
27 | */
28 | function extractStreamFromResult(streamResult) {
29 | if (!streamResult) {
30 | throw new StreamingError(
31 | 'Stream result is null or undefined',
32 | STREAMING_ERROR_CODES.NOT_ASYNC_ITERABLE
33 | );
34 | }
35 |
36 | // Try extraction strategies in priority order
37 | const stream = tryExtractStream(streamResult);
38 |
39 | if (!stream) {
40 | throw new StreamingError(
41 | 'Stream object is not async iterable or readable',
42 | STREAMING_ERROR_CODES.NOT_ASYNC_ITERABLE
43 | );
44 | }
45 |
46 | return stream;
47 | }
48 |
49 | /**
50 | * Try to extract stream using various strategies
51 | */
52 | function tryExtractStream(streamResult) {
53 | const streamExtractors = [
54 | { key: 'partialObjectStream', extractor: (obj) => obj.partialObjectStream },
55 | { key: 'textStream', extractor: (obj) => extractCallable(obj.textStream) },
56 | { key: 'stream', extractor: (obj) => extractCallable(obj.stream) },
57 | { key: 'baseStream', extractor: (obj) => obj.baseStream }
58 | ];
59 |
60 | for (const { key, extractor } of streamExtractors) {
61 | const stream = extractor(streamResult);
62 | if (stream && isStreamable(stream)) {
63 | return stream;
64 | }
65 | }
66 |
67 | // Check if already streamable
68 | return isStreamable(streamResult) ? streamResult : null;
69 | }
70 |
71 | /**
72 | * Extract a property that might be a function or direct value
73 | */
74 | function extractCallable(property) {
75 | if (!property) return null;
76 | return typeof property === 'function' ? property() : property;
77 | }
78 |
79 | /**
80 | * Check if object is streamable (async iterable or readable stream)
81 | */
82 | function isStreamable(obj) {
83 | return (
84 | obj &&
85 | (typeof obj[Symbol.asyncIterator] === 'function' ||
86 | (obj.getReader && typeof obj.getReader === 'function'))
87 | );
88 | }
89 |
90 | /**
91 | * Handle streaming AI service call and parsing
92 | * @param {Object} config - Configuration object
93 | * @param {Object} prompts - System and user prompts
94 | * @param {number} numTasks - Number of tasks to generate
95 | * @returns {Promise<Object>} Parsed tasks and telemetry
96 | */
97 | export async function handleStreamingService(config, prompts, numTasks) {
98 | const context = createStreamingContext(config, prompts, numTasks);
99 |
100 | await initializeProgress(config, numTasks, context.estimatedInputTokens);
101 |
102 | const aiServiceResponse = await callAIServiceWithTimeout(
103 | config,
104 | prompts,
105 | config.streamingTimeout
106 | );
107 |
108 | const { progressTracker, priorityMap } = await setupProgressTracking(
109 | config,
110 | numTasks
111 | );
112 |
113 | const streamingResult = await processStreamResponse(
114 | aiServiceResponse.mainResult,
115 | config,
116 | prompts,
117 | numTasks,
118 | progressTracker,
119 | priorityMap,
120 | context.defaultPriority,
121 | context.estimatedInputTokens,
122 | context.logger
123 | );
124 |
125 | validateStreamingResult(streamingResult);
126 |
127 | // If we have usage data from streaming, log telemetry now
128 | if (streamingResult.usage && config.projectRoot) {
129 | const { logAiUsage } = await import('../../ai-services-unified.js');
130 | const { getUserId } = await import('../../config-manager.js');
131 | const userId = getUserId(config.projectRoot);
132 |
133 | if (userId && aiServiceResponse.providerName && aiServiceResponse.modelId) {
134 | try {
135 | const telemetryData = await logAiUsage({
136 | userId,
137 | commandName: 'parse-prd',
138 | providerName: aiServiceResponse.providerName,
139 | modelId: aiServiceResponse.modelId,
140 | inputTokens: streamingResult.usage.promptTokens || 0,
141 | outputTokens: streamingResult.usage.completionTokens || 0,
142 | outputType: config.isMCP ? 'mcp' : 'cli'
143 | });
144 |
145 | // Add telemetry to the response
146 | if (telemetryData) {
147 | aiServiceResponse.telemetryData = telemetryData;
148 | }
149 | } catch (telemetryError) {
150 | context.logger.report(
151 | `Failed to log telemetry: ${telemetryError.message}`,
152 | 'debug'
153 | );
154 | }
155 | }
156 | }
157 |
158 | return prepareFinalResult(
159 | streamingResult,
160 | aiServiceResponse,
161 | context.estimatedInputTokens,
162 | progressTracker
163 | );
164 | }
165 |
166 | /**
167 | * Create streaming context with common values
168 | */
169 | function createStreamingContext(config, prompts, numTasks) {
170 | const { systemPrompt, userPrompt } = prompts;
171 | return {
172 | logger: new LoggingConfig(config.mcpLog, config.reportProgress),
173 | estimatedInputTokens: estimateTokens(systemPrompt + userPrompt),
174 | defaultPriority: getDefaultPriority(config.projectRoot) || 'medium'
175 | };
176 | }
177 |
178 | /**
179 | * Validate streaming result has tasks
180 | */
181 | function validateStreamingResult(streamingResult) {
182 | if (streamingResult.parsedTasks.length === 0) {
183 | throw new Error('No tasks were generated from the PRD');
184 | }
185 | }
186 |
187 | /**
188 | * Initialize progress reporting
189 | */
190 | async function initializeProgress(config, numTasks, estimatedInputTokens) {
191 | if (config.reportProgress) {
192 | await config.reportProgress({
193 | progress: 0,
194 | total: numTasks,
195 | message: `Starting PRD analysis (Input: ${estimatedInputTokens} tokens)${config.research ? ' with research' : ''}...`
196 | });
197 | }
198 | }
199 |
200 | /**
201 | * Call AI service with timeout
202 | */
203 | async function callAIServiceWithTimeout(config, prompts, timeout) {
204 | const { systemPrompt, userPrompt } = prompts;
205 |
206 | return await TimeoutManager.withTimeout(
207 | streamObjectService({
208 | role: config.research ? 'research' : 'main',
209 | session: config.session,
210 | projectRoot: config.projectRoot,
211 | schema: prdResponseSchema,
212 | systemPrompt,
213 | prompt: userPrompt,
214 | commandName: 'parse-prd',
215 | outputType: config.isMCP ? 'mcp' : 'cli'
216 | }),
217 | timeout,
218 | 'Streaming operation'
219 | );
220 | }
221 |
222 | /**
223 | * Setup progress tracking for CLI output
224 | */
225 | async function setupProgressTracking(config, numTasks) {
226 | const priorityMap = getPriorityIndicators(config.isMCP);
227 | let progressTracker = null;
228 |
229 | if (config.outputFormat === 'text' && !config.isMCP) {
230 | progressTracker = createParsePrdTracker({
231 | numUnits: numTasks,
232 | unitName: 'task',
233 | append: config.append
234 | });
235 |
236 | const modelId = config.research ? getResearchModelId() : getMainModelId();
237 | const parameters = getParametersForRole(
238 | config.research ? 'research' : 'main'
239 | );
240 |
241 | displayParsePrdStart({
242 | prdFilePath: config.prdPath,
243 | outputPath: config.tasksPath,
244 | numTasks,
245 | append: config.append,
246 | research: config.research,
247 | force: config.force,
248 | existingTasks: [],
249 | nextId: 1,
250 | model: modelId || 'Default',
251 | temperature: parameters?.temperature || 0.7
252 | });
253 |
254 | progressTracker.start();
255 | }
256 |
257 | return { progressTracker, priorityMap };
258 | }
259 |
260 | /**
261 | * Process stream response based on stream type
262 | */
263 | async function processStreamResponse(
264 | streamResult,
265 | config,
266 | prompts,
267 | numTasks,
268 | progressTracker,
269 | priorityMap,
270 | defaultPriority,
271 | estimatedInputTokens,
272 | logger
273 | ) {
274 | const { systemPrompt, userPrompt } = prompts;
275 | const context = {
276 | config: {
277 | ...config,
278 | schema: prdResponseSchema // Add the schema for generateObject fallback
279 | },
280 | numTasks,
281 | progressTracker,
282 | priorityMap,
283 | defaultPriority,
284 | estimatedInputTokens,
285 | prompt: userPrompt,
286 | systemPrompt: systemPrompt
287 | };
288 |
289 | try {
290 | const streamingState = {
291 | lastPartialObject: null,
292 | taskCount: 0,
293 | estimatedOutputTokens: 0,
294 | usage: null
295 | };
296 |
297 | await processPartialStream(
298 | streamResult.partialObjectStream,
299 | streamingState,
300 | context
301 | );
302 |
303 | // Wait for usage data if available
304 | if (streamResult.usage) {
305 | try {
306 | streamingState.usage = await streamResult.usage;
307 | } catch (usageError) {
308 | logger.report(
309 | `Failed to get usage data: ${usageError.message}`,
310 | 'debug'
311 | );
312 | }
313 | }
314 |
315 | return finalizeStreamingResults(streamingState, context);
316 | } catch (error) {
317 | logger.report(
318 | `StreamObject processing failed: ${error.message}. Falling back to generateObject.`,
319 | 'debug'
320 | );
321 | return await processWithGenerateObject(context, logger);
322 | }
323 | }
324 |
325 | /**
326 | * Process the partial object stream
327 | */
328 | async function processPartialStream(partialStream, state, context) {
329 | for await (const partialObject of partialStream) {
330 | state.lastPartialObject = partialObject;
331 |
332 | if (partialObject) {
333 | state.estimatedOutputTokens = estimateTokens(
334 | JSON.stringify(partialObject)
335 | );
336 | }
337 |
338 | await processStreamingTasks(partialObject, state, context);
339 | }
340 | }
341 |
342 | /**
343 | * Process tasks from a streaming partial object
344 | */
345 | async function processStreamingTasks(partialObject, state, context) {
346 | if (!partialObject?.tasks || !Array.isArray(partialObject.tasks)) {
347 | return;
348 | }
349 |
350 | const newTaskCount = partialObject.tasks.length;
351 |
352 | if (newTaskCount > state.taskCount) {
353 | await processNewTasks(
354 | partialObject.tasks,
355 | state.taskCount,
356 | newTaskCount,
357 | state.estimatedOutputTokens,
358 | context
359 | );
360 | state.taskCount = newTaskCount;
361 | } else if (context.progressTracker && state.estimatedOutputTokens > 0) {
362 | context.progressTracker.updateTokens(
363 | context.estimatedInputTokens,
364 | state.estimatedOutputTokens,
365 | true
366 | );
367 | }
368 | }
369 |
370 | /**
371 | * Process newly appeared tasks in the stream
372 | */
373 | async function processNewTasks(
374 | tasks,
375 | startIndex,
376 | endIndex,
377 | estimatedOutputTokens,
378 | context
379 | ) {
380 | for (let i = startIndex; i < endIndex; i++) {
381 | const task = tasks[i] || {};
382 |
383 | if (task.title) {
384 | await reportTaskProgress({
385 | task,
386 | currentCount: i + 1,
387 | totalTasks: context.numTasks,
388 | estimatedTokens: estimatedOutputTokens,
389 | progressTracker: context.progressTracker,
390 | reportProgress: context.config.reportProgress,
391 | priorityMap: context.priorityMap,
392 | defaultPriority: context.defaultPriority,
393 | estimatedInputTokens: context.estimatedInputTokens
394 | });
395 | } else {
396 | await reportPlaceholderTask(i + 1, estimatedOutputTokens, context);
397 | }
398 | }
399 | }
400 |
401 | /**
402 | * Report a placeholder task while it's being generated
403 | */
404 | async function reportPlaceholderTask(
405 | taskNumber,
406 | estimatedOutputTokens,
407 | context
408 | ) {
409 | const {
410 | progressTracker,
411 | config,
412 | numTasks,
413 | defaultPriority,
414 | estimatedInputTokens
415 | } = context;
416 |
417 | if (progressTracker) {
418 | progressTracker.addTaskLine(
419 | taskNumber,
420 | `Generating task ${taskNumber}...`,
421 | defaultPriority
422 | );
423 | progressTracker.updateTokens(
424 | estimatedInputTokens,
425 | estimatedOutputTokens,
426 | true
427 | );
428 | }
429 |
430 | if (config.reportProgress && !progressTracker) {
431 | await config.reportProgress({
432 | progress: taskNumber,
433 | total: numTasks,
434 | message: `Generating task ${taskNumber}/${numTasks}...`
435 | });
436 | }
437 | }
438 |
439 | /**
440 | * Finalize streaming results and update progress display
441 | */
442 | async function finalizeStreamingResults(state, context) {
443 | const { lastPartialObject, estimatedOutputTokens, taskCount, usage } = state;
444 |
445 | if (!lastPartialObject?.tasks || !Array.isArray(lastPartialObject.tasks)) {
446 | throw new Error('No tasks generated from streamObject');
447 | }
448 |
449 | // Use actual token counts if available, otherwise use estimates
450 | const finalOutputTokens = usage?.completionTokens || estimatedOutputTokens;
451 | const finalInputTokens = usage?.promptTokens || context.estimatedInputTokens;
452 |
453 | if (context.progressTracker) {
454 | await updateFinalProgress(
455 | lastPartialObject.tasks,
456 | taskCount,
457 | usage ? finalOutputTokens : estimatedOutputTokens,
458 | context,
459 | usage ? finalInputTokens : null
460 | );
461 | }
462 |
463 | return {
464 | parsedTasks: lastPartialObject.tasks,
465 | estimatedOutputTokens: finalOutputTokens,
466 | actualInputTokens: finalInputTokens,
467 | usage,
468 | usedFallback: false
469 | };
470 | }
471 |
472 | /**
473 | * Update progress tracker with final task content
474 | */
475 | async function updateFinalProgress(
476 | tasks,
477 | taskCount,
478 | outputTokens,
479 | context,
480 | actualInputTokens = null
481 | ) {
482 | const { progressTracker, defaultPriority, estimatedInputTokens } = context;
483 |
484 | if (taskCount > 0) {
485 | updateTaskLines(tasks, progressTracker, defaultPriority);
486 | } else {
487 | await reportAllTasks(tasks, outputTokens, context);
488 | }
489 |
490 | progressTracker.updateTokens(
491 | actualInputTokens || estimatedInputTokens,
492 | outputTokens,
493 | false
494 | );
495 | progressTracker.stop();
496 | }
497 |
498 | /**
499 | * Update task lines in progress tracker with final content
500 | */
501 | function updateTaskLines(tasks, progressTracker, defaultPriority) {
502 | for (let i = 0; i < tasks.length; i++) {
503 | const task = tasks[i];
504 | if (task?.title) {
505 | progressTracker.addTaskLine(
506 | i + 1,
507 | task.title,
508 | task.priority || defaultPriority
509 | );
510 | }
511 | }
512 | }
513 |
514 | /**
515 | * Report all tasks that were not streamed incrementally
516 | */
517 | async function reportAllTasks(tasks, estimatedOutputTokens, context) {
518 | for (let i = 0; i < tasks.length; i++) {
519 | const task = tasks[i];
520 | if (task?.title) {
521 | await reportTaskProgress({
522 | task,
523 | currentCount: i + 1,
524 | totalTasks: context.numTasks,
525 | estimatedTokens: estimatedOutputTokens,
526 | progressTracker: context.progressTracker,
527 | reportProgress: context.config.reportProgress,
528 | priorityMap: context.priorityMap,
529 | defaultPriority: context.defaultPriority,
530 | estimatedInputTokens: context.estimatedInputTokens
531 | });
532 | }
533 | }
534 | }
535 |
536 | /**
537 | * Process with generateObject as fallback when streaming fails
538 | */
539 | async function processWithGenerateObject(context, logger) {
540 | logger.report('Using generateObject fallback for PRD parsing', 'info');
541 |
542 | // Show placeholder tasks while generating
543 | if (context.progressTracker) {
544 | for (let i = 0; i < context.numTasks; i++) {
545 | context.progressTracker.addTaskLine(
546 | i + 1,
547 | `Generating task ${i + 1}...`,
548 | context.defaultPriority
549 | );
550 | context.progressTracker.updateTokens(
551 | context.estimatedInputTokens,
552 | 0,
553 | true
554 | );
555 | }
556 | }
557 |
558 | // Use generateObjectService instead of streaming
559 | const result = await generateObjectService({
560 | role: context.config.research ? 'research' : 'main',
561 | commandName: 'parse-prd',
562 | prompt: context.prompt,
563 | systemPrompt: context.systemPrompt,
564 | schema: context.config.schema,
565 | outputFormat: context.config.outputFormat || 'text',
566 | projectRoot: context.config.projectRoot,
567 | session: context.config.session
568 | });
569 |
570 | // Extract tasks from the result (handle both direct tasks and mainResult.tasks)
571 | const tasks = result?.mainResult || result;
572 |
573 | // Process the generated tasks
574 | if (tasks && Array.isArray(tasks.tasks)) {
575 | // Update progress tracker with final tasks
576 | if (context.progressTracker) {
577 | for (let i = 0; i < tasks.tasks.length; i++) {
578 | const task = tasks.tasks[i];
579 | if (task && task.title) {
580 | context.progressTracker.addTaskLine(
581 | i + 1,
582 | task.title,
583 | task.priority || context.defaultPriority
584 | );
585 | }
586 | }
587 |
588 | // Final token update - use actual telemetry if available
589 | const outputTokens =
590 | result.telemetryData?.outputTokens ||
591 | estimateTokens(JSON.stringify(tasks));
592 | const inputTokens =
593 | result.telemetryData?.inputTokens || context.estimatedInputTokens;
594 |
595 | context.progressTracker.updateTokens(inputTokens, outputTokens, false);
596 | }
597 |
598 | return {
599 | parsedTasks: tasks.tasks,
600 | estimatedOutputTokens:
601 | result.telemetryData?.outputTokens ||
602 | estimateTokens(JSON.stringify(tasks)),
603 | actualInputTokens: result.telemetryData?.inputTokens,
604 | telemetryData: result.telemetryData,
605 | usedFallback: true
606 | };
607 | }
608 |
609 | throw new Error('Failed to generate tasks using generateObject fallback');
610 | }
611 |
612 | /**
613 | * Prepare final result with cleanup
614 | */
615 | function prepareFinalResult(
616 | streamingResult,
617 | aiServiceResponse,
618 | estimatedInputTokens,
619 | progressTracker
620 | ) {
621 | let summary = null;
622 | if (progressTracker) {
623 | summary = progressTracker.getSummary();
624 | progressTracker.cleanup();
625 | }
626 |
627 | // If we have actual usage data from streaming, update the AI service response
628 | if (streamingResult.usage && aiServiceResponse) {
629 | // Map the Vercel AI SDK usage format to our telemetry format
630 | const usage = streamingResult.usage;
631 | if (!aiServiceResponse.usage) {
632 | aiServiceResponse.usage = {
633 | promptTokens: usage.promptTokens || 0,
634 | completionTokens: usage.completionTokens || 0,
635 | totalTokens: usage.totalTokens || 0
636 | };
637 | }
638 |
639 | // The telemetry should have been logged in the unified service runner
640 | // but if not, the usage is now available for telemetry calculation
641 | }
642 |
643 | return {
644 | parsedTasks: streamingResult.parsedTasks,
645 | aiServiceResponse,
646 | estimatedInputTokens:
647 | streamingResult.actualInputTokens || estimatedInputTokens,
648 | estimatedOutputTokens: streamingResult.estimatedOutputTokens,
649 | usedFallback: streamingResult.usedFallback,
650 | progressTracker,
651 | summary
652 | };
653 | }
654 |
```