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

# Directory Structure

```
├── .changeset
│   ├── config.json
│   └── README.md
├── .claude
│   ├── 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

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

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

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

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

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

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

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

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

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

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

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

```javascript
  1 | /**
  2 |  * complexity-report.js
  3 |  * Direct function implementation for displaying complexity analysis report
  4 |  */
  5 | 
  6 | import {
  7 | 	readComplexityReport,
  8 | 	enableSilentMode,
  9 | 	disableSilentMode
 10 | } from '../../../../scripts/modules/utils.js';
 11 | 
 12 | /**
 13 |  * Direct function wrapper for displaying the complexity report with error handling and caching.
 14 |  *
 15 |  * @param {Object} args - Command arguments containing reportPath.
 16 |  * @param {string} args.reportPath - Explicit path to the complexity report file.
 17 |  * @param {Object} log - Logger object
 18 |  * @returns {Promise<Object>} - Result object with success status and data/error information
 19 |  */
 20 | export async function complexityReportDirect(args, log) {
 21 | 	// Destructure expected args
 22 | 	const { reportPath } = args;
 23 | 	try {
 24 | 		log.info(`Getting complexity report with args: ${JSON.stringify(args)}`);
 25 | 
 26 | 		// Check if reportPath was provided
 27 | 		if (!reportPath) {
 28 | 			log.error('complexityReportDirect called without reportPath');
 29 | 			return {
 30 | 				success: false,
 31 | 				error: { code: 'MISSING_ARGUMENT', message: 'reportPath is required' }
 32 | 			};
 33 | 		}
 34 | 
 35 | 		// Use the provided report path
 36 | 		log.info(`Looking for complexity report at: ${reportPath}`);
 37 | 
 38 | 		// Generate cache key based on report path
 39 | 		const cacheKey = `complexityReport:${reportPath}`;
 40 | 
 41 | 		// Define the core action function to read the report
 42 | 		const coreActionFn = async () => {
 43 | 			try {
 44 | 				// Enable silent mode to prevent console logs from interfering with JSON response
 45 | 				enableSilentMode();
 46 | 
 47 | 				const report = readComplexityReport(reportPath);
 48 | 
 49 | 				// Restore normal logging
 50 | 				disableSilentMode();
 51 | 
 52 | 				if (!report) {
 53 | 					log.warn(`No complexity report found at ${reportPath}`);
 54 | 					return {
 55 | 						success: false,
 56 | 						error: {
 57 | 							code: 'FILE_NOT_FOUND_ERROR',
 58 | 							message: `No complexity report found at ${reportPath}. Run 'analyze-complexity' first.`
 59 | 						}
 60 | 					};
 61 | 				}
 62 | 
 63 | 				return {
 64 | 					success: true,
 65 | 					data: {
 66 | 						report,
 67 | 						reportPath
 68 | 					}
 69 | 				};
 70 | 			} catch (error) {
 71 | 				// Make sure to restore normal logging even if there's an error
 72 | 				disableSilentMode();
 73 | 
 74 | 				log.error(`Error reading complexity report: ${error.message}`);
 75 | 				return {
 76 | 					success: false,
 77 | 					error: {
 78 | 						code: 'READ_ERROR',
 79 | 						message: error.message
 80 | 					}
 81 | 				};
 82 | 			}
 83 | 		};
 84 | 
 85 | 		// Use the caching utility
 86 | 		try {
 87 | 			const result = await coreActionFn();
 88 | 			log.info('complexityReportDirect completed');
 89 | 			return result;
 90 | 		} catch (error) {
 91 | 			// Ensure silent mode is disabled
 92 | 			disableSilentMode();
 93 | 
 94 | 			log.error(`Unexpected error during complexityReport: ${error.message}`);
 95 | 			return {
 96 | 				success: false,
 97 | 				error: {
 98 | 					code: 'UNEXPECTED_ERROR',
 99 | 					message: error.message
100 | 				}
101 | 			};
102 | 		}
103 | 	} catch (error) {
104 | 		// Ensure silent mode is disabled if an outer error occurs
105 | 		disableSilentMode();
106 | 
107 | 		log.error(`Error in complexityReportDirect: ${error.message}`);
108 | 		return {
109 | 			success: false,
110 | 			error: {
111 | 				code: 'UNEXPECTED_ERROR',
112 | 				message: error.message
113 | 			}
114 | 		};
115 | 	}
116 | }
117 | 
```

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

```javascript
  1 | import chalk from 'chalk';
  2 | import { isSilentMode } from '../../scripts/modules/utils.js';
  3 | import { getLogLevel } from '../../scripts/modules/config-manager.js';
  4 | 
  5 | // Define log levels
  6 | const LOG_LEVELS = {
  7 | 	debug: 0,
  8 | 	info: 1,
  9 | 	warn: 2,
 10 | 	error: 3,
 11 | 	success: 4
 12 | };
 13 | 
 14 | // Get log level from config manager or default to info
 15 | const LOG_LEVEL = LOG_LEVELS[getLogLevel().toLowerCase()] ?? LOG_LEVELS.info;
 16 | 
 17 | /**
 18 |  * Logs a message with the specified level
 19 |  * @param {string} level - The log level (debug, info, warn, error, success)
 20 |  * @param  {...any} args - Arguments to log
 21 |  */
 22 | function log(level, ...args) {
 23 | 	// Skip logging if silent mode is enabled
 24 | 	if (isSilentMode()) {
 25 | 		return;
 26 | 	}
 27 | 
 28 | 	// Use text prefixes instead of emojis
 29 | 	const prefixes = {
 30 | 		debug: chalk.gray('[DEBUG]'),
 31 | 		info: chalk.blue('[INFO]'),
 32 | 		warn: chalk.yellow('[WARN]'),
 33 | 		error: chalk.red('[ERROR]'),
 34 | 		success: chalk.green('[SUCCESS]')
 35 | 	};
 36 | 
 37 | 	if (LOG_LEVELS[level] !== undefined && LOG_LEVELS[level] >= LOG_LEVEL) {
 38 | 		const prefix = prefixes[level] || '';
 39 | 		let coloredArgs = args;
 40 | 
 41 | 		try {
 42 | 			switch (level) {
 43 | 				case 'error':
 44 | 					coloredArgs = args.map((arg) =>
 45 | 						typeof arg === 'string' ? chalk.red(arg) : arg
 46 | 					);
 47 | 					break;
 48 | 				case 'warn':
 49 | 					coloredArgs = args.map((arg) =>
 50 | 						typeof arg === 'string' ? chalk.yellow(arg) : arg
 51 | 					);
 52 | 					break;
 53 | 				case 'success':
 54 | 					coloredArgs = args.map((arg) =>
 55 | 						typeof arg === 'string' ? chalk.green(arg) : arg
 56 | 					);
 57 | 					break;
 58 | 				case 'info':
 59 | 					coloredArgs = args.map((arg) =>
 60 | 						typeof arg === 'string' ? chalk.blue(arg) : arg
 61 | 					);
 62 | 					break;
 63 | 				case 'debug':
 64 | 					coloredArgs = args.map((arg) =>
 65 | 						typeof arg === 'string' ? chalk.gray(arg) : arg
 66 | 					);
 67 | 					break;
 68 | 				// default: use original args (no color)
 69 | 			}
 70 | 		} catch (colorError) {
 71 | 			// Fallback if chalk fails on an argument
 72 | 			// Use console.error here for internal logger errors, separate from normal logging
 73 | 			console.error('Internal Logger Error applying chalk color:', colorError);
 74 | 			coloredArgs = args;
 75 | 		}
 76 | 
 77 | 		// Revert to console.log - FastMCP's context logger (context.log)
 78 | 		// is responsible for directing logs correctly (e.g., to stderr)
 79 | 		// during tool execution without upsetting the client connection.
 80 | 		// Logs outside of tool execution (like startup) will go to stdout.
 81 | 		console.log(prefix, ...coloredArgs);
 82 | 	}
 83 | }
 84 | 
 85 | /**
 86 |  * Create a logger object with methods for different log levels
 87 |  * @returns {Object} Logger object with info, error, debug, warn, and success methods
 88 |  */
 89 | export function createLogger() {
 90 | 	const createLogMethod =
 91 | 		(level) =>
 92 | 		(...args) =>
 93 | 			log(level, ...args);
 94 | 
 95 | 	return {
 96 | 		debug: createLogMethod('debug'),
 97 | 		info: createLogMethod('info'),
 98 | 		warn: createLogMethod('warn'),
 99 | 		error: createLogMethod('error'),
100 | 		success: createLogMethod('success'),
101 | 		log: log // Also expose the raw log function
102 | 	};
103 | }
104 | 
105 | // Export a default logger instance
106 | const logger = createLogger();
107 | 
108 | export default logger;
109 | export { log, LOG_LEVELS };
110 | 
```

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

```typescript
  1 | /**
  2 |  * Main App Component
  3 |  */
  4 | 
  5 | import React, { useReducer, useState, useEffect, useRef } from 'react';
  6 | import { VSCodeContext } from './contexts/VSCodeContext';
  7 | import { QueryProvider } from './providers/QueryProvider';
  8 | import { AppContent } from './components/AppContent';
  9 | import { ToastContainer } from './components/ToastContainer';
 10 | import { ErrorBoundary } from './components/ErrorBoundary';
 11 | import { appReducer, initialState } from './reducers/appReducer';
 12 | import { useWebviewHeight } from './hooks/useWebviewHeight';
 13 | import { useVSCodeMessages } from './hooks/useVSCodeMessages';
 14 | import {
 15 | 	showSuccessToast,
 16 | 	showInfoToast,
 17 | 	showWarningToast,
 18 | 	showErrorToast,
 19 | 	createToast
 20 | } from './utils/toast';
 21 | 
 22 | export const App: React.FC = () => {
 23 | 	const [state, dispatch] = useReducer(appReducer, initialState);
 24 | 	const [vscode] = useState(() => window.acquireVsCodeApi?.());
 25 | 	const availableHeight = useWebviewHeight();
 26 | 	const { sendMessage } = useVSCodeMessages(vscode, state, dispatch);
 27 | 	const hasInitialized = useRef(false);
 28 | 
 29 | 	// Initialize the webview
 30 | 	useEffect(() => {
 31 | 		if (hasInitialized.current) return;
 32 | 		hasInitialized.current = true;
 33 | 
 34 | 		if (!vscode) {
 35 | 			console.warn('⚠️ VS Code API not available - running in standalone mode');
 36 | 			dispatch({
 37 | 				type: 'SET_CONNECTION_STATUS',
 38 | 				payload: { isConnected: false, status: 'Standalone Mode' }
 39 | 			});
 40 | 			return;
 41 | 		}
 42 | 
 43 | 		console.log('🔄 Initializing webview...');
 44 | 
 45 | 		// Notify extension that webview is ready
 46 | 		vscode.postMessage({ type: 'ready' });
 47 | 
 48 | 		// React Query will handle task fetching, so we only need to load tags data
 49 | 		sendMessage({ type: 'getTags' })
 50 | 			.then((tagsData) => {
 51 | 				if (tagsData?.tags && tagsData?.currentTag) {
 52 | 					const tagNames = tagsData.tags.map((tag: any) => tag.name || tag);
 53 | 					dispatch({
 54 | 						type: 'SET_TAG_DATA',
 55 | 						payload: {
 56 | 							currentTag: tagsData.currentTag,
 57 | 							availableTags: tagNames
 58 | 						}
 59 | 					});
 60 | 				}
 61 | 			})
 62 | 			.catch((error) => {
 63 | 				console.error('❌ Failed to load tags:', error);
 64 | 			});
 65 | 	}, [vscode, sendMessage, dispatch]);
 66 | 
 67 | 	const contextValue = {
 68 | 		vscode,
 69 | 		state,
 70 | 		dispatch,
 71 | 		sendMessage,
 72 | 		availableHeight,
 73 | 		// Toast notification functions
 74 | 		showSuccessToast: showSuccessToast(dispatch),
 75 | 		showInfoToast: showInfoToast(dispatch),
 76 | 		showWarningToast: showWarningToast(dispatch),
 77 | 		showErrorToast: showErrorToast(dispatch)
 78 | 	};
 79 | 
 80 | 	return (
 81 | 		<QueryProvider>
 82 | 			<VSCodeContext.Provider value={contextValue}>
 83 | 				<ErrorBoundary
 84 | 					onError={(error) => {
 85 | 						// Handle React errors and show appropriate toast
 86 | 						dispatch({
 87 | 							type: 'ADD_TOAST',
 88 | 							payload: createToast(
 89 | 								'error',
 90 | 								'Component Error',
 91 | 								`A React component crashed: ${error.message}`,
 92 | 								10000
 93 | 							)
 94 | 						});
 95 | 					}}
 96 | 				>
 97 | 					<AppContent />
 98 | 					<ToastContainer
 99 | 						notifications={state.toastNotifications}
100 | 						onDismiss={(id) => dispatch({ type: 'REMOVE_TOAST', payload: id })}
101 | 					/>
102 | 				</ErrorBoundary>
103 | 			</VSCodeContext.Provider>
104 | 		</QueryProvider>
105 | 	);
106 | };
107 | 
```

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

```javascript
  1 | /**
  2 |  * tools/research.js
  3 |  * Tool to perform AI-powered research queries with project context
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	createErrorResponse,
  9 | 	handleApiResult,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { researchDirect } from '../core/task-master-core.js';
 13 | import { resolveTag } from '../../../scripts/modules/utils.js';
 14 | 
 15 | /**
 16 |  * Register the research tool with the MCP server
 17 |  * @param {Object} server - FastMCP server instance
 18 |  */
 19 | export function registerResearchTool(server) {
 20 | 	server.addTool({
 21 | 		name: 'research',
 22 | 		description: 'Perform AI-powered research queries with project context',
 23 | 
 24 | 		parameters: z.object({
 25 | 			query: z.string().describe('Research query/prompt (required)'),
 26 | 			taskIds: z
 27 | 				.string()
 28 | 				.optional()
 29 | 				.describe(
 30 | 					'Comma-separated list of task/subtask IDs for context (e.g., "15,16.2,17")'
 31 | 				),
 32 | 			filePaths: z
 33 | 				.string()
 34 | 				.optional()
 35 | 				.describe(
 36 | 					'Comma-separated list of file paths for context (e.g., "src/api.js,docs/readme.md")'
 37 | 				),
 38 | 			customContext: z
 39 | 				.string()
 40 | 				.optional()
 41 | 				.describe('Additional custom context text to include in the research'),
 42 | 			includeProjectTree: z
 43 | 				.boolean()
 44 | 				.optional()
 45 | 				.describe(
 46 | 					'Include project file tree structure in context (default: false)'
 47 | 				),
 48 | 			detailLevel: z
 49 | 				.enum(['low', 'medium', 'high'])
 50 | 				.optional()
 51 | 				.describe('Detail level for the research response (default: medium)'),
 52 | 			saveTo: z
 53 | 				.string()
 54 | 				.optional()
 55 | 				.describe(
 56 | 					'Automatically save research results to specified task/subtask ID (e.g., "15" or "15.2")'
 57 | 				),
 58 | 			saveToFile: z
 59 | 				.boolean()
 60 | 				.optional()
 61 | 				.describe(
 62 | 					'Save research results to .taskmaster/docs/research/ directory (default: false)'
 63 | 				),
 64 | 			projectRoot: z
 65 | 				.string()
 66 | 				.describe('The directory of the project. Must be an absolute path.'),
 67 | 			tag: z.string().optional().describe('Tag context to operate on')
 68 | 		}),
 69 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 70 | 			try {
 71 | 				const resolvedTag = resolveTag({
 72 | 					projectRoot: args.projectRoot,
 73 | 					tag: args.tag
 74 | 				});
 75 | 				log.info(
 76 | 					`Starting research with query: "${args.query.substring(0, 100)}${args.query.length > 100 ? '...' : ''}"`
 77 | 				);
 78 | 
 79 | 				// Call the direct function
 80 | 				const result = await researchDirect(
 81 | 					{
 82 | 						query: args.query,
 83 | 						taskIds: args.taskIds,
 84 | 						filePaths: args.filePaths,
 85 | 						customContext: args.customContext,
 86 | 						includeProjectTree: args.includeProjectTree || false,
 87 | 						detailLevel: args.detailLevel || 'medium',
 88 | 						saveTo: args.saveTo,
 89 | 						saveToFile: args.saveToFile || false,
 90 | 						projectRoot: args.projectRoot,
 91 | 						tag: resolvedTag
 92 | 					},
 93 | 					log,
 94 | 					{ session }
 95 | 				);
 96 | 
 97 | 				return handleApiResult(
 98 | 					result,
 99 | 					log,
100 | 					'Error performing research',
101 | 					undefined,
102 | 					args.projectRoot
103 | 				);
104 | 			} catch (error) {
105 | 				log.error(`Error in research tool: ${error.message}`);
106 | 				return createErrorResponse(error.message);
107 | 			}
108 | 		})
109 | 	});
110 | }
111 | 
```

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

```markdown
  1 | ---
  2 | title: "Installation(2)"
  3 | description: "This guide walks you through setting up Task Master in your development environment."
  4 | ---
  5 | 
  6 | ## Initial Setup
  7 | 
  8 | <Tip>
  9 |   MCP (Model Control Protocol) provides the easiest way to get started with Task Master directly in your editor.
 10 | </Tip>
 11 | 
 12 | <AccordionGroup>
 13 |   <Accordion title="Option 1: Using MCP (Recommended)" icon="sparkles">
 14 |     <Steps>
 15 |       <Step title="Add the MCP config to your editor">
 16 |         <Link href="https://cursor.sh">Cursor</Link> recommended, but it works with other text editors
 17 |         ```json
 18 |         {
 19 |         	"mcpServers": {
 20 |         		"taskmaster-ai": {
 21 |         			"command": "npx",
 22 |         			"args": ["-y", "task-master-ai"],
 23 |         			"env": {
 24 |         				"ANTHROPIC_API_KEY": "YOUR_ANTHROPIC_API_KEY_HERE",
 25 |         				"PERPLEXITY_API_KEY": "YOUR_PERPLEXITY_API_KEY_HERE",
 26 |         				"MODEL": "claude-3-7-sonnet-20250219",
 27 |         				"PERPLEXITY_MODEL": "sonar-pro",
 28 |         				"MAX_TOKENS": 128000,
 29 |         				"TEMPERATURE": 0.2,
 30 |         				"DEFAULT_SUBTASKS": 5,
 31 |         				"DEFAULT_PRIORITY": "medium"
 32 |         			}
 33 |         		}
 34 |         	}
 35 |         }
 36 |         ```
 37 |       </Step>
 38 |       <Step title="Enable the MCP in your editor settings">
 39 |         
 40 |       </Step>
 41 |       <Step title="Prompt the AI to initialize Task Master">
 42 |         > "Can you please initialize taskmaster-ai into my project?"
 43 | 
 44 |         **The AI will:**
 45 | 
 46 |         1. Create necessary project structure
 47 |         2. Set up initial configuration files
 48 |         3. Guide you through the rest of the process
 49 |         4. Place your PRD document in the `scripts/` directory (e.g., `scripts/prd.txt`)
 50 |         5. **Use natural language commands** to interact with Task Master:
 51 | 
 52 |            > "Can you parse my PRD at scripts/prd.txt?"
 53 |            >
 54 |            > "What's the next task I should work on?"
 55 |            >
 56 |            > "Can you help me implement task 3?"
 57 |       </Step>
 58 |     </Steps>
 59 |   </Accordion>
 60 |   <Accordion title="Option 2: Manual Installation">
 61 |     If you prefer to use the command line interface directly:
 62 | 
 63 |     <Steps>
 64 |       <Step title="Install">
 65 |         <CodeGroup>
 66 | 
 67 |         ```bash Global
 68 |         npm install -g task-master-ai
 69 |         ```
 70 | 
 71 |         
 72 |         ```bash Local
 73 |         npm install task-master-ai
 74 |         ```
 75 | 
 76 |         </CodeGroup>
 77 |       </Step>
 78 |       <Step title="Initialize a new project">
 79 |         <CodeGroup>
 80 | 
 81 |         ```bash Global
 82 |         task-master init
 83 |         ```
 84 | 
 85 |         
 86 |         ```bash Local
 87 |         npx task-master-init
 88 |         ```
 89 | 
 90 |         </CodeGroup>
 91 |       </Step>
 92 |     </Steps>
 93 |     This will prompt you for project details and set up a new project with the necessary files and structure.
 94 |   </Accordion>
 95 | </AccordionGroup>
 96 | 
 97 | ## Common Commands
 98 | 
 99 | <Tip>
100 |   After setting up Task Master, you can use these commands (either via AI prompts or CLI)
101 | </Tip>
102 | 
103 | ```bash
104 | # Parse a PRD and generate tasks
105 | task-master parse-prd your-prd.txt
106 | 
107 | # List all tasks
108 | task-master list
109 | 
110 | # Show the next task to work on
111 | task-master next
112 | 
113 | # Generate task files
114 | task-master generate 
115 | 
```

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | 
  3 | // Mock the claude-code SDK module
  4 | jest.unstable_mockModule(
  5 | 	'../../../src/ai-providers/custom-sdk/claude-code/index.js',
  6 | 	() => ({
  7 | 		createClaudeCode: jest.fn(() => {
  8 | 			const provider = (modelId, settings) => ({
  9 | 				// Mock language model
 10 | 				id: modelId,
 11 | 				settings
 12 | 			});
 13 | 			provider.languageModel = jest.fn((id, settings) => ({ id, settings }));
 14 | 			provider.chat = provider.languageModel;
 15 | 			return provider;
 16 | 		})
 17 | 	})
 18 | );
 19 | 
 20 | // Mock the base provider
 21 | jest.unstable_mockModule('../../../src/ai-providers/base-provider.js', () => ({
 22 | 	BaseAIProvider: class {
 23 | 		constructor() {
 24 | 			this.name = 'Base Provider';
 25 | 		}
 26 | 		handleError(context, error) {
 27 | 			throw error;
 28 | 		}
 29 | 	}
 30 | }));
 31 | 
 32 | // Import after mocking
 33 | const { ClaudeCodeProvider } = await import(
 34 | 	'../../../src/ai-providers/claude-code.js'
 35 | );
 36 | 
 37 | describe('ClaudeCodeProvider', () => {
 38 | 	let provider;
 39 | 
 40 | 	beforeEach(() => {
 41 | 		provider = new ClaudeCodeProvider();
 42 | 		jest.clearAllMocks();
 43 | 	});
 44 | 
 45 | 	describe('constructor', () => {
 46 | 		it('should set the provider name to Claude Code', () => {
 47 | 			expect(provider.name).toBe('Claude Code');
 48 | 		});
 49 | 	});
 50 | 
 51 | 	describe('validateAuth', () => {
 52 | 		it('should not throw an error (no API key required)', () => {
 53 | 			expect(() => provider.validateAuth({})).not.toThrow();
 54 | 		});
 55 | 
 56 | 		it('should not require any parameters', () => {
 57 | 			expect(() => provider.validateAuth()).not.toThrow();
 58 | 		});
 59 | 
 60 | 		it('should work with any params passed', () => {
 61 | 			expect(() =>
 62 | 				provider.validateAuth({
 63 | 					apiKey: 'some-key',
 64 | 					baseURL: 'https://example.com'
 65 | 				})
 66 | 			).not.toThrow();
 67 | 		});
 68 | 	});
 69 | 
 70 | 	describe('getClient', () => {
 71 | 		it('should return a claude code client', () => {
 72 | 			const client = provider.getClient({});
 73 | 			expect(client).toBeDefined();
 74 | 			expect(typeof client).toBe('function');
 75 | 		});
 76 | 
 77 | 		it('should create client without API key or base URL', () => {
 78 | 			const client = provider.getClient({});
 79 | 			expect(client).toBeDefined();
 80 | 		});
 81 | 
 82 | 		it('should handle params even though they are not used', () => {
 83 | 			const client = provider.getClient({
 84 | 				baseURL: 'https://example.com',
 85 | 				apiKey: 'unused-key'
 86 | 			});
 87 | 			expect(client).toBeDefined();
 88 | 		});
 89 | 
 90 | 		it('should have languageModel and chat methods', () => {
 91 | 			const client = provider.getClient({});
 92 | 			expect(client.languageModel).toBeDefined();
 93 | 			expect(client.chat).toBeDefined();
 94 | 			expect(client.chat).toBe(client.languageModel);
 95 | 		});
 96 | 	});
 97 | 
 98 | 	describe('error handling', () => {
 99 | 		it('should handle client initialization errors', async () => {
100 | 			// Force an error by making createClaudeCode throw
101 | 			const { createClaudeCode } = await import(
102 | 				'../../../src/ai-providers/custom-sdk/claude-code/index.js'
103 | 			);
104 | 			createClaudeCode.mockImplementationOnce(() => {
105 | 				throw new Error('Mock initialization error');
106 | 			});
107 | 
108 | 			// Create a new provider instance to use the mocked createClaudeCode
109 | 			const errorProvider = new ClaudeCodeProvider();
110 | 			expect(() => errorProvider.getClient({})).toThrow(
111 | 				'Mock initialization error'
112 | 			);
113 | 		});
114 | 	});
115 | });
116 | 
```

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

```javascript
  1 | /**
  2 |  * tools/expand-all.js
  3 |  * Tool for expanding all pending tasks with subtasks
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	createErrorResponse,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { expandAllTasksDirect } from '../core/task-master-core.js';
 13 | import { findTasksPath } from '../core/utils/path-utils.js';
 14 | import { resolveTag } from '../../../scripts/modules/utils.js';
 15 | 
 16 | /**
 17 |  * Register the expandAll tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerExpandAllTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'expand_all',
 23 | 		description:
 24 | 			'Expand all pending tasks into subtasks based on complexity or defaults',
 25 | 		parameters: z.object({
 26 | 			num: z
 27 | 				.string()
 28 | 				.optional()
 29 | 				.describe(
 30 | 					'Target number of subtasks per task (uses complexity/defaults otherwise)'
 31 | 				),
 32 | 			research: z
 33 | 				.boolean()
 34 | 				.optional()
 35 | 				.describe(
 36 | 					'Enable research-backed subtask generation (e.g., using Perplexity)'
 37 | 				),
 38 | 			prompt: z
 39 | 				.string()
 40 | 				.optional()
 41 | 				.describe(
 42 | 					'Additional context to guide subtask generation for all tasks'
 43 | 				),
 44 | 			force: z
 45 | 				.boolean()
 46 | 				.optional()
 47 | 				.describe(
 48 | 					'Force regeneration of subtasks for tasks that already have them'
 49 | 				),
 50 | 			file: z
 51 | 				.string()
 52 | 				.optional()
 53 | 				.describe(
 54 | 					'Absolute path to the tasks file in the /tasks folder inside the project root (default: tasks/tasks.json)'
 55 | 				),
 56 | 			projectRoot: z
 57 | 				.string()
 58 | 				.optional()
 59 | 				.describe(
 60 | 					'Absolute path to the project root directory (derived from session if possible)'
 61 | 				),
 62 | 			tag: z.string().optional().describe('Tag context to operate on')
 63 | 		}),
 64 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 65 | 			try {
 66 | 				log.info(
 67 | 					`Tool expand_all execution started with args: ${JSON.stringify(args)}`
 68 | 				);
 69 | 
 70 | 				const resolvedTag = resolveTag({
 71 | 					projectRoot: args.projectRoot,
 72 | 					tag: args.tag
 73 | 				});
 74 | 				let tasksJsonPath;
 75 | 				try {
 76 | 					tasksJsonPath = findTasksPath(
 77 | 						{ projectRoot: args.projectRoot, file: args.file },
 78 | 						log
 79 | 					);
 80 | 					log.info(`Resolved tasks.json path: ${tasksJsonPath}`);
 81 | 				} catch (error) {
 82 | 					log.error(`Error finding tasks.json: ${error.message}`);
 83 | 					return createErrorResponse(
 84 | 						`Failed to find tasks.json: ${error.message}`
 85 | 					);
 86 | 				}
 87 | 
 88 | 				const result = await expandAllTasksDirect(
 89 | 					{
 90 | 						tasksJsonPath: tasksJsonPath,
 91 | 						num: args.num,
 92 | 						research: args.research,
 93 | 						prompt: args.prompt,
 94 | 						force: args.force,
 95 | 						projectRoot: args.projectRoot,
 96 | 						tag: resolvedTag
 97 | 					},
 98 | 					log,
 99 | 					{ session }
100 | 				);
101 | 
102 | 				return handleApiResult(
103 | 					result,
104 | 					log,
105 | 					'Error expanding all tasks',
106 | 					undefined,
107 | 					args.projectRoot
108 | 				);
109 | 			} catch (error) {
110 | 				log.error(
111 | 					`Unexpected error in expand_all tool execute: ${error.message}`
112 | 				);
113 | 				if (error.stack) {
114 | 					log.error(error.stack);
115 | 				}
116 | 				return createErrorResponse(
117 | 					`An unexpected error occurred: ${error.message}`
118 | 				);
119 | 			}
120 | 		})
121 | 	});
122 | }
123 | 
```

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

```javascript
 1 | import { jest } from '@jest/globals';
 2 | 
 3 | // Mock the base provider to avoid circular dependencies
 4 | jest.unstable_mockModule('../../src/ai-providers/base-provider.js', () => ({
 5 | 	BaseAIProvider: class {
 6 | 		constructor() {
 7 | 			this.name = 'Base Provider';
 8 | 		}
 9 | 		handleError(context, error) {
10 | 			throw error;
11 | 		}
12 | 	}
13 | }));
14 | 
15 | // Mock the claude-code SDK to simulate it not being installed
16 | jest.unstable_mockModule('@anthropic-ai/claude-code', () => {
17 | 	throw new Error("Cannot find module '@anthropic-ai/claude-code'");
18 | });
19 | 
20 | // Import after mocking
21 | const { ClaudeCodeProvider } = await import(
22 | 	'../../src/ai-providers/claude-code.js'
23 | );
24 | 
25 | describe('Claude Code Optional Dependency Integration', () => {
26 | 	describe('when @anthropic-ai/claude-code is not installed', () => {
27 | 		it('should allow provider instantiation', () => {
28 | 			// Provider should instantiate without error
29 | 			const provider = new ClaudeCodeProvider();
30 | 			expect(provider).toBeDefined();
31 | 			expect(provider.name).toBe('Claude Code');
32 | 		});
33 | 
34 | 		it('should allow client creation', () => {
35 | 			const provider = new ClaudeCodeProvider();
36 | 			// Client creation should work
37 | 			const client = provider.getClient({});
38 | 			expect(client).toBeDefined();
39 | 			expect(typeof client).toBe('function');
40 | 		});
41 | 
42 | 		it('should fail with clear error when trying to use the model', async () => {
43 | 			const provider = new ClaudeCodeProvider();
44 | 			const client = provider.getClient({});
45 | 			const model = client('opus');
46 | 
47 | 			// The actual usage should fail with the lazy loading error
48 | 			await expect(
49 | 				model.doGenerate({
50 | 					prompt: [{ role: 'user', content: 'Hello' }],
51 | 					mode: { type: 'regular' }
52 | 				})
53 | 			).rejects.toThrow(
54 | 				"Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider."
55 | 			);
56 | 		});
57 | 
58 | 		it('should provide helpful error message for streaming', async () => {
59 | 			const provider = new ClaudeCodeProvider();
60 | 			const client = provider.getClient({});
61 | 			const model = client('sonnet');
62 | 
63 | 			await expect(
64 | 				model.doStream({
65 | 					prompt: [{ role: 'user', content: 'Hello' }],
66 | 					mode: { type: 'regular' }
67 | 				})
68 | 			).rejects.toThrow(
69 | 				"Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider."
70 | 			);
71 | 		});
72 | 	});
73 | 
74 | 	describe('provider behavior', () => {
75 | 		it('should not require API key', () => {
76 | 			const provider = new ClaudeCodeProvider();
77 | 			// Should not throw
78 | 			expect(() => provider.validateAuth()).not.toThrow();
79 | 			expect(() => provider.validateAuth({ apiKey: null })).not.toThrow();
80 | 		});
81 | 
82 | 		it('should work with ai-services-unified when provider is configured', async () => {
83 | 			// This tests that the provider can be selected but will fail appropriately
84 | 			// when the actual model is used
85 | 			const provider = new ClaudeCodeProvider();
86 | 			expect(provider).toBeDefined();
87 | 
88 | 			// In real usage, ai-services-unified would:
89 | 			// 1. Get the provider instance (works)
90 | 			// 2. Call provider.getClient() (works)
91 | 			// 3. Create a model (works)
92 | 			// 4. Try to generate (fails with clear error)
93 | 		});
94 | 	});
95 | });
96 | 
```

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

```javascript
  1 | /**
  2 |  * tools/update-task.js
  3 |  * Tool to update a single task by ID with new information
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	createErrorResponse,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { updateTaskByIdDirect } from '../core/task-master-core.js';
 13 | import { findTasksPath } from '../core/utils/path-utils.js';
 14 | import { resolveTag } from '../../../scripts/modules/utils.js';
 15 | 
 16 | /**
 17 |  * Register the update-task tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerUpdateTaskTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'update_task',
 23 | 		description:
 24 | 			'Updates a single task by ID with new information or context provided in the prompt.',
 25 | 		parameters: z.object({
 26 | 			id: z
 27 | 				.string() // ID can be number or string like "1.2"
 28 | 				.describe(
 29 | 					"ID of the task (e.g., '15') to update. Subtasks are supported using the update-subtask tool."
 30 | 				),
 31 | 			prompt: z
 32 | 				.string()
 33 | 				.describe('New information or context to incorporate into the task'),
 34 | 			research: z
 35 | 				.boolean()
 36 | 				.optional()
 37 | 				.describe('Use Perplexity AI for research-backed updates'),
 38 | 			append: z
 39 | 				.boolean()
 40 | 				.optional()
 41 | 				.describe(
 42 | 					'Append timestamped information to task details instead of full update'
 43 | 				),
 44 | 			file: z.string().optional().describe('Absolute path to the tasks file'),
 45 | 			projectRoot: z
 46 | 				.string()
 47 | 				.describe('The directory of the project. Must be an absolute path.'),
 48 | 			tag: z.string().optional().describe('Tag context to operate on')
 49 | 		}),
 50 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 51 | 			const toolName = 'update_task';
 52 | 			try {
 53 | 				const resolvedTag = resolveTag({
 54 | 					projectRoot: args.projectRoot,
 55 | 					tag: args.tag
 56 | 				});
 57 | 				log.info(
 58 | 					`Executing ${toolName} tool with args: ${JSON.stringify(args)}`
 59 | 				);
 60 | 
 61 | 				let tasksJsonPath;
 62 | 				try {
 63 | 					tasksJsonPath = findTasksPath(
 64 | 						{ projectRoot: args.projectRoot, file: args.file },
 65 | 						log
 66 | 					);
 67 | 					log.info(`${toolName}: Resolved tasks path: ${tasksJsonPath}`);
 68 | 				} catch (error) {
 69 | 					log.error(`${toolName}: Error finding tasks.json: ${error.message}`);
 70 | 					return createErrorResponse(
 71 | 						`Failed to find tasks.json: ${error.message}`
 72 | 					);
 73 | 				}
 74 | 
 75 | 				// 3. Call Direct Function - Include projectRoot
 76 | 				const result = await updateTaskByIdDirect(
 77 | 					{
 78 | 						tasksJsonPath: tasksJsonPath,
 79 | 						id: args.id,
 80 | 						prompt: args.prompt,
 81 | 						research: args.research,
 82 | 						append: args.append,
 83 | 						projectRoot: args.projectRoot,
 84 | 						tag: resolvedTag
 85 | 					},
 86 | 					log,
 87 | 					{ session }
 88 | 				);
 89 | 
 90 | 				// 4. Handle Result
 91 | 				log.info(
 92 | 					`${toolName}: Direct function result: success=${result.success}`
 93 | 				);
 94 | 				return handleApiResult(
 95 | 					result,
 96 | 					log,
 97 | 					'Error updating task',
 98 | 					undefined,
 99 | 					args.projectRoot
100 | 				);
101 | 			} catch (error) {
102 | 				log.error(
103 | 					`Critical error in ${toolName} tool execute: ${error.message}`
104 | 				);
105 | 				return createErrorResponse(
106 | 					`Internal tool error (${toolName}): ${error.message}`
107 | 				);
108 | 			}
109 | 		})
110 | 	});
111 | }
112 | 
```

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

```json
  1 | {
  2 | 	"$schema": "http://json-schema.org/draft-07/schema#",
  3 | 	"$id": "https://github.com/eyaltoledano/claude-task-master/blob/main/src/prompts/schemas/prompt-template.schema.json",
  4 | 	"version": "1.0.0",
  5 | 	"title": "Task Master Prompt Template",
  6 | 	"description": "Schema for Task Master AI prompt template files",
  7 | 	"type": "object",
  8 | 	"required": ["id", "version", "description", "prompts"],
  9 | 	"properties": {
 10 | 		"id": {
 11 | 			"type": "string",
 12 | 			"pattern": "^[a-z0-9-]+$",
 13 | 			"description": "Unique identifier for the prompt template"
 14 | 		},
 15 | 		"version": {
 16 | 			"type": "string",
 17 | 			"pattern": "^\\d+\\.\\d+\\.\\d+$",
 18 | 			"description": "Semantic version of the prompt template"
 19 | 		},
 20 | 		"description": {
 21 | 			"type": "string",
 22 | 			"minLength": 1,
 23 | 			"description": "Brief description of what this prompt does"
 24 | 		},
 25 | 		"metadata": {
 26 | 			"$ref": "#/definitions/metadata"
 27 | 		},
 28 | 		"parameters": {
 29 | 			"type": "object",
 30 | 			"additionalProperties": {
 31 | 				"$ref": "#/definitions/parameter"
 32 | 			}
 33 | 		},
 34 | 		"prompts": {
 35 | 			"type": "object",
 36 | 			"properties": {
 37 | 				"default": {
 38 | 					"$ref": "#/definitions/promptVariant"
 39 | 				}
 40 | 			},
 41 | 			"additionalProperties": {
 42 | 				"$ref": "#/definitions/conditionalPromptVariant"
 43 | 			}
 44 | 		}
 45 | 	},
 46 | 	"definitions": {
 47 | 		"parameter": {
 48 | 			"type": "object",
 49 | 			"required": ["type", "description"],
 50 | 			"properties": {
 51 | 				"type": {
 52 | 					"type": "string",
 53 | 					"enum": ["string", "number", "boolean", "array", "object"]
 54 | 				},
 55 | 				"description": {
 56 | 					"type": "string",
 57 | 					"minLength": 1
 58 | 				},
 59 | 				"required": {
 60 | 					"type": "boolean",
 61 | 					"default": false
 62 | 				},
 63 | 				"default": {
 64 | 					"description": "Default value for optional parameters"
 65 | 				},
 66 | 				"enum": {
 67 | 					"type": "array",
 68 | 					"description": "Valid values for string parameters"
 69 | 				},
 70 | 				"pattern": {
 71 | 					"type": "string",
 72 | 					"description": "Regular expression pattern for string validation"
 73 | 				},
 74 | 				"minimum": {
 75 | 					"type": "number",
 76 | 					"description": "Minimum value for number parameters"
 77 | 				},
 78 | 				"maximum": {
 79 | 					"type": "number",
 80 | 					"description": "Maximum value for number parameters"
 81 | 				}
 82 | 			}
 83 | 		},
 84 | 		"promptVariant": {
 85 | 			"type": "object",
 86 | 			"required": ["system", "user"],
 87 | 			"properties": {
 88 | 				"system": {
 89 | 					"type": "string",
 90 | 					"minLength": 1
 91 | 				},
 92 | 				"user": {
 93 | 					"type": "string",
 94 | 					"minLength": 1
 95 | 				}
 96 | 			}
 97 | 		},
 98 | 		"conditionalPromptVariant": {
 99 | 			"allOf": [
100 | 				{ "$ref": "#/definitions/promptVariant" },
101 | 				{
102 | 					"type": "object",
103 | 					"properties": {
104 | 						"condition": {
105 | 							"type": "string",
106 | 							"description": "JavaScript expression for variant selection"
107 | 						}
108 | 					}
109 | 				}
110 | 			]
111 | 		},
112 | 		"metadata": {
113 | 			"type": "object",
114 | 			"properties": {
115 | 				"author": { "type": "string" },
116 | 				"created": { "type": "string", "format": "date-time" },
117 | 				"updated": { "type": "string", "format": "date-time" },
118 | 				"tags": {
119 | 					"type": "array",
120 | 					"items": { "type": "string" }
121 | 				},
122 | 				"category": {
123 | 					"type": "string",
124 | 					"enum": [
125 | 						"task",
126 | 						"analysis",
127 | 						"research",
128 | 						"parsing",
129 | 						"update",
130 | 						"expansion"
131 | 					]
132 | 				}
133 | 			}
134 | 		}
135 | 	}
136 | }
137 | 
```

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

```json
 1 | {
 2 | 	"id": "analyze-complexity",
 3 | 	"version": "1.0.0",
 4 | 	"description": "Analyze task complexity and generate expansion recommendations",
 5 | 	"metadata": {
 6 | 		"author": "system",
 7 | 		"created": "2024-01-01T00:00:00Z",
 8 | 		"updated": "2024-01-01T00:00:00Z",
 9 | 		"tags": ["analysis", "complexity", "expansion", "recommendations"]
10 | 	},
11 | 	"parameters": {
12 | 		"tasks": {
13 | 			"type": "array",
14 | 			"required": true,
15 | 			"description": "Array of tasks to analyze"
16 | 		},
17 | 		"gatheredContext": {
18 | 			"type": "string",
19 | 			"default": "",
20 | 			"description": "Additional project context"
21 | 		},
22 | 		"threshold": {
23 | 			"type": "number",
24 | 			"default": 5,
25 | 			"min": 1,
26 | 			"max": 10,
27 | 			"description": "Complexity threshold for expansion recommendation"
28 | 		},
29 | 		"useResearch": {
30 | 			"type": "boolean",
31 | 			"default": false,
32 | 			"description": "Use research mode for deeper analysis"
33 | 		},
34 | 		"hasCodebaseAnalysis": {
35 | 			"type": "boolean",
36 | 			"default": false,
37 | 			"description": "Whether codebase analysis is available"
38 | 		},
39 | 		"projectRoot": {
40 | 			"type": "string",
41 | 			"default": "",
42 | 			"description": "Project root path for context"
43 | 		}
44 | 	},
45 | 	"prompts": {
46 | 		"default": {
47 | 			"system": "You are an expert software architect and project manager analyzing task complexity. Respond only with the requested valid JSON array.",
48 | 			"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."
49 | 		}
50 | 	}
51 | }
52 | 
```

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

```javascript
  1 | /**
  2 |  * generate-task-files.js
  3 |  * Direct function implementation for generating task files from tasks.json
  4 |  */
  5 | 
  6 | import { generateTaskFiles } from '../../../../scripts/modules/task-manager.js';
  7 | import {
  8 | 	enableSilentMode,
  9 | 	disableSilentMode
 10 | } from '../../../../scripts/modules/utils.js';
 11 | 
 12 | /**
 13 |  * Direct function wrapper for generateTaskFiles with error handling.
 14 |  *
 15 |  * @param {Object} args - Command arguments containing tasksJsonPath and outputDir.
 16 |  * @param {string} args.tasksJsonPath - Path to the tasks.json file.
 17 |  * @param {string} args.outputDir - Path to the output directory.
 18 |  * @param {string} args.projectRoot - Project root path (for MCP/env fallback)
 19 |  * @param {string} args.tag - Tag for the task (optional)
 20 |  * @param {Object} log - Logger object.
 21 |  * @returns {Promise<Object>} - Result object with success status and data/error information.
 22 |  */
 23 | export async function generateTaskFilesDirect(args, log) {
 24 | 	// Destructure expected args
 25 | 	const { tasksJsonPath, outputDir, projectRoot, tag } = args;
 26 | 	try {
 27 | 		log.info(`Generating task files with args: ${JSON.stringify(args)}`);
 28 | 
 29 | 		// Check if paths were provided
 30 | 		if (!tasksJsonPath) {
 31 | 			const errorMessage = 'tasksJsonPath is required but was not provided.';
 32 | 			log.error(errorMessage);
 33 | 			return {
 34 | 				success: false,
 35 | 				error: { code: 'MISSING_ARGUMENT', message: errorMessage }
 36 | 			};
 37 | 		}
 38 | 		if (!outputDir) {
 39 | 			const errorMessage = 'outputDir is required but was not provided.';
 40 | 			log.error(errorMessage);
 41 | 			return {
 42 | 				success: false,
 43 | 				error: { code: 'MISSING_ARGUMENT', message: errorMessage }
 44 | 			};
 45 | 		}
 46 | 
 47 | 		// Use the provided paths
 48 | 		const tasksPath = tasksJsonPath;
 49 | 		const resolvedOutputDir = outputDir;
 50 | 
 51 | 		log.info(`Generating task files from ${tasksPath} to ${resolvedOutputDir}`);
 52 | 
 53 | 		// Execute core generateTaskFiles function in a separate try/catch
 54 | 		try {
 55 | 			// Enable silent mode to prevent logs from being written to stdout
 56 | 			enableSilentMode();
 57 | 
 58 | 			// Pass projectRoot and tag so the core respects context
 59 | 			generateTaskFiles(tasksPath, resolvedOutputDir, {
 60 | 				projectRoot,
 61 | 				tag,
 62 | 				mcpLog: log
 63 | 			});
 64 | 
 65 | 			// Restore normal logging after task generation
 66 | 			disableSilentMode();
 67 | 		} catch (genError) {
 68 | 			// Make sure to restore normal logging even if there's an error
 69 | 			disableSilentMode();
 70 | 
 71 | 			log.error(`Error in generateTaskFiles: ${genError.message}`);
 72 | 			return {
 73 | 				success: false,
 74 | 				error: { code: 'GENERATE_FILES_ERROR', message: genError.message }
 75 | 			};
 76 | 		}
 77 | 
 78 | 		// Return success with file paths
 79 | 		return {
 80 | 			success: true,
 81 | 			data: {
 82 | 				message: `Successfully generated task files`,
 83 | 				tasksPath: tasksPath,
 84 | 				outputDir: resolvedOutputDir,
 85 | 				taskFiles:
 86 | 					'Individual task files have been generated in the output directory'
 87 | 			}
 88 | 		};
 89 | 	} catch (error) {
 90 | 		// Make sure to restore normal logging if an outer error occurs
 91 | 		disableSilentMode();
 92 | 
 93 | 		log.error(`Error generating task files: ${error.message}`);
 94 | 		return {
 95 | 			success: false,
 96 | 			error: {
 97 | 				code: 'GENERATE_TASKS_ERROR',
 98 | 				message: error.message || 'Unknown error generating task files'
 99 | 			}
100 | 		};
101 | 	}
102 | }
103 | 
```

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

```yaml
  1 | name: Extension Release
  2 | 
  3 | on:
  4 |   push:
  5 |     tags:
  6 |       - "extension@*"
  7 | 
  8 | permissions:
  9 |   contents: write
 10 | 
 11 | concurrency: extension-release-${{ github.ref }}
 12 | 
 13 | jobs:
 14 |   publish-extension:
 15 |     runs-on: ubuntu-latest
 16 |     environment: extension-release
 17 |     steps:
 18 |       - uses: actions/checkout@v4
 19 | 
 20 |       - uses: actions/setup-node@v4
 21 |         with:
 22 |           node-version: 20
 23 | 
 24 |       - name: Cache node_modules
 25 |         uses: actions/cache@v4
 26 |         with:
 27 |           path: |
 28 |             node_modules
 29 |             */*/node_modules
 30 |           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
 31 |           restore-keys: |
 32 |             ${{ runner.os }}-node-
 33 | 
 34 |       - name: Install Extension Dependencies
 35 |         working-directory: apps/extension
 36 |         run: npm ci
 37 |         timeout-minutes: 5
 38 | 
 39 |       - name: Type Check Extension
 40 |         working-directory: apps/extension
 41 |         run: npm run check-types
 42 |         env:
 43 |           FORCE_COLOR: 1
 44 | 
 45 |       - name: Build Extension
 46 |         working-directory: apps/extension
 47 |         run: npm run build
 48 |         env:
 49 |           FORCE_COLOR: 1
 50 | 
 51 |       - name: Package Extension
 52 |         working-directory: apps/extension
 53 |         run: npm run package
 54 |         env:
 55 |           FORCE_COLOR: 1
 56 | 
 57 |       - name: Create VSIX Package
 58 |         working-directory: apps/extension/vsix-build
 59 |         run: npx vsce package --no-dependencies
 60 |         env:
 61 |           FORCE_COLOR: 1
 62 | 
 63 |       - name: Get VSIX filename
 64 |         id: vsix-info
 65 |         working-directory: apps/extension/vsix-build
 66 |         run: |
 67 |           VSIX_FILE=$(find . -maxdepth 1 -name "*.vsix" -type f | head -n1 | xargs basename)
 68 |           if [ -z "$VSIX_FILE" ]; then
 69 |             echo "Error: No VSIX file found"
 70 |             exit 1
 71 |           fi
 72 |           echo "vsix-filename=$VSIX_FILE" >> "$GITHUB_OUTPUT"
 73 |           echo "Found VSIX: $VSIX_FILE"
 74 | 
 75 |       - name: Publish to VS Code Marketplace
 76 |         working-directory: apps/extension/vsix-build
 77 |         run: npx vsce publish --packagePath "${{ steps.vsix-info.outputs.vsix-filename }}"
 78 |         env:
 79 |           VSCE_PAT: ${{ secrets.VSCE_PAT }}
 80 |           FORCE_COLOR: 1
 81 | 
 82 |       - name: Install Open VSX CLI
 83 |         run: npm install -g ovsx
 84 | 
 85 |       - name: Publish to Open VSX Registry
 86 |         working-directory: apps/extension/vsix-build
 87 |         run: ovsx publish "${{ steps.vsix-info.outputs.vsix-filename }}"
 88 |         env:
 89 |           OVSX_PAT: ${{ secrets.OVSX_PAT }}
 90 |           FORCE_COLOR: 1
 91 | 
 92 |       - name: Upload Build Artifacts
 93 |         uses: actions/upload-artifact@v4
 94 |         with:
 95 |           name: extension-release-${{ github.ref_name }}
 96 |           path: |
 97 |             apps/extension/vsix-build/*.vsix
 98 |             apps/extension/dist/
 99 |           retention-days: 90
100 | 
101 |   notify-success:
102 |     needs: publish-extension
103 |     if: success()
104 |     runs-on: ubuntu-latest
105 |     steps:
106 |       - name: Success Notification
107 |         run: |
108 |           echo "🎉 Extension ${{ github.ref_name }} successfully published!"
109 |           echo "📦 Available on VS Code Marketplace"
110 |           echo "🌍 Available on Open VSX Registry"
111 |           echo "🏷️ GitHub release created: ${{ github.ref_name }}"
```

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

```javascript
  1 | /**
  2 |  * Mock for move-task module
  3 |  * Provides mock implementations for testing scenarios
  4 |  */
  5 | 
  6 | // Mock the moveTask function from the core module
  7 | const mockMoveTask = jest
  8 | 	.fn()
  9 | 	.mockImplementation(
 10 | 		async (tasksPath, sourceId, destinationId, generateFiles, options) => {
 11 | 			// Simulate successful move operation
 12 | 			return {
 13 | 				success: true,
 14 | 				sourceId,
 15 | 				destinationId,
 16 | 				message: `Successfully moved task ${sourceId} to ${destinationId}`,
 17 | 				...options
 18 | 			};
 19 | 		}
 20 | 	);
 21 | 
 22 | // Mock the moveTaskDirect function
 23 | const mockMoveTaskDirect = jest
 24 | 	.fn()
 25 | 	.mockImplementation(async (args, log, context = {}) => {
 26 | 		// Validate required parameters
 27 | 		if (!args.sourceId) {
 28 | 			return {
 29 | 				success: false,
 30 | 				error: {
 31 | 					message: 'Source ID is required',
 32 | 					code: 'MISSING_SOURCE_ID'
 33 | 				}
 34 | 			};
 35 | 		}
 36 | 
 37 | 		if (!args.destinationId) {
 38 | 			return {
 39 | 				success: false,
 40 | 				error: {
 41 | 					message: 'Destination ID is required',
 42 | 					code: 'MISSING_DESTINATION_ID'
 43 | 				}
 44 | 			};
 45 | 		}
 46 | 
 47 | 		// Simulate successful move
 48 | 		return {
 49 | 			success: true,
 50 | 			data: {
 51 | 				sourceId: args.sourceId,
 52 | 				destinationId: args.destinationId,
 53 | 				message: `Successfully moved task/subtask ${args.sourceId} to ${args.destinationId}`,
 54 | 				tag: args.tag,
 55 | 				projectRoot: args.projectRoot
 56 | 			}
 57 | 		};
 58 | 	});
 59 | 
 60 | // Mock the moveTaskCrossTagDirect function
 61 | const mockMoveTaskCrossTagDirect = jest
 62 | 	.fn()
 63 | 	.mockImplementation(async (args, log, context = {}) => {
 64 | 		// Validate required parameters
 65 | 		if (!args.sourceIds) {
 66 | 			return {
 67 | 				success: false,
 68 | 				error: {
 69 | 					message: 'Source IDs are required',
 70 | 					code: 'MISSING_SOURCE_IDS'
 71 | 				}
 72 | 			};
 73 | 		}
 74 | 
 75 | 		if (!args.sourceTag) {
 76 | 			return {
 77 | 				success: false,
 78 | 				error: {
 79 | 					message: 'Source tag is required for cross-tag moves',
 80 | 					code: 'MISSING_SOURCE_TAG'
 81 | 				}
 82 | 			};
 83 | 		}
 84 | 
 85 | 		if (!args.targetTag) {
 86 | 			return {
 87 | 				success: false,
 88 | 				error: {
 89 | 					message: 'Target tag is required for cross-tag moves',
 90 | 					code: 'MISSING_TARGET_TAG'
 91 | 				}
 92 | 			};
 93 | 		}
 94 | 
 95 | 		if (args.sourceTag === args.targetTag) {
 96 | 			return {
 97 | 				success: false,
 98 | 				error: {
 99 | 					message: `Source and target tags are the same ("${args.sourceTag}")`,
100 | 					code: 'SAME_SOURCE_TARGET_TAG'
101 | 				}
102 | 			};
103 | 		}
104 | 
105 | 		// Simulate successful cross-tag move
106 | 		return {
107 | 			success: true,
108 | 			data: {
109 | 				sourceIds: args.sourceIds,
110 | 				sourceTag: args.sourceTag,
111 | 				targetTag: args.targetTag,
112 | 				message: `Successfully moved tasks ${args.sourceIds} from ${args.sourceTag} to ${args.targetTag}`,
113 | 				withDependencies: args.withDependencies || false,
114 | 				ignoreDependencies: args.ignoreDependencies || false
115 | 			}
116 | 		};
117 | 	});
118 | 
119 | // Mock the registerMoveTaskTool function
120 | const mockRegisterMoveTaskTool = jest.fn().mockImplementation((server) => {
121 | 	// Simulate tool registration
122 | 	server.addTool({
123 | 		name: 'move_task',
124 | 		description: 'Move a task or subtask to a new position',
125 | 		parameters: {},
126 | 		execute: jest.fn()
127 | 	});
128 | });
129 | 
130 | // Export the mock functions
131 | export {
132 | 	mockMoveTask,
133 | 	mockMoveTaskDirect,
134 | 	mockMoveTaskCrossTagDirect,
135 | 	mockRegisterMoveTaskTool
136 | };
137 | 
138 | // Default export for the main moveTask function
139 | export default mockMoveTask;
140 | 
```

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

```javascript
 1 | import fs from 'fs';
 2 | import path from 'path';
 3 | import { geminiProfile } from '../../../src/profiles/gemini.js';
 4 | 
 5 | describe('Gemini Profile Initialization Functionality', () => {
 6 | 	let geminiProfileContent;
 7 | 
 8 | 	beforeAll(() => {
 9 | 		const geminiJsPath = path.join(
10 | 			process.cwd(),
11 | 			'src',
12 | 			'profiles',
13 | 			'gemini.js'
14 | 		);
15 | 		geminiProfileContent = fs.readFileSync(geminiJsPath, 'utf8');
16 | 	});
17 | 
18 | 	test('gemini.js has correct profile configuration', () => {
19 | 		// Check for explicit, non-default values in the source file
20 | 		expect(geminiProfileContent).toContain("name: 'gemini'");
21 | 		expect(geminiProfileContent).toContain("displayName: 'Gemini'");
22 | 		expect(geminiProfileContent).toContain("url: 'codeassist.google'");
23 | 		expect(geminiProfileContent).toContain(
24 | 			"docsUrl: 'github.com/google-gemini/gemini-cli'"
25 | 		);
26 | 		expect(geminiProfileContent).toContain("profileDir: '.gemini'");
27 | 		expect(geminiProfileContent).toContain("rulesDir: '.'"); // non-default
28 | 		expect(geminiProfileContent).toContain("mcpConfigName: 'settings.json'"); // non-default
29 | 		expect(geminiProfileContent).toContain('includeDefaultRules: false'); // non-default
30 | 		expect(geminiProfileContent).toContain("'AGENTS.md': 'GEMINI.md'");
31 | 
32 | 		// Check the final computed properties on the profile object
33 | 		expect(geminiProfile.profileName).toBe('gemini');
34 | 		expect(geminiProfile.displayName).toBe('Gemini');
35 | 		expect(geminiProfile.profileDir).toBe('.gemini');
36 | 		expect(geminiProfile.rulesDir).toBe('.');
37 | 		expect(geminiProfile.mcpConfig).toBe(true); // computed from mcpConfigName
38 | 		expect(geminiProfile.mcpConfigName).toBe('settings.json');
39 | 		expect(geminiProfile.mcpConfigPath).toBe('.gemini/settings.json'); // computed
40 | 		expect(geminiProfile.includeDefaultRules).toBe(false);
41 | 		expect(geminiProfile.fileMap['AGENTS.md']).toBe('GEMINI.md');
42 | 	});
43 | 
44 | 	test('gemini.js has no lifecycle functions', () => {
45 | 		// Gemini profile should not have any lifecycle functions
46 | 		expect(geminiProfileContent).not.toContain('function onAddRulesProfile');
47 | 		expect(geminiProfileContent).not.toContain('function onRemoveRulesProfile');
48 | 		expect(geminiProfileContent).not.toContain(
49 | 			'function onPostConvertRulesProfile'
50 | 		);
51 | 		expect(geminiProfileContent).not.toContain('onAddRulesProfile:');
52 | 		expect(geminiProfileContent).not.toContain('onRemoveRulesProfile:');
53 | 		expect(geminiProfileContent).not.toContain('onPostConvertRulesProfile:');
54 | 	});
55 | 
56 | 	test('gemini.js uses custom MCP config name', () => {
57 | 		// Gemini uses settings.json instead of mcp.json
58 | 		expect(geminiProfileContent).toContain("mcpConfigName: 'settings.json'");
59 | 		// Should not contain mcp.json as a config value (comments are OK)
60 | 		expect(geminiProfileContent).not.toMatch(
61 | 			/mcpConfigName:\s*['"]mcp\.json['"]/
62 | 		);
63 | 	});
64 | 
65 | 	test('gemini.js has minimal implementation', () => {
66 | 		// Verify the profile is minimal (no extra functions or logic)
67 | 		const lines = geminiProfileContent.split('\n');
68 | 		const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
69 | 		// Should be around 16 lines (import, export, and profile definition)
70 | 		expect(nonEmptyLines.length).toBeLessThan(20);
71 | 	});
72 | });
73 | 
```

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

```typescript
  1 | /**
  2 |  * @fileoverview Configuration Loader Service
  3 |  * Responsible for loading configuration from various file sources
  4 |  */
  5 | 
  6 | import { promises as fs } from 'node:fs';
  7 | import path from 'node:path';
  8 | import type { PartialConfiguration } from '../../interfaces/configuration.interface.js';
  9 | import { DEFAULT_CONFIG_VALUES } from '../../interfaces/configuration.interface.js';
 10 | import {
 11 | 	ERROR_CODES,
 12 | 	TaskMasterError
 13 | } from '../../errors/task-master-error.js';
 14 | 
 15 | /**
 16 |  * ConfigLoader handles loading configuration from files
 17 |  * Single responsibility: File-based configuration loading
 18 |  */
 19 | export class ConfigLoader {
 20 | 	private localConfigPath: string;
 21 | 	private globalConfigPath: string;
 22 | 
 23 | 	constructor(projectRoot: string) {
 24 | 		this.localConfigPath = path.join(projectRoot, '.taskmaster', 'config.json');
 25 | 		this.globalConfigPath = path.join(
 26 | 			process.env.HOME || '',
 27 | 			'.taskmaster',
 28 | 			'config.json'
 29 | 		);
 30 | 	}
 31 | 
 32 | 	/**
 33 | 	 * Get default configuration values
 34 | 	 */
 35 | 	getDefaultConfig(): PartialConfiguration {
 36 | 		return {
 37 | 			models: {
 38 | 				main: DEFAULT_CONFIG_VALUES.MODELS.MAIN,
 39 | 				fallback: DEFAULT_CONFIG_VALUES.MODELS.FALLBACK
 40 | 			},
 41 | 			storage: {
 42 | 				type: DEFAULT_CONFIG_VALUES.STORAGE.TYPE,
 43 | 				encoding: DEFAULT_CONFIG_VALUES.STORAGE.ENCODING,
 44 | 				enableBackup: false,
 45 | 				maxBackups: DEFAULT_CONFIG_VALUES.STORAGE.MAX_BACKUPS,
 46 | 				enableCompression: false,
 47 | 				atomicOperations: true
 48 | 			},
 49 | 			version: DEFAULT_CONFIG_VALUES.VERSION
 50 | 		};
 51 | 	}
 52 | 
 53 | 	/**
 54 | 	 * Load local project configuration
 55 | 	 */
 56 | 	async loadLocalConfig(): Promise<PartialConfiguration | null> {
 57 | 		try {
 58 | 			const configData = await fs.readFile(this.localConfigPath, 'utf-8');
 59 | 			return JSON.parse(configData);
 60 | 		} catch (error: any) {
 61 | 			if (error.code === 'ENOENT') {
 62 | 				// File doesn't exist, return null
 63 | 				console.debug('No local config.json found, using defaults');
 64 | 				return null;
 65 | 			}
 66 | 			throw new TaskMasterError(
 67 | 				'Failed to load local configuration',
 68 | 				ERROR_CODES.CONFIG_ERROR,
 69 | 				{ configPath: this.localConfigPath },
 70 | 				error
 71 | 			);
 72 | 		}
 73 | 	}
 74 | 
 75 | 	/**
 76 | 	 * Load global user configuration
 77 | 	 * @future-implementation Full implementation pending
 78 | 	 */
 79 | 	async loadGlobalConfig(): Promise<PartialConfiguration | null> {
 80 | 		// TODO: Implement in future PR
 81 | 		// For now, return null to indicate no global config
 82 | 		return null;
 83 | 
 84 | 		// Future implementation:
 85 | 		// try {
 86 | 		//   const configData = await fs.readFile(this.globalConfigPath, 'utf-8');
 87 | 		//   return JSON.parse(configData);
 88 | 		// } catch (error: any) {
 89 | 		//   if (error.code === 'ENOENT') {
 90 | 		//     return null;
 91 | 		//   }
 92 | 		//   throw new TaskMasterError(
 93 | 		//     'Failed to load global configuration',
 94 | 		//     ERROR_CODES.CONFIG_ERROR,
 95 | 		//     { configPath: this.globalConfigPath },
 96 | 		//     error
 97 | 		//   );
 98 | 		// }
 99 | 	}
100 | 
101 | 	/**
102 | 	 * Check if local config exists
103 | 	 */
104 | 	async hasLocalConfig(): Promise<boolean> {
105 | 		try {
106 | 			await fs.access(this.localConfigPath);
107 | 			return true;
108 | 		} catch {
109 | 			return false;
110 | 		}
111 | 	}
112 | 
113 | 	/**
114 | 	 * Check if global config exists
115 | 	 */
116 | 	async hasGlobalConfig(): Promise<boolean> {
117 | 		try {
118 | 			await fs.access(this.globalConfigPath);
119 | 			return true;
120 | 		} catch {
121 | 			return false;
122 | 		}
123 | 	}
124 | }
125 | 
```

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

```javascript
  1 | /**
  2 |  * tools/parsePRD.js
  3 |  * Tool to parse PRD document and generate tasks
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	withNormalizedProjectRoot,
 10 | 	createErrorResponse,
 11 | 	checkProgressCapability
 12 | } from './utils.js';
 13 | import { parsePRDDirect } from '../core/task-master-core.js';
 14 | import {
 15 | 	PRD_FILE,
 16 | 	TASKMASTER_DOCS_DIR,
 17 | 	TASKMASTER_TASKS_FILE
 18 | } from '../../../src/constants/paths.js';
 19 | import { resolveTag } from '../../../scripts/modules/utils.js';
 20 | 
 21 | /**
 22 |  * Register the parse_prd tool
 23 |  * @param {Object} server - FastMCP server instance
 24 |  */
 25 | export function registerParsePRDTool(server) {
 26 | 	server.addTool({
 27 | 		name: 'parse_prd',
 28 | 		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.`,
 29 | 
 30 | 		parameters: z.object({
 31 | 			input: z
 32 | 				.string()
 33 | 				.optional()
 34 | 				.default(PRD_FILE)
 35 | 				.describe('Absolute path to the PRD document file (.txt, .md, etc.)'),
 36 | 			projectRoot: z
 37 | 				.string()
 38 | 				.describe('The directory of the project. Must be an absolute path.'),
 39 | 			tag: z.string().optional().describe('Tag context to operate on'),
 40 | 			output: z
 41 | 				.string()
 42 | 				.optional()
 43 | 				.describe(
 44 | 					`Output path for tasks.json file (default: ${TASKMASTER_TASKS_FILE})`
 45 | 				),
 46 | 			numTasks: z
 47 | 				.string()
 48 | 				.optional()
 49 | 				.describe(
 50 | 					'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.'
 51 | 				),
 52 | 			force: z
 53 | 				.boolean()
 54 | 				.optional()
 55 | 				.default(false)
 56 | 				.describe('Overwrite existing output file without prompting.'),
 57 | 			research: z
 58 | 				.boolean()
 59 | 				.optional()
 60 | 				.describe(
 61 | 					'Enable Taskmaster to use the research role for potentially more informed task generation. Requires appropriate API key.'
 62 | 				),
 63 | 			append: z
 64 | 				.boolean()
 65 | 				.optional()
 66 | 				.describe('Append generated tasks to existing file.')
 67 | 		}),
 68 | 		execute: withNormalizedProjectRoot(
 69 | 			async (args, { log, session, reportProgress }) => {
 70 | 				try {
 71 | 					const resolvedTag = resolveTag({
 72 | 						projectRoot: args.projectRoot,
 73 | 						tag: args.tag
 74 | 					});
 75 | 					const progressCapability = checkProgressCapability(
 76 | 						reportProgress,
 77 | 						log
 78 | 					);
 79 | 					const result = await parsePRDDirect(
 80 | 						{
 81 | 							...args,
 82 | 							tag: resolvedTag
 83 | 						},
 84 | 						log,
 85 | 						{ session, reportProgress: progressCapability }
 86 | 					);
 87 | 					return handleApiResult(
 88 | 						result,
 89 | 						log,
 90 | 						'Error parsing PRD',
 91 | 						undefined,
 92 | 						args.projectRoot
 93 | 					);
 94 | 				} catch (error) {
 95 | 					log.error(`Error in parse_prd: ${error.message}`);
 96 | 					return createErrorResponse(`Failed to parse PRD: ${error.message}`);
 97 | 				}
 98 | 			}
 99 | 		)
100 | 	});
101 | }
102 | 
```

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

```yaml
 1 | name: Pre-Release (RC)
 2 | 
 3 | on:
 4 |   workflow_dispatch: # Allows manual triggering from GitHub UI/API
 5 | 
 6 | concurrency: pre-release-${{ github.ref_name }}
 7 | jobs:
 8 |   rc:
 9 |     runs-on: ubuntu-latest
10 |     # Only allow pre-releases on non-main branches
11 |     if: github.ref != 'refs/heads/main'
12 |     environment: extension-release
13 |     steps:
14 |       - uses: actions/checkout@v4
15 |         with:
16 |           fetch-depth: 0
17 | 
18 |       - uses: actions/setup-node@v4
19 |         with:
20 |           node-version: 20
21 |           cache: "npm"
22 | 
23 |       - name: Cache node_modules
24 |         uses: actions/cache@v4
25 |         with:
26 |           path: |
27 |             node_modules
28 |             */*/node_modules
29 |           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
30 |           restore-keys: |
31 |             ${{ runner.os }}-node-
32 | 
33 |       - name: Install dependencies
34 |         run: npm ci
35 |         timeout-minutes: 2
36 | 
37 |       - name: Enter RC mode (if not already in RC mode)
38 |         run: |
39 |           # Check if we're in pre-release mode with the "rc" tag
40 |           if [ -f .changeset/pre.json ]; then
41 |             MODE=$(jq -r '.mode' .changeset/pre.json 2>/dev/null || echo '')
42 |             TAG=$(jq -r '.tag' .changeset/pre.json 2>/dev/null || echo '')
43 |             
44 |             if [ "$MODE" = "exit" ]; then
45 |               echo "Pre-release mode is in 'exit' state, re-entering RC mode..."
46 |               npx changeset pre enter rc
47 |             elif [ "$MODE" = "pre" ] && [ "$TAG" != "rc" ]; then
48 |               echo "In pre-release mode but with wrong tag ($TAG), switching to RC..."
49 |               npx changeset pre exit
50 |               npx changeset pre enter rc
51 |             elif [ "$MODE" = "pre" ] && [ "$TAG" = "rc" ]; then
52 |               echo "Already in RC pre-release mode"
53 |             else
54 |               echo "Unknown mode state: $MODE, entering RC mode..."
55 |               npx changeset pre enter rc
56 |             fi
57 |           else
58 |             echo "No pre.json found, entering RC mode..."
59 |             npx changeset pre enter rc
60 |           fi
61 | 
62 |       - name: Version RC packages
63 |         run: npx changeset version
64 |         env:
65 |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66 |           NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
67 | 
68 |       - name: Run format
69 |         run: npm run format
70 |         env:
71 |           FORCE_COLOR: 1
72 | 
73 |       - name: Build packages
74 |         run: npm run turbo:build
75 |         env:
76 |           NODE_ENV: production
77 |           FORCE_COLOR: 1
78 |           TM_PUBLIC_BASE_DOMAIN: ${{ secrets.TM_PUBLIC_BASE_DOMAIN }}
79 |           TM_PUBLIC_SUPABASE_URL: ${{ secrets.TM_PUBLIC_SUPABASE_URL }}
80 |           TM_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.TM_PUBLIC_SUPABASE_ANON_KEY }}
81 | 
82 |       - name: Create Release Candidate Pull Request or Publish Release Candidate to npm
83 |         uses: changesets/action@v1
84 |         with:
85 |           publish: npx changeset publish
86 |         env:
87 |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
88 |           NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
89 | 
90 |       - name: Commit & Push changes
91 |         uses: actions-js/push@master
92 |         with:
93 |           github_token: ${{ secrets.GITHUB_TOKEN }}
94 |           branch: ${{ github.ref }}
95 |           message: "chore: rc version bump"
96 | 
```

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

```typescript
  1 | /**
  2 |  * Error Boundary Component
  3 |  */
  4 | 
  5 | import React from 'react';
  6 | 
  7 | interface ErrorBoundaryState {
  8 | 	hasError: boolean;
  9 | 	error?: Error;
 10 | 	errorInfo?: React.ErrorInfo;
 11 | }
 12 | 
 13 | interface ErrorBoundaryProps {
 14 | 	children: React.ReactNode;
 15 | 	onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
 16 | }
 17 | 
 18 | export class ErrorBoundary extends React.Component<
 19 | 	ErrorBoundaryProps,
 20 | 	ErrorBoundaryState
 21 | > {
 22 | 	constructor(props: ErrorBoundaryProps) {
 23 | 		super(props);
 24 | 		this.state = { hasError: false };
 25 | 	}
 26 | 
 27 | 	static getDerivedStateFromError(error: Error): ErrorBoundaryState {
 28 | 		return { hasError: true, error };
 29 | 	}
 30 | 
 31 | 	componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
 32 | 		console.error('React Error Boundary caught:', error, errorInfo);
 33 | 
 34 | 		// Log to extension
 35 | 		if (this.props.onError) {
 36 | 			this.props.onError(error, errorInfo);
 37 | 		}
 38 | 
 39 | 		// Send error to extension for centralized handling
 40 | 		if (window.acquireVsCodeApi) {
 41 | 			const vscode = window.acquireVsCodeApi();
 42 | 			vscode.postMessage({
 43 | 				type: 'reactError',
 44 | 				data: {
 45 | 					message: error.message,
 46 | 					stack: error.stack,
 47 | 					componentStack: errorInfo.componentStack,
 48 | 					timestamp: Date.now()
 49 | 				}
 50 | 			});
 51 | 		}
 52 | 	}
 53 | 
 54 | 	render() {
 55 | 		if (this.state.hasError) {
 56 | 			return (
 57 | 				<div className="min-h-screen flex items-center justify-center bg-vscode-background">
 58 | 					<div className="max-w-md mx-auto text-center p-6">
 59 | 						<div className="w-16 h-16 mx-auto mb-4 text-red-400">
 60 | 							<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
 61 | 								<path
 62 | 									strokeLinecap="round"
 63 | 									strokeLinejoin="round"
 64 | 									strokeWidth={2}
 65 | 									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"
 66 | 								/>
 67 | 							</svg>
 68 | 						</div>
 69 | 						<h2 className="text-xl font-semibold text-vscode-foreground mb-2">
 70 | 							Something went wrong
 71 | 						</h2>
 72 | 						<p className="text-vscode-foreground/70 mb-4">
 73 | 							The Task Master Kanban board encountered an unexpected error.
 74 | 						</p>
 75 | 						<div className="space-y-2">
 76 | 							<button
 77 | 								onClick={() =>
 78 | 									this.setState({
 79 | 										hasError: false,
 80 | 										error: undefined,
 81 | 										errorInfo: undefined
 82 | 									})
 83 | 								}
 84 | 								className="w-full px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-md transition-colors"
 85 | 							>
 86 | 								Try Again
 87 | 							</button>
 88 | 							<button
 89 | 								onClick={() => window.location.reload()}
 90 | 								className="w-full px-4 py-2 bg-gray-500 hover:bg-gray-600 text-white rounded-md transition-colors"
 91 | 							>
 92 | 								Reload Extension
 93 | 							</button>
 94 | 						</div>
 95 | 						{this.state.error && (
 96 | 							<details className="mt-4 text-left">
 97 | 								<summary className="text-sm text-vscode-foreground/50 cursor-pointer">
 98 | 									Error Details
 99 | 								</summary>
100 | 								<pre className="mt-2 text-xs text-vscode-foreground/70 bg-vscode-input/30 p-2 rounded overflow-auto max-h-32">
101 | 									{this.state.error.message}
102 | 									{this.state.error.stack && `\n\n${this.state.error.stack}`}
103 | 								</pre>
104 | 							</details>
105 | 						)}
106 | 					</div>
107 | 				</div>
108 | 			);
109 | 		}
110 | 
111 | 		return this.props.children;
112 | 	}
113 | }
114 | 
```

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

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

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

```typescript
  1 | /**
  2 |  * Shared types for the webview application
  3 |  */
  4 | 
  5 | export interface TaskMasterTask {
  6 | 	id: string;
  7 | 	title: string;
  8 | 	description: string;
  9 | 	status: 'pending' | 'in-progress' | 'done' | 'deferred' | 'review';
 10 | 	priority: 'high' | 'medium' | 'low';
 11 | 	dependencies?: string[];
 12 | 	details?: string;
 13 | 	testStrategy?: string;
 14 | 	subtasks?: TaskMasterTask[];
 15 | 	complexityScore?: number;
 16 | }
 17 | 
 18 | export interface TaskUpdates {
 19 | 	title?: string;
 20 | 	description?: string;
 21 | 	details?: string;
 22 | 	priority?: TaskMasterTask['priority'];
 23 | 	testStrategy?: string;
 24 | 	dependencies?: string[];
 25 | }
 26 | 
 27 | export interface WebviewMessage {
 28 | 	type: string;
 29 | 	requestId?: string;
 30 | 	data?: any;
 31 | 	success?: boolean;
 32 | 	[key: string]: any;
 33 | }
 34 | 
 35 | export interface ToastNotification {
 36 | 	id: string;
 37 | 	type: 'success' | 'info' | 'warning' | 'error';
 38 | 	title: string;
 39 | 	message: string;
 40 | 	duration?: number;
 41 | }
 42 | 
 43 | export interface AppState {
 44 | 	tasks: TaskMasterTask[];
 45 | 	loading: boolean;
 46 | 	error?: string;
 47 | 	requestId: number;
 48 | 	isConnected: boolean;
 49 | 	connectionStatus: string;
 50 | 	editingTask?: { taskId: string | null; editData?: TaskMasterTask };
 51 | 	polling: {
 52 | 		isActive: boolean;
 53 | 		errorCount: number;
 54 | 		lastUpdate?: number;
 55 | 		isUserInteracting: boolean;
 56 | 		isOfflineMode: boolean;
 57 | 		reconnectAttempts: number;
 58 | 		maxReconnectAttempts: number;
 59 | 		lastSuccessfulConnection?: number;
 60 | 		connectionStatus: 'online' | 'offline' | 'reconnecting';
 61 | 	};
 62 | 	toastNotifications: ToastNotification[];
 63 | 	currentView: 'kanban' | 'task-details' | 'config';
 64 | 	selectedTaskId?: string;
 65 | 	// Tag-related state
 66 | 	currentTag: string;
 67 | 	availableTags: string[];
 68 | }
 69 | 
 70 | export type AppAction =
 71 | 	| { type: 'SET_TASKS'; payload: TaskMasterTask[] }
 72 | 	| { type: 'SET_LOADING'; payload: boolean }
 73 | 	| { type: 'SET_ERROR'; payload: string }
 74 | 	| { type: 'CLEAR_ERROR' }
 75 | 	| { type: 'INCREMENT_REQUEST_ID' }
 76 | 	| {
 77 | 			type: 'UPDATE_TASK_STATUS';
 78 | 			payload: { taskId: string; newStatus: TaskMasterTask['status'] };
 79 | 	  }
 80 | 	| {
 81 | 			type: 'UPDATE_TASK_CONTENT';
 82 | 			payload: { taskId: string; updates: TaskUpdates };
 83 | 	  }
 84 | 	| {
 85 | 			type: 'SET_CONNECTION_STATUS';
 86 | 			payload: { isConnected: boolean; status: string };
 87 | 	  }
 88 | 	| {
 89 | 			type: 'SET_EDITING_TASK';
 90 | 			payload: { taskId: string | null; editData?: TaskMasterTask };
 91 | 	  }
 92 | 	| {
 93 | 			type: 'SET_POLLING_STATUS';
 94 | 			payload: { isActive: boolean; errorCount?: number };
 95 | 	  }
 96 | 	| { type: 'SET_USER_INTERACTING'; payload: boolean }
 97 | 	| { type: 'TASKS_UPDATED_FROM_POLLING'; payload: TaskMasterTask[] }
 98 | 	| {
 99 | 			type: 'SET_NETWORK_STATUS';
100 | 			payload: {
101 | 				isOfflineMode: boolean;
102 | 				connectionStatus: 'online' | 'offline' | 'reconnecting';
103 | 				reconnectAttempts?: number;
104 | 				maxReconnectAttempts?: number;
105 | 				lastSuccessfulConnection?: number;
106 | 			};
107 | 	  }
108 | 	| { type: 'LOAD_CACHED_TASKS'; payload: TaskMasterTask[] }
109 | 	| { type: 'ADD_TOAST'; payload: ToastNotification }
110 | 	| { type: 'REMOVE_TOAST'; payload: string }
111 | 	| { type: 'CLEAR_ALL_TOASTS' }
112 | 	| { type: 'NAVIGATE_TO_TASK'; payload: string }
113 | 	| { type: 'NAVIGATE_TO_KANBAN' }
114 | 	| { type: 'NAVIGATE_TO_CONFIG' }
115 | 	| { type: 'SET_CURRENT_TAG'; payload: string }
116 | 	| { type: 'SET_AVAILABLE_TAGS'; payload: string[] }
117 | 	| {
118 | 			type: 'SET_TAG_DATA';
119 | 			payload: { currentTag: string; availableTags: string[] };
120 | 	  };
121 | 
```

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

```javascript
  1 | /**
  2 |  * tools/add-task.js
  3 |  * Tool to add a new task using AI
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	createErrorResponse,
  9 | 	handleApiResult,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { addTaskDirect } from '../core/task-master-core.js';
 13 | import { findTasksPath } from '../core/utils/path-utils.js';
 14 | import { resolveTag } from '../../../scripts/modules/utils.js';
 15 | 
 16 | /**
 17 |  * Register the addTask tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerAddTaskTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'add_task',
 23 | 		description: 'Add a new task using AI',
 24 | 		parameters: z.object({
 25 | 			prompt: z
 26 | 				.string()
 27 | 				.optional()
 28 | 				.describe(
 29 | 					'Description of the task to add (required if not using manual fields)'
 30 | 				),
 31 | 			title: z
 32 | 				.string()
 33 | 				.optional()
 34 | 				.describe('Task title (for manual task creation)'),
 35 | 			description: z
 36 | 				.string()
 37 | 				.optional()
 38 | 				.describe('Task description (for manual task creation)'),
 39 | 			details: z
 40 | 				.string()
 41 | 				.optional()
 42 | 				.describe('Implementation details (for manual task creation)'),
 43 | 			testStrategy: z
 44 | 				.string()
 45 | 				.optional()
 46 | 				.describe('Test strategy (for manual task creation)'),
 47 | 			dependencies: z
 48 | 				.string()
 49 | 				.optional()
 50 | 				.describe('Comma-separated list of task IDs this task depends on'),
 51 | 			priority: z
 52 | 				.string()
 53 | 				.optional()
 54 | 				.describe('Task priority (high, medium, low)'),
 55 | 			file: z
 56 | 				.string()
 57 | 				.optional()
 58 | 				.describe('Path to the tasks file (default: tasks/tasks.json)'),
 59 | 			projectRoot: z
 60 | 				.string()
 61 | 				.describe('The directory of the project. Must be an absolute path.'),
 62 | 			tag: z.string().optional().describe('Tag context to operate on'),
 63 | 			research: z
 64 | 				.boolean()
 65 | 				.optional()
 66 | 				.describe('Whether to use research capabilities for task creation')
 67 | 		}),
 68 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 69 | 			try {
 70 | 				log.info(`Starting add-task with args: ${JSON.stringify(args)}`);
 71 | 
 72 | 				const resolvedTag = resolveTag({
 73 | 					projectRoot: args.projectRoot,
 74 | 					tag: args.tag
 75 | 				});
 76 | 
 77 | 				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
 78 | 				let tasksJsonPath;
 79 | 				try {
 80 | 					tasksJsonPath = findTasksPath(
 81 | 						{ projectRoot: args.projectRoot, file: args.file },
 82 | 						log
 83 | 					);
 84 | 				} catch (error) {
 85 | 					log.error(`Error finding tasks.json: ${error.message}`);
 86 | 					return createErrorResponse(
 87 | 						`Failed to find tasks.json: ${error.message}`
 88 | 					);
 89 | 				}
 90 | 
 91 | 				// Call the direct functionP
 92 | 				const result = await addTaskDirect(
 93 | 					{
 94 | 						tasksJsonPath: tasksJsonPath,
 95 | 						prompt: args.prompt,
 96 | 						title: args.title,
 97 | 						description: args.description,
 98 | 						details: args.details,
 99 | 						testStrategy: args.testStrategy,
100 | 						dependencies: args.dependencies,
101 | 						priority: args.priority,
102 | 						research: args.research,
103 | 						projectRoot: args.projectRoot,
104 | 						tag: resolvedTag
105 | 					},
106 | 					log,
107 | 					{ session }
108 | 				);
109 | 
110 | 				return handleApiResult(
111 | 					result,
112 | 					log,
113 | 					'Error adding task',
114 | 					undefined,
115 | 					args.projectRoot
116 | 				);
117 | 			} catch (error) {
118 | 				log.error(`Error in add-task tool: ${error.message}`);
119 | 				return createErrorResponse(error.message);
120 | 			}
121 | 		})
122 | 	});
123 | }
124 | 
```

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

```javascript
 1 | import { jest } from '@jest/globals';
 2 | import fs from 'fs';
 3 | import path from 'path';
 4 | import os from 'os';
 5 | import { execSync } from 'child_process';
 6 | 
 7 | describe('Rules Files Inclusion in Package', () => {
 8 | 	// This test verifies that the required rules files are included in the final package
 9 | 
10 | 	test('package.json includes dist/** in the "files" array for bundled files', () => {
11 | 		// Read the package.json file
12 | 		const packageJsonPath = path.join(process.cwd(), 'package.json');
13 | 		const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
14 | 
15 | 		// Check if dist/** is included in the files array (which contains bundled output including assets)
16 | 		expect(packageJson.files).toContain('dist/**');
17 | 	});
18 | 
19 | 	test('source rules files exist in assets/rules directory', () => {
20 | 		// Verify that the actual rules files exist
21 | 		const rulesDir = path.join(process.cwd(), 'assets', 'rules');
22 | 		expect(fs.existsSync(rulesDir)).toBe(true);
23 | 
24 | 		// Check for the 4 files that currently exist
25 | 		const expectedFiles = [
26 | 			'dev_workflow.mdc',
27 | 			'taskmaster.mdc',
28 | 			'self_improve.mdc',
29 | 			'cursor_rules.mdc'
30 | 		];
31 | 
32 | 		expectedFiles.forEach((file) => {
33 | 			const filePath = path.join(rulesDir, file);
34 | 			expect(fs.existsSync(filePath)).toBe(true);
35 | 		});
36 | 	});
37 | 
38 | 	test('roo.js profile contains logic for Roo directory creation and file copying', () => {
39 | 		// Read the roo.js profile file
40 | 		const rooJsPath = path.join(process.cwd(), 'src', 'profiles', 'roo.js');
41 | 		const rooJsContent = fs.readFileSync(rooJsPath, 'utf8');
42 | 
43 | 		// Check for the main handler function
44 | 		expect(
45 | 			rooJsContent.includes('onAddRulesProfile(targetDir, assetsDir)')
46 | 		).toBe(true);
47 | 
48 | 		// Check for general recursive copy of assets/roocode
49 | 		expect(
50 | 			rooJsContent.includes('copyRecursiveSync(sourceDir, targetDir)')
51 | 		).toBe(true);
52 | 
53 | 		// Check for updated path handling
54 | 		expect(rooJsContent.includes("path.join(assetsDir, 'roocode')")).toBe(true);
55 | 
56 | 		// Check for .roomodes file copying logic (source and destination paths)
57 | 		expect(rooJsContent.includes("path.join(sourceDir, '.roomodes')")).toBe(
58 | 			true
59 | 		);
60 | 		expect(rooJsContent.includes("path.join(targetDir, '.roomodes')")).toBe(
61 | 			true
62 | 		);
63 | 
64 | 		// Check for mode-specific rule file copying logic
65 | 		expect(rooJsContent.includes('for (const mode of ROO_MODES)')).toBe(true);
66 | 		expect(
67 | 			rooJsContent.includes(
68 | 				'path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`)'
69 | 			)
70 | 		).toBe(true);
71 | 		expect(
72 | 			rooJsContent.includes(
73 | 				"path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`)"
74 | 			)
75 | 		).toBe(true);
76 | 
77 | 		// Check for import of ROO_MODES from profiles.js
78 | 		expect(
79 | 			rooJsContent.includes(
80 | 				"import { ROO_MODES } from '../constants/profiles.js'"
81 | 			)
82 | 		).toBe(true);
83 | 
84 | 		// Verify mode variable is used in the template strings (this confirms modes are being processed)
85 | 		expect(rooJsContent.includes('rules-${mode}')).toBe(true);
86 | 		expect(rooJsContent.includes('${mode}-rules')).toBe(true);
87 | 	});
88 | 
89 | 	test('source Roo files exist in assets directory', () => {
90 | 		// Verify that the source files for Roo integration exist
91 | 		expect(
92 | 			fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roo'))
93 | 		).toBe(true);
94 | 		expect(
95 | 			fs.existsSync(path.join(process.cwd(), 'assets', 'roocode', '.roomodes'))
96 | 		).toBe(true);
97 | 	});
98 | });
99 | 
```

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

```javascript
  1 | /**
  2 |  * tools/get-tasks.js
  3 |  * Tool to get all tasks from Task Master
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	createErrorResponse,
  9 | 	handleApiResult,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { listTasksDirect } from '../core/task-master-core.js';
 13 | import {
 14 | 	resolveTasksPath,
 15 | 	resolveComplexityReportPath
 16 | } from '../core/utils/path-utils.js';
 17 | 
 18 | import { resolveTag } from '../../../scripts/modules/utils.js';
 19 | 
 20 | /**
 21 |  * Register the getTasks tool with the MCP server
 22 |  * @param {Object} server - FastMCP server instance
 23 |  */
 24 | export function registerListTasksTool(server) {
 25 | 	server.addTool({
 26 | 		name: 'get_tasks',
 27 | 		description:
 28 | 			'Get all tasks from Task Master, optionally filtering by status and including subtasks.',
 29 | 		parameters: z.object({
 30 | 			status: z
 31 | 				.string()
 32 | 				.optional()
 33 | 				.describe(
 34 | 					"Filter tasks by status (e.g., 'pending', 'done') or multiple statuses separated by commas (e.g., 'blocked,deferred')"
 35 | 				),
 36 | 			withSubtasks: z
 37 | 				.boolean()
 38 | 				.optional()
 39 | 				.describe(
 40 | 					'Include subtasks nested within their parent tasks in the response'
 41 | 				),
 42 | 			file: z
 43 | 				.string()
 44 | 				.optional()
 45 | 				.describe(
 46 | 					'Path to the tasks file (relative to project root or absolute)'
 47 | 				),
 48 | 			complexityReport: z
 49 | 				.string()
 50 | 				.optional()
 51 | 				.describe(
 52 | 					'Path to the complexity report file (relative to project root or absolute)'
 53 | 				),
 54 | 			projectRoot: z
 55 | 				.string()
 56 | 				.describe('The directory of the project. Must be an absolute path.'),
 57 | 			tag: z.string().optional().describe('Tag context to operate on')
 58 | 		}),
 59 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 60 | 			try {
 61 | 				log.info(`Getting tasks with filters: ${JSON.stringify(args)}`);
 62 | 
 63 | 				const resolvedTag = resolveTag({
 64 | 					projectRoot: args.projectRoot,
 65 | 					tag: args.tag
 66 | 				});
 67 | 				// Resolve the path to tasks.json using new path utilities
 68 | 				let tasksJsonPath;
 69 | 				try {
 70 | 					tasksJsonPath = resolveTasksPath(args, log);
 71 | 				} catch (error) {
 72 | 					log.error(`Error finding tasks.json: ${error.message}`);
 73 | 					return createErrorResponse(
 74 | 						`Failed to find tasks.json: ${error.message}`
 75 | 					);
 76 | 				}
 77 | 
 78 | 				// Resolve the path to complexity report
 79 | 				let complexityReportPath;
 80 | 				try {
 81 | 					complexityReportPath = resolveComplexityReportPath(
 82 | 						{ ...args, tag: resolvedTag },
 83 | 						session
 84 | 					);
 85 | 				} catch (error) {
 86 | 					log.error(`Error finding complexity report: ${error.message}`);
 87 | 					// This is optional, so we don't fail the operation
 88 | 					complexityReportPath = null;
 89 | 				}
 90 | 
 91 | 				const result = await listTasksDirect(
 92 | 					{
 93 | 						tasksJsonPath: tasksJsonPath,
 94 | 						status: args.status,
 95 | 						withSubtasks: args.withSubtasks,
 96 | 						reportPath: complexityReportPath,
 97 | 						projectRoot: args.projectRoot,
 98 | 						tag: resolvedTag
 99 | 					},
100 | 					log,
101 | 					{ session }
102 | 				);
103 | 
104 | 				log.info(
105 | 					`Retrieved ${result.success ? result.data?.tasks?.length || 0 : 0} tasks`
106 | 				);
107 | 				return handleApiResult(
108 | 					result,
109 | 					log,
110 | 					'Error getting tasks',
111 | 					undefined,
112 | 					args.projectRoot
113 | 				);
114 | 			} catch (error) {
115 | 				log.error(`Error getting tasks: ${error.message}`);
116 | 				return createErrorResponse(error.message);
117 | 			}
118 | 		})
119 | 	});
120 | }
121 | 
122 | // We no longer need the formatTasksResponse function as we're returning raw JSON data
123 | 
```

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | import fs from 'fs';
  3 | import path from 'path';
  4 | import os from 'os';
  5 | 
  6 | // Mock external modules
  7 | jest.mock('child_process', () => ({
  8 | 	execSync: jest.fn()
  9 | }));
 10 | 
 11 | // Mock console methods
 12 | jest.mock('console', () => ({
 13 | 	log: jest.fn(),
 14 | 	info: jest.fn(),
 15 | 	warn: jest.fn(),
 16 | 	error: jest.fn(),
 17 | 	clear: jest.fn()
 18 | }));
 19 | 
 20 | describe('Codex Profile Integration', () => {
 21 | 	let tempDir;
 22 | 
 23 | 	beforeEach(() => {
 24 | 		jest.clearAllMocks();
 25 | 
 26 | 		// Create a temporary directory for testing
 27 | 		tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-master-test-'));
 28 | 
 29 | 		// Spy on fs methods
 30 | 		jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
 31 | 		jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
 32 | 			if (filePath.toString().includes('AGENTS.md')) {
 33 | 				return 'Sample AGENTS.md content for Codex integration';
 34 | 			}
 35 | 			return '{}';
 36 | 		});
 37 | 		jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
 38 | 		jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
 39 | 	});
 40 | 
 41 | 	afterEach(() => {
 42 | 		// Clean up the temporary directory
 43 | 		try {
 44 | 			fs.rmSync(tempDir, { recursive: true, force: true });
 45 | 		} catch (err) {
 46 | 			console.error(`Error cleaning up: ${err.message}`);
 47 | 		}
 48 | 	});
 49 | 
 50 | 	// Test function that simulates the Codex profile file copying behavior
 51 | 	function mockCreateCodexStructure() {
 52 | 		// Codex profile copies AGENTS.md to AGENTS.md in project root (same name)
 53 | 		const sourceContent = 'Sample AGENTS.md content for Codex integration';
 54 | 		fs.writeFileSync(path.join(tempDir, 'AGENTS.md'), sourceContent);
 55 | 	}
 56 | 
 57 | 	test('creates AGENTS.md file in project root', () => {
 58 | 		// Act
 59 | 		mockCreateCodexStructure();
 60 | 
 61 | 		// Assert
 62 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
 63 | 			path.join(tempDir, 'AGENTS.md'),
 64 | 			'Sample AGENTS.md content for Codex integration'
 65 | 		);
 66 | 	});
 67 | 
 68 | 	test('does not create any profile directories', () => {
 69 | 		// Act
 70 | 		mockCreateCodexStructure();
 71 | 
 72 | 		// Assert - Codex profile should not create any directories
 73 | 		// Only the temp directory creation calls should exist
 74 | 		const mkdirCalls = fs.mkdirSync.mock.calls.filter(
 75 | 			(call) => !call[0].includes('task-master-test-')
 76 | 		);
 77 | 		expect(mkdirCalls).toHaveLength(0);
 78 | 	});
 79 | 
 80 | 	test('does not create MCP configuration files', () => {
 81 | 		// Act
 82 | 		mockCreateCodexStructure();
 83 | 
 84 | 		// Assert - Codex profile should not create any MCP config files
 85 | 		const writeFileCalls = fs.writeFileSync.mock.calls;
 86 | 		const mcpConfigCalls = writeFileCalls.filter(
 87 | 			(call) =>
 88 | 				call[0].toString().includes('mcp.json') ||
 89 | 				call[0].toString().includes('mcp_settings.json')
 90 | 		);
 91 | 		expect(mcpConfigCalls).toHaveLength(0);
 92 | 	});
 93 | 
 94 | 	test('only creates the target integration guide file', () => {
 95 | 		// Act
 96 | 		mockCreateCodexStructure();
 97 | 
 98 | 		// Assert - Should only create AGENTS.md
 99 | 		const writeFileCalls = fs.writeFileSync.mock.calls;
100 | 		expect(writeFileCalls).toHaveLength(1);
101 | 		expect(writeFileCalls[0][0]).toBe(path.join(tempDir, 'AGENTS.md'));
102 | 	});
103 | 
104 | 	test('uses the same filename as source (AGENTS.md)', () => {
105 | 		// Act
106 | 		mockCreateCodexStructure();
107 | 
108 | 		// Assert - Codex should keep the same filename unlike Claude which renames it
109 | 		const writeFileCalls = fs.writeFileSync.mock.calls;
110 | 		expect(writeFileCalls[0][0]).toContain('AGENTS.md');
111 | 		expect(writeFileCalls[0][0]).not.toContain('CLAUDE.md');
112 | 	});
113 | });
114 | 
```

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | 
  3 | // Mock cli-progress factory before importing BaseProgressTracker
  4 | jest.unstable_mockModule(
  5 | 	'../../../src/progress/cli-progress-factory.js',
  6 | 	() => ({
  7 | 		newMultiBar: jest.fn(() => ({
  8 | 			create: jest.fn(() => ({
  9 | 				update: jest.fn()
 10 | 			})),
 11 | 			stop: jest.fn()
 12 | 		}))
 13 | 	})
 14 | );
 15 | 
 16 | const { newMultiBar } = await import(
 17 | 	'../../../src/progress/cli-progress-factory.js'
 18 | );
 19 | const { BaseProgressTracker } = await import(
 20 | 	'../../../src/progress/base-progress-tracker.js'
 21 | );
 22 | 
 23 | describe('BaseProgressTracker', () => {
 24 | 	let tracker;
 25 | 	let mockMultiBar;
 26 | 	let mockProgressBar;
 27 | 	let mockTimeTokensBar;
 28 | 
 29 | 	beforeEach(() => {
 30 | 		jest.clearAllMocks();
 31 | 		jest.useFakeTimers();
 32 | 
 33 | 		// Setup mocks
 34 | 		mockProgressBar = { update: jest.fn() };
 35 | 		mockTimeTokensBar = { update: jest.fn() };
 36 | 		mockMultiBar = {
 37 | 			create: jest
 38 | 				.fn()
 39 | 				.mockReturnValueOnce(mockTimeTokensBar)
 40 | 				.mockReturnValueOnce(mockProgressBar),
 41 | 			stop: jest.fn()
 42 | 		};
 43 | 		newMultiBar.mockReturnValue(mockMultiBar);
 44 | 
 45 | 		tracker = new BaseProgressTracker({ numUnits: 10, unitName: 'task' });
 46 | 	});
 47 | 
 48 | 	afterEach(() => {
 49 | 		jest.useRealTimers();
 50 | 	});
 51 | 
 52 | 	describe('cleanup', () => {
 53 | 		it('should stop and clear timer interval', () => {
 54 | 			tracker.start();
 55 | 			expect(tracker._timerInterval).toBeTruthy();
 56 | 
 57 | 			tracker.cleanup();
 58 | 			expect(tracker._timerInterval).toBeNull();
 59 | 		});
 60 | 
 61 | 		it('should stop and null multibar reference', () => {
 62 | 			tracker.start();
 63 | 			expect(tracker.multibar).toBeTruthy();
 64 | 
 65 | 			tracker.cleanup();
 66 | 			expect(mockMultiBar.stop).toHaveBeenCalled();
 67 | 			expect(tracker.multibar).toBeNull();
 68 | 		});
 69 | 
 70 | 		it('should null progress bar references', () => {
 71 | 			tracker.start();
 72 | 			expect(tracker.timeTokensBar).toBeTruthy();
 73 | 			expect(tracker.progressBar).toBeTruthy();
 74 | 
 75 | 			tracker.cleanup();
 76 | 			expect(tracker.timeTokensBar).toBeNull();
 77 | 			expect(tracker.progressBar).toBeNull();
 78 | 		});
 79 | 
 80 | 		it('should set finished state', () => {
 81 | 			tracker.start();
 82 | 			expect(tracker.isStarted).toBe(true);
 83 | 			expect(tracker.isFinished).toBe(false);
 84 | 
 85 | 			tracker.cleanup();
 86 | 			expect(tracker.isStarted).toBe(false);
 87 | 			expect(tracker.isFinished).toBe(true);
 88 | 		});
 89 | 
 90 | 		it('should handle cleanup when multibar.stop throws error', () => {
 91 | 			tracker.start();
 92 | 			mockMultiBar.stop.mockImplementation(() => {
 93 | 				throw new Error('Stop failed');
 94 | 			});
 95 | 
 96 | 			expect(() => tracker.cleanup()).not.toThrow();
 97 | 			expect(tracker.multibar).toBeNull();
 98 | 		});
 99 | 
100 | 		it('should be safe to call multiple times', () => {
101 | 			tracker.start();
102 | 
103 | 			tracker.cleanup();
104 | 			tracker.cleanup();
105 | 			tracker.cleanup();
106 | 
107 | 			expect(mockMultiBar.stop).toHaveBeenCalledTimes(1);
108 | 		});
109 | 
110 | 		it('should be safe to call without starting', () => {
111 | 			expect(() => tracker.cleanup()).not.toThrow();
112 | 			expect(tracker.multibar).toBeNull();
113 | 		});
114 | 	});
115 | 
116 | 	describe('stop vs cleanup', () => {
117 | 		it('stop should call cleanup and null multibar reference', () => {
118 | 			tracker.start();
119 | 			tracker.stop();
120 | 
121 | 			// stop() now calls cleanup() which nulls the multibar
122 | 			expect(tracker.multibar).toBeNull();
123 | 			expect(tracker.isFinished).toBe(true);
124 | 		});
125 | 
126 | 		it('cleanup should null multibar preventing getSummary', () => {
127 | 			tracker.start();
128 | 			tracker.cleanup();
129 | 
130 | 			expect(tracker.multibar).toBeNull();
131 | 			expect(tracker.isFinished).toBe(true);
132 | 		});
133 | 	});
134 | });
135 | 
```

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

```json
 1 | {
 2 | 	"meta": {
 3 | 		"generatedAt": "2025-06-13T06:52:00.611Z",
 4 | 		"tasksAnalyzed": 5,
 5 | 		"totalTasks": 5,
 6 | 		"analysisCount": 5,
 7 | 		"thresholdScore": 5,
 8 | 		"projectName": "Taskmaster",
 9 | 		"usedResearch": true
10 | 	},
11 | 	"complexityAnalysis": [
12 | 		{
13 | 			"taskId": 1,
14 | 			"taskTitle": "Setup Project Repository and Node.js Environment",
15 | 			"complexityScore": 4,
16 | 			"recommendedSubtasks": 6,
17 | 			"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.",
18 | 			"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."
19 | 		},
20 | 		{
21 | 			"taskId": 2,
22 | 			"taskTitle": "Implement Core Functionality and CLI Interface",
23 | 			"complexityScore": 7,
24 | 			"recommendedSubtasks": 7,
25 | 			"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.",
26 | 			"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."
27 | 		},
28 | 		{
29 | 			"taskId": 3,
30 | 			"taskTitle": "Implement Testing Suite and Validation",
31 | 			"complexityScore": 6,
32 | 			"recommendedSubtasks": 6,
33 | 			"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.",
34 | 			"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."
35 | 		},
36 | 		{
37 | 			"taskId": 4,
38 | 			"taskTitle": "Setup Node.js Project with CLI Interface",
39 | 			"complexityScore": 5,
40 | 			"recommendedSubtasks": 7,
41 | 			"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.",
42 | 			"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."
43 | 		},
44 | 		{
45 | 			"taskId": 5,
46 | 			"taskTitle": "Implement Core Functionality with Testing",
47 | 			"complexityScore": 8,
48 | 			"recommendedSubtasks": 8,
49 | 			"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.",
50 | 			"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."
51 | 		}
52 | 	]
53 | }
54 | 
```

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

```javascript
  1 | import { FastMCP } from 'fastmcp';
  2 | import path from 'path';
  3 | import dotenv from 'dotenv';
  4 | import { fileURLToPath } from 'url';
  5 | import fs from 'fs';
  6 | import logger from './logger.js';
  7 | import { registerTaskMasterTools } from './tools/index.js';
  8 | import ProviderRegistry from '../../src/provider-registry/index.js';
  9 | import { MCPProvider } from './providers/mcp-provider.js';
 10 | import packageJson from '../../package.json' with { type: 'json' };
 11 | 
 12 | // Load environment variables
 13 | dotenv.config();
 14 | 
 15 | // Constants
 16 | const __filename = fileURLToPath(import.meta.url);
 17 | const __dirname = path.dirname(__filename);
 18 | 
 19 | /**
 20 |  * Main MCP server class that integrates with Task Master
 21 |  */
 22 | class TaskMasterMCPServer {
 23 | 	constructor() {
 24 | 		this.options = {
 25 | 			name: 'Task Master MCP Server',
 26 | 			version: packageJson.version
 27 | 		};
 28 | 
 29 | 		this.server = new FastMCP(this.options);
 30 | 		this.initialized = false;
 31 | 
 32 | 		// Bind methods
 33 | 		this.init = this.init.bind(this);
 34 | 		this.start = this.start.bind(this);
 35 | 		this.stop = this.stop.bind(this);
 36 | 
 37 | 		// Setup logging
 38 | 		this.logger = logger;
 39 | 	}
 40 | 
 41 | 	/**
 42 | 	 * Initialize the MCP server with necessary tools and routes
 43 | 	 */
 44 | 	async init() {
 45 | 		if (this.initialized) return;
 46 | 
 47 | 		// Pass the manager instance to the tool registration function
 48 | 		registerTaskMasterTools(this.server, this.asyncManager);
 49 | 
 50 | 		this.initialized = true;
 51 | 
 52 | 		return this;
 53 | 	}
 54 | 
 55 | 	/**
 56 | 	 * Start the MCP server
 57 | 	 */
 58 | 	async start() {
 59 | 		if (!this.initialized) {
 60 | 			await this.init();
 61 | 		}
 62 | 
 63 | 		this.server.on('connect', (event) => {
 64 | 			event.session.server.sendLoggingMessage({
 65 | 				data: {
 66 | 					context: event.session.context,
 67 | 					message: `MCP Server connected: ${event.session.name}`
 68 | 				},
 69 | 				level: 'info'
 70 | 			});
 71 | 			this.registerRemoteProvider(event.session);
 72 | 		});
 73 | 
 74 | 		// Start the FastMCP server with increased timeout
 75 | 		await this.server.start({
 76 | 			transportType: 'stdio',
 77 | 			timeout: 120000 // 2 minutes timeout (in milliseconds)
 78 | 		});
 79 | 
 80 | 		return this;
 81 | 	}
 82 | 
 83 | 	/**
 84 | 	 * Register both MCP providers with the provider registry
 85 | 	 */
 86 | 	registerRemoteProvider(session) {
 87 | 		// Check if the server has at least one session
 88 | 		if (session) {
 89 | 			// Make sure session has required capabilities
 90 | 			if (!session.clientCapabilities || !session.clientCapabilities.sampling) {
 91 | 				session.server.sendLoggingMessage({
 92 | 					data: {
 93 | 						context: session.context,
 94 | 						message: `MCP session missing required sampling capabilities, providers not registered`
 95 | 					},
 96 | 					level: 'info'
 97 | 				});
 98 | 				return;
 99 | 			}
100 | 
101 | 			// Register MCP provider with the Provider Registry
102 | 
103 | 			// Register the unified MCP provider
104 | 			const mcpProvider = new MCPProvider();
105 | 			mcpProvider.setSession(session);
106 | 
107 | 			// Register provider with the registry
108 | 			const providerRegistry = ProviderRegistry.getInstance();
109 | 			providerRegistry.registerProvider('mcp', mcpProvider);
110 | 
111 | 			session.server.sendLoggingMessage({
112 | 				data: {
113 | 					context: session.context,
114 | 					message: `MCP Server connected`
115 | 				},
116 | 				level: 'info'
117 | 			});
118 | 		} else {
119 | 			session.server.sendLoggingMessage({
120 | 				data: {
121 | 					context: session.context,
122 | 					message: `No MCP sessions available, providers not registered`
123 | 				},
124 | 				level: 'warn'
125 | 			});
126 | 		}
127 | 	}
128 | 
129 | 	/**
130 | 	 * Stop the MCP server
131 | 	 */
132 | 	async stop() {
133 | 		if (this.server) {
134 | 			await this.server.stop();
135 | 		}
136 | 	}
137 | }
138 | 
139 | export default TaskMasterMCPServer;
140 | 
```
Page 8/52FirstPrevNextLast