#
tokens: 49848/50000 35/975 files (page 9/50)
lines: off (toggle) GitHub
raw markdown copy
This is page 9 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/progress/tracker-ui.js:
--------------------------------------------------------------------------------

```javascript
import chalk from 'chalk';

/**
 * Factory for creating progress bar elements
 */
class ProgressBarFactory {
	constructor(multibar) {
		if (!multibar) {
			throw new Error('Multibar instance is required');
		}
		this.multibar = multibar;
	}

	/**
	 * Creates a progress bar with the given format
	 */
	createBar(format, payload = {}) {
		if (typeof format !== 'string') {
			throw new Error('Format must be a string');
		}

		const bar = this.multibar.create(
			1, // total
			1, // current
			{},
			{
				format,
				barsize: 1,
				hideCursor: true,
				clearOnComplete: false
			}
		);

		bar.update(1, payload);
		return bar;
	}

	/**
	 * Creates a header with borders
	 */
	createHeader(headerFormat, borderFormat) {
		this.createBar(borderFormat); // Top border
		this.createBar(headerFormat); // Header
		this.createBar(borderFormat); // Bottom border
	}

	/**
	 * Creates a data row
	 */
	createRow(rowFormat, payload) {
		if (!payload || typeof payload !== 'object') {
			throw new Error('Payload must be an object');
		}
		return this.createBar(rowFormat, payload);
	}

	/**
	 * Creates a border element
	 */
	createBorder(borderFormat) {
		return this.createBar(borderFormat);
	}
}

/**
 * Creates a bordered header for progress tables.
 * @param {Object} multibar - The multibar instance.
 * @param {string} headerFormat - Format string for the header row.
 * @param {string} borderFormat - Format string for the top and bottom borders.
 * @returns {void}
 */
export function createProgressHeader(multibar, headerFormat, borderFormat) {
	const factory = new ProgressBarFactory(multibar);
	factory.createHeader(headerFormat, borderFormat);
}

/**
 * Creates a formatted data row for progress tables.
 * @param {Object} multibar - The multibar instance.
 * @param {string} rowFormat - Format string for the row.
 * @param {Object} payload - Data payload for the row format.
 * @returns {void}
 */
export function createProgressRow(multibar, rowFormat, payload) {
	const factory = new ProgressBarFactory(multibar);
	factory.createRow(rowFormat, payload);
}

/**
 * Creates a border row for progress tables.
 * @param {Object} multibar - The multibar instance.
 * @param {string} borderFormat - Format string for the border.
 * @returns {void}
 */
export function createBorder(multibar, borderFormat) {
	const factory = new ProgressBarFactory(multibar);
	factory.createBorder(borderFormat);
}

/**
 * Builder for creating progress tables with consistent formatting
 */
export class ProgressTableBuilder {
	constructor(multibar) {
		this.factory = new ProgressBarFactory(multibar);
		this.borderStyle = '─';
		this.columnSeparator = '|';
	}

	/**
	 * Shows a formatted table header
	 */
	showHeader(columns = null) {
		// Default columns for task display
		const defaultColumns = [
			{ text: 'TASK', width: 6 },
			{ text: 'PRI', width: 5 },
			{ text: 'TITLE', width: 64 }
		];

		const cols = columns || defaultColumns;
		const headerText = ' ' + cols.map((c) => c.text).join(' | ') + ' ';
		const borderLine = this.createBorderLine(cols.map((c) => c.width));

		this.factory.createHeader(headerText, borderLine);
		return this;
	}

	/**
	 * Creates a border line based on column widths
	 */
	createBorderLine(columnWidths) {
		return columnWidths
			.map((width) => this.borderStyle.repeat(width))
			.join('─┼─');
	}

	/**
	 * Adds a task row to the table
	 */
	addTaskRow(taskId, priority, title) {
		const format = ` ${taskId} | ${priority} | {title}`;
		this.factory.createRow(format, { title });

		// Add separator after each row
		const borderLine = '------+-----+' + '─'.repeat(64);
		this.factory.createBorder(borderLine);
		return this;
	}

	/**
	 * Creates a summary row
	 */
	addSummaryRow(label, value) {
		const format = `  ${label}: {value}`;
		this.factory.createRow(format, { value });
		return this;
	}
}

```

--------------------------------------------------------------------------------
/tests/unit/mcp/tools/move-task-cross-tag-options.test.js:
--------------------------------------------------------------------------------

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

// Mocks
const mockFindTasksPath = jest
	.fn()
	.mockReturnValue('/test/path/.taskmaster/tasks/tasks.json');
jest.unstable_mockModule(
	'../../../../mcp-server/src/core/utils/path-utils.js',
	() => ({
		findTasksPath: mockFindTasksPath
	})
);

const mockEnableSilentMode = jest.fn();
const mockDisableSilentMode = jest.fn();
jest.unstable_mockModule('../../../../scripts/modules/utils.js', () => ({
	enableSilentMode: mockEnableSilentMode,
	disableSilentMode: mockDisableSilentMode
}));

// Spyable mock for moveTasksBetweenTags
const mockMoveTasksBetweenTags = jest.fn();
jest.unstable_mockModule(
	'../../../../scripts/modules/task-manager/move-task.js',
	() => ({
		moveTasksBetweenTags: mockMoveTasksBetweenTags
	})
);

// Import after mocks
const { moveTaskCrossTagDirect } = await import(
	'../../../../mcp-server/src/core/direct-functions/move-task-cross-tag.js'
);

describe('MCP Cross-Tag Move Direct Function - options & suggestions', () => {
	const mockLog = { info: jest.fn(), warn: jest.fn(), error: jest.fn() };

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

	it('passes only withDependencies/ignoreDependencies (no force) to core', async () => {
		// Arrange: make core throw tag validation after call to capture params
		mockMoveTasksBetweenTags.mockImplementation(() => {
			const err = new Error('Source tag "invalid" not found or invalid');
			err.code = 'INVALID_SOURCE_TAG';
			throw err;
		});

		// Act
		await moveTaskCrossTagDirect(
			{
				sourceIds: '1,2',
				sourceTag: 'backlog',
				targetTag: 'in-progress',
				withDependencies: true,
				projectRoot: '/test'
			},
			mockLog
		);

		// Assert options argument (5th param)
		expect(mockMoveTasksBetweenTags).toHaveBeenCalled();
		const args = mockMoveTasksBetweenTags.mock.calls[0];
		const moveOptions = args[4];
		expect(moveOptions).toEqual({
			withDependencies: true,
			ignoreDependencies: false
		});
		expect('force' in moveOptions).toBe(false);
	});

	it('returns conflict suggestions on cross-tag dependency conflicts', async () => {
		// Arrange: core throws cross-tag dependency conflicts
		mockMoveTasksBetweenTags.mockImplementation(() => {
			const err = new Error(
				'Cannot move tasks: 2 cross-tag dependency conflicts found'
			);
			err.code = 'CROSS_TAG_DEPENDENCY_CONFLICTS';
			throw err;
		});

		// Act
		const result = await moveTaskCrossTagDirect(
			{
				sourceIds: '1',
				sourceTag: 'backlog',
				targetTag: 'in-progress',
				projectRoot: '/test'
			},
			mockLog
		);

		// Assert
		expect(result.success).toBe(false);
		expect(result.error.code).toBe('CROSS_TAG_DEPENDENCY_CONFLICT');
		expect(Array.isArray(result.error.suggestions)).toBe(true);
		// Key suggestions
		const s = result.error.suggestions.join(' ');
		expect(s).toContain('--with-dependencies');
		expect(s).toContain('--ignore-dependencies');
		expect(s).toContain('validate-dependencies');
		expect(s).toContain('Move dependencies first');
	});

	it('returns ID collision suggestions when target tag already has the ID', async () => {
		// Arrange: core throws TASK_ALREADY_EXISTS structured error
		mockMoveTasksBetweenTags.mockImplementation(() => {
			const err = new Error(
				'Task 1 already exists in target tag "in-progress"'
			);
			err.code = 'TASK_ALREADY_EXISTS';
			throw err;
		});

		// Act
		const result = await moveTaskCrossTagDirect(
			{
				sourceIds: '1',
				sourceTag: 'backlog',
				targetTag: 'in-progress',
				projectRoot: '/test'
			},
			mockLog
		);

		// Assert
		expect(result.success).toBe(false);
		expect(result.error.code).toBe('TASK_ALREADY_EXISTS');
		const joined = (result.error.suggestions || []).join(' ');
		expect(joined).toContain('different target tag');
		expect(joined).toContain('different set of IDs');
		expect(joined).toContain('within-tag');
	});
});

```

--------------------------------------------------------------------------------
/.github/workflows/weekly-metrics-discord.yml:
--------------------------------------------------------------------------------

```yaml
name: Weekly Metrics to Discord
# description: Sends weekly metrics summary to Discord channel

on:
  schedule:
    - cron: "0 9 * * 1" # Every Monday at 9 AM
  workflow_dispatch:

permissions:
  contents: read
  issues: read
  pull-requests: read

jobs:
  weekly-metrics:
    runs-on: ubuntu-latest
    env:
      DISCORD_WEBHOOK: ${{ secrets.DISCORD_METRICS_WEBHOOK }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Get dates for last 14 days
        run: |
          set -Eeuo pipefail
          # Last 14 days
          first_day=$(date -d "14 days ago" +%Y-%m-%d)
          last_day=$(date +%Y-%m-%d)

          echo "first_day=$first_day" >> $GITHUB_ENV
          echo "last_day=$last_day" >> $GITHUB_ENV
          echo "week_of=$(date -d '7 days ago' +'Week of %B %d, %Y')" >> $GITHUB_ENV
          echo "date_range=Past 14 days ($first_day to $last_day)" >> $GITHUB_ENV

      - name: Generate issue metrics
        uses: github/issue-metrics@v3
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SEARCH_QUERY: "repo:${{ github.repository }} is:issue created:${{ env.first_day }}..${{ env.last_day }}"
          HIDE_TIME_TO_ANSWER: true
          HIDE_LABEL_METRICS: false
          OUTPUT_FILE: issue_metrics.md

      - name: Generate PR created metrics
        uses: github/issue-metrics@v3
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SEARCH_QUERY: "repo:${{ github.repository }} is:pr created:${{ env.first_day }}..${{ env.last_day }}"
          OUTPUT_FILE: pr_created_metrics.md

      - name: Generate PR merged metrics
        uses: github/issue-metrics@v3
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SEARCH_QUERY: "repo:${{ github.repository }} is:pr is:merged merged:${{ env.first_day }}..${{ env.last_day }}"
          OUTPUT_FILE: pr_merged_metrics.md

      - name: Debug generated metrics
        run: |
          set -Eeuo pipefail
          echo "Listing markdown files in workspace:"
          ls -la *.md || true
          for f in issue_metrics.md pr_created_metrics.md pr_merged_metrics.md; do
            if [ -f "$f" ]; then
              echo "== $f (first 10 lines) =="
              head -n 10 "$f"
            else
              echo "Missing $f"
            fi
          done

      - name: Parse metrics
        id: metrics
        run: node .github/scripts/parse-metrics.mjs

      - name: Send to Discord
        uses: sarisia/actions-status-discord@v1
        if: env.DISCORD_WEBHOOK != ''
        with:
          webhook: ${{ env.DISCORD_WEBHOOK }}
          status: Success
          title: "📊 Weekly Metrics Report"
          description: |
            **${{ env.week_of }}**
            *${{ env.date_range }}*

            **🎯 Issues**
            • Created: ${{ steps.metrics.outputs.issues_created }}
            • Closed: ${{ steps.metrics.outputs.issues_closed }}
            • Avg Response Time: ${{ steps.metrics.outputs.issue_avg_first_response }}
            • Avg Time to Close: ${{ steps.metrics.outputs.issue_avg_time_to_close }}

            **🔀 Pull Requests**
            • Created: ${{ steps.metrics.outputs.prs_created }}
            • Merged: ${{ steps.metrics.outputs.prs_merged }}
            • Avg Response Time: ${{ steps.metrics.outputs.pr_avg_first_response }}
            • Avg Time to Merge: ${{ steps.metrics.outputs.pr_avg_merge_time }}

            **📈 Visual Analytics**
            https://repobeats.axiom.co/api/embed/b439f28f0ab5bd7a2da19505355693cd2c55bfd4.svg
          color: 0x58AFFF
          username: Task Master Metrics Bot
          avatar_url: https://raw.githubusercontent.com/eyaltoledano/claude-task-master/main/images/logo.png

```

--------------------------------------------------------------------------------
/apps/extension/src/webview/utils/logger.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Webview Logger Utility
 * Provides conditional logging based on environment
 */

type LogLevel = 'log' | 'warn' | 'error' | 'debug' | 'info';

interface LogEntry {
	level: LogLevel;
	message: string;
	data?: any;
	timestamp: number;
}

class WebviewLogger {
	private static instance: WebviewLogger;
	private enabled: boolean;
	private logHistory: LogEntry[] = [];
	private maxHistorySize = 100;

	private constructor() {
		// Enable logging in development, disable in production
		// Check for development mode via various indicators
		this.enabled = this.isDevelopment();
	}

	static getInstance(): WebviewLogger {
		if (!WebviewLogger.instance) {
			WebviewLogger.instance = new WebviewLogger();
		}
		return WebviewLogger.instance;
	}

	private isDevelopment(): boolean {
		// Check various indicators for development mode
		// VS Code webviews don't have process.env, so we check other indicators
		return (
			// Check if running in localhost (development server)
			window.location.hostname === 'localhost' ||
			// Check for development query parameter
			window.location.search.includes('debug=true') ||
			// Check for VS Code development mode indicator
			(window as any).__VSCODE_DEV_MODE__ === true ||
			// Default to false in production
			false
		);
	}

	private addToHistory(entry: LogEntry): void {
		this.logHistory.push(entry);
		if (this.logHistory.length > this.maxHistorySize) {
			this.logHistory.shift();
		}
	}

	private logMessage(level: LogLevel, message: string, ...args: any[]): void {
		const entry: LogEntry = {
			level,
			message,
			data: args.length > 0 ? args : undefined,
			timestamp: Date.now()
		};

		this.addToHistory(entry);

		if (!this.enabled) {
			return;
		}

		// Format the message with timestamp
		const timestamp = new Date().toISOString();
		const prefix = `[${timestamp}] [${level.toUpperCase()}]`;

		// Use appropriate console method
		switch (level) {
			case 'error':
				console.error(prefix, message, ...args);
				break;
			case 'warn':
				console.warn(prefix, message, ...args);
				break;
			case 'debug':
				console.debug(prefix, message, ...args);
				break;
			case 'info':
				console.info(prefix, message, ...args);
				break;
			default:
				console.log(prefix, message, ...args);
		}
	}

	log(message: string, ...args: any[]): void {
		this.logMessage('log', message, ...args);
	}

	error(message: string, ...args: any[]): void {
		// Always log errors, even in production
		const entry: LogEntry = {
			level: 'error',
			message,
			data: args.length > 0 ? args : undefined,
			timestamp: Date.now()
		};
		this.addToHistory(entry);
		console.error(`[${new Date().toISOString()}] [ERROR]`, message, ...args);
	}

	warn(message: string, ...args: any[]): void {
		this.logMessage('warn', message, ...args);
	}

	debug(message: string, ...args: any[]): void {
		this.logMessage('debug', message, ...args);
	}

	info(message: string, ...args: any[]): void {
		this.logMessage('info', message, ...args);
	}

	// Enable/disable logging dynamically
	setEnabled(enabled: boolean): void {
		this.enabled = enabled;
		if (enabled) {
			console.log('[WebviewLogger] Logging enabled');
		}
	}

	// Get log history (useful for debugging)
	getHistory(): LogEntry[] {
		return [...this.logHistory];
	}

	// Clear log history
	clearHistory(): void {
		this.logHistory = [];
	}

	// Export logs as string (useful for bug reports)
	exportLogs(): string {
		return this.logHistory
			.map((entry) => {
				const timestamp = new Date(entry.timestamp).toISOString();
				const data = entry.data ? JSON.stringify(entry.data) : '';
				return `[${timestamp}] [${entry.level.toUpperCase()}] ${entry.message} ${data}`;
			})
			.join('\n');
	}
}

// Export singleton instance
export const logger = WebviewLogger.getInstance();

// Export type for use in other files
export type { WebviewLogger };

```

--------------------------------------------------------------------------------
/apps/extension/src/services/terminal-manager.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Terminal Manager - Handles task execution in VS Code terminals
 * Uses @tm/core for consistent task management with the CLI
 */

import * as vscode from 'vscode';
import { createTmCore, type TmCore } from '@tm/core';
import type { ExtensionLogger } from '../utils/logger';

export interface TerminalExecutionOptions {
	taskId: string;
	taskTitle: string;
	tag?: string;
}

export interface TerminalExecutionResult {
	success: boolean;
	error?: string;
	terminalName?: string;
}

export class TerminalManager {
	private terminals = new Map<string, vscode.Terminal>();
	private tmCore?: TmCore;

	constructor(
		private context: vscode.ExtensionContext,
		private logger: ExtensionLogger
	) {}

	/**
	 * Execute a task in a new VS Code terminal with Claude
	 * Uses @tm/core for consistent task management with the CLI
	 */
	async executeTask(
		options: TerminalExecutionOptions
	): Promise<TerminalExecutionResult> {
		const { taskTitle, tag } = options;
		// Ensure taskId is always a string
		const taskId = String(options.taskId);

		this.logger.log(
			`Starting task execution for ${taskId}: ${taskTitle}${tag ? ` (tag: ${tag})` : ''}`
		);
		this.logger.log(`TaskId type: ${typeof taskId}, value: ${taskId}`);

		try {
			// Initialize tm-core if needed
			await this.initializeCore();

			// Use tm-core to start the task (same as CLI)
			const startResult = await this.tmCore!.tasks.start(taskId, {
				dryRun: false,
				force: false,
				updateStatus: true
			});

			if (!startResult.started || !startResult.executionOutput) {
				throw new Error(
					startResult.error || 'Failed to start task with tm-core'
				);
			}

			// Create terminal with custom TaskMaster icon
			const terminalName = `Task ${taskId}: ${taskTitle}`;
			const terminal = this.createTerminal(terminalName);

			// Store terminal reference for potential cleanup
			this.terminals.set(taskId, terminal);

			// Show terminal and run Claude command
			terminal.show();
			const command = `claude "${startResult.executionOutput}"`;
			terminal.sendText(command);

			this.logger.log(`Launched Claude for task ${taskId} using tm-core`);

			return {
				success: true,
				terminalName
			};
		} catch (error) {
			this.logger.error('Failed to execute task:', error);
			return {
				success: false,
				error: error instanceof Error ? error.message : 'Unknown error'
			};
		}
	}

	/**
	 * Create a new terminal with TaskMaster branding
	 */
	private createTerminal(name: string): vscode.Terminal {
		const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;

		return vscode.window.createTerminal({
			name,
			cwd: workspaceRoot,
			iconPath: new vscode.ThemeIcon('play') // Use a VS Code built-in icon for now
		});
	}

	/**
	 * Initialize TaskMaster Core (same as CLI)
	 */
	private async initializeCore(): Promise<void> {
		if (!this.tmCore) {
			const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
			if (!workspaceRoot) {
				throw new Error('No workspace folder found');
			}
			this.tmCore = await createTmCore({ projectPath: workspaceRoot });
		}
	}

	/**
	 * Get terminal by task ID (if still active)
	 */
	getTerminalByTaskId(taskId: string): vscode.Terminal | undefined {
		return this.terminals.get(taskId);
	}

	/**
	 * Clean up terminated terminals
	 */
	cleanupTerminal(taskId: string): void {
		const terminal = this.terminals.get(taskId);
		if (terminal) {
			this.terminals.delete(taskId);
		}
	}

	/**
	 * Dispose all managed terminals and clean up tm-core
	 */
	async dispose(): Promise<void> {
		this.terminals.forEach((terminal) => {
			try {
				terminal.dispose();
			} catch (error) {
				this.logger.error('Failed to dispose terminal:', error);
			}
		});
		this.terminals.clear();

		// Clear tm-core reference (no explicit cleanup needed)
		if (this.tmCore) {
			this.tmCore = undefined;
		}
	}
}

```

--------------------------------------------------------------------------------
/packages/tm-core/src/modules/auth/managers/auth-manager.spec.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Tests for AuthManager singleton behavior
 */

import { beforeEach, describe, expect, it, vi } from 'vitest';

// Mock the logger to verify warnings (must be hoisted before SUT import)
const mockLogger = {
	warn: vi.fn(),
	info: vi.fn(),
	debug: vi.fn(),
	error: vi.fn()
};

vi.mock('../logger/index.js', () => ({
	getLogger: () => mockLogger
}));

// Spy on CredentialStore constructor to verify config propagation
const CredentialStoreSpy = vi.fn();
vi.mock('./credential-store.js', () => {
	return {
		CredentialStore: class {
			static getInstance(config?: any) {
				return new (this as any)(config);
			}
			static resetInstance() {
				// Mock reset instance method
			}
			constructor(config: any) {
				CredentialStoreSpy(config);
			}
			getCredentials(_options?: any) {
				return null;
			}
			saveCredentials() {}
			clearCredentials() {}
			hasCredentials() {
				return false;
			}
		}
	};
});

// Mock OAuthService to avoid side effects
vi.mock('./oauth-service.js', () => {
	return {
		OAuthService: class {
			constructor() {}
			authenticate() {
				return Promise.resolve({});
			}
			getAuthorizationUrl() {
				return null;
			}
		}
	};
});

// Mock SupabaseAuthClient to avoid side effects
vi.mock('../clients/supabase-client.js', () => {
	return {
		SupabaseAuthClient: class {
			constructor() {}
			refreshSession() {
				return Promise.resolve({});
			}
			signOut() {
				return Promise.resolve();
			}
		}
	};
});

// Import SUT after mocks
import { AuthManager } from './auth-manager.js';

describe('AuthManager Singleton', () => {
	beforeEach(() => {
		// Reset singleton before each test
		AuthManager.resetInstance();
		vi.clearAllMocks();
		CredentialStoreSpy.mockClear();
	});

	it('should return the same instance on multiple calls', () => {
		const instance1 = AuthManager.getInstance();
		const instance2 = AuthManager.getInstance();

		expect(instance1).toBe(instance2);
	});

	it('should use config on first call', async () => {
		const config = {
			baseUrl: 'https://test.auth.com',
			configDir: '/test/config',
			configFile: '/test/config/auth.json'
		};

		const instance = AuthManager.getInstance(config);
		expect(instance).toBeDefined();

		// Assert that CredentialStore was constructed with the provided config
		expect(CredentialStoreSpy).toHaveBeenCalledTimes(1);
		expect(CredentialStoreSpy).toHaveBeenCalledWith(config);

		// Verify the config is passed to internal components through observable behavior
		// getCredentials would look in the configured file path
		const credentials = await instance.getCredentials();
		expect(credentials).toBeNull(); // File doesn't exist, but config was propagated correctly
	});

	it('should warn when config is provided after initialization', () => {
		// Clear previous calls
		mockLogger.warn.mockClear();

		// First call with config
		AuthManager.getInstance({ baseUrl: 'https://first.auth.com' });

		// Second call with different config
		AuthManager.getInstance({ baseUrl: 'https://second.auth.com' });

		// Verify warning was logged
		expect(mockLogger.warn).toHaveBeenCalledWith(
			expect.stringMatching(/config.*after initialization.*ignored/i)
		);
	});

	it('should not warn when no config is provided after initialization', () => {
		// Clear previous calls
		mockLogger.warn.mockClear();

		// First call with config
		AuthManager.getInstance({ configDir: '/test/config' });

		// Second call without config
		AuthManager.getInstance();

		// Verify no warning was logged
		expect(mockLogger.warn).not.toHaveBeenCalled();
	});

	it('should allow resetting the instance', () => {
		const instance1 = AuthManager.getInstance();

		// Reset the instance
		AuthManager.resetInstance();

		// Get new instance
		const instance2 = AuthManager.getInstance();

		// They should be different instances
		expect(instance1).not.toBe(instance2);
	});
});

```

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

```typescript
/**
 * Message format conversion utilities for Grok CLI provider
 */

import type { GrokCliMessage, GrokCliResponse } from './types.js';

/**
 * AI SDK message type (simplified interface)
 */
interface AISDKMessage {
	role: string;
	content:
		| string
		| Array<{ type: string; text?: string }>
		| { text?: string; [key: string]: unknown };
}

/**
 * Convert AI SDK messages to Grok CLI compatible format
 * @param messages - AI SDK message array
 * @returns Grok CLI compatible messages
 */
export function convertToGrokCliMessages(
	messages: AISDKMessage[]
): GrokCliMessage[] {
	return messages.map((message) => {
		// Handle different message content types
		let content = '';

		if (typeof message.content === 'string') {
			content = message.content;
		} else if (Array.isArray(message.content)) {
			// Handle multi-part content (text and images)
			content = message.content
				.filter((part) => part.type === 'text')
				.map((part) => part.text || '')
				.join('\n');
		} else if (message.content && typeof message.content === 'object') {
			// Handle object content
			content = message.content.text || JSON.stringify(message.content);
		}

		return {
			role: message.role,
			content: content.trim()
		};
	});
}

/**
 * Convert Grok CLI response to AI SDK format
 * @param responseText - Raw response text from Grok CLI (JSONL format)
 * @returns AI SDK compatible response object
 */
export function convertFromGrokCliResponse(responseText: string): {
	text: string;
	usage?: {
		promptTokens: number;
		completionTokens: number;
		totalTokens: number;
	};
} {
	try {
		// Grok CLI outputs JSONL format - each line is a separate JSON message
		const lines = responseText
			.trim()
			.split('\n')
			.filter((line) => line.trim());

		// Parse each line as JSON and find assistant messages
		const messages: GrokCliResponse[] = [];
		for (const line of lines) {
			try {
				const message = JSON.parse(line) as GrokCliResponse;
				messages.push(message);
			} catch (parseError) {
				// Skip invalid JSON lines
				continue;
			}
		}

		// Find the last assistant message
		const assistantMessage = messages
			.filter((msg) => msg.role === 'assistant')
			.pop();

		if (assistantMessage && assistantMessage.content) {
			return {
				text: assistantMessage.content,
				usage: assistantMessage.usage
					? {
							promptTokens: assistantMessage.usage.prompt_tokens || 0,
							completionTokens: assistantMessage.usage.completion_tokens || 0,
							totalTokens: assistantMessage.usage.total_tokens || 0
						}
					: undefined
			};
		}

		// Fallback: if no assistant message found, return the raw text
		return {
			text: responseText.trim(),
			usage: undefined
		};
	} catch (error) {
		// If parsing fails completely, treat as plain text response
		return {
			text: responseText.trim(),
			usage: undefined
		};
	}
}

/**
 * Create a prompt string for Grok CLI from messages
 * @param messages - AI SDK message array
 * @returns Formatted prompt string
 */
export function createPromptFromMessages(messages: AISDKMessage[]): string {
	const grokMessages = convertToGrokCliMessages(messages);

	// Create a conversation-style prompt
	const prompt = grokMessages
		.map((message) => {
			switch (message.role) {
				case 'system':
					return `System: ${message.content}`;
				case 'user':
					return `User: ${message.content}`;
				case 'assistant':
					return `Assistant: ${message.content}`;
				default:
					return `${message.role}: ${message.content}`;
			}
		})
		.join('\n\n');

	return prompt;
}

/**
 * Escape shell arguments for safe CLI execution
 * @param arg - Argument to escape
 * @returns Shell-escaped argument
 */
export function escapeShellArg(arg: string | unknown): string {
	if (typeof arg !== 'string') {
		arg = String(arg);
	}

	// Replace single quotes with '\''
	return "'" + (arg as string).replace(/'/g, "'\\''") + "'";
}

```

--------------------------------------------------------------------------------
/apps/cli/src/ui/display/tables.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Table display utilities
 * Provides table creation and formatting for tasks
 */

import type { Subtask, Task, TaskPriority } from '@tm/core';
import chalk from 'chalk';
import Table from 'cli-table3';
import { getComplexityWithColor } from '../formatters/complexity-formatters.js';
import { getPriorityWithColor } from '../formatters/priority-formatters.js';
import { getStatusWithColor } from '../formatters/status-formatters.js';
import { getBoxWidth, truncate } from '../layout/helpers.js';

/**
 * Default priority for tasks/subtasks when not specified
 */
const DEFAULT_PRIORITY: TaskPriority = 'medium';

/**
 * Create a task table for display
 */
export function createTaskTable(
	tasks: (Task | Subtask)[],
	options?: {
		showSubtasks?: boolean;
		showComplexity?: boolean;
		showDependencies?: boolean;
	}
): string {
	const {
		showSubtasks = false,
		showComplexity = false,
		showDependencies = true
	} = options || {};

	// Calculate dynamic column widths based on terminal width
	const tableWidth = getBoxWidth(0.9, 100);
	// Adjust column widths to better match the original layout
	const baseColWidths = showComplexity
		? [
				Math.floor(tableWidth * 0.1),
				Math.floor(tableWidth * 0.4),
				Math.floor(tableWidth * 0.15),
				Math.floor(tableWidth * 0.1),
				Math.floor(tableWidth * 0.2),
				Math.floor(tableWidth * 0.1)
			] // ID, Title, Status, Priority, Dependencies, Complexity
		: [
				Math.floor(tableWidth * 0.08),
				Math.floor(tableWidth * 0.4),
				Math.floor(tableWidth * 0.18),
				Math.floor(tableWidth * 0.12),
				Math.floor(tableWidth * 0.2)
			]; // ID, Title, Status, Priority, Dependencies

	const headers = [
		chalk.blue.bold('ID'),
		chalk.blue.bold('Title'),
		chalk.blue.bold('Status'),
		chalk.blue.bold('Priority')
	];
	const colWidths = baseColWidths.slice(0, 4);

	if (showDependencies) {
		headers.push(chalk.blue.bold('Dependencies'));
		colWidths.push(baseColWidths[4]);
	}

	if (showComplexity) {
		headers.push(chalk.blue.bold('Complexity'));
		colWidths.push(baseColWidths[5] || 12);
	}

	const table = new Table({
		head: headers,
		style: { head: [], border: [] },
		colWidths,
		wordWrap: true
	});

	tasks.forEach((task) => {
		const row: string[] = [
			chalk.cyan(task.id.toString()),
			truncate(task.title, colWidths[1] - 3),
			getStatusWithColor(task.status, true), // Use table version
			getPriorityWithColor(task.priority)
		];

		if (showDependencies) {
			// For table display, show simple format without status icons
			if (!task.dependencies || task.dependencies.length === 0) {
				row.push(chalk.gray('None'));
			} else {
				row.push(
					chalk.cyan(task.dependencies.map((d) => String(d)).join(', '))
				);
			}
		}

		if (showComplexity) {
			// Show complexity score from report if available
			if (typeof task.complexity === 'number') {
				row.push(getComplexityWithColor(task.complexity));
			} else {
				row.push(chalk.gray('N/A'));
			}
		}

		table.push(row);

		// Add subtasks if requested
		if (showSubtasks && task.subtasks && task.subtasks.length > 0) {
			task.subtasks.forEach((subtask) => {
				const subRow: string[] = [
					chalk.gray(` └─ ${subtask.id}`),
					chalk.gray(truncate(subtask.title, colWidths[1] - 6)),
					chalk.gray(getStatusWithColor(subtask.status, true)),
					chalk.gray(subtask.priority || DEFAULT_PRIORITY)
				];

				if (showDependencies) {
					subRow.push(
						chalk.gray(
							subtask.dependencies && subtask.dependencies.length > 0
								? subtask.dependencies.map((dep) => String(dep)).join(', ')
								: 'None'
						)
					);
				}

				if (showComplexity) {
					const complexityDisplay =
						typeof subtask.complexity === 'number'
							? getComplexityWithColor(subtask.complexity)
							: '--';
					subRow.push(chalk.gray(complexityDisplay));
				}

				table.push(subRow);
			});
		}
	});

	return table.toString();
}

```

--------------------------------------------------------------------------------
/scripts/modules/task-manager/remove-subtask.js:
--------------------------------------------------------------------------------

```javascript
import { log, readJSON, writeJSON } from '../utils.js';

/**
 * Remove a subtask from its parent task
 * @param {string} tasksPath - Path to the tasks.json file
 * @param {string} subtaskId - ID of the subtask to remove in format "parentId.subtaskId"
 * @param {boolean} convertToTask - Whether to convert the subtask to a standalone task
 * @param {boolean} generateFiles - Whether to regenerate task files after removing the subtask
 * @param {Object} context - Context object containing projectRoot and tag information
 * @param {string} [context.projectRoot] - Project root path
 * @param {string} [context.tag] - Tag for the task
 * @returns {Object|null} The removed subtask if convertToTask is true, otherwise null
 */
async function removeSubtask(
	tasksPath,
	subtaskId,
	convertToTask = false,
	generateFiles = false,
	context = {}
) {
	const { projectRoot, tag } = context;
	try {
		log('info', `Removing subtask ${subtaskId}...`);

		// Read the existing tasks with proper context
		const data = readJSON(tasksPath, projectRoot, tag);
		if (!data || !data.tasks) {
			throw new Error(`Invalid or missing tasks file at ${tasksPath}`);
		}

		// Parse the subtask ID (format: "parentId.subtaskId")
		if (!subtaskId.includes('.')) {
			throw new Error(
				`Invalid subtask ID format: ${subtaskId}. Expected format: "parentId.subtaskId"`
			);
		}

		const [parentIdStr, subtaskIdStr] = subtaskId.split('.');
		const parentId = parseInt(parentIdStr, 10);
		const subtaskIdNum = parseInt(subtaskIdStr, 10);

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

		// Check if parent has subtasks
		if (!parentTask.subtasks || parentTask.subtasks.length === 0) {
			throw new Error(`Parent task ${parentId} has no subtasks`);
		}

		// Find the subtask to remove
		const subtaskIndex = parentTask.subtasks.findIndex(
			(st) => st.id === subtaskIdNum
		);
		if (subtaskIndex === -1) {
			throw new Error(`Subtask ${subtaskId} not found`);
		}

		// Get a copy of the subtask before removing it
		const removedSubtask = { ...parentTask.subtasks[subtaskIndex] };

		// Remove the subtask from the parent
		parentTask.subtasks.splice(subtaskIndex, 1);

		// If parent has no more subtasks, remove the subtasks array
		if (parentTask.subtasks.length === 0) {
			parentTask.subtasks = undefined;
		}

		let convertedTask = null;

		// Convert the subtask to a standalone task if requested
		if (convertToTask) {
			log('info', `Converting subtask ${subtaskId} to a standalone task...`);

			// Find the highest task ID to determine the next ID
			const highestId = Math.max(...data.tasks.map((t) => t.id));
			const newTaskId = highestId + 1;

			// Create the new task from the subtask
			convertedTask = {
				id: newTaskId,
				title: removedSubtask.title,
				description: removedSubtask.description || '',
				details: removedSubtask.details || '',
				status: removedSubtask.status || 'pending',
				dependencies: removedSubtask.dependencies || [],
				priority: parentTask.priority || 'medium' // Inherit priority from parent
			};

			// Add the parent task as a dependency if not already present
			if (!convertedTask.dependencies.includes(parentId)) {
				convertedTask.dependencies.push(parentId);
			}

			// Add the converted task to the tasks array
			data.tasks.push(convertedTask);

			log('info', `Created new task ${newTaskId} from subtask ${subtaskId}`);
		} else {
			log('info', `Subtask ${subtaskId} deleted`);
		}

		// Write the updated tasks back to the file with proper context
		writeJSON(tasksPath, data, projectRoot, tag);

		// Note: Task file generation is no longer supported and has been removed

		return convertedTask;
	} catch (error) {
		log('error', `Error removing subtask: ${error.message}`);
		throw error;
	}
}

export default removeSubtask;

```

--------------------------------------------------------------------------------
/tests/integration/profiles/roo-files-inclusion.test.js:
--------------------------------------------------------------------------------

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

describe('Roo Files Inclusion in Package', () => {
	// This test verifies that the required Roo files are included in the final package

	test('package.json includes dist/** in the "files" array for bundled files', () => {
		// Read the package.json file
		const packageJsonPath = path.join(process.cwd(), 'package.json');
		const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));

		// Check if dist/** is included in the files array (which contains bundled output including Roo files)
		expect(packageJson.files).toContain('dist/**');
	});

	test('roo.js profile contains logic for Roo directory creation and file copying', () => {
		// Read the roo.js profile file
		const rooJsPath = path.join(process.cwd(), 'src', 'profiles', 'roo.js');
		const rooJsContent = fs.readFileSync(rooJsPath, 'utf8');

		// Check for the main handler function
		expect(
			rooJsContent.includes('onAddRulesProfile(targetDir, assetsDir)')
		).toBe(true);

		// Check for general recursive copy of assets/roocode
		expect(
			rooJsContent.includes('copyRecursiveSync(sourceDir, targetDir)')
		).toBe(true);

		// Check for updated path handling
		expect(rooJsContent.includes("path.join(assetsDir, 'roocode')")).toBe(true);

		// Check for .roomodes file copying logic (source and destination paths)
		expect(rooJsContent.includes("path.join(sourceDir, '.roomodes')")).toBe(
			true
		);
		expect(rooJsContent.includes("path.join(targetDir, '.roomodes')")).toBe(
			true
		);

		// Check for mode-specific rule file copying logic
		expect(rooJsContent.includes('for (const mode of ROO_MODES)')).toBe(true);
		expect(
			rooJsContent.includes(
				'path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`)'
			)
		).toBe(true);
		expect(
			rooJsContent.includes(
				"path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`)"
			)
		).toBe(true);

		// Check for import of ROO_MODES from profiles.js instead of local definition
		expect(
			rooJsContent.includes(
				"import { ROO_MODES } from '../constants/profiles.js'"
			)
		).toBe(true);

		// Verify ROO_MODES is used in the for loop
		expect(rooJsContent.includes('for (const mode of ROO_MODES)')).toBe(true);

		// Verify mode variable is used in the template strings (this confirms modes are being processed)
		expect(rooJsContent.includes('rules-${mode}')).toBe(true);
		expect(rooJsContent.includes('${mode}-rules')).toBe(true);

		// Verify that the ROO_MODES constant is properly imported and used
		// We should be able to find the template literals that use the mode variable
		expect(rooJsContent.includes('`rules-${mode}`')).toBe(true);
		expect(rooJsContent.includes('`${mode}-rules`')).toBe(true);
		expect(rooJsContent.includes('Copied ${mode}-rules to ${dest}')).toBe(true);

		// Also verify that the expected mode names are defined in the imported constant
		// by checking that the import is from the correct file that contains all 6 modes
		const profilesConstantsPath = path.join(
			process.cwd(),
			'src',
			'constants',
			'profiles.js'
		);
		const profilesContent = fs.readFileSync(profilesConstantsPath, 'utf8');

		// Check that ROO_MODES is exported and contains all expected modes
		expect(profilesContent.includes('export const ROO_MODES')).toBe(true);
		const expectedModes = [
			'architect',
			'ask',
			'orchestrator',
			'code',
			'debug',
			'test'
		];
		expectedModes.forEach((mode) => {
			expect(profilesContent.includes(`'${mode}'`)).toBe(true);
		});
	});

	test('source Roo files exist in assets directory', () => {
		// Verify that the source files for Roo integration exist
		expect(
			fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roo'))
		).toBe(true);
		expect(
			fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roomodes'))
		).toBe(true);
	});
});

```

--------------------------------------------------------------------------------
/packages/tm-core/src/modules/auth/services/context-store.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Context storage for app-specific user preferences
 *
 * This store manages user preferences and context separate from auth tokens.
 * - selectedContext (org/brief selection)
 * - userId and email (for convenience)
 * - Any other app-specific data
 *
 * Stored at: ~/.taskmaster/context.json
 */

import fs from 'fs';
import path from 'path';
import { getLogger } from '../../../common/logger/index.js';
import { AuthenticationError, UserContext } from '../types.js';

const DEFAULT_CONTEXT_FILE = path.join(
	process.env.HOME || process.env.USERPROFILE || '~',
	'.taskmaster',
	'context.json'
);

export interface StoredContext {
	userId?: string;
	email?: string;
	selectedContext?: UserContext;
	lastUpdated: string;
}

export class ContextStore {
	private static instance: ContextStore | null = null;
	private logger = getLogger('ContextStore');
	private contextPath: string;

	private constructor(contextPath: string = DEFAULT_CONTEXT_FILE) {
		this.contextPath = contextPath;
	}

	/**
	 * Get singleton instance
	 */
	static getInstance(contextPath?: string): ContextStore {
		if (!ContextStore.instance) {
			ContextStore.instance = new ContextStore(contextPath);
		}
		return ContextStore.instance;
	}

	/**
	 * Reset singleton (for testing)
	 */
	static resetInstance(): void {
		ContextStore.instance = null;
	}

	/**
	 * Get stored context
	 */
	getContext(): StoredContext | null {
		try {
			if (!fs.existsSync(this.contextPath)) {
				return null;
			}

			const data = JSON.parse(fs.readFileSync(this.contextPath, 'utf8'));
			this.logger.debug('Loaded context from disk');
			return data;
		} catch (error) {
			this.logger.error('Failed to read context:', error);
			return null;
		}
	}

	/**
	 * Save context
	 */
	saveContext(context: Partial<StoredContext>): void {
		try {
			// Load existing context
			const existing = this.getContext() || {};

			// Merge with new data
			const updated: StoredContext = {
				...existing,
				...context,
				lastUpdated: new Date().toISOString()
			};

			// Ensure directory exists
			const dir = path.dirname(this.contextPath);
			if (!fs.existsSync(dir)) {
				fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
			}

			// Write atomically
			const tempFile = `${this.contextPath}.tmp`;
			fs.writeFileSync(tempFile, JSON.stringify(updated, null, 2), {
				mode: 0o600
			});
			fs.renameSync(tempFile, this.contextPath);

			this.logger.debug('Saved context to disk');
		} catch (error) {
			throw new AuthenticationError(
				`Failed to save context: ${(error as Error).message}`,
				'SAVE_FAILED',
				error
			);
		}
	}

	/**
	 * Update user context (org/brief selection)
	 */
	updateUserContext(userContext: Partial<UserContext>): void {
		const existing = this.getContext();
		const currentUserContext = existing?.selectedContext || {};

		const updated: UserContext = {
			...currentUserContext,
			...userContext,
			updatedAt: new Date().toISOString()
		};

		this.saveContext({
			...existing,
			selectedContext: updated
		});
	}

	/**
	 * Get user context (org/brief selection)
	 */
	getUserContext(): UserContext | null {
		const context = this.getContext();
		return context?.selectedContext || null;
	}

	/**
	 * Clear user context
	 */
	clearUserContext(): void {
		const existing = this.getContext();
		if (existing) {
			const { selectedContext, ...rest } = existing;
			this.saveContext(rest);
		}
	}

	/**
	 * Clear all context
	 */
	clearContext(): void {
		try {
			if (fs.existsSync(this.contextPath)) {
				fs.unlinkSync(this.contextPath);
				this.logger.debug('Cleared context from disk');
			}
		} catch (error) {
			throw new AuthenticationError(
				`Failed to clear context: ${(error as Error).message}`,
				'CLEAR_FAILED',
				error
			);
		}
	}

	/**
	 * Check if context exists
	 */
	hasContext(): boolean {
		return this.getContext() !== null;
	}

	/**
	 * Get context file path
	 */
	getContextPath(): string {
		return this.contextPath;
	}
}

```

--------------------------------------------------------------------------------
/mcp-server/src/index.js:
--------------------------------------------------------------------------------

```javascript
import { FastMCP } from 'fastmcp';
import path from 'path';
import dotenv from 'dotenv';
import { fileURLToPath } from 'url';
import fs from 'fs';
import logger from './logger.js';
import {
	registerTaskMasterTools,
	getToolsConfiguration
} from './tools/index.js';
import ProviderRegistry from '../../src/provider-registry/index.js';
import { MCPProvider } from './providers/mcp-provider.js';
import packageJson from '../../package.json' with { type: 'json' };

dotenv.config();

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

/**
 * Main MCP server class that integrates with Task Master
 */
class TaskMasterMCPServer {
	constructor() {
		this.options = {
			name: 'Task Master MCP Server',
			version: packageJson.version
		};

		this.server = new FastMCP(this.options);
		this.initialized = false;

		this.init = this.init.bind(this);
		this.start = this.start.bind(this);
		this.stop = this.stop.bind(this);

		this.logger = logger;
	}

	/**
	 * Initialize the MCP server with necessary tools and routes
	 */
	async init() {
		if (this.initialized) return;

		const normalizedToolMode = getToolsConfiguration();

		this.logger.info('Task Master MCP Server starting...');
		this.logger.info(`Tool mode configuration: ${normalizedToolMode}`);

		const registrationResult = registerTaskMasterTools(
			this.server,
			normalizedToolMode
		);

		this.logger.info(
			`Normalized tool mode: ${registrationResult.normalizedMode}`
		);
		this.logger.info(
			`Registered ${registrationResult.registeredTools.length} tools successfully`
		);

		if (registrationResult.registeredTools.length > 0) {
			this.logger.debug(
				`Registered tools: ${registrationResult.registeredTools.join(', ')}`
			);
		}

		if (registrationResult.failedTools.length > 0) {
			this.logger.warn(
				`Failed to register ${registrationResult.failedTools.length} tools: ${registrationResult.failedTools.join(', ')}`
			);
		}

		this.initialized = true;

		return this;
	}

	/**
	 * Start the MCP server
	 */
	async start() {
		if (!this.initialized) {
			await this.init();
		}

		this.server.on('connect', (event) => {
			event.session.server.sendLoggingMessage({
				data: {
					context: event.session.context,
					message: `MCP Server connected: ${event.session.name}`
				},
				level: 'info'
			});
			this.registerRemoteProvider(event.session);
		});

		// Start the FastMCP server with increased timeout
		await this.server.start({
			transportType: 'stdio',
			timeout: 120000 // 2 minutes timeout (in milliseconds)
		});

		return this;
	}

	/**
	 * Register both MCP providers with the provider registry
	 */
	registerRemoteProvider(session) {
		// Check if the server has at least one session
		if (session) {
			// Make sure session has required capabilities
			if (!session.clientCapabilities || !session.clientCapabilities.sampling) {
				session.server.sendLoggingMessage({
					data: {
						context: session.context,
						message: `MCP session missing required sampling capabilities, providers not registered`
					},
					level: 'info'
				});
				return;
			}

			// Register MCP provider with the Provider Registry

			// Register the unified MCP provider
			const mcpProvider = new MCPProvider();
			mcpProvider.setSession(session);

			// Register provider with the registry
			const providerRegistry = ProviderRegistry.getInstance();
			providerRegistry.registerProvider('mcp', mcpProvider);

			session.server.sendLoggingMessage({
				data: {
					context: session.context,
					message: `MCP Server connected`
				},
				level: 'info'
			});
		} else {
			session.server.sendLoggingMessage({
				data: {
					context: session.context,
					message: `No MCP sessions available, providers not registered`
				},
				level: 'warn'
			});
		}
	}

	/**
	 * Stop the MCP server
	 */
	async stop() {
		if (this.server) {
			await this.server.stop();
		}
	}
}

export default TaskMasterMCPServer;

```

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

```typescript
/**
 * Run ID generation and validation utilities for the global storage system.
 * Uses ISO 8601 timestamps with millisecond precision for unique, chronologically-ordered run IDs.
 *
 * @module run-id-generator
 */

// Collision detection state
let lastTimestamp = 0;
let counter = 0;

/**
 * Generates a unique run ID using ISO 8601 timestamp format with millisecond precision.
 * The ID is guaranteed to be chronologically sortable and URL-safe.
 * Includes collision detection to ensure uniqueness even when called in rapid succession.
 *
 * @param {Date} [date=new Date()] - Optional date to use for the run ID. Defaults to current time.
 * @returns {string} ISO 8601 formatted timestamp (e.g., '2024-01-15T10:30:45.123Z')
 *
 * @example
 * generateRunId() // returns '2024-01-15T10:30:45.123Z'
 * generateRunId(new Date('2024-01-15T10:00:00.000Z')) // returns '2024-01-15T10:00:00.000Z'
 */
export function generateRunId(date: Date = new Date()): string {
	const timestamp = date.getTime();

	// Collision detection: if same millisecond, wait for next millisecond
	if (timestamp === lastTimestamp) {
		counter++;
		// Wait for next millisecond to ensure uniqueness
		let newTimestamp = timestamp;
		while (newTimestamp === timestamp) {
			newTimestamp = Date.now();
		}
		date = new Date(newTimestamp);
		lastTimestamp = newTimestamp;
		counter = 0;
	} else {
		lastTimestamp = timestamp;
		counter = 0;
	}

	return date.toISOString();
}

/**
 * Validates whether a string is a valid run ID.
 * A valid run ID must be:
 * - In ISO 8601 format with milliseconds
 * - In UTC timezone (ends with 'Z')
 * - A valid date when parsed
 *
 * @param {any} runId - The value to validate
 * @returns {boolean} True if the value is a valid run ID
 *
 * @example
 * isValidRunId('2024-01-15T10:30:45.123Z') // returns true
 * isValidRunId('invalid') // returns false
 * isValidRunId('2024-01-15T10:30:45Z') // returns false (missing milliseconds)
 */
export function isValidRunId(runId: any): boolean {
	if (!runId || typeof runId !== 'string') {
		return false;
	}

	// Check format: YYYY-MM-DDTHH:mm:ss.sssZ
	const isoFormatRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
	if (!isoFormatRegex.test(runId)) {
		return false;
	}

	// Validate it's a real date
	const date = new Date(runId);
	if (isNaN(date.getTime())) {
		return false;
	}

	// Ensure the parsed date matches the input (catches invalid dates like 2024-13-01)
	return date.toISOString() === runId;
}

/**
 * Parses a run ID string into a Date object.
 *
 * @param {any} runId - The run ID to parse
 * @returns {Date | null} Date object if valid, null if invalid
 *
 * @example
 * parseRunId('2024-01-15T10:30:45.123Z') // returns Date object
 * parseRunId('invalid') // returns null
 */
export function parseRunId(runId: any): Date | null {
	if (!isValidRunId(runId)) {
		return null;
	}

	return new Date(runId);
}

/**
 * Compares two run IDs chronologically.
 * Returns a negative number if id1 is earlier, positive if id1 is later, or 0 if equal.
 * Can be used as a comparator function for Array.sort().
 *
 * @param {string} id1 - First run ID to compare
 * @param {string} id2 - Second run ID to compare
 * @returns {number} Negative if id1 < id2, positive if id1 > id2, zero if equal
 * @throws {Error} If either run ID is invalid
 *
 * @example
 * compareRunIds('2024-01-15T10:00:00.000Z', '2024-01-15T11:00:00.000Z') // returns negative number
 * ['2024-01-15T14:00:00.000Z', '2024-01-15T10:00:00.000Z'].sort(compareRunIds)
 * // returns ['2024-01-15T10:00:00.000Z', '2024-01-15T14:00:00.000Z']
 */
export function compareRunIds(id1: string, id2: string): number {
	if (!isValidRunId(id1)) {
		throw new Error(`Invalid run ID: ${id1}`);
	}

	if (!isValidRunId(id2)) {
		throw new Error(`Invalid run ID: ${id2}`);
	}

	// String comparison works for ISO 8601 timestamps
	// because they are lexicographically sortable
	if (id1 < id2) return -1;
	if (id1 > id2) return 1;
	return 0;
}

```

--------------------------------------------------------------------------------
/tests/unit/scripts/modules/task-manager/remove-task.test.js:
--------------------------------------------------------------------------------

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

// --- Mock dependencies BEFORE module import ---
jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
	readJSON: jest.fn(),
	writeJSON: jest.fn(),
	log: jest.fn(),
	CONFIG: {
		model: 'mock-model',
		maxTokens: 4000,
		temperature: 0.7,
		debug: false
	},
	findTaskById: jest.fn(),
	truncate: jest.fn((t) => t),
	isSilentMode: jest.fn(() => false)
}));

jest.unstable_mockModule(
	'../../../../../scripts/modules/task-manager/generate-task-files.js',
	() => ({
		default: jest.fn().mockResolvedValue()
	})
);

// fs is used for file deletion side-effects – stub the methods we touch
jest.unstable_mockModule('fs', () => ({
	existsSync: jest.fn(() => true),
	unlinkSync: jest.fn()
}));

// path is fine to keep as real since only join/dirname used – no side effects

// Import mocked modules
const { readJSON, writeJSON, log } = await import(
	'../../../../../scripts/modules/utils.js'
);
const generateTaskFiles = (
	await import(
		'../../../../../scripts/modules/task-manager/generate-task-files.js'
	)
).default;
const fs = await import('fs');

// Import module under test (AFTER mocks in place)
const { default: removeTask } = await import(
	'../../../../../scripts/modules/task-manager/remove-task.js'
);

// ---- Test data helpers ----
const buildSampleTaggedTasks = () => ({
	master: {
		tasks: [
			{ id: 1, title: 'Task 1', status: 'pending', dependencies: [] },
			{ id: 2, title: 'Task 2', status: 'pending', dependencies: [1] },
			{
				id: 3,
				title: 'Parent',
				status: 'pending',
				dependencies: [],
				subtasks: [
					{ id: 1, title: 'Sub 3.1', status: 'pending', dependencies: [] }
				]
			}
		]
	},
	other: {
		tasks: [{ id: 99, title: 'Shadow', status: 'pending', dependencies: [1] }]
	}
});

// Utility to deep clone sample each test
const getFreshData = () => JSON.parse(JSON.stringify(buildSampleTaggedTasks()));

// ----- Tests -----

describe('removeTask', () => {
	beforeEach(() => {
		jest.clearAllMocks();
		// readJSON returns deep copy so each test isolated
		readJSON.mockImplementation(() => {
			return {
				...getFreshData().master,
				tag: 'master',
				_rawTaggedData: getFreshData()
			};
		});
		writeJSON.mockResolvedValue();
		log.mockImplementation(() => {});
		fs.unlinkSync.mockImplementation(() => {});
	});

	test('removes a main task and cleans dependencies across tags', async () => {
		const result = await removeTask('tasks/tasks.json', '1', { tag: 'master' });

		// Expect success true
		expect(result.success).toBe(true);
		// writeJSON called with data where task 1 is gone in master & dependencies removed in other tags
		const written = writeJSON.mock.calls[0][1];
		expect(written.master.tasks.find((t) => t.id === 1)).toBeUndefined();
		// deps removed from child tasks
		const task2 = written.master.tasks.find((t) => t.id === 2);
		expect(task2.dependencies).not.toContain(1);
		const shadow = written.other.tasks.find((t) => t.id === 99);
		expect(shadow.dependencies).not.toContain(1);
		// Task file deletion attempted
		expect(fs.unlinkSync).toHaveBeenCalled();
	});

	test('removes a subtask only and leaves parent intact', async () => {
		const result = await removeTask('tasks/tasks.json', '3.1', {
			tag: 'master'
		});

		expect(result.success).toBe(true);
		const written = writeJSON.mock.calls[0][1];
		const parent = written.master.tasks.find((t) => t.id === 3);
		expect(parent.subtasks || []).toHaveLength(0);
		// Ensure parent still exists
		expect(parent).toBeDefined();
		// No task files should be deleted for subtasks
		expect(fs.unlinkSync).not.toHaveBeenCalled();
	});

	test('handles non-existent task gracefully', async () => {
		const result = await removeTask('tasks/tasks.json', '42', {
			tag: 'master'
		});
		expect(result.success).toBe(false);
		expect(result.error).toContain('not found');
		// writeJSON not called because nothing changed
		expect(writeJSON).not.toHaveBeenCalled();
	});
});

```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/expand-all-tasks.js:
--------------------------------------------------------------------------------

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

import { expandAllTasks } from '../../../../scripts/modules/task-manager.js';
import {
	enableSilentMode,
	disableSilentMode
} from '../../../../scripts/modules/utils.js';
import { createLogWrapper } from '../../tools/utils.js';
import { resolveComplexityReportOutputPath } from '../../../../src/utils/path-utils.js';

/**
 * Expand all pending tasks with subtasks (Direct Function Wrapper)
 * @param {Object} args - Function arguments
 * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 * @param {number|string} [args.num] - Number of subtasks to generate
 * @param {boolean} [args.research] - Enable research-backed subtask generation
 * @param {string} [args.prompt] - Additional context to guide subtask generation
 * @param {boolean} [args.force] - Force regeneration of subtasks for tasks that already have them
 * @param {string} [args.projectRoot] - Project root path.
 * @param {string} [args.tag] - Tag for the task (optional)
 * @param {Object} log - Logger object from FastMCP
 * @param {Object} context - Context object containing session
 * @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
 */
export async function expandAllTasksDirect(args, log, context = {}) {
	const { session } = context; // Extract session
	// Destructure expected args, including projectRoot and complexityReportPath
	const {
		tasksJsonPath,
		num,
		research,
		prompt,
		force,
		projectRoot,
		tag,
		complexityReportPath: providedComplexityReportPath
	} = args;

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

	// Use provided complexity report path or compute it
	const complexityReportPath =
		providedComplexityReportPath ||
		resolveComplexityReportOutputPath(null, { projectRoot, tag }, log);

	log.info(
		`Expand all tasks will use complexity report at: ${complexityReportPath}`
	);

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

	enableSilentMode(); // Enable silent mode for the core function call
	try {
		log.info(
			`Calling core expandAllTasks with args: ${JSON.stringify({ num, research, prompt, force, projectRoot, tag })}`
		);

		// Parse parameters (ensure correct types)
		const numSubtasks = num ? parseInt(num, 10) : undefined;
		const useResearch = research === true;
		const additionalContext = prompt || '';
		const forceFlag = force === true;

		// Call the core function, passing options and the context object { session, mcpLog, projectRoot, tag, complexityReportPath }
		const result = await expandAllTasks(
			tasksJsonPath,
			numSubtasks,
			useResearch,
			additionalContext,
			forceFlag,
			{ session, mcpLog, projectRoot, tag, complexityReportPath },
			'json'
		);

		// Core function now returns a summary object including the *aggregated* telemetryData
		return {
			success: true,
			data: {
				message: `Expand all operation completed. Expanded: ${result.expandedCount}, Failed: ${result.failedCount}, Skipped: ${result.skippedCount}`,
				details: {
					expandedCount: result.expandedCount,
					failedCount: result.failedCount,
					skippedCount: result.skippedCount,
					tasksToExpand: result.tasksToExpand
				},
				telemetryData: result.telemetryData // Pass the aggregated object
			}
		};
	} catch (error) {
		// Log the error using the MCP logger
		log.error(`Error during core expandAllTasks execution: ${error.message}`);
		// Optionally log stack trace if available and debug enabled
		// if (error.stack && log.debug) { log.debug(error.stack); }

		return {
			success: false,
			error: {
				code: 'CORE_FUNCTION_ERROR', // Or a more specific code if possible
				message: error.message
			}
		};
	} finally {
		disableSilentMode(); // IMPORTANT: Ensure silent mode is always disabled
	}
}

```

--------------------------------------------------------------------------------
/tests/integration/claude-code-optional.test.js:
--------------------------------------------------------------------------------

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

// Mock AI SDK functions at the top level
const generateText = jest.fn();
const streamText = jest.fn();

jest.unstable_mockModule('ai', () => ({
	generateObject: jest.fn(),
	generateText,
	streamText,
	streamObject: jest.fn(),
	zodSchema: jest.fn(),
	JSONParseError: class JSONParseError extends Error {},
	NoObjectGeneratedError: class NoObjectGeneratedError extends Error {}
}));

// Mock successful provider creation for all tests
const mockProvider = jest.fn((modelId) => ({
	id: modelId,
	doGenerate: jest.fn(),
	doStream: jest.fn()
}));
mockProvider.languageModel = jest.fn((id, settings) => ({ id, settings }));
mockProvider.chat = mockProvider.languageModel;

jest.unstable_mockModule('ai-sdk-provider-claude-code', () => ({
	createClaudeCode: jest.fn(() => mockProvider)
}));

// Import the provider after mocking
const { ClaudeCodeProvider } = await import(
	'../../src/ai-providers/claude-code.js'
);

describe('Claude Code Integration (Optional)', () => {
	beforeEach(() => {
		jest.clearAllMocks();
	});

	it('should create a working provider instance', () => {
		const provider = new ClaudeCodeProvider();
		expect(provider.name).toBe('Claude Code');
		expect(provider.getSupportedModels()).toEqual(['opus', 'sonnet', 'haiku']);
	});

	it('should support model validation', () => {
		const provider = new ClaudeCodeProvider();
		expect(provider.isModelSupported('sonnet')).toBe(true);
		expect(provider.isModelSupported('opus')).toBe(true);
		expect(provider.isModelSupported('haiku')).toBe(true);
		expect(provider.isModelSupported('unknown')).toBe(false);
	});

	it('should create a client successfully', () => {
		const provider = new ClaudeCodeProvider();
		const client = provider.getClient();

		expect(client).toBeDefined();
		expect(typeof client).toBe('function');
		expect(client.languageModel).toBeDefined();
		expect(client.chat).toBeDefined();
		expect(client.chat).toBe(client.languageModel);
	});

	it('should pass command-specific settings to client', async () => {
		const provider = new ClaudeCodeProvider();
		const client = provider.getClient({ commandName: 'test-command' });

		expect(client).toBeDefined();
		expect(typeof client).toBe('function');
		const { createClaudeCode } = await import('ai-sdk-provider-claude-code');
		expect(createClaudeCode).toHaveBeenCalledTimes(1);
	});

	it('should handle AI SDK generateText integration', async () => {
		const provider = new ClaudeCodeProvider();
		const client = provider.getClient();

		// Mock successful generation
		generateText.mockResolvedValueOnce({
			text: 'Hello from Claude Code!',
			usage: { totalTokens: 10 }
		});

		const result = await generateText({
			model: client('sonnet'),
			messages: [{ role: 'user', content: 'Hello' }]
		});

		expect(result.text).toBe('Hello from Claude Code!');
		expect(generateText).toHaveBeenCalledWith({
			model: expect.any(Object),
			messages: [{ role: 'user', content: 'Hello' }]
		});
	});

	it('should handle AI SDK streamText integration', async () => {
		const provider = new ClaudeCodeProvider();
		const client = provider.getClient();

		// Mock successful streaming
		const mockStream = {
			textStream: (async function* () {
				yield 'Streamed response';
			})()
		};
		streamText.mockResolvedValueOnce(mockStream);

		const streamResult = await streamText({
			model: client('sonnet'),
			messages: [{ role: 'user', content: 'Stream test' }]
		});

		expect(streamResult.textStream).toBeDefined();
		expect(streamText).toHaveBeenCalledWith({
			model: expect.any(Object),
			messages: [{ role: 'user', content: 'Stream test' }]
		});
	});

	it('should not require authentication validation', () => {
		const provider = new ClaudeCodeProvider();
		expect(provider.isRequiredApiKey()).toBe(false);
		expect(() => provider.validateAuth()).not.toThrow();
		expect(() => provider.validateAuth({})).not.toThrow();
		expect(() => provider.validateAuth({ commandName: 'test' })).not.toThrow();
	});
});

```

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

```typescript
/**
 * @fileoverview Unit tests for ConfigLoader service
 */

import fs from 'node:fs/promises';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { DEFAULT_CONFIG_VALUES } from '../../../common/interfaces/configuration.interface.js';
import { ConfigLoader } from './config-loader.service.js';

vi.mock('node:fs', () => ({
	promises: {
		readFile: vi.fn(),
		access: vi.fn()
	}
}));

describe('ConfigLoader', () => {
	let configLoader: ConfigLoader;
	const testProjectRoot = '/test/project';

	beforeEach(() => {
		configLoader = new ConfigLoader(testProjectRoot);
		vi.clearAllMocks();
	});

	afterEach(() => {
		vi.restoreAllMocks();
	});

	describe('getDefaultConfig', () => {
		it('should return default configuration values', () => {
			const config = configLoader.getDefaultConfig();

			expect(config.models).toEqual({
				main: DEFAULT_CONFIG_VALUES.MODELS.MAIN,
				fallback: DEFAULT_CONFIG_VALUES.MODELS.FALLBACK
			});

			expect(config.storage).toEqual({
				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
			});

			expect(config.version).toBe(DEFAULT_CONFIG_VALUES.VERSION);
		});
	});

	describe('loadLocalConfig', () => {
		it('should load and parse local configuration file', async () => {
			const mockConfig = {
				models: { main: 'test-model' },
				storage: { type: 'api' as const }
			};

			vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockConfig));

			const result = await configLoader.loadLocalConfig();

			expect(fs.readFile).toHaveBeenCalledWith(
				'/test/project/.taskmaster/config.json',
				'utf-8'
			);
			expect(result).toEqual(mockConfig);
		});

		it('should return null when config file does not exist', async () => {
			const error = new Error('File not found') as any;
			error.code = 'ENOENT';
			vi.mocked(fs.readFile).mockRejectedValue(error);

			const result = await configLoader.loadLocalConfig();

			expect(result).toBeNull();
		});

		it('should throw TaskMasterError for other file errors', async () => {
			const error = new Error('Permission denied');
			vi.mocked(fs.readFile).mockRejectedValue(error);

			await expect(configLoader.loadLocalConfig()).rejects.toThrow(
				'Failed to load local configuration'
			);
		});

		it('should throw error for invalid JSON', async () => {
			vi.mocked(fs.readFile).mockResolvedValue('invalid json');

			await expect(configLoader.loadLocalConfig()).rejects.toThrow();
		});
	});

	describe('loadGlobalConfig', () => {
		it('should return null (not implemented yet)', async () => {
			const result = await configLoader.loadGlobalConfig();
			expect(result).toBeNull();
		});
	});

	describe('hasLocalConfig', () => {
		it('should return true when local config exists', async () => {
			vi.mocked(fs.access).mockResolvedValue(undefined);

			const result = await configLoader.hasLocalConfig();

			expect(fs.access).toHaveBeenCalledWith(
				'/test/project/.taskmaster/config.json'
			);
			expect(result).toBe(true);
		});

		it('should return false when local config does not exist', async () => {
			vi.mocked(fs.access).mockRejectedValue(new Error('Not found'));

			const result = await configLoader.hasLocalConfig();

			expect(result).toBe(false);
		});
	});

	describe('hasGlobalConfig', () => {
		it('should check global config path', async () => {
			vi.mocked(fs.access).mockResolvedValue(undefined);

			const result = await configLoader.hasGlobalConfig();

			expect(fs.access).toHaveBeenCalledWith(
				expect.stringContaining('.taskmaster/config.json')
			);
			expect(result).toBe(true);
		});

		it('should return false when global config does not exist', async () => {
			vi.mocked(fs.access).mockRejectedValue(new Error('Not found'));

			const result = await configLoader.hasGlobalConfig();

			expect(result).toBe(false);
		});
	});
});

```

--------------------------------------------------------------------------------
/apps/extension/src/components/ui/shadcn-io/kanban/index.tsx:
--------------------------------------------------------------------------------

```typescript
'use client';

import { Card } from '@/components/ui/card';
import { cn } from '@/lib/utils';
import {
	DndContext,
	DragOverlay,
	MouseSensor,
	TouchSensor,
	rectIntersection,
	useDraggable,
	useDroppable,
	useSensor,
	useSensors
} from '@dnd-kit/core';
import type { DragEndEvent } from '@dnd-kit/core';
import type React from 'react';
import type { ReactNode } from 'react';

export type { DragEndEvent } from '@dnd-kit/core';

export type Status = {
	id: string;
	name: string;
	color: string;
};

export type Feature = {
	id: string;
	name: string;
	startAt: Date;
	endAt: Date;
	status: Status;
};

export type KanbanBoardProps = {
	id: Status['id'];
	children: ReactNode;
	className?: string;
};

export const KanbanBoard = ({ id, children, className }: KanbanBoardProps) => {
	const { isOver, setNodeRef } = useDroppable({ id });

	return (
		<div
			className={cn(
				'flex h-full min-h-40 flex-col gap-2 rounded-md border bg-secondary p-2 text-xs shadow-sm outline transition-all',
				isOver ? 'outline-primary' : 'outline-transparent',
				className
			)}
			ref={setNodeRef}
		>
			{children}
		</div>
	);
};

export type KanbanCardProps = Pick<Feature, 'id' | 'name'> & {
	index: number;
	parent: string;
	children?: ReactNode;
	className?: string;
	onClick?: (event: React.MouseEvent) => void;
	onDoubleClick?: (event: React.MouseEvent) => void;
};

export const KanbanCard = ({
	id,
	name,
	index,
	parent,
	children,
	className,
	onClick,
	onDoubleClick
}: KanbanCardProps) => {
	const { attributes, listeners, setNodeRef, transform, isDragging } =
		useDraggable({
			id,
			data: { index, parent }
		});

	return (
		<Card
			className={cn(
				'rounded-md p-3 shadow-sm',
				isDragging && 'cursor-grabbing opacity-0',
				!isDragging && 'cursor-pointer',
				className
			)}
			style={{
				transform: transform
					? `translateX(${transform.x}px) translateY(${transform.y}px)`
					: 'none'
			}}
			{...attributes}
			{...listeners}
			onClick={(e) => !isDragging && onClick?.(e)}
			onDoubleClick={onDoubleClick}
			ref={setNodeRef}
		>
			{children ?? <p className="m-0 font-medium text-sm">{name}</p>}
		</Card>
	);
};

export type KanbanCardsProps = {
	children: ReactNode;
	className?: string;
};

export const KanbanCards = ({ children, className }: KanbanCardsProps) => (
	<div className={cn('flex flex-1 flex-col gap-2', className)}>{children}</div>
);

export type KanbanHeaderProps =
	| {
			children: ReactNode;
	  }
	| {
			name: Status['name'];
			color: Status['color'];
			className?: string;
	  };

export const KanbanHeader = (props: KanbanHeaderProps) =>
	'children' in props ? (
		props.children
	) : (
		<div className={cn('flex shrink-0 items-center gap-2', props.className)}>
			<div
				className="h-2 w-2 rounded-full"
				style={{ backgroundColor: props.color }}
			/>
			<p className="m-0 font-semibold text-sm">{props.name}</p>
		</div>
	);

export type KanbanProviderProps = {
	children: ReactNode;
	onDragEnd: (event: DragEndEvent) => void;
	onDragStart?: (event: DragEndEvent) => void;
	onDragCancel?: () => void;
	className?: string;
	dragOverlay?: ReactNode;
};

export const KanbanProvider = ({
	children,
	onDragEnd,
	onDragStart,
	onDragCancel,
	className,
	dragOverlay
}: KanbanProviderProps) => {
	// Configure sensors with activation constraints to prevent accidental drags
	const sensors = useSensors(
		// Only start a drag if you've moved more than 8px
		useSensor(MouseSensor, {
			activationConstraint: { distance: 8 }
		}),
		// On touch devices, require a short press + small move
		useSensor(TouchSensor, {
			activationConstraint: { delay: 150, tolerance: 5 }
		})
	);

	return (
		<DndContext
			sensors={sensors}
			collisionDetection={rectIntersection}
			onDragEnd={onDragEnd}
			onDragStart={onDragStart}
			onDragCancel={onDragCancel}
		>
			<div
				className={cn(
					'grid w-full auto-cols-fr grid-flow-col gap-4',
					className
				)}
			>
				{children}
			</div>
			<DragOverlay>{dragOverlay}</DragOverlay>
		</DndContext>
	);
};

```

--------------------------------------------------------------------------------
/.taskmaster/docs/research/2025-06-14_test-save-functionality.md:
--------------------------------------------------------------------------------

```markdown
---
title: Research Session
query: "test save functionality"
date: 6/14/2025
time: 5:50:23 PM
timestamp: 2025-06-14T21:50:23.797Z
exchanges: 1
---

# Research Session

## Initial Query

**Question:** test save functionality

**Response:**

## Testing Save Functionality in a Task Management System

**Core Steps for Testing Save Functionality**

- Create or update a task in the system using the CLI or UI.
- Trigger the save operation (e.g., by issuing a command or clicking a save button).
- Verify that the data is correctly written to the persistent storage (e.g., `tasks.json`).
- Reload or re-query the data to ensure the saved changes persist and are accurately reflected.
- Validate that all required fields and relationships (such as dependencies and subtasks) are preserved and correctly structured.

**Key Test Scenarios**

- **Basic Save:** Add a new task and confirm it appears in `tasks.json` with all fields populated.
- **Update Save:** Modify an existing task (e.g., change status, description, or priority) and verify the changes are saved.
- **Subtask and Dependency Preservation:** Update a parent task and ensure that completed subtasks and dependency links remain intact, especially after using commands like `updateTask`.
- **Batch Operations:** Perform bulk updates or additions and confirm all affected tasks are saved correctly, with no data loss or corruption.
- **Validation:** Attempt to save invalid data and ensure the system rejects it or provides meaningful error messages, confirming validation logic is enforced.

**Recommended Practices**

- Use automated tests to simulate save operations for various scenarios, including edge cases (e.g., circular dependencies, missing required fields).
- After each save, reload the data and compare it to the expected state to ensure integrity.
- Log all save operations and errors for traceability and debugging.
- Regularly back up the `tasks.json` file to prevent data loss during testing and development.

**Relevant Features from Industry Standards**

- Task management systems typically provide robust save and update mechanisms, ensuring that all task attributes (including dependencies, subtasks, and comments) are consistently maintained[1][2][5].
- Validation and error handling are critical to prevent corrupt or incomplete saves, especially when handling complex relationships or batch operations[1][5].
- User feedback (such as success messages or error alerts) is important for confirming save actions and guiding users in case of issues[1][2].

**Example Test Case**

1. Create a task with subtasks and dependencies.
2. Mark some subtasks as complete.
3. Use the `updateTask` command to modify the parent task.
4. Save and reload the data.
5. Assert that:
   - The parent task reflects the updates.
   - Completed subtasks remain marked as complete.
   - Dependencies are unchanged and valid.

**Summary Table: Save Functionality Test Aspects**

| Aspect                | What to Test                                      | Expected Outcome                        |
|-----------------------|---------------------------------------------------|-----------------------------------------|
| Basic Save            | Add/update task, save, reload                     | Data persists and matches expectations  |
| Subtask Preservation  | Update parent, check subtask status               | Completed subtasks remain complete      |
| Dependency Handling   | Save with dependencies, reload                    | Dependencies are intact and valid       |
| Batch Operations      | Bulk save/update, reload                          | All changes persist, no data loss       |
| Validation            | Save invalid data                                 | Error is raised, data not corrupted     |

These steps and scenarios ensure that the save functionality is robust, reliable, and meets the requirements of a modern task management system[1][2][5].


---

*Generated by Task Master Research Command*  
*Timestamp: 2025-06-14T21:50:23.797Z*

```

--------------------------------------------------------------------------------
/tests/unit/scripts/modules/task-manager/parse-prd-schema.test.js:
--------------------------------------------------------------------------------

```javascript
import { describe, it, expect } from '@jest/globals';
import { prdResponseSchema } from '../../../../../scripts/modules/task-manager/parse-prd/parse-prd-config.js';

describe('PRD Response Schema', () => {
	const validTask = {
		id: 1,
		title: 'Test Task',
		description: 'Test description',
		details: 'Test details',
		testStrategy: 'Test strategy',
		priority: 'high',
		dependencies: [],
		status: 'pending'
	};

	describe('Valid responses', () => {
		it('should accept response with tasks and metadata', () => {
			const response = {
				tasks: [validTask],
				metadata: {
					projectName: 'Test Project',
					totalTasks: 1,
					sourceFile: 'test.txt',
					generatedAt: '2025-01-01T00:00:00Z'
				}
			};

			const result = prdResponseSchema.safeParse(response);
			expect(result.success).toBe(true);
		});

		it('should accept response with tasks and null metadata', () => {
			const response = {
				tasks: [validTask],
				metadata: null
			};

			const result = prdResponseSchema.safeParse(response);
			expect(result.success).toBe(true);
		});

		it('should accept response with only tasks (no metadata field)', () => {
			// This is what ZAI returns - just the tasks array without metadata
			const response = {
				tasks: [validTask]
			};

			const result = prdResponseSchema.safeParse(response);
			expect(result.success).toBe(true);
			if (result.success) {
				// With .default(null), omitted metadata becomes null
				expect(result.data.metadata).toBeNull();
			}
		});

		it('should accept response with multiple tasks', () => {
			const response = {
				tasks: [validTask, { ...validTask, id: 2, title: 'Second Task' }]
			};

			const result = prdResponseSchema.safeParse(response);
			expect(result.success).toBe(true);
		});
	});

	describe('Invalid responses', () => {
		it('should reject response without tasks field', () => {
			const response = {
				metadata: null
			};

			const result = prdResponseSchema.safeParse(response);
			expect(result.success).toBe(false);
		});

		it('should reject response with empty tasks array and invalid metadata', () => {
			const response = {
				tasks: [],
				metadata: 'invalid'
			};

			const result = prdResponseSchema.safeParse(response);
			expect(result.success).toBe(false);
		});

		it('should reject task with missing required fields', () => {
			const response = {
				tasks: [
					{
						id: 1,
						title: 'Test'
						// missing other required fields
					}
				]
			};

			const result = prdResponseSchema.safeParse(response);
			expect(result.success).toBe(false);
		});

		it('should reject task with invalid priority', () => {
			const response = {
				tasks: [
					{
						...validTask,
						priority: 'invalid'
					}
				]
			};

			const result = prdResponseSchema.safeParse(response);
			expect(result.success).toBe(false);
		});
	});

	describe('ZAI-specific response format', () => {
		it('should handle ZAI response format (tasks only, no metadata)', () => {
			// This is the actual format ZAI returns
			const zaiResponse = {
				tasks: [
					{
						id: 24,
						title: 'Core Todo Data Management',
						description:
							'Implement the core data structure and CRUD operations',
						status: 'pending',
						dependencies: [],
						priority: 'high',
						details: 'Create a Todo data model with properties...',
						testStrategy: 'Unit tests for TodoManager class...'
					},
					{
						id: 25,
						title: 'Todo UI and User Interactions',
						description: 'Create the user interface components',
						status: 'pending',
						dependencies: [24],
						priority: 'high',
						details: 'Build a simple HTML/CSS/JS interface...',
						testStrategy: 'UI component tests...'
					}
				]
			};

			const result = prdResponseSchema.safeParse(zaiResponse);
			expect(result.success).toBe(true);
			if (result.success) {
				expect(result.data.tasks).toHaveLength(2);
				// With .default(null), omitted metadata becomes null (not undefined)
				expect(result.data.metadata).toBeNull();
			}
		});
	});
});

```

--------------------------------------------------------------------------------
/packages/tm-core/src/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Main entry point for @tm/core
 * Provides unified access to all Task Master functionality through TmCore
 */

import type { TasksDomain } from './modules/tasks/tasks-domain.js';

// ========== Primary API ==========

/**
 * Create a new TmCore instance - The ONLY way to use tm-core
 *
 * @example
 * ```typescript
 * import { createTmCore } from '@tm/core';
 *
 * const tmcore = await createTmCore({
 *   projectPath: process.cwd()
 * });
 *
 * // Access domains
 * await tmcore.auth.login({ ... });
 * const tasks = await tmcore.tasks.list();
 * await tmcore.workflow.start({ taskId: '1' });
 * await tmcore.git.commit('feat: add feature');
 * const config = tmcore.config.get('models.main');
 * ```
 */
export { createTmCore, TmCore, type TmCoreOptions } from './tm-core.js';

// ========== Type Exports ==========

// Common types that consumers need
export type * from './common/types/index.js';

// Common interfaces
export type * from './common/interfaces/index.js';

// Storage interfaces - TagInfo and TagsWithStatsResult
export type {
	TagInfo,
	TagsWithStatsResult
} from './common/interfaces/storage.interface.js';

// Constants
export * from './common/constants/index.js';

// Errors
export * from './common/errors/index.js';

// Utils
export * from './common/utils/index.js';
export * from './utils/time.utils.js';

// ========== Domain-Specific Type Exports ==========

// Task types
export type {
	TaskListResult,
	GetTaskListOptions
} from './modules/tasks/services/task-service.js';

export type {
	StartTaskOptions,
	StartTaskResult,
	ConflictCheckResult
} from './modules/tasks/services/task-execution-service.js';

export type {
	PreflightResult,
	CheckResult
} from './modules/tasks/services/preflight-checker.service.js';

// Task domain result types
export type TaskWithSubtaskResult = Awaited<ReturnType<TasksDomain['get']>>;

// Auth types
export type {
	AuthCredentials,
	OAuthFlowOptions,
	UserContext
} from './modules/auth/types.js';
export { AuthenticationError } from './modules/auth/types.js';

// Brief types
export type { Brief } from './modules/briefs/types.js';
export type { TagWithStats } from './modules/briefs/services/brief-service.js';

// Workflow types
export type {
	StartWorkflowOptions,
	WorkflowStatus,
	NextAction
} from './modules/workflow/services/workflow.service.js';

export type {
	WorkflowPhase,
	TDDPhase,
	WorkflowContext,
	WorkflowState,
	TestResult
} from './modules/workflow/types.js';

// Git types
export type { CommitMessageOptions } from './modules/git/services/commit-message-generator.js';

// Integration types
export type {
	ExportTasksOptions,
	ExportResult
} from './modules/integration/services/export.service.js';

// Reports types
export type {
	ComplexityReport,
	ComplexityReportMetadata,
	ComplexityAnalysis,
	TaskComplexityData
} from './modules/reports/types.js';

// ========== Advanced API (for CLI/Extension/MCP) ==========

// Auth - Advanced
export { AuthManager } from './modules/auth/managers/auth-manager.js';

// Briefs - Advanced
export { BriefsDomain } from './modules/briefs/briefs-domain.js';
export { BriefService } from './modules/briefs/services/brief-service.js';

// Workflow - Advanced
export { WorkflowOrchestrator } from './modules/workflow/orchestrators/workflow-orchestrator.js';
export { WorkflowStateManager } from './modules/workflow/managers/workflow-state-manager.js';
export { WorkflowService } from './modules/workflow/services/workflow.service.js';
export type { SubtaskInfo } from './modules/workflow/types.js';

// Git - Advanced
export { GitAdapter } from './modules/git/adapters/git-adapter.js';
export { CommitMessageGenerator } from './modules/git/services/commit-message-generator.js';

// Tasks - Advanced
export { PreflightChecker } from './modules/tasks/services/preflight-checker.service.js';
export { TaskLoaderService } from './modules/tasks/services/task-loader.service.js';

// Integration - Advanced
export { ExportService } from './modules/integration/services/export.service.js';

```

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

```typescript
/**
 * @fileoverview ID generation utilities for Task Master
 * Provides functions to generate unique identifiers for tasks and subtasks
 */

import { randomBytes } from 'node:crypto';

/**
 * Generates a unique task ID using the format: TASK-{timestamp}-{random}
 *
 * @returns A unique task ID string
 * @example
 * ```typescript
 * const taskId = generateTaskId();
 * // Returns something like: "TASK-1704067200000-A7B3"
 * ```
 */
export function generateTaskId(): string {
	const timestamp = Date.now();
	const random = generateRandomString(4);
	return `TASK-${timestamp}-${random}`;
}

/**
 * Generates a subtask ID using the format: {parentId}.{sequential}
 *
 * @param parentId - The ID of the parent task
 * @param existingSubtasks - Array of existing subtask IDs to determine the next sequential number
 * @returns A unique subtask ID string
 * @example
 * ```typescript
 * const subtaskId = generateSubtaskId("TASK-123-A7B3", ["TASK-123-A7B3.1"]);
 * // Returns: "TASK-123-A7B3.2"
 * ```
 */
export function generateSubtaskId(
	parentId: string,
	existingSubtasks: string[] = []
): string {
	// Find existing subtasks for this parent
	const parentSubtasks = existingSubtasks.filter((id) =>
		id.startsWith(`${parentId}.`)
	);

	// Extract sequential numbers and find the highest
	const sequentialNumbers = parentSubtasks
		.map((id) => {
			const parts = id.split('.');
			const lastPart = parts[parts.length - 1];
			return Number.parseInt(lastPart, 10);
		})
		.filter((num) => !Number.isNaN(num))
		.sort((a, b) => a - b);

	// Determine the next sequential number
	const nextSequential =
		sequentialNumbers.length > 0 ? Math.max(...sequentialNumbers) + 1 : 1;

	return `${parentId}.${nextSequential}`;
}

/**
 * Generates a random alphanumeric string of specified length
 * Uses crypto.randomBytes for cryptographically secure randomness
 *
 * @param length - The desired length of the random string
 * @returns A random alphanumeric string
 * @internal
 */
function generateRandomString(length: number): string {
	const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
	const bytes = randomBytes(length);
	let result = '';

	for (let i = 0; i < length; i++) {
		result += chars[bytes[i] % chars.length];
	}

	return result;
}

/**
 * Validates a task ID format
 *
 * @param id - The ID to validate
 * @returns True if the ID matches the expected task ID format
 * @example
 * ```typescript
 * isValidTaskId("TASK-1704067200000-A7B3"); // true
 * isValidTaskId("invalid-id"); // false
 * ```
 */
export function isValidTaskId(id: string): boolean {
	const taskIdRegex = /^TASK-\d{13}-[A-Z0-9]{4}$/;
	return taskIdRegex.test(id);
}

/**
 * Validates a subtask ID format
 *
 * @param id - The ID to validate
 * @returns True if the ID matches the expected subtask ID format
 * @example
 * ```typescript
 * isValidSubtaskId("TASK-1704067200000-A7B3.1"); // true
 * isValidSubtaskId("TASK-1704067200000-A7B3.1.2"); // true (nested subtask)
 * isValidSubtaskId("invalid.id"); // false
 * ```
 */
export function isValidSubtaskId(id: string): boolean {
	const parts = id.split('.');
	if (parts.length < 2) return false;

	// First part should be a valid task ID
	const taskIdPart = parts[0];
	if (!isValidTaskId(taskIdPart)) return false;

	// Remaining parts should be positive integers
	const sequentialParts = parts.slice(1);
	return sequentialParts.every((part) => {
		const num = Number.parseInt(part, 10);
		return !Number.isNaN(num) && num > 0 && part === num.toString();
	});
}

/**
 * Extracts the parent task ID from a subtask ID
 *
 * @param subtaskId - The subtask ID
 * @returns The parent task ID, or null if the input is not a valid subtask ID
 * @example
 * ```typescript
 * getParentTaskId("TASK-1704067200000-A7B3.1.2"); // "TASK-1704067200000-A7B3"
 * getParentTaskId("TASK-1704067200000-A7B3"); // null (not a subtask)
 * ```
 */
export function getParentTaskId(subtaskId: string): string | null {
	if (!isValidSubtaskId(subtaskId)) return null;

	const parts = subtaskId.split('.');
	return parts[0];
}

```

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

```typescript
/**
 * Task Repository - Simplified version
 * Handles data access with caching
 */

import { EventEmitter } from '../utils/event-emitter';
import type { ExtensionLogger } from '../utils/logger';
import type { TaskMasterApi, TaskMasterTask } from '../utils/task-master-api';

// Use the TaskMasterTask type directly to ensure compatibility
export type Task = TaskMasterTask;

export class TaskRepository extends EventEmitter {
	private cache: Task[] | null = null;
	private cacheTimestamp = 0;
	private readonly CACHE_DURATION = 30000; // 30 seconds

	constructor(
		private api: TaskMasterApi,
		private logger: ExtensionLogger
	) {
		super();
	}

	async getAll(options?: {
		tag?: string;
		withSubtasks?: boolean;
	}): Promise<Task[]> {
		// If a tag is specified, always fetch fresh data
		const shouldUseCache =
			!options?.tag &&
			this.cache &&
			Date.now() - this.cacheTimestamp < this.CACHE_DURATION;

		if (shouldUseCache) {
			return this.cache || [];
		}

		try {
			const result = await this.api.getTasks({
				withSubtasks: options?.withSubtasks ?? true,
				tag: options?.tag
			});

			if (result.success && result.data) {
				this.cache = result.data;
				this.cacheTimestamp = Date.now();
				this.emit('tasks:updated', result.data);
				return result.data;
			}

			throw new Error(result.error || 'Failed to fetch tasks');
		} catch (error) {
			this.logger.error('Failed to get tasks', error);
			throw error;
		}
	}

	async getById(taskId: string): Promise<Task | null> {
		// First check cache
		if (this.cache) {
			// Handle both main tasks and subtasks
			for (const task of this.cache) {
				if (task.id === taskId) {
					return task;
				}
				// Check subtasks
				if (task.subtasks) {
					for (const subtask of task.subtasks) {
						if (
							subtask.id.toString() === taskId ||
							`${task.id}.${subtask.id}` === taskId
						) {
							return {
								...subtask,
								id: subtask.id.toString(),
								description: subtask.description || '',
								status: (subtask.status ||
									'pending') as TaskMasterTask['status'],
								priority: 'medium' as const,
								dependencies:
									subtask.dependencies?.map((d) => d.toString()) || []
							};
						}
					}
				}
			}
		}

		// If not in cache, fetch all and search
		const tasks = await this.getAll();
		for (const task of tasks) {
			if (task.id === taskId) {
				return task;
			}
			// Check subtasks
			if (task.subtasks) {
				for (const subtask of task.subtasks) {
					if (
						subtask.id.toString() === taskId ||
						`${task.id}.${subtask.id}` === taskId
					) {
						return {
							...subtask,
							id: subtask.id.toString(),
							description: subtask.description || '',
							status: (subtask.status || 'pending') as TaskMasterTask['status'],
							priority: 'medium' as const,
							dependencies: subtask.dependencies?.map((d) => d.toString()) || []
						};
					}
				}
			}
		}

		return null;
	}

	async updateStatus(taskId: string, status: Task['status']): Promise<void> {
		try {
			const result = await this.api.updateTaskStatus(taskId, status);

			if (!result.success) {
				throw new Error(result.error || 'Failed to update status');
			}

			// Invalidate cache
			this.cache = null;

			// Fetch updated tasks
			await this.getAll();
		} catch (error) {
			this.logger.error('Failed to update task status', error);
			throw error;
		}
	}

	async updateContent(taskId: string, updates: any): Promise<void> {
		try {
			const result = await this.api.updateTask(taskId, updates, {
				append: false,
				research: false
			});

			if (!result.success) {
				throw new Error(result.error || 'Failed to update task');
			}

			// Invalidate cache
			this.cache = null;

			// Fetch updated tasks
			await this.getAll();
		} catch (error) {
			this.logger.error('Failed to update task content', error);
			throw error;
		}
	}

	async refresh(): Promise<void> {
		this.cache = null;
		await this.getAll();
	}

	isConnected(): boolean {
		return this.api.getConnectionStatus().isConnected;
	}
}

```

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

```typescript
/**
 * Error handling utilities for Grok CLI provider
 */

import { APICallError, LoadAPIKeyError } from '@ai-sdk/provider';
import type { GrokCliErrorMetadata } from './types.js';

/**
 * Parameters for creating API call errors
 */
interface CreateAPICallErrorParams {
	/** Error message */
	message: string;
	/** Error code */
	code?: string;
	/** Process exit code */
	exitCode?: number;
	/** Standard error output */
	stderr?: string;
	/** Standard output */
	stdout?: string;
	/** Excerpt of the prompt */
	promptExcerpt?: string;
	/** Whether the error is retryable */
	isRetryable?: boolean;
}

/**
 * Parameters for creating authentication errors
 */
interface CreateAuthenticationErrorParams {
	/** Error message */
	message?: string;
}

/**
 * Parameters for creating timeout errors
 */
interface CreateTimeoutErrorParams {
	/** Error message */
	message: string;
	/** Excerpt of the prompt */
	promptExcerpt?: string;
	/** Timeout in milliseconds */
	timeoutMs: number;
}

/**
 * Parameters for creating installation errors
 */
interface CreateInstallationErrorParams {
	/** Error message */
	message?: string;
}

/**
 * Create an API call error with Grok CLI specific metadata
 */
export function createAPICallError({
	message,
	code,
	exitCode,
	stderr,
	stdout,
	promptExcerpt,
	isRetryable = false
}: CreateAPICallErrorParams): APICallError {
	const metadata: GrokCliErrorMetadata = {
		code,
		exitCode,
		stderr,
		stdout,
		promptExcerpt
	};

	return new APICallError({
		message,
		isRetryable,
		url: 'grok-cli://command',
		requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : undefined,
		data: metadata
	});
}

/**
 * Create an authentication error
 */
export function createAuthenticationError({
	message
}: CreateAuthenticationErrorParams): LoadAPIKeyError {
	return new LoadAPIKeyError({
		message:
			message ||
			'Authentication failed. Please ensure Grok CLI is properly configured with API key.'
	});
}

/**
 * Create a timeout error
 */
export function createTimeoutError({
	message,
	promptExcerpt,
	timeoutMs
}: CreateTimeoutErrorParams): APICallError {
	const metadata: GrokCliErrorMetadata & { timeoutMs: number } = {
		code: 'TIMEOUT',
		promptExcerpt,
		timeoutMs
	};

	return new APICallError({
		message,
		isRetryable: true,
		url: 'grok-cli://command',
		requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : undefined,
		data: metadata
	});
}

/**
 * Create a CLI installation error
 */
export function createInstallationError({
	message
}: CreateInstallationErrorParams): APICallError {
	return new APICallError({
		message:
			message ||
			'Grok CLI is not installed or not found in PATH. Please install with: npm install -g @vibe-kit/grok-cli',
		isRetryable: false,
		url: 'grok-cli://installation',
		requestBodyValues: undefined
	});
}

/**
 * Check if an error is an authentication error
 */
export function isAuthenticationError(
	error: unknown
): error is LoadAPIKeyError {
	if (error instanceof LoadAPIKeyError) return true;
	if (error instanceof APICallError) {
		const metadata = error.data as GrokCliErrorMetadata | undefined;
		if (!metadata) return false;
		return (
			metadata.exitCode === 401 ||
			metadata.code === 'AUTHENTICATION_ERROR' ||
			metadata.code === 'UNAUTHORIZED'
		);
	}
	return false;
}

/**
 * Check if an error is a timeout error
 */
export function isTimeoutError(error: unknown): error is APICallError {
	if (
		error instanceof APICallError &&
		(error.data as GrokCliErrorMetadata)?.code === 'TIMEOUT'
	)
		return true;
	return false;
}

/**
 * Check if an error is an installation error
 */
export function isInstallationError(error: unknown): error is APICallError {
	if (error instanceof APICallError && error.url === 'grok-cli://installation')
		return true;
	return false;
}

/**
 * Get error metadata from an error
 */
export function getErrorMetadata(
	error: unknown
): GrokCliErrorMetadata | undefined {
	if (error instanceof APICallError && error.data) {
		return error.data as GrokCliErrorMetadata;
	}
	return undefined;
}

```

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

```typescript
/**
 * @fileoverview WorkflowActivityLogger - Logs all workflow events to activity.jsonl
 *
 * Subscribes to all WorkflowOrchestrator events and persists them to a JSONL file
 * for debugging, auditing, and workflow analysis.
 */

import { getLogger } from '../../../common/logger/index.js';
import {
	type ActivityEvent,
	logActivity
} from '../../storage/adapters/activity-logger.js';
import type { WorkflowOrchestrator } from '../orchestrators/workflow-orchestrator.js';
import type { WorkflowEventData, WorkflowEventType } from '../types.js';

/**
 * All workflow event types that should be logged
 */
const WORKFLOW_EVENT_TYPES: WorkflowEventType[] = [
	'workflow:started',
	'workflow:completed',
	'workflow:error',
	'workflow:resumed',
	'phase:entered',
	'phase:exited',
	'tdd:feature-already-implemented',
	'tdd:red:started',
	'tdd:red:completed',
	'tdd:green:started',
	'tdd:green:completed',
	'tdd:commit:started',
	'tdd:commit:completed',
	'subtask:started',
	'subtask:completed',
	'subtask:failed',
	'test:run',
	'test:passed',
	'test:failed',
	'git:branch:created',
	'git:commit:created',
	'error:occurred',
	'state:persisted',
	'progress:updated',
	'adapter:configured'
];

/**
 * Logs all workflow events to an activity.jsonl file
 */
export class WorkflowActivityLogger {
	private readonly activityLogPath: string;
	private readonly orchestrator: WorkflowOrchestrator;
	private readonly logger = getLogger('WorkflowActivityLogger');
	private readonly listenerMap: Map<
		WorkflowEventType,
		(event: WorkflowEventData) => void
	> = new Map();
	private isActive = false;

	constructor(orchestrator: WorkflowOrchestrator, activityLogPath: string) {
		this.orchestrator = orchestrator;
		this.activityLogPath = activityLogPath;
	}

	/**
	 * Start logging workflow events
	 */
	start(): void {
		if (this.isActive) {
			this.logger.warn('Activity logger is already active');
			return;
		}

		// Subscribe to all workflow events, storing listener references for cleanup
		WORKFLOW_EVENT_TYPES.forEach((eventType) => {
			const listener = (event: WorkflowEventData) => this.logEvent(event);
			this.listenerMap.set(eventType, listener);
			this.orchestrator.on(eventType, listener);
		});

		this.isActive = true;
		this.logger.debug(
			`Activity logger started, logging to: ${this.activityLogPath}`
		);
	}

	/**
	 * Stop logging workflow events and remove all listeners
	 */
	stop(): void {
		if (!this.isActive) {
			return;
		}

		// Remove all registered listeners
		this.listenerMap.forEach((listener, eventType) => {
			this.orchestrator.off(eventType, listener);
		});

		// Clear the listener map
		this.listenerMap.clear();

		this.isActive = false;
		this.logger.debug('Activity logger stopped and listeners removed');
	}

	/**
	 * Log a workflow event to the activity log
	 */
	private async logEvent(event: WorkflowEventData): Promise<void> {
		if (!this.isActive) {
			return;
		}

		try {
			// Convert timestamp to ISO string, handling both Date objects and string/number timestamps
			const ts =
				(event.timestamp as any) instanceof Date
					? (event.timestamp as Date).toISOString()
					: new Date(event.timestamp as any).toISOString();

			// Convert WorkflowEventData to ActivityEvent format
			const activityEvent: Omit<ActivityEvent, 'timestamp'> = {
				type: event.type,
				phase: event.phase,
				tddPhase: event.tddPhase,
				subtaskId: event.subtaskId,
				// Event timestamp kept as ISO for readability; storage layer adds its own "timestamp"
				eventTimestamp: ts,
				...(event.data || {})
			};

			await logActivity(this.activityLogPath, activityEvent);
		} catch (error: any) {
			// Log errors but don't throw - we don't want activity logging to break the workflow
			this.logger.error(
				`Failed to log activity event ${event.type}: ${error.message}`
			);
		}
	}

	/**
	 * Get the path to the activity log file
	 */
	getActivityLogPath(): string {
		return this.activityLogPath;
	}

	/**
	 * Check if the logger is currently active
	 */
	isLogging(): boolean {
		return this.isActive;
	}
}

```

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

```markdown
---
title: PRD Creation and Parsing
sidebarTitle: "PRD Creation and Parsing"
---

# Writing a PRD

A PRD (Product Requirements Document) is the starting point of every task flow in Task Master. It defines what you're building and why. A clear PRD dramatically improves the quality of your tasks, your model outputs, and your final product — so it’s worth taking the time to get it right.

<Tip>
You don’t need to define your whole app up front. You can write a focused PRD just for the next feature or module you’re working on.
</Tip>

<Tip>
You can start with an empty project or you can start with a feature PRD on an existing project.
</Tip>

<Tip>
You can add and parse multiple PRDs per project using the --append flag
</Tip>

## What Makes a Good PRD?

- Clear objective — what’s the outcome or feature?
- Context — what’s already in place or assumed?
- Constraints — what limits or requirements need to be respected?
- Reasoning — why are you building it this way?

The more context you give the model, the better the breakdown and results.

---

## Writing a PRD for Task Master

<Note>
Two example PRD templates are available in `.taskmaster/templates/`:
- `example_prd.md` - Simple template for straightforward projects (`.md` recommended for better editor support)
- `example_prd_rpg.md` - Advanced RPG (Repository Planning Graph) template for complex projects with dependencies

**Why `.md`?** While both `.txt` and `.md` work, Markdown files provide syntax highlighting, proper rendering in VS Code/GitHub, and better collaboration through formatted documentation.
</Note>


You can co-write your PRD with an LLM model using the following workflow:

1. **Chat about requirements** — explain what you want to build.
2. **Show an example PRD** — share the example PRD so the model understands the expected format. The example uses formatting that work well with Task Master's code. Following the example will yield better results.
3. **Iterate and refine** — work with the model to shape the draft into a clear and well-structured PRD.

This approach works great in Cursor, or anywhere you use a chat-based LLM.

### Choosing Between Templates

**Use `example_prd.md` when:**
- Building straightforward features
- Working on smaller projects
- Dependencies are simple and obvious

**Use `example_prd_rpg.md` when:**
- Building complex systems with multiple modules
- Need explicit dependency management
- Want structured guidance on architecture decisions
- Planning a large codebase from scratch

The RPG template teaches you to think about:
1. **Functional decomposition** (WHAT the system does)
2. **Structural decomposition** (HOW it's organized in code)
3. **Explicit dependencies** (WHAT depends on WHAT)
4. **Topological ordering** (build foundation first, then layers)

<Tip>
For complex projects, using the RPG template with a code-context-aware ai agent produces the best results because the AI can understand your existing codebase structure. [Learn more about the RPG method →](/capabilities/rpg-method)
</Tip>

---

## Where to Save Your PRD

Place your PRD file in the `.taskmaster/docs` folder in your project.

- You can have **multiple PRDs** per project.
- Name your PRDs clearly so they're easy to reference later.
  - Examples: `dashboard_redesign.md`, `user_onboarding.md`
  - Tip: Use `.md` extension for better editor support and syntax highlighting

---

# Parse your PRD into Tasks

This is where the Task Master magic begins.

In Cursor's AI chat, instruct the agent to generate tasks from your PRD:

```
Please use the task-master parse-prd command to generate tasks from my PRD. The PRD is located at .taskmaster/docs/<prd-name>.md.
```

The agent will execute the following command which you can alternatively paste into the CLI:

```bash
task-master parse-prd .taskmaster/docs/<prd-name>.md
```

This will:

- Parse your PRD document
- Generate a structured `tasks.json` file with tasks, dependencies, priorities, and test strategies

Now that you have written and parsed a PRD, you are ready to start setting up your tasks.



```

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

```typescript
import { describe, expect, it, vi } from 'vitest';
import type { Tables } from '../types/database.types.js';
import { TaskMapper } from './TaskMapper.js';

type TaskRow = Tables<'tasks'>;

describe('TaskMapper', () => {
	describe('extractMetadataField', () => {
		it('should extract string field from metadata', () => {
			const taskRow: TaskRow = {
				id: '123',
				display_id: '1',
				title: 'Test Task',
				description: 'Test description',
				status: 'todo',
				priority: 'medium',
				parent_task_id: null,
				subtask_position: 0,
				created_at: new Date().toISOString(),
				updated_at: new Date().toISOString(),
				metadata: {
					details: 'Some details',
					testStrategy: 'Test with unit tests'
				},
				complexity: null,
				assignee_id: null,
				estimated_hours: null,
				actual_hours: null,
				due_date: null,
				completed_at: null
			};

			const task = TaskMapper.mapDatabaseTaskToTask(taskRow, [], new Map());

			expect(task.details).toBe('Some details');
			expect(task.testStrategy).toBe('Test with unit tests');
		});

		it('should use default value when metadata field is missing', () => {
			const taskRow: TaskRow = {
				id: '123',
				display_id: '1',
				title: 'Test Task',
				description: 'Test description',
				status: 'todo',
				priority: 'medium',
				parent_task_id: null,
				subtask_position: 0,
				created_at: new Date().toISOString(),
				updated_at: new Date().toISOString(),
				metadata: {},
				complexity: null,
				assignee_id: null,
				estimated_hours: null,
				actual_hours: null,
				due_date: null,
				completed_at: null
			};

			const task = TaskMapper.mapDatabaseTaskToTask(taskRow, [], new Map());

			expect(task.details).toBe('');
			expect(task.testStrategy).toBe('');
		});

		it('should use default value when metadata is null', () => {
			const taskRow: TaskRow = {
				id: '123',
				display_id: '1',
				title: 'Test Task',
				description: 'Test description',
				status: 'todo',
				priority: 'medium',
				parent_task_id: null,
				subtask_position: 0,
				created_at: new Date().toISOString(),
				updated_at: new Date().toISOString(),
				metadata: null,
				complexity: null,
				assignee_id: null,
				estimated_hours: null,
				actual_hours: null,
				due_date: null,
				completed_at: null
			};

			const task = TaskMapper.mapDatabaseTaskToTask(taskRow, [], new Map());

			expect(task.details).toBe('');
			expect(task.testStrategy).toBe('');
		});

		it('should use default value and warn when metadata field has wrong type', () => {
			const consoleWarnSpy = vi
				.spyOn(console, 'warn')
				.mockImplementation(() => {});

			const taskRow: TaskRow = {
				id: '123',
				display_id: '1',
				title: 'Test Task',
				description: 'Test description',
				status: 'todo',
				priority: 'medium',
				parent_task_id: null,
				subtask_position: 0,
				created_at: new Date().toISOString(),
				updated_at: new Date().toISOString(),
				metadata: {
					details: 12345, // Wrong type: number instead of string
					testStrategy: ['test1', 'test2'] // Wrong type: array instead of string
				},
				complexity: null,
				assignee_id: null,
				estimated_hours: null,
				actual_hours: null,
				due_date: null,
				completed_at: null
			};

			const task = TaskMapper.mapDatabaseTaskToTask(taskRow, [], new Map());

			// Should use empty string defaults when type doesn't match
			expect(task.details).toBe('');
			expect(task.testStrategy).toBe('');

			// Should have logged warnings
			expect(consoleWarnSpy).toHaveBeenCalledWith(
				expect.stringContaining('Type mismatch in metadata field "details"')
			);
			expect(consoleWarnSpy).toHaveBeenCalledWith(
				expect.stringContaining(
					'Type mismatch in metadata field "testStrategy"'
				)
			);

			consoleWarnSpy.mockRestore();
		});
	});

	describe('mapStatus', () => {
		it('should map database status to internal status', () => {
			expect(TaskMapper.mapStatus('todo')).toBe('pending');
			expect(TaskMapper.mapStatus('in_progress')).toBe('in-progress');
			expect(TaskMapper.mapStatus('done')).toBe('done');
		});
	});
});

```

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

```markdown
# Task Master Commands for Claude Code

Complete guide to using Task Master through Claude Code's slash commands.

## Overview

All Task Master functionality is available through the `/project:tm/` namespace with natural language support and intelligent features.

## Quick Start

```bash
# Install Task Master
/project:tm/setup/quick-install

# Initialize project
/project:tm/init/quick

# Parse requirements
/project:tm/parse-prd requirements.md

# Start working
/project:tm/next
```

## Command Structure

Commands are organized hierarchically to match Task Master's CLI:
- Main commands at `/project:tm/[command]`
- Subcommands for specific operations `/project:tm/[command]/[subcommand]`
- Natural language arguments accepted throughout

## Complete Command Reference

### Setup & Configuration
- `/project:tm/setup/install` - Full installation guide
- `/project:tm/setup/quick-install` - One-line install
- `/project:tm/init` - Initialize project
- `/project:tm/init/quick` - Quick init with -y
- `/project:tm/models` - View AI config
- `/project:tm/models/setup` - Configure AI

### Task Generation
- `/project:tm/parse-prd` - Generate from PRD
- `/project:tm/parse-prd/with-research` - Enhanced parsing
- `/project:tm/generate` - Create task files

### Task Management
- `/project:tm/list` - List with natural language filters
- `/project:tm/list/with-subtasks` - Hierarchical view
- `/project:tm/list/by-status <status>` - Filter by status
- `/project:tm/show <id>` - Task details
- `/project:tm/add-task` - Create task
- `/project:tm/update` - Update tasks
- `/project:tm/remove-task` - Delete task

### Status Management
- `/project:tm/set-status/to-pending <id>`
- `/project:tm/set-status/to-in-progress <id>`
- `/project:tm/set-status/to-done <id>`
- `/project:tm/set-status/to-review <id>`
- `/project:tm/set-status/to-deferred <id>`
- `/project:tm/set-status/to-cancelled <id>`

### Task Analysis
- `/project:tm/analyze-complexity` - AI analysis
- `/project:tm/complexity-report` - View report
- `/project:tm/expand <id>` - Break down task
- `/project:tm/expand/all` - Expand all complex

### Dependencies
- `/project:tm/add-dependency` - Add dependency
- `/project:tm/remove-dependency` - Remove dependency
- `/project:tm/validate-dependencies` - Check issues
- `/project:tm/fix-dependencies` - Auto-fix

### Workflows
- `/project:tm/workflows/smart-flow` - Adaptive workflows
- `/project:tm/workflows/pipeline` - Chain commands
- `/project:tm/workflows/auto-implement` - AI implementation

### Utilities
- `/project:tm/status` - Project dashboard
- `/project:tm/next` - Next task recommendation
- `/project:tm/utils/analyze` - Project analysis
- `/project:tm/learn` - Interactive help

## Key Features

### Natural Language Support
All commands understand natural language:
```
/project:tm/list pending high priority
/project:tm/update mark 23 as done
/project:tm/add-task implement OAuth login
```

### Smart Context
Commands analyze project state and provide intelligent suggestions based on:
- Current task status
- Dependencies
- Team patterns
- Project phase

### Visual Enhancements
- Progress bars and indicators
- Status badges
- Organized displays
- Clear hierarchies

## Common Workflows

### Daily Development
```
/project:tm/workflows/smart-flow morning
/project:tm/next
/project:tm/set-status/to-in-progress <id>
/project:tm/set-status/to-done <id>
```

### Task Breakdown
```
/project:tm/show <id>
/project:tm/expand <id>
/project:tm/list/with-subtasks
```

### Sprint Planning
```
/project:tm/analyze-complexity
/project:tm/workflows/pipeline init → expand/all → status
```

## Migration from Old Commands

| Old | New |
|-----|-----|
| `/project:task-master:list` | `/project:tm/list` |
| `/project:task-master:complete` | `/project:tm/set-status/to-done` |
| `/project:workflows:auto-implement` | `/project:tm/workflows/auto-implement` |

## Tips

1. Use `/project:tm/` + Tab for command discovery
2. Natural language is supported everywhere
3. Commands provide smart defaults
4. Chain commands for automation
5. Check `/project:tm/learn` for interactive help
```

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

```markdown
# Task Master Commands for Claude Code

Complete guide to using Task Master through Claude Code's slash commands.

## Overview

All Task Master functionality is available through the `/project:tm/` namespace with natural language support and intelligent features.

## Quick Start

```bash
# Install Task Master
/project:tm/setup/quick-install

# Initialize project
/project:tm/init/quick

# Parse requirements
/project:tm/parse-prd requirements.md

# Start working
/project:tm/next
```

## Command Structure

Commands are organized hierarchically to match Task Master's CLI:
- Main commands at `/project:tm/[command]`
- Subcommands for specific operations `/project:tm/[command]/[subcommand]`
- Natural language arguments accepted throughout

## Complete Command Reference

### Setup & Configuration
- `/project:tm/setup/install` - Full installation guide
- `/project:tm/setup/quick-install` - One-line install
- `/project:tm/init` - Initialize project
- `/project:tm/init/quick` - Quick init with -y
- `/project:tm/models` - View AI config
- `/project:tm/models/setup` - Configure AI

### Task Generation
- `/project:tm/parse-prd` - Generate from PRD
- `/project:tm/parse-prd/with-research` - Enhanced parsing
- `/project:tm/generate` - Create task files

### Task Management
- `/project:tm/list` - List with natural language filters
- `/project:tm/list/with-subtasks` - Hierarchical view
- `/project:tm/list/by-status <status>` - Filter by status
- `/project:tm/show <id>` - Task details
- `/project:tm/add-task` - Create task
- `/project:tm/update` - Update tasks
- `/project:tm/remove-task` - Delete task

### Status Management
- `/project:tm/set-status/to-pending <id>`
- `/project:tm/set-status/to-in-progress <id>`
- `/project:tm/set-status/to-done <id>`
- `/project:tm/set-status/to-review <id>`
- `/project:tm/set-status/to-deferred <id>`
- `/project:tm/set-status/to-cancelled <id>`

### Task Analysis
- `/project:tm/analyze-complexity` - AI analysis
- `/project:tm/complexity-report` - View report
- `/project:tm/expand <id>` - Break down task
- `/project:tm/expand/all` - Expand all complex

### Dependencies
- `/project:tm/add-dependency` - Add dependency
- `/project:tm/remove-dependency` - Remove dependency
- `/project:tm/validate-dependencies` - Check issues
- `/project:tm/fix-dependencies` - Auto-fix

### Workflows
- `/project:tm/workflows/smart-flow` - Adaptive workflows
- `/project:tm/workflows/pipeline` - Chain commands
- `/project:tm/workflows/auto-implement` - AI implementation

### Utilities
- `/project:tm/status` - Project dashboard
- `/project:tm/next` - Next task recommendation
- `/project:tm/utils/analyze` - Project analysis
- `/project:tm/learn` - Interactive help

## Key Features

### Natural Language Support
All commands understand natural language:
```
/project:tm/list pending high priority
/project:tm/update mark 23 as done
/project:tm/add-task implement OAuth login
```

### Smart Context
Commands analyze project state and provide intelligent suggestions based on:
- Current task status
- Dependencies
- Team patterns
- Project phase

### Visual Enhancements
- Progress bars and indicators
- Status badges
- Organized displays
- Clear hierarchies

## Common Workflows

### Daily Development
```
/project:tm/workflows/smart-flow morning
/project:tm/next
/project:tm/set-status/to-in-progress <id>
/project:tm/set-status/to-done <id>
```

### Task Breakdown
```
/project:tm/show <id>
/project:tm/expand <id>
/project:tm/list/with-subtasks
```

### Sprint Planning
```
/project:tm/analyze-complexity
/project:tm/workflows/pipeline init → expand/all → status
```

## Migration from Old Commands

| Old | New |
|-----|-----|
| `/project:task-master:list` | `/project:tm/list` |
| `/project:task-master:complete` | `/project:tm/set-status/to-done` |
| `/project:workflows:auto-implement` | `/project:tm/workflows/auto-implement` |

## Tips

1. Use `/project:tm/` + Tab for command discovery
2. Natural language is supported everywhere
3. Commands provide smart defaults
4. Chain commands for automation
5. Check `/project:tm/learn` for interactive help
```

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

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

import { clearSubtasks } from '../../../../scripts/modules/task-manager.js';
import {
	enableSilentMode,
	disableSilentMode,
	readJSON
} from '../../../../scripts/modules/utils.js';
import fs from 'fs';
import path from 'path';

/**
 * Clear subtasks from specified tasks
 * @param {Object} args - Function arguments
 * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 * @param {string} [args.id] - Task IDs (comma-separated) to clear subtasks from
 * @param {boolean} [args.all] - Clear subtasks from all tasks
 * @param {string} [args.tag] - Tag context to operate on (defaults to current active tag)
 * @param {string} [args.projectRoot] - Project root path (for MCP/env fallback)
 * @param {Object} log - Logger object
 * @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
 */
export async function clearSubtasksDirect(args, log) {
	// Destructure expected args
	const { tasksJsonPath, id, all, tag, projectRoot } = args;
	try {
		log.info(`Clearing subtasks with args: ${JSON.stringify(args)}`);

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

		// Either id or all must be provided
		if (!id && !all) {
			return {
				success: false,
				error: {
					code: 'INPUT_VALIDATION_ERROR',
					message:
						'Either task IDs with id parameter or all parameter must be provided'
				}
			};
		}

		// Use provided path
		const tasksPath = tasksJsonPath;

		// Check if tasks.json exists
		if (!fs.existsSync(tasksPath)) {
			return {
				success: false,
				error: {
					code: 'FILE_NOT_FOUND_ERROR',
					message: `Tasks file not found at ${tasksPath}`
				}
			};
		}

		let taskIds;

		// Use readJSON which handles silent migration and tag resolution
		const data = readJSON(tasksPath, projectRoot, tag);

		if (!data || !data.tasks) {
			return {
				success: false,
				error: {
					code: 'INPUT_VALIDATION_ERROR',
					message: `No tasks found in tasks file: ${tasksPath}`
				}
			};
		}

		const currentTag = data.tag || tag;
		const tasks = data.tasks;

		// If all is specified, get all task IDs
		if (all) {
			log.info(`Clearing subtasks from all tasks in tag '${currentTag}'`);
			if (tasks.length === 0) {
				return {
					success: false,
					error: {
						code: 'INPUT_VALIDATION_ERROR',
						message: `No tasks found in tag context '${currentTag}'`
					}
				};
			}
			taskIds = tasks.map((t) => t.id).join(',');
		} else {
			// Use the provided task IDs
			taskIds = id;
		}

		log.info(`Clearing subtasks from tasks: ${taskIds} in tag '${currentTag}'`);

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

		// Call the core function
		clearSubtasks(tasksPath, taskIds, { projectRoot, tag: currentTag });

		// Restore normal logging
		disableSilentMode();

		// Read the updated data to provide a summary
		const updatedData = readJSON(tasksPath, projectRoot, currentTag);
		const taskIdArray = taskIds.split(',').map((id) => parseInt(id.trim(), 10));

		// Build a summary of what was done
		const clearedTasksCount = taskIdArray.length;
		const updatedTasks = updatedData.tasks || [];

		const taskSummary = taskIdArray.map((id) => {
			const task = updatedTasks.find((t) => t.id === id);
			return task ? { id, title: task.title } : { id, title: 'Task not found' };
		});

		return {
			success: true,
			data: {
				message: `Successfully cleared subtasks from ${clearedTasksCount} task(s) in tag '${currentTag}'`,
				tasksCleared: taskSummary,
				tag: currentTag
			}
		};
	} catch (error) {
		// Make sure to restore normal logging even if there's an error
		disableSilentMode();

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

```

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

```javascript
/**
 * openai-compatible.js
 * Generic base class for OpenAI-compatible API providers.
 * This allows any provider with an OpenAI-compatible API to be easily integrated.
 */

import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { BaseAIProvider } from './base-provider.js';

/**
 * Base class for OpenAI-compatible providers (LM Studio, Z.ai, etc.)
 * Provides a flexible foundation for any service with OpenAI-compatible endpoints.
 */
export class OpenAICompatibleProvider extends BaseAIProvider {
	/**
	 * @param {object} config - Provider configuration
	 * @param {string} config.name - Provider display name
	 * @param {string} config.apiKeyEnvVar - Environment variable name for API key
	 * @param {boolean} [config.requiresApiKey=true] - Whether API key is required
	 * @param {string} [config.defaultBaseURL] - Default base URL for the API
	 * @param {Function} [config.getBaseURL] - Function to determine base URL from params
	 * @param {boolean} [config.supportsStructuredOutputs] - Whether provider supports structured outputs
	 */
	constructor(config) {
		super();

		if (!config.name) {
			throw new Error('Provider name is required');
		}
		if (!config.apiKeyEnvVar) {
			throw new Error('API key environment variable name is required');
		}

		this.name = config.name;
		this.apiKeyEnvVar = config.apiKeyEnvVar;
		this.requiresApiKey = config.requiresApiKey !== false; // Default to true
		this.defaultBaseURL = config.defaultBaseURL;
		this.getBaseURLFromParams = config.getBaseURL;
		this.supportsStructuredOutputs = config.supportsStructuredOutputs;
	}

	/**
	 * Returns the environment variable name required for this provider's API key.
	 * @returns {string} The environment variable name for the API key
	 */
	getRequiredApiKeyName() {
		return this.apiKeyEnvVar;
	}

	/**
	 * Returns whether this provider requires an API key.
	 * @returns {boolean} True if API key is required
	 */
	isRequiredApiKey() {
		return this.requiresApiKey;
	}

	/**
	 * Override auth validation based on requiresApiKey setting
	 * @param {object} params - Parameters to validate
	 */
	validateAuth(params) {
		if (this.requiresApiKey && !params.apiKey) {
			throw new Error(`${this.name} API key is required`);
		}
	}

	/**
	 * Determines the base URL to use for the API.
	 * @param {object} params - Client parameters
	 * @returns {string|undefined} The base URL to use
	 */
	getBaseURL(params) {
		// If custom baseURL provided in params, use it
		if (params.baseURL) {
			return params.baseURL;
		}

		// If provider has a custom getBaseURL function, use it
		if (this.getBaseURLFromParams) {
			return this.getBaseURLFromParams(params);
		}

		// Otherwise use default baseURL if available
		return this.defaultBaseURL;
	}

	/**
	 * Creates and returns an OpenAI-compatible client instance.
	 * @param {object} params - Parameters for client initialization
	 * @param {string} [params.apiKey] - API key (required if requiresApiKey is true)
	 * @param {string} [params.baseURL] - Optional custom API endpoint
	 * @returns {Function} OpenAI-compatible client function
	 * @throws {Error} If required parameters are missing or initialization fails
	 */
	getClient(params) {
		try {
			const { apiKey } = params;
			const fetchImpl = this.createProxyFetch();

			const baseURL = this.getBaseURL(params);

			const clientConfig = {
				// Provider name for SDK (required, used for logging/debugging)
				name: this.name.toLowerCase().replace(/[^a-z0-9]/g, '-')
			};

			// Only include apiKey if provider requires it
			if (this.requiresApiKey && apiKey) {
				clientConfig.apiKey = apiKey;
			}

			// Include baseURL if available
			if (baseURL) {
				clientConfig.baseURL = baseURL;
			}

			// Configure structured outputs support if specified
			if (this.supportsStructuredOutputs !== undefined) {
				clientConfig.supportsStructuredOutputs = this.supportsStructuredOutputs;
			}

			// Add proxy support if available
			if (fetchImpl) {
				clientConfig.fetch = fetchImpl;
			}

			return createOpenAICompatible(clientConfig);
		} catch (error) {
			this.handleError('client initialization', error);
		}
	}
}

```

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

```typescript
/**
 * Barebones storage adapter for Supabase Auth sessions
 *
 * This is a simple key-value store that lets Supabase manage sessions
 * without interference. No parsing, no merging, no guessing - just storage.
 *
 * Supabase handles:
 * - Session refresh and token rotation
 * - Expiry checking
 * - Token validation
 *
 * We handle:
 * - Simple get/set/remove/clear operations
 * - Persistence to ~/.taskmaster/session.json with atomic writes via steno
 */

import fs from 'fs/promises';
import fsSync from 'fs';
import path from 'path';
import { Writer } from 'steno';
import type { SupportedStorage } from '@supabase/supabase-js';
import { getLogger } from '../../../common/logger/index.js';

const DEFAULT_SESSION_FILE = path.join(
	process.env.HOME || process.env.USERPROFILE || '~',
	'.taskmaster',
	'session.json'
);

export class SupabaseSessionStorage implements SupportedStorage {
	private storage: Map<string, string> = new Map();
	private persistPath: string;
	private logger = getLogger('SupabaseSessionStorage');
	private writer: Writer;
	private initPromise: Promise<void>;

	constructor(persistPath: string = DEFAULT_SESSION_FILE) {
		this.persistPath = persistPath;
		this.writer = new Writer(persistPath);
		this.initPromise = this.load();
	}

	/**
	 * Load session data from disk on initialization
	 */
	private async load(): Promise<void> {
		try {
			// Ensure directory exists
			const dir = path.dirname(this.persistPath);
			await fs.mkdir(dir, { recursive: true, mode: 0o700 });

			// Try to read existing session
			if (fsSync.existsSync(this.persistPath)) {
				const data = JSON.parse(await fs.readFile(this.persistPath, 'utf8'));
				Object.entries(data).forEach(([k, v]) =>
					this.storage.set(k, v as string)
				);
				this.logger.debug('Loaded session from disk', {
					keys: Array.from(this.storage.keys())
				});
			}
		} catch (error) {
			this.logger.error('Failed to load session:', error);
			// Don't throw - allow starting with fresh session
		}
	}

	/**
	 * Persist session data to disk immediately
	 * Uses steno for atomic writes with fsync guarantees
	 * This prevents race conditions in rapid CLI command sequences
	 */
	private async persist(): Promise<void> {
		try {
			const data = Object.fromEntries(this.storage);
			const jsonContent = JSON.stringify(data, null, 2);

			// steno handles atomic writes with temp file + rename
			// and ensures data is flushed to disk
			await this.writer.write(jsonContent + '\n');

			this.logger.debug('Persisted session to disk (steno)');
		} catch (error) {
			this.logger.error('Failed to persist session:', error);
			// Don't throw - session is still in memory
		}
	}

	/**
	 * Get item from storage
	 * Supabase will call this to retrieve session data
	 * Returns a promise to ensure initialization completes first
	 */
	async getItem(key: string): Promise<string | null> {
		// Wait for initialization to complete
		await this.initPromise;

		const value = this.storage.get(key) ?? null;
		this.logger.debug('getItem called', { key, hasValue: !!value });
		return value;
	}

	/**
	 * Set item in storage
	 * Supabase will call this to store/update session data
	 * CRITICAL: This is called during token refresh - must persist immediately
	 */
	async setItem(key: string, value: string): Promise<void> {
		// Wait for initialization to complete
		await this.initPromise;

		this.logger.debug('setItem called', { key });
		this.storage.set(key, value);

		// Immediately persist on every write
		// steno ensures atomic writes with fsync
		await this.persist();
	}

	/**
	 * Remove item from storage
	 * Supabase will call this during sign out
	 */
	async removeItem(key: string): Promise<void> {
		// Wait for initialization to complete
		await this.initPromise;

		this.logger.debug('removeItem called', { key });
		this.storage.delete(key);
		await this.persist();
	}

	/**
	 * Clear all session data
	 * Useful for complete logout scenarios
	 */
	async clear(): Promise<void> {
		// Wait for initialization to complete
		await this.initPromise;

		this.logger.debug('clear called');
		this.storage.clear();
		await this.persist();
	}
}

```

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

```typescript
/**
 * Tests for message conversion utilities
 */

import { describe, expect, it } from 'vitest';
import {
	convertFromGrokCliResponse,
	convertToGrokCliMessages,
	createPromptFromMessages,
	escapeShellArg
} from './message-converter.js';

describe('convertToGrokCliMessages', () => {
	it('should convert string content messages', () => {
		const messages = [
			{ role: 'user', content: 'Hello, world!' },
			{ role: 'assistant', content: 'Hi there!' }
		];

		const result = convertToGrokCliMessages(messages);

		expect(result).toEqual([
			{ role: 'user', content: 'Hello, world!' },
			{ role: 'assistant', content: 'Hi there!' }
		]);
	});

	it('should convert array content messages', () => {
		const messages = [
			{
				role: 'user',
				content: [
					{ type: 'text', text: 'Hello' },
					{ type: 'text', text: 'World' }
				]
			}
		];

		const result = convertToGrokCliMessages(messages);

		expect(result).toEqual([{ role: 'user', content: 'Hello\nWorld' }]);
	});

	it('should convert object content messages', () => {
		const messages = [
			{
				role: 'user',
				content: { text: 'Hello from object' }
			}
		];

		const result = convertToGrokCliMessages(messages);

		expect(result).toEqual([{ role: 'user', content: 'Hello from object' }]);
	});
});

describe('convertFromGrokCliResponse', () => {
	it('should parse JSONL response format', () => {
		const responseText = `{"role": "assistant", "content": "Hello there!", "usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15}}`;

		const result = convertFromGrokCliResponse(responseText);

		expect(result).toEqual({
			text: 'Hello there!',
			usage: {
				promptTokens: 10,
				completionTokens: 5,
				totalTokens: 15
			}
		});
	});

	it('should handle multiple lines in JSONL format', () => {
		const responseText = `{"role": "user", "content": "Hello"}
{"role": "assistant", "content": "Hi there!", "usage": {"prompt_tokens": 5, "completion_tokens": 3}}`;

		const result = convertFromGrokCliResponse(responseText);

		expect(result).toEqual({
			text: 'Hi there!',
			usage: {
				promptTokens: 5,
				completionTokens: 3,
				totalTokens: 0
			}
		});
	});

	it('should fallback to raw text when parsing fails', () => {
		const responseText = 'Invalid JSON response';

		const result = convertFromGrokCliResponse(responseText);

		expect(result).toEqual({
			text: 'Invalid JSON response',
			usage: undefined
		});
	});
});

describe('createPromptFromMessages', () => {
	it('should create formatted prompt from messages', () => {
		const messages = [
			{ role: 'system', content: 'You are a helpful assistant.' },
			{ role: 'user', content: 'What is 2+2?' },
			{ role: 'assistant', content: '2+2 equals 4.' }
		];

		const result = createPromptFromMessages(messages);

		expect(result).toBe(
			'System: You are a helpful assistant.\n\nUser: What is 2+2?\n\nAssistant: 2+2 equals 4.'
		);
	});

	it('should handle custom role names', () => {
		const messages = [{ role: 'custom', content: 'Custom message' }];

		const result = createPromptFromMessages(messages);

		expect(result).toBe('custom: Custom message');
	});

	it('should trim whitespace from message content', () => {
		const messages = [
			{ role: 'user', content: '  Hello with spaces  ' },
			{ role: 'assistant', content: '\n\nResponse with newlines\n\n' }
		];

		const result = createPromptFromMessages(messages);

		expect(result).toBe(
			'User: Hello with spaces\n\nAssistant: Response with newlines'
		);
	});
});

describe('escapeShellArg', () => {
	it('should escape single quotes', () => {
		const arg = "It's a test";
		const result = escapeShellArg(arg);
		expect(result).toBe("'It'\\''s a test'");
	});

	it('should handle strings without special characters', () => {
		const arg = 'simple string';
		const result = escapeShellArg(arg);
		expect(result).toBe("'simple string'");
	});

	it('should convert non-string values to strings', () => {
		const arg = 123;
		const result = escapeShellArg(arg);
		expect(result).toBe("'123'");
	});

	it('should handle empty strings', () => {
		const arg = '';
		const result = escapeShellArg(arg);
		expect(result).toBe("''");
	});
});

```

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

```typescript
/**
 * @fileoverview Environment Configuration Provider
 * Extracts configuration from environment variables
 */

import type { PartialConfiguration } from '../../../common/interfaces/configuration.interface.js';
import { getLogger } from '../../../common/logger/index.js';

/**
 * Environment variable mapping definition
 */
interface EnvMapping {
	/** Environment variable name */
	env: string;
	/** Path in configuration object */
	path: readonly string[];
	/** Optional validator function */
	validate?: (value: string) => boolean;
	/** Whether this is runtime state (not configuration) */
	isRuntimeState?: boolean;
}

/**
 * EnvironmentConfigProvider extracts configuration from environment variables
 * Single responsibility: Environment variable configuration extraction
 */
export class EnvironmentConfigProvider {
	private readonly logger = getLogger('EnvironmentConfigProvider');

	/**
	 * Default environment variable mappings
	 */
	private static readonly DEFAULT_MAPPINGS: EnvMapping[] = [
		{
			env: 'TASKMASTER_STORAGE_TYPE',
			path: ['storage', 'type'],
			validate: (v: string) => ['file', 'api'].includes(v)
		},
		{ env: 'TASKMASTER_API_ENDPOINT', path: ['storage', 'apiEndpoint'] },
		{ env: 'TASKMASTER_API_TOKEN', path: ['storage', 'apiAccessToken'] },
		{ env: 'TASKMASTER_MODEL_MAIN', path: ['models', 'main'] },
		{ env: 'TASKMASTER_MODEL_RESEARCH', path: ['models', 'research'] },
		{ env: 'TASKMASTER_MODEL_FALLBACK', path: ['models', 'fallback'] },
		{
			env: 'TASKMASTER_RESPONSE_LANGUAGE',
			path: ['custom', 'responseLanguage']
		}
	];

	/**
	 * Runtime state mappings (separate from configuration)
	 */
	private static readonly RUNTIME_STATE_MAPPINGS: EnvMapping[] = [
		{ env: 'TASKMASTER_TAG', path: ['activeTag'], isRuntimeState: true }
	];

	private mappings: EnvMapping[];

	constructor(customMappings?: EnvMapping[]) {
		this.mappings = customMappings || [
			...EnvironmentConfigProvider.DEFAULT_MAPPINGS,
			...EnvironmentConfigProvider.RUNTIME_STATE_MAPPINGS
		];
	}

	/**
	 * Load configuration from environment variables
	 */
	loadConfig(): PartialConfiguration {
		const config: PartialConfiguration = {};

		for (const mapping of this.mappings) {
			// Skip runtime state variables
			if (mapping.isRuntimeState) continue;

			const value = process.env[mapping.env];
			if (!value) continue;

			// Validate value if validator is provided
			if (mapping.validate && !mapping.validate(value)) {
				this.logger.warn(`Invalid value for ${mapping.env}: ${value}`);
				continue;
			}

			// Set the value in the config object
			this.setNestedProperty(config, mapping.path, value);
		}

		return config;
	}

	/**
	 * Get runtime state from environment variables
	 */
	getRuntimeState(): Record<string, string> {
		const state: Record<string, string> = {};

		for (const mapping of this.mappings) {
			if (!mapping.isRuntimeState) continue;

			const value = process.env[mapping.env];
			if (value) {
				const key = mapping.path[mapping.path.length - 1];
				state[key] = value;
			}
		}

		return state;
	}

	/**
	 * Helper to set a nested property in an object
	 */
	private setNestedProperty(
		obj: any,
		path: readonly string[],
		value: any
	): void {
		const lastKey = path[path.length - 1];
		const keys = path.slice(0, -1);

		let current = obj;
		for (const key of keys) {
			if (!current[key]) {
				current[key] = {};
			}
			current = current[key];
		}

		current[lastKey] = value;
	}

	/**
	 * Check if an environment variable is set
	 */
	hasEnvVar(envName: string): boolean {
		return envName in process.env && process.env[envName] !== undefined;
	}

	/**
	 * Get all environment variables that match our prefix
	 */
	getAllTaskmasterEnvVars(): Record<string, string> {
		const vars: Record<string, string> = {};
		const prefix = 'TASKMASTER_';

		for (const [key, value] of Object.entries(process.env)) {
			if (key.startsWith(prefix) && value !== undefined) {
				vars[key] = value;
			}
		}

		return vars;
	}

	/**
	 * Add a custom mapping
	 */
	addMapping(mapping: EnvMapping): void {
		this.mappings.push(mapping);
	}

	/**
	 * Get current mappings
	 */
	getMappings(): EnvMapping[] {
		return [...this.mappings];
	}
}

```

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

```markdown
# Phase 0: Spike - Autonomous TDD Workflow ✅ COMPLETE

## Objective
Validate feasibility and build foundational understanding before full implementation.

## Status
**COMPLETED** - All deliverables implemented and validated.

See `apps/cli/src/commands/autopilot.command.ts` for implementation.

## Scope
- Implement CLI skeleton `tm autopilot` with dry-run mode
- Show planned steps from a real task with subtasks
- Detect test runner from package.json
- Detect git state and render preflight report

## Deliverables

### 1. CLI Command Skeleton
- Create `apps/cli/src/commands/autopilot.command.ts`
- Support `tm autopilot <taskId>` command
- Implement `--dry-run` flag
- Basic help text and usage information

### 2. Preflight Detection System
- Detect test runner from package.json (npm test, pnpm test, etc.)
- Check git working tree state (clean/dirty)
- Validate required tools are available (git, gh, node/npm)
- Detect default branch

### 3. Dry-Run Execution Plan Display
Display planned execution for a task including:
- Preflight checks status
- Branch name that would be created
- Tag that would be set
- List of subtasks in execution order
- For each subtask:
  - RED phase: test file that would be created
  - GREEN phase: implementation files that would be modified
  - COMMIT: commit message that would be used
- Finalization steps: test suite run, coverage check, push, PR creation

### 4. Task Loading & Validation
- Load task from TaskMaster state
- Validate task exists and has subtasks
- If no subtasks, show message about needing to expand first
- Show dependency order for subtasks

## Example Output

```bash
$ tm autopilot 42 --dry-run

Autopilot Plan for Task #42 [analytics]: User metrics tracking
─────────────────────────────────────────────────────────────

Preflight Checks:
  ✓ Working tree is clean
  ✓ Test command detected: npm test
  ✓ Tools available: git, gh, node, npm
  ✓ Current branch: main (will create new branch)
  ✓ Task has 3 subtasks ready to execute

Branch & Tag:
  → Will create branch: analytics/task-42-user-metrics
  → Will set active tag: analytics

Execution Plan (3 subtasks):

  1. Subtask 42.1: Add metrics schema
     RED:    Generate tests → src/__tests__/schema.test.js
     GREEN:  Implement code → src/schema.js
     COMMIT: "feat(metrics): add metrics schema (task 42.1)"

  2. Subtask 42.2: Add collection endpoint [depends on 42.1]
     RED:    Generate tests → src/api/__tests__/metrics.test.js
     GREEN:  Implement code → src/api/metrics.js
     COMMIT: "feat(metrics): add collection endpoint (task 42.2)"

  3. Subtask 42.3: Add dashboard widget [depends on 42.2]
     RED:    Generate tests → src/components/__tests__/MetricsWidget.test.jsx
     GREEN:  Implement code → src/components/MetricsWidget.jsx
     COMMIT: "feat(metrics): add dashboard widget (task 42.3)"

Finalization:
  → Run full test suite with coverage (threshold: 80%)
  → Push branch to origin (will confirm)
  → Create PR targeting main

Estimated commits: 3
Estimated duration: ~20-30 minutes (depends on implementation complexity)

Run without --dry-run to execute.
```

## Success Criteria
- Dry-run output is clear and matches expected workflow
- Preflight detection works correctly on the project repo
- Task loading integrates with existing TaskMaster state
- No actual git operations or file modifications occur in dry-run mode

## Out of Scope
- Actual test generation
- Actual code implementation
- Git operations (branch creation, commits, push)
- PR creation
- Test execution

## Implementation Notes
- Reuse existing `TaskService` from `packages/tm-core`
- Use existing git utilities from `scripts/modules/utils/git-utils.js`
- Load task/subtask data from `.taskmaster/tasks/tasks.json`
- Detect test command via package.json → scripts.test field

## Dependencies
- Existing TaskMaster CLI structure
- Existing task storage format
- Git utilities

## Estimated Effort
2-3 days

## Validation
Test dry-run mode with:
- Task with 1 subtask
- Task with multiple subtasks
- Task with dependencies between subtasks
- Task without subtasks (should show warning)
- Dirty git working tree (should warn)
- Missing tools (should error with helpful message)

```
Page 9/50FirstPrevNextLast