#
tokens: 49840/50000 47/821 files (page 6/38)
lines: off (toggle) GitHub
raw markdown copy
This is page 6 of 38. Use http://codebase.md/eyaltoledano/claude-task-master?page={x} to view the full context.

# Directory Structure

```
├── .changeset
│   ├── config.json
│   └── README.md
├── .claude
│   ├── agents
│   │   ├── task-checker.md
│   │   ├── task-executor.md
│   │   └── task-orchestrator.md
│   ├── commands
│   │   ├── dedupe.md
│   │   └── tm
│   │       ├── add-dependency
│   │       │   └── add-dependency.md
│   │       ├── add-subtask
│   │       │   ├── add-subtask.md
│   │       │   └── convert-task-to-subtask.md
│   │       ├── add-task
│   │       │   └── add-task.md
│   │       ├── analyze-complexity
│   │       │   └── analyze-complexity.md
│   │       ├── complexity-report
│   │       │   └── complexity-report.md
│   │       ├── expand
│   │       │   ├── expand-all-tasks.md
│   │       │   └── expand-task.md
│   │       ├── fix-dependencies
│   │       │   └── fix-dependencies.md
│   │       ├── generate
│   │       │   └── generate-tasks.md
│   │       ├── help.md
│   │       ├── init
│   │       │   ├── init-project-quick.md
│   │       │   └── init-project.md
│   │       ├── learn.md
│   │       ├── list
│   │       │   ├── list-tasks-by-status.md
│   │       │   ├── list-tasks-with-subtasks.md
│   │       │   └── list-tasks.md
│   │       ├── models
│   │       │   ├── setup-models.md
│   │       │   └── view-models.md
│   │       ├── next
│   │       │   └── next-task.md
│   │       ├── parse-prd
│   │       │   ├── parse-prd-with-research.md
│   │       │   └── parse-prd.md
│   │       ├── remove-dependency
│   │       │   └── remove-dependency.md
│   │       ├── remove-subtask
│   │       │   └── remove-subtask.md
│   │       ├── remove-subtasks
│   │       │   ├── remove-all-subtasks.md
│   │       │   └── remove-subtasks.md
│   │       ├── remove-task
│   │       │   └── remove-task.md
│   │       ├── set-status
│   │       │   ├── to-cancelled.md
│   │       │   ├── to-deferred.md
│   │       │   ├── to-done.md
│   │       │   ├── to-in-progress.md
│   │       │   ├── to-pending.md
│   │       │   └── to-review.md
│   │       ├── setup
│   │       │   ├── install-taskmaster.md
│   │       │   └── quick-install-taskmaster.md
│   │       ├── show
│   │       │   └── show-task.md
│   │       ├── status
│   │       │   └── project-status.md
│   │       ├── sync-readme
│   │       │   └── sync-readme.md
│   │       ├── tm-main.md
│   │       ├── update
│   │       │   ├── update-single-task.md
│   │       │   ├── update-task.md
│   │       │   └── update-tasks-from-id.md
│   │       ├── utils
│   │       │   └── analyze-project.md
│   │       ├── validate-dependencies
│   │       │   └── validate-dependencies.md
│   │       └── workflows
│   │           ├── auto-implement-tasks.md
│   │           ├── command-pipeline.md
│   │           └── smart-workflow.md
│   └── TM_COMMANDS_GUIDE.md
├── .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
│   └── 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
│   │   ├── 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
│   │   ├── test-prd.txt
│   │   └── tm-core-phase-1.txt
│   ├── reports
│   │   ├── task-complexity-report_cc-kiro-hooks.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.txt
├── .vscode
│   ├── extensions.json
│   └── settings.json
├── apps
│   ├── cli
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── commands
│   │   │   │   ├── auth.command.ts
│   │   │   │   ├── context.command.ts
│   │   │   │   ├── list.command.ts
│   │   │   │   ├── set-status.command.ts
│   │   │   │   ├── show.command.ts
│   │   │   │   └── start.command.ts
│   │   │   ├── index.ts
│   │   │   ├── ui
│   │   │   │   ├── components
│   │   │   │   │   ├── dashboard.component.ts
│   │   │   │   │   ├── header.component.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── next-task.component.ts
│   │   │   │   │   ├── suggested-steps.component.ts
│   │   │   │   │   └── task-detail.component.ts
│   │   │   │   └── index.ts
│   │   │   └── utils
│   │   │       ├── auto-update.ts
│   │   │       └── ui.ts
│   │   └── tsconfig.json
│   ├── 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
│   │   │   └── task-structure.mdx
│   │   ├── CHANGELOG.md
│   │   ├── docs.json
│   │   ├── favicon.svg
│   │   ├── getting-started
│   │   │   ├── 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
│   │   ├── 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
├── assets
│   ├── .windsurfrules
│   ├── AGENTS.md
│   ├── claude
│   │   ├── agents
│   │   │   ├── task-checker.md
│   │   │   ├── task-executor.md
│   │   │   └── task-orchestrator.md
│   │   ├── commands
│   │   │   └── tm
│   │   │       ├── add-dependency
│   │   │       │   └── add-dependency.md
│   │   │       ├── add-subtask
│   │   │       │   ├── add-subtask.md
│   │   │       │   └── convert-task-to-subtask.md
│   │   │       ├── add-task
│   │   │       │   └── add-task.md
│   │   │       ├── analyze-complexity
│   │   │       │   └── analyze-complexity.md
│   │   │       ├── clear-subtasks
│   │   │       │   ├── clear-all-subtasks.md
│   │   │       │   └── clear-subtasks.md
│   │   │       ├── complexity-report
│   │   │       │   └── complexity-report.md
│   │   │       ├── expand
│   │   │       │   ├── expand-all-tasks.md
│   │   │       │   └── expand-task.md
│   │   │       ├── fix-dependencies
│   │   │       │   └── fix-dependencies.md
│   │   │       ├── generate
│   │   │       │   └── generate-tasks.md
│   │   │       ├── help.md
│   │   │       ├── init
│   │   │       │   ├── init-project-quick.md
│   │   │       │   └── init-project.md
│   │   │       ├── learn.md
│   │   │       ├── list
│   │   │       │   ├── list-tasks-by-status.md
│   │   │       │   ├── list-tasks-with-subtasks.md
│   │   │       │   └── list-tasks.md
│   │   │       ├── models
│   │   │       │   ├── setup-models.md
│   │   │       │   └── view-models.md
│   │   │       ├── next
│   │   │       │   └── next-task.md
│   │   │       ├── parse-prd
│   │   │       │   ├── parse-prd-with-research.md
│   │   │       │   └── parse-prd.md
│   │   │       ├── remove-dependency
│   │   │       │   └── remove-dependency.md
│   │   │       ├── remove-subtask
│   │   │       │   └── remove-subtask.md
│   │   │       ├── remove-subtasks
│   │   │       │   ├── remove-all-subtasks.md
│   │   │       │   └── remove-subtasks.md
│   │   │       ├── remove-task
│   │   │       │   └── remove-task.md
│   │   │       ├── set-status
│   │   │       │   ├── to-cancelled.md
│   │   │       │   ├── to-deferred.md
│   │   │       │   ├── to-done.md
│   │   │       │   ├── to-in-progress.md
│   │   │       │   ├── to-pending.md
│   │   │       │   └── to-review.md
│   │   │       ├── setup
│   │   │       │   ├── install-taskmaster.md
│   │   │       │   └── quick-install-taskmaster.md
│   │   │       ├── show
│   │   │       │   └── show-task.md
│   │   │       ├── status
│   │   │       │   └── project-status.md
│   │   │       ├── sync-readme
│   │   │       │   └── sync-readme.md
│   │   │       ├── tm-main.md
│   │   │       ├── update
│   │   │       │   ├── update-single-task.md
│   │   │       │   ├── update-task.md
│   │   │       │   └── update-tasks-from-id.md
│   │   │       ├── utils
│   │   │       │   └── analyze-project.md
│   │   │       ├── validate-dependencies
│   │   │       │   └── validate-dependencies.md
│   │   │       └── workflows
│   │   │           ├── auto-implement-tasks.md
│   │   │           ├── command-pipeline.md
│   │   │           └── smart-workflow.md
│   │   └── TM_COMMANDS_GUIDE.md
│   ├── config.json
│   ├── env.example
│   ├── example_prd.txt
│   ├── 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.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
│   ├── CLI-COMMANDER-PATTERN.md
│   ├── command-reference.md
│   ├── configuration.md
│   ├── contributor-docs
│   │   └── testing-roo-integration.md
│   ├── cross-tag-task-movement.md
│   ├── examples
│   │   └── claude-code-usage.md
│   ├── examples.md
│   ├── licensing.md
│   ├── mcp-provider-guide.md
│   ├── mcp-provider.md
│   ├── migration-guide.md
│   ├── models.md
│   ├── providers
│   │   └── gemini-cli.md
│   ├── README.md
│   ├── scripts
│   │   └── models-json-to-markdown.js
│   ├── task-structure.md
│   └── tutorial.md
├── images
│   └── 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
│       │   │   ├── list-tasks.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
│       │   │   ├── show-task.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
│           ├── get-task.js
│           ├── get-tasks.js
│           ├── index.js
│           ├── initialize-project.js
│           ├── list-tags.js
│           ├── models.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.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
│   ├── build-config
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src
│   │   │   └── tsdown.base.ts
│   │   └── tsconfig.json
│   └── tm-core
│       ├── .gitignore
│       ├── CHANGELOG.md
│       ├── docs
│       │   └── listTasks-architecture.md
│       ├── package.json
│       ├── POC-STATUS.md
│       ├── README.md
│       ├── src
│       │   ├── auth
│       │   │   ├── auth-manager.test.ts
│       │   │   ├── auth-manager.ts
│       │   │   ├── config.ts
│       │   │   ├── credential-store.test.ts
│       │   │   ├── credential-store.ts
│       │   │   ├── index.ts
│       │   │   ├── oauth-service.ts
│       │   │   ├── supabase-session-storage.ts
│       │   │   └── types.ts
│       │   ├── clients
│       │   │   ├── index.ts
│       │   │   └── supabase-client.ts
│       │   ├── config
│       │   │   ├── config-manager.spec.ts
│       │   │   ├── config-manager.ts
│       │   │   ├── index.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
│       │   ├── constants
│       │   │   └── index.ts
│       │   ├── entities
│       │   │   └── task.entity.ts
│       │   ├── errors
│       │   │   ├── index.ts
│       │   │   └── task-master-error.ts
│       │   ├── executors
│       │   │   ├── base-executor.ts
│       │   │   ├── claude-executor.ts
│       │   │   ├── executor-factory.ts
│       │   │   ├── executor-service.ts
│       │   │   ├── index.ts
│       │   │   └── types.ts
│       │   ├── index.ts
│       │   ├── interfaces
│       │   │   ├── ai-provider.interface.ts
│       │   │   ├── configuration.interface.ts
│       │   │   ├── index.ts
│       │   │   └── storage.interface.ts
│       │   ├── logger
│       │   │   ├── factory.ts
│       │   │   ├── index.ts
│       │   │   └── logger.ts
│       │   ├── mappers
│       │   │   └── TaskMapper.ts
│       │   ├── parser
│       │   │   └── index.ts
│       │   ├── providers
│       │   │   ├── ai
│       │   │   │   ├── base-provider.ts
│       │   │   │   └── index.ts
│       │   │   └── index.ts
│       │   ├── repositories
│       │   │   ├── supabase-task-repository.ts
│       │   │   └── task-repository.interface.ts
│       │   ├── services
│       │   │   ├── index.ts
│       │   │   ├── organization.service.ts
│       │   │   ├── task-execution-service.ts
│       │   │   └── task-service.ts
│       │   ├── storage
│       │   │   ├── api-storage.ts
│       │   │   ├── file-storage
│       │   │   │   ├── file-operations.ts
│       │   │   │   ├── file-storage.ts
│       │   │   │   ├── format-handler.ts
│       │   │   │   ├── index.ts
│       │   │   │   └── path-resolver.ts
│       │   │   ├── index.ts
│       │   │   └── storage-factory.ts
│       │   ├── subpath-exports.test.ts
│       │   ├── task-master-core.ts
│       │   ├── types
│       │   │   ├── database.types.ts
│       │   │   ├── index.ts
│       │   │   └── legacy.ts
│       │   └── utils
│       │       ├── id-generator.ts
│       │       └── index.ts
│       ├── tests
│       │   ├── integration
│       │   │   └── list-tasks.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
│   ├── dev.js
│   ├── init.js
│   ├── modules
│   │   ├── ai-services-unified.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
├── src
│   ├── ai-providers
│   │   ├── anthropic.js
│   │   ├── azure.js
│   │   ├── base-provider.js
│   │   ├── bedrock.js
│   │   ├── claude-code.js
│   │   ├── custom-sdk
│   │   │   ├── claude-code
│   │   │   │   ├── errors.js
│   │   │   │   ├── index.js
│   │   │   │   ├── json-extractor.js
│   │   │   │   ├── language-model.js
│   │   │   │   ├── message-converter.js
│   │   │   │   └── types.js
│   │   │   └── grok-cli
│   │   │       ├── errors.js
│   │   │       ├── index.js
│   │   │       ├── json-extractor.js
│   │   │       ├── language-model.js
│   │   │       ├── message-converter.js
│   │   │       └── types.js
│   │   ├── gemini-cli.js
│   │   ├── google-vertex.js
│   │   ├── google.js
│   │   ├── grok-cli.js
│   │   ├── groq.js
│   │   ├── index.js
│   │   ├── ollama.js
│   │   ├── openai.js
│   │   ├── openrouter.js
│   │   ├── perplexity.js
│   │   └── xai.js
│   ├── constants
│   │   ├── commands.js
│   │   ├── paths.js
│   │   ├── profiles.js
│   │   ├── providers.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
│   ├── 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
│   ├── fixture
│   │   └── test-tasks.json
│   ├── fixtures
│   │   ├── .taskmasterconfig
│   │   ├── sample-claude-response.js
│   │   ├── sample-prd.txt
│   │   └── sample-tasks.js
│   ├── integration
│   │   ├── 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
│   ├── 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
│       │   ├── claude-code.test.js
│       │   ├── custom-sdk
│       │   │   └── claude-code
│       │   │       └── language-model.test.js
│       │   ├── gemini-cli.test.js
│       │   ├── mcp-components.test.js
│       │   └── openai.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
│       ├── 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
│       ├── providers
│       │   └── provider-registry.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
│       │       │   ├── move-task-cross-tag.test.js
│       │       │   ├── move-task.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
```

# Files

--------------------------------------------------------------------------------
/apps/docs/capabilities/mcp.mdx:
--------------------------------------------------------------------------------

```markdown
---
title: MCP Tools
sidebarTitle: "MCP Tools"
---

# MCP Tools

This document provides an overview of the MCP (Machine-to-Machine Communication Protocol) interface for the Task Master application. The MCP interface is defined in the `mcp-server/` directory and exposes the application's core functionalities as a set of tools that can be called remotely.

## Core Concepts

The MCP interface is built on top of the `fastmcp` library and registers a set of tools that correspond to the core functionalities of the Task Master application. These tools are defined in the `mcp-server/src/tools/` directory and are registered with the MCP server in `mcp-server/src/tools/index.js`.

Each tool is defined with a name, a description, and a set of parameters that are validated using the `zod` library. The `execute` function of each tool calls the corresponding core logic function from `scripts/modules/task-manager.js`.

## Tool Categories

The MCP tools can be categorized in the same way as the core functionalities:

### 1. Task and Subtask Management

-   **`add_task`**: Creates a new task.
-   **`add_subtask`**: Adds a subtask to a parent task.
-   **`remove_task`**: Removes one or more tasks or subtasks.
-   **`remove_subtask`**: Removes a subtask from its parent.
-   **`update_task`**: Updates a single task.
-   **`update_subtask`**: Appends information to a subtask.
-   **`update`**: Updates multiple tasks.
-   **`move_task`**: Moves a task or subtask.
-   **`clear_subtasks`**: Clears all subtasks from one or more tasks.

### 2. Task Information and Status

-   **`get_tasks`**: Lists all tasks.
-   **`get_task`**: Shows the details of a specific task.
-   **`next_task`**: Shows the next task to work on.
-   **`set_task_status`**: Sets the status of a task or subtask.

### 3. Task Analysis and Expansion

-   **`parse_prd`**: Parses a PRD to generate tasks.
-   **`expand_task`**: Expands a task into subtasks.
-   **`expand_all`**: Expands all eligible tasks.
-   **`analyze_project_complexity`**: Analyzes task complexity.
-   **`complexity_report`**: Displays the complexity analysis report.

### 4. Dependency Management

-   **`add_dependency`**: Adds a dependency to a task.
-   **`remove_dependency`**: Removes a dependency from a task.
-   **`validate_dependencies`**: Validates the dependencies of all tasks.
-   **`fix_dependencies`**: Fixes any invalid dependencies.

### 5. Project and Configuration

-   **`initialize_project`**: Initializes a new project.
-   **`generate`**: Generates individual task files.
-   **`models`**: Manages AI model configurations.
-   **`research`**: Performs AI-powered research.

### 6. Tag Management

-   **`add_tag`**: Creates a new tag.
-   **`delete_tag`**: Deletes a tag.
-   **`list_tags`**: Lists all tags.
-   **`use_tag`**: Switches to a different tag.
-   **`rename_tag`**: Renames a tag.
-   **`copy_tag`**: Copies a tag.
```

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

```javascript
/**
 * delete-tag.js
 * Direct function implementation for deleting a tag
 */

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

/**
 * Direct function wrapper for deleting a tag with error handling.
 *
 * @param {Object} args - Command arguments
 * @param {string} args.name - Name of the tag to delete
 * @param {boolean} [args.yes=false] - Skip confirmation prompts
 * @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
 * @param {string} [args.projectRoot] - Project root path
 * @param {Object} log - Logger object
 * @param {Object} context - Additional context (session)
 * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 */
export async function deleteTagDirect(args, log, context = {}) {
	// Destructure expected args
	const { tasksJsonPath, name, yes = false, projectRoot } = args;
	const { session } = context;

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

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

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

		// Check required parameters
		if (!name || typeof name !== 'string') {
			log.error('Missing required parameter: name');
			disableSilentMode();
			return {
				success: false,
				error: {
					code: 'MISSING_PARAMETER',
					message: 'Tag name is required and must be a string'
				}
			};
		}

		log.info(`Deleting tag: ${name}`);

		// Prepare options
		const options = {
			yes // For MCP, we always skip confirmation prompts
		};

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

		// Restore normal logging
		disableSilentMode();

		return {
			success: true,
			data: {
				tagName: result.tagName,
				deleted: result.deleted,
				tasksDeleted: result.tasksDeleted,
				wasCurrentTag: result.wasCurrentTag,
				switchedToMaster: result.switchedToMaster,
				message: `Successfully deleted tag "${result.tagName}"`
			}
		};
	} catch (error) {
		// Make sure to restore normal logging even if there's an error
		disableSilentMode();

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

```

--------------------------------------------------------------------------------
/tests/unit/scripts/modules/task-manager/update-task-by-id.test.js:
--------------------------------------------------------------------------------

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

jest.unstable_mockModule('fs', () => {
	const mockFs = {
		existsSync: jest.fn(() => true),
		writeFileSync: jest.fn(),
		readFileSync: jest.fn(),
		unlinkSync: jest.fn()
	};
	return { default: mockFs, ...mockFs };
});

jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
	readJSON: jest.fn(),
	writeJSON: jest.fn(),
	log: jest.fn(),
	isSilentMode: jest.fn(() => false),
	findProjectRoot: jest.fn(() => '/project'),
	flattenTasksWithSubtasks: jest.fn(() => []),
	truncate: jest.fn((t) => t),
	isEmpty: jest.fn(() => false),
	resolveEnvVariable: jest.fn(),
	findTaskById: jest.fn(),
	getCurrentTag: jest.fn(() => 'master')
}));

jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({
	getStatusWithColor: jest.fn((s) => s),
	startLoadingIndicator: jest.fn(() => ({ stop: jest.fn() })),
	stopLoadingIndicator: jest.fn(),
	displayAiUsageSummary: jest.fn()
}));

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

jest.unstable_mockModule(
	'../../../../../scripts/modules/ai-services-unified.js',
	() => ({
		generateTextService: jest
			.fn()
			.mockResolvedValue({ mainResult: { content: '{}' }, telemetryData: {} })
	})
);

jest.unstable_mockModule(
	'../../../../../scripts/modules/config-manager.js',
	() => ({
		getDebugFlag: jest.fn(() => false),
		isApiKeySet: jest.fn(() => true),
		hasCodebaseAnalysis: jest.fn(() => false)
	})
);

const { readJSON, log } = await import(
	'../../../../../scripts/modules/utils.js'
);
const { default: updateTaskById } = await import(
	'../../../../../scripts/modules/task-manager/update-task-by-id.js'
);

describe('updateTaskById validation', () => {
	beforeEach(() => {
		jest.clearAllMocks();
		jest.spyOn(process, 'exit').mockImplementation(() => {
			throw new Error('process.exit called');
		});
	});

	test('throws error if prompt is empty', async () => {
		await expect(
			updateTaskById(
				'tasks/tasks.json',
				1,
				'',
				false,
				{ tag: 'master' },
				'json'
			)
		).rejects.toThrow('Prompt cannot be empty');
	});

	test('throws error if task file missing', async () => {
		const fs = await import('fs');
		fs.existsSync.mockReturnValue(false);
		await expect(
			updateTaskById(
				'tasks/tasks.json',
				1,
				'prompt',
				false,
				{
					tag: 'master'
				},
				'json'
			)
		).rejects.toThrow('Tasks file not found');
	});

	test('throws error when task ID not found', async () => {
		const fs = await import('fs');
		fs.existsSync.mockReturnValue(true);
		readJSON.mockReturnValue({ tag: 'master', tasks: [] });
		await expect(
			updateTaskById(
				'tasks/tasks.json',
				42,
				'prompt',
				false,
				{
					tag: 'master'
				},
				'json'
			)
		).rejects.toThrow('Task with ID 42 not found');
		expect(log).toHaveBeenCalled();
	});
});

```

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

```markdown
---
title: "Cursor AI Integration"
description: "Learn how to set up and use Task Master with Cursor AI"
---

## Setting up Cursor AI Integration

<Check>
  Task Master is designed to work seamlessly with [Cursor AI](https://www.cursor.so/), providing a structured workflow for AI-driven development.
</Check>

<AccordionGroup>
  <Accordion title="Using Cursor with MCP (Recommended)" icon="sparkles">
    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:

    ```
    What tasks are available to work on next?
    Can you analyze the complexity of our tasks?
    I'd like to implement task 4. What does it involve?
    ```
  </Accordion>
  <Accordion title="Manual Cursor Setup">
    If you're not using MCP, you can still set up Cursor integration:

    <Steps>
      <Step title="After initializing your project, open it in Cursor">
        The `.cursor/rules/dev_workflow.mdc` file is automatically loaded by Cursor, providing the AI with knowledge about the task management system
      </Step>
      <Step title="Place your PRD document in the scripts/ directory (e.g., scripts/prd.txt)">
        
      </Step>
      <Step title="Open Cursor's AI chat and switch to Agent mode">
        
      </Step>
    </Steps>
  </Accordion>
  <Accordion title="Alternative MCP Setup in Cursor">
    <Steps>
      <Step title="Go to Cursor settings">
        
      </Step>
      <Step title="Navigate to the MCP section">
        
      </Step>
      <Step title="Click on 'Add New MCP Server'">
        
      </Step>
      <Step title="Configure with the following details:">
        - Name: "Task Master"
        - Type: "Command"
        - Command: "npx -y task-master-ai"
      </Step>
      <Step title="Save Settings">
        
      </Step>
    </Steps>
    Once configured, you can interact with Task Master's task management commands directly through Cursor's interface, providing a more integrated experience.
  </Accordion>
</AccordionGroup>

## Initial Task Generation

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

```
Please use the task-master parse-prd command to generate tasks from my PRD. The PRD is located at scripts/prd.txt.
```

The agent will execute:

```bash
task-master parse-prd scripts/prd.txt
```

This will:

- Parse your PRD document
- Generate a structured `tasks.json` file with tasks, dependencies, priorities, and test strategies
- The agent will understand this process due to the Cursor rules

### Generate Individual Task Files

Next, ask the agent to generate individual task files:

```
Please generate individual task files from tasks.json
```

The agent will execute:

```bash
task-master generate
```

This creates individual task files in the `tasks/` directory (e.g., `task_001.txt`, `task_002.txt`), making it easier to reference specific tasks.

```

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

```javascript
/**
 * @fileoverview Extract JSON from MCP response, handling markdown blocks and other formatting
 */

/**
 * Extract JSON from MCP AI response
 * @param {string} text - The text to extract JSON from
 * @returns {string} - The extracted JSON string
 */
export function extractJson(text) {
	// Remove markdown code blocks if present
	let jsonText = text.trim();

	// Remove ```json blocks
	jsonText = jsonText.replace(/^```json\s*/gm, '');
	jsonText = jsonText.replace(/^```\s*/gm, '');
	jsonText = jsonText.replace(/```\s*$/gm, '');

	// Remove common TypeScript/JavaScript patterns
	jsonText = jsonText.replace(/^const\s+\w+\s*=\s*/, ''); // Remove "const varName = "
	jsonText = jsonText.replace(/^let\s+\w+\s*=\s*/, ''); // Remove "let varName = "
	jsonText = jsonText.replace(/^var\s+\w+\s*=\s*/, ''); // Remove "var varName = "
	jsonText = jsonText.replace(/;?\s*$/, ''); // Remove trailing semicolons

	// Remove explanatory text before JSON (common with AI responses)
	jsonText = jsonText.replace(/^.*?(?=\{|\[)/s, '');

	// Remove explanatory text after JSON
	const lines = jsonText.split('\n');
	let jsonEndIndex = -1;
	let braceCount = 0;
	let inString = false;
	let escapeNext = false;

	// Find the end of the JSON by tracking braces
	for (let i = 0; i < jsonText.length; i++) {
		const char = jsonText[i];

		if (escapeNext) {
			escapeNext = false;
			continue;
		}

		if (char === '\\') {
			escapeNext = true;
			continue;
		}

		if (char === '"' && !escapeNext) {
			inString = !inString;
			continue;
		}

		if (!inString) {
			if (char === '{' || char === '[') {
				braceCount++;
			} else if (char === '}' || char === ']') {
				braceCount--;
				if (braceCount === 0) {
					jsonEndIndex = i;
					break;
				}
			}
		}
	}

	if (jsonEndIndex > -1) {
		jsonText = jsonText.substring(0, jsonEndIndex + 1);
	}

	// Try to extract JSON object or array if previous method didn't work
	if (jsonEndIndex === -1) {
		const objectMatch = jsonText.match(/{[\s\S]*}/);
		const arrayMatch = jsonText.match(/\[[\s\S]*\]/);

		if (objectMatch) {
			jsonText = objectMatch[0];
		} else if (arrayMatch) {
			jsonText = arrayMatch[0];
		}
	}

	// First try to parse as valid JSON
	try {
		JSON.parse(jsonText);
		return jsonText;
	} catch {
		// If it's not valid JSON, it might be a JavaScript object literal
		// Try to convert it to valid JSON
		try {
			// This is a simple conversion that handles basic cases
			// Replace unquoted keys with quoted keys
			const converted = jsonText
				.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":')
				// Replace single quotes with double quotes
				.replace(/'/g, '"')
				// Handle trailing commas
				.replace(/,\s*([}\]])/g, '$1');

			// Validate the converted JSON
			JSON.parse(converted);
			return converted;
		} catch {
			// If all else fails, return the original text
			// The calling code will handle the error appropriately
			return text;
		}
	}
}

```

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

```javascript
/**
 * tools/update-subtask.js
 * Tool to append additional information to a specific subtask
 */

import { z } from 'zod';
import {
	handleApiResult,
	createErrorResponse,
	withNormalizedProjectRoot
} from './utils.js';
import { updateSubtaskByIdDirect } from '../core/task-master-core.js';
import { findTasksPath } from '../core/utils/path-utils.js';
import { resolveTag } from '../../../scripts/modules/utils.js';

/**
 * Register the update-subtask tool with the MCP server
 * @param {Object} server - FastMCP server instance
 */
export function registerUpdateSubtaskTool(server) {
	server.addTool({
		name: 'update_subtask',
		description:
			'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.',
		parameters: z.object({
			id: z
				.string()
				.describe(
					'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.'
				),
			prompt: z.string().describe('Information to add to the subtask'),
			research: z
				.boolean()
				.optional()
				.describe('Use Perplexity AI for research-backed updates'),
			file: z.string().optional().describe('Absolute path to the tasks file'),
			projectRoot: z
				.string()
				.describe('The directory of the project. Must be an absolute path.'),
			tag: z.string().optional().describe('Tag context to operate on')
		}),
		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
			const toolName = 'update_subtask';

			try {
				const resolvedTag = resolveTag({
					projectRoot: args.projectRoot,
					tag: args.tag
				});
				log.info(`Updating subtask with args: ${JSON.stringify(args)}`);

				let tasksJsonPath;
				try {
					tasksJsonPath = findTasksPath(
						{ projectRoot: args.projectRoot, file: args.file },
						log
					);
				} catch (error) {
					log.error(`${toolName}: Error finding tasks.json: ${error.message}`);
					return createErrorResponse(
						`Failed to find tasks.json: ${error.message}`
					);
				}

				const result = await updateSubtaskByIdDirect(
					{
						tasksJsonPath: tasksJsonPath,
						id: args.id,
						prompt: args.prompt,
						research: args.research,
						projectRoot: args.projectRoot,
						tag: resolvedTag
					},
					log,
					{ session }
				);

				if (result.success) {
					log.info(`Successfully updated subtask with ID ${args.id}`);
				} else {
					log.error(
						`Failed to update subtask: ${result.error?.message || 'Unknown error'}`
					);
				}

				return handleApiResult(
					result,
					log,
					'Error updating subtask',
					undefined,
					args.projectRoot
				);
			} catch (error) {
				log.error(
					`Critical error in ${toolName} tool execute: ${error.message}`
				);
				return createErrorResponse(
					`Internal tool error (${toolName}): ${error.message}`
				);
			}
		})
	});
}

```

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

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

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

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

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

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

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

		// Spy on fs methods
		jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
		jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
			if (filePath.toString().includes('.trae')) {
				return 'Existing trae rules content';
			}
			return '{}';
		});
		jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
		jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
	});

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

	// Test function that simulates the createProjectStructure behavior for Trae files
	function mockCreateTraeStructure() {
		// Create main .trae directory
		fs.mkdirSync(path.join(tempDir, '.trae'), { recursive: true });

		// Create rules directory
		fs.mkdirSync(path.join(tempDir, '.trae', 'rules'), { recursive: true });

		// Create rule files
		const ruleFiles = [
			'dev_workflow.md',
			'taskmaster.md',
			'architecture.md',
			'commands.md',
			'dependencies.md'
		];

		for (const ruleFile of ruleFiles) {
			fs.writeFileSync(
				path.join(tempDir, '.trae', 'rules', ruleFile),
				`Content for ${ruleFile}`
			);
		}
	}

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

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

	test('creates rule files for Trae', () => {
		// Act
		mockCreateTraeStructure();

		// Assert - check rule files are created
		expect(fs.writeFileSync).toHaveBeenCalledWith(
			path.join(tempDir, '.trae', 'rules', 'dev_workflow.md'),
			expect.any(String)
		);
		expect(fs.writeFileSync).toHaveBeenCalledWith(
			path.join(tempDir, '.trae', 'rules', 'taskmaster.md'),
			expect.any(String)
		);
		expect(fs.writeFileSync).toHaveBeenCalledWith(
			path.join(tempDir, '.trae', 'rules', 'architecture.md'),
			expect.any(String)
		);
	});

	test('does not create MCP configuration files', () => {
		// Act
		mockCreateTraeStructure();

		// Assert - Trae doesn't use MCP configuration
		expect(fs.writeFileSync).not.toHaveBeenCalledWith(
			path.join(tempDir, '.trae', 'mcp.json'),
			expect.any(String)
		);
	});
});

```

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

```javascript
/**
 * tools/update.js
 * Tool to update tasks based on new context/prompt
 */

import { z } from 'zod';
import {
	handleApiResult,
	createErrorResponse,
	withNormalizedProjectRoot
} from './utils.js';
import { updateTasksDirect } from '../core/task-master-core.js';
import { findTasksPath } from '../core/utils/path-utils.js';
import { resolveTag } from '../../../scripts/modules/utils.js';

/**
 * Register the update tool with the MCP server
 * @param {Object} server - FastMCP server instance
 */
export function registerUpdateTool(server) {
	server.addTool({
		name: 'update',
		description:
			"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.",
		parameters: z.object({
			from: z
				.string()
				.describe(
					"Task ID from which to start updating (inclusive). IMPORTANT: This tool uses 'from', not 'id'"
				),
			prompt: z
				.string()
				.describe('Explanation of changes or new context to apply'),
			research: z
				.boolean()
				.optional()
				.describe('Use Perplexity AI for research-backed updates'),
			file: z
				.string()
				.optional()
				.describe('Path to the tasks file relative to project root'),
			projectRoot: z
				.string()
				.optional()
				.describe(
					'The directory of the project. (Optional, usually from session)'
				),
			tag: z.string().optional().describe('Tag context to operate on')
		}),
		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
			const toolName = 'update';
			const { from, prompt, research, file, projectRoot, tag } = args;

			const resolvedTag = resolveTag({
				projectRoot: args.projectRoot,
				tag: args.tag
			});

			try {
				log.info(
					`Executing ${toolName} tool with normalized root: ${projectRoot}`
				);

				let tasksJsonPath;
				try {
					tasksJsonPath = findTasksPath({ projectRoot, file }, log);
					log.info(`${toolName}: Resolved tasks path: ${tasksJsonPath}`);
				} catch (error) {
					log.error(`${toolName}: Error finding tasks.json: ${error.message}`);
					return createErrorResponse(
						`Failed to find tasks.json within project root '${projectRoot}': ${error.message}`
					);
				}

				const result = await updateTasksDirect(
					{
						tasksJsonPath: tasksJsonPath,
						from: from,
						prompt: prompt,
						research: research,
						projectRoot: projectRoot,
						tag: resolvedTag
					},
					log,
					{ session }
				);

				log.info(
					`${toolName}: Direct function result: success=${result.success}`
				);
				return handleApiResult(
					result,
					log,
					'Error updating tasks',
					undefined,
					args.projectRoot
				);
			} catch (error) {
				log.error(
					`Critical error in ${toolName} tool execute: ${error.message}`
				);
				return createErrorResponse(
					`Internal tool error (${toolName}): ${error.message}`
				);
			}
		})
	});
}

```

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

```javascript
/**
 * add-dependency.js
 * Direct function implementation for adding a dependency to a task
 */

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

/**
 * Direct function wrapper for addDependency with error handling.
 *
 * @param {Object} args - Command arguments
 * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 * @param {string|number} args.id - Task ID to add dependency to
 * @param {string|number} args.dependsOn - Task ID that will become a dependency
 * @param {string} args.tag - Tag for the task (optional)
 * @param {string} args.projectRoot - Project root path (for MCP/env fallback)
 * @param {Object} log - Logger object
 * @returns {Promise<Object>} - Result object with success status and data/error information
 */
export async function addDependencyDirect(args, log) {
	// Destructure expected args
	const { tasksJsonPath, id, dependsOn, tag, projectRoot } = args;
	try {
		log.info(`Adding dependency with args: ${JSON.stringify(args)}`);

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

		// Validate required parameters
		if (!id) {
			return {
				success: false,
				error: {
					code: 'INPUT_VALIDATION_ERROR',
					message: 'Task ID (id) is required'
				}
			};
		}

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

		// Use provided path
		const tasksPath = tasksJsonPath;

		// Format IDs for the core function
		const taskId =
			id && id.includes && id.includes('.') ? id : parseInt(id, 10);
		const dependencyId =
			dependsOn && dependsOn.includes && dependsOn.includes('.')
				? dependsOn
				: parseInt(dependsOn, 10);

		log.info(
			`Adding dependency: task ${taskId} will depend on ${dependencyId}`
		);

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

		// Create context object
		const context = { projectRoot, tag };

		// Call the core function using the provided path
		await addDependency(tasksPath, taskId, dependencyId, context);

		// Restore normal logging
		disableSilentMode();

		return {
			success: true,
			data: {
				message: `Successfully added dependency: Task ${taskId} now depends on ${dependencyId}`,
				taskId: taskId,
				dependencyId: dependencyId
			}
		};
	} catch (error) {
		// Make sure to restore normal logging even if there's an error
		disableSilentMode();

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

```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/complexity-report.js:
--------------------------------------------------------------------------------

```javascript
/**
 * complexity-report.js
 * Direct function implementation for displaying complexity analysis report
 */

import {
	readComplexityReport,
	enableSilentMode,
	disableSilentMode
} from '../../../../scripts/modules/utils.js';

/**
 * Direct function wrapper for displaying the complexity report with error handling and caching.
 *
 * @param {Object} args - Command arguments containing reportPath.
 * @param {string} args.reportPath - Explicit path to the complexity report file.
 * @param {Object} log - Logger object
 * @returns {Promise<Object>} - Result object with success status and data/error information
 */
export async function complexityReportDirect(args, log) {
	// Destructure expected args
	const { reportPath } = args;
	try {
		log.info(`Getting complexity report with args: ${JSON.stringify(args)}`);

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

		// Use the provided report path
		log.info(`Looking for complexity report at: ${reportPath}`);

		// Generate cache key based on report path
		const cacheKey = `complexityReport:${reportPath}`;

		// Define the core action function to read the report
		const coreActionFn = async () => {
			try {
				// Enable silent mode to prevent console logs from interfering with JSON response
				enableSilentMode();

				const report = readComplexityReport(reportPath);

				// Restore normal logging
				disableSilentMode();

				if (!report) {
					log.warn(`No complexity report found at ${reportPath}`);
					return {
						success: false,
						error: {
							code: 'FILE_NOT_FOUND_ERROR',
							message: `No complexity report found at ${reportPath}. Run 'analyze-complexity' first.`
						}
					};
				}

				return {
					success: true,
					data: {
						report,
						reportPath
					}
				};
			} catch (error) {
				// Make sure to restore normal logging even if there's an error
				disableSilentMode();

				log.error(`Error reading complexity report: ${error.message}`);
				return {
					success: false,
					error: {
						code: 'READ_ERROR',
						message: error.message
					}
				};
			}
		};

		// Use the caching utility
		try {
			const result = await coreActionFn();
			log.info('complexityReportDirect completed');
			return result;
		} catch (error) {
			// Ensure silent mode is disabled
			disableSilentMode();

			log.error(`Unexpected error during complexityReport: ${error.message}`);
			return {
				success: false,
				error: {
					code: 'UNEXPECTED_ERROR',
					message: error.message
				}
			};
		}
	} catch (error) {
		// Ensure silent mode is disabled if an outer error occurs
		disableSilentMode();

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

```

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

```javascript
import chalk from 'chalk';
import { isSilentMode } from '../../scripts/modules/utils.js';
import { getLogLevel } from '../../scripts/modules/config-manager.js';

// Define log levels
const LOG_LEVELS = {
	debug: 0,
	info: 1,
	warn: 2,
	error: 3,
	success: 4
};

// Get log level from config manager or default to info
const LOG_LEVEL = LOG_LEVELS[getLogLevel().toLowerCase()] ?? LOG_LEVELS.info;

/**
 * Logs a message with the specified level
 * @param {string} level - The log level (debug, info, warn, error, success)
 * @param  {...any} args - Arguments to log
 */
function log(level, ...args) {
	// Skip logging if silent mode is enabled
	if (isSilentMode()) {
		return;
	}

	// Use text prefixes instead of emojis
	const prefixes = {
		debug: chalk.gray('[DEBUG]'),
		info: chalk.blue('[INFO]'),
		warn: chalk.yellow('[WARN]'),
		error: chalk.red('[ERROR]'),
		success: chalk.green('[SUCCESS]')
	};

	if (LOG_LEVELS[level] !== undefined && LOG_LEVELS[level] >= LOG_LEVEL) {
		const prefix = prefixes[level] || '';
		let coloredArgs = args;

		try {
			switch (level) {
				case 'error':
					coloredArgs = args.map((arg) =>
						typeof arg === 'string' ? chalk.red(arg) : arg
					);
					break;
				case 'warn':
					coloredArgs = args.map((arg) =>
						typeof arg === 'string' ? chalk.yellow(arg) : arg
					);
					break;
				case 'success':
					coloredArgs = args.map((arg) =>
						typeof arg === 'string' ? chalk.green(arg) : arg
					);
					break;
				case 'info':
					coloredArgs = args.map((arg) =>
						typeof arg === 'string' ? chalk.blue(arg) : arg
					);
					break;
				case 'debug':
					coloredArgs = args.map((arg) =>
						typeof arg === 'string' ? chalk.gray(arg) : arg
					);
					break;
				// default: use original args (no color)
			}
		} catch (colorError) {
			// Fallback if chalk fails on an argument
			// Use console.error here for internal logger errors, separate from normal logging
			console.error('Internal Logger Error applying chalk color:', colorError);
			coloredArgs = args;
		}

		// Revert to console.log - FastMCP's context logger (context.log)
		// is responsible for directing logs correctly (e.g., to stderr)
		// during tool execution without upsetting the client connection.
		// Logs outside of tool execution (like startup) will go to stdout.
		console.log(prefix, ...coloredArgs);
	}
}

/**
 * Create a logger object with methods for different log levels
 * @returns {Object} Logger object with info, error, debug, warn, and success methods
 */
export function createLogger() {
	const createLogMethod =
		(level) =>
		(...args) =>
			log(level, ...args);

	return {
		debug: createLogMethod('debug'),
		info: createLogMethod('info'),
		warn: createLogMethod('warn'),
		error: createLogMethod('error'),
		success: createLogMethod('success'),
		log: log // Also expose the raw log function
	};
}

// Export a default logger instance
const logger = createLogger();

export default logger;
export { log, LOG_LEVELS };

```

--------------------------------------------------------------------------------
/apps/extension/src/webview/App.tsx:
--------------------------------------------------------------------------------

```typescript
/**
 * Main App Component
 */

import React, { useReducer, useState, useEffect, useRef } from 'react';
import { VSCodeContext } from './contexts/VSCodeContext';
import { QueryProvider } from './providers/QueryProvider';
import { AppContent } from './components/AppContent';
import { ToastContainer } from './components/ToastContainer';
import { ErrorBoundary } from './components/ErrorBoundary';
import { appReducer, initialState } from './reducers/appReducer';
import { useWebviewHeight } from './hooks/useWebviewHeight';
import { useVSCodeMessages } from './hooks/useVSCodeMessages';
import {
	showSuccessToast,
	showInfoToast,
	showWarningToast,
	showErrorToast,
	createToast
} from './utils/toast';

export const App: React.FC = () => {
	const [state, dispatch] = useReducer(appReducer, initialState);
	const [vscode] = useState(() => window.acquireVsCodeApi?.());
	const availableHeight = useWebviewHeight();
	const { sendMessage } = useVSCodeMessages(vscode, state, dispatch);
	const hasInitialized = useRef(false);

	// Initialize the webview
	useEffect(() => {
		if (hasInitialized.current) return;
		hasInitialized.current = true;

		if (!vscode) {
			console.warn('⚠️ VS Code API not available - running in standalone mode');
			dispatch({
				type: 'SET_CONNECTION_STATUS',
				payload: { isConnected: false, status: 'Standalone Mode' }
			});
			return;
		}

		console.log('🔄 Initializing webview...');

		// Notify extension that webview is ready
		vscode.postMessage({ type: 'ready' });

		// React Query will handle task fetching, so we only need to load tags data
		sendMessage({ type: 'getTags' })
			.then((tagsData) => {
				if (tagsData?.tags && tagsData?.currentTag) {
					const tagNames = tagsData.tags.map((tag: any) => tag.name || tag);
					dispatch({
						type: 'SET_TAG_DATA',
						payload: {
							currentTag: tagsData.currentTag,
							availableTags: tagNames
						}
					});
				}
			})
			.catch((error) => {
				console.error('❌ Failed to load tags:', error);
			});
	}, [vscode, sendMessage, dispatch]);

	const contextValue = {
		vscode,
		state,
		dispatch,
		sendMessage,
		availableHeight,
		// Toast notification functions
		showSuccessToast: showSuccessToast(dispatch),
		showInfoToast: showInfoToast(dispatch),
		showWarningToast: showWarningToast(dispatch),
		showErrorToast: showErrorToast(dispatch)
	};

	return (
		<QueryProvider>
			<VSCodeContext.Provider value={contextValue}>
				<ErrorBoundary
					onError={(error) => {
						// Handle React errors and show appropriate toast
						dispatch({
							type: 'ADD_TOAST',
							payload: createToast(
								'error',
								'Component Error',
								`A React component crashed: ${error.message}`,
								10000
							)
						});
					}}
				>
					<AppContent />
					<ToastContainer
						notifications={state.toastNotifications}
						onDismiss={(id) => dispatch({ type: 'REMOVE_TOAST', payload: id })}
					/>
				</ErrorBoundary>
			</VSCodeContext.Provider>
		</QueryProvider>
	);
};

```

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

```javascript
/**
 * tools/research.js
 * Tool to perform AI-powered research queries with project context
 */

import { z } from 'zod';
import {
	createErrorResponse,
	handleApiResult,
	withNormalizedProjectRoot
} from './utils.js';
import { researchDirect } from '../core/task-master-core.js';
import { resolveTag } from '../../../scripts/modules/utils.js';

/**
 * Register the research tool with the MCP server
 * @param {Object} server - FastMCP server instance
 */
export function registerResearchTool(server) {
	server.addTool({
		name: 'research',
		description: 'Perform AI-powered research queries with project context',

		parameters: z.object({
			query: z.string().describe('Research query/prompt (required)'),
			taskIds: z
				.string()
				.optional()
				.describe(
					'Comma-separated list of task/subtask IDs for context (e.g., "15,16.2,17")'
				),
			filePaths: z
				.string()
				.optional()
				.describe(
					'Comma-separated list of file paths for context (e.g., "src/api.js,docs/readme.md")'
				),
			customContext: z
				.string()
				.optional()
				.describe('Additional custom context text to include in the research'),
			includeProjectTree: z
				.boolean()
				.optional()
				.describe(
					'Include project file tree structure in context (default: false)'
				),
			detailLevel: z
				.enum(['low', 'medium', 'high'])
				.optional()
				.describe('Detail level for the research response (default: medium)'),
			saveTo: z
				.string()
				.optional()
				.describe(
					'Automatically save research results to specified task/subtask ID (e.g., "15" or "15.2")'
				),
			saveToFile: z
				.boolean()
				.optional()
				.describe(
					'Save research results to .taskmaster/docs/research/ directory (default: false)'
				),
			projectRoot: z
				.string()
				.describe('The directory of the project. Must be an absolute path.'),
			tag: z.string().optional().describe('Tag context to operate on')
		}),
		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
			try {
				const resolvedTag = resolveTag({
					projectRoot: args.projectRoot,
					tag: args.tag
				});
				log.info(
					`Starting research with query: "${args.query.substring(0, 100)}${args.query.length > 100 ? '...' : ''}"`
				);

				// Call the direct function
				const result = await researchDirect(
					{
						query: args.query,
						taskIds: args.taskIds,
						filePaths: args.filePaths,
						customContext: args.customContext,
						includeProjectTree: args.includeProjectTree || false,
						detailLevel: args.detailLevel || 'medium',
						saveTo: args.saveTo,
						saveToFile: args.saveToFile || false,
						projectRoot: args.projectRoot,
						tag: resolvedTag
					},
					log,
					{ session }
				);

				return handleApiResult(
					result,
					log,
					'Error performing research',
					undefined,
					args.projectRoot
				);
			} catch (error) {
				log.error(`Error in research tool: ${error.message}`);
				return createErrorResponse(error.message);
			}
		})
	});
}

```

--------------------------------------------------------------------------------
/apps/docs/archive/Installation.mdx:
--------------------------------------------------------------------------------

```markdown
---
title: "Installation(2)"
description: "This guide walks you through setting up Task Master in your development environment."
---

## Initial Setup

<Tip>
  MCP (Model Control Protocol) provides the easiest way to get started with Task Master directly in your editor.
</Tip>

<AccordionGroup>
  <Accordion title="Option 1: Using MCP (Recommended)" icon="sparkles">
    <Steps>
      <Step title="Add the MCP config to your editor">
        <Link href="https://cursor.sh">Cursor</Link> recommended, but it works with other text editors
        ```json
        {
        	"mcpServers": {
        		"taskmaster-ai": {
        			"command": "npx",
        			"args": ["-y", "task-master-ai"],
        			"env": {
        				"ANTHROPIC_API_KEY": "YOUR_ANTHROPIC_API_KEY_HERE",
        				"PERPLEXITY_API_KEY": "YOUR_PERPLEXITY_API_KEY_HERE",
        				"MODEL": "claude-3-7-sonnet-20250219",
        				"PERPLEXITY_MODEL": "sonar-pro",
        				"MAX_TOKENS": 128000,
        				"TEMPERATURE": 0.2,
        				"DEFAULT_SUBTASKS": 5,
        				"DEFAULT_PRIORITY": "medium"
        			}
        		}
        	}
        }
        ```
      </Step>
      <Step title="Enable the MCP in your editor settings">
        
      </Step>
      <Step title="Prompt the AI to initialize Task Master">
        > "Can you please initialize taskmaster-ai into my project?"

        **The AI will:**

        1. Create necessary project structure
        2. Set up initial configuration files
        3. Guide you through the rest of the process
        4. Place your PRD document in the `scripts/` directory (e.g., `scripts/prd.txt`)
        5. **Use natural language commands** to interact with Task Master:

           > "Can you parse my PRD at scripts/prd.txt?"
           >
           > "What's the next task I should work on?"
           >
           > "Can you help me implement task 3?"
      </Step>
    </Steps>
  </Accordion>
  <Accordion title="Option 2: Manual Installation">
    If you prefer to use the command line interface directly:

    <Steps>
      <Step title="Install">
        <CodeGroup>

        ```bash Global
        npm install -g task-master-ai
        ```

        
        ```bash Local
        npm install task-master-ai
        ```

        </CodeGroup>
      </Step>
      <Step title="Initialize a new project">
        <CodeGroup>

        ```bash Global
        task-master init
        ```

        
        ```bash Local
        npx task-master-init
        ```

        </CodeGroup>
      </Step>
    </Steps>
    This will prompt you for project details and set up a new project with the necessary files and structure.
  </Accordion>
</AccordionGroup>

## Common Commands

<Tip>
  After setting up Task Master, you can use these commands (either via AI prompts or CLI)
</Tip>

```bash
# Parse a PRD and generate tasks
task-master parse-prd your-prd.txt

# List all tasks
task-master list

# Show the next task to work on
task-master next

# Generate task files
task-master generate 

```

--------------------------------------------------------------------------------
/tests/unit/ai-providers/claude-code.test.js:
--------------------------------------------------------------------------------

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

// Mock the claude-code SDK module
jest.unstable_mockModule(
	'../../../src/ai-providers/custom-sdk/claude-code/index.js',
	() => ({
		createClaudeCode: jest.fn(() => {
			const provider = (modelId, settings) => ({
				// Mock language model
				id: modelId,
				settings
			});
			provider.languageModel = jest.fn((id, settings) => ({ id, settings }));
			provider.chat = provider.languageModel;
			return provider;
		})
	})
);

// Mock the base provider
jest.unstable_mockModule('../../../src/ai-providers/base-provider.js', () => ({
	BaseAIProvider: class {
		constructor() {
			this.name = 'Base Provider';
		}
		handleError(context, error) {
			throw error;
		}
	}
}));

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

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

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

	describe('constructor', () => {
		it('should set the provider name to Claude Code', () => {
			expect(provider.name).toBe('Claude Code');
		});
	});

	describe('validateAuth', () => {
		it('should not throw an error (no API key required)', () => {
			expect(() => provider.validateAuth({})).not.toThrow();
		});

		it('should not require any parameters', () => {
			expect(() => provider.validateAuth()).not.toThrow();
		});

		it('should work with any params passed', () => {
			expect(() =>
				provider.validateAuth({
					apiKey: 'some-key',
					baseURL: 'https://example.com'
				})
			).not.toThrow();
		});
	});

	describe('getClient', () => {
		it('should return a claude code client', () => {
			const client = provider.getClient({});
			expect(client).toBeDefined();
			expect(typeof client).toBe('function');
		});

		it('should create client without API key or base URL', () => {
			const client = provider.getClient({});
			expect(client).toBeDefined();
		});

		it('should handle params even though they are not used', () => {
			const client = provider.getClient({
				baseURL: 'https://example.com',
				apiKey: 'unused-key'
			});
			expect(client).toBeDefined();
		});

		it('should have languageModel and chat methods', () => {
			const client = provider.getClient({});
			expect(client.languageModel).toBeDefined();
			expect(client.chat).toBeDefined();
			expect(client.chat).toBe(client.languageModel);
		});
	});

	describe('error handling', () => {
		it('should handle client initialization errors', async () => {
			// Force an error by making createClaudeCode throw
			const { createClaudeCode } = await import(
				'../../../src/ai-providers/custom-sdk/claude-code/index.js'
			);
			createClaudeCode.mockImplementationOnce(() => {
				throw new Error('Mock initialization error');
			});

			// Create a new provider instance to use the mocked createClaudeCode
			const errorProvider = new ClaudeCodeProvider();
			expect(() => errorProvider.getClient({})).toThrow(
				'Mock initialization error'
			);
		});
	});
});

```

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

```javascript
/**
 * tools/expand-all.js
 * Tool for expanding all pending tasks with subtasks
 */

import { z } from 'zod';
import {
	handleApiResult,
	createErrorResponse,
	withNormalizedProjectRoot
} from './utils.js';
import { expandAllTasksDirect } from '../core/task-master-core.js';
import { findTasksPath } from '../core/utils/path-utils.js';
import { resolveTag } from '../../../scripts/modules/utils.js';

/**
 * Register the expandAll tool with the MCP server
 * @param {Object} server - FastMCP server instance
 */
export function registerExpandAllTool(server) {
	server.addTool({
		name: 'expand_all',
		description:
			'Expand all pending tasks into subtasks based on complexity or defaults',
		parameters: z.object({
			num: z
				.string()
				.optional()
				.describe(
					'Target number of subtasks per task (uses complexity/defaults otherwise)'
				),
			research: z
				.boolean()
				.optional()
				.describe(
					'Enable research-backed subtask generation (e.g., using Perplexity)'
				),
			prompt: z
				.string()
				.optional()
				.describe(
					'Additional context to guide subtask generation for all tasks'
				),
			force: z
				.boolean()
				.optional()
				.describe(
					'Force regeneration of subtasks for tasks that already have them'
				),
			file: z
				.string()
				.optional()
				.describe(
					'Absolute path to the tasks file in the /tasks folder inside the project root (default: tasks/tasks.json)'
				),
			projectRoot: z
				.string()
				.optional()
				.describe(
					'Absolute path to the project root directory (derived from session if possible)'
				),
			tag: z.string().optional().describe('Tag context to operate on')
		}),
		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
			try {
				log.info(
					`Tool expand_all execution started with args: ${JSON.stringify(args)}`
				);

				const resolvedTag = resolveTag({
					projectRoot: args.projectRoot,
					tag: args.tag
				});
				let tasksJsonPath;
				try {
					tasksJsonPath = findTasksPath(
						{ projectRoot: args.projectRoot, file: args.file },
						log
					);
					log.info(`Resolved tasks.json path: ${tasksJsonPath}`);
				} catch (error) {
					log.error(`Error finding tasks.json: ${error.message}`);
					return createErrorResponse(
						`Failed to find tasks.json: ${error.message}`
					);
				}

				const result = await expandAllTasksDirect(
					{
						tasksJsonPath: tasksJsonPath,
						num: args.num,
						research: args.research,
						prompt: args.prompt,
						force: args.force,
						projectRoot: args.projectRoot,
						tag: resolvedTag
					},
					log,
					{ session }
				);

				return handleApiResult(
					result,
					log,
					'Error expanding all tasks',
					undefined,
					args.projectRoot
				);
			} catch (error) {
				log.error(
					`Unexpected error in expand_all tool execute: ${error.message}`
				);
				if (error.stack) {
					log.error(error.stack);
				}
				return createErrorResponse(
					`An unexpected error occurred: ${error.message}`
				);
			}
		})
	});
}

```

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

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

// Mock the base provider to avoid circular dependencies
jest.unstable_mockModule('../../src/ai-providers/base-provider.js', () => ({
	BaseAIProvider: class {
		constructor() {
			this.name = 'Base Provider';
		}
		handleError(context, error) {
			throw error;
		}
	}
}));

// Mock the claude-code SDK to simulate it not being installed
jest.unstable_mockModule('@anthropic-ai/claude-code', () => {
	throw new Error("Cannot find module '@anthropic-ai/claude-code'");
});

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

describe('Claude Code Optional Dependency Integration', () => {
	describe('when @anthropic-ai/claude-code is not installed', () => {
		it('should allow provider instantiation', () => {
			// Provider should instantiate without error
			const provider = new ClaudeCodeProvider();
			expect(provider).toBeDefined();
			expect(provider.name).toBe('Claude Code');
		});

		it('should allow client creation', () => {
			const provider = new ClaudeCodeProvider();
			// Client creation should work
			const client = provider.getClient({});
			expect(client).toBeDefined();
			expect(typeof client).toBe('function');
		});

		it('should fail with clear error when trying to use the model', async () => {
			const provider = new ClaudeCodeProvider();
			const client = provider.getClient({});
			const model = client('opus');

			// The actual usage should fail with the lazy loading error
			await expect(
				model.doGenerate({
					prompt: [{ role: 'user', content: 'Hello' }],
					mode: { type: 'regular' }
				})
			).rejects.toThrow(
				"Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider."
			);
		});

		it('should provide helpful error message for streaming', async () => {
			const provider = new ClaudeCodeProvider();
			const client = provider.getClient({});
			const model = client('sonnet');

			await expect(
				model.doStream({
					prompt: [{ role: 'user', content: 'Hello' }],
					mode: { type: 'regular' }
				})
			).rejects.toThrow(
				"Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider."
			);
		});
	});

	describe('provider behavior', () => {
		it('should not require API key', () => {
			const provider = new ClaudeCodeProvider();
			// Should not throw
			expect(() => provider.validateAuth()).not.toThrow();
			expect(() => provider.validateAuth({ apiKey: null })).not.toThrow();
		});

		it('should work with ai-services-unified when provider is configured', async () => {
			// This tests that the provider can be selected but will fail appropriately
			// when the actual model is used
			const provider = new ClaudeCodeProvider();
			expect(provider).toBeDefined();

			// In real usage, ai-services-unified would:
			// 1. Get the provider instance (works)
			// 2. Call provider.getClient() (works)
			// 3. Create a model (works)
			// 4. Try to generate (fails with clear error)
		});
	});
});

```

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

```javascript
/**
 * tools/update-task.js
 * Tool to update a single task by ID with new information
 */

import { z } from 'zod';
import {
	handleApiResult,
	createErrorResponse,
	withNormalizedProjectRoot
} from './utils.js';
import { updateTaskByIdDirect } from '../core/task-master-core.js';
import { findTasksPath } from '../core/utils/path-utils.js';
import { resolveTag } from '../../../scripts/modules/utils.js';

/**
 * Register the update-task tool with the MCP server
 * @param {Object} server - FastMCP server instance
 */
export function registerUpdateTaskTool(server) {
	server.addTool({
		name: 'update_task',
		description:
			'Updates a single task by ID with new information or context provided in the prompt.',
		parameters: z.object({
			id: z
				.string() // ID can be number or string like "1.2"
				.describe(
					"ID of the task (e.g., '15') to update. Subtasks are supported using the update-subtask tool."
				),
			prompt: z
				.string()
				.describe('New information or context to incorporate into the task'),
			research: z
				.boolean()
				.optional()
				.describe('Use Perplexity AI for research-backed updates'),
			append: z
				.boolean()
				.optional()
				.describe(
					'Append timestamped information to task details instead of full update'
				),
			file: z.string().optional().describe('Absolute path to the tasks file'),
			projectRoot: z
				.string()
				.describe('The directory of the project. Must be an absolute path.'),
			tag: z.string().optional().describe('Tag context to operate on')
		}),
		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
			const toolName = 'update_task';
			try {
				const resolvedTag = resolveTag({
					projectRoot: args.projectRoot,
					tag: args.tag
				});
				log.info(
					`Executing ${toolName} tool with args: ${JSON.stringify(args)}`
				);

				let tasksJsonPath;
				try {
					tasksJsonPath = findTasksPath(
						{ projectRoot: args.projectRoot, file: args.file },
						log
					);
					log.info(`${toolName}: Resolved tasks path: ${tasksJsonPath}`);
				} catch (error) {
					log.error(`${toolName}: Error finding tasks.json: ${error.message}`);
					return createErrorResponse(
						`Failed to find tasks.json: ${error.message}`
					);
				}

				// 3. Call Direct Function - Include projectRoot
				const result = await updateTaskByIdDirect(
					{
						tasksJsonPath: tasksJsonPath,
						id: args.id,
						prompt: args.prompt,
						research: args.research,
						append: args.append,
						projectRoot: args.projectRoot,
						tag: resolvedTag
					},
					log,
					{ session }
				);

				// 4. Handle Result
				log.info(
					`${toolName}: Direct function result: success=${result.success}`
				);
				return handleApiResult(
					result,
					log,
					'Error updating task',
					undefined,
					args.projectRoot
				);
			} catch (error) {
				log.error(
					`Critical error in ${toolName} tool execute: ${error.message}`
				);
				return createErrorResponse(
					`Internal tool error (${toolName}): ${error.message}`
				);
			}
		})
	});
}

```

--------------------------------------------------------------------------------
/src/prompts/schemas/prompt-template.schema.json:
--------------------------------------------------------------------------------

```json
{
	"$schema": "http://json-schema.org/draft-07/schema#",
	"$id": "https://github.com/eyaltoledano/claude-task-master/blob/main/src/prompts/schemas/prompt-template.schema.json",
	"version": "1.0.0",
	"title": "Task Master Prompt Template",
	"description": "Schema for Task Master AI prompt template files",
	"type": "object",
	"required": ["id", "version", "description", "prompts"],
	"properties": {
		"id": {
			"type": "string",
			"pattern": "^[a-z0-9-]+$",
			"description": "Unique identifier for the prompt template"
		},
		"version": {
			"type": "string",
			"pattern": "^\\d+\\.\\d+\\.\\d+$",
			"description": "Semantic version of the prompt template"
		},
		"description": {
			"type": "string",
			"minLength": 1,
			"description": "Brief description of what this prompt does"
		},
		"metadata": {
			"$ref": "#/definitions/metadata"
		},
		"parameters": {
			"type": "object",
			"additionalProperties": {
				"$ref": "#/definitions/parameter"
			}
		},
		"prompts": {
			"type": "object",
			"properties": {
				"default": {
					"$ref": "#/definitions/promptVariant"
				}
			},
			"additionalProperties": {
				"$ref": "#/definitions/conditionalPromptVariant"
			}
		}
	},
	"definitions": {
		"parameter": {
			"type": "object",
			"required": ["type", "description"],
			"properties": {
				"type": {
					"type": "string",
					"enum": ["string", "number", "boolean", "array", "object"]
				},
				"description": {
					"type": "string",
					"minLength": 1
				},
				"required": {
					"type": "boolean",
					"default": false
				},
				"default": {
					"description": "Default value for optional parameters"
				},
				"enum": {
					"type": "array",
					"description": "Valid values for string parameters"
				},
				"pattern": {
					"type": "string",
					"description": "Regular expression pattern for string validation"
				},
				"minimum": {
					"type": "number",
					"description": "Minimum value for number parameters"
				},
				"maximum": {
					"type": "number",
					"description": "Maximum value for number parameters"
				}
			}
		},
		"promptVariant": {
			"type": "object",
			"required": ["system", "user"],
			"properties": {
				"system": {
					"type": "string",
					"minLength": 1
				},
				"user": {
					"type": "string",
					"minLength": 1
				}
			}
		},
		"conditionalPromptVariant": {
			"allOf": [
				{ "$ref": "#/definitions/promptVariant" },
				{
					"type": "object",
					"properties": {
						"condition": {
							"type": "string",
							"description": "JavaScript expression for variant selection"
						}
					}
				}
			]
		},
		"metadata": {
			"type": "object",
			"properties": {
				"author": { "type": "string" },
				"created": { "type": "string", "format": "date-time" },
				"updated": { "type": "string", "format": "date-time" },
				"tags": {
					"type": "array",
					"items": { "type": "string" }
				},
				"category": {
					"type": "string",
					"enum": [
						"task",
						"analysis",
						"research",
						"parsing",
						"update",
						"expansion"
					]
				}
			}
		}
	}
}

```

--------------------------------------------------------------------------------
/src/prompts/analyze-complexity.json:
--------------------------------------------------------------------------------

```json
{
	"id": "analyze-complexity",
	"version": "1.0.0",
	"description": "Analyze task complexity and generate expansion recommendations",
	"metadata": {
		"author": "system",
		"created": "2024-01-01T00:00:00Z",
		"updated": "2024-01-01T00:00:00Z",
		"tags": ["analysis", "complexity", "expansion", "recommendations"]
	},
	"parameters": {
		"tasks": {
			"type": "array",
			"required": true,
			"description": "Array of tasks to analyze"
		},
		"gatheredContext": {
			"type": "string",
			"default": "",
			"description": "Additional project context"
		},
		"threshold": {
			"type": "number",
			"default": 5,
			"min": 1,
			"max": 10,
			"description": "Complexity threshold for expansion recommendation"
		},
		"useResearch": {
			"type": "boolean",
			"default": false,
			"description": "Use research mode for deeper analysis"
		},
		"hasCodebaseAnalysis": {
			"type": "boolean",
			"default": false,
			"description": "Whether codebase analysis is available"
		},
		"projectRoot": {
			"type": "string",
			"default": "",
			"description": "Project root path for context"
		}
	},
	"prompts": {
		"default": {
			"system": "You are an expert software architect and project manager analyzing task complexity. Respond only with the requested valid JSON array.",
			"user": "{{#if hasCodebaseAnalysis}}## IMPORTANT: Codebase Analysis Required\n\nYou have access to powerful codebase analysis tools. Before analyzing task complexity:\n\n1. Use the Glob tool to explore the project structure and understand the codebase size\n2. Use the Grep tool to search for existing implementations related to each task\n3. Use the Read tool to examine key files that would be affected by these tasks\n4. Understand the current implementation state, patterns used, and technical debt\n\nBased on your codebase analysis:\n- Assess complexity based on ACTUAL code that needs to be modified/created\n- Consider existing abstractions and patterns that could simplify implementation\n- Identify tasks that require refactoring vs. greenfield development\n- Factor in dependencies between existing code and new features\n- Provide more accurate subtask recommendations based on real code structure\n\nProject Root: {{projectRoot}}\n\n{{/if}}Analyze the following tasks to determine their complexity (1-10 scale) and recommend the number of subtasks for expansion. Provide a brief reasoning and an initial expansion prompt for each.{{#if useResearch}} Consider current best practices, common implementation patterns, and industry standards in your analysis.{{/if}}\n\nTasks:\n{{{json tasks}}}\n{{#if gatheredContext}}\n\n# Project Context\n\n{{gatheredContext}}\n{{/if}}\n\nRespond ONLY with a valid JSON array matching the schema:\n[\n  {\n    \"taskId\": <number>,\n    \"taskTitle\": \"<string>\",\n    \"complexityScore\": <number 1-10>,\n    \"recommendedSubtasks\": <number>,\n    \"expansionPrompt\": \"<string>\",\n    \"reasoning\": \"<string>\"\n  },\n  ...\n]\n\nDo not include any explanatory text, markdown formatting, or code block markers before or after the JSON array."
		}
	}
}

```

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

```javascript
/**
 * generate-task-files.js
 * Direct function implementation for generating task files from tasks.json
 */

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

/**
 * Direct function wrapper for generateTaskFiles with error handling.
 *
 * @param {Object} args - Command arguments containing tasksJsonPath and outputDir.
 * @param {string} args.tasksJsonPath - Path to the tasks.json file.
 * @param {string} args.outputDir - Path to the output directory.
 * @param {string} args.projectRoot - Project root path (for MCP/env fallback)
 * @param {string} args.tag - Tag for the task (optional)
 * @param {Object} log - Logger object.
 * @returns {Promise<Object>} - Result object with success status and data/error information.
 */
export async function generateTaskFilesDirect(args, log) {
	// Destructure expected args
	const { tasksJsonPath, outputDir, projectRoot, tag } = args;
	try {
		log.info(`Generating task files with args: ${JSON.stringify(args)}`);

		// Check if paths were provided
		if (!tasksJsonPath) {
			const errorMessage = 'tasksJsonPath is required but was not provided.';
			log.error(errorMessage);
			return {
				success: false,
				error: { code: 'MISSING_ARGUMENT', message: errorMessage }
			};
		}
		if (!outputDir) {
			const errorMessage = 'outputDir is required but was not provided.';
			log.error(errorMessage);
			return {
				success: false,
				error: { code: 'MISSING_ARGUMENT', message: errorMessage }
			};
		}

		// Use the provided paths
		const tasksPath = tasksJsonPath;
		const resolvedOutputDir = outputDir;

		log.info(`Generating task files from ${tasksPath} to ${resolvedOutputDir}`);

		// Execute core generateTaskFiles function in a separate try/catch
		try {
			// Enable silent mode to prevent logs from being written to stdout
			enableSilentMode();

			// Pass projectRoot and tag so the core respects context
			generateTaskFiles(tasksPath, resolvedOutputDir, {
				projectRoot,
				tag,
				mcpLog: log
			});

			// Restore normal logging after task generation
			disableSilentMode();
		} catch (genError) {
			// Make sure to restore normal logging even if there's an error
			disableSilentMode();

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

		// Return success with file paths
		return {
			success: true,
			data: {
				message: `Successfully generated task files`,
				tasksPath: tasksPath,
				outputDir: resolvedOutputDir,
				taskFiles:
					'Individual task files have been generated in the output directory'
			}
		};
	} catch (error) {
		// Make sure to restore normal logging if an outer error occurs
		disableSilentMode();

		log.error(`Error generating task files: ${error.message}`);
		return {
			success: false,
			error: {
				code: 'GENERATE_TASKS_ERROR',
				message: error.message || 'Unknown error generating task files'
			}
		};
	}
}

```

--------------------------------------------------------------------------------
/.github/workflows/extension-release.yml:
--------------------------------------------------------------------------------

```yaml
name: Extension Release

on:
  push:
    tags:
      - "extension@*"

permissions:
  contents: write

concurrency: extension-release-${{ github.ref }}

jobs:
  publish-extension:
    runs-on: ubuntu-latest
    environment: extension-release
    steps:
      - uses: actions/checkout@v4

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

      - name: Cache node_modules
        uses: actions/cache@v4
        with:
          path: |
            node_modules
            */*/node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install Extension Dependencies
        working-directory: apps/extension
        run: npm ci
        timeout-minutes: 5

      - name: Type Check Extension
        working-directory: apps/extension
        run: npm run check-types
        env:
          FORCE_COLOR: 1

      - name: Build Extension
        working-directory: apps/extension
        run: npm run build
        env:
          FORCE_COLOR: 1

      - name: Package Extension
        working-directory: apps/extension
        run: npm run package
        env:
          FORCE_COLOR: 1

      - name: Create VSIX Package
        working-directory: apps/extension/vsix-build
        run: npx vsce package --no-dependencies
        env:
          FORCE_COLOR: 1

      - name: Get VSIX filename
        id: vsix-info
        working-directory: apps/extension/vsix-build
        run: |
          VSIX_FILE=$(find . -maxdepth 1 -name "*.vsix" -type f | head -n1 | xargs basename)
          if [ -z "$VSIX_FILE" ]; then
            echo "Error: No VSIX file found"
            exit 1
          fi
          echo "vsix-filename=$VSIX_FILE" >> "$GITHUB_OUTPUT"
          echo "Found VSIX: $VSIX_FILE"

      - name: Publish to VS Code Marketplace
        working-directory: apps/extension/vsix-build
        run: npx vsce publish --packagePath "${{ steps.vsix-info.outputs.vsix-filename }}"
        env:
          VSCE_PAT: ${{ secrets.VSCE_PAT }}
          FORCE_COLOR: 1

      - name: Install Open VSX CLI
        run: npm install -g ovsx

      - name: Publish to Open VSX Registry
        working-directory: apps/extension/vsix-build
        run: ovsx publish "${{ steps.vsix-info.outputs.vsix-filename }}"
        env:
          OVSX_PAT: ${{ secrets.OVSX_PAT }}
          FORCE_COLOR: 1

      - name: Upload Build Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: extension-release-${{ github.ref_name }}
          path: |
            apps/extension/vsix-build/*.vsix
            apps/extension/dist/
          retention-days: 90

  notify-success:
    needs: publish-extension
    if: success()
    runs-on: ubuntu-latest
    steps:
      - name: Success Notification
        run: |
          echo "🎉 Extension ${{ github.ref_name }} successfully published!"
          echo "📦 Available on VS Code Marketplace"
          echo "🌍 Available on Open VSX Registry"
          echo "🏷️ GitHub release created: ${{ github.ref_name }}"
```

--------------------------------------------------------------------------------
/tests/unit/mcp/tools/__mocks__/move-task.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Mock for move-task module
 * Provides mock implementations for testing scenarios
 */

// Mock the moveTask function from the core module
const mockMoveTask = jest
	.fn()
	.mockImplementation(
		async (tasksPath, sourceId, destinationId, generateFiles, options) => {
			// Simulate successful move operation
			return {
				success: true,
				sourceId,
				destinationId,
				message: `Successfully moved task ${sourceId} to ${destinationId}`,
				...options
			};
		}
	);

// Mock the moveTaskDirect function
const mockMoveTaskDirect = jest
	.fn()
	.mockImplementation(async (args, log, context = {}) => {
		// Validate required parameters
		if (!args.sourceId) {
			return {
				success: false,
				error: {
					message: 'Source ID is required',
					code: 'MISSING_SOURCE_ID'
				}
			};
		}

		if (!args.destinationId) {
			return {
				success: false,
				error: {
					message: 'Destination ID is required',
					code: 'MISSING_DESTINATION_ID'
				}
			};
		}

		// Simulate successful move
		return {
			success: true,
			data: {
				sourceId: args.sourceId,
				destinationId: args.destinationId,
				message: `Successfully moved task/subtask ${args.sourceId} to ${args.destinationId}`,
				tag: args.tag,
				projectRoot: args.projectRoot
			}
		};
	});

// Mock the moveTaskCrossTagDirect function
const mockMoveTaskCrossTagDirect = jest
	.fn()
	.mockImplementation(async (args, log, context = {}) => {
		// Validate required parameters
		if (!args.sourceIds) {
			return {
				success: false,
				error: {
					message: 'Source IDs are required',
					code: 'MISSING_SOURCE_IDS'
				}
			};
		}

		if (!args.sourceTag) {
			return {
				success: false,
				error: {
					message: 'Source tag is required for cross-tag moves',
					code: 'MISSING_SOURCE_TAG'
				}
			};
		}

		if (!args.targetTag) {
			return {
				success: false,
				error: {
					message: 'Target tag is required for cross-tag moves',
					code: 'MISSING_TARGET_TAG'
				}
			};
		}

		if (args.sourceTag === args.targetTag) {
			return {
				success: false,
				error: {
					message: `Source and target tags are the same ("${args.sourceTag}")`,
					code: 'SAME_SOURCE_TARGET_TAG'
				}
			};
		}

		// Simulate successful cross-tag move
		return {
			success: true,
			data: {
				sourceIds: args.sourceIds,
				sourceTag: args.sourceTag,
				targetTag: args.targetTag,
				message: `Successfully moved tasks ${args.sourceIds} from ${args.sourceTag} to ${args.targetTag}`,
				withDependencies: args.withDependencies || false,
				ignoreDependencies: args.ignoreDependencies || false
			}
		};
	});

// Mock the registerMoveTaskTool function
const mockRegisterMoveTaskTool = jest.fn().mockImplementation((server) => {
	// Simulate tool registration
	server.addTool({
		name: 'move_task',
		description: 'Move a task or subtask to a new position',
		parameters: {},
		execute: jest.fn()
	});
});

// Export the mock functions
export {
	mockMoveTask,
	mockMoveTaskDirect,
	mockMoveTaskCrossTagDirect,
	mockRegisterMoveTaskTool
};

// Default export for the main moveTask function
export default mockMoveTask;

```

--------------------------------------------------------------------------------
/tests/integration/profiles/gemini-init-functionality.test.js:
--------------------------------------------------------------------------------

```javascript
import fs from 'fs';
import path from 'path';
import { geminiProfile } from '../../../src/profiles/gemini.js';

describe('Gemini Profile Initialization Functionality', () => {
	let geminiProfileContent;

	beforeAll(() => {
		const geminiJsPath = path.join(
			process.cwd(),
			'src',
			'profiles',
			'gemini.js'
		);
		geminiProfileContent = fs.readFileSync(geminiJsPath, 'utf8');
	});

	test('gemini.js has correct profile configuration', () => {
		// Check for explicit, non-default values in the source file
		expect(geminiProfileContent).toContain("name: 'gemini'");
		expect(geminiProfileContent).toContain("displayName: 'Gemini'");
		expect(geminiProfileContent).toContain("url: 'codeassist.google'");
		expect(geminiProfileContent).toContain(
			"docsUrl: 'github.com/google-gemini/gemini-cli'"
		);
		expect(geminiProfileContent).toContain("profileDir: '.gemini'");
		expect(geminiProfileContent).toContain("rulesDir: '.'"); // non-default
		expect(geminiProfileContent).toContain("mcpConfigName: 'settings.json'"); // non-default
		expect(geminiProfileContent).toContain('includeDefaultRules: false'); // non-default
		expect(geminiProfileContent).toContain("'AGENTS.md': 'GEMINI.md'");

		// Check the final computed properties on the profile object
		expect(geminiProfile.profileName).toBe('gemini');
		expect(geminiProfile.displayName).toBe('Gemini');
		expect(geminiProfile.profileDir).toBe('.gemini');
		expect(geminiProfile.rulesDir).toBe('.');
		expect(geminiProfile.mcpConfig).toBe(true); // computed from mcpConfigName
		expect(geminiProfile.mcpConfigName).toBe('settings.json');
		expect(geminiProfile.mcpConfigPath).toBe('.gemini/settings.json'); // computed
		expect(geminiProfile.includeDefaultRules).toBe(false);
		expect(geminiProfile.fileMap['AGENTS.md']).toBe('GEMINI.md');
	});

	test('gemini.js has no lifecycle functions', () => {
		// Gemini profile should not have any lifecycle functions
		expect(geminiProfileContent).not.toContain('function onAddRulesProfile');
		expect(geminiProfileContent).not.toContain('function onRemoveRulesProfile');
		expect(geminiProfileContent).not.toContain(
			'function onPostConvertRulesProfile'
		);
		expect(geminiProfileContent).not.toContain('onAddRulesProfile:');
		expect(geminiProfileContent).not.toContain('onRemoveRulesProfile:');
		expect(geminiProfileContent).not.toContain('onPostConvertRulesProfile:');
	});

	test('gemini.js uses custom MCP config name', () => {
		// Gemini uses settings.json instead of mcp.json
		expect(geminiProfileContent).toContain("mcpConfigName: 'settings.json'");
		// Should not contain mcp.json as a config value (comments are OK)
		expect(geminiProfileContent).not.toMatch(
			/mcpConfigName:\s*['"]mcp\.json['"]/
		);
	});

	test('gemini.js has minimal implementation', () => {
		// Verify the profile is minimal (no extra functions or logic)
		const lines = geminiProfileContent.split('\n');
		const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
		// Should be around 16 lines (import, export, and profile definition)
		expect(nonEmptyLines.length).toBeLessThan(20);
	});
});

```

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

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

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

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

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

	/**
	 * Get default configuration values
	 */
	getDefaultConfig(): PartialConfiguration {
		return {
			models: {
				main: DEFAULT_CONFIG_VALUES.MODELS.MAIN,
				fallback: DEFAULT_CONFIG_VALUES.MODELS.FALLBACK
			},
			storage: {
				type: DEFAULT_CONFIG_VALUES.STORAGE.TYPE,
				encoding: DEFAULT_CONFIG_VALUES.STORAGE.ENCODING,
				enableBackup: false,
				maxBackups: DEFAULT_CONFIG_VALUES.STORAGE.MAX_BACKUPS,
				enableCompression: false,
				atomicOperations: true
			},
			version: DEFAULT_CONFIG_VALUES.VERSION
		};
	}

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

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

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

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

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

```

--------------------------------------------------------------------------------
/mcp-server/src/tools/parse-prd.js:
--------------------------------------------------------------------------------

```javascript
/**
 * tools/parsePRD.js
 * Tool to parse PRD document and generate tasks
 */

import { z } from 'zod';
import {
	handleApiResult,
	withNormalizedProjectRoot,
	createErrorResponse,
	checkProgressCapability
} from './utils.js';
import { parsePRDDirect } from '../core/task-master-core.js';
import {
	PRD_FILE,
	TASKMASTER_DOCS_DIR,
	TASKMASTER_TASKS_FILE
} from '../../../src/constants/paths.js';
import { resolveTag } from '../../../scripts/modules/utils.js';

/**
 * Register the parse_prd tool
 * @param {Object} server - FastMCP server instance
 */
export function registerParsePRDTool(server) {
	server.addTool({
		name: 'parse_prd',
		description: `Parse a Product Requirements Document (PRD) text file to automatically generate initial tasks. Reinitializing the project is not necessary to run this tool. It is recommended to run parse-prd after initializing the project and creating/importing a prd.txt file in the project root's ${TASKMASTER_DOCS_DIR} directory.`,

		parameters: z.object({
			input: z
				.string()
				.optional()
				.default(PRD_FILE)
				.describe('Absolute path to the PRD document file (.txt, .md, etc.)'),
			projectRoot: z
				.string()
				.describe('The directory of the project. Must be an absolute path.'),
			tag: z.string().optional().describe('Tag context to operate on'),
			output: z
				.string()
				.optional()
				.describe(
					`Output path for tasks.json file (default: ${TASKMASTER_TASKS_FILE})`
				),
			numTasks: z
				.string()
				.optional()
				.describe(
					'Approximate number of top-level tasks to generate (default: 10). As the agent, if you have enough information, ensure to enter a number of tasks that would logically scale with project complexity. Setting to 0 will allow Taskmaster to determine the appropriate number of tasks based on the complexity of the PRD. Avoid entering numbers above 50 due to context window limitations.'
				),
			force: z
				.boolean()
				.optional()
				.default(false)
				.describe('Overwrite existing output file without prompting.'),
			research: z
				.boolean()
				.optional()
				.describe(
					'Enable Taskmaster to use the research role for potentially more informed task generation. Requires appropriate API key.'
				),
			append: z
				.boolean()
				.optional()
				.describe('Append generated tasks to existing file.')
		}),
		execute: withNormalizedProjectRoot(
			async (args, { log, session, reportProgress }) => {
				try {
					const resolvedTag = resolveTag({
						projectRoot: args.projectRoot,
						tag: args.tag
					});
					const progressCapability = checkProgressCapability(
						reportProgress,
						log
					);
					const result = await parsePRDDirect(
						{
							...args,
							tag: resolvedTag
						},
						log,
						{ session, reportProgress: progressCapability }
					);
					return handleApiResult(
						result,
						log,
						'Error parsing PRD',
						undefined,
						args.projectRoot
					);
				} catch (error) {
					log.error(`Error in parse_prd: ${error.message}`);
					return createErrorResponse(`Failed to parse PRD: ${error.message}`);
				}
			}
		)
	});
}

```

--------------------------------------------------------------------------------
/.github/workflows/pre-release.yml:
--------------------------------------------------------------------------------

```yaml
name: Pre-Release (RC)

on:
  workflow_dispatch: # Allows manual triggering from GitHub UI/API

concurrency: pre-release-${{ github.ref_name }}
jobs:
  rc:
    runs-on: ubuntu-latest
    # Only allow pre-releases on non-main branches
    if: github.ref != 'refs/heads/main'
    environment: extension-release
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

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

      - name: Cache node_modules
        uses: actions/cache@v4
        with:
          path: |
            node_modules
            */*/node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install dependencies
        run: npm ci
        timeout-minutes: 2

      - name: Enter RC mode (if not already in RC mode)
        run: |
          # Check if we're in pre-release mode with the "rc" tag
          if [ -f .changeset/pre.json ]; then
            MODE=$(jq -r '.mode' .changeset/pre.json 2>/dev/null || echo '')
            TAG=$(jq -r '.tag' .changeset/pre.json 2>/dev/null || echo '')
            
            if [ "$MODE" = "exit" ]; then
              echo "Pre-release mode is in 'exit' state, re-entering RC mode..."
              npx changeset pre enter rc
            elif [ "$MODE" = "pre" ] && [ "$TAG" != "rc" ]; then
              echo "In pre-release mode but with wrong tag ($TAG), switching to RC..."
              npx changeset pre exit
              npx changeset pre enter rc
            elif [ "$MODE" = "pre" ] && [ "$TAG" = "rc" ]; then
              echo "Already in RC pre-release mode"
            else
              echo "Unknown mode state: $MODE, entering RC mode..."
              npx changeset pre enter rc
            fi
          else
            echo "No pre.json found, entering RC mode..."
            npx changeset pre enter rc
          fi

      - name: Version RC packages
        run: npx changeset version
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Run format
        run: npm run format
        env:
          FORCE_COLOR: 1

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

      - name: Create Release Candidate Pull Request or Publish Release Candidate to npm
        uses: changesets/action@v1
        with:
          publish: npx changeset publish
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Commit & Push changes
        uses: actions-js/push@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.ref }}
          message: "chore: rc version bump"

```

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

```typescript
/**
 * Error Boundary Component
 */

import React from 'react';

interface ErrorBoundaryState {
	hasError: boolean;
	error?: Error;
	errorInfo?: React.ErrorInfo;
}

interface ErrorBoundaryProps {
	children: React.ReactNode;
	onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
}

export class ErrorBoundary extends React.Component<
	ErrorBoundaryProps,
	ErrorBoundaryState
> {
	constructor(props: ErrorBoundaryProps) {
		super(props);
		this.state = { hasError: false };
	}

	static getDerivedStateFromError(error: Error): ErrorBoundaryState {
		return { hasError: true, error };
	}

	componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
		console.error('React Error Boundary caught:', error, errorInfo);

		// Log to extension
		if (this.props.onError) {
			this.props.onError(error, errorInfo);
		}

		// Send error to extension for centralized handling
		if (window.acquireVsCodeApi) {
			const vscode = window.acquireVsCodeApi();
			vscode.postMessage({
				type: 'reactError',
				data: {
					message: error.message,
					stack: error.stack,
					componentStack: errorInfo.componentStack,
					timestamp: Date.now()
				}
			});
		}
	}

	render() {
		if (this.state.hasError) {
			return (
				<div className="min-h-screen flex items-center justify-center bg-vscode-background">
					<div className="max-w-md mx-auto text-center p-6">
						<div className="w-16 h-16 mx-auto mb-4 text-red-400">
							<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
								<path
									strokeLinecap="round"
									strokeLinejoin="round"
									strokeWidth={2}
									d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.962-.833-2.732 0L3.732 19c-.77.833.192 2.5 1.732 2.5z"
								/>
							</svg>
						</div>
						<h2 className="text-xl font-semibold text-vscode-foreground mb-2">
							Something went wrong
						</h2>
						<p className="text-vscode-foreground/70 mb-4">
							The Task Master Kanban board encountered an unexpected error.
						</p>
						<div className="space-y-2">
							<button
								onClick={() =>
									this.setState({
										hasError: false,
										error: undefined,
										errorInfo: undefined
									})
								}
								className="w-full px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-md transition-colors"
							>
								Try Again
							</button>
							<button
								onClick={() => window.location.reload()}
								className="w-full px-4 py-2 bg-gray-500 hover:bg-gray-600 text-white rounded-md transition-colors"
							>
								Reload Extension
							</button>
						</div>
						{this.state.error && (
							<details className="mt-4 text-left">
								<summary className="text-sm text-vscode-foreground/50 cursor-pointer">
									Error Details
								</summary>
								<pre className="mt-2 text-xs text-vscode-foreground/70 bg-vscode-input/30 p-2 rounded overflow-auto max-h-32">
									{this.state.error.message}
									{this.state.error.stack && `\n\n${this.state.error.stack}`}
								</pre>
							</details>
						)}
					</div>
				</div>
			);
		}

		return this.props.children;
	}
}

```

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

```javascript
/**
 * rename-tag.js
 * Direct function implementation for renaming a tag
 */

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

/**
 * Direct function wrapper for renaming a tag with error handling.
 *
 * @param {Object} args - Command arguments
 * @param {string} args.oldName - Current name of the tag to rename
 * @param {string} args.newName - New name for the tag
 * @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
 * @param {string} [args.projectRoot] - Project root path
 * @param {Object} log - Logger object
 * @param {Object} context - Additional context (session)
 * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 */
export async function renameTagDirect(args, log, context = {}) {
	// Destructure expected args
	const { tasksJsonPath, oldName, newName, projectRoot } = args;
	const { session } = context;

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

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

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

		// Check required parameters
		if (!oldName || typeof oldName !== 'string') {
			log.error('Missing required parameter: oldName');
			disableSilentMode();
			return {
				success: false,
				error: {
					code: 'MISSING_PARAMETER',
					message: 'Old tag name is required and must be a string'
				}
			};
		}

		if (!newName || typeof newName !== 'string') {
			log.error('Missing required parameter: newName');
			disableSilentMode();
			return {
				success: false,
				error: {
					code: 'MISSING_PARAMETER',
					message: 'New tag name is required and must be a string'
				}
			};
		}

		log.info(`Renaming tag from "${oldName}" to "${newName}"`);

		// Call the renameTag function
		const result = await renameTag(
			tasksJsonPath,
			oldName,
			newName,
			{}, // options (empty for now)
			{
				session,
				mcpLog,
				projectRoot
			},
			'json' // outputFormat - use 'json' to suppress CLI UI
		);

		// Restore normal logging
		disableSilentMode();

		return {
			success: true,
			data: {
				oldName: result.oldName,
				newName: result.newName,
				renamed: result.renamed,
				taskCount: result.taskCount,
				wasCurrentTag: result.wasCurrentTag,
				message: `Successfully renamed tag from "${result.oldName}" to "${result.newName}"`
			}
		};
	} catch (error) {
		// Make sure to restore normal logging even if there's an error
		disableSilentMode();

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

```

--------------------------------------------------------------------------------
/apps/extension/src/webview/types/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Shared types for the webview application
 */

export interface TaskMasterTask {
	id: string;
	title: string;
	description: string;
	status: 'pending' | 'in-progress' | 'done' | 'deferred' | 'review';
	priority: 'high' | 'medium' | 'low';
	dependencies?: string[];
	details?: string;
	testStrategy?: string;
	subtasks?: TaskMasterTask[];
	complexityScore?: number;
}

export interface TaskUpdates {
	title?: string;
	description?: string;
	details?: string;
	priority?: TaskMasterTask['priority'];
	testStrategy?: string;
	dependencies?: string[];
}

export interface WebviewMessage {
	type: string;
	requestId?: string;
	data?: any;
	success?: boolean;
	[key: string]: any;
}

export interface ToastNotification {
	id: string;
	type: 'success' | 'info' | 'warning' | 'error';
	title: string;
	message: string;
	duration?: number;
}

export interface AppState {
	tasks: TaskMasterTask[];
	loading: boolean;
	error?: string;
	requestId: number;
	isConnected: boolean;
	connectionStatus: string;
	editingTask?: { taskId: string | null; editData?: TaskMasterTask };
	polling: {
		isActive: boolean;
		errorCount: number;
		lastUpdate?: number;
		isUserInteracting: boolean;
		isOfflineMode: boolean;
		reconnectAttempts: number;
		maxReconnectAttempts: number;
		lastSuccessfulConnection?: number;
		connectionStatus: 'online' | 'offline' | 'reconnecting';
	};
	toastNotifications: ToastNotification[];
	currentView: 'kanban' | 'task-details' | 'config';
	selectedTaskId?: string;
	// Tag-related state
	currentTag: string;
	availableTags: string[];
}

export type AppAction =
	| { type: 'SET_TASKS'; payload: TaskMasterTask[] }
	| { type: 'SET_LOADING'; payload: boolean }
	| { type: 'SET_ERROR'; payload: string }
	| { type: 'CLEAR_ERROR' }
	| { type: 'INCREMENT_REQUEST_ID' }
	| {
			type: 'UPDATE_TASK_STATUS';
			payload: { taskId: string; newStatus: TaskMasterTask['status'] };
	  }
	| {
			type: 'UPDATE_TASK_CONTENT';
			payload: { taskId: string; updates: TaskUpdates };
	  }
	| {
			type: 'SET_CONNECTION_STATUS';
			payload: { isConnected: boolean; status: string };
	  }
	| {
			type: 'SET_EDITING_TASK';
			payload: { taskId: string | null; editData?: TaskMasterTask };
	  }
	| {
			type: 'SET_POLLING_STATUS';
			payload: { isActive: boolean; errorCount?: number };
	  }
	| { type: 'SET_USER_INTERACTING'; payload: boolean }
	| { type: 'TASKS_UPDATED_FROM_POLLING'; payload: TaskMasterTask[] }
	| {
			type: 'SET_NETWORK_STATUS';
			payload: {
				isOfflineMode: boolean;
				connectionStatus: 'online' | 'offline' | 'reconnecting';
				reconnectAttempts?: number;
				maxReconnectAttempts?: number;
				lastSuccessfulConnection?: number;
			};
	  }
	| { type: 'LOAD_CACHED_TASKS'; payload: TaskMasterTask[] }
	| { type: 'ADD_TOAST'; payload: ToastNotification }
	| { type: 'REMOVE_TOAST'; payload: string }
	| { type: 'CLEAR_ALL_TOASTS' }
	| { type: 'NAVIGATE_TO_TASK'; payload: string }
	| { type: 'NAVIGATE_TO_KANBAN' }
	| { type: 'NAVIGATE_TO_CONFIG' }
	| { type: 'SET_CURRENT_TAG'; payload: string }
	| { type: 'SET_AVAILABLE_TAGS'; payload: string[] }
	| {
			type: 'SET_TAG_DATA';
			payload: { currentTag: string; availableTags: string[] };
	  };

```

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

```javascript
/**
 * tools/add-task.js
 * Tool to add a new task using AI
 */

import { z } from 'zod';
import {
	createErrorResponse,
	handleApiResult,
	withNormalizedProjectRoot
} from './utils.js';
import { addTaskDirect } from '../core/task-master-core.js';
import { findTasksPath } from '../core/utils/path-utils.js';
import { resolveTag } from '../../../scripts/modules/utils.js';

/**
 * Register the addTask tool with the MCP server
 * @param {Object} server - FastMCP server instance
 */
export function registerAddTaskTool(server) {
	server.addTool({
		name: 'add_task',
		description: 'Add a new task using AI',
		parameters: z.object({
			prompt: z
				.string()
				.optional()
				.describe(
					'Description of the task to add (required if not using manual fields)'
				),
			title: z
				.string()
				.optional()
				.describe('Task title (for manual task creation)'),
			description: z
				.string()
				.optional()
				.describe('Task description (for manual task creation)'),
			details: z
				.string()
				.optional()
				.describe('Implementation details (for manual task creation)'),
			testStrategy: z
				.string()
				.optional()
				.describe('Test strategy (for manual task creation)'),
			dependencies: z
				.string()
				.optional()
				.describe('Comma-separated list of task IDs this task depends on'),
			priority: z
				.string()
				.optional()
				.describe('Task priority (high, medium, low)'),
			file: z
				.string()
				.optional()
				.describe('Path to the tasks file (default: tasks/tasks.json)'),
			projectRoot: z
				.string()
				.describe('The directory of the project. Must be an absolute path.'),
			tag: z.string().optional().describe('Tag context to operate on'),
			research: z
				.boolean()
				.optional()
				.describe('Whether to use research capabilities for task creation')
		}),
		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
			try {
				log.info(`Starting add-task with args: ${JSON.stringify(args)}`);

				const resolvedTag = resolveTag({
					projectRoot: args.projectRoot,
					tag: args.tag
				});

				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
				let tasksJsonPath;
				try {
					tasksJsonPath = findTasksPath(
						{ projectRoot: args.projectRoot, file: args.file },
						log
					);
				} catch (error) {
					log.error(`Error finding tasks.json: ${error.message}`);
					return createErrorResponse(
						`Failed to find tasks.json: ${error.message}`
					);
				}

				// Call the direct functionP
				const result = await addTaskDirect(
					{
						tasksJsonPath: tasksJsonPath,
						prompt: args.prompt,
						title: args.title,
						description: args.description,
						details: args.details,
						testStrategy: args.testStrategy,
						dependencies: args.dependencies,
						priority: args.priority,
						research: args.research,
						projectRoot: args.projectRoot,
						tag: resolvedTag
					},
					log,
					{ session }
				);

				return handleApiResult(
					result,
					log,
					'Error adding task',
					undefined,
					args.projectRoot
				);
			} catch (error) {
				log.error(`Error in add-task tool: ${error.message}`);
				return createErrorResponse(error.message);
			}
		})
	});
}

```

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

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

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

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

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

	test('source rules files exist in assets/rules directory', () => {
		// Verify that the actual rules files exist
		const rulesDir = path.join(process.cwd(), 'assets', 'rules');
		expect(fs.existsSync(rulesDir)).toBe(true);

		// Check for the 4 files that currently exist
		const expectedFiles = [
			'dev_workflow.mdc',
			'taskmaster.mdc',
			'self_improve.mdc',
			'cursor_rules.mdc'
		];

		expectedFiles.forEach((file) => {
			const filePath = path.join(rulesDir, file);
			expect(fs.existsSync(filePath)).toBe(true);
		});
	});

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

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

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

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

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

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

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

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

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

```

--------------------------------------------------------------------------------
/mcp-server/src/tools/get-tasks.js:
--------------------------------------------------------------------------------

```javascript
/**
 * tools/get-tasks.js
 * Tool to get all tasks from Task Master
 */

import { z } from 'zod';
import {
	createErrorResponse,
	handleApiResult,
	withNormalizedProjectRoot
} from './utils.js';
import { listTasksDirect } from '../core/task-master-core.js';
import {
	resolveTasksPath,
	resolveComplexityReportPath
} from '../core/utils/path-utils.js';

import { resolveTag } from '../../../scripts/modules/utils.js';

/**
 * Register the getTasks tool with the MCP server
 * @param {Object} server - FastMCP server instance
 */
export function registerListTasksTool(server) {
	server.addTool({
		name: 'get_tasks',
		description:
			'Get all tasks from Task Master, optionally filtering by status and including subtasks.',
		parameters: z.object({
			status: z
				.string()
				.optional()
				.describe(
					"Filter tasks by status (e.g., 'pending', 'done') or multiple statuses separated by commas (e.g., 'blocked,deferred')"
				),
			withSubtasks: z
				.boolean()
				.optional()
				.describe(
					'Include subtasks nested within their parent tasks in the response'
				),
			file: z
				.string()
				.optional()
				.describe(
					'Path to the tasks file (relative to project root or absolute)'
				),
			complexityReport: z
				.string()
				.optional()
				.describe(
					'Path to the complexity report file (relative to project root or absolute)'
				),
			projectRoot: z
				.string()
				.describe('The directory of the project. Must be an absolute path.'),
			tag: z.string().optional().describe('Tag context to operate on')
		}),
		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
			try {
				log.info(`Getting tasks with filters: ${JSON.stringify(args)}`);

				const resolvedTag = resolveTag({
					projectRoot: args.projectRoot,
					tag: args.tag
				});
				// Resolve the path to tasks.json using new path utilities
				let tasksJsonPath;
				try {
					tasksJsonPath = resolveTasksPath(args, log);
				} catch (error) {
					log.error(`Error finding tasks.json: ${error.message}`);
					return createErrorResponse(
						`Failed to find tasks.json: ${error.message}`
					);
				}

				// Resolve the path to complexity report
				let complexityReportPath;
				try {
					complexityReportPath = resolveComplexityReportPath(
						{ ...args, tag: resolvedTag },
						session
					);
				} catch (error) {
					log.error(`Error finding complexity report: ${error.message}`);
					// This is optional, so we don't fail the operation
					complexityReportPath = null;
				}

				const result = await listTasksDirect(
					{
						tasksJsonPath: tasksJsonPath,
						status: args.status,
						withSubtasks: args.withSubtasks,
						reportPath: complexityReportPath,
						projectRoot: args.projectRoot,
						tag: resolvedTag
					},
					log,
					{ session }
				);

				log.info(
					`Retrieved ${result.success ? result.data?.tasks?.length || 0 : 0} tasks`
				);
				return handleApiResult(
					result,
					log,
					'Error getting tasks',
					undefined,
					args.projectRoot
				);
			} catch (error) {
				log.error(`Error getting tasks: ${error.message}`);
				return createErrorResponse(error.message);
			}
		})
	});
}

// We no longer need the formatTasksResponse function as we're returning raw JSON data

```

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

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

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

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

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

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

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

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

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

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

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

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

	test('does not create any profile directories', () => {
		// Act
		mockCreateCodexStructure();

		// Assert - Codex profile should not create any directories
		// Only the temp directory creation calls should exist
		const mkdirCalls = fs.mkdirSync.mock.calls.filter(
			(call) => !call[0].includes('task-master-test-')
		);
		expect(mkdirCalls).toHaveLength(0);
	});

	test('does not create MCP configuration files', () => {
		// Act
		mockCreateCodexStructure();

		// Assert - Codex profile should not create any MCP config files
		const writeFileCalls = fs.writeFileSync.mock.calls;
		const mcpConfigCalls = writeFileCalls.filter(
			(call) =>
				call[0].toString().includes('mcp.json') ||
				call[0].toString().includes('mcp_settings.json')
		);
		expect(mcpConfigCalls).toHaveLength(0);
	});

	test('only creates the target integration guide file', () => {
		// Act
		mockCreateCodexStructure();

		// Assert - Should only create AGENTS.md
		const writeFileCalls = fs.writeFileSync.mock.calls;
		expect(writeFileCalls).toHaveLength(1);
		expect(writeFileCalls[0][0]).toBe(path.join(tempDir, 'AGENTS.md'));
	});

	test('uses the same filename as source (AGENTS.md)', () => {
		// Act
		mockCreateCodexStructure();

		// Assert - Codex should keep the same filename unlike Claude which renames it
		const writeFileCalls = fs.writeFileSync.mock.calls;
		expect(writeFileCalls[0][0]).toContain('AGENTS.md');
		expect(writeFileCalls[0][0]).not.toContain('CLAUDE.md');
	});
});

```

--------------------------------------------------------------------------------
/tests/unit/progress/base-progress-tracker.test.js:
--------------------------------------------------------------------------------

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

// Mock cli-progress factory before importing BaseProgressTracker
jest.unstable_mockModule(
	'../../../src/progress/cli-progress-factory.js',
	() => ({
		newMultiBar: jest.fn(() => ({
			create: jest.fn(() => ({
				update: jest.fn()
			})),
			stop: jest.fn()
		}))
	})
);

const { newMultiBar } = await import(
	'../../../src/progress/cli-progress-factory.js'
);
const { BaseProgressTracker } = await import(
	'../../../src/progress/base-progress-tracker.js'
);

describe('BaseProgressTracker', () => {
	let tracker;
	let mockMultiBar;
	let mockProgressBar;
	let mockTimeTokensBar;

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

		// Setup mocks
		mockProgressBar = { update: jest.fn() };
		mockTimeTokensBar = { update: jest.fn() };
		mockMultiBar = {
			create: jest
				.fn()
				.mockReturnValueOnce(mockTimeTokensBar)
				.mockReturnValueOnce(mockProgressBar),
			stop: jest.fn()
		};
		newMultiBar.mockReturnValue(mockMultiBar);

		tracker = new BaseProgressTracker({ numUnits: 10, unitName: 'task' });
	});

	afterEach(() => {
		jest.useRealTimers();
	});

	describe('cleanup', () => {
		it('should stop and clear timer interval', () => {
			tracker.start();
			expect(tracker._timerInterval).toBeTruthy();

			tracker.cleanup();
			expect(tracker._timerInterval).toBeNull();
		});

		it('should stop and null multibar reference', () => {
			tracker.start();
			expect(tracker.multibar).toBeTruthy();

			tracker.cleanup();
			expect(mockMultiBar.stop).toHaveBeenCalled();
			expect(tracker.multibar).toBeNull();
		});

		it('should null progress bar references', () => {
			tracker.start();
			expect(tracker.timeTokensBar).toBeTruthy();
			expect(tracker.progressBar).toBeTruthy();

			tracker.cleanup();
			expect(tracker.timeTokensBar).toBeNull();
			expect(tracker.progressBar).toBeNull();
		});

		it('should set finished state', () => {
			tracker.start();
			expect(tracker.isStarted).toBe(true);
			expect(tracker.isFinished).toBe(false);

			tracker.cleanup();
			expect(tracker.isStarted).toBe(false);
			expect(tracker.isFinished).toBe(true);
		});

		it('should handle cleanup when multibar.stop throws error', () => {
			tracker.start();
			mockMultiBar.stop.mockImplementation(() => {
				throw new Error('Stop failed');
			});

			expect(() => tracker.cleanup()).not.toThrow();
			expect(tracker.multibar).toBeNull();
		});

		it('should be safe to call multiple times', () => {
			tracker.start();

			tracker.cleanup();
			tracker.cleanup();
			tracker.cleanup();

			expect(mockMultiBar.stop).toHaveBeenCalledTimes(1);
		});

		it('should be safe to call without starting', () => {
			expect(() => tracker.cleanup()).not.toThrow();
			expect(tracker.multibar).toBeNull();
		});
	});

	describe('stop vs cleanup', () => {
		it('stop should call cleanup and null multibar reference', () => {
			tracker.start();
			tracker.stop();

			// stop() now calls cleanup() which nulls the multibar
			expect(tracker.multibar).toBeNull();
			expect(tracker.isFinished).toBe(true);
		});

		it('cleanup should null multibar preventing getSummary', () => {
			tracker.start();
			tracker.cleanup();

			expect(tracker.multibar).toBeNull();
			expect(tracker.isFinished).toBe(true);
		});
	});
});

```

--------------------------------------------------------------------------------
/.taskmaster/reports/task-complexity-report_test-prd-tag.json:
--------------------------------------------------------------------------------

```json
{
	"meta": {
		"generatedAt": "2025-06-13T06:52:00.611Z",
		"tasksAnalyzed": 5,
		"totalTasks": 5,
		"analysisCount": 5,
		"thresholdScore": 5,
		"projectName": "Taskmaster",
		"usedResearch": true
	},
	"complexityAnalysis": [
		{
			"taskId": 1,
			"taskTitle": "Setup Project Repository and Node.js Environment",
			"complexityScore": 4,
			"recommendedSubtasks": 6,
			"expansionPrompt": "Break down the setup process into subtasks such as initializing npm, creating directory structure, installing dependencies, configuring package.json, adding configuration files, and setting up the main entry point.",
			"reasoning": "This task involves several standard setup steps that are well-defined and sequential, with low algorithmic complexity but moderate procedural detail. Each step is independent and can be assigned as a subtask, making the overall complexity moderate."
		},
		{
			"taskId": 2,
			"taskTitle": "Implement Core Functionality and CLI Interface",
			"complexityScore": 7,
			"recommendedSubtasks": 7,
			"expansionPrompt": "Expand into subtasks for implementing main logic, designing CLI commands, creating the CLI entry point, integrating business logic, adding error handling, formatting output, and ensuring CLI executability.",
			"reasoning": "This task requires both application logic and user interface (CLI) development, including error handling and integration. The need to coordinate between core logic and CLI, plus ensuring usability, increases complexity and warrants detailed subtasking."
		},
		{
			"taskId": 3,
			"taskTitle": "Implement Testing Suite and Validation",
			"complexityScore": 6,
			"recommendedSubtasks": 6,
			"expansionPrompt": "Divide into subtasks for configuring Jest, writing unit tests, writing integration tests, testing CLI commands, setting up coverage reporting, and preparing test fixtures/mocks.",
			"reasoning": "Comprehensive testing involves multiple types of tests and configuration steps. While each is straightforward, the breadth of coverage and need for automation and validation increases the overall complexity."
		},
		{
			"taskId": 4,
			"taskTitle": "Setup Node.js Project with CLI Interface",
			"complexityScore": 5,
			"recommendedSubtasks": 7,
			"expansionPrompt": "Break down into subtasks for npm initialization, package.json setup, directory structure creation, dependency installation, CLI entry point creation, package.json bin configuration, and CLI executability.",
			"reasoning": "This task combines project setup with initial CLI implementation. While each step is standard, the integration of CLI elements adds a layer of complexity beyond a basic setup."
		},
		{
			"taskId": 5,
			"taskTitle": "Implement Core Functionality with Testing",
			"complexityScore": 8,
			"recommendedSubtasks": 8,
			"expansionPrompt": "Expand into subtasks for implementing each feature (A, B, C), setting up the testing framework, writing tests for each feature, integrating CLI with core logic, and adding coverage reporting.",
			"reasoning": "This task requires simultaneous development of multiple features, integration with CLI, and comprehensive testing. The coordination and depth required for both implementation and validation make it the most complex among the listed tasks."
		}
	]
}

```

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

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

// Load environment variables
dotenv.config();

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

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

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

		// Bind methods
		this.init = this.init.bind(this);
		this.start = this.start.bind(this);
		this.stop = this.stop.bind(this);

		// Setup logging
		this.logger = logger;
	}

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

		// Pass the manager instance to the tool registration function
		registerTaskMasterTools(this.server, this.asyncManager);

		this.initialized = true;

		return this;
	}

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

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

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

		return this;
	}

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

			// Register MCP provider with the Provider Registry

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

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

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

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

export default TaskMasterMCPServer;

```

--------------------------------------------------------------------------------
/src/ai-providers/custom-sdk/claude-code/errors.js:
--------------------------------------------------------------------------------

```javascript
/**
 * @fileoverview Error handling utilities for Claude Code provider
 */

import { APICallError, LoadAPIKeyError } from '@ai-sdk/provider';

/**
 * @typedef {import('./types.js').ClaudeCodeErrorMetadata} ClaudeCodeErrorMetadata
 */

/**
 * Create an API call error with Claude Code specific metadata
 * @param {Object} params - Error parameters
 * @param {string} params.message - Error message
 * @param {string} [params.code] - Error code
 * @param {number} [params.exitCode] - Process exit code
 * @param {string} [params.stderr] - Standard error output
 * @param {string} [params.promptExcerpt] - Excerpt of the prompt
 * @param {boolean} [params.isRetryable=false] - Whether the error is retryable
 * @returns {APICallError}
 */
export function createAPICallError({
	message,
	code,
	exitCode,
	stderr,
	promptExcerpt,
	isRetryable = false
}) {
	/** @type {ClaudeCodeErrorMetadata} */
	const metadata = {
		code,
		exitCode,
		stderr,
		promptExcerpt
	};

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

/**
 * Create an authentication error
 * @param {Object} params - Error parameters
 * @param {string} params.message - Error message
 * @returns {LoadAPIKeyError}
 */
export function createAuthenticationError({ message }) {
	return new LoadAPIKeyError({
		message:
			message ||
			'Authentication failed. Please ensure Claude Code CLI is properly authenticated.'
	});
}

/**
 * Create a timeout error
 * @param {Object} params - Error parameters
 * @param {string} params.message - Error message
 * @param {string} [params.promptExcerpt] - Excerpt of the prompt
 * @param {number} params.timeoutMs - Timeout in milliseconds
 * @returns {APICallError}
 */
export function createTimeoutError({ message, promptExcerpt, timeoutMs }) {
	// Store timeoutMs in metadata for potential use by error handlers
	/** @type {ClaudeCodeErrorMetadata & { timeoutMs: number }} */
	const metadata = {
		code: 'TIMEOUT',
		promptExcerpt,
		timeoutMs
	};

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

/**
 * Check if an error is an authentication error
 * @param {unknown} error - Error to check
 * @returns {boolean}
 */
export function isAuthenticationError(error) {
	if (error instanceof LoadAPIKeyError) return true;
	if (
		error instanceof APICallError &&
		/** @type {ClaudeCodeErrorMetadata} */ (error.data)?.exitCode === 401
	)
		return true;
	return false;
}

/**
 * Check if an error is a timeout error
 * @param {unknown} error - Error to check
 * @returns {boolean}
 */
export function isTimeoutError(error) {
	if (
		error instanceof APICallError &&
		/** @type {ClaudeCodeErrorMetadata} */ (error.data)?.code === 'TIMEOUT'
	)
		return true;
	return false;
}

/**
 * Get error metadata from an error
 * @param {unknown} error - Error to extract metadata from
 * @returns {ClaudeCodeErrorMetadata|undefined}
 */
export function getErrorMetadata(error) {
	if (error instanceof APICallError && error.data) {
		return /** @type {ClaudeCodeErrorMetadata} */ (error.data);
	}
	return undefined;
}

```

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

```javascript
/**
 * list-tasks.js
 * Direct function implementation for listing tasks
 */

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

/**
 * Direct function wrapper for listTasks with error handling and caching.
 *
 * @param {Object} args - Command arguments (now expecting tasksJsonPath explicitly).
 * @param {string} args.tasksJsonPath - Path to the tasks.json file.
 * @param {string} args.reportPath - Path to the report file.
 * @param {string} args.status - Status of the task.
 * @param {boolean} args.withSubtasks - Whether to include subtasks.
 * @param {string} args.projectRoot - Project root path (for MCP/env fallback)
 * @param {string} args.tag - Tag for the task (optional)
 * @param {Object} log - Logger object.
 * @returns {Promise<Object>} - Task list result { success: boolean, data?: any, error?: { code: string, message: string } }.
 */
export async function listTasksDirect(args, log, context = {}) {
	// Destructure the explicit tasksJsonPath from args
	const { tasksJsonPath, reportPath, status, withSubtasks, projectRoot, tag } =
		args;
	const { session } = context;

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

	// Use the explicit tasksJsonPath for cache key
	const statusFilter = status || 'all';
	const withSubtasksFilter = withSubtasks || false;

	// Define the action function to be executed on cache miss
	const coreListTasksAction = async () => {
		try {
			// Enable silent mode to prevent console logs from interfering with JSON response
			enableSilentMode();

			log.info(
				`Executing core listTasks function for path: ${tasksJsonPath}, filter: ${statusFilter}, subtasks: ${withSubtasksFilter}`
			);
			// Pass the explicit tasksJsonPath to the core function
			const resultData = listTasks(
				tasksJsonPath,
				statusFilter,
				reportPath,
				withSubtasksFilter,
				'json',
				{ projectRoot, session, tag }
			);

			if (!resultData || !resultData.tasks) {
				log.error('Invalid or empty response from listTasks core function');
				return {
					success: false,
					error: {
						code: 'INVALID_CORE_RESPONSE',
						message: 'Invalid or empty response from listTasks core function'
					}
				};
			}

			log.info(
				`Core listTasks function retrieved ${resultData.tasks.length} tasks`
			);

			// Restore normal logging
			disableSilentMode();

			return { success: true, data: resultData };
		} catch (error) {
			// Make sure to restore normal logging even if there's an error
			disableSilentMode();

			log.error(`Core listTasks function failed: ${error.message}`);
			return {
				success: false,
				error: {
					code: 'LIST_TASKS_CORE_ERROR',
					message: error.message || 'Failed to list tasks'
				}
			};
		}
	};

	try {
		const result = await coreListTasksAction();
		log.info('listTasksDirect completed');
		return result;
	} catch (error) {
		log.error(`Unexpected error during listTasks: ${error.message}`);
		console.error(error.stack);
		return {
			success: false,
			error: {
				code: 'UNEXPECTED_ERROR',
				message: error.message
			}
		};
	}
}

```

--------------------------------------------------------------------------------
/.github/scripts/check-pre-release-mode.mjs:
--------------------------------------------------------------------------------

```
#!/usr/bin/env node
import { readFileSync, existsSync } from 'node:fs';
import { join, dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

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

// Get context from command line argument or environment
const context = process.argv[2] || process.env.GITHUB_WORKFLOW || 'manual';

function findRootDir(startDir) {
	let currentDir = resolve(startDir);
	while (currentDir !== '/') {
		if (existsSync(join(currentDir, 'package.json'))) {
			try {
				const pkg = JSON.parse(
					readFileSync(join(currentDir, 'package.json'), 'utf8')
				);
				if (pkg.name === 'task-master-ai' || pkg.repository) {
					return currentDir;
				}
			} catch {}
		}
		currentDir = dirname(currentDir);
	}
	throw new Error('Could not find root directory');
}

function checkPreReleaseMode() {
	console.log('🔍 Checking if branch is in pre-release mode...');

	const rootDir = findRootDir(__dirname);
	const preJsonPath = join(rootDir, '.changeset', 'pre.json');

	// Check if pre.json exists
	if (!existsSync(preJsonPath)) {
		console.log('✅ Not in active pre-release mode - safe to proceed');
		process.exit(0);
	}

	try {
		// Read and parse pre.json
		const preJsonContent = readFileSync(preJsonPath, 'utf8');
		const preJson = JSON.parse(preJsonContent);

		// Check if we're in active pre-release mode
		if (preJson.mode === 'pre') {
			console.error('❌ ERROR: This branch is in active pre-release mode!');
			console.error('');

			// Provide context-specific error messages
			if (context === 'Release Check' || context === 'pull_request') {
				console.error(
					'Pre-release mode must be exited before merging to main.'
				);
				console.error('');
				console.error(
					'To fix this, run the following commands in your branch:'
				);
				console.error('  npx changeset pre exit');
				console.error('  git add -u');
				console.error('  git commit -m "chore: exit pre-release mode"');
				console.error('  git push');
				console.error('');
				console.error('Then update this pull request.');
			} else if (context === 'Release' || context === 'main') {
				console.error(
					'Pre-release mode should only be used on feature branches, not main.'
				);
				console.error('');
				console.error('To fix this, run the following commands locally:');
				console.error('  npx changeset pre exit');
				console.error('  git add -u');
				console.error('  git commit -m "chore: exit pre-release mode"');
				console.error('  git push origin main');
				console.error('');
				console.error('Then re-run this workflow.');
			} else {
				console.error('Pre-release mode must be exited before proceeding.');
				console.error('');
				console.error('To fix this, run the following commands:');
				console.error('  npx changeset pre exit');
				console.error('  git add -u');
				console.error('  git commit -m "chore: exit pre-release mode"');
				console.error('  git push');
			}

			process.exit(1);
		}

		console.log('✅ Not in active pre-release mode - safe to proceed');
		process.exit(0);
	} catch (error) {
		console.error(`❌ ERROR: Unable to parse .changeset/pre.json – aborting.`);
		console.error(`Error details: ${error.message}`);
		process.exit(1);
	}
}

// Run the check
checkPreReleaseMode();

```

--------------------------------------------------------------------------------
/apps/extension/src/services/config-service.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Config Service
 * Manages Task Master config.json file operations
 */

import * as path from 'path';
import * as fs from 'fs/promises';
import * as vscode from 'vscode';
import type { ExtensionLogger } from '../utils/logger';

export interface TaskMasterConfigJson {
	anthropicApiKey?: string;
	perplexityApiKey?: string;
	openaiApiKey?: string;
	googleApiKey?: string;
	xaiApiKey?: string;
	openrouterApiKey?: string;
	mistralApiKey?: string;
	debug?: boolean;
	models?: {
		main?: string;
		research?: string;
		fallback?: string;
	};
}

export class ConfigService {
	private configCache: TaskMasterConfigJson | null = null;
	private lastReadTime = 0;
	private readonly CACHE_DURATION = 5000; // 5 seconds

	constructor(private logger: ExtensionLogger) {}

	/**
	 * Read Task Master config.json from the workspace
	 */
	async readConfig(): Promise<TaskMasterConfigJson | null> {
		// Check cache first
		if (
			this.configCache &&
			Date.now() - this.lastReadTime < this.CACHE_DURATION
		) {
			return this.configCache;
		}

		try {
			const workspaceRoot = this.getWorkspaceRoot();
			if (!workspaceRoot) {
				this.logger.warn('No workspace folder found');
				return null;
			}

			const configPath = path.join(workspaceRoot, '.taskmaster', 'config.json');

			try {
				const configContent = await fs.readFile(configPath, 'utf-8');
				const config = JSON.parse(configContent) as TaskMasterConfigJson;

				// Cache the result
				this.configCache = config;
				this.lastReadTime = Date.now();

				this.logger.debug('Successfully read Task Master config', {
					hasModels: !!config.models,
					debug: config.debug
				});

				return config;
			} catch (error) {
				if ((error as any).code === 'ENOENT') {
					this.logger.debug('Task Master config.json not found');
				} else {
					this.logger.error('Failed to read Task Master config', error);
				}
				return null;
			}
		} catch (error) {
			this.logger.error('Error accessing Task Master config', error);
			return null;
		}
	}

	/**
	 * Get safe config for display (with sensitive data masked)
	 */
	async getSafeConfig(): Promise<Record<string, any> | null> {
		const config = await this.readConfig();
		if (!config) {
			return null;
		}

		// Create a safe copy with masked API keys
		const safeConfig: Record<string, any> = {
			...config
		};

		// Mask all API keys
		const apiKeyFields = [
			'anthropicApiKey',
			'perplexityApiKey',
			'openaiApiKey',
			'googleApiKey',
			'xaiApiKey',
			'openrouterApiKey',
			'mistralApiKey'
		];

		for (const field of apiKeyFields) {
			if (safeConfig[field]) {
				safeConfig[field] = this.maskApiKey(safeConfig[field]);
			}
		}

		return safeConfig;
	}

	/**
	 * Mask API key for display
	 * Shows only the last 4 characters for better security
	 */
	private maskApiKey(key: string): string {
		if (key.length <= 4) {
			return '****';
		}
		const visibleChars = 4;
		const maskedLength = key.length - visibleChars;
		return (
			'*'.repeat(Math.min(maskedLength, 12)) +
			key.substring(key.length - visibleChars)
		);
	}

	/**
	 * Clear cache
	 */
	clearCache(): void {
		this.configCache = null;
		this.lastReadTime = 0;
	}

	/**
	 * Get workspace root path
	 */
	private getWorkspaceRoot(): string | undefined {
		return vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
	}
}

```

--------------------------------------------------------------------------------
/src/progress/cli-progress-factory.js:
--------------------------------------------------------------------------------

```javascript
import cliProgress from 'cli-progress';

/**
 * Default configuration for progress bars
 * Extracted to avoid duplication and provide single source of truth
 */
const DEFAULT_CONFIG = {
	clearOnComplete: false,
	stopOnComplete: true,
	hideCursor: true,
	barsize: 40 // Standard terminal width for progress bar
};

/**
 * Available presets for progress bar styling
 * Makes it easy to see what options are available
 */
const PRESETS = {
	shades_classic: cliProgress.Presets.shades_classic,
	shades_grey: cliProgress.Presets.shades_grey,
	rect: cliProgress.Presets.rect,
	legacy: cliProgress.Presets.legacy
};

/**
 * Factory class for creating CLI progress bars
 * Provides a consistent interface for creating both single and multi-bar instances
 */
export class ProgressBarFactory {
	constructor(defaultOptions = {}, defaultPreset = PRESETS.shades_classic) {
		this.defaultOptions = { ...DEFAULT_CONFIG, ...defaultOptions };
		this.defaultPreset = defaultPreset;
	}

	/**
	 * Creates a new single progress bar
	 * @param {Object} opts - Custom options to override defaults
	 * @param {Object} preset - Progress bar preset for styling
	 * @returns {cliProgress.SingleBar} Configured single progress bar instance
	 */
	createSingleBar(opts = {}, preset = null) {
		const config = this._mergeConfig(opts);
		const barPreset = preset || this.defaultPreset;

		return new cliProgress.SingleBar(config, barPreset);
	}

	/**
	 * Creates a new multi-bar container
	 * @param {Object} opts - Custom options to override defaults
	 * @param {Object} preset - Progress bar preset for styling
	 * @returns {cliProgress.MultiBar} Configured multi-bar instance
	 */
	createMultiBar(opts = {}, preset = null) {
		const config = this._mergeConfig(opts);
		const barPreset = preset || this.defaultPreset;

		return new cliProgress.MultiBar(config, barPreset);
	}

	/**
	 * Merges custom options with defaults
	 * @private
	 * @param {Object} customOpts - Custom options to merge
	 * @returns {Object} Merged configuration
	 */
	_mergeConfig(customOpts) {
		return { ...this.defaultOptions, ...customOpts };
	}

	/**
	 * Updates the default configuration
	 * @param {Object} options - New default options
	 */
	setDefaultOptions(options) {
		this.defaultOptions = { ...this.defaultOptions, ...options };
	}

	/**
	 * Updates the default preset
	 * @param {Object} preset - New default preset
	 */
	setDefaultPreset(preset) {
		this.defaultPreset = preset;
	}
}

// Create a default factory instance for backward compatibility
const defaultFactory = new ProgressBarFactory();

/**
 * Legacy function for creating a single progress bar
 * @deprecated Use ProgressBarFactory.createSingleBar() instead
 * @param {Object} opts - Progress bar options
 * @returns {cliProgress.SingleBar} Single progress bar instance
 */
export function newSingle(opts = {}) {
	return defaultFactory.createSingleBar(opts);
}

/**
 * Legacy function for creating a multi-bar
 * @deprecated Use ProgressBarFactory.createMultiBar() instead
 * @param {Object} opts - Progress bar options
 * @returns {cliProgress.MultiBar} Multi-bar instance
 */
export function newMultiBar(opts = {}) {
	return defaultFactory.createMultiBar(opts);
}

// Export presets for easy access
export { PRESETS };

// Export the factory class as default
export default ProgressBarFactory;

```

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

```javascript
/**
 * tools/add-subtask.js
 * Tool for adding subtasks to existing tasks
 */

import { z } from 'zod';
import {
	handleApiResult,
	createErrorResponse,
	withNormalizedProjectRoot
} from './utils.js';
import { addSubtaskDirect } from '../core/task-master-core.js';
import { findTasksPath } from '../core/utils/path-utils.js';
import { resolveTag } from '../../../scripts/modules/utils.js';

/**
 * Register the addSubtask tool with the MCP server
 * @param {Object} server - FastMCP server instance
 */
export function registerAddSubtaskTool(server) {
	server.addTool({
		name: 'add_subtask',
		description: 'Add a subtask to an existing task',
		parameters: z.object({
			id: z.string().describe('Parent task ID (required)'),
			taskId: z
				.string()
				.optional()
				.describe('Existing task ID to convert to subtask'),
			title: z
				.string()
				.optional()
				.describe('Title for the new subtask (when creating a new subtask)'),
			description: z
				.string()
				.optional()
				.describe('Description for the new subtask'),
			details: z
				.string()
				.optional()
				.describe('Implementation details for the new subtask'),
			status: z
				.string()
				.optional()
				.describe("Status for the new subtask (default: 'pending')"),
			dependencies: z
				.string()
				.optional()
				.describe('Comma-separated list of dependency IDs for the new subtask'),
			file: z
				.string()
				.optional()
				.describe(
					'Absolute path to the tasks file (default: tasks/tasks.json)'
				),
			skipGenerate: z
				.boolean()
				.optional()
				.describe('Skip regenerating task files'),
			projectRoot: z
				.string()
				.describe('The directory of the project. Must be an absolute path.'),
			tag: z.string().optional().describe('Tag context to operate on')
		}),
		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
			try {
				const resolvedTag = resolveTag({
					projectRoot: args.projectRoot,
					tag: args.tag
				});
				log.info(`Adding subtask with args: ${JSON.stringify(args)}`);

				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
				let tasksJsonPath;
				try {
					tasksJsonPath = findTasksPath(
						{ projectRoot: args.projectRoot, file: args.file },
						log
					);
				} catch (error) {
					log.error(`Error finding tasks.json: ${error.message}`);
					return createErrorResponse(
						`Failed to find tasks.json: ${error.message}`
					);
				}

				const result = await addSubtaskDirect(
					{
						tasksJsonPath: tasksJsonPath,
						id: args.id,
						taskId: args.taskId,
						title: args.title,
						description: args.description,
						details: args.details,
						status: args.status,
						dependencies: args.dependencies,
						skipGenerate: args.skipGenerate,
						projectRoot: args.projectRoot,
						tag: resolvedTag
					},
					log,
					{ session }
				);

				if (result.success) {
					log.info(`Subtask added successfully: ${result.data.message}`);
				} else {
					log.error(`Failed to add subtask: ${result.error.message}`);
				}

				return handleApiResult(
					result,
					log,
					'Error adding subtask',
					undefined,
					args.projectRoot
				);
			} catch (error) {
				log.error(`Error in addSubtask tool: ${error.message}`);
				return createErrorResponse(error.message);
			}
		})
	});
}

```

--------------------------------------------------------------------------------
/src/provider-registry/index.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Provider Registry - Singleton for managing AI providers
 *
 * This module implements a singleton registry that allows dynamic registration
 * of AI providers at runtime, while maintaining compatibility with the existing
 * static PROVIDERS object in ai-services-unified.js.
 */

// Singleton instance
let instance = null;

/**
 * Provider Registry class - Manages dynamic provider registration
 */
class ProviderRegistry {
	constructor() {
		// Private provider map
		this._providers = new Map();

		// Flag to track initialization
		this._initialized = false;
	}

	/**
	 * Get the singleton instance
	 * @returns {ProviderRegistry} The singleton instance
	 */
	static getInstance() {
		if (!instance) {
			instance = new ProviderRegistry();
		}
		return instance;
	}

	/**
	 * Initialize the registry
	 * @returns {ProviderRegistry} The singleton instance
	 */
	initialize() {
		if (this._initialized) {
			return this;
		}

		this._initialized = true;
		return this;
	}

	/**
	 * Register a provider with the registry
	 * @param {string} providerName - The name of the provider
	 * @param {object} provider - The provider instance
	 * @param {object} options - Additional options for registration
	 * @returns {ProviderRegistry} The singleton instance for chaining
	 */
	registerProvider(providerName, provider, options = {}) {
		if (!providerName || typeof providerName !== 'string') {
			throw new Error('Provider name must be a non-empty string');
		}

		if (!provider) {
			throw new Error('Provider instance is required');
		}

		// Validate that provider implements the required interface
		if (
			typeof provider.generateText !== 'function' ||
			typeof provider.streamText !== 'function' ||
			typeof provider.generateObject !== 'function'
		) {
			throw new Error('Provider must implement BaseAIProvider interface');
		}

		// Add provider to the registry
		this._providers.set(providerName, {
			instance: provider,
			options,
			registeredAt: new Date()
		});

		return this;
	}

	/**
	 * Check if a provider exists in the registry
	 * @param {string} providerName - The name of the provider
	 * @returns {boolean} True if the provider exists
	 */
	hasProvider(providerName) {
		return this._providers.has(providerName);
	}

	/**
	 * Get a provider from the registry
	 * @param {string} providerName - The name of the provider
	 * @returns {object|null} The provider instance or null if not found
	 */
	getProvider(providerName) {
		const providerEntry = this._providers.get(providerName);
		return providerEntry ? providerEntry.instance : null;
	}

	/**
	 * Get all registered providers
	 * @returns {Map} Map of all registered providers
	 */
	getAllProviders() {
		return new Map(this._providers);
	}

	/**
	 * Remove a provider from the registry
	 * @param {string} providerName - The name of the provider
	 * @returns {boolean} True if the provider was removed
	 */
	unregisterProvider(providerName) {
		if (this._providers.has(providerName)) {
			this._providers.delete(providerName);
			return true;
		}
		return false;
	}

	/**
	 * Reset the registry (primarily for testing)
	 */
	reset() {
		this._providers.clear();
		this._initialized = false;
	}
}

ProviderRegistry.getInstance().initialize(); // Ensure singleton is initialized on import
// Export singleton getter
export default ProviderRegistry;

```

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

```javascript
/**
 * copy-tag.js
 * Direct function implementation for copying a tag
 */

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

/**
 * Direct function wrapper for copying a tag with error handling.
 *
 * @param {Object} args - Command arguments
 * @param {string} args.sourceName - Name of the source tag to copy from
 * @param {string} args.targetName - Name of the new tag to create
 * @param {string} [args.description] - Optional description for the new tag
 * @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
 * @param {string} [args.projectRoot] - Project root path
 * @param {Object} log - Logger object
 * @param {Object} context - Additional context (session)
 * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 */
export async function copyTagDirect(args, log, context = {}) {
	// Destructure expected args
	const { tasksJsonPath, sourceName, targetName, description, projectRoot } =
		args;
	const { session } = context;

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

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

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

		// Check required parameters
		if (!sourceName || typeof sourceName !== 'string') {
			log.error('Missing required parameter: sourceName');
			disableSilentMode();
			return {
				success: false,
				error: {
					code: 'MISSING_PARAMETER',
					message: 'Source tag name is required and must be a string'
				}
			};
		}

		if (!targetName || typeof targetName !== 'string') {
			log.error('Missing required parameter: targetName');
			disableSilentMode();
			return {
				success: false,
				error: {
					code: 'MISSING_PARAMETER',
					message: 'Target tag name is required and must be a string'
				}
			};
		}

		log.info(`Copying tag from "${sourceName}" to "${targetName}"`);

		// Prepare options
		const options = {
			description
		};

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

		// Restore normal logging
		disableSilentMode();

		return {
			success: true,
			data: {
				sourceName: result.sourceName,
				targetName: result.targetName,
				copied: result.copied,
				tasksCopied: result.tasksCopied,
				description: result.description,
				message: `Successfully copied tag from "${result.sourceName}" to "${result.targetName}"`
			}
		};
	} catch (error) {
		// Make sure to restore normal logging even if there's an error
		disableSilentMode();

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

```

--------------------------------------------------------------------------------
/scripts/modules/task-manager/parse-prd/parse-prd-config.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Configuration classes and schemas for PRD parsing
 */

import { z } from 'zod';
import { TASK_PRIORITY_OPTIONS } from '../../../../src/constants/task-priority.js';
import { getCurrentTag, isSilentMode, log } from '../../utils.js';
import { Duration } from '../../../../src/utils/timeout-manager.js';
import { hasCodebaseAnalysis } from '../../config-manager.js';

// ============================================================================
// SCHEMAS
// ============================================================================

// Define the Zod schema for a SINGLE task object
export const prdSingleTaskSchema = z.object({
	id: z.number(),
	title: z.string().min(1),
	description: z.string().min(1),
	details: z.string(),
	testStrategy: z.string(),
	priority: z.enum(TASK_PRIORITY_OPTIONS),
	dependencies: z.array(z.number()),
	status: z.string()
});

// Define the Zod schema for the ENTIRE expected AI response object
export const prdResponseSchema = z.object({
	tasks: z.array(prdSingleTaskSchema),
	metadata: z.object({
		projectName: z.string(),
		totalTasks: z.number(),
		sourceFile: z.string(),
		generatedAt: z.string()
	})
});

// ============================================================================
// CONFIGURATION CLASSES
// ============================================================================

/**
 * Configuration object for PRD parsing
 */
export class PrdParseConfig {
	constructor(prdPath, tasksPath, numTasks, options = {}) {
		this.prdPath = prdPath;
		this.tasksPath = tasksPath;
		this.numTasks = numTasks;
		this.force = options.force || false;
		this.append = options.append || false;
		this.research = options.research || false;
		this.reportProgress = options.reportProgress;
		this.mcpLog = options.mcpLog;
		this.session = options.session;
		this.projectRoot = options.projectRoot;
		this.tag = options.tag;
		this.streamingTimeout =
			options.streamingTimeout || Duration.seconds(180).milliseconds;

		// Derived values
		this.targetTag = this.tag || getCurrentTag(this.projectRoot) || 'master';
		this.isMCP = !!this.mcpLog;
		this.outputFormat = this.isMCP && !this.reportProgress ? 'json' : 'text';

		// Feature flag: Temporarily disable streaming, use generateObject instead
		// TODO: Re-enable streaming once issues are resolved
		const ENABLE_STREAMING = false;

		this.useStreaming =
			ENABLE_STREAMING &&
			(typeof this.reportProgress === 'function' ||
				this.outputFormat === 'text');
	}

	/**
	 * Check if codebase analysis is available (Claude Code or Gemini CLI)
	 */
	hasCodebaseAnalysis() {
		return hasCodebaseAnalysis(this.research, this.projectRoot, this.session);
	}
}

/**
 * Logging configuration and utilities
 */
export class LoggingConfig {
	constructor(mcpLog, reportProgress) {
		this.isMCP = !!mcpLog;
		this.outputFormat = this.isMCP && !reportProgress ? 'json' : 'text';

		this.logFn = mcpLog || {
			info: (...args) => log('info', ...args),
			warn: (...args) => log('warn', ...args),
			error: (...args) => log('error', ...args),
			debug: (...args) => log('debug', ...args),
			success: (...args) => log('success', ...args)
		};
	}

	report(message, level = 'info') {
		if (this.logFn && typeof this.logFn[level] === 'function') {
			this.logFn[level](message);
		} else if (!isSilentMode() && this.outputFormat === 'text') {
			log(level, message);
		}
	}
}

```

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

```yaml
name: Extension CI

on:
  push:
    branches:
      - main
      - next
    paths:
      - 'apps/extension/**'
      - '.github/workflows/extension-ci.yml'
  pull_request:
    branches:
      - main
      - next
    paths:
      - 'apps/extension/**'
      - '.github/workflows/extension-ci.yml'

permissions:
  contents: read

jobs:
  setup:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

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

      - name: Cache node_modules
        uses: actions/cache@v4
        with:
          path: |
            node_modules
            */*/node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install Extension Dependencies
        working-directory: apps/extension
        run: npm ci
        timeout-minutes: 5

  typecheck:
    needs: setup
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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


      - name: Restore node_modules
        uses: actions/cache@v4
        with:
          path: |
            node_modules
            */*/node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install if cache miss
        working-directory: apps/extension
        run: npm ci
        timeout-minutes: 3

      - name: Type Check Extension
        working-directory: apps/extension
        run: npm run check-types
        env:
          FORCE_COLOR: 1

  build:
    needs: setup
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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


      - name: Restore node_modules
        uses: actions/cache@v4
        with:
          path: |
            node_modules
            */*/node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install if cache miss
        working-directory: apps/extension
        run: npm ci
        timeout-minutes: 3

      - name: Build Extension
        working-directory: apps/extension
        run: npm run build
        env:
          FORCE_COLOR: 1

      - name: Package Extension
        working-directory: apps/extension
        run: npm run package
        env:
          FORCE_COLOR: 1

      - name: Verify Package Contents
        working-directory: apps/extension
        run: |
          echo "Checking vsix-build contents..."
          ls -la vsix-build/
          echo "Checking dist contents..."
          ls -la vsix-build/dist/
          echo "Checking package.json exists..."
          test -f vsix-build/package.json

      - name: Create VSIX Package (Test)
        working-directory: apps/extension/vsix-build
        run: npx vsce package --no-dependencies
        env:
          FORCE_COLOR: 1

      - name: Upload Extension Artifact
        uses: actions/upload-artifact@v4
        with:
          name: extension-package
          path: |
            apps/extension/vsix-build/*.vsix
            apps/extension/dist/
          retention-days: 30


```
Page 6/38FirstPrevNextLast