#
tokens: 48820/50000 36/821 files (page 7/52)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 7 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

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

```javascript
 1 | import fs from 'fs';
 2 | import path from 'path';
 3 | import { clineProfile } from '../../../src/profiles/cline.js';
 4 | 
 5 | describe('Cline Profile Initialization Functionality', () => {
 6 | 	let clineProfileContent;
 7 | 
 8 | 	beforeAll(() => {
 9 | 		const clineJsPath = path.join(process.cwd(), 'src', 'profiles', 'cline.js');
10 | 		clineProfileContent = fs.readFileSync(clineJsPath, 'utf8');
11 | 	});
12 | 
13 | 	test('cline.js uses factory pattern with correct configuration', () => {
14 | 		// Check for explicit, non-default values in the source file
15 | 		expect(clineProfileContent).toContain("name: 'cline'");
16 | 		expect(clineProfileContent).toContain("displayName: 'Cline'");
17 | 		expect(clineProfileContent).toContain("profileDir: '.clinerules'"); // non-default
18 | 		expect(clineProfileContent).toContain("rulesDir: '.clinerules'"); // non-default
19 | 		expect(clineProfileContent).toContain('mcpConfig: false'); // non-default
20 | 
21 | 		// Check the final computed properties on the profile object
22 | 		expect(clineProfile.profileName).toBe('cline');
23 | 		expect(clineProfile.displayName).toBe('Cline');
24 | 		expect(clineProfile.profileDir).toBe('.clinerules');
25 | 		expect(clineProfile.rulesDir).toBe('.clinerules');
26 | 		expect(clineProfile.mcpConfig).toBe(false);
27 | 		expect(clineProfile.mcpConfigName).toBe(null);
28 | 	});
29 | 
30 | 	test('cline.js configures .mdc to .md extension mapping', () => {
31 | 		// Check that the profile object has the correct file mapping behavior (cline converts to .md)
32 | 		expect(clineProfile.fileMap['rules/cursor_rules.mdc']).toBe(
33 | 			'cline_rules.md'
34 | 		);
35 | 	});
36 | 
37 | 	test('cline.js uses standard tool mappings', () => {
38 | 		// Check that the profile uses default tool mappings (equivalent to COMMON_TOOL_MAPPINGS.STANDARD)
39 | 		// This verifies the architectural pattern: no custom toolMappings = standard tool names
40 | 		expect(clineProfileContent).not.toContain('toolMappings:');
41 | 		expect(clineProfileContent).not.toContain('apply_diff');
42 | 		expect(clineProfileContent).not.toContain('search_files');
43 | 
44 | 		// Verify the result: default mappings means tools keep their original names
45 | 		expect(clineProfile.conversionConfig.toolNames.edit_file).toBe('edit_file');
46 | 		expect(clineProfile.conversionConfig.toolNames.search).toBe('search');
47 | 	});
48 | 
49 | 	test('cline.js has custom file mapping for cursor_rules.mdc', () => {
50 | 		// Check actual behavior - cline gets default rule files
51 | 		expect(Object.keys(clineProfile.fileMap)).toContain(
52 | 			'rules/cursor_rules.mdc'
53 | 		);
54 | 		expect(clineProfile.fileMap['rules/cursor_rules.mdc']).toBe(
55 | 			'cline_rules.md'
56 | 		);
57 | 	});
58 | 
59 | 	test('cline.js uses createProfile factory function', () => {
60 | 		expect(clineProfileContent).toContain('createProfile');
61 | 		expect(clineProfileContent).toContain('export const clineProfile');
62 | 	});
63 | });
64 | 
```

--------------------------------------------------------------------------------
/packages/tm-core/src/executors/executor-service.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Service for managing task execution
  3 |  */
  4 | 
  5 | import type { Task } from '../types/index.js';
  6 | import type {
  7 | 	ITaskExecutor,
  8 | 	ExecutorOptions,
  9 | 	ExecutionResult,
 10 | 	ExecutorType
 11 | } from './types.js';
 12 | import { ExecutorFactory } from './executor-factory.js';
 13 | import { getLogger } from '../logger/index.js';
 14 | 
 15 | export interface ExecutorServiceOptions {
 16 | 	projectRoot: string;
 17 | 	defaultExecutor?: ExecutorType;
 18 | 	executorConfig?: Record<string, any>;
 19 | }
 20 | 
 21 | export class ExecutorService {
 22 | 	private logger = getLogger('ExecutorService');
 23 | 	private projectRoot: string;
 24 | 	private defaultExecutor?: ExecutorType;
 25 | 	private executorConfig: Record<string, any>;
 26 | 	private currentExecutor?: ITaskExecutor;
 27 | 
 28 | 	constructor(options: ExecutorServiceOptions) {
 29 | 		this.projectRoot = options.projectRoot;
 30 | 		this.defaultExecutor = options.defaultExecutor;
 31 | 		this.executorConfig = options.executorConfig || {};
 32 | 	}
 33 | 
 34 | 	/**
 35 | 	 * Execute a task
 36 | 	 */
 37 | 	async executeTask(
 38 | 		task: Task,
 39 | 		executorType?: ExecutorType
 40 | 	): Promise<ExecutionResult> {
 41 | 		try {
 42 | 			// Determine executor type
 43 | 			const type =
 44 | 				executorType ||
 45 | 				this.defaultExecutor ||
 46 | 				(await ExecutorFactory.getDefaultExecutor(this.projectRoot));
 47 | 			if (!type) {
 48 | 				return {
 49 | 					success: false,
 50 | 					taskId: task.id,
 51 | 					executorType: 'claude',
 52 | 					error:
 53 | 						'No executor available. Please install Claude CLI or specify an executor type.',
 54 | 					startTime: new Date().toISOString()
 55 | 				};
 56 | 			}
 57 | 
 58 | 			// Create executor
 59 | 			const executorOptions: ExecutorOptions = {
 60 | 				type,
 61 | 				projectRoot: this.projectRoot,
 62 | 				config: this.executorConfig
 63 | 			};
 64 | 
 65 | 			this.currentExecutor = ExecutorFactory.create(executorOptions);
 66 | 
 67 | 			// Check if executor is available
 68 | 			const isAvailable = await this.currentExecutor.isAvailable();
 69 | 			if (!isAvailable) {
 70 | 				return {
 71 | 					success: false,
 72 | 					taskId: task.id,
 73 | 					executorType: type,
 74 | 					error: `Executor ${type} is not available or not configured properly`,
 75 | 					startTime: new Date().toISOString()
 76 | 				};
 77 | 			}
 78 | 
 79 | 			// Execute the task
 80 | 			this.logger.info(`Starting task ${task.id} with ${type} executor`);
 81 | 			const result = await this.currentExecutor.execute(task);
 82 | 
 83 | 			return result;
 84 | 		} catch (error: any) {
 85 | 			this.logger.error(`Failed to execute task ${task.id}:`, error);
 86 | 			return {
 87 | 				success: false,
 88 | 				taskId: task.id,
 89 | 				executorType: executorType || 'claude',
 90 | 				error: error.message || 'Unknown error occurred',
 91 | 				startTime: new Date().toISOString()
 92 | 			};
 93 | 		}
 94 | 	}
 95 | 
 96 | 	/**
 97 | 	 * Stop the current task execution
 98 | 	 */
 99 | 	async stopCurrentTask(): Promise<void> {
100 | 		if (this.currentExecutor && this.currentExecutor.stop) {
101 | 			await this.currentExecutor.stop();
102 | 			this.currentExecutor = undefined;
103 | 		}
104 | 	}
105 | }
106 | 
```

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

```javascript
  1 | /**
  2 |  * tools/remove-task.js
  3 |  * Tool to remove a task by ID
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	createErrorResponse,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { removeTaskDirect } 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 remove-task tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerRemoveTaskTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'remove_task',
 23 | 		description: 'Remove a task or subtask permanently from the tasks list',
 24 | 		parameters: z.object({
 25 | 			id: z
 26 | 				.string()
 27 | 				.describe(
 28 | 					"ID of the task or subtask to remove (e.g., '5' or '5.2'). Can be comma-separated to update multiple tasks/subtasks at once."
 29 | 				),
 30 | 			file: z.string().optional().describe('Absolute path to the tasks file'),
 31 | 			projectRoot: z
 32 | 				.string()
 33 | 				.describe('The directory of the project. Must be an absolute path.'),
 34 | 			confirm: z
 35 | 				.boolean()
 36 | 				.optional()
 37 | 				.describe('Whether to skip confirmation prompt (default: false)'),
 38 | 			tag: z
 39 | 				.string()
 40 | 				.optional()
 41 | 				.describe(
 42 | 					'Specify which tag context to operate on. Defaults to the current active tag.'
 43 | 				)
 44 | 		}),
 45 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 46 | 			try {
 47 | 				log.info(`Removing task(s) with ID(s): ${args.id}`);
 48 | 
 49 | 				const resolvedTag = resolveTag({
 50 | 					projectRoot: args.projectRoot,
 51 | 					tag: args.tag
 52 | 				});
 53 | 
 54 | 				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
 55 | 				let tasksJsonPath;
 56 | 				try {
 57 | 					tasksJsonPath = findTasksPath(
 58 | 						{ projectRoot: args.projectRoot, file: args.file },
 59 | 						log
 60 | 					);
 61 | 				} catch (error) {
 62 | 					log.error(`Error finding tasks.json: ${error.message}`);
 63 | 					return createErrorResponse(
 64 | 						`Failed to find tasks.json: ${error.message}`
 65 | 					);
 66 | 				}
 67 | 
 68 | 				log.info(`Using tasks file path: ${tasksJsonPath}`);
 69 | 
 70 | 				const result = await removeTaskDirect(
 71 | 					{
 72 | 						tasksJsonPath: tasksJsonPath,
 73 | 						id: args.id,
 74 | 						projectRoot: args.projectRoot,
 75 | 						tag: resolvedTag
 76 | 					},
 77 | 					log,
 78 | 					{ session }
 79 | 				);
 80 | 
 81 | 				if (result.success) {
 82 | 					log.info(`Successfully removed task: ${args.id}`);
 83 | 				} else {
 84 | 					log.error(`Failed to remove task: ${result.error.message}`);
 85 | 				}
 86 | 
 87 | 				return handleApiResult(
 88 | 					result,
 89 | 					log,
 90 | 					'Error removing task',
 91 | 					undefined,
 92 | 					args.projectRoot
 93 | 				);
 94 | 			} catch (error) {
 95 | 				log.error(`Error in remove-task tool: ${error.message}`);
 96 | 				return createErrorResponse(`Failed to remove task: ${error.message}`);
 97 | 			}
 98 | 		})
 99 | 	});
100 | }
101 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/tools/scope-up.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tools/scope-up.js
  3 |  * Tool to scope up task complexity
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	createErrorResponse,
  9 | 	handleApiResult,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { scopeUpDirect } 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 scopeUp tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerScopeUpTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'scope_up_task',
 23 | 		description: 'Increase the complexity of one or more tasks using AI',
 24 | 		parameters: z.object({
 25 | 			id: z
 26 | 				.string()
 27 | 				.describe(
 28 | 					'Comma-separated list of task IDs to scope up (e.g., "1,3,5")'
 29 | 				),
 30 | 			strength: z
 31 | 				.string()
 32 | 				.optional()
 33 | 				.describe(
 34 | 					'Strength level: light, regular, or heavy (default: regular)'
 35 | 				),
 36 | 			prompt: z
 37 | 				.string()
 38 | 				.optional()
 39 | 				.describe('Custom prompt for specific scoping adjustments'),
 40 | 			file: z
 41 | 				.string()
 42 | 				.optional()
 43 | 				.describe('Path to the tasks file (default: tasks/tasks.json)'),
 44 | 			projectRoot: z
 45 | 				.string()
 46 | 				.describe('The directory of the project. Must be an absolute path.'),
 47 | 			tag: z.string().optional().describe('Tag context to operate on'),
 48 | 			research: z
 49 | 				.boolean()
 50 | 				.optional()
 51 | 				.describe('Whether to use research capabilities for scoping')
 52 | 		}),
 53 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 54 | 			try {
 55 | 				log.info(`Starting scope-up with args: ${JSON.stringify(args)}`);
 56 | 
 57 | 				const resolvedTag = resolveTag({
 58 | 					projectRoot: args.projectRoot,
 59 | 					tag: args.tag
 60 | 				});
 61 | 
 62 | 				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
 63 | 				let tasksJsonPath;
 64 | 				try {
 65 | 					tasksJsonPath = findTasksPath(
 66 | 						{ projectRoot: args.projectRoot, file: args.file },
 67 | 						log
 68 | 					);
 69 | 				} catch (error) {
 70 | 					log.error(`Error finding tasks.json: ${error.message}`);
 71 | 					return createErrorResponse(
 72 | 						`Failed to find tasks.json: ${error.message}`
 73 | 					);
 74 | 				}
 75 | 
 76 | 				// Call the direct function
 77 | 				const result = await scopeUpDirect(
 78 | 					{
 79 | 						tasksJsonPath: tasksJsonPath,
 80 | 						id: args.id,
 81 | 						strength: args.strength,
 82 | 						prompt: args.prompt,
 83 | 						research: args.research,
 84 | 						projectRoot: args.projectRoot,
 85 | 						tag: resolvedTag
 86 | 					},
 87 | 					log,
 88 | 					{ session }
 89 | 				);
 90 | 
 91 | 				return handleApiResult(
 92 | 					result,
 93 | 					log,
 94 | 					'Error scoping up task',
 95 | 					undefined,
 96 | 					args.projectRoot
 97 | 				);
 98 | 			} catch (error) {
 99 | 				log.error(`Error in scope-up tool: ${error.message}`);
100 | 				return createErrorResponse(error.message);
101 | 			}
102 | 		})
103 | 	});
104 | }
105 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/custom-sdk/message-converter.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * src/ai-providers/custom-sdk/mcp/message-converter.js
  3 |  *
  4 |  * Message conversion utilities for converting between AI SDK prompt format
  5 |  * and MCP sampling format.
  6 |  */
  7 | 
  8 | /**
  9 |  * Convert AI SDK prompt format to MCP sampling format
 10 |  * @param {Array} prompt - AI SDK prompt array
 11 |  * @returns {object} MCP format with messages and systemPrompt
 12 |  */
 13 | export function convertToMCPFormat(prompt) {
 14 | 	const messages = [];
 15 | 	let systemPrompt = '';
 16 | 
 17 | 	for (const message of prompt) {
 18 | 		if (message.role === 'system') {
 19 | 			// Extract system prompt
 20 | 			systemPrompt = extractTextContent(message.content);
 21 | 		} else if (message.role === 'user' || message.role === 'assistant') {
 22 | 			// Convert user/assistant messages
 23 | 			messages.push({
 24 | 				role: message.role,
 25 | 				content: {
 26 | 					type: 'text',
 27 | 					text: extractTextContent(message.content)
 28 | 				}
 29 | 			});
 30 | 		}
 31 | 	}
 32 | 
 33 | 	return {
 34 | 		messages,
 35 | 		systemPrompt
 36 | 	};
 37 | }
 38 | 
 39 | /**
 40 |  * Convert MCP response format to AI SDK format
 41 |  * @param {object} response - MCP sampling response
 42 |  * @returns {object} AI SDK compatible result
 43 |  */
 44 | export function convertFromMCPFormat(response) {
 45 | 	// Handle different possible response formats
 46 | 	let text = '';
 47 | 	let usage = null;
 48 | 	let finishReason = 'stop';
 49 | 	let warnings = [];
 50 | 
 51 | 	if (typeof response === 'string') {
 52 | 		text = response;
 53 | 	} else if (response.content) {
 54 | 		text = extractTextContent(response.content);
 55 | 		usage = response.usage;
 56 | 		finishReason = response.finishReason || 'stop';
 57 | 	} else if (response.text) {
 58 | 		text = response.text;
 59 | 		usage = response.usage;
 60 | 		finishReason = response.finishReason || 'stop';
 61 | 	} else {
 62 | 		// Fallback: try to extract text from response
 63 | 		text = JSON.stringify(response);
 64 | 		warnings.push('Unexpected MCP response format, used JSON fallback');
 65 | 	}
 66 | 
 67 | 	return {
 68 | 		text,
 69 | 		usage,
 70 | 		finishReason,
 71 | 		warnings
 72 | 	};
 73 | }
 74 | 
 75 | /**
 76 |  * Extract text content from various content formats
 77 |  * @param {string|Array|object} content - Content in various formats
 78 |  * @returns {string} Extracted text
 79 |  */
 80 | function extractTextContent(content) {
 81 | 	if (typeof content === 'string') {
 82 | 		return content;
 83 | 	}
 84 | 
 85 | 	if (Array.isArray(content)) {
 86 | 		// Handle array of content parts
 87 | 		return content
 88 | 			.map((part) => {
 89 | 				if (typeof part === 'string') {
 90 | 					return part;
 91 | 				}
 92 | 				if (part.type === 'text' && part.text) {
 93 | 					return part.text;
 94 | 				}
 95 | 				if (part.text) {
 96 | 					return part.text;
 97 | 				}
 98 | 				// Skip non-text content (images, etc.)
 99 | 				return '';
100 | 			})
101 | 			.filter((text) => text.length > 0)
102 | 			.join(' ');
103 | 	}
104 | 
105 | 	if (content && typeof content === 'object') {
106 | 		if (content.type === 'text' && content.text) {
107 | 			return content.text;
108 | 		}
109 | 		if (content.text) {
110 | 			return content.text;
111 | 		}
112 | 	}
113 | 
114 | 	// Fallback
115 | 	return String(content || '');
116 | }
117 | 
```

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

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

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

```javascript
 1 | /**
 2 |  * tools/add-dependency.js
 3 |  * Tool for adding a dependency to a task
 4 |  */
 5 | 
 6 | import { z } from 'zod';
 7 | import {
 8 | 	handleApiResult,
 9 | 	createErrorResponse,
10 | 	withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { addDependencyDirect } 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 addDependency tool with the MCP server
18 |  * @param {Object} server - FastMCP server instance
19 |  */
20 | export function registerAddDependencyTool(server) {
21 | 	server.addTool({
22 | 		name: 'add_dependency',
23 | 		description: 'Add a dependency relationship between two tasks',
24 | 		parameters: z.object({
25 | 			id: z.string().describe('ID of task that will depend on another task'),
26 | 			dependsOn: z
27 | 				.string()
28 | 				.describe('ID of task that will become a dependency'),
29 | 			file: z
30 | 				.string()
31 | 				.optional()
32 | 				.describe(
33 | 					'Absolute path to the tasks file (default: tasks/tasks.json)'
34 | 				),
35 | 			projectRoot: z
36 | 				.string()
37 | 				.describe('The directory of the project. Must be an absolute path.'),
38 | 			tag: z.string().optional().describe('Tag context to operate on')
39 | 		}),
40 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
41 | 			try {
42 | 				log.info(
43 | 					`Adding dependency for task ${args.id} to depend on ${args.dependsOn}`
44 | 				);
45 | 				const resolvedTag = resolveTag({
46 | 					projectRoot: args.projectRoot,
47 | 					tag: args.tag
48 | 				});
49 | 				let tasksJsonPath;
50 | 				try {
51 | 					tasksJsonPath = findTasksPath(
52 | 						{ projectRoot: args.projectRoot, file: args.file },
53 | 						log
54 | 					);
55 | 				} catch (error) {
56 | 					log.error(`Error finding tasks.json: ${error.message}`);
57 | 					return createErrorResponse(
58 | 						`Failed to find tasks.json: ${error.message}`
59 | 					);
60 | 				}
61 | 
62 | 				// Call the direct function with the resolved path
63 | 				const result = await addDependencyDirect(
64 | 					{
65 | 						// Pass the explicitly resolved path
66 | 						tasksJsonPath: tasksJsonPath,
67 | 						// Pass other relevant args
68 | 						id: args.id,
69 | 						dependsOn: args.dependsOn,
70 | 						projectRoot: args.projectRoot,
71 | 						tag: resolvedTag
72 | 					},
73 | 					log
74 | 					// Remove context object
75 | 				);
76 | 
77 | 				// Log result
78 | 				if (result.success) {
79 | 					log.info(`Successfully added dependency: ${result.data.message}`);
80 | 				} else {
81 | 					log.error(`Failed to add dependency: ${result.error.message}`);
82 | 				}
83 | 
84 | 				// Use handleApiResult to format the response
85 | 				return handleApiResult(
86 | 					result,
87 | 					log,
88 | 					'Error adding dependency',
89 | 					undefined,
90 | 					args.projectRoot
91 | 				);
92 | 			} catch (error) {
93 | 				log.error(`Error in addDependency tool: ${error.message}`);
94 | 				return createErrorResponse(error.message);
95 | 			}
96 | 		})
97 | 	});
98 | }
99 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/tools/clear-subtasks.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tools/clear-subtasks.js
  3 |  * Tool for clearing subtasks from parent tasks
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	createErrorResponse,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { clearSubtasksDirect } 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 clearSubtasks tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerClearSubtasksTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'clear_subtasks',
 23 | 		description: 'Clear subtasks from specified tasks',
 24 | 		parameters: z
 25 | 			.object({
 26 | 				id: z
 27 | 					.string()
 28 | 					.optional()
 29 | 					.describe('Task IDs (comma-separated) to clear subtasks from'),
 30 | 				all: z.boolean().optional().describe('Clear subtasks from all tasks'),
 31 | 				file: z
 32 | 					.string()
 33 | 					.optional()
 34 | 					.describe(
 35 | 						'Absolute path to the tasks file (default: tasks/tasks.json)'
 36 | 					),
 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 | 			.refine((data) => data.id || data.all, {
 43 | 				message: "Either 'id' or 'all' parameter must be provided",
 44 | 				path: ['id', 'all']
 45 | 			}),
 46 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 47 | 			try {
 48 | 				log.info(`Clearing subtasks with args: ${JSON.stringify(args)}`);
 49 | 
 50 | 				const resolvedTag = resolveTag({
 51 | 					projectRoot: args.projectRoot,
 52 | 					tag: args.tag
 53 | 				});
 54 | 
 55 | 				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
 56 | 				let tasksJsonPath;
 57 | 				try {
 58 | 					tasksJsonPath = findTasksPath(
 59 | 						{ projectRoot: args.projectRoot, file: args.file },
 60 | 						log
 61 | 					);
 62 | 				} catch (error) {
 63 | 					log.error(`Error finding tasks.json: ${error.message}`);
 64 | 					return createErrorResponse(
 65 | 						`Failed to find tasks.json: ${error.message}`
 66 | 					);
 67 | 				}
 68 | 
 69 | 				const result = await clearSubtasksDirect(
 70 | 					{
 71 | 						tasksJsonPath: tasksJsonPath,
 72 | 						id: args.id,
 73 | 						all: args.all,
 74 | 
 75 | 						projectRoot: args.projectRoot,
 76 | 						tag: resolvedTag
 77 | 					},
 78 | 					log,
 79 | 					{ session }
 80 | 				);
 81 | 
 82 | 				if (result.success) {
 83 | 					log.info(`Subtasks cleared successfully: ${result.data.message}`);
 84 | 				} else {
 85 | 					log.error(`Failed to clear subtasks: ${result.error.message}`);
 86 | 				}
 87 | 
 88 | 				return handleApiResult(
 89 | 					result,
 90 | 					log,
 91 | 					'Error clearing subtasks',
 92 | 					undefined,
 93 | 					args.projectRoot
 94 | 				);
 95 | 			} catch (error) {
 96 | 				log.error(`Error in clearSubtasks tool: ${error.message}`);
 97 | 				return createErrorResponse(error.message);
 98 | 			}
 99 | 		})
100 | 	});
101 | }
102 | 
```

--------------------------------------------------------------------------------
/tests/unit/scripts/modules/utils-tag-aware-paths.test.js:
--------------------------------------------------------------------------------

```javascript
 1 | /**
 2 |  * Test for getTagAwareFilePath utility function
 3 |  * Tests the fix for Issue #850
 4 |  */
 5 | 
 6 | import { getTagAwareFilePath } from '../../../../scripts/modules/utils.js';
 7 | import path from 'path';
 8 | 
 9 | describe('getTagAwareFilePath utility function', () => {
10 | 	const projectRoot = '/test/project';
11 | 	const basePath = '.taskmaster/reports/task-complexity-report.json';
12 | 
13 | 	it('should return base path for master tag', () => {
14 | 		const result = getTagAwareFilePath(basePath, 'master', projectRoot);
15 | 		const expected = path.join(projectRoot, basePath);
16 | 		expect(result).toBe(expected);
17 | 	});
18 | 
19 | 	it('should return base path for null tag', () => {
20 | 		const result = getTagAwareFilePath(basePath, null, projectRoot);
21 | 		const expected = path.join(projectRoot, basePath);
22 | 		expect(result).toBe(expected);
23 | 	});
24 | 
25 | 	it('should return base path for undefined tag', () => {
26 | 		const result = getTagAwareFilePath(basePath, undefined, projectRoot);
27 | 		const expected = path.join(projectRoot, basePath);
28 | 		expect(result).toBe(expected);
29 | 	});
30 | 
31 | 	it('should return tag-specific path for non-master tag', () => {
32 | 		const tag = 'feature-branch';
33 | 		const result = getTagAwareFilePath(basePath, tag, projectRoot);
34 | 		const expected = path.join(
35 | 			projectRoot,
36 | 			'.taskmaster/reports/task-complexity-report_feature-branch.json'
37 | 		);
38 | 		expect(result).toBe(expected);
39 | 	});
40 | 
41 | 	it('should handle different file extensions', () => {
42 | 		const csvBasePath = '.taskmaster/reports/export.csv';
43 | 		const tag = 'dev-branch';
44 | 		const result = getTagAwareFilePath(csvBasePath, tag, projectRoot);
45 | 		const expected = path.join(
46 | 			projectRoot,
47 | 			'.taskmaster/reports/export_dev-branch.csv'
48 | 		);
49 | 		expect(result).toBe(expected);
50 | 	});
51 | 
52 | 	it('should handle paths without extensions', () => {
53 | 		const noExtPath = '.taskmaster/reports/summary';
54 | 		const tag = 'test-tag';
55 | 		const result = getTagAwareFilePath(noExtPath, tag, projectRoot);
56 | 		// Since there's no extension, it should append the tag
57 | 		const expected = path.join(
58 | 			projectRoot,
59 | 			'.taskmaster/reports/summary_test-tag'
60 | 		);
61 | 		expect(result).toBe(expected);
62 | 	});
63 | 
64 | 	it('should use default project root when not provided', () => {
65 | 		const tag = 'feature-tag';
66 | 		const result = getTagAwareFilePath(basePath, tag);
67 | 		const expected = path.join(
68 | 			'.',
69 | 			'.taskmaster/reports/task-complexity-report_feature-tag.json'
70 | 		);
71 | 		expect(result).toBe(expected);
72 | 	});
73 | 
74 | 	it('should handle complex tag names with special characters', () => {
75 | 		const tag = 'feature-user-auth-v2';
76 | 		const result = getTagAwareFilePath(basePath, tag, projectRoot);
77 | 		const expected = path.join(
78 | 			projectRoot,
79 | 			'.taskmaster/reports/task-complexity-report_feature-user-auth-v2.json'
80 | 		);
81 | 		expect(result).toBe(expected);
82 | 	});
83 | });
84 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/tools/scope-down.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tools/scope-down.js
  3 |  * Tool to scope down task complexity
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	createErrorResponse,
  9 | 	handleApiResult,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { scopeDownDirect } 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 scopeDown tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerScopeDownTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'scope_down_task',
 23 | 		description: 'Decrease the complexity of one or more tasks using AI',
 24 | 		parameters: z.object({
 25 | 			id: z
 26 | 				.string()
 27 | 				.describe(
 28 | 					'Comma-separated list of task IDs to scope down (e.g., "1,3,5")'
 29 | 				),
 30 | 			strength: z
 31 | 				.string()
 32 | 				.optional()
 33 | 				.describe(
 34 | 					'Strength level: light, regular, or heavy (default: regular)'
 35 | 				),
 36 | 			prompt: z
 37 | 				.string()
 38 | 				.optional()
 39 | 				.describe('Custom prompt for specific scoping adjustments'),
 40 | 			file: z
 41 | 				.string()
 42 | 				.optional()
 43 | 				.describe('Path to the tasks file (default: tasks/tasks.json)'),
 44 | 			projectRoot: z
 45 | 				.string()
 46 | 				.describe('The directory of the project. Must be an absolute path.'),
 47 | 			tag: z.string().optional().describe('Tag context to operate on'),
 48 | 			research: z
 49 | 				.boolean()
 50 | 				.optional()
 51 | 				.describe('Whether to use research capabilities for scoping')
 52 | 		}),
 53 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 54 | 			try {
 55 | 				log.info(`Starting scope-down with args: ${JSON.stringify(args)}`);
 56 | 
 57 | 				const resolvedTag = resolveTag({
 58 | 					projectRoot: args.projectRoot,
 59 | 					tag: args.tag
 60 | 				});
 61 | 
 62 | 				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
 63 | 				let tasksJsonPath;
 64 | 				try {
 65 | 					tasksJsonPath = findTasksPath(
 66 | 						{ projectRoot: args.projectRoot, file: args.file },
 67 | 						log
 68 | 					);
 69 | 				} catch (error) {
 70 | 					log.error(`Error finding tasks.json: ${error.message}`);
 71 | 					return createErrorResponse(
 72 | 						`Failed to find tasks.json: ${error.message}`
 73 | 					);
 74 | 				}
 75 | 
 76 | 				// Call the direct function
 77 | 				const result = await scopeDownDirect(
 78 | 					{
 79 | 						tasksJsonPath: tasksJsonPath,
 80 | 						id: args.id,
 81 | 						strength: args.strength,
 82 | 						prompt: args.prompt,
 83 | 						research: args.research,
 84 | 						projectRoot: args.projectRoot,
 85 | 						tag: resolvedTag
 86 | 					},
 87 | 					log,
 88 | 					{ session }
 89 | 				);
 90 | 
 91 | 				return handleApiResult(
 92 | 					result,
 93 | 					log,
 94 | 					'Error scoping down task',
 95 | 					undefined,
 96 | 					args.projectRoot
 97 | 				);
 98 | 			} catch (error) {
 99 | 				log.error(`Error in scope-down tool: ${error.message}`);
100 | 				return createErrorResponse(error.message);
101 | 			}
102 | 		})
103 | 	});
104 | }
105 | 
```

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

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

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

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

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | 
  3 | // --- Mocks ---
  4 | // Only mock the specific functions that move-task actually uses
  5 | jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
  6 | 	readJSON: jest.fn(),
  7 | 	writeJSON: jest.fn(),
  8 | 	log: jest.fn(),
  9 | 	setTasksForTag: jest.fn(),
 10 | 	traverseDependencies: jest.fn(() => [])
 11 | }));
 12 | 
 13 | jest.unstable_mockModule(
 14 | 	'../../../../../scripts/modules/task-manager/generate-task-files.js',
 15 | 	() => ({
 16 | 		default: jest.fn().mockResolvedValue()
 17 | 	})
 18 | );
 19 | 
 20 | jest.unstable_mockModule(
 21 | 	'../../../../../scripts/modules/task-manager/is-task-dependent.js',
 22 | 	() => ({
 23 | 		default: jest.fn(() => false)
 24 | 	})
 25 | );
 26 | 
 27 | jest.unstable_mockModule(
 28 | 	'../../../../../scripts/modules/dependency-manager.js',
 29 | 	() => ({
 30 | 		findCrossTagDependencies: jest.fn(() => []),
 31 | 		getDependentTaskIds: jest.fn(() => []),
 32 | 		validateSubtaskMove: jest.fn()
 33 | 	})
 34 | );
 35 | 
 36 | const { readJSON, writeJSON, log } = await import(
 37 | 	'../../../../../scripts/modules/utils.js'
 38 | );
 39 | const generateTaskFiles = (
 40 | 	await import(
 41 | 		'../../../../../scripts/modules/task-manager/generate-task-files.js'
 42 | 	)
 43 | ).default;
 44 | 
 45 | const { default: moveTask } = await import(
 46 | 	'../../../../../scripts/modules/task-manager/move-task.js'
 47 | );
 48 | 
 49 | const sampleTagged = () => ({
 50 | 	master: {
 51 | 		tasks: [
 52 | 			{ id: 1, title: 'A' },
 53 | 			{ id: 2, title: 'B', subtasks: [{ id: 1, title: 'B.1' }] }
 54 | 		],
 55 | 		metadata: {}
 56 | 	},
 57 | 	feature: {
 58 | 		tasks: [{ id: 10, title: 'X' }],
 59 | 		metadata: {}
 60 | 	}
 61 | });
 62 | 
 63 | const clone = () => JSON.parse(JSON.stringify(sampleTagged()));
 64 | 
 65 | describe('moveTask (unit)', () => {
 66 | 	beforeEach(() => {
 67 | 		jest.clearAllMocks();
 68 | 		readJSON.mockImplementation((path, projectRoot, tag) => {
 69 | 			const data = clone();
 70 | 			return { ...data[tag], tag, _rawTaggedData: data };
 71 | 		});
 72 | 		writeJSON.mockResolvedValue();
 73 | 		log.mockImplementation(() => {});
 74 | 	});
 75 | 
 76 | 	test('moves task to new ID in same tag', async () => {
 77 | 		await moveTask('tasks.json', '1', '3', false, { tag: 'master' });
 78 | 		expect(writeJSON).toHaveBeenCalled();
 79 | 		const written = writeJSON.mock.calls[0][1];
 80 | 		const ids = written.master.tasks.map((t) => t.id);
 81 | 		expect(ids).toEqual(expect.arrayContaining([2, 3]));
 82 | 		expect(ids).not.toContain(1);
 83 | 	});
 84 | 
 85 | 	test('throws when counts of source and dest mismatch', async () => {
 86 | 		await expect(
 87 | 			moveTask('tasks.json', '1,2', '3', {}, { tag: 'master' })
 88 | 		).rejects.toThrow(/Number of source IDs/);
 89 | 	});
 90 | 
 91 | 	test('batch move calls generateTaskFiles once when flag true', async () => {
 92 | 		await moveTask('tasks.json', '1,2', '3,4', true, { tag: 'master' });
 93 | 		expect(generateTaskFiles).toHaveBeenCalledTimes(1);
 94 | 	});
 95 | 
 96 | 	test('error when tag invalid', async () => {
 97 | 		await expect(
 98 | 			moveTask('tasks.json', '1', '2', false, { tag: 'ghost' })
 99 | 		).rejects.toThrow(/tag "ghost" not found/);
100 | 	});
101 | });
102 | 
```

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

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

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

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

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

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

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

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

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

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

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

```javascript
 1 | /**
 2 |  * models.js
 3 |  * MCP tool for managing AI model configurations
 4 |  */
 5 | 
 6 | import { z } from 'zod';
 7 | import {
 8 | 	handleApiResult,
 9 | 	createErrorResponse,
10 | 	withNormalizedProjectRoot
11 | } from './utils.js';
12 | import { modelsDirect } from '../core/task-master-core.js';
13 | 
14 | /**
15 |  * Register the models tool with the MCP server
16 |  * @param {Object} server - FastMCP server instance
17 |  */
18 | export function registerModelsTool(server) {
19 | 	server.addTool({
20 | 		name: 'models',
21 | 		description:
22 | 			'Get information about available AI models or set model configurations. Run without arguments to get the current model configuration and API key status for the selected model providers.',
23 | 		parameters: z.object({
24 | 			setMain: z
25 | 				.string()
26 | 				.optional()
27 | 				.describe(
28 | 					'Set the primary model for task generation/updates. Model provider API key is required in the MCP config ENV.'
29 | 				),
30 | 			setResearch: z
31 | 				.string()
32 | 				.optional()
33 | 				.describe(
34 | 					'Set the model for research-backed operations. Model provider API key is required in the MCP config ENV.'
35 | 				),
36 | 			setFallback: z
37 | 				.string()
38 | 				.optional()
39 | 				.describe(
40 | 					'Set the model to use if the primary fails. Model provider API key is required in the MCP config ENV.'
41 | 				),
42 | 			listAvailableModels: z
43 | 				.boolean()
44 | 				.optional()
45 | 				.describe(
46 | 					'List all available models not currently in use. Input/output costs values are in dollars (3 is $3.00).'
47 | 				),
48 | 			projectRoot: z
49 | 				.string()
50 | 				.describe('The directory of the project. Must be an absolute path.'),
51 | 			openrouter: z
52 | 				.boolean()
53 | 				.optional()
54 | 				.describe('Indicates the set model ID is a custom OpenRouter model.'),
55 | 			ollama: z
56 | 				.boolean()
57 | 				.optional()
58 | 				.describe('Indicates the set model ID is a custom Ollama model.'),
59 | 			bedrock: z
60 | 				.boolean()
61 | 				.optional()
62 | 				.describe('Indicates the set model ID is a custom AWS Bedrock model.'),
63 | 			azure: z
64 | 				.boolean()
65 | 				.optional()
66 | 				.describe('Indicates the set model ID is a custom Azure OpenAI model.'),
67 | 			vertex: z
68 | 				.boolean()
69 | 				.optional()
70 | 				.describe(
71 | 					'Indicates the set model ID is a custom Google Vertex AI model.'
72 | 				)
73 | 		}),
74 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
75 | 			try {
76 | 				log.info(`Starting models tool with args: ${JSON.stringify(args)}`);
77 | 
78 | 				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
79 | 				const result = await modelsDirect(
80 | 					{ ...args, projectRoot: args.projectRoot },
81 | 					log,
82 | 					{ session }
83 | 				);
84 | 
85 | 				return handleApiResult(
86 | 					result,
87 | 					log,
88 | 					'Error managing models',
89 | 					undefined,
90 | 					args.projectRoot
91 | 				);
92 | 			} catch (error) {
93 | 				log.error(`Error in models tool: ${error.message}`);
94 | 				return createErrorResponse(error.message);
95 | 			}
96 | 		})
97 | 	});
98 | }
99 | 
```

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

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

--------------------------------------------------------------------------------
/.claude/commands/tm/analyze-complexity/analyze-complexity.md:
--------------------------------------------------------------------------------

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

--------------------------------------------------------------------------------
/assets/claude/commands/tm/analyze-complexity/analyze-complexity.md:
--------------------------------------------------------------------------------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

```markdown
 1 | ---
 2 | title: MCP Tools
 3 | sidebarTitle: "MCP Tools"
 4 | ---
 5 | 
 6 | # MCP Tools
 7 | 
 8 | This document provides an overview of the MCP (Machine-to-Machine Communication Protocol) interface for the Task Master application. The MCP interface is defined in the `mcp-server/` directory and exposes the application's core functionalities as a set of tools that can be called remotely.
 9 | 
10 | ## Core Concepts
11 | 
12 | The MCP interface is built on top of the `fastmcp` library and registers a set of tools that correspond to the core functionalities of the Task Master application. These tools are defined in the `mcp-server/src/tools/` directory and are registered with the MCP server in `mcp-server/src/tools/index.js`.
13 | 
14 | Each tool is defined with a name, a description, and a set of parameters that are validated using the `zod` library. The `execute` function of each tool calls the corresponding core logic function from `scripts/modules/task-manager.js`.
15 | 
16 | ## Tool Categories
17 | 
18 | The MCP tools can be categorized in the same way as the core functionalities:
19 | 
20 | ### 1. Task and Subtask Management
21 | 
22 | -   **`add_task`**: Creates a new task.
23 | -   **`add_subtask`**: Adds a subtask to a parent task.
24 | -   **`remove_task`**: Removes one or more tasks or subtasks.
25 | -   **`remove_subtask`**: Removes a subtask from its parent.
26 | -   **`update_task`**: Updates a single task.
27 | -   **`update_subtask`**: Appends information to a subtask.
28 | -   **`update`**: Updates multiple tasks.
29 | -   **`move_task`**: Moves a task or subtask.
30 | -   **`clear_subtasks`**: Clears all subtasks from one or more tasks.
31 | 
32 | ### 2. Task Information and Status
33 | 
34 | -   **`get_tasks`**: Lists all tasks.
35 | -   **`get_task`**: Shows the details of a specific task.
36 | -   **`next_task`**: Shows the next task to work on.
37 | -   **`set_task_status`**: Sets the status of a task or subtask.
38 | 
39 | ### 3. Task Analysis and Expansion
40 | 
41 | -   **`parse_prd`**: Parses a PRD to generate tasks.
42 | -   **`expand_task`**: Expands a task into subtasks.
43 | -   **`expand_all`**: Expands all eligible tasks.
44 | -   **`analyze_project_complexity`**: Analyzes task complexity.
45 | -   **`complexity_report`**: Displays the complexity analysis report.
46 | 
47 | ### 4. Dependency Management
48 | 
49 | -   **`add_dependency`**: Adds a dependency to a task.
50 | -   **`remove_dependency`**: Removes a dependency from a task.
51 | -   **`validate_dependencies`**: Validates the dependencies of all tasks.
52 | -   **`fix_dependencies`**: Fixes any invalid dependencies.
53 | 
54 | ### 5. Project and Configuration
55 | 
56 | -   **`initialize_project`**: Initializes a new project.
57 | -   **`generate`**: Generates individual task files.
58 | -   **`models`**: Manages AI model configurations.
59 | -   **`research`**: Performs AI-powered research.
60 | 
61 | ### 6. Tag Management
62 | 
63 | -   **`add_tag`**: Creates a new tag.
64 | -   **`delete_tag`**: Deletes a tag.
65 | -   **`list_tags`**: Lists all tags.
66 | -   **`use_tag`**: Switches to a different tag.
67 | -   **`rename_tag`**: Renames a tag.
68 | -   **`copy_tag`**: Copies a tag.
```

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

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

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | 
  3 | jest.unstable_mockModule('fs', () => {
  4 | 	const mockFs = {
  5 | 		existsSync: jest.fn(() => true),
  6 | 		writeFileSync: jest.fn(),
  7 | 		readFileSync: jest.fn(),
  8 | 		unlinkSync: jest.fn()
  9 | 	};
 10 | 	return { default: mockFs, ...mockFs };
 11 | });
 12 | 
 13 | jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
 14 | 	readJSON: jest.fn(),
 15 | 	writeJSON: jest.fn(),
 16 | 	log: jest.fn(),
 17 | 	isSilentMode: jest.fn(() => false),
 18 | 	findProjectRoot: jest.fn(() => '/project'),
 19 | 	flattenTasksWithSubtasks: jest.fn(() => []),
 20 | 	truncate: jest.fn((t) => t),
 21 | 	isEmpty: jest.fn(() => false),
 22 | 	resolveEnvVariable: jest.fn(),
 23 | 	findTaskById: jest.fn(),
 24 | 	getCurrentTag: jest.fn(() => 'master')
 25 | }));
 26 | 
 27 | jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({
 28 | 	getStatusWithColor: jest.fn((s) => s),
 29 | 	startLoadingIndicator: jest.fn(() => ({ stop: jest.fn() })),
 30 | 	stopLoadingIndicator: jest.fn(),
 31 | 	displayAiUsageSummary: jest.fn()
 32 | }));
 33 | 
 34 | jest.unstable_mockModule(
 35 | 	'../../../../../scripts/modules/task-manager/generate-task-files.js',
 36 | 	() => ({
 37 | 		default: jest.fn().mockResolvedValue()
 38 | 	})
 39 | );
 40 | 
 41 | jest.unstable_mockModule(
 42 | 	'../../../../../scripts/modules/ai-services-unified.js',
 43 | 	() => ({
 44 | 		generateTextService: jest
 45 | 			.fn()
 46 | 			.mockResolvedValue({ mainResult: { content: '{}' }, telemetryData: {} })
 47 | 	})
 48 | );
 49 | 
 50 | jest.unstable_mockModule(
 51 | 	'../../../../../scripts/modules/config-manager.js',
 52 | 	() => ({
 53 | 		getDebugFlag: jest.fn(() => false),
 54 | 		isApiKeySet: jest.fn(() => true),
 55 | 		hasCodebaseAnalysis: jest.fn(() => false)
 56 | 	})
 57 | );
 58 | 
 59 | const { readJSON, log } = await import(
 60 | 	'../../../../../scripts/modules/utils.js'
 61 | );
 62 | const { default: updateTaskById } = await import(
 63 | 	'../../../../../scripts/modules/task-manager/update-task-by-id.js'
 64 | );
 65 | 
 66 | describe('updateTaskById validation', () => {
 67 | 	beforeEach(() => {
 68 | 		jest.clearAllMocks();
 69 | 		jest.spyOn(process, 'exit').mockImplementation(() => {
 70 | 			throw new Error('process.exit called');
 71 | 		});
 72 | 	});
 73 | 
 74 | 	test('throws error if prompt is empty', async () => {
 75 | 		await expect(
 76 | 			updateTaskById(
 77 | 				'tasks/tasks.json',
 78 | 				1,
 79 | 				'',
 80 | 				false,
 81 | 				{ tag: 'master' },
 82 | 				'json'
 83 | 			)
 84 | 		).rejects.toThrow('Prompt cannot be empty');
 85 | 	});
 86 | 
 87 | 	test('throws error if task file missing', async () => {
 88 | 		const fs = await import('fs');
 89 | 		fs.existsSync.mockReturnValue(false);
 90 | 		await expect(
 91 | 			updateTaskById(
 92 | 				'tasks/tasks.json',
 93 | 				1,
 94 | 				'prompt',
 95 | 				false,
 96 | 				{
 97 | 					tag: 'master'
 98 | 				},
 99 | 				'json'
100 | 			)
101 | 		).rejects.toThrow('Tasks file not found');
102 | 	});
103 | 
104 | 	test('throws error when task ID not found', async () => {
105 | 		const fs = await import('fs');
106 | 		fs.existsSync.mockReturnValue(true);
107 | 		readJSON.mockReturnValue({ tag: 'master', tasks: [] });
108 | 		await expect(
109 | 			updateTaskById(
110 | 				'tasks/tasks.json',
111 | 				42,
112 | 				'prompt',
113 | 				false,
114 | 				{
115 | 					tag: 'master'
116 | 				},
117 | 				'json'
118 | 			)
119 | 		).rejects.toThrow('Task with ID 42 not found');
120 | 		expect(log).toHaveBeenCalled();
121 | 	});
122 | });
123 | 
```

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

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