#
tokens: 44227/50000 4/975 files (page 34/50)
lines: off (toggle) GitHub
raw markdown copy
This is page 34 of 50. Use http://codebase.md/eyaltoledano/claude-task-master?lines=false&page={x} to view the full context.

# Directory Structure

```
├── .changeset
│   ├── config.json
│   └── README.md
├── .claude
│   ├── commands
│   │   └── dedupe.md
│   └── TM_COMMANDS_GUIDE.md
├── .claude-plugin
│   └── marketplace.json
├── .coderabbit.yaml
├── .cursor
│   ├── mcp.json
│   └── rules
│       ├── ai_providers.mdc
│       ├── ai_services.mdc
│       ├── architecture.mdc
│       ├── changeset.mdc
│       ├── commands.mdc
│       ├── context_gathering.mdc
│       ├── cursor_rules.mdc
│       ├── dependencies.mdc
│       ├── dev_workflow.mdc
│       ├── git_workflow.mdc
│       ├── glossary.mdc
│       ├── mcp.mdc
│       ├── new_features.mdc
│       ├── self_improve.mdc
│       ├── tags.mdc
│       ├── taskmaster.mdc
│       ├── tasks.mdc
│       ├── telemetry.mdc
│       ├── test_workflow.mdc
│       ├── tests.mdc
│       ├── ui.mdc
│       └── utilities.mdc
├── .cursorignore
├── .env.example
├── .github
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   ├── enhancements---feature-requests.md
│   │   └── feedback.md
│   ├── PULL_REQUEST_TEMPLATE
│   │   ├── bugfix.md
│   │   ├── config.yml
│   │   ├── feature.md
│   │   └── integration.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── scripts
│   │   ├── auto-close-duplicates.mjs
│   │   ├── backfill-duplicate-comments.mjs
│   │   ├── check-pre-release-mode.mjs
│   │   ├── parse-metrics.mjs
│   │   ├── release.mjs
│   │   ├── tag-extension.mjs
│   │   ├── utils.mjs
│   │   └── validate-changesets.mjs
│   └── workflows
│       ├── auto-close-duplicates.yml
│       ├── backfill-duplicate-comments.yml
│       ├── ci.yml
│       ├── claude-dedupe-issues.yml
│       ├── claude-docs-trigger.yml
│       ├── claude-docs-updater.yml
│       ├── claude-issue-triage.yml
│       ├── claude.yml
│       ├── extension-ci.yml
│       ├── extension-release.yml
│       ├── log-issue-events.yml
│       ├── pre-release.yml
│       ├── release-check.yml
│       ├── release.yml
│       ├── update-models-md.yml
│       └── weekly-metrics-discord.yml
├── .gitignore
├── .kiro
│   ├── hooks
│   │   ├── tm-code-change-task-tracker.kiro.hook
│   │   ├── tm-complexity-analyzer.kiro.hook
│   │   ├── tm-daily-standup-assistant.kiro.hook
│   │   ├── tm-git-commit-task-linker.kiro.hook
│   │   ├── tm-pr-readiness-checker.kiro.hook
│   │   ├── tm-task-dependency-auto-progression.kiro.hook
│   │   └── tm-test-success-task-completer.kiro.hook
│   ├── settings
│   │   └── mcp.json
│   └── steering
│       ├── dev_workflow.md
│       ├── kiro_rules.md
│       ├── self_improve.md
│       ├── taskmaster_hooks_workflow.md
│       └── taskmaster.md
├── .manypkg.json
├── .mcp.json
├── .npmignore
├── .nvmrc
├── .taskmaster
│   ├── CLAUDE.md
│   ├── config.json
│   ├── docs
│   │   ├── autonomous-tdd-git-workflow.md
│   │   ├── MIGRATION-ROADMAP.md
│   │   ├── prd-tm-start.txt
│   │   ├── prd.txt
│   │   ├── README.md
│   │   ├── research
│   │   │   ├── 2025-06-14_how-can-i-improve-the-scope-up-and-scope-down-comm.md
│   │   │   ├── 2025-06-14_should-i-be-using-any-specific-libraries-for-this.md
│   │   │   ├── 2025-06-14_test-save-functionality.md
│   │   │   ├── 2025-06-14_test-the-fix-for-duplicate-saves-final-test.md
│   │   │   └── 2025-08-01_do-we-need-to-add-new-commands-or-can-we-just-weap.md
│   │   ├── task-template-importing-prd.txt
│   │   ├── tdd-workflow-phase-0-spike.md
│   │   ├── tdd-workflow-phase-1-core-rails.md
│   │   ├── tdd-workflow-phase-1-orchestrator.md
│   │   ├── tdd-workflow-phase-2-pr-resumability.md
│   │   ├── tdd-workflow-phase-3-extensibility-guardrails.md
│   │   ├── test-prd.txt
│   │   └── tm-core-phase-1.txt
│   ├── reports
│   │   ├── task-complexity-report_autonomous-tdd-git-workflow.json
│   │   ├── task-complexity-report_cc-kiro-hooks.json
│   │   ├── task-complexity-report_tdd-phase-1-core-rails.json
│   │   ├── task-complexity-report_tdd-workflow-phase-0.json
│   │   ├── task-complexity-report_test-prd-tag.json
│   │   ├── task-complexity-report_tm-core-phase-1.json
│   │   ├── task-complexity-report.json
│   │   └── tm-core-complexity.json
│   ├── state.json
│   ├── tasks
│   │   ├── task_001_tm-start.txt
│   │   ├── task_002_tm-start.txt
│   │   ├── task_003_tm-start.txt
│   │   ├── task_004_tm-start.txt
│   │   ├── task_007_tm-start.txt
│   │   └── tasks.json
│   └── templates
│       ├── example_prd_rpg.md
│       └── example_prd.md
├── .vscode
│   ├── extensions.json
│   └── settings.json
├── apps
│   ├── cli
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── command-registry.ts
│   │   │   ├── commands
│   │   │   │   ├── auth.command.ts
│   │   │   │   ├── autopilot
│   │   │   │   │   ├── abort.command.ts
│   │   │   │   │   ├── commit.command.ts
│   │   │   │   │   ├── complete.command.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── next.command.ts
│   │   │   │   │   ├── resume.command.ts
│   │   │   │   │   ├── shared.ts
│   │   │   │   │   ├── start.command.ts
│   │   │   │   │   └── status.command.ts
│   │   │   │   ├── briefs.command.ts
│   │   │   │   ├── context.command.ts
│   │   │   │   ├── export.command.ts
│   │   │   │   ├── list.command.ts
│   │   │   │   ├── models
│   │   │   │   │   ├── custom-providers.ts
│   │   │   │   │   ├── fetchers.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── prompts.ts
│   │   │   │   │   ├── setup.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── next.command.ts
│   │   │   │   ├── set-status.command.ts
│   │   │   │   ├── show.command.ts
│   │   │   │   ├── start.command.ts
│   │   │   │   └── tags.command.ts
│   │   │   ├── index.ts
│   │   │   ├── lib
│   │   │   │   └── model-management.ts
│   │   │   ├── types
│   │   │   │   └── tag-management.d.ts
│   │   │   ├── ui
│   │   │   │   ├── components
│   │   │   │   │   ├── cardBox.component.ts
│   │   │   │   │   ├── dashboard.component.ts
│   │   │   │   │   ├── header.component.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── next-task.component.ts
│   │   │   │   │   ├── suggested-steps.component.ts
│   │   │   │   │   └── task-detail.component.ts
│   │   │   │   ├── display
│   │   │   │   │   ├── messages.ts
│   │   │   │   │   └── tables.ts
│   │   │   │   ├── formatters
│   │   │   │   │   ├── complexity-formatters.ts
│   │   │   │   │   ├── dependency-formatters.ts
│   │   │   │   │   ├── priority-formatters.ts
│   │   │   │   │   ├── status-formatters.spec.ts
│   │   │   │   │   └── status-formatters.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── layout
│   │   │   │       ├── helpers.spec.ts
│   │   │   │       └── helpers.ts
│   │   │   └── utils
│   │   │       ├── auth-helpers.ts
│   │   │       ├── auto-update.ts
│   │   │       ├── brief-selection.ts
│   │   │       ├── display-helpers.ts
│   │   │       ├── error-handler.ts
│   │   │       ├── index.ts
│   │   │       ├── project-root.ts
│   │   │       ├── task-status.ts
│   │   │       ├── ui.spec.ts
│   │   │       └── ui.ts
│   │   ├── tests
│   │   │   ├── integration
│   │   │   │   └── commands
│   │   │   │       └── autopilot
│   │   │   │           └── workflow.test.ts
│   │   │   └── unit
│   │   │       ├── commands
│   │   │       │   ├── autopilot
│   │   │       │   │   └── shared.test.ts
│   │   │       │   ├── list.command.spec.ts
│   │   │       │   └── show.command.spec.ts
│   │   │       └── ui
│   │   │           └── dashboard.component.spec.ts
│   │   ├── tsconfig.json
│   │   └── vitest.config.ts
│   ├── docs
│   │   ├── archive
│   │   │   ├── ai-client-utils-example.mdx
│   │   │   ├── ai-development-workflow.mdx
│   │   │   ├── command-reference.mdx
│   │   │   ├── configuration.mdx
│   │   │   ├── cursor-setup.mdx
│   │   │   ├── examples.mdx
│   │   │   └── Installation.mdx
│   │   ├── best-practices
│   │   │   ├── advanced-tasks.mdx
│   │   │   ├── configuration-advanced.mdx
│   │   │   └── index.mdx
│   │   ├── capabilities
│   │   │   ├── cli-root-commands.mdx
│   │   │   ├── index.mdx
│   │   │   ├── mcp.mdx
│   │   │   ├── rpg-method.mdx
│   │   │   └── task-structure.mdx
│   │   ├── CHANGELOG.md
│   │   ├── command-reference.mdx
│   │   ├── configuration.mdx
│   │   ├── docs.json
│   │   ├── favicon.svg
│   │   ├── getting-started
│   │   │   ├── api-keys.mdx
│   │   │   ├── contribute.mdx
│   │   │   ├── faq.mdx
│   │   │   └── quick-start
│   │   │       ├── configuration-quick.mdx
│   │   │       ├── execute-quick.mdx
│   │   │       ├── installation.mdx
│   │   │       ├── moving-forward.mdx
│   │   │       ├── prd-quick.mdx
│   │   │       ├── quick-start.mdx
│   │   │       ├── requirements.mdx
│   │   │       ├── rules-quick.mdx
│   │   │       └── tasks-quick.mdx
│   │   ├── introduction.mdx
│   │   ├── licensing.md
│   │   ├── logo
│   │   │   ├── dark.svg
│   │   │   ├── light.svg
│   │   │   └── task-master-logo.png
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── style.css
│   │   ├── tdd-workflow
│   │   │   ├── ai-agent-integration.mdx
│   │   │   └── quickstart.mdx
│   │   ├── vercel.json
│   │   └── whats-new.mdx
│   ├── extension
│   │   ├── .vscodeignore
│   │   ├── assets
│   │   │   ├── banner.png
│   │   │   ├── icon-dark.svg
│   │   │   ├── icon-light.svg
│   │   │   ├── icon.png
│   │   │   ├── screenshots
│   │   │   │   ├── kanban-board.png
│   │   │   │   └── task-details.png
│   │   │   └── sidebar-icon.svg
│   │   ├── CHANGELOG.md
│   │   ├── components.json
│   │   ├── docs
│   │   │   ├── extension-CI-setup.md
│   │   │   └── extension-development-guide.md
│   │   ├── esbuild.js
│   │   ├── LICENSE
│   │   ├── package.json
│   │   ├── package.mjs
│   │   ├── package.publish.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── components
│   │   │   │   ├── ConfigView.tsx
│   │   │   │   ├── constants.ts
│   │   │   │   ├── TaskDetails
│   │   │   │   │   ├── AIActionsSection.tsx
│   │   │   │   │   ├── DetailsSection.tsx
│   │   │   │   │   ├── PriorityBadge.tsx
│   │   │   │   │   ├── SubtasksSection.tsx
│   │   │   │   │   ├── TaskMetadataSidebar.tsx
│   │   │   │   │   └── useTaskDetails.ts
│   │   │   │   ├── TaskDetailsView.tsx
│   │   │   │   ├── TaskMasterLogo.tsx
│   │   │   │   └── ui
│   │   │   │       ├── badge.tsx
│   │   │   │       ├── breadcrumb.tsx
│   │   │   │       ├── button.tsx
│   │   │   │       ├── card.tsx
│   │   │   │       ├── collapsible.tsx
│   │   │   │       ├── CollapsibleSection.tsx
│   │   │   │       ├── dropdown-menu.tsx
│   │   │   │       ├── label.tsx
│   │   │   │       ├── scroll-area.tsx
│   │   │   │       ├── separator.tsx
│   │   │   │       ├── shadcn-io
│   │   │   │       │   └── kanban
│   │   │   │       │       └── index.tsx
│   │   │   │       └── textarea.tsx
│   │   │   ├── extension.ts
│   │   │   ├── index.ts
│   │   │   ├── lib
│   │   │   │   └── utils.ts
│   │   │   ├── services
│   │   │   │   ├── config-service.ts
│   │   │   │   ├── error-handler.ts
│   │   │   │   ├── notification-preferences.ts
│   │   │   │   ├── polling-service.ts
│   │   │   │   ├── polling-strategies.ts
│   │   │   │   ├── sidebar-webview-manager.ts
│   │   │   │   ├── task-repository.ts
│   │   │   │   ├── terminal-manager.ts
│   │   │   │   └── webview-manager.ts
│   │   │   ├── test
│   │   │   │   └── extension.test.ts
│   │   │   ├── utils
│   │   │   │   ├── configManager.ts
│   │   │   │   ├── connectionManager.ts
│   │   │   │   ├── errorHandler.ts
│   │   │   │   ├── event-emitter.ts
│   │   │   │   ├── logger.ts
│   │   │   │   ├── mcpClient.ts
│   │   │   │   ├── notificationPreferences.ts
│   │   │   │   └── task-master-api
│   │   │   │       ├── cache
│   │   │   │       │   └── cache-manager.ts
│   │   │   │       ├── index.ts
│   │   │   │       ├── mcp-client.ts
│   │   │   │       ├── transformers
│   │   │   │       │   └── task-transformer.ts
│   │   │   │       └── types
│   │   │   │           └── index.ts
│   │   │   └── webview
│   │   │       ├── App.tsx
│   │   │       ├── components
│   │   │       │   ├── AppContent.tsx
│   │   │       │   ├── EmptyState.tsx
│   │   │       │   ├── ErrorBoundary.tsx
│   │   │       │   ├── PollingStatus.tsx
│   │   │       │   ├── PriorityBadge.tsx
│   │   │       │   ├── SidebarView.tsx
│   │   │       │   ├── TagDropdown.tsx
│   │   │       │   ├── TaskCard.tsx
│   │   │       │   ├── TaskEditModal.tsx
│   │   │       │   ├── TaskMasterKanban.tsx
│   │   │       │   ├── ToastContainer.tsx
│   │   │       │   └── ToastNotification.tsx
│   │   │       ├── constants
│   │   │       │   └── index.ts
│   │   │       ├── contexts
│   │   │       │   └── VSCodeContext.tsx
│   │   │       ├── hooks
│   │   │       │   ├── useTaskQueries.ts
│   │   │       │   ├── useVSCodeMessages.ts
│   │   │       │   └── useWebviewHeight.ts
│   │   │       ├── index.css
│   │   │       ├── index.tsx
│   │   │       ├── providers
│   │   │       │   └── QueryProvider.tsx
│   │   │       ├── reducers
│   │   │       │   └── appReducer.ts
│   │   │       ├── sidebar.tsx
│   │   │       ├── types
│   │   │       │   └── index.ts
│   │   │       └── utils
│   │   │           ├── logger.ts
│   │   │           └── toast.ts
│   │   └── tsconfig.json
│   └── mcp
│       ├── CHANGELOG.md
│       ├── package.json
│       ├── src
│       │   ├── index.ts
│       │   ├── shared
│       │   │   ├── types.ts
│       │   │   └── utils.ts
│       │   └── tools
│       │       ├── autopilot
│       │       │   ├── abort.tool.ts
│       │       │   ├── commit.tool.ts
│       │       │   ├── complete.tool.ts
│       │       │   ├── finalize.tool.ts
│       │       │   ├── index.ts
│       │       │   ├── next.tool.ts
│       │       │   ├── resume.tool.ts
│       │       │   ├── start.tool.ts
│       │       │   └── status.tool.ts
│       │       ├── README-ZOD-V3.md
│       │       └── tasks
│       │           ├── get-task.tool.ts
│       │           ├── get-tasks.tool.ts
│       │           └── index.ts
│       ├── tsconfig.json
│       └── vitest.config.ts
├── assets
│   ├── .windsurfrules
│   ├── AGENTS.md
│   ├── claude
│   │   └── TM_COMMANDS_GUIDE.md
│   ├── config.json
│   ├── env.example
│   ├── example_prd_rpg.txt
│   ├── example_prd.txt
│   ├── GEMINI.md
│   ├── gitignore
│   ├── kiro-hooks
│   │   ├── tm-code-change-task-tracker.kiro.hook
│   │   ├── tm-complexity-analyzer.kiro.hook
│   │   ├── tm-daily-standup-assistant.kiro.hook
│   │   ├── tm-git-commit-task-linker.kiro.hook
│   │   ├── tm-pr-readiness-checker.kiro.hook
│   │   ├── tm-task-dependency-auto-progression.kiro.hook
│   │   └── tm-test-success-task-completer.kiro.hook
│   ├── roocode
│   │   ├── .roo
│   │   │   ├── rules-architect
│   │   │   │   └── architect-rules
│   │   │   ├── rules-ask
│   │   │   │   └── ask-rules
│   │   │   ├── rules-code
│   │   │   │   └── code-rules
│   │   │   ├── rules-debug
│   │   │   │   └── debug-rules
│   │   │   ├── rules-orchestrator
│   │   │   │   └── orchestrator-rules
│   │   │   └── rules-test
│   │   │       └── test-rules
│   │   └── .roomodes
│   ├── rules
│   │   ├── cursor_rules.mdc
│   │   ├── dev_workflow.mdc
│   │   ├── self_improve.mdc
│   │   ├── taskmaster_hooks_workflow.mdc
│   │   └── taskmaster.mdc
│   └── scripts_README.md
├── bin
│   └── task-master.js
├── biome.json
├── CHANGELOG.md
├── CLAUDE_CODE_PLUGIN.md
├── CLAUDE.md
├── context
│   ├── chats
│   │   ├── add-task-dependencies-1.md
│   │   └── max-min-tokens.txt.md
│   ├── fastmcp-core.txt
│   ├── fastmcp-docs.txt
│   ├── MCP_INTEGRATION.md
│   ├── mcp-js-sdk-docs.txt
│   ├── mcp-protocol-repo.txt
│   ├── mcp-protocol-schema-03262025.json
│   └── mcp-protocol-spec.txt
├── CONTRIBUTING.md
├── docs
│   ├── claude-code-integration.md
│   ├── CLI-COMMANDER-PATTERN.md
│   ├── command-reference.md
│   ├── configuration.md
│   ├── contributor-docs
│   │   ├── testing-roo-integration.md
│   │   └── worktree-setup.md
│   ├── cross-tag-task-movement.md
│   ├── examples
│   │   ├── claude-code-usage.md
│   │   └── codex-cli-usage.md
│   ├── examples.md
│   ├── licensing.md
│   ├── mcp-provider-guide.md
│   ├── mcp-provider.md
│   ├── migration-guide.md
│   ├── models.md
│   ├── providers
│   │   ├── codex-cli.md
│   │   └── gemini-cli.md
│   ├── README.md
│   ├── scripts
│   │   └── models-json-to-markdown.js
│   ├── task-structure.md
│   └── tutorial.md
├── images
│   ├── hamster-hiring.png
│   └── logo.png
├── index.js
├── jest.config.js
├── jest.resolver.cjs
├── LICENSE
├── llms-install.md
├── mcp-server
│   ├── server.js
│   └── src
│       ├── core
│       │   ├── __tests__
│       │   │   └── context-manager.test.js
│       │   ├── context-manager.js
│       │   ├── direct-functions
│       │   │   ├── add-dependency.js
│       │   │   ├── add-subtask.js
│       │   │   ├── add-tag.js
│       │   │   ├── add-task.js
│       │   │   ├── analyze-task-complexity.js
│       │   │   ├── cache-stats.js
│       │   │   ├── clear-subtasks.js
│       │   │   ├── complexity-report.js
│       │   │   ├── copy-tag.js
│       │   │   ├── create-tag-from-branch.js
│       │   │   ├── delete-tag.js
│       │   │   ├── expand-all-tasks.js
│       │   │   ├── expand-task.js
│       │   │   ├── fix-dependencies.js
│       │   │   ├── generate-task-files.js
│       │   │   ├── initialize-project.js
│       │   │   ├── list-tags.js
│       │   │   ├── models.js
│       │   │   ├── move-task-cross-tag.js
│       │   │   ├── move-task.js
│       │   │   ├── next-task.js
│       │   │   ├── parse-prd.js
│       │   │   ├── remove-dependency.js
│       │   │   ├── remove-subtask.js
│       │   │   ├── remove-task.js
│       │   │   ├── rename-tag.js
│       │   │   ├── research.js
│       │   │   ├── response-language.js
│       │   │   ├── rules.js
│       │   │   ├── scope-down.js
│       │   │   ├── scope-up.js
│       │   │   ├── set-task-status.js
│       │   │   ├── update-subtask-by-id.js
│       │   │   ├── update-task-by-id.js
│       │   │   ├── update-tasks.js
│       │   │   ├── use-tag.js
│       │   │   └── validate-dependencies.js
│       │   ├── task-master-core.js
│       │   └── utils
│       │       ├── env-utils.js
│       │       └── path-utils.js
│       ├── custom-sdk
│       │   ├── errors.js
│       │   ├── index.js
│       │   ├── json-extractor.js
│       │   ├── language-model.js
│       │   ├── message-converter.js
│       │   └── schema-converter.js
│       ├── index.js
│       ├── logger.js
│       ├── providers
│       │   └── mcp-provider.js
│       └── tools
│           ├── add-dependency.js
│           ├── add-subtask.js
│           ├── add-tag.js
│           ├── add-task.js
│           ├── analyze.js
│           ├── clear-subtasks.js
│           ├── complexity-report.js
│           ├── copy-tag.js
│           ├── delete-tag.js
│           ├── expand-all.js
│           ├── expand-task.js
│           ├── fix-dependencies.js
│           ├── generate.js
│           ├── get-operation-status.js
│           ├── index.js
│           ├── initialize-project.js
│           ├── list-tags.js
│           ├── models.js
│           ├── move-task.js
│           ├── next-task.js
│           ├── parse-prd.js
│           ├── README-ZOD-V3.md
│           ├── remove-dependency.js
│           ├── remove-subtask.js
│           ├── remove-task.js
│           ├── rename-tag.js
│           ├── research.js
│           ├── response-language.js
│           ├── rules.js
│           ├── scope-down.js
│           ├── scope-up.js
│           ├── set-task-status.js
│           ├── tool-registry.js
│           ├── update-subtask.js
│           ├── update-task.js
│           ├── update.js
│           ├── use-tag.js
│           ├── utils.js
│           └── validate-dependencies.js
├── mcp-test.js
├── output.json
├── package-lock.json
├── package.json
├── packages
│   ├── ai-sdk-provider-grok-cli
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── errors.test.ts
│   │   │   ├── errors.ts
│   │   │   ├── grok-cli-language-model.ts
│   │   │   ├── grok-cli-provider.test.ts
│   │   │   ├── grok-cli-provider.ts
│   │   │   ├── index.ts
│   │   │   ├── json-extractor.test.ts
│   │   │   ├── json-extractor.ts
│   │   │   ├── message-converter.test.ts
│   │   │   ├── message-converter.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   ├── build-config
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src
│   │   │   └── tsdown.base.ts
│   │   └── tsconfig.json
│   ├── claude-code-plugin
│   │   ├── .claude-plugin
│   │   │   └── plugin.json
│   │   ├── .gitignore
│   │   ├── agents
│   │   │   ├── task-checker.md
│   │   │   ├── task-executor.md
│   │   │   └── task-orchestrator.md
│   │   ├── CHANGELOG.md
│   │   ├── commands
│   │   │   ├── add-dependency.md
│   │   │   ├── add-subtask.md
│   │   │   ├── add-task.md
│   │   │   ├── analyze-complexity.md
│   │   │   ├── analyze-project.md
│   │   │   ├── auto-implement-tasks.md
│   │   │   ├── command-pipeline.md
│   │   │   ├── complexity-report.md
│   │   │   ├── convert-task-to-subtask.md
│   │   │   ├── expand-all-tasks.md
│   │   │   ├── expand-task.md
│   │   │   ├── fix-dependencies.md
│   │   │   ├── generate-tasks.md
│   │   │   ├── help.md
│   │   │   ├── init-project-quick.md
│   │   │   ├── init-project.md
│   │   │   ├── install-taskmaster.md
│   │   │   ├── learn.md
│   │   │   ├── list-tasks-by-status.md
│   │   │   ├── list-tasks-with-subtasks.md
│   │   │   ├── list-tasks.md
│   │   │   ├── next-task.md
│   │   │   ├── parse-prd-with-research.md
│   │   │   ├── parse-prd.md
│   │   │   ├── project-status.md
│   │   │   ├── quick-install-taskmaster.md
│   │   │   ├── remove-all-subtasks.md
│   │   │   ├── remove-dependency.md
│   │   │   ├── remove-subtask.md
│   │   │   ├── remove-subtasks.md
│   │   │   ├── remove-task.md
│   │   │   ├── setup-models.md
│   │   │   ├── show-task.md
│   │   │   ├── smart-workflow.md
│   │   │   ├── sync-readme.md
│   │   │   ├── tm-main.md
│   │   │   ├── to-cancelled.md
│   │   │   ├── to-deferred.md
│   │   │   ├── to-done.md
│   │   │   ├── to-in-progress.md
│   │   │   ├── to-pending.md
│   │   │   ├── to-review.md
│   │   │   ├── update-single-task.md
│   │   │   ├── update-task.md
│   │   │   ├── update-tasks-from-id.md
│   │   │   ├── validate-dependencies.md
│   │   │   └── view-models.md
│   │   ├── mcp.json
│   │   └── package.json
│   ├── tm-bridge
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── add-tag-bridge.ts
│   │   │   ├── bridge-types.ts
│   │   │   ├── bridge-utils.ts
│   │   │   ├── expand-bridge.ts
│   │   │   ├── index.ts
│   │   │   ├── tags-bridge.ts
│   │   │   ├── update-bridge.ts
│   │   │   └── use-tag-bridge.ts
│   │   └── tsconfig.json
│   └── tm-core
│       ├── .gitignore
│       ├── CHANGELOG.md
│       ├── docs
│       │   └── listTasks-architecture.md
│       ├── package.json
│       ├── POC-STATUS.md
│       ├── README.md
│       ├── src
│       │   ├── common
│       │   │   ├── constants
│       │   │   │   ├── index.ts
│       │   │   │   ├── paths.ts
│       │   │   │   └── providers.ts
│       │   │   ├── errors
│       │   │   │   ├── index.ts
│       │   │   │   └── task-master-error.ts
│       │   │   ├── interfaces
│       │   │   │   ├── configuration.interface.ts
│       │   │   │   ├── index.ts
│       │   │   │   └── storage.interface.ts
│       │   │   ├── logger
│       │   │   │   ├── factory.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── logger.spec.ts
│       │   │   │   └── logger.ts
│       │   │   ├── mappers
│       │   │   │   ├── TaskMapper.test.ts
│       │   │   │   └── TaskMapper.ts
│       │   │   ├── types
│       │   │   │   ├── database.types.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── legacy.ts
│       │   │   │   └── repository-types.ts
│       │   │   └── utils
│       │   │       ├── git-utils.ts
│       │   │       ├── id-generator.ts
│       │   │       ├── index.ts
│       │   │       ├── path-helpers.ts
│       │   │       ├── path-normalizer.spec.ts
│       │   │       ├── path-normalizer.ts
│       │   │       ├── project-root-finder.spec.ts
│       │   │       ├── project-root-finder.ts
│       │   │       ├── run-id-generator.spec.ts
│       │   │       └── run-id-generator.ts
│       │   ├── index.ts
│       │   ├── modules
│       │   │   ├── ai
│       │   │   │   ├── index.ts
│       │   │   │   ├── interfaces
│       │   │   │   │   └── ai-provider.interface.ts
│       │   │   │   └── providers
│       │   │   │       ├── base-provider.ts
│       │   │   │       └── index.ts
│       │   │   ├── auth
│       │   │   │   ├── auth-domain.spec.ts
│       │   │   │   ├── auth-domain.ts
│       │   │   │   ├── config.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── managers
│       │   │   │   │   ├── auth-manager.spec.ts
│       │   │   │   │   └── auth-manager.ts
│       │   │   │   ├── services
│       │   │   │   │   ├── context-store.ts
│       │   │   │   │   ├── oauth-service.ts
│       │   │   │   │   ├── organization.service.ts
│       │   │   │   │   ├── supabase-session-storage.spec.ts
│       │   │   │   │   └── supabase-session-storage.ts
│       │   │   │   └── types.ts
│       │   │   ├── briefs
│       │   │   │   ├── briefs-domain.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── services
│       │   │   │   │   └── brief-service.ts
│       │   │   │   ├── types.ts
│       │   │   │   └── utils
│       │   │   │       └── url-parser.ts
│       │   │   ├── commands
│       │   │   │   └── index.ts
│       │   │   ├── config
│       │   │   │   ├── config-domain.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── managers
│       │   │   │   │   ├── config-manager.spec.ts
│       │   │   │   │   └── config-manager.ts
│       │   │   │   └── services
│       │   │   │       ├── config-loader.service.spec.ts
│       │   │   │       ├── config-loader.service.ts
│       │   │   │       ├── config-merger.service.spec.ts
│       │   │   │       ├── config-merger.service.ts
│       │   │   │       ├── config-persistence.service.spec.ts
│       │   │   │       ├── config-persistence.service.ts
│       │   │   │       ├── environment-config-provider.service.spec.ts
│       │   │   │       ├── environment-config-provider.service.ts
│       │   │   │       ├── index.ts
│       │   │   │       ├── runtime-state-manager.service.spec.ts
│       │   │   │       └── runtime-state-manager.service.ts
│       │   │   ├── dependencies
│       │   │   │   └── index.ts
│       │   │   ├── execution
│       │   │   │   ├── executors
│       │   │   │   │   ├── base-executor.ts
│       │   │   │   │   ├── claude-executor.ts
│       │   │   │   │   └── executor-factory.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── services
│       │   │   │   │   └── executor-service.ts
│       │   │   │   └── types.ts
│       │   │   ├── git
│       │   │   │   ├── adapters
│       │   │   │   │   ├── git-adapter.test.ts
│       │   │   │   │   └── git-adapter.ts
│       │   │   │   ├── git-domain.ts
│       │   │   │   ├── index.ts
│       │   │   │   └── services
│       │   │   │       ├── branch-name-generator.spec.ts
│       │   │   │       ├── branch-name-generator.ts
│       │   │   │       ├── commit-message-generator.test.ts
│       │   │   │       ├── commit-message-generator.ts
│       │   │   │       ├── scope-detector.test.ts
│       │   │   │       ├── scope-detector.ts
│       │   │   │       ├── template-engine.test.ts
│       │   │   │       └── template-engine.ts
│       │   │   ├── integration
│       │   │   │   ├── clients
│       │   │   │   │   ├── index.ts
│       │   │   │   │   └── supabase-client.ts
│       │   │   │   ├── integration-domain.ts
│       │   │   │   └── services
│       │   │   │       ├── export.service.ts
│       │   │   │       ├── task-expansion.service.ts
│       │   │   │       └── task-retrieval.service.ts
│       │   │   ├── reports
│       │   │   │   ├── index.ts
│       │   │   │   ├── managers
│       │   │   │   │   └── complexity-report-manager.ts
│       │   │   │   └── types.ts
│       │   │   ├── storage
│       │   │   │   ├── adapters
│       │   │   │   │   ├── activity-logger.ts
│       │   │   │   │   ├── api-storage.ts
│       │   │   │   │   └── file-storage
│       │   │   │   │       ├── file-operations.ts
│       │   │   │   │       ├── file-storage.ts
│       │   │   │   │       ├── format-handler.ts
│       │   │   │   │       ├── index.ts
│       │   │   │   │       └── path-resolver.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── services
│       │   │   │   │   └── storage-factory.ts
│       │   │   │   └── utils
│       │   │   │       └── api-client.ts
│       │   │   ├── tasks
│       │   │   │   ├── entities
│       │   │   │   │   └── task.entity.ts
│       │   │   │   ├── parser
│       │   │   │   │   └── index.ts
│       │   │   │   ├── repositories
│       │   │   │   │   ├── supabase
│       │   │   │   │   │   ├── dependency-fetcher.ts
│       │   │   │   │   │   ├── index.ts
│       │   │   │   │   │   └── supabase-repository.ts
│       │   │   │   │   └── task-repository.interface.ts
│       │   │   │   ├── services
│       │   │   │   │   ├── preflight-checker.service.ts
│       │   │   │   │   ├── tag.service.ts
│       │   │   │   │   ├── task-execution-service.ts
│       │   │   │   │   ├── task-loader.service.ts
│       │   │   │   │   └── task-service.ts
│       │   │   │   └── tasks-domain.ts
│       │   │   ├── ui
│       │   │   │   └── index.ts
│       │   │   └── workflow
│       │   │       ├── managers
│       │   │       │   ├── workflow-state-manager.spec.ts
│       │   │       │   └── workflow-state-manager.ts
│       │   │       ├── orchestrators
│       │   │       │   ├── workflow-orchestrator.test.ts
│       │   │       │   └── workflow-orchestrator.ts
│       │   │       ├── services
│       │   │       │   ├── test-result-validator.test.ts
│       │   │       │   ├── test-result-validator.ts
│       │   │       │   ├── test-result-validator.types.ts
│       │   │       │   ├── workflow-activity-logger.ts
│       │   │       │   └── workflow.service.ts
│       │   │       ├── types.ts
│       │   │       └── workflow-domain.ts
│       │   ├── subpath-exports.test.ts
│       │   ├── tm-core.ts
│       │   └── utils
│       │       └── time.utils.ts
│       ├── tests
│       │   ├── auth
│       │   │   └── auth-refresh.test.ts
│       │   ├── integration
│       │   │   ├── auth-token-refresh.test.ts
│       │   │   ├── list-tasks.test.ts
│       │   │   └── storage
│       │   │       └── activity-logger.test.ts
│       │   ├── mocks
│       │   │   └── mock-provider.ts
│       │   ├── setup.ts
│       │   └── unit
│       │       ├── base-provider.test.ts
│       │       ├── executor.test.ts
│       │       └── smoke.test.ts
│       ├── tsconfig.json
│       └── vitest.config.ts
├── README-task-master.md
├── README.md
├── scripts
│   ├── create-worktree.sh
│   ├── dev.js
│   ├── init.js
│   ├── list-worktrees.sh
│   ├── modules
│   │   ├── ai-services-unified.js
│   │   ├── bridge-utils.js
│   │   ├── commands.js
│   │   ├── config-manager.js
│   │   ├── dependency-manager.js
│   │   ├── index.js
│   │   ├── prompt-manager.js
│   │   ├── supported-models.json
│   │   ├── sync-readme.js
│   │   ├── task-manager
│   │   │   ├── add-subtask.js
│   │   │   ├── add-task.js
│   │   │   ├── analyze-task-complexity.js
│   │   │   ├── clear-subtasks.js
│   │   │   ├── expand-all-tasks.js
│   │   │   ├── expand-task.js
│   │   │   ├── find-next-task.js
│   │   │   ├── generate-task-files.js
│   │   │   ├── is-task-dependent.js
│   │   │   ├── list-tasks.js
│   │   │   ├── migrate.js
│   │   │   ├── models.js
│   │   │   ├── move-task.js
│   │   │   ├── parse-prd
│   │   │   │   ├── index.js
│   │   │   │   ├── parse-prd-config.js
│   │   │   │   ├── parse-prd-helpers.js
│   │   │   │   ├── parse-prd-non-streaming.js
│   │   │   │   ├── parse-prd-streaming.js
│   │   │   │   └── parse-prd.js
│   │   │   ├── remove-subtask.js
│   │   │   ├── remove-task.js
│   │   │   ├── research.js
│   │   │   ├── response-language.js
│   │   │   ├── scope-adjustment.js
│   │   │   ├── set-task-status.js
│   │   │   ├── tag-management.js
│   │   │   ├── task-exists.js
│   │   │   ├── update-single-task-status.js
│   │   │   ├── update-subtask-by-id.js
│   │   │   ├── update-task-by-id.js
│   │   │   └── update-tasks.js
│   │   ├── task-manager.js
│   │   ├── ui.js
│   │   ├── update-config-tokens.js
│   │   ├── utils
│   │   │   ├── contextGatherer.js
│   │   │   ├── fuzzyTaskSearch.js
│   │   │   └── git-utils.js
│   │   └── utils.js
│   ├── task-complexity-report.json
│   ├── test-claude-errors.js
│   └── test-claude.js
├── sonar-project.properties
├── src
│   ├── ai-providers
│   │   ├── anthropic.js
│   │   ├── azure.js
│   │   ├── base-provider.js
│   │   ├── bedrock.js
│   │   ├── claude-code.js
│   │   ├── codex-cli.js
│   │   ├── gemini-cli.js
│   │   ├── google-vertex.js
│   │   ├── google.js
│   │   ├── grok-cli.js
│   │   ├── groq.js
│   │   ├── index.js
│   │   ├── lmstudio.js
│   │   ├── ollama.js
│   │   ├── openai-compatible.js
│   │   ├── openai.js
│   │   ├── openrouter.js
│   │   ├── perplexity.js
│   │   ├── xai.js
│   │   ├── zai-coding.js
│   │   └── zai.js
│   ├── constants
│   │   ├── commands.js
│   │   ├── paths.js
│   │   ├── profiles.js
│   │   ├── rules-actions.js
│   │   ├── task-priority.js
│   │   └── task-status.js
│   ├── profiles
│   │   ├── amp.js
│   │   ├── base-profile.js
│   │   ├── claude.js
│   │   ├── cline.js
│   │   ├── codex.js
│   │   ├── cursor.js
│   │   ├── gemini.js
│   │   ├── index.js
│   │   ├── kilo.js
│   │   ├── kiro.js
│   │   ├── opencode.js
│   │   ├── roo.js
│   │   ├── trae.js
│   │   ├── vscode.js
│   │   ├── windsurf.js
│   │   └── zed.js
│   ├── progress
│   │   ├── base-progress-tracker.js
│   │   ├── cli-progress-factory.js
│   │   ├── parse-prd-tracker.js
│   │   ├── progress-tracker-builder.js
│   │   └── tracker-ui.js
│   ├── prompts
│   │   ├── add-task.json
│   │   ├── analyze-complexity.json
│   │   ├── expand-task.json
│   │   ├── parse-prd.json
│   │   ├── README.md
│   │   ├── research.json
│   │   ├── schemas
│   │   │   ├── parameter.schema.json
│   │   │   ├── prompt-template.schema.json
│   │   │   ├── README.md
│   │   │   └── variant.schema.json
│   │   ├── update-subtask.json
│   │   ├── update-task.json
│   │   └── update-tasks.json
│   ├── provider-registry
│   │   └── index.js
│   ├── schemas
│   │   ├── add-task.js
│   │   ├── analyze-complexity.js
│   │   ├── base-schemas.js
│   │   ├── expand-task.js
│   │   ├── parse-prd.js
│   │   ├── registry.js
│   │   ├── update-subtask.js
│   │   ├── update-task.js
│   │   └── update-tasks.js
│   ├── task-master.js
│   ├── ui
│   │   ├── confirm.js
│   │   ├── indicators.js
│   │   └── parse-prd.js
│   └── utils
│       ├── asset-resolver.js
│       ├── create-mcp-config.js
│       ├── format.js
│       ├── getVersion.js
│       ├── logger-utils.js
│       ├── manage-gitignore.js
│       ├── path-utils.js
│       ├── profiles.js
│       ├── rule-transformer.js
│       ├── stream-parser.js
│       └── timeout-manager.js
├── test-clean-tags.js
├── test-config-manager.js
├── test-prd.txt
├── test-tag-functions.js
├── test-version-check-full.js
├── test-version-check.js
├── tests
│   ├── e2e
│   │   ├── e2e_helpers.sh
│   │   ├── parse_llm_output.cjs
│   │   ├── run_e2e.sh
│   │   ├── run_fallback_verification.sh
│   │   └── test_llm_analysis.sh
│   ├── fixtures
│   │   ├── .taskmasterconfig
│   │   ├── sample-claude-response.js
│   │   ├── sample-prd.txt
│   │   └── sample-tasks.js
│   ├── helpers
│   │   └── tool-counts.js
│   ├── integration
│   │   ├── claude-code-error-handling.test.js
│   │   ├── claude-code-optional.test.js
│   │   ├── cli
│   │   │   ├── commands.test.js
│   │   │   ├── complex-cross-tag-scenarios.test.js
│   │   │   └── move-cross-tag.test.js
│   │   ├── manage-gitignore.test.js
│   │   ├── mcp-server
│   │   │   └── direct-functions.test.js
│   │   ├── move-task-cross-tag.integration.test.js
│   │   ├── move-task-simple.integration.test.js
│   │   ├── profiles
│   │   │   ├── amp-init-functionality.test.js
│   │   │   ├── claude-init-functionality.test.js
│   │   │   ├── cline-init-functionality.test.js
│   │   │   ├── codex-init-functionality.test.js
│   │   │   ├── cursor-init-functionality.test.js
│   │   │   ├── gemini-init-functionality.test.js
│   │   │   ├── opencode-init-functionality.test.js
│   │   │   ├── roo-files-inclusion.test.js
│   │   │   ├── roo-init-functionality.test.js
│   │   │   ├── rules-files-inclusion.test.js
│   │   │   ├── trae-init-functionality.test.js
│   │   │   ├── vscode-init-functionality.test.js
│   │   │   └── windsurf-init-functionality.test.js
│   │   └── providers
│   │       └── temperature-support.test.js
│   ├── manual
│   │   ├── progress
│   │   │   ├── parse-prd-analysis.js
│   │   │   ├── test-parse-prd.js
│   │   │   └── TESTING_GUIDE.md
│   │   └── prompts
│   │       ├── prompt-test.js
│   │       └── README.md
│   ├── README.md
│   ├── setup.js
│   └── unit
│       ├── ai-providers
│       │   ├── base-provider.test.js
│       │   ├── claude-code.test.js
│       │   ├── codex-cli.test.js
│       │   ├── gemini-cli.test.js
│       │   ├── lmstudio.test.js
│       │   ├── mcp-components.test.js
│       │   ├── openai-compatible.test.js
│       │   ├── openai.test.js
│       │   ├── provider-registry.test.js
│       │   ├── zai-coding.test.js
│       │   ├── zai-provider.test.js
│       │   ├── zai-schema-introspection.test.js
│       │   └── zai.test.js
│       ├── ai-services-unified.test.js
│       ├── commands.test.js
│       ├── config-manager.test.js
│       ├── config-manager.test.mjs
│       ├── dependency-manager.test.js
│       ├── init.test.js
│       ├── initialize-project.test.js
│       ├── kebab-case-validation.test.js
│       ├── manage-gitignore.test.js
│       ├── mcp
│       │   └── tools
│       │       ├── __mocks__
│       │       │   └── move-task.js
│       │       ├── add-task.test.js
│       │       ├── analyze-complexity.test.js
│       │       ├── expand-all.test.js
│       │       ├── get-tasks.test.js
│       │       ├── initialize-project.test.js
│       │       ├── move-task-cross-tag-options.test.js
│       │       ├── move-task-cross-tag.test.js
│       │       ├── remove-task.test.js
│       │       └── tool-registration.test.js
│       ├── mcp-providers
│       │   ├── mcp-components.test.js
│       │   └── mcp-provider.test.js
│       ├── parse-prd.test.js
│       ├── profiles
│       │   ├── amp-integration.test.js
│       │   ├── claude-integration.test.js
│       │   ├── cline-integration.test.js
│       │   ├── codex-integration.test.js
│       │   ├── cursor-integration.test.js
│       │   ├── gemini-integration.test.js
│       │   ├── kilo-integration.test.js
│       │   ├── kiro-integration.test.js
│       │   ├── mcp-config-validation.test.js
│       │   ├── opencode-integration.test.js
│       │   ├── profile-safety-check.test.js
│       │   ├── roo-integration.test.js
│       │   ├── rule-transformer-cline.test.js
│       │   ├── rule-transformer-cursor.test.js
│       │   ├── rule-transformer-gemini.test.js
│       │   ├── rule-transformer-kilo.test.js
│       │   ├── rule-transformer-kiro.test.js
│       │   ├── rule-transformer-opencode.test.js
│       │   ├── rule-transformer-roo.test.js
│       │   ├── rule-transformer-trae.test.js
│       │   ├── rule-transformer-vscode.test.js
│       │   ├── rule-transformer-windsurf.test.js
│       │   ├── rule-transformer-zed.test.js
│       │   ├── rule-transformer.test.js
│       │   ├── selective-profile-removal.test.js
│       │   ├── subdirectory-support.test.js
│       │   ├── trae-integration.test.js
│       │   ├── vscode-integration.test.js
│       │   ├── windsurf-integration.test.js
│       │   └── zed-integration.test.js
│       ├── progress
│       │   └── base-progress-tracker.test.js
│       ├── prompt-manager.test.js
│       ├── prompts
│       │   ├── expand-task-prompt.test.js
│       │   └── prompt-migration.test.js
│       ├── scripts
│       │   └── modules
│       │       ├── commands
│       │       │   ├── move-cross-tag.test.js
│       │       │   └── README.md
│       │       ├── dependency-manager
│       │       │   ├── circular-dependencies.test.js
│       │       │   ├── cross-tag-dependencies.test.js
│       │       │   └── fix-dependencies-command.test.js
│       │       ├── task-manager
│       │       │   ├── add-subtask.test.js
│       │       │   ├── add-task.test.js
│       │       │   ├── analyze-task-complexity.test.js
│       │       │   ├── clear-subtasks.test.js
│       │       │   ├── complexity-report-tag-isolation.test.js
│       │       │   ├── expand-all-tasks.test.js
│       │       │   ├── expand-task.test.js
│       │       │   ├── find-next-task.test.js
│       │       │   ├── generate-task-files.test.js
│       │       │   ├── list-tasks.test.js
│       │       │   ├── models-baseurl.test.js
│       │       │   ├── move-task-cross-tag.test.js
│       │       │   ├── move-task.test.js
│       │       │   ├── parse-prd-schema.test.js
│       │       │   ├── parse-prd.test.js
│       │       │   ├── remove-subtask.test.js
│       │       │   ├── remove-task.test.js
│       │       │   ├── research.test.js
│       │       │   ├── scope-adjustment.test.js
│       │       │   ├── set-task-status.test.js
│       │       │   ├── setup.js
│       │       │   ├── update-single-task-status.test.js
│       │       │   ├── update-subtask-by-id.test.js
│       │       │   ├── update-task-by-id.test.js
│       │       │   └── update-tasks.test.js
│       │       ├── ui
│       │       │   └── cross-tag-error-display.test.js
│       │       └── utils-tag-aware-paths.test.js
│       ├── task-finder.test.js
│       ├── task-manager
│       │   ├── clear-subtasks.test.js
│       │   ├── move-task.test.js
│       │   ├── tag-boundary.test.js
│       │   └── tag-management.test.js
│       ├── task-master.test.js
│       ├── ui
│       │   └── indicators.test.js
│       ├── ui.test.js
│       ├── utils-strip-ansi.test.js
│       └── utils.test.js
├── tsconfig.json
├── tsdown.config.ts
├── turbo.json
└── update-task-migration-plan.md
```

# Files

--------------------------------------------------------------------------------
/apps/extension/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
# Change Log

## 0.26.0

### Minor Changes

- [#1403](https://github.com/eyaltoledano/claude-task-master/pull/1403) [`a381376`](https://github.com/eyaltoledano/claude-task-master/commit/a381376ba4925593bfb49d934693a83c804cd2a5) Thanks [@EDKarlsson](https://github.com/EDKarlsson)! - Updated readme mcp configuration example to not include package=task-master-ai.

## 0.26.0-rc.0

### Minor Changes

- [#1403](https://github.com/eyaltoledano/claude-task-master/pull/1403) [`a381376`](https://github.com/eyaltoledano/claude-task-master/commit/a381376ba4925593bfb49d934693a83c804cd2a5) Thanks [@EDKarlsson](https://github.com/EDKarlsson)! - Updated readme mcp configuration example to not include package=task-master-ai.

## 0.25.6

## 0.25.6-rc.0

### Patch Changes

- Updated dependencies [[`f12a16d`](https://github.com/eyaltoledano/claude-task-master/commit/f12a16d09649f62148515f11f616157c7d0bd2d5), [`3010b90`](https://github.com/eyaltoledano/claude-task-master/commit/3010b90d98f3a7d8636caa92fc33d6ee69d4bed0), [`2a910a4`](https://github.com/eyaltoledano/claude-task-master/commit/2a910a40bac375f9f61d797bf55597303d556b48), [`aaf903f`](https://github.com/eyaltoledano/claude-task-master/commit/aaf903ff2f606c779a22e9a4b240ab57b3683815), [`90e6bdc`](https://github.com/eyaltoledano/claude-task-master/commit/90e6bdcf1c59f65ad27fcdfe3b13b9dca7e77654)]:
  - [email protected]

## 0.25.5

### Patch Changes

- Updated dependencies [[`b43b7ce`](https://github.com/eyaltoledano/claude-task-master/commit/b43b7ce201625eee956fb2f8cd332f238bb78c21), [`aaacc3d`](https://github.com/eyaltoledano/claude-task-master/commit/aaacc3dae36247b4de72b2d2697f49e5df6d01e3), [`0079b7d`](https://github.com/eyaltoledano/claude-task-master/commit/0079b7defdad550811f704c470fdd01955d91d4d), [`0b2c696`](https://github.com/eyaltoledano/claude-task-master/commit/0b2c6967c4605c33a100cff16f6ce8ff09ad06f0), [`4f984f8`](https://github.com/eyaltoledano/claude-task-master/commit/4f984f8a6965da9f9c7edd60ddfd6560ac022917), [`7b5a7c4`](https://github.com/eyaltoledano/claude-task-master/commit/7b5a7c4495a68b782f7407fc5d0e0d3ae81f42f5), [`caee040`](https://github.com/eyaltoledano/claude-task-master/commit/caee040907f856d31a660171c9e6d966f23c632e), [`18aa416`](https://github.com/eyaltoledano/claude-task-master/commit/18aa416035f44345bde1c7321490345733a5d042), [`18aa416`](https://github.com/eyaltoledano/claude-task-master/commit/18aa416035f44345bde1c7321490345733a5d042), [`738ec51`](https://github.com/eyaltoledano/claude-task-master/commit/738ec51c049a295a12839b2dfddaf05e23b8fede), [`d67b81d`](https://github.com/eyaltoledano/claude-task-master/commit/d67b81d25ddd927fabb6f5deb368e8993519c541), [`b5fe723`](https://github.com/eyaltoledano/claude-task-master/commit/b5fe723f8ead928e9f2dbde13b833ee70ac3382d), [`2b69936`](https://github.com/eyaltoledano/claude-task-master/commit/2b69936ee7b34346d6de5175af20e077359e2e2a), [`986ac11`](https://github.com/eyaltoledano/claude-task-master/commit/986ac117aee00bcd3e6830a0f76e1ad6d10e0bca), [`20004a3`](https://github.com/eyaltoledano/claude-task-master/commit/20004a39ea848f747e1ff48981bfe176554e4055)]:
  - [email protected]

## 0.25.5-rc.0

### Patch Changes

- Updated dependencies [[`aaacc3d`](https://github.com/eyaltoledano/claude-task-master/commit/aaacc3dae36247b4de72b2d2697f49e5df6d01e3), [`0079b7d`](https://github.com/eyaltoledano/claude-task-master/commit/0079b7defdad550811f704c470fdd01955d91d4d), [`0b2c696`](https://github.com/eyaltoledano/claude-task-master/commit/0b2c6967c4605c33a100cff16f6ce8ff09ad06f0), [`18aa416`](https://github.com/eyaltoledano/claude-task-master/commit/18aa416035f44345bde1c7321490345733a5d042), [`18aa416`](https://github.com/eyaltoledano/claude-task-master/commit/18aa416035f44345bde1c7321490345733a5d042), [`738ec51`](https://github.com/eyaltoledano/claude-task-master/commit/738ec51c049a295a12839b2dfddaf05e23b8fede), [`d67b81d`](https://github.com/eyaltoledano/claude-task-master/commit/d67b81d25ddd927fabb6f5deb368e8993519c541), [`b5fe723`](https://github.com/eyaltoledano/claude-task-master/commit/b5fe723f8ead928e9f2dbde13b833ee70ac3382d), [`2b69936`](https://github.com/eyaltoledano/claude-task-master/commit/2b69936ee7b34346d6de5175af20e077359e2e2a), [`986ac11`](https://github.com/eyaltoledano/claude-task-master/commit/986ac117aee00bcd3e6830a0f76e1ad6d10e0bca), [`20004a3`](https://github.com/eyaltoledano/claude-task-master/commit/20004a39ea848f747e1ff48981bfe176554e4055)]:
  - [email protected]

## 0.25.4

### Patch Changes

- Updated dependencies [[`af53525`](https://github.com/eyaltoledano/claude-task-master/commit/af53525cbc660a595b67d4bb90d906911c71f45d)]:
  - [email protected]

## 0.25.3

### Patch Changes

- Updated dependencies [[`044a7bf`](https://github.com/eyaltoledano/claude-task-master/commit/044a7bfc98049298177bc655cf341d7a8b6a0011)]:
  - [email protected]

## 0.25.2

### Patch Changes

- Updated dependencies [[`f487736`](https://github.com/eyaltoledano/claude-task-master/commit/f487736670ef8c484059f676293777eabb249c9e), [`c911608`](https://github.com/eyaltoledano/claude-task-master/commit/c911608f60454253f4e024b57ca84e5a5a53f65c), [`1a18794`](https://github.com/eyaltoledano/claude-task-master/commit/1a1879483b86c118a4e46c02cbf4acebfcf6bcf9)]:
  - [email protected]

## 0.25.2-rc.1

### Patch Changes

- Updated dependencies [[`1a18794`](https://github.com/eyaltoledano/claude-task-master/commit/1a1879483b86c118a4e46c02cbf4acebfcf6bcf9)]:
  - [email protected]

## 0.25.2-rc.0

### Patch Changes

- Updated dependencies [[`f487736`](https://github.com/eyaltoledano/claude-task-master/commit/f487736670ef8c484059f676293777eabb249c9e)]:
  - [email protected]

## 0.25.0

### Minor Changes

- [#1200](https://github.com/eyaltoledano/claude-task-master/pull/1200) [`fce8414`](https://github.com/eyaltoledano/claude-task-master/commit/fce841490a9ebbf1801a42dd8a29397379cf1142) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Add "Start Task" button to VS Code extension for seamless Claude Code integration

  You can now click a "Start Task" button directly in the Task Master extension which will open a new terminal and automatically execute the task using Claude Code. This provides a seamless workflow from viewing tasks in the extension to implementing them without leaving VS Code.

- [#1201](https://github.com/eyaltoledano/claude-task-master/pull/1201) [`83af314`](https://github.com/eyaltoledano/claude-task-master/commit/83af314879fc0e563581161c60d2bd089899313e) Thanks [@losolosol](https://github.com/losolosol)! - Added a Start Build button to the VSCODE Task Properties Right Panel

### Patch Changes

- [#1229](https://github.com/eyaltoledano/claude-task-master/pull/1229) [`674d1f6`](https://github.com/eyaltoledano/claude-task-master/commit/674d1f6de7ea98116b61bdae6198bafe6c4e7c1a) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix MCP not connecting to new Taskmaster version

- Updated dependencies [[`4e12643`](https://github.com/eyaltoledano/claude-task-master/commit/4e126430a092fb54afb035514fb3d46115714f97), [`fce8414`](https://github.com/eyaltoledano/claude-task-master/commit/fce841490a9ebbf1801a42dd8a29397379cf1142), [`fce8414`](https://github.com/eyaltoledano/claude-task-master/commit/fce841490a9ebbf1801a42dd8a29397379cf1142), [`fce8414`](https://github.com/eyaltoledano/claude-task-master/commit/fce841490a9ebbf1801a42dd8a29397379cf1142), [`a621ff0`](https://github.com/eyaltoledano/claude-task-master/commit/a621ff05eafb51a147a9aabd7b37ddc0e45b0869), [`e6de285`](https://github.com/eyaltoledano/claude-task-master/commit/e6de285ceacb0a397e952a63435cd32a9c731515), [`fce8414`](https://github.com/eyaltoledano/claude-task-master/commit/fce841490a9ebbf1801a42dd8a29397379cf1142)]:
  - [email protected]

## 0.25.0-rc.0

### Minor Changes

- [#1201](https://github.com/eyaltoledano/claude-task-master/pull/1201) [`83af314`](https://github.com/eyaltoledano/claude-task-master/commit/83af314879fc0e563581161c60d2bd089899313e) Thanks [@losolosol](https://github.com/losolosol)! - Added a Start Build button to the VSCODE Task Properties Right Panel

### Patch Changes

- Updated dependencies [[`137ef36`](https://github.com/eyaltoledano/claude-task-master/commit/137ef362789a9cdfdb1925e35e0438c1fa6c69ee)]:
  - [email protected]

## 0.24.2

### Patch Changes

- Updated dependencies [[`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f), [`df26c65`](https://github.com/eyaltoledano/claude-task-master/commit/df26c65632000874a73504963b08f18c46283144), [`37af0f1`](https://github.com/eyaltoledano/claude-task-master/commit/37af0f191227a68d119b7f89a377bf932ee3ac66), [`c4f92f6`](https://github.com/eyaltoledano/claude-task-master/commit/c4f92f6a0aee3435c56eb8d27d9aa9204284833e), [`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f), [`4dad2fd`](https://github.com/eyaltoledano/claude-task-master/commit/4dad2fd613ceac56a65ae9d3c1c03092b8860ac9)]:
  - [email protected]

## 0.24.2-rc.1

### Patch Changes

- Updated dependencies [[`c4f92f6`](https://github.com/eyaltoledano/claude-task-master/commit/c4f92f6a0aee3435c56eb8d27d9aa9204284833e)]:
  - [email protected]

## 0.24.2-rc.0

### Patch Changes

- Updated dependencies [[`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f), [`37af0f1`](https://github.com/eyaltoledano/claude-task-master/commit/37af0f191227a68d119b7f89a377bf932ee3ac66), [`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f), [`4dad2fd`](https://github.com/eyaltoledano/claude-task-master/commit/4dad2fd613ceac56a65ae9d3c1c03092b8860ac9)]:
  - [email protected]

## 0.24.1

### Patch Changes

- Updated dependencies [[`8933557`](https://github.com/eyaltoledano/claude-task-master/commit/89335578ffffc65504b2055c0c85aa7521e5e79b), [`db720a9`](https://github.com/eyaltoledano/claude-task-master/commit/db720a954d390bb44838cd021b8813dde8f3d8de)]:
  - [email protected]

## 0.24.0

### Minor Changes

- [#1100](https://github.com/eyaltoledano/claude-task-master/pull/1100) [`30ca144`](https://github.com/eyaltoledano/claude-task-master/commit/30ca144231c36a6c63911f20adc225d38fb15a2f) Thanks [@vedovelli](https://github.com/vedovelli)! - Display current task ID on task details page

### Patch Changes

- Updated dependencies [[`04e11b5`](https://github.com/eyaltoledano/claude-task-master/commit/04e11b5e828597c0ba5b82ca7d5fb6f933e4f1e8), [`fc47714`](https://github.com/eyaltoledano/claude-task-master/commit/fc477143400fd11d953727bf1b4277af5ad308d1), [`782728f`](https://github.com/eyaltoledano/claude-task-master/commit/782728ff95aa2e3b766d48273b57f6c6753e8573), [`3dee60d`](https://github.com/eyaltoledano/claude-task-master/commit/3dee60dc3d566e3cff650accb30f994b8bb3a15e), [`e3ed4d7`](https://github.com/eyaltoledano/claude-task-master/commit/e3ed4d7c14b56894d7da675eb2b757423bea8f9d), [`04e11b5`](https://github.com/eyaltoledano/claude-task-master/commit/04e11b5e828597c0ba5b82ca7d5fb6f933e4f1e8), [`95640dc`](https://github.com/eyaltoledano/claude-task-master/commit/95640dcde87ce7879858c0a951399fb49f3b6397), [`311b243`](https://github.com/eyaltoledano/claude-task-master/commit/311b2433e23c771c8d3a4d3f5ac577302b8321e5)]:
  - [email protected]

## 0.24.0-rc.0

### Minor Changes

- [#1040](https://github.com/eyaltoledano/claude-task-master/pull/1040) [`fc47714`](https://github.com/eyaltoledano/claude-task-master/commit/fc477143400fd11d953727bf1b4277af5ad308d1) Thanks [@DomVidja](https://github.com/DomVidja)! - "Add Kilo Code profile integration with custom modes and MCP configuration"

- [#1100](https://github.com/eyaltoledano/claude-task-master/pull/1100) [`30ca144`](https://github.com/eyaltoledano/claude-task-master/commit/30ca144231c36a6c63911f20adc225d38fb15a2f) Thanks [@vedovelli](https://github.com/vedovelli)! - Display current task ID on task details page

### Patch Changes

- Updated dependencies [[`04e11b5`](https://github.com/eyaltoledano/claude-task-master/commit/04e11b5e828597c0ba5b82ca7d5fb6f933e4f1e8), [`fc47714`](https://github.com/eyaltoledano/claude-task-master/commit/fc477143400fd11d953727bf1b4277af5ad308d1), [`782728f`](https://github.com/eyaltoledano/claude-task-master/commit/782728ff95aa2e3b766d48273b57f6c6753e8573), [`3dee60d`](https://github.com/eyaltoledano/claude-task-master/commit/3dee60dc3d566e3cff650accb30f994b8bb3a15e), [`e3ed4d7`](https://github.com/eyaltoledano/claude-task-master/commit/e3ed4d7c14b56894d7da675eb2b757423bea8f9d), [`04e11b5`](https://github.com/eyaltoledano/claude-task-master/commit/04e11b5e828597c0ba5b82ca7d5fb6f933e4f1e8), [`95640dc`](https://github.com/eyaltoledano/claude-task-master/commit/95640dcde87ce7879858c0a951399fb49f3b6397), [`311b243`](https://github.com/eyaltoledano/claude-task-master/commit/311b2433e23c771c8d3a4d3f5ac577302b8321e5)]:
  - [email protected]

## 0.23.1

### Patch Changes

- [#1090](https://github.com/eyaltoledano/claude-task-master/pull/1090) [`a464e55`](https://github.com/eyaltoledano/claude-task-master/commit/a464e550b886ef81b09df80588fe5881bce83d93) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix issues with some users not being able to connect to Taskmaster MCP server while using the extension

- Updated dependencies [[`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b), [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7)]:
  - [email protected]

## 0.23.1-rc.1

### Patch Changes

- Updated dependencies [[`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b)]:
  - [email protected]

## 0.23.1-rc.0

### Patch Changes

- [#1090](https://github.com/eyaltoledano/claude-task-master/pull/1090) [`a464e55`](https://github.com/eyaltoledano/claude-task-master/commit/a464e550b886ef81b09df80588fe5881bce83d93) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix issues with some users not being able to connect to Taskmaster MCP server while using the extension

- Updated dependencies [[`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5), [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1), [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7)]:
  - [email protected]

## 0.23.0

### Minor Changes

- [#1064](https://github.com/eyaltoledano/claude-task-master/pull/1064) [`b82d858`](https://github.com/eyaltoledano/claude-task-master/commit/b82d858f81a1e702ad59d84d5ae8a2ca84359a83) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - 🎉 **Introducing TaskMaster Extension!**

  We're thrilled to launch the first version of our Code extension, bringing the power of TaskMaster directly into your favorite code editor. While this is our initial release and we've kept things focused, it already packs powerful features to supercharge your development workflow.

  ## ✨ Key Features

  ### 📋 Visual Task Management
  - **Kanban Board View**: Visualize all your tasks in an intuitive board layout directly in VS Code
  - **Drag & Drop**: Easily change task status by dragging cards between columns
  - **Real-time Updates**: See changes instantly as you work through your project

  ### 🏷️ Multi-Context Support
  - **Tag Switching**: Seamlessly switch between different project contexts/tags
  - **Isolated Workflows**: Keep different features or experiments organized separately

  ### 🤖 AI-Powered Task Updates
  - **Smart Updates**: Use TaskMaster's AI capabilities to update tasks and subtasks
  - **Context-Aware**: Leverages your existing TaskMaster configuration and models

  ### 📊 Rich Task Information
  - **Complexity Scores**: See task complexity ratings at a glance
  - **Subtask Visualization**: Expand tasks to view and manage subtasks
  - **Dependency Graphs**: Understand task relationships and dependencies visually

  ### ⚙️ Configuration Management
  - **Visual Config Editor**: View and understand your `.taskmaster/config.json` settings
  - **Easy Access**: No more manual JSON editing for common configuration tasks

  ### 🚀 Quick Actions
  - **Status Updates**: Change task status with a single click
  - **Task Details**: Access full task information without leaving VS Code
  - **Integrated Commands**: All TaskMaster commands available through the command palette

  ## 🎯 What's Next?

  This is just the beginning! We wanted to get a solid foundation into your hands quickly. The extension will evolve rapidly with your feedback, adding more advanced features, better visualizations, and deeper integration with your development workflow.

  Thank you for being part of the TaskMaster journey. Your workflow has never looked better! 🚀

## 0.23.0-rc.1

### Minor Changes

- [#1064](https://github.com/eyaltoledano/claude-task-master/pull/1064) [`b82d858`](https://github.com/eyaltoledano/claude-task-master/commit/b82d858f81a1e702ad59d84d5ae8a2ca84359a83) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - 🎉 **Introducing TaskMaster Extension!**

  We're thrilled to launch the first version of our Code extension, bringing the power of TaskMaster directly into your favorite code editor. While this is our initial release and we've kept things focused, it already packs powerful features to supercharge your development workflow.

  ## ✨ Key Features

  ### 📋 Visual Task Management
  - **Kanban Board View**: Visualize all your tasks in an intuitive board layout directly in VS Code
  - **Drag & Drop**: Easily change task status by dragging cards between columns
  - **Real-time Updates**: See changes instantly as you work through your project

  ### 🏷️ Multi-Context Support
  - **Tag Switching**: Seamlessly switch between different project contexts/tags
  - **Isolated Workflows**: Keep different features or experiments organized separately

  ### 🤖 AI-Powered Task Updates
  - **Smart Updates**: Use TaskMaster's AI capabilities to update tasks and subtasks
  - **Context-Aware**: Leverages your existing TaskMaster configuration and models

  ### 📊 Rich Task Information
  - **Complexity Scores**: See task complexity ratings at a glance
  - **Subtask Visualization**: Expand tasks to view and manage subtasks
  - **Dependency Graphs**: Understand task relationships and dependencies visually

  ### ⚙️ Configuration Management
  - **Visual Config Editor**: View and understand your `.taskmaster/config.json` settings
  - **Easy Access**: No more manual JSON editing for common configuration tasks

  ### 🚀 Quick Actions
  - **Status Updates**: Change task status with a single click
  - **Task Details**: Access full task information without leaving VS Code
  - **Integrated Commands**: All TaskMaster commands available through the command palette

  ## 🎯 What's Next?

  This is just the beginning! We wanted to get a solid foundation into your hands quickly. The extension will evolve rapidly with your feedback, adding more advanced features, better visualizations, and deeper integration with your development workflow.

  Thank you for being part of the TaskMaster journey. Your workflow has never looked better! 🚀

## 0.23.0-rc.0

### Minor Changes

- [#997](https://github.com/eyaltoledano/claude-task-master/pull/997) [`64302dc`](https://github.com/eyaltoledano/claude-task-master/commit/64302dc1918f673fcdac05b29411bf76ffe93505) Thanks [@DavidMaliglowka](https://github.com/DavidMaliglowka)! - 🎉 **Introducing TaskMaster Extension!**

  We're thrilled to launch the first version of our Code extension, bringing the power of TaskMaster directly into your favorite code editor. While this is our initial release and we've kept things focused, it already packs powerful features to supercharge your development workflow.

  ## ✨ Key Features

  ### 📋 Visual Task Management
  - **Kanban Board View**: Visualize all your tasks in an intuitive board layout directly in VS Code
  - **Drag & Drop**: Easily change task status by dragging cards between columns
  - **Real-time Updates**: See changes instantly as you work through your project

  ### 🏷️ Multi-Context Support
  - **Tag Switching**: Seamlessly switch between different project contexts/tags
  - **Isolated Workflows**: Keep different features or experiments organized separately

  ### 🤖 AI-Powered Task Updates
  - **Smart Updates**: Use TaskMaster's AI capabilities to update tasks and subtasks
  - **Context-Aware**: Leverages your existing TaskMaster configuration and models

  ### 📊 Rich Task Information
  - **Complexity Scores**: See task complexity ratings at a glance
  - **Subtask Visualization**: Expand tasks to view and manage subtasks
  - **Dependency Graphs**: Understand task relationships and dependencies visually

  ### ⚙️ Configuration Management
  - **Visual Config Editor**: View and understand your `.taskmaster/config.json` settings
  - **Easy Access**: No more manual JSON editing for common configuration tasks

  ### 🚀 Quick Actions
  - **Status Updates**: Change task status with a single click
  - **Task Details**: Access full task information without leaving VS Code
  - **Integrated Commands**: All TaskMaster commands available through the command palette

  ## 🎯 What's Next?

  This is just the beginning! We wanted to get a solid foundation into your hands quickly. The extension will evolve rapidly with your feedback, adding more advanced features, better visualizations, and deeper integration with your development workflow.

  Thank you for being part of the TaskMaster journey. Your workflow has never looked better! 🚀

```

--------------------------------------------------------------------------------
/tests/integration/cli/move-cross-tag.test.js:
--------------------------------------------------------------------------------

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

// --- Define mock functions ---
const mockMoveTasksBetweenTags = jest.fn();
const mockMoveTask = jest.fn();
const mockGenerateTaskFiles = jest.fn();
const mockLog = jest.fn();

// --- Setup mocks using unstable_mockModule ---
jest.unstable_mockModule(
	'../../../scripts/modules/task-manager/move-task.js',
	() => ({
		default: mockMoveTask,
		moveTasksBetweenTags: mockMoveTasksBetweenTags
	})
);

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

jest.unstable_mockModule('../../../scripts/modules/utils.js', () => ({
	log: mockLog,
	readJSON: jest.fn(),
	writeJSON: jest.fn(),
	findProjectRoot: jest.fn(() => '/test/project/root'),
	getCurrentTag: jest.fn(() => 'master')
}));

// --- Mock chalk for consistent output formatting ---
const mockChalk = {
	red: jest.fn((text) => text),
	yellow: jest.fn((text) => text),
	blue: jest.fn((text) => text),
	green: jest.fn((text) => text),
	gray: jest.fn((text) => text),
	dim: jest.fn((text) => text),
	bold: {
		cyan: jest.fn((text) => text),
		white: jest.fn((text) => text),
		red: jest.fn((text) => text)
	},
	cyan: {
		bold: jest.fn((text) => text)
	},
	white: {
		bold: jest.fn((text) => text)
	}
};

jest.unstable_mockModule('chalk', () => ({
	default: mockChalk
}));

// --- Import modules (AFTER mock setup) ---
let moveTaskModule, generateTaskFilesModule, utilsModule, chalk;

describe('Cross-Tag Move CLI Integration', () => {
	// Setup dynamic imports before tests run
	beforeAll(async () => {
		moveTaskModule = await import(
			'../../../scripts/modules/task-manager/move-task.js'
		);
		generateTaskFilesModule = await import(
			'../../../scripts/modules/task-manager/generate-task-files.js'
		);
		utilsModule = await import('../../../scripts/modules/utils.js');
		chalk = (await import('chalk')).default;
	});

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

	// Helper function to capture console output and process.exit calls
	function captureConsoleAndExit() {
		const originalConsoleError = console.error;
		const originalConsoleLog = console.log;
		const originalProcessExit = process.exit;

		const errorMessages = [];
		const logMessages = [];
		const exitCodes = [];

		console.error = jest.fn((...args) => {
			errorMessages.push(args.join(' '));
		});

		console.log = jest.fn((...args) => {
			logMessages.push(args.join(' '));
		});

		process.exit = jest.fn((code) => {
			exitCodes.push(code);
		});

		return {
			errorMessages,
			logMessages,
			exitCodes,
			restore: () => {
				console.error = originalConsoleError;
				console.log = originalConsoleLog;
				process.exit = originalProcessExit;
			}
		};
	}

	// --- Replicate the move command action handler logic from commands.js ---
	async function moveAction(options) {
		const sourceId = options.from;
		const destinationId = options.to;
		const fromTag = options.fromTag;
		const toTag = options.toTag;
		const withDependencies = options.withDependencies;
		const ignoreDependencies = options.ignoreDependencies;
		const force = options.force;

		// Get the source tag - fallback to current tag if not provided
		const sourceTag = fromTag || utilsModule.getCurrentTag();

		// Check if this is a cross-tag move (different tags)
		const isCrossTagMove = sourceTag && toTag && sourceTag !== toTag;

		if (isCrossTagMove) {
			// Cross-tag move logic
			if (!sourceId) {
				const error = new Error(
					'--from parameter is required for cross-tag moves'
				);
				console.error(chalk.red(`Error: ${error.message}`));
				throw error;
			}

			const taskIds = sourceId.split(',').map((id) => parseInt(id.trim(), 10));

			// Validate parsed task IDs
			for (let i = 0; i < taskIds.length; i++) {
				if (isNaN(taskIds[i])) {
					const error = new Error(
						`Invalid task ID at position ${i + 1}: "${sourceId.split(',')[i].trim()}" is not a valid number`
					);
					console.error(chalk.red(`Error: ${error.message}`));
					throw error;
				}
			}

			const tasksPath = path.join(
				utilsModule.findProjectRoot(),
				'.taskmaster',
				'tasks',
				'tasks.json'
			);

			try {
				const result = await moveTaskModule.moveTasksBetweenTags(
					tasksPath,
					taskIds,
					sourceTag,
					toTag,
					{
						withDependencies,
						ignoreDependencies
					}
				);

				console.log(chalk.green('Successfully moved task(s) between tags'));

				// Print advisory tips when present
				if (result && Array.isArray(result.tips) && result.tips.length > 0) {
					console.log('Next Steps:');
					result.tips.forEach((t) => console.log(`  • ${t}`));
				}

				// Generate task files for both tags
				await generateTaskFilesModule.default(
					tasksPath,
					path.dirname(tasksPath),
					{ tag: sourceTag }
				);
				await generateTaskFilesModule.default(
					tasksPath,
					path.dirname(tasksPath),
					{ tag: toTag }
				);
			} catch (error) {
				console.error(chalk.red(`Error: ${error.message}`));
				// Print ID collision guidance similar to CLI help block
				if (
					typeof error?.message === 'string' &&
					error.message.includes('already exists in target tag')
				) {
					console.log('');
					console.log('Conflict: ID already exists in target tag');
					console.log(
						'  • Choose a different target tag without conflicting IDs'
					);
					console.log('  • Move a different set of IDs (avoid existing ones)');
					console.log(
						'  • If needed, move within-tag to a new ID first, then cross-tag move'
					);
				}
				throw error;
			}
		} else {
			// Handle case where both tags are provided but are the same
			if (sourceTag && toTag && sourceTag === toTag) {
				// If both tags are the same and we have destinationId, treat as within-tag move
				if (destinationId) {
					if (!sourceId) {
						const error = new Error(
							'Both --from and --to parameters are required for within-tag moves'
						);
						console.error(chalk.red(`Error: ${error.message}`));
						throw error;
					}

					// Call the existing moveTask function for within-tag moves
					try {
						await moveTaskModule.default(sourceId, destinationId);
						console.log(chalk.green('Successfully moved task'));
					} catch (error) {
						console.error(chalk.red(`Error: ${error.message}`));
						throw error;
					}
				} else {
					// Same tags but no destinationId - this is an error
					const error = new Error(
						`Source and target tags are the same ("${sourceTag}") but no destination specified`
					);
					console.error(chalk.red(`Error: ${error.message}`));
					console.log(
						chalk.yellow(
							'For within-tag moves, use: task-master move --from=<sourceId> --to=<destinationId>'
						)
					);
					console.log(
						chalk.yellow(
							'For cross-tag moves, use different tags: task-master move --from=<sourceId> --from-tag=<sourceTag> --to-tag=<targetTag>'
						)
					);
					throw error;
				}
			} else {
				// Within-tag move logic (existing functionality)
				if (!sourceId || !destinationId) {
					const error = new Error(
						'Both --from and --to parameters are required for within-tag moves'
					);
					console.error(chalk.red(`Error: ${error.message}`));
					throw error;
				}

				// Call the existing moveTask function for within-tag moves
				try {
					await moveTaskModule.default(sourceId, destinationId);
					console.log(chalk.green('Successfully moved task'));
				} catch (error) {
					console.error(chalk.red(`Error: ${error.message}`));
					throw error;
				}
			}
		}
	}

	it('should move task without dependencies successfully', async () => {
		// Mock successful cross-tag move
		mockMoveTasksBetweenTags.mockResolvedValue(undefined);
		mockGenerateTaskFiles.mockResolvedValue(undefined);

		const options = {
			from: '2',
			fromTag: 'backlog',
			toTag: 'in-progress'
		};

		await moveAction(options);

		expect(mockMoveTasksBetweenTags).toHaveBeenCalledWith(
			expect.stringContaining('tasks.json'),
			[2],
			'backlog',
			'in-progress',
			{
				withDependencies: undefined,
				ignoreDependencies: undefined
			}
		);
	});

	it('should fail to move task with cross-tag dependencies', async () => {
		// Mock dependency conflict error
		mockMoveTasksBetweenTags.mockRejectedValue(
			new Error('Cannot move task due to cross-tag dependency conflicts')
		);

		const options = {
			from: '1',
			fromTag: 'backlog',
			toTag: 'in-progress'
		};

		const { errorMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'Cannot move task due to cross-tag dependency conflicts'
		);

		expect(mockMoveTasksBetweenTags).toHaveBeenCalled();
		expect(
			errorMessages.some((msg) =>
				msg.includes('cross-tag dependency conflicts')
			)
		).toBe(true);

		restore();
	});

	it('should move task with dependencies when --with-dependencies is used', async () => {
		// Mock successful cross-tag move with dependencies
		mockMoveTasksBetweenTags.mockResolvedValue(undefined);
		mockGenerateTaskFiles.mockResolvedValue(undefined);

		const options = {
			from: '1',
			fromTag: 'backlog',
			toTag: 'in-progress',
			withDependencies: true
		};

		await moveAction(options);

		expect(mockMoveTasksBetweenTags).toHaveBeenCalledWith(
			expect.stringContaining('tasks.json'),
			[1],
			'backlog',
			'in-progress',
			{
				withDependencies: true,
				ignoreDependencies: undefined
			}
		);
	});

	it('should break dependencies when --ignore-dependencies is used', async () => {
		// Mock successful cross-tag move with dependency breaking
		mockMoveTasksBetweenTags.mockResolvedValue(undefined);
		mockGenerateTaskFiles.mockResolvedValue(undefined);

		const options = {
			from: '1',
			fromTag: 'backlog',
			toTag: 'in-progress',
			ignoreDependencies: true
		};

		await moveAction(options);

		expect(mockMoveTasksBetweenTags).toHaveBeenCalledWith(
			expect.stringContaining('tasks.json'),
			[1],
			'backlog',
			'in-progress',
			{
				withDependencies: undefined,
				ignoreDependencies: true
			}
		);
	});

	it('should create target tag if it does not exist', async () => {
		// Mock successful cross-tag move to new tag
		mockMoveTasksBetweenTags.mockResolvedValue(undefined);
		mockGenerateTaskFiles.mockResolvedValue(undefined);

		const options = {
			from: '2',
			fromTag: 'backlog',
			toTag: 'new-tag'
		};

		await moveAction(options);

		expect(mockMoveTasksBetweenTags).toHaveBeenCalledWith(
			expect.stringContaining('tasks.json'),
			[2],
			'backlog',
			'new-tag',
			{
				withDependencies: undefined,
				ignoreDependencies: undefined
			}
		);
	});

	it('should fail to move a subtask directly', async () => {
		// Mock subtask movement error
		mockMoveTasksBetweenTags.mockRejectedValue(
			new Error(
				'Cannot move subtasks directly between tags. Please promote the subtask to a full task first.'
			)
		);

		const options = {
			from: '1.2',
			fromTag: 'backlog',
			toTag: 'in-progress'
		};

		const { errorMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'Cannot move subtasks directly between tags. Please promote the subtask to a full task first.'
		);

		expect(mockMoveTasksBetweenTags).toHaveBeenCalled();
		expect(errorMessages.some((msg) => msg.includes('subtasks directly'))).toBe(
			true
		);

		restore();
	});

	it('should provide helpful error messages for dependency conflicts', async () => {
		// Mock dependency conflict with detailed error
		mockMoveTasksBetweenTags.mockRejectedValue(
			new Error(
				'Cross-tag dependency conflicts detected. Task 1 depends on Task 2 which is in a different tag.'
			)
		);

		const options = {
			from: '1',
			fromTag: 'backlog',
			toTag: 'in-progress'
		};

		const { errorMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'Cross-tag dependency conflicts detected. Task 1 depends on Task 2 which is in a different tag.'
		);

		expect(mockMoveTasksBetweenTags).toHaveBeenCalled();
		expect(
			errorMessages.some((msg) =>
				msg.includes('Cross-tag dependency conflicts detected')
			)
		).toBe(true);

		restore();
	});

	it('should print advisory tips when result.tips are returned (ignore-dependencies)', async () => {
		const { errorMessages, logMessages, restore } = captureConsoleAndExit();
		try {
			// Arrange: mock move to return tips
			mockMoveTasksBetweenTags.mockResolvedValue({
				message: 'ok',
				tips: [
					'Run "task-master validate-dependencies" to check for dependency issues.',
					'Run "task-master fix-dependencies" to automatically repair dangling dependencies.'
				]
			});

			await moveAction({
				from: '2',
				fromTag: 'backlog',
				toTag: 'in-progress',
				ignoreDependencies: true
			});

			const joined = logMessages.join('\n');
			expect(joined).toContain('Next Steps');
			expect(joined).toContain('validate-dependencies');
			expect(joined).toContain('fix-dependencies');
		} finally {
			restore();
		}
	});

	it('should print ID collision suggestions when target already has the ID', async () => {
		const { errorMessages, logMessages, restore } = captureConsoleAndExit();
		try {
			// Arrange: mock move to throw collision
			const err = new Error(
				'Task 1 already exists in target tag "in-progress"'
			);
			mockMoveTasksBetweenTags.mockRejectedValue(err);

			await expect(
				moveAction({ from: '1', fromTag: 'backlog', toTag: 'in-progress' })
			).rejects.toThrow('already exists in target tag');

			const joined = logMessages.join('\n');
			expect(joined).toContain('Conflict: ID already exists in target tag');
			expect(joined).toContain('different target tag');
			expect(joined).toContain('different set of IDs');
			expect(joined).toContain('within-tag');
		} finally {
			restore();
		}
	});

	it('should handle same tag error correctly', async () => {
		const options = {
			from: '1',
			fromTag: 'backlog',
			toTag: 'backlog' // Same tag but no destination
		};

		const { errorMessages, logMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'Source and target tags are the same ("backlog") but no destination specified'
		);

		expect(
			errorMessages.some((msg) =>
				msg.includes(
					'Source and target tags are the same ("backlog") but no destination specified'
				)
			)
		).toBe(true);
		expect(
			logMessages.some((msg) => msg.includes('For within-tag moves'))
		).toBe(true);
		expect(logMessages.some((msg) => msg.includes('For cross-tag moves'))).toBe(
			true
		);

		restore();
	});

	it('should use current tag when --from-tag is not provided', async () => {
		// Mock successful move with current tag fallback
		mockMoveTasksBetweenTags.mockResolvedValue({
			message: 'Successfully moved task(s) between tags'
		});

		// Mock getCurrentTag to return 'master'
		utilsModule.getCurrentTag.mockReturnValue('master');

		// Simulate command: task-master move --from=1 --to-tag=in-progress
		// (no --from-tag provided, should use current tag 'master')
		await moveAction({
			from: '1',
			toTag: 'in-progress',
			withDependencies: false,
			ignoreDependencies: false
			// fromTag is intentionally not provided to test fallback
		});

		// Verify that moveTasksBetweenTags was called with 'master' as source tag
		expect(mockMoveTasksBetweenTags).toHaveBeenCalledWith(
			expect.stringContaining('.taskmaster/tasks/tasks.json'),
			[1], // parseInt converts string to number
			'master', // Should use current tag as fallback
			'in-progress',
			{
				withDependencies: false,
				ignoreDependencies: false
			}
		);

		// Verify that generateTaskFiles was called for both tags
		expect(generateTaskFilesModule.default).toHaveBeenCalledWith(
			expect.stringContaining('.taskmaster/tasks/tasks.json'),
			expect.stringContaining('.taskmaster/tasks'),
			{ tag: 'master' }
		);
		expect(generateTaskFilesModule.default).toHaveBeenCalledWith(
			expect.stringContaining('.taskmaster/tasks/tasks.json'),
			expect.stringContaining('.taskmaster/tasks'),
			{ tag: 'in-progress' }
		);
	});

	it('should move multiple tasks with comma-separated IDs successfully', async () => {
		// Mock successful cross-tag move for multiple tasks
		mockMoveTasksBetweenTags.mockResolvedValue(undefined);
		mockGenerateTaskFiles.mockResolvedValue(undefined);

		const options = {
			from: '1,2,3',
			fromTag: 'backlog',
			toTag: 'in-progress'
		};

		await moveAction(options);

		expect(mockMoveTasksBetweenTags).toHaveBeenCalledWith(
			expect.stringContaining('tasks.json'),
			[1, 2, 3], // Should parse comma-separated string to array of integers
			'backlog',
			'in-progress',
			{
				withDependencies: undefined,
				ignoreDependencies: undefined
			}
		);

		// Verify task files are generated for both tags
		expect(mockGenerateTaskFiles).toHaveBeenCalledTimes(2);
		expect(mockGenerateTaskFiles).toHaveBeenCalledWith(
			expect.stringContaining('tasks.json'),
			expect.stringContaining('.taskmaster/tasks'),
			{ tag: 'backlog' }
		);
		expect(mockGenerateTaskFiles).toHaveBeenCalledWith(
			expect.stringContaining('tasks.json'),
			expect.stringContaining('.taskmaster/tasks'),
			{ tag: 'in-progress' }
		);
	});

	// Note: --force flag is no longer supported for cross-tag moves

	it('should fail when invalid task ID is provided', async () => {
		const options = {
			from: '1,abc,3', // Invalid ID in middle
			fromTag: 'backlog',
			toTag: 'in-progress'
		};

		const { errorMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'Invalid task ID at position 2: "abc" is not a valid number'
		);

		expect(
			errorMessages.some((msg) => msg.includes('Invalid task ID at position 2'))
		).toBe(true);

		restore();
	});

	it('should fail when first task ID is invalid', async () => {
		const options = {
			from: 'abc,2,3', // Invalid ID at start
			fromTag: 'backlog',
			toTag: 'in-progress'
		};

		const { errorMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'Invalid task ID at position 1: "abc" is not a valid number'
		);

		expect(
			errorMessages.some((msg) => msg.includes('Invalid task ID at position 1'))
		).toBe(true);

		restore();
	});

	it('should fail when last task ID is invalid', async () => {
		const options = {
			from: '1,2,xyz', // Invalid ID at end
			fromTag: 'backlog',
			toTag: 'in-progress'
		};

		const { errorMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'Invalid task ID at position 3: "xyz" is not a valid number'
		);

		expect(
			errorMessages.some((msg) => msg.includes('Invalid task ID at position 3'))
		).toBe(true);

		restore();
	});

	it('should fail when single invalid task ID is provided', async () => {
		const options = {
			from: 'invalid',
			fromTag: 'backlog',
			toTag: 'in-progress'
		};

		const { errorMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'Invalid task ID at position 1: "invalid" is not a valid number'
		);

		expect(
			errorMessages.some((msg) => msg.includes('Invalid task ID at position 1'))
		).toBe(true);

		restore();
	});

	// Note: --force combinations removed

	// Note: --force combinations removed

	// Note: --force combinations removed

	it('should handle whitespace in comma-separated task IDs', async () => {
		// Mock successful cross-tag move with whitespace
		mockMoveTasksBetweenTags.mockResolvedValue(undefined);
		mockGenerateTaskFiles.mockResolvedValue(undefined);

		const options = {
			from: ' 1 , 2 , 3 ', // Whitespace around IDs and commas
			fromTag: 'backlog',
			toTag: 'in-progress'
		};

		await moveAction(options);

		expect(mockMoveTasksBetweenTags).toHaveBeenCalledWith(
			expect.stringContaining('tasks.json'),
			[1, 2, 3], // Should trim whitespace and parse as integers
			'backlog',
			'in-progress',
			{
				withDependencies: undefined,
				ignoreDependencies: undefined,
				force: undefined
			}
		);
	});

	it('should fail when --from parameter is missing for cross-tag move', async () => {
		const options = {
			fromTag: 'backlog',
			toTag: 'in-progress'
			// from is intentionally missing
		};

		const { errorMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'--from parameter is required for cross-tag moves'
		);

		expect(
			errorMessages.some((msg) =>
				msg.includes('--from parameter is required for cross-tag moves')
			)
		).toBe(true);

		restore();
	});

	it('should fail when both --from and --to are missing for within-tag move', async () => {
		const options = {
			// Both from and to are missing for within-tag move
		};

		const { errorMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'Both --from and --to parameters are required for within-tag moves'
		);

		expect(
			errorMessages.some((msg) =>
				msg.includes(
					'Both --from and --to parameters are required for within-tag moves'
				)
			)
		).toBe(true);

		restore();
	});

	it('should handle within-tag move when only --from is provided', async () => {
		// Mock successful within-tag move
		mockMoveTask.mockResolvedValue(undefined);

		const options = {
			from: '1',
			to: '2'
			// No tags specified, should use within-tag logic
		};

		await moveAction(options);

		expect(mockMoveTask).toHaveBeenCalledWith('1', '2');
		expect(mockMoveTasksBetweenTags).not.toHaveBeenCalled();
	});

	it('should handle within-tag move when both tags are the same', async () => {
		// Mock successful within-tag move
		mockMoveTask.mockResolvedValue(undefined);

		const options = {
			from: '1',
			to: '2',
			fromTag: 'master',
			toTag: 'master' // Same tag, should use within-tag logic
		};

		await moveAction(options);

		expect(mockMoveTask).toHaveBeenCalledWith('1', '2');
		expect(mockMoveTasksBetweenTags).not.toHaveBeenCalled();
	});

	it('should fail when both tags are the same but no destination is provided', async () => {
		const options = {
			from: '1',
			fromTag: 'master',
			toTag: 'master' // Same tag but no destination
		};

		const { errorMessages, logMessages, restore } = captureConsoleAndExit();

		await expect(moveAction(options)).rejects.toThrow(
			'Source and target tags are the same ("master") but no destination specified'
		);

		expect(
			errorMessages.some((msg) =>
				msg.includes(
					'Source and target tags are the same ("master") but no destination specified'
				)
			)
		).toBe(true);
		expect(
			logMessages.some((msg) => msg.includes('For within-tag moves'))
		).toBe(true);
		expect(logMessages.some((msg) => msg.includes('For cross-tag moves'))).toBe(
			true
		);

		restore();
	});
});

```

--------------------------------------------------------------------------------
/scripts/modules/supported-models.json:
--------------------------------------------------------------------------------

```json
{
	"anthropic": [
		{
			"id": "claude-sonnet-4-20250514",
			"swe_score": 0.727,
			"cost_per_1m_tokens": {
				"input": 3.0,
				"output": 15.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 64000,
			"supported": true
		},
		{
			"id": "claude-opus-4-20250514",
			"swe_score": 0.725,
			"cost_per_1m_tokens": {
				"input": 15.0,
				"output": 75.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 32000,
			"supported": true
		},
		{
			"id": "claude-3-7-sonnet-20250219",
			"swe_score": 0.623,
			"cost_per_1m_tokens": {
				"input": 3.0,
				"output": 15.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 120000,
			"supported": true
		},
		{
			"id": "claude-3-5-sonnet-20241022",
			"swe_score": 0.49,
			"cost_per_1m_tokens": {
				"input": 3.0,
				"output": 15.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 8192,
			"supported": true
		},
		{
			"id": "claude-sonnet-4-5-20250929",
			"swe_score": 0.73,
			"cost_per_1m_tokens": {
				"input": 3.0,
				"output": 15.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 64000,
			"supported": true
		},
		{
			"id": "claude-haiku-4-5-20251001",
			"swe_score": 0.45,
			"cost_per_1m_tokens": {
				"input": 1.0,
				"output": 5.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 200000,
			"supported": true
		}
	],
	"claude-code": [
		{
			"id": "opus",
			"swe_score": 0.725,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 32000,
			"supported": true
		},
		{
			"id": "sonnet",
			"swe_score": 0.727,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 64000,
			"supported": true
		},
		{
			"id": "haiku",
			"swe_score": 0.45,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 200000,
			"supported": true
		}
	],
	"codex-cli": [
		{
			"id": "gpt-5",
			"swe_score": 0.749,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 128000,
			"supported": true
		},
		{
			"id": "gpt-5-codex",
			"swe_score": 0.749,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 128000,
			"supported": true
		}
	],
	"mcp": [
		{
			"id": "mcp-sampling",
			"swe_score": null,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 100000,
			"supported": true
		}
	],
	"gemini-cli": [
		{
			"id": "gemini-3-pro-preview",
			"swe_score": 0.762,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 65536,
			"supported": true
		},
		{
			"id": "gemini-2.5-pro",
			"swe_score": 0.72,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 65536,
			"supported": true
		},
		{
			"id": "gemini-2.5-flash",
			"swe_score": 0.71,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 65536,
			"supported": true
		}
	],
	"grok-cli": [
		{
			"id": "grok-4-latest",
			"name": "Grok 4 Latest",
			"swe_score": 0.7,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 131072,
			"supported": true
		},
		{
			"id": "grok-3-latest",
			"name": "Grok 3 Latest",
			"swe_score": 0.65,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 131072,
			"supported": true
		},
		{
			"id": "grok-3-fast",
			"name": "Grok 3 Fast",
			"swe_score": 0.6,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 131072,
			"supported": true
		},
		{
			"id": "grok-3-mini-fast",
			"name": "Grok 3 Mini Fast",
			"swe_score": 0.55,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 32768,
			"supported": true
		}
	],
	"openai": [
		{
			"id": "gpt-4o",
			"swe_score": 0.332,
			"cost_per_1m_tokens": {
				"input": 2.5,
				"output": 10.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 16384,
			"supported": true
		},
		{
			"id": "o1",
			"swe_score": 0.489,
			"cost_per_1m_tokens": {
				"input": 15.0,
				"output": 60.0
			},
			"allowed_roles": ["main"],
			"supported": true
		},
		{
			"id": "o3",
			"swe_score": 0.5,
			"cost_per_1m_tokens": {
				"input": 2.0,
				"output": 8.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 100000,
			"supported": true
		},
		{
			"id": "o3-mini",
			"swe_score": 0.493,
			"cost_per_1m_tokens": {
				"input": 1.1,
				"output": 4.4
			},
			"allowed_roles": ["main"],
			"max_tokens": 100000,
			"supported": true
		},
		{
			"id": "o4-mini",
			"swe_score": 0.45,
			"cost_per_1m_tokens": {
				"input": 1.1,
				"output": 4.4
			},
			"allowed_roles": ["main", "fallback"],
			"supported": true
		},
		{
			"id": "o1-mini",
			"swe_score": 0.4,
			"cost_per_1m_tokens": {
				"input": 1.1,
				"output": 4.4
			},
			"allowed_roles": ["main"],
			"supported": true
		},
		{
			"id": "o1-pro",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 150.0,
				"output": 600.0
			},
			"allowed_roles": ["main"],
			"supported": true
		},
		{
			"id": "gpt-4-5-preview",
			"swe_score": 0.38,
			"cost_per_1m_tokens": {
				"input": 75.0,
				"output": 150.0
			},
			"allowed_roles": ["main"],
			"supported": true
		},
		{
			"id": "gpt-4-1-mini",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.4,
				"output": 1.6
			},
			"allowed_roles": ["main"],
			"supported": true
		},
		{
			"id": "gpt-4-1-nano",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.1,
				"output": 0.4
			},
			"allowed_roles": ["main"],
			"supported": true
		},
		{
			"id": "gpt-4o-mini",
			"swe_score": 0.3,
			"cost_per_1m_tokens": {
				"input": 0.15,
				"output": 0.6
			},
			"allowed_roles": ["main"],
			"supported": true
		},
		{
			"id": "gpt-4o-search-preview",
			"swe_score": 0.33,
			"cost_per_1m_tokens": {
				"input": 2.5,
				"output": 10.0
			},
			"allowed_roles": ["research"],
			"supported": true
		},
		{
			"id": "gpt-4o-mini-search-preview",
			"swe_score": 0.3,
			"cost_per_1m_tokens": {
				"input": 0.15,
				"output": 0.6
			},
			"allowed_roles": ["research"],
			"supported": true
		},
		{
			"id": "gpt-5",
			"swe_score": 0.749,
			"cost_per_1m_tokens": {
				"input": 5.0,
				"output": 20.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 100000,
			"temperature": 1,
			"supported": true
		}
	],
	"google": [
		{
			"id": "gemini-3-pro-preview",
			"swe_score": 0.762,
			"cost_per_1m_tokens": {
				"input": 2.0,
				"output": 12.0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 1000000,
			"supported": true
		},
		{
			"id": "gemini-2.5-pro-preview-05-06",
			"swe_score": 0.638,
			"cost_per_1m_tokens": null,
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1048000,
			"supported": true
		},
		{
			"id": "gemini-2.5-pro-preview-03-25",
			"swe_score": 0.638,
			"cost_per_1m_tokens": null,
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1048000,
			"supported": true
		},
		{
			"id": "gemini-2.5-flash-preview-04-17",
			"swe_score": 0.604,
			"cost_per_1m_tokens": null,
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1048000,
			"supported": true
		},
		{
			"id": "gemini-2.0-flash",
			"swe_score": 0.518,
			"cost_per_1m_tokens": {
				"input": 0.15,
				"output": 0.6
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1048000,
			"supported": true
		},
		{
			"id": "gemini-2.0-flash-lite",
			"swe_score": 0,
			"cost_per_1m_tokens": null,
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1048000,
			"supported": true
		}
	],
	"xai": [
		{
			"id": "grok-3",
			"name": "Grok 3",
			"swe_score": null,
			"cost_per_1m_tokens": {
				"input": 3,
				"output": 15
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 131072,
			"supported": true
		},
		{
			"id": "grok-3-fast",
			"name": "Grok 3 Fast",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 5,
				"output": 25
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 131072,
			"supported": true
		},
		{
			"id": "grok-4",
			"name": "Grok 4",
			"swe_score": null,
			"cost_per_1m_tokens": {
				"input": 3,
				"output": 15
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 131072,
			"supported": true
		}
	],
	"groq": [
		{
			"id": "moonshotai/kimi-k2-instruct",
			"swe_score": 0.66,
			"cost_per_1m_tokens": {
				"input": 1.0,
				"output": 3.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 16384,
			"supported": true
		},
		{
			"id": "llama-3.3-70b-versatile",
			"swe_score": 0.55,
			"cost_per_1m_tokens": {
				"input": 0.59,
				"output": 0.79
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 32768,
			"supported": true
		},
		{
			"id": "llama-3.1-8b-instant",
			"swe_score": 0.32,
			"cost_per_1m_tokens": {
				"input": 0.05,
				"output": 0.08
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 131072,
			"supported": true
		},
		{
			"id": "llama-4-scout",
			"swe_score": 0.45,
			"cost_per_1m_tokens": {
				"input": 0.11,
				"output": 0.34
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 32768,
			"supported": true
		},
		{
			"id": "llama-4-maverick",
			"swe_score": 0.52,
			"cost_per_1m_tokens": {
				"input": 0.5,
				"output": 0.77
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 32768,
			"supported": true
		},
		{
			"id": "mixtral-8x7b-32768",
			"swe_score": 0.35,
			"cost_per_1m_tokens": {
				"input": 0.24,
				"output": 0.24
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 32768,
			"supported": true
		},
		{
			"id": "qwen-qwq-32b-preview",
			"swe_score": 0.4,
			"cost_per_1m_tokens": {
				"input": 0.18,
				"output": 0.18
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 32768,
			"supported": true
		},
		{
			"id": "deepseek-r1-distill-llama-70b",
			"swe_score": 0.52,
			"cost_per_1m_tokens": {
				"input": 0.75,
				"output": 0.99
			},
			"allowed_roles": ["main", "research"],
			"max_tokens": 8192,
			"supported": true
		},
		{
			"id": "gemma2-9b-it",
			"swe_score": 0.3,
			"cost_per_1m_tokens": {
				"input": 0.2,
				"output": 0.2
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 8192,
			"supported": true
		},
		{
			"id": "whisper-large-v3",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.11,
				"output": 0
			},
			"allowed_roles": ["main"],
			"max_tokens": 0,
			"supported": true
		}
	],
	"perplexity": [
		{
			"id": "sonar-pro",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 3,
				"output": 15
			},
			"allowed_roles": ["main", "research"],
			"max_tokens": 8700,
			"supported": true
		},
		{
			"id": "sonar",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 1,
				"output": 1
			},
			"allowed_roles": ["research"],
			"max_tokens": 8700,
			"supported": true
		},
		{
			"id": "sonar-deep-research",
			"swe_score": 0.211,
			"cost_per_1m_tokens": {
				"input": 2,
				"output": 8
			},
			"allowed_roles": ["research"],
			"max_tokens": 8700,
			"supported": true
		},
		{
			"id": "sonar-reasoning-pro",
			"swe_score": 0.211,
			"cost_per_1m_tokens": {
				"input": 2,
				"output": 8
			},
			"allowed_roles": ["main", "research", "fallback"],
			"max_tokens": 8700,
			"supported": true
		},
		{
			"id": "sonar-reasoning",
			"swe_score": 0.211,
			"cost_per_1m_tokens": {
				"input": 1,
				"output": 5
			},
			"allowed_roles": ["main", "research", "fallback"],
			"max_tokens": 8700,
			"supported": true
		}
	],
	"openrouter": [
		{
			"id": "google/gemini-2.5-flash-preview-05-20",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.15,
				"output": 0.6
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1048576,
			"supported": true
		},
		{
			"id": "google/gemini-2.5-flash-preview-05-20:thinking",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.15,
				"output": 3.5
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1048576,
			"supported": true
		},
		{
			"id": "google/gemini-2.5-pro-exp-03-25",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1000000,
			"supported": true
		},
		{
			"id": "deepseek/deepseek-chat-v3-0324:free",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 163840,
			"supported": false,
			"reason": "Free OpenRouter models are not supported due to severe rate limits, lack of tool use support, and other reliability issues that make them impractical for production use."
		},
		{
			"id": "deepseek/deepseek-chat-v3-0324",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.27,
				"output": 1.1
			},
			"allowed_roles": ["main"],
			"max_tokens": 64000,
			"supported": true
		},
		{
			"id": "openai/gpt-4.1",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 2,
				"output": 8
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1000000,
			"supported": true
		},
		{
			"id": "openai/gpt-4.1-mini",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.4,
				"output": 1.6
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1000000,
			"supported": true
		},
		{
			"id": "openai/gpt-4.1-nano",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.1,
				"output": 0.4
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1000000,
			"supported": true
		},
		{
			"id": "openai/o3",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 10,
				"output": 40
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 200000,
			"supported": true
		},
		{
			"id": "openai/codex-mini",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 1.5,
				"output": 6
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 100000,
			"supported": true
		},
		{
			"id": "openai/gpt-4o-mini",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.15,
				"output": 0.6
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 100000,
			"supported": true
		},
		{
			"id": "openai/o4-mini",
			"swe_score": 0.45,
			"cost_per_1m_tokens": {
				"input": 1.1,
				"output": 4.4
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 100000,
			"supported": true
		},
		{
			"id": "openai/o4-mini-high",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 1.1,
				"output": 4.4
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 100000,
			"supported": true
		},
		{
			"id": "openai/o1-pro",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 150,
				"output": 600
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 100000,
			"supported": true
		},
		{
			"id": "meta-llama/llama-3.3-70b-instruct",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 120,
				"output": 600
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1048576,
			"supported": true
		},
		{
			"id": "meta-llama/llama-4-maverick",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.18,
				"output": 0.6
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1000000,
			"supported": true
		},
		{
			"id": "meta-llama/llama-4-scout",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.08,
				"output": 0.3
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 1000000,
			"supported": true
		},
		{
			"id": "qwen/qwen-max",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 1.6,
				"output": 6.4
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 32768,
			"supported": true
		},
		{
			"id": "qwen/qwen-turbo",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.05,
				"output": 0.2
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 32768,
			"supported": true
		},
		{
			"id": "qwen/qwen3-235b-a22b",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.14,
				"output": 2
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 24000,
			"supported": true
		},
		{
			"id": "mistralai/mistral-small-3.1-24b-instruct:free",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 96000,
			"supported": false,
			"reason": "Free OpenRouter models are not supported due to severe rate limits, lack of tool use support, and other reliability issues that make them impractical for production use."
		},
		{
			"id": "mistralai/mistral-small-3.1-24b-instruct",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.1,
				"output": 0.3
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 128000,
			"supported": true
		},
		{
			"id": "mistralai/devstral-small",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.1,
				"output": 0.3
			},
			"allowed_roles": ["main"],
			"max_tokens": 110000,
			"supported": true
		},
		{
			"id": "mistralai/mistral-nemo",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0.03,
				"output": 0.07
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 100000,
			"supported": true
		},
		{
			"id": "thudm/glm-4-32b:free",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 32768,
			"supported": false,
			"reason": "Free OpenRouter models are not supported due to severe rate limits, lack of tool use support, and other reliability issues that make them impractical for production use."
		}
	],
	"zai": [
		{
			"id": "glm-4.6",
			"swe_score": 0.68,
			"cost_per_1m_tokens": {
				"input": 0.6,
				"output": 2.2
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 204800,
			"supported": true
		},
		{
			"id": "glm-4.5",
			"swe_score": 0.65,
			"cost_per_1m_tokens": {
				"input": 0.6,
				"output": 2.2
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 131072,
			"supported": true
		},
		{
			"id": "glm-4.5-air",
			"swe_score": 0.62,
			"cost_per_1m_tokens": {
				"input": 0.2,
				"output": 1.1
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 131072,
			"supported": true
		}
	],
	"zai-coding": [
		{
			"id": "glm-4.6",
			"swe_score": 0.68,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 204800,
			"supported": true
		},
		{
			"id": "glm-4.5",
			"swe_score": 0.65,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 131072,
			"supported": true
		},
		{
			"id": "glm-4.5-air",
			"swe_score": 0.62,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 131072,
			"supported": true
		}
	],
	"ollama": [
		{
			"id": "gpt-oss:latest",
			"swe_score": 0.607,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 128000,
			"supported": true
		},
		{
			"id": "gpt-oss:20b",
			"swe_score": 0.607,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 128000,
			"supported": true
		},
		{
			"id": "gpt-oss:120b",
			"swe_score": 0.624,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 128000,
			"supported": true
		},
		{
			"id": "devstral:latest",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"supported": true
		},
		{
			"id": "qwen3:latest",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"supported": true
		},
		{
			"id": "qwen3:14b",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"supported": true
		},
		{
			"id": "qwen3:32b",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"supported": true
		},
		{
			"id": "mistral-small3.1:latest",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"supported": true
		},
		{
			"id": "llama3.3:latest",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"supported": true
		},
		{
			"id": "phi4:latest",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 0,
				"output": 0
			},
			"allowed_roles": ["main", "fallback"],
			"supported": true
		}
	],
	"azure": [
		{
			"id": "gpt-4o",
			"swe_score": 0.332,
			"cost_per_1m_tokens": {
				"input": 2.5,
				"output": 10
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 16384,
			"supported": true
		},
		{
			"id": "gpt-4o-mini",
			"swe_score": 0.3,
			"cost_per_1m_tokens": {
				"input": 0.15,
				"output": 0.6
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 16384,
			"supported": true
		},
		{
			"id": "gpt-4-1",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 2.0,
				"output": 10.0
			},
			"allowed_roles": ["main", "fallback"],
			"max_tokens": 16384,
			"supported": true
		}
	],
	"bedrock": [
		{
			"id": "us.anthropic.claude-3-haiku-20240307-v1:0",
			"swe_score": 0.4,
			"cost_per_1m_tokens": {
				"input": 0.25,
				"output": 1.25
			},
			"allowed_roles": ["main", "fallback"],
			"supported": true
		},
		{
			"id": "us.anthropic.claude-3-opus-20240229-v1:0",
			"swe_score": 0.725,
			"cost_per_1m_tokens": {
				"input": 15,
				"output": 75
			},
			"allowed_roles": ["main", "fallback", "research"],
			"supported": true
		},
		{
			"id": "us.anthropic.claude-3-5-sonnet-20240620-v1:0",
			"swe_score": 0.49,
			"cost_per_1m_tokens": {
				"input": 3,
				"output": 15
			},
			"allowed_roles": ["main", "fallback", "research"],
			"supported": true
		},
		{
			"id": "us.anthropic.claude-3-5-sonnet-20241022-v2:0",
			"swe_score": 0.49,
			"cost_per_1m_tokens": {
				"input": 3,
				"output": 15
			},
			"allowed_roles": ["main", "fallback", "research"],
			"supported": true
		},
		{
			"id": "us.anthropic.claude-3-7-sonnet-20250219-v1:0",
			"swe_score": 0.623,
			"cost_per_1m_tokens": {
				"input": 3,
				"output": 15
			},
			"allowed_roles": ["main", "fallback", "research"],
			"max_tokens": 65536,
			"supported": true
		},
		{
			"id": "us.anthropic.claude-3-5-haiku-20241022-v1:0",
			"swe_score": 0.4,
			"cost_per_1m_tokens": {
				"input": 0.8,
				"output": 4
			},
			"allowed_roles": ["main", "fallback"],
			"supported": true
		},
		{
			"id": "us.anthropic.claude-opus-4-20250514-v1:0",
			"swe_score": 0.725,
			"cost_per_1m_tokens": {
				"input": 15,
				"output": 75
			},
			"allowed_roles": ["main", "fallback", "research"],
			"supported": true
		},
		{
			"id": "us.anthropic.claude-sonnet-4-20250514-v1:0",
			"swe_score": 0.727,
			"cost_per_1m_tokens": {
				"input": 3,
				"output": 15
			},
			"allowed_roles": ["main", "fallback", "research"],
			"supported": true
		},
		{
			"id": "us.deepseek.r1-v1:0",
			"swe_score": 0,
			"cost_per_1m_tokens": {
				"input": 1.35,
				"output": 5.4
			},
			"allowed_roles": ["research"],
			"max_tokens": 65536,
			"supported": true
		}
	]
}

```

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

```typescript
/**
 * @fileoverview Refactored file-based storage implementation for Task Master
 */

import path from 'node:path';
import type {
	IStorage,
	LoadTasksOptions,
	StorageStats,
	UpdateStatusResult
} from '../../../../common/interfaces/storage.interface.js';
import type {
	Task,
	TaskMetadata,
	TaskStatus
} from '../../../../common/types/index.js';
import {
	ERROR_CODES,
	TaskMasterError
} from '../../../../common/errors/task-master-error.js';
import { ComplexityReportManager } from '../../../reports/managers/complexity-report-manager.js';
import { FileOperations } from './file-operations.js';
import { FormatHandler } from './format-handler.js';
import { PathResolver } from './path-resolver.js';

/**
 * File-based storage implementation using a single tasks.json file with separated concerns
 */
export class FileStorage implements IStorage {
	private formatHandler: FormatHandler;
	private fileOps: FileOperations;
	private pathResolver: PathResolver;
	private complexityManager: ComplexityReportManager;

	constructor(projectPath: string) {
		this.formatHandler = new FormatHandler();
		this.fileOps = new FileOperations();
		this.pathResolver = new PathResolver(projectPath);
		this.complexityManager = new ComplexityReportManager(projectPath);
	}

	/**
	 * Initialize storage by creating necessary directories
	 */
	async initialize(): Promise<void> {
		await this.fileOps.ensureDir(this.pathResolver.getTasksDir());
	}

	/**
	 * Close storage and cleanup resources
	 */
	async close(): Promise<void> {
		await this.fileOps.cleanup();
	}

	/**
	 * Get the storage type
	 */
	getStorageType(): 'file' {
		return 'file';
	}

	/**
	 * Get the current brief name (not applicable for file storage)
	 * @returns null (file storage doesn't use briefs)
	 */
	getCurrentBriefName(): null {
		return null;
	}

	/**
	 * Get statistics about the storage
	 */
	async getStats(): Promise<StorageStats> {
		const filePath = this.pathResolver.getTasksPath();

		try {
			const stats = await this.fileOps.getStats(filePath);
			const data = await this.fileOps.readJson(filePath);
			const tags = this.formatHandler.extractTags(data);

			let totalTasks = 0;
			const tagStats = tags.map((tag) => {
				const tasks = this.formatHandler.extractTasks(data, tag);
				const taskCount = tasks.length;
				totalTasks += taskCount;

				return {
					tag,
					taskCount,
					lastModified: stats.mtime.toISOString()
				};
			});

			return {
				totalTasks,
				totalTags: tags.length,
				lastModified: stats.mtime.toISOString(),
				storageSize: 0, // Could calculate actual file sizes if needed
				tagStats
			};
		} catch (error: any) {
			if (error.code === 'ENOENT') {
				return {
					totalTasks: 0,
					totalTags: 0,
					lastModified: new Date().toISOString(),
					storageSize: 0,
					tagStats: []
				};
			}
			throw new Error(`Failed to get storage stats: ${error.message}`);
		}
	}

	/**
	 * Load tasks from the single tasks.json file for a specific tag
	 * Enriches tasks with complexity data from the complexity report
	 */
	async loadTasks(tag?: string, options?: LoadTasksOptions): Promise<Task[]> {
		const filePath = this.pathResolver.getTasksPath();
		const resolvedTag = tag || 'master';

		try {
			const rawData = await this.fileOps.readJson(filePath);
			let tasks = this.formatHandler.extractTasks(rawData, resolvedTag);

			// Apply filters if provided
			if (options) {
				// Filter by status if specified
				if (options.status) {
					tasks = tasks.filter((task) => task.status === options.status);
				}

				// Exclude subtasks if specified
				if (options.excludeSubtasks) {
					tasks = tasks.map((task) => ({
						...task,
						subtasks: []
					}));
				}
			}

			return await this.enrichTasksWithComplexity(tasks, resolvedTag);
		} catch (error: any) {
			if (error.code === 'ENOENT') {
				return []; // File doesn't exist, return empty array
			}
			throw new Error(`Failed to load tasks: ${error.message}`);
		}
	}

	/**
	 * Load a single task by ID from the tasks.json file
	 * Handles both regular tasks and subtasks (with dotted notation like "1.2")
	 */
	async loadTask(taskId: string, tag?: string): Promise<Task | null> {
		const tasks = await this.loadTasks(tag);

		// Check if this is a subtask (contains a dot)
		if (taskId.includes('.')) {
			const [parentId, subtaskId] = taskId.split('.');
			const parentTask = tasks.find((t) => String(t.id) === parentId);

			if (!parentTask || !parentTask.subtasks) {
				return null;
			}

			const subtask = parentTask.subtasks.find(
				(st) => String(st.id) === subtaskId
			);
			if (!subtask) {
				return null;
			}

			const toFullSubId = (maybeDotId: string | number): string => {
				const depId = String(maybeDotId);
				return depId.includes('.') ? depId : `${parentTask.id}.${depId}`;
			};
			const resolvedDependencies =
				subtask.dependencies?.map((dep) => toFullSubId(dep)) ?? [];

			// Return a Task-like object for the subtask with the full dotted ID
			// Following the same pattern as findTaskById in utils.js
			const subtaskResult = {
				...subtask,
				id: taskId, // Use the full dotted ID
				title: subtask.title || `Subtask ${subtaskId}`,
				description: subtask.description || '',
				status: subtask.status || 'pending',
				priority: subtask.priority || parentTask.priority || 'medium',
				dependencies: resolvedDependencies,
				details: subtask.details || '',
				testStrategy: subtask.testStrategy || '',
				subtasks: [],
				tags: parentTask.tags || [],
				assignee: subtask.assignee || parentTask.assignee,
				complexity: subtask.complexity || parentTask.complexity,
				createdAt: subtask.createdAt || parentTask.createdAt,
				updatedAt: subtask.updatedAt || parentTask.updatedAt,
				// Add reference to parent task for context (like utils.js does)
				parentTask: {
					id: parentTask.id,
					title: parentTask.title,
					status: parentTask.status
				},
				isSubtask: true
			};

			return subtaskResult;
		}

		// Handle regular task lookup
		return tasks.find((task) => String(task.id) === String(taskId)) || null;
	}

	/**
	 * Save tasks for a specific tag in the single tasks.json file
	 */
	async saveTasks(tasks: Task[], tag?: string): Promise<void> {
		const filePath = this.pathResolver.getTasksPath();
		const resolvedTag = tag || 'master';

		// Ensure directory exists
		await this.fileOps.ensureDir(this.pathResolver.getTasksDir());

		// Get existing data from the file
		let existingData: any = {};
		try {
			existingData = await this.fileOps.readJson(filePath);
		} catch (error: any) {
			if (error.code !== 'ENOENT') {
				throw new Error(`Failed to read existing tasks: ${error.message}`);
			}
			// File doesn't exist, start with empty data
		}

		// Create metadata for this tag
		const metadata: TaskMetadata = {
			version: '1.0.0',
			lastModified: new Date().toISOString(),
			taskCount: tasks.length,
			completedCount: tasks.filter((t) => t.status === 'done').length,
			tags: [resolvedTag]
		};

		// Normalize tasks
		const normalizedTasks = this.normalizeTaskIds(tasks);

		// Update the specific tag in the existing data structure
		if (
			this.formatHandler.detectFormat(existingData) === 'legacy' ||
			Object.keys(existingData).some(
				(key) => key !== 'tasks' && key !== 'metadata'
			)
		) {
			// Legacy format - update/add the tag
			existingData[resolvedTag] = {
				tasks: normalizedTasks,
				metadata
			};
		} else if (resolvedTag === 'master') {
			// Standard format for master tag
			existingData = {
				tasks: normalizedTasks,
				metadata
			};
		} else {
			// Convert to legacy format when adding non-master tags
			const masterTasks = existingData.tasks || [];
			const masterMetadata = existingData.metadata || metadata;

			existingData = {
				master: {
					tasks: masterTasks,
					metadata: masterMetadata
				},
				[resolvedTag]: {
					tasks: normalizedTasks,
					metadata
				}
			};
		}

		// Write the updated file
		await this.fileOps.writeJson(filePath, existingData);
	}

	/**
	 * Normalize task IDs - keep Task IDs as strings, Subtask IDs as numbers
	 */
	private normalizeTaskIds(tasks: Task[]): Task[] {
		return tasks.map((task) => ({
			...task,
			id: String(task.id), // Task IDs are strings
			dependencies: task.dependencies?.map((dep) => String(dep)) || [],
			subtasks:
				task.subtasks?.map((subtask) => ({
					...subtask,
					id: Number(subtask.id), // Subtask IDs are numbers
					parentId: String(subtask.parentId) // Parent ID is string (Task ID)
				})) || []
		}));
	}

	/**
	 * Check if the tasks file exists
	 */
	async exists(_tag?: string): Promise<boolean> {
		const filePath = this.pathResolver.getTasksPath();
		return this.fileOps.exists(filePath);
	}

	/**
	 * Get all available tags from the single tasks.json file
	 */
	async getAllTags(): Promise<string[]> {
		try {
			const filePath = this.pathResolver.getTasksPath();
			const data = await this.fileOps.readJson(filePath);
			return this.formatHandler.extractTags(data);
		} catch (error: any) {
			if (error.code === 'ENOENT') {
				return []; // File doesn't exist
			}
			throw new Error(`Failed to get tags: ${error.message}`);
		}
	}

	/**
	 * Load metadata from the single tasks.json file for a specific tag
	 */
	async loadMetadata(tag?: string): Promise<TaskMetadata | null> {
		const filePath = this.pathResolver.getTasksPath();
		const resolvedTag = tag || 'master';

		try {
			const rawData = await this.fileOps.readJson(filePath);
			return this.formatHandler.extractMetadata(rawData, resolvedTag);
		} catch (error: any) {
			if (error.code === 'ENOENT') {
				return null;
			}
			throw new Error(`Failed to load metadata: ${error.message}`);
		}
	}

	/**
	 * Save metadata (stored with tasks)
	 */
	async saveMetadata(_metadata: TaskMetadata, tag?: string): Promise<void> {
		const tasks = await this.loadTasks(tag);
		await this.saveTasks(tasks, tag);
	}

	/**
	 * Append tasks to existing storage
	 */
	async appendTasks(tasks: Task[], tag?: string): Promise<void> {
		const existingTasks = await this.loadTasks(tag);
		const allTasks = [...existingTasks, ...tasks];
		await this.saveTasks(allTasks, tag);
	}

	/**
	 * Update a specific task
	 */
	async updateTask(
		taskId: string,
		updates: Partial<Task>,
		tag?: string
	): Promise<void> {
		const tasks = await this.loadTasks(tag);
		const taskIndex = tasks.findIndex((t) => String(t.id) === String(taskId));

		if (taskIndex === -1) {
			throw new Error(`Task ${taskId} not found`);
		}

		tasks[taskIndex] = {
			...tasks[taskIndex],
			...updates,
			id: String(taskId) // Keep consistent with normalizeTaskIds
		};
		await this.saveTasks(tasks, tag);
	}

	/**
	 * Update task with AI-powered prompt
	 * For file storage, this should NOT be called - client must handle AI processing first
	 */
	async updateTaskWithPrompt(
		_taskId: string,
		_prompt: string,
		_tag?: string,
		_options?: { useResearch?: boolean; mode?: 'append' | 'update' | 'rewrite' }
	): Promise<void> {
		throw new Error(
			'File storage does not support updateTaskWithPrompt. ' +
				'Client-side AI logic must process the prompt before calling updateTask().'
		);
	}

	/**
	 * Expand task into subtasks with AI-powered generation
	 * For file storage, this should NOT be called - client must handle AI processing first
	 */
	async expandTaskWithPrompt(
		_taskId: string,
		_tag?: string,
		_options?: {
			numSubtasks?: number;
			useResearch?: boolean;
			additionalContext?: string;
			force?: boolean;
		}
	): Promise<void> {
		throw new Error(
			'File storage does not support expandTaskWithPrompt. ' +
				'Client-side AI logic must process the expansion before calling updateTask().'
		);
	}

	/**
	 * Update task or subtask status by ID - handles file storage logic with parent/subtask relationships
	 */
	async updateTaskStatus(
		taskId: string,
		newStatus: TaskStatus,
		tag?: string
	): Promise<UpdateStatusResult> {
		const tasks = await this.loadTasks(tag);

		// Check if this is a subtask (contains a dot)
		if (taskId.includes('.')) {
			return this.updateSubtaskStatusInFile(tasks, taskId, newStatus, tag);
		}

		// Handle regular task update
		const taskIndex = tasks.findIndex((t) => String(t.id) === String(taskId));

		if (taskIndex === -1) {
			throw new Error(`Task ${taskId} not found`);
		}

		const oldStatus = tasks[taskIndex].status;
		if (oldStatus === newStatus) {
			return {
				success: true,
				oldStatus,
				newStatus,
				taskId: String(taskId)
			};
		}

		tasks[taskIndex] = {
			...tasks[taskIndex],
			status: newStatus,
			updatedAt: new Date().toISOString()
		};

		await this.saveTasks(tasks, tag);

		return {
			success: true,
			oldStatus,
			newStatus,
			taskId: String(taskId)
		};
	}

	/**
	 * Update subtask status within file storage - handles parent status auto-adjustment
	 */
	private async updateSubtaskStatusInFile(
		tasks: Task[],
		subtaskId: string,
		newStatus: TaskStatus,
		tag?: string
	): Promise<UpdateStatusResult> {
		// Parse the subtask ID to get parent ID and subtask ID
		const parts = subtaskId.split('.');
		if (parts.length !== 2) {
			throw new Error(
				`Invalid subtask ID format: ${subtaskId}. Expected format: parentId.subtaskId`
			);
		}

		const [parentId, subIdRaw] = parts;
		const subId = subIdRaw.trim();
		if (!/^\d+$/.test(subId)) {
			throw new Error(
				`Invalid subtask ID: ${subId}. Subtask ID must be a positive integer.`
			);
		}
		const subtaskNumericId = Number(subId);

		// Find the parent task
		const parentTaskIndex = tasks.findIndex(
			(t) => String(t.id) === String(parentId)
		);

		if (parentTaskIndex === -1) {
			throw new Error(`Parent task ${parentId} not found`);
		}

		const parentTask = tasks[parentTaskIndex];

		// Find the subtask within the parent task
		const subtaskIndex = parentTask.subtasks.findIndex(
			(st) => st.id === subtaskNumericId || String(st.id) === subId
		);

		if (subtaskIndex === -1) {
			throw new Error(
				`Subtask ${subtaskId} not found in parent task ${parentId}`
			);
		}

		const oldStatus = parentTask.subtasks[subtaskIndex].status || 'pending';
		if (oldStatus === newStatus) {
			return {
				success: true,
				oldStatus,
				newStatus,
				taskId: subtaskId
			};
		}

		const now = new Date().toISOString();

		// Update the subtask status
		parentTask.subtasks[subtaskIndex] = {
			...parentTask.subtasks[subtaskIndex],
			status: newStatus,
			updatedAt: now
		};

		// Auto-adjust parent status based on subtask statuses
		const subs = parentTask.subtasks;
		let parentNewStatus = parentTask.status;
		if (subs.length > 0) {
			const norm = (s: any) => s.status || 'pending';
			const isDoneLike = (s: any) => {
				const st = norm(s);
				return st === 'done' || st === 'completed';
			};
			const allDone = subs.every(isDoneLike);
			const anyInProgress = subs.some((s) => norm(s) === 'in-progress');
			const anyDone = subs.some(isDoneLike);
			const allPending = subs.every((s) => norm(s) === 'pending');

			if (allDone) parentNewStatus = 'done';
			else if (anyInProgress || anyDone) parentNewStatus = 'in-progress';
			else if (allPending) parentNewStatus = 'pending';
		}

		// Always bump updatedAt; update status only if changed
		tasks[parentTaskIndex] = {
			...parentTask,
			...(parentNewStatus !== parentTask.status
				? { status: parentNewStatus }
				: {}),
			updatedAt: now
		};

		await this.saveTasks(tasks, tag);

		return {
			success: true,
			oldStatus,
			newStatus,
			taskId: subtaskId
		};
	}

	/**
	 * Delete a task
	 */
	async deleteTask(taskId: string, tag?: string): Promise<void> {
		const tasks = await this.loadTasks(tag);
		const filteredTasks = tasks.filter((t) => String(t.id) !== String(taskId));

		if (filteredTasks.length === tasks.length) {
			throw new Error(`Task ${taskId} not found`);
		}

		await this.saveTasks(filteredTasks, tag);
	}

	/**
	 * Create a new tag in the tasks.json file
	 */
	async createTag(
		tagName: string,
		options?: { copyFrom?: string; description?: string }
	): Promise<void> {
		const filePath = this.pathResolver.getTasksPath();

		try {
			const existingData = await this.fileOps.readJson(filePath);
			const format = this.formatHandler.detectFormat(existingData);

			if (format === 'legacy') {
				// Legacy format - add new tag key
				if (tagName in existingData) {
					throw new TaskMasterError(
						`Tag ${tagName} already exists`,
						ERROR_CODES.VALIDATION_ERROR
					);
				}

				// Get tasks to copy if specified
				let tasksToCopy = [];
				if (options?.copyFrom) {
					if (
						options.copyFrom in existingData &&
						existingData[options.copyFrom].tasks
					) {
						tasksToCopy = JSON.parse(
							JSON.stringify(existingData[options.copyFrom].tasks)
						);
					}
				}

				// Create new tag structure
				existingData[tagName] = {
					tasks: tasksToCopy,
					metadata: {
						created: new Date().toISOString(),
						updatedAt: new Date().toISOString(),
						description:
							options?.description ||
							`Tag created on ${new Date().toLocaleDateString()}`,
						tags: [tagName]
					}
				};

				await this.fileOps.writeJson(filePath, existingData);
			} else {
				// Standard format - need to convert to legacy format first
				const masterTasks = existingData.tasks || [];
				const masterMetadata = existingData.metadata || {};

				// Get tasks to copy (from master in this case)
				let tasksToCopy = [];
				if (options?.copyFrom === 'master' || !options?.copyFrom) {
					tasksToCopy = JSON.parse(JSON.stringify(masterTasks));
				}

				const newData = {
					master: {
						tasks: masterTasks,
						metadata: { ...masterMetadata, tags: ['master'] }
					},
					[tagName]: {
						tasks: tasksToCopy,
						metadata: {
							created: new Date().toISOString(),
							updatedAt: new Date().toISOString(),
							description:
								options?.description ||
								`Tag created on ${new Date().toLocaleDateString()}`,
							tags: [tagName]
						}
					}
				};

				await this.fileOps.writeJson(filePath, newData);
			}
		} catch (error: any) {
			if (error.code === 'ENOENT') {
				throw new Error('Tasks file not found - initialize project first');
			}
			throw error;
		}
	}

	/**
	 * Delete a tag from the single tasks.json file
	 */
	async deleteTag(tag: string): Promise<void> {
		const filePath = this.pathResolver.getTasksPath();

		try {
			const existingData = await this.fileOps.readJson(filePath);

			if (this.formatHandler.detectFormat(existingData) === 'legacy') {
				// Legacy format - remove the tag key
				if (tag in existingData) {
					delete existingData[tag];
					await this.fileOps.writeJson(filePath, existingData);
				} else {
					throw new Error(`Tag ${tag} not found`);
				}
			} else if (tag === 'master') {
				// Standard format - delete the entire file for master tag
				await this.fileOps.deleteFile(filePath);
			} else {
				throw new Error(`Tag ${tag} not found in standard format`);
			}
		} catch (error: any) {
			if (error.code === 'ENOENT') {
				throw new Error(`Tag ${tag} not found - file doesn't exist`);
			}
			throw error;
		}
	}

	/**
	 * Rename a tag within the single tasks.json file
	 */
	async renameTag(oldTag: string, newTag: string): Promise<void> {
		const filePath = this.pathResolver.getTasksPath();

		try {
			const existingData = await this.fileOps.readJson(filePath);

			if (this.formatHandler.detectFormat(existingData) === 'legacy') {
				// Legacy format - rename the tag key
				if (oldTag in existingData) {
					existingData[newTag] = existingData[oldTag];
					delete existingData[oldTag];

					// Update metadata tags array
					if (existingData[newTag].metadata) {
						existingData[newTag].metadata.tags = [newTag];
					}

					await this.fileOps.writeJson(filePath, existingData);
				} else {
					throw new Error(`Tag ${oldTag} not found`);
				}
			} else if (oldTag === 'master') {
				// Convert standard format to legacy when renaming master
				const masterTasks = existingData.tasks || [];
				const masterMetadata = existingData.metadata || {};

				const newData = {
					[newTag]: {
						tasks: masterTasks,
						metadata: { ...masterMetadata, tags: [newTag] }
					}
				};

				await this.fileOps.writeJson(filePath, newData);
			} else {
				throw new Error(`Tag ${oldTag} not found in standard format`);
			}
		} catch (error: any) {
			if (error.code === 'ENOENT') {
				throw new Error(`Tag ${oldTag} not found - file doesn't exist`);
			}
			throw error;
		}
	}

	/**
	 * Copy a tag within the single tasks.json file
	 */
	async copyTag(sourceTag: string, targetTag: string): Promise<void> {
		const tasks = await this.loadTasks(sourceTag);

		if (tasks.length === 0) {
			throw new Error(`Source tag ${sourceTag} not found or has no tasks`);
		}

		await this.saveTasks(tasks, targetTag);
	}

	/**
	 * Get all tags with detailed statistics including task counts
	 * For file storage, reads tags from tasks.json and calculates statistics
	 */
	async getTagsWithStats(): Promise<{
		tags: Array<{
			name: string;
			isCurrent: boolean;
			taskCount: number;
			completedTasks: number;
			statusBreakdown: Record<string, number>;
			subtaskCounts?: {
				totalSubtasks: number;
				subtasksByStatus: Record<string, number>;
			};
			created?: string;
			description?: string;
		}>;
		currentTag: string | null;
		totalTags: number;
	}> {
		const availableTags = await this.getAllTags();

		// Get active tag from state.json
		const activeTag = await this.getActiveTagFromState();

		const tagsWithStats = await Promise.all(
			availableTags.map(async (tagName) => {
				try {
					// Load tasks for this tag
					const tasks = await this.loadTasks(tagName);

					// Calculate statistics
					const statusBreakdown: Record<string, number> = {};
					let completedTasks = 0;

					const subtaskCounts = {
						totalSubtasks: 0,
						subtasksByStatus: {} as Record<string, number>
					};

					tasks.forEach((task) => {
						// Count task status
						const status = task.status || 'pending';
						statusBreakdown[status] = (statusBreakdown[status] || 0) + 1;

						if (status === 'done') {
							completedTasks++;
						}

						// Count subtasks
						if (task.subtasks && task.subtasks.length > 0) {
							subtaskCounts.totalSubtasks += task.subtasks.length;

							task.subtasks.forEach((subtask) => {
								const subStatus = subtask.status || 'pending';
								subtaskCounts.subtasksByStatus[subStatus] =
									(subtaskCounts.subtasksByStatus[subStatus] || 0) + 1;
							});
						}
					});

					// Load metadata to get created date and description
					const metadata = await this.loadMetadata(tagName);

					return {
						name: tagName,
						isCurrent: tagName === activeTag,
						taskCount: tasks.length,
						completedTasks,
						statusBreakdown,
						subtaskCounts:
							subtaskCounts.totalSubtasks > 0 ? subtaskCounts : undefined,
						created: metadata?.created,
						description: metadata?.description
					};
				} catch (error) {
					// If we can't load tasks for a tag, return it with 0 tasks
					return {
						name: tagName,
						isCurrent: tagName === activeTag,
						taskCount: 0,
						completedTasks: 0,
						statusBreakdown: {}
					};
				}
			})
		);

		return {
			tags: tagsWithStats,
			currentTag: activeTag,
			totalTags: tagsWithStats.length
		};
	}

	/**
	 * Get the active tag from state.json
	 * @returns The active tag name or 'master' as default
	 */
	private async getActiveTagFromState(): Promise<string> {
		try {
			const statePath = path.join(
				this.pathResolver.getBasePath(),
				'state.json'
			);
			const stateData = await this.fileOps.readJson(statePath);
			return stateData?.currentTag || 'master';
		} catch (error) {
			// If state.json doesn't exist or can't be read, default to 'master'
			return 'master';
		}
	}

	/**
	 * Enrich tasks with complexity data from the complexity report
	 * Private helper method called by loadTasks()
	 */
	private async enrichTasksWithComplexity(
		tasks: Task[],
		tag: string
	): Promise<Task[]> {
		// Get all task IDs for bulk lookup
		const taskIds = tasks.map((t) => t.id);

		// Load complexity data for all tasks at once (more efficient)
		const complexityMap = await this.complexityManager.getComplexityForTasks(
			taskIds,
			tag
		);

		// If no complexity data found, return tasks as-is
		if (complexityMap.size === 0) {
			return tasks;
		}

		// Enrich each task with its complexity data
		return tasks.map((task) => {
			const complexityData = complexityMap.get(String(task.id));
			if (!complexityData) {
				return task;
			}

			// Merge complexity data into the task
			return {
				...task,
				complexity: complexityData.complexityScore,
				recommendedSubtasks: complexityData.recommendedSubtasks,
				expansionPrompt: complexityData.expansionPrompt,
				complexityReasoning: complexityData.complexityReasoning
			};
		});
	}
}

// Export as default for convenience
export default FileStorage;

```
Page 34/50FirstPrevNextLast