#
tokens: 49164/50000 33/975 files (page 8/69)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 8 of 69. Use http://codebase.md/eyaltoledano/claude-task-master?lines=true&page={x} to view the full context.

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/mcp-server/src/tools/next-task.js:
--------------------------------------------------------------------------------

```javascript
 1 | /**
 2 |  * tools/next-task.js
 3 |  * Tool to find the next task to work on based on dependencies and status
 4 |  */
 5 | 
 6 | import { z } from 'zod';
 7 | import {
 8 | 	createErrorResponse,
 9 | 	handleApiResult,
10 | 	withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { nextTaskDirect } from '../core/task-master-core.js';
13 | import {
14 | 	resolveTasksPath,
15 | 	resolveComplexityReportPath
16 | } from '../core/utils/path-utils.js';
17 | import { resolveTag } from '../../../scripts/modules/utils.js';
18 | 
19 | /**
20 |  * Register the nextTask tool with the MCP server
21 |  * @param {Object} server - FastMCP server instance
22 |  */
23 | export function registerNextTaskTool(server) {
24 | 	server.addTool({
25 | 		name: 'next_task',
26 | 		description:
27 | 			'Find the next task to work on based on dependencies and status',
28 | 		parameters: z.object({
29 | 			file: z.string().optional().describe('Absolute path to the tasks file'),
30 | 			complexityReport: z
31 | 				.string()
32 | 				.optional()
33 | 				.describe(
34 | 					'Path to the complexity report file (relative to project root or absolute)'
35 | 				),
36 | 			projectRoot: z
37 | 				.string()
38 | 				.describe('The directory of the project. Must be an absolute path.'),
39 | 			tag: z.string().optional().describe('Tag context to operate on')
40 | 		}),
41 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
42 | 			try {
43 | 				log.info(`Finding next task with args: ${JSON.stringify(args)}`);
44 | 				const resolvedTag = resolveTag({
45 | 					projectRoot: args.projectRoot,
46 | 					tag: args.tag
47 | 				});
48 | 
49 | 				// Resolve the path to tasks.json using new path utilities
50 | 				let tasksJsonPath;
51 | 				try {
52 | 					tasksJsonPath = resolveTasksPath(args, session);
53 | 				} catch (error) {
54 | 					log.error(`Error finding tasks.json: ${error.message}`);
55 | 					return createErrorResponse(
56 | 						`Failed to find tasks.json: ${error.message}`
57 | 					);
58 | 				}
59 | 
60 | 				// Resolve the path to complexity report (optional)
61 | 				let complexityReportPath;
62 | 				try {
63 | 					complexityReportPath = resolveComplexityReportPath(
64 | 						{ ...args, tag: resolvedTag },
65 | 						session
66 | 					);
67 | 				} catch (error) {
68 | 					log.error(`Error finding complexity report: ${error.message}`);
69 | 					// This is optional, so we don't fail the operation
70 | 					complexityReportPath = null;
71 | 				}
72 | 
73 | 				const result = await nextTaskDirect(
74 | 					{
75 | 						tasksJsonPath: tasksJsonPath,
76 | 						reportPath: complexityReportPath,
77 | 						projectRoot: args.projectRoot,
78 | 						tag: resolvedTag
79 | 					},
80 | 					log,
81 | 					{ session }
82 | 				);
83 | 
84 | 				log.info(`Next task result: ${result.success ? 'found' : 'none'}`);
85 | 				return handleApiResult(
86 | 					result,
87 | 					log,
88 | 					'Error finding next task',
89 | 					undefined,
90 | 					args.projectRoot
91 | 				);
92 | 			} catch (error) {
93 | 				log.error(`Error finding next task: ${error.message}`);
94 | 				return createErrorResponse(error.message);
95 | 			}
96 | 		})
97 | 	});
98 | }
99 | 
```

--------------------------------------------------------------------------------
/src/utils/asset-resolver.js:
--------------------------------------------------------------------------------

```javascript
 1 | /**
 2 |  * Asset Resolver Module
 3 |  * Handles resolving paths to asset files in the package
 4 |  *
 5 |  * The public/assets folder is copied to dist/assets during build via tsup's publicDir,
 6 |  * so we can reliably find it relative to the bundled files.
 7 |  */
 8 | import fs from 'fs';
 9 | import path from 'path';
10 | import { fileURLToPath } from 'url';
11 | 
12 | const __filename = fileURLToPath(import.meta.url);
13 | const __dirname = path.dirname(__filename);
14 | 
15 | /**
16 |  * Get the assets directory path
17 |  * When bundled, assets are in dist/assets
18 |  * When in development, assets are in public/assets
19 |  * @returns {string} Path to the assets directory
20 |  */
21 | export function getAssetsDir() {
22 | 	// Check multiple possible locations
23 | 	const possiblePaths = [
24 | 		// When running from dist (bundled) - assets are in dist/assets
25 | 		path.join(__dirname, 'assets'),
26 | 		path.join(__dirname, '..', 'assets'),
27 | 		// When running from source in development - now in public/assets
28 | 		path.join(__dirname, '..', '..', 'public', 'assets'),
29 | 		// When installed as npm package - assets at package root
30 | 		path.join(process.cwd(), 'assets'),
31 | 		// For npx usage - check node_modules
32 | 		path.join(
33 | 			process.cwd(),
34 | 			'node_modules',
35 | 			'task-master-ai',
36 | 			'dist',
37 | 			'assets'
38 | 		),
39 | 		path.join(process.cwd(), 'node_modules', 'task-master-ai', 'assets')
40 | 	];
41 | 
42 | 	// Find the first existing assets directory
43 | 	for (const assetPath of possiblePaths) {
44 | 		if (fs.existsSync(assetPath)) {
45 | 			// Verify it's actually the assets directory by checking for known files
46 | 			const testFile = path.join(assetPath, 'rules', 'taskmaster.mdc');
47 | 			if (fs.existsSync(testFile)) {
48 | 				return assetPath;
49 | 			}
50 | 		}
51 | 	}
52 | 
53 | 	// If no assets directory found, throw an error
54 | 	throw new Error(
55 | 		'Assets directory not found. This is likely a packaging issue.'
56 | 	);
57 | }
58 | 
59 | /**
60 |  * Get path to a specific asset file
61 |  * @param {string} relativePath - Path relative to assets directory
62 |  * @returns {string} Full path to the asset file
63 |  */
64 | export function getAssetPath(relativePath) {
65 | 	const assetsDir = getAssetsDir();
66 | 	return path.join(assetsDir, relativePath);
67 | }
68 | 
69 | /**
70 |  * Check if an asset file exists
71 |  * @param {string} relativePath - Path relative to assets directory
72 |  * @returns {boolean} True if the asset exists
73 |  */
74 | export function assetExists(relativePath) {
75 | 	try {
76 | 		const assetPath = getAssetPath(relativePath);
77 | 		return fs.existsSync(assetPath);
78 | 	} catch (error) {
79 | 		return false;
80 | 	}
81 | }
82 | 
83 | /**
84 |  * Read an asset file
85 |  * @param {string} relativePath - Path relative to assets directory
86 |  * @param {string} encoding - File encoding (default: 'utf8')
87 |  * @returns {string|Buffer} File contents
88 |  */
89 | export function readAsset(relativePath, encoding = 'utf8') {
90 | 	const assetPath = getAssetPath(relativePath);
91 | 	return fs.readFileSync(assetPath, encoding);
92 | }
93 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/move-task.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * Direct function wrapper for moveTask
  3 |  */
  4 | 
  5 | import { moveTask } from '../../../../scripts/modules/task-manager.js';
  6 | import { findTasksPath } from '../utils/path-utils.js';
  7 | import {
  8 | 	enableSilentMode,
  9 | 	disableSilentMode
 10 | } from '../../../../scripts/modules/utils.js';
 11 | 
 12 | /**
 13 |  * Move a task or subtask to a new position
 14 |  * @param {Object} args - Function arguments
 15 |  * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file
 16 |  * @param {string} args.sourceId - ID of the task/subtask to move (e.g., '5' or '5.2' or '5,6,7')
 17 |  * @param {string} args.destinationId - ID of the destination (e.g., '7' or '7.3' or '7,8,9')
 18 |  * @param {string} args.file - Alternative path to the tasks.json file
 19 |  * @param {string} args.projectRoot - Project root directory
 20 |  * @param {string} args.tag - Tag for the task (optional)
 21 |  * @param {boolean} args.generateFiles - Whether to regenerate task files after moving (default: true)
 22 |  * @param {Object} log - Logger object
 23 |  * @returns {Promise<{success: boolean, data?: Object, error?: Object}>}
 24 |  */
 25 | export async function moveTaskDirect(args, log, context = {}) {
 26 | 	const { session } = context;
 27 | 	const { projectRoot, tag } = args;
 28 | 
 29 | 	// Validate required parameters
 30 | 	if (!args.sourceId) {
 31 | 		return {
 32 | 			success: false,
 33 | 			error: {
 34 | 				message: 'Source ID is required',
 35 | 				code: 'MISSING_SOURCE_ID'
 36 | 			}
 37 | 		};
 38 | 	}
 39 | 
 40 | 	if (!args.destinationId) {
 41 | 		return {
 42 | 			success: false,
 43 | 			error: {
 44 | 				message: 'Destination ID is required',
 45 | 				code: 'MISSING_DESTINATION_ID'
 46 | 			}
 47 | 		};
 48 | 	}
 49 | 
 50 | 	try {
 51 | 		// Find tasks.json path if not provided
 52 | 		let tasksPath = args.tasksJsonPath || args.file;
 53 | 		if (!tasksPath) {
 54 | 			if (!args.projectRoot) {
 55 | 				return {
 56 | 					success: false,
 57 | 					error: {
 58 | 						message:
 59 | 							'Project root is required if tasksJsonPath is not provided',
 60 | 						code: 'MISSING_PROJECT_ROOT'
 61 | 					}
 62 | 				};
 63 | 			}
 64 | 			tasksPath = findTasksPath(args, log);
 65 | 		}
 66 | 
 67 | 		// Enable silent mode to prevent console output during MCP operation
 68 | 		enableSilentMode();
 69 | 
 70 | 		// Call the core moveTask function with file generation control
 71 | 		const generateFiles = args.generateFiles !== false; // Default to true
 72 | 		const result = await moveTask(
 73 | 			tasksPath,
 74 | 			args.sourceId,
 75 | 			args.destinationId,
 76 | 			generateFiles,
 77 | 			{
 78 | 				projectRoot,
 79 | 				tag
 80 | 			}
 81 | 		);
 82 | 
 83 | 		// Restore console output
 84 | 		disableSilentMode();
 85 | 
 86 | 		return {
 87 | 			success: true,
 88 | 			data: {
 89 | 				...result,
 90 | 				message: `Successfully moved task/subtask ${args.sourceId} to ${args.destinationId}`
 91 | 			}
 92 | 		};
 93 | 	} catch (error) {
 94 | 		// Restore console output in case of error
 95 | 		disableSilentMode();
 96 | 
 97 | 		log.error(`Failed to move task: ${error.message}`);
 98 | 
 99 | 		return {
100 | 			success: false,
101 | 			error: {
102 | 				message: error.message,
103 | 				code: 'MOVE_TASK_ERROR'
104 | 			}
105 | 		};
106 | 	}
107 | }
108 | 
```

--------------------------------------------------------------------------------
/.github/workflows/claude-dedupe-issues.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Claude Issue Dedupe
 2 | # description: Automatically dedupe GitHub issues using Claude Code
 3 | 
 4 | on:
 5 |   issues:
 6 |     types: [opened]
 7 |   workflow_dispatch:
 8 |     inputs:
 9 |       issue_number:
10 |         description: "Issue number to process for duplicate detection"
11 |         required: true
12 |         type: string
13 | 
14 | jobs:
15 |   claude-dedupe-issues:
16 |     runs-on: ubuntu-latest
17 |     timeout-minutes: 10
18 |     permissions:
19 |       contents: read
20 |       issues: write
21 | 
22 |     steps:
23 |       - name: Checkout repository
24 |         uses: actions/checkout@v4
25 | 
26 |       - name: Run Claude Code slash command
27 |         uses: anthropics/claude-code-base-action@beta
28 |         with:
29 |           prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
30 |           anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
31 |           claude_env: |
32 |             GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33 | 
34 |       - name: Log duplicate comment event to Statsig
35 |         if: always()
36 |         env:
37 |           STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}
38 |         run: |
39 |           ISSUE_NUMBER=${{ github.event.issue.number || inputs.issue_number }}
40 |           REPO=${{ github.repository }}
41 | 
42 |           if [ -z "$STATSIG_API_KEY" ]; then
43 |             echo "STATSIG_API_KEY not found, skipping Statsig logging"
44 |             exit 0
45 |           fi
46 | 
47 |           # Prepare the event payload
48 |           EVENT_PAYLOAD=$(jq -n \
49 |             --arg issue_number "$ISSUE_NUMBER" \
50 |             --arg repo "$REPO" \
51 |             --arg triggered_by "${{ github.event_name }}" \
52 |             '{
53 |               events: [{
54 |                 eventName: "github_duplicate_comment_added",
55 |                 value: 1,
56 |                 metadata: {
57 |                   repository: $repo,
58 |                   issue_number: ($issue_number | tonumber),
59 |                   triggered_by: $triggered_by,
60 |                   workflow_run_id: "${{ github.run_id }}"
61 |                 },
62 |                 time: (now | floor | tostring)
63 |               }]
64 |             }')
65 | 
66 |           # Send to Statsig API
67 |           echo "Logging duplicate comment event to Statsig for issue #${ISSUE_NUMBER}"
68 | 
69 |           RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://events.statsigapi.net/v1/log_event \
70 |             -H "Content-Type: application/json" \
71 |             -H "STATSIG-API-KEY: ${STATSIG_API_KEY}" \
72 |             -d "$EVENT_PAYLOAD")
73 | 
74 |           HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
75 |           BODY=$(echo "$RESPONSE" | head -n-1)
76 | 
77 |           if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 202 ]; then
78 |             echo "Successfully logged duplicate comment event for issue #${ISSUE_NUMBER}"
79 |           else
80 |             echo "Failed to log duplicate comment event for issue #${ISSUE_NUMBER}. HTTP ${HTTP_CODE}: ${BODY}"
81 |           fi
82 | 
```

--------------------------------------------------------------------------------
/tests/unit/ai-providers/mcp-components.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tests/unit/ai-providers/mcp-components.test.js
  3 |  * Unit tests for MCP AI SDK custom components
  4 |  */
  5 | 
  6 | import { jest } from '@jest/globals';
  7 | 
  8 | describe('MCP Custom SDK Components', () => {
  9 | 	describe('Message Converter', () => {
 10 | 		let messageConverter;
 11 | 
 12 | 		beforeAll(async () => {
 13 | 			const module = await import(
 14 | 				'../../../mcp-server/src/custom-sdk/message-converter.js'
 15 | 			);
 16 | 			messageConverter = module;
 17 | 		});
 18 | 
 19 | 		describe('convertToMCPFormat', () => {
 20 | 			it('should convert AI SDK messages to MCP format', () => {
 21 | 				const input = [
 22 | 					{ role: 'system', content: 'You are a helpful assistant.' },
 23 | 					{ role: 'user', content: 'Hello!' }
 24 | 				];
 25 | 
 26 | 				const result = messageConverter.convertToMCPFormat(input);
 27 | 
 28 | 				expect(result).toBeDefined();
 29 | 				expect(result.messages).toBeDefined();
 30 | 				expect(Array.isArray(result.messages)).toBe(true);
 31 | 				expect(result.systemPrompt).toBe('You are a helpful assistant.');
 32 | 				expect(result.messages).toHaveLength(1);
 33 | 				expect(result.messages[0].role).toBe('user');
 34 | 				expect(result.messages[0].content.text).toBe('Hello!');
 35 | 			});
 36 | 		});
 37 | 
 38 | 		describe('convertFromMCPFormat', () => {
 39 | 			it('should convert MCP response to AI SDK format', () => {
 40 | 				const input = {
 41 | 					content: 'Hello! How can I help you?',
 42 | 					usage: { inputTokens: 10, outputTokens: 8 }
 43 | 				};
 44 | 
 45 | 				const result = messageConverter.convertFromMCPFormat(input);
 46 | 
 47 | 				expect(result).toBeDefined();
 48 | 				expect(result.text).toBe('Hello! How can I help you?');
 49 | 				expect(result.usage).toEqual({ inputTokens: 10, outputTokens: 8 });
 50 | 				expect(result.finishReason).toBe('stop');
 51 | 				expect(result.warnings).toBeDefined();
 52 | 			});
 53 | 		});
 54 | 	});
 55 | 
 56 | 	describe('Language Model', () => {
 57 | 		let languageModel;
 58 | 
 59 | 		beforeAll(async () => {
 60 | 			const module = await import(
 61 | 				'../../../mcp-server/src/custom-sdk/language-model.js'
 62 | 			);
 63 | 			languageModel = module;
 64 | 		});
 65 | 
 66 | 		it('should export MCPLanguageModel class', () => {
 67 | 			expect(languageModel.MCPLanguageModel).toBeDefined();
 68 | 			expect(typeof languageModel.MCPLanguageModel).toBe('function');
 69 | 		});
 70 | 	});
 71 | 
 72 | 	describe('Error Handling', () => {
 73 | 		let errors;
 74 | 
 75 | 		beforeAll(async () => {
 76 | 			const module = await import(
 77 | 				'../../../mcp-server/src/custom-sdk/errors.js'
 78 | 			);
 79 | 			errors = module;
 80 | 		});
 81 | 
 82 | 		it('should export error classes', () => {
 83 | 			expect(errors.MCPError).toBeDefined();
 84 | 			expect(typeof errors.MCPError).toBe('function');
 85 | 		});
 86 | 	});
 87 | 
 88 | 	describe('Index Module', () => {
 89 | 		let index;
 90 | 
 91 | 		beforeAll(async () => {
 92 | 			const module = await import(
 93 | 				'../../../mcp-server/src/custom-sdk/index.js'
 94 | 			);
 95 | 			index = module;
 96 | 		});
 97 | 
 98 | 		it('should export createMCP function', () => {
 99 | 			expect(index.createMCP).toBeDefined();
100 | 			expect(typeof index.createMCP).toBe('function');
101 | 		});
102 | 	});
103 | });
104 | 
```

--------------------------------------------------------------------------------
/tests/unit/mcp-providers/mcp-components.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tests/unit/mcp-providers/mcp-components.test.js
  3 |  * Unit tests for MCP AI SDK custom components
  4 |  */
  5 | 
  6 | import { jest } from '@jest/globals';
  7 | 
  8 | describe('MCP Custom SDK Components', () => {
  9 | 	describe('Message Converter', () => {
 10 | 		let messageConverter;
 11 | 
 12 | 		beforeAll(async () => {
 13 | 			const module = await import(
 14 | 				'../../../mcp-server/src/custom-sdk/message-converter.js'
 15 | 			);
 16 | 			messageConverter = module;
 17 | 		});
 18 | 
 19 | 		describe('convertToMCPFormat', () => {
 20 | 			it('should convert AI SDK messages to MCP format', () => {
 21 | 				const input = [
 22 | 					{ role: 'system', content: 'You are a helpful assistant.' },
 23 | 					{ role: 'user', content: 'Hello!' }
 24 | 				];
 25 | 
 26 | 				const result = messageConverter.convertToMCPFormat(input);
 27 | 
 28 | 				expect(result).toBeDefined();
 29 | 				expect(result.messages).toBeDefined();
 30 | 				expect(Array.isArray(result.messages)).toBe(true);
 31 | 				expect(result.systemPrompt).toBe('You are a helpful assistant.');
 32 | 				expect(result.messages).toHaveLength(1);
 33 | 				expect(result.messages[0].role).toBe('user');
 34 | 				expect(result.messages[0].content.text).toBe('Hello!');
 35 | 			});
 36 | 		});
 37 | 
 38 | 		describe('convertFromMCPFormat', () => {
 39 | 			it('should convert MCP response to AI SDK format', () => {
 40 | 				const input = {
 41 | 					content: 'Hello! How can I help you?',
 42 | 					usage: { inputTokens: 10, outputTokens: 8 }
 43 | 				};
 44 | 
 45 | 				const result = messageConverter.convertFromMCPFormat(input);
 46 | 
 47 | 				expect(result).toBeDefined();
 48 | 				expect(result.text).toBe('Hello! How can I help you?');
 49 | 				expect(result.usage).toEqual({ inputTokens: 10, outputTokens: 8 });
 50 | 				expect(result.finishReason).toBe('stop');
 51 | 				expect(result.warnings).toBeDefined();
 52 | 			});
 53 | 		});
 54 | 	});
 55 | 
 56 | 	describe('Language Model', () => {
 57 | 		let languageModel;
 58 | 
 59 | 		beforeAll(async () => {
 60 | 			const module = await import(
 61 | 				'../../../mcp-server/src/custom-sdk/language-model.js'
 62 | 			);
 63 | 			languageModel = module;
 64 | 		});
 65 | 
 66 | 		it('should export MCPLanguageModel class', () => {
 67 | 			expect(languageModel.MCPLanguageModel).toBeDefined();
 68 | 			expect(typeof languageModel.MCPLanguageModel).toBe('function');
 69 | 		});
 70 | 	});
 71 | 
 72 | 	describe('Error Handling', () => {
 73 | 		let errors;
 74 | 
 75 | 		beforeAll(async () => {
 76 | 			const module = await import(
 77 | 				'../../../mcp-server/src/custom-sdk/errors.js'
 78 | 			);
 79 | 			errors = module;
 80 | 		});
 81 | 
 82 | 		it('should export error classes', () => {
 83 | 			expect(errors.MCPError).toBeDefined();
 84 | 			expect(typeof errors.MCPError).toBe('function');
 85 | 		});
 86 | 	});
 87 | 
 88 | 	describe('Index Module', () => {
 89 | 		let index;
 90 | 
 91 | 		beforeAll(async () => {
 92 | 			const module = await import(
 93 | 				'../../../mcp-server/src/custom-sdk/index.js'
 94 | 			);
 95 | 			index = module;
 96 | 		});
 97 | 
 98 | 		it('should export createMCP function', () => {
 99 | 			expect(index.createMCP).toBeDefined();
100 | 			expect(typeof index.createMCP).toBe('function');
101 | 		});
102 | 	});
103 | });
104 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/tools/remove-subtask.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tools/remove-subtask.js
  3 |  * Tool for removing subtasks from parent tasks
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	createErrorResponse,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { removeSubtaskDirect } from '../core/task-master-core.js';
 13 | import { findTasksPath } from '../core/utils/path-utils.js';
 14 | import { resolveTag } from '../../../scripts/modules/utils.js';
 15 | 
 16 | /**
 17 |  * Register the removeSubtask tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerRemoveSubtaskTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'remove_subtask',
 23 | 		description: 'Remove a subtask from its parent task',
 24 | 		parameters: z.object({
 25 | 			id: z
 26 | 				.string()
 27 | 				.describe(
 28 | 					"Subtask ID to remove in format 'parentId.subtaskId' (required)"
 29 | 				),
 30 | 			convert: z
 31 | 				.boolean()
 32 | 				.optional()
 33 | 				.describe(
 34 | 					'Convert the subtask to a standalone task instead of deleting it'
 35 | 				),
 36 | 			file: z
 37 | 				.string()
 38 | 				.optional()
 39 | 				.describe(
 40 | 					'Absolute path to the tasks file (default: tasks/tasks.json)'
 41 | 				),
 42 | 			skipGenerate: z
 43 | 				.boolean()
 44 | 				.optional()
 45 | 				.describe('Skip regenerating task files'),
 46 | 			projectRoot: z
 47 | 				.string()
 48 | 				.describe('The directory of the project. Must be an absolute path.'),
 49 | 			tag: z.string().optional().describe('Tag context to operate on')
 50 | 		}),
 51 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 52 | 			try {
 53 | 				const resolvedTag = resolveTag({
 54 | 					projectRoot: args.projectRoot,
 55 | 					tag: args.tag
 56 | 				});
 57 | 				log.info(`Removing subtask with args: ${JSON.stringify(args)}`);
 58 | 
 59 | 				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
 60 | 				let tasksJsonPath;
 61 | 				try {
 62 | 					tasksJsonPath = findTasksPath(
 63 | 						{ projectRoot: args.projectRoot, file: args.file },
 64 | 						log
 65 | 					);
 66 | 				} catch (error) {
 67 | 					log.error(`Error finding tasks.json: ${error.message}`);
 68 | 					return createErrorResponse(
 69 | 						`Failed to find tasks.json: ${error.message}`
 70 | 					);
 71 | 				}
 72 | 
 73 | 				const result = await removeSubtaskDirect(
 74 | 					{
 75 | 						tasksJsonPath: tasksJsonPath,
 76 | 						id: args.id,
 77 | 						convert: args.convert,
 78 | 						skipGenerate: args.skipGenerate,
 79 | 						projectRoot: args.projectRoot,
 80 | 						tag: resolvedTag
 81 | 					},
 82 | 					log,
 83 | 					{ session }
 84 | 				);
 85 | 
 86 | 				if (result.success) {
 87 | 					log.info(`Subtask removed successfully: ${result.data.message}`);
 88 | 				} else {
 89 | 					log.error(`Failed to remove subtask: ${result.error.message}`);
 90 | 				}
 91 | 
 92 | 				return handleApiResult(
 93 | 					result,
 94 | 					log,
 95 | 					'Error removing subtask',
 96 | 					undefined,
 97 | 					args.projectRoot
 98 | 				);
 99 | 			} catch (error) {
100 | 				log.error(`Error in removeSubtask tool: ${error.message}`);
101 | 				return createErrorResponse(error.message);
102 | 			}
103 | 		})
104 | 	});
105 | }
106 | 
```

--------------------------------------------------------------------------------
/packages/ai-sdk-provider-grok-cli/src/json-extractor.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Tests for JSON extraction utilities
 3 |  */
 4 | 
 5 | import { describe, expect, it } from 'vitest';
 6 | import { extractJson } from './json-extractor.js';
 7 | 
 8 | describe('extractJson', () => {
 9 | 	it('should extract JSON from markdown code blocks', () => {
10 | 		const text = '```json\n{"name": "test", "value": 42}\n```';
11 | 		const result = extractJson(text);
12 | 		expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
13 | 	});
14 | 
15 | 	it('should extract JSON from generic code blocks', () => {
16 | 		const text = '```\n{"name": "test", "value": 42}\n```';
17 | 		const result = extractJson(text);
18 | 		expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
19 | 	});
20 | 
21 | 	it('should remove JavaScript variable declarations', () => {
22 | 		const text = 'const result = {"name": "test", "value": 42};';
23 | 		const result = extractJson(text);
24 | 		expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
25 | 	});
26 | 
27 | 	it('should handle let variable declarations', () => {
28 | 		const text = 'let data = {"name": "test", "value": 42};';
29 | 		const result = extractJson(text);
30 | 		expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
31 | 	});
32 | 
33 | 	it('should handle var variable declarations', () => {
34 | 		const text = 'var config = {"name": "test", "value": 42};';
35 | 		const result = extractJson(text);
36 | 		expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
37 | 	});
38 | 
39 | 	it('should extract JSON arrays', () => {
40 | 		const text = '[{"name": "test1"}, {"name": "test2"}]';
41 | 		const result = extractJson(text);
42 | 		expect(JSON.parse(result)).toEqual([{ name: 'test1' }, { name: 'test2' }]);
43 | 	});
44 | 
45 | 	it('should convert JavaScript object literals to JSON', () => {
46 | 		const text = "{name: 'test', value: 42}";
47 | 		const result = extractJson(text);
48 | 		expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
49 | 	});
50 | 
51 | 	it('should return valid JSON (canonical formatting)', () => {
52 | 		const text = '{"name": "test", "value": 42}';
53 | 		const result = extractJson(text);
54 | 		expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
55 | 	});
56 | 
57 | 	it('should return original text when JSON parsing fails completely', () => {
58 | 		const text = 'This is not JSON at all';
59 | 		const result = extractJson(text);
60 | 		expect(result).toBe('This is not JSON at all');
61 | 	});
62 | 
63 | 	it('should handle complex nested objects', () => {
64 | 		const text =
65 | 			'```json\n{\n  "user": {\n    "name": "John",\n    "age": 30\n  },\n  "items": [1, 2, 3]\n}\n```';
66 | 		const result = extractJson(text);
67 | 		expect(JSON.parse(result)).toEqual({
68 | 			user: {
69 | 				name: 'John',
70 | 				age: 30
71 | 			},
72 | 			items: [1, 2, 3]
73 | 		});
74 | 	});
75 | 
76 | 	it('should handle mixed quotes in object literals', () => {
77 | 		const text = `{name: "test", value: 'mixed quotes'}`;
78 | 		const result = extractJson(text);
79 | 		expect(JSON.parse(result)).toEqual({ name: 'test', value: 'mixed quotes' });
80 | 	});
81 | });
82 | 
```

--------------------------------------------------------------------------------
/packages/claude-code-plugin/commands/analyze-complexity.md:
--------------------------------------------------------------------------------

```markdown
  1 | Analyze task complexity and generate expansion recommendations.
  2 | 
  3 | Arguments: $ARGUMENTS
  4 | 
  5 | Perform deep analysis of task complexity across the project.
  6 | 
  7 | ## Complexity Analysis
  8 | 
  9 | Uses AI to analyze tasks and recommend which ones need breakdown.
 10 | 
 11 | ## Execution Options
 12 | 
 13 | ```bash
 14 | task-master analyze-complexity [--research] [--threshold=5]
 15 | ```
 16 | 
 17 | ## Analysis Parameters
 18 | 
 19 | - `--research` → Use research AI for deeper analysis
 20 | - `--threshold=5` → Only flag tasks above complexity 5
 21 | - Default: Analyze all pending tasks
 22 | 
 23 | ## Analysis Process
 24 | 
 25 | ### 1. **Task Evaluation**
 26 | For each task, AI evaluates:
 27 | - Technical complexity
 28 | - Time requirements
 29 | - Dependency complexity
 30 | - Risk factors
 31 | - Knowledge requirements
 32 | 
 33 | ### 2. **Complexity Scoring**
 34 | Assigns score 1-10 based on:
 35 | - Implementation difficulty
 36 | - Integration challenges
 37 | - Testing requirements
 38 | - Unknown factors
 39 | - Technical debt risk
 40 | 
 41 | ### 3. **Recommendations**
 42 | For complex tasks:
 43 | - Suggest expansion approach
 44 | - Recommend subtask breakdown
 45 | - Identify risk areas
 46 | - Propose mitigation strategies
 47 | 
 48 | ## Smart Analysis Features
 49 | 
 50 | 1. **Pattern Recognition**
 51 |    - Similar task comparisons
 52 |    - Historical complexity accuracy
 53 |    - Team velocity consideration
 54 |    - Technology stack factors
 55 | 
 56 | 2. **Contextual Factors**
 57 |    - Team expertise
 58 |    - Available resources
 59 |    - Timeline constraints
 60 |    - Business criticality
 61 | 
 62 | 3. **Risk Assessment**
 63 |    - Technical risks
 64 |    - Timeline risks
 65 |    - Dependency risks
 66 |    - Knowledge gaps
 67 | 
 68 | ## Output Format
 69 | 
 70 | ```
 71 | Task Complexity Analysis Report
 72 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 73 | 
 74 | High Complexity Tasks (>7):
 75 | 📍 #5 "Implement real-time sync" - Score: 9/10
 76 |    Factors: WebSocket complexity, state management, conflict resolution
 77 |    Recommendation: Expand into 5-7 subtasks
 78 |    Risks: Performance, data consistency
 79 | 
 80 | 📍 #12 "Migrate database schema" - Score: 8/10
 81 |    Factors: Data migration, zero downtime, rollback strategy
 82 |    Recommendation: Expand into 4-5 subtasks
 83 |    Risks: Data loss, downtime
 84 | 
 85 | Medium Complexity Tasks (5-7):
 86 | 📍 #23 "Add export functionality" - Score: 6/10
 87 |    Consider expansion if timeline tight
 88 | 
 89 | Low Complexity Tasks (<5):
 90 | ✅ 15 tasks - No expansion needed
 91 | 
 92 | Summary:
 93 | - Expand immediately: 2 tasks
 94 | - Consider expanding: 5 tasks
 95 | - Keep as-is: 15 tasks
 96 | ```
 97 | 
 98 | ## Actionable Output
 99 | 
100 | For each high-complexity task:
101 | 1. Complexity score with reasoning
102 | 2. Specific expansion suggestions
103 | 3. Risk mitigation approaches
104 | 4. Recommended subtask structure
105 | 
106 | ## Integration
107 | 
108 | Results are:
109 | - Saved to `.taskmaster/reports/complexity-analysis.md`
110 | - Used by expand command
111 | - Inform sprint planning
112 | - Guide resource allocation
113 | 
114 | ## Next Steps
115 | 
116 | After analysis:
117 | ```
118 | /taskmaster:expand 5    # Expand specific task
119 | /taskmaster:expand-all  # Expand all recommended
120 | /taskmaster:complexity-report  # View detailed report
121 | ```
```

--------------------------------------------------------------------------------
/apps/docs/configuration.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "Configuration"
 3 | description: "Configure Task Master through environment variables in a .env file"
 4 | ---
 5 | 
 6 | ## Required Configuration
 7 | 
 8 | <Note>
 9 |   Task Master requires an Anthropic API key to function. Add this to your `.env` file:
10 | 
11 |   ```bash
12 |   ANTHROPIC_API_KEY=sk-ant-api03-your-api-key
13 |   ```
14 | 
15 |   You can obtain an API key from the [Anthropic Console](https://console.anthropic.com/).
16 | </Note>
17 | 
18 | ## Optional Configuration
19 | 
20 | | Variable | Default Value | Description | Example |
21 | | --- | --- | --- | --- |
22 | | `MODEL` | `"claude-3-7-sonnet-20250219"` | Claude model to use | `MODEL=claude-3-opus-20240229` |
23 | | `MAX_TOKENS` | `"4000"` | Maximum tokens for responses | `MAX_TOKENS=8000` |
24 | | `TEMPERATURE` | `"0.7"` | Temperature for model responses | `TEMPERATURE=0.5` |
25 | | `DEBUG` | `"false"` | Enable debug logging | `DEBUG=true` |
26 | | `LOG_LEVEL` | `"info"` | Console output level | `LOG_LEVEL=debug` |
27 | | `DEFAULT_SUBTASKS` | `"3"` | Default subtask count | `DEFAULT_SUBTASKS=5` |
28 | | `DEFAULT_PRIORITY` | `"medium"` | Default priority | `DEFAULT_PRIORITY=high` |
29 | | `PROJECT_NAME` | `"MCP SaaS MVP"` | Project name in metadata | `PROJECT_NAME=My Awesome Project` |
30 | | `PROJECT_VERSION` | `"1.0.0"` | Version in metadata | `PROJECT_VERSION=2.1.0` |
31 | | `PERPLEXITY_API_KEY` | - | For research-backed features | `PERPLEXITY_API_KEY=pplx-...` |
32 | | `PERPLEXITY_MODEL` | `"sonar-medium-online"` | Perplexity model | `PERPLEXITY_MODEL=sonar-large-online` |
33 | 
34 | ## TDD Workflow Configuration
35 | 
36 | Additional options for autonomous TDD workflow:
37 | 
38 | | Variable | Default | Description |
39 | | --- | --- | --- |
40 | | `TM_MAX_ATTEMPTS` | `3` | Max attempts per subtask before marking blocked |
41 | | `TM_AUTO_COMMIT` | `true` | Auto-commit after GREEN phase |
42 | | `TM_PROJECT_ROOT` | Current dir | Default project root |
43 | 
44 | ## Example .env File
45 | 
46 | ```
47 | # Required
48 | ANTHROPIC_API_KEY=sk-ant-api03-your-api-key
49 | 
50 | # Optional - Claude Configuration
51 | MODEL=claude-3-7-sonnet-20250219
52 | MAX_TOKENS=4000
53 | TEMPERATURE=0.7
54 | 
55 | # Optional - Perplexity API for Research
56 | PERPLEXITY_API_KEY=pplx-your-api-key
57 | PERPLEXITY_MODEL=sonar-medium-online
58 | 
59 | # Optional - Project Info
60 | PROJECT_NAME=My Project
61 | PROJECT_VERSION=1.0.0
62 | 
63 | # Optional - Application Configuration
64 | DEFAULT_SUBTASKS=3
65 | DEFAULT_PRIORITY=medium
66 | DEBUG=false
67 | LOG_LEVEL=info
68 | 
69 | # TDD Workflow
70 | TM_MAX_ATTEMPTS=3
71 | TM_AUTO_COMMIT=true
72 | ```
73 | 
74 | ## Troubleshooting
75 | 
76 | ### If `task-master init` doesn't respond:
77 | 
78 | Try running it with Node directly:
79 | 
80 | ```bash
81 | node node_modules/claude-task-master/scripts/init.js
82 | ```
83 | 
84 | Or clone the repository and run:
85 | 
86 | ```bash
87 | git clone https://github.com/eyaltoledano/claude-task-master.git
88 | cd claude-task-master
89 | node scripts/init.js
90 | ```
91 | 
92 | <Note>
93 | For advanced configuration options and detailed customization, see our [Advanced Configuration Guide] page.
94 | </Note>
95 | 
```

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

```typescript
  1 | /**
  2 |  * @fileoverview Configuration Merger Service
  3 |  * Responsible for merging configurations from multiple sources with precedence
  4 |  */
  5 | 
  6 | import type { PartialConfiguration } from '../../../common/interfaces/configuration.interface.js';
  7 | 
  8 | /**
  9 |  * Configuration source with precedence
 10 |  */
 11 | export interface ConfigSource {
 12 | 	/** Source name for debugging */
 13 | 	name: string;
 14 | 	/** Configuration data from this source */
 15 | 	config: PartialConfiguration;
 16 | 	/** Precedence level (higher = more important) */
 17 | 	precedence: number;
 18 | }
 19 | 
 20 | /**
 21 |  * Configuration precedence levels (higher number = higher priority)
 22 |  */
 23 | export const CONFIG_PRECEDENCE = {
 24 | 	DEFAULTS: 0,
 25 | 	GLOBAL: 1, // Reserved for future implementation
 26 | 	LOCAL: 2,
 27 | 	ENVIRONMENT: 3
 28 | } as const;
 29 | 
 30 | /**
 31 |  * ConfigMerger handles merging configurations with precedence rules
 32 |  * Single responsibility: Configuration merging logic
 33 |  */
 34 | export class ConfigMerger {
 35 | 	private configSources: ConfigSource[] = [];
 36 | 
 37 | 	/**
 38 | 	 * Add a configuration source
 39 | 	 */
 40 | 	addSource(source: ConfigSource): void {
 41 | 		this.configSources.push(source);
 42 | 	}
 43 | 
 44 | 	/**
 45 | 	 * Clear all configuration sources
 46 | 	 */
 47 | 	clearSources(): void {
 48 | 		this.configSources = [];
 49 | 	}
 50 | 
 51 | 	/**
 52 | 	 * Merge all configuration sources based on precedence
 53 | 	 */
 54 | 	merge(): PartialConfiguration {
 55 | 		// Sort sources by precedence (lowest first)
 56 | 		const sortedSources = [...this.configSources].sort(
 57 | 			(a, b) => a.precedence - b.precedence
 58 | 		);
 59 | 
 60 | 		// Merge from lowest to highest precedence
 61 | 		let merged: PartialConfiguration = {};
 62 | 		for (const source of sortedSources) {
 63 | 			merged = this.deepMerge(merged, source.config);
 64 | 		}
 65 | 
 66 | 		return merged;
 67 | 	}
 68 | 
 69 | 	/**
 70 | 	 * Deep merge two configuration objects
 71 | 	 * Higher precedence values override lower ones
 72 | 	 */
 73 | 	private deepMerge(target: any, source: any): any {
 74 | 		if (!source) return target;
 75 | 		if (!target) return source;
 76 | 
 77 | 		const result = { ...target };
 78 | 
 79 | 		for (const key in source) {
 80 | 			if (source[key] === null || source[key] === undefined) {
 81 | 				continue;
 82 | 			}
 83 | 
 84 | 			if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
 85 | 				result[key] = this.deepMerge(result[key] || {}, source[key]);
 86 | 			} else {
 87 | 				result[key] = source[key];
 88 | 			}
 89 | 		}
 90 | 
 91 | 		return result;
 92 | 	}
 93 | 
 94 | 	/**
 95 | 	 * Get configuration sources for debugging
 96 | 	 */
 97 | 	getSources(): ConfigSource[] {
 98 | 		return [...this.configSources].sort((a, b) => b.precedence - a.precedence);
 99 | 	}
100 | 
101 | 	/**
102 | 	 * Check if a source exists
103 | 	 */
104 | 	hasSource(name: string): boolean {
105 | 		return this.configSources.some((source) => source.name === name);
106 | 	}
107 | 
108 | 	/**
109 | 	 * Remove a source by name
110 | 	 */
111 | 	removeSource(name: string): boolean {
112 | 		const initialLength = this.configSources.length;
113 | 		this.configSources = this.configSources.filter(
114 | 			(source) => source.name !== name
115 | 		);
116 | 		return this.configSources.length < initialLength;
117 | 	}
118 | }
119 | 
```

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | import fs from 'fs';
  3 | import path from 'path';
  4 | import os from 'os';
  5 | 
  6 | // Mock external modules
  7 | jest.mock('child_process', () => ({
  8 | 	execSync: jest.fn()
  9 | }));
 10 | 
 11 | // Mock console methods
 12 | jest.mock('console', () => ({
 13 | 	log: jest.fn(),
 14 | 	info: jest.fn(),
 15 | 	warn: jest.fn(),
 16 | 	error: jest.fn(),
 17 | 	clear: jest.fn()
 18 | }));
 19 | 
 20 | describe('Cline Integration', () => {
 21 | 	let tempDir;
 22 | 
 23 | 	beforeEach(() => {
 24 | 		jest.clearAllMocks();
 25 | 
 26 | 		// Create a temporary directory for testing
 27 | 		tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-master-test-'));
 28 | 
 29 | 		// Spy on fs methods
 30 | 		jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
 31 | 		jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
 32 | 			if (filePath.toString().includes('.clinerules')) {
 33 | 				return 'Existing cline rules content';
 34 | 			}
 35 | 			return '{}';
 36 | 		});
 37 | 		jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
 38 | 		jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
 39 | 	});
 40 | 
 41 | 	afterEach(() => {
 42 | 		// Clean up the temporary directory
 43 | 		try {
 44 | 			fs.rmSync(tempDir, { recursive: true, force: true });
 45 | 		} catch (err) {
 46 | 			console.error(`Error cleaning up: ${err.message}`);
 47 | 		}
 48 | 	});
 49 | 
 50 | 	// Test function that simulates the createProjectStructure behavior for Cline files
 51 | 	function mockCreateClineStructure() {
 52 | 		// Create main .clinerules directory
 53 | 		fs.mkdirSync(path.join(tempDir, '.clinerules'), { recursive: true });
 54 | 
 55 | 		// Create rule files
 56 | 		const ruleFiles = [
 57 | 			'dev_workflow.md',
 58 | 			'taskmaster.md',
 59 | 			'architecture.md',
 60 | 			'commands.md',
 61 | 			'dependencies.md'
 62 | 		];
 63 | 
 64 | 		for (const ruleFile of ruleFiles) {
 65 | 			fs.writeFileSync(
 66 | 				path.join(tempDir, '.clinerules', ruleFile),
 67 | 				`Content for ${ruleFile}`
 68 | 			);
 69 | 		}
 70 | 	}
 71 | 
 72 | 	test('creates all required .clinerules directories', () => {
 73 | 		// Act
 74 | 		mockCreateClineStructure();
 75 | 
 76 | 		// Assert
 77 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
 78 | 			path.join(tempDir, '.clinerules'),
 79 | 			{ recursive: true }
 80 | 		);
 81 | 	});
 82 | 
 83 | 	test('creates rule files for Cline', () => {
 84 | 		// Act
 85 | 		mockCreateClineStructure();
 86 | 
 87 | 		// Assert - check rule files are created
 88 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
 89 | 			path.join(tempDir, '.clinerules', 'dev_workflow.md'),
 90 | 			expect.any(String)
 91 | 		);
 92 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
 93 | 			path.join(tempDir, '.clinerules', 'taskmaster.md'),
 94 | 			expect.any(String)
 95 | 		);
 96 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
 97 | 			path.join(tempDir, '.clinerules', 'architecture.md'),
 98 | 			expect.any(String)
 99 | 		);
100 | 	});
101 | 
102 | 	test('does not create MCP configuration files', () => {
103 | 		// Act
104 | 		mockCreateClineStructure();
105 | 
106 | 		// Assert - Cline doesn't use MCP configuration
107 | 		expect(fs.writeFileSync).not.toHaveBeenCalledWith(
108 | 			path.join(tempDir, '.clinerules', 'mcp.json'),
109 | 			expect.any(String)
110 | 		);
111 | 	});
112 | });
113 | 
```

--------------------------------------------------------------------------------
/apps/cli/src/commands/models/types.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * @fileoverview Type definitions for model setup functionality
  3 |  */
  4 | 
  5 | /**
  6 |  * Represents a model role in the system
  7 |  */
  8 | export type ModelRole = 'main' | 'research' | 'fallback';
  9 | 
 10 | /**
 11 |  * Custom provider option identifiers
 12 |  */
 13 | export const CUSTOM_PROVIDER_IDS = {
 14 | 	OPENROUTER: '__CUSTOM_OPENROUTER__',
 15 | 	OLLAMA: '__CUSTOM_OLLAMA__',
 16 | 	BEDROCK: '__CUSTOM_BEDROCK__',
 17 | 	AZURE: '__CUSTOM_AZURE__',
 18 | 	VERTEX: '__CUSTOM_VERTEX__',
 19 | 	LMSTUDIO: '__CUSTOM_LMSTUDIO__',
 20 | 	OPENAI_COMPATIBLE: '__CUSTOM_OPENAI_COMPATIBLE__'
 21 | } as const;
 22 | 
 23 | export type CustomProviderId =
 24 | 	(typeof CUSTOM_PROVIDER_IDS)[keyof typeof CUSTOM_PROVIDER_IDS];
 25 | 
 26 | /**
 27 |  * Special control values for model selection
 28 |  */
 29 | export const CONTROL_VALUES = {
 30 | 	CANCEL: '__CANCEL__',
 31 | 	NO_CHANGE: '__NO_CHANGE__'
 32 | } as const;
 33 | 
 34 | /**
 35 |  * Model information for display
 36 |  */
 37 | export interface ModelInfo {
 38 | 	id: string;
 39 | 	name?: string;
 40 | 	provider: string;
 41 | 	cost_per_1m_tokens?: {
 42 | 		input: number;
 43 | 		output: number;
 44 | 	};
 45 | 	allowed_roles: ModelRole[];
 46 | }
 47 | 
 48 | /**
 49 |  * Currently configured model for a role
 50 |  */
 51 | export interface CurrentModel {
 52 | 	modelId?: string;
 53 | 	provider?: string;
 54 | 	baseURL?: string;
 55 | }
 56 | 
 57 | /**
 58 |  * Current models configuration
 59 |  */
 60 | export interface CurrentModels {
 61 | 	main: CurrentModel | null;
 62 | 	research: CurrentModel | null;
 63 | 	fallback: CurrentModel | null;
 64 | }
 65 | 
 66 | /**
 67 |  * Model selection choice for inquirer prompts
 68 |  */
 69 | export interface ModelChoice {
 70 | 	name: string;
 71 | 	value: { id: string; provider: string } | CustomProviderId | string | null;
 72 | 	short?: string;
 73 | 	type?: 'separator';
 74 | }
 75 | 
 76 | /**
 77 |  * Prompt data for a specific role
 78 |  */
 79 | export interface PromptData {
 80 | 	choices: (ModelChoice | any)[]; // any to accommodate Separator instances
 81 | 	default: number;
 82 | }
 83 | 
 84 | /**
 85 |  * Result from model fetcher functions
 86 |  */
 87 | export interface FetchResult<T> {
 88 | 	success: boolean;
 89 | 	data?: T;
 90 | 	error?: string;
 91 | }
 92 | 
 93 | /**
 94 |  * OpenRouter model response
 95 |  */
 96 | export interface OpenRouterModel {
 97 | 	id: string;
 98 | 	name?: string;
 99 | 	description?: string;
100 | }
101 | 
102 | /**
103 |  * Ollama model response
104 |  */
105 | export interface OllamaModel {
106 | 	model: string;
107 | 	name: string;
108 | 	modified_at?: string;
109 | }
110 | 
111 | /**
112 |  * Custom provider handler configuration
113 |  */
114 | export interface CustomProviderConfig {
115 | 	id: CustomProviderId;
116 | 	name: string;
117 | 	provider: string;
118 | 	promptMessage: (role: ModelRole) => string;
119 | 	validate?: (modelId: string, baseURL?: string) => Promise<boolean>;
120 | 	checkEnvVars?: () => boolean;
121 | 	fetchModels?: () => Promise<FetchResult<unknown[]>>;
122 | 	requiresBaseURL?: boolean;
123 | 	defaultBaseURL?: string;
124 | }
125 | 
126 | /**
127 |  * Model setup options
128 |  */
129 | export interface ModelSetupOptions {
130 | 	projectRoot: string;
131 | 	providerHint?: string;
132 | }
133 | 
134 | /**
135 |  * Model set result
136 |  */
137 | export interface ModelSetResult {
138 | 	success: boolean;
139 | 	data?: {
140 | 		message: string;
141 | 		provider: string;
142 | 		modelId: string;
143 | 		warning?: string;
144 | 	};
145 | 	error?: {
146 | 		message: string;
147 | 	};
148 | }
149 | 
```

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

```javascript
 1 | import { jest } from '@jest/globals';
 2 | import { ContextManager } from '../context-manager.js';
 3 | 
 4 | describe('ContextManager', () => {
 5 | 	let contextManager;
 6 | 
 7 | 	beforeEach(() => {
 8 | 		contextManager = new ContextManager({
 9 | 			maxCacheSize: 10,
10 | 			ttl: 1000, // 1 second for testing
11 | 			maxContextSize: 1000
12 | 		});
13 | 	});
14 | 
15 | 	describe('getContext', () => {
16 | 		it('should create a new context when not in cache', async () => {
17 | 			const context = await contextManager.getContext('test-id', {
18 | 				test: true
19 | 			});
20 | 			expect(context.id).toBe('test-id');
21 | 			expect(context.metadata.test).toBe(true);
22 | 			expect(contextManager.stats.misses).toBe(1);
23 | 			expect(contextManager.stats.hits).toBe(0);
24 | 		});
25 | 
26 | 		it('should return cached context when available', async () => {
27 | 			// First call creates the context
28 | 			await contextManager.getContext('test-id', { test: true });
29 | 
30 | 			// Second call should hit cache
31 | 			const context = await contextManager.getContext('test-id', {
32 | 				test: true
33 | 			});
34 | 			expect(context.id).toBe('test-id');
35 | 			expect(context.metadata.test).toBe(true);
36 | 			expect(contextManager.stats.hits).toBe(1);
37 | 			expect(contextManager.stats.misses).toBe(1);
38 | 		});
39 | 
40 | 		it('should respect TTL settings', async () => {
41 | 			// Create context
42 | 			await contextManager.getContext('test-id', { test: true });
43 | 
44 | 			// Wait for TTL to expire
45 | 			await new Promise((resolve) => setTimeout(resolve, 1100));
46 | 
47 | 			// Should create new context
48 | 			await contextManager.getContext('test-id', { test: true });
49 | 			expect(contextManager.stats.misses).toBe(2);
50 | 			expect(contextManager.stats.hits).toBe(0);
51 | 		});
52 | 	});
53 | 
54 | 	describe('updateContext', () => {
55 | 		it('should update existing context metadata', async () => {
56 | 			await contextManager.getContext('test-id', { initial: true });
57 | 			const updated = await contextManager.updateContext('test-id', {
58 | 				updated: true
59 | 			});
60 | 
61 | 			expect(updated.metadata.initial).toBe(true);
62 | 			expect(updated.metadata.updated).toBe(true);
63 | 		});
64 | 	});
65 | 
66 | 	describe('invalidateContext', () => {
67 | 		it('should remove context from cache', async () => {
68 | 			await contextManager.getContext('test-id', { test: true });
69 | 			contextManager.invalidateContext('test-id', { test: true });
70 | 
71 | 			// Should be a cache miss
72 | 			await contextManager.getContext('test-id', { test: true });
73 | 			expect(contextManager.stats.invalidations).toBe(1);
74 | 			expect(contextManager.stats.misses).toBe(2);
75 | 		});
76 | 	});
77 | 
78 | 	describe('getStats', () => {
79 | 		it('should return current cache statistics', async () => {
80 | 			await contextManager.getContext('test-id', { test: true });
81 | 			const stats = contextManager.getStats();
82 | 
83 | 			expect(stats.hits).toBe(0);
84 | 			expect(stats.misses).toBe(1);
85 | 			expect(stats.invalidations).toBe(0);
86 | 			expect(stats.size).toBe(1);
87 | 			expect(stats.maxSize).toBe(10);
88 | 			expect(stats.ttl).toBe(1000);
89 | 		});
90 | 	});
91 | });
92 | 
```

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

```typescript
  1 | /**
  2 |  * Grok CLI provider implementation for AI SDK v5
  3 |  */
  4 | 
  5 | import type { LanguageModelV2, ProviderV2 } from '@ai-sdk/provider';
  6 | import { NoSuchModelError } from '@ai-sdk/provider';
  7 | import { GrokCliLanguageModel } from './grok-cli-language-model.js';
  8 | import type { GrokCliModelId, GrokCliSettings } from './types.js';
  9 | 
 10 | /**
 11 |  * Grok CLI provider interface that extends the AI SDK's ProviderV2
 12 |  */
 13 | export interface GrokCliProvider extends ProviderV2 {
 14 | 	/**
 15 | 	 * Creates a language model instance for the specified model ID.
 16 | 	 * This is a shorthand for calling `languageModel()`.
 17 | 	 */
 18 | 	(modelId: GrokCliModelId, settings?: GrokCliSettings): LanguageModelV2;
 19 | 
 20 | 	/**
 21 | 	 * Creates a language model instance for text generation.
 22 | 	 */
 23 | 	languageModel(
 24 | 		modelId: GrokCliModelId,
 25 | 		settings?: GrokCliSettings
 26 | 	): LanguageModelV2;
 27 | 
 28 | 	/**
 29 | 	 * Alias for `languageModel()` to maintain compatibility with AI SDK patterns.
 30 | 	 */
 31 | 	chat(modelId: GrokCliModelId, settings?: GrokCliSettings): LanguageModelV2;
 32 | 
 33 | 	textEmbeddingModel(modelId: string): never;
 34 | 	imageModel(modelId: string): never;
 35 | }
 36 | 
 37 | /**
 38 |  * Configuration options for creating a Grok CLI provider instance
 39 |  */
 40 | export interface GrokCliProviderSettings {
 41 | 	/**
 42 | 	 * Default settings to use for all models created by this provider.
 43 | 	 * Individual model settings will override these defaults.
 44 | 	 */
 45 | 	defaultSettings?: GrokCliSettings;
 46 | }
 47 | 
 48 | /**
 49 |  * Creates a Grok CLI provider instance with the specified configuration.
 50 |  * The provider can be used to create language models for interacting with Grok models.
 51 |  */
 52 | export function createGrokCli(
 53 | 	options: GrokCliProviderSettings = {}
 54 | ): GrokCliProvider {
 55 | 	const createModel = (
 56 | 		modelId: GrokCliModelId,
 57 | 		settings: GrokCliSettings = {}
 58 | 	): LanguageModelV2 => {
 59 | 		const mergedSettings = {
 60 | 			...options.defaultSettings,
 61 | 			...settings
 62 | 		};
 63 | 
 64 | 		return new GrokCliLanguageModel({
 65 | 			id: modelId,
 66 | 			settings: mergedSettings
 67 | 		});
 68 | 	};
 69 | 
 70 | 	const provider = function (
 71 | 		modelId: GrokCliModelId,
 72 | 		settings?: GrokCliSettings
 73 | 	) {
 74 | 		if (new.target) {
 75 | 			throw new Error(
 76 | 				'The Grok CLI model function cannot be called with the new keyword.'
 77 | 			);
 78 | 		}
 79 | 
 80 | 		return createModel(modelId, settings);
 81 | 	};
 82 | 
 83 | 	provider.languageModel = createModel;
 84 | 	provider.chat = createModel; // Alias for languageModel
 85 | 
 86 | 	// Add textEmbeddingModel method that throws NoSuchModelError
 87 | 	provider.textEmbeddingModel = (modelId: string) => {
 88 | 		throw new NoSuchModelError({
 89 | 			modelId,
 90 | 			modelType: 'textEmbeddingModel'
 91 | 		});
 92 | 	};
 93 | 
 94 | 	provider.imageModel = (modelId: string) => {
 95 | 		throw new NoSuchModelError({
 96 | 			modelId,
 97 | 			modelType: 'imageModel'
 98 | 		});
 99 | 	};
100 | 
101 | 	return provider as GrokCliProvider;
102 | }
103 | 
104 | /**
105 |  * Default Grok CLI provider instance.
106 |  * Pre-configured provider for quick usage without custom settings.
107 |  */
108 | export const grokCli = createGrokCli();
109 | 
```

--------------------------------------------------------------------------------
/packages/tm-core/src/common/utils/path-normalizer.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Path normalization utilities for global storage system.
 3 |  * Converts project paths to storage-safe directory names using base64url encoding.
 4 |  *
 5 |  * This provides a bijective (one-to-one) mapping that preserves all characters
 6 |  * and supports perfect round-trip conversion between paths and storage names.
 7 |  *
 8 |  * @module path-normalizer
 9 |  */
10 | 
11 | /**
12 |  * Normalizes a project path to a storage-safe directory name using base64url encoding.
13 |  * This encoding is filesystem-safe (no slashes, backslashes, or special characters)
14 |  * and fully reversible, preserving hyphens and all other characters in paths.
15 |  *
16 |  * @param {string} projectPath - The project path to normalize
17 |  * @returns {string} The base64url-encoded path safe for use as a directory name
18 |  *
19 |  * @example
20 |  * normalizeProjectPath('/Users/test/project') // returns base64url encoded string
21 |  * normalizeProjectPath('C:\\Users\\test') // returns base64url encoded string
22 |  * normalizeProjectPath('/projects/my-app') // returns base64url encoded string (hyphens preserved)
23 |  */
24 | export function normalizeProjectPath(projectPath: string): string {
25 | 	if (!projectPath) {
26 | 		return '';
27 | 	}
28 | 
29 | 	// Use base64url encoding: filesystem-safe and fully reversible
30 | 	return Buffer.from(projectPath, 'utf-8').toString('base64url');
31 | }
32 | 
33 | /**
34 |  * Denormalizes a storage directory name back to the original path.
35 |  * Decodes base64url-encoded paths with perfect fidelity.
36 |  *
37 |  * @param {string} normalizedPath - The base64url-encoded path to decode
38 |  * @returns {string} The original path with all characters preserved
39 |  *
40 |  * @example
41 |  * denormalizeProjectPath(normalizeProjectPath('/Users/test/project')) // returns '/Users/test/project'
42 |  * denormalizeProjectPath(normalizeProjectPath('/projects/my-app')) // returns '/projects/my-app'
43 |  */
44 | export function denormalizeProjectPath(normalizedPath: string): string {
45 | 	if (!normalizedPath) {
46 | 		return '';
47 | 	}
48 | 
49 | 	// Validate that input is valid base64url before attempting to decode
50 | 	if (!isValidNormalizedPath(normalizedPath)) {
51 | 		// Return original string for backward compatibility with non-base64url inputs
52 | 		return normalizedPath;
53 | 	}
54 | 
55 | 	return Buffer.from(normalizedPath, 'base64url').toString('utf-8');
56 | }
57 | 
58 | /**
59 |  * Validates whether a path is in normalized (base64url) format.
60 |  * Valid base64url strings contain only: A-Z, a-z, 0-9, -, _
61 |  *
62 |  * @param {string} path - The path to validate
63 |  * @returns {boolean} True if the path is in normalized base64url format
64 |  *
65 |  * @example
66 |  * isValidNormalizedPath('VXNlcnMvdGVzdC9wcm9qZWN0') // returns true (valid base64url)
67 |  * isValidNormalizedPath('Users/test/project') // returns false (contains slashes)
68 |  */
69 | export function isValidNormalizedPath(path: string): boolean {
70 | 	if (path === '') {
71 | 		return true;
72 | 	}
73 | 
74 | 	// Check if path is valid base64url: only A-Z, a-z, 0-9, -, _
75 | 	return /^[A-Za-z0-9_-]*$/.test(path);
76 | }
77 | 
```

--------------------------------------------------------------------------------
/CLAUDE_CODE_PLUGIN.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Taskmaster AI - Claude Code Marketplace
  2 | 
  3 | This repository includes a Claude Code plugin marketplace in `.claude-plugin/marketplace.json`.
  4 | 
  5 | ## Installation
  6 | 
  7 | ### From GitHub (Public Repository)
  8 | 
  9 | Once this repository is pushed to GitHub, users can install with:
 10 | 
 11 | ```bash
 12 | # Add the marketplace
 13 | /plugin marketplace add eyaltoledano/claude-task-master
 14 | 
 15 | # Install the plugin
 16 | /plugin install taskmaster@taskmaster
 17 | ```
 18 | 
 19 | ### Local Development/Testing
 20 | 
 21 | ```bash
 22 | # From the project root directory
 23 | cd /path/to/claude-task-master
 24 | 
 25 | # Build the plugin first
 26 | cd packages/claude-code-plugin
 27 | npm run build
 28 | cd ../..
 29 | 
 30 | # In Claude Code
 31 | /plugin marketplace add .
 32 | /plugin install taskmaster@taskmaster
 33 | ```
 34 | 
 35 | ## Marketplace Structure
 36 | 
 37 | ```
 38 | claude-task-master/
 39 | ├── .claude-plugin/
 40 | │   └── marketplace.json        # Marketplace manifest (at repo root)
 41 | │
 42 | ├── packages/claude-code-plugin/
 43 | │   ├── src/build.ts           # Build tooling
 44 | │   └── [generated plugin files]
 45 | │
 46 | └── assets/claude/              # Plugin source files
 47 |     ├── commands/
 48 |     └── agents/
 49 | ```
 50 | 
 51 | ## Available Plugins
 52 | 
 53 | ### taskmaster
 54 | 
 55 | AI-powered task management system for ambitious development workflows.
 56 | 
 57 | **Features:**
 58 | 
 59 | - 49 slash commands for comprehensive task management
 60 | - 3 specialized AI agents (orchestrator, executor, checker)
 61 | - MCP server integration
 62 | - Complexity analysis and auto-expansion
 63 | - Dependency management and validation
 64 | - Automated workflow capabilities
 65 | 
 66 | **Quick Start:**
 67 | 
 68 | ```bash
 69 | /tm:init
 70 | /tm:parse-prd
 71 | /tm:next
 72 | ```
 73 | 
 74 | ## For Contributors
 75 | 
 76 | ### Adding New Plugins
 77 | 
 78 | To add more plugins to this marketplace:
 79 | 
 80 | 1. **Update marketplace.json**:
 81 | 
 82 |    ```json
 83 |    {
 84 |      "plugins": [
 85 |        {
 86 |          "name": "new-plugin",
 87 |          "source": "./path/to/plugin",
 88 |          "description": "Plugin description",
 89 |          "version": "1.0.0"
 90 |        }
 91 |      ]
 92 |    }
 93 |    ```
 94 | 
 95 | 2. **Commit and push** the changes
 96 | 
 97 | 3. **Users update** with: `/plugin marketplace update taskmaster`
 98 | 
 99 | ### Marketplace Versioning
100 | 
101 | The marketplace version is tracked in `.claude-plugin/marketplace.json`:
102 | 
103 | ```json
104 | {
105 |   "metadata": {
106 |     "version": "1.0.0"
107 |   }
108 | }
109 | ```
110 | 
111 | Increment the version when adding or updating plugins.
112 | 
113 | ## Team Configuration
114 | 
115 | Organizations can auto-install this marketplace for all team members by adding to `.claude/settings.json`:
116 | 
117 | ```json
118 | {
119 |   "extraKnownMarketplaces": {
120 |     "task-master": {
121 |       "source": {
122 |         "source": "github",
123 |         "repo": "eyaltoledano/claude-task-master"
124 |       }
125 |     }
126 |   },
127 |   "enabledPlugins": {
128 |     "taskmaster": {
129 |       "marketplace": "taskmaster"
130 |     }
131 |   }
132 | }
133 | ```
134 | 
135 | Team members who trust the repository folder will automatically get the marketplace and plugins installed.
136 | 
137 | ## Documentation
138 | 
139 | - [Claude Code Plugin Docs](https://docs.claude.com/en/docs/claude-code/plugins)
140 | - [Marketplace Documentation](https://docs.claude.com/en/docs/claude-code/plugin-marketplaces)
141 | 
```

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

```typescript
  1 | import * as vscode from 'vscode';
  2 | 
  3 | /**
  4 |  * Logger interface for dependency injection
  5 |  */
  6 | export interface ILogger {
  7 | 	log(message: string, ...args: any[]): void;
  8 | 	error(message: string, ...args: any[]): void;
  9 | 	warn(message: string, ...args: any[]): void;
 10 | 	debug(message: string, ...args: any[]): void;
 11 | 	show(): void;
 12 | 	dispose(): void;
 13 | }
 14 | 
 15 | /**
 16 |  * Logger that outputs to VS Code's output channel instead of console
 17 |  * This prevents interference with MCP stdio communication
 18 |  */
 19 | export class ExtensionLogger implements ILogger {
 20 | 	private static instance: ExtensionLogger;
 21 | 	private outputChannel: vscode.OutputChannel;
 22 | 	private debugMode: boolean;
 23 | 
 24 | 	private constructor() {
 25 | 		this.outputChannel = vscode.window.createOutputChannel('TaskMaster');
 26 | 		const config = vscode.workspace.getConfiguration('taskmaster');
 27 | 		this.debugMode = config.get<boolean>('debug.enableLogging', true);
 28 | 	}
 29 | 
 30 | 	static getInstance(): ExtensionLogger {
 31 | 		if (!ExtensionLogger.instance) {
 32 | 			ExtensionLogger.instance = new ExtensionLogger();
 33 | 		}
 34 | 		return ExtensionLogger.instance;
 35 | 	}
 36 | 
 37 | 	log(message: string, ...args: any[]): void {
 38 | 		if (!this.debugMode) {
 39 | 			return;
 40 | 		}
 41 | 		const timestamp = new Date().toISOString();
 42 | 		const formattedMessage = this.formatMessage(message, args);
 43 | 		this.outputChannel.appendLine(`[${timestamp}] ${formattedMessage}`);
 44 | 	}
 45 | 
 46 | 	error(message: string, ...args: any[]): void {
 47 | 		const timestamp = new Date().toISOString();
 48 | 		const formattedMessage = this.formatMessage(message, args);
 49 | 		this.outputChannel.appendLine(`[${timestamp}] ERROR: ${formattedMessage}`);
 50 | 	}
 51 | 
 52 | 	warn(message: string, ...args: any[]): void {
 53 | 		if (!this.debugMode) {
 54 | 			return;
 55 | 		}
 56 | 		const timestamp = new Date().toISOString();
 57 | 		const formattedMessage = this.formatMessage(message, args);
 58 | 		this.outputChannel.appendLine(`[${timestamp}] WARN: ${formattedMessage}`);
 59 | 	}
 60 | 
 61 | 	debug(message: string, ...args: any[]): void {
 62 | 		if (!this.debugMode) {
 63 | 			return;
 64 | 		}
 65 | 		const timestamp = new Date().toISOString();
 66 | 		const formattedMessage = this.formatMessage(message, args);
 67 | 		this.outputChannel.appendLine(`[${timestamp}] DEBUG: ${formattedMessage}`);
 68 | 	}
 69 | 
 70 | 	private formatMessage(message: string, args: any[]): string {
 71 | 		if (args.length === 0) {
 72 | 			return message;
 73 | 		}
 74 | 
 75 | 		// Convert objects to JSON for better readability
 76 | 		const formattedArgs = args.map((arg) => {
 77 | 			if (typeof arg === 'object' && arg !== null) {
 78 | 				try {
 79 | 					return JSON.stringify(arg, null, 2);
 80 | 				} catch {
 81 | 					return String(arg);
 82 | 				}
 83 | 			}
 84 | 			return String(arg);
 85 | 		});
 86 | 
 87 | 		return `${message} ${formattedArgs.join(' ')}`;
 88 | 	}
 89 | 
 90 | 	show(): void {
 91 | 		this.outputChannel.show();
 92 | 	}
 93 | 
 94 | 	dispose(): void {
 95 | 		this.outputChannel.dispose();
 96 | 	}
 97 | 
 98 | 	setDebugMode(enabled: boolean): void {
 99 | 		this.debugMode = enabled;
100 | 	}
101 | }
102 | 
103 | // Export a singleton instance for convenience
104 | export const logger = ExtensionLogger.getInstance();
105 | 
```

--------------------------------------------------------------------------------
/src/progress/progress-tracker-builder.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * Configuration for progress tracker features
  3 |  */
  4 | class TrackerConfig {
  5 | 	constructor() {
  6 | 		this.features = new Set();
  7 | 		this.spinnerFrames = null;
  8 | 		this.unitName = 'unit';
  9 | 		this.totalUnits = 100;
 10 | 	}
 11 | 
 12 | 	addFeature(feature) {
 13 | 		this.features.add(feature);
 14 | 	}
 15 | 
 16 | 	hasFeature(feature) {
 17 | 		return this.features.has(feature);
 18 | 	}
 19 | 
 20 | 	getOptions() {
 21 | 		return {
 22 | 			numUnits: this.totalUnits,
 23 | 			unitName: this.unitName,
 24 | 			spinnerFrames: this.spinnerFrames,
 25 | 			features: Array.from(this.features)
 26 | 		};
 27 | 	}
 28 | }
 29 | 
 30 | /**
 31 |  * Builder for creating configured progress trackers
 32 |  */
 33 | export class ProgressTrackerBuilder {
 34 | 	constructor() {
 35 | 		this.config = new TrackerConfig();
 36 | 	}
 37 | 
 38 | 	withPercent() {
 39 | 		this.config.addFeature('percent');
 40 | 		return this;
 41 | 	}
 42 | 
 43 | 	withTokens() {
 44 | 		this.config.addFeature('tokens');
 45 | 		return this;
 46 | 	}
 47 | 
 48 | 	withTasks() {
 49 | 		this.config.addFeature('tasks');
 50 | 		return this;
 51 | 	}
 52 | 
 53 | 	withSpinner(messages) {
 54 | 		if (!messages || !Array.isArray(messages)) {
 55 | 			throw new Error('Spinner messages must be an array');
 56 | 		}
 57 | 		this.config.spinnerFrames = messages;
 58 | 		return this;
 59 | 	}
 60 | 
 61 | 	withUnits(total, unitName = 'unit') {
 62 | 		this.config.totalUnits = total;
 63 | 		this.config.unitName = unitName;
 64 | 		return this;
 65 | 	}
 66 | 
 67 | 	build() {
 68 | 		return new ProgressTracker(this.config);
 69 | 	}
 70 | }
 71 | 
 72 | /**
 73 |  * Base progress tracker with configurable features
 74 |  */
 75 | class ProgressTracker {
 76 | 	constructor(config) {
 77 | 		this.config = config;
 78 | 		this.isActive = false;
 79 | 		this.current = 0;
 80 | 		this.spinnerIndex = 0;
 81 | 		this.startTime = null;
 82 | 	}
 83 | 
 84 | 	start() {
 85 | 		this.isActive = true;
 86 | 		this.startTime = Date.now();
 87 | 		this.current = 0;
 88 | 
 89 | 		if (this.config.spinnerFrames) {
 90 | 			this._startSpinner();
 91 | 		}
 92 | 	}
 93 | 
 94 | 	update(data = {}) {
 95 | 		if (!this.isActive) return;
 96 | 
 97 | 		if (data.current !== undefined) {
 98 | 			this.current = data.current;
 99 | 		}
100 | 
101 | 		const progress = this._buildProgressData(data);
102 | 		return progress;
103 | 	}
104 | 
105 | 	finish() {
106 | 		this.isActive = false;
107 | 
108 | 		if (this.spinnerInterval) {
109 | 			clearInterval(this.spinnerInterval);
110 | 			this.spinnerInterval = null;
111 | 		}
112 | 
113 | 		return this._buildSummary();
114 | 	}
115 | 
116 | 	_startSpinner() {
117 | 		this.spinnerInterval = setInterval(() => {
118 | 			this.spinnerIndex =
119 | 				(this.spinnerIndex + 1) % this.config.spinnerFrames.length;
120 | 		}, 100);
121 | 	}
122 | 
123 | 	_buildProgressData(data) {
124 | 		const progress = { ...data };
125 | 
126 | 		if (this.config.hasFeature('percent')) {
127 | 			progress.percentage = Math.round(
128 | 				(this.current / this.config.totalUnits) * 100
129 | 			);
130 | 		}
131 | 
132 | 		if (this.config.hasFeature('tasks')) {
133 | 			progress.tasks = `${this.current}/${this.config.totalUnits}`;
134 | 		}
135 | 
136 | 		if (this.config.spinnerFrames) {
137 | 			progress.spinner = this.config.spinnerFrames[this.spinnerIndex];
138 | 		}
139 | 
140 | 		return progress;
141 | 	}
142 | 
143 | 	_buildSummary() {
144 | 		const elapsed = Date.now() - this.startTime;
145 | 		return {
146 | 			total: this.config.totalUnits,
147 | 			completed: this.current,
148 | 			elapsedMs: elapsed,
149 | 			features: Array.from(this.config.features)
150 | 		};
151 | 	}
152 | }
153 | 
```

--------------------------------------------------------------------------------
/src/profiles/kiro.js:
--------------------------------------------------------------------------------

```javascript
 1 | // Kiro profile for rule-transformer
 2 | import { createProfile } from './base-profile.js';
 3 | import fs from 'fs';
 4 | import path from 'path';
 5 | import { log } from '../../scripts/modules/utils.js';
 6 | 
 7 | // Create and export kiro profile using the base factory
 8 | export const kiroProfile = createProfile({
 9 | 	name: 'kiro',
10 | 	displayName: 'Kiro',
11 | 	url: 'kiro.dev',
12 | 	docsUrl: 'kiro.dev/docs',
13 | 	profileDir: '.kiro',
14 | 	rulesDir: '.kiro/steering', // Kiro rules location (full path)
15 | 	mcpConfig: true,
16 | 	mcpConfigName: 'settings/mcp.json', // Create directly in settings subdirectory
17 | 	includeDefaultRules: true, // Include default rules to get all the standard files
18 | 	targetExtension: '.md',
19 | 	fileMap: {
20 | 		// Override specific mappings - the base profile will create:
21 | 		// 'rules/cursor_rules.mdc': 'kiro_rules.md'
22 | 		// 'rules/dev_workflow.mdc': 'dev_workflow.md'
23 | 		// 'rules/self_improve.mdc': 'self_improve.md'
24 | 		// 'rules/taskmaster.mdc': 'taskmaster.md'
25 | 		// We can add additional custom mappings here if needed
26 | 		'rules/taskmaster_hooks_workflow.mdc': 'taskmaster_hooks_workflow.md'
27 | 	},
28 | 	customReplacements: [
29 | 		// Core Kiro directory structure changes
30 | 		{ from: /\.cursor\/rules/g, to: '.kiro/steering' },
31 | 		{ from: /\.cursor\/mcp\.json/g, to: '.kiro/settings/mcp.json' },
32 | 
33 | 		// Fix any remaining kiro/rules references that might be created during transformation
34 | 		{ from: /\.kiro\/rules/g, to: '.kiro/steering' },
35 | 
36 | 		// Essential markdown link transformations for Kiro structure
37 | 		{
38 | 			from: /\[(.+?)\]\(mdc:\.cursor\/rules\/(.+?)\.mdc\)/g,
39 | 			to: '[$1](.kiro/steering/$2.md)'
40 | 		},
41 | 
42 | 		// Kiro specific terminology
43 | 		{ from: /rules directory/g, to: 'steering directory' },
44 | 		{ from: /cursor rules/gi, to: 'Kiro steering files' },
45 | 
46 | 		// Transform frontmatter to Kiro format
47 | 		// This regex matches the entire frontmatter block and replaces it
48 | 		{
49 | 			from: /^---\n(?:description:\s*[^\n]*\n)?(?:globs:\s*[^\n]*\n)?(?:alwaysApply:\s*true\n)?---/m,
50 | 			to: '---\ninclusion: always\n---'
51 | 		}
52 | 	],
53 | 
54 | 	// Add lifecycle hook to copy Kiro hooks
55 | 	onPostConvert: (projectRoot, assetsDir) => {
56 | 		const hooksSourceDir = path.join(assetsDir, 'kiro-hooks');
57 | 		const hooksTargetDir = path.join(projectRoot, '.kiro', 'hooks');
58 | 
59 | 		// Create hooks directory if it doesn't exist
60 | 		if (!fs.existsSync(hooksTargetDir)) {
61 | 			fs.mkdirSync(hooksTargetDir, { recursive: true });
62 | 		}
63 | 
64 | 		// Copy all .kiro.hook files
65 | 		if (fs.existsSync(hooksSourceDir)) {
66 | 			const hookFiles = fs
67 | 				.readdirSync(hooksSourceDir)
68 | 				.filter((f) => f.endsWith('.kiro.hook'));
69 | 
70 | 			hookFiles.forEach((file) => {
71 | 				const sourcePath = path.join(hooksSourceDir, file);
72 | 				const targetPath = path.join(hooksTargetDir, file);
73 | 
74 | 				fs.copyFileSync(sourcePath, targetPath);
75 | 			});
76 | 
77 | 			if (hookFiles.length > 0) {
78 | 				log(
79 | 					'info',
80 | 					`[Kiro] Installed ${hookFiles.length} Taskmaster hooks in .kiro/hooks/`
81 | 				);
82 | 			}
83 | 		}
84 | 	}
85 | });
86 | 
```

--------------------------------------------------------------------------------
/apps/extension/src/services/notification-preferences.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Notification Preferences Service
  3 |  * Manages user preferences for notifications
  4 |  */
  5 | 
  6 | import * as vscode from 'vscode';
  7 | import { ErrorCategory, ErrorSeverity } from './error-handler';
  8 | 
  9 | export enum NotificationLevel {
 10 | 	ALL = 'all',
 11 | 	ERRORS_ONLY = 'errors_only',
 12 | 	CRITICAL_ONLY = 'critical_only',
 13 | 	NONE = 'none'
 14 | }
 15 | 
 16 | interface NotificationRule {
 17 | 	category: ErrorCategory;
 18 | 	minSeverity: ErrorSeverity;
 19 | 	enabled: boolean;
 20 | }
 21 | 
 22 | export class NotificationPreferences {
 23 | 	private defaultRules: NotificationRule[] = [
 24 | 		{
 25 | 			category: ErrorCategory.MCP_CONNECTION,
 26 | 			minSeverity: ErrorSeverity.HIGH,
 27 | 			enabled: true
 28 | 		},
 29 | 		{
 30 | 			category: ErrorCategory.CONFIGURATION,
 31 | 			minSeverity: ErrorSeverity.MEDIUM,
 32 | 			enabled: true
 33 | 		},
 34 | 		{
 35 | 			category: ErrorCategory.TASK_LOADING,
 36 | 			minSeverity: ErrorSeverity.HIGH,
 37 | 			enabled: true
 38 | 		},
 39 | 		{
 40 | 			category: ErrorCategory.NETWORK,
 41 | 			minSeverity: ErrorSeverity.HIGH,
 42 | 			enabled: true
 43 | 		},
 44 | 		{
 45 | 			category: ErrorCategory.INTERNAL,
 46 | 			minSeverity: ErrorSeverity.CRITICAL,
 47 | 			enabled: true
 48 | 		}
 49 | 	];
 50 | 
 51 | 	/**
 52 | 	 * Check if a notification should be shown
 53 | 	 */
 54 | 	shouldShowNotification(
 55 | 		category: ErrorCategory,
 56 | 		severity: ErrorSeverity
 57 | 	): boolean {
 58 | 		// Get user's notification level preference
 59 | 		const level = this.getNotificationLevel();
 60 | 
 61 | 		if (level === NotificationLevel.NONE) {
 62 | 			return false;
 63 | 		}
 64 | 
 65 | 		if (
 66 | 			level === NotificationLevel.CRITICAL_ONLY &&
 67 | 			severity !== ErrorSeverity.CRITICAL
 68 | 		) {
 69 | 			return false;
 70 | 		}
 71 | 
 72 | 		if (
 73 | 			level === NotificationLevel.ERRORS_ONLY &&
 74 | 			severity !== ErrorSeverity.CRITICAL &&
 75 | 			severity !== ErrorSeverity.HIGH
 76 | 		) {
 77 | 			return false;
 78 | 		}
 79 | 
 80 | 		// Check category-specific rules
 81 | 		const rule = this.defaultRules.find((r) => r.category === category);
 82 | 		if (!rule || !rule.enabled) {
 83 | 			return false;
 84 | 		}
 85 | 
 86 | 		// Check if severity meets minimum threshold
 87 | 		return this.compareSeverity(severity, rule.minSeverity) >= 0;
 88 | 	}
 89 | 
 90 | 	/**
 91 | 	 * Get user's notification level preference
 92 | 	 */
 93 | 	private getNotificationLevel(): NotificationLevel {
 94 | 		const config = vscode.workspace.getConfiguration('taskmaster');
 95 | 		return config.get<NotificationLevel>(
 96 | 			'notifications.level',
 97 | 			NotificationLevel.ERRORS_ONLY
 98 | 		);
 99 | 	}
100 | 
101 | 	/**
102 | 	 * Compare severity levels
103 | 	 */
104 | 	private compareSeverity(a: ErrorSeverity, b: ErrorSeverity): number {
105 | 		const severityOrder = {
106 | 			[ErrorSeverity.LOW]: 0,
107 | 			[ErrorSeverity.MEDIUM]: 1,
108 | 			[ErrorSeverity.HIGH]: 2,
109 | 			[ErrorSeverity.CRITICAL]: 3
110 | 		};
111 | 		return severityOrder[a] - severityOrder[b];
112 | 	}
113 | 
114 | 	/**
115 | 	 * Get toast notification duration based on severity
116 | 	 */
117 | 	getToastDuration(severity: ErrorSeverity): number {
118 | 		switch (severity) {
119 | 			case ErrorSeverity.CRITICAL:
120 | 				return 10000; // 10 seconds
121 | 			case ErrorSeverity.HIGH:
122 | 				return 7000; // 7 seconds
123 | 			case ErrorSeverity.MEDIUM:
124 | 				return 5000; // 5 seconds
125 | 			case ErrorSeverity.LOW:
126 | 				return 3000; // 3 seconds
127 | 		}
128 | 	}
129 | }
130 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/tools/expand-task.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tools/expand-task.js
  3 |  * Tool to expand a task into subtasks
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	createErrorResponse,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { expandTaskDirect } from '../core/task-master-core.js';
 13 | import {
 14 | 	findTasksPath,
 15 | 	findComplexityReportPath
 16 | } from '../core/utils/path-utils.js';
 17 | import { resolveTag } from '../../../scripts/modules/utils.js';
 18 | 
 19 | /**
 20 |  * Register the expand-task tool with the MCP server
 21 |  * @param {Object} server - FastMCP server instance
 22 |  */
 23 | export function registerExpandTaskTool(server) {
 24 | 	server.addTool({
 25 | 		name: 'expand_task',
 26 | 		description: 'Expand a task into subtasks for detailed implementation',
 27 | 		parameters: z.object({
 28 | 			id: z.string().describe('ID of task to expand'),
 29 | 			num: z.string().optional().describe('Number of subtasks to generate'),
 30 | 			research: z
 31 | 				.boolean()
 32 | 				.optional()
 33 | 				.default(false)
 34 | 				.describe('Use research role for generation'),
 35 | 			prompt: z
 36 | 				.string()
 37 | 				.optional()
 38 | 				.describe('Additional context for subtask generation'),
 39 | 			file: z
 40 | 				.string()
 41 | 				.optional()
 42 | 				.describe(
 43 | 					'Path to the tasks file relative to project root (e.g., tasks/tasks.json)'
 44 | 				),
 45 | 			projectRoot: z
 46 | 				.string()
 47 | 				.describe('The directory of the project. Must be an absolute path.'),
 48 | 			force: z
 49 | 				.boolean()
 50 | 				.optional()
 51 | 				.default(false)
 52 | 				.describe('Force expansion even if subtasks exist'),
 53 | 			tag: z.string().optional().describe('Tag context to operate on')
 54 | 		}),
 55 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 56 | 			try {
 57 | 				log.info(`Starting expand-task with args: ${JSON.stringify(args)}`);
 58 | 				const resolvedTag = resolveTag({
 59 | 					projectRoot: args.projectRoot,
 60 | 					tag: args.tag
 61 | 				});
 62 | 				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
 63 | 				let tasksJsonPath;
 64 | 				try {
 65 | 					tasksJsonPath = findTasksPath(
 66 | 						{ projectRoot: args.projectRoot, file: args.file },
 67 | 						log
 68 | 					);
 69 | 				} catch (error) {
 70 | 					log.error(`Error finding tasks.json: ${error.message}`);
 71 | 					return createErrorResponse(
 72 | 						`Failed to find tasks.json: ${error.message}`
 73 | 					);
 74 | 				}
 75 | 
 76 | 				const complexityReportPath = findComplexityReportPath(
 77 | 					{ ...args, tag: resolvedTag },
 78 | 					log
 79 | 				);
 80 | 
 81 | 				const result = await expandTaskDirect(
 82 | 					{
 83 | 						tasksJsonPath: tasksJsonPath,
 84 | 						id: args.id,
 85 | 						num: args.num,
 86 | 						research: args.research,
 87 | 						prompt: args.prompt,
 88 | 						force: args.force,
 89 | 						complexityReportPath,
 90 | 						projectRoot: args.projectRoot,
 91 | 						tag: resolvedTag
 92 | 					},
 93 | 					log,
 94 | 					{ session }
 95 | 				);
 96 | 
 97 | 				return handleApiResult(
 98 | 					result,
 99 | 					log,
100 | 					'Error expanding task',
101 | 					undefined,
102 | 					args.projectRoot
103 | 				);
104 | 			} catch (error) {
105 | 				log.error(`Error in expand-task tool: ${error.message}`);
106 | 				return createErrorResponse(error.message);
107 | 			}
108 | 		})
109 | 	});
110 | }
111 | 
```

--------------------------------------------------------------------------------
/apps/extension/src/utils/task-master-api/types/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * TaskMaster API Types
  3 |  * All type definitions for the TaskMaster API
  4 |  */
  5 | 
  6 | // MCP Response Types
  7 | export interface MCPTaskResponse {
  8 | 	data?: {
  9 | 		tasks?: Array<{
 10 | 			id: number | string;
 11 | 			title: string;
 12 | 			description: string;
 13 | 			status: string;
 14 | 			priority: string;
 15 | 			details?: string;
 16 | 			testStrategy?: string;
 17 | 			dependencies?: Array<number | string>;
 18 | 			complexityScore?: number;
 19 | 			subtasks?: Array<{
 20 | 				id: number;
 21 | 				title: string;
 22 | 				description?: string;
 23 | 				status: string;
 24 | 				details?: string;
 25 | 				dependencies?: Array<number | string>;
 26 | 			}>;
 27 | 		}>;
 28 | 		tag?: {
 29 | 			currentTag: string;
 30 | 			availableTags: string[];
 31 | 		};
 32 | 	};
 33 | 	version?: {
 34 | 		version: string;
 35 | 		name: string;
 36 | 	};
 37 | 	error?: string;
 38 | }
 39 | 
 40 | // Internal Task Interface
 41 | export interface TaskMasterTask {
 42 | 	id: string;
 43 | 	title: string;
 44 | 	description: string;
 45 | 	status:
 46 | 		| 'pending'
 47 | 		| 'in-progress'
 48 | 		| 'review'
 49 | 		| 'done'
 50 | 		| 'deferred'
 51 | 		| 'cancelled';
 52 | 	priority: 'high' | 'medium' | 'low';
 53 | 	details?: string;
 54 | 	testStrategy?: string;
 55 | 	dependencies?: string[];
 56 | 	complexityScore?: number;
 57 | 	subtasks?: Array<{
 58 | 		id: number;
 59 | 		title: string;
 60 | 		description?: string;
 61 | 		status: string;
 62 | 		details?: string;
 63 | 		testStrategy?: string;
 64 | 		dependencies?: Array<number | string>;
 65 | 	}>;
 66 | }
 67 | 
 68 | // API Response Wrapper
 69 | export interface TaskMasterApiResponse<T = any> {
 70 | 	success: boolean;
 71 | 	data?: T;
 72 | 	error?: string;
 73 | 	requestDuration?: number;
 74 | }
 75 | 
 76 | // API Configuration
 77 | export interface TaskMasterApiConfig {
 78 | 	timeout: number;
 79 | 	retryAttempts: number;
 80 | 	cacheDuration: number;
 81 | 	projectRoot?: string;
 82 | 	cache?: CacheConfig;
 83 | }
 84 | 
 85 | export interface CacheConfig {
 86 | 	maxSize: number;
 87 | 	enableBackgroundRefresh: boolean;
 88 | 	refreshInterval: number;
 89 | 	enableAnalytics: boolean;
 90 | 	enablePrefetch: boolean;
 91 | 	compressionEnabled: boolean;
 92 | 	persistToDisk: boolean;
 93 | }
 94 | 
 95 | // Cache Types
 96 | export interface CacheEntry {
 97 | 	data: any;
 98 | 	timestamp: number;
 99 | 	accessCount: number;
100 | 	lastAccessed: number;
101 | 	size: number;
102 | 	ttl?: number;
103 | 	tags: string[];
104 | }
105 | 
106 | export interface CacheAnalytics {
107 | 	hits: number;
108 | 	misses: number;
109 | 	evictions: number;
110 | 	refreshes: number;
111 | 	totalSize: number;
112 | 	averageAccessTime: number;
113 | 	hitRate: number;
114 | }
115 | 
116 | // Method Options
117 | export interface GetTasksOptions {
118 | 	status?: string;
119 | 	withSubtasks?: boolean;
120 | 	tag?: string;
121 | 	projectRoot?: string;
122 | }
123 | 
124 | export interface UpdateTaskStatusOptions {
125 | 	projectRoot?: string;
126 | }
127 | 
128 | export interface UpdateTaskOptions {
129 | 	projectRoot?: string;
130 | 	append?: boolean;
131 | 	research?: boolean;
132 | }
133 | 
134 | export interface UpdateSubtaskOptions {
135 | 	projectRoot?: string;
136 | 	research?: boolean;
137 | }
138 | 
139 | export interface AddSubtaskOptions {
140 | 	projectRoot?: string;
141 | }
142 | 
143 | export interface TaskUpdate {
144 | 	title?: string;
145 | 	description?: string;
146 | 	details?: string;
147 | 	priority?: 'high' | 'medium' | 'low';
148 | 	testStrategy?: string;
149 | 	dependencies?: string[];
150 | }
151 | 
152 | export interface SubtaskData {
153 | 	title: string;
154 | 	description?: string;
155 | 	dependencies?: string[];
156 | 	status?: string;
157 | }
158 | 
```

--------------------------------------------------------------------------------
/tests/helpers/tool-counts.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tool-counts.js
  3 |  * Shared helper for validating tool counts across tests and validation scripts
  4 |  */
  5 | 
  6 | import {
  7 | 	getToolCounts,
  8 | 	getToolCategories
  9 | } from '../../mcp-server/src/tools/tool-registry.js';
 10 | 
 11 | /**
 12 |  * Expected tool counts - update these when tools are added/removed
 13 |  * These serve as the canonical source of truth for expected counts
 14 |  */
 15 | export const EXPECTED_TOOL_COUNTS = {
 16 | 	core: 7,
 17 | 	standard: 15,
 18 | 	total: 44
 19 | };
 20 | 
 21 | /**
 22 |  * Expected core tools list for validation
 23 |  */
 24 | export const EXPECTED_CORE_TOOLS = [
 25 | 	'get_tasks',
 26 | 	'next_task',
 27 | 	'get_task',
 28 | 	'set_task_status',
 29 | 	'update_subtask',
 30 | 	'parse_prd',
 31 | 	'expand_task'
 32 | ];
 33 | 
 34 | /**
 35 |  * Validate that actual tool counts match expected counts
 36 |  * @returns {Object} Validation result with isValid flag and details
 37 |  */
 38 | export function validateToolCounts() {
 39 | 	const actual = getToolCounts();
 40 | 	const expected = EXPECTED_TOOL_COUNTS;
 41 | 
 42 | 	const isValid =
 43 | 		actual.core === expected.core &&
 44 | 		actual.standard === expected.standard &&
 45 | 		actual.total === expected.total;
 46 | 
 47 | 	return {
 48 | 		isValid,
 49 | 		actual,
 50 | 		expected,
 51 | 		differences: {
 52 | 			core: actual.core - expected.core,
 53 | 			standard: actual.standard - expected.standard,
 54 | 			total: actual.total - expected.total
 55 | 		}
 56 | 	};
 57 | }
 58 | 
 59 | /**
 60 |  * Validate that tool categories have correct structure and content
 61 |  * @returns {Object} Validation result
 62 |  */
 63 | export function validateToolStructure() {
 64 | 	const categories = getToolCategories();
 65 | 	const counts = getToolCounts();
 66 | 
 67 | 	// Check that core tools are subset of standard tools
 68 | 	const coreInStandard = categories.core.every((tool) =>
 69 | 		categories.standard.includes(tool)
 70 | 	);
 71 | 
 72 | 	// Check that standard tools are subset of all tools
 73 | 	const standardInAll = categories.standard.every((tool) =>
 74 | 		categories.all.includes(tool)
 75 | 	);
 76 | 
 77 | 	// Check that expected core tools match actual
 78 | 	const expectedCoreMatch =
 79 | 		EXPECTED_CORE_TOOLS.every((tool) => categories.core.includes(tool)) &&
 80 | 		categories.core.every((tool) => EXPECTED_CORE_TOOLS.includes(tool));
 81 | 
 82 | 	// Check array lengths match counts
 83 | 	const lengthsMatch =
 84 | 		categories.core.length === counts.core &&
 85 | 		categories.standard.length === counts.standard &&
 86 | 		categories.all.length === counts.total;
 87 | 
 88 | 	return {
 89 | 		isValid:
 90 | 			coreInStandard && standardInAll && expectedCoreMatch && lengthsMatch,
 91 | 		details: {
 92 | 			coreInStandard,
 93 | 			standardInAll,
 94 | 			expectedCoreMatch,
 95 | 			lengthsMatch
 96 | 		},
 97 | 		categories,
 98 | 		counts
 99 | 	};
100 | }
101 | 
102 | /**
103 |  * Get a detailed report of all tool information
104 |  * @returns {Object} Comprehensive tool information
105 |  */
106 | export function getToolReport() {
107 | 	const counts = getToolCounts();
108 | 	const categories = getToolCategories();
109 | 	const validation = validateToolCounts();
110 | 	const structure = validateToolStructure();
111 | 
112 | 	return {
113 | 		counts,
114 | 		categories,
115 | 		validation,
116 | 		structure,
117 | 		summary: {
118 | 			totalValid: validation.isValid && structure.isValid,
119 | 			countsValid: validation.isValid,
120 | 			structureValid: structure.isValid
121 | 		}
122 | 	};
123 | }
124 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/remove-dependency.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * Direct function wrapper for removeDependency
  3 |  */
  4 | 
  5 | import { removeDependency } from '../../../../scripts/modules/dependency-manager.js';
  6 | import {
  7 | 	enableSilentMode,
  8 | 	disableSilentMode
  9 | } from '../../../../scripts/modules/utils.js';
 10 | 
 11 | /**
 12 |  * Remove a dependency from a task
 13 |  * @param {Object} args - Function arguments
 14 |  * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 15 |  * @param {string|number} args.id - Task ID to remove dependency from
 16 |  * @param {string|number} args.dependsOn - Task ID to remove as a dependency
 17 |  * @param {string} args.projectRoot - Project root path (for MCP/env fallback)
 18 |  * @param {string} args.tag - Tag for the task (optional)
 19 |  * @param {Object} log - Logger object
 20 |  * @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
 21 |  */
 22 | export async function removeDependencyDirect(args, log) {
 23 | 	// Destructure expected args
 24 | 	const { tasksJsonPath, id, dependsOn, projectRoot, tag } = args;
 25 | 	try {
 26 | 		log.info(`Removing dependency with args: ${JSON.stringify(args)}`);
 27 | 
 28 | 		// Check if tasksJsonPath was provided
 29 | 		if (!tasksJsonPath) {
 30 | 			log.error('removeDependencyDirect called without tasksJsonPath');
 31 | 			return {
 32 | 				success: false,
 33 | 				error: {
 34 | 					code: 'MISSING_ARGUMENT',
 35 | 					message: 'tasksJsonPath is required'
 36 | 				}
 37 | 			};
 38 | 		}
 39 | 
 40 | 		// Validate required parameters
 41 | 		if (!id) {
 42 | 			return {
 43 | 				success: false,
 44 | 				error: {
 45 | 					code: 'INPUT_VALIDATION_ERROR',
 46 | 					message: 'Task ID (id) is required'
 47 | 				}
 48 | 			};
 49 | 		}
 50 | 
 51 | 		if (!dependsOn) {
 52 | 			return {
 53 | 				success: false,
 54 | 				error: {
 55 | 					code: 'INPUT_VALIDATION_ERROR',
 56 | 					message: 'Dependency ID (dependsOn) is required'
 57 | 				}
 58 | 			};
 59 | 		}
 60 | 
 61 | 		// Use provided path
 62 | 		const tasksPath = tasksJsonPath;
 63 | 
 64 | 		// Format IDs for the core function
 65 | 		const taskId =
 66 | 			id && id.includes && id.includes('.') ? id : parseInt(id, 10);
 67 | 		const dependencyId =
 68 | 			dependsOn && dependsOn.includes && dependsOn.includes('.')
 69 | 				? dependsOn
 70 | 				: parseInt(dependsOn, 10);
 71 | 
 72 | 		log.info(
 73 | 			`Removing dependency: task ${taskId} no longer depends on ${dependencyId}`
 74 | 		);
 75 | 
 76 | 		// Enable silent mode to prevent console logs from interfering with JSON response
 77 | 		enableSilentMode();
 78 | 
 79 | 		// Call the core function using the provided tasksPath
 80 | 		await removeDependency(tasksPath, taskId, dependencyId, {
 81 | 			projectRoot,
 82 | 			tag
 83 | 		});
 84 | 
 85 | 		// Restore normal logging
 86 | 		disableSilentMode();
 87 | 
 88 | 		return {
 89 | 			success: true,
 90 | 			data: {
 91 | 				message: `Successfully removed dependency: Task ${taskId} no longer depends on ${dependencyId}`,
 92 | 				taskId: taskId,
 93 | 				dependencyId: dependencyId
 94 | 			}
 95 | 		};
 96 | 	} catch (error) {
 97 | 		// Make sure to restore normal logging even if there's an error
 98 | 		disableSilentMode();
 99 | 
100 | 		log.error(`Error in removeDependencyDirect: ${error.message}`);
101 | 		return {
102 | 			success: false,
103 | 			error: {
104 | 				code: 'CORE_FUNCTION_ERROR',
105 | 				message: error.message
106 | 			}
107 | 		};
108 | 	}
109 | }
110 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/delete-tag.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * delete-tag.js
  3 |  * Direct function implementation for deleting a tag
  4 |  */
  5 | 
  6 | import { deleteTag } from '../../../../scripts/modules/task-manager/tag-management.js';
  7 | import {
  8 | 	enableSilentMode,
  9 | 	disableSilentMode
 10 | } from '../../../../scripts/modules/utils.js';
 11 | import { createLogWrapper } from '../../tools/utils.js';
 12 | 
 13 | /**
 14 |  * Direct function wrapper for deleting a tag with error handling.
 15 |  *
 16 |  * @param {Object} args - Command arguments
 17 |  * @param {string} args.name - Name of the tag to delete
 18 |  * @param {boolean} [args.yes=false] - Skip confirmation prompts
 19 |  * @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
 20 |  * @param {string} [args.projectRoot] - Project root path
 21 |  * @param {Object} log - Logger object
 22 |  * @param {Object} context - Additional context (session)
 23 |  * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 24 |  */
 25 | export async function deleteTagDirect(args, log, context = {}) {
 26 | 	// Destructure expected args
 27 | 	const { tasksJsonPath, name, yes = false, projectRoot } = args;
 28 | 	const { session } = context;
 29 | 
 30 | 	// Enable silent mode to prevent console logs from interfering with JSON response
 31 | 	enableSilentMode();
 32 | 
 33 | 	// Create logger wrapper using the utility
 34 | 	const mcpLog = createLogWrapper(log);
 35 | 
 36 | 	try {
 37 | 		// Check if tasksJsonPath was provided
 38 | 		if (!tasksJsonPath) {
 39 | 			log.error('deleteTagDirect called without tasksJsonPath');
 40 | 			disableSilentMode();
 41 | 			return {
 42 | 				success: false,
 43 | 				error: {
 44 | 					code: 'MISSING_ARGUMENT',
 45 | 					message: 'tasksJsonPath is required'
 46 | 				}
 47 | 			};
 48 | 		}
 49 | 
 50 | 		// Check required parameters
 51 | 		if (!name || typeof name !== 'string') {
 52 | 			log.error('Missing required parameter: name');
 53 | 			disableSilentMode();
 54 | 			return {
 55 | 				success: false,
 56 | 				error: {
 57 | 					code: 'MISSING_PARAMETER',
 58 | 					message: 'Tag name is required and must be a string'
 59 | 				}
 60 | 			};
 61 | 		}
 62 | 
 63 | 		log.info(`Deleting tag: ${name}`);
 64 | 
 65 | 		// Prepare options
 66 | 		const options = {
 67 | 			yes // For MCP, we always skip confirmation prompts
 68 | 		};
 69 | 
 70 | 		// Call the deleteTag function
 71 | 		const result = await deleteTag(
 72 | 			tasksJsonPath,
 73 | 			name,
 74 | 			options,
 75 | 			{
 76 | 				session,
 77 | 				mcpLog,
 78 | 				projectRoot
 79 | 			},
 80 | 			'json' // outputFormat - use 'json' to suppress CLI UI
 81 | 		);
 82 | 
 83 | 		// Restore normal logging
 84 | 		disableSilentMode();
 85 | 
 86 | 		return {
 87 | 			success: true,
 88 | 			data: {
 89 | 				tagName: result.tagName,
 90 | 				deleted: result.deleted,
 91 | 				tasksDeleted: result.tasksDeleted,
 92 | 				wasCurrentTag: result.wasCurrentTag,
 93 | 				switchedToMaster: result.switchedToMaster,
 94 | 				message: `Successfully deleted tag "${result.tagName}"`
 95 | 			}
 96 | 		};
 97 | 	} catch (error) {
 98 | 		// Make sure to restore normal logging even if there's an error
 99 | 		disableSilentMode();
100 | 
101 | 		log.error(`Error in deleteTagDirect: ${error.message}`);
102 | 		return {
103 | 			success: false,
104 | 			error: {
105 | 				code: error.code || 'DELETE_TAG_ERROR',
106 | 				message: error.message
107 | 			}
108 | 		};
109 | 	}
110 | }
111 | 
```

--------------------------------------------------------------------------------
/tests/unit/ai-providers/codex-cli.test.js:
--------------------------------------------------------------------------------

```javascript
 1 | import { jest } from '@jest/globals';
 2 | 
 3 | // Mock the ai module
 4 | jest.unstable_mockModule('ai', () => ({
 5 | 	generateObject: jest.fn(),
 6 | 	generateText: jest.fn(),
 7 | 	streamText: jest.fn()
 8 | }));
 9 | 
10 | // Mock the codex-cli SDK module
11 | jest.unstable_mockModule('ai-sdk-provider-codex-cli', () => ({
12 | 	createCodexCli: jest.fn((options) => {
13 | 		const provider = (modelId, settings) => ({ id: modelId, settings });
14 | 		provider.languageModel = jest.fn((id, settings) => ({ id, settings }));
15 | 		provider.chat = provider.languageModel;
16 | 		return provider;
17 | 	})
18 | }));
19 | 
20 | // Mock config getters
21 | jest.unstable_mockModule('../../../scripts/modules/config-manager.js', () => ({
22 | 	getCodexCliSettingsForCommand: jest.fn(() => ({ allowNpx: true })),
23 | 	getSupportedModelsForProvider: jest.fn(() => ['gpt-5', 'gpt-5-codex']),
24 | 	// Provide commonly imported getters to satisfy other module imports if any
25 | 	getDebugFlag: jest.fn(() => false),
26 | 	getLogLevel: jest.fn(() => 'info')
27 | }));
28 | 
29 | // Mock base provider
30 | jest.unstable_mockModule('../../../src/ai-providers/base-provider.js', () => ({
31 | 	BaseAIProvider: class {
32 | 		constructor() {
33 | 			this.name = 'Base Provider';
34 | 		}
35 | 		handleError(_ctx, err) {
36 | 			throw err;
37 | 		}
38 | 		validateParams(params) {
39 | 			if (!params.modelId) throw new Error('Model ID is required');
40 | 		}
41 | 		validateMessages(msgs) {
42 | 			if (!Array.isArray(msgs)) throw new Error('Invalid messages array');
43 | 		}
44 | 	}
45 | }));
46 | 
47 | const { CodexCliProvider } = await import(
48 | 	'../../../src/ai-providers/codex-cli.js'
49 | );
50 | const { createCodexCli } = await import('ai-sdk-provider-codex-cli');
51 | const { getCodexCliSettingsForCommand } = await import(
52 | 	'../../../scripts/modules/config-manager.js'
53 | );
54 | 
55 | describe('CodexCliProvider', () => {
56 | 	let provider;
57 | 
58 | 	beforeEach(() => {
59 | 		jest.clearAllMocks();
60 | 		provider = new CodexCliProvider();
61 | 	});
62 | 
63 | 	it('sets provider name and supported models', () => {
64 | 		expect(provider.name).toBe('Codex CLI');
65 | 		expect(provider.supportedModels).toEqual(['gpt-5', 'gpt-5-codex']);
66 | 	});
67 | 
68 | 	it('does not require API key', () => {
69 | 		expect(provider.isRequiredApiKey()).toBe(false);
70 | 	});
71 | 
72 | 	it('creates client with merged default settings', async () => {
73 | 		const client = await provider.getClient({ commandName: 'parse-prd' });
74 | 		expect(client).toBeDefined();
75 | 		expect(createCodexCli).toHaveBeenCalledWith({
76 | 			defaultSettings: expect.objectContaining({ allowNpx: true })
77 | 		});
78 | 		expect(getCodexCliSettingsForCommand).toHaveBeenCalledWith('parse-prd');
79 | 	});
80 | 
81 | 	it('injects OPENAI_API_KEY only when apiKey provided', async () => {
82 | 		const client = await provider.getClient({
83 | 			commandName: 'expand',
84 | 			apiKey: 'sk-test'
85 | 		});
86 | 		const call = createCodexCli.mock.calls[0][0];
87 | 		expect(call.defaultSettings.env.OPENAI_API_KEY).toBe('sk-test');
88 | 		// Ensure env is not set when apiKey not provided
89 | 		await provider.getClient({ commandName: 'expand' });
90 | 		const second = createCodexCli.mock.calls[1][0];
91 | 		expect(second.defaultSettings.env).toBeUndefined();
92 | 	});
93 | });
94 | 
```

--------------------------------------------------------------------------------
/apps/docs/archive/cursor-setup.mdx:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "Cursor AI Integration"
 3 | description: "Learn how to set up and use Task Master with Cursor AI"
 4 | ---
 5 | 
 6 | ## Setting up Cursor AI Integration
 7 | 
 8 | <Check>
 9 |   Task Master is designed to work seamlessly with [Cursor AI](https://www.cursor.so/), providing a structured workflow for AI-driven development.
10 | </Check>
11 | 
12 | <AccordionGroup>
13 |   <Accordion title="Using Cursor with MCP (Recommended)" icon="sparkles">
14 |     If you've already set up Task Master with MCP in Cursor, the integration is automatic. You can simply use natural language to interact with Task Master:
15 | 
16 |     ```
17 |     What tasks are available to work on next?
18 |     Can you analyze the complexity of our tasks?
19 |     I'd like to implement task 4. What does it involve?
20 |     ```
21 |   </Accordion>
22 |   <Accordion title="Manual Cursor Setup">
23 |     If you're not using MCP, you can still set up Cursor integration:
24 | 
25 |     <Steps>
26 |       <Step title="After initializing your project, open it in Cursor">
27 |         The `.cursor/rules/dev_workflow.mdc` file is automatically loaded by Cursor, providing the AI with knowledge about the task management system
28 |       </Step>
29 |       <Step title="Place your PRD document in the scripts/ directory (e.g., scripts/prd.txt)">
30 |         
31 |       </Step>
32 |       <Step title="Open Cursor's AI chat and switch to Agent mode">
33 |         
34 |       </Step>
35 |     </Steps>
36 |   </Accordion>
37 |   <Accordion title="Alternative MCP Setup in Cursor">
38 |     <Steps>
39 |       <Step title="Go to Cursor settings">
40 |         
41 |       </Step>
42 |       <Step title="Navigate to the MCP section">
43 |         
44 |       </Step>
45 |       <Step title="Click on 'Add New MCP Server'">
46 |         
47 |       </Step>
48 |       <Step title="Configure with the following details:">
49 |         - Name: "Task Master"
50 |         - Type: "Command"
51 |         - Command: "npx -y task-master-ai"
52 |       </Step>
53 |       <Step title="Save Settings">
54 |         
55 |       </Step>
56 |     </Steps>
57 |     Once configured, you can interact with Task Master's task management commands directly through Cursor's interface, providing a more integrated experience.
58 |   </Accordion>
59 | </AccordionGroup>
60 | 
61 | ## Initial Task Generation
62 | 
63 | In Cursor's AI chat, instruct the agent to generate tasks from your PRD:
64 | 
65 | ```
66 | Please use the task-master parse-prd command to generate tasks from my PRD. The PRD is located at scripts/prd.txt.
67 | ```
68 | 
69 | The agent will execute:
70 | 
71 | ```bash
72 | task-master parse-prd scripts/prd.txt
73 | ```
74 | 
75 | This will:
76 | 
77 | - Parse your PRD document
78 | - Generate a structured `tasks.json` file with tasks, dependencies, priorities, and test strategies
79 | - The agent will understand this process due to the Cursor rules
80 | 
81 | ### Generate Individual Task Files
82 | 
83 | Next, ask the agent to generate individual task files:
84 | 
85 | ```
86 | Please generate individual task files from tasks.json
87 | ```
88 | 
89 | The agent will execute:
90 | 
91 | ```bash
92 | task-master generate
93 | ```
94 | 
95 | This creates individual task files in the `tasks/` directory (e.g., `task_001.txt`, `task_002.txt`), making it easier to reference specific tasks.
96 | 
```

--------------------------------------------------------------------------------
/tests/unit/profiles/rule-transformer-gemini.test.js:
--------------------------------------------------------------------------------

```javascript
 1 | import { jest } from '@jest/globals';
 2 | import { getRulesProfile } from '../../../src/utils/rule-transformer.js';
 3 | import { geminiProfile } from '../../../src/profiles/gemini.js';
 4 | 
 5 | describe('Rule Transformer - Gemini Profile', () => {
 6 | 	test('should have correct profile configuration', () => {
 7 | 		const geminiProfile = getRulesProfile('gemini');
 8 | 
 9 | 		expect(geminiProfile).toBeDefined();
10 | 		expect(geminiProfile.profileName).toBe('gemini');
11 | 		expect(geminiProfile.displayName).toBe('Gemini');
12 | 		expect(geminiProfile.profileDir).toBe('.gemini');
13 | 		expect(geminiProfile.rulesDir).toBe('.');
14 | 		expect(geminiProfile.mcpConfig).toBe(true);
15 | 		expect(geminiProfile.mcpConfigName).toBe('settings.json');
16 | 		expect(geminiProfile.mcpConfigPath).toBe('.gemini/settings.json');
17 | 		expect(geminiProfile.includeDefaultRules).toBe(false);
18 | 		expect(geminiProfile.fileMap).toEqual({
19 | 			'AGENT.md': 'AGENTS.md',
20 | 			'GEMINI.md': 'GEMINI.md'
21 | 		});
22 | 	});
23 | 
24 | 	test('should have minimal profile implementation', () => {
25 | 		// Verify that gemini.js is minimal (no lifecycle functions)
26 | 		expect(geminiProfile.onAddRulesProfile).toBeUndefined();
27 | 		expect(geminiProfile.onRemoveRulesProfile).toBeUndefined();
28 | 		expect(geminiProfile.onPostConvertRulesProfile).toBeUndefined();
29 | 	});
30 | 
31 | 	test('should use settings.json instead of mcp.json', () => {
32 | 		const geminiProfile = getRulesProfile('gemini');
33 | 		expect(geminiProfile.mcpConfigName).toBe('settings.json');
34 | 		expect(geminiProfile.mcpConfigPath).toBe('.gemini/settings.json');
35 | 	});
36 | 
37 | 	test('should not include default rules', () => {
38 | 		const geminiProfile = getRulesProfile('gemini');
39 | 		expect(geminiProfile.includeDefaultRules).toBe(false);
40 | 	});
41 | 
42 | 	test('should have correct file mapping', () => {
43 | 		const geminiProfile = getRulesProfile('gemini');
44 | 		expect(geminiProfile.fileMap).toEqual({
45 | 			'AGENT.md': 'AGENTS.md',
46 | 			'GEMINI.md': 'GEMINI.md'
47 | 		});
48 | 	});
49 | 
50 | 	test('should place AGENTS.md and GEMINI.md in root directory', () => {
51 | 		const geminiProfile = getRulesProfile('gemini');
52 | 		// rulesDir determines where fileMap files go
53 | 		expect(geminiProfile.rulesDir).toBe('.');
54 | 		// This means both AGENTS.md and GEMINI.md will be placed in the root
55 | 		// Both files are auto-loaded by Gemini CLI
56 | 	});
57 | 
58 | 	test('should place settings.json in .gemini directory', () => {
59 | 		const geminiProfile = getRulesProfile('gemini');
60 | 		// profileDir + mcpConfigName determines MCP config location
61 | 		expect(geminiProfile.profileDir).toBe('.gemini');
62 | 		expect(geminiProfile.mcpConfigName).toBe('settings.json');
63 | 		expect(geminiProfile.mcpConfigPath).toBe('.gemini/settings.json');
64 | 	});
65 | 
66 | 	test('should have proper conversion config', () => {
67 | 		const geminiProfile = getRulesProfile('gemini');
68 | 		// Gemini should have the standard conversion config
69 | 		expect(geminiProfile.conversionConfig).toBeDefined();
70 | 		expect(geminiProfile.globalReplacements).toBeDefined();
71 | 		expect(Array.isArray(geminiProfile.globalReplacements)).toBe(true);
72 | 	});
73 | });
74 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/custom-sdk/json-extractor.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * @fileoverview Extract JSON from MCP response, handling markdown blocks and other formatting
  3 |  */
  4 | 
  5 | /**
  6 |  * Extract JSON from MCP AI response
  7 |  * @param {string} text - The text to extract JSON from
  8 |  * @returns {string} - The extracted JSON string
  9 |  */
 10 | export function extractJson(text) {
 11 | 	// Remove markdown code blocks if present
 12 | 	let jsonText = text.trim();
 13 | 
 14 | 	// Remove ```json blocks
 15 | 	jsonText = jsonText.replace(/^```json\s*/gm, '');
 16 | 	jsonText = jsonText.replace(/^```\s*/gm, '');
 17 | 	jsonText = jsonText.replace(/```\s*$/gm, '');
 18 | 
 19 | 	// Remove common TypeScript/JavaScript patterns
 20 | 	jsonText = jsonText.replace(/^const\s+\w+\s*=\s*/, ''); // Remove "const varName = "
 21 | 	jsonText = jsonText.replace(/^let\s+\w+\s*=\s*/, ''); // Remove "let varName = "
 22 | 	jsonText = jsonText.replace(/^var\s+\w+\s*=\s*/, ''); // Remove "var varName = "
 23 | 	jsonText = jsonText.replace(/;?\s*$/, ''); // Remove trailing semicolons
 24 | 
 25 | 	// Remove explanatory text before JSON (common with AI responses)
 26 | 	jsonText = jsonText.replace(/^.*?(?=\{|\[)/s, '');
 27 | 
 28 | 	// Remove explanatory text after JSON
 29 | 	const lines = jsonText.split('\n');
 30 | 	let jsonEndIndex = -1;
 31 | 	let braceCount = 0;
 32 | 	let inString = false;
 33 | 	let escapeNext = false;
 34 | 
 35 | 	// Find the end of the JSON by tracking braces
 36 | 	for (let i = 0; i < jsonText.length; i++) {
 37 | 		const char = jsonText[i];
 38 | 
 39 | 		if (escapeNext) {
 40 | 			escapeNext = false;
 41 | 			continue;
 42 | 		}
 43 | 
 44 | 		if (char === '\\') {
 45 | 			escapeNext = true;
 46 | 			continue;
 47 | 		}
 48 | 
 49 | 		if (char === '"' && !escapeNext) {
 50 | 			inString = !inString;
 51 | 			continue;
 52 | 		}
 53 | 
 54 | 		if (!inString) {
 55 | 			if (char === '{' || char === '[') {
 56 | 				braceCount++;
 57 | 			} else if (char === '}' || char === ']') {
 58 | 				braceCount--;
 59 | 				if (braceCount === 0) {
 60 | 					jsonEndIndex = i;
 61 | 					break;
 62 | 				}
 63 | 			}
 64 | 		}
 65 | 	}
 66 | 
 67 | 	if (jsonEndIndex > -1) {
 68 | 		jsonText = jsonText.substring(0, jsonEndIndex + 1);
 69 | 	}
 70 | 
 71 | 	// Try to extract JSON object or array if previous method didn't work
 72 | 	if (jsonEndIndex === -1) {
 73 | 		const objectMatch = jsonText.match(/{[\s\S]*}/);
 74 | 		const arrayMatch = jsonText.match(/\[[\s\S]*\]/);
 75 | 
 76 | 		if (objectMatch) {
 77 | 			jsonText = objectMatch[0];
 78 | 		} else if (arrayMatch) {
 79 | 			jsonText = arrayMatch[0];
 80 | 		}
 81 | 	}
 82 | 
 83 | 	// First try to parse as valid JSON
 84 | 	try {
 85 | 		JSON.parse(jsonText);
 86 | 		return jsonText;
 87 | 	} catch {
 88 | 		// If it's not valid JSON, it might be a JavaScript object literal
 89 | 		// Try to convert it to valid JSON
 90 | 		try {
 91 | 			// This is a simple conversion that handles basic cases
 92 | 			// Replace unquoted keys with quoted keys
 93 | 			const converted = jsonText
 94 | 				.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":')
 95 | 				// Replace single quotes with double quotes
 96 | 				.replace(/'/g, '"')
 97 | 				// Handle trailing commas
 98 | 				.replace(/,\s*([}\]])/g, '$1');
 99 | 
100 | 			// Validate the converted JSON
101 | 			JSON.parse(converted);
102 | 			return converted;
103 | 		} catch {
104 | 			// If all else fails, return the original text
105 | 			// The calling code will handle the error appropriately
106 | 			return text;
107 | 		}
108 | 	}
109 | }
110 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/tools/update-subtask.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tools/update-subtask.js
  3 |  * Tool to append additional information to a specific subtask
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	createErrorResponse,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { updateSubtaskByIdDirect } from '../core/task-master-core.js';
 13 | import { findTasksPath } from '../core/utils/path-utils.js';
 14 | import { resolveTag } from '../../../scripts/modules/utils.js';
 15 | 
 16 | /**
 17 |  * Register the update-subtask tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerUpdateSubtaskTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'update_subtask',
 23 | 		description:
 24 | 			'Appends timestamped information to a specific subtask without replacing existing content. If you just want to update the subtask status, use set_task_status instead.',
 25 | 		parameters: z.object({
 26 | 			id: z
 27 | 				.string()
 28 | 				.describe(
 29 | 					'ID of the subtask to update in format "parentId.subtaskId" (e.g., "5.2"). Parent ID is the ID of the task that contains the subtask.'
 30 | 				),
 31 | 			prompt: z.string().describe('Information to add to the subtask'),
 32 | 			research: z
 33 | 				.boolean()
 34 | 				.optional()
 35 | 				.describe('Use Perplexity AI for research-backed updates'),
 36 | 			file: z.string().optional().describe('Absolute path to the tasks file'),
 37 | 			projectRoot: z
 38 | 				.string()
 39 | 				.describe('The directory of the project. Must be an absolute path.'),
 40 | 			tag: z.string().optional().describe('Tag context to operate on')
 41 | 		}),
 42 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 43 | 			const toolName = 'update_subtask';
 44 | 
 45 | 			try {
 46 | 				const resolvedTag = resolveTag({
 47 | 					projectRoot: args.projectRoot,
 48 | 					tag: args.tag
 49 | 				});
 50 | 				log.info(`Updating subtask with args: ${JSON.stringify(args)}`);
 51 | 
 52 | 				let tasksJsonPath;
 53 | 				try {
 54 | 					tasksJsonPath = findTasksPath(
 55 | 						{ projectRoot: args.projectRoot, file: args.file },
 56 | 						log
 57 | 					);
 58 | 				} catch (error) {
 59 | 					log.error(`${toolName}: Error finding tasks.json: ${error.message}`);
 60 | 					return createErrorResponse(
 61 | 						`Failed to find tasks.json: ${error.message}`
 62 | 					);
 63 | 				}
 64 | 
 65 | 				const result = await updateSubtaskByIdDirect(
 66 | 					{
 67 | 						tasksJsonPath: tasksJsonPath,
 68 | 						id: args.id,
 69 | 						prompt: args.prompt,
 70 | 						research: args.research,
 71 | 						projectRoot: args.projectRoot,
 72 | 						tag: resolvedTag
 73 | 					},
 74 | 					log,
 75 | 					{ session }
 76 | 				);
 77 | 
 78 | 				if (result.success) {
 79 | 					log.info(`Successfully updated subtask with ID ${args.id}`);
 80 | 				} else {
 81 | 					log.error(
 82 | 						`Failed to update subtask: ${result.error?.message || 'Unknown error'}`
 83 | 					);
 84 | 				}
 85 | 
 86 | 				return handleApiResult(
 87 | 					result,
 88 | 					log,
 89 | 					'Error updating subtask',
 90 | 					undefined,
 91 | 					args.projectRoot
 92 | 				);
 93 | 			} catch (error) {
 94 | 				log.error(
 95 | 					`Critical error in ${toolName} tool execute: ${error.message}`
 96 | 				);
 97 | 				return createErrorResponse(
 98 | 					`Internal tool error (${toolName}): ${error.message}`
 99 | 				);
100 | 			}
101 | 		})
102 | 	});
103 | }
104 | 
```

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | import fs from 'fs';
  3 | import path from 'path';
  4 | import os from 'os';
  5 | 
  6 | // Mock external modules
  7 | jest.mock('child_process', () => ({
  8 | 	execSync: jest.fn()
  9 | }));
 10 | 
 11 | // Mock console methods
 12 | jest.mock('console', () => ({
 13 | 	log: jest.fn(),
 14 | 	info: jest.fn(),
 15 | 	warn: jest.fn(),
 16 | 	error: jest.fn(),
 17 | 	clear: jest.fn()
 18 | }));
 19 | 
 20 | describe('Trae Integration', () => {
 21 | 	let tempDir;
 22 | 
 23 | 	beforeEach(() => {
 24 | 		jest.clearAllMocks();
 25 | 
 26 | 		// Create a temporary directory for testing
 27 | 		tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-master-test-'));
 28 | 
 29 | 		// Spy on fs methods
 30 | 		jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
 31 | 		jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
 32 | 			if (filePath.toString().includes('.trae')) {
 33 | 				return 'Existing trae rules content';
 34 | 			}
 35 | 			return '{}';
 36 | 		});
 37 | 		jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
 38 | 		jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
 39 | 	});
 40 | 
 41 | 	afterEach(() => {
 42 | 		// Clean up the temporary directory
 43 | 		try {
 44 | 			fs.rmSync(tempDir, { recursive: true, force: true });
 45 | 		} catch (err) {
 46 | 			console.error(`Error cleaning up: ${err.message}`);
 47 | 		}
 48 | 	});
 49 | 
 50 | 	// Test function that simulates the createProjectStructure behavior for Trae files
 51 | 	function mockCreateTraeStructure() {
 52 | 		// Create main .trae directory
 53 | 		fs.mkdirSync(path.join(tempDir, '.trae'), { recursive: true });
 54 | 
 55 | 		// Create rules directory
 56 | 		fs.mkdirSync(path.join(tempDir, '.trae', 'rules'), { recursive: true });
 57 | 
 58 | 		// Create rule files
 59 | 		const ruleFiles = [
 60 | 			'dev_workflow.md',
 61 | 			'taskmaster.md',
 62 | 			'architecture.md',
 63 | 			'commands.md',
 64 | 			'dependencies.md'
 65 | 		];
 66 | 
 67 | 		for (const ruleFile of ruleFiles) {
 68 | 			fs.writeFileSync(
 69 | 				path.join(tempDir, '.trae', 'rules', ruleFile),
 70 | 				`Content for ${ruleFile}`
 71 | 			);
 72 | 		}
 73 | 	}
 74 | 
 75 | 	test('creates all required .trae directories', () => {
 76 | 		// Act
 77 | 		mockCreateTraeStructure();
 78 | 
 79 | 		// Assert
 80 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(tempDir, '.trae'), {
 81 | 			recursive: true
 82 | 		});
 83 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
 84 | 			path.join(tempDir, '.trae', 'rules'),
 85 | 			{ recursive: true }
 86 | 		);
 87 | 	});
 88 | 
 89 | 	test('creates rule files for Trae', () => {
 90 | 		// Act
 91 | 		mockCreateTraeStructure();
 92 | 
 93 | 		// Assert - check rule files are created
 94 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
 95 | 			path.join(tempDir, '.trae', 'rules', 'dev_workflow.md'),
 96 | 			expect.any(String)
 97 | 		);
 98 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
 99 | 			path.join(tempDir, '.trae', 'rules', 'taskmaster.md'),
100 | 			expect.any(String)
101 | 		);
102 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
103 | 			path.join(tempDir, '.trae', 'rules', 'architecture.md'),
104 | 			expect.any(String)
105 | 		);
106 | 	});
107 | 
108 | 	test('does not create MCP configuration files', () => {
109 | 		// Act
110 | 		mockCreateTraeStructure();
111 | 
112 | 		// Assert - Trae doesn't use MCP configuration
113 | 		expect(fs.writeFileSync).not.toHaveBeenCalledWith(
114 | 			path.join(tempDir, '.trae', 'mcp.json'),
115 | 			expect.any(String)
116 | 		);
117 | 	});
118 | });
119 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/tools/update.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tools/update.js
  3 |  * Tool to update tasks based on new context/prompt
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	createErrorResponse,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { updateTasksDirect } from '../core/task-master-core.js';
 13 | import { findTasksPath } from '../core/utils/path-utils.js';
 14 | import { resolveTag } from '../../../scripts/modules/utils.js';
 15 | 
 16 | /**
 17 |  * Register the update tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerUpdateTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'update',
 23 | 		description:
 24 | 			"Update multiple upcoming tasks (with ID >= 'from' ID) based on new context or changes provided in the prompt. Use 'update_task' instead for a single specific task or 'update_subtask' for subtasks.",
 25 | 		parameters: z.object({
 26 | 			from: z
 27 | 				.string()
 28 | 				.describe(
 29 | 					"Task ID from which to start updating (inclusive). IMPORTANT: This tool uses 'from', not 'id'"
 30 | 				),
 31 | 			prompt: z
 32 | 				.string()
 33 | 				.describe('Explanation of changes or new context to apply'),
 34 | 			research: z
 35 | 				.boolean()
 36 | 				.optional()
 37 | 				.describe('Use Perplexity AI for research-backed updates'),
 38 | 			file: z
 39 | 				.string()
 40 | 				.optional()
 41 | 				.describe('Path to the tasks file relative to project root'),
 42 | 			projectRoot: z
 43 | 				.string()
 44 | 				.optional()
 45 | 				.describe(
 46 | 					'The directory of the project. (Optional, usually from session)'
 47 | 				),
 48 | 			tag: z.string().optional().describe('Tag context to operate on')
 49 | 		}),
 50 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 51 | 			const toolName = 'update';
 52 | 			const { from, prompt, research, file, projectRoot, tag } = args;
 53 | 
 54 | 			const resolvedTag = resolveTag({
 55 | 				projectRoot: args.projectRoot,
 56 | 				tag: args.tag
 57 | 			});
 58 | 
 59 | 			try {
 60 | 				log.info(
 61 | 					`Executing ${toolName} tool with normalized root: ${projectRoot}`
 62 | 				);
 63 | 
 64 | 				let tasksJsonPath;
 65 | 				try {
 66 | 					tasksJsonPath = findTasksPath({ projectRoot, file }, log);
 67 | 					log.info(`${toolName}: Resolved tasks path: ${tasksJsonPath}`);
 68 | 				} catch (error) {
 69 | 					log.error(`${toolName}: Error finding tasks.json: ${error.message}`);
 70 | 					return createErrorResponse(
 71 | 						`Failed to find tasks.json within project root '${projectRoot}': ${error.message}`
 72 | 					);
 73 | 				}
 74 | 
 75 | 				const result = await updateTasksDirect(
 76 | 					{
 77 | 						tasksJsonPath: tasksJsonPath,
 78 | 						from: from,
 79 | 						prompt: prompt,
 80 | 						research: research,
 81 | 						projectRoot: projectRoot,
 82 | 						tag: resolvedTag
 83 | 					},
 84 | 					log,
 85 | 					{ session }
 86 | 				);
 87 | 
 88 | 				log.info(
 89 | 					`${toolName}: Direct function result: success=${result.success}`
 90 | 				);
 91 | 				return handleApiResult(
 92 | 					result,
 93 | 					log,
 94 | 					'Error updating tasks',
 95 | 					undefined,
 96 | 					args.projectRoot
 97 | 				);
 98 | 			} catch (error) {
 99 | 				log.error(
100 | 					`Critical error in ${toolName} tool execute: ${error.message}`
101 | 				);
102 | 				return createErrorResponse(
103 | 					`Internal tool error (${toolName}): ${error.message}`
104 | 				);
105 | 			}
106 | 		})
107 | 	});
108 | }
109 | 
```
Page 8/69FirstPrevNextLast