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

--------------------------------------------------------------------------------
/context/fastmcp-core.txt:
--------------------------------------------------------------------------------

```
   1 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
   2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
   3 | import {
   4 |   CallToolRequestSchema,
   5 |   ClientCapabilities,
   6 |   CompleteRequestSchema,
   7 |   CreateMessageRequestSchema,
   8 |   ErrorCode,
   9 |   GetPromptRequestSchema,
  10 |   ListPromptsRequestSchema,
  11 |   ListResourcesRequestSchema,
  12 |   ListResourceTemplatesRequestSchema,
  13 |   ListToolsRequestSchema,
  14 |   McpError,
  15 |   ReadResourceRequestSchema,
  16 |   Root,
  17 |   RootsListChangedNotificationSchema,
  18 |   ServerCapabilities,
  19 |   SetLevelRequestSchema,
  20 | } from "@modelcontextprotocol/sdk/types.js";
  21 | import { zodToJsonSchema } from "zod-to-json-schema";
  22 | import { z } from "zod";
  23 | import { setTimeout as delay } from "timers/promises";
  24 | import { readFile } from "fs/promises";
  25 | import { fileTypeFromBuffer } from "file-type";
  26 | import { StrictEventEmitter } from "strict-event-emitter-types";
  27 | import { EventEmitter } from "events";
  28 | import Fuse from "fuse.js";
  29 | import { startSSEServer } from "mcp-proxy";
  30 | import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
  31 | import parseURITemplate from "uri-templates";
  32 | import http from "http";
  33 | import {
  34 |   fetch
  35 | } from "undici";
  36 | 
  37 | export type SSEServer = {
  38 |   close: () => Promise<void>;
  39 | };
  40 | 
  41 | type FastMCPEvents<T extends FastMCPSessionAuth> = {
  42 |   connect: (event: { session: FastMCPSession<T> }) => void;
  43 |   disconnect: (event: { session: FastMCPSession<T> }) => void;
  44 | };
  45 | 
  46 | type FastMCPSessionEvents = {
  47 |   rootsChanged: (event: { roots: Root[] }) => void;
  48 |   error: (event: { error: Error }) => void;
  49 | };
  50 | 
  51 | /**
  52 |  * Generates an image content object from a URL, file path, or buffer.
  53 |  */
  54 | export const imageContent = async (
  55 |   input: { url: string } | { path: string } | { buffer: Buffer },
  56 | ): Promise<ImageContent> => {
  57 |   let rawData: Buffer;
  58 | 
  59 |   if ("url" in input) {
  60 |     const response = await fetch(input.url);
  61 | 
  62 |     if (!response.ok) {
  63 |       throw new Error(`Failed to fetch image from URL: ${response.statusText}`);
  64 |     }
  65 | 
  66 |     rawData = Buffer.from(await response.arrayBuffer());
  67 |   } else if ("path" in input) {
  68 |     rawData = await readFile(input.path);
  69 |   } else if ("buffer" in input) {
  70 |     rawData = input.buffer;
  71 |   } else {
  72 |     throw new Error(
  73 |       "Invalid input: Provide a valid 'url', 'path', or 'buffer'",
  74 |     );
  75 |   }
  76 | 
  77 |   const mimeType = await fileTypeFromBuffer(rawData);
  78 | 
  79 |   const base64Data = rawData.toString("base64");
  80 | 
  81 |   return {
  82 |     type: "image",
  83 |     data: base64Data,
  84 |     mimeType: mimeType?.mime ?? "image/png",
  85 |   } as const;
  86 | };
  87 | 
  88 | abstract class FastMCPError extends Error {
  89 |   public constructor(message?: string) {
  90 |     super(message);
  91 |     this.name = new.target.name;
  92 |   }
  93 | }
  94 | 
  95 | type Extra = unknown;
  96 | 
  97 | type Extras = Record<string, Extra>;
  98 | 
  99 | export class UnexpectedStateError extends FastMCPError {
 100 |   public extras?: Extras;
 101 | 
 102 |   public constructor(message: string, extras?: Extras) {
 103 |     super(message);
 104 |     this.name = new.target.name;
 105 |     this.extras = extras;
 106 |   }
 107 | }
 108 | 
 109 | /**
 110 |  * An error that is meant to be surfaced to the user.
 111 |  */
 112 | export class UserError extends UnexpectedStateError {}
 113 | 
 114 | type ToolParameters = z.ZodTypeAny;
 115 | 
 116 | type Literal = boolean | null | number | string | undefined;
 117 | 
 118 | type SerializableValue =
 119 |   | Literal
 120 |   | SerializableValue[]
 121 |   | { [key: string]: SerializableValue };
 122 | 
 123 | type Progress = {
 124 |   /**
 125 |    * The progress thus far. This should increase every time progress is made, even if the total is unknown.
 126 |    */
 127 |   progress: number;
 128 |   /**
 129 |    * Total number of items to process (or total progress required), if known.
 130 |    */
 131 |   total?: number;
 132 | };
 133 | 
 134 | type Context<T extends FastMCPSessionAuth> = {
 135 |   session: T | undefined;
 136 |   reportProgress: (progress: Progress) => Promise<void>;
 137 |   log: {
 138 |     debug: (message: string, data?: SerializableValue) => void;
 139 |     error: (message: string, data?: SerializableValue) => void;
 140 |     info: (message: string, data?: SerializableValue) => void;
 141 |     warn: (message: string, data?: SerializableValue) => void;
 142 |   };
 143 | };
 144 | 
 145 | type TextContent = {
 146 |   type: "text";
 147 |   text: string;
 148 | };
 149 | 
 150 | const TextContentZodSchema = z
 151 |   .object({
 152 |     type: z.literal("text"),
 153 |     /**
 154 |      * The text content of the message.
 155 |      */
 156 |     text: z.string(),
 157 |   })
 158 |   .strict() satisfies z.ZodType<TextContent>;
 159 | 
 160 | type ImageContent = {
 161 |   type: "image";
 162 |   data: string;
 163 |   mimeType: string;
 164 | };
 165 | 
 166 | const ImageContentZodSchema = z
 167 |   .object({
 168 |     type: z.literal("image"),
 169 |     /**
 170 |      * The base64-encoded image data.
 171 |      */
 172 |     data: z.string().base64(),
 173 |     /**
 174 |      * The MIME type of the image. Different providers may support different image types.
 175 |      */
 176 |     mimeType: z.string(),
 177 |   })
 178 |   .strict() satisfies z.ZodType<ImageContent>;
 179 | 
 180 | type Content = TextContent | ImageContent;
 181 | 
 182 | const ContentZodSchema = z.discriminatedUnion("type", [
 183 |   TextContentZodSchema,
 184 |   ImageContentZodSchema,
 185 | ]) satisfies z.ZodType<Content>;
 186 | 
 187 | type ContentResult = {
 188 |   content: Content[];
 189 |   isError?: boolean;
 190 | };
 191 | 
 192 | const ContentResultZodSchema = z
 193 |   .object({
 194 |     content: ContentZodSchema.array(),
 195 |     isError: z.boolean().optional(),
 196 |   })
 197 |   .strict() satisfies z.ZodType<ContentResult>;
 198 | 
 199 | type Completion = {
 200 |   values: string[];
 201 |   total?: number;
 202 |   hasMore?: boolean;
 203 | };
 204 | 
 205 | /**
 206 |  * https://github.com/modelcontextprotocol/typescript-sdk/blob/3164da64d085ec4e022ae881329eee7b72f208d4/src/types.ts#L983-L1003
 207 |  */
 208 | const CompletionZodSchema = z.object({
 209 |   /**
 210 |    * An array of completion values. Must not exceed 100 items.
 211 |    */
 212 |   values: z.array(z.string()).max(100),
 213 |   /**
 214 |    * The total number of completion options available. This can exceed the number of values actually sent in the response.
 215 |    */
 216 |   total: z.optional(z.number().int()),
 217 |   /**
 218 |    * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.
 219 |    */
 220 |   hasMore: z.optional(z.boolean()),
 221 | }) satisfies z.ZodType<Completion>;
 222 | 
 223 | type Tool<T extends FastMCPSessionAuth, Params extends ToolParameters = ToolParameters> = {
 224 |   name: string;
 225 |   description?: string;
 226 |   parameters?: Params;
 227 |   execute: (
 228 |     args: z.infer<Params>,
 229 |     context: Context<T>,
 230 |   ) => Promise<string | ContentResult | TextContent | ImageContent>;
 231 | };
 232 | 
 233 | type ResourceResult =
 234 |   | {
 235 |       text: string;
 236 |     }
 237 |   | {
 238 |       blob: string;
 239 |     };
 240 | 
 241 | type InputResourceTemplateArgument = Readonly<{
 242 |   name: string;
 243 |   description?: string;
 244 |   complete?: ArgumentValueCompleter;
 245 | }>;
 246 | 
 247 | type ResourceTemplateArgument = Readonly<{
 248 |   name: string;
 249 |   description?: string;
 250 |   complete?: ArgumentValueCompleter;
 251 | }>;
 252 | 
 253 | type ResourceTemplate<
 254 |   Arguments extends ResourceTemplateArgument[] = ResourceTemplateArgument[],
 255 | > = {
 256 |   uriTemplate: string;
 257 |   name: string;
 258 |   description?: string;
 259 |   mimeType?: string;
 260 |   arguments: Arguments;
 261 |   complete?: (name: string, value: string) => Promise<Completion>;
 262 |   load: (
 263 |     args: ResourceTemplateArgumentsToObject<Arguments>,
 264 |   ) => Promise<ResourceResult>;
 265 | };
 266 | 
 267 | type ResourceTemplateArgumentsToObject<T extends { name: string }[]> = {
 268 |   [K in T[number]["name"]]: string;
 269 | };
 270 | 
 271 | type InputResourceTemplate<
 272 |   Arguments extends ResourceTemplateArgument[] = ResourceTemplateArgument[],
 273 | > = {
 274 |   uriTemplate: string;
 275 |   name: string;
 276 |   description?: string;
 277 |   mimeType?: string;
 278 |   arguments: Arguments;
 279 |   load: (
 280 |     args: ResourceTemplateArgumentsToObject<Arguments>,
 281 |   ) => Promise<ResourceResult>;
 282 | };
 283 | 
 284 | type Resource = {
 285 |   uri: string;
 286 |   name: string;
 287 |   description?: string;
 288 |   mimeType?: string;
 289 |   load: () => Promise<ResourceResult | ResourceResult[]>;
 290 |   complete?: (name: string, value: string) => Promise<Completion>;
 291 | };
 292 | 
 293 | type ArgumentValueCompleter = (value: string) => Promise<Completion>;
 294 | 
 295 | type InputPromptArgument = Readonly<{
 296 |   name: string;
 297 |   description?: string;
 298 |   required?: boolean;
 299 |   complete?: ArgumentValueCompleter;
 300 |   enum?: string[];
 301 | }>;
 302 | 
 303 | type PromptArgumentsToObject<T extends { name: string; required?: boolean }[]> =
 304 |   {
 305 |     [K in T[number]["name"]]: Extract<
 306 |       T[number],
 307 |       { name: K }
 308 |     >["required"] extends true
 309 |       ? string
 310 |       : string | undefined;
 311 |   };
 312 | 
 313 | type InputPrompt<
 314 |   Arguments extends InputPromptArgument[] = InputPromptArgument[],
 315 |   Args = PromptArgumentsToObject<Arguments>,
 316 | > = {
 317 |   name: string;
 318 |   description?: string;
 319 |   arguments?: InputPromptArgument[];
 320 |   load: (args: Args) => Promise<string>;
 321 | };
 322 | 
 323 | type PromptArgument = Readonly<{
 324 |   name: string;
 325 |   description?: string;
 326 |   required?: boolean;
 327 |   complete?: ArgumentValueCompleter;
 328 |   enum?: string[];
 329 | }>;
 330 | 
 331 | type Prompt<
 332 |   Arguments extends PromptArgument[] = PromptArgument[],
 333 |   Args = PromptArgumentsToObject<Arguments>,
 334 | > = {
 335 |   arguments?: PromptArgument[];
 336 |   complete?: (name: string, value: string) => Promise<Completion>;
 337 |   description?: string;
 338 |   load: (args: Args) => Promise<string>;
 339 |   name: string;
 340 | };
 341 | 
 342 | type ServerOptions<T extends FastMCPSessionAuth> = {
 343 |   name: string;
 344 |   version: `${number}.${number}.${number}`;
 345 |   authenticate?: Authenticate<T>;
 346 | };
 347 | 
 348 | type LoggingLevel =
 349 |   | "debug"
 350 |   | "info"
 351 |   | "notice"
 352 |   | "warning"
 353 |   | "error"
 354 |   | "critical"
 355 |   | "alert"
 356 |   | "emergency";
 357 | 
 358 | const FastMCPSessionEventEmitterBase: {
 359 |   new (): StrictEventEmitter<EventEmitter, FastMCPSessionEvents>;
 360 | } = EventEmitter;
 361 | 
 362 | class FastMCPSessionEventEmitter extends FastMCPSessionEventEmitterBase {}
 363 | 
 364 | type SamplingResponse = {
 365 |   model: string;
 366 |   stopReason?: "endTurn" | "stopSequence" | "maxTokens" | string;
 367 |   role: "user" | "assistant";
 368 |   content: TextContent | ImageContent;
 369 | };
 370 | 
 371 | type FastMCPSessionAuth = Record<string, unknown> | undefined;
 372 | 
 373 | export class FastMCPSession<T extends FastMCPSessionAuth = FastMCPSessionAuth> extends FastMCPSessionEventEmitter {
 374 |   #capabilities: ServerCapabilities = {};
 375 |   #clientCapabilities?: ClientCapabilities;
 376 |   #loggingLevel: LoggingLevel = "info";
 377 |   #prompts: Prompt[] = [];
 378 |   #resources: Resource[] = [];
 379 |   #resourceTemplates: ResourceTemplate[] = [];
 380 |   #roots: Root[] = [];
 381 |   #server: Server;
 382 |   #auth: T | undefined;
 383 | 
 384 |   constructor({
 385 |     auth,
 386 |     name,
 387 |     version,
 388 |     tools,
 389 |     resources,
 390 |     resourcesTemplates,
 391 |     prompts,
 392 |   }: {
 393 |     auth?: T;
 394 |     name: string;
 395 |     version: string;
 396 |     tools: Tool<T>[];
 397 |     resources: Resource[];
 398 |     resourcesTemplates: InputResourceTemplate[];
 399 |     prompts: Prompt[];
 400 |   }) {
 401 |     super();
 402 | 
 403 |     this.#auth = auth;
 404 | 
 405 |     if (tools.length) {
 406 |       this.#capabilities.tools = {};
 407 |     }
 408 | 
 409 |     if (resources.length || resourcesTemplates.length) {
 410 |       this.#capabilities.resources = {};
 411 |     }
 412 | 
 413 |     if (prompts.length) {
 414 |       for (const prompt of prompts) {
 415 |         this.addPrompt(prompt);
 416 |       }
 417 | 
 418 |       this.#capabilities.prompts = {};
 419 |     }
 420 | 
 421 |     this.#capabilities.logging = {};
 422 | 
 423 |     this.#server = new Server(
 424 |       { name: name, version: version },
 425 |       { capabilities: this.#capabilities },
 426 |     );
 427 | 
 428 |     this.setupErrorHandling();
 429 |     this.setupLoggingHandlers();
 430 |     this.setupRootsHandlers();
 431 |     this.setupCompleteHandlers();
 432 | 
 433 |     if (tools.length) {
 434 |       this.setupToolHandlers(tools);
 435 |     }
 436 | 
 437 |     if (resources.length || resourcesTemplates.length) {
 438 |       for (const resource of resources) {
 439 |         this.addResource(resource);
 440 |       }
 441 | 
 442 |       this.setupResourceHandlers(resources);
 443 | 
 444 |       if (resourcesTemplates.length) {
 445 |         for (const resourceTemplate of resourcesTemplates) {
 446 |           this.addResourceTemplate(resourceTemplate);
 447 |         }
 448 | 
 449 |         this.setupResourceTemplateHandlers(resourcesTemplates);
 450 |       }
 451 |     }
 452 | 
 453 |     if (prompts.length) {
 454 |       this.setupPromptHandlers(prompts);
 455 |     }
 456 |   }
 457 | 
 458 |   private addResource(inputResource: Resource) {
 459 |     this.#resources.push(inputResource);
 460 |   }
 461 | 
 462 |   private addResourceTemplate(inputResourceTemplate: InputResourceTemplate) {
 463 |     const completers: Record<string, ArgumentValueCompleter> = {};
 464 | 
 465 |     for (const argument of inputResourceTemplate.arguments ?? []) {
 466 |       if (argument.complete) {
 467 |         completers[argument.name] = argument.complete;
 468 |       }
 469 |     }
 470 | 
 471 |     const resourceTemplate = {
 472 |       ...inputResourceTemplate,
 473 |       complete: async (name: string, value: string) => {
 474 |         if (completers[name]) {
 475 |           return await completers[name](value);
 476 |         }
 477 | 
 478 |         return {
 479 |           values: [],
 480 |         };
 481 |       },
 482 |     };
 483 | 
 484 |     this.#resourceTemplates.push(resourceTemplate);
 485 |   }
 486 | 
 487 |   private addPrompt(inputPrompt: InputPrompt) {
 488 |     const completers: Record<string, ArgumentValueCompleter> = {};
 489 |     const enums: Record<string, string[]> = {};
 490 | 
 491 |     for (const argument of inputPrompt.arguments ?? []) {
 492 |       if (argument.complete) {
 493 |         completers[argument.name] = argument.complete;
 494 |       }
 495 | 
 496 |       if (argument.enum) {
 497 |         enums[argument.name] = argument.enum;
 498 |       }
 499 |     }
 500 | 
 501 |     const prompt = {
 502 |       ...inputPrompt,
 503 |       complete: async (name: string, value: string) => {
 504 |         if (completers[name]) {
 505 |           return await completers[name](value);
 506 |         }
 507 | 
 508 |         if (enums[name]) {
 509 |           const fuse = new Fuse(enums[name], {
 510 |             keys: ["value"],
 511 |           });
 512 | 
 513 |           const result = fuse.search(value);
 514 | 
 515 |           return {
 516 |             values: result.map((item) => item.item),
 517 |             total: result.length,
 518 |           };
 519 |         }
 520 | 
 521 |         return {
 522 |           values: [],
 523 |         };
 524 |       },
 525 |     };
 526 | 
 527 |     this.#prompts.push(prompt);
 528 |   }
 529 | 
 530 |   public get clientCapabilities(): ClientCapabilities | null {
 531 |     return this.#clientCapabilities ?? null;
 532 |   }
 533 | 
 534 |   public get server(): Server {
 535 |     return this.#server;
 536 |   }
 537 | 
 538 |   #pingInterval: ReturnType<typeof setInterval> | null = null;
 539 | 
 540 |   public async requestSampling(
 541 |     message: z.infer<typeof CreateMessageRequestSchema>["params"],
 542 |   ): Promise<SamplingResponse> {
 543 |     return this.#server.createMessage(message);
 544 |   }
 545 | 
 546 |   public async connect(transport: Transport) {
 547 |     if (this.#server.transport) {
 548 |       throw new UnexpectedStateError("Server is already connected");
 549 |     }
 550 | 
 551 |     await this.#server.connect(transport);
 552 | 
 553 |     let attempt = 0;
 554 | 
 555 |     while (attempt++ < 10) {
 556 |       const capabilities = await this.#server.getClientCapabilities();
 557 | 
 558 |       if (capabilities) {
 559 |         this.#clientCapabilities = capabilities;
 560 | 
 561 |         break;
 562 |       }
 563 | 
 564 |       await delay(100);
 565 |     }
 566 | 
 567 |     if (!this.#clientCapabilities) {
 568 |       console.warn('[warning] FastMCP could not infer client capabilities')
 569 |     }
 570 | 
 571 |     if (this.#clientCapabilities?.roots?.listChanged) {
 572 |       try {
 573 |         const roots = await this.#server.listRoots();
 574 |         this.#roots = roots.roots;
 575 |       } catch(e) {
 576 |         console.error(`[error] FastMCP received error listing roots.\n\n${e instanceof Error ? e.stack : JSON.stringify(e)}`)
 577 |       }
 578 |     }
 579 | 
 580 |     this.#pingInterval = setInterval(async () => {
 581 |       try {
 582 |         await this.#server.ping();
 583 |       } catch (error) {
 584 |         this.emit("error", {
 585 |           error: error as Error,
 586 |         });
 587 |       }
 588 |     }, 1000);
 589 |   }
 590 | 
 591 |   public get roots(): Root[] {
 592 |     return this.#roots;
 593 |   }
 594 | 
 595 |   public async close() {
 596 |     if (this.#pingInterval) {
 597 |       clearInterval(this.#pingInterval);
 598 |     }
 599 | 
 600 |     try {
 601 |       await this.#server.close();
 602 |     } catch (error) {
 603 |       console.error("[MCP Error]", "could not close server", error);
 604 |     }
 605 |   }
 606 | 
 607 |   private setupErrorHandling() {
 608 |     this.#server.onerror = (error) => {
 609 |       console.error("[MCP Error]", error);
 610 |     };
 611 |   }
 612 | 
 613 |   public get loggingLevel(): LoggingLevel {
 614 |     return this.#loggingLevel;
 615 |   }
 616 | 
 617 |   private setupCompleteHandlers() {
 618 |     this.#server.setRequestHandler(CompleteRequestSchema, async (request) => {
 619 |       if (request.params.ref.type === "ref/prompt") {
 620 |         const prompt = this.#prompts.find(
 621 |           (prompt) => prompt.name === request.params.ref.name,
 622 |         );
 623 | 
 624 |         if (!prompt) {
 625 |           throw new UnexpectedStateError("Unknown prompt", {
 626 |             request,
 627 |           });
 628 |         }
 629 | 
 630 |         if (!prompt.complete) {
 631 |           throw new UnexpectedStateError("Prompt does not support completion", {
 632 |             request,
 633 |           });
 634 |         }
 635 | 
 636 |         const completion = CompletionZodSchema.parse(
 637 |           await prompt.complete(
 638 |             request.params.argument.name,
 639 |             request.params.argument.value,
 640 |           ),
 641 |         );
 642 | 
 643 |         return {
 644 |           completion,
 645 |         };
 646 |       }
 647 | 
 648 |       if (request.params.ref.type === "ref/resource") {
 649 |         const resource = this.#resourceTemplates.find(
 650 |           (resource) => resource.uriTemplate === request.params.ref.uri,
 651 |         );
 652 | 
 653 |         if (!resource) {
 654 |           throw new UnexpectedStateError("Unknown resource", {
 655 |             request,
 656 |           });
 657 |         }
 658 | 
 659 |         if (!("uriTemplate" in resource)) {
 660 |           throw new UnexpectedStateError("Unexpected resource");
 661 |         }
 662 | 
 663 |         if (!resource.complete) {
 664 |           throw new UnexpectedStateError(
 665 |             "Resource does not support completion",
 666 |             {
 667 |               request,
 668 |             },
 669 |           );
 670 |         }
 671 | 
 672 |         const completion = CompletionZodSchema.parse(
 673 |           await resource.complete(
 674 |             request.params.argument.name,
 675 |             request.params.argument.value,
 676 |           ),
 677 |         );
 678 | 
 679 |         return {
 680 |           completion,
 681 |         };
 682 |       }
 683 | 
 684 |       throw new UnexpectedStateError("Unexpected completion request", {
 685 |         request,
 686 |       });
 687 |     });
 688 |   }
 689 | 
 690 |   private setupRootsHandlers() {
 691 |     this.#server.setNotificationHandler(
 692 |       RootsListChangedNotificationSchema,
 693 |       () => {
 694 |         this.#server.listRoots().then((roots) => {
 695 |           this.#roots = roots.roots;
 696 | 
 697 |           this.emit("rootsChanged", {
 698 |             roots: roots.roots,
 699 |           });
 700 |         });
 701 |       },
 702 |     );
 703 |   }
 704 | 
 705 |   private setupLoggingHandlers() {
 706 |     this.#server.setRequestHandler(SetLevelRequestSchema, (request) => {
 707 |       this.#loggingLevel = request.params.level;
 708 | 
 709 |       return {};
 710 |     });
 711 |   }
 712 | 
 713 |   private setupToolHandlers(tools: Tool<T>[]) {
 714 |     this.#server.setRequestHandler(ListToolsRequestSchema, async () => {
 715 |       return {
 716 |         tools: tools.map((tool) => {
 717 |           return {
 718 |             name: tool.name,
 719 |             description: tool.description,
 720 |             inputSchema: tool.parameters
 721 |               ? zodToJsonSchema(tool.parameters)
 722 |               : undefined,
 723 |           };
 724 |         }),
 725 |       };
 726 |     });
 727 | 
 728 |     this.#server.setRequestHandler(CallToolRequestSchema, async (request) => {
 729 |       const tool = tools.find((tool) => tool.name === request.params.name);
 730 | 
 731 |       if (!tool) {
 732 |         throw new McpError(
 733 |           ErrorCode.MethodNotFound,
 734 |           `Unknown tool: ${request.params.name}`,
 735 |         );
 736 |       }
 737 | 
 738 |       let args: any = undefined;
 739 | 
 740 |       if (tool.parameters) {
 741 |         const parsed = tool.parameters.safeParse(request.params.arguments);
 742 | 
 743 |         if (!parsed.success) {
 744 |           throw new McpError(
 745 |             ErrorCode.InvalidParams,
 746 |             `Invalid ${request.params.name} parameters`,
 747 |           );
 748 |         }
 749 | 
 750 |         args = parsed.data;
 751 |       }
 752 | 
 753 |       const progressToken = request.params?._meta?.progressToken;
 754 | 
 755 |       let result: ContentResult;
 756 | 
 757 |       try {
 758 |         const reportProgress = async (progress: Progress) => {
 759 |           await this.#server.notification({
 760 |             method: "notifications/progress",
 761 |             params: {
 762 |               ...progress,
 763 |               progressToken,
 764 |             },
 765 |           });
 766 |         };
 767 | 
 768 |         const log = {
 769 |           debug: (message: string, context?: SerializableValue) => {
 770 |             this.#server.sendLoggingMessage({
 771 |               level: "debug",
 772 |               data: {
 773 |                 message,
 774 |                 context,
 775 |               },
 776 |             });
 777 |           },
 778 |           error: (message: string, context?: SerializableValue) => {
 779 |             this.#server.sendLoggingMessage({
 780 |               level: "error",
 781 |               data: {
 782 |                 message,
 783 |                 context,
 784 |               },
 785 |             });
 786 |           },
 787 |           info: (message: string, context?: SerializableValue) => {
 788 |             this.#server.sendLoggingMessage({
 789 |               level: "info",
 790 |               data: {
 791 |                 message,
 792 |                 context,
 793 |               },
 794 |             });
 795 |           },
 796 |           warn: (message: string, context?: SerializableValue) => {
 797 |             this.#server.sendLoggingMessage({
 798 |               level: "warning",
 799 |               data: {
 800 |                 message,
 801 |                 context,
 802 |               },
 803 |             });
 804 |           },
 805 |         };
 806 | 
 807 |         const maybeStringResult = await tool.execute(args, {
 808 |           reportProgress,
 809 |           log,
 810 |           session: this.#auth,
 811 |         });
 812 | 
 813 |         if (typeof maybeStringResult === "string") {
 814 |           result = ContentResultZodSchema.parse({
 815 |             content: [{ type: "text", text: maybeStringResult }],
 816 |           });
 817 |         } else if ("type" in maybeStringResult) {
 818 |           result = ContentResultZodSchema.parse({
 819 |             content: [maybeStringResult],
 820 |           });
 821 |         } else {
 822 |           result = ContentResultZodSchema.parse(maybeStringResult);
 823 |         }
 824 |       } catch (error) {
 825 |         if (error instanceof UserError) {
 826 |           return {
 827 |             content: [{ type: "text", text: error.message }],
 828 |             isError: true,
 829 |           };
 830 |         }
 831 | 
 832 |         return {
 833 |           content: [{ type: "text", text: `Error: ${error}` }],
 834 |           isError: true,
 835 |         };
 836 |       }
 837 | 
 838 |       return result;
 839 |     });
 840 |   }
 841 | 
 842 |   private setupResourceHandlers(resources: Resource[]) {
 843 |     this.#server.setRequestHandler(ListResourcesRequestSchema, async () => {
 844 |       return {
 845 |         resources: resources.map((resource) => {
 846 |           return {
 847 |             uri: resource.uri,
 848 |             name: resource.name,
 849 |             mimeType: resource.mimeType,
 850 |           };
 851 |         }),
 852 |       };
 853 |     });
 854 | 
 855 |     this.#server.setRequestHandler(
 856 |       ReadResourceRequestSchema,
 857 |       async (request) => {
 858 |         if ("uri" in request.params) {
 859 |           const resource = resources.find(
 860 |             (resource) =>
 861 |               "uri" in resource && resource.uri === request.params.uri,
 862 |           );
 863 | 
 864 |           if (!resource) {
 865 |             for (const resourceTemplate of this.#resourceTemplates) {
 866 |               const uriTemplate = parseURITemplate(
 867 |                 resourceTemplate.uriTemplate,
 868 |               );
 869 | 
 870 |               const match = uriTemplate.fromUri(request.params.uri);
 871 | 
 872 |               if (!match) {
 873 |                 continue;
 874 |               }
 875 | 
 876 |               const uri = uriTemplate.fill(match);
 877 | 
 878 |               const result = await resourceTemplate.load(match);
 879 | 
 880 |               return {
 881 |                 contents: [
 882 |                   {
 883 |                     uri: uri,
 884 |                     mimeType: resourceTemplate.mimeType,
 885 |                     name: resourceTemplate.name,
 886 |                     ...result,
 887 |                   },
 888 |                 ],
 889 |               };
 890 |             }
 891 | 
 892 |             throw new McpError(
 893 |               ErrorCode.MethodNotFound,
 894 |               `Unknown resource: ${request.params.uri}`,
 895 |             );
 896 |           }
 897 | 
 898 |           if (!("uri" in resource)) {
 899 |             throw new UnexpectedStateError("Resource does not support reading");
 900 |           }
 901 | 
 902 |           let maybeArrayResult: Awaited<ReturnType<Resource["load"]>>;
 903 | 
 904 |           try {
 905 |             maybeArrayResult = await resource.load();
 906 |           } catch (error) {
 907 |             throw new McpError(
 908 |               ErrorCode.InternalError,
 909 |               `Error reading resource: ${error}`,
 910 |               {
 911 |                 uri: resource.uri,
 912 |               },
 913 |             );
 914 |           }
 915 | 
 916 |           if (Array.isArray(maybeArrayResult)) {
 917 |             return {
 918 |               contents: maybeArrayResult.map((result) => ({
 919 |                 uri: resource.uri,
 920 |                 mimeType: resource.mimeType,
 921 |                 name: resource.name,
 922 |                 ...result,
 923 |               })),
 924 |             };
 925 |           } else {
 926 |             return {
 927 |               contents: [
 928 |                 {
 929 |                   uri: resource.uri,
 930 |                   mimeType: resource.mimeType,
 931 |                   name: resource.name,
 932 |                   ...maybeArrayResult,
 933 |                 },
 934 |               ],
 935 |             };
 936 |           }
 937 |         }
 938 | 
 939 |         throw new UnexpectedStateError("Unknown resource request", {
 940 |           request,
 941 |         });
 942 |       },
 943 |     );
 944 |   }
 945 | 
 946 |   private setupResourceTemplateHandlers(resourceTemplates: ResourceTemplate[]) {
 947 |     this.#server.setRequestHandler(
 948 |       ListResourceTemplatesRequestSchema,
 949 |       async () => {
 950 |         return {
 951 |           resourceTemplates: resourceTemplates.map((resourceTemplate) => {
 952 |             return {
 953 |               name: resourceTemplate.name,
 954 |               uriTemplate: resourceTemplate.uriTemplate,
 955 |             };
 956 |           }),
 957 |         };
 958 |       },
 959 |     );
 960 |   }
 961 | 
 962 |   private setupPromptHandlers(prompts: Prompt[]) {
 963 |     this.#server.setRequestHandler(ListPromptsRequestSchema, async () => {
 964 |       return {
 965 |         prompts: prompts.map((prompt) => {
 966 |           return {
 967 |             name: prompt.name,
 968 |             description: prompt.description,
 969 |             arguments: prompt.arguments,
 970 |             complete: prompt.complete,
 971 |           };
 972 |         }),
 973 |       };
 974 |     });
 975 | 
 976 |     this.#server.setRequestHandler(GetPromptRequestSchema, async (request) => {
 977 |       const prompt = prompts.find(
 978 |         (prompt) => prompt.name === request.params.name,
 979 |       );
 980 | 
 981 |       if (!prompt) {
 982 |         throw new McpError(
 983 |           ErrorCode.MethodNotFound,
 984 |           `Unknown prompt: ${request.params.name}`,
 985 |         );
 986 |       }
 987 | 
 988 |       const args = request.params.arguments;
 989 | 
 990 |       for (const arg of prompt.arguments ?? []) {
 991 |         if (arg.required && !(args && arg.name in args)) {
 992 |           throw new McpError(
 993 |             ErrorCode.InvalidRequest,
 994 |             `Missing required argument: ${arg.name}`,
 995 |           );
 996 |         }
 997 |       }
 998 | 
 999 |       let result: Awaited<ReturnType<Prompt["load"]>>;
1000 | 
1001 |       try {
1002 |         result = await prompt.load(args as Record<string, string | undefined>);
1003 |       } catch (error) {
1004 |         throw new McpError(
1005 |           ErrorCode.InternalError,
1006 |           `Error loading prompt: ${error}`,
1007 |         );
1008 |       }
1009 | 
1010 |       return {
1011 |         description: prompt.description,
1012 |         messages: [
1013 |           {
1014 |             role: "user",
1015 |             content: { type: "text", text: result },
1016 |           },
1017 |         ],
1018 |       };
1019 |     });
1020 |   }
1021 | }
1022 | 
1023 | const FastMCPEventEmitterBase: {
1024 |   new (): StrictEventEmitter<EventEmitter, FastMCPEvents<FastMCPSessionAuth>>;
1025 | } = EventEmitter;
1026 | 
1027 | class FastMCPEventEmitter extends FastMCPEventEmitterBase {}
1028 | 
1029 | type Authenticate<T> = (request: http.IncomingMessage) => Promise<T>;
1030 | 
1031 | export class FastMCP<T extends Record<string, unknown> | undefined = undefined> extends FastMCPEventEmitter {
1032 |   #options: ServerOptions<T>;
1033 |   #prompts: InputPrompt[] = [];
1034 |   #resources: Resource[] = [];
1035 |   #resourcesTemplates: InputResourceTemplate[] = [];
1036 |   #sessions: FastMCPSession<T>[] = [];
1037 |   #sseServer: SSEServer | null = null;
1038 |   #tools: Tool<T>[] = [];
1039 |   #authenticate: Authenticate<T> | undefined;
1040 | 
1041 |   constructor(public options: ServerOptions<T>) {
1042 |     super();
1043 | 
1044 |     this.#options = options;
1045 |     this.#authenticate = options.authenticate;
1046 |   }
1047 | 
1048 |   public get sessions(): FastMCPSession<T>[] {
1049 |     return this.#sessions;
1050 |   }
1051 | 
1052 |   /**
1053 |    * Adds a tool to the server.
1054 |    */
1055 |   public addTool<Params extends ToolParameters>(tool: Tool<T, Params>) {
1056 |     this.#tools.push(tool as unknown as Tool<T>);
1057 |   }
1058 | 
1059 |   /**
1060 |    * Adds a resource to the server.
1061 |    */
1062 |   public addResource(resource: Resource) {
1063 |     this.#resources.push(resource);
1064 |   }
1065 | 
1066 |   /**
1067 |    * Adds a resource template to the server.
1068 |    */
1069 |   public addResourceTemplate<
1070 |     const Args extends InputResourceTemplateArgument[],
1071 |   >(resource: InputResourceTemplate<Args>) {
1072 |     this.#resourcesTemplates.push(resource);
1073 |   }
1074 | 
1075 |   /**
1076 |    * Adds a prompt to the server.
1077 |    */
1078 |   public addPrompt<const Args extends InputPromptArgument[]>(
1079 |     prompt: InputPrompt<Args>,
1080 |   ) {
1081 |     this.#prompts.push(prompt);
1082 |   }
1083 | 
1084 |   /**
1085 |    * Starts the server.
1086 |    */
1087 |   public async start(
1088 |     options:
1089 |       | { transportType: "stdio" }
1090 |       | {
1091 |           transportType: "sse";
1092 |           sse: { endpoint: `/${string}`; port: number };
1093 |         } = {
1094 |       transportType: "stdio",
1095 |     },
1096 |   ) {
1097 |     if (options.transportType === "stdio") {
1098 |       const transport = new StdioServerTransport();
1099 | 
1100 |       const session = new FastMCPSession<T>({
1101 |         name: this.#options.name,
1102 |         version: this.#options.version,
1103 |         tools: this.#tools,
1104 |         resources: this.#resources,
1105 |         resourcesTemplates: this.#resourcesTemplates,
1106 |         prompts: this.#prompts,
1107 |       });
1108 | 
1109 |       await session.connect(transport);
1110 | 
1111 |       this.#sessions.push(session);
1112 | 
1113 |       this.emit("connect", {
1114 |         session,
1115 |       });
1116 | 
1117 |     } else if (options.transportType === "sse") {
1118 |       this.#sseServer = await startSSEServer<FastMCPSession<T>>({
1119 |         endpoint: options.sse.endpoint as `/${string}`,
1120 |         port: options.sse.port,
1121 |         createServer: async (request) => {
1122 |           let auth: T | undefined;
1123 | 
1124 |           if (this.#authenticate) {
1125 |             auth = await this.#authenticate(request);
1126 |           }
1127 | 
1128 |           return new FastMCPSession<T>({
1129 |             auth,
1130 |             name: this.#options.name,
1131 |             version: this.#options.version,
1132 |             tools: this.#tools,
1133 |             resources: this.#resources,
1134 |             resourcesTemplates: this.#resourcesTemplates,
1135 |             prompts: this.#prompts,
1136 |           });
1137 |         },
1138 |         onClose: (session) => {
1139 |           this.emit("disconnect", {
1140 |             session,
1141 |           });
1142 |         },
1143 |         onConnect: async (session) => {
1144 |           this.#sessions.push(session);
1145 | 
1146 |           this.emit("connect", {
1147 |             session,
1148 |           });
1149 |         },
1150 |       });
1151 | 
1152 |       console.info(
1153 |         `server is running on SSE at http://localhost:${options.sse.port}${options.sse.endpoint}`,
1154 |       );
1155 |     } else {
1156 |       throw new Error("Invalid transport type");
1157 |     }
1158 |   }
1159 | 
1160 |   /**
1161 |    * Stops the server.
1162 |    */
1163 |   public async stop() {
1164 |     if (this.#sseServer) {
1165 |       this.#sseServer.close();
1166 |     }
1167 |   }
1168 | }
1169 | 
1170 | export type { Context };
1171 | export type { Tool, ToolParameters };
1172 | export type { Content, TextContent, ImageContent, ContentResult };
1173 | export type { Progress, SerializableValue };
1174 | export type { Resource, ResourceResult };
1175 | export type { ResourceTemplate, ResourceTemplateArgument };
1176 | export type { Prompt, PromptArgument };
1177 | export type { InputPrompt, InputPromptArgument };
1178 | export type { ServerOptions, LoggingLevel };
1179 | export type { FastMCPEvents, FastMCPSessionEvents };
```

--------------------------------------------------------------------------------
/.kiro/steering/dev_workflow.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | inclusion: always
  3 | ---
  4 | 
  5 | # Taskmaster Development Workflow
  6 | 
  7 | This guide outlines the standard process for using Taskmaster to manage software development projects. It is written as a set of instructions for you, the AI agent.
  8 | 
  9 | - **Your Default Stance**: For most projects, the user can work directly within the `master` task context. Your initial actions should operate on this default context unless a clear pattern for multi-context work emerges.
 10 | - **Your Goal**: Your role is to elevate the user's workflow by intelligently introducing advanced features like **Tagged Task Lists** when you detect the appropriate context. Do not force tags on the user; suggest them as a helpful solution to a specific need.
 11 | 
 12 | ## The Basic Loop
 13 | The fundamental development cycle you will facilitate is:
 14 | 1.  **`list`**: Show the user what needs to be done.
 15 | 2.  **`next`**: Help the user decide what to work on.
 16 | 3.  **`show <id>`**: Provide details for a specific task.
 17 | 4.  **`expand <id>`**: Break down a complex task into smaller, manageable subtasks.
 18 | 5.  **Implement**: The user writes the code and tests.
 19 | 6.  **`update-subtask`**: Log progress and findings on behalf of the user.
 20 | 7.  **`set-status`**: Mark tasks and subtasks as `done` as work is completed.
 21 | 8.  **Repeat**.
 22 | 
 23 | All your standard command executions should operate on the user's current task context, which defaults to `master`.
 24 | 
 25 | ---
 26 | 
 27 | ## Standard Development Workflow Process
 28 | 
 29 | ### Simple Workflow (Default Starting Point)
 30 | 
 31 | For new projects or when users are getting started, operate within the `master` tag context:
 32 | 
 33 | -   Start new projects by running `initialize_project` tool / `task-master init` or `parse_prd` / `task-master parse-prd --input='<prd-file.txt>'` (see @`taskmaster.md`) to generate initial tasks.json with tagged structure
 34 | -   Configure rule sets during initialization with `--rules` flag (e.g., `task-master init --rules kiro,windsurf`) or manage them later with `task-master rules add/remove` commands  
 35 | -   Begin coding sessions with `get_tasks` / `task-master list` (see @`taskmaster.md`) to see current tasks, status, and IDs
 36 | -   Determine the next task to work on using `next_task` / `task-master next` (see @`taskmaster.md`)
 37 | -   Analyze task complexity with `analyze_project_complexity` / `task-master analyze-complexity --research` (see @`taskmaster.md`) before breaking down tasks
 38 | -   Review complexity report using `complexity_report` / `task-master complexity-report` (see @`taskmaster.md`)
 39 | -   Select tasks based on dependencies (all marked 'done'), priority level, and ID order
 40 | -   View specific task details using `get_task` / `task-master show <id>` (see @`taskmaster.md`) to understand implementation requirements
 41 | -   Break down complex tasks using `expand_task` / `task-master expand --id=<id> --force --research` (see @`taskmaster.md`) with appropriate flags like `--force` (to replace existing subtasks) and `--research`
 42 | -   Implement code following task details, dependencies, and project standards
 43 | -   Mark completed tasks with `set_task_status` / `task-master set-status --id=<id> --status=done` (see @`taskmaster.md`)
 44 | -   Update dependent tasks when implementation differs from original plan using `update` / `task-master update --from=<id> --prompt="..."` or `update_task` / `task-master update-task --id=<id> --prompt="..."` (see @`taskmaster.md`)
 45 | 
 46 | ---
 47 | 
 48 | ## Leveling Up: Agent-Led Multi-Context Workflows
 49 | 
 50 | While the basic workflow is powerful, your primary opportunity to add value is by identifying when to introduce **Tagged Task Lists**. These patterns are your tools for creating a more organized and efficient development environment for the user, especially if you detect agentic or parallel development happening across the same session.
 51 | 
 52 | **Critical Principle**: Most users should never see a difference in their experience. Only introduce advanced workflows when you detect clear indicators that the project has evolved beyond simple task management.
 53 | 
 54 | ### When to Introduce Tags: Your Decision Patterns
 55 | 
 56 | Here are the patterns to look for. When you detect one, you should propose the corresponding workflow to the user.
 57 | 
 58 | #### Pattern 1: Simple Git Feature Branching
 59 | This is the most common and direct use case for tags.
 60 | 
 61 | - **Trigger**: The user creates a new git branch (e.g., `git checkout -b feature/user-auth`).
 62 | - **Your Action**: Propose creating a new tag that mirrors the branch name to isolate the feature's tasks from `master`.
 63 | - **Your Suggested Prompt**: *"I see you've created a new branch named 'feature/user-auth'. To keep all related tasks neatly organized and separate from your main list, I can create a corresponding task tag for you. This helps prevent merge conflicts in your `tasks.json` file later. Shall I create the 'feature-user-auth' tag?"*
 64 | - **Tool to Use**: `task-master add-tag --from-branch`
 65 | 
 66 | #### Pattern 2: Team Collaboration
 67 | - **Trigger**: The user mentions working with teammates (e.g., "My teammate Alice is handling the database schema," or "I need to review Bob's work on the API.").
 68 | - **Your Action**: Suggest creating a separate tag for the user's work to prevent conflicts with shared master context.
 69 | - **Your Suggested Prompt**: *"Since you're working with Alice, I can create a separate task context for your work to avoid conflicts. This way, Alice can continue working with the master list while you have your own isolated context. When you're ready to merge your work, we can coordinate the tasks back to master. Shall I create a tag for your current work?"*
 70 | - **Tool to Use**: `task-master add-tag my-work --copy-from-current --description="My tasks while collaborating with Alice"`
 71 | 
 72 | #### Pattern 3: Experiments or Risky Refactors
 73 | - **Trigger**: The user wants to try something that might not be kept (e.g., "I want to experiment with switching our state management library," or "Let's refactor the old API module, but I want to keep the current tasks as a reference.").
 74 | - **Your Action**: Propose creating a sandboxed tag for the experimental work.
 75 | - **Your Suggested Prompt**: *"This sounds like a great experiment. To keep these new tasks separate from our main plan, I can create a temporary 'experiment-zustand' tag for this work. If we decide not to proceed, we can simply delete the tag without affecting the main task list. Sound good?"*
 76 | - **Tool to Use**: `task-master add-tag experiment-zustand --description="Exploring Zustand migration"`
 77 | 
 78 | #### Pattern 4: Large Feature Initiatives (PRD-Driven)
 79 | This is a more structured approach for significant new features or epics.
 80 | 
 81 | - **Trigger**: The user describes a large, multi-step feature that would benefit from a formal plan.
 82 | - **Your Action**: Propose a comprehensive, PRD-driven workflow.
 83 | - **Your Suggested Prompt**: *"This sounds like a significant new feature. To manage this effectively, I suggest we create a dedicated task context for it. Here's the plan: I'll create a new tag called 'feature-xyz', then we can draft a Product Requirements Document (PRD) together to scope the work. Once the PRD is ready, I'll automatically generate all the necessary tasks within that new tag. How does that sound?"*
 84 | - **Your Implementation Flow**:
 85 |     1.  **Create an empty tag**: `task-master add-tag feature-xyz --description "Tasks for the new XYZ feature"`. You can also start by creating a git branch if applicable, and then create the tag from that branch.
 86 |     2.  **Collaborate & Create PRD**: Work with the user to create a detailed PRD file (e.g., `.taskmaster/docs/feature-xyz-prd.txt`).
 87 |     3.  **Parse PRD into the new tag**: `task-master parse-prd .taskmaster/docs/feature-xyz-prd.txt --tag feature-xyz`
 88 |     4.  **Prepare the new task list**: Follow up by suggesting `analyze-complexity` and `expand-all` for the newly created tasks within the `feature-xyz` tag.
 89 | 
 90 | #### Pattern 5: Version-Based Development
 91 | Tailor your approach based on the project maturity indicated by tag names.
 92 | 
 93 | - **Prototype/MVP Tags** (`prototype`, `mvp`, `poc`, `v0.x`):
 94 |   - **Your Approach**: Focus on speed and functionality over perfection
 95 |   - **Task Generation**: Create tasks that emphasize "get it working" over "get it perfect"
 96 |   - **Complexity Level**: Lower complexity, fewer subtasks, more direct implementation paths
 97 |   - **Research Prompts**: Include context like "This is a prototype - prioritize speed and basic functionality over optimization"
 98 |   - **Example Prompt Addition**: *"Since this is for the MVP, I'll focus on tasks that get core functionality working quickly rather than over-engineering."*
 99 | 
100 | - **Production/Mature Tags** (`v1.0+`, `production`, `stable`):
101 |   - **Your Approach**: Emphasize robustness, testing, and maintainability
102 |   - **Task Generation**: Include comprehensive error handling, testing, documentation, and optimization
103 |   - **Complexity Level**: Higher complexity, more detailed subtasks, thorough implementation paths
104 |   - **Research Prompts**: Include context like "This is for production - prioritize reliability, performance, and maintainability"
105 |   - **Example Prompt Addition**: *"Since this is for production, I'll ensure tasks include proper error handling, testing, and documentation."*
106 | 
107 | ### Advanced Workflow (Tag-Based & PRD-Driven)
108 | 
109 | **When to Transition**: Recognize when the project has evolved (or has initiated a project which existing code) beyond simple task management. Look for these indicators:
110 | - User mentions teammates or collaboration needs
111 | - Project has grown to 15+ tasks with mixed priorities
112 | - User creates feature branches or mentions major initiatives
113 | - User initializes Taskmaster on an existing, complex codebase
114 | - User describes large features that would benefit from dedicated planning
115 | 
116 | **Your Role in Transition**: Guide the user to a more sophisticated workflow that leverages tags for organization and PRDs for comprehensive planning.
117 | 
118 | #### Master List Strategy (High-Value Focus)
119 | Once you transition to tag-based workflows, the `master` tag should ideally contain only:
120 | - **High-level deliverables** that provide significant business value
121 | - **Major milestones** and epic-level features
122 | - **Critical infrastructure** work that affects the entire project
123 | - **Release-blocking** items
124 | 
125 | **What NOT to put in master**:
126 | - Detailed implementation subtasks (these go in feature-specific tags' parent tasks)
127 | - Refactoring work (create dedicated tags like `refactor-auth`)
128 | - Experimental features (use `experiment-*` tags)
129 | - Team member-specific tasks (use person-specific tags)
130 | 
131 | #### PRD-Driven Feature Development
132 | 
133 | **For New Major Features**:
134 | 1. **Identify the Initiative**: When user describes a significant feature
135 | 2. **Create Dedicated Tag**: `add_tag feature-[name] --description="[Feature description]"`
136 | 3. **Collaborative PRD Creation**: Work with user to create comprehensive PRD in `.taskmaster/docs/feature-[name]-prd.txt`
137 | 4. **Parse & Prepare**: 
138 |    - `parse_prd .taskmaster/docs/feature-[name]-prd.txt --tag=feature-[name]`
139 |    - `analyze_project_complexity --tag=feature-[name] --research`
140 |    - `expand_all --tag=feature-[name] --research`
141 | 5. **Add Master Reference**: Create a high-level task in `master` that references the feature tag
142 | 
143 | **For Existing Codebase Analysis**:
144 | When users initialize Taskmaster on existing projects:
145 | 1. **Codebase Discovery**: Use your native tools for producing deep context about the code base. You may use `research` tool with `--tree` and `--files` to collect up to date information using the existing architecture as context.
146 | 2. **Collaborative Assessment**: Work with user to identify improvement areas, technical debt, or new features
147 | 3. **Strategic PRD Creation**: Co-author PRDs that include:
148 |    - Current state analysis (based on your codebase research)
149 |    - Proposed improvements or new features
150 |    - Implementation strategy considering existing code
151 | 4. **Tag-Based Organization**: Parse PRDs into appropriate tags (`refactor-api`, `feature-dashboard`, `tech-debt`, etc.)
152 | 5. **Master List Curation**: Keep only the most valuable initiatives in master
153 | 
154 | The parse-prd's `--append` flag enables the user to parse multiple PRDs within tags or across tags. PRDs should be focused and the number of tasks they are parsed into should be strategically chosen relative to the PRD's complexity and level of detail.
155 | 
156 | ### Workflow Transition Examples
157 | 
158 | **Example 1: Simple → Team-Based**
159 | ```
160 | User: "Alice is going to help with the API work"
161 | Your Response: "Great! To avoid conflicts, I'll create a separate task context for your work. Alice can continue with the master list while you work in your own context. When you're ready to merge, we can coordinate the tasks back together."
162 | Action: add_tag my-api-work --copy-from-current --description="My API tasks while collaborating with Alice"
163 | ```
164 | 
165 | **Example 2: Simple → PRD-Driven**
166 | ```
167 | User: "I want to add a complete user dashboard with analytics, user management, and reporting"
168 | Your Response: "This sounds like a major feature that would benefit from detailed planning. Let me create a dedicated context for this work and we can draft a PRD together to ensure we capture all requirements."
169 | Actions: 
170 | 1. add_tag feature-dashboard --description="User dashboard with analytics and management"
171 | 2. Collaborate on PRD creation
172 | 3. parse_prd dashboard-prd.txt --tag=feature-dashboard
173 | 4. Add high-level "User Dashboard" task to master
174 | ```
175 | 
176 | **Example 3: Existing Project → Strategic Planning**
177 | ```
178 | User: "I just initialized Taskmaster on my existing React app. It's getting messy and I want to improve it."
179 | Your Response: "Let me research your codebase to understand the current architecture, then we can create a strategic plan for improvements."
180 | Actions:
181 | 1. research "Current React app architecture and improvement opportunities" --tree --files=src/
182 | 2. Collaborate on improvement PRD based on findings
183 | 3. Create tags for different improvement areas (refactor-components, improve-state-management, etc.)
184 | 4. Keep only major improvement initiatives in master
185 | ```
186 | 
187 | ---
188 | 
189 | ## Primary Interaction: MCP Server vs. CLI
190 | 
191 | Taskmaster offers two primary ways to interact:
192 | 
193 | 1.  **MCP Server (Recommended for Integrated Tools)**:
194 |     - For AI agents and integrated development environments (like Kiro), interacting via the **MCP server is the preferred method**.
195 |     - The MCP server exposes Taskmaster functionality through a set of tools (e.g., `get_tasks`, `add_subtask`).
196 |     - This method offers better performance, structured data exchange, and richer error handling compared to CLI parsing.
197 |     - Refer to @`mcp.md` for details on the MCP architecture and available tools.
198 |     - A comprehensive list and description of MCP tools and their corresponding CLI commands can be found in @`taskmaster.md`.
199 |     - **Restart the MCP server** if core logic in `scripts/modules` or MCP tool/direct function definitions change.
200 |     - **Note**: MCP tools fully support tagged task lists with complete tag management capabilities.
201 | 
202 | 2.  **`task-master` CLI (For Users & Fallback)**:
203 |     - The global `task-master` command provides a user-friendly interface for direct terminal interaction.
204 |     - It can also serve as a fallback if the MCP server is inaccessible or a specific function isn't exposed via MCP.
205 |     - Install globally with `npm install -g task-master-ai` or use locally via `npx task-master-ai ...`.
206 |     - The CLI commands often mirror the MCP tools (e.g., `task-master list` corresponds to `get_tasks`).
207 |     - Refer to @`taskmaster.md` for a detailed command reference.
208 |     - **Tagged Task Lists**: CLI fully supports the new tagged system with seamless migration.
209 | 
210 | ## How the Tag System Works (For Your Reference)
211 | 
212 | - **Data Structure**: Tasks are organized into separate contexts (tags) like "master", "feature-branch", or "v2.0".
213 | - **Silent Migration**: Existing projects automatically migrate to use a "master" tag with zero disruption.
214 | - **Context Isolation**: Tasks in different tags are completely separate. Changes in one tag do not affect any other tag.
215 | - **Manual Control**: The user is always in control. There is no automatic switching. You facilitate switching by using `use-tag <name>`.
216 | - **Full CLI & MCP Support**: All tag management commands are available through both the CLI and MCP tools for you to use. Refer to @`taskmaster.md` for a full command list.
217 | 
218 | ---
219 | 
220 | ## Task Complexity Analysis
221 | 
222 | -   Run `analyze_project_complexity` / `task-master analyze-complexity --research` (see @`taskmaster.md`) for comprehensive analysis
223 | -   Review complexity report via `complexity_report` / `task-master complexity-report` (see @`taskmaster.md`) for a formatted, readable version.
224 | -   Focus on tasks with highest complexity scores (8-10) for detailed breakdown
225 | -   Use analysis results to determine appropriate subtask allocation
226 | -   Note that reports are automatically used by the `expand_task` tool/command
227 | 
228 | ## Task Breakdown Process
229 | 
230 | -   Use `expand_task` / `task-master expand --id=<id>`. It automatically uses the complexity report if found, otherwise generates default number of subtasks.
231 | -   Use `--num=<number>` to specify an explicit number of subtasks, overriding defaults or complexity report recommendations.
232 | -   Add `--research` flag to leverage Perplexity AI for research-backed expansion.
233 | -   Add `--force` flag to clear existing subtasks before generating new ones (default is to append).
234 | -   Use `--prompt="<context>"` to provide additional context when needed.
235 | -   Review and adjust generated subtasks as necessary.
236 | -   Use `expand_all` tool or `task-master expand --all` to expand multiple pending tasks at once, respecting flags like `--force` and `--research`.
237 | -   If subtasks need complete replacement (regardless of the `--force` flag on `expand`), clear them first with `clear_subtasks` / `task-master clear-subtasks --id=<id>`.
238 | 
239 | ## Implementation Drift Handling
240 | 
241 | -   When implementation differs significantly from planned approach
242 | -   When future tasks need modification due to current implementation choices
243 | -   When new dependencies or requirements emerge
244 | -   Use `update` / `task-master update --from=<futureTaskId> --prompt='<explanation>\nUpdate context...' --research` to update multiple future tasks.
245 | -   Use `update_task` / `task-master update-task --id=<taskId> --prompt='<explanation>\nUpdate context...' --research` to update a single specific task.
246 | 
247 | ## Task Status Management
248 | 
249 | -   Use 'pending' for tasks ready to be worked on
250 | -   Use 'done' for completed and verified tasks
251 | -   Use 'deferred' for postponed tasks
252 | -   Add custom status values as needed for project-specific workflows
253 | 
254 | ## Task Structure Fields
255 | 
256 | - **id**: Unique identifier for the task (Example: `1`, `1.1`)
257 | - **title**: Brief, descriptive title (Example: `"Initialize Repo"`)
258 | - **description**: Concise summary of what the task involves (Example: `"Create a new repository, set up initial structure."`)
259 | - **status**: Current state of the task (Example: `"pending"`, `"done"`, `"deferred"`)
260 | - **dependencies**: IDs of prerequisite tasks (Example: `[1, 2.1]`)
261 |     - Dependencies are displayed with status indicators (✅ for completed, ⏱️ for pending)
262 |     - This helps quickly identify which prerequisite tasks are blocking work
263 | - **priority**: Importance level (Example: `"high"`, `"medium"`, `"low"`)
264 | - **details**: In-depth implementation instructions (Example: `"Use GitHub client ID/secret, handle callback, set session token."`) 
265 | - **testStrategy**: Verification approach (Example: `"Deploy and call endpoint to confirm 'Hello World' response."`) 
266 | - **subtasks**: List of smaller, more specific tasks (Example: `[{"id": 1, "title": "Configure OAuth", ...}]`) 
267 | - Refer to task structure details (previously linked to `tasks.md`).
268 | 
269 | ## Configuration Management (Updated)
270 | 
271 | Taskmaster configuration is managed through two main mechanisms:
272 | 
273 | 1.  **`.taskmaster/config.json` File (Primary):**
274 |     *   Located in the project root directory.
275 |     *   Stores most configuration settings: AI model selections (main, research, fallback), parameters (max tokens, temperature), logging level, default subtasks/priority, project name, etc.
276 |     *   **Tagged System Settings**: Includes `global.defaultTag` (defaults to "master") and `tags` section for tag management configuration.
277 |     *   **Managed via `task-master models --setup` command.** Do not edit manually unless you know what you are doing.
278 |     *   **View/Set specific models via `task-master models` command or `models` MCP tool.**
279 |     *   Created automatically when you run `task-master models --setup` for the first time or during tagged system migration.
280 | 
281 | 2.  **Environment Variables (`.env` / `mcp.json`):**
282 |     *   Used **only** for sensitive API keys and specific endpoint URLs.
283 |     *   Place API keys (one per provider) in a `.env` file in the project root for CLI usage.
284 |     *   For MCP/Kiro integration, configure these keys in the `env` section of `.kiro/mcp.json`.
285 |     *   Available keys/variables: See `assets/env.example` or the Configuration section in the command reference (previously linked to `taskmaster.md`).
286 | 
287 | 3.  **`.taskmaster/state.json` File (Tagged System State):**
288 |     *   Tracks current tag context and migration status.
289 |     *   Automatically created during tagged system migration.
290 |     *   Contains: `currentTag`, `lastSwitched`, `migrationNoticeShown`.
291 | 
292 | **Important:** Non-API key settings (like model selections, `MAX_TOKENS`, `TASKMASTER_LOG_LEVEL`) are **no longer configured via environment variables**. Use the `task-master models` command (or `--setup` for interactive configuration) or the `models` MCP tool.
293 | **If AI commands FAIL in MCP** verify that the API key for the selected provider is present in the `env` section of `.kiro/mcp.json`.
294 | **If AI commands FAIL in CLI** verify that the API key for the selected provider is present in the `.env` file in the root of the project.
295 | 
296 | ## Rules Management
297 | 
298 | Taskmaster supports multiple AI coding assistant rule sets that can be configured during project initialization or managed afterward:
299 | 
300 | - **Available Profiles**: Claude Code, Cline, Codex, Kiro, Roo Code, Trae, Windsurf (claude, cline, codex, kiro, roo, trae, windsurf)
301 | - **During Initialization**: Use `task-master init --rules kiro,windsurf` to specify which rule sets to include
302 | - **After Initialization**: Use `task-master rules add <profiles>` or `task-master rules remove <profiles>` to manage rule sets
303 | - **Interactive Setup**: Use `task-master rules setup` to launch an interactive prompt for selecting rule profiles
304 | - **Default Behavior**: If no `--rules` flag is specified during initialization, all available rule profiles are included
305 | - **Rule Structure**: Each profile creates its own directory (e.g., `.kiro/steering`, `.roo/rules`) with appropriate configuration files
306 | 
307 | ## Determining the Next Task
308 | 
309 | - Run `next_task` / `task-master next` to show the next task to work on.
310 | - The command identifies tasks with all dependencies satisfied
311 | - Tasks are prioritized by priority level, dependency count, and ID
312 | - The command shows comprehensive task information including:
313 |     - Basic task details and description
314 |     - Implementation details
315 |     - Subtasks (if they exist)
316 |     - Contextual suggested actions
317 | - Recommended before starting any new development work
318 | - Respects your project's dependency structure
319 | - Ensures tasks are completed in the appropriate sequence
320 | - Provides ready-to-use commands for common task actions
321 | 
322 | ## Viewing Specific Task Details
323 | 
324 | - Run `get_task` / `task-master show <id>` to view a specific task.
325 | - Use dot notation for subtasks: `task-master show 1.2` (shows subtask 2 of task 1)
326 | - Displays comprehensive information similar to the next command, but for a specific task
327 | - For parent tasks, shows all subtasks and their current status
328 | - For subtasks, shows parent task information and relationship
329 | - Provides contextual suggested actions appropriate for the specific task
330 | - Useful for examining task details before implementation or checking status
331 | 
332 | ## Managing Task Dependencies
333 | 
334 | - Use `add_dependency` / `task-master add-dependency --id=<id> --depends-on=<id>` to add a dependency.
335 | - Use `remove_dependency` / `task-master remove-dependency --id=<id> --depends-on=<id>` to remove a dependency.
336 | - The system prevents circular dependencies and duplicate dependency entries
337 | - Dependencies are checked for existence before being added or removed
338 | - Task files are automatically regenerated after dependency changes
339 | - Dependencies are visualized with status indicators in task listings and files
340 | 
341 | ## Task Reorganization
342 | 
343 | - Use `move_task` / `task-master move --from=<id> --to=<id>` to move tasks or subtasks within the hierarchy
344 | - This command supports several use cases:
345 |   - Moving a standalone task to become a subtask (e.g., `--from=5 --to=7`)
346 |   - Moving a subtask to become a standalone task (e.g., `--from=5.2 --to=7`) 
347 |   - Moving a subtask to a different parent (e.g., `--from=5.2 --to=7.3`)
348 |   - Reordering subtasks within the same parent (e.g., `--from=5.2 --to=5.4`)
349 |   - Moving a task to a new, non-existent ID position (e.g., `--from=5 --to=25`)
350 |   - Moving multiple tasks at once using comma-separated IDs (e.g., `--from=10,11,12 --to=16,17,18`)
351 | - The system includes validation to prevent data loss:
352 |   - Allows moving to non-existent IDs by creating placeholder tasks
353 |   - Prevents moving to existing task IDs that have content (to avoid overwriting)
354 |   - Validates source tasks exist before attempting to move them
355 | - The system maintains proper parent-child relationships and dependency integrity
356 | - Task files are automatically regenerated after the move operation
357 | - This provides greater flexibility in organizing and refining your task structure as project understanding evolves
358 | - This is especially useful when dealing with potential merge conflicts arising from teams creating tasks on separate branches. Solve these conflicts very easily by moving your tasks and keeping theirs.
359 | 
360 | ## Iterative Subtask Implementation
361 | 
362 | Once a task has been broken down into subtasks using `expand_task` or similar methods, follow this iterative process for implementation:
363 | 
364 | 1.  **Understand the Goal (Preparation):**
365 |     *   Use `get_task` / `task-master show <subtaskId>` (see @`taskmaster.md`) to thoroughly understand the specific goals and requirements of the subtask.
366 | 
367 | 2.  **Initial Exploration & Planning (Iteration 1):**
368 |     *   This is the first attempt at creating a concrete implementation plan.
369 |     *   Explore the codebase to identify the precise files, functions, and even specific lines of code that will need modification.
370 |     *   Determine the intended code changes (diffs) and their locations.
371 |     *   Gather *all* relevant details from this exploration phase.
372 | 
373 | 3.  **Log the Plan:**
374 |     *   Run `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='<detailed plan>'`.
375 |     *   Provide the *complete and detailed* findings from the exploration phase in the prompt. Include file paths, line numbers, proposed diffs, reasoning, and any potential challenges identified. Do not omit details. The goal is to create a rich, timestamped log within the subtask's `details`.
376 | 
377 | 4.  **Verify the Plan:**
378 |     *   Run `get_task` / `task-master show <subtaskId>` again to confirm that the detailed implementation plan has been successfully appended to the subtask's details.
379 | 
380 | 5.  **Begin Implementation:**
381 |     *   Set the subtask status using `set_task_status` / `task-master set-status --id=<subtaskId> --status=in-progress`.
382 |     *   Start coding based on the logged plan.
383 | 
384 | 6.  **Refine and Log Progress (Iteration 2+):**
385 |     *   As implementation progresses, you will encounter challenges, discover nuances, or confirm successful approaches.
386 |     *   **Before appending new information**: Briefly review the *existing* details logged in the subtask (using `get_task` or recalling from context) to ensure the update adds fresh insights and avoids redundancy.
387 |     *   **Regularly** use `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='<update details>\n- What worked...\n- What didn't work...'` to append new findings.
388 |     *   **Crucially, log:**
389 |         *   What worked ("fundamental truths" discovered).
390 |         *   What didn't work and why (to avoid repeating mistakes).
391 |         *   Specific code snippets or configurations that were successful.
392 |         *   Decisions made, especially if confirmed with user input.
393 |         *   Any deviations from the initial plan and the reasoning.
394 |     *   The objective is to continuously enrich the subtask's details, creating a log of the implementation journey that helps the AI (and human developers) learn, adapt, and avoid repeating errors.
395 | 
396 | 7.  **Review & Update Rules (Post-Implementation):**
397 |     *   Once the implementation for the subtask is functionally complete, review all code changes and the relevant chat history.
398 |     *   Identify any new or modified code patterns, conventions, or best practices established during the implementation.
399 |     *   Create new or update existing rules following internal guidelines (previously linked to `cursor_rules.md` and `self_improve.md`).
400 | 
401 | 8.  **Mark Task Complete:**
402 |     *   After verifying the implementation and updating any necessary rules, mark the subtask as completed: `set_task_status` / `task-master set-status --id=<subtaskId> --status=done`.
403 | 
404 | 9.  **Commit Changes (If using Git):**
405 |     *   Stage the relevant code changes and any updated/new rule files (`git add .`).
406 |     *   Craft a comprehensive Git commit message summarizing the work done for the subtask, including both code implementation and any rule adjustments.
407 |     *   Execute the commit command directly in the terminal (e.g., `git commit -m 'feat(module): Implement feature X for subtask <subtaskId>\n\n- Details about changes...\n- Updated rule Y for pattern Z'`).
408 |     *   Consider if a Changeset is needed according to internal versioning guidelines (previously linked to `changeset.md`). If so, run `npm run changeset`, stage the generated file, and amend the commit or create a new one.
409 | 
410 | 10. **Proceed to Next Subtask:**
411 |     *   Identify the next subtask (e.g., using `next_task` / `task-master next`).
412 | 
413 | ## Code Analysis & Refactoring Techniques
414 | 
415 | - **Top-Level Function Search**:
416 |     - Useful for understanding module structure or planning refactors.
417 |     - Use grep/ripgrep to find exported functions/constants:
418 |       `rg "export (async function|function|const) \w+"` or similar patterns.
419 |     - Can help compare functions between files during migrations or identify potential naming conflicts.
420 | 
421 | ---
422 | *This workflow provides a general guideline. Adapt it based on your specific project needs and team practices.*
```

--------------------------------------------------------------------------------
/scripts/modules/task-manager/move-task.js:
--------------------------------------------------------------------------------

```javascript
   1 | import path from 'path';
   2 | import {
   3 | 	log,
   4 | 	readJSON,
   5 | 	writeJSON,
   6 | 	setTasksForTag,
   7 | 	traverseDependencies
   8 | } from '../utils.js';
   9 | import generateTaskFiles from './generate-task-files.js';
  10 | import {
  11 | 	findCrossTagDependencies,
  12 | 	getDependentTaskIds,
  13 | 	validateSubtaskMove
  14 | } from '../dependency-manager.js';
  15 | 
  16 | /**
  17 |  * Find all dependencies recursively for a set of source tasks with depth limiting
  18 |  * @param {Array} sourceTasks - The source tasks to find dependencies for
  19 |  * @param {Array} allTasks - All available tasks from all tags
  20 |  * @param {Object} options - Options object
  21 |  * @param {number} options.maxDepth - Maximum recursion depth (default: 50)
  22 |  * @param {boolean} options.includeSelf - Whether to include self-references (default: false)
  23 |  * @returns {Array} Array of all dependency task IDs
  24 |  */
  25 | function findAllDependenciesRecursively(sourceTasks, allTasks, options = {}) {
  26 | 	return traverseDependencies(sourceTasks, allTasks, {
  27 | 		...options,
  28 | 		direction: 'forward',
  29 | 		logger: { warn: console.warn }
  30 | 	});
  31 | }
  32 | 
  33 | /**
  34 |  * Structured error class for move operations
  35 |  */
  36 | class MoveTaskError extends Error {
  37 | 	constructor(code, message, data = {}) {
  38 | 		super(message);
  39 | 		this.name = 'MoveTaskError';
  40 | 		this.code = code;
  41 | 		this.data = data;
  42 | 	}
  43 | }
  44 | 
  45 | /**
  46 |  * Error codes for move operations
  47 |  */
  48 | const MOVE_ERROR_CODES = {
  49 | 	CROSS_TAG_DEPENDENCY_CONFLICTS: 'CROSS_TAG_DEPENDENCY_CONFLICTS',
  50 | 	CANNOT_MOVE_SUBTASK: 'CANNOT_MOVE_SUBTASK',
  51 | 	SOURCE_TARGET_TAGS_SAME: 'SOURCE_TARGET_TAGS_SAME',
  52 | 	TASK_NOT_FOUND: 'TASK_NOT_FOUND',
  53 | 	SUBTASK_NOT_FOUND: 'SUBTASK_NOT_FOUND',
  54 | 	PARENT_TASK_NOT_FOUND: 'PARENT_TASK_NOT_FOUND',
  55 | 	PARENT_TASK_NO_SUBTASKS: 'PARENT_TASK_NO_SUBTASKS',
  56 | 	DESTINATION_TASK_NOT_FOUND: 'DESTINATION_TASK_NOT_FOUND',
  57 | 	TASK_ALREADY_EXISTS: 'TASK_ALREADY_EXISTS',
  58 | 	INVALID_TASKS_FILE: 'INVALID_TASKS_FILE',
  59 | 	ID_COUNT_MISMATCH: 'ID_COUNT_MISMATCH',
  60 | 	INVALID_SOURCE_TAG: 'INVALID_SOURCE_TAG',
  61 | 	INVALID_TARGET_TAG: 'INVALID_TARGET_TAG'
  62 | };
  63 | 
  64 | /**
  65 |  * Normalize a dependency value to its numeric parent task ID.
  66 |  * - Numbers are returned as-is (if finite)
  67 |  * - Numeric strings are parsed ("5" -> 5)
  68 |  * - Dotted strings return the parent portion ("5.2" -> 5)
  69 |  * - Empty/invalid values return null
  70 |  * - null/undefined are preserved
  71 |  * @param {number|string|null|undefined} dep
  72 |  * @returns {number|null|undefined}
  73 |  */
  74 | function normalizeDependency(dep) {
  75 | 	if (dep === null || dep === undefined) return dep;
  76 | 	if (typeof dep === 'number') return Number.isFinite(dep) ? dep : null;
  77 | 	if (typeof dep === 'string') {
  78 | 		const trimmed = dep.trim();
  79 | 		if (trimmed === '') return null;
  80 | 		const parentPart = trimmed.includes('.') ? trimmed.split('.')[0] : trimmed;
  81 | 		const parsed = parseInt(parentPart, 10);
  82 | 		return Number.isFinite(parsed) ? parsed : null;
  83 | 	}
  84 | 	return null;
  85 | }
  86 | 
  87 | /**
  88 |  * Normalize an array of dependency values to numeric IDs.
  89 |  * Preserves null/undefined input (returns as-is) and filters out invalid entries.
  90 |  * @param {Array<any>|null|undefined} deps
  91 |  * @returns {Array<number>|null|undefined}
  92 |  */
  93 | function normalizeDependencies(deps) {
  94 | 	if (deps === null || deps === undefined) return deps;
  95 | 	if (!Array.isArray(deps)) return deps;
  96 | 	return deps
  97 | 		.map((d) => normalizeDependency(d))
  98 | 		.filter((n) => Number.isFinite(n));
  99 | }
 100 | 
 101 | /**
 102 |  * Move one or more tasks/subtasks to new positions
 103 |  * @param {string} tasksPath - Path to tasks.json file
 104 |  * @param {string} sourceId - ID(s) of the task/subtask to move (e.g., '5' or '5.2' or '5,6,7')
 105 |  * @param {string} destinationId - ID(s) of the destination (e.g., '7' or '7.3' or '7,8,9')
 106 |  * @param {boolean} generateFiles - Whether to regenerate task files after moving
 107 |  * @param {Object} options - Additional options
 108 |  * @param {string} options.projectRoot - Project root directory for tag resolution
 109 |  * @param {string} options.tag - Explicit tag to use (optional)
 110 |  * @returns {Object} Result object with moved task details
 111 |  */
 112 | async function moveTask(
 113 | 	tasksPath,
 114 | 	sourceId,
 115 | 	destinationId,
 116 | 	generateFiles = false,
 117 | 	options = {}
 118 | ) {
 119 | 	const { projectRoot, tag } = options;
 120 | 	// Check if we have comma-separated IDs (batch move)
 121 | 	const sourceIds = sourceId.split(',').map((id) => id.trim());
 122 | 	const destinationIds = destinationId.split(',').map((id) => id.trim());
 123 | 
 124 | 	if (sourceIds.length !== destinationIds.length) {
 125 | 		throw new MoveTaskError(
 126 | 			MOVE_ERROR_CODES.ID_COUNT_MISMATCH,
 127 | 			`Number of source IDs (${sourceIds.length}) must match number of destination IDs (${destinationIds.length})`
 128 | 		);
 129 | 	}
 130 | 
 131 | 	// For batch moves, process each pair sequentially
 132 | 	if (sourceIds.length > 1) {
 133 | 		const results = [];
 134 | 		for (let i = 0; i < sourceIds.length; i++) {
 135 | 			const result = await moveTask(
 136 | 				tasksPath,
 137 | 				sourceIds[i],
 138 | 				destinationIds[i],
 139 | 				false, // Don't generate files for each individual move
 140 | 				options
 141 | 			);
 142 | 			results.push(result);
 143 | 		}
 144 | 
 145 | 		// Generate files once at the end if requested
 146 | 		if (generateFiles) {
 147 | 			await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
 148 | 				tag: tag,
 149 | 				projectRoot: projectRoot
 150 | 			});
 151 | 		}
 152 | 
 153 | 		return {
 154 | 			message: `Successfully moved ${sourceIds.length} tasks/subtasks`,
 155 | 			moves: results
 156 | 		};
 157 | 	}
 158 | 
 159 | 	// Single move logic
 160 | 	// Read the raw data without tag resolution to preserve tagged structure
 161 | 	let rawData = readJSON(tasksPath, projectRoot, tag);
 162 | 
 163 | 	// Handle the case where readJSON returns resolved data with _rawTaggedData
 164 | 	if (rawData && rawData._rawTaggedData) {
 165 | 		// Use the raw tagged data and discard the resolved view
 166 | 		rawData = rawData._rawTaggedData;
 167 | 	}
 168 | 
 169 | 	// Ensure the tag exists in the raw data
 170 | 	if (!rawData || !rawData[tag] || !Array.isArray(rawData[tag].tasks)) {
 171 | 		throw new MoveTaskError(
 172 | 			MOVE_ERROR_CODES.INVALID_TASKS_FILE,
 173 | 			`Invalid tasks file or tag "${tag}" not found at ${tasksPath}`
 174 | 		);
 175 | 	}
 176 | 
 177 | 	// Get the tasks for the current tag
 178 | 	const tasks = rawData[tag].tasks;
 179 | 
 180 | 	log(
 181 | 		'info',
 182 | 		`Moving task/subtask ${sourceId} to ${destinationId} (tag: ${tag})`
 183 | 	);
 184 | 
 185 | 	// Parse source and destination IDs
 186 | 	const isSourceSubtask = sourceId.includes('.');
 187 | 	const isDestSubtask = destinationId.includes('.');
 188 | 
 189 | 	let result;
 190 | 
 191 | 	if (isSourceSubtask && isDestSubtask) {
 192 | 		// Subtask to subtask
 193 | 		result = moveSubtaskToSubtask(tasks, sourceId, destinationId);
 194 | 	} else if (isSourceSubtask && !isDestSubtask) {
 195 | 		// Subtask to task
 196 | 		result = moveSubtaskToTask(tasks, sourceId, destinationId);
 197 | 	} else if (!isSourceSubtask && isDestSubtask) {
 198 | 		// Task to subtask
 199 | 		result = moveTaskToSubtask(tasks, sourceId, destinationId);
 200 | 	} else {
 201 | 		// Task to task
 202 | 		result = moveTaskToTask(tasks, sourceId, destinationId);
 203 | 	}
 204 | 
 205 | 	// Update the data structure with the modified tasks
 206 | 	rawData[tag].tasks = tasks;
 207 | 
 208 | 	// Always write the data object, never the _rawTaggedData directly
 209 | 	// The writeJSON function will filter out _rawTaggedData automatically
 210 | 	writeJSON(tasksPath, rawData, options.projectRoot, tag);
 211 | 
 212 | 	if (generateFiles) {
 213 | 		await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
 214 | 			tag: tag,
 215 | 			projectRoot: projectRoot
 216 | 		});
 217 | 	}
 218 | 
 219 | 	return result;
 220 | }
 221 | 
 222 | // Helper functions for different move scenarios
 223 | function moveSubtaskToSubtask(tasks, sourceId, destinationId) {
 224 | 	// Parse IDs
 225 | 	const [sourceParentId, sourceSubtaskId] = sourceId
 226 | 		.split('.')
 227 | 		.map((id) => parseInt(id, 10));
 228 | 	const [destParentId, destSubtaskId] = destinationId
 229 | 		.split('.')
 230 | 		.map((id) => parseInt(id, 10));
 231 | 
 232 | 	// Find source and destination parent tasks
 233 | 	const sourceParentTask = tasks.find((t) => t.id === sourceParentId);
 234 | 	const destParentTask = tasks.find((t) => t.id === destParentId);
 235 | 
 236 | 	if (!sourceParentTask) {
 237 | 		throw new MoveTaskError(
 238 | 			MOVE_ERROR_CODES.PARENT_TASK_NOT_FOUND,
 239 | 			`Source parent task with ID ${sourceParentId} not found`
 240 | 		);
 241 | 	}
 242 | 	if (!destParentTask) {
 243 | 		throw new MoveTaskError(
 244 | 			MOVE_ERROR_CODES.PARENT_TASK_NOT_FOUND,
 245 | 			`Destination parent task with ID ${destParentId} not found`
 246 | 		);
 247 | 	}
 248 | 
 249 | 	// Initialize subtasks arrays if they don't exist (based on commit fixes)
 250 | 	if (!sourceParentTask.subtasks) {
 251 | 		sourceParentTask.subtasks = [];
 252 | 	}
 253 | 	if (!destParentTask.subtasks) {
 254 | 		destParentTask.subtasks = [];
 255 | 	}
 256 | 
 257 | 	// Find source subtask
 258 | 	const sourceSubtaskIndex = sourceParentTask.subtasks.findIndex(
 259 | 		(st) => st.id === sourceSubtaskId
 260 | 	);
 261 | 	if (sourceSubtaskIndex === -1) {
 262 | 		throw new MoveTaskError(
 263 | 			MOVE_ERROR_CODES.SUBTASK_NOT_FOUND,
 264 | 			`Source subtask ${sourceId} not found`
 265 | 		);
 266 | 	}
 267 | 
 268 | 	const sourceSubtask = sourceParentTask.subtasks[sourceSubtaskIndex];
 269 | 
 270 | 	if (sourceParentId === destParentId) {
 271 | 		// Moving within the same parent
 272 | 		if (destParentTask.subtasks.length > 0) {
 273 | 			const destSubtaskIndex = destParentTask.subtasks.findIndex(
 274 | 				(st) => st.id === destSubtaskId
 275 | 			);
 276 | 			if (destSubtaskIndex !== -1) {
 277 | 				// Remove from old position
 278 | 				sourceParentTask.subtasks.splice(sourceSubtaskIndex, 1);
 279 | 				// Insert at new position (adjust index if moving within same array)
 280 | 				const adjustedIndex =
 281 | 					sourceSubtaskIndex < destSubtaskIndex
 282 | 						? destSubtaskIndex - 1
 283 | 						: destSubtaskIndex;
 284 | 				destParentTask.subtasks.splice(adjustedIndex + 1, 0, sourceSubtask);
 285 | 			} else {
 286 | 				// Destination subtask doesn't exist, insert at end
 287 | 				sourceParentTask.subtasks.splice(sourceSubtaskIndex, 1);
 288 | 				destParentTask.subtasks.push(sourceSubtask);
 289 | 			}
 290 | 		} else {
 291 | 			// No existing subtasks, this will be the first one
 292 | 			sourceParentTask.subtasks.splice(sourceSubtaskIndex, 1);
 293 | 			destParentTask.subtasks.push(sourceSubtask);
 294 | 		}
 295 | 	} else {
 296 | 		// Moving between different parents
 297 | 		moveSubtaskToAnotherParent(
 298 | 			sourceSubtask,
 299 | 			sourceParentTask,
 300 | 			sourceSubtaskIndex,
 301 | 			destParentTask,
 302 | 			destSubtaskId
 303 | 		);
 304 | 	}
 305 | 
 306 | 	return {
 307 | 		message: `Moved subtask ${sourceId} to ${destinationId}`,
 308 | 		movedItem: sourceSubtask
 309 | 	};
 310 | }
 311 | 
 312 | function moveSubtaskToTask(tasks, sourceId, destinationId) {
 313 | 	// Parse source ID
 314 | 	const [sourceParentId, sourceSubtaskId] = sourceId
 315 | 		.split('.')
 316 | 		.map((id) => parseInt(id, 10));
 317 | 	const destTaskId = parseInt(destinationId, 10);
 318 | 
 319 | 	// Find source parent and destination task
 320 | 	const sourceParentTask = tasks.find((t) => t.id === sourceParentId);
 321 | 
 322 | 	if (!sourceParentTask) {
 323 | 		throw new MoveTaskError(
 324 | 			MOVE_ERROR_CODES.PARENT_TASK_NOT_FOUND,
 325 | 			`Source parent task with ID ${sourceParentId} not found`
 326 | 		);
 327 | 	}
 328 | 	if (!sourceParentTask.subtasks) {
 329 | 		throw new MoveTaskError(
 330 | 			MOVE_ERROR_CODES.PARENT_TASK_NO_SUBTASKS,
 331 | 			`Source parent task ${sourceParentId} has no subtasks`
 332 | 		);
 333 | 	}
 334 | 
 335 | 	// Find source subtask
 336 | 	const sourceSubtaskIndex = sourceParentTask.subtasks.findIndex(
 337 | 		(st) => st.id === sourceSubtaskId
 338 | 	);
 339 | 	if (sourceSubtaskIndex === -1) {
 340 | 		throw new MoveTaskError(
 341 | 			MOVE_ERROR_CODES.SUBTASK_NOT_FOUND,
 342 | 			`Source subtask ${sourceId} not found`
 343 | 		);
 344 | 	}
 345 | 
 346 | 	const sourceSubtask = sourceParentTask.subtasks[sourceSubtaskIndex];
 347 | 
 348 | 	// Check if destination task exists
 349 | 	const existingDestTask = tasks.find((t) => t.id === destTaskId);
 350 | 	if (existingDestTask) {
 351 | 		throw new MoveTaskError(
 352 | 			MOVE_ERROR_CODES.TASK_ALREADY_EXISTS,
 353 | 			`Cannot move to existing task ID ${destTaskId}. Choose a different ID or use subtask destination.`
 354 | 		);
 355 | 	}
 356 | 
 357 | 	// Create new task from subtask
 358 | 	const newTask = {
 359 | 		id: destTaskId,
 360 | 		title: sourceSubtask.title,
 361 | 		description: sourceSubtask.description,
 362 | 		status: sourceSubtask.status || 'pending',
 363 | 		dependencies: sourceSubtask.dependencies || [],
 364 | 		priority: sourceSubtask.priority || 'medium',
 365 | 		details: sourceSubtask.details || '',
 366 | 		testStrategy: sourceSubtask.testStrategy || '',
 367 | 		subtasks: []
 368 | 	};
 369 | 
 370 | 	// Remove subtask from source parent
 371 | 	sourceParentTask.subtasks.splice(sourceSubtaskIndex, 1);
 372 | 
 373 | 	// Insert new task in correct position
 374 | 	const insertIndex = tasks.findIndex((t) => t.id > destTaskId);
 375 | 	if (insertIndex === -1) {
 376 | 		tasks.push(newTask);
 377 | 	} else {
 378 | 		tasks.splice(insertIndex, 0, newTask);
 379 | 	}
 380 | 
 381 | 	return {
 382 | 		message: `Converted subtask ${sourceId} to task ${destinationId}`,
 383 | 		movedItem: newTask
 384 | 	};
 385 | }
 386 | 
 387 | function moveTaskToSubtask(tasks, sourceId, destinationId) {
 388 | 	// Parse IDs
 389 | 	const sourceTaskId = parseInt(sourceId, 10);
 390 | 	const [destParentId, destSubtaskId] = destinationId
 391 | 		.split('.')
 392 | 		.map((id) => parseInt(id, 10));
 393 | 
 394 | 	// Find source task and destination parent
 395 | 	const sourceTaskIndex = tasks.findIndex((t) => t.id === sourceTaskId);
 396 | 	const destParentTask = tasks.find((t) => t.id === destParentId);
 397 | 
 398 | 	if (sourceTaskIndex === -1) {
 399 | 		throw new MoveTaskError(
 400 | 			MOVE_ERROR_CODES.TASK_NOT_FOUND,
 401 | 			`Source task with ID ${sourceTaskId} not found`
 402 | 		);
 403 | 	}
 404 | 	if (!destParentTask) {
 405 | 		throw new MoveTaskError(
 406 | 			MOVE_ERROR_CODES.PARENT_TASK_NOT_FOUND,
 407 | 			`Destination parent task with ID ${destParentId} not found`
 408 | 		);
 409 | 	}
 410 | 
 411 | 	const sourceTask = tasks[sourceTaskIndex];
 412 | 
 413 | 	// Initialize subtasks array if it doesn't exist (based on commit fixes)
 414 | 	if (!destParentTask.subtasks) {
 415 | 		destParentTask.subtasks = [];
 416 | 	}
 417 | 
 418 | 	// Create new subtask from task
 419 | 	const newSubtask = {
 420 | 		id: destSubtaskId,
 421 | 		title: sourceTask.title,
 422 | 		description: sourceTask.description,
 423 | 		status: sourceTask.status || 'pending',
 424 | 		dependencies: sourceTask.dependencies || [],
 425 | 		details: sourceTask.details || '',
 426 | 		testStrategy: sourceTask.testStrategy || ''
 427 | 	};
 428 | 
 429 | 	// Find insertion position (based on commit fixes)
 430 | 	let destSubtaskIndex = -1;
 431 | 	if (destParentTask.subtasks.length > 0) {
 432 | 		destSubtaskIndex = destParentTask.subtasks.findIndex(
 433 | 			(st) => st.id === destSubtaskId
 434 | 		);
 435 | 		if (destSubtaskIndex === -1) {
 436 | 			// Subtask doesn't exist, we'll insert at the end
 437 | 			destSubtaskIndex = destParentTask.subtasks.length - 1;
 438 | 		}
 439 | 	}
 440 | 
 441 | 	// Insert at specific position (based on commit fixes)
 442 | 	const insertPosition = destSubtaskIndex === -1 ? 0 : destSubtaskIndex + 1;
 443 | 	destParentTask.subtasks.splice(insertPosition, 0, newSubtask);
 444 | 
 445 | 	// Remove the original task from the tasks array
 446 | 	tasks.splice(sourceTaskIndex, 1);
 447 | 
 448 | 	return {
 449 | 		message: `Converted task ${sourceId} to subtask ${destinationId}`,
 450 | 		movedItem: newSubtask
 451 | 	};
 452 | }
 453 | 
 454 | function moveTaskToTask(tasks, sourceId, destinationId) {
 455 | 	const sourceTaskId = parseInt(sourceId, 10);
 456 | 	const destTaskId = parseInt(destinationId, 10);
 457 | 
 458 | 	// Find source task
 459 | 	const sourceTaskIndex = tasks.findIndex((t) => t.id === sourceTaskId);
 460 | 	if (sourceTaskIndex === -1) {
 461 | 		throw new MoveTaskError(
 462 | 			MOVE_ERROR_CODES.TASK_NOT_FOUND,
 463 | 			`Source task with ID ${sourceTaskId} not found`
 464 | 		);
 465 | 	}
 466 | 
 467 | 	const sourceTask = tasks[sourceTaskIndex];
 468 | 
 469 | 	// Check if destination exists
 470 | 	const destTaskIndex = tasks.findIndex((t) => t.id === destTaskId);
 471 | 
 472 | 	if (destTaskIndex !== -1) {
 473 | 		// Destination exists - this could be overwriting or swapping
 474 | 		const destTask = tasks[destTaskIndex];
 475 | 
 476 | 		// For now, throw an error to avoid accidental overwrites
 477 | 		throw new MoveTaskError(
 478 | 			MOVE_ERROR_CODES.TASK_ALREADY_EXISTS,
 479 | 			`Task with ID ${destTaskId} already exists. Use a different destination ID.`
 480 | 		);
 481 | 	} else {
 482 | 		// Destination doesn't exist - create new task ID
 483 | 		return moveTaskToNewId(tasks, sourceTaskIndex, sourceTask, destTaskId);
 484 | 	}
 485 | }
 486 | 
 487 | function moveSubtaskToAnotherParent(
 488 | 	sourceSubtask,
 489 | 	sourceParentTask,
 490 | 	sourceSubtaskIndex,
 491 | 	destParentTask,
 492 | 	destSubtaskId
 493 | ) {
 494 | 	const destSubtaskId_num = parseInt(destSubtaskId, 10);
 495 | 
 496 | 	// Create new subtask with destination ID
 497 | 	const newSubtask = {
 498 | 		...sourceSubtask,
 499 | 		id: destSubtaskId_num
 500 | 	};
 501 | 
 502 | 	// Initialize subtasks array if it doesn't exist (based on commit fixes)
 503 | 	if (!destParentTask.subtasks) {
 504 | 		destParentTask.subtasks = [];
 505 | 	}
 506 | 
 507 | 	// Find insertion position
 508 | 	let destSubtaskIndex = -1;
 509 | 	if (destParentTask.subtasks.length > 0) {
 510 | 		destSubtaskIndex = destParentTask.subtasks.findIndex(
 511 | 			(st) => st.id === destSubtaskId_num
 512 | 		);
 513 | 		if (destSubtaskIndex === -1) {
 514 | 			// Subtask doesn't exist, we'll insert at the end
 515 | 			destSubtaskIndex = destParentTask.subtasks.length - 1;
 516 | 		}
 517 | 	}
 518 | 
 519 | 	// Insert at the destination position (based on commit fixes)
 520 | 	const insertPosition = destSubtaskIndex === -1 ? 0 : destSubtaskIndex + 1;
 521 | 	destParentTask.subtasks.splice(insertPosition, 0, newSubtask);
 522 | 
 523 | 	// Remove the subtask from the original parent
 524 | 	sourceParentTask.subtasks.splice(sourceSubtaskIndex, 1);
 525 | 
 526 | 	return newSubtask;
 527 | }
 528 | 
 529 | function moveTaskToNewId(tasks, sourceTaskIndex, sourceTask, destTaskId) {
 530 | 	const destTaskIndex = tasks.findIndex((t) => t.id === destTaskId);
 531 | 
 532 | 	// Create moved task with new ID
 533 | 	const movedTask = {
 534 | 		...sourceTask,
 535 | 		id: destTaskId
 536 | 	};
 537 | 
 538 | 	// Update any dependencies that reference the old task ID
 539 | 	tasks.forEach((task) => {
 540 | 		if (task.dependencies && task.dependencies.includes(sourceTask.id)) {
 541 | 			const depIndex = task.dependencies.indexOf(sourceTask.id);
 542 | 			task.dependencies[depIndex] = destTaskId;
 543 | 		}
 544 | 		if (task.subtasks) {
 545 | 			task.subtasks.forEach((subtask) => {
 546 | 				if (
 547 | 					subtask.dependencies &&
 548 | 					subtask.dependencies.includes(sourceTask.id)
 549 | 				) {
 550 | 					const depIndex = subtask.dependencies.indexOf(sourceTask.id);
 551 | 					subtask.dependencies[depIndex] = destTaskId;
 552 | 				}
 553 | 			});
 554 | 		}
 555 | 	});
 556 | 
 557 | 	// Update dependencies within movedTask's subtasks that reference sibling subtasks
 558 | 	if (Array.isArray(movedTask.subtasks)) {
 559 | 		movedTask.subtasks.forEach((subtask) => {
 560 | 			if (Array.isArray(subtask.dependencies)) {
 561 | 				subtask.dependencies = subtask.dependencies.map((dep) => {
 562 | 					// If dependency is a string like "oldParent.subId", update to "newParent.subId"
 563 | 					if (typeof dep === 'string' && dep.includes('.')) {
 564 | 						const [depParent, depSub] = dep.split('.');
 565 | 						if (parseInt(depParent, 10) === sourceTask.id) {
 566 | 							return `${destTaskId}.${depSub}`;
 567 | 						}
 568 | 					}
 569 | 					// If dependency is a number, and matches a subtask ID in the moved task, leave as is (context is implied)
 570 | 					return dep;
 571 | 				});
 572 | 			}
 573 | 		});
 574 | 	}
 575 | 
 576 | 	// Strategy based on commit fixes: remove source first, then replace destination
 577 | 	// This avoids index shifting problems
 578 | 
 579 | 	// Remove the source task first
 580 | 	tasks.splice(sourceTaskIndex, 1);
 581 | 
 582 | 	// Adjust the destination index if the source was before the destination
 583 | 	// Since we removed the source, indices after it shift down by 1
 584 | 	const adjustedDestIndex =
 585 | 		sourceTaskIndex < destTaskIndex ? destTaskIndex - 1 : destTaskIndex;
 586 | 
 587 | 	// Replace the placeholder destination task with the moved task (based on commit fixes)
 588 | 	if (adjustedDestIndex >= 0 && adjustedDestIndex < tasks.length) {
 589 | 		tasks[adjustedDestIndex] = movedTask;
 590 | 	} else {
 591 | 		// Insert at the end if index is out of bounds
 592 | 		tasks.push(movedTask);
 593 | 	}
 594 | 
 595 | 	log('info', `Moved task ${sourceTask.id} to new ID ${destTaskId}`);
 596 | 
 597 | 	return {
 598 | 		message: `Moved task ${sourceTask.id} to new ID ${destTaskId}`,
 599 | 		movedItem: movedTask
 600 | 	};
 601 | }
 602 | 
 603 | /**
 604 |  * Get all tasks from all tags with tag information
 605 |  * @param {Object} rawData - The raw tagged data object
 606 |  * @returns {Array} A flat array of all task objects with tag property
 607 |  */
 608 | function getAllTasksWithTags(rawData) {
 609 | 	let allTasks = [];
 610 | 	for (const tagName in rawData) {
 611 | 		if (
 612 | 			Object.prototype.hasOwnProperty.call(rawData, tagName) &&
 613 | 			rawData[tagName] &&
 614 | 			Array.isArray(rawData[tagName].tasks)
 615 | 		) {
 616 | 			const tasksWithTag = rawData[tagName].tasks.map((task) => ({
 617 | 				...task,
 618 | 				tag: tagName
 619 | 			}));
 620 | 			allTasks = allTasks.concat(tasksWithTag);
 621 | 		}
 622 | 	}
 623 | 	return allTasks;
 624 | }
 625 | 
 626 | /**
 627 |  * Validate move operation parameters and data
 628 |  * @param {string} tasksPath - Path to tasks.json file
 629 |  * @param {Array} taskIds - Array of task IDs to move
 630 |  * @param {string} sourceTag - Source tag name
 631 |  * @param {string} targetTag - Target tag name
 632 |  * @param {Object} context - Context object
 633 |  * @returns {Object} Validation result with rawData and sourceTasks
 634 |  */
 635 | async function validateMove(tasksPath, taskIds, sourceTag, targetTag, context) {
 636 | 	const { projectRoot } = context;
 637 | 
 638 | 	// Read the raw data without tag resolution to preserve tagged structure
 639 | 	let rawData = readJSON(tasksPath, projectRoot, sourceTag);
 640 | 
 641 | 	// Handle the case where readJSON returns resolved data with _rawTaggedData
 642 | 	if (rawData && rawData._rawTaggedData) {
 643 | 		rawData = rawData._rawTaggedData;
 644 | 	}
 645 | 
 646 | 	// Validate source tag exists
 647 | 	if (
 648 | 		!rawData ||
 649 | 		!rawData[sourceTag] ||
 650 | 		!Array.isArray(rawData[sourceTag].tasks)
 651 | 	) {
 652 | 		throw new MoveTaskError(
 653 | 			MOVE_ERROR_CODES.INVALID_SOURCE_TAG,
 654 | 			`Source tag "${sourceTag}" not found or invalid`
 655 | 		);
 656 | 	}
 657 | 
 658 | 	// Create target tag if it doesn't exist
 659 | 	if (!rawData[targetTag]) {
 660 | 		rawData[targetTag] = { tasks: [] };
 661 | 		log('info', `Created new tag "${targetTag}"`);
 662 | 	}
 663 | 
 664 | 	// Normalize all IDs to strings once for consistent comparison
 665 | 	const normalizedSearchIds = taskIds.map((id) => String(id));
 666 | 
 667 | 	const sourceTasks = rawData[sourceTag].tasks.filter((t) => {
 668 | 		const normalizedTaskId = String(t.id);
 669 | 		return normalizedSearchIds.includes(normalizedTaskId);
 670 | 	});
 671 | 
 672 | 	// Validate subtask movement
 673 | 	taskIds.forEach((taskId) => {
 674 | 		validateSubtaskMove(taskId, sourceTag, targetTag);
 675 | 	});
 676 | 
 677 | 	return { rawData, sourceTasks };
 678 | }
 679 | 
 680 | /**
 681 |  * Load and prepare task data for move operation
 682 |  * @param {Object} validation - Validation result from validateMove
 683 |  * @returns {Object} Prepared data with rawData, sourceTasks, and allTasks
 684 |  */
 685 | async function prepareTaskData(validation) {
 686 | 	const { rawData, sourceTasks } = validation;
 687 | 
 688 | 	// Get all tasks for validation
 689 | 	const allTasks = getAllTasksWithTags(rawData);
 690 | 
 691 | 	return { rawData, sourceTasks, allTasks };
 692 | }
 693 | 
 694 | /**
 695 |  * Resolve dependencies and determine tasks to move
 696 |  * @param {Array} sourceTasks - Source tasks to move
 697 |  * @param {Array} allTasks - All available tasks from all tags
 698 |  * @param {Object} options - Move options
 699 |  * @param {Array} taskIds - Original task IDs
 700 |  * @param {string} sourceTag - Source tag name
 701 |  * @param {string} targetTag - Target tag name
 702 |  * @returns {Object} Tasks to move and dependency resolution info
 703 |  */
 704 | async function resolveDependencies(
 705 | 	sourceTasks,
 706 | 	allTasks,
 707 | 	options,
 708 | 	taskIds,
 709 | 	sourceTag,
 710 | 	targetTag
 711 | ) {
 712 | 	const { withDependencies = false, ignoreDependencies = false } = options;
 713 | 
 714 | 	// Scope allTasks to the source tag to avoid cross-tag contamination when
 715 | 	// computing dependency chains for --with-dependencies
 716 | 	const tasksInSourceTag = Array.isArray(allTasks)
 717 | 		? allTasks.filter((t) => t && t.tag === sourceTag)
 718 | 		: [];
 719 | 
 720 | 	// Handle --with-dependencies flag first (regardless of cross-tag dependencies)
 721 | 	if (withDependencies) {
 722 | 		// Move dependent tasks along with main tasks
 723 | 		// Find ALL dependencies recursively, but only using tasks from the source tag
 724 | 		const allDependentTaskIdsRaw = findAllDependenciesRecursively(
 725 | 			sourceTasks,
 726 | 			tasksInSourceTag,
 727 | 			{ maxDepth: 100, includeSelf: false }
 728 | 		);
 729 | 
 730 | 		// Filter dependent IDs to those that actually exist in the source tag
 731 | 		const sourceTagIds = new Set(
 732 | 			tasksInSourceTag.map((t) =>
 733 | 				typeof t.id === 'string' ? parseInt(t.id, 10) : t.id
 734 | 			)
 735 | 		);
 736 | 		const allDependentTaskIds = allDependentTaskIdsRaw.filter((depId) => {
 737 | 			// Only numeric task IDs are eligible to be moved (subtasks cannot be moved cross-tag)
 738 | 			const normalizedId = normalizeDependency(depId);
 739 | 			return Number.isFinite(normalizedId) && sourceTagIds.has(normalizedId);
 740 | 		});
 741 | 
 742 | 		const allTaskIdsToMove = [...new Set([...taskIds, ...allDependentTaskIds])];
 743 | 
 744 | 		log(
 745 | 			'info',
 746 | 			`Moving ${allTaskIdsToMove.length} tasks (including dependencies): ${allTaskIdsToMove.join(', ')}`
 747 | 		);
 748 | 
 749 | 		return {
 750 | 			tasksToMove: allTaskIdsToMove,
 751 | 			dependencyResolution: {
 752 | 				type: 'with-dependencies',
 753 | 				dependentTasks: allDependentTaskIds
 754 | 			}
 755 | 		};
 756 | 	}
 757 | 
 758 | 	// Find cross-tag dependencies (these shouldn't exist since dependencies are only within tags)
 759 | 	const crossTagDependencies = findCrossTagDependencies(
 760 | 		sourceTasks,
 761 | 		sourceTag,
 762 | 		targetTag,
 763 | 		allTasks
 764 | 	);
 765 | 
 766 | 	if (crossTagDependencies.length > 0) {
 767 | 		if (ignoreDependencies) {
 768 | 			// Break cross-tag dependencies (edge case - shouldn't normally happen)
 769 | 			sourceTasks.forEach((task) => {
 770 | 				const sourceTagTasks = tasksInSourceTag;
 771 | 				const targetTagTasks = Array.isArray(allTasks)
 772 | 					? allTasks.filter((t) => t && t.tag === targetTag)
 773 | 					: [];
 774 | 				task.dependencies = task.dependencies.filter((depId) => {
 775 | 					const parentTaskId = normalizeDependency(depId);
 776 | 
 777 | 					// If dependency resolves to a task in the source tag, drop it (would be cross-tag after move)
 778 | 					if (
 779 | 						Number.isFinite(parentTaskId) &&
 780 | 						sourceTagTasks.some((t) => t.id === parentTaskId)
 781 | 					) {
 782 | 						return false;
 783 | 					}
 784 | 
 785 | 					// If dependency resolves to a task in the target tag, keep it
 786 | 					if (
 787 | 						Number.isFinite(parentTaskId) &&
 788 | 						targetTagTasks.some((t) => t.id === parentTaskId)
 789 | 					) {
 790 | 						return true;
 791 | 					}
 792 | 
 793 | 					// Otherwise, keep as-is (unknown/unresolved dependency)
 794 | 					return true;
 795 | 				});
 796 | 			});
 797 | 
 798 | 			log(
 799 | 				'warn',
 800 | 				`Removed ${crossTagDependencies.length} cross-tag dependencies`
 801 | 			);
 802 | 
 803 | 			return {
 804 | 				tasksToMove: taskIds,
 805 | 				dependencyResolution: {
 806 | 					type: 'ignored-dependencies',
 807 | 					conflicts: crossTagDependencies
 808 | 				}
 809 | 			};
 810 | 		} else {
 811 | 			// Block move and show error
 812 | 			throw new MoveTaskError(
 813 | 				MOVE_ERROR_CODES.CROSS_TAG_DEPENDENCY_CONFLICTS,
 814 | 				`Cannot move tasks: ${crossTagDependencies.length} cross-tag dependency conflicts found`,
 815 | 				{
 816 | 					conflicts: crossTagDependencies,
 817 | 					sourceTag,
 818 | 					targetTag,
 819 | 					taskIds
 820 | 				}
 821 | 			);
 822 | 		}
 823 | 	}
 824 | 
 825 | 	return {
 826 | 		tasksToMove: taskIds,
 827 | 		dependencyResolution: { type: 'no-conflicts' }
 828 | 	};
 829 | }
 830 | 
 831 | /**
 832 |  * Execute the actual move operation
 833 |  * @param {Array} tasksToMove - Array of task IDs to move
 834 |  * @param {string} sourceTag - Source tag name
 835 |  * @param {string} targetTag - Target tag name
 836 |  * @param {Object} rawData - Raw data object
 837 |  * @param {Object} context - Context object
 838 |  * @param {string} tasksPath - Path to tasks.json file
 839 |  * @returns {Object} Move operation result
 840 |  */
 841 | async function executeMoveOperation(
 842 | 	tasksToMove,
 843 | 	sourceTag,
 844 | 	targetTag,
 845 | 	rawData,
 846 | 	context,
 847 | 	tasksPath
 848 | ) {
 849 | 	const { projectRoot } = context;
 850 | 	const movedTasks = [];
 851 | 
 852 | 	// Move each task from source to target tag
 853 | 	for (const taskId of tasksToMove) {
 854 | 		// Normalize taskId to number for comparison
 855 | 		const normalizedTaskId =
 856 | 			typeof taskId === 'string' ? parseInt(taskId, 10) : taskId;
 857 | 
 858 | 		const sourceTaskIndex = rawData[sourceTag].tasks.findIndex(
 859 | 			(t) => t.id === normalizedTaskId
 860 | 		);
 861 | 
 862 | 		if (sourceTaskIndex === -1) {
 863 | 			throw new MoveTaskError(
 864 | 				MOVE_ERROR_CODES.TASK_NOT_FOUND,
 865 | 				`Task ${taskId} not found in source tag "${sourceTag}"`
 866 | 			);
 867 | 		}
 868 | 
 869 | 		const taskToMove = rawData[sourceTag].tasks[sourceTaskIndex];
 870 | 
 871 | 		// Check for ID conflicts in target tag
 872 | 		const existingTaskIndex = rawData[targetTag].tasks.findIndex(
 873 | 			(t) => t.id === normalizedTaskId
 874 | 		);
 875 | 		if (existingTaskIndex !== -1) {
 876 | 			throw new MoveTaskError(
 877 | 				MOVE_ERROR_CODES.TASK_ALREADY_EXISTS,
 878 | 				`Task ${taskId} already exists in target tag "${targetTag}"`,
 879 | 				{
 880 | 					conflictingId: normalizedTaskId,
 881 | 					targetTag,
 882 | 					suggestions: [
 883 | 						'Choose a different target tag without conflicting IDs',
 884 | 						'Move a different set of IDs (avoid existing ones)',
 885 | 						'If needed, move within-tag to a new ID first, then cross-tag move'
 886 | 					]
 887 | 				}
 888 | 			);
 889 | 		}
 890 | 
 891 | 		// Remove from source tag
 892 | 		rawData[sourceTag].tasks.splice(sourceTaskIndex, 1);
 893 | 
 894 | 		// Preserve task metadata and add to target tag
 895 | 		const taskWithPreservedMetadata = preserveTaskMetadata(
 896 | 			taskToMove,
 897 | 			sourceTag,
 898 | 			targetTag
 899 | 		);
 900 | 		rawData[targetTag].tasks.push(taskWithPreservedMetadata);
 901 | 
 902 | 		movedTasks.push({
 903 | 			id: taskId,
 904 | 			fromTag: sourceTag,
 905 | 			toTag: targetTag
 906 | 		});
 907 | 
 908 | 		log('info', `Moved task ${taskId} from "${sourceTag}" to "${targetTag}"`);
 909 | 	}
 910 | 
 911 | 	return { rawData, movedTasks };
 912 | }
 913 | 
 914 | /**
 915 |  * Finalize the move operation by saving data and returning result
 916 |  * @param {Object} moveResult - Result from executeMoveOperation
 917 |  * @param {string} tasksPath - Path to tasks.json file
 918 |  * @param {Object} context - Context object
 919 |  * @param {string} sourceTag - Source tag name
 920 |  * @param {string} targetTag - Target tag name
 921 |  * @returns {Object} Final result object
 922 |  */
 923 | async function finalizeMove(
 924 | 	moveResult,
 925 | 	tasksPath,
 926 | 	context,
 927 | 	sourceTag,
 928 | 	targetTag,
 929 | 	dependencyResolution
 930 | ) {
 931 | 	const { projectRoot } = context;
 932 | 	const { rawData, movedTasks } = moveResult;
 933 | 
 934 | 	// Write the updated data
 935 | 	writeJSON(tasksPath, rawData, projectRoot, null);
 936 | 
 937 | 	const response = {
 938 | 		message: `Successfully moved ${movedTasks.length} tasks from "${sourceTag}" to "${targetTag}"`,
 939 | 		movedTasks
 940 | 	};
 941 | 
 942 | 	// If we intentionally broke cross-tag dependencies, provide tips to validate & fix
 943 | 	if (
 944 | 		dependencyResolution &&
 945 | 		dependencyResolution.type === 'ignored-dependencies'
 946 | 	) {
 947 | 		response.tips = [
 948 | 			'Run "task-master validate-dependencies" to check for dependency issues.',
 949 | 			'Run "task-master fix-dependencies" to automatically repair dangling dependencies.'
 950 | 		];
 951 | 	}
 952 | 
 953 | 	return response;
 954 | }
 955 | 
 956 | /**
 957 |  * Move tasks between different tags with dependency handling
 958 |  * @param {string} tasksPath - Path to tasks.json file
 959 |  * @param {Array} taskIds - Array of task IDs to move
 960 |  * @param {string} sourceTag - Source tag name
 961 |  * @param {string} targetTag - Target tag name
 962 |  * @param {Object} options - Move options
 963 |  * @param {boolean} options.withDependencies - Move dependent tasks along with main task
 964 |  * @param {boolean} options.ignoreDependencies - Break cross-tag dependencies during move
 965 |  * @param {Object} context - Context object containing projectRoot and tag information
 966 |  * @returns {Object} Result object with moved task details
 967 |  */
 968 | async function moveTasksBetweenTags(
 969 | 	tasksPath,
 970 | 	taskIds,
 971 | 	sourceTag,
 972 | 	targetTag,
 973 | 	options = {},
 974 | 	context = {}
 975 | ) {
 976 | 	// 1. Validation phase
 977 | 	const validation = await validateMove(
 978 | 		tasksPath,
 979 | 		taskIds,
 980 | 		sourceTag,
 981 | 		targetTag,
 982 | 		context
 983 | 	);
 984 | 
 985 | 	// 2. Load and prepare data
 986 | 	const { rawData, sourceTasks, allTasks } = await prepareTaskData(validation);
 987 | 
 988 | 	// 3. Handle dependencies
 989 | 	const { tasksToMove, dependencyResolution } = await resolveDependencies(
 990 | 		sourceTasks,
 991 | 		allTasks,
 992 | 		options,
 993 | 		taskIds,
 994 | 		sourceTag,
 995 | 		targetTag
 996 | 	);
 997 | 
 998 | 	// 4. Execute move
 999 | 	const moveResult = await executeMoveOperation(
1000 | 		tasksToMove,
1001 | 		sourceTag,
1002 | 		targetTag,
1003 | 		rawData,
1004 | 		context,
1005 | 		tasksPath
1006 | 	);
1007 | 
1008 | 	// 5. Save and return
1009 | 	return await finalizeMove(
1010 | 		moveResult,
1011 | 		tasksPath,
1012 | 		context,
1013 | 		sourceTag,
1014 | 		targetTag,
1015 | 		dependencyResolution
1016 | 	);
1017 | }
1018 | 
1019 | /**
1020 |  * Detect ID conflicts in target tag
1021 |  * @param {Array} taskIds - Array of task IDs to check
1022 |  * @param {string} targetTag - Target tag name
1023 |  * @param {Object} rawData - Raw data object
1024 |  * @returns {Array} Array of conflicting task IDs
1025 |  */
1026 | function detectIdConflicts(taskIds, targetTag, rawData) {
1027 | 	const conflicts = [];
1028 | 
1029 | 	if (!rawData[targetTag] || !Array.isArray(rawData[targetTag].tasks)) {
1030 | 		return conflicts;
1031 | 	}
1032 | 
1033 | 	taskIds.forEach((taskId) => {
1034 | 		// Normalize taskId to number for comparison
1035 | 		const normalizedTaskId =
1036 | 			typeof taskId === 'string' ? parseInt(taskId, 10) : taskId;
1037 | 		const existingTask = rawData[targetTag].tasks.find(
1038 | 			(t) => t.id === normalizedTaskId
1039 | 		);
1040 | 		if (existingTask) {
1041 | 			conflicts.push(taskId);
1042 | 		}
1043 | 	});
1044 | 
1045 | 	return conflicts;
1046 | }
1047 | 
1048 | /**
1049 |  * Preserve task metadata during cross-tag moves
1050 |  * @param {Object} task - Task object
1051 |  * @param {string} sourceTag - Source tag name
1052 |  * @param {string} targetTag - Target tag name
1053 |  * @returns {Object} Task object with preserved metadata
1054 |  */
1055 | function preserveTaskMetadata(task, sourceTag, targetTag) {
1056 | 	// Update the tag property to reflect the new location
1057 | 	task.tag = targetTag;
1058 | 
1059 | 	// Add move history to task metadata
1060 | 	if (!task.metadata) {
1061 | 		task.metadata = {};
1062 | 	}
1063 | 
1064 | 	if (!task.metadata.moveHistory) {
1065 | 		task.metadata.moveHistory = [];
1066 | 	}
1067 | 
1068 | 	task.metadata.moveHistory.push({
1069 | 		fromTag: sourceTag,
1070 | 		toTag: targetTag,
1071 | 		timestamp: new Date().toISOString()
1072 | 	});
1073 | 
1074 | 	return task;
1075 | }
1076 | 
1077 | export default moveTask;
1078 | export {
1079 | 	moveTasksBetweenTags,
1080 | 	getAllTasksWithTags,
1081 | 	detectIdConflicts,
1082 | 	preserveTaskMetadata,
1083 | 	MoveTaskError,
1084 | 	MOVE_ERROR_CODES
1085 | };
1086 | 
```
Page 40/52FirstPrevNextLast