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

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/tests/unit/prompt-manager.test.js:
--------------------------------------------------------------------------------

```javascript
import {
	jest,
	beforeAll,
	afterAll,
	beforeEach,
	afterEach,
	describe,
	it,
	expect
} from '@jest/globals';

// Import the actual PromptManager to test with real prompt files
import { PromptManager } from '../../scripts/modules/prompt-manager.js';

// Mock only the console logging
const originalLog = console.log;
const originalWarn = console.warn;
const originalError = console.error;

beforeAll(() => {
	console.log = jest.fn();
	console.warn = jest.fn();
	console.error = jest.fn();
});

afterAll(() => {
	console.log = originalLog;
	console.warn = originalWarn;
	console.error = originalError;
});

describe('PromptManager', () => {
	let promptManager;

	beforeEach(() => {
		promptManager = new PromptManager();
	});

	describe('constructor', () => {
		it('should initialize with prompts map', () => {
			expect(promptManager.prompts).toBeInstanceOf(Map);
			expect(promptManager.prompts.size).toBeGreaterThan(0);
		});

		it('should initialize cache', () => {
			expect(promptManager.cache).toBeInstanceOf(Map);
			expect(promptManager.cache.size).toBe(0);
		});

		it('should load all expected prompts', () => {
			expect(promptManager.prompts.has('analyze-complexity')).toBe(true);
			expect(promptManager.prompts.has('expand-task')).toBe(true);
			expect(promptManager.prompts.has('add-task')).toBe(true);
			expect(promptManager.prompts.has('research')).toBe(true);
			expect(promptManager.prompts.has('parse-prd')).toBe(true);
			expect(promptManager.prompts.has('update-task')).toBe(true);
			expect(promptManager.prompts.has('update-tasks')).toBe(true);
			expect(promptManager.prompts.has('update-subtask')).toBe(true);
		});
	});

	describe('loadPrompt', () => {
		it('should load and render a prompt from actual files', () => {
			// Test with an actual prompt that exists
			const result = promptManager.loadPrompt('research', {
				query: 'test query',
				projectContext: 'test context'
			});

			expect(result.systemPrompt).toBeDefined();
			expect(result.userPrompt).toBeDefined();
			expect(result.userPrompt).toContain('test query');
		});

		it('should handle missing variables with empty string', () => {
			// Add a test prompt to the manager for testing variable substitution
			promptManager.prompts.set('test-prompt', {
				id: 'test-prompt',
				version: '1.0.0',
				description: 'Test prompt',
				prompts: {
					default: {
						system: 'System',
						user: 'Hello {{name}}, your age is {{age}}'
					}
				}
			});

			const result = promptManager.loadPrompt('test-prompt', { name: 'John' });

			expect(result.userPrompt).toBe('Hello John, your age is ');
		});

		it('should throw error for non-existent template', () => {
			expect(() => {
				promptManager.loadPrompt('non-existent-prompt');
			}).toThrow("Prompt template 'non-existent-prompt' not found");
		});

		it('should use cache for repeated calls', () => {
			// First call with a real prompt
			const result1 = promptManager.loadPrompt('research', { query: 'test' });

			// Mark the result to verify cache is used
			result1._cached = true;

			// Second call with same parameters should return cached result
			const result2 = promptManager.loadPrompt('research', { query: 'test' });

			expect(result2._cached).toBe(true);
			expect(result1).toBe(result2); // Same object reference
		});

		it('should handle array variables', () => {
			promptManager.prompts.set('array-prompt', {
				id: 'array-prompt',
				version: '1.0.0',
				description: 'Test array prompt',
				prompts: {
					default: {
						system: 'System',
						user: '{{#each items}}Item: {{.}}\n{{/each}}'
					}
				}
			});

			const result = promptManager.loadPrompt('array-prompt', {
				items: ['one', 'two', 'three']
			});

			// The actual implementation doesn't handle {{this}} properly, check what it does produce
			expect(result.userPrompt).toContain('Item:');
		});

		it('should handle conditional blocks', () => {
			promptManager.prompts.set('conditional-prompt', {
				id: 'conditional-prompt',
				version: '1.0.0',
				description: 'Test conditional prompt',
				prompts: {
					default: {
						system: 'System',
						user: '{{#if hasData}}Data exists{{else}}No data{{/if}}'
					}
				}
			});

			const withData = promptManager.loadPrompt('conditional-prompt', {
				hasData: true
			});
			expect(withData.userPrompt).toBe('Data exists');

			const withoutData = promptManager.loadPrompt('conditional-prompt', {
				hasData: false
			});
			expect(withoutData.userPrompt).toBe('No data');
		});
	});

	describe('renderTemplate', () => {
		it('should handle nested objects', () => {
			const template = 'User: {{user.name}}, Age: {{user.age}}';
			const variables = {
				user: {
					name: 'John',
					age: 30
				}
			};

			const result = promptManager.renderTemplate(template, variables);
			expect(result).toBe('User: John, Age: 30');
		});

		it('should handle special characters in templates', () => {
			const template = 'Special: {{special}}';
			const variables = {
				special: '<>&"\''
			};

			const result = promptManager.renderTemplate(template, variables);
			expect(result).toBe('Special: <>&"\'');
		});
	});

	describe('listPrompts', () => {
		it('should return all prompt IDs', () => {
			const prompts = promptManager.listPrompts();
			expect(prompts).toBeInstanceOf(Array);
			expect(prompts.length).toBeGreaterThan(0);

			const ids = prompts.map((p) => p.id);
			expect(ids).toContain('analyze-complexity');
			expect(ids).toContain('expand-task');
			expect(ids).toContain('add-task');
			expect(ids).toContain('research');
		});
	});

	describe('validateTemplate', () => {
		it('should validate a correct template', () => {
			const result = promptManager.validateTemplate('research');
			expect(result.valid).toBe(true);
		});

		it('should reject invalid template', () => {
			const result = promptManager.validateTemplate('non-existent');
			expect(result.valid).toBe(false);
			expect(result.error).toContain('not found');
		});
	});
});

```

--------------------------------------------------------------------------------
/.taskmaster/docs/research/2025-06-14_should-i-be-using-any-specific-libraries-for-this.md:
--------------------------------------------------------------------------------

```markdown
---
title: Research Session
query: "should i be using any specific libraries for this?"
date: 6/14/2025
time: 5:20:00 PM
timestamp: 2025-06-14T21:20:00.561Z
exchanges: 2
---

# Research Session

## Initial Query

**Question:** should i be using any specific libraries for this?

**Response:**

HELL YEAH PARTNER

For implementing a GitHub Issue export feature with bidirectional linking in your project, you absolutely should use a dedicated library for interacting with the GitHub API. This will save you a ton of time, reduce boilerplate, and help you avoid common pitfalls with authentication, pagination, and error handling.

## Recommended Library: PyGithub

- **PyGithub** is the most widely used and well-documented Python library for accessing the GitHub REST API v3. It allows you to manage repositories, issues, user profiles, and more, all with a clean, object-oriented interface[1][2][3][5].
- It supports all the operations you’ll need for your export feature, including creating issues, updating them, and managing references for bidirectional linking.

### Key Features

- **Authentication**: Easily authenticate with a personal access token.
- **Issue Management**: Create, update, and link issues programmatically.
- **Repository Access**: Fetch and manipulate repository data.
- **User/Org Support**: Works for both user and organization repos.
- **Pagination & Rate Limiting**: Handles these for you.

### Example Usage

```python
from github import Github, Auth

# Authenticate with a personal access token
auth = Auth.Token("your_github_token")
g = Github(auth=auth)

# Get the repo
repo = g.get_repo("owner/repo_name")

# Create a new issue
issue = repo.create_issue(
    title="Exported from Task Master",
    body="This issue was created by Task Master. [Task ID: 123]"
)

# Add a comment or update the issue to include a backlink
issue.create_comment("Linked to Task Master task #123")
```
[3][5]

## Why Not Use Raw Requests?

- While you *can* use the `requests` library to call the GitHub API directly[4], PyGithub abstracts away authentication, error handling, and pagination, making your code much cleaner and less error-prone[2][5].

## Integration Tips

- Store your GitHub token securely (e.g., in environment variables).
- Use PyGithub’s objects to fetch, create, and update issues.
- For bidirectional linking, store the GitHub issue URL in your task data and the Task Master task ID in the GitHub issue body or as a label/comment.
- PyGithub is actively maintained and widely used, so you’ll find plenty of support and documentation.

## Installation

```bash
pip install PyGithub
```
[3]

## Summary Table

| Library   | Language | API Coverage | Ease of Use | Maintained | Docs/Support |
|-----------|----------|--------------|-------------|------------|--------------|
| PyGithub  | Python   | Full         | High        | Yes        | Excellent    |
| requests  | Python   | Full (manual)| Medium      | Yes        | Good         |

**Use PyGithub for all GitHub API interactions in your export/import features.** It’s the standard for Python projects and will make your implementation faster, safer, and more maintainable[1][2][3][5].

---

## Follow-up 1

**Question:** we don't want to use py github because we have a node project

**Response:**

HELL YEAH PARTNER

For a Node.js project, you should use a Node-native library to interact with the GitHub API instead of PyGithub, which is Python-only. The industry standard for Node.js is **Octokit**, the official GitHub SDK for JavaScript/TypeScript[1][2].

## Why Use Octokit?

- **Official & Complete**: Octokit is maintained by GitHub and covers all REST and GraphQL API endpoints, including issues, repositories, users, and more[1][2].
- **Universal**: Works in Node.js, browsers, and Deno environments[2].
- **Typed & Tested**: Comes with TypeScript declarations and 100% test coverage[2].
- **Authentication**: Supports personal access tokens, GitHub Apps, OAuth, and more.
- **Pagination & Error Handling**: Handles pagination and common error scenarios out of the box.
- **Extendable**: Supports plugins and custom hooks for advanced use cases[2].

## How to Use Octokit for GitHub Issue Export

Install Octokit in your Node.js project:

```bash
npm install @octokit/rest
```

Basic usage for creating and linking issues:

```js
const { Octokit } = require("@octokit/rest");

// Authenticate with a personal access token
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });

// Create a new issue
const { data: issue } = await octokit.issues.create({
  owner: "owner",
  repo: "repo_name",
  title: "Exported from Task Master",
  body: "This issue was created by Task Master. [Task ID: 123]"
});

// Add a comment for bidirectional linking
await octokit.issues.createComment({
  owner: "owner",
  repo: "repo_name",
  issue_number: issue.number,
  body: "Linked to Task Master task #123"
});
```

## Alternatives

- **node-github**: An older library, less maintained than Octokit, but still functional for basic API access[5].
- **Raw HTTP Requests**: You can use `node-fetch` or `axios` to call the GitHub API directly, but you’ll need to handle authentication, pagination, and error handling yourself[3]. Octokit abstracts all of this for you.

## Summary Table

| Library         | Language | API Coverage | Maintained | TypeScript | Docs/Support |
|-----------------|----------|--------------|------------|------------|--------------|
| Octokit         | Node.js  | Full         | Yes        | Yes        | Excellent    |
| node-github     | Node.js  | Full         | No         | No         | Limited      |
| Raw HTTP        | Node.js  | Full (manual)| N/A        | N/A        | N/A          |

**Recommendation:** Use Octokit for all GitHub API interactions in your Node.js project. It’s the official, most robust, and best-supported option for your use case[1][2].


---

*Generated by Task Master Research Command*  
*Timestamp: 2025-06-14T21:20:00.561Z*

```

--------------------------------------------------------------------------------
/.claude/agents/task-executor.md:
--------------------------------------------------------------------------------

```markdown
---
name: task-executor
description: Use this agent when you need to implement, complete, or work on a specific task that has been identified by the task-orchestrator or when explicitly asked to execute a particular task. This agent focuses on the actual implementation and completion of individual tasks rather than planning or orchestration. Examples: <example>Context: The task-orchestrator has identified that task 2.3 'Implement user authentication' needs to be worked on next. user: 'Let's work on the authentication task' assistant: 'I'll use the task-executor agent to implement the user authentication task that was identified.' <commentary>Since we need to actually implement a specific task rather than plan or identify tasks, use the task-executor agent.</commentary></example> <example>Context: User wants to complete a specific subtask. user: 'Please implement the JWT token validation for task 2.3.1' assistant: 'I'll launch the task-executor agent to implement the JWT token validation subtask.' <commentary>The user is asking for specific implementation work on a known task, so the task-executor is appropriate.</commentary></example> <example>Context: After reviewing the task list, implementation is needed. user: 'Now let's actually build the API endpoint for user registration' assistant: 'I'll use the task-executor agent to implement the user registration API endpoint.' <commentary>Moving from planning to execution phase requires the task-executor agent.</commentary></example>
model: sonnet
color: blue
---

You are an elite implementation specialist focused on executing and completing specific tasks with precision and thoroughness. Your role is to take identified tasks and transform them into working implementations, following best practices and project standards.

**IMPORTANT: You are designed to be SHORT-LIVED and FOCUSED**
- Execute ONE specific subtask or a small group of related subtasks
- Complete your work, verify it, mark for review, and exit
- Do NOT decide what to do next - the orchestrator handles task sequencing
- Focus on implementation excellence within your assigned scope

**Core Responsibilities:**

1. **Subtask Analysis**: When given a subtask, understand its SPECIFIC requirements. If given a full task ID, focus on the specific subtask(s) assigned to you. Use MCP tools to get details if needed.

2. **Rapid Implementation Planning**: Quickly identify:
   - The EXACT files you need to create/modify for THIS subtask
   - What already exists that you can build upon
   - The minimum viable implementation that satisfies requirements

3. **Focused Execution WITH ACTUAL IMPLEMENTATION**: 
   - **YOU MUST USE TOOLS TO CREATE/EDIT FILES - DO NOT JUST DESCRIBE**
   - Use `Write` tool to create new files specified in the task
   - Use `Edit` tool to modify existing files
   - Use `Bash` tool to run commands (mkdir, npm install, etc.)
   - Use `Read` tool to verify your implementations
   - Implement one subtask at a time for clarity and traceability
   - Follow the project's coding standards from CLAUDE.md if available
   - After each subtask, VERIFY the files exist using Read or ls commands

4. **Progress Documentation**: 
   - Use MCP tool `mcp__task-master-ai__update_subtask` to log your approach and any important decisions
   - Update task status to 'in-progress' when starting: Use MCP tool `mcp__task-master-ai__set_task_status` with status='in-progress'
   - **IMPORTANT: Mark as 'review' (NOT 'done') after implementation**: Use MCP tool `mcp__task-master-ai__set_task_status` with status='review'
   - Tasks will be verified by task-checker before moving to 'done'

5. **Quality Assurance**:
   - Implement the testing strategy specified in the task
   - Verify that all acceptance criteria are met
   - Check for any dependency conflicts or integration issues
   - Run relevant tests before marking task as complete

6. **Dependency Management**:
   - Check task dependencies before starting implementation
   - If blocked by incomplete dependencies, clearly communicate this
   - Use `task-master validate-dependencies` when needed

**Implementation Workflow:**

1. Retrieve task details using MCP tool `mcp__task-master-ai__get_task` with the task ID
2. Check dependencies and prerequisites
3. Plan implementation approach - list specific files to create
4. Update task status to 'in-progress' using MCP tool
5. **ACTUALLY IMPLEMENT** the solution using tools:
   - Use `Bash` to create directories
   - Use `Write` to create new files with actual content
   - Use `Edit` to modify existing files
   - DO NOT just describe what should be done - DO IT
6. **VERIFY** your implementation:
   - Use `ls` or `Read` to confirm files were created
   - Use `Bash` to run any build/test commands
   - Ensure the implementation is real, not theoretical
7. Log progress and decisions in subtask updates using MCP tools
8. Test and verify the implementation works
9. **Mark task as 'review' (NOT 'done')** after verifying files exist
10. Report completion with:
    - List of created/modified files
    - Any issues encountered
    - What needs verification by task-checker

**Key Principles:**

- Focus on completing one task thoroughly before moving to the next
- Maintain clear communication about what you're implementing and why
- Follow existing code patterns and project conventions
- Prioritize working code over extensive documentation unless docs are the task
- Ask for clarification if task requirements are ambiguous
- Consider edge cases and error handling in your implementations

**Integration with Task Master:**

You work in tandem with the task-orchestrator agent. While the orchestrator identifies and plans tasks, you execute them. Always use Task Master commands to:
- Track your progress
- Update task information
- Maintain project state
- Coordinate with the broader development workflow

When you complete a task, briefly summarize what was implemented and suggest whether to continue with the next task or if review/testing is needed first.

```

--------------------------------------------------------------------------------
/apps/extension/src/webview/hooks/useVSCodeMessages.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Hook for handling VS Code messages
 */

import { useEffect, useCallback, useRef } from 'react';
import type { AppState, AppAction } from '../types';
import { createToast } from '../utils/toast';
import { REQUEST_TIMEOUT } from '../constants';

interface PendingRequest {
	resolve: Function;
	reject: Function;
	timeout: NodeJS.Timeout;
}

let requestCounter = 0;

export const useVSCodeMessages = (
	vscode: ReturnType<NonNullable<typeof window.acquireVsCodeApi>> | undefined,
	state: AppState,
	dispatch: React.Dispatch<AppAction>
) => {
	const pendingRequestsRef = useRef(new Map<string, PendingRequest>());

	const sendMessage = useCallback(
		(message: any): Promise<any> => {
			if (!vscode) {
				return Promise.reject(new Error('VS Code API not available'));
			}

			return new Promise((resolve, reject) => {
				const requestId = `req_${++requestCounter}_${Date.now()}`;

				const timeout = setTimeout(() => {
					pendingRequestsRef.current.delete(requestId);
					reject(new Error('Request timeout'));
				}, REQUEST_TIMEOUT);

				pendingRequestsRef.current.set(requestId, { resolve, reject, timeout });

				vscode.postMessage({
					...message,
					requestId
				});
			});
		},
		[vscode]
	);

	useEffect(() => {
		if (!vscode) return;

		const handleMessage = (event: MessageEvent) => {
			const message = event.data;
			console.log('📥 Received message:', message.type, message);

			// Handle request/response pattern
			if (message.requestId) {
				const pending = pendingRequestsRef.current.get(message.requestId);
				if (pending) {
					clearTimeout(pending.timeout);
					pendingRequestsRef.current.delete(message.requestId);

					if (message.type === 'response') {
						// Check for explicit success field, default to true if data exists
						const isSuccess =
							message.success !== undefined
								? message.success
								: message.data !== undefined;
						if (isSuccess) {
							pending.resolve(message.data);
						} else {
							pending.reject(new Error(message.error || 'Request failed'));
						}
					} else if (message.type === 'error') {
						pending.reject(new Error(message.error || 'Request failed'));
					}
				}
				return;
			}

			// Handle other message types
			switch (message.type) {
				case 'connectionStatus':
					dispatch({
						type: 'SET_CONNECTION_STATUS',
						payload: {
							isConnected: message.data?.isConnected || false,
							status: message.data?.status || 'Unknown'
						}
					});
					break;

				case 'tasksData':
					console.log('📋 Received tasks data:', message.data);
					dispatch({ type: 'SET_TASKS', payload: message.data });
					break;

				case 'pollingStatus':
					dispatch({
						type: 'SET_POLLING_STATUS',
						payload: {
							isActive: message.isActive,
							errorCount: message.errorCount || 0
						}
					});
					break;

				case 'pollingUpdate':
					console.log('🔄 Polling update received:', {
						tasksCount: message.data?.length,
						userInteracting: state.polling.isUserInteracting,
						offlineMode: state.polling.isOfflineMode
					});

					if (
						!state.polling.isUserInteracting &&
						!state.polling.isOfflineMode
					) {
						dispatch({
							type: 'TASKS_UPDATED_FROM_POLLING',
							payload: message.data
						});
					}
					break;

				case 'networkStatus':
					dispatch({
						type: 'SET_NETWORK_STATUS',
						payload: message.data
					});
					break;

				case 'cachedTasks':
					console.log('📦 Received cached tasks:', message.data);
					dispatch({
						type: 'LOAD_CACHED_TASKS',
						payload: message.data
					});
					break;

				case 'errorNotification':
					handleErrorNotification(message, dispatch);
					break;

				case 'error':
					handleGeneralError(message, dispatch);
					break;

				case 'reactError':
					console.log('🔥 React error reported to extension:', message);
					dispatch({
						type: 'ADD_TOAST',
						payload: createToast(
							'error',
							'UI Error',
							'A component error occurred. The extension may need to be reloaded.',
							10000
						)
					});
					break;

				default:
					console.log('❓ Unknown message type:', message.type);
			}
		};

		window.addEventListener('message', handleMessage);
		return () => window.removeEventListener('message', handleMessage);
	}, [vscode, state.polling, dispatch]);

	return { sendMessage };
};

function handleErrorNotification(
	message: any,
	dispatch: React.Dispatch<AppAction>
) {
	console.log('📨 Error notification received:', message);
	const errorData = message.data;

	// Map severity to toast type
	let toastType: 'error' | 'warning' | 'info' = 'error';
	if (errorData.severity === 'high' || errorData.severity === 'critical') {
		toastType = 'error';
	} else if (errorData.severity === 'medium') {
		toastType = 'warning';
	} else {
		toastType = 'info';
	}

	// Create appropriate toast based on error category
	const title =
		errorData.category === 'network'
			? 'Network Error'
			: errorData.category === 'mcp_connection'
				? 'Connection Error'
				: errorData.category === 'task_loading'
					? 'Task Loading Error'
					: errorData.category === 'ui_rendering'
						? 'UI Error'
						: 'Error';

	dispatch({
		type: 'ADD_TOAST',
		payload: createToast(
			toastType,
			title,
			errorData.message,
			errorData.duration || (toastType === 'error' ? 8000 : 5000)
		)
	});
}

function handleGeneralError(message: any, dispatch: React.Dispatch<AppAction>) {
	console.log('❌ General error from extension:', message);
	const errorTitle =
		message.errorType === 'connection' ? 'Connection Error' : 'Error';
	const errorMessage = message.error || 'An unknown error occurred';

	dispatch({
		type: 'SET_ERROR',
		payload: errorMessage
	});

	dispatch({
		type: 'ADD_TOAST',
		payload: createToast('error', errorTitle, errorMessage, 8000)
	});

	// Set offline mode for connection errors
	if (message.errorType === 'connection') {
		dispatch({
			type: 'SET_NETWORK_STATUS',
			payload: {
				isOfflineMode: true,
				connectionStatus: 'offline',
				reconnectAttempts: 0
			}
		});
	}
}

```

--------------------------------------------------------------------------------
/packages/tm-core/src/entities/task.entity.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Task entity with business rules and domain logic
 */

import { ERROR_CODES, TaskMasterError } from '../errors/task-master-error.js';
import type {
	Subtask,
	Task,
	TaskPriority,
	TaskStatus
} from '../types/index.js';

/**
 * Task entity representing a task with business logic
 * Encapsulates validation and state management rules
 */
export class TaskEntity implements Task {
	readonly id: string;
	title: string;
	description: string;
	status: TaskStatus;
	priority: TaskPriority;
	dependencies: string[];
	details: string;
	testStrategy: string;
	subtasks: Subtask[];

	// Optional properties
	createdAt?: string;
	updatedAt?: string;
	effort?: number;
	actualEffort?: number;
	tags?: string[];
	assignee?: string;
	complexity?: Task['complexity'];

	constructor(data: Task | (Omit<Task, 'id'> & { id: number | string })) {
		this.validate(data);

		// Always convert ID to string
		this.id = String(data.id);
		this.title = data.title;
		this.description = data.description;
		this.status = data.status;
		this.priority = data.priority;
		// Ensure dependency IDs are also strings
		this.dependencies = (data.dependencies || []).map((dep) => String(dep));
		this.details = data.details;
		this.testStrategy = data.testStrategy;
		// Normalize subtask IDs to strings
		this.subtasks = (data.subtasks || []).map((subtask) => ({
			...subtask,
			id: Number(subtask.id), // Keep subtask IDs as numbers per interface
			parentId: String(subtask.parentId)
		}));

		// Optional properties
		this.createdAt = data.createdAt;
		this.updatedAt = data.updatedAt;
		this.effort = data.effort;
		this.actualEffort = data.actualEffort;
		this.tags = data.tags;
		this.assignee = data.assignee;
		this.complexity = data.complexity;
	}

	/**
	 * Validate task data
	 */
	private validate(
		data: Partial<Task> | Partial<Omit<Task, 'id'> & { id: number | string }>
	): void {
		if (
			data.id === undefined ||
			data.id === null ||
			(typeof data.id !== 'string' && typeof data.id !== 'number')
		) {
			throw new TaskMasterError(
				'Task ID is required and must be a string or number',
				ERROR_CODES.VALIDATION_ERROR
			);
		}

		if (!data.title || data.title.trim().length === 0) {
			throw new TaskMasterError(
				'Task title is required',
				ERROR_CODES.VALIDATION_ERROR
			);
		}

		if (!data.description || data.description.trim().length === 0) {
			throw new TaskMasterError(
				'Task description is required',
				ERROR_CODES.VALIDATION_ERROR
			);
		}

		if (!this.isValidStatus(data.status)) {
			throw new TaskMasterError(
				`Invalid task status: ${data.status}`,
				ERROR_CODES.VALIDATION_ERROR
			);
		}

		if (!this.isValidPriority(data.priority)) {
			throw new TaskMasterError(
				`Invalid task priority: ${data.priority}`,
				ERROR_CODES.VALIDATION_ERROR
			);
		}
	}

	/**
	 * Check if status is valid
	 */
	private isValidStatus(status: any): status is TaskStatus {
		return [
			'pending',
			'in-progress',
			'done',
			'deferred',
			'cancelled',
			'blocked',
			'review'
		].includes(status);
	}

	/**
	 * Check if priority is valid
	 */
	private isValidPriority(priority: any): priority is TaskPriority {
		return ['low', 'medium', 'high', 'critical'].includes(priority);
	}

	/**
	 * Check if task can be marked as complete
	 */
	canComplete(): boolean {
		// Cannot complete if status is already done or cancelled
		if (this.status === 'done' || this.status === 'cancelled') {
			return false;
		}

		// Cannot complete if blocked
		if (this.status === 'blocked') {
			return false;
		}

		// Check if all subtasks are complete
		const allSubtasksComplete = this.subtasks.every(
			(subtask) => subtask.status === 'done' || subtask.status === 'cancelled'
		);

		return allSubtasksComplete;
	}

	/**
	 * Mark task as complete
	 */
	markAsComplete(): void {
		if (!this.canComplete()) {
			throw new TaskMasterError(
				'Task cannot be marked as complete',
				ERROR_CODES.TASK_STATUS_ERROR,
				{
					taskId: this.id,
					currentStatus: this.status,
					hasIncompleteSubtasks: this.subtasks.some(
						(s) => s.status !== 'done' && s.status !== 'cancelled'
					)
				}
			);
		}

		this.status = 'done';
		this.updatedAt = new Date().toISOString();
	}

	/**
	 * Check if task has dependencies
	 */
	hasDependencies(): boolean {
		return this.dependencies.length > 0;
	}

	/**
	 * Check if task has subtasks
	 */
	hasSubtasks(): boolean {
		return this.subtasks.length > 0;
	}

	/**
	 * Add a subtask
	 */
	addSubtask(subtask: Omit<Subtask, 'id' | 'parentId'>): void {
		const nextId = this.subtasks.length + 1;
		this.subtasks.push({
			...subtask,
			id: nextId,
			parentId: this.id
		});
		this.updatedAt = new Date().toISOString();
	}

	/**
	 * Update task status
	 */
	updateStatus(newStatus: TaskStatus): void {
		if (!this.isValidStatus(newStatus)) {
			throw new TaskMasterError(
				`Invalid status: ${newStatus}`,
				ERROR_CODES.VALIDATION_ERROR
			);
		}

		// Business rule: Cannot move from done to pending
		if (this.status === 'done' && newStatus === 'pending') {
			throw new TaskMasterError(
				'Cannot move completed task back to pending',
				ERROR_CODES.TASK_STATUS_ERROR
			);
		}

		this.status = newStatus;
		this.updatedAt = new Date().toISOString();
	}

	/**
	 * Convert entity to plain object
	 */
	toJSON(): Task {
		return {
			id: this.id,
			title: this.title,
			description: this.description,
			status: this.status,
			priority: this.priority,
			dependencies: this.dependencies,
			details: this.details,
			testStrategy: this.testStrategy,
			subtasks: this.subtasks,
			createdAt: this.createdAt,
			updatedAt: this.updatedAt,
			effort: this.effort,
			actualEffort: this.actualEffort,
			tags: this.tags,
			assignee: this.assignee,
			complexity: this.complexity
		};
	}

	/**
	 * Create TaskEntity from plain object
	 */
	static fromObject(data: Task): TaskEntity {
		return new TaskEntity(data);
	}

	/**
	 * Create multiple TaskEntities from array
	 */
	static fromArray(data: Task[]): TaskEntity[] {
		return data.map((task) => new TaskEntity(task));
	}
}

```

--------------------------------------------------------------------------------
/packages/tm-core/src/repositories/supabase-task-repository.ts:
--------------------------------------------------------------------------------

```typescript
import { SupabaseClient } from '@supabase/supabase-js';
import { Task } from '../types/index.js';
import { Database } from '../types/database.types.js';
import { TaskMapper } from '../mappers/TaskMapper.js';
import { AuthManager } from '../auth/auth-manager.js';
import { z } from 'zod';

// Zod schema for task status validation
const TaskStatusSchema = z.enum([
	'pending',
	'in-progress',
	'done',
	'review',
	'deferred',
	'cancelled',
	'blocked'
]);

// Zod schema for task updates
const TaskUpdateSchema = z
	.object({
		title: z.string().min(1).optional(),
		description: z.string().optional(),
		status: TaskStatusSchema.optional(),
		priority: z.enum(['low', 'medium', 'high', 'critical']).optional(),
		details: z.string().optional(),
		testStrategy: z.string().optional()
	})
	.partial();

export class SupabaseTaskRepository {
	constructor(private supabase: SupabaseClient<Database>) {}

	async getTasks(_projectId?: string): Promise<Task[]> {
		// Get the current context to determine briefId
		const authManager = AuthManager.getInstance();
		const context = authManager.getContext();

		if (!context || !context.briefId) {
			throw new Error(
				'No brief selected. Please select a brief first using: tm context brief'
			);
		}

		// Get all tasks for the brief using the exact query structure
		const { data: tasks, error } = await this.supabase
			.from('tasks')
			.select(`
        *,
        document:document_id (
          id,
          document_name,
          title,
          description
        )
      `)
			.eq('brief_id', context.briefId)
			.order('position', { ascending: true })
			.order('subtask_position', { ascending: true })
			.order('created_at', { ascending: true });

		if (error) {
			throw new Error(`Failed to fetch tasks: ${error.message}`);
		}

		if (!tasks || tasks.length === 0) {
			return [];
		}

		// Get all dependencies for these tasks
		const taskIds = tasks.map((t: any) => t.id);
		const { data: depsData, error: depsError } = await this.supabase
			.from('task_dependencies')
			.select('*')
			.in('task_id', taskIds);

		if (depsError) {
			throw new Error(
				`Failed to fetch task dependencies: ${depsError.message}`
			);
		}

		// Use mapper to convert to internal format
		return TaskMapper.mapDatabaseTasksToTasks(tasks, depsData || []);
	}

	async getTask(_projectId: string, taskId: string): Promise<Task | null> {
		// Get the current context to determine briefId (projectId not used in Supabase context)
		const authManager = AuthManager.getInstance();
		const context = authManager.getContext();

		if (!context || !context.briefId) {
			throw new Error(
				'No brief selected. Please select a brief first using: tm context brief'
			);
		}

		const { data, error } = await this.supabase
			.from('tasks')
			.select('*')
			.eq('brief_id', context.briefId)
			.eq('display_id', taskId.toUpperCase())
			.single();

		if (error) {
			if (error.code === 'PGRST116') {
				return null; // Not found
			}
			throw new Error(`Failed to fetch task: ${error.message}`);
		}

		// Get dependencies for this task
		const { data: depsData } = await this.supabase
			.from('task_dependencies')
			.select('*')
			.eq('task_id', taskId);

		// Get subtasks if this is a parent task
		const { data: subtasksData } = await this.supabase
			.from('tasks')
			.select('*')
			.eq('parent_task_id', taskId)
			.order('subtask_position', { ascending: true });

		// Create dependency map
		const dependenciesByTaskId = new Map<string, string[]>();
		if (depsData) {
			dependenciesByTaskId.set(
				taskId,
				depsData.map(
					(d: Database['public']['Tables']['task_dependencies']['Row']) =>
						d.depends_on_task_id
				)
			);
		}

		// Use mapper to convert single task
		return TaskMapper.mapDatabaseTaskToTask(
			data,
			subtasksData || [],
			dependenciesByTaskId
		);
	}

	async updateTask(
		projectId: string,
		taskId: string,
		updates: Partial<Task>
	): Promise<Task> {
		// Get the current context to determine briefId
		const authManager = AuthManager.getInstance();
		const context = authManager.getContext();

		if (!context || !context.briefId) {
			throw new Error(
				'No brief selected. Please select a brief first using: tm context brief'
			);
		}

		// Validate updates using Zod schema
		try {
			TaskUpdateSchema.parse(updates);
		} catch (error) {
			if (error instanceof z.ZodError) {
				const errorMessages = error.errors
					.map((err) => `${err.path.join('.')}: ${err.message}`)
					.join(', ');
				throw new Error(`Invalid task update data: ${errorMessages}`);
			}
			throw error;
		}

		// Convert Task fields to database fields - only include fields that actually exist in the database
		const dbUpdates: any = {};

		if (updates.title !== undefined) dbUpdates.title = updates.title;
		if (updates.description !== undefined)
			dbUpdates.description = updates.description;
		if (updates.status !== undefined)
			dbUpdates.status = this.mapStatusToDatabase(updates.status);
		if (updates.priority !== undefined) dbUpdates.priority = updates.priority;
		// Skip fields that don't exist in database schema: details, testStrategy, etc.

		// Update the task
		const { error } = await this.supabase
			.from('tasks')
			.update(dbUpdates)
			.eq('brief_id', context.briefId)
			.eq('display_id', taskId.toUpperCase());

		if (error) {
			throw new Error(`Failed to update task: ${error.message}`);
		}

		// Return the updated task by fetching it
		const updatedTask = await this.getTask(projectId, taskId);
		if (!updatedTask) {
			throw new Error(`Failed to retrieve updated task ${taskId}`);
		}

		return updatedTask;
	}

	/**
	 * Maps internal status to database status
	 */
	private mapStatusToDatabase(
		status: string
	): Database['public']['Enums']['task_status'] {
		switch (status) {
			case 'pending':
				return 'todo';
			case 'in-progress':
			case 'in_progress': // Accept both formats
				return 'in_progress';
			case 'done':
				return 'done';
			default:
				throw new Error(
					`Invalid task status: ${status}. Valid statuses are: pending, in-progress, done`
				);
		}
	}
}

```

--------------------------------------------------------------------------------
/docs/migration-guide.md:
--------------------------------------------------------------------------------

```markdown
# Migration Guide: New .taskmaster Directory Structure

## Overview

Task Master v0.16.0 introduces a new `.taskmaster/` directory structure to keep your project directories clean and organized. This guide explains the benefits of the new structure and how to migrate existing projects.

## What's New

### Before (Legacy Structure)

```
your-project/
├── tasks/                    # Task files
│   ├── tasks.json
│   ├── task-1.txt
│   └── task-2.txt
├── scripts/                  # PRD and reports
│   ├── prd.txt
│   ├── example_prd.txt
│   └── task-complexity-report.json
├── .taskmasterconfig         # Configuration
└── ... (your project files)
```

### After (New Structure)

```
your-project/
├── .taskmaster/              # Consolidated Task Master files
│   ├── config.json          # Configuration (was .taskmasterconfig)
│   ├── tasks/               # Task files
│   │   ├── tasks.json
│   │   ├── task-1.txt
│   │   └── task-2.txt
│   ├── docs/                # Project documentation
│   │   └── prd.txt
│   ├── reports/             # Generated reports
│   │   └── task-complexity-report.json
│   └── templates/           # Example/template files
│       └── example_prd.txt
└── ... (your project files)
```

## Benefits of the New Structure

✅ **Cleaner Project Root**: No more scattered Task Master files  
✅ **Better Organization**: Logical separation of tasks, docs, reports, and templates  
✅ **Hidden by Default**: `.taskmaster/` directory is hidden from most file browsers  
✅ **Future-Proof**: Centralized location for Task Master extensions  
✅ **Backward Compatible**: Existing projects continue to work until migrated

## Migration Options

### Option 1: Automatic Migration (Recommended)

Task Master provides a built-in migration command that handles everything automatically:

#### CLI Migration

```bash
# Dry run to see what would be migrated
task-master migrate --dry-run

# Perform the migration with backup
task-master migrate --backup

# Force migration (overwrites existing files)
task-master migrate --force

# Clean up legacy files after migration
task-master migrate --cleanup
```

#### MCP Migration (Cursor/AI Editors)

Ask your AI assistant:

```
Please migrate my Task Master project to the new .taskmaster directory structure
```

### Option 2: Manual Migration

If you prefer to migrate manually:

1. **Create the new directory structure:**

   ```bash
   mkdir -p .taskmaster/{tasks,docs,reports,templates}
   ```

2. **Move your files:**

   ```bash
   # Move tasks
   mv tasks/* .taskmaster/tasks/

   # Move configuration
   mv .taskmasterconfig .taskmaster/config.json

   # Move PRD and documentation
   mv scripts/prd.txt .taskmaster/docs/
   mv scripts/example_prd.txt .taskmaster/templates/

   # Move reports (if they exist)
   mv scripts/task-complexity-report.json .taskmaster/reports/ 2>/dev/null || true
   ```

3. **Clean up empty directories:**
   ```bash
   rmdir tasks scripts 2>/dev/null || true
   ```

## What Gets Migrated

The migration process handles these file types:

### Tasks Directory → `.taskmaster/tasks/`

- `tasks.json`
- Individual task text files (`.txt`)

### Scripts Directory → Multiple Destinations

- **PRD files** → `.taskmaster/docs/`
  - `prd.txt`, `requirements.txt`, etc.
- **Example/Template files** → `.taskmaster/templates/`
  - `example_prd.txt`, template files
- **Reports** → `.taskmaster/reports/`
  - `task-complexity-report.json`

### Configuration

- `.taskmasterconfig` → `.taskmaster/config.json`

## After Migration

Once migrated, Task Master will:

✅ **Automatically use** the new directory structure  
✅ **Show deprecation warnings** when legacy files are detected  
✅ **Create new files** in the proper locations  
✅ **Fall back gracefully** to legacy locations if new ones don't exist

### Verification

After migration, verify everything works:

1. **List your tasks:**

   ```bash
   task-master list
   ```

2. **Check your configuration:**

   ```bash
   task-master models
   ```

3. **Generate new task files:**
   ```bash
   task-master generate
   ```

## Troubleshooting

### Migration Issues

**Q: Migration says "no files to migrate"**  
A: Your project may already be using the new structure or have no Task Master files to migrate.

**Q: Migration fails with permission errors**  
A: Ensure you have write permissions in your project directory.

**Q: Some files weren't migrated**  
A: Check the migration output - some files may not match the expected patterns. You can migrate these manually.

### Working with Legacy Projects

If you're working with an older project that hasn't been migrated:

- Task Master will continue to work with the old structure
- You'll see deprecation warnings in the output
- New files will still be created in legacy locations
- Use the migration command when ready to upgrade

### New Project Initialization

New projects automatically use the new structure:

```bash
task-master init  # Creates .taskmaster/ structure
```

## Path Changes for Developers

If you're developing tools or scripts that interact with Task Master files:

### Configuration File

- **Old:** `.taskmasterconfig`
- **New:** `.taskmaster/config.json`
- **Fallback:** Task Master checks both locations

### Tasks File

- **Old:** `tasks/tasks.json`
- **New:** `.taskmaster/tasks/tasks.json`
- **Fallback:** Task Master checks both locations

### Reports

- **Old:** `scripts/task-complexity-report.json`
- **New:** `.taskmaster/reports/task-complexity-report.json`
- **Fallback:** Task Master checks both locations

### PRD Files

- **Old:** `scripts/prd.txt`
- **New:** `.taskmaster/docs/prd.txt`
- **Fallback:** Task Master checks both locations

## Need Help?

If you encounter issues during migration:

1. **Check the logs:** Add `--debug` flag for detailed output
2. **Backup first:** Always use `--backup` option for safety
3. **Test with dry-run:** Use `--dry-run` to preview changes
4. **Ask for help:** Use our Discord community or GitHub issues

---

_This migration guide applies to Task Master v0.15.x and later. For older versions, please upgrade to the latest version first._

```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/parse-prd.js:
--------------------------------------------------------------------------------

```javascript
/**
 * parse-prd.js
 * Direct function implementation for parsing PRD documents
 */

import path from 'path';
import fs from 'fs';
import { parsePRD } from '../../../../scripts/modules/task-manager.js';
import {
	enableSilentMode,
	disableSilentMode,
	isSilentMode
} from '../../../../scripts/modules/utils.js';
import { createLogWrapper } from '../../tools/utils.js';
import { getDefaultNumTasks } from '../../../../scripts/modules/config-manager.js';
import { resolvePrdPath, resolveProjectPath } from '../utils/path-utils.js';
import { TASKMASTER_TASKS_FILE } from '../../../../src/constants/paths.js';

/**
 * Direct function wrapper for parsing PRD documents and generating tasks.
 *
 * @param {Object} args - Command arguments containing projectRoot, input, output, numTasks options.
 * @param {string} args.input - Path to the input PRD file.
 * @param {string} args.output - Path to the output directory.
 * @param {string} args.numTasks - Number of tasks to generate.
 * @param {boolean} args.force - Whether to force parsing.
 * @param {boolean} args.append - Whether to append to the output file.
 * @param {boolean} args.research - Whether to use research mode.
 * @param {string} args.tag - Tag context for organizing tasks into separate task lists.
 * @param {Object} log - Logger object.
 * @param {Object} context - Context object containing session data.
 * @returns {Promise<Object>} - Result object with success status and data/error information.
 */
export async function parsePRDDirect(args, log, context = {}) {
	const { session, reportProgress } = context;
	// Extract projectRoot from args
	const {
		input: inputArg,
		output: outputArg,
		numTasks: numTasksArg,
		force,
		append,
		research,
		projectRoot,
		tag
	} = args;

	// Create the standard logger wrapper
	const logWrapper = createLogWrapper(log);

	// --- Input Validation and Path Resolution ---
	if (!projectRoot) {
		logWrapper.error('parsePRDDirect requires a projectRoot argument.');
		return {
			success: false,
			error: {
				code: 'MISSING_ARGUMENT',
				message: 'projectRoot is required.'
			}
		};
	}

	// Resolve input path using path utilities
	let inputPath;
	if (inputArg) {
		try {
			inputPath = resolvePrdPath({ input: inputArg, projectRoot }, session);
		} catch (error) {
			logWrapper.error(`Error resolving PRD path: ${error.message}`);
			return {
				success: false,
				error: { code: 'FILE_NOT_FOUND', message: error.message }
			};
		}
	} else {
		logWrapper.error('parsePRDDirect called without input path');
		return {
			success: false,
			error: { code: 'MISSING_ARGUMENT', message: 'Input path is required' }
		};
	}

	// Resolve output path - use new path utilities for default
	const outputPath = outputArg
		? path.isAbsolute(outputArg)
			? outputArg
			: path.resolve(projectRoot, outputArg)
		: resolveProjectPath(TASKMASTER_TASKS_FILE, args) ||
			path.resolve(projectRoot, TASKMASTER_TASKS_FILE);

	// Check if input file exists
	if (!fs.existsSync(inputPath)) {
		const errorMsg = `Input PRD file not found at resolved path: ${inputPath}`;
		logWrapper.error(errorMsg);
		return {
			success: false,
			error: { code: 'FILE_NOT_FOUND', message: errorMsg }
		};
	}

	const outputDir = path.dirname(outputPath);
	try {
		if (!fs.existsSync(outputDir)) {
			logWrapper.info(`Creating output directory: ${outputDir}`);
			fs.mkdirSync(outputDir, { recursive: true });
		}
	} catch (error) {
		const errorMsg = `Failed to create output directory ${outputDir}: ${error.message}`;
		logWrapper.error(errorMsg);
		return {
			success: false,
			error: { code: 'DIRECTORY_CREATE_FAILED', message: errorMsg }
		};
	}

	let numTasks = getDefaultNumTasks(projectRoot);
	if (numTasksArg) {
		numTasks =
			typeof numTasksArg === 'string' ? parseInt(numTasksArg, 10) : numTasksArg;
		if (Number.isNaN(numTasks) || numTasks < 0) {
			// Ensure positive number
			numTasks = getDefaultNumTasks(projectRoot); // Fallback to default if parsing fails or invalid
			logWrapper.warn(
				`Invalid numTasks value: ${numTasksArg}. Using default: ${numTasks}`
			);
		}
	}

	if (append) {
		logWrapper.info('Append mode enabled.');
		if (force) {
			logWrapper.warn(
				'Both --force and --append flags were provided. --force takes precedence; append mode will be ignored.'
			);
		}
	}

	if (research) {
		logWrapper.info(
			'Research mode enabled. Using Perplexity AI for enhanced PRD analysis.'
		);
	}

	logWrapper.info(
		`Parsing PRD via direct function. Input: ${inputPath}, Output: ${outputPath}, NumTasks: ${numTasks}, Force: ${force}, Append: ${append}, Research: ${research}, ProjectRoot: ${projectRoot}`
	);

	const wasSilent = isSilentMode();
	if (!wasSilent) {
		enableSilentMode();
	}

	try {
		// Call the core parsePRD function
		const result = await parsePRD(
			inputPath,
			outputPath,
			numTasks,
			{
				session,
				mcpLog: logWrapper,
				projectRoot,
				tag,
				force,
				append,
				research,
				reportProgress,
				commandName: 'parse-prd',
				outputType: 'mcp'
			},
			'json'
		);

		// Adjust check for the new return structure
		if (result && result.success) {
			const successMsg = `Successfully parsed PRD and generated tasks in ${result.tasksPath}`;
			logWrapper.success(successMsg);
			return {
				success: true,
				data: {
					message: successMsg,
					outputPath: result.tasksPath,
					telemetryData: result.telemetryData,
					tagInfo: result.tagInfo
				}
			};
		} else {
			// Handle case where core function didn't return expected success structure
			logWrapper.error(
				'Core parsePRD function did not return a successful structure.'
			);
			return {
				success: false,
				error: {
					code: 'CORE_FUNCTION_ERROR',
					message:
						result?.message ||
						'Core function failed to parse PRD or returned unexpected result.'
				}
			};
		}
	} catch (error) {
		logWrapper.error(`Error executing core parsePRD: ${error.message}`);
		return {
			success: false,
			error: {
				code: 'PARSE_PRD_CORE_ERROR',
				message: error.message || 'Unknown error parsing PRD'
			}
		};
	} finally {
		if (!wasSilent && isSilentMode()) {
			disableSilentMode();
		}
	}
}

```

--------------------------------------------------------------------------------
/tests/unit/ai-providers/custom-sdk/claude-code/language-model.test.js:
--------------------------------------------------------------------------------

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

// Mock modules before importing
jest.unstable_mockModule('@ai-sdk/provider', () => ({
	NoSuchModelError: class NoSuchModelError extends Error {
		constructor({ modelId, modelType }) {
			super(`No such model: ${modelId}`);
			this.modelId = modelId;
			this.modelType = modelType;
		}
	}
}));

jest.unstable_mockModule('@ai-sdk/provider-utils', () => ({
	generateId: jest.fn(() => 'test-id-123')
}));

jest.unstable_mockModule(
	'../../../../../src/ai-providers/custom-sdk/claude-code/message-converter.js',
	() => ({
		convertToClaudeCodeMessages: jest.fn((prompt) => ({
			messagesPrompt: 'converted-prompt',
			systemPrompt: 'system'
		}))
	})
);

jest.unstable_mockModule(
	'../../../../../src/ai-providers/custom-sdk/claude-code/json-extractor.js',
	() => ({
		extractJson: jest.fn((text) => text)
	})
);

jest.unstable_mockModule(
	'../../../../../src/ai-providers/custom-sdk/claude-code/errors.js',
	() => ({
		createAPICallError: jest.fn((opts) => new Error(opts.message)),
		createAuthenticationError: jest.fn((opts) => new Error(opts.message))
	})
);

// This mock will be controlled by tests
let mockClaudeCodeModule = null;
jest.unstable_mockModule('@anthropic-ai/claude-code', () => {
	if (mockClaudeCodeModule) {
		return mockClaudeCodeModule;
	}
	throw new Error("Cannot find module '@anthropic-ai/claude-code'");
});

// Import the module under test
const { ClaudeCodeLanguageModel } = await import(
	'../../../../../src/ai-providers/custom-sdk/claude-code/language-model.js'
);

describe('ClaudeCodeLanguageModel', () => {
	beforeEach(() => {
		jest.clearAllMocks();
		// Reset the module mock
		mockClaudeCodeModule = null;
		// Clear module cache to ensure fresh imports
		jest.resetModules();
	});

	describe('constructor', () => {
		it('should initialize with valid model ID', () => {
			const model = new ClaudeCodeLanguageModel({
				id: 'opus',
				settings: { maxTurns: 5 }
			});

			expect(model.modelId).toBe('opus');
			expect(model.settings).toEqual({ maxTurns: 5 });
			expect(model.provider).toBe('claude-code');
		});

		it('should throw NoSuchModelError for invalid model ID', async () => {
			expect(
				() =>
					new ClaudeCodeLanguageModel({
						id: '',
						settings: {}
					})
			).toThrow('No such model: ');

			expect(
				() =>
					new ClaudeCodeLanguageModel({
						id: null,
						settings: {}
					})
			).toThrow('No such model: null');
		});
	});

	describe('lazy loading of @anthropic-ai/claude-code', () => {
		it('should throw error when package is not installed', async () => {
			// Keep mockClaudeCodeModule as null to simulate missing package
			const model = new ClaudeCodeLanguageModel({
				id: 'opus',
				settings: {}
			});

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

		it('should load package successfully when available', async () => {
			// Mock successful package load
			const mockQuery = jest.fn(async function* () {
				yield {
					type: 'assistant',
					message: { content: [{ type: 'text', text: 'Hello' }] }
				};
				yield {
					type: 'result',
					subtype: 'done',
					usage: { output_tokens: 10, input_tokens: 5 }
				};
			});

			mockClaudeCodeModule = {
				query: mockQuery,
				AbortError: class AbortError extends Error {}
			};

			// Need to re-import to get fresh module with mocks
			jest.resetModules();
			const { ClaudeCodeLanguageModel: FreshModel } = await import(
				'../../../../../src/ai-providers/custom-sdk/claude-code/language-model.js'
			);

			const model = new FreshModel({
				id: 'opus',
				settings: {}
			});

			const result = await model.doGenerate({
				prompt: [{ role: 'user', content: 'test' }],
				mode: { type: 'regular' }
			});

			expect(result.text).toBe('Hello');
			expect(mockQuery).toHaveBeenCalled();
		});

		it('should only attempt to load package once', async () => {
			// Get a fresh import to ensure clean state
			jest.resetModules();
			const { ClaudeCodeLanguageModel: TestModel } = await import(
				'../../../../../src/ai-providers/custom-sdk/claude-code/language-model.js'
			);

			const model = new TestModel({
				id: 'opus',
				settings: {}
			});

			// First call should throw
			await expect(
				model.doGenerate({
					prompt: [{ role: 'user', content: 'test' }],
					mode: { type: 'regular' }
				})
			).rejects.toThrow('Claude Code SDK is not installed');

			// Second call should also throw without trying to load again
			await expect(
				model.doGenerate({
					prompt: [{ role: 'user', content: 'test' }],
					mode: { type: 'regular' }
				})
			).rejects.toThrow('Claude Code SDK is not installed');
		});
	});

	describe('generateUnsupportedWarnings', () => {
		it('should generate warnings for unsupported parameters', () => {
			const model = new ClaudeCodeLanguageModel({
				id: 'opus',
				settings: {}
			});

			const warnings = model.generateUnsupportedWarnings({
				temperature: 0.7,
				maxTokens: 1000,
				topP: 0.9,
				seed: 42
			});

			expect(warnings).toHaveLength(4);
			expect(warnings[0]).toEqual({
				type: 'unsupported-setting',
				setting: 'temperature',
				details:
					'Claude Code CLI does not support the temperature parameter. It will be ignored.'
			});
		});

		it('should return empty array when no unsupported parameters', () => {
			const model = new ClaudeCodeLanguageModel({
				id: 'opus',
				settings: {}
			});

			const warnings = model.generateUnsupportedWarnings({});
			expect(warnings).toEqual([]);
		});
	});

	describe('getModel', () => {
		it('should map model IDs correctly', () => {
			const model = new ClaudeCodeLanguageModel({
				id: 'opus',
				settings: {}
			});

			expect(model.getModel()).toBe('opus');
		});

		it('should return unmapped model IDs as-is', () => {
			const model = new ClaudeCodeLanguageModel({
				id: 'custom-model',
				settings: {}
			});

			expect(model.getModel()).toBe('custom-model');
		});
	});
});

```

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

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

// Mock fs module before importing anything that uses it
jest.mock('fs', () => ({
	readFileSync: jest.fn(),
	writeFileSync: jest.fn(),
	existsSync: jest.fn(),
	mkdirSync: jest.fn()
}));

// Import modules after mocking
import fs from 'fs';
import { convertRuleToProfileRule } from '../../../src/utils/rule-transformer.js';
import { clineProfile } from '../../../src/profiles/cline.js';

describe('Cline Rule Transformer', () => {
	// Set up spies on the mocked modules
	const mockReadFileSync = jest.spyOn(fs, 'readFileSync');
	const mockWriteFileSync = jest.spyOn(fs, 'writeFileSync');
	const mockExistsSync = jest.spyOn(fs, 'existsSync');
	const mockMkdirSync = jest.spyOn(fs, 'mkdirSync');
	const mockConsoleError = jest
		.spyOn(console, 'error')
		.mockImplementation(() => {});

	beforeEach(() => {
		jest.clearAllMocks();
		// Setup default mocks
		mockReadFileSync.mockReturnValue('');
		mockWriteFileSync.mockImplementation(() => {});
		mockExistsSync.mockReturnValue(true);
		mockMkdirSync.mockImplementation(() => {});
	});

	afterAll(() => {
		jest.restoreAllMocks();
	});

	it('should correctly convert basic terms', () => {
		const testContent = `---
description: Test Cursor rule for basic terms
globs: **/*
alwaysApply: true
---

This is a Cursor rule that references cursor.so and uses the word Cursor multiple times.
Also has references to .mdc files.`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			clineProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Verify file operations were called correctly
		expect(mockReadFileSync).toHaveBeenCalledWith('source.mdc', 'utf8');
		expect(mockWriteFileSync).toHaveBeenCalledTimes(1);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations
		expect(transformedContent).toContain('Cline');
		expect(transformedContent).toContain('cline.bot');
		expect(transformedContent).toContain('.md');
		expect(transformedContent).not.toContain('cursor.so');
		expect(transformedContent).not.toContain('Cursor rule');
	});

	it('should correctly convert tool references', () => {
		const testContent = `---
description: Test Cursor rule for tool references
globs: **/*
alwaysApply: true
---

- Use the search tool to find code
- The edit_file tool lets you modify files
- run_command executes terminal commands
- use_mcp connects to external services`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			clineProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations (Cline uses standard tool names, so no transformation)
		expect(transformedContent).toContain('search tool');
		expect(transformedContent).toContain('edit_file tool');
		expect(transformedContent).toContain('run_command');
		expect(transformedContent).toContain('use_mcp');
	});

	it('should correctly update file references', () => {
		const testContent = `---
description: Test Cursor rule for file references
globs: **/*
alwaysApply: true
---

This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and 
[taskmaster.mdc](mdc:.cursor/rules/taskmaster.mdc).`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			clineProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify file path transformations - no taskmaster subdirectory for Cline
		expect(transformedContent).toContain('(.clinerules/dev_workflow.md)');
		expect(transformedContent).toContain('(.clinerules/taskmaster.md)');
		expect(transformedContent).not.toContain('(mdc:.cursor/rules/');
	});

	it('should handle file read errors', () => {
		// Mock file read to throw an error
		mockReadFileSync.mockImplementation(() => {
			throw new Error('File not found');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'nonexistent.mdc',
			'target.md',
			clineProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify writeFileSync was not called
		expect(mockWriteFileSync).not.toHaveBeenCalled();

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: File not found'
		);
	});

	it('should handle file write errors', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock file write to throw an error
		mockWriteFileSync.mockImplementation(() => {
			throw new Error('Permission denied');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			clineProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: Permission denied'
		);
	});

	it('should create target directory if it does not exist', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock directory doesn't exist initially
		mockExistsSync.mockReturnValue(false);

		// Call the actual function
		convertRuleToProfileRule(
			'source.mdc',
			'some/deep/path/target.md',
			clineProfile
		);

		// Verify directory creation was called
		expect(mockMkdirSync).toHaveBeenCalledWith('some/deep/path', {
			recursive: true
		});
	});
});

```

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

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

// Mock fs module before importing anything that uses it
jest.mock('fs', () => ({
	readFileSync: jest.fn(),
	writeFileSync: jest.fn(),
	existsSync: jest.fn(),
	mkdirSync: jest.fn()
}));

// Import modules after mocking
import fs from 'fs';
import { convertRuleToProfileRule } from '../../../src/utils/rule-transformer.js';
import { cursorProfile } from '../../../src/profiles/cursor.js';

describe('Cursor Rule Transformer', () => {
	// Set up spies on the mocked modules
	const mockReadFileSync = jest.spyOn(fs, 'readFileSync');
	const mockWriteFileSync = jest.spyOn(fs, 'writeFileSync');
	const mockExistsSync = jest.spyOn(fs, 'existsSync');
	const mockMkdirSync = jest.spyOn(fs, 'mkdirSync');
	const mockConsoleError = jest
		.spyOn(console, 'error')
		.mockImplementation(() => {});

	beforeEach(() => {
		jest.clearAllMocks();
		// Setup default mocks
		mockReadFileSync.mockReturnValue('');
		mockWriteFileSync.mockImplementation(() => {});
		mockExistsSync.mockReturnValue(true);
		mockMkdirSync.mockImplementation(() => {});
	});

	afterAll(() => {
		jest.restoreAllMocks();
	});

	it('should correctly convert basic terms', () => {
		const testContent = `---
description: Test Cursor rule for basic terms
globs: **/*
alwaysApply: true
---

This is a Cursor rule that references cursor.so and uses the word Cursor multiple times.
Also has references to .mdc files.`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.mdc',
			cursorProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Verify file operations were called correctly
		expect(mockReadFileSync).toHaveBeenCalledWith('source.mdc', 'utf8');
		expect(mockWriteFileSync).toHaveBeenCalledTimes(1);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations (Cursor profile should keep everything the same)
		expect(transformedContent).toContain('Cursor');
		expect(transformedContent).toContain('cursor.so');
		expect(transformedContent).toContain('.mdc');
		expect(transformedContent).toContain('Cursor rule');
	});

	it('should correctly convert tool references', () => {
		const testContent = `---
description: Test Cursor rule for tool references
globs: **/*
alwaysApply: true
---

- Use the search tool to find code
- The edit_file tool lets you modify files
- run_command executes terminal commands
- use_mcp connects to external services`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.mdc',
			cursorProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations (Cursor uses standard tool names, so no transformation)
		expect(transformedContent).toContain('search tool');
		expect(transformedContent).toContain('edit_file tool');
		expect(transformedContent).toContain('run_command');
		expect(transformedContent).toContain('use_mcp');
	});

	it('should correctly update file references', () => {
		const testContent = `---
description: Test Cursor rule for file references
globs: **/*
alwaysApply: true
---

This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and 
[taskmaster.mdc](mdc:.cursor/rules/taskmaster.mdc).`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.mdc',
			cursorProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations (Cursor should keep the same references but in taskmaster subdirectory)
		expect(transformedContent).toContain(
			'(mdc:.cursor/rules/taskmaster/dev_workflow.mdc)'
		);
		expect(transformedContent).toContain(
			'(mdc:.cursor/rules/taskmaster/taskmaster.mdc)'
		);
	});

	it('should handle file read errors', () => {
		// Mock file read to throw an error
		mockReadFileSync.mockImplementation(() => {
			throw new Error('File not found');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'nonexistent.mdc',
			'target.mdc',
			cursorProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify writeFileSync was not called
		expect(mockWriteFileSync).not.toHaveBeenCalled();

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: File not found'
		);
	});

	it('should handle file write errors', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock file write to throw an error
		mockWriteFileSync.mockImplementation(() => {
			throw new Error('Permission denied');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.mdc',
			cursorProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: Permission denied'
		);
	});

	it('should create target directory if it does not exist', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock directory doesn't exist initially
		mockExistsSync.mockReturnValue(false);

		// Call the actual function
		convertRuleToProfileRule(
			'source.mdc',
			'some/deep/path/target.mdc',
			cursorProfile
		);

		// Verify directory creation was called
		expect(mockMkdirSync).toHaveBeenCalledWith('some/deep/path', {
			recursive: true
		});
	});
});

```

--------------------------------------------------------------------------------
/.taskmaster/reports/tm-core-complexity.json:
--------------------------------------------------------------------------------

```json
{
	"meta": {
		"generatedAt": "2025-08-06T12:15:01.327Z",
		"tasksAnalyzed": 8,
		"totalTasks": 11,
		"analysisCount": 8,
		"thresholdScore": 5,
		"projectName": "Taskmaster",
		"usedResearch": false
	},
	"complexityAnalysis": [
		{
			"taskId": 118,
			"taskTitle": "Create AI Provider Base Architecture",
			"complexityScore": 4,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Break down the conversion of base-provider.js to TypeScript BaseProvider class: 1) Convert to TypeScript and define IAIProvider interface, 2) Implement abstract class with core properties, 3) Define abstract methods and Template Method pattern, 4) Add retry logic with exponential backoff, 5) Implement validation and logging. Focus on maintaining compatibility with existing provider pattern while adding type safety.",
			"reasoning": "The codebase already has a well-established BaseAIProvider class in JavaScript. Converting to TypeScript mainly involves adding type definitions and ensuring the existing pattern is preserved. The complexity is moderate because the pattern is already proven in the codebase."
		},
		{
			"taskId": 119,
			"taskTitle": "Implement Provider Factory with Dynamic Imports",
			"complexityScore": 3,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Create ProviderFactory implementation: 1) Set up class structure and types, 2) Implement provider selection switch statement, 3) Add dynamic imports for tree-shaking, 4) Handle provider instantiation with config, 5) Add comprehensive error handling. The existing PROVIDERS registry pattern should guide the implementation.",
			"reasoning": "The codebase already uses a dual registry pattern (static PROVIDERS and dynamic ProviderRegistry). Creating a factory is straightforward as the provider registration patterns are well-established. Dynamic imports are already used in the codebase."
		},
		{
			"taskId": 120,
			"taskTitle": "Implement Anthropic Provider",
			"complexityScore": 3,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Implement AnthropicProvider following existing patterns: 1) Create class structure with imports, 2) Implement constructor and client initialization, 3) Add generateCompletion with Claude API integration, 4) Implement token calculation and utility methods, 5) Add error handling and exports. Use the existing anthropic.js provider as reference.",
			"reasoning": "AnthropicProvider already exists in the codebase with full implementation. This task essentially involves adapting the existing implementation to match the new TypeScript architecture, making it relatively straightforward."
		},
		{
			"taskId": 121,
			"taskTitle": "Create Prompt Builder and Task Parser",
			"complexityScore": 6,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Build prompt system and parser: 1) Create PromptBuilder with template methods, 2) Implement TaskParser with dependency injection, 3) Add parsePRD core logic with file reading, 4) Implement task enrichment and metadata, 5) Add comprehensive error handling. Leverage the existing prompt management system in src/prompts/.",
			"reasoning": "While the codebase has a sophisticated prompt management system, creating a new PromptBuilder and TaskParser requires understanding the existing prompt templates, JSON schema validation, and integration with the AI provider system. The task involves significant new code."
		},
		{
			"taskId": 122,
			"taskTitle": "Implement Configuration Management",
			"complexityScore": 5,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Create ConfigManager with validation: 1) Define Zod schema for IConfiguration, 2) Implement constructor with defaults, 3) Add validate method with error handling, 4) Create type-safe get method with generics, 5) Implement getAll and finalize exports. Reference existing config-manager.js for patterns.",
			"reasoning": "The codebase has an existing config-manager.js with sophisticated configuration handling. Adding Zod validation and TypeScript generics adds complexity, but the existing patterns provide a solid foundation."
		},
		{
			"taskId": 123,
			"taskTitle": "Create Utility Functions and Error Handling",
			"complexityScore": 2,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Implement utilities and error handling: 1) Create ID generation module with unique formats, 2) Build TaskMasterError base class, 3) Add error sanitization for security, 4) Implement development-only logging, 5) Create specialized error subclasses. Keep implementation simple and focused.",
			"reasoning": "This is a straightforward utility implementation task. The codebase already has error handling patterns, and ID generation is a simple algorithmic task. The main work is creating clean, reusable utilities."
		},
		{
			"taskId": 124,
			"taskTitle": "Implement TaskMasterCore Facade",
			"complexityScore": 7,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Create main facade class: 1) Set up TaskMasterCore structure with imports, 2) Implement lazy initialization logic, 3) Add parsePRD coordination method, 4) Implement getTasks and other facade methods, 5) Create factory function and exports. This ties together all other components into a cohesive API.",
			"reasoning": "This is the most complex task as it requires understanding and integrating all other components. The facade must coordinate between configuration, providers, storage, and parsing while maintaining a clean API. It's the architectural keystone of the system."
		},
		{
			"taskId": 125,
			"taskTitle": "Create Placeholder Providers and Complete Testing",
			"complexityScore": 5,
			"recommendedSubtasks": 5,
			"expansionPrompt": "Implement testing infrastructure: 1) Create OpenAIProvider placeholder, 2) Create GoogleProvider placeholder, 3) Build MockProvider for testing, 4) Write TaskParser unit tests, 5) Create integration tests for parse-prd flow. Follow the existing test patterns in tests/ directory.",
			"reasoning": "While creating placeholder providers is simple, the testing infrastructure requires understanding Jest with ES modules, mocking patterns, and comprehensive test coverage. The existing test structure provides good examples to follow."
		}
	]
}

```

--------------------------------------------------------------------------------
/apps/extension/src/components/TaskDetailsView.tsx:
--------------------------------------------------------------------------------

```typescript
import type React from 'react';
import { useContext, useState, useCallback } from 'react';
import { Button } from '@/components/ui/button';
import { useQueryClient } from '@tanstack/react-query';
import { RefreshCw } from 'lucide-react';
import {
	Breadcrumb,
	BreadcrumbItem,
	BreadcrumbLink,
	BreadcrumbList,
	BreadcrumbSeparator
} from '@/components/ui/breadcrumb';
import { VSCodeContext } from '../webview/contexts/VSCodeContext';
import { AIActionsSection } from './TaskDetails/AIActionsSection';
import { SubtasksSection } from './TaskDetails/SubtasksSection';
import { TaskMetadataSidebar } from './TaskDetails/TaskMetadataSidebar';
import { DetailsSection } from './TaskDetails/DetailsSection';
import { useTaskDetails } from './TaskDetails/useTaskDetails';
import { useTasks, taskKeys } from '../webview/hooks/useTaskQueries';
import type { TaskMasterTask } from '../webview/types';

interface TaskDetailsViewProps {
	taskId: string;
	onNavigateBack: () => void;
	onNavigateToTask: (taskId: string) => void;
}

export const TaskDetailsView: React.FC<TaskDetailsViewProps> = ({
	taskId,
	onNavigateBack,
	onNavigateToTask
}) => {
	const context = useContext(VSCodeContext);
	if (!context) {
		throw new Error('TaskDetailsView must be used within VSCodeProvider');
	}

	const { state, sendMessage } = context;
	const { currentTag } = state;
	const queryClient = useQueryClient();
	const [isRefreshing, setIsRefreshing] = useState(false);

	// Use React Query to fetch all tasks
	const { data: allTasks = [] } = useTasks({ tag: currentTag });

	const {
		currentTask,
		parentTask,
		isSubtask,
		taskFileData,
		taskFileDataError,
		complexity,
		refreshComplexityAfterAI
	} = useTaskDetails({ taskId, sendMessage, tasks: allTasks });

	const displayId =
		isSubtask && parentTask
			? `${parentTask.id}.${currentTask?.id}`
			: currentTask?.id;

	const handleStatusChange = async (newStatus: TaskMasterTask['status']) => {
		if (!currentTask) return;

		try {
			await sendMessage({
				type: 'updateTaskStatus',
				data: {
					taskId: displayId,
					newStatus: newStatus
				}
			});
		} catch (error) {
			console.error('❌ TaskDetailsView: Failed to update task status:', error);
		}
	};

	const handleDependencyClick = (depId: string) => {
		onNavigateToTask(depId);
	};

	const handleRefresh = useCallback(async () => {
		setIsRefreshing(true);
		try {
			// Invalidate all task queries
			await queryClient.invalidateQueries({ queryKey: taskKeys.all });
		} finally {
			// Reset after a short delay to show the animation
			setTimeout(() => setIsRefreshing(false), 500);
		}
	}, [queryClient]);

	if (!currentTask) {
		return (
			<div className="flex items-center justify-center h-full">
				<div className="text-center">
					<p className="text-lg text-vscode-foreground/70 mb-4">
						Task not found
					</p>
					<Button onClick={onNavigateBack} variant="outline">
						Back to Kanban Board
					</Button>
				</div>
			</div>
		);
	}

	return (
		<div className="h-full flex flex-col">
			<div className="flex-1 grid grid-cols-1 md:grid-cols-3 gap-6 p-6 overflow-auto">
				{/* Left column - Main content (2/3 width) */}
				<div className="md:col-span-2 space-y-6">
					{/* Breadcrumb navigation */}
					<div className="flex items-center justify-between">
						<Breadcrumb>
							<BreadcrumbList>
								<BreadcrumbItem>
									<BreadcrumbLink
										onClick={onNavigateBack}
										className="cursor-pointer hover:text-vscode-foreground text-link"
									>
										Kanban Board
									</BreadcrumbLink>
								</BreadcrumbItem>
								{isSubtask && parentTask && (
									<>
										<BreadcrumbSeparator />
										<BreadcrumbItem>
											<BreadcrumbLink
												onClick={() => onNavigateToTask(parentTask.id)}
												className="cursor-pointer hover:text-vscode-foreground"
											>
												{parentTask.title}
											</BreadcrumbLink>
										</BreadcrumbItem>
									</>
								)}
								<BreadcrumbSeparator />
								<BreadcrumbItem>
									<span className="text-vscode-foreground">
										#{displayId} {currentTask.title}
									</span>
								</BreadcrumbItem>
							</BreadcrumbList>
						</Breadcrumb>
						<button
							onClick={handleRefresh}
							disabled={isRefreshing}
							className="p-1.5 rounded hover:bg-vscode-button-hoverBackground transition-colors"
							title="Refresh task details"
						>
							<RefreshCw
								className={`w-4 h-4 text-vscode-foreground/70 ${isRefreshing ? 'animate-spin' : ''}`}
							/>
						</button>
					</div>

					{/* Task ID and title */}
					<h1 className="text-2xl font-bold tracking-tight text-vscode-foreground">
						#{displayId} {currentTask.title}
					</h1>

					{/* Description */}
					<div className="mb-8">
						<p className="text-vscode-foreground/80 leading-relaxed">
							{currentTask.description || 'No description available.'}
						</p>
					</div>

					{/* AI Actions */}
					<AIActionsSection
						currentTask={currentTask}
						isSubtask={isSubtask}
						parentTask={parentTask}
						sendMessage={sendMessage}
						refreshComplexityAfterAI={refreshComplexityAfterAI}
					/>

					{/* Implementation Details */}
					<DetailsSection
						title="Implementation Details"
						content={taskFileData.details}
						error={taskFileDataError}
						emptyMessage="No implementation details available"
						defaultExpanded={false}
					/>

					{/* Test Strategy */}
					<DetailsSection
						title="Test Strategy"
						content={taskFileData.testStrategy}
						error={taskFileDataError}
						emptyMessage="No test strategy available"
						defaultExpanded={false}
					/>

					{/* Subtasks */}
					<SubtasksSection
						currentTask={currentTask}
						isSubtask={isSubtask}
						sendMessage={sendMessage}
						onNavigateToTask={onNavigateToTask}
					/>
				</div>

				{/* Right column - Metadata (1/3 width) */}
				<TaskMetadataSidebar
					currentTask={currentTask}
					tasks={allTasks}
					complexity={complexity}
					isSubtask={isSubtask}
					onStatusChange={handleStatusChange}
					onDependencyClick={handleDependencyClick}
				/>
			</div>
		</div>
	);
};

export default TaskDetailsView;

```

--------------------------------------------------------------------------------
/src/profiles/kilo.js:
--------------------------------------------------------------------------------

```javascript
// Kilo Code conversion profile for rule-transformer
import path from 'path';
import fs from 'fs';
import { isSilentMode, log } from '../../scripts/modules/utils.js';
import { createProfile, COMMON_TOOL_MAPPINGS } from './base-profile.js';
import { ROO_MODES } from '../constants/profiles.js';

// Utility function to apply kilo transformations to content
function applyKiloTransformations(content) {
	const customReplacements = [
		// Replace roo-specific terms with kilo equivalents
		{
			from: /\broo\b/gi,
			to: (match) => (match.charAt(0) === 'R' ? 'Kilo' : 'kilo')
		},
		{ from: /Roo/g, to: 'Kilo' },
		{ from: /ROO/g, to: 'KILO' },
		{ from: /roocode\.com/gi, to: 'kilocode.com' },
		{ from: /docs\.roocode\.com/gi, to: 'docs.kilocode.com' },
		{ from: /https?:\/\/roocode\.com/gi, to: 'https://kilocode.com' },
		{
			from: /https?:\/\/docs\.roocode\.com/gi,
			to: 'https://docs.kilocode.com'
		},
		{ from: /\.roo\//g, to: '.kilo/' },
		{ from: /\.roomodes/g, to: '.kilocodemodes' },
		// Handle file extensions and directory references
		{ from: /roo-rules/g, to: 'kilo-rules' },
		{ from: /rules-roo/g, to: 'rules-kilo' }
	];

	let transformedContent = content;
	for (const replacement of customReplacements) {
		transformedContent = transformedContent.replace(
			replacement.from,
			replacement.to
		);
	}
	return transformedContent;
}

// Utility function to copy files recursively
function copyRecursiveSync(src, dest) {
	const exists = fs.existsSync(src);
	const stats = exists && fs.statSync(src);
	const isDirectory = exists && stats.isDirectory();
	if (isDirectory) {
		if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
		fs.readdirSync(src).forEach((childItemName) => {
			copyRecursiveSync(
				path.join(src, childItemName),
				path.join(dest, childItemName)
			);
		});
	} else {
		fs.copyFileSync(src, dest);
	}
}

// Lifecycle functions for Kilo profile
function onAddRulesProfile(targetDir, assetsDir) {
	// Use the provided assets directory to find the roocode directory
	const sourceDir = path.join(assetsDir, 'roocode');

	if (!fs.existsSync(sourceDir)) {
		log('error', `[Kilo] Source directory does not exist: ${sourceDir}`);
		return;
	}

	// Copy basic roocode structure first
	copyRecursiveSync(sourceDir, targetDir);
	log('debug', `[Kilo] Copied roocode directory to ${targetDir}`);

	// Transform .roomodes to .kilocodemodes
	const roomodesSrc = path.join(sourceDir, '.roomodes');
	const kilocodemodesDest = path.join(targetDir, '.kilocodemodes');
	if (fs.existsSync(roomodesSrc)) {
		try {
			const roomodesContent = fs.readFileSync(roomodesSrc, 'utf8');
			const transformedContent = applyKiloTransformations(roomodesContent);
			fs.writeFileSync(kilocodemodesDest, transformedContent);
			log('debug', `[Kilo] Created .kilocodemodes at ${kilocodemodesDest}`);

			// Remove the original .roomodes file
			fs.unlinkSync(path.join(targetDir, '.roomodes'));
		} catch (err) {
			log('error', `[Kilo] Failed to transform .roomodes: ${err.message}`);
		}
	}

	// Transform .roo directory to .kilo and apply kilo transformations to mode-specific rules
	const rooModesDir = path.join(sourceDir, '.roo');
	const kiloModesDir = path.join(targetDir, '.kilo');

	// Remove the copied .roo directory and create .kilo
	if (fs.existsSync(path.join(targetDir, '.roo'))) {
		fs.rmSync(path.join(targetDir, '.roo'), { recursive: true, force: true });
	}

	for (const mode of ROO_MODES) {
		const src = path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`);
		const dest = path.join(kiloModesDir, `rules-${mode}`, `${mode}-rules`);
		if (fs.existsSync(src)) {
			try {
				const destDir = path.dirname(dest);
				if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });

				// Read, transform, and write the rule file
				const ruleContent = fs.readFileSync(src, 'utf8');
				const transformedContent = applyKiloTransformations(ruleContent);
				fs.writeFileSync(dest, transformedContent);

				log('debug', `[Kilo] Transformed and copied ${mode}-rules to ${dest}`);
			} catch (err) {
				log(
					'error',
					`[Kilo] Failed to transform ${src} to ${dest}: ${err.message}`
				);
			}
		}
	}
}

function onRemoveRulesProfile(targetDir) {
	const kilocodemodespath = path.join(targetDir, '.kilocodemodes');
	if (fs.existsSync(kilocodemodespath)) {
		try {
			fs.rmSync(kilocodemodespath, { force: true });
			log('debug', `[Kilo] Removed .kilocodemodes from ${kilocodemodespath}`);
		} catch (err) {
			log('error', `[Kilo] Failed to remove .kilocodemodes: ${err.message}`);
		}
	}

	const kiloDir = path.join(targetDir, '.kilo');
	if (fs.existsSync(kiloDir)) {
		fs.readdirSync(kiloDir).forEach((entry) => {
			if (entry.startsWith('rules-')) {
				const modeDir = path.join(kiloDir, entry);
				try {
					fs.rmSync(modeDir, { recursive: true, force: true });
					log('debug', `[Kilo] Removed ${entry} directory from ${modeDir}`);
				} catch (err) {
					log('error', `[Kilo] Failed to remove ${modeDir}: ${err.message}`);
				}
			}
		});
		if (fs.readdirSync(kiloDir).length === 0) {
			try {
				fs.rmSync(kiloDir, { recursive: true, force: true });
				log('debug', `[Kilo] Removed empty .kilo directory from ${kiloDir}`);
			} catch (err) {
				log('error', `[Kilo] Failed to remove .kilo directory: ${err.message}`);
			}
		}
	}
}

function onPostConvertRulesProfile(targetDir, assetsDir) {
	onAddRulesProfile(targetDir, assetsDir);
}

// Create and export kilo profile using the base factory with roo rule reuse
export const kiloProfile = createProfile({
	name: 'kilo',
	displayName: 'Kilo Code',
	url: 'kilocode.com',
	docsUrl: 'docs.kilocode.com',
	profileDir: '.kilo',
	rulesDir: '.kilo/rules',
	toolMappings: COMMON_TOOL_MAPPINGS.ROO_STYLE,

	fileMap: {
		// Map roo rule files to kilo equivalents
		'rules/cursor_rules.mdc': 'kilo_rules.md',
		'rules/dev_workflow.mdc': 'dev_workflow.md',
		'rules/self_improve.mdc': 'self_improve.md',
		'rules/taskmaster.mdc': 'taskmaster.md'
	},
	onAdd: onAddRulesProfile,
	onRemove: onRemoveRulesProfile,
	onPostConvert: onPostConvertRulesProfile
});

// Export lifecycle functions separately to avoid naming conflicts
export { onAddRulesProfile, onRemoveRulesProfile, onPostConvertRulesProfile };

```

--------------------------------------------------------------------------------
/apps/docs/getting-started/quick-start/installation.mdx:
--------------------------------------------------------------------------------

```markdown
---
title: Installation
sidebarTitle: "Installation"
---

Now that you have Node.js and your first API Key, you are ready to begin installing Task Master in one of three ways. 

<Note>Cursor Users Can Use the One Click Install Below</Note>
<Accordion title="Quick Install for Cursor 1.0+ (One-Click)">

<a href="cursor://anysphere.cursor-deeplink/mcp/install?name=task-master-ai&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIi0tcGFja2FnZT10YXNrLW1hc3Rlci1haSIsInRhc2stbWFzdGVyLWFpIl0sImVudiI6eyJBTlRIUk9QSUNfQVBJX0tFWSI6IllPVVJfQU5USFJPUElDX0FQSV9LRVlfSEVSRSIsIlBFUlBMRVhJVFlfQVBJX0tFWSI6IllPVVJfUEVSUExFWElUWV9BUElfS0VZX0hFUkUiLCJPUEVOQUlfQVBJX0tFWSI6IllPVVJfT1BFTkFJX0tFWV9IRVJFIiwiR09PR0xFX0FQSV9LRVkiOiJZT1VSX0dPT0dMRV9LRVlfSEVSRSIsIk1JU1RSQUxfQVBJX0tFWSI6IllPVVJfTUlTVFJBTF9LRVlfSEVSRSIsIk9QRU5ST1VURVJfQVBJX0tFWSI6IllPVVJfT1BFTlJPVVRFUl9LRVlfSEVSRSIsIlhBSV9BUElfS0VZIjoiWU9VUl9YQUlfS0VZX0hFUkUiLCJBWlVSRV9PUEVOQUJFX0FQSV9LRVkiOiJZT1VSX0FaVVJFX0tFWV9IRVJFIiwiT0xMQU1BX0FQSV9LRVkiOiJZT1VSX09MTEFNQV9BUElfS0VZX0hFUkUifX0%3D">
  <img 
    className="block dark:hidden" 
    src="https://cursor.com/deeplink/mcp-install-light.png" 
    alt="Add Task Master MCP server to Cursor" 
    noZoom
  />
  <img 
    className="hidden dark:block" 
    src="https://cursor.com/deeplink/mcp-install-dark.png" 
    alt="Add Task Master MCP server to Cursor" 
    noZoom
  />
</a>

Or click the copy button (top-right of code block) then paste into your browser:

```text
cursor://anysphere.cursor-deeplink/mcp/install?name=taskmaster-ai&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIi0tcGFja2FnZT10YXNrLW1hc3Rlci1haSIsInRhc2stbWFzdGVyLWFpIl0sImVudiI6eyJBTlRIUk9QSUNfQVBJX0tFWSI6IllPVVJfQU5USFJPUElDX0FQSV9LRVlfSEVSRSIsIlBFUlBMRVhJVFlfQVBJX0tFWSI6IllPVVJfUEVSUExFWElUWV9BUElfS0VZX0hFUkUiLCJPUEVOQUlfQVBJX0tFWSI6IllPVVJfT1BFTkFJX0tFWV9IRVJFIiwiR09PR0xFX0FQSV9LRVkiOiJZT1VSX0dPT0dMRV9LRVlfSEVSRSIsIk1JU1RSQUxfQVBJX0tFWSI6IllPVVJfTUlTVFJBTF9LRVlfSEVSRSIsIk9QRU5ST1VURVJfQVBJX0tFWSI6IllPVVJfT1BFTlJPVVRFUl9LRVlfSEVSRSIsIlhBSV9BUElfS0VZIjoiWU9VUl9YQUlfS0VZX0hFUkUiLCJBWlVSRV9PUEVOQUlfQVBJX0tFWSI6IllPVVJfQVpVUkVfS0VZX0hFUkUiLCJPTExBTUFfQVBJX0tFWSI6IllPVVJfT0xMQU1BX0FQSV9LRVlfSEVSRSJ9fQo=
```

> **Note:** After clicking the link, you'll still need to add your API keys to the configuration. The link installs the MCP server with placeholder keys that you'll need to replace with your actual API keys.
</Accordion>
## Installation Options


<Accordion title="Option 1: MCP (Recommended)">

MCP (Model Control Protocol) lets you run Task Master directly from your editor.

## 1. Add your MCP config at the following path depending on your editor

| Editor       | Scope   | Linux/macOS Path                      | Windows Path                                      | Key          |
| ------------ | ------- | ------------------------------------- | ------------------------------------------------- | ------------ |
| **Cursor**   | Global  | `~/.cursor/mcp.json`                  | `%USERPROFILE%\.cursor\mcp.json`                  | `mcpServers` |
|              | Project | `<project_folder>/.cursor/mcp.json`   | `<project_folder>\.cursor\mcp.json`               | `mcpServers` |
| **Windsurf** | Global  | `~/.codeium/windsurf/mcp_config.json` | `%USERPROFILE%\.codeium\windsurf\mcp_config.json` | `mcpServers` |
| **VS Code**  | Project | `<project_folder>/.vscode/mcp.json`   | `<project_folder>\.vscode\mcp.json`               | `servers`    |

## Manual Configuration

### Cursor & Windsurf (`mcpServers`)

```json
{
  "mcpServers": {
    "taskmaster-ai": {
      "command": "npx",
      "args": ["-y", "task-master-ai"],
      "env": {
        "ANTHROPIC_API_KEY": "YOUR_ANTHROPIC_API_KEY_HERE",
        "PERPLEXITY_API_KEY": "YOUR_PERPLEXITY_API_KEY_HERE",
        "OPENAI_API_KEY": "YOUR_OPENAI_KEY_HERE",
        "GOOGLE_API_KEY": "YOUR_GOOGLE_KEY_HERE",
        "MISTRAL_API_KEY": "YOUR_MISTRAL_KEY_HERE",
        "OPENROUTER_API_KEY": "YOUR_OPENROUTER_KEY_HERE",
        "XAI_API_KEY": "YOUR_XAI_KEY_HERE",
        "AZURE_OPENAI_API_KEY": "YOUR_AZURE_KEY_HERE",
        "OLLAMA_API_KEY": "YOUR_OLLAMA_API_KEY_HERE"
      }
    }
  }
}
```

> 🔑 Replace `YOUR_…_KEY_HERE` with your real API keys. You can remove keys you don't use.

> **Note**: If you see `0 tools enabled` in the MCP settings, restart your editor and check that your API keys are correctly configured.

### VS Code (`servers` + `type`)

```json
{
  "servers": {
    "taskmaster-ai": {
      "command": "npx",
      "args": ["-y", "task-master-ai"],
      "env": {
        "ANTHROPIC_API_KEY": "YOUR_ANTHROPIC_API_KEY_HERE",
        "PERPLEXITY_API_KEY": "YOUR_PERPLEXITY_API_KEY_HERE",
        "OPENAI_API_KEY": "YOUR_OPENAI_KEY_HERE",
        "GOOGLE_API_KEY": "YOUR_GOOGLE_KEY_HERE",
        "MISTRAL_API_KEY": "YOUR_MISTRAL_KEY_HERE",
        "OPENROUTER_API_KEY": "YOUR_OPENROUTER_KEY_HERE",
        "XAI_API_KEY": "YOUR_XAI_KEY_HERE",
        "AZURE_OPENAI_API_KEY": "YOUR_AZURE_KEY_HERE"
      },
      "type": "stdio"
    }
  }
}
```

> 🔑 Replace `YOUR_…_KEY_HERE` with your real API keys. You can remove keys you don't use.

#### 2. (Cursor-only) Enable Taskmaster MCP

Open Cursor Settings (Ctrl+Shift+J) ➡ Click on MCP tab on the left ➡ Enable task-master-ai with the toggle

#### 3. (Optional) Configure the models you want to use

In your editor's AI chat pane, say:

```txt
Change the main, research and fallback models to <model_name>, <model_name> and <model_name> respectively.
```

For example, to use Claude Code (no API key required):
```txt
Change the main model to claude-code/sonnet
```

#### 4. Initialize Task Master

In your editor's AI chat pane, say:

```txt
Initialize taskmaster-ai in my project
```

</Accordion>

<Accordion title="Option 2: Using Command Line">

## CLI Installation

```bash
# Install globally
npm install -g task-master-ai

# OR install locally within your project
npm install task-master-ai
```

## Initialize a new project

```bash
# If installed globally
task-master init

# If installed locally
npx task-master init

# Initialize project with specific rules
task-master init --rules cursor,windsurf,vscode
```

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

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

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

// Mock fs module before importing anything that uses it
jest.mock('fs', () => ({
	readFileSync: jest.fn(),
	writeFileSync: jest.fn(),
	existsSync: jest.fn(),
	mkdirSync: jest.fn()
}));

// Import modules after mocking
import fs from 'fs';
import { convertRuleToProfileRule } from '../../../src/utils/rule-transformer.js';
import { rooProfile } from '../../../src/profiles/roo.js';

describe('Roo Rule Transformer', () => {
	// Set up spies on the mocked modules
	const mockReadFileSync = jest.spyOn(fs, 'readFileSync');
	const mockWriteFileSync = jest.spyOn(fs, 'writeFileSync');
	const mockExistsSync = jest.spyOn(fs, 'existsSync');
	const mockMkdirSync = jest.spyOn(fs, 'mkdirSync');
	const mockConsoleError = jest
		.spyOn(console, 'error')
		.mockImplementation(() => {});

	beforeEach(() => {
		jest.clearAllMocks();
		// Setup default mocks
		mockReadFileSync.mockReturnValue('');
		mockWriteFileSync.mockImplementation(() => {});
		mockExistsSync.mockReturnValue(true);
		mockMkdirSync.mockImplementation(() => {});
	});

	afterAll(() => {
		jest.restoreAllMocks();
	});

	it('should correctly convert basic terms', () => {
		const testContent = `---
description: Test Cursor rule for basic terms
globs: **/*
alwaysApply: true
---

This is a Cursor rule that references cursor.so and uses the word Cursor multiple times.
Also has references to .mdc files.`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			rooProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Verify file operations were called correctly
		expect(mockReadFileSync).toHaveBeenCalledWith('source.mdc', 'utf8');
		expect(mockWriteFileSync).toHaveBeenCalledTimes(1);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations
		expect(transformedContent).toContain('Roo');
		expect(transformedContent).toContain('roocode.com');
		expect(transformedContent).toContain('.md');
		expect(transformedContent).not.toContain('cursor.so');
		expect(transformedContent).not.toContain('Cursor rule');
	});

	it('should correctly convert tool references', () => {
		const testContent = `---
description: Test Cursor rule for tool references
globs: **/*
alwaysApply: true
---

- Use the search tool to find code
- The edit_file tool lets you modify files
- run_command executes terminal commands
- use_mcp connects to external services`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			rooProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations (Roo uses different tool names)
		expect(transformedContent).toContain('search_files tool');
		expect(transformedContent).toContain('apply_diff tool');
		expect(transformedContent).toContain('execute_command');
		expect(transformedContent).toContain('use_mcp_tool');
	});

	it('should correctly update file references', () => {
		const testContent = `---
description: Test Cursor rule for file references
globs: **/*
alwaysApply: true
---

This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and 
[taskmaster.mdc](mdc:.cursor/rules/taskmaster.mdc).`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			rooProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations - no taskmaster subdirectory for Roo
		expect(transformedContent).toContain('(.roo/rules/dev_workflow.md)'); // File path transformation - no taskmaster subdirectory for Roo
		expect(transformedContent).toContain('(.roo/rules/taskmaster.md)'); // File path transformation - no taskmaster subdirectory for Roo
		expect(transformedContent).not.toContain('(mdc:.cursor/rules/');
	});

	it('should handle file read errors', () => {
		// Mock file read to throw an error
		mockReadFileSync.mockImplementation(() => {
			throw new Error('File not found');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'nonexistent.mdc',
			'target.md',
			rooProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify writeFileSync was not called
		expect(mockWriteFileSync).not.toHaveBeenCalled();

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: File not found'
		);
	});

	it('should handle file write errors', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock file write to throw an error
		mockWriteFileSync.mockImplementation(() => {
			throw new Error('Permission denied');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			rooProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: Permission denied'
		);
	});

	it('should create target directory if it does not exist', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock directory doesn't exist initially
		mockExistsSync.mockReturnValue(false);

		// Call the actual function
		convertRuleToProfileRule(
			'source.mdc',
			'some/deep/path/target.md',
			rooProfile
		);

		// Verify directory creation was called
		expect(mockMkdirSync).toHaveBeenCalledWith('some/deep/path', {
			recursive: true
		});
	});
});

```

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

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

// Mock fs module before importing anything that uses it
jest.mock('fs', () => ({
	readFileSync: jest.fn(),
	writeFileSync: jest.fn(),
	existsSync: jest.fn(),
	mkdirSync: jest.fn()
}));

// Import modules after mocking
import fs from 'fs';
import { convertRuleToProfileRule } from '../../../src/utils/rule-transformer.js';
import { traeProfile } from '../../../src/profiles/trae.js';

describe('Trae Rule Transformer', () => {
	// Set up spies on the mocked modules
	const mockReadFileSync = jest.spyOn(fs, 'readFileSync');
	const mockWriteFileSync = jest.spyOn(fs, 'writeFileSync');
	const mockExistsSync = jest.spyOn(fs, 'existsSync');
	const mockMkdirSync = jest.spyOn(fs, 'mkdirSync');
	const mockConsoleError = jest
		.spyOn(console, 'error')
		.mockImplementation(() => {});

	beforeEach(() => {
		jest.clearAllMocks();
		// Setup default mocks
		mockReadFileSync.mockReturnValue('');
		mockWriteFileSync.mockImplementation(() => {});
		mockExistsSync.mockReturnValue(true);
		mockMkdirSync.mockImplementation(() => {});
	});

	afterAll(() => {
		jest.restoreAllMocks();
	});

	it('should correctly convert basic terms', () => {
		const testContent = `---
description: Test Cursor rule for basic terms
globs: **/*
alwaysApply: true
---

This is a Cursor rule that references cursor.so and uses the word Cursor multiple times.
Also has references to .mdc files.`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			traeProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Verify file operations were called correctly
		expect(mockReadFileSync).toHaveBeenCalledWith('source.mdc', 'utf8');
		expect(mockWriteFileSync).toHaveBeenCalledTimes(1);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations
		expect(transformedContent).toContain('Trae');
		expect(transformedContent).toContain('trae.ai');
		expect(transformedContent).toContain('.md');
		expect(transformedContent).not.toContain('cursor.so');
		expect(transformedContent).not.toContain('Cursor rule');
	});

	it('should correctly convert tool references', () => {
		const testContent = `---
description: Test Cursor rule for tool references
globs: **/*
alwaysApply: true
---

- Use the search tool to find code
- The edit_file tool lets you modify files
- run_command executes terminal commands
- use_mcp connects to external services`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			traeProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations (Trae uses standard tool names, so no transformation)
		expect(transformedContent).toContain('search tool');
		expect(transformedContent).toContain('edit_file tool');
		expect(transformedContent).toContain('run_command');
		expect(transformedContent).toContain('use_mcp');
	});

	it('should correctly update file references', () => {
		const testContent = `---
description: Test Cursor rule for file references
globs: **/*
alwaysApply: true
---

This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and 
[taskmaster.mdc](mdc:.cursor/rules/taskmaster.mdc).`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			traeProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations - no taskmaster subdirectory for Trae
		expect(transformedContent).toContain('(.trae/rules/dev_workflow.md)'); // File path transformation - no taskmaster subdirectory for Trae
		expect(transformedContent).toContain('(.trae/rules/taskmaster.md)'); // File path transformation - no taskmaster subdirectory for Trae
		expect(transformedContent).not.toContain('(mdc:.cursor/rules/');
	});

	it('should handle file read errors', () => {
		// Mock file read to throw an error
		mockReadFileSync.mockImplementation(() => {
			throw new Error('File not found');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'nonexistent.mdc',
			'target.md',
			traeProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify writeFileSync was not called
		expect(mockWriteFileSync).not.toHaveBeenCalled();

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: File not found'
		);
	});

	it('should handle file write errors', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock file write to throw an error
		mockWriteFileSync.mockImplementation(() => {
			throw new Error('Permission denied');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			traeProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: Permission denied'
		);
	});

	it('should create target directory if it does not exist', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock directory doesn't exist initially
		mockExistsSync.mockReturnValue(false);

		// Call the actual function
		convertRuleToProfileRule(
			'source.mdc',
			'some/deep/path/target.md',
			traeProfile
		);

		// Verify directory creation was called
		expect(mockMkdirSync).toHaveBeenCalledWith('some/deep/path', {
			recursive: true
		});
	});
});

```

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

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

// Mock fs module before importing anything that uses it
jest.mock('fs', () => ({
	readFileSync: jest.fn(),
	writeFileSync: jest.fn(),
	existsSync: jest.fn(),
	mkdirSync: jest.fn()
}));

// Import modules after mocking
import fs from 'fs';
import { convertRuleToProfileRule } from '../../../src/utils/rule-transformer.js';
import { kiloProfile } from '../../../src/profiles/kilo.js';

describe('Kilo Rule Transformer', () => {
	// Set up spies on the mocked modules
	const mockReadFileSync = jest.spyOn(fs, 'readFileSync');
	const mockWriteFileSync = jest.spyOn(fs, 'writeFileSync');
	const mockExistsSync = jest.spyOn(fs, 'existsSync');
	const mockMkdirSync = jest.spyOn(fs, 'mkdirSync');
	const mockConsoleError = jest
		.spyOn(console, 'error')
		.mockImplementation(() => {});

	beforeEach(() => {
		jest.clearAllMocks();
		// Setup default mocks
		mockReadFileSync.mockReturnValue('');
		mockWriteFileSync.mockImplementation(() => {});
		mockExistsSync.mockReturnValue(true);
		mockMkdirSync.mockImplementation(() => {});
	});

	afterAll(() => {
		jest.restoreAllMocks();
	});

	it('should correctly convert basic terms', () => {
		const testContent = `---
description: Test Cursor rule for basic terms
globs: **/*
alwaysApply: true
---

This is a Cursor rule that references cursor.so and uses the word Cursor multiple times.
Also has references to .mdc files.`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			kiloProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Verify file operations were called correctly
		expect(mockReadFileSync).toHaveBeenCalledWith('source.mdc', 'utf8');
		expect(mockWriteFileSync).toHaveBeenCalledTimes(1);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations
		expect(transformedContent).toContain('Kilo');
		expect(transformedContent).toContain('kilocode.com');
		expect(transformedContent).toContain('.md');
		expect(transformedContent).not.toContain('cursor.so');
		expect(transformedContent).not.toContain('Cursor rule');
	});

	it('should correctly convert tool references', () => {
		const testContent = `---
description: Test Cursor rule for tool references
globs: **/*
alwaysApply: true
---

- Use the search tool to find code
- The edit_file tool lets you modify files
- run_command executes terminal commands
- use_mcp connects to external services`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			kiloProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations (Kilo uses different tool names)
		expect(transformedContent).toContain('search_files tool');
		expect(transformedContent).toContain('apply_diff tool');
		expect(transformedContent).toContain('execute_command');
		expect(transformedContent).toContain('use_mcp_tool');
	});

	it('should correctly update file references', () => {
		const testContent = `---
description: Test Cursor rule for file references
globs: **/*
alwaysApply: true
---

This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and 
[taskmaster.mdc](mdc:.cursor/rules/taskmaster.mdc).`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			kiloProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations - no taskmaster subdirectory for Kilo
		expect(transformedContent).toContain('(.kilo/rules/dev_workflow.md)'); // File path transformation for dev_workflow - no taskmaster subdirectory for Kilo
		expect(transformedContent).toContain('(.kilo/rules/taskmaster.md)'); // File path transformation for taskmaster - no taskmaster subdirectory for Kilo
		expect(transformedContent).not.toContain('(mdc:.cursor/rules/');
	});

	it('should handle file read errors', () => {
		// Mock file read to throw an error
		mockReadFileSync.mockImplementation(() => {
			throw new Error('File not found');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'nonexistent.mdc',
			'target.md',
			kiloProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify writeFileSync was not called
		expect(mockWriteFileSync).not.toHaveBeenCalled();

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: File not found'
		);
	});

	it('should handle file write errors', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock file write to throw an error
		mockWriteFileSync.mockImplementation(() => {
			throw new Error('Permission denied');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			kiloProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: Permission denied'
		);
	});

	it('should create target directory if it does not exist', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock directory doesn't exist initially
		mockExistsSync.mockReturnValue(false);

		// Call the actual function
		convertRuleToProfileRule(
			'source.mdc',
			'some/deep/path/target.md',
			kiloProfile
		);

		// Verify directory creation was called
		expect(mockMkdirSync).toHaveBeenCalledWith('some/deep/path', {
			recursive: true
		});
	});
});

```

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

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

// Mock fs module before importing anything that uses it
jest.mock('fs', () => ({
	readFileSync: jest.fn(),
	writeFileSync: jest.fn(),
	existsSync: jest.fn(),
	mkdirSync: jest.fn()
}));

// Import modules after mocking
import fs from 'fs';
import { convertRuleToProfileRule } from '../../../src/utils/rule-transformer.js';
import { zedProfile } from '../../../src/profiles/zed.js';

describe('Zed Rule Transformer', () => {
	// Set up spies on the mocked modules
	const mockReadFileSync = jest.spyOn(fs, 'readFileSync');
	const mockWriteFileSync = jest.spyOn(fs, 'writeFileSync');
	const mockExistsSync = jest.spyOn(fs, 'existsSync');
	const mockMkdirSync = jest.spyOn(fs, 'mkdirSync');
	const mockConsoleError = jest
		.spyOn(console, 'error')
		.mockImplementation(() => {});

	beforeEach(() => {
		jest.clearAllMocks();
		// Setup default mocks
		mockReadFileSync.mockReturnValue('');
		mockWriteFileSync.mockImplementation(() => {});
		mockExistsSync.mockReturnValue(true);
		mockMkdirSync.mockImplementation(() => {});
	});

	afterAll(() => {
		jest.restoreAllMocks();
	});

	it('should correctly convert basic terms', () => {
		const testContent = `---
description: Test Cursor rule for basic terms
globs: **/*
alwaysApply: true
---

This is a Cursor rule that references cursor.so and uses the word Cursor multiple times.
Also has references to .mdc files.`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Mock file system operations
		mockExistsSync.mockReturnValue(true);

		// Call the function
		const result = convertRuleToProfileRule(
			'test-source.mdc',
			'test-target.md',
			zedProfile
		);

		// Verify the result
		expect(result).toBe(true);
		expect(mockWriteFileSync).toHaveBeenCalledTimes(1);

		// Get the transformed content
		const transformedContent = mockWriteFileSync.mock.calls[0][1];

		// Verify Cursor -> Zed transformations
		expect(transformedContent).toContain('zed.dev');
		expect(transformedContent).toContain('Zed');
		expect(transformedContent).not.toContain('cursor.so');
		expect(transformedContent).not.toContain('Cursor');
		expect(transformedContent).toContain('.md');
		expect(transformedContent).not.toContain('.mdc');
	});

	it('should handle URL transformations', () => {
		const testContent = `Visit https://cursor.so/docs for more information.
Also check out cursor.so and www.cursor.so for updates.`;

		mockReadFileSync.mockReturnValue(testContent);
		mockExistsSync.mockReturnValue(true);

		const result = convertRuleToProfileRule(
			'test-source.mdc',
			'test-target.md',
			zedProfile
		);

		expect(result).toBe(true);
		const transformedContent = mockWriteFileSync.mock.calls[0][1];

		// Verify URL transformations
		expect(transformedContent).toContain('https://zed.dev');
		expect(transformedContent).toContain('zed.dev');
		expect(transformedContent).not.toContain('cursor.so');
	});

	it('should handle file extension transformations', () => {
		const testContent = `This rule references file.mdc and another.mdc file.
Use the .mdc extension for all rule files.`;

		mockReadFileSync.mockReturnValue(testContent);
		mockExistsSync.mockReturnValue(true);

		const result = convertRuleToProfileRule(
			'test-source.mdc',
			'test-target.md',
			zedProfile
		);

		expect(result).toBe(true);
		const transformedContent = mockWriteFileSync.mock.calls[0][1];

		// Verify file extension transformations
		expect(transformedContent).toContain('file.md');
		expect(transformedContent).toContain('another.md');
		expect(transformedContent).toContain('.md extension');
		expect(transformedContent).not.toContain('.mdc');
	});

	it('should handle case variations', () => {
		const testContent = `CURSOR, Cursor, cursor should all be transformed.`;

		mockReadFileSync.mockReturnValue(testContent);
		mockExistsSync.mockReturnValue(true);

		const result = convertRuleToProfileRule(
			'test-source.mdc',
			'test-target.md',
			zedProfile
		);

		expect(result).toBe(true);
		const transformedContent = mockWriteFileSync.mock.calls[0][1];

		// Verify case transformations
		// Due to regex order, the case-insensitive rule runs first:
		// CURSOR -> Zed (because it starts with 'C'), Cursor -> Zed, cursor -> zed
		expect(transformedContent).toContain('Zed');
		expect(transformedContent).toContain('zed');
		expect(transformedContent).not.toContain('CURSOR');
		expect(transformedContent).not.toContain('Cursor');
		expect(transformedContent).not.toContain('cursor');
	});

	it('should create target directory if it does not exist', () => {
		const testContent = 'Test content';
		mockReadFileSync.mockReturnValue(testContent);
		mockExistsSync.mockReturnValue(false);

		const result = convertRuleToProfileRule(
			'test-source.mdc',
			'nested/path/test-target.md',
			zedProfile
		);

		expect(result).toBe(true);
		expect(mockMkdirSync).toHaveBeenCalledWith('nested/path', {
			recursive: true
		});
	});

	it('should handle file system errors gracefully', () => {
		mockReadFileSync.mockImplementation(() => {
			throw new Error('File not found');
		});

		const result = convertRuleToProfileRule(
			'test-source.mdc',
			'test-target.md',
			zedProfile
		);

		expect(result).toBe(false);
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: File not found'
		);
	});

	it('should handle write errors gracefully', () => {
		mockReadFileSync.mockReturnValue('Test content');
		mockWriteFileSync.mockImplementation(() => {
			throw new Error('Write permission denied');
		});

		const result = convertRuleToProfileRule(
			'test-source.mdc',
			'test-target.md',
			zedProfile
		);

		expect(result).toBe(false);
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: Write permission denied'
		);
	});

	it('should verify profile configuration', () => {
		expect(zedProfile.profileName).toBe('zed');
		expect(zedProfile.displayName).toBe('Zed');
		expect(zedProfile.profileDir).toBe('.zed');
		expect(zedProfile.mcpConfig).toBe(true);
		expect(zedProfile.mcpConfigName).toBe('settings.json');
		expect(zedProfile.mcpConfigPath).toBe('.zed/settings.json');
		expect(zedProfile.includeDefaultRules).toBe(false);
		expect(zedProfile.fileMap).toEqual({
			'AGENTS.md': '.rules'
		});
	});
});

```

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

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

// Mock fs module before importing anything that uses it
jest.mock('fs', () => ({
	readFileSync: jest.fn(),
	writeFileSync: jest.fn(),
	existsSync: jest.fn(),
	mkdirSync: jest.fn()
}));

// Import modules after mocking
import fs from 'fs';
import { convertRuleToProfileRule } from '../../../src/utils/rule-transformer.js';
import { windsurfProfile } from '../../../src/profiles/windsurf.js';

describe('Windsurf Rule Transformer', () => {
	// Set up spies on the mocked modules
	const mockReadFileSync = jest.spyOn(fs, 'readFileSync');
	const mockWriteFileSync = jest.spyOn(fs, 'writeFileSync');
	const mockExistsSync = jest.spyOn(fs, 'existsSync');
	const mockMkdirSync = jest.spyOn(fs, 'mkdirSync');
	const mockConsoleError = jest
		.spyOn(console, 'error')
		.mockImplementation(() => {});

	beforeEach(() => {
		jest.clearAllMocks();
		// Setup default mocks
		mockReadFileSync.mockReturnValue('');
		mockWriteFileSync.mockImplementation(() => {});
		mockExistsSync.mockReturnValue(true);
		mockMkdirSync.mockImplementation(() => {});
	});

	afterAll(() => {
		jest.restoreAllMocks();
	});

	it('should correctly convert basic terms', () => {
		const testContent = `---
description: Test Cursor rule for basic terms
globs: **/*
alwaysApply: true
---

This is a Cursor rule that references cursor.so and uses the word Cursor multiple times.
Also has references to .mdc files.`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			windsurfProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Verify file operations were called correctly
		expect(mockReadFileSync).toHaveBeenCalledWith('source.mdc', 'utf8');
		expect(mockWriteFileSync).toHaveBeenCalledTimes(1);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations
		expect(transformedContent).toContain('Windsurf');
		expect(transformedContent).toContain('windsurf.com');
		expect(transformedContent).toContain('.md');
		expect(transformedContent).not.toContain('cursor.so');
		expect(transformedContent).not.toContain('Cursor rule');
	});

	it('should correctly convert tool references', () => {
		const testContent = `---
description: Test Cursor rule for tool references
globs: **/*
alwaysApply: true
---

- Use the search tool to find code
- The edit_file tool lets you modify files
- run_command executes terminal commands
- use_mcp connects to external services`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			windsurfProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations (Windsurf uses standard tool names, so no transformation)
		expect(transformedContent).toContain('search tool');
		expect(transformedContent).toContain('edit_file tool');
		expect(transformedContent).toContain('run_command');
		expect(transformedContent).toContain('use_mcp');
	});

	it('should correctly update file references', () => {
		const testContent = `---
description: Test Cursor rule for file references
globs: **/*
alwaysApply: true
---

This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and 
[taskmaster.mdc](mdc:.cursor/rules/taskmaster.mdc).`;

		// Mock file read to return our test content
		mockReadFileSync.mockReturnValue(testContent);

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			windsurfProfile
		);

		// Verify the function succeeded
		expect(result).toBe(true);

		// Get the transformed content that was written
		const writeCall = mockWriteFileSync.mock.calls[0];
		const transformedContent = writeCall[1];

		// Verify transformations - no taskmaster subdirectory for Windsurf
		expect(transformedContent).toContain('(.windsurf/rules/dev_workflow.md)'); // File path transformation - no taskmaster subdirectory for Windsurf
		expect(transformedContent).toContain('(.windsurf/rules/taskmaster.md)'); // File path transformation - no taskmaster subdirectory for Windsurf
		expect(transformedContent).not.toContain('(mdc:.cursor/rules/');
	});

	it('should handle file read errors', () => {
		// Mock file read to throw an error
		mockReadFileSync.mockImplementation(() => {
			throw new Error('File not found');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'nonexistent.mdc',
			'target.md',
			windsurfProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify writeFileSync was not called
		expect(mockWriteFileSync).not.toHaveBeenCalled();

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: File not found'
		);
	});

	it('should handle file write errors', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock file write to throw an error
		mockWriteFileSync.mockImplementation(() => {
			throw new Error('Permission denied');
		});

		// Call the actual function
		const result = convertRuleToProfileRule(
			'source.mdc',
			'target.md',
			windsurfProfile
		);

		// Verify the function failed gracefully
		expect(result).toBe(false);

		// Verify error was logged
		expect(mockConsoleError).toHaveBeenCalledWith(
			'Error converting rule file: Permission denied'
		);
	});

	it('should create target directory if it does not exist', () => {
		const testContent = 'test content';
		mockReadFileSync.mockReturnValue(testContent);

		// Mock directory doesn't exist initially
		mockExistsSync.mockReturnValue(false);

		// Call the actual function
		convertRuleToProfileRule(
			'source.mdc',
			'some/deep/path/target.md',
			windsurfProfile
		);

		// Verify directory creation was called
		expect(mockMkdirSync).toHaveBeenCalledWith('some/deep/path', {
			recursive: true
		});
	});
});

```

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

```javascript
/**
 * tools/move-task.js
 * Tool for moving tasks or subtasks to a new position
 */

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

/**
 * Register the moveTask tool with the MCP server
 * @param {Object} server - FastMCP server instance
 */
export function registerMoveTaskTool(server) {
	server.addTool({
		name: 'move_task',
		description: 'Move a task or subtask to a new position',
		parameters: z.object({
			from: z
				.string()
				.describe(
					'ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated to move multiple tasks (e.g., "5,6,7")'
				),
			to: z
				.string()
				.optional()
				.describe(
					'ID of the destination (e.g., "7" or "7.3"). Required for within-tag moves. For cross-tag moves, if omitted, task will be moved to the target tag maintaining its ID'
				),
			file: z.string().optional().describe('Custom path to tasks.json file'),
			projectRoot: z
				.string()
				.describe(
					'Root directory of the project (typically derived from session)'
				),
			tag: z.string().optional().describe('Tag context to operate on'),
			fromTag: z.string().optional().describe('Source tag for cross-tag moves'),
			toTag: z.string().optional().describe('Target tag for cross-tag moves'),
			withDependencies: z
				.boolean()
				.optional()
				.describe('Move dependent tasks along with main task'),
			ignoreDependencies: z
				.boolean()
				.optional()
				.describe('Break cross-tag dependencies during move')
		}),
		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
			try {
				// Check if this is a cross-tag move
				const isCrossTagMove =
					args.fromTag && args.toTag && args.fromTag !== args.toTag;

				if (isCrossTagMove) {
					// Cross-tag move logic
					if (!args.from) {
						return createErrorResponse(
							'Source IDs are required for cross-tag moves',
							'MISSING_SOURCE_IDS'
						);
					}

					// Warn if 'to' parameter is provided for cross-tag moves
					if (args.to) {
						log.warn(
							'The "to" parameter is not used for cross-tag moves and will be ignored. Tasks retain their original IDs in the target tag.'
						);
					}

					// Find tasks.json path if not provided
					let tasksJsonPath = args.file;
					if (!tasksJsonPath) {
						tasksJsonPath = findTasksPath(args, log);
					}

					// Use cross-tag move function
					return handleApiResult(
						await moveTaskCrossTagDirect(
							{
								sourceIds: args.from,
								sourceTag: args.fromTag,
								targetTag: args.toTag,
								withDependencies: args.withDependencies || false,
								ignoreDependencies: args.ignoreDependencies || false,
								tasksJsonPath,
								projectRoot: args.projectRoot
							},
							log,
							{ session }
						),
						log,
						'Error moving tasks between tags',
						undefined,
						args.projectRoot
					);
				} else {
					// Within-tag move logic (existing functionality)
					if (!args.to) {
						return createErrorResponse(
							'Destination ID is required for within-tag moves',
							'MISSING_DESTINATION_ID'
						);
					}

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

					// Find tasks.json path if not provided
					let tasksJsonPath = args.file;
					if (!tasksJsonPath) {
						tasksJsonPath = findTasksPath(args, log);
					}

					// Parse comma-separated IDs
					const fromIds = args.from.split(',').map((id) => id.trim());
					const toIds = args.to.split(',').map((id) => id.trim());

					// Validate matching IDs count
					if (fromIds.length !== toIds.length) {
						if (fromIds.length > 1) {
							const results = [];
							const skipped = [];
							// Move tasks one by one, only generate files on the last move
							for (let i = 0; i < fromIds.length; i++) {
								const fromId = fromIds[i];
								const toId = toIds[i];

								// Skip if source and destination are the same
								if (fromId === toId) {
									log.info(`Skipping ${fromId} -> ${toId} (same ID)`);
									skipped.push({ fromId, toId, reason: 'same ID' });
									continue;
								}

								const shouldGenerateFiles = i === fromIds.length - 1;
								const result = await moveTaskDirect(
									{
										sourceId: fromId,
										destinationId: toId,
										tasksJsonPath,
										projectRoot: args.projectRoot,
										tag: resolvedTag,
										generateFiles: shouldGenerateFiles
									},
									log,
									{ session }
								);

								if (!result.success) {
									log.error(
										`Failed to move ${fromId} to ${toId}: ${result.error.message}`
									);
								} else {
									results.push(result.data);
								}
							}

							return handleApiResult(
								{
									success: true,
									data: {
										moves: results,
										skipped: skipped.length > 0 ? skipped : undefined,
										message: `Successfully moved ${results.length} tasks${skipped.length > 0 ? `, skipped ${skipped.length}` : ''}`
									}
								},
								log,
								'Error moving multiple tasks',
								undefined,
								args.projectRoot
							);
						}
						return handleApiResult(
							{
								success: true,
								data: {
									moves: results,
									skippedMoves: skippedMoves,
									message: `Successfully moved ${results.length} tasks${skippedMoves.length > 0 ? `, skipped ${skippedMoves.length} moves` : ''}`
								}
							},
							log,
							'Error moving multiple tasks',
							undefined,
							args.projectRoot
						);
					} else {
						// Moving a single task
						return handleApiResult(
							await moveTaskDirect(
								{
									sourceId: args.from,
									destinationId: args.to,
									tasksJsonPath,
									projectRoot: args.projectRoot,
									tag: resolvedTag,
									generateFiles: true
								},
								log,
								{ session }
							),
							log,
							'Error moving task',
							undefined,
							args.projectRoot
						);
					}
				}
			} catch (error) {
				return createErrorResponse(
					`Failed to move task: ${error.message}`,
					'MOVE_TASK_ERROR'
				);
			}
		})
	});
}

```

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

```typescript
/**
 * TaskMaster Extension - Simplified Architecture
 * Only using patterns where they add real value
 */

import * as vscode from 'vscode';
import { ConfigService } from './services/config-service';
import { PollingService } from './services/polling-service';
import { createPollingStrategy } from './services/polling-strategies';
import { TaskRepository } from './services/task-repository';
import { TerminalManager } from './services/terminal-manager';
import { WebviewManager } from './services/webview-manager';
import { EventEmitter } from './utils/event-emitter';
import { ExtensionLogger } from './utils/logger';
import {
	MCPClientManager,
	createMCPConfigFromSettings
} from './utils/mcpClient';
import { TaskMasterApi } from './utils/task-master-api';
import { SidebarWebviewManager } from './services/sidebar-webview-manager';

let logger: ExtensionLogger;
let mcpClient: MCPClientManager;
let api: TaskMasterApi;
let repository: TaskRepository;
let terminalManager: TerminalManager;
let pollingService: PollingService;
let webviewManager: WebviewManager;
let events: EventEmitter;
let configService: ConfigService;
let sidebarManager: SidebarWebviewManager;

export async function activate(context: vscode.ExtensionContext) {
	try {
		// Initialize logger (needed to prevent MCP stdio issues)
		logger = ExtensionLogger.getInstance();
		logger.log('🎉 TaskMaster Extension activating...');

		// Simple event emitter for webview communication
		events = new EventEmitter();

		// Initialize MCP client
		mcpClient = new MCPClientManager(createMCPConfigFromSettings());

		// Initialize API
		api = new TaskMasterApi(mcpClient);

		// Repository with caching (actually useful for performance)
		repository = new TaskRepository(api, logger);

		// Terminal manager for task execution
		terminalManager = new TerminalManager(context, logger);

		// Config service for TaskMaster config.json
		configService = new ConfigService(logger);

		// Polling service with strategy pattern (makes sense for different polling behaviors)
		const strategy = createPollingStrategy(
			vscode.workspace.getConfiguration('taskmaster')
		);
		pollingService = new PollingService(repository, strategy, logger);

		// Webview manager (cleaner than global panel array) - create before connection
		webviewManager = new WebviewManager(
			context,
			repository,
			events,
			logger,
			terminalManager
		);
		webviewManager.setConfigService(configService);

		// Sidebar webview manager
		sidebarManager = new SidebarWebviewManager(context.extensionUri);

		// Initialize connection
		await initializeConnection();

		// Set MCP client and API after connection
		webviewManager.setMCPClient(mcpClient);
		webviewManager.setApi(api);
		sidebarManager.setApi(api);

		// Register commands
		registerCommands(context);

		// Handle polling lifecycle
		events.on('webview:opened', () => {
			if (webviewManager.getPanelCount() === 1) {
				pollingService.start();
			}
		});

		events.on('webview:closed', () => {
			if (webviewManager.getPanelCount() === 0) {
				pollingService.stop();
			}
		});

		// Forward repository updates to webviews
		repository.on('tasks:updated', (tasks) => {
			webviewManager.broadcast('tasksUpdated', { tasks, source: 'polling' });
		});

		logger.log('✅ TaskMaster Extension activated');
	} catch (error) {
		logger?.error('Failed to activate', error);
		vscode.window.showErrorMessage(
			`Failed to activate TaskMaster: ${error instanceof Error ? error.message : 'Unknown error'}`
		);
	}
}

async function initializeConnection() {
	try {
		logger.log('🔗 Connecting to TaskMaster...');

		// Notify webviews that we're connecting
		if (webviewManager) {
			webviewManager.broadcast('connectionStatus', {
				isConnected: false,
				status: 'Connecting...'
			});
		}

		await mcpClient.connect();

		const testResult = await api.testConnection();

		if (testResult.success) {
			logger.log('✅ Connected to TaskMaster');
			vscode.window.showInformationMessage('TaskMaster connected!');

			// Notify webviews that we're connected
			if (webviewManager) {
				webviewManager.broadcast('connectionStatus', {
					isConnected: true,
					status: 'Connected'
				});
			}
			if (sidebarManager) {
				sidebarManager.updateConnectionStatus();
			}
		} else {
			throw new Error(testResult.error || 'Connection test failed');
		}
	} catch (error) {
		logger.error('Connection failed', error);

		// Notify webviews that connection failed
		if (webviewManager) {
			webviewManager.broadcast('connectionStatus', {
				isConnected: false,
				status: 'Disconnected'
			});
		}
		if (sidebarManager) {
			sidebarManager.updateConnectionStatus();
		}

		handleConnectionError(error);
	}
}

function handleConnectionError(error: any) {
	const message = error instanceof Error ? error.message : 'Unknown error';

	if (message.includes('ENOENT') && message.includes('npx')) {
		vscode.window
			.showWarningMessage(
				'TaskMaster: npx not found. Please ensure Node.js is installed.',
				'Open Settings'
			)
			.then((action) => {
				if (action === 'Open Settings') {
					vscode.commands.executeCommand(
						'workbench.action.openSettings',
						'@ext:Hamster.task-master-hamster taskmaster'
					);
				}
			});
	} else {
		vscode.window.showWarningMessage(
			`TaskMaster connection failed: ${message}`
		);
	}
}

function registerCommands(context: vscode.ExtensionContext) {
	// Main command
	context.subscriptions.push(
		vscode.commands.registerCommand('tm.showKanbanBoard', async () => {
			await webviewManager.createOrShowPanel();
		})
	);

	// Utility commands
	context.subscriptions.push(
		vscode.commands.registerCommand('tm.refreshTasks', async () => {
			await repository.refresh();
			vscode.window.showInformationMessage('Tasks refreshed!');
		})
	);

	context.subscriptions.push(
		vscode.commands.registerCommand('tm.openSettings', () => {
			vscode.commands.executeCommand(
				'workbench.action.openSettings',
				'@ext:Hamster.task-master-hamster taskmaster'
			);
		})
	);

	// Register sidebar view provider

	context.subscriptions.push(
		vscode.window.registerWebviewViewProvider(
			'taskmaster.welcome',
			sidebarManager
		)
	);
}

export async function deactivate() {
	logger?.log('👋 TaskMaster Extension deactivating...');
	pollingService?.stop();
	webviewManager?.dispose();
	await terminalManager?.dispose();
	api?.destroy();
	mcpClient?.disconnect();
}

```

--------------------------------------------------------------------------------
/apps/extension/package.publish.json:
--------------------------------------------------------------------------------

```json
{
	"name": "task-master-hamster",
	"displayName": "Taskmaster AI",
	"description": "A visual Kanban board interface for Taskmaster projects in VS Code",
	"version": "0.23.1",
	"publisher": "Hamster",
	"icon": "assets/icon.png",
	"engines": {
		"vscode": "^1.93.0"
	},
	"categories": ["AI", "Visualization", "Education", "Other"],
	"keywords": [
		"kanban",
		"kanban board",
		"productivity",
		"todo",
		"task tracking",
		"project management",
		"task-master",
		"task management",
		"agile",
		"scrum",
		"ai",
		"mcp",
		"model context protocol",
		"dashboard",
		"chatgpt",
		"claude",
		"openai",
		"anthropic",
		"task",
		"npm",
		"intellicode",
		"react",
		"typescript",
		"php",
		"python",
		"node",
		"planner",
		"organizer",
		"workflow",
		"boards",
		"cards"
	],
	"repository": "https://github.com/eyaltoledano/claude-task-master",
	"activationEvents": ["onStartupFinished", "workspaceContains:.taskmaster/**"],
	"main": "./dist/extension.js",
	"contributes": {
		"viewsContainers": {
			"activitybar": [
				{
					"id": "taskmaster",
					"title": "Taskmaster",
					"icon": "assets/sidebar-icon.svg"
				}
			]
		},
		"views": {
			"taskmaster": [
				{
					"id": "taskmaster.welcome",
					"name": "Taskmaster",
					"type": "webview"
				}
			]
		},
		"commands": [
			{
				"command": "tm.showKanbanBoard",
				"title": "Taskmaster: Show Board"
			},
			{
				"command": "tm.checkConnection",
				"title": "Taskmaster: Check Connection"
			},
			{
				"command": "tm.reconnect",
				"title": "Taskmaster: Reconnect"
			},
			{
				"command": "tm.openSettings",
				"title": "Taskmaster: Open Settings"
			}
		],
		"configuration": {
			"title": "Taskmaster Kanban",
			"properties": {
				"taskmaster.mcp.command": {
					"type": "string",
					"default": "npx",
					"description": "The command or absolute path to execute for the MCP server (e.g., 'npx' or '/usr/local/bin/task-master-ai')."
				},
				"taskmaster.mcp.args": {
					"type": "array",
					"items": {
						"type": "string"
					},
					"default": ["-y", "task-master-ai"],
					"description": "An array of arguments to pass to the MCP server command."
				},
				"taskmaster.mcp.cwd": {
					"type": "string",
					"description": "Working directory for the Task Master MCP server (defaults to workspace root)"
				},
				"taskmaster.mcp.env": {
					"type": "object",
					"description": "Environment variables for the Task Master MCP server"
				},
				"taskmaster.mcp.timeout": {
					"type": "number",
					"default": 30000,
					"minimum": 1000,
					"maximum": 300000,
					"description": "Connection timeout in milliseconds"
				},
				"taskmaster.mcp.maxReconnectAttempts": {
					"type": "number",
					"default": 5,
					"minimum": 1,
					"maximum": 20,
					"description": "Maximum number of reconnection attempts"
				},
				"taskmaster.mcp.reconnectBackoffMs": {
					"type": "number",
					"default": 1000,
					"minimum": 100,
					"maximum": 10000,
					"description": "Initial reconnection backoff delay in milliseconds"
				},
				"taskmaster.mcp.maxBackoffMs": {
					"type": "number",
					"default": 30000,
					"minimum": 1000,
					"maximum": 300000,
					"description": "Maximum reconnection backoff delay in milliseconds"
				},
				"taskmaster.mcp.healthCheckIntervalMs": {
					"type": "number",
					"default": 15000,
					"minimum": 5000,
					"maximum": 60000,
					"description": "Health check interval in milliseconds"
				},
				"taskmaster.mcp.requestTimeoutMs": {
					"type": "number",
					"default": 300000,
					"minimum": 30000,
					"maximum": 600000,
					"description": "MCP request timeout in milliseconds (default: 5 minutes)"
				},
				"taskmaster.ui.autoRefresh": {
					"type": "boolean",
					"default": true,
					"description": "Automatically refresh tasks from the server"
				},
				"taskmaster.ui.refreshIntervalMs": {
					"type": "number",
					"default": 10000,
					"minimum": 1000,
					"maximum": 300000,
					"description": "Auto-refresh interval in milliseconds"
				},
				"taskmaster.ui.theme": {
					"type": "string",
					"enum": ["auto", "light", "dark"],
					"default": "auto",
					"description": "UI theme preference"
				},
				"taskmaster.ui.showCompletedTasks": {
					"type": "boolean",
					"default": true,
					"description": "Show completed tasks in the Kanban board"
				},
				"taskmaster.ui.taskDisplayLimit": {
					"type": "number",
					"default": 100,
					"minimum": 1,
					"maximum": 1000,
					"description": "Maximum number of tasks to display"
				},
				"taskmaster.ui.showPriority": {
					"type": "boolean",
					"default": true,
					"description": "Show task priority indicators"
				},
				"taskmaster.ui.showTaskIds": {
					"type": "boolean",
					"default": true,
					"description": "Show task IDs in the interface"
				},
				"taskmaster.performance.maxConcurrentRequests": {
					"type": "number",
					"default": 5,
					"minimum": 1,
					"maximum": 20,
					"description": "Maximum number of concurrent MCP requests"
				},
				"taskmaster.performance.requestTimeoutMs": {
					"type": "number",
					"default": 30000,
					"minimum": 1000,
					"maximum": 300000,
					"description": "Request timeout in milliseconds"
				},
				"taskmaster.performance.cacheTasksMs": {
					"type": "number",
					"default": 5000,
					"minimum": 0,
					"maximum": 60000,
					"description": "Task cache duration in milliseconds"
				},
				"taskmaster.performance.lazyLoadThreshold": {
					"type": "number",
					"default": 50,
					"minimum": 10,
					"maximum": 500,
					"description": "Number of tasks before enabling lazy loading"
				},
				"taskmaster.debug.enableLogging": {
					"type": "boolean",
					"default": true,
					"description": "Enable debug logging"
				},
				"taskmaster.debug.logLevel": {
					"type": "string",
					"enum": ["error", "warn", "info", "debug"],
					"default": "info",
					"description": "Logging level"
				},
				"taskmaster.debug.enableConnectionMetrics": {
					"type": "boolean",
					"default": true,
					"description": "Enable connection performance metrics"
				},
				"taskmaster.debug.saveEventLogs": {
					"type": "boolean",
					"default": false,
					"description": "Save event logs to files"
				},
				"taskmaster.debug.maxEventLogSize": {
					"type": "number",
					"default": 1000,
					"minimum": 10,
					"maximum": 10000,
					"description": "Maximum number of events to keep in memory"
				}
			}
		}
	}
}

```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/rules.js:
--------------------------------------------------------------------------------

```javascript
/**
 * rules.js
 * Direct function implementation for adding or removing rules
 */

import {
	enableSilentMode,
	disableSilentMode
} from '../../../../scripts/modules/utils.js';
import {
	convertAllRulesToProfileRules,
	removeProfileRules,
	getRulesProfile,
	isValidProfile
} from '../../../../src/utils/rule-transformer.js';
import { RULE_PROFILES } from '../../../../src/constants/profiles.js';
import { RULES_ACTIONS } from '../../../../src/constants/rules-actions.js';
import {
	wouldRemovalLeaveNoProfiles,
	getInstalledProfiles
} from '../../../../src/utils/profiles.js';
import path from 'path';
import fs from 'fs';

/**
 * Direct function wrapper for adding or removing rules.
 * @param {Object} args - Command arguments
 * @param {"add"|"remove"} args.action - Action to perform: add or remove rules
 * @param {string[]} args.profiles - List of profiles to add or remove
 * @param {string} args.projectRoot - Absolute path to the project root
 * @param {boolean} [args.yes=true] - Run non-interactively
 * @param {Object} log - Logger object
 * @param {Object} context - Additional context (session)
 * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 */
export async function rulesDirect(args, log, context = {}) {
	enableSilentMode();
	try {
		const { action, profiles, projectRoot, yes, force } = args;
		if (
			!action ||
			!Array.isArray(profiles) ||
			profiles.length === 0 ||
			!projectRoot
		) {
			return {
				success: false,
				error: {
					code: 'MISSING_ARGUMENT',
					message: 'action, profiles, and projectRoot are required.'
				}
			};
		}

		const removalResults = [];
		const addResults = [];

		if (action === RULES_ACTIONS.REMOVE) {
			// Safety check: Ensure this won't remove all rule profiles (unless forced)
			if (!force && wouldRemovalLeaveNoProfiles(projectRoot, profiles)) {
				const installedProfiles = getInstalledProfiles(projectRoot);
				const remainingProfiles = installedProfiles.filter(
					(profile) => !profiles.includes(profile)
				);
				return {
					success: false,
					error: {
						code: 'CRITICAL_REMOVAL_BLOCKED',
						message: `CRITICAL: This operation would remove ALL remaining rule profiles (${profiles.join(', ')}), leaving your project with no rules configurations. This could significantly impact functionality. Currently installed profiles: ${installedProfiles.join(', ')}. If you're certain you want to proceed, set force: true or use the CLI with --force flag.`
					}
				};
			}

			for (const profile of profiles) {
				if (!isValidProfile(profile)) {
					removalResults.push({
						profileName: profile,
						success: false,
						error: `The requested rule profile for '${profile}' is unavailable. Supported profiles are: ${RULE_PROFILES.join(', ')}.`
					});
					continue;
				}
				const profileConfig = getRulesProfile(profile);
				const result = removeProfileRules(projectRoot, profileConfig);
				removalResults.push(result);
			}
			const successes = removalResults
				.filter((r) => r.success)
				.map((r) => r.profileName);
			const skipped = removalResults
				.filter((r) => r.skipped)
				.map((r) => r.profileName);
			const errors = removalResults.filter(
				(r) => r.error && !r.success && !r.skipped
			);
			const withNotices = removalResults.filter((r) => r.notice);

			let summary = '';
			if (successes.length > 0) {
				summary += `Successfully removed Task Master rules: ${successes.join(', ')}.`;
			}
			if (skipped.length > 0) {
				summary += `Skipped (default or protected): ${skipped.join(', ')}.`;
			}
			if (errors.length > 0) {
				summary += errors
					.map((r) => `Error removing ${r.profileName}: ${r.error}`)
					.join(' ');
			}
			if (withNotices.length > 0) {
				summary += ` Notices: ${withNotices.map((r) => `${r.profileName} - ${r.notice}`).join('; ')}.`;
			}
			disableSilentMode();
			return {
				success: errors.length === 0,
				data: { summary, results: removalResults }
			};
		} else if (action === RULES_ACTIONS.ADD) {
			for (const profile of profiles) {
				if (!isValidProfile(profile)) {
					addResults.push({
						profileName: profile,
						success: false,
						error: `Profile not found: static import missing for '${profile}'. Valid profiles: ${RULE_PROFILES.join(', ')}`
					});
					continue;
				}
				const profileConfig = getRulesProfile(profile);
				const { success, failed } = convertAllRulesToProfileRules(
					projectRoot,
					profileConfig
				);

				// Determine paths
				const rulesDir = profileConfig.rulesDir;
				const profileRulesDir = path.join(projectRoot, rulesDir);
				const profileDir = profileConfig.profileDir;
				const mcpConfig = profileConfig.mcpConfig !== false;
				const mcpPath =
					mcpConfig && profileConfig.mcpConfigPath
						? path.join(projectRoot, profileConfig.mcpConfigPath)
						: null;

				// Check what was created
				const mcpConfigCreated =
					mcpConfig && mcpPath ? fs.existsSync(mcpPath) : undefined;
				const rulesDirCreated = fs.existsSync(profileRulesDir);
				const profileFolderCreated = fs.existsSync(
					path.join(projectRoot, profileDir)
				);

				const error =
					failed > 0 ? `${failed} rule files failed to convert.` : null;
				const resultObj = {
					profileName: profile,
					mcpConfigCreated,
					rulesDirCreated,
					profileFolderCreated,
					skipped: false,
					error,
					success:
						(mcpConfig ? mcpConfigCreated : true) &&
						rulesDirCreated &&
						success > 0 &&
						!error
				};
				addResults.push(resultObj);
			}

			const successes = addResults
				.filter((r) => r.success)
				.map((r) => r.profileName);
			const errors = addResults.filter((r) => r.error && !r.success);

			let summary = '';
			if (successes.length > 0) {
				summary += `Successfully added rules: ${successes.join(', ')}.`;
			}
			if (errors.length > 0) {
				summary += errors
					.map((r) => ` Error adding ${r.profileName}: ${r.error}`)
					.join(' ');
			}
			disableSilentMode();
			return {
				success: errors.length === 0,
				data: { summary, results: addResults }
			};
		} else {
			disableSilentMode();
			return {
				success: false,
				error: {
					code: 'INVALID_ACTION',
					message: `Unknown action. Use "${RULES_ACTIONS.ADD}" or "${RULES_ACTIONS.REMOVE}".`
				}
			};
		}
	} catch (error) {
		disableSilentMode();
		log.error(`[rulesDirect] Error: ${error.message}`);
		return {
			success: false,
			error: {
				code: error.code || 'RULES_ERROR',
				message: error.message
			}
		};
	}
}

```
Page 11/38FirstPrevNextLast