#
tokens: 49368/50000 23/975 files (page 13/69)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 13 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

--------------------------------------------------------------------------------
/packages/tm-core/src/common/utils/id-generator.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * @fileoverview ID generation utilities for Task Master
  3 |  * Provides functions to generate unique identifiers for tasks and subtasks
  4 |  */
  5 | 
  6 | import { randomBytes } from 'node:crypto';
  7 | 
  8 | /**
  9 |  * Generates a unique task ID using the format: TASK-{timestamp}-{random}
 10 |  *
 11 |  * @returns A unique task ID string
 12 |  * @example
 13 |  * ```typescript
 14 |  * const taskId = generateTaskId();
 15 |  * // Returns something like: "TASK-1704067200000-A7B3"
 16 |  * ```
 17 |  */
 18 | export function generateTaskId(): string {
 19 | 	const timestamp = Date.now();
 20 | 	const random = generateRandomString(4);
 21 | 	return `TASK-${timestamp}-${random}`;
 22 | }
 23 | 
 24 | /**
 25 |  * Generates a subtask ID using the format: {parentId}.{sequential}
 26 |  *
 27 |  * @param parentId - The ID of the parent task
 28 |  * @param existingSubtasks - Array of existing subtask IDs to determine the next sequential number
 29 |  * @returns A unique subtask ID string
 30 |  * @example
 31 |  * ```typescript
 32 |  * const subtaskId = generateSubtaskId("TASK-123-A7B3", ["TASK-123-A7B3.1"]);
 33 |  * // Returns: "TASK-123-A7B3.2"
 34 |  * ```
 35 |  */
 36 | export function generateSubtaskId(
 37 | 	parentId: string,
 38 | 	existingSubtasks: string[] = []
 39 | ): string {
 40 | 	// Find existing subtasks for this parent
 41 | 	const parentSubtasks = existingSubtasks.filter((id) =>
 42 | 		id.startsWith(`${parentId}.`)
 43 | 	);
 44 | 
 45 | 	// Extract sequential numbers and find the highest
 46 | 	const sequentialNumbers = parentSubtasks
 47 | 		.map((id) => {
 48 | 			const parts = id.split('.');
 49 | 			const lastPart = parts[parts.length - 1];
 50 | 			return Number.parseInt(lastPart, 10);
 51 | 		})
 52 | 		.filter((num) => !Number.isNaN(num))
 53 | 		.sort((a, b) => a - b);
 54 | 
 55 | 	// Determine the next sequential number
 56 | 	const nextSequential =
 57 | 		sequentialNumbers.length > 0 ? Math.max(...sequentialNumbers) + 1 : 1;
 58 | 
 59 | 	return `${parentId}.${nextSequential}`;
 60 | }
 61 | 
 62 | /**
 63 |  * Generates a random alphanumeric string of specified length
 64 |  * Uses crypto.randomBytes for cryptographically secure randomness
 65 |  *
 66 |  * @param length - The desired length of the random string
 67 |  * @returns A random alphanumeric string
 68 |  * @internal
 69 |  */
 70 | function generateRandomString(length: number): string {
 71 | 	const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
 72 | 	const bytes = randomBytes(length);
 73 | 	let result = '';
 74 | 
 75 | 	for (let i = 0; i < length; i++) {
 76 | 		result += chars[bytes[i] % chars.length];
 77 | 	}
 78 | 
 79 | 	return result;
 80 | }
 81 | 
 82 | /**
 83 |  * Validates a task ID format
 84 |  *
 85 |  * @param id - The ID to validate
 86 |  * @returns True if the ID matches the expected task ID format
 87 |  * @example
 88 |  * ```typescript
 89 |  * isValidTaskId("TASK-1704067200000-A7B3"); // true
 90 |  * isValidTaskId("invalid-id"); // false
 91 |  * ```
 92 |  */
 93 | export function isValidTaskId(id: string): boolean {
 94 | 	const taskIdRegex = /^TASK-\d{13}-[A-Z0-9]{4}$/;
 95 | 	return taskIdRegex.test(id);
 96 | }
 97 | 
 98 | /**
 99 |  * Validates a subtask ID format
100 |  *
101 |  * @param id - The ID to validate
102 |  * @returns True if the ID matches the expected subtask ID format
103 |  * @example
104 |  * ```typescript
105 |  * isValidSubtaskId("TASK-1704067200000-A7B3.1"); // true
106 |  * isValidSubtaskId("TASK-1704067200000-A7B3.1.2"); // true (nested subtask)
107 |  * isValidSubtaskId("invalid.id"); // false
108 |  * ```
109 |  */
110 | export function isValidSubtaskId(id: string): boolean {
111 | 	const parts = id.split('.');
112 | 	if (parts.length < 2) return false;
113 | 
114 | 	// First part should be a valid task ID
115 | 	const taskIdPart = parts[0];
116 | 	if (!isValidTaskId(taskIdPart)) return false;
117 | 
118 | 	// Remaining parts should be positive integers
119 | 	const sequentialParts = parts.slice(1);
120 | 	return sequentialParts.every((part) => {
121 | 		const num = Number.parseInt(part, 10);
122 | 		return !Number.isNaN(num) && num > 0 && part === num.toString();
123 | 	});
124 | }
125 | 
126 | /**
127 |  * Extracts the parent task ID from a subtask ID
128 |  *
129 |  * @param subtaskId - The subtask ID
130 |  * @returns The parent task ID, or null if the input is not a valid subtask ID
131 |  * @example
132 |  * ```typescript
133 |  * getParentTaskId("TASK-1704067200000-A7B3.1.2"); // "TASK-1704067200000-A7B3"
134 |  * getParentTaskId("TASK-1704067200000-A7B3"); // null (not a subtask)
135 |  * ```
136 |  */
137 | export function getParentTaskId(subtaskId: string): string | null {
138 | 	if (!isValidSubtaskId(subtaskId)) return null;
139 | 
140 | 	const parts = subtaskId.split('.');
141 | 	return parts[0];
142 | }
143 | 
```

--------------------------------------------------------------------------------
/apps/extension/src/services/task-repository.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Task Repository - Simplified version
  3 |  * Handles data access with caching
  4 |  */
  5 | 
  6 | import { EventEmitter } from '../utils/event-emitter';
  7 | import type { ExtensionLogger } from '../utils/logger';
  8 | import type { TaskMasterApi, TaskMasterTask } from '../utils/task-master-api';
  9 | 
 10 | // Use the TaskMasterTask type directly to ensure compatibility
 11 | export type Task = TaskMasterTask;
 12 | 
 13 | export class TaskRepository extends EventEmitter {
 14 | 	private cache: Task[] | null = null;
 15 | 	private cacheTimestamp = 0;
 16 | 	private readonly CACHE_DURATION = 30000; // 30 seconds
 17 | 
 18 | 	constructor(
 19 | 		private api: TaskMasterApi,
 20 | 		private logger: ExtensionLogger
 21 | 	) {
 22 | 		super();
 23 | 	}
 24 | 
 25 | 	async getAll(options?: {
 26 | 		tag?: string;
 27 | 		withSubtasks?: boolean;
 28 | 	}): Promise<Task[]> {
 29 | 		// If a tag is specified, always fetch fresh data
 30 | 		const shouldUseCache =
 31 | 			!options?.tag &&
 32 | 			this.cache &&
 33 | 			Date.now() - this.cacheTimestamp < this.CACHE_DURATION;
 34 | 
 35 | 		if (shouldUseCache) {
 36 | 			return this.cache || [];
 37 | 		}
 38 | 
 39 | 		try {
 40 | 			const result = await this.api.getTasks({
 41 | 				withSubtasks: options?.withSubtasks ?? true,
 42 | 				tag: options?.tag
 43 | 			});
 44 | 
 45 | 			if (result.success && result.data) {
 46 | 				this.cache = result.data;
 47 | 				this.cacheTimestamp = Date.now();
 48 | 				this.emit('tasks:updated', result.data);
 49 | 				return result.data;
 50 | 			}
 51 | 
 52 | 			throw new Error(result.error || 'Failed to fetch tasks');
 53 | 		} catch (error) {
 54 | 			this.logger.error('Failed to get tasks', error);
 55 | 			throw error;
 56 | 		}
 57 | 	}
 58 | 
 59 | 	async getById(taskId: string): Promise<Task | null> {
 60 | 		// First check cache
 61 | 		if (this.cache) {
 62 | 			// Handle both main tasks and subtasks
 63 | 			for (const task of this.cache) {
 64 | 				if (task.id === taskId) {
 65 | 					return task;
 66 | 				}
 67 | 				// Check subtasks
 68 | 				if (task.subtasks) {
 69 | 					for (const subtask of task.subtasks) {
 70 | 						if (
 71 | 							subtask.id.toString() === taskId ||
 72 | 							`${task.id}.${subtask.id}` === taskId
 73 | 						) {
 74 | 							return {
 75 | 								...subtask,
 76 | 								id: subtask.id.toString(),
 77 | 								description: subtask.description || '',
 78 | 								status: (subtask.status ||
 79 | 									'pending') as TaskMasterTask['status'],
 80 | 								priority: 'medium' as const,
 81 | 								dependencies:
 82 | 									subtask.dependencies?.map((d) => d.toString()) || []
 83 | 							};
 84 | 						}
 85 | 					}
 86 | 				}
 87 | 			}
 88 | 		}
 89 | 
 90 | 		// If not in cache, fetch all and search
 91 | 		const tasks = await this.getAll();
 92 | 		for (const task of tasks) {
 93 | 			if (task.id === taskId) {
 94 | 				return task;
 95 | 			}
 96 | 			// Check subtasks
 97 | 			if (task.subtasks) {
 98 | 				for (const subtask of task.subtasks) {
 99 | 					if (
100 | 						subtask.id.toString() === taskId ||
101 | 						`${task.id}.${subtask.id}` === taskId
102 | 					) {
103 | 						return {
104 | 							...subtask,
105 | 							id: subtask.id.toString(),
106 | 							description: subtask.description || '',
107 | 							status: (subtask.status || 'pending') as TaskMasterTask['status'],
108 | 							priority: 'medium' as const,
109 | 							dependencies: subtask.dependencies?.map((d) => d.toString()) || []
110 | 						};
111 | 					}
112 | 				}
113 | 			}
114 | 		}
115 | 
116 | 		return null;
117 | 	}
118 | 
119 | 	async updateStatus(taskId: string, status: Task['status']): Promise<void> {
120 | 		try {
121 | 			const result = await this.api.updateTaskStatus(taskId, status);
122 | 
123 | 			if (!result.success) {
124 | 				throw new Error(result.error || 'Failed to update status');
125 | 			}
126 | 
127 | 			// Invalidate cache
128 | 			this.cache = null;
129 | 
130 | 			// Fetch updated tasks
131 | 			await this.getAll();
132 | 		} catch (error) {
133 | 			this.logger.error('Failed to update task status', error);
134 | 			throw error;
135 | 		}
136 | 	}
137 | 
138 | 	async updateContent(taskId: string, updates: any): Promise<void> {
139 | 		try {
140 | 			const result = await this.api.updateTask(taskId, updates, {
141 | 				append: false,
142 | 				research: false
143 | 			});
144 | 
145 | 			if (!result.success) {
146 | 				throw new Error(result.error || 'Failed to update task');
147 | 			}
148 | 
149 | 			// Invalidate cache
150 | 			this.cache = null;
151 | 
152 | 			// Fetch updated tasks
153 | 			await this.getAll();
154 | 		} catch (error) {
155 | 			this.logger.error('Failed to update task content', error);
156 | 			throw error;
157 | 		}
158 | 	}
159 | 
160 | 	async refresh(): Promise<void> {
161 | 		this.cache = null;
162 | 		await this.getAll();
163 | 	}
164 | 
165 | 	isConnected(): boolean {
166 | 		return this.api.getConnectionStatus().isConnected;
167 | 	}
168 | }
169 | 
```

--------------------------------------------------------------------------------
/packages/ai-sdk-provider-grok-cli/src/errors.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Error handling utilities for Grok CLI provider
  3 |  */
  4 | 
  5 | import { APICallError, LoadAPIKeyError } from '@ai-sdk/provider';
  6 | import type { GrokCliErrorMetadata } from './types.js';
  7 | 
  8 | /**
  9 |  * Parameters for creating API call errors
 10 |  */
 11 | interface CreateAPICallErrorParams {
 12 | 	/** Error message */
 13 | 	message: string;
 14 | 	/** Error code */
 15 | 	code?: string;
 16 | 	/** Process exit code */
 17 | 	exitCode?: number;
 18 | 	/** Standard error output */
 19 | 	stderr?: string;
 20 | 	/** Standard output */
 21 | 	stdout?: string;
 22 | 	/** Excerpt of the prompt */
 23 | 	promptExcerpt?: string;
 24 | 	/** Whether the error is retryable */
 25 | 	isRetryable?: boolean;
 26 | }
 27 | 
 28 | /**
 29 |  * Parameters for creating authentication errors
 30 |  */
 31 | interface CreateAuthenticationErrorParams {
 32 | 	/** Error message */
 33 | 	message?: string;
 34 | }
 35 | 
 36 | /**
 37 |  * Parameters for creating timeout errors
 38 |  */
 39 | interface CreateTimeoutErrorParams {
 40 | 	/** Error message */
 41 | 	message: string;
 42 | 	/** Excerpt of the prompt */
 43 | 	promptExcerpt?: string;
 44 | 	/** Timeout in milliseconds */
 45 | 	timeoutMs: number;
 46 | }
 47 | 
 48 | /**
 49 |  * Parameters for creating installation errors
 50 |  */
 51 | interface CreateInstallationErrorParams {
 52 | 	/** Error message */
 53 | 	message?: string;
 54 | }
 55 | 
 56 | /**
 57 |  * Create an API call error with Grok CLI specific metadata
 58 |  */
 59 | export function createAPICallError({
 60 | 	message,
 61 | 	code,
 62 | 	exitCode,
 63 | 	stderr,
 64 | 	stdout,
 65 | 	promptExcerpt,
 66 | 	isRetryable = false
 67 | }: CreateAPICallErrorParams): APICallError {
 68 | 	const metadata: GrokCliErrorMetadata = {
 69 | 		code,
 70 | 		exitCode,
 71 | 		stderr,
 72 | 		stdout,
 73 | 		promptExcerpt
 74 | 	};
 75 | 
 76 | 	return new APICallError({
 77 | 		message,
 78 | 		isRetryable,
 79 | 		url: 'grok-cli://command',
 80 | 		requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : undefined,
 81 | 		data: metadata
 82 | 	});
 83 | }
 84 | 
 85 | /**
 86 |  * Create an authentication error
 87 |  */
 88 | export function createAuthenticationError({
 89 | 	message
 90 | }: CreateAuthenticationErrorParams): LoadAPIKeyError {
 91 | 	return new LoadAPIKeyError({
 92 | 		message:
 93 | 			message ||
 94 | 			'Authentication failed. Please ensure Grok CLI is properly configured with API key.'
 95 | 	});
 96 | }
 97 | 
 98 | /**
 99 |  * Create a timeout error
100 |  */
101 | export function createTimeoutError({
102 | 	message,
103 | 	promptExcerpt,
104 | 	timeoutMs
105 | }: CreateTimeoutErrorParams): APICallError {
106 | 	const metadata: GrokCliErrorMetadata & { timeoutMs: number } = {
107 | 		code: 'TIMEOUT',
108 | 		promptExcerpt,
109 | 		timeoutMs
110 | 	};
111 | 
112 | 	return new APICallError({
113 | 		message,
114 | 		isRetryable: true,
115 | 		url: 'grok-cli://command',
116 | 		requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : undefined,
117 | 		data: metadata
118 | 	});
119 | }
120 | 
121 | /**
122 |  * Create a CLI installation error
123 |  */
124 | export function createInstallationError({
125 | 	message
126 | }: CreateInstallationErrorParams): APICallError {
127 | 	return new APICallError({
128 | 		message:
129 | 			message ||
130 | 			'Grok CLI is not installed or not found in PATH. Please install with: npm install -g @vibe-kit/grok-cli',
131 | 		isRetryable: false,
132 | 		url: 'grok-cli://installation',
133 | 		requestBodyValues: undefined
134 | 	});
135 | }
136 | 
137 | /**
138 |  * Check if an error is an authentication error
139 |  */
140 | export function isAuthenticationError(
141 | 	error: unknown
142 | ): error is LoadAPIKeyError {
143 | 	if (error instanceof LoadAPIKeyError) return true;
144 | 	if (error instanceof APICallError) {
145 | 		const metadata = error.data as GrokCliErrorMetadata | undefined;
146 | 		if (!metadata) return false;
147 | 		return (
148 | 			metadata.exitCode === 401 ||
149 | 			metadata.code === 'AUTHENTICATION_ERROR' ||
150 | 			metadata.code === 'UNAUTHORIZED'
151 | 		);
152 | 	}
153 | 	return false;
154 | }
155 | 
156 | /**
157 |  * Check if an error is a timeout error
158 |  */
159 | export function isTimeoutError(error: unknown): error is APICallError {
160 | 	if (
161 | 		error instanceof APICallError &&
162 | 		(error.data as GrokCliErrorMetadata)?.code === 'TIMEOUT'
163 | 	)
164 | 		return true;
165 | 	return false;
166 | }
167 | 
168 | /**
169 |  * Check if an error is an installation error
170 |  */
171 | export function isInstallationError(error: unknown): error is APICallError {
172 | 	if (error instanceof APICallError && error.url === 'grok-cli://installation')
173 | 		return true;
174 | 	return false;
175 | }
176 | 
177 | /**
178 |  * Get error metadata from an error
179 |  */
180 | export function getErrorMetadata(
181 | 	error: unknown
182 | ): GrokCliErrorMetadata | undefined {
183 | 	if (error instanceof APICallError && error.data) {
184 | 		return error.data as GrokCliErrorMetadata;
185 | 	}
186 | 	return undefined;
187 | }
188 | 
```

--------------------------------------------------------------------------------
/packages/tm-core/src/modules/workflow/services/workflow-activity-logger.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * @fileoverview WorkflowActivityLogger - Logs all workflow events to activity.jsonl
  3 |  *
  4 |  * Subscribes to all WorkflowOrchestrator events and persists them to a JSONL file
  5 |  * for debugging, auditing, and workflow analysis.
  6 |  */
  7 | 
  8 | import { getLogger } from '../../../common/logger/index.js';
  9 | import {
 10 | 	type ActivityEvent,
 11 | 	logActivity
 12 | } from '../../storage/adapters/activity-logger.js';
 13 | import type { WorkflowOrchestrator } from '../orchestrators/workflow-orchestrator.js';
 14 | import type { WorkflowEventData, WorkflowEventType } from '../types.js';
 15 | 
 16 | /**
 17 |  * All workflow event types that should be logged
 18 |  */
 19 | const WORKFLOW_EVENT_TYPES: WorkflowEventType[] = [
 20 | 	'workflow:started',
 21 | 	'workflow:completed',
 22 | 	'workflow:error',
 23 | 	'workflow:resumed',
 24 | 	'phase:entered',
 25 | 	'phase:exited',
 26 | 	'tdd:feature-already-implemented',
 27 | 	'tdd:red:started',
 28 | 	'tdd:red:completed',
 29 | 	'tdd:green:started',
 30 | 	'tdd:green:completed',
 31 | 	'tdd:commit:started',
 32 | 	'tdd:commit:completed',
 33 | 	'subtask:started',
 34 | 	'subtask:completed',
 35 | 	'subtask:failed',
 36 | 	'test:run',
 37 | 	'test:passed',
 38 | 	'test:failed',
 39 | 	'git:branch:created',
 40 | 	'git:commit:created',
 41 | 	'error:occurred',
 42 | 	'state:persisted',
 43 | 	'progress:updated',
 44 | 	'adapter:configured'
 45 | ];
 46 | 
 47 | /**
 48 |  * Logs all workflow events to an activity.jsonl file
 49 |  */
 50 | export class WorkflowActivityLogger {
 51 | 	private readonly activityLogPath: string;
 52 | 	private readonly orchestrator: WorkflowOrchestrator;
 53 | 	private readonly logger = getLogger('WorkflowActivityLogger');
 54 | 	private readonly listenerMap: Map<
 55 | 		WorkflowEventType,
 56 | 		(event: WorkflowEventData) => void
 57 | 	> = new Map();
 58 | 	private isActive = false;
 59 | 
 60 | 	constructor(orchestrator: WorkflowOrchestrator, activityLogPath: string) {
 61 | 		this.orchestrator = orchestrator;
 62 | 		this.activityLogPath = activityLogPath;
 63 | 	}
 64 | 
 65 | 	/**
 66 | 	 * Start logging workflow events
 67 | 	 */
 68 | 	start(): void {
 69 | 		if (this.isActive) {
 70 | 			this.logger.warn('Activity logger is already active');
 71 | 			return;
 72 | 		}
 73 | 
 74 | 		// Subscribe to all workflow events, storing listener references for cleanup
 75 | 		WORKFLOW_EVENT_TYPES.forEach((eventType) => {
 76 | 			const listener = (event: WorkflowEventData) => this.logEvent(event);
 77 | 			this.listenerMap.set(eventType, listener);
 78 | 			this.orchestrator.on(eventType, listener);
 79 | 		});
 80 | 
 81 | 		this.isActive = true;
 82 | 		this.logger.debug(
 83 | 			`Activity logger started, logging to: ${this.activityLogPath}`
 84 | 		);
 85 | 	}
 86 | 
 87 | 	/**
 88 | 	 * Stop logging workflow events and remove all listeners
 89 | 	 */
 90 | 	stop(): void {
 91 | 		if (!this.isActive) {
 92 | 			return;
 93 | 		}
 94 | 
 95 | 		// Remove all registered listeners
 96 | 		this.listenerMap.forEach((listener, eventType) => {
 97 | 			this.orchestrator.off(eventType, listener);
 98 | 		});
 99 | 
100 | 		// Clear the listener map
101 | 		this.listenerMap.clear();
102 | 
103 | 		this.isActive = false;
104 | 		this.logger.debug('Activity logger stopped and listeners removed');
105 | 	}
106 | 
107 | 	/**
108 | 	 * Log a workflow event to the activity log
109 | 	 */
110 | 	private async logEvent(event: WorkflowEventData): Promise<void> {
111 | 		if (!this.isActive) {
112 | 			return;
113 | 		}
114 | 
115 | 		try {
116 | 			// Convert timestamp to ISO string, handling both Date objects and string/number timestamps
117 | 			const ts =
118 | 				(event.timestamp as any) instanceof Date
119 | 					? (event.timestamp as Date).toISOString()
120 | 					: new Date(event.timestamp as any).toISOString();
121 | 
122 | 			// Convert WorkflowEventData to ActivityEvent format
123 | 			const activityEvent: Omit<ActivityEvent, 'timestamp'> = {
124 | 				type: event.type,
125 | 				phase: event.phase,
126 | 				tddPhase: event.tddPhase,
127 | 				subtaskId: event.subtaskId,
128 | 				// Event timestamp kept as ISO for readability; storage layer adds its own "timestamp"
129 | 				eventTimestamp: ts,
130 | 				...(event.data || {})
131 | 			};
132 | 
133 | 			await logActivity(this.activityLogPath, activityEvent);
134 | 		} catch (error: any) {
135 | 			// Log errors but don't throw - we don't want activity logging to break the workflow
136 | 			this.logger.error(
137 | 				`Failed to log activity event ${event.type}: ${error.message}`
138 | 			);
139 | 		}
140 | 	}
141 | 
142 | 	/**
143 | 	 * Get the path to the activity log file
144 | 	 */
145 | 	getActivityLogPath(): string {
146 | 		return this.activityLogPath;
147 | 	}
148 | 
149 | 	/**
150 | 	 * Check if the logger is currently active
151 | 	 */
152 | 	isLogging(): boolean {
153 | 		return this.isActive;
154 | 	}
155 | }
156 | 
```

--------------------------------------------------------------------------------
/apps/docs/getting-started/quick-start/prd-quick.mdx:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: PRD Creation and Parsing
  3 | sidebarTitle: "PRD Creation and Parsing"
  4 | ---
  5 | 
  6 | # Writing a PRD
  7 | 
  8 | A PRD (Product Requirements Document) is the starting point of every task flow in Task Master. It defines what you're building and why. A clear PRD dramatically improves the quality of your tasks, your model outputs, and your final product — so it’s worth taking the time to get it right.
  9 | 
 10 | <Tip>
 11 | You don’t need to define your whole app up front. You can write a focused PRD just for the next feature or module you’re working on.
 12 | </Tip>
 13 | 
 14 | <Tip>
 15 | You can start with an empty project or you can start with a feature PRD on an existing project.
 16 | </Tip>
 17 | 
 18 | <Tip>
 19 | You can add and parse multiple PRDs per project using the --append flag
 20 | </Tip>
 21 | 
 22 | ## What Makes a Good PRD?
 23 | 
 24 | - Clear objective — what’s the outcome or feature?
 25 | - Context — what’s already in place or assumed?
 26 | - Constraints — what limits or requirements need to be respected?
 27 | - Reasoning — why are you building it this way?
 28 | 
 29 | The more context you give the model, the better the breakdown and results.
 30 | 
 31 | ---
 32 | 
 33 | ## Writing a PRD for Task Master
 34 | 
 35 | <Note>
 36 | Two example PRD templates are available in `.taskmaster/templates/`:
 37 | - `example_prd.md` - Simple template for straightforward projects (`.md` recommended for better editor support)
 38 | - `example_prd_rpg.md` - Advanced RPG (Repository Planning Graph) template for complex projects with dependencies
 39 | 
 40 | **Why `.md`?** While both `.txt` and `.md` work, Markdown files provide syntax highlighting, proper rendering in VS Code/GitHub, and better collaboration through formatted documentation.
 41 | </Note>
 42 | 
 43 | 
 44 | You can co-write your PRD with an LLM model using the following workflow:
 45 | 
 46 | 1. **Chat about requirements** — explain what you want to build.
 47 | 2. **Show an example PRD** — share the example PRD so the model understands the expected format. The example uses formatting that work well with Task Master's code. Following the example will yield better results.
 48 | 3. **Iterate and refine** — work with the model to shape the draft into a clear and well-structured PRD.
 49 | 
 50 | This approach works great in Cursor, or anywhere you use a chat-based LLM.
 51 | 
 52 | ### Choosing Between Templates
 53 | 
 54 | **Use `example_prd.md` when:**
 55 | - Building straightforward features
 56 | - Working on smaller projects
 57 | - Dependencies are simple and obvious
 58 | 
 59 | **Use `example_prd_rpg.md` when:**
 60 | - Building complex systems with multiple modules
 61 | - Need explicit dependency management
 62 | - Want structured guidance on architecture decisions
 63 | - Planning a large codebase from scratch
 64 | 
 65 | The RPG template teaches you to think about:
 66 | 1. **Functional decomposition** (WHAT the system does)
 67 | 2. **Structural decomposition** (HOW it's organized in code)
 68 | 3. **Explicit dependencies** (WHAT depends on WHAT)
 69 | 4. **Topological ordering** (build foundation first, then layers)
 70 | 
 71 | <Tip>
 72 | For complex projects, using the RPG template with a code-context-aware ai agent produces the best results because the AI can understand your existing codebase structure. [Learn more about the RPG method →](/capabilities/rpg-method)
 73 | </Tip>
 74 | 
 75 | ---
 76 | 
 77 | ## Where to Save Your PRD
 78 | 
 79 | Place your PRD file in the `.taskmaster/docs` folder in your project.
 80 | 
 81 | - You can have **multiple PRDs** per project.
 82 | - Name your PRDs clearly so they're easy to reference later.
 83 |   - Examples: `dashboard_redesign.md`, `user_onboarding.md`
 84 |   - Tip: Use `.md` extension for better editor support and syntax highlighting
 85 | 
 86 | ---
 87 | 
 88 | # Parse your PRD into Tasks
 89 | 
 90 | This is where the Task Master magic begins.
 91 | 
 92 | In Cursor's AI chat, instruct the agent to generate tasks from your PRD:
 93 | 
 94 | ```
 95 | Please use the task-master parse-prd command to generate tasks from my PRD. The PRD is located at .taskmaster/docs/<prd-name>.md.
 96 | ```
 97 | 
 98 | The agent will execute the following command which you can alternatively paste into the CLI:
 99 | 
100 | ```bash
101 | task-master parse-prd .taskmaster/docs/<prd-name>.md
102 | ```
103 | 
104 | This will:
105 | 
106 | - Parse your PRD document
107 | - Generate a structured `tasks.json` file with tasks, dependencies, priorities, and test strategies
108 | 
109 | Now that you have written and parsed a PRD, you are ready to start setting up your tasks.
110 | 
111 | 
112 | 
```

--------------------------------------------------------------------------------
/packages/tm-core/src/common/mappers/TaskMapper.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { describe, expect, it, vi } from 'vitest';
  2 | import type { Tables } from '../types/database.types.js';
  3 | import { TaskMapper } from './TaskMapper.js';
  4 | 
  5 | type TaskRow = Tables<'tasks'>;
  6 | 
  7 | describe('TaskMapper', () => {
  8 | 	describe('extractMetadataField', () => {
  9 | 		it('should extract string field from metadata', () => {
 10 | 			const taskRow: TaskRow = {
 11 | 				id: '123',
 12 | 				display_id: '1',
 13 | 				title: 'Test Task',
 14 | 				description: 'Test description',
 15 | 				status: 'todo',
 16 | 				priority: 'medium',
 17 | 				parent_task_id: null,
 18 | 				subtask_position: 0,
 19 | 				created_at: new Date().toISOString(),
 20 | 				updated_at: new Date().toISOString(),
 21 | 				metadata: {
 22 | 					details: 'Some details',
 23 | 					testStrategy: 'Test with unit tests'
 24 | 				},
 25 | 				complexity: null,
 26 | 				assignee_id: null,
 27 | 				estimated_hours: null,
 28 | 				actual_hours: null,
 29 | 				due_date: null,
 30 | 				completed_at: null
 31 | 			};
 32 | 
 33 | 			const task = TaskMapper.mapDatabaseTaskToTask(taskRow, [], new Map());
 34 | 
 35 | 			expect(task.details).toBe('Some details');
 36 | 			expect(task.testStrategy).toBe('Test with unit tests');
 37 | 		});
 38 | 
 39 | 		it('should use default value when metadata field is missing', () => {
 40 | 			const taskRow: TaskRow = {
 41 | 				id: '123',
 42 | 				display_id: '1',
 43 | 				title: 'Test Task',
 44 | 				description: 'Test description',
 45 | 				status: 'todo',
 46 | 				priority: 'medium',
 47 | 				parent_task_id: null,
 48 | 				subtask_position: 0,
 49 | 				created_at: new Date().toISOString(),
 50 | 				updated_at: new Date().toISOString(),
 51 | 				metadata: {},
 52 | 				complexity: null,
 53 | 				assignee_id: null,
 54 | 				estimated_hours: null,
 55 | 				actual_hours: null,
 56 | 				due_date: null,
 57 | 				completed_at: null
 58 | 			};
 59 | 
 60 | 			const task = TaskMapper.mapDatabaseTaskToTask(taskRow, [], new Map());
 61 | 
 62 | 			expect(task.details).toBe('');
 63 | 			expect(task.testStrategy).toBe('');
 64 | 		});
 65 | 
 66 | 		it('should use default value when metadata is null', () => {
 67 | 			const taskRow: TaskRow = {
 68 | 				id: '123',
 69 | 				display_id: '1',
 70 | 				title: 'Test Task',
 71 | 				description: 'Test description',
 72 | 				status: 'todo',
 73 | 				priority: 'medium',
 74 | 				parent_task_id: null,
 75 | 				subtask_position: 0,
 76 | 				created_at: new Date().toISOString(),
 77 | 				updated_at: new Date().toISOString(),
 78 | 				metadata: null,
 79 | 				complexity: null,
 80 | 				assignee_id: null,
 81 | 				estimated_hours: null,
 82 | 				actual_hours: null,
 83 | 				due_date: null,
 84 | 				completed_at: null
 85 | 			};
 86 | 
 87 | 			const task = TaskMapper.mapDatabaseTaskToTask(taskRow, [], new Map());
 88 | 
 89 | 			expect(task.details).toBe('');
 90 | 			expect(task.testStrategy).toBe('');
 91 | 		});
 92 | 
 93 | 		it('should use default value and warn when metadata field has wrong type', () => {
 94 | 			const consoleWarnSpy = vi
 95 | 				.spyOn(console, 'warn')
 96 | 				.mockImplementation(() => {});
 97 | 
 98 | 			const taskRow: TaskRow = {
 99 | 				id: '123',
100 | 				display_id: '1',
101 | 				title: 'Test Task',
102 | 				description: 'Test description',
103 | 				status: 'todo',
104 | 				priority: 'medium',
105 | 				parent_task_id: null,
106 | 				subtask_position: 0,
107 | 				created_at: new Date().toISOString(),
108 | 				updated_at: new Date().toISOString(),
109 | 				metadata: {
110 | 					details: 12345, // Wrong type: number instead of string
111 | 					testStrategy: ['test1', 'test2'] // Wrong type: array instead of string
112 | 				},
113 | 				complexity: null,
114 | 				assignee_id: null,
115 | 				estimated_hours: null,
116 | 				actual_hours: null,
117 | 				due_date: null,
118 | 				completed_at: null
119 | 			};
120 | 
121 | 			const task = TaskMapper.mapDatabaseTaskToTask(taskRow, [], new Map());
122 | 
123 | 			// Should use empty string defaults when type doesn't match
124 | 			expect(task.details).toBe('');
125 | 			expect(task.testStrategy).toBe('');
126 | 
127 | 			// Should have logged warnings
128 | 			expect(consoleWarnSpy).toHaveBeenCalledWith(
129 | 				expect.stringContaining('Type mismatch in metadata field "details"')
130 | 			);
131 | 			expect(consoleWarnSpy).toHaveBeenCalledWith(
132 | 				expect.stringContaining(
133 | 					'Type mismatch in metadata field "testStrategy"'
134 | 				)
135 | 			);
136 | 
137 | 			consoleWarnSpy.mockRestore();
138 | 		});
139 | 	});
140 | 
141 | 	describe('mapStatus', () => {
142 | 		it('should map database status to internal status', () => {
143 | 			expect(TaskMapper.mapStatus('todo')).toBe('pending');
144 | 			expect(TaskMapper.mapStatus('in_progress')).toBe('in-progress');
145 | 			expect(TaskMapper.mapStatus('done')).toBe('done');
146 | 		});
147 | 	});
148 | });
149 | 
```

--------------------------------------------------------------------------------
/.claude/TM_COMMANDS_GUIDE.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Task Master Commands for Claude Code
  2 | 
  3 | Complete guide to using Task Master through Claude Code's slash commands.
  4 | 
  5 | ## Overview
  6 | 
  7 | All Task Master functionality is available through the `/project:tm/` namespace with natural language support and intelligent features.
  8 | 
  9 | ## Quick Start
 10 | 
 11 | ```bash
 12 | # Install Task Master
 13 | /project:tm/setup/quick-install
 14 | 
 15 | # Initialize project
 16 | /project:tm/init/quick
 17 | 
 18 | # Parse requirements
 19 | /project:tm/parse-prd requirements.md
 20 | 
 21 | # Start working
 22 | /project:tm/next
 23 | ```
 24 | 
 25 | ## Command Structure
 26 | 
 27 | Commands are organized hierarchically to match Task Master's CLI:
 28 | - Main commands at `/project:tm/[command]`
 29 | - Subcommands for specific operations `/project:tm/[command]/[subcommand]`
 30 | - Natural language arguments accepted throughout
 31 | 
 32 | ## Complete Command Reference
 33 | 
 34 | ### Setup & Configuration
 35 | - `/project:tm/setup/install` - Full installation guide
 36 | - `/project:tm/setup/quick-install` - One-line install
 37 | - `/project:tm/init` - Initialize project
 38 | - `/project:tm/init/quick` - Quick init with -y
 39 | - `/project:tm/models` - View AI config
 40 | - `/project:tm/models/setup` - Configure AI
 41 | 
 42 | ### Task Generation
 43 | - `/project:tm/parse-prd` - Generate from PRD
 44 | - `/project:tm/parse-prd/with-research` - Enhanced parsing
 45 | - `/project:tm/generate` - Create task files
 46 | 
 47 | ### Task Management
 48 | - `/project:tm/list` - List with natural language filters
 49 | - `/project:tm/list/with-subtasks` - Hierarchical view
 50 | - `/project:tm/list/by-status <status>` - Filter by status
 51 | - `/project:tm/show <id>` - Task details
 52 | - `/project:tm/add-task` - Create task
 53 | - `/project:tm/update` - Update tasks
 54 | - `/project:tm/remove-task` - Delete task
 55 | 
 56 | ### Status Management
 57 | - `/project:tm/set-status/to-pending <id>`
 58 | - `/project:tm/set-status/to-in-progress <id>`
 59 | - `/project:tm/set-status/to-done <id>`
 60 | - `/project:tm/set-status/to-review <id>`
 61 | - `/project:tm/set-status/to-deferred <id>`
 62 | - `/project:tm/set-status/to-cancelled <id>`
 63 | 
 64 | ### Task Analysis
 65 | - `/project:tm/analyze-complexity` - AI analysis
 66 | - `/project:tm/complexity-report` - View report
 67 | - `/project:tm/expand <id>` - Break down task
 68 | - `/project:tm/expand/all` - Expand all complex
 69 | 
 70 | ### Dependencies
 71 | - `/project:tm/add-dependency` - Add dependency
 72 | - `/project:tm/remove-dependency` - Remove dependency
 73 | - `/project:tm/validate-dependencies` - Check issues
 74 | - `/project:tm/fix-dependencies` - Auto-fix
 75 | 
 76 | ### Workflows
 77 | - `/project:tm/workflows/smart-flow` - Adaptive workflows
 78 | - `/project:tm/workflows/pipeline` - Chain commands
 79 | - `/project:tm/workflows/auto-implement` - AI implementation
 80 | 
 81 | ### Utilities
 82 | - `/project:tm/status` - Project dashboard
 83 | - `/project:tm/next` - Next task recommendation
 84 | - `/project:tm/utils/analyze` - Project analysis
 85 | - `/project:tm/learn` - Interactive help
 86 | 
 87 | ## Key Features
 88 | 
 89 | ### Natural Language Support
 90 | All commands understand natural language:
 91 | ```
 92 | /project:tm/list pending high priority
 93 | /project:tm/update mark 23 as done
 94 | /project:tm/add-task implement OAuth login
 95 | ```
 96 | 
 97 | ### Smart Context
 98 | Commands analyze project state and provide intelligent suggestions based on:
 99 | - Current task status
100 | - Dependencies
101 | - Team patterns
102 | - Project phase
103 | 
104 | ### Visual Enhancements
105 | - Progress bars and indicators
106 | - Status badges
107 | - Organized displays
108 | - Clear hierarchies
109 | 
110 | ## Common Workflows
111 | 
112 | ### Daily Development
113 | ```
114 | /project:tm/workflows/smart-flow morning
115 | /project:tm/next
116 | /project:tm/set-status/to-in-progress <id>
117 | /project:tm/set-status/to-done <id>
118 | ```
119 | 
120 | ### Task Breakdown
121 | ```
122 | /project:tm/show <id>
123 | /project:tm/expand <id>
124 | /project:tm/list/with-subtasks
125 | ```
126 | 
127 | ### Sprint Planning
128 | ```
129 | /project:tm/analyze-complexity
130 | /project:tm/workflows/pipeline init → expand/all → status
131 | ```
132 | 
133 | ## Migration from Old Commands
134 | 
135 | | Old | New |
136 | |-----|-----|
137 | | `/project:task-master:list` | `/project:tm/list` |
138 | | `/project:task-master:complete` | `/project:tm/set-status/to-done` |
139 | | `/project:workflows:auto-implement` | `/project:tm/workflows/auto-implement` |
140 | 
141 | ## Tips
142 | 
143 | 1. Use `/project:tm/` + Tab for command discovery
144 | 2. Natural language is supported everywhere
145 | 3. Commands provide smart defaults
146 | 4. Chain commands for automation
147 | 5. Check `/project:tm/learn` for interactive help
```

--------------------------------------------------------------------------------
/assets/claude/TM_COMMANDS_GUIDE.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Task Master Commands for Claude Code
  2 | 
  3 | Complete guide to using Task Master through Claude Code's slash commands.
  4 | 
  5 | ## Overview
  6 | 
  7 | All Task Master functionality is available through the `/project:tm/` namespace with natural language support and intelligent features.
  8 | 
  9 | ## Quick Start
 10 | 
 11 | ```bash
 12 | # Install Task Master
 13 | /project:tm/setup/quick-install
 14 | 
 15 | # Initialize project
 16 | /project:tm/init/quick
 17 | 
 18 | # Parse requirements
 19 | /project:tm/parse-prd requirements.md
 20 | 
 21 | # Start working
 22 | /project:tm/next
 23 | ```
 24 | 
 25 | ## Command Structure
 26 | 
 27 | Commands are organized hierarchically to match Task Master's CLI:
 28 | - Main commands at `/project:tm/[command]`
 29 | - Subcommands for specific operations `/project:tm/[command]/[subcommand]`
 30 | - Natural language arguments accepted throughout
 31 | 
 32 | ## Complete Command Reference
 33 | 
 34 | ### Setup & Configuration
 35 | - `/project:tm/setup/install` - Full installation guide
 36 | - `/project:tm/setup/quick-install` - One-line install
 37 | - `/project:tm/init` - Initialize project
 38 | - `/project:tm/init/quick` - Quick init with -y
 39 | - `/project:tm/models` - View AI config
 40 | - `/project:tm/models/setup` - Configure AI
 41 | 
 42 | ### Task Generation
 43 | - `/project:tm/parse-prd` - Generate from PRD
 44 | - `/project:tm/parse-prd/with-research` - Enhanced parsing
 45 | - `/project:tm/generate` - Create task files
 46 | 
 47 | ### Task Management
 48 | - `/project:tm/list` - List with natural language filters
 49 | - `/project:tm/list/with-subtasks` - Hierarchical view
 50 | - `/project:tm/list/by-status <status>` - Filter by status
 51 | - `/project:tm/show <id>` - Task details
 52 | - `/project:tm/add-task` - Create task
 53 | - `/project:tm/update` - Update tasks
 54 | - `/project:tm/remove-task` - Delete task
 55 | 
 56 | ### Status Management
 57 | - `/project:tm/set-status/to-pending <id>`
 58 | - `/project:tm/set-status/to-in-progress <id>`
 59 | - `/project:tm/set-status/to-done <id>`
 60 | - `/project:tm/set-status/to-review <id>`
 61 | - `/project:tm/set-status/to-deferred <id>`
 62 | - `/project:tm/set-status/to-cancelled <id>`
 63 | 
 64 | ### Task Analysis
 65 | - `/project:tm/analyze-complexity` - AI analysis
 66 | - `/project:tm/complexity-report` - View report
 67 | - `/project:tm/expand <id>` - Break down task
 68 | - `/project:tm/expand/all` - Expand all complex
 69 | 
 70 | ### Dependencies
 71 | - `/project:tm/add-dependency` - Add dependency
 72 | - `/project:tm/remove-dependency` - Remove dependency
 73 | - `/project:tm/validate-dependencies` - Check issues
 74 | - `/project:tm/fix-dependencies` - Auto-fix
 75 | 
 76 | ### Workflows
 77 | - `/project:tm/workflows/smart-flow` - Adaptive workflows
 78 | - `/project:tm/workflows/pipeline` - Chain commands
 79 | - `/project:tm/workflows/auto-implement` - AI implementation
 80 | 
 81 | ### Utilities
 82 | - `/project:tm/status` - Project dashboard
 83 | - `/project:tm/next` - Next task recommendation
 84 | - `/project:tm/utils/analyze` - Project analysis
 85 | - `/project:tm/learn` - Interactive help
 86 | 
 87 | ## Key Features
 88 | 
 89 | ### Natural Language Support
 90 | All commands understand natural language:
 91 | ```
 92 | /project:tm/list pending high priority
 93 | /project:tm/update mark 23 as done
 94 | /project:tm/add-task implement OAuth login
 95 | ```
 96 | 
 97 | ### Smart Context
 98 | Commands analyze project state and provide intelligent suggestions based on:
 99 | - Current task status
100 | - Dependencies
101 | - Team patterns
102 | - Project phase
103 | 
104 | ### Visual Enhancements
105 | - Progress bars and indicators
106 | - Status badges
107 | - Organized displays
108 | - Clear hierarchies
109 | 
110 | ## Common Workflows
111 | 
112 | ### Daily Development
113 | ```
114 | /project:tm/workflows/smart-flow morning
115 | /project:tm/next
116 | /project:tm/set-status/to-in-progress <id>
117 | /project:tm/set-status/to-done <id>
118 | ```
119 | 
120 | ### Task Breakdown
121 | ```
122 | /project:tm/show <id>
123 | /project:tm/expand <id>
124 | /project:tm/list/with-subtasks
125 | ```
126 | 
127 | ### Sprint Planning
128 | ```
129 | /project:tm/analyze-complexity
130 | /project:tm/workflows/pipeline init → expand/all → status
131 | ```
132 | 
133 | ## Migration from Old Commands
134 | 
135 | | Old | New |
136 | |-----|-----|
137 | | `/project:task-master:list` | `/project:tm/list` |
138 | | `/project:task-master:complete` | `/project:tm/set-status/to-done` |
139 | | `/project:workflows:auto-implement` | `/project:tm/workflows/auto-implement` |
140 | 
141 | ## Tips
142 | 
143 | 1. Use `/project:tm/` + Tab for command discovery
144 | 2. Natural language is supported everywhere
145 | 3. Commands provide smart defaults
146 | 4. Chain commands for automation
147 | 5. Check `/project:tm/learn` for interactive help
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/clear-subtasks.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * Direct function wrapper for clearSubtasks
  3 |  */
  4 | 
  5 | import { clearSubtasks } from '../../../../scripts/modules/task-manager.js';
  6 | import {
  7 | 	enableSilentMode,
  8 | 	disableSilentMode,
  9 | 	readJSON
 10 | } from '../../../../scripts/modules/utils.js';
 11 | import fs from 'fs';
 12 | import path from 'path';
 13 | 
 14 | /**
 15 |  * Clear subtasks from specified tasks
 16 |  * @param {Object} args - Function arguments
 17 |  * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 18 |  * @param {string} [args.id] - Task IDs (comma-separated) to clear subtasks from
 19 |  * @param {boolean} [args.all] - Clear subtasks from all tasks
 20 |  * @param {string} [args.tag] - Tag context to operate on (defaults to current active tag)
 21 |  * @param {string} [args.projectRoot] - Project root path (for MCP/env fallback)
 22 |  * @param {Object} log - Logger object
 23 |  * @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
 24 |  */
 25 | export async function clearSubtasksDirect(args, log) {
 26 | 	// Destructure expected args
 27 | 	const { tasksJsonPath, id, all, tag, projectRoot } = args;
 28 | 	try {
 29 | 		log.info(`Clearing subtasks with args: ${JSON.stringify(args)}`);
 30 | 
 31 | 		// Check if tasksJsonPath was provided
 32 | 		if (!tasksJsonPath) {
 33 | 			log.error('clearSubtasksDirect called without tasksJsonPath');
 34 | 			return {
 35 | 				success: false,
 36 | 				error: {
 37 | 					code: 'MISSING_ARGUMENT',
 38 | 					message: 'tasksJsonPath is required'
 39 | 				}
 40 | 			};
 41 | 		}
 42 | 
 43 | 		// Either id or all must be provided
 44 | 		if (!id && !all) {
 45 | 			return {
 46 | 				success: false,
 47 | 				error: {
 48 | 					code: 'INPUT_VALIDATION_ERROR',
 49 | 					message:
 50 | 						'Either task IDs with id parameter or all parameter must be provided'
 51 | 				}
 52 | 			};
 53 | 		}
 54 | 
 55 | 		// Use provided path
 56 | 		const tasksPath = tasksJsonPath;
 57 | 
 58 | 		// Check if tasks.json exists
 59 | 		if (!fs.existsSync(tasksPath)) {
 60 | 			return {
 61 | 				success: false,
 62 | 				error: {
 63 | 					code: 'FILE_NOT_FOUND_ERROR',
 64 | 					message: `Tasks file not found at ${tasksPath}`
 65 | 				}
 66 | 			};
 67 | 		}
 68 | 
 69 | 		let taskIds;
 70 | 
 71 | 		// Use readJSON which handles silent migration and tag resolution
 72 | 		const data = readJSON(tasksPath, projectRoot, tag);
 73 | 
 74 | 		if (!data || !data.tasks) {
 75 | 			return {
 76 | 				success: false,
 77 | 				error: {
 78 | 					code: 'INPUT_VALIDATION_ERROR',
 79 | 					message: `No tasks found in tasks file: ${tasksPath}`
 80 | 				}
 81 | 			};
 82 | 		}
 83 | 
 84 | 		const currentTag = data.tag || tag;
 85 | 		const tasks = data.tasks;
 86 | 
 87 | 		// If all is specified, get all task IDs
 88 | 		if (all) {
 89 | 			log.info(`Clearing subtasks from all tasks in tag '${currentTag}'`);
 90 | 			if (tasks.length === 0) {
 91 | 				return {
 92 | 					success: false,
 93 | 					error: {
 94 | 						code: 'INPUT_VALIDATION_ERROR',
 95 | 						message: `No tasks found in tag context '${currentTag}'`
 96 | 					}
 97 | 				};
 98 | 			}
 99 | 			taskIds = tasks.map((t) => t.id).join(',');
100 | 		} else {
101 | 			// Use the provided task IDs
102 | 			taskIds = id;
103 | 		}
104 | 
105 | 		log.info(`Clearing subtasks from tasks: ${taskIds} in tag '${currentTag}'`);
106 | 
107 | 		// Enable silent mode to prevent console logs from interfering with JSON response
108 | 		enableSilentMode();
109 | 
110 | 		// Call the core function
111 | 		clearSubtasks(tasksPath, taskIds, { projectRoot, tag: currentTag });
112 | 
113 | 		// Restore normal logging
114 | 		disableSilentMode();
115 | 
116 | 		// Read the updated data to provide a summary
117 | 		const updatedData = readJSON(tasksPath, projectRoot, currentTag);
118 | 		const taskIdArray = taskIds.split(',').map((id) => parseInt(id.trim(), 10));
119 | 
120 | 		// Build a summary of what was done
121 | 		const clearedTasksCount = taskIdArray.length;
122 | 		const updatedTasks = updatedData.tasks || [];
123 | 
124 | 		const taskSummary = taskIdArray.map((id) => {
125 | 			const task = updatedTasks.find((t) => t.id === id);
126 | 			return task ? { id, title: task.title } : { id, title: 'Task not found' };
127 | 		});
128 | 
129 | 		return {
130 | 			success: true,
131 | 			data: {
132 | 				message: `Successfully cleared subtasks from ${clearedTasksCount} task(s) in tag '${currentTag}'`,
133 | 				tasksCleared: taskSummary,
134 | 				tag: currentTag
135 | 			}
136 | 		};
137 | 	} catch (error) {
138 | 		// Make sure to restore normal logging even if there's an error
139 | 		disableSilentMode();
140 | 
141 | 		log.error(`Error in clearSubtasksDirect: ${error.message}`);
142 | 		return {
143 | 			success: false,
144 | 			error: {
145 | 				code: 'CORE_FUNCTION_ERROR',
146 | 				message: error.message
147 | 			}
148 | 		};
149 | 	}
150 | }
151 | 
```

--------------------------------------------------------------------------------
/src/ai-providers/openai-compatible.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * openai-compatible.js
  3 |  * Generic base class for OpenAI-compatible API providers.
  4 |  * This allows any provider with an OpenAI-compatible API to be easily integrated.
  5 |  */
  6 | 
  7 | import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
  8 | import { BaseAIProvider } from './base-provider.js';
  9 | 
 10 | /**
 11 |  * Base class for OpenAI-compatible providers (LM Studio, Z.ai, etc.)
 12 |  * Provides a flexible foundation for any service with OpenAI-compatible endpoints.
 13 |  */
 14 | export class OpenAICompatibleProvider extends BaseAIProvider {
 15 | 	/**
 16 | 	 * @param {object} config - Provider configuration
 17 | 	 * @param {string} config.name - Provider display name
 18 | 	 * @param {string} config.apiKeyEnvVar - Environment variable name for API key
 19 | 	 * @param {boolean} [config.requiresApiKey=true] - Whether API key is required
 20 | 	 * @param {string} [config.defaultBaseURL] - Default base URL for the API
 21 | 	 * @param {Function} [config.getBaseURL] - Function to determine base URL from params
 22 | 	 * @param {boolean} [config.supportsStructuredOutputs] - Whether provider supports structured outputs
 23 | 	 */
 24 | 	constructor(config) {
 25 | 		super();
 26 | 
 27 | 		if (!config.name) {
 28 | 			throw new Error('Provider name is required');
 29 | 		}
 30 | 		if (!config.apiKeyEnvVar) {
 31 | 			throw new Error('API key environment variable name is required');
 32 | 		}
 33 | 
 34 | 		this.name = config.name;
 35 | 		this.apiKeyEnvVar = config.apiKeyEnvVar;
 36 | 		this.requiresApiKey = config.requiresApiKey !== false; // Default to true
 37 | 		this.defaultBaseURL = config.defaultBaseURL;
 38 | 		this.getBaseURLFromParams = config.getBaseURL;
 39 | 		this.supportsStructuredOutputs = config.supportsStructuredOutputs;
 40 | 	}
 41 | 
 42 | 	/**
 43 | 	 * Returns the environment variable name required for this provider's API key.
 44 | 	 * @returns {string} The environment variable name for the API key
 45 | 	 */
 46 | 	getRequiredApiKeyName() {
 47 | 		return this.apiKeyEnvVar;
 48 | 	}
 49 | 
 50 | 	/**
 51 | 	 * Returns whether this provider requires an API key.
 52 | 	 * @returns {boolean} True if API key is required
 53 | 	 */
 54 | 	isRequiredApiKey() {
 55 | 		return this.requiresApiKey;
 56 | 	}
 57 | 
 58 | 	/**
 59 | 	 * Override auth validation based on requiresApiKey setting
 60 | 	 * @param {object} params - Parameters to validate
 61 | 	 */
 62 | 	validateAuth(params) {
 63 | 		if (this.requiresApiKey && !params.apiKey) {
 64 | 			throw new Error(`${this.name} API key is required`);
 65 | 		}
 66 | 	}
 67 | 
 68 | 	/**
 69 | 	 * Determines the base URL to use for the API.
 70 | 	 * @param {object} params - Client parameters
 71 | 	 * @returns {string|undefined} The base URL to use
 72 | 	 */
 73 | 	getBaseURL(params) {
 74 | 		// If custom baseURL provided in params, use it
 75 | 		if (params.baseURL) {
 76 | 			return params.baseURL;
 77 | 		}
 78 | 
 79 | 		// If provider has a custom getBaseURL function, use it
 80 | 		if (this.getBaseURLFromParams) {
 81 | 			return this.getBaseURLFromParams(params);
 82 | 		}
 83 | 
 84 | 		// Otherwise use default baseURL if available
 85 | 		return this.defaultBaseURL;
 86 | 	}
 87 | 
 88 | 	/**
 89 | 	 * Creates and returns an OpenAI-compatible client instance.
 90 | 	 * @param {object} params - Parameters for client initialization
 91 | 	 * @param {string} [params.apiKey] - API key (required if requiresApiKey is true)
 92 | 	 * @param {string} [params.baseURL] - Optional custom API endpoint
 93 | 	 * @returns {Function} OpenAI-compatible client function
 94 | 	 * @throws {Error} If required parameters are missing or initialization fails
 95 | 	 */
 96 | 	getClient(params) {
 97 | 		try {
 98 | 			const { apiKey } = params;
 99 | 			const fetchImpl = this.createProxyFetch();
100 | 
101 | 			const baseURL = this.getBaseURL(params);
102 | 
103 | 			const clientConfig = {
104 | 				// Provider name for SDK (required, used for logging/debugging)
105 | 				name: this.name.toLowerCase().replace(/[^a-z0-9]/g, '-')
106 | 			};
107 | 
108 | 			// Only include apiKey if provider requires it
109 | 			if (this.requiresApiKey && apiKey) {
110 | 				clientConfig.apiKey = apiKey;
111 | 			}
112 | 
113 | 			// Include baseURL if available
114 | 			if (baseURL) {
115 | 				clientConfig.baseURL = baseURL;
116 | 			}
117 | 
118 | 			// Configure structured outputs support if specified
119 | 			if (this.supportsStructuredOutputs !== undefined) {
120 | 				clientConfig.supportsStructuredOutputs = this.supportsStructuredOutputs;
121 | 			}
122 | 
123 | 			// Add proxy support if available
124 | 			if (fetchImpl) {
125 | 				clientConfig.fetch = fetchImpl;
126 | 			}
127 | 
128 | 			return createOpenAICompatible(clientConfig);
129 | 		} catch (error) {
130 | 			this.handleError('client initialization', error);
131 | 		}
132 | 	}
133 | }
134 | 
```

--------------------------------------------------------------------------------
/packages/tm-core/src/modules/auth/services/supabase-session-storage.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Barebones storage adapter for Supabase Auth sessions
  3 |  *
  4 |  * This is a simple key-value store that lets Supabase manage sessions
  5 |  * without interference. No parsing, no merging, no guessing - just storage.
  6 |  *
  7 |  * Supabase handles:
  8 |  * - Session refresh and token rotation
  9 |  * - Expiry checking
 10 |  * - Token validation
 11 |  *
 12 |  * We handle:
 13 |  * - Simple get/set/remove/clear operations
 14 |  * - Persistence to ~/.taskmaster/session.json with atomic writes via steno
 15 |  */
 16 | 
 17 | import fs from 'fs/promises';
 18 | import fsSync from 'fs';
 19 | import path from 'path';
 20 | import { Writer } from 'steno';
 21 | import type { SupportedStorage } from '@supabase/supabase-js';
 22 | import { getLogger } from '../../../common/logger/index.js';
 23 | 
 24 | const DEFAULT_SESSION_FILE = path.join(
 25 | 	process.env.HOME || process.env.USERPROFILE || '~',
 26 | 	'.taskmaster',
 27 | 	'session.json'
 28 | );
 29 | 
 30 | export class SupabaseSessionStorage implements SupportedStorage {
 31 | 	private storage: Map<string, string> = new Map();
 32 | 	private persistPath: string;
 33 | 	private logger = getLogger('SupabaseSessionStorage');
 34 | 	private writer: Writer;
 35 | 	private initPromise: Promise<void>;
 36 | 
 37 | 	constructor(persistPath: string = DEFAULT_SESSION_FILE) {
 38 | 		this.persistPath = persistPath;
 39 | 		this.writer = new Writer(persistPath);
 40 | 		this.initPromise = this.load();
 41 | 	}
 42 | 
 43 | 	/**
 44 | 	 * Load session data from disk on initialization
 45 | 	 */
 46 | 	private async load(): Promise<void> {
 47 | 		try {
 48 | 			// Ensure directory exists
 49 | 			const dir = path.dirname(this.persistPath);
 50 | 			await fs.mkdir(dir, { recursive: true, mode: 0o700 });
 51 | 
 52 | 			// Try to read existing session
 53 | 			if (fsSync.existsSync(this.persistPath)) {
 54 | 				const data = JSON.parse(await fs.readFile(this.persistPath, 'utf8'));
 55 | 				Object.entries(data).forEach(([k, v]) =>
 56 | 					this.storage.set(k, v as string)
 57 | 				);
 58 | 				this.logger.debug('Loaded session from disk', {
 59 | 					keys: Array.from(this.storage.keys())
 60 | 				});
 61 | 			}
 62 | 		} catch (error) {
 63 | 			this.logger.error('Failed to load session:', error);
 64 | 			// Don't throw - allow starting with fresh session
 65 | 		}
 66 | 	}
 67 | 
 68 | 	/**
 69 | 	 * Persist session data to disk immediately
 70 | 	 * Uses steno for atomic writes with fsync guarantees
 71 | 	 * This prevents race conditions in rapid CLI command sequences
 72 | 	 */
 73 | 	private async persist(): Promise<void> {
 74 | 		try {
 75 | 			const data = Object.fromEntries(this.storage);
 76 | 			const jsonContent = JSON.stringify(data, null, 2);
 77 | 
 78 | 			// steno handles atomic writes with temp file + rename
 79 | 			// and ensures data is flushed to disk
 80 | 			await this.writer.write(jsonContent + '\n');
 81 | 
 82 | 			this.logger.debug('Persisted session to disk (steno)');
 83 | 		} catch (error) {
 84 | 			this.logger.error('Failed to persist session:', error);
 85 | 			// Don't throw - session is still in memory
 86 | 		}
 87 | 	}
 88 | 
 89 | 	/**
 90 | 	 * Get item from storage
 91 | 	 * Supabase will call this to retrieve session data
 92 | 	 * Returns a promise to ensure initialization completes first
 93 | 	 */
 94 | 	async getItem(key: string): Promise<string | null> {
 95 | 		// Wait for initialization to complete
 96 | 		await this.initPromise;
 97 | 
 98 | 		const value = this.storage.get(key) ?? null;
 99 | 		this.logger.debug('getItem called', { key, hasValue: !!value });
100 | 		return value;
101 | 	}
102 | 
103 | 	/**
104 | 	 * Set item in storage
105 | 	 * Supabase will call this to store/update session data
106 | 	 * CRITICAL: This is called during token refresh - must persist immediately
107 | 	 */
108 | 	async setItem(key: string, value: string): Promise<void> {
109 | 		// Wait for initialization to complete
110 | 		await this.initPromise;
111 | 
112 | 		this.logger.debug('setItem called', { key });
113 | 		this.storage.set(key, value);
114 | 
115 | 		// Immediately persist on every write
116 | 		// steno ensures atomic writes with fsync
117 | 		await this.persist();
118 | 	}
119 | 
120 | 	/**
121 | 	 * Remove item from storage
122 | 	 * Supabase will call this during sign out
123 | 	 */
124 | 	async removeItem(key: string): Promise<void> {
125 | 		// Wait for initialization to complete
126 | 		await this.initPromise;
127 | 
128 | 		this.logger.debug('removeItem called', { key });
129 | 		this.storage.delete(key);
130 | 		await this.persist();
131 | 	}
132 | 
133 | 	/**
134 | 	 * Clear all session data
135 | 	 * Useful for complete logout scenarios
136 | 	 */
137 | 	async clear(): Promise<void> {
138 | 		// Wait for initialization to complete
139 | 		await this.initPromise;
140 | 
141 | 		this.logger.debug('clear called');
142 | 		this.storage.clear();
143 | 		await this.persist();
144 | 	}
145 | }
146 | 
```

--------------------------------------------------------------------------------
/packages/ai-sdk-provider-grok-cli/src/message-converter.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Tests for message conversion utilities
  3 |  */
  4 | 
  5 | import { describe, expect, it } from 'vitest';
  6 | import {
  7 | 	convertFromGrokCliResponse,
  8 | 	convertToGrokCliMessages,
  9 | 	createPromptFromMessages,
 10 | 	escapeShellArg
 11 | } from './message-converter.js';
 12 | 
 13 | describe('convertToGrokCliMessages', () => {
 14 | 	it('should convert string content messages', () => {
 15 | 		const messages = [
 16 | 			{ role: 'user', content: 'Hello, world!' },
 17 | 			{ role: 'assistant', content: 'Hi there!' }
 18 | 		];
 19 | 
 20 | 		const result = convertToGrokCliMessages(messages);
 21 | 
 22 | 		expect(result).toEqual([
 23 | 			{ role: 'user', content: 'Hello, world!' },
 24 | 			{ role: 'assistant', content: 'Hi there!' }
 25 | 		]);
 26 | 	});
 27 | 
 28 | 	it('should convert array content messages', () => {
 29 | 		const messages = [
 30 | 			{
 31 | 				role: 'user',
 32 | 				content: [
 33 | 					{ type: 'text', text: 'Hello' },
 34 | 					{ type: 'text', text: 'World' }
 35 | 				]
 36 | 			}
 37 | 		];
 38 | 
 39 | 		const result = convertToGrokCliMessages(messages);
 40 | 
 41 | 		expect(result).toEqual([{ role: 'user', content: 'Hello\nWorld' }]);
 42 | 	});
 43 | 
 44 | 	it('should convert object content messages', () => {
 45 | 		const messages = [
 46 | 			{
 47 | 				role: 'user',
 48 | 				content: { text: 'Hello from object' }
 49 | 			}
 50 | 		];
 51 | 
 52 | 		const result = convertToGrokCliMessages(messages);
 53 | 
 54 | 		expect(result).toEqual([{ role: 'user', content: 'Hello from object' }]);
 55 | 	});
 56 | });
 57 | 
 58 | describe('convertFromGrokCliResponse', () => {
 59 | 	it('should parse JSONL response format', () => {
 60 | 		const responseText = `{"role": "assistant", "content": "Hello there!", "usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15}}`;
 61 | 
 62 | 		const result = convertFromGrokCliResponse(responseText);
 63 | 
 64 | 		expect(result).toEqual({
 65 | 			text: 'Hello there!',
 66 | 			usage: {
 67 | 				promptTokens: 10,
 68 | 				completionTokens: 5,
 69 | 				totalTokens: 15
 70 | 			}
 71 | 		});
 72 | 	});
 73 | 
 74 | 	it('should handle multiple lines in JSONL format', () => {
 75 | 		const responseText = `{"role": "user", "content": "Hello"}
 76 | {"role": "assistant", "content": "Hi there!", "usage": {"prompt_tokens": 5, "completion_tokens": 3}}`;
 77 | 
 78 | 		const result = convertFromGrokCliResponse(responseText);
 79 | 
 80 | 		expect(result).toEqual({
 81 | 			text: 'Hi there!',
 82 | 			usage: {
 83 | 				promptTokens: 5,
 84 | 				completionTokens: 3,
 85 | 				totalTokens: 0
 86 | 			}
 87 | 		});
 88 | 	});
 89 | 
 90 | 	it('should fallback to raw text when parsing fails', () => {
 91 | 		const responseText = 'Invalid JSON response';
 92 | 
 93 | 		const result = convertFromGrokCliResponse(responseText);
 94 | 
 95 | 		expect(result).toEqual({
 96 | 			text: 'Invalid JSON response',
 97 | 			usage: undefined
 98 | 		});
 99 | 	});
100 | });
101 | 
102 | describe('createPromptFromMessages', () => {
103 | 	it('should create formatted prompt from messages', () => {
104 | 		const messages = [
105 | 			{ role: 'system', content: 'You are a helpful assistant.' },
106 | 			{ role: 'user', content: 'What is 2+2?' },
107 | 			{ role: 'assistant', content: '2+2 equals 4.' }
108 | 		];
109 | 
110 | 		const result = createPromptFromMessages(messages);
111 | 
112 | 		expect(result).toBe(
113 | 			'System: You are a helpful assistant.\n\nUser: What is 2+2?\n\nAssistant: 2+2 equals 4.'
114 | 		);
115 | 	});
116 | 
117 | 	it('should handle custom role names', () => {
118 | 		const messages = [{ role: 'custom', content: 'Custom message' }];
119 | 
120 | 		const result = createPromptFromMessages(messages);
121 | 
122 | 		expect(result).toBe('custom: Custom message');
123 | 	});
124 | 
125 | 	it('should trim whitespace from message content', () => {
126 | 		const messages = [
127 | 			{ role: 'user', content: '  Hello with spaces  ' },
128 | 			{ role: 'assistant', content: '\n\nResponse with newlines\n\n' }
129 | 		];
130 | 
131 | 		const result = createPromptFromMessages(messages);
132 | 
133 | 		expect(result).toBe(
134 | 			'User: Hello with spaces\n\nAssistant: Response with newlines'
135 | 		);
136 | 	});
137 | });
138 | 
139 | describe('escapeShellArg', () => {
140 | 	it('should escape single quotes', () => {
141 | 		const arg = "It's a test";
142 | 		const result = escapeShellArg(arg);
143 | 		expect(result).toBe("'It'\\''s a test'");
144 | 	});
145 | 
146 | 	it('should handle strings without special characters', () => {
147 | 		const arg = 'simple string';
148 | 		const result = escapeShellArg(arg);
149 | 		expect(result).toBe("'simple string'");
150 | 	});
151 | 
152 | 	it('should convert non-string values to strings', () => {
153 | 		const arg = 123;
154 | 		const result = escapeShellArg(arg);
155 | 		expect(result).toBe("'123'");
156 | 	});
157 | 
158 | 	it('should handle empty strings', () => {
159 | 		const arg = '';
160 | 		const result = escapeShellArg(arg);
161 | 		expect(result).toBe("''");
162 | 	});
163 | });
164 | 
```

--------------------------------------------------------------------------------
/packages/tm-core/src/modules/config/services/environment-config-provider.service.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * @fileoverview Environment Configuration Provider
  3 |  * Extracts configuration from environment variables
  4 |  */
  5 | 
  6 | import type { PartialConfiguration } from '../../../common/interfaces/configuration.interface.js';
  7 | import { getLogger } from '../../../common/logger/index.js';
  8 | 
  9 | /**
 10 |  * Environment variable mapping definition
 11 |  */
 12 | interface EnvMapping {
 13 | 	/** Environment variable name */
 14 | 	env: string;
 15 | 	/** Path in configuration object */
 16 | 	path: readonly string[];
 17 | 	/** Optional validator function */
 18 | 	validate?: (value: string) => boolean;
 19 | 	/** Whether this is runtime state (not configuration) */
 20 | 	isRuntimeState?: boolean;
 21 | }
 22 | 
 23 | /**
 24 |  * EnvironmentConfigProvider extracts configuration from environment variables
 25 |  * Single responsibility: Environment variable configuration extraction
 26 |  */
 27 | export class EnvironmentConfigProvider {
 28 | 	private readonly logger = getLogger('EnvironmentConfigProvider');
 29 | 
 30 | 	/**
 31 | 	 * Default environment variable mappings
 32 | 	 */
 33 | 	private static readonly DEFAULT_MAPPINGS: EnvMapping[] = [
 34 | 		{
 35 | 			env: 'TASKMASTER_STORAGE_TYPE',
 36 | 			path: ['storage', 'type'],
 37 | 			validate: (v: string) => ['file', 'api'].includes(v)
 38 | 		},
 39 | 		{ env: 'TASKMASTER_API_ENDPOINT', path: ['storage', 'apiEndpoint'] },
 40 | 		{ env: 'TASKMASTER_API_TOKEN', path: ['storage', 'apiAccessToken'] },
 41 | 		{ env: 'TASKMASTER_MODEL_MAIN', path: ['models', 'main'] },
 42 | 		{ env: 'TASKMASTER_MODEL_RESEARCH', path: ['models', 'research'] },
 43 | 		{ env: 'TASKMASTER_MODEL_FALLBACK', path: ['models', 'fallback'] },
 44 | 		{
 45 | 			env: 'TASKMASTER_RESPONSE_LANGUAGE',
 46 | 			path: ['custom', 'responseLanguage']
 47 | 		}
 48 | 	];
 49 | 
 50 | 	/**
 51 | 	 * Runtime state mappings (separate from configuration)
 52 | 	 */
 53 | 	private static readonly RUNTIME_STATE_MAPPINGS: EnvMapping[] = [
 54 | 		{ env: 'TASKMASTER_TAG', path: ['activeTag'], isRuntimeState: true }
 55 | 	];
 56 | 
 57 | 	private mappings: EnvMapping[];
 58 | 
 59 | 	constructor(customMappings?: EnvMapping[]) {
 60 | 		this.mappings = customMappings || [
 61 | 			...EnvironmentConfigProvider.DEFAULT_MAPPINGS,
 62 | 			...EnvironmentConfigProvider.RUNTIME_STATE_MAPPINGS
 63 | 		];
 64 | 	}
 65 | 
 66 | 	/**
 67 | 	 * Load configuration from environment variables
 68 | 	 */
 69 | 	loadConfig(): PartialConfiguration {
 70 | 		const config: PartialConfiguration = {};
 71 | 
 72 | 		for (const mapping of this.mappings) {
 73 | 			// Skip runtime state variables
 74 | 			if (mapping.isRuntimeState) continue;
 75 | 
 76 | 			const value = process.env[mapping.env];
 77 | 			if (!value) continue;
 78 | 
 79 | 			// Validate value if validator is provided
 80 | 			if (mapping.validate && !mapping.validate(value)) {
 81 | 				this.logger.warn(`Invalid value for ${mapping.env}: ${value}`);
 82 | 				continue;
 83 | 			}
 84 | 
 85 | 			// Set the value in the config object
 86 | 			this.setNestedProperty(config, mapping.path, value);
 87 | 		}
 88 | 
 89 | 		return config;
 90 | 	}
 91 | 
 92 | 	/**
 93 | 	 * Get runtime state from environment variables
 94 | 	 */
 95 | 	getRuntimeState(): Record<string, string> {
 96 | 		const state: Record<string, string> = {};
 97 | 
 98 | 		for (const mapping of this.mappings) {
 99 | 			if (!mapping.isRuntimeState) continue;
100 | 
101 | 			const value = process.env[mapping.env];
102 | 			if (value) {
103 | 				const key = mapping.path[mapping.path.length - 1];
104 | 				state[key] = value;
105 | 			}
106 | 		}
107 | 
108 | 		return state;
109 | 	}
110 | 
111 | 	/**
112 | 	 * Helper to set a nested property in an object
113 | 	 */
114 | 	private setNestedProperty(
115 | 		obj: any,
116 | 		path: readonly string[],
117 | 		value: any
118 | 	): void {
119 | 		const lastKey = path[path.length - 1];
120 | 		const keys = path.slice(0, -1);
121 | 
122 | 		let current = obj;
123 | 		for (const key of keys) {
124 | 			if (!current[key]) {
125 | 				current[key] = {};
126 | 			}
127 | 			current = current[key];
128 | 		}
129 | 
130 | 		current[lastKey] = value;
131 | 	}
132 | 
133 | 	/**
134 | 	 * Check if an environment variable is set
135 | 	 */
136 | 	hasEnvVar(envName: string): boolean {
137 | 		return envName in process.env && process.env[envName] !== undefined;
138 | 	}
139 | 
140 | 	/**
141 | 	 * Get all environment variables that match our prefix
142 | 	 */
143 | 	getAllTaskmasterEnvVars(): Record<string, string> {
144 | 		const vars: Record<string, string> = {};
145 | 		const prefix = 'TASKMASTER_';
146 | 
147 | 		for (const [key, value] of Object.entries(process.env)) {
148 | 			if (key.startsWith(prefix) && value !== undefined) {
149 | 				vars[key] = value;
150 | 			}
151 | 		}
152 | 
153 | 		return vars;
154 | 	}
155 | 
156 | 	/**
157 | 	 * Add a custom mapping
158 | 	 */
159 | 	addMapping(mapping: EnvMapping): void {
160 | 		this.mappings.push(mapping);
161 | 	}
162 | 
163 | 	/**
164 | 	 * Get current mappings
165 | 	 */
166 | 	getMappings(): EnvMapping[] {
167 | 		return [...this.mappings];
168 | 	}
169 | }
170 | 
```

--------------------------------------------------------------------------------
/.taskmaster/docs/tdd-workflow-phase-0-spike.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Phase 0: Spike - Autonomous TDD Workflow ✅ COMPLETE
  2 | 
  3 | ## Objective
  4 | Validate feasibility and build foundational understanding before full implementation.
  5 | 
  6 | ## Status
  7 | **COMPLETED** - All deliverables implemented and validated.
  8 | 
  9 | See `apps/cli/src/commands/autopilot.command.ts` for implementation.
 10 | 
 11 | ## Scope
 12 | - Implement CLI skeleton `tm autopilot` with dry-run mode
 13 | - Show planned steps from a real task with subtasks
 14 | - Detect test runner from package.json
 15 | - Detect git state and render preflight report
 16 | 
 17 | ## Deliverables
 18 | 
 19 | ### 1. CLI Command Skeleton
 20 | - Create `apps/cli/src/commands/autopilot.command.ts`
 21 | - Support `tm autopilot <taskId>` command
 22 | - Implement `--dry-run` flag
 23 | - Basic help text and usage information
 24 | 
 25 | ### 2. Preflight Detection System
 26 | - Detect test runner from package.json (npm test, pnpm test, etc.)
 27 | - Check git working tree state (clean/dirty)
 28 | - Validate required tools are available (git, gh, node/npm)
 29 | - Detect default branch
 30 | 
 31 | ### 3. Dry-Run Execution Plan Display
 32 | Display planned execution for a task including:
 33 | - Preflight checks status
 34 | - Branch name that would be created
 35 | - Tag that would be set
 36 | - List of subtasks in execution order
 37 | - For each subtask:
 38 |   - RED phase: test file that would be created
 39 |   - GREEN phase: implementation files that would be modified
 40 |   - COMMIT: commit message that would be used
 41 | - Finalization steps: test suite run, coverage check, push, PR creation
 42 | 
 43 | ### 4. Task Loading & Validation
 44 | - Load task from TaskMaster state
 45 | - Validate task exists and has subtasks
 46 | - If no subtasks, show message about needing to expand first
 47 | - Show dependency order for subtasks
 48 | 
 49 | ## Example Output
 50 | 
 51 | ```bash
 52 | $ tm autopilot 42 --dry-run
 53 | 
 54 | Autopilot Plan for Task #42 [analytics]: User metrics tracking
 55 | ─────────────────────────────────────────────────────────────
 56 | 
 57 | Preflight Checks:
 58 |   ✓ Working tree is clean
 59 |   ✓ Test command detected: npm test
 60 |   ✓ Tools available: git, gh, node, npm
 61 |   ✓ Current branch: main (will create new branch)
 62 |   ✓ Task has 3 subtasks ready to execute
 63 | 
 64 | Branch & Tag:
 65 |   → Will create branch: analytics/task-42-user-metrics
 66 |   → Will set active tag: analytics
 67 | 
 68 | Execution Plan (3 subtasks):
 69 | 
 70 |   1. Subtask 42.1: Add metrics schema
 71 |      RED:    Generate tests → src/__tests__/schema.test.js
 72 |      GREEN:  Implement code → src/schema.js
 73 |      COMMIT: "feat(metrics): add metrics schema (task 42.1)"
 74 | 
 75 |   2. Subtask 42.2: Add collection endpoint [depends on 42.1]
 76 |      RED:    Generate tests → src/api/__tests__/metrics.test.js
 77 |      GREEN:  Implement code → src/api/metrics.js
 78 |      COMMIT: "feat(metrics): add collection endpoint (task 42.2)"
 79 | 
 80 |   3. Subtask 42.3: Add dashboard widget [depends on 42.2]
 81 |      RED:    Generate tests → src/components/__tests__/MetricsWidget.test.jsx
 82 |      GREEN:  Implement code → src/components/MetricsWidget.jsx
 83 |      COMMIT: "feat(metrics): add dashboard widget (task 42.3)"
 84 | 
 85 | Finalization:
 86 |   → Run full test suite with coverage (threshold: 80%)
 87 |   → Push branch to origin (will confirm)
 88 |   → Create PR targeting main
 89 | 
 90 | Estimated commits: 3
 91 | Estimated duration: ~20-30 minutes (depends on implementation complexity)
 92 | 
 93 | Run without --dry-run to execute.
 94 | ```
 95 | 
 96 | ## Success Criteria
 97 | - Dry-run output is clear and matches expected workflow
 98 | - Preflight detection works correctly on the project repo
 99 | - Task loading integrates with existing TaskMaster state
100 | - No actual git operations or file modifications occur in dry-run mode
101 | 
102 | ## Out of Scope
103 | - Actual test generation
104 | - Actual code implementation
105 | - Git operations (branch creation, commits, push)
106 | - PR creation
107 | - Test execution
108 | 
109 | ## Implementation Notes
110 | - Reuse existing `TaskService` from `packages/tm-core`
111 | - Use existing git utilities from `scripts/modules/utils/git-utils.js`
112 | - Load task/subtask data from `.taskmaster/tasks/tasks.json`
113 | - Detect test command via package.json → scripts.test field
114 | 
115 | ## Dependencies
116 | - Existing TaskMaster CLI structure
117 | - Existing task storage format
118 | - Git utilities
119 | 
120 | ## Estimated Effort
121 | 2-3 days
122 | 
123 | ## Validation
124 | Test dry-run mode with:
125 | - Task with 1 subtask
126 | - Task with multiple subtasks
127 | - Task with dependencies between subtasks
128 | - Task without subtasks (should show warning)
129 | - Dirty git working tree (should warn)
130 | - Missing tools (should error with helpful message)
131 | 
```

--------------------------------------------------------------------------------
/src/profiles/cursor.js:
--------------------------------------------------------------------------------

```javascript
  1 | // Cursor conversion profile for rule-transformer
  2 | import path from 'path';
  3 | import fs from 'fs';
  4 | import { log } from '../../scripts/modules/utils.js';
  5 | import { createProfile } from './base-profile.js';
  6 | 
  7 | // Helper copy; use cpSync when available, fallback to manual recursion
  8 | function copyRecursiveSync(src, dest) {
  9 | 	if (fs.cpSync) {
 10 | 		try {
 11 | 			fs.cpSync(src, dest, { recursive: true, force: true });
 12 | 			return;
 13 | 		} catch (err) {
 14 | 			throw new Error(`Failed to copy ${src} to ${dest}: ${err.message}`);
 15 | 		}
 16 | 	}
 17 | 	const exists = fs.existsSync(src);
 18 | 	let stats = null;
 19 | 	let isDirectory = false;
 20 | 
 21 | 	if (exists) {
 22 | 		try {
 23 | 			stats = fs.statSync(src);
 24 | 			isDirectory = stats.isDirectory();
 25 | 		} catch (err) {
 26 | 			// Handle TOCTOU race condition - treat as non-existent/not-a-directory
 27 | 			isDirectory = false;
 28 | 		}
 29 | 	}
 30 | 
 31 | 	if (isDirectory) {
 32 | 		try {
 33 | 			if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
 34 | 			for (const child of fs.readdirSync(src)) {
 35 | 				copyRecursiveSync(path.join(src, child), path.join(dest, child));
 36 | 			}
 37 | 		} catch (err) {
 38 | 			throw new Error(
 39 | 				`Failed to copy directory ${src} to ${dest}: ${err.message}`
 40 | 			);
 41 | 		}
 42 | 	} else {
 43 | 		try {
 44 | 			// ensure parent exists for file copies
 45 | 			fs.mkdirSync(path.dirname(dest), { recursive: true });
 46 | 			fs.copyFileSync(src, dest);
 47 | 		} catch (err) {
 48 | 			throw new Error(`Failed to copy file ${src} to ${dest}: ${err.message}`);
 49 | 		}
 50 | 	}
 51 | }
 52 | 
 53 | // Helper function to recursively remove directory
 54 | function removeDirectoryRecursive(dirPath) {
 55 | 	if (fs.existsSync(dirPath)) {
 56 | 		try {
 57 | 			fs.rmSync(dirPath, { recursive: true, force: true });
 58 | 			return true;
 59 | 		} catch (err) {
 60 | 			log('error', `Failed to remove directory ${dirPath}: ${err.message}`);
 61 | 			return false;
 62 | 		}
 63 | 	}
 64 | 	return true;
 65 | }
 66 | 
 67 | // Resolve the Cursor profile directory from either project root, profile root, or rules dir
 68 | function resolveCursorProfileDir(baseDir) {
 69 | 	const base = path.basename(baseDir);
 70 | 	// If called with .../.cursor/rules -> return .../.cursor
 71 | 	if (base === 'rules' && path.basename(path.dirname(baseDir)) === '.cursor') {
 72 | 		return path.dirname(baseDir);
 73 | 	}
 74 | 	// If called with .../.cursor -> return as-is
 75 | 	if (base === '.cursor') return baseDir;
 76 | 	// Otherwise assume project root and append .cursor
 77 | 	return path.join(baseDir, '.cursor');
 78 | }
 79 | 
 80 | // Lifecycle functions for Cursor profile
 81 | function onAddRulesProfile(targetDir, assetsDir) {
 82 | 	// Copy commands directory recursively
 83 | 	const commandsSourceDir = path.join(assetsDir, 'claude', 'commands');
 84 | 	const profileDir = resolveCursorProfileDir(targetDir);
 85 | 	const commandsDestDir = path.join(profileDir, 'commands');
 86 | 
 87 | 	if (!fs.existsSync(commandsSourceDir)) {
 88 | 		log(
 89 | 			'warn',
 90 | 			`[Cursor] Source commands directory does not exist: ${commandsSourceDir}`
 91 | 		);
 92 | 		return;
 93 | 	}
 94 | 
 95 | 	try {
 96 | 		// Ensure fresh state to avoid stale command files
 97 | 		try {
 98 | 			fs.rmSync(commandsDestDir, { recursive: true, force: true });
 99 | 			log(
100 | 				'debug',
101 | 				`[Cursor] Removed existing commands directory: ${commandsDestDir}`
102 | 			);
103 | 		} catch (deleteErr) {
104 | 			// Directory might not exist, which is fine
105 | 			log(
106 | 				'debug',
107 | 				`[Cursor] Commands directory did not exist or could not be removed: ${deleteErr.message}`
108 | 			);
109 | 		}
110 | 
111 | 		copyRecursiveSync(commandsSourceDir, commandsDestDir);
112 | 		log('debug', `[Cursor] Copied commands directory to ${commandsDestDir}`);
113 | 	} catch (err) {
114 | 		log(
115 | 			'error',
116 | 			`[Cursor] An error occurred during commands copy: ${err.message}`
117 | 		);
118 | 	}
119 | }
120 | 
121 | function onRemoveRulesProfile(targetDir) {
122 | 	// Remove .cursor/commands directory recursively
123 | 	const profileDir = resolveCursorProfileDir(targetDir);
124 | 	const commandsDir = path.join(profileDir, 'commands');
125 | 	if (removeDirectoryRecursive(commandsDir)) {
126 | 		log(
127 | 			'debug',
128 | 			`[Cursor] Ensured commands directory removed at ${commandsDir}`
129 | 		);
130 | 	}
131 | }
132 | 
133 | // Create and export cursor profile using the base factory
134 | export const cursorProfile = createProfile({
135 | 	name: 'cursor',
136 | 	displayName: 'Cursor',
137 | 	url: 'cursor.so',
138 | 	docsUrl: 'docs.cursor.com',
139 | 	targetExtension: '.mdc', // Cursor keeps .mdc extension
140 | 	supportsRulesSubdirectories: true,
141 | 	onAdd: onAddRulesProfile,
142 | 	onRemove: onRemoveRulesProfile
143 | });
144 | 
145 | // Export lifecycle functions separately to avoid naming conflicts
146 | export { onAddRulesProfile, onRemoveRulesProfile };
147 | 
```

--------------------------------------------------------------------------------
/docs/scripts/models-json-to-markdown.js:
--------------------------------------------------------------------------------

```javascript
  1 | import fs from 'fs';
  2 | import path from 'path';
  3 | import { fileURLToPath } from 'url';
  4 | 
  5 | const __filename = fileURLToPath(import.meta.url);
  6 | const __dirname = path.dirname(__filename);
  7 | 
  8 | const supportedModelsPath = path.join(
  9 | 	__dirname,
 10 | 	'..',
 11 | 	'modules',
 12 | 	'supported-models.json'
 13 | );
 14 | const outputMarkdownPath = path.join(
 15 | 	__dirname,
 16 | 	'..',
 17 | 	'..',
 18 | 	'docs',
 19 | 	'models.md'
 20 | );
 21 | 
 22 | function formatCost(cost) {
 23 | 	if (cost === null || cost === undefined) {
 24 | 		return '—';
 25 | 	}
 26 | 	return cost;
 27 | }
 28 | 
 29 | function formatSweScore(score) {
 30 | 	if (score === null || score === undefined || score === 0) {
 31 | 		return '—';
 32 | 	}
 33 | 	return score.toString();
 34 | }
 35 | 
 36 | function generateMarkdownTable(title, models) {
 37 | 	if (!models || models.length === 0) {
 38 | 		return `## ${title}\n\nNo models in this category.\n\n`;
 39 | 	}
 40 | 	let table = `## ${title}\n\n`;
 41 | 	table += '| Provider | Model Name | SWE Score | Input Cost | Output Cost |\n';
 42 | 	table += '|---|---|---|---|---|\n';
 43 | 	models.forEach((model) => {
 44 | 		table += `| ${model.provider} | ${model.modelName} | ${formatSweScore(model.sweScore)} | ${formatCost(model.inputCost)} | ${formatCost(model.outputCost)} |\n`;
 45 | 	});
 46 | 	table += '\n';
 47 | 	return table;
 48 | }
 49 | 
 50 | function generateUnsupportedTable(models) {
 51 | 	if (!models || models.length === 0) {
 52 | 		return '## Unsupported Models\n\nNo unsupported models found.\n\n';
 53 | 	}
 54 | 	let table = '## Unsupported Models\n\n';
 55 | 	table += '| Provider | Model Name | Reason |\n';
 56 | 	table += '|---|---|---|\n';
 57 | 	models.forEach((model) => {
 58 | 		table += `| ${model.provider} | ${model.modelName} | ${model.reason || '—'} |\n`;
 59 | 	});
 60 | 	table += '\n';
 61 | 	return table;
 62 | }
 63 | 
 64 | function main() {
 65 | 	try {
 66 | 		const correctSupportedModelsPath = path.join(
 67 | 			__dirname,
 68 | 			'..',
 69 | 			'..',
 70 | 			'scripts',
 71 | 			'modules',
 72 | 			'supported-models.json'
 73 | 		);
 74 | 		const correctOutputMarkdownPath = path.join(__dirname, '..', 'models.md');
 75 | 
 76 | 		const supportedModelsContent = fs.readFileSync(
 77 | 			correctSupportedModelsPath,
 78 | 			'utf8'
 79 | 		);
 80 | 		const supportedModels = JSON.parse(supportedModelsContent);
 81 | 
 82 | 		const mainModels = [];
 83 | 		const researchModels = [];
 84 | 		const fallbackModels = [];
 85 | 		const unsupportedModels = [];
 86 | 
 87 | 		for (const provider in supportedModels) {
 88 | 			if (Object.hasOwnProperty.call(supportedModels, provider)) {
 89 | 				const models = supportedModels[provider];
 90 | 				models.forEach((model) => {
 91 | 					const isSupported = model.supported !== false; // default to true if missing
 92 | 					if (isSupported) {
 93 | 						const modelEntry = {
 94 | 							provider: provider,
 95 | 							modelName: model.id,
 96 | 							sweScore: model.swe_score,
 97 | 							inputCost: model.cost_per_1m_tokens
 98 | 								? model.cost_per_1m_tokens.input
 99 | 								: null,
100 | 							outputCost: model.cost_per_1m_tokens
101 | 								? model.cost_per_1m_tokens.output
102 | 								: null
103 | 						};
104 | 						if (model.allowed_roles && model.allowed_roles.includes('main')) {
105 | 							mainModels.push(modelEntry);
106 | 						}
107 | 						if (
108 | 							model.allowed_roles &&
109 | 							model.allowed_roles.includes('research')
110 | 						) {
111 | 							researchModels.push(modelEntry);
112 | 						}
113 | 						if (
114 | 							model.allowed_roles &&
115 | 							model.allowed_roles.includes('fallback')
116 | 						) {
117 | 							fallbackModels.push(modelEntry);
118 | 						}
119 | 					} else {
120 | 						unsupportedModels.push({
121 | 							provider: provider,
122 | 							modelName: model.id,
123 | 							reason: model.reason || 'Not specified'
124 | 						});
125 | 					}
126 | 				});
127 | 			}
128 | 		}
129 | 
130 | 		const date = new Date();
131 | 		const monthNames = [
132 | 			'January',
133 | 			'February',
134 | 			'March',
135 | 			'April',
136 | 			'May',
137 | 			'June',
138 | 			'July',
139 | 			'August',
140 | 			'September',
141 | 			'October',
142 | 			'November',
143 | 			'December'
144 | 		];
145 | 		const formattedDate = `${monthNames[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
146 | 
147 | 		let markdownContent = `# Available Models as of ${formattedDate}\n\n`;
148 | 		markdownContent += generateMarkdownTable('Main Models', mainModels);
149 | 		markdownContent += generateMarkdownTable('Research Models', researchModels);
150 | 		markdownContent += generateMarkdownTable('Fallback Models', fallbackModels);
151 | 		markdownContent += generateUnsupportedTable(unsupportedModels);
152 | 
153 | 		fs.writeFileSync(correctOutputMarkdownPath, markdownContent, 'utf8');
154 | 		console.log(`Successfully updated ${correctOutputMarkdownPath}`);
155 | 	} catch (error) {
156 | 		console.error('Error transforming models.json to models.md:', error);
157 | 		process.exit(1);
158 | 	}
159 | }
160 | 
161 | main();
162 | 
```

--------------------------------------------------------------------------------
/tests/unit/prompts/expand-task-prompt.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | import { PromptManager } from '../../../scripts/modules/prompt-manager.js';
  2 | import { ExpandTaskResponseSchema } from '../../../src/schemas/expand-task.js';
  3 | import { SubtaskSchema } from '../../../src/schemas/base-schemas.js';
  4 | 
  5 | describe('expand-task prompt template', () => {
  6 | 	let promptManager;
  7 | 
  8 | 	beforeEach(() => {
  9 | 		promptManager = new PromptManager();
 10 | 	});
 11 | 
 12 | 	const testTask = {
 13 | 		id: 1,
 14 | 		title: 'Setup AWS Infrastructure',
 15 | 		description: 'Provision core AWS services',
 16 | 		details: 'Create VPC, subnets, and security groups'
 17 | 	};
 18 | 
 19 | 	const baseParams = {
 20 | 		task: testTask,
 21 | 		subtaskCount: 3,
 22 | 		nextSubtaskId: 1,
 23 | 		additionalContext: '',
 24 | 		complexityReasoningContext: '',
 25 | 		gatheredContext: '',
 26 | 		useResearch: false,
 27 | 		expansionPrompt: undefined
 28 | 	};
 29 | 
 30 | 	test('default variant includes task context', () => {
 31 | 		const { userPrompt } = promptManager.loadPrompt(
 32 | 			'expand-task',
 33 | 			baseParams,
 34 | 			'default'
 35 | 		);
 36 | 
 37 | 		expect(userPrompt).toContain(testTask.title);
 38 | 		expect(userPrompt).toContain(testTask.description);
 39 | 		expect(userPrompt).toContain(testTask.details);
 40 | 		expect(userPrompt).toContain('Task ID: 1');
 41 | 	});
 42 | 
 43 | 	test('research variant includes task context', () => {
 44 | 		const params = { ...baseParams, useResearch: true };
 45 | 		const { userPrompt } = promptManager.loadPrompt(
 46 | 			'expand-task',
 47 | 			params,
 48 | 			'research'
 49 | 		);
 50 | 
 51 | 		expect(userPrompt).toContain(testTask.title);
 52 | 		expect(userPrompt).toContain(testTask.description);
 53 | 		expect(userPrompt).toContain(testTask.details);
 54 | 		expect(userPrompt).toContain('Parent Task:');
 55 | 		expect(userPrompt).toContain('ID: 1');
 56 | 	});
 57 | 
 58 | 	test('complexity-report variant includes task context', () => {
 59 | 		const params = {
 60 | 			...baseParams,
 61 | 			expansionPrompt: 'Focus on security best practices',
 62 | 			complexityReasoningContext: 'High complexity due to security requirements'
 63 | 		};
 64 | 		const { userPrompt } = promptManager.loadPrompt(
 65 | 			'expand-task',
 66 | 			params,
 67 | 			'complexity-report'
 68 | 		);
 69 | 
 70 | 		// The fix ensures task context is included
 71 | 		expect(userPrompt).toContain('Parent Task:');
 72 | 		expect(userPrompt).toContain(`ID: ${testTask.id}`);
 73 | 		expect(userPrompt).toContain(`Title: ${testTask.title}`);
 74 | 		expect(userPrompt).toContain(`Description: ${testTask.description}`);
 75 | 		expect(userPrompt).toContain(`Current details: ${testTask.details}`);
 76 | 
 77 | 		// Also includes the expansion prompt
 78 | 		expect(userPrompt).toContain(params.expansionPrompt);
 79 | 		expect(userPrompt).toContain(params.complexityReasoningContext);
 80 | 	});
 81 | 
 82 | 	test('ExpandTaskResponseSchema defines required subtask fields', () => {
 83 | 		// Test the schema definition directly instead of weak substring matching
 84 | 		const schema = ExpandTaskResponseSchema;
 85 | 		const subtasksSchema = schema.shape.subtasks;
 86 | 		const subtaskSchema = subtasksSchema.element;
 87 | 
 88 | 		// Verify the schema has the required fields
 89 | 		expect(subtaskSchema).toBe(SubtaskSchema);
 90 | 		expect(SubtaskSchema.shape).toHaveProperty('id');
 91 | 		expect(SubtaskSchema.shape).toHaveProperty('title');
 92 | 		expect(SubtaskSchema.shape).toHaveProperty('description');
 93 | 		expect(SubtaskSchema.shape).toHaveProperty('dependencies');
 94 | 		expect(SubtaskSchema.shape).toHaveProperty('details');
 95 | 		expect(SubtaskSchema.shape).toHaveProperty('status');
 96 | 		expect(SubtaskSchema.shape).toHaveProperty('testStrategy');
 97 | 	});
 98 | 
 99 | 	test('complexity-report variant fails without task context regression test', () => {
100 | 		// This test ensures we don't regress to the old behavior where
101 | 		// complexity-report variant only used expansionPrompt without task context
102 | 		const params = {
103 | 			...baseParams,
104 | 			expansionPrompt: 'Generic expansion prompt'
105 | 		};
106 | 
107 | 		const { userPrompt } = promptManager.loadPrompt(
108 | 			'expand-task',
109 | 			params,
110 | 			'complexity-report'
111 | 		);
112 | 
113 | 		// Count occurrences of task-specific content
114 | 		const titleOccurrences = (
115 | 			userPrompt.match(new RegExp(testTask.title, 'g')) || []
116 | 		).length;
117 | 		const descriptionOccurrences = (
118 | 			userPrompt.match(new RegExp(testTask.description, 'g')) || []
119 | 		).length;
120 | 
121 | 		// Should have at least one occurrence of title and description
122 | 		expect(titleOccurrences).toBeGreaterThanOrEqual(1);
123 | 		expect(descriptionOccurrences).toBeGreaterThanOrEqual(1);
124 | 
125 | 		// Should not be ONLY the expansion prompt
126 | 		expect(userPrompt.length).toBeGreaterThan(
127 | 			params.expansionPrompt.length + 100
128 | 		);
129 | 	});
130 | });
131 | 
```

--------------------------------------------------------------------------------
/tests/unit/profiles/kiro-integration.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | import { jest } from '@jest/globals';
  2 | import fs from 'fs';
  3 | import path from 'path';
  4 | import os from 'os';
  5 | 
  6 | // Mock external modules
  7 | jest.mock('child_process', () => ({
  8 | 	execSync: jest.fn()
  9 | }));
 10 | 
 11 | // Mock console methods
 12 | jest.mock('console', () => ({
 13 | 	log: jest.fn(),
 14 | 	info: jest.fn(),
 15 | 	warn: jest.fn(),
 16 | 	error: jest.fn(),
 17 | 	clear: jest.fn()
 18 | }));
 19 | 
 20 | describe('Kiro Integration', () => {
 21 | 	let tempDir;
 22 | 
 23 | 	beforeEach(() => {
 24 | 		jest.clearAllMocks();
 25 | 
 26 | 		// Create a temporary directory for testing
 27 | 		tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-master-test-'));
 28 | 
 29 | 		// Spy on fs methods
 30 | 		jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
 31 | 		jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
 32 | 			if (filePath.toString().includes('mcp.json')) {
 33 | 				return JSON.stringify({ mcpServers: {} }, null, 2);
 34 | 			}
 35 | 			return '{}';
 36 | 		});
 37 | 		jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
 38 | 		jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
 39 | 	});
 40 | 
 41 | 	afterEach(() => {
 42 | 		// Clean up the temporary directory
 43 | 		try {
 44 | 			fs.rmSync(tempDir, { recursive: true, force: true });
 45 | 		} catch (err) {
 46 | 			console.error(`Error cleaning up: ${err.message}`);
 47 | 		}
 48 | 	});
 49 | 
 50 | 	// Test function that simulates the createProjectStructure behavior for Kiro files
 51 | 	function mockCreateKiroStructure() {
 52 | 		// This function simulates the actual kiro profile creation logic
 53 | 		// It explicitly calls the mocked fs methods to ensure consistency with the test environment
 54 | 
 55 | 		// Simulate directory creation calls - these will call the mocked mkdirSync
 56 | 		fs.mkdirSync(path.join(tempDir, '.kiro'), { recursive: true });
 57 | 		fs.mkdirSync(path.join(tempDir, '.kiro', 'steering'), { recursive: true });
 58 | 		fs.mkdirSync(path.join(tempDir, '.kiro', 'settings'), { recursive: true });
 59 | 
 60 | 		// Create MCP config file at .kiro/settings/mcp.json
 61 | 		// This will call the mocked writeFileSync
 62 | 		fs.writeFileSync(
 63 | 			path.join(tempDir, '.kiro', 'settings', 'mcp.json'),
 64 | 			JSON.stringify({ mcpServers: {} }, null, 2)
 65 | 		);
 66 | 
 67 | 		// Create kiro rule files in steering directory
 68 | 		// All these will call the mocked writeFileSync
 69 | 		fs.writeFileSync(
 70 | 			path.join(tempDir, '.kiro', 'steering', 'kiro_rules.md'),
 71 | 			'# Kiro Rules\n\nKiro-specific rules and instructions.'
 72 | 		);
 73 | 		fs.writeFileSync(
 74 | 			path.join(tempDir, '.kiro', 'steering', 'dev_workflow.md'),
 75 | 			'# Development Workflow\n\nDevelopment workflow instructions.'
 76 | 		);
 77 | 		fs.writeFileSync(
 78 | 			path.join(tempDir, '.kiro', 'steering', 'self_improve.md'),
 79 | 			'# Self Improvement\n\nSelf improvement guidelines.'
 80 | 		);
 81 | 		fs.writeFileSync(
 82 | 			path.join(tempDir, '.kiro', 'steering', 'taskmaster.md'),
 83 | 			'# Task Master\n\nTask Master integration instructions.'
 84 | 		);
 85 | 	}
 86 | 
 87 | 	test('creates all required .kiro directories', () => {
 88 | 		// Act
 89 | 		mockCreateKiroStructure();
 90 | 
 91 | 		// Assert
 92 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(tempDir, '.kiro'), {
 93 | 			recursive: true
 94 | 		});
 95 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
 96 | 			path.join(tempDir, '.kiro', 'steering'),
 97 | 			{
 98 | 				recursive: true
 99 | 			}
100 | 		);
101 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
102 | 			path.join(tempDir, '.kiro', 'settings'),
103 | 			{
104 | 				recursive: true
105 | 			}
106 | 		);
107 | 	});
108 | 
109 | 	test('creates Kiro mcp.json with mcpServers format', () => {
110 | 		// Act
111 | 		mockCreateKiroStructure();
112 | 
113 | 		// Assert
114 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
115 | 			path.join(tempDir, '.kiro', 'settings', 'mcp.json'),
116 | 			JSON.stringify({ mcpServers: {} }, null, 2)
117 | 		);
118 | 	});
119 | 
120 | 	test('creates rule files in steering directory', () => {
121 | 		// Act
122 | 		mockCreateKiroStructure();
123 | 
124 | 		// Assert
125 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
126 | 			path.join(tempDir, '.kiro', 'steering', 'kiro_rules.md'),
127 | 			'# Kiro Rules\n\nKiro-specific rules and instructions.'
128 | 		);
129 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
130 | 			path.join(tempDir, '.kiro', 'steering', 'dev_workflow.md'),
131 | 			'# Development Workflow\n\nDevelopment workflow instructions.'
132 | 		);
133 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
134 | 			path.join(tempDir, '.kiro', 'steering', 'self_improve.md'),
135 | 			'# Self Improvement\n\nSelf improvement guidelines.'
136 | 		);
137 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
138 | 			path.join(tempDir, '.kiro', 'steering', 'taskmaster.md'),
139 | 			'# Task Master\n\nTask Master integration instructions.'
140 | 		);
141 | 	});
142 | });
143 | 
```

--------------------------------------------------------------------------------
/packages/tm-core/src/modules/briefs/briefs-domain.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * @fileoverview Briefs Domain Facade
  3 |  * Public API for brief-related operations
  4 |  */
  5 | 
  6 | import {
  7 | 	ERROR_CODES,
  8 | 	TaskMasterError
  9 | } from '../../common/errors/task-master-error.js';
 10 | import { AuthManager } from '../auth/managers/auth-manager.js';
 11 | import type { TaskRepository } from '../tasks/repositories/task-repository.interface.js';
 12 | import { BriefService, type TagWithStats } from './services/brief-service.js';
 13 | import { BriefUrlParser } from './utils/url-parser.js';
 14 | 
 15 | /**
 16 |  * Briefs Domain - Unified API for brief operations
 17 |  * Handles brief switching, matching, and statistics
 18 |  */
 19 | export class BriefsDomain {
 20 | 	private briefService: BriefService;
 21 | 	private authManager: AuthManager;
 22 | 
 23 | 	constructor() {
 24 | 		this.briefService = new BriefService();
 25 | 		this.authManager = AuthManager.getInstance();
 26 | 	}
 27 | 
 28 | 	/**
 29 | 	 * Resolve a brief by name, ID, URL, or partial ID without updating context
 30 | 	 * Returns the full brief object
 31 | 	 *
 32 | 	 * Supports:
 33 | 	 * - Hamster URLs (e.g., https://app.tryhamster.com/home/hamster/briefs/abc123)
 34 | 	 * - Full UUID
 35 | 	 * - Last 8 characters of UUID
 36 | 	 * - Brief name (exact or partial match)
 37 | 	 *
 38 | 	 * @param input - Raw input: URL, UUID, last 8 chars, or brief name
 39 | 	 * @param orgId - Optional organization ID. If not provided, tries to extract from URL or uses current context.
 40 | 	 * @returns The resolved brief object
 41 | 	 */
 42 | 	async resolveBrief(input: string, orgId?: string): Promise<any> {
 43 | 		// Parse input using dedicated URL parser
 44 | 		const parsed = BriefUrlParser.parse(input);
 45 | 		const briefIdOrName = parsed.briefId || input.trim();
 46 | 
 47 | 		// Resolve organization ID (priority: parameter > URL > context)
 48 | 		let resolvedOrgId = orgId;
 49 | 
 50 | 		// Try to extract org slug from URL if not provided
 51 | 		if (!resolvedOrgId && parsed.orgSlug) {
 52 | 			try {
 53 | 				const orgs = await this.authManager.getOrganizations();
 54 | 				const matchingOrg = orgs.find(
 55 | 					(org) =>
 56 | 						org.slug?.toLowerCase() === parsed.orgSlug?.toLowerCase() ||
 57 | 						org.name.toLowerCase() === parsed.orgSlug?.toLowerCase()
 58 | 				);
 59 | 				if (matchingOrg) {
 60 | 					resolvedOrgId = matchingOrg.id;
 61 | 				}
 62 | 			} catch {
 63 | 				// If we can't fetch orgs, fall through to context
 64 | 			}
 65 | 		}
 66 | 
 67 | 		// Fall back to context if still not resolved
 68 | 		if (!resolvedOrgId) {
 69 | 			resolvedOrgId = this.authManager.getContext()?.orgId;
 70 | 		}
 71 | 
 72 | 		if (!resolvedOrgId) {
 73 | 			throw new TaskMasterError(
 74 | 				'No organization selected. Run "tm context org" first.',
 75 | 				ERROR_CODES.CONFIG_ERROR
 76 | 			);
 77 | 		}
 78 | 
 79 | 		// Fetch all briefs for the org
 80 | 		const briefs = await this.authManager.getBriefs(resolvedOrgId);
 81 | 
 82 | 		// Find matching brief using service
 83 | 		const matchingBrief = await this.briefService.findBrief(
 84 | 			briefs,
 85 | 			briefIdOrName
 86 | 		);
 87 | 
 88 | 		this.briefService.validateBriefFound(matchingBrief, briefIdOrName);
 89 | 
 90 | 		return matchingBrief;
 91 | 	}
 92 | 
 93 | 	/**
 94 | 	 * Switch to a different brief by name or ID
 95 | 	 * Validates context, finds matching brief, and updates auth context
 96 | 	 */
 97 | 	async switchBrief(briefNameOrId: string): Promise<void> {
 98 | 		// Use resolveBrief to find the brief
 99 | 		const matchingBrief = await this.resolveBrief(briefNameOrId);
100 | 
101 | 		// Update context with the found brief
102 | 		await this.authManager.updateContext({
103 | 			briefId: matchingBrief.id,
104 | 			briefName:
105 | 				matchingBrief.document?.title || `Brief ${matchingBrief.id.slice(-8)}`,
106 | 			briefStatus: matchingBrief.status,
107 | 			briefUpdatedAt: matchingBrief.updatedAt
108 | 		});
109 | 	}
110 | 
111 | 	/**
112 | 	 * Get all briefs with detailed statistics including task counts
113 | 	 * Used for API storage to show brief statistics
114 | 	 */
115 | 	async getBriefsWithStats(
116 | 		repository: TaskRepository,
117 | 		projectId: string
118 | 	): Promise<{
119 | 		tags: TagWithStats[];
120 | 		currentTag: string | null;
121 | 		totalTags: number;
122 | 	}> {
123 | 		const context = this.authManager.getContext();
124 | 
125 | 		if (!context?.orgId) {
126 | 			throw new TaskMasterError(
127 | 				'No organization context available',
128 | 				ERROR_CODES.MISSING_CONFIGURATION,
129 | 				{
130 | 					operation: 'getBriefsWithStats',
131 | 					userMessage:
132 | 						'No organization selected. Please authenticate first using: tm auth login'
133 | 				}
134 | 			);
135 | 		}
136 | 
137 | 		// Get all briefs for the organization (through auth manager)
138 | 		const briefs = await this.authManager.getBriefs(context.orgId);
139 | 
140 | 		// Use BriefService to calculate stats
141 | 		return this.briefService.getTagsWithStats(
142 | 			briefs,
143 | 			context.briefId,
144 | 			repository,
145 | 			projectId
146 | 		);
147 | 	}
148 | }
149 | 
```

--------------------------------------------------------------------------------
/packages/claude-code-plugin/commands/tm-main.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Task Master Command Reference
  2 | 
  3 | Comprehensive command structure for Task Master integration with Claude Code.
  4 | 
  5 | ## Command Organization
  6 | 
  7 | Commands are organized hierarchically to match Task Master's CLI structure while providing enhanced Claude Code integration.
  8 | 
  9 | ## Project Setup & Configuration
 10 | 
 11 | ### `/taskmaster:init`
 12 | - `init-project` - Initialize new project (handles PRD files intelligently)
 13 | - `init-project-quick` - Quick setup with auto-confirmation (-y flag)
 14 | 
 15 | ### `/taskmaster:models`
 16 | - `view-models` - View current AI model configuration
 17 | - `setup-models` - Interactive model configuration
 18 | - `set-main` - Set primary generation model
 19 | - `set-research` - Set research model
 20 | - `set-fallback` - Set fallback model
 21 | 
 22 | ## Task Generation
 23 | 
 24 | ### `/taskmaster:parse-prd`
 25 | - `parse-prd` - Generate tasks from PRD document
 26 | - `parse-prd-with-research` - Enhanced parsing with research mode
 27 | 
 28 | ### `/taskmaster:generate`
 29 | - `generate-tasks` - Create individual task files from tasks.json
 30 | 
 31 | ## Task Management
 32 | 
 33 | ### `/taskmaster:list`
 34 | - `list-tasks` - Smart listing with natural language filters
 35 | - `list-tasks-with-subtasks` - Include subtasks in hierarchical view
 36 | - `list-tasks-by-status` - Filter by specific status
 37 | 
 38 | ### `/taskmaster:set-status`
 39 | - `to-pending` - Reset task to pending
 40 | - `to-in-progress` - Start working on task
 41 | - `to-done` - Mark task complete
 42 | - `to-review` - Submit for review
 43 | - `to-deferred` - Defer task
 44 | - `to-cancelled` - Cancel task
 45 | 
 46 | ### `/taskmaster:sync-readme`
 47 | - `sync-readme` - Export tasks to README.md with formatting
 48 | 
 49 | ### `/taskmaster:update`
 50 | - `update-task` - Update tasks with natural language
 51 | - `update-tasks-from-id` - Update multiple tasks from a starting point
 52 | - `update-single-task` - Update specific task
 53 | 
 54 | ### `/taskmaster:add-task`
 55 | - `add-task` - Add new task with AI assistance
 56 | 
 57 | ### `/taskmaster:remove-task`
 58 | - `remove-task` - Remove task with confirmation
 59 | 
 60 | ## Subtask Management
 61 | 
 62 | ### `/taskmaster:add-subtask`
 63 | - `add-subtask` - Add new subtask to parent
 64 | - `convert-task-to-subtask` - Convert existing task to subtask
 65 | 
 66 | ### `/taskmaster:remove-subtask`
 67 | - `remove-subtask` - Remove subtask (with optional conversion)
 68 | 
 69 | ### `/taskmaster:clear-subtasks`
 70 | - `clear-subtasks` - Clear subtasks from specific task
 71 | - `clear-all-subtasks` - Clear all subtasks globally
 72 | 
 73 | ## Task Analysis & Breakdown
 74 | 
 75 | ### `/taskmaster:analyze-complexity`
 76 | - `analyze-complexity` - Analyze and generate expansion recommendations
 77 | 
 78 | ### `/taskmaster:complexity-report`
 79 | - `complexity-report` - Display complexity analysis report
 80 | 
 81 | ### `/taskmaster:expand`
 82 | - `expand-task` - Break down specific task
 83 | - `expand-all-tasks` - Expand all eligible tasks
 84 | - `with-research` - Enhanced expansion
 85 | 
 86 | ## Task Navigation
 87 | 
 88 | ### `/taskmaster:next`
 89 | - `next-task` - Intelligent next task recommendation
 90 | 
 91 | ### `/taskmaster:show`
 92 | - `show-task` - Display detailed task information
 93 | 
 94 | ### `/taskmaster:status`
 95 | - `project-status` - Comprehensive project dashboard
 96 | 
 97 | ## Dependency Management
 98 | 
 99 | ### `/taskmaster:add-dependency`
100 | - `add-dependency` - Add task dependency
101 | 
102 | ### `/taskmaster:remove-dependency`
103 | - `remove-dependency` - Remove task dependency
104 | 
105 | ### `/taskmaster:validate-dependencies`
106 | - `validate-dependencies` - Check for dependency issues
107 | 
108 | ### `/taskmaster:fix-dependencies`
109 | - `fix-dependencies` - Automatically fix dependency problems
110 | 
111 | ## Workflows & Automation
112 | 
113 | ### `/taskmaster:workflows`
114 | - `smart-workflow` - Context-aware intelligent workflow execution
115 | - `command-pipeline` - Chain multiple commands together
116 | - `auto-implement-tasks` - Advanced auto-implementation with code generation
117 | 
118 | ## Utilities
119 | 
120 | ### `/taskmaster:utils`
121 | - `analyze-project` - Deep project analysis and insights
122 | 
123 | ### `/taskmaster:setup`
124 | - `install-taskmaster` - Comprehensive installation guide
125 | - `quick-install-taskmaster` - One-line global installation
126 | 
127 | ## Usage Patterns
128 | 
129 | ### Natural Language
130 | Most commands accept natural language arguments:
131 | ```
132 | /taskmaster:add-task create user authentication system
133 | /taskmaster:update mark all API tasks as high priority
134 | /taskmaster:list show blocked tasks
135 | ```
136 | 
137 | ### ID-Based Commands
138 | Commands requiring IDs intelligently parse from $ARGUMENTS:
139 | ```
140 | /taskmaster:show 45
141 | /taskmaster:expand 23
142 | /taskmaster:set-status/to-done 67
143 | ```
144 | 
145 | ### Smart Defaults
146 | Commands provide intelligent defaults and suggestions based on context.
```

--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------

```yaml
  1 | name: CI
  2 | 
  3 | on:
  4 |   push:
  5 |     branches:
  6 |       - main
  7 |       - next
  8 |   pull_request:
  9 |   workflow_dispatch:
 10 | 
 11 | concurrency:
 12 |   group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
 13 |   cancel-in-progress: true
 14 | 
 15 | permissions:
 16 |   contents: read
 17 | 
 18 | env:
 19 |   DO_NOT_TRACK: 1
 20 |   NODE_ENV: development
 21 | 
 22 | jobs:
 23 |   # Fast checks that can run in parallel
 24 |   format-check:
 25 |     name: Format Check
 26 |     runs-on: ubuntu-latest
 27 |     steps:
 28 |       - uses: actions/checkout@v4
 29 |         with:
 30 |           fetch-depth: 2
 31 | 
 32 |       - uses: actions/setup-node@v4
 33 |         with:
 34 |           node-version: 20
 35 |           cache: "npm"
 36 | 
 37 |       - name: Install dependencies
 38 |         run: npm install --frozen-lockfile --prefer-offline
 39 |         timeout-minutes: 5
 40 | 
 41 |       - name: Format Check
 42 |         run: npm run format-check
 43 |         env:
 44 |           FORCE_COLOR: 1
 45 | 
 46 |   changeset-validation:
 47 |     name: Validate Changesets
 48 |     runs-on: ubuntu-latest
 49 |     steps:
 50 |       - uses: actions/checkout@v4
 51 |         with:
 52 |           fetch-depth: 0
 53 | 
 54 |       - uses: dorny/paths-filter@v3
 55 |         id: changes
 56 |         with:
 57 |           filters: |
 58 |             changesets:
 59 |               - '.changeset/**'
 60 |               - '.github/scripts/validate-changesets.mjs'
 61 | 
 62 |       - uses: actions/setup-node@v4
 63 |         if: steps.changes.outputs.changesets == 'true'
 64 |         with:
 65 |           node-version: 20
 66 |           cache: "npm"
 67 | 
 68 |       - name: Validate changeset package references
 69 |         if: steps.changes.outputs.changesets == 'true'
 70 |         run: |
 71 |           # Validate that changesets only reference public packages
 72 |           # This catches issues like using @tm/cli instead of task-master-ai
 73 |           node .github/scripts/validate-changesets.mjs
 74 |         env:
 75 |           FORCE_COLOR: 1
 76 | 
 77 |   typecheck:
 78 |     name: Typecheck
 79 |     timeout-minutes: 10
 80 |     runs-on: ubuntu-latest
 81 |     steps:
 82 |       - uses: actions/checkout@v4
 83 |         with:
 84 |           fetch-depth: 2
 85 | 
 86 |       - uses: actions/setup-node@v4
 87 |         with:
 88 |           node-version: 20
 89 |           cache: "npm"
 90 | 
 91 |       - name: Install dependencies
 92 |         run: npm install --frozen-lockfile --prefer-offline
 93 |         timeout-minutes: 5
 94 | 
 95 |       - name: Typecheck
 96 |         run: npm run turbo:typecheck
 97 |         env:
 98 |           FORCE_COLOR: 1
 99 | 
100 |   # Build job to ensure everything compiles
101 |   build:
102 |     name: Build
103 |     runs-on: ubuntu-latest
104 |     steps:
105 |       - uses: actions/checkout@v4
106 |         with:
107 |           fetch-depth: 2
108 | 
109 |       - uses: actions/setup-node@v4
110 |         with:
111 |           node-version: 20
112 |           cache: "npm"
113 | 
114 |       - name: Install dependencies
115 |         run: npm install --frozen-lockfile --prefer-offline
116 |         timeout-minutes: 5
117 | 
118 |       - name: Build
119 |         run: npm run turbo:build
120 |         env:
121 |           NODE_ENV: production
122 |           FORCE_COLOR: 1
123 |           TM_PUBLIC_BASE_DOMAIN: ${{ secrets.TM_PUBLIC_BASE_DOMAIN }}
124 |           TM_PUBLIC_SUPABASE_URL: ${{ secrets.TM_PUBLIC_SUPABASE_URL }}
125 |           TM_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.TM_PUBLIC_SUPABASE_ANON_KEY }}
126 | 
127 |       - name: Upload build artifacts
128 |         uses: actions/upload-artifact@v4
129 |         with:
130 |           name: build-artifacts
131 |           path: dist/
132 |           retention-days: 1
133 | 
134 |   test:
135 |     name: Test
136 |     timeout-minutes: 15
137 |     runs-on: ubuntu-latest
138 |     needs: [format-check, typecheck, build, changeset-validation]
139 |     if: always() && !cancelled() && !contains(needs.*.result, 'failure')
140 |     steps:
141 |       - uses: actions/checkout@v4
142 |         with:
143 |           fetch-depth: 2
144 | 
145 |       - uses: actions/setup-node@v4
146 |         with:
147 |           node-version: 20
148 |           cache: "npm"
149 | 
150 |       - name: Install dependencies
151 |         run: npm install --frozen-lockfile --prefer-offline
152 |         timeout-minutes: 5
153 | 
154 |       - name: Download build artifacts
155 |         uses: actions/download-artifact@v4
156 |         with:
157 |           name: build-artifacts
158 |           path: dist/
159 | 
160 |       - name: Run Tests
161 |         run: |
162 |           npm run test:coverage -- --coverageThreshold '{"global":{"branches":0,"functions":0,"lines":0,"statements":0}}' --detectOpenHandles --forceExit
163 |         env:
164 |           NODE_ENV: test
165 |           CI: true
166 |           FORCE_COLOR: 1
167 | 
168 |       - name: Upload Test Results
169 |         if: always()
170 |         uses: actions/upload-artifact@v4
171 |         with:
172 |           name: test-results
173 |           path: |
174 |             test-results
175 |             coverage
176 |             junit.xml
177 |           retention-days: 30
178 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/create-tag-from-branch.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * create-tag-from-branch.js
  3 |  * Direct function implementation for creating tags from git branches
  4 |  */
  5 | 
  6 | import { createTagFromBranch } from '../../../../scripts/modules/task-manager/tag-management.js';
  7 | import {
  8 | 	getCurrentBranch,
  9 | 	isGitRepository
 10 | } from '../../../../scripts/modules/utils/git-utils.js';
 11 | import {
 12 | 	enableSilentMode,
 13 | 	disableSilentMode
 14 | } from '../../../../scripts/modules/utils.js';
 15 | import { createLogWrapper } from '../../tools/utils.js';
 16 | 
 17 | /**
 18 |  * Direct function wrapper for creating tags from git branches with error handling.
 19 |  *
 20 |  * @param {Object} args - Command arguments
 21 |  * @param {string} args.tasksJsonPath - Path to the tasks.json file (resolved by tool)
 22 |  * @param {string} [args.branchName] - Git branch name (optional, uses current branch if not provided)
 23 |  * @param {boolean} [args.copyFromCurrent] - Copy tasks from current tag
 24 |  * @param {string} [args.copyFromTag] - Copy tasks from specific tag
 25 |  * @param {string} [args.description] - Custom description for the tag
 26 |  * @param {boolean} [args.autoSwitch] - Automatically switch to the new tag
 27 |  * @param {string} [args.projectRoot] - Project root path
 28 |  * @param {Object} log - Logger object
 29 |  * @param {Object} context - Additional context (session)
 30 |  * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 31 |  */
 32 | export async function createTagFromBranchDirect(args, log, context = {}) {
 33 | 	// Destructure expected args
 34 | 	const {
 35 | 		tasksJsonPath,
 36 | 		branchName,
 37 | 		copyFromCurrent,
 38 | 		copyFromTag,
 39 | 		description,
 40 | 		autoSwitch,
 41 | 		projectRoot
 42 | 	} = args;
 43 | 	const { session } = context;
 44 | 
 45 | 	// Enable silent mode to prevent console logs from interfering with JSON response
 46 | 	enableSilentMode();
 47 | 
 48 | 	// Create logger wrapper using the utility
 49 | 	const mcpLog = createLogWrapper(log);
 50 | 
 51 | 	try {
 52 | 		// Check if tasksJsonPath was provided
 53 | 		if (!tasksJsonPath) {
 54 | 			log.error('createTagFromBranchDirect called without tasksJsonPath');
 55 | 			disableSilentMode();
 56 | 			return {
 57 | 				success: false,
 58 | 				error: {
 59 | 					code: 'MISSING_ARGUMENT',
 60 | 					message: 'tasksJsonPath is required'
 61 | 				}
 62 | 			};
 63 | 		}
 64 | 
 65 | 		// Check if projectRoot was provided
 66 | 		if (!projectRoot) {
 67 | 			log.error('createTagFromBranchDirect called without projectRoot');
 68 | 			disableSilentMode();
 69 | 			return {
 70 | 				success: false,
 71 | 				error: {
 72 | 					code: 'MISSING_ARGUMENT',
 73 | 					message: 'projectRoot is required'
 74 | 				}
 75 | 			};
 76 | 		}
 77 | 
 78 | 		// Check if we're in a git repository
 79 | 		if (!(await isGitRepository(projectRoot))) {
 80 | 			log.error('Not in a git repository');
 81 | 			disableSilentMode();
 82 | 			return {
 83 | 				success: false,
 84 | 				error: {
 85 | 					code: 'NOT_GIT_REPOSITORY',
 86 | 					message: 'Not in a git repository. Cannot create tag from branch.'
 87 | 				}
 88 | 			};
 89 | 		}
 90 | 
 91 | 		// Determine branch name
 92 | 		let targetBranch = branchName;
 93 | 		if (!targetBranch) {
 94 | 			targetBranch = await getCurrentBranch(projectRoot);
 95 | 			if (!targetBranch) {
 96 | 				log.error('Could not determine current git branch');
 97 | 				disableSilentMode();
 98 | 				return {
 99 | 					success: false,
100 | 					error: {
101 | 						code: 'NO_CURRENT_BRANCH',
102 | 						message: 'Could not determine current git branch'
103 | 					}
104 | 				};
105 | 			}
106 | 		}
107 | 
108 | 		log.info(`Creating tag from git branch: ${targetBranch}`);
109 | 
110 | 		// Prepare options
111 | 		const options = {
112 | 			copyFromCurrent: copyFromCurrent || false,
113 | 			copyFromTag,
114 | 			description:
115 | 				description || `Tag created from git branch "${targetBranch}"`,
116 | 			autoSwitch: autoSwitch || false
117 | 		};
118 | 
119 | 		// Call the createTagFromBranch function
120 | 		const result = await createTagFromBranch(
121 | 			tasksJsonPath,
122 | 			targetBranch,
123 | 			options,
124 | 			{
125 | 				session,
126 | 				mcpLog,
127 | 				projectRoot
128 | 			},
129 | 			'json' // outputFormat - use 'json' to suppress CLI UI
130 | 		);
131 | 
132 | 		// Restore normal logging
133 | 		disableSilentMode();
134 | 
135 | 		return {
136 | 			success: true,
137 | 			data: {
138 | 				branchName: result.branchName,
139 | 				tagName: result.tagName,
140 | 				created: result.created,
141 | 				mappingUpdated: result.mappingUpdated,
142 | 				autoSwitched: result.autoSwitched,
143 | 				message: `Successfully created tag "${result.tagName}" from branch "${result.branchName}"`
144 | 			}
145 | 		};
146 | 	} catch (error) {
147 | 		// Make sure to restore normal logging even if there's an error
148 | 		disableSilentMode();
149 | 
150 | 		log.error(`Error in createTagFromBranchDirect: ${error.message}`);
151 | 		return {
152 | 			success: false,
153 | 			error: {
154 | 				code: error.code || 'CREATE_TAG_FROM_BRANCH_ERROR',
155 | 				message: error.message
156 | 			}
157 | 		};
158 | 	}
159 | }
160 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/add-subtask.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * Direct function wrapper for addSubtask
  3 |  */
  4 | 
  5 | import { addSubtask } from '../../../../scripts/modules/task-manager.js';
  6 | import {
  7 | 	enableSilentMode,
  8 | 	disableSilentMode
  9 | } from '../../../../scripts/modules/utils.js';
 10 | 
 11 | /**
 12 |  * Add a subtask to an existing task
 13 |  * @param {Object} args - Function arguments
 14 |  * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 15 |  * @param {string} args.id - Parent task ID
 16 |  * @param {string} [args.taskId] - Existing task ID to convert to subtask (optional)
 17 |  * @param {string} [args.title] - Title for new subtask (when creating a new subtask)
 18 |  * @param {string} [args.description] - Description for new subtask
 19 |  * @param {string} [args.details] - Implementation details for new subtask
 20 |  * @param {string} [args.status] - Status for new subtask (default: 'pending')
 21 |  * @param {string} [args.dependencies] - Comma-separated list of dependency IDs
 22 |  * @param {boolean} [args.skipGenerate] - Skip regenerating task files
 23 |  * @param {string} [args.projectRoot] - Project root directory
 24 |  * @param {string} [args.tag] - Tag for the task
 25 |  * @param {Object} log - Logger object
 26 |  * @returns {Promise<{success: boolean, data?: Object, error?: string}>}
 27 |  */
 28 | export async function addSubtaskDirect(args, log) {
 29 | 	// Destructure expected args
 30 | 	const {
 31 | 		tasksJsonPath,
 32 | 		id,
 33 | 		taskId,
 34 | 		title,
 35 | 		description,
 36 | 		details,
 37 | 		status,
 38 | 		dependencies: dependenciesStr,
 39 | 		skipGenerate,
 40 | 		projectRoot,
 41 | 		tag
 42 | 	} = args;
 43 | 	try {
 44 | 		log.info(`Adding subtask with args: ${JSON.stringify(args)}`);
 45 | 
 46 | 		// Check if tasksJsonPath was provided
 47 | 		if (!tasksJsonPath) {
 48 | 			log.error('addSubtaskDirect called without tasksJsonPath');
 49 | 			return {
 50 | 				success: false,
 51 | 				error: {
 52 | 					code: 'MISSING_ARGUMENT',
 53 | 					message: 'tasksJsonPath is required'
 54 | 				}
 55 | 			};
 56 | 		}
 57 | 
 58 | 		if (!id) {
 59 | 			return {
 60 | 				success: false,
 61 | 				error: {
 62 | 					code: 'INPUT_VALIDATION_ERROR',
 63 | 					message: 'Parent task ID is required'
 64 | 				}
 65 | 			};
 66 | 		}
 67 | 
 68 | 		// Either taskId or title must be provided
 69 | 		if (!taskId && !title) {
 70 | 			return {
 71 | 				success: false,
 72 | 				error: {
 73 | 					code: 'INPUT_VALIDATION_ERROR',
 74 | 					message: 'Either taskId or title must be provided'
 75 | 				}
 76 | 			};
 77 | 		}
 78 | 
 79 | 		// Use provided path
 80 | 		const tasksPath = tasksJsonPath;
 81 | 
 82 | 		// Parse dependencies if provided
 83 | 		let dependencies = [];
 84 | 		if (dependenciesStr) {
 85 | 			dependencies = dependenciesStr.split(',').map((depId) => {
 86 | 				// Handle both regular IDs and dot notation
 87 | 				return depId.includes('.') ? depId.trim() : parseInt(depId.trim(), 10);
 88 | 			});
 89 | 		}
 90 | 
 91 | 		// Convert existingTaskId to a number if provided
 92 | 		const existingTaskId = taskId ? parseInt(taskId, 10) : null;
 93 | 
 94 | 		// Convert parent ID to a number
 95 | 		const parentId = parseInt(id, 10);
 96 | 
 97 | 		// Determine if we should generate files
 98 | 		const generateFiles = !skipGenerate;
 99 | 
100 | 		// Enable silent mode to prevent console logs from interfering with JSON response
101 | 		enableSilentMode();
102 | 
103 | 		const context = { projectRoot, tag };
104 | 
105 | 		// Case 1: Convert existing task to subtask
106 | 		if (existingTaskId) {
107 | 			log.info(`Converting task ${existingTaskId} to a subtask of ${parentId}`);
108 | 			const result = await addSubtask(
109 | 				tasksPath,
110 | 				parentId,
111 | 				existingTaskId,
112 | 				null,
113 | 				generateFiles,
114 | 				context
115 | 			);
116 | 
117 | 			// Restore normal logging
118 | 			disableSilentMode();
119 | 
120 | 			return {
121 | 				success: true,
122 | 				data: {
123 | 					message: `Task ${existingTaskId} successfully converted to a subtask of task ${parentId}`,
124 | 					subtask: result
125 | 				}
126 | 			};
127 | 		}
128 | 		// Case 2: Create new subtask
129 | 		else {
130 | 			log.info(`Creating new subtask for parent task ${parentId}`);
131 | 
132 | 			const newSubtaskData = {
133 | 				title: title,
134 | 				description: description || '',
135 | 				details: details || '',
136 | 				status: status || 'pending',
137 | 				dependencies: dependencies
138 | 			};
139 | 
140 | 			const result = await addSubtask(
141 | 				tasksPath,
142 | 				parentId,
143 | 				null,
144 | 				newSubtaskData,
145 | 				generateFiles,
146 | 				context
147 | 			);
148 | 
149 | 			// Restore normal logging
150 | 			disableSilentMode();
151 | 
152 | 			return {
153 | 				success: true,
154 | 				data: {
155 | 					message: `New subtask ${parentId}.${result.id} successfully created`,
156 | 					subtask: result
157 | 				}
158 | 			};
159 | 		}
160 | 	} catch (error) {
161 | 		// Make sure to restore normal logging even if there's an error
162 | 		disableSilentMode();
163 | 
164 | 		log.error(`Error in addSubtaskDirect: ${error.message}`);
165 | 		return {
166 | 			success: false,
167 | 			error: {
168 | 				code: 'CORE_FUNCTION_ERROR',
169 | 				message: error.message
170 | 			}
171 | 		};
172 | 	}
173 | }
174 | 
```
Page 13/69FirstPrevNextLast