#
tokens: 49052/50000 30/975 files (page 10/50)
lines: off (toggle) GitHub
raw markdown copy
This is page 10 of 50. Use http://codebase.md/eyaltoledano/claude-task-master?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

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

```javascript
// Cursor conversion profile for rule-transformer
import path from 'path';
import fs from 'fs';
import { log } from '../../scripts/modules/utils.js';
import { createProfile } from './base-profile.js';

// Helper copy; use cpSync when available, fallback to manual recursion
function copyRecursiveSync(src, dest) {
	if (fs.cpSync) {
		try {
			fs.cpSync(src, dest, { recursive: true, force: true });
			return;
		} catch (err) {
			throw new Error(`Failed to copy ${src} to ${dest}: ${err.message}`);
		}
	}
	const exists = fs.existsSync(src);
	let stats = null;
	let isDirectory = false;

	if (exists) {
		try {
			stats = fs.statSync(src);
			isDirectory = stats.isDirectory();
		} catch (err) {
			// Handle TOCTOU race condition - treat as non-existent/not-a-directory
			isDirectory = false;
		}
	}

	if (isDirectory) {
		try {
			if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
			for (const child of fs.readdirSync(src)) {
				copyRecursiveSync(path.join(src, child), path.join(dest, child));
			}
		} catch (err) {
			throw new Error(
				`Failed to copy directory ${src} to ${dest}: ${err.message}`
			);
		}
	} else {
		try {
			// ensure parent exists for file copies
			fs.mkdirSync(path.dirname(dest), { recursive: true });
			fs.copyFileSync(src, dest);
		} catch (err) {
			throw new Error(`Failed to copy file ${src} to ${dest}: ${err.message}`);
		}
	}
}

// Helper function to recursively remove directory
function removeDirectoryRecursive(dirPath) {
	if (fs.existsSync(dirPath)) {
		try {
			fs.rmSync(dirPath, { recursive: true, force: true });
			return true;
		} catch (err) {
			log('error', `Failed to remove directory ${dirPath}: ${err.message}`);
			return false;
		}
	}
	return true;
}

// Resolve the Cursor profile directory from either project root, profile root, or rules dir
function resolveCursorProfileDir(baseDir) {
	const base = path.basename(baseDir);
	// If called with .../.cursor/rules -> return .../.cursor
	if (base === 'rules' && path.basename(path.dirname(baseDir)) === '.cursor') {
		return path.dirname(baseDir);
	}
	// If called with .../.cursor -> return as-is
	if (base === '.cursor') return baseDir;
	// Otherwise assume project root and append .cursor
	return path.join(baseDir, '.cursor');
}

// Lifecycle functions for Cursor profile
function onAddRulesProfile(targetDir, assetsDir) {
	// Copy commands directory recursively
	const commandsSourceDir = path.join(assetsDir, 'claude', 'commands');
	const profileDir = resolveCursorProfileDir(targetDir);
	const commandsDestDir = path.join(profileDir, 'commands');

	if (!fs.existsSync(commandsSourceDir)) {
		log(
			'warn',
			`[Cursor] Source commands directory does not exist: ${commandsSourceDir}`
		);
		return;
	}

	try {
		// Ensure fresh state to avoid stale command files
		try {
			fs.rmSync(commandsDestDir, { recursive: true, force: true });
			log(
				'debug',
				`[Cursor] Removed existing commands directory: ${commandsDestDir}`
			);
		} catch (deleteErr) {
			// Directory might not exist, which is fine
			log(
				'debug',
				`[Cursor] Commands directory did not exist or could not be removed: ${deleteErr.message}`
			);
		}

		copyRecursiveSync(commandsSourceDir, commandsDestDir);
		log('debug', `[Cursor] Copied commands directory to ${commandsDestDir}`);
	} catch (err) {
		log(
			'error',
			`[Cursor] An error occurred during commands copy: ${err.message}`
		);
	}
}

function onRemoveRulesProfile(targetDir) {
	// Remove .cursor/commands directory recursively
	const profileDir = resolveCursorProfileDir(targetDir);
	const commandsDir = path.join(profileDir, 'commands');
	if (removeDirectoryRecursive(commandsDir)) {
		log(
			'debug',
			`[Cursor] Ensured commands directory removed at ${commandsDir}`
		);
	}
}

// Create and export cursor profile using the base factory
export const cursorProfile = createProfile({
	name: 'cursor',
	displayName: 'Cursor',
	url: 'cursor.so',
	docsUrl: 'docs.cursor.com',
	targetExtension: '.mdc', // Cursor keeps .mdc extension
	supportsRulesSubdirectories: true,
	onAdd: onAddRulesProfile,
	onRemove: onRemoveRulesProfile
});

// Export lifecycle functions separately to avoid naming conflicts
export { onAddRulesProfile, onRemoveRulesProfile };

```

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

```javascript
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const supportedModelsPath = path.join(
	__dirname,
	'..',
	'modules',
	'supported-models.json'
);
const outputMarkdownPath = path.join(
	__dirname,
	'..',
	'..',
	'docs',
	'models.md'
);

function formatCost(cost) {
	if (cost === null || cost === undefined) {
		return '—';
	}
	return cost;
}

function formatSweScore(score) {
	if (score === null || score === undefined || score === 0) {
		return '—';
	}
	return score.toString();
}

function generateMarkdownTable(title, models) {
	if (!models || models.length === 0) {
		return `## ${title}\n\nNo models in this category.\n\n`;
	}
	let table = `## ${title}\n\n`;
	table += '| Provider | Model Name | SWE Score | Input Cost | Output Cost |\n';
	table += '|---|---|---|---|---|\n';
	models.forEach((model) => {
		table += `| ${model.provider} | ${model.modelName} | ${formatSweScore(model.sweScore)} | ${formatCost(model.inputCost)} | ${formatCost(model.outputCost)} |\n`;
	});
	table += '\n';
	return table;
}

function generateUnsupportedTable(models) {
	if (!models || models.length === 0) {
		return '## Unsupported Models\n\nNo unsupported models found.\n\n';
	}
	let table = '## Unsupported Models\n\n';
	table += '| Provider | Model Name | Reason |\n';
	table += '|---|---|---|\n';
	models.forEach((model) => {
		table += `| ${model.provider} | ${model.modelName} | ${model.reason || '—'} |\n`;
	});
	table += '\n';
	return table;
}

function main() {
	try {
		const correctSupportedModelsPath = path.join(
			__dirname,
			'..',
			'..',
			'scripts',
			'modules',
			'supported-models.json'
		);
		const correctOutputMarkdownPath = path.join(__dirname, '..', 'models.md');

		const supportedModelsContent = fs.readFileSync(
			correctSupportedModelsPath,
			'utf8'
		);
		const supportedModels = JSON.parse(supportedModelsContent);

		const mainModels = [];
		const researchModels = [];
		const fallbackModels = [];
		const unsupportedModels = [];

		for (const provider in supportedModels) {
			if (Object.hasOwnProperty.call(supportedModels, provider)) {
				const models = supportedModels[provider];
				models.forEach((model) => {
					const isSupported = model.supported !== false; // default to true if missing
					if (isSupported) {
						const modelEntry = {
							provider: provider,
							modelName: model.id,
							sweScore: model.swe_score,
							inputCost: model.cost_per_1m_tokens
								? model.cost_per_1m_tokens.input
								: null,
							outputCost: model.cost_per_1m_tokens
								? model.cost_per_1m_tokens.output
								: null
						};
						if (model.allowed_roles && model.allowed_roles.includes('main')) {
							mainModels.push(modelEntry);
						}
						if (
							model.allowed_roles &&
							model.allowed_roles.includes('research')
						) {
							researchModels.push(modelEntry);
						}
						if (
							model.allowed_roles &&
							model.allowed_roles.includes('fallback')
						) {
							fallbackModels.push(modelEntry);
						}
					} else {
						unsupportedModels.push({
							provider: provider,
							modelName: model.id,
							reason: model.reason || 'Not specified'
						});
					}
				});
			}
		}

		const date = new Date();
		const monthNames = [
			'January',
			'February',
			'March',
			'April',
			'May',
			'June',
			'July',
			'August',
			'September',
			'October',
			'November',
			'December'
		];
		const formattedDate = `${monthNames[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;

		let markdownContent = `# Available Models as of ${formattedDate}\n\n`;
		markdownContent += generateMarkdownTable('Main Models', mainModels);
		markdownContent += generateMarkdownTable('Research Models', researchModels);
		markdownContent += generateMarkdownTable('Fallback Models', fallbackModels);
		markdownContent += generateUnsupportedTable(unsupportedModels);

		fs.writeFileSync(correctOutputMarkdownPath, markdownContent, 'utf8');
		console.log(`Successfully updated ${correctOutputMarkdownPath}`);
	} catch (error) {
		console.error('Error transforming models.json to models.md:', error);
		process.exit(1);
	}
}

main();

```

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

```javascript
import { PromptManager } from '../../../scripts/modules/prompt-manager.js';
import { ExpandTaskResponseSchema } from '../../../src/schemas/expand-task.js';
import { SubtaskSchema } from '../../../src/schemas/base-schemas.js';

describe('expand-task prompt template', () => {
	let promptManager;

	beforeEach(() => {
		promptManager = new PromptManager();
	});

	const testTask = {
		id: 1,
		title: 'Setup AWS Infrastructure',
		description: 'Provision core AWS services',
		details: 'Create VPC, subnets, and security groups'
	};

	const baseParams = {
		task: testTask,
		subtaskCount: 3,
		nextSubtaskId: 1,
		additionalContext: '',
		complexityReasoningContext: '',
		gatheredContext: '',
		useResearch: false,
		expansionPrompt: undefined
	};

	test('default variant includes task context', () => {
		const { userPrompt } = promptManager.loadPrompt(
			'expand-task',
			baseParams,
			'default'
		);

		expect(userPrompt).toContain(testTask.title);
		expect(userPrompt).toContain(testTask.description);
		expect(userPrompt).toContain(testTask.details);
		expect(userPrompt).toContain('Task ID: 1');
	});

	test('research variant includes task context', () => {
		const params = { ...baseParams, useResearch: true };
		const { userPrompt } = promptManager.loadPrompt(
			'expand-task',
			params,
			'research'
		);

		expect(userPrompt).toContain(testTask.title);
		expect(userPrompt).toContain(testTask.description);
		expect(userPrompt).toContain(testTask.details);
		expect(userPrompt).toContain('Parent Task:');
		expect(userPrompt).toContain('ID: 1');
	});

	test('complexity-report variant includes task context', () => {
		const params = {
			...baseParams,
			expansionPrompt: 'Focus on security best practices',
			complexityReasoningContext: 'High complexity due to security requirements'
		};
		const { userPrompt } = promptManager.loadPrompt(
			'expand-task',
			params,
			'complexity-report'
		);

		// The fix ensures task context is included
		expect(userPrompt).toContain('Parent Task:');
		expect(userPrompt).toContain(`ID: ${testTask.id}`);
		expect(userPrompt).toContain(`Title: ${testTask.title}`);
		expect(userPrompt).toContain(`Description: ${testTask.description}`);
		expect(userPrompt).toContain(`Current details: ${testTask.details}`);

		// Also includes the expansion prompt
		expect(userPrompt).toContain(params.expansionPrompt);
		expect(userPrompt).toContain(params.complexityReasoningContext);
	});

	test('ExpandTaskResponseSchema defines required subtask fields', () => {
		// Test the schema definition directly instead of weak substring matching
		const schema = ExpandTaskResponseSchema;
		const subtasksSchema = schema.shape.subtasks;
		const subtaskSchema = subtasksSchema.element;

		// Verify the schema has the required fields
		expect(subtaskSchema).toBe(SubtaskSchema);
		expect(SubtaskSchema.shape).toHaveProperty('id');
		expect(SubtaskSchema.shape).toHaveProperty('title');
		expect(SubtaskSchema.shape).toHaveProperty('description');
		expect(SubtaskSchema.shape).toHaveProperty('dependencies');
		expect(SubtaskSchema.shape).toHaveProperty('details');
		expect(SubtaskSchema.shape).toHaveProperty('status');
		expect(SubtaskSchema.shape).toHaveProperty('testStrategy');
	});

	test('complexity-report variant fails without task context regression test', () => {
		// This test ensures we don't regress to the old behavior where
		// complexity-report variant only used expansionPrompt without task context
		const params = {
			...baseParams,
			expansionPrompt: 'Generic expansion prompt'
		};

		const { userPrompt } = promptManager.loadPrompt(
			'expand-task',
			params,
			'complexity-report'
		);

		// Count occurrences of task-specific content
		const titleOccurrences = (
			userPrompt.match(new RegExp(testTask.title, 'g')) || []
		).length;
		const descriptionOccurrences = (
			userPrompt.match(new RegExp(testTask.description, 'g')) || []
		).length;

		// Should have at least one occurrence of title and description
		expect(titleOccurrences).toBeGreaterThanOrEqual(1);
		expect(descriptionOccurrences).toBeGreaterThanOrEqual(1);

		// Should not be ONLY the expansion prompt
		expect(userPrompt.length).toBeGreaterThan(
			params.expansionPrompt.length + 100
		);
	});
});

```

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

```javascript
import { jest } from '@jest/globals';
import fs from 'fs';
import path from 'path';
import os from 'os';

// Mock external modules
jest.mock('child_process', () => ({
	execSync: jest.fn()
}));

// Mock console methods
jest.mock('console', () => ({
	log: jest.fn(),
	info: jest.fn(),
	warn: jest.fn(),
	error: jest.fn(),
	clear: jest.fn()
}));

describe('Kiro Integration', () => {
	let tempDir;

	beforeEach(() => {
		jest.clearAllMocks();

		// Create a temporary directory for testing
		tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-master-test-'));

		// Spy on fs methods
		jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
		jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
			if (filePath.toString().includes('mcp.json')) {
				return JSON.stringify({ mcpServers: {} }, null, 2);
			}
			return '{}';
		});
		jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
		jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
	});

	afterEach(() => {
		// Clean up the temporary directory
		try {
			fs.rmSync(tempDir, { recursive: true, force: true });
		} catch (err) {
			console.error(`Error cleaning up: ${err.message}`);
		}
	});

	// Test function that simulates the createProjectStructure behavior for Kiro files
	function mockCreateKiroStructure() {
		// This function simulates the actual kiro profile creation logic
		// It explicitly calls the mocked fs methods to ensure consistency with the test environment

		// Simulate directory creation calls - these will call the mocked mkdirSync
		fs.mkdirSync(path.join(tempDir, '.kiro'), { recursive: true });
		fs.mkdirSync(path.join(tempDir, '.kiro', 'steering'), { recursive: true });
		fs.mkdirSync(path.join(tempDir, '.kiro', 'settings'), { recursive: true });

		// Create MCP config file at .kiro/settings/mcp.json
		// This will call the mocked writeFileSync
		fs.writeFileSync(
			path.join(tempDir, '.kiro', 'settings', 'mcp.json'),
			JSON.stringify({ mcpServers: {} }, null, 2)
		);

		// Create kiro rule files in steering directory
		// All these will call the mocked writeFileSync
		fs.writeFileSync(
			path.join(tempDir, '.kiro', 'steering', 'kiro_rules.md'),
			'# Kiro Rules\n\nKiro-specific rules and instructions.'
		);
		fs.writeFileSync(
			path.join(tempDir, '.kiro', 'steering', 'dev_workflow.md'),
			'# Development Workflow\n\nDevelopment workflow instructions.'
		);
		fs.writeFileSync(
			path.join(tempDir, '.kiro', 'steering', 'self_improve.md'),
			'# Self Improvement\n\nSelf improvement guidelines.'
		);
		fs.writeFileSync(
			path.join(tempDir, '.kiro', 'steering', 'taskmaster.md'),
			'# Task Master\n\nTask Master integration instructions.'
		);
	}

	test('creates all required .kiro directories', () => {
		// Act
		mockCreateKiroStructure();

		// Assert
		expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(tempDir, '.kiro'), {
			recursive: true
		});
		expect(fs.mkdirSync).toHaveBeenCalledWith(
			path.join(tempDir, '.kiro', 'steering'),
			{
				recursive: true
			}
		);
		expect(fs.mkdirSync).toHaveBeenCalledWith(
			path.join(tempDir, '.kiro', 'settings'),
			{
				recursive: true
			}
		);
	});

	test('creates Kiro mcp.json with mcpServers format', () => {
		// Act
		mockCreateKiroStructure();

		// Assert
		expect(fs.writeFileSync).toHaveBeenCalledWith(
			path.join(tempDir, '.kiro', 'settings', 'mcp.json'),
			JSON.stringify({ mcpServers: {} }, null, 2)
		);
	});

	test('creates rule files in steering directory', () => {
		// Act
		mockCreateKiroStructure();

		// Assert
		expect(fs.writeFileSync).toHaveBeenCalledWith(
			path.join(tempDir, '.kiro', 'steering', 'kiro_rules.md'),
			'# Kiro Rules\n\nKiro-specific rules and instructions.'
		);
		expect(fs.writeFileSync).toHaveBeenCalledWith(
			path.join(tempDir, '.kiro', 'steering', 'dev_workflow.md'),
			'# Development Workflow\n\nDevelopment workflow instructions.'
		);
		expect(fs.writeFileSync).toHaveBeenCalledWith(
			path.join(tempDir, '.kiro', 'steering', 'self_improve.md'),
			'# Self Improvement\n\nSelf improvement guidelines.'
		);
		expect(fs.writeFileSync).toHaveBeenCalledWith(
			path.join(tempDir, '.kiro', 'steering', 'taskmaster.md'),
			'# Task Master\n\nTask Master integration instructions.'
		);
	});
});

```

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

```typescript
/**
 * @fileoverview Briefs Domain Facade
 * Public API for brief-related operations
 */

import {
	ERROR_CODES,
	TaskMasterError
} from '../../common/errors/task-master-error.js';
import { AuthManager } from '../auth/managers/auth-manager.js';
import type { TaskRepository } from '../tasks/repositories/task-repository.interface.js';
import { BriefService, type TagWithStats } from './services/brief-service.js';
import { BriefUrlParser } from './utils/url-parser.js';

/**
 * Briefs Domain - Unified API for brief operations
 * Handles brief switching, matching, and statistics
 */
export class BriefsDomain {
	private briefService: BriefService;
	private authManager: AuthManager;

	constructor() {
		this.briefService = new BriefService();
		this.authManager = AuthManager.getInstance();
	}

	/**
	 * Resolve a brief by name, ID, URL, or partial ID without updating context
	 * Returns the full brief object
	 *
	 * Supports:
	 * - Hamster URLs (e.g., https://app.tryhamster.com/home/hamster/briefs/abc123)
	 * - Full UUID
	 * - Last 8 characters of UUID
	 * - Brief name (exact or partial match)
	 *
	 * @param input - Raw input: URL, UUID, last 8 chars, or brief name
	 * @param orgId - Optional organization ID. If not provided, tries to extract from URL or uses current context.
	 * @returns The resolved brief object
	 */
	async resolveBrief(input: string, orgId?: string): Promise<any> {
		// Parse input using dedicated URL parser
		const parsed = BriefUrlParser.parse(input);
		const briefIdOrName = parsed.briefId || input.trim();

		// Resolve organization ID (priority: parameter > URL > context)
		let resolvedOrgId = orgId;

		// Try to extract org slug from URL if not provided
		if (!resolvedOrgId && parsed.orgSlug) {
			try {
				const orgs = await this.authManager.getOrganizations();
				const matchingOrg = orgs.find(
					(org) =>
						org.slug?.toLowerCase() === parsed.orgSlug?.toLowerCase() ||
						org.name.toLowerCase() === parsed.orgSlug?.toLowerCase()
				);
				if (matchingOrg) {
					resolvedOrgId = matchingOrg.id;
				}
			} catch {
				// If we can't fetch orgs, fall through to context
			}
		}

		// Fall back to context if still not resolved
		if (!resolvedOrgId) {
			resolvedOrgId = this.authManager.getContext()?.orgId;
		}

		if (!resolvedOrgId) {
			throw new TaskMasterError(
				'No organization selected. Run "tm context org" first.',
				ERROR_CODES.CONFIG_ERROR
			);
		}

		// Fetch all briefs for the org
		const briefs = await this.authManager.getBriefs(resolvedOrgId);

		// Find matching brief using service
		const matchingBrief = await this.briefService.findBrief(
			briefs,
			briefIdOrName
		);

		this.briefService.validateBriefFound(matchingBrief, briefIdOrName);

		return matchingBrief;
	}

	/**
	 * Switch to a different brief by name or ID
	 * Validates context, finds matching brief, and updates auth context
	 */
	async switchBrief(briefNameOrId: string): Promise<void> {
		// Use resolveBrief to find the brief
		const matchingBrief = await this.resolveBrief(briefNameOrId);

		// Update context with the found brief
		await this.authManager.updateContext({
			briefId: matchingBrief.id,
			briefName:
				matchingBrief.document?.title || `Brief ${matchingBrief.id.slice(-8)}`,
			briefStatus: matchingBrief.status,
			briefUpdatedAt: matchingBrief.updatedAt
		});
	}

	/**
	 * Get all briefs with detailed statistics including task counts
	 * Used for API storage to show brief statistics
	 */
	async getBriefsWithStats(
		repository: TaskRepository,
		projectId: string
	): Promise<{
		tags: TagWithStats[];
		currentTag: string | null;
		totalTags: number;
	}> {
		const context = this.authManager.getContext();

		if (!context?.orgId) {
			throw new TaskMasterError(
				'No organization context available',
				ERROR_CODES.MISSING_CONFIGURATION,
				{
					operation: 'getBriefsWithStats',
					userMessage:
						'No organization selected. Please authenticate first using: tm auth login'
				}
			);
		}

		// Get all briefs for the organization (through auth manager)
		const briefs = await this.authManager.getBriefs(context.orgId);

		// Use BriefService to calculate stats
		return this.briefService.getTagsWithStats(
			briefs,
			context.briefId,
			repository,
			projectId
		);
	}
}

```

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

```markdown
# Task Master Command Reference

Comprehensive command structure for Task Master integration with Claude Code.

## Command Organization

Commands are organized hierarchically to match Task Master's CLI structure while providing enhanced Claude Code integration.

## Project Setup & Configuration

### `/taskmaster:init`
- `init-project` - Initialize new project (handles PRD files intelligently)
- `init-project-quick` - Quick setup with auto-confirmation (-y flag)

### `/taskmaster:models`
- `view-models` - View current AI model configuration
- `setup-models` - Interactive model configuration
- `set-main` - Set primary generation model
- `set-research` - Set research model
- `set-fallback` - Set fallback model

## Task Generation

### `/taskmaster:parse-prd`
- `parse-prd` - Generate tasks from PRD document
- `parse-prd-with-research` - Enhanced parsing with research mode

### `/taskmaster:generate`
- `generate-tasks` - Create individual task files from tasks.json

## Task Management

### `/taskmaster:list`
- `list-tasks` - Smart listing with natural language filters
- `list-tasks-with-subtasks` - Include subtasks in hierarchical view
- `list-tasks-by-status` - Filter by specific status

### `/taskmaster:set-status`
- `to-pending` - Reset task to pending
- `to-in-progress` - Start working on task
- `to-done` - Mark task complete
- `to-review` - Submit for review
- `to-deferred` - Defer task
- `to-cancelled` - Cancel task

### `/taskmaster:sync-readme`
- `sync-readme` - Export tasks to README.md with formatting

### `/taskmaster:update`
- `update-task` - Update tasks with natural language
- `update-tasks-from-id` - Update multiple tasks from a starting point
- `update-single-task` - Update specific task

### `/taskmaster:add-task`
- `add-task` - Add new task with AI assistance

### `/taskmaster:remove-task`
- `remove-task` - Remove task with confirmation

## Subtask Management

### `/taskmaster:add-subtask`
- `add-subtask` - Add new subtask to parent
- `convert-task-to-subtask` - Convert existing task to subtask

### `/taskmaster:remove-subtask`
- `remove-subtask` - Remove subtask (with optional conversion)

### `/taskmaster:clear-subtasks`
- `clear-subtasks` - Clear subtasks from specific task
- `clear-all-subtasks` - Clear all subtasks globally

## Task Analysis & Breakdown

### `/taskmaster:analyze-complexity`
- `analyze-complexity` - Analyze and generate expansion recommendations

### `/taskmaster:complexity-report`
- `complexity-report` - Display complexity analysis report

### `/taskmaster:expand`
- `expand-task` - Break down specific task
- `expand-all-tasks` - Expand all eligible tasks
- `with-research` - Enhanced expansion

## Task Navigation

### `/taskmaster:next`
- `next-task` - Intelligent next task recommendation

### `/taskmaster:show`
- `show-task` - Display detailed task information

### `/taskmaster:status`
- `project-status` - Comprehensive project dashboard

## Dependency Management

### `/taskmaster:add-dependency`
- `add-dependency` - Add task dependency

### `/taskmaster:remove-dependency`
- `remove-dependency` - Remove task dependency

### `/taskmaster:validate-dependencies`
- `validate-dependencies` - Check for dependency issues

### `/taskmaster:fix-dependencies`
- `fix-dependencies` - Automatically fix dependency problems

## Workflows & Automation

### `/taskmaster:workflows`
- `smart-workflow` - Context-aware intelligent workflow execution
- `command-pipeline` - Chain multiple commands together
- `auto-implement-tasks` - Advanced auto-implementation with code generation

## Utilities

### `/taskmaster:utils`
- `analyze-project` - Deep project analysis and insights

### `/taskmaster:setup`
- `install-taskmaster` - Comprehensive installation guide
- `quick-install-taskmaster` - One-line global installation

## Usage Patterns

### Natural Language
Most commands accept natural language arguments:
```
/taskmaster:add-task create user authentication system
/taskmaster:update mark all API tasks as high priority
/taskmaster:list show blocked tasks
```

### ID-Based Commands
Commands requiring IDs intelligently parse from $ARGUMENTS:
```
/taskmaster:show 45
/taskmaster:expand 23
/taskmaster:set-status/to-done 67
```

### Smart Defaults
Commands provide intelligent defaults and suggestions based on context.
```

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

```yaml
name: CI

on:
  push:
    branches:
      - main
      - next
  pull_request:
  workflow_dispatch:

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

env:
  DO_NOT_TRACK: 1
  NODE_ENV: development

jobs:
  # Fast checks that can run in parallel
  format-check:
    name: Format Check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - name: Install dependencies
        run: npm install --frozen-lockfile --prefer-offline
        timeout-minutes: 5

      - name: Format Check
        run: npm run format-check
        env:
          FORCE_COLOR: 1

  changeset-validation:
    name: Validate Changesets
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: dorny/paths-filter@v3
        id: changes
        with:
          filters: |
            changesets:
              - '.changeset/**'
              - '.github/scripts/validate-changesets.mjs'

      - uses: actions/setup-node@v4
        if: steps.changes.outputs.changesets == 'true'
        with:
          node-version: 20
          cache: "npm"

      - name: Validate changeset package references
        if: steps.changes.outputs.changesets == 'true'
        run: |
          # Validate that changesets only reference public packages
          # This catches issues like using @tm/cli instead of task-master-ai
          node .github/scripts/validate-changesets.mjs
        env:
          FORCE_COLOR: 1

  typecheck:
    name: Typecheck
    timeout-minutes: 10
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - name: Install dependencies
        run: npm install --frozen-lockfile --prefer-offline
        timeout-minutes: 5

      - name: Typecheck
        run: npm run turbo:typecheck
        env:
          FORCE_COLOR: 1

  # Build job to ensure everything compiles
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - name: Install dependencies
        run: npm install --frozen-lockfile --prefer-offline
        timeout-minutes: 5

      - name: Build
        run: npm run turbo:build
        env:
          NODE_ENV: production
          FORCE_COLOR: 1
          TM_PUBLIC_BASE_DOMAIN: ${{ secrets.TM_PUBLIC_BASE_DOMAIN }}
          TM_PUBLIC_SUPABASE_URL: ${{ secrets.TM_PUBLIC_SUPABASE_URL }}
          TM_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.TM_PUBLIC_SUPABASE_ANON_KEY }}

      - name: Upload build artifacts
        uses: actions/upload-artifact@v4
        with:
          name: build-artifacts
          path: dist/
          retention-days: 1

  test:
    name: Test
    timeout-minutes: 15
    runs-on: ubuntu-latest
    needs: [format-check, typecheck, build, changeset-validation]
    if: always() && !cancelled() && !contains(needs.*.result, 'failure')
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - name: Install dependencies
        run: npm install --frozen-lockfile --prefer-offline
        timeout-minutes: 5

      - name: Download build artifacts
        uses: actions/download-artifact@v4
        with:
          name: build-artifacts
          path: dist/

      - name: Run Tests
        run: |
          npm run test:coverage -- --coverageThreshold '{"global":{"branches":0,"functions":0,"lines":0,"statements":0}}' --detectOpenHandles --forceExit
        env:
          NODE_ENV: test
          CI: true
          FORCE_COLOR: 1

      - name: Upload Test Results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-results
          path: |
            test-results
            coverage
            junit.xml
          retention-days: 30

```

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

```javascript
/**
 * create-tag-from-branch.js
 * Direct function implementation for creating tags from git branches
 */

import { createTagFromBranch } from '../../../../scripts/modules/task-manager/tag-management.js';
import {
	getCurrentBranch,
	isGitRepository
} from '../../../../scripts/modules/utils/git-utils.js';
import {
	enableSilentMode,
	disableSilentMode
} from '../../../../scripts/modules/utils.js';
import { createLogWrapper } from '../../tools/utils.js';

/**
 * Direct function wrapper for creating tags from git branches with error handling.
 *
 * @param {Object} args - Command arguments
 * @param {string} args.tasksJsonPath - Path to the tasks.json file (resolved by tool)
 * @param {string} [args.branchName] - Git branch name (optional, uses current branch if not provided)
 * @param {boolean} [args.copyFromCurrent] - Copy tasks from current tag
 * @param {string} [args.copyFromTag] - Copy tasks from specific tag
 * @param {string} [args.description] - Custom description for the tag
 * @param {boolean} [args.autoSwitch] - Automatically switch to the new tag
 * @param {string} [args.projectRoot] - Project root path
 * @param {Object} log - Logger object
 * @param {Object} context - Additional context (session)
 * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 */
export async function createTagFromBranchDirect(args, log, context = {}) {
	// Destructure expected args
	const {
		tasksJsonPath,
		branchName,
		copyFromCurrent,
		copyFromTag,
		description,
		autoSwitch,
		projectRoot
	} = args;
	const { session } = context;

	// Enable silent mode to prevent console logs from interfering with JSON response
	enableSilentMode();

	// Create logger wrapper using the utility
	const mcpLog = createLogWrapper(log);

	try {
		// Check if tasksJsonPath was provided
		if (!tasksJsonPath) {
			log.error('createTagFromBranchDirect called without tasksJsonPath');
			disableSilentMode();
			return {
				success: false,
				error: {
					code: 'MISSING_ARGUMENT',
					message: 'tasksJsonPath is required'
				}
			};
		}

		// Check if projectRoot was provided
		if (!projectRoot) {
			log.error('createTagFromBranchDirect called without projectRoot');
			disableSilentMode();
			return {
				success: false,
				error: {
					code: 'MISSING_ARGUMENT',
					message: 'projectRoot is required'
				}
			};
		}

		// Check if we're in a git repository
		if (!(await isGitRepository(projectRoot))) {
			log.error('Not in a git repository');
			disableSilentMode();
			return {
				success: false,
				error: {
					code: 'NOT_GIT_REPOSITORY',
					message: 'Not in a git repository. Cannot create tag from branch.'
				}
			};
		}

		// Determine branch name
		let targetBranch = branchName;
		if (!targetBranch) {
			targetBranch = await getCurrentBranch(projectRoot);
			if (!targetBranch) {
				log.error('Could not determine current git branch');
				disableSilentMode();
				return {
					success: false,
					error: {
						code: 'NO_CURRENT_BRANCH',
						message: 'Could not determine current git branch'
					}
				};
			}
		}

		log.info(`Creating tag from git branch: ${targetBranch}`);

		// Prepare options
		const options = {
			copyFromCurrent: copyFromCurrent || false,
			copyFromTag,
			description:
				description || `Tag created from git branch "${targetBranch}"`,
			autoSwitch: autoSwitch || false
		};

		// Call the createTagFromBranch function
		const result = await createTagFromBranch(
			tasksJsonPath,
			targetBranch,
			options,
			{
				session,
				mcpLog,
				projectRoot
			},
			'json' // outputFormat - use 'json' to suppress CLI UI
		);

		// Restore normal logging
		disableSilentMode();

		return {
			success: true,
			data: {
				branchName: result.branchName,
				tagName: result.tagName,
				created: result.created,
				mappingUpdated: result.mappingUpdated,
				autoSwitched: result.autoSwitched,
				message: `Successfully created tag "${result.tagName}" from branch "${result.branchName}"`
			}
		};
	} catch (error) {
		// Make sure to restore normal logging even if there's an error
		disableSilentMode();

		log.error(`Error in createTagFromBranchDirect: ${error.message}`);
		return {
			success: false,
			error: {
				code: error.code || 'CREATE_TAG_FROM_BRANCH_ERROR',
				message: error.message
			}
		};
	}
}

```

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

```javascript
/**
 * Direct function wrapper for addSubtask
 */

import { addSubtask } from '../../../../scripts/modules/task-manager.js';
import {
	enableSilentMode,
	disableSilentMode
} from '../../../../scripts/modules/utils.js';

/**
 * Add a subtask to an existing task
 * @param {Object} args - Function arguments
 * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 * @param {string} args.id - Parent task ID
 * @param {string} [args.taskId] - Existing task ID to convert to subtask (optional)
 * @param {string} [args.title] - Title for new subtask (when creating a new subtask)
 * @param {string} [args.description] - Description for new subtask
 * @param {string} [args.details] - Implementation details for new subtask
 * @param {string} [args.status] - Status for new subtask (default: 'pending')
 * @param {string} [args.dependencies] - Comma-separated list of dependency IDs
 * @param {boolean} [args.skipGenerate] - Skip regenerating task files
 * @param {string} [args.projectRoot] - Project root directory
 * @param {string} [args.tag] - Tag for the task
 * @param {Object} log - Logger object
 * @returns {Promise<{success: boolean, data?: Object, error?: string}>}
 */
export async function addSubtaskDirect(args, log) {
	// Destructure expected args
	const {
		tasksJsonPath,
		id,
		taskId,
		title,
		description,
		details,
		status,
		dependencies: dependenciesStr,
		skipGenerate,
		projectRoot,
		tag
	} = args;
	try {
		log.info(`Adding subtask with args: ${JSON.stringify(args)}`);

		// Check if tasksJsonPath was provided
		if (!tasksJsonPath) {
			log.error('addSubtaskDirect called without tasksJsonPath');
			return {
				success: false,
				error: {
					code: 'MISSING_ARGUMENT',
					message: 'tasksJsonPath is required'
				}
			};
		}

		if (!id) {
			return {
				success: false,
				error: {
					code: 'INPUT_VALIDATION_ERROR',
					message: 'Parent task ID is required'
				}
			};
		}

		// Either taskId or title must be provided
		if (!taskId && !title) {
			return {
				success: false,
				error: {
					code: 'INPUT_VALIDATION_ERROR',
					message: 'Either taskId or title must be provided'
				}
			};
		}

		// Use provided path
		const tasksPath = tasksJsonPath;

		// Parse dependencies if provided
		let dependencies = [];
		if (dependenciesStr) {
			dependencies = dependenciesStr.split(',').map((depId) => {
				// Handle both regular IDs and dot notation
				return depId.includes('.') ? depId.trim() : parseInt(depId.trim(), 10);
			});
		}

		// Convert existingTaskId to a number if provided
		const existingTaskId = taskId ? parseInt(taskId, 10) : null;

		// Convert parent ID to a number
		const parentId = parseInt(id, 10);

		// Determine if we should generate files
		const generateFiles = !skipGenerate;

		// Enable silent mode to prevent console logs from interfering with JSON response
		enableSilentMode();

		const context = { projectRoot, tag };

		// Case 1: Convert existing task to subtask
		if (existingTaskId) {
			log.info(`Converting task ${existingTaskId} to a subtask of ${parentId}`);
			const result = await addSubtask(
				tasksPath,
				parentId,
				existingTaskId,
				null,
				generateFiles,
				context
			);

			// Restore normal logging
			disableSilentMode();

			return {
				success: true,
				data: {
					message: `Task ${existingTaskId} successfully converted to a subtask of task ${parentId}`,
					subtask: result
				}
			};
		}
		// Case 2: Create new subtask
		else {
			log.info(`Creating new subtask for parent task ${parentId}`);

			const newSubtaskData = {
				title: title,
				description: description || '',
				details: details || '',
				status: status || 'pending',
				dependencies: dependencies
			};

			const result = await addSubtask(
				tasksPath,
				parentId,
				null,
				newSubtaskData,
				generateFiles,
				context
			);

			// Restore normal logging
			disableSilentMode();

			return {
				success: true,
				data: {
					message: `New subtask ${parentId}.${result.id} successfully created`,
					subtask: result
				}
			};
		}
	} catch (error) {
		// Make sure to restore normal logging even if there's an error
		disableSilentMode();

		log.error(`Error in addSubtaskDirect: ${error.message}`);
		return {
			success: false,
			error: {
				code: 'CORE_FUNCTION_ERROR',
				message: error.message
			}
		};
	}
}

```

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

```markdown
---
title: Configuration
sidebarTitle: "Configuration"

---

Before getting started with Task Master, you'll need to set up your API keys. There are a couple of ways to do this depending on whether you're using the CLI or working inside MCP. It's also a good time to start getting familiar with the other configuration options available — even if you don’t need to adjust them yet, knowing what’s possible will help down the line.

## API Key Setup

Task Master uses environment variables to securely store provider API keys and optional endpoint URLs.

### MCP Usage: mcp.json file

For MCP/Cursor usage: Configure keys in the env section of your .cursor/mcp.json file.

```java .env lines icon="java"
{
	"mcpServers": {
		"task-master-ai": {
			"command": "npx",
			"args": ["-y", "task-master-ai"],
			"env": {
				"ANTHROPIC_API_KEY": "ANTHROPIC_API_KEY_HERE",
				"PERPLEXITY_API_KEY": "PERPLEXITY_API_KEY_HERE",
				"OPENAI_API_KEY": "OPENAI_API_KEY_HERE",
				"GOOGLE_API_KEY": "GOOGLE_API_KEY_HERE",
				"XAI_API_KEY": "XAI_API_KEY_HERE",
				"OPENROUTER_API_KEY": "OPENROUTER_API_KEY_HERE",
				"MISTRAL_API_KEY": "MISTRAL_API_KEY_HERE",
				"AZURE_OPENAI_API_KEY": "AZURE_OPENAI_API_KEY_HERE",
				"OLLAMA_API_KEY": "OLLAMA_API_KEY_HERE",
				"GITHUB_API_KEY": "GITHUB_API_KEY_HERE"
			}
		}
	}
}
```

<Tip>
**Optimize Context Usage**: You can control which Task Master MCP tools are loaded using the `TASK_MASTER_TOOLS` environment variable. This helps reduce LLM context usage by only loading the tools you need.

Options:
- `all` (default) - All 36 tools
- `standard` - 15 commonly used tools
- `core` or `lean` - 7 essential tools

Example:
```json
"env": {
  "TASK_MASTER_TOOLS": "standard",
  "ANTHROPIC_API_KEY": "your_key_here"
}
```

See the [MCP Tools documentation](/capabilities/mcp#configurable-tool-loading) for details.
</Tip>

### CLI Usage: `.env` File

Create a `.env` file in your project root and include the keys for the providers you plan to use:



```java .env lines icon="java"
# Required API keys for providers configured in .taskmaster/config.json
ANTHROPIC_API_KEY=sk-ant-api03-your-key-here
PERPLEXITY_API_KEY=pplx-your-key-here
# OPENAI_API_KEY=sk-your-key-here
# GOOGLE_API_KEY=AIzaSy...
# AZURE_OPENAI_API_KEY=your-azure-openai-api-key-here
# etc.

# Optional Endpoint Overrides
# Use a specific provider's base URL, e.g., for an OpenAI-compatible API
# OPENAI_BASE_URL=https://api.third-party.com/v1
#
# Azure OpenAI Configuration
# AZURE_OPENAI_ENDPOINT=https://your-resource-name.openai.azure.com/ or https://your-endpoint-name.cognitiveservices.azure.com/openai/deployments
# OLLAMA_BASE_URL=http://custom-ollama-host:11434/api

# Google Vertex AI Configuration (Required if using 'vertex' provider)
# VERTEX_PROJECT_ID=your-gcp-project-id
```

## What Else Can Be Configured?

The main configuration file (`.taskmaster/config.json`) allows you to control nearly every aspect of Task Master’s behavior. Here’s a high-level look at what you can customize:

<Tip>
You don’t need to configure everything up front. Most settings can be left as defaults or updated later as your workflow evolves.
</Tip>

<Accordion title="View Configuration Options">

### Models and Providers
- Role-based model setup: `main`, `research`, `fallback`
- Provider selection (Anthropic, OpenAI, Perplexity, etc.)
- Model IDs per role
- Temperature, max tokens, and other generation settings
- Custom base URLs for OpenAI-compatible APIs

### Global Settings
- `logLevel`: Logging verbosity
- `debug`: Enable/disable debug mode
- `projectName`: Optional name for your project
- `defaultTag`: Default tag for task grouping
- `defaultSubtasks`: Number of subtasks to auto-generate
- `defaultPriority`: Priority level for new tasks

### API Endpoint Overrides
- `ollamaBaseURL`: Custom Ollama server URL
- `azureBaseURL`: Global Azure endpoint
- `vertexProjectId`: Google Vertex AI project ID
- `vertexLocation`: Region for Vertex AI models

### Tag and Git Integration
- Default tag context per project
- Support for task isolation by tag
- Manual tag creation from Git branches

### State Management
- Active tag tracking
- Migration state
- Last tag switch timestamp

</Accordion>

<Note>
For advanced configuration options and detailed customization, see our [Advanced Configuration Guide](/best-practices/configuration-advanced) page.
</Note>
```

--------------------------------------------------------------------------------
/packages/tm-bridge/src/expand-bridge.ts:
--------------------------------------------------------------------------------

```typescript
import boxen from 'boxen';
import chalk from 'chalk';
import ora from 'ora';
import type { BaseBridgeParams } from './bridge-types.js';
import { checkStorageType } from './bridge-utils.js';

/**
 * Parameters for the expand bridge function
 */
export interface ExpandBridgeParams extends BaseBridgeParams {
	/** Task ID (can be numeric "1" or alphanumeric "TAS-49") */
	taskId: string | number;
	/** Number of subtasks to generate (optional) */
	numSubtasks?: number;
	/** Whether to use research AI */
	useResearch?: boolean;
	/** Additional context for generation */
	additionalContext?: string;
	/** Force regeneration even if subtasks exist */
	force?: boolean;
}

/**
 * Result returned when API storage handles the expansion
 */
export interface RemoteExpandResult {
	success: boolean;
	taskId: string | number;
	message: string;
	telemetryData: null;
	tagInfo: null;
}

/**
 * Shared bridge function for expand-task command.
 * Checks if using API storage and delegates to remote AI service if so.
 *
 * @param params - Bridge parameters
 * @returns Result object if API storage handled it, null if should fall through to file storage
 */
export async function tryExpandViaRemote(
	params: ExpandBridgeParams
): Promise<RemoteExpandResult | null> {
	const {
		taskId,
		numSubtasks,
		useResearch = false,
		additionalContext,
		force = false,
		projectRoot,
		tag,
		isMCP = false,
		outputFormat = 'text',
		report
	} = params;

	// Check storage type using shared utility
	const { isApiStorage, tmCore } = await checkStorageType(
		projectRoot,
		report,
		'falling back to file-based expansion'
	);

	if (!isApiStorage || !tmCore) {
		// Not API storage - signal caller to fall through to file-based logic
		return null;
	}

	// API STORAGE PATH: Delegate to remote AI service
	report('info', `Delegating expansion to Hamster for task ${taskId}`);

	// Show CLI output if not MCP
	if (!isMCP && outputFormat === 'text') {
		const showDebug = process.env.TM_DEBUG === '1';
		const contextPreview =
			showDebug && additionalContext
				? `${additionalContext.substring(0, 60)}${additionalContext.length > 60 ? '...' : ''}`
				: additionalContext
					? '[provided]'
					: '[none]';

		console.log(
			boxen(
				chalk.blue.bold(`Expanding Task via Hamster`) +
					'\n\n' +
					chalk.white(`Task ID: ${taskId}`) +
					'\n' +
					chalk.white(`Subtasks: ${numSubtasks || 'auto'}`) +
					'\n' +
					chalk.white(`Use Research: ${useResearch ? 'yes' : 'no'}`) +
					'\n' +
					chalk.white(`Force: ${force ? 'yes' : 'no'}`) +
					'\n' +
					chalk.white(`Context: ${contextPreview}`),
				{
					padding: 1,
					borderColor: 'blue',
					borderStyle: 'round',
					margin: { top: 1, bottom: 1 }
				}
			)
		);
	}

	const spinner =
		!isMCP && outputFormat === 'text'
			? ora({ text: 'Expanding task on Hamster...', color: 'cyan' }).start()
			: null;

	try {
		// Call the API storage method which handles the remote expansion
		const result = await tmCore.tasks.expand(String(taskId), tag, {
			numSubtasks,
			useResearch,
			additionalContext,
			force
		});

		if (spinner) {
			spinner.succeed('Task expansion queued successfully');
		}

		if (outputFormat === 'text') {
			// Build message conditionally based on result
			let messageLines = [
				chalk.green(`Successfully queued expansion for task ${taskId}`),
				'',
				chalk.white('The task expansion has been queued on Hamster'),
				chalk.white('Subtasks will be generated in the background.')
			];

			// Add task link if available
			if (result?.taskLink) {
				messageLines.push('');
				messageLines.push(
					chalk.white('View task: ') + chalk.blue.underline(result.taskLink)
				);
			}

			// Always add CLI alternative
			messageLines.push('');
			messageLines.push(
				chalk.dim(`Or run: ${chalk.yellow(`task-master show ${taskId}`)}`)
			);

			console.log(
				boxen(messageLines.join('\n'), {
					padding: 1,
					borderColor: 'green',
					borderStyle: 'round'
				})
			);
		}

		// Return success result - signals that we handled it
		return {
			success: true,
			taskId: taskId,
			message: result?.message || 'Task expansion queued via remote AI service',
			telemetryData: null,
			tagInfo: null
		};
	} catch (expandError) {
		if (spinner) {
			spinner.fail('Expansion failed');
		}

		// tm-core already formatted the error properly, just re-throw
		throw expandError;
	}
}

```

--------------------------------------------------------------------------------
/apps/mcp/src/tools/autopilot/complete.tool.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview autopilot-complete MCP tool
 * Complete the current TDD phase with test result validation
 */

import { z } from 'zod';
import {
	handleApiResult,
	withNormalizedProjectRoot
} from '../../shared/utils.js';
import type { MCPContext } from '../../shared/types.js';
import { WorkflowService } from '@tm/core';
import type { FastMCP } from 'fastmcp';

const CompletePhaseSchema = z.object({
	projectRoot: z
		.string()
		.describe('Absolute path to the project root directory'),
	testResults: z
		.object({
			total: z.number().describe('Total number of tests'),
			passed: z.number().describe('Number of passing tests'),
			failed: z.number().describe('Number of failing tests'),
			skipped: z.number().optional().describe('Number of skipped tests')
		})
		.describe('Test results from running the test suite')
});

type CompletePhaseArgs = z.infer<typeof CompletePhaseSchema>;

/**
 * Register the autopilot_complete_phase tool with the MCP server
 */
export function registerAutopilotCompleteTool(server: FastMCP) {
	server.addTool({
		name: 'autopilot_complete_phase',
		description:
			'Complete the current TDD phase (RED, GREEN, or COMMIT) with test result validation. RED phase: expects failures (if 0 failures, feature is already implemented and subtask auto-completes). GREEN phase: expects all tests passing.',
		parameters: CompletePhaseSchema,
		execute: withNormalizedProjectRoot(
			async (args: CompletePhaseArgs, context: MCPContext) => {
				const { projectRoot, testResults } = args;

				try {
					context.log.info(
						`Completing current phase in workflow for ${projectRoot}`
					);

					const workflowService = new WorkflowService(projectRoot);

					// Check if workflow exists
					if (!(await workflowService.hasWorkflow())) {
						return handleApiResult({
							result: {
								success: false,
								error: {
									message:
										'No active workflow found. Start a workflow with autopilot_start'
								}
							},
							log: context.log,
							projectRoot
						});
					}

					// Resume workflow to get current state
					await workflowService.resumeWorkflow();
					const currentStatus = workflowService.getStatus();

					// Validate that we're in a TDD phase (RED or GREEN)
					if (!currentStatus.tddPhase) {
						return handleApiResult({
							result: {
								success: false,
								error: {
									message: `Cannot complete phase: not in a TDD phase (current phase: ${currentStatus.phase})`
								}
							},
							log: context.log,
							projectRoot
						});
					}

					// COMMIT phase completion is handled by autopilot_commit tool
					if (currentStatus.tddPhase === 'COMMIT') {
						return handleApiResult({
							result: {
								success: false,
								error: {
									message:
										'Cannot complete COMMIT phase with this tool. Use autopilot_commit instead'
								}
							},
							log: context.log,
							projectRoot
						});
					}

					// Map TDD phase to TestResult phase (only RED or GREEN allowed)
					const phase = currentStatus.tddPhase as 'RED' | 'GREEN';

					// Construct full TestResult with phase
					const fullTestResults = {
						total: testResults.total,
						passed: testResults.passed,
						failed: testResults.failed,
						skipped: testResults.skipped ?? 0,
						phase
					};

					// Complete phase with test results
					const status = await workflowService.completePhase(fullTestResults);
					const nextAction = workflowService.getNextAction();

					context.log.info(
						`Phase completed. New phase: ${status.tddPhase || status.phase}`
					);

					return handleApiResult({
						result: {
							success: true,
							data: {
								message: `Phase completed. Transitioned to ${status.tddPhase || status.phase}`,
								...status,
								nextAction: nextAction.action,
								actionDescription: nextAction.description,
								nextSteps: nextAction.nextSteps
							}
						},
						log: context.log,
						projectRoot
					});
				} catch (error: any) {
					context.log.error(`Error in autopilot-complete: ${error.message}`);
					if (error.stack) {
						context.log.debug(error.stack);
					}
					return handleApiResult({
						result: {
							success: false,
							error: {
								message: `Failed to complete phase: ${error.message}`
							}
						},
						log: context.log,
						projectRoot
					});
				}
			}
		)
	});
}

```

--------------------------------------------------------------------------------
/apps/cli/src/commands/autopilot/commit.command.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Commit Command - Create commit with enhanced message generation
 */

import { CommitMessageGenerator, GitAdapter, WorkflowService } from '@tm/core';
import { Command } from 'commander';
import { AutopilotBaseOptions, OutputFormatter } from './shared.js';
import { getProjectRoot } from '../../utils/project-root.js';

type CommitOptions = AutopilotBaseOptions;

/**
 * Commit Command - Create commit using enhanced message generator
 */
export class CommitCommand extends Command {
	constructor() {
		super('commit');

		this.description('Create a commit for the completed GREEN phase').action(
			async (options: CommitOptions) => {
				await this.execute(options);
			}
		);
	}

	private async execute(options: CommitOptions): Promise<void> {
		// Inherit parent options
		const parentOpts = this.parent?.opts() as AutopilotBaseOptions;
		const mergedOptions: CommitOptions = {
			...parentOpts,
			...options,
			projectRoot: getProjectRoot(
				options.projectRoot || parentOpts?.projectRoot
			)
		};

		const formatter = new OutputFormatter(mergedOptions.json || false);

		try {
			const projectRoot = mergedOptions.projectRoot!;

			// Create workflow service (manages WorkflowStateManager internally)
			const workflowService = new WorkflowService(projectRoot);

			// Check if workflow exists
			if (!(await workflowService.hasWorkflow())) {
				formatter.error('No active workflow', {
					suggestion: 'Start a workflow with: autopilot start <taskId>'
				});
				process.exit(1);
			}

			// Resume workflow (loads state with single WorkflowStateManager instance)
			await workflowService.resumeWorkflow();
			const status = workflowService.getStatus();
			const workflowContext = workflowService.getContext();

			// Verify in COMMIT phase
			if (status.tddPhase !== 'COMMIT') {
				formatter.error('Not in COMMIT phase', {
					currentPhase: status.tddPhase || status.phase,
					suggestion: 'Complete RED and GREEN phases first'
				});
				process.exit(1);
			}

			// Verify there's an active subtask
			if (!status.currentSubtask) {
				formatter.error('No current subtask');
				process.exit(1);
			}

			// Initialize git adapter
			const gitAdapter = new GitAdapter(projectRoot);
			await gitAdapter.ensureGitRepository();

			// Check for staged changes
			const hasStagedChanges = await gitAdapter.hasStagedChanges();
			if (!hasStagedChanges) {
				// Stage all changes
				formatter.info('No staged changes, staging all changes...');
				await gitAdapter.stageFiles(['.']);
			}

			// Get changed files for scope detection
			const gitStatus = await gitAdapter.getStatus();
			const changedFiles = [...gitStatus.staged, ...gitStatus.modified];

			// Generate commit message
			const messageGenerator = new CommitMessageGenerator();
			const testResults = workflowContext.lastTestResults;

			const commitMessage = messageGenerator.generateMessage({
				type: 'feat',
				description: status.currentSubtask.title,
				changedFiles,
				taskId: status.taskId,
				phase: status.tddPhase,
				tag: (workflowContext.metadata.tag as string) || undefined,
				testsPassing: testResults?.passed,
				testsFailing: testResults?.failed,
				coveragePercent: undefined // Could be added if available
			});

			// Create commit with metadata
			await gitAdapter.createCommit(commitMessage, {
				metadata: {
					taskId: status.taskId,
					subtaskId: status.currentSubtask.id,
					phase: 'COMMIT',
					tddCycle: 'complete'
				}
			});

			// Get commit info
			const lastCommit = await gitAdapter.getLastCommit();

			// Complete COMMIT phase and advance workflow
			// This handles all transitions internally with a single WorkflowStateManager
			const newStatus = await workflowService.commit();

			const isComplete = newStatus.phase === 'COMPLETE';

			// Output success
			formatter.success('Commit created', {
				commitHash: lastCommit.hash.substring(0, 7),
				message: commitMessage.split('\n')[0], // First line only
				subtask: {
					id: status.currentSubtask.id,
					title: status.currentSubtask.title
				},
				progress: newStatus.progress,
				nextAction: isComplete
					? 'All subtasks complete. Run: autopilot status'
					: 'Start next subtask with RED phase'
			});
		} catch (error) {
			formatter.error((error as Error).message);
			if (mergedOptions.verbose) {
				console.error((error as Error).stack);
			}
			process.exit(1);
		}
	}
}

```

--------------------------------------------------------------------------------
/mcp-server/src/core/context-manager.js:
--------------------------------------------------------------------------------

```javascript
/**
 * context-manager.js
 * Context and cache management for Task Master MCP Server
 */

import { FastMCP } from 'fastmcp';
import { LRUCache } from 'lru-cache';

/**
 * Configuration options for the ContextManager
 * @typedef {Object} ContextManagerConfig
 * @property {number} maxCacheSize - Maximum number of items in the cache
 * @property {number} ttl - Time to live for cached items in milliseconds
 * @property {number} maxContextSize - Maximum size of context window in tokens
 */

export class ContextManager {
	/**
	 * Create a new ContextManager instance
	 * @param {ContextManagerConfig} config - Configuration options
	 */
	constructor(config = {}) {
		this.config = {
			maxCacheSize: config.maxCacheSize || 1000,
			ttl: config.ttl || 1000 * 60 * 5, // 5 minutes default
			maxContextSize: config.maxContextSize || 4000
		};

		// Initialize LRU cache for context data
		this.cache = new LRUCache({
			max: this.config.maxCacheSize,
			ttl: this.config.ttl,
			updateAgeOnGet: true
		});

		// Cache statistics
		this.stats = {
			hits: 0,
			misses: 0,
			invalidations: 0
		};
	}

	/**
	 * Create a new context or retrieve from cache
	 * @param {string} contextId - Unique identifier for the context
	 * @param {Object} metadata - Additional metadata for the context
	 * @returns {Object} Context object with metadata
	 */
	async getContext(contextId, metadata = {}) {
		const cacheKey = this._getCacheKey(contextId, metadata);

		// Try to get from cache first
		const cached = this.cache.get(cacheKey);
		if (cached) {
			this.stats.hits++;
			return cached;
		}

		this.stats.misses++;

		// Create new context if not in cache
		const context = {
			id: contextId,
			metadata: {
				...metadata,
				created: new Date().toISOString()
			}
		};

		// Cache the new context
		this.cache.set(cacheKey, context);

		return context;
	}

	/**
	 * Update an existing context
	 * @param {string} contextId - Context identifier
	 * @param {Object} updates - Updates to apply to the context
	 * @returns {Object} Updated context
	 */
	async updateContext(contextId, updates) {
		const context = await this.getContext(contextId);

		// Apply updates to context
		Object.assign(context.metadata, updates);

		// Update cache
		const cacheKey = this._getCacheKey(contextId, context.metadata);
		this.cache.set(cacheKey, context);

		return context;
	}

	/**
	 * Invalidate a context in the cache
	 * @param {string} contextId - Context identifier
	 * @param {Object} metadata - Metadata used in the cache key
	 */
	invalidateContext(contextId, metadata = {}) {
		const cacheKey = this._getCacheKey(contextId, metadata);
		this.cache.delete(cacheKey);
		this.stats.invalidations++;
	}

	/**
	 * Get cached data associated with a specific key.
	 * Increments cache hit stats if found.
	 * @param {string} key - The cache key.
	 * @returns {any | undefined} The cached data or undefined if not found/expired.
	 */
	getCachedData(key) {
		const cached = this.cache.get(key);
		if (cached !== undefined) {
			// Check for undefined specifically, as null/false might be valid cached values
			this.stats.hits++;
			return cached;
		}
		this.stats.misses++;
		return undefined;
	}

	/**
	 * Set data in the cache with a specific key.
	 * @param {string} key - The cache key.
	 * @param {any} data - The data to cache.
	 */
	setCachedData(key, data) {
		this.cache.set(key, data);
	}

	/**
	 * Invalidate a specific cache key.
	 * Increments invalidation stats.
	 * @param {string} key - The cache key to invalidate.
	 */
	invalidateCacheKey(key) {
		this.cache.delete(key);
		this.stats.invalidations++;
	}

	/**
	 * Get cache statistics
	 * @returns {Object} Cache statistics
	 */
	getStats() {
		return {
			hits: this.stats.hits,
			misses: this.stats.misses,
			invalidations: this.stats.invalidations,
			size: this.cache.size,
			maxSize: this.config.maxCacheSize,
			ttl: this.config.ttl
		};
	}

	/**
	 * Generate a cache key from context ID and metadata
	 * @private
	 * @deprecated No longer used for direct cache key generation outside the manager.
	 *             Prefer generating specific keys in calling functions.
	 */
	_getCacheKey(contextId, metadata) {
		// Kept for potential backward compatibility or internal use if needed later.
		return `${contextId}:${JSON.stringify(metadata)}`;
	}
}

// Export a singleton instance with default config
export const contextManager = new ContextManager();

```

--------------------------------------------------------------------------------
/packages/tm-core/src/modules/git/services/commit-message-generator.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * CommitMessageGenerator - Generate conventional commit messages with metadata
 *
 * Combines TemplateEngine and ScopeDetector to create structured commit messages
 * that follow conventional commits specification and include task metadata.
 */

import { ScopeDetector } from './scope-detector.js';
import { TemplateEngine } from './template-engine.js';

export interface CommitMessageOptions {
	type: string;
	description: string;
	changedFiles: string[];
	scope?: string;
	body?: string;
	breaking?: boolean;
	taskId?: string;
	phase?: string;
	tag?: string;
	testsPassing?: number;
	testsFailing?: number;
	coveragePercent?: number;
}

export interface ValidationResult {
	isValid: boolean;
	errors: string[];
}

export interface ParsedCommitMessage {
	type: string;
	scope?: string;
	breaking: boolean;
	description: string;
	body?: string;
}

const CONVENTIONAL_COMMIT_TYPES = [
	'feat',
	'fix',
	'docs',
	'style',
	'refactor',
	'perf',
	'test',
	'build',
	'ci',
	'chore',
	'revert'
];

export class CommitMessageGenerator {
	private templateEngine: TemplateEngine;
	private scopeDetector: ScopeDetector;

	constructor(
		customTemplates?: Record<string, string>,
		customScopeMappings?: Record<string, string>,
		customScopePriorities?: Record<string, number>
	) {
		this.templateEngine = new TemplateEngine(customTemplates);
		this.scopeDetector = new ScopeDetector(
			customScopeMappings,
			customScopePriorities
		);
	}

	/**
	 * Generate a conventional commit message with metadata
	 */
	generateMessage(options: CommitMessageOptions): string {
		const {
			type,
			description,
			changedFiles,
			scope: manualScope,
			body,
			breaking = false,
			taskId,
			phase,
			tag,
			testsPassing,
			testsFailing,
			coveragePercent
		} = options;

		// Determine scope (manual override or auto-detect)
		const scope = manualScope ?? this.scopeDetector.detectScope(changedFiles);

		// Build template variables
		const variables = {
			type,
			scope,
			breaking: breaking ? '!' : '',
			description,
			body,
			taskId,
			phase,
			tag,
			testsPassing,
			testsFailing,
			coveragePercent
		};

		// Generate message from template
		return this.templateEngine.render('commitMessage', variables);
	}

	/**
	 * Validate that a commit message follows conventional commits format
	 */
	validateConventionalCommit(message: string): ValidationResult {
		const errors: string[] = [];

		// Parse first line (header)
		const lines = message.split('\n');
		const header = lines[0];

		if (!header) {
			errors.push('Missing commit message');
			return { isValid: false, errors };
		}

		// Check format: type(scope)?: description
		const headerRegex = /^(\w+)(?:\(([^)]+)\))?(!)?:\s*(.+)$/;
		const match = header.match(headerRegex);

		if (!match) {
			errors.push(
				'Invalid conventional commit format. Expected: type(scope): description'
			);
			return { isValid: false, errors };
		}

		const [, type, , , description] = match;

		// Validate type
		if (!CONVENTIONAL_COMMIT_TYPES.includes(type)) {
			errors.push(
				`Invalid commit type "${type}". Must be one of: ${CONVENTIONAL_COMMIT_TYPES.join(', ')}`
			);
		}

		// Validate description
		if (!description || description.trim().length === 0) {
			errors.push('Missing description');
		}

		return {
			isValid: errors.length === 0,
			errors
		};
	}

	/**
	 * Parse a conventional commit message into its components
	 */
	parseCommitMessage(message: string): ParsedCommitMessage {
		const lines = message.split('\n');
		const header = lines[0];

		// Parse header: type(scope)!: description
		const headerRegex = /^(\w+)(?:\(([^)]+)\))?(!)?:\s*(.+)$/;
		const match = header.match(headerRegex);

		if (!match) {
			throw new Error('Invalid conventional commit format');
		}

		const [, type, scope, breaking, description] = match;

		// Body is everything after the first blank line
		const bodyStartIndex = lines.findIndex((line, i) => i > 0 && line === '');
		const body =
			bodyStartIndex !== -1
				? lines
						.slice(bodyStartIndex + 1)
						.join('\n')
						.trim()
				: undefined;

		return {
			type,
			scope,
			breaking: breaking === '!',
			description,
			body
		};
	}

	/**
	 * Get the scope detector instance (for testing/customization)
	 */
	getScopeDetector(): ScopeDetector {
		return this.scopeDetector;
	}

	/**
	 * Get the template engine instance (for testing/customization)
	 */
	getTemplateEngine(): TemplateEngine {
		return this.templateEngine;
	}
}

```

--------------------------------------------------------------------------------
/.github/scripts/parse-metrics.mjs:
--------------------------------------------------------------------------------

```
#!/usr/bin/env node

import { readFileSync, existsSync, writeFileSync } from 'fs';

function parseMetricsTable(content, metricName) {
	const lines = content.split('\n');

	for (let i = 0; i < lines.length; i++) {
		const line = lines[i].trim();
		// Match a markdown table row like: | Metric Name | value | ...
		const safeName = metricName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
		const re = new RegExp(`^\\|\\s*${safeName}\\s*\\|\\s*([^|]+)\\|?`);
		const match = line.match(re);
		if (match) {
			return match[1].trim() || 'N/A';
		}
	}
	return 'N/A';
}

function parseCountMetric(content, metricName) {
	const result = parseMetricsTable(content, metricName);
	// Extract number from string, handling commas and spaces
	const numberMatch = result.toString().match(/[\d,]+/);
	if (numberMatch) {
		const number = parseInt(numberMatch[0].replace(/,/g, ''));
		return isNaN(number) ? 0 : number;
	}
	return 0;
}

function main() {
	const metrics = {
		issues_created: 0,
		issues_closed: 0,
		prs_created: 0,
		prs_merged: 0,
		issue_avg_first_response: 'N/A',
		issue_avg_time_to_close: 'N/A',
		pr_avg_first_response: 'N/A',
		pr_avg_merge_time: 'N/A'
	};

	// Parse issue metrics
	if (existsSync('issue_metrics.md')) {
		console.log('📄 Found issue_metrics.md, parsing...');
		const issueContent = readFileSync('issue_metrics.md', 'utf8');

		metrics.issues_created = parseCountMetric(
			issueContent,
			'Total number of items created'
		);
		metrics.issues_closed = parseCountMetric(
			issueContent,
			'Number of items closed'
		);
		metrics.issue_avg_first_response = parseMetricsTable(
			issueContent,
			'Time to first response'
		);
		metrics.issue_avg_time_to_close = parseMetricsTable(
			issueContent,
			'Time to close'
		);
	} else {
		console.warn('[parse-metrics] issue_metrics.md not found; using defaults.');
	}

	// Parse PR created metrics
	if (existsSync('pr_created_metrics.md')) {
		console.log('📄 Found pr_created_metrics.md, parsing...');
		const prCreatedContent = readFileSync('pr_created_metrics.md', 'utf8');

		metrics.prs_created = parseCountMetric(
			prCreatedContent,
			'Total number of items created'
		);
		metrics.pr_avg_first_response = parseMetricsTable(
			prCreatedContent,
			'Time to first response'
		);
	} else {
		console.warn(
			'[parse-metrics] pr_created_metrics.md not found; using defaults.'
		);
	}

	// Parse PR merged metrics (for more accurate merge data)
	if (existsSync('pr_merged_metrics.md')) {
		console.log('📄 Found pr_merged_metrics.md, parsing...');
		const prMergedContent = readFileSync('pr_merged_metrics.md', 'utf8');

		metrics.prs_merged = parseCountMetric(
			prMergedContent,
			'Total number of items created'
		);
		// For merged PRs, "Time to close" is actually time to merge
		metrics.pr_avg_merge_time = parseMetricsTable(
			prMergedContent,
			'Time to close'
		);
	} else {
		console.warn(
			'[parse-metrics] pr_merged_metrics.md not found; falling back to pr_metrics.md.'
		);
		// Fallback: try old pr_metrics.md if it exists
		if (existsSync('pr_metrics.md')) {
			console.log('📄 Falling back to pr_metrics.md...');
			const prContent = readFileSync('pr_metrics.md', 'utf8');

			const mergedCount = parseCountMetric(prContent, 'Number of items merged');
			metrics.prs_merged =
				mergedCount || parseCountMetric(prContent, 'Number of items closed');

			const maybeMergeTime = parseMetricsTable(
				prContent,
				'Average time to merge'
			);
			metrics.pr_avg_merge_time =
				maybeMergeTime !== 'N/A'
					? maybeMergeTime
					: parseMetricsTable(prContent, 'Time to close');
		} else {
			console.warn('[parse-metrics] pr_metrics.md not found; using defaults.');
		}
	}

	// Output for GitHub Actions
	const output = Object.entries(metrics)
		.map(([key, value]) => `${key}=${value}`)
		.join('\n');

	// Always output to stdout for debugging
	console.log('\n=== FINAL METRICS ===');
	Object.entries(metrics).forEach(([key, value]) => {
		console.log(`${key}: ${value}`);
	});

	// Write to GITHUB_OUTPUT if in GitHub Actions
	if (process.env.GITHUB_OUTPUT) {
		try {
			writeFileSync(process.env.GITHUB_OUTPUT, output + '\n', { flag: 'a' });
			console.log(
				`\nSuccessfully wrote metrics to ${process.env.GITHUB_OUTPUT}`
			);
		} catch (error) {
			console.error(`Failed to write to GITHUB_OUTPUT: ${error.message}`);
			process.exit(1);
		}
	} else {
		console.log(
			'\nNo GITHUB_OUTPUT environment variable found, skipping file write'
		);
	}
}

main();

```

--------------------------------------------------------------------------------
/apps/extension/src/webview/components/ToastNotification.tsx:
--------------------------------------------------------------------------------

```typescript
/**
 * Toast Notification Component
 */

import React, { useState, useEffect } from 'react';
import type { ToastNotification as ToastType } from '../types';

interface ToastNotificationProps {
	notification: ToastType;
	onDismiss: (id: string) => void;
}

export const ToastNotification: React.FC<ToastNotificationProps> = ({
	notification,
	onDismiss
}) => {
	const [isVisible, setIsVisible] = useState(true);
	const [progress, setProgress] = useState(100);
	const duration = notification.duration || 5000; // 5 seconds default

	useEffect(() => {
		const progressInterval = setInterval(() => {
			setProgress((prev) => {
				const decrease = (100 / duration) * 100; // Update every 100ms
				return Math.max(0, prev - decrease);
			});
		}, 100);

		const timeoutId = setTimeout(() => {
			setIsVisible(false);
			setTimeout(() => onDismiss(notification.id), 300); // Wait for animation
		}, duration);

		return () => {
			clearInterval(progressInterval);
			clearTimeout(timeoutId);
		};
	}, [notification.id, duration, onDismiss]);

	const getIcon = () => {
		switch (notification.type) {
			case 'success':
				return (
					<svg
						className="w-5 h-5 text-green-400"
						fill="none"
						stroke="currentColor"
						viewBox="0 0 24 24"
					>
						<path
							strokeLinecap="round"
							strokeLinejoin="round"
							strokeWidth={2}
							d="M5 13l4 4L19 7"
						/>
					</svg>
				);
			case 'info':
				return (
					<svg
						className="w-5 h-5 text-blue-400"
						fill="none"
						stroke="currentColor"
						viewBox="0 0 24 24"
					>
						<path
							strokeLinecap="round"
							strokeLinejoin="round"
							strokeWidth={2}
							d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
						/>
					</svg>
				);
			case 'warning':
				return (
					<svg
						className="w-5 h-5 text-yellow-400"
						fill="none"
						stroke="currentColor"
						viewBox="0 0 24 24"
					>
						<path
							strokeLinecap="round"
							strokeLinejoin="round"
							strokeWidth={2}
							d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.667-2.308-1.667-3.08 0L3.34 19c-.77 1.333.192 3 1.732 3z"
						/>
					</svg>
				);
			case 'error':
				return (
					<svg
						className="w-5 h-5 text-red-400"
						fill="none"
						stroke="currentColor"
						viewBox="0 0 24 24"
					>
						<path
							strokeLinecap="round"
							strokeLinejoin="round"
							strokeWidth={2}
							d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
						/>
					</svg>
				);
		}
	};

	const bgColor = {
		success: 'bg-green-900/90',
		info: 'bg-blue-900/90',
		warning: 'bg-yellow-900/90',
		error: 'bg-red-900/90'
	}[notification.type];

	const borderColor = {
		success: 'border-green-600',
		info: 'border-blue-600',
		warning: 'border-yellow-600',
		error: 'border-red-600'
	}[notification.type];

	const progressColor = {
		success: 'bg-green-400',
		info: 'bg-blue-400',
		warning: 'bg-yellow-400',
		error: 'bg-red-400'
	}[notification.type];

	return (
		<div
			className={`${bgColor} ${borderColor} border rounded-lg shadow-lg p-4 mb-2 transition-all duration-300 ${
				isVisible ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-full'
			} max-w-sm w-full relative overflow-hidden`}
		>
			<div className="flex items-start">
				<div className="flex-shrink-0">{getIcon()}</div>
				<div className="ml-3 flex-1">
					<h3 className="text-sm font-medium text-white">
						{notification.title}
					</h3>
					<p className="mt-1 text-sm text-gray-300">{notification.message}</p>
				</div>
				<button
					onClick={() => onDismiss(notification.id)}
					className="ml-4 flex-shrink-0 inline-flex text-gray-400 hover:text-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white"
				>
					<span className="sr-only">Close</span>
					<svg className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
						<path
							fillRule="evenodd"
							d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
							clipRule="evenodd"
						/>
					</svg>
				</button>
			</div>
			{/* Progress bar */}
			<div className="absolute bottom-0 left-0 w-full h-1 bg-gray-700">
				<div
					className={`h-full ${progressColor} transition-all duration-100 ease-linear`}
					style={{ width: `${progress}%` }}
				/>
			</div>
		</div>
	);
};

```

--------------------------------------------------------------------------------
/apps/extension/esbuild.js:
--------------------------------------------------------------------------------

```javascript
const esbuild = require('esbuild');
const path = require('path');

const production = process.argv.includes('--production');
const watch = process.argv.includes('--watch');

/**
 * @type {import('esbuild').Plugin}
 */
const esbuildProblemMatcherPlugin = {
	name: 'esbuild-problem-matcher',

	setup(build) {
		build.onStart(() => {
			console.log('[watch] build started');
		});
		build.onEnd((result) => {
			result.errors.forEach(({ text, location }) => {
				console.error(`✘ [ERROR] ${text}`);
				console.error(
					`    ${location.file}:${location.line}:${location.column}:`
				);
			});
			console.log('[watch] build finished');
		});
	}
};

/**
 * @type {import('esbuild').Plugin}
 */
const aliasPlugin = {
	name: 'alias',
	setup(build) {
		// Handle @/ aliases for shadcn/ui
		build.onResolve({ filter: /^@\// }, (args) => {
			const resolvedPath = path.resolve(__dirname, 'src', args.path.slice(2));

			// Try to resolve with common TypeScript extensions
			const fs = require('fs');
			const extensions = ['.tsx', '.ts', '.jsx', '.js'];

			// Check if it's a file first
			for (const ext of extensions) {
				const fullPath = resolvedPath + ext;
				if (fs.existsSync(fullPath)) {
					return { path: fullPath };
				}
			}

			// Check if it's a directory with index file
			for (const ext of extensions) {
				const indexPath = path.join(resolvedPath, 'index' + ext);
				if (fs.existsSync(indexPath)) {
					return { path: indexPath };
				}
			}

			// Fallback to original behavior
			return { path: resolvedPath };
		});
	}
};

async function main() {
	// Build configuration for the VS Code extension
	const extensionCtx = await esbuild.context({
		entryPoints: ['src/extension.ts'],
		bundle: true,
		format: 'cjs',
		minify: production,
		sourcemap: !production ? 'inline' : false,
		sourcesContent: !production,
		platform: 'node',
		outdir: 'dist',
		external: ['vscode'],
		logLevel: 'silent',
		// Add production optimizations
		...(production && {
			drop: ['debugger'],
			pure: ['console.log', 'console.debug', 'console.trace']
		}),
		plugins: [esbuildProblemMatcherPlugin, aliasPlugin]
	});

	// Build configuration for the React webview
	const webviewCtx = await esbuild.context({
		entryPoints: ['src/webview/index.tsx'],
		bundle: true,
		format: 'iife',
		globalName: 'App',
		minify: production,
		sourcemap: !production ? 'inline' : false,
		sourcesContent: !production,
		platform: 'browser',
		outdir: 'dist',
		logLevel: 'silent',
		target: ['es2020'],
		jsx: 'automatic',
		jsxImportSource: 'react',
		external: ['*.css'],
		// Bundle React with webview since it's not available in the runtime
		// This prevents the multiple React instances issue
		// Ensure React is resolved from the workspace root to avoid duplicates
		alias: {
			react: path.resolve(__dirname, '../../node_modules/react'),
			'react-dom': path.resolve(__dirname, '../../node_modules/react-dom')
		},
		define: {
			'process.env.NODE_ENV': production ? '"production"' : '"development"',
			global: 'globalThis'
		},
		// Add production optimizations for webview too
		...(production && {
			drop: ['debugger'],
			pure: ['console.log', 'console.debug', 'console.trace']
		}),
		plugins: [esbuildProblemMatcherPlugin, aliasPlugin]
	});

	// Build configuration for the React sidebar
	const sidebarCtx = await esbuild.context({
		entryPoints: ['src/webview/sidebar.tsx'],
		bundle: true,
		format: 'iife',
		globalName: 'SidebarApp',
		minify: production,
		sourcemap: !production ? 'inline' : false,
		sourcesContent: !production,
		platform: 'browser',
		outdir: 'dist',
		logLevel: 'silent',
		target: ['es2020'],
		jsx: 'automatic',
		jsxImportSource: 'react',
		external: ['*.css'],
		alias: {
			react: path.resolve(__dirname, '../../node_modules/react'),
			'react-dom': path.resolve(__dirname, '../../node_modules/react-dom')
		},
		define: {
			'process.env.NODE_ENV': production ? '"production"' : '"development"',
			global: 'globalThis'
		},
		...(production && {
			drop: ['debugger'],
			pure: ['console.log', 'console.debug', 'console.trace']
		}),
		plugins: [esbuildProblemMatcherPlugin, aliasPlugin]
	});

	if (watch) {
		await Promise.all([
			extensionCtx.watch(),
			webviewCtx.watch(),
			sidebarCtx.watch()
		]);
	} else {
		await Promise.all([
			extensionCtx.rebuild(),
			webviewCtx.rebuild(),
			sidebarCtx.rebuild()
		]);
		await extensionCtx.dispose();
		await webviewCtx.dispose();
		await sidebarCtx.dispose();
	}
}

main().catch((e) => {
	console.error(e);
	process.exit(1);
});

```

--------------------------------------------------------------------------------
/tests/unit/scripts/modules/task-manager/setup.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Common setup for task-manager module tests
 */
import { jest } from '@jest/globals';

// Sample test data
export const sampleTasks = {
	meta: { projectName: 'Test Project' },
	tasks: [
		{
			id: 1,
			title: 'Task 1',
			description: 'First task description',
			status: 'pending',
			dependencies: [],
			priority: 'high',
			details: 'Detailed information for task 1',
			testStrategy: 'Test strategy for task 1'
		},
		{
			id: 2,
			title: 'Task 2',
			description: 'Second task description',
			status: 'pending',
			dependencies: [1],
			priority: 'medium',
			details: 'Detailed information for task 2',
			testStrategy: 'Test strategy for task 2'
		},
		{
			id: 3,
			title: 'Task with Subtasks',
			description: 'Task with subtasks description',
			status: 'pending',
			dependencies: [1, 2],
			priority: 'high',
			details: 'Detailed information for task 3',
			testStrategy: 'Test strategy for task 3',
			subtasks: [
				{
					id: 1,
					title: 'Subtask 1',
					description: 'First subtask',
					status: 'pending',
					dependencies: [],
					details: 'Details for subtask 1'
				},
				{
					id: 2,
					title: 'Subtask 2',
					description: 'Second subtask',
					status: 'pending',
					dependencies: [1],
					details: 'Details for subtask 2'
				}
			]
		}
	]
};

export const emptySampleTasks = {
	meta: { projectName: 'Empty Project' },
	tasks: []
};

export const sampleClaudeResponse = {
	tasks: [
		{
			id: 1,
			title: 'Setup Project',
			description: 'Initialize the project structure',
			status: 'pending',
			dependencies: [],
			priority: 'high',
			details:
				'Create repository, configure build system, and setup dev environment',
			testStrategy: 'Verify project builds and tests run'
		},
		{
			id: 2,
			title: 'Implement Core Feature',
			description: 'Create the main functionality',
			status: 'pending',
			dependencies: [1],
			priority: 'high',
			details: 'Implement the core business logic for the application',
			testStrategy:
				'Unit tests for core functions, integration tests for workflows'
		}
	]
};

// Common mock setup function
export const setupCommonMocks = () => {
	// Clear mocks before setup
	jest.clearAllMocks();

	// Mock implementations
	const mocks = {
		readFileSync: jest.fn(),
		existsSync: jest.fn(),
		mkdirSync: jest.fn(),
		writeFileSync: jest.fn(),
		readJSON: jest.fn(),
		writeJSON: jest.fn(),
		log: jest.fn(),
		isTaskDependentOn: jest.fn().mockReturnValue(false),
		formatDependenciesWithStatus: jest.fn(),
		displayTaskList: jest.fn(),
		validateAndFixDependencies: jest.fn(),
		generateObjectService: jest.fn().mockResolvedValue({
			mainResult: { tasks: [] },
			telemetryData: {}
		})
	};

	return mocks;
};

// Helper to create a deep copy of objects to avoid test pollution
export const cloneData = (data) => JSON.parse(JSON.stringify(data));

/**
 * Shared mock implementation for getTagAwareFilePath that matches the actual implementation
 * This ensures consistent behavior across all test files, particularly regarding projectRoot handling.
 *
 * The key difference from previous inconsistent implementations was that some tests were not
 * properly handling the projectRoot parameter, leading to different behaviors between test files.
 *
 * @param {string} basePath - The base file path
 * @param {string|null} tag - The tag name (null, undefined, or 'master' uses base path)
 * @param {string} [projectRoot='.'] - The project root directory
 * @returns {string} The resolved file path
 */
export const createGetTagAwareFilePathMock = () => {
	return jest.fn((basePath, tag, projectRoot = '.') => {
		// Handle projectRoot consistently - this was the key fix
		const fullPath = projectRoot ? `${projectRoot}/${basePath}` : basePath;

		if (!tag || tag === 'master') {
			return fullPath;
		}

		// Mock the slugification behavior (matches actual implementation)
		const slugifiedTag = tag.replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();
		const idx = fullPath.lastIndexOf('.');
		return `${fullPath.slice(0, idx)}_${slugifiedTag}${fullPath.slice(idx)}`;
	});
};

/**
 * Shared mock implementation for slugifyTagForFilePath that matches the actual implementation
 * @param {string} tagName - The tag name to slugify
 * @returns {string} Slugified tag name safe for filesystem use
 */
export const createSlugifyTagForFilePathMock = () => {
	return jest.fn((tagName) => {
		if (!tagName || typeof tagName !== 'string') {
			return 'unknown-tag';
		}
		return tagName.replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();
	});
};

```

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

```javascript
import { jest } from '@jest/globals';
import fs from 'fs';
import path from 'path';
import os from 'os';

// Mock external modules
jest.mock('child_process', () => ({
	execSync: jest.fn()
}));

// Mock console methods
jest.mock('console', () => ({
	log: jest.fn(),
	info: jest.fn(),
	warn: jest.fn(),
	error: jest.fn(),
	clear: jest.fn()
}));

describe('Gemini Profile Integration', () => {
	let tempDir;

	beforeEach(() => {
		jest.clearAllMocks();

		// Create a temporary directory for testing
		tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-master-test-'));

		// Spy on fs methods
		jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
		jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
			if (filePath.toString().includes('AGENTS.md')) {
				return 'Sample AGENTS.md content for Gemini integration';
			}
			return '{}';
		});
		jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
		jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
	});

	afterEach(() => {
		// Clean up the temporary directory
		try {
			fs.rmSync(tempDir, { recursive: true, force: true });
		} catch (err) {
			console.error(`Error cleaning up: ${err.message}`);
		}
	});

	// Test function that simulates the Gemini profile file copying behavior
	function mockCreateGeminiStructure() {
		// Gemini profile copies AGENTS.md to GEMINI.md in project root
		const sourceContent = 'Sample AGENTS.md content for Gemini integration';
		fs.writeFileSync(path.join(tempDir, 'GEMINI.md'), sourceContent);

		// Gemini profile creates .gemini directory
		fs.mkdirSync(path.join(tempDir, '.gemini'), { recursive: true });

		// Gemini profile creates settings.json in .gemini directory
		const settingsContent = JSON.stringify(
			{
				mcpServers: {
					'task-master-ai': {
						command: 'npx',
						args: ['-y', 'task-master-ai'],
						env: {
							YOUR_ANTHROPIC_API_KEY: 'your-api-key-here',
							YOUR_PERPLEXITY_API_KEY: 'your-api-key-here',
							YOUR_OPENAI_API_KEY: 'your-api-key-here',
							YOUR_GOOGLE_API_KEY: 'your-api-key-here',
							YOUR_MISTRAL_API_KEY: 'your-api-key-here',
							YOUR_AZURE_OPENAI_API_KEY: 'your-api-key-here',
							YOUR_AZURE_OPENAI_ENDPOINT: 'your-endpoint-here',
							YOUR_OPENROUTER_API_KEY: 'your-api-key-here',
							YOUR_XAI_API_KEY: 'your-api-key-here',
							YOUR_OLLAMA_API_KEY: 'your-api-key-here',
							YOUR_OLLAMA_BASE_URL: 'http://localhost:11434/api',
							YOUR_AWS_ACCESS_KEY_ID: 'your-access-key-id',
							YOUR_AWS_SECRET_ACCESS_KEY: 'your-secret-access-key',
							YOUR_AWS_REGION: 'us-east-1'
						}
					}
				}
			},
			null,
			2
		);
		fs.writeFileSync(
			path.join(tempDir, '.gemini', 'settings.json'),
			settingsContent
		);
	}

	test('creates GEMINI.md file in project root', () => {
		// Act
		mockCreateGeminiStructure();

		// Assert
		expect(fs.writeFileSync).toHaveBeenCalledWith(
			path.join(tempDir, 'GEMINI.md'),
			'Sample AGENTS.md content for Gemini integration'
		);
	});

	test('creates .gemini profile directory', () => {
		// Act
		mockCreateGeminiStructure();

		// Assert
		expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(tempDir, '.gemini'), {
			recursive: true
		});
	});

	test('creates MCP configuration as settings.json', () => {
		// Act
		mockCreateGeminiStructure();

		// Assert - Gemini profile should create settings.json instead of mcp.json
		const writeFileCalls = fs.writeFileSync.mock.calls;
		const settingsJsonCall = writeFileCalls.find((call) =>
			call[0].toString().includes('.gemini/settings.json')
		);
		expect(settingsJsonCall).toBeDefined();
	});

	test('uses settings.json instead of mcp.json', () => {
		// Act
		mockCreateGeminiStructure();

		// Assert - Should use settings.json, not mcp.json
		const writeFileCalls = fs.writeFileSync.mock.calls;
		const mcpJsonCalls = writeFileCalls.filter((call) =>
			call[0].toString().includes('mcp.json')
		);
		expect(mcpJsonCalls).toHaveLength(0);

		const settingsJsonCalls = writeFileCalls.filter((call) =>
			call[0].toString().includes('settings.json')
		);
		expect(settingsJsonCalls).toHaveLength(1);
	});

	test('renames AGENTS.md to GEMINI.md', () => {
		// Act
		mockCreateGeminiStructure();

		// Assert - Gemini should rename AGENTS.md to GEMINI.md
		const writeFileCalls = fs.writeFileSync.mock.calls;
		const geminiMdCall = writeFileCalls.find((call) =>
			call[0].toString().includes('GEMINI.md')
		);
		expect(geminiMdCall).toBeDefined();
		expect(geminiMdCall[0]).toBe(path.join(tempDir, 'GEMINI.md'));
	});
});

```

--------------------------------------------------------------------------------
/tests/unit/scripts/modules/task-manager/update-single-task-status.test.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Tests for the updateSingleTaskStatus function
 */
import { jest } from '@jest/globals';

// Import test fixtures
import {
	isValidTaskStatus,
	TASK_STATUS_OPTIONS
} from '../../../../../src/constants/task-status.js';

// Sample tasks data for testing
const sampleTasks = {
	tasks: [
		{
			id: 1,
			title: 'Task 1',
			description: 'First task',
			status: 'pending',
			dependencies: []
		},
		{
			id: 2,
			title: 'Task 2',
			description: 'Second task',
			status: 'pending',
			dependencies: []
		},
		{
			id: 3,
			title: 'Task 3',
			description: 'Third task with subtasks',
			status: 'pending',
			dependencies: [],
			subtasks: [
				{
					id: 1,
					title: 'Subtask 3.1',
					description: 'First subtask',
					status: 'pending',
					dependencies: []
				},
				{
					id: 2,
					title: 'Subtask 3.2',
					description: 'Second subtask',
					status: 'pending',
					dependencies: []
				}
			]
		}
	]
};

// Simplified version of updateSingleTaskStatus for testing
const testUpdateSingleTaskStatus = (tasksData, taskIdInput, newStatus) => {
	if (!isValidTaskStatus(newStatus)) {
		throw new Error(
			`Error: Invalid status value: ${newStatus}. Use one of: ${TASK_STATUS_OPTIONS.join(', ')}`
		);
	}

	// Check if it's a subtask (e.g., "1.2")
	if (taskIdInput.includes('.')) {
		const [parentId, subtaskId] = taskIdInput
			.split('.')
			.map((id) => parseInt(id, 10));

		// Find the parent task
		const parentTask = tasksData.tasks.find((t) => t.id === parentId);
		if (!parentTask) {
			throw new Error(`Parent task ${parentId} not found`);
		}

		// Find the subtask
		if (!parentTask.subtasks) {
			throw new Error(`Parent task ${parentId} has no subtasks`);
		}

		const subtask = parentTask.subtasks.find((st) => st.id === subtaskId);
		if (!subtask) {
			throw new Error(
				`Subtask ${subtaskId} not found in parent task ${parentId}`
			);
		}

		// Update the subtask status
		subtask.status = newStatus;

		// Check if all subtasks are done (if setting to 'done')
		if (
			newStatus.toLowerCase() === 'done' ||
			newStatus.toLowerCase() === 'completed'
		) {
			const allSubtasksDone = parentTask.subtasks.every(
				(st) => st.status === 'done' || st.status === 'completed'
			);

			// For testing, we don't need to output suggestions
		}
	} else {
		// Handle regular task
		const taskId = parseInt(taskIdInput, 10);
		const task = tasksData.tasks.find((t) => t.id === taskId);

		if (!task) {
			throw new Error(`Task ${taskId} not found`);
		}

		// Update the task status
		task.status = newStatus;

		// If marking as done, also mark all subtasks as done
		if (
			(newStatus.toLowerCase() === 'done' ||
				newStatus.toLowerCase() === 'completed') &&
			task.subtasks &&
			task.subtasks.length > 0
		) {
			task.subtasks.forEach((subtask) => {
				subtask.status = newStatus;
			});
		}
	}

	return true;
};

describe('updateSingleTaskStatus function', () => {
	test('should update regular task status', async () => {
		// Arrange
		const testTasksData = JSON.parse(JSON.stringify(sampleTasks));

		// Act
		const result = testUpdateSingleTaskStatus(testTasksData, '2', 'done');

		// Assert
		expect(result).toBe(true);
		expect(testTasksData.tasks[1].status).toBe('done');
	});

	test('should throw error for invalid status', async () => {
		// Arrange
		const testTasksData = JSON.parse(JSON.stringify(sampleTasks));

		// Assert
		expect(() =>
			testUpdateSingleTaskStatus(testTasksData, '2', 'Done')
		).toThrow(/Error: Invalid status value: Done./);
	});

	test('should update subtask status', async () => {
		// Arrange
		const testTasksData = JSON.parse(JSON.stringify(sampleTasks));

		// Act
		const result = testUpdateSingleTaskStatus(testTasksData, '3.1', 'done');

		// Assert
		expect(result).toBe(true);
		expect(testTasksData.tasks[2].subtasks[0].status).toBe('done');
	});

	test('should handle parent tasks without subtasks', async () => {
		// Arrange
		const testTasksData = JSON.parse(JSON.stringify(sampleTasks));

		// Remove subtasks from task 3
		const taskWithoutSubtasks = { ...testTasksData.tasks[2] };
		delete taskWithoutSubtasks.subtasks;
		testTasksData.tasks[2] = taskWithoutSubtasks;

		// Assert
		expect(() =>
			testUpdateSingleTaskStatus(testTasksData, '3.1', 'done')
		).toThrow('has no subtasks');
	});

	test('should handle non-existent subtask ID', async () => {
		// Arrange
		const testTasksData = JSON.parse(JSON.stringify(sampleTasks));

		// Assert
		expect(() =>
			testUpdateSingleTaskStatus(testTasksData, '3.99', 'done')
		).toThrow('Subtask 99 not found');
	});
});

```

--------------------------------------------------------------------------------
/packages/tm-core/tests/auth/auth-refresh.test.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs';
import os from 'os';
import path from 'path';
import type { Session } from '@supabase/supabase-js';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { AuthManager } from '../../src/auth/auth-manager';
import { CredentialStore } from '../../src/auth/credential-store';
import type { AuthCredentials } from '../../src/auth/types';

describe('AuthManager Token Refresh', () => {
	let authManager: AuthManager;
	let credentialStore: CredentialStore;
	let tmpDir: string;
	let authFile: string;

	beforeEach(() => {
		// Reset singletons
		AuthManager.resetInstance();
		CredentialStore.resetInstance();

		// Create temporary directory for test isolation
		tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tm-auth-refresh-'));
		authFile = path.join(tmpDir, 'auth.json');

		// Initialize AuthManager with test config (this will create CredentialStore internally)
		authManager = AuthManager.getInstance({
			configDir: tmpDir,
			configFile: authFile
		});

		// Get the CredentialStore instance that AuthManager created
		credentialStore = CredentialStore.getInstance();
		credentialStore.clearCredentials();
	});

	afterEach(() => {
		// Clean up
		try {
			credentialStore.clearCredentials();
		} catch {
			// Ignore cleanup errors
		}
		AuthManager.resetInstance();
		CredentialStore.resetInstance();
		vi.restoreAllMocks();

		// Remove temporary directory
		if (tmpDir && fs.existsSync(tmpDir)) {
			fs.rmSync(tmpDir, { recursive: true, force: true });
		}
	});

	it('should return expired credentials to enable refresh flows', () => {
		// Set up expired credentials with refresh token
		const expiredCredentials: AuthCredentials = {
			token: 'expired_access_token',
			refreshToken: 'valid_refresh_token',
			userId: 'test-user-id',
			email: '[email protected]',
			expiresAt: new Date(Date.now() - 1000).toISOString(), // Expired 1 second ago
			savedAt: new Date().toISOString()
		};

		credentialStore.saveCredentials(expiredCredentials);

		// Get credentials should return them even if expired
		// Refresh will be handled by explicit calls or client operations
		const credentials = authManager.getCredentials();

		expect(credentials).not.toBeNull();
		expect(credentials?.token).toBe('expired_access_token');
		expect(credentials?.refreshToken).toBe('valid_refresh_token');
	});

	it('should return valid credentials', () => {
		// Set up valid (non-expired) credentials
		const validCredentials: AuthCredentials = {
			token: 'valid_access_token',
			refreshToken: 'valid_refresh_token',
			userId: 'test-user-id',
			email: '[email protected]',
			expiresAt: new Date(Date.now() + 3600000).toISOString(), // Expires in 1 hour
			savedAt: new Date().toISOString()
		};

		credentialStore.saveCredentials(validCredentials);

		const credentials = authManager.getCredentials();

		expect(credentials?.token).toBe('valid_access_token');
	});

	it('should return expired credentials even without refresh token', () => {
		// Set up expired credentials WITHOUT refresh token
		// We still return them - it's up to the caller to handle
		const expiredCredentials: AuthCredentials = {
			token: 'expired_access_token',
			refreshToken: undefined,
			userId: 'test-user-id',
			email: '[email protected]',
			expiresAt: new Date(Date.now() - 1000).toISOString(), // Expired 1 second ago
			savedAt: new Date().toISOString()
		};

		credentialStore.saveCredentials(expiredCredentials);

		const credentials = authManager.getCredentials();

		// Returns credentials even if expired
		expect(credentials).not.toBeNull();
		expect(credentials?.token).toBe('expired_access_token');
	});

	it('should return null if no credentials exist', () => {
		const credentials = authManager.getCredentials();
		expect(credentials).toBeNull();
	});

	it('should return credentials regardless of refresh token validity', () => {
		// Set up expired credentials with refresh token
		const expiredCredentials: AuthCredentials = {
			token: 'expired_access_token',
			refreshToken: 'invalid_refresh_token',
			userId: 'test-user-id',
			email: '[email protected]',
			expiresAt: new Date(Date.now() - 1000).toISOString(),
			savedAt: new Date().toISOString()
		};

		credentialStore.saveCredentials(expiredCredentials);

		const credentials = authManager.getCredentials();

		// Returns credentials - refresh will be attempted by the client which will handle failure
		expect(credentials).not.toBeNull();
		expect(credentials?.token).toBe('expired_access_token');
		expect(credentials?.refreshToken).toBe('invalid_refresh_token');
	});
});

```

--------------------------------------------------------------------------------
/src/ai-providers/google-vertex.js:
--------------------------------------------------------------------------------

```javascript
/**
 * google-vertex.js
 * AI provider implementation for Google Vertex AI models using Vercel AI SDK.
 */

import { createVertex } from '@ai-sdk/google-vertex';
import { BaseAIProvider } from './base-provider.js';
import { resolveEnvVariable } from '../../scripts/modules/utils.js';
import { log } from '../../scripts/modules/utils.js';

// Vertex-specific error classes
class VertexAuthError extends Error {
	constructor(message) {
		super(message);
		this.name = 'VertexAuthError';
		this.code = 'vertex_auth_error';
	}
}

class VertexConfigError extends Error {
	constructor(message) {
		super(message);
		this.name = 'VertexConfigError';
		this.code = 'vertex_config_error';
	}
}

class VertexApiError extends Error {
	constructor(message, statusCode) {
		super(message);
		this.name = 'VertexApiError';
		this.code = 'vertex_api_error';
		this.statusCode = statusCode;
	}
}

export class VertexAIProvider extends BaseAIProvider {
	constructor() {
		super();
		this.name = 'Google Vertex AI';
	}

	/**
	 * Returns the required API key environment variable name for Google Vertex AI.
	 * @returns {string} The environment variable name
	 */
	getRequiredApiKeyName() {
		return 'GOOGLE_API_KEY';
	}

	/**
	 * Validates Vertex AI-specific authentication parameters
	 * @param {object} params - Parameters to validate
	 * @throws {Error} If required parameters are missing
	 */
	validateAuth(params) {
		const { apiKey, projectId, location, credentials } = params;

		// Check for API key OR service account credentials
		if (!apiKey && !credentials) {
			throw new VertexAuthError(
				'Either Google API key (GOOGLE_API_KEY) or service account credentials (GOOGLE_APPLICATION_CREDENTIALS) is required for Vertex AI'
			);
		}

		// Project ID is required for Vertex AI
		if (!projectId) {
			throw new VertexConfigError(
				'Google Cloud project ID is required for Vertex AI. Set VERTEX_PROJECT_ID environment variable.'
			);
		}

		// Location is required for Vertex AI
		if (!location) {
			throw new VertexConfigError(
				'Google Cloud location is required for Vertex AI. Set VERTEX_LOCATION environment variable (e.g., "us-central1").'
			);
		}
	}

	/**
	 * Creates and returns a Google Vertex AI client instance.
	 * @param {object} params - Parameters for client initialization
	 * @param {string} [params.apiKey] - Google API key
	 * @param {string} params.projectId - Google Cloud project ID
	 * @param {string} params.location - Google Cloud location (e.g., "us-central1")
	 * @param {object} [params.credentials] - Service account credentials object
	 * @param {string} [params.baseURL] - Optional custom API endpoint
	 * @returns {Function} Google Vertex AI client function
	 * @throws {Error} If required parameters are missing or initialization fails
	 */
	getClient(params) {
		try {
			const { apiKey, projectId, location, credentials, baseURL } = params;
			const fetchImpl = this.createProxyFetch();

			// Configure auth options - either API key or service account
			const authOptions = {};
			if (apiKey) {
				authOptions.apiKey = apiKey;
			} else if (credentials) {
				authOptions.googleAuthOptions = credentials;
			}

			// Return Vertex AI client
			return createVertex({
				...authOptions,
				projectId,
				location,
				...(baseURL && { baseURL }),
				...(fetchImpl && { fetch: fetchImpl })
			});
		} catch (error) {
			this.handleError('client initialization', error);
		}
	}

	/**
	 * Handle errors from Vertex AI
	 * @param {string} operation - Description of the operation that failed
	 * @param {Error} error - The error object
	 * @throws {Error} Rethrows the error with additional context
	 */
	handleError(operation, error) {
		log('error', `Vertex AI ${operation} error:`, error);

		// Handle known error types
		if (
			error.name === 'VertexAuthError' ||
			error.name === 'VertexConfigError' ||
			error.name === 'VertexApiError'
		) {
			throw error;
		}

		// Handle network/API errors
		if (error.response) {
			const statusCode = error.response.status;
			const errorMessage = error.response.data?.error?.message || error.message;

			// Categorize by status code
			if (statusCode === 401 || statusCode === 403) {
				throw new VertexAuthError(`Authentication failed: ${errorMessage}`);
			} else if (statusCode === 400) {
				throw new VertexConfigError(`Invalid request: ${errorMessage}`);
			} else {
				throw new VertexApiError(
					`API error (${statusCode}): ${errorMessage}`,
					statusCode
				);
			}
		}

		// Generic error handling
		throw new Error(`Vertex AI ${operation} failed: ${error.message}`);
	}
}

```

--------------------------------------------------------------------------------
/apps/cli/src/commands/autopilot/start.command.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Start Command - Initialize and start TDD workflow
 */

import { type WorkflowContext, createTmCore } from '@tm/core';
import { Command } from 'commander';
import {
	AutopilotBaseOptions,
	OutputFormatter,
	createGitAdapter,
	createOrchestrator,
	hasWorkflowState,
	parseSubtasks,
	validateTaskId
} from './shared.js';
import { getProjectRoot } from '../../utils/project-root.js';

interface StartOptions extends AutopilotBaseOptions {
	force?: boolean;
	maxAttempts?: string;
}

/**
 * Start Command - Initialize new TDD workflow
 */
export class StartCommand extends Command {
	constructor() {
		super('start');

		this.description('Initialize and start a new TDD workflow for a task')
			.argument('<taskId>', 'Task ID to start workflow for')
			.option('-f, --force', 'Force start even if workflow state exists')
			.option('--max-attempts <number>', 'Maximum attempts per subtask', '3')
			.action(async (taskId: string, options: StartOptions) => {
				await this.execute(taskId, options);
			});
	}

	private async execute(taskId: string, options: StartOptions): Promise<void> {
		// Inherit parent options
		const parentOpts = this.parent?.opts() as AutopilotBaseOptions;
		const mergedOptions: StartOptions = {
			...parentOpts,
			...options,
			projectRoot: getProjectRoot(
				options.projectRoot || parentOpts?.projectRoot
			)
		};

		const formatter = new OutputFormatter(mergedOptions.json || false);

		try {
			// Validate task ID
			if (!validateTaskId(taskId)) {
				formatter.error('Invalid task ID format', {
					taskId,
					expected: 'Format: number or number.number (e.g., "1" or "1.2")'
				});
				process.exit(1);
			}

			// Check for existing workflow state
			const hasState = await hasWorkflowState(mergedOptions.projectRoot!);
			if (hasState && !mergedOptions.force) {
				formatter.error(
					'Workflow state already exists. Use --force to overwrite or resume with "autopilot resume"'
				);
				process.exit(1);
			}

			// Initialize Task Master Core
			const tmCore = await createTmCore({
				projectPath: mergedOptions.projectRoot!
			});

			// Get current tag from ConfigManager
			const currentTag = tmCore.config.getActiveTag();

			// Load task
			formatter.info(`Loading task ${taskId}...`);
			const { task } = await tmCore.tasks.get(taskId);

			if (!task) {
				formatter.error('Task not found', { taskId });
				process.exit(1);
			}

			// Validate task has subtasks
			if (!task.subtasks || task.subtasks.length === 0) {
				formatter.error('Task has no subtasks. Expand task first.', {
					taskId,
					suggestion: `Run: task-master expand --id=${taskId}`
				});
				process.exit(1);
			}

			// Initialize Git adapter
			const gitAdapter = createGitAdapter(mergedOptions.projectRoot!);
			await gitAdapter.ensureGitRepository();
			await gitAdapter.ensureCleanWorkingTree();

			// Parse subtasks
			const maxAttempts = parseInt(mergedOptions.maxAttempts || '3', 10);
			const subtasks = parseSubtasks(task, maxAttempts);

			// Create workflow context
			const context: WorkflowContext = {
				taskId: task.id,
				subtasks,
				currentSubtaskIndex: 0,
				errors: [],
				metadata: {
					startedAt: new Date().toISOString(),
					tags: task.tags || []
				}
			};

			// Create orchestrator with persistence
			const orchestrator = createOrchestrator(
				context,
				mergedOptions.projectRoot!
			);

			// Complete PREFLIGHT phase
			orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' });

			// Generate descriptive branch name
			const sanitizedTitle = task.title
				.toLowerCase()
				.replace(/[^a-z0-9]+/g, '-')
				.replace(/^-+|-+$/g, '')
				.substring(0, 50);
			const formattedTaskId = taskId.replace(/\./g, '-');
			const tagPrefix = currentTag ? `${currentTag}/` : '';
			const branchName = `${tagPrefix}task-${formattedTaskId}-${sanitizedTitle}`;

			// Create and checkout branch
			formatter.info(`Creating branch: ${branchName}`);
			await gitAdapter.createAndCheckoutBranch(branchName);

			// Transition to SUBTASK_LOOP
			orchestrator.transition({
				type: 'BRANCH_CREATED',
				branchName
			});

			// Output success
			formatter.success('TDD workflow started', {
				taskId: task.id,
				title: task.title,
				phase: orchestrator.getCurrentPhase(),
				tddPhase: orchestrator.getCurrentTDDPhase(),
				branchName,
				subtasks: subtasks.length,
				currentSubtask: subtasks[0]?.title
			});

			// Clean up
		} catch (error) {
			formatter.error((error as Error).message);
			if (mergedOptions.verbose) {
				console.error((error as Error).stack);
			}
			process.exit(1);
		}
	}
}

```

--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node

/**
 * Task Master
 * Copyright (c) 2025 Eyal Toledano, Ralph Khreish
 *
 * This software is licensed under the MIT License with Commons Clause.
 * You may use this software for any purpose, including commercial applications,
 * and modify and redistribute it freely, subject to the following restrictions:
 *
 * 1. You may not sell this software or offer it as a service.
 * 2. The origin of this software must not be misrepresented.
 * 3. Altered source versions must be plainly marked as such.
 *
 * For the full license text, see the LICENSE file in the root directory.
 */

/**
 * Claude Task Master
 * A task management system for AI-driven development with Claude
 */

// This file serves as the main entry point for the package
// The primary functionality is provided through the CLI commands

import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
import { createRequire } from 'module';
import { spawn } from 'child_process';
import { Command } from 'commander';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const require = createRequire(import.meta.url);

// Get package information
const packageJson = require('./package.json');

// Export the path to the dev.js script for programmatic usage
export const devScriptPath = resolve(__dirname, './scripts/dev.js');

// Export a function to initialize a new project programmatically
export const initProject = async (options = {}) => {
	const init = await import('./scripts/init.js');
	return init.initializeProject(options);
};

// Export a function to run init as a CLI command
export const runInitCLI = async (options = {}) => {
	try {
		const init = await import('./scripts/init.js');
		const result = await init.initializeProject(options);
		return result;
	} catch (error) {
		console.error('Initialization failed:', error.message);
		if (process.env.DEBUG === 'true') {
			console.error('Debug stack trace:', error.stack);
		}
		throw error; // Re-throw to be handled by the command handler
	}
};

// Export version information
export const version = packageJson.version;

// CLI implementation
if (import.meta.url === `file://${process.argv[1]}`) {
	const program = new Command();

	program
		.name('task-master')
		.description('Claude Task Master CLI')
		.version(version);

	program
		.command('init')
		.description('Initialize a new project')
		.option('-y, --yes', 'Skip prompts and use default values')
		.option('-n, --name <n>', 'Project name')
		.option('-d, --description <description>', 'Project description')
		.option('-v, --version <version>', 'Project version', '0.1.0')
		.option('-a, --author <author>', 'Author name')
		.option('--skip-install', 'Skip installing dependencies')
		.option('--dry-run', 'Show what would be done without making changes')
		.option('--aliases', 'Add shell aliases (tm, taskmaster)')
		.option('--no-aliases', 'Skip shell aliases (tm, taskmaster)')
		.option('--git', 'Initialize Git repository')
		.option('--no-git', 'Skip Git repository initialization')
		.option('--git-tasks', 'Store tasks in Git')
		.option('--no-git-tasks', 'No Git storage of tasks')
		.action(async (cmdOptions) => {
			try {
				await runInitCLI(cmdOptions);
			} catch (err) {
				console.error('Init failed:', err.message);
				process.exit(1);
			}
		});

	program
		.command('dev')
		.description('Run the dev.js script')
		.allowUnknownOption(true)
		.action(() => {
			const args = process.argv.slice(process.argv.indexOf('dev') + 1);
			const child = spawn('node', [devScriptPath, ...args], {
				stdio: 'inherit',
				cwd: process.cwd()
			});

			child.on('close', (code) => {
				process.exit(code);
			});
		});

	// Add shortcuts for common dev.js commands
	program
		.command('list')
		.description('List all tasks')
		.action(() => {
			const child = spawn('node', [devScriptPath, 'list'], {
				stdio: 'inherit',
				cwd: process.cwd()
			});

			child.on('close', (code) => {
				process.exit(code);
			});
		});

	program
		.command('next')
		.description('Show the next task to work on')
		.action(() => {
			const child = spawn('node', [devScriptPath, 'next'], {
				stdio: 'inherit',
				cwd: process.cwd()
			});

			child.on('close', (code) => {
				process.exit(code);
			});
		});

	program
		.command('generate')
		.description('Generate task files')
		.action(() => {
			const child = spawn('node', [devScriptPath, 'generate'], {
				stdio: 'inherit',
				cwd: process.cwd()
			});

			child.on('close', (code) => {
				process.exit(code);
			});
		});

	program.parse(process.argv);
}

```

--------------------------------------------------------------------------------
/apps/mcp/src/tools/tasks/get-tasks.tool.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview get-tasks MCP tool
 * Get all tasks from Task Master with optional filtering
 */

import { z } from 'zod';
import {
	handleApiResult,
	withNormalizedProjectRoot
} from '../../shared/utils.js';
import type { MCPContext } from '../../shared/types.js';
import { createTmCore, type TaskStatus, type Task } from '@tm/core';
import type { FastMCP } from 'fastmcp';

const GetTasksSchema = z.object({
	projectRoot: z
		.string()
		.describe('The directory of the project. Must be an absolute path.'),
	status: z
		.string()
		.optional()
		.describe(
			"Filter tasks by status (e.g., 'pending', 'done') or multiple statuses separated by commas (e.g., 'blocked,deferred')"
		),
	withSubtasks: z
		.boolean()
		.optional()
		.describe('Include subtasks nested within their parent tasks in the response'),
	tag: z.string().optional().describe('Tag context to operate on')
});

type GetTasksArgs = z.infer<typeof GetTasksSchema>;

/**
 * Register the get_tasks tool with the MCP server
 */
export function registerGetTasksTool(server: FastMCP) {
	server.addTool({
		name: 'get_tasks',
		description:
			'Get all tasks from Task Master, optionally filtering by status and including subtasks.',
		parameters: GetTasksSchema,
		execute: withNormalizedProjectRoot(
			async (args: GetTasksArgs, context: MCPContext) => {
				const { projectRoot, status, withSubtasks, tag } = args;

				try {
					context.log.info(
						`Getting tasks from ${projectRoot}${status ? ` with status filter: ${status}` : ''}${tag ? ` for tag: ${tag}` : ''}`
					);

					// Create tm-core with logging callback
					const tmCore = await createTmCore({
						projectPath: projectRoot,
						loggerConfig: {
							mcpMode: true,
							logCallback: context.log
						}
					});

					// Build filter
					const filter =
						status && status !== 'all'
							? {
									status: status
										.split(',')
										.map((s: string) => s.trim() as TaskStatus)
								}
							: undefined;

					// Call tm-core tasks.list()
					const result = await tmCore.tasks.list({
						tag,
						filter,
						includeSubtasks: withSubtasks
					});

					context.log.info(
						`Retrieved ${result.tasks?.length || 0} tasks (${result.filtered} filtered, ${result.total} total)`
					);

					// Calculate stats using reduce for cleaner code
					const totalTasks = result.total;
					const taskCounts = result.tasks.reduce(
						(acc, task) => {
							acc[task.status] = (acc[task.status] || 0) + 1;
							return acc;
						},
						{} as Record<string, number>
					);

					const completionPercentage =
						totalTasks > 0 ? ((taskCounts.done || 0) / totalTasks) * 100 : 0;

					// Count subtasks using reduce
					const subtaskCounts = result.tasks.reduce(
						(acc, task) => {
							task.subtasks?.forEach((st) => {
								acc.total++;
								acc[st.status] = (acc[st.status] || 0) + 1;
							});
							return acc;
						},
						{ total: 0 } as Record<string, number>
					);

					const subtaskCompletionPercentage =
						subtaskCounts.total > 0
							? ((subtaskCounts.done || 0) / subtaskCounts.total) * 100
							: 0;

					return handleApiResult({
						result: {
							success: true,
							data: {
								tasks: result.tasks as Task[],
								filter: status || 'all',
								stats: {
									total: totalTasks,
									completed: taskCounts.done || 0,
									inProgress: taskCounts['in-progress'] || 0,
									pending: taskCounts.pending || 0,
									blocked: taskCounts.blocked || 0,
									deferred: taskCounts.deferred || 0,
									cancelled: taskCounts.cancelled || 0,
									review: taskCounts.review || 0,
									completionPercentage,
									subtasks: {
										total: subtaskCounts.total,
										completed: subtaskCounts.done || 0,
										inProgress: subtaskCounts['in-progress'] || 0,
										pending: subtaskCounts.pending || 0,
										blocked: subtaskCounts.blocked || 0,
										deferred: subtaskCounts.deferred || 0,
										cancelled: subtaskCounts.cancelled || 0,
										completionPercentage: subtaskCompletionPercentage
									}
								}
							}
						},
						log: context.log,
						projectRoot,
						tag: result.tag
					});
				} catch (error: any) {
					context.log.error(`Error in get-tasks: ${error.message}`);
					if (error.stack) {
						context.log.debug(error.stack);
					}
					return handleApiResult({
						result: {
							success: false,
							error: {
								message: `Failed to get tasks: ${error.message}`
							}
						},
						log: context.log,
						projectRoot
					});
				}
			}
		)
	});
}

```

--------------------------------------------------------------------------------
/packages/tm-core/src/modules/storage/adapters/activity-logger.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Activity.jsonl append-only logging system for workflow tracking.
 * Uses newline-delimited JSON (JSONL) format for structured event logging.
 *
 * @module activity-logger
 */

import path from 'path';
import fs from 'fs-extra';

/**
 * Activity log entry structure
 */
export interface ActivityEvent {
	timestamp: string;
	type: string;
	[key: string]: any;
}

/**
 * Filter criteria for activity log queries
 */
export interface ActivityFilter {
	type?: string;
	timestampFrom?: string;
	timestampTo?: string;
	predicate?: (event: ActivityEvent) => boolean;
}

/**
 * Appends an activity event to the log file.
 * Uses atomic append operations to ensure data integrity.
 *
 * @param {string} activityPath - Path to the activity.jsonl file
 * @param {Omit<ActivityEvent, 'timestamp'>} event - Event data to log (timestamp added automatically)
 * @returns {Promise<void>}
 *
 * @example
 * await logActivity('/path/to/activity.jsonl', {
 *   type: 'phase-start',
 *   phase: 'red'
 * });
 */
export async function logActivity(
	activityPath: string,
	event: Omit<ActivityEvent, 'timestamp'>
): Promise<void> {
	// Add timestamp to event
	const logEntry = {
		...event,
		timestamp: new Date().toISOString()
	} as ActivityEvent;

	// Ensure directory exists
	await fs.ensureDir(path.dirname(activityPath));

	// Convert to JSONL format (single line with newline)
	const line = JSON.stringify(logEntry) + '\n';

	// Append to file atomically
	// Using 'a' flag ensures atomic append on most systems
	await fs.appendFile(activityPath, line, 'utf-8');
}

/**
 * Reads and parses all events from an activity log file.
 * Returns events in chronological order.
 *
 * @param {string} activityPath - Path to the activity.jsonl file
 * @returns {Promise<ActivityEvent[]>} Array of activity events
 * @throws {Error} If file contains invalid JSON
 *
 * @example
 * const events = await readActivityLog('/path/to/activity.jsonl');
 * console.log(`Found ${events.length} events`);
 */
export async function readActivityLog(
	activityPath: string
): Promise<ActivityEvent[]> {
	// Return empty array if file doesn't exist
	if (!(await fs.pathExists(activityPath))) {
		return [];
	}

	// Read file content
	const content = await fs.readFile(activityPath, 'utf-8');

	// Parse JSONL (newline-delimited JSON)
	const lines = content.trim().split('\n');
	const events: ActivityEvent[] = [];

	for (let i = 0; i < lines.length; i++) {
		const line = lines[i].trim();

		// Skip empty lines
		if (!line) {
			continue;
		}

		// Parse JSON
		try {
			const event = JSON.parse(line);
			events.push(event);
		} catch (error) {
			const errorMessage =
				error instanceof Error ? error.message : String(error);
			throw new Error(`Invalid JSON at line ${i + 1}: ${errorMessage}`);
		}
	}

	return events;
}

/**
 * Filters activity log events based on criteria.
 * Supports filtering by event type, timestamp range, and custom predicates.
 *
 * @param {string} activityPath - Path to the activity.jsonl file
 * @param {ActivityFilter} filter - Filter criteria
 * @returns {Promise<ActivityEvent[]>} Filtered array of events
 *
 * @example
 * // Filter by event type
 * const phaseEvents = await filterActivityLog('/path/to/activity.jsonl', {
 *   type: 'phase-start'
 * });
 *
 * // Filter by timestamp range
 * const recentEvents = await filterActivityLog('/path/to/activity.jsonl', {
 *   timestampFrom: '2024-01-15T10:00:00.000Z'
 * });
 *
 * // Filter with custom predicate
 * const failedTests = await filterActivityLog('/path/to/activity.jsonl', {
 *   predicate: (event) => event.type === 'test-run' && event.result === 'fail'
 * });
 */
export async function filterActivityLog(
	activityPath: string,
	filter: ActivityFilter & Record<string, any>
): Promise<ActivityEvent[]> {
	const events = await readActivityLog(activityPath);

	return events.filter((event) => {
		// Filter by type
		if (filter.type && event.type !== filter.type) {
			return false;
		}

		// Filter by timestamp range
		if (filter.timestampFrom && event.timestamp < filter.timestampFrom) {
			return false;
		}

		if (filter.timestampTo && event.timestamp > filter.timestampTo) {
			return false;
		}

		// Filter by custom predicate
		if (filter.predicate && !filter.predicate(event)) {
			return false;
		}

		// Filter by other fields (exact match)
		for (const [key, value] of Object.entries(filter)) {
			if (
				key === 'type' ||
				key === 'timestampFrom' ||
				key === 'timestampTo' ||
				key === 'predicate'
			) {
				continue;
			}

			if (event[key] !== value) {
				return false;
			}
		}

		return true;
	});
}

```

--------------------------------------------------------------------------------
/scripts/modules/task-manager/find-next-task.js:
--------------------------------------------------------------------------------

```javascript
import { log } from '../utils.js';
import { addComplexityToTask } from '../utils.js';

/**
 * Return the next work item:
 *   •  Prefer an eligible SUBTASK that belongs to any parent task
 *      whose own status is `in-progress`.
 *   •  If no such subtask exists, fall back to the best top-level task
 *      (previous behaviour).
 *
 * The function still exports the same name (`findNextTask`) so callers
 * don't need to change.  It now always returns an object with
 *  ─ id            →  number  (task)  or  "parentId.subId"  (subtask)
 *  ─ title         →  string
 *  ─ status        →  string
 *  ─ priority      →  string  ("high" | "medium" | "low")
 *  ─ dependencies  →  array   (all IDs expressed in the same dotted form)
 *  ─ parentId      →  number  (present only when it's a subtask)
 *
 * @param {Object[]} tasks  – full array of top-level tasks, each may contain .subtasks[]
 * @param {Object} [complexityReport=null] - Optional complexity report object
 * @returns {Object|null}   – next work item or null if nothing is eligible
 */
function findNextTask(tasks, complexityReport = null) {
	// ---------- helpers ----------------------------------------------------
	const priorityValues = { high: 3, medium: 2, low: 1 };

	const toFullSubId = (parentId, maybeDotId) => {
		//  "12.3"  ->  "12.3"
		//        4 ->  "12.4"   (numeric / short form)
		if (typeof maybeDotId === 'string' && maybeDotId.includes('.')) {
			return maybeDotId;
		}
		return `${parentId}.${maybeDotId}`;
	};

	// ---------- build completed-ID set (tasks *and* subtasks) --------------
	const completedIds = new Set();
	tasks.forEach((t) => {
		if (t.status === 'done' || t.status === 'completed') {
			completedIds.add(String(t.id));
		}
		if (Array.isArray(t.subtasks)) {
			t.subtasks.forEach((st) => {
				if (st.status === 'done' || st.status === 'completed') {
					completedIds.add(`${t.id}.${st.id}`);
				}
			});
		}
	});

	// ---------- 1) look for eligible subtasks ------------------------------
	const candidateSubtasks = [];

	tasks
		.filter((t) => t.status === 'in-progress' && Array.isArray(t.subtasks))
		.forEach((parent) => {
			parent.subtasks.forEach((st) => {
				const stStatus = (st.status || 'pending').toLowerCase();
				if (stStatus !== 'pending' && stStatus !== 'in-progress') return;

				const fullDeps =
					st.dependencies?.map((d) => toFullSubId(parent.id, d)) ?? [];

				const depsSatisfied =
					fullDeps.length === 0 ||
					fullDeps.every((depId) => completedIds.has(String(depId)));

				if (depsSatisfied) {
					candidateSubtasks.push({
						id: `${parent.id}.${st.id}`,
						title: st.title || `Subtask ${st.id}`,
						status: st.status || 'pending',
						priority: st.priority || parent.priority || 'medium',
						dependencies: fullDeps,
						parentId: parent.id
					});
				}
			});
		});

	if (candidateSubtasks.length > 0) {
		// sort by priority → dep-count → parent-id → sub-id
		candidateSubtasks.sort((a, b) => {
			const pa = priorityValues[a.priority] ?? 2;
			const pb = priorityValues[b.priority] ?? 2;
			if (pb !== pa) return pb - pa;

			if (a.dependencies.length !== b.dependencies.length)
				return a.dependencies.length - b.dependencies.length;

			// compare parent then sub-id numerically
			const [aPar, aSub] = a.id.split('.').map(Number);
			const [bPar, bSub] = b.id.split('.').map(Number);
			if (aPar !== bPar) return aPar - bPar;
			return aSub - bSub;
		});
		const nextTask = candidateSubtasks[0];

		// Add complexity to the task before returning
		if (nextTask && complexityReport) {
			addComplexityToTask(nextTask, complexityReport);
		}

		return nextTask;
	}

	// ---------- 2) fall back to top-level tasks (original logic) ------------
	const eligibleTasks = tasks.filter((task) => {
		const status = (task.status || 'pending').toLowerCase();
		if (status !== 'pending' && status !== 'in-progress') return false;
		const deps = task.dependencies ?? [];
		return deps.every((depId) => completedIds.has(String(depId)));
	});

	if (eligibleTasks.length === 0) return null;

	const nextTask = eligibleTasks.sort((a, b) => {
		const pa = priorityValues[a.priority || 'medium'] ?? 2;
		const pb = priorityValues[b.priority || 'medium'] ?? 2;
		if (pb !== pa) return pb - pa;

		const da = (a.dependencies ?? []).length;
		const db = (b.dependencies ?? []).length;
		if (da !== db) return da - db;

		return a.id - b.id;
	})[0];

	// Add complexity to the task before returning
	if (nextTask && complexityReport) {
		addComplexityToTask(nextTask, complexityReport);
	}

	return nextTask;
}

export default findNextTask;

```

--------------------------------------------------------------------------------
/tests/unit/ai-providers/openai.test.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Tests for OpenAI Provider
 *
 * This test suite covers:
 * 1. Validation of maxTokens parameter
 * 2. Client creation and configuration
 * 3. Model handling
 */

import { jest } from '@jest/globals';

// Mock the utils module to prevent logging during tests
jest.mock('../../../scripts/modules/utils.js', () => ({
	log: jest.fn()
}));

// Import the provider
import { OpenAIProvider } from '../../../src/ai-providers/openai.js';

describe('OpenAIProvider', () => {
	let provider;

	beforeEach(() => {
		provider = new OpenAIProvider();
		jest.clearAllMocks();
	});

	describe('validateOptionalParams', () => {
		it('should accept valid maxTokens values', () => {
			expect(() =>
				provider.validateOptionalParams({ maxTokens: 1000 })
			).not.toThrow();
			expect(() =>
				provider.validateOptionalParams({ maxTokens: 1 })
			).not.toThrow();
			expect(() =>
				provider.validateOptionalParams({ maxTokens: '1000' })
			).not.toThrow();
		});

		it('should reject invalid maxTokens values', () => {
			expect(() => provider.validateOptionalParams({ maxTokens: 0 })).toThrow(
				Error
			);
			expect(() => provider.validateOptionalParams({ maxTokens: -1 })).toThrow(
				Error
			);
			expect(() => provider.validateOptionalParams({ maxTokens: NaN })).toThrow(
				Error
			);
			expect(() =>
				provider.validateOptionalParams({ maxTokens: Infinity })
			).toThrow(Error);
			expect(() =>
				provider.validateOptionalParams({ maxTokens: 'invalid' })
			).toThrow(Error);
		});

		it('should accept valid temperature values', () => {
			expect(() =>
				provider.validateOptionalParams({ temperature: 0 })
			).not.toThrow();
			expect(() =>
				provider.validateOptionalParams({ temperature: 0.5 })
			).not.toThrow();
			expect(() =>
				provider.validateOptionalParams({ temperature: 1 })
			).not.toThrow();
		});

		it('should reject invalid temperature values', () => {
			expect(() =>
				provider.validateOptionalParams({ temperature: -0.1 })
			).toThrow(Error);
			expect(() =>
				provider.validateOptionalParams({ temperature: 1.1 })
			).toThrow(Error);
		});
	});

	describe('getRequiredApiKeyName', () => {
		it('should return OPENAI_API_KEY', () => {
			expect(provider.getRequiredApiKeyName()).toBe('OPENAI_API_KEY');
		});
	});

	describe('getClient', () => {
		it('should create client even without API key (validation deferred to SDK)', () => {
			// getClient() no longer validates API key - validation is deferred to SDK initialization
			const client = provider.getClient({});
			expect(typeof client).toBe('function');
		});

		it('should create client with apiKey only', () => {
			const params = {
				apiKey: 'sk-test-123'
			};

			// The getClient method should return a function
			const client = provider.getClient(params);
			expect(typeof client).toBe('function');

			// The client function should be callable and return a model object
			const model = client('gpt-4');
			expect(model).toBeDefined();
			expect(model.modelId).toBe('gpt-4');
		});

		it('should create client with apiKey and baseURL', () => {
			const params = {
				apiKey: 'sk-test-456',
				baseURL: 'https://api.openai.example'
			};

			// Should not throw when baseURL is provided
			const client = provider.getClient(params);
			expect(typeof client).toBe('function');

			// The client function should be callable and return a model object
			const model = client('gpt-5');
			expect(model).toBeDefined();
			expect(model.modelId).toBe('gpt-5');
		});

		it('should return the same client instance for the same parameters', () => {
			const params = {
				apiKey: 'sk-test-789'
			};

			// Multiple calls with same params should work
			const client1 = provider.getClient(params);
			const client2 = provider.getClient(params);

			expect(typeof client1).toBe('function');
			expect(typeof client2).toBe('function');

			// Both clients should be able to create models
			const model1 = client1('gpt-4');
			const model2 = client2('gpt-4');
			expect(model1.modelId).toBe('gpt-4');
			expect(model2.modelId).toBe('gpt-4');
		});

		it('should handle different model IDs correctly', () => {
			const client = provider.getClient({ apiKey: 'sk-test-models' });

			// Test with different models
			const gpt4 = client('gpt-4');
			expect(gpt4.modelId).toBe('gpt-4');

			const gpt5 = client('gpt-5');
			expect(gpt5.modelId).toBe('gpt-5');

			const gpt35 = client('gpt-3.5-turbo');
			expect(gpt35.modelId).toBe('gpt-3.5-turbo');
		});
	});

	describe('name property', () => {
		it('should have OpenAI as the provider name', () => {
			expect(provider.name).toBe('OpenAI');
		});
	});
});

```

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

```typescript
/**
 * @fileoverview Configuration Loader Service
 * Responsible for loading configuration from various file sources
 */

import fs from 'node:fs/promises';
import path from 'node:path';
import {
	ERROR_CODES,
	TaskMasterError
} from '../../../common/errors/task-master-error.js';
import type { PartialConfiguration } from '../../../common/interfaces/configuration.interface.js';
import { DEFAULT_CONFIG_VALUES } from '../../../common/interfaces/configuration.interface.js';

/**
 * ConfigLoader handles loading configuration from files
 * Single responsibility: File-based configuration loading
 */
export class ConfigLoader {
	private localConfigPath: string;
	private globalConfigPath: string;

	constructor(projectRoot: string) {
		this.localConfigPath = path.join(projectRoot, '.taskmaster', 'config.json');
		this.globalConfigPath = path.join(
			process.env.HOME || '',
			'.taskmaster',
			'config.json'
		);
	}

	/**
	 * Get default configuration values
	 */
	getDefaultConfig(): PartialConfiguration {
		return {
			models: {
				main: DEFAULT_CONFIG_VALUES.MODELS.MAIN,
				fallback: DEFAULT_CONFIG_VALUES.MODELS.FALLBACK
			},
			workflow: {
				enableAutopilot: DEFAULT_CONFIG_VALUES.WORKFLOW.ENABLE_AUTOPILOT,
				maxPhaseAttempts: DEFAULT_CONFIG_VALUES.WORKFLOW.MAX_PHASE_ATTEMPTS,
				branchPattern: DEFAULT_CONFIG_VALUES.WORKFLOW.BRANCH_PATTERN,
				requireCleanWorkingTree:
					DEFAULT_CONFIG_VALUES.WORKFLOW.REQUIRE_CLEAN_WORKING_TREE,
				autoStageChanges: DEFAULT_CONFIG_VALUES.WORKFLOW.AUTO_STAGE_CHANGES,
				includeCoAuthor: DEFAULT_CONFIG_VALUES.WORKFLOW.INCLUDE_CO_AUTHOR,
				coAuthorName: DEFAULT_CONFIG_VALUES.WORKFLOW.CO_AUTHOR_NAME,
				coAuthorEmail: DEFAULT_CONFIG_VALUES.WORKFLOW.CO_AUTHOR_EMAIL,
				testThresholds: {
					minTests: DEFAULT_CONFIG_VALUES.WORKFLOW.MIN_TESTS,
					maxFailuresInGreen:
						DEFAULT_CONFIG_VALUES.WORKFLOW.MAX_FAILURES_IN_GREEN
				},
				commitMessageTemplate:
					DEFAULT_CONFIG_VALUES.WORKFLOW.COMMIT_MESSAGE_TEMPLATE,
				allowedCommitTypes: [
					...DEFAULT_CONFIG_VALUES.WORKFLOW.ALLOWED_COMMIT_TYPES
				],
				defaultCommitType: DEFAULT_CONFIG_VALUES.WORKFLOW.DEFAULT_COMMIT_TYPE,
				operationTimeout: DEFAULT_CONFIG_VALUES.WORKFLOW.OPERATION_TIMEOUT,
				enableActivityLogging:
					DEFAULT_CONFIG_VALUES.WORKFLOW.ENABLE_ACTIVITY_LOGGING,
				activityLogPath: DEFAULT_CONFIG_VALUES.WORKFLOW.ACTIVITY_LOG_PATH,
				enableStateBackup: DEFAULT_CONFIG_VALUES.WORKFLOW.ENABLE_STATE_BACKUP,
				maxStateBackups: DEFAULT_CONFIG_VALUES.WORKFLOW.MAX_STATE_BACKUPS,
				abortOnMaxAttempts: DEFAULT_CONFIG_VALUES.WORKFLOW.ABORT_ON_MAX_ATTEMPTS
			},
			storage: {
				type: DEFAULT_CONFIG_VALUES.STORAGE.TYPE,
				encoding: DEFAULT_CONFIG_VALUES.STORAGE.ENCODING,
				enableBackup: false,
				maxBackups: DEFAULT_CONFIG_VALUES.STORAGE.MAX_BACKUPS,
				enableCompression: false,
				atomicOperations: true
			},
			version: DEFAULT_CONFIG_VALUES.VERSION
		};
	}

	/**
	 * Load local project configuration
	 */
	async loadLocalConfig(): Promise<PartialConfiguration | null> {
		try {
			const configData = await fs.readFile(this.localConfigPath, 'utf-8');
			return JSON.parse(configData);
		} catch (error: any) {
			if (error.code === 'ENOENT') {
				// File doesn't exist, return null
				console.debug('No local config.json found, using defaults');
				return null;
			}
			throw new TaskMasterError(
				'Failed to load local configuration',
				ERROR_CODES.CONFIG_ERROR,
				{ configPath: this.localConfigPath },
				error
			);
		}
	}

	/**
	 * Load global user configuration
	 * @future-implementation Full implementation pending
	 */
	async loadGlobalConfig(): Promise<PartialConfiguration | null> {
		// TODO: Implement in future PR
		// For now, return null to indicate no global config
		return null;

		// Future implementation:
		// try {
		//   const configData = await fs.readFile(this.globalConfigPath, 'utf-8');
		//   return JSON.parse(configData);
		// } catch (error: any) {
		//   if (error.code === 'ENOENT') {
		//     return null;
		//   }
		//   throw new TaskMasterError(
		//     'Failed to load global configuration',
		//     ERROR_CODES.CONFIG_ERROR,
		//     { configPath: this.globalConfigPath },
		//     error
		//   );
		// }
	}

	/**
	 * Check if local config exists
	 */
	async hasLocalConfig(): Promise<boolean> {
		try {
			await fs.access(this.localConfigPath);
			return true;
		} catch {
			return false;
		}
	}

	/**
	 * Check if global config exists
	 */
	async hasGlobalConfig(): Promise<boolean> {
		try {
			await fs.access(this.globalConfigPath);
			return true;
		} catch {
			return false;
		}
	}
}

```
Page 10/50FirstPrevNextLast