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

# Directory Structure

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

# Files

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

```javascript
  1 | /**
  2 |  * @fileoverview Error handling utilities for Claude Code provider
  3 |  */
  4 | 
  5 | import { APICallError, LoadAPIKeyError } from '@ai-sdk/provider';
  6 | 
  7 | /**
  8 |  * @typedef {import('./types.js').ClaudeCodeErrorMetadata} ClaudeCodeErrorMetadata
  9 |  */
 10 | 
 11 | /**
 12 |  * Create an API call error with Claude Code specific metadata
 13 |  * @param {Object} params - Error parameters
 14 |  * @param {string} params.message - Error message
 15 |  * @param {string} [params.code] - Error code
 16 |  * @param {number} [params.exitCode] - Process exit code
 17 |  * @param {string} [params.stderr] - Standard error output
 18 |  * @param {string} [params.promptExcerpt] - Excerpt of the prompt
 19 |  * @param {boolean} [params.isRetryable=false] - Whether the error is retryable
 20 |  * @returns {APICallError}
 21 |  */
 22 | export function createAPICallError({
 23 | 	message,
 24 | 	code,
 25 | 	exitCode,
 26 | 	stderr,
 27 | 	promptExcerpt,
 28 | 	isRetryable = false
 29 | }) {
 30 | 	/** @type {ClaudeCodeErrorMetadata} */
 31 | 	const metadata = {
 32 | 		code,
 33 | 		exitCode,
 34 | 		stderr,
 35 | 		promptExcerpt
 36 | 	};
 37 | 
 38 | 	return new APICallError({
 39 | 		message,
 40 | 		isRetryable,
 41 | 		url: 'claude-code-cli://command',
 42 | 		requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : undefined,
 43 | 		data: metadata
 44 | 	});
 45 | }
 46 | 
 47 | /**
 48 |  * Create an authentication error
 49 |  * @param {Object} params - Error parameters
 50 |  * @param {string} params.message - Error message
 51 |  * @returns {LoadAPIKeyError}
 52 |  */
 53 | export function createAuthenticationError({ message }) {
 54 | 	return new LoadAPIKeyError({
 55 | 		message:
 56 | 			message ||
 57 | 			'Authentication failed. Please ensure Claude Code CLI is properly authenticated.'
 58 | 	});
 59 | }
 60 | 
 61 | /**
 62 |  * Create a timeout error
 63 |  * @param {Object} params - Error parameters
 64 |  * @param {string} params.message - Error message
 65 |  * @param {string} [params.promptExcerpt] - Excerpt of the prompt
 66 |  * @param {number} params.timeoutMs - Timeout in milliseconds
 67 |  * @returns {APICallError}
 68 |  */
 69 | export function createTimeoutError({ message, promptExcerpt, timeoutMs }) {
 70 | 	// Store timeoutMs in metadata for potential use by error handlers
 71 | 	/** @type {ClaudeCodeErrorMetadata & { timeoutMs: number }} */
 72 | 	const metadata = {
 73 | 		code: 'TIMEOUT',
 74 | 		promptExcerpt,
 75 | 		timeoutMs
 76 | 	};
 77 | 
 78 | 	return new APICallError({
 79 | 		message,
 80 | 		isRetryable: true,
 81 | 		url: 'claude-code-cli://command',
 82 | 		requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : undefined,
 83 | 		data: metadata
 84 | 	});
 85 | }
 86 | 
 87 | /**
 88 |  * Check if an error is an authentication error
 89 |  * @param {unknown} error - Error to check
 90 |  * @returns {boolean}
 91 |  */
 92 | export function isAuthenticationError(error) {
 93 | 	if (error instanceof LoadAPIKeyError) return true;
 94 | 	if (
 95 | 		error instanceof APICallError &&
 96 | 		/** @type {ClaudeCodeErrorMetadata} */ (error.data)?.exitCode === 401
 97 | 	)
 98 | 		return true;
 99 | 	return false;
100 | }
101 | 
102 | /**
103 |  * Check if an error is a timeout error
104 |  * @param {unknown} error - Error to check
105 |  * @returns {boolean}
106 |  */
107 | export function isTimeoutError(error) {
108 | 	if (
109 | 		error instanceof APICallError &&
110 | 		/** @type {ClaudeCodeErrorMetadata} */ (error.data)?.code === 'TIMEOUT'
111 | 	)
112 | 		return true;
113 | 	return false;
114 | }
115 | 
116 | /**
117 |  * Get error metadata from an error
118 |  * @param {unknown} error - Error to extract metadata from
119 |  * @returns {ClaudeCodeErrorMetadata|undefined}
120 |  */
121 | export function getErrorMetadata(error) {
122 | 	if (error instanceof APICallError && error.data) {
123 | 		return /** @type {ClaudeCodeErrorMetadata} */ (error.data);
124 | 	}
125 | 	return undefined;
126 | }
127 | 
```

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

```javascript
  1 | /**
  2 |  * list-tasks.js
  3 |  * Direct function implementation for listing tasks
  4 |  */
  5 | 
  6 | import { listTasks } from '../../../../scripts/modules/task-manager.js';
  7 | import {
  8 | 	enableSilentMode,
  9 | 	disableSilentMode
 10 | } from '../../../../scripts/modules/utils.js';
 11 | 
 12 | /**
 13 |  * Direct function wrapper for listTasks with error handling and caching.
 14 |  *
 15 |  * @param {Object} args - Command arguments (now expecting tasksJsonPath explicitly).
 16 |  * @param {string} args.tasksJsonPath - Path to the tasks.json file.
 17 |  * @param {string} args.reportPath - Path to the report file.
 18 |  * @param {string} args.status - Status of the task.
 19 |  * @param {boolean} args.withSubtasks - Whether to include subtasks.
 20 |  * @param {string} args.projectRoot - Project root path (for MCP/env fallback)
 21 |  * @param {string} args.tag - Tag for the task (optional)
 22 |  * @param {Object} log - Logger object.
 23 |  * @returns {Promise<Object>} - Task list result { success: boolean, data?: any, error?: { code: string, message: string } }.
 24 |  */
 25 | export async function listTasksDirect(args, log, context = {}) {
 26 | 	// Destructure the explicit tasksJsonPath from args
 27 | 	const { tasksJsonPath, reportPath, status, withSubtasks, projectRoot, tag } =
 28 | 		args;
 29 | 	const { session } = context;
 30 | 
 31 | 	if (!tasksJsonPath) {
 32 | 		log.error('listTasksDirect called without tasksJsonPath');
 33 | 		return {
 34 | 			success: false,
 35 | 			error: {
 36 | 				code: 'MISSING_ARGUMENT',
 37 | 				message: 'tasksJsonPath is required'
 38 | 			}
 39 | 		};
 40 | 	}
 41 | 
 42 | 	// Use the explicit tasksJsonPath for cache key
 43 | 	const statusFilter = status || 'all';
 44 | 	const withSubtasksFilter = withSubtasks || false;
 45 | 
 46 | 	// Define the action function to be executed on cache miss
 47 | 	const coreListTasksAction = async () => {
 48 | 		try {
 49 | 			// Enable silent mode to prevent console logs from interfering with JSON response
 50 | 			enableSilentMode();
 51 | 
 52 | 			log.info(
 53 | 				`Executing core listTasks function for path: ${tasksJsonPath}, filter: ${statusFilter}, subtasks: ${withSubtasksFilter}`
 54 | 			);
 55 | 			// Pass the explicit tasksJsonPath to the core function
 56 | 			const resultData = listTasks(
 57 | 				tasksJsonPath,
 58 | 				statusFilter,
 59 | 				reportPath,
 60 | 				withSubtasksFilter,
 61 | 				'json',
 62 | 				{ projectRoot, session, tag }
 63 | 			);
 64 | 
 65 | 			if (!resultData || !resultData.tasks) {
 66 | 				log.error('Invalid or empty response from listTasks core function');
 67 | 				return {
 68 | 					success: false,
 69 | 					error: {
 70 | 						code: 'INVALID_CORE_RESPONSE',
 71 | 						message: 'Invalid or empty response from listTasks core function'
 72 | 					}
 73 | 				};
 74 | 			}
 75 | 
 76 | 			log.info(
 77 | 				`Core listTasks function retrieved ${resultData.tasks.length} tasks`
 78 | 			);
 79 | 
 80 | 			// Restore normal logging
 81 | 			disableSilentMode();
 82 | 
 83 | 			return { success: true, data: resultData };
 84 | 		} catch (error) {
 85 | 			// Make sure to restore normal logging even if there's an error
 86 | 			disableSilentMode();
 87 | 
 88 | 			log.error(`Core listTasks function failed: ${error.message}`);
 89 | 			return {
 90 | 				success: false,
 91 | 				error: {
 92 | 					code: 'LIST_TASKS_CORE_ERROR',
 93 | 					message: error.message || 'Failed to list tasks'
 94 | 				}
 95 | 			};
 96 | 		}
 97 | 	};
 98 | 
 99 | 	try {
100 | 		const result = await coreListTasksAction();
101 | 		log.info('listTasksDirect completed');
102 | 		return result;
103 | 	} catch (error) {
104 | 		log.error(`Unexpected error during listTasks: ${error.message}`);
105 | 		console.error(error.stack);
106 | 		return {
107 | 			success: false,
108 | 			error: {
109 | 				code: 'UNEXPECTED_ERROR',
110 | 				message: error.message
111 | 			}
112 | 		};
113 | 	}
114 | }
115 | 
```

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

```
  1 | #!/usr/bin/env node
  2 | import { readFileSync, existsSync } from 'node:fs';
  3 | import { join, dirname, resolve } from 'node:path';
  4 | import { fileURLToPath } from 'node:url';
  5 | 
  6 | const __filename = fileURLToPath(import.meta.url);
  7 | const __dirname = dirname(__filename);
  8 | 
  9 | // Get context from command line argument or environment
 10 | const context = process.argv[2] || process.env.GITHUB_WORKFLOW || 'manual';
 11 | 
 12 | function findRootDir(startDir) {
 13 | 	let currentDir = resolve(startDir);
 14 | 	while (currentDir !== '/') {
 15 | 		if (existsSync(join(currentDir, 'package.json'))) {
 16 | 			try {
 17 | 				const pkg = JSON.parse(
 18 | 					readFileSync(join(currentDir, 'package.json'), 'utf8')
 19 | 				);
 20 | 				if (pkg.name === 'task-master-ai' || pkg.repository) {
 21 | 					return currentDir;
 22 | 				}
 23 | 			} catch {}
 24 | 		}
 25 | 		currentDir = dirname(currentDir);
 26 | 	}
 27 | 	throw new Error('Could not find root directory');
 28 | }
 29 | 
 30 | function checkPreReleaseMode() {
 31 | 	console.log('🔍 Checking if branch is in pre-release mode...');
 32 | 
 33 | 	const rootDir = findRootDir(__dirname);
 34 | 	const preJsonPath = join(rootDir, '.changeset', 'pre.json');
 35 | 
 36 | 	// Check if pre.json exists
 37 | 	if (!existsSync(preJsonPath)) {
 38 | 		console.log('✅ Not in active pre-release mode - safe to proceed');
 39 | 		process.exit(0);
 40 | 	}
 41 | 
 42 | 	try {
 43 | 		// Read and parse pre.json
 44 | 		const preJsonContent = readFileSync(preJsonPath, 'utf8');
 45 | 		const preJson = JSON.parse(preJsonContent);
 46 | 
 47 | 		// Check if we're in active pre-release mode
 48 | 		if (preJson.mode === 'pre') {
 49 | 			console.error('❌ ERROR: This branch is in active pre-release mode!');
 50 | 			console.error('');
 51 | 
 52 | 			// Provide context-specific error messages
 53 | 			if (context === 'Release Check' || context === 'pull_request') {
 54 | 				console.error(
 55 | 					'Pre-release mode must be exited before merging to main.'
 56 | 				);
 57 | 				console.error('');
 58 | 				console.error(
 59 | 					'To fix this, run the following commands in your branch:'
 60 | 				);
 61 | 				console.error('  npx changeset pre exit');
 62 | 				console.error('  git add -u');
 63 | 				console.error('  git commit -m "chore: exit pre-release mode"');
 64 | 				console.error('  git push');
 65 | 				console.error('');
 66 | 				console.error('Then update this pull request.');
 67 | 			} else if (context === 'Release' || context === 'main') {
 68 | 				console.error(
 69 | 					'Pre-release mode should only be used on feature branches, not main.'
 70 | 				);
 71 | 				console.error('');
 72 | 				console.error('To fix this, run the following commands locally:');
 73 | 				console.error('  npx changeset pre exit');
 74 | 				console.error('  git add -u');
 75 | 				console.error('  git commit -m "chore: exit pre-release mode"');
 76 | 				console.error('  git push origin main');
 77 | 				console.error('');
 78 | 				console.error('Then re-run this workflow.');
 79 | 			} else {
 80 | 				console.error('Pre-release mode must be exited before proceeding.');
 81 | 				console.error('');
 82 | 				console.error('To fix this, run the following commands:');
 83 | 				console.error('  npx changeset pre exit');
 84 | 				console.error('  git add -u');
 85 | 				console.error('  git commit -m "chore: exit pre-release mode"');
 86 | 				console.error('  git push');
 87 | 			}
 88 | 
 89 | 			process.exit(1);
 90 | 		}
 91 | 
 92 | 		console.log('✅ Not in active pre-release mode - safe to proceed');
 93 | 		process.exit(0);
 94 | 	} catch (error) {
 95 | 		console.error(`❌ ERROR: Unable to parse .changeset/pre.json – aborting.`);
 96 | 		console.error(`Error details: ${error.message}`);
 97 | 		process.exit(1);
 98 | 	}
 99 | }
100 | 
101 | // Run the check
102 | checkPreReleaseMode();
103 | 
```

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

```typescript
  1 | /**
  2 |  * Config Service
  3 |  * Manages Task Master config.json file operations
  4 |  */
  5 | 
  6 | import * as path from 'path';
  7 | import * as fs from 'fs/promises';
  8 | import * as vscode from 'vscode';
  9 | import type { ExtensionLogger } from '../utils/logger';
 10 | 
 11 | export interface TaskMasterConfigJson {
 12 | 	anthropicApiKey?: string;
 13 | 	perplexityApiKey?: string;
 14 | 	openaiApiKey?: string;
 15 | 	googleApiKey?: string;
 16 | 	xaiApiKey?: string;
 17 | 	openrouterApiKey?: string;
 18 | 	mistralApiKey?: string;
 19 | 	debug?: boolean;
 20 | 	models?: {
 21 | 		main?: string;
 22 | 		research?: string;
 23 | 		fallback?: string;
 24 | 	};
 25 | }
 26 | 
 27 | export class ConfigService {
 28 | 	private configCache: TaskMasterConfigJson | null = null;
 29 | 	private lastReadTime = 0;
 30 | 	private readonly CACHE_DURATION = 5000; // 5 seconds
 31 | 
 32 | 	constructor(private logger: ExtensionLogger) {}
 33 | 
 34 | 	/**
 35 | 	 * Read Task Master config.json from the workspace
 36 | 	 */
 37 | 	async readConfig(): Promise<TaskMasterConfigJson | null> {
 38 | 		// Check cache first
 39 | 		if (
 40 | 			this.configCache &&
 41 | 			Date.now() - this.lastReadTime < this.CACHE_DURATION
 42 | 		) {
 43 | 			return this.configCache;
 44 | 		}
 45 | 
 46 | 		try {
 47 | 			const workspaceRoot = this.getWorkspaceRoot();
 48 | 			if (!workspaceRoot) {
 49 | 				this.logger.warn('No workspace folder found');
 50 | 				return null;
 51 | 			}
 52 | 
 53 | 			const configPath = path.join(workspaceRoot, '.taskmaster', 'config.json');
 54 | 
 55 | 			try {
 56 | 				const configContent = await fs.readFile(configPath, 'utf-8');
 57 | 				const config = JSON.parse(configContent) as TaskMasterConfigJson;
 58 | 
 59 | 				// Cache the result
 60 | 				this.configCache = config;
 61 | 				this.lastReadTime = Date.now();
 62 | 
 63 | 				this.logger.debug('Successfully read Task Master config', {
 64 | 					hasModels: !!config.models,
 65 | 					debug: config.debug
 66 | 				});
 67 | 
 68 | 				return config;
 69 | 			} catch (error) {
 70 | 				if ((error as any).code === 'ENOENT') {
 71 | 					this.logger.debug('Task Master config.json not found');
 72 | 				} else {
 73 | 					this.logger.error('Failed to read Task Master config', error);
 74 | 				}
 75 | 				return null;
 76 | 			}
 77 | 		} catch (error) {
 78 | 			this.logger.error('Error accessing Task Master config', error);
 79 | 			return null;
 80 | 		}
 81 | 	}
 82 | 
 83 | 	/**
 84 | 	 * Get safe config for display (with sensitive data masked)
 85 | 	 */
 86 | 	async getSafeConfig(): Promise<Record<string, any> | null> {
 87 | 		const config = await this.readConfig();
 88 | 		if (!config) {
 89 | 			return null;
 90 | 		}
 91 | 
 92 | 		// Create a safe copy with masked API keys
 93 | 		const safeConfig: Record<string, any> = {
 94 | 			...config
 95 | 		};
 96 | 
 97 | 		// Mask all API keys
 98 | 		const apiKeyFields = [
 99 | 			'anthropicApiKey',
100 | 			'perplexityApiKey',
101 | 			'openaiApiKey',
102 | 			'googleApiKey',
103 | 			'xaiApiKey',
104 | 			'openrouterApiKey',
105 | 			'mistralApiKey'
106 | 		];
107 | 
108 | 		for (const field of apiKeyFields) {
109 | 			if (safeConfig[field]) {
110 | 				safeConfig[field] = this.maskApiKey(safeConfig[field]);
111 | 			}
112 | 		}
113 | 
114 | 		return safeConfig;
115 | 	}
116 | 
117 | 	/**
118 | 	 * Mask API key for display
119 | 	 * Shows only the last 4 characters for better security
120 | 	 */
121 | 	private maskApiKey(key: string): string {
122 | 		if (key.length <= 4) {
123 | 			return '****';
124 | 		}
125 | 		const visibleChars = 4;
126 | 		const maskedLength = key.length - visibleChars;
127 | 		return (
128 | 			'*'.repeat(Math.min(maskedLength, 12)) +
129 | 			key.substring(key.length - visibleChars)
130 | 		);
131 | 	}
132 | 
133 | 	/**
134 | 	 * Clear cache
135 | 	 */
136 | 	clearCache(): void {
137 | 		this.configCache = null;
138 | 		this.lastReadTime = 0;
139 | 	}
140 | 
141 | 	/**
142 | 	 * Get workspace root path
143 | 	 */
144 | 	private getWorkspaceRoot(): string | undefined {
145 | 		return vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
146 | 	}
147 | }
148 | 
```

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

```javascript
  1 | import cliProgress from 'cli-progress';
  2 | 
  3 | /**
  4 |  * Default configuration for progress bars
  5 |  * Extracted to avoid duplication and provide single source of truth
  6 |  */
  7 | const DEFAULT_CONFIG = {
  8 | 	clearOnComplete: false,
  9 | 	stopOnComplete: true,
 10 | 	hideCursor: true,
 11 | 	barsize: 40 // Standard terminal width for progress bar
 12 | };
 13 | 
 14 | /**
 15 |  * Available presets for progress bar styling
 16 |  * Makes it easy to see what options are available
 17 |  */
 18 | const PRESETS = {
 19 | 	shades_classic: cliProgress.Presets.shades_classic,
 20 | 	shades_grey: cliProgress.Presets.shades_grey,
 21 | 	rect: cliProgress.Presets.rect,
 22 | 	legacy: cliProgress.Presets.legacy
 23 | };
 24 | 
 25 | /**
 26 |  * Factory class for creating CLI progress bars
 27 |  * Provides a consistent interface for creating both single and multi-bar instances
 28 |  */
 29 | export class ProgressBarFactory {
 30 | 	constructor(defaultOptions = {}, defaultPreset = PRESETS.shades_classic) {
 31 | 		this.defaultOptions = { ...DEFAULT_CONFIG, ...defaultOptions };
 32 | 		this.defaultPreset = defaultPreset;
 33 | 	}
 34 | 
 35 | 	/**
 36 | 	 * Creates a new single progress bar
 37 | 	 * @param {Object} opts - Custom options to override defaults
 38 | 	 * @param {Object} preset - Progress bar preset for styling
 39 | 	 * @returns {cliProgress.SingleBar} Configured single progress bar instance
 40 | 	 */
 41 | 	createSingleBar(opts = {}, preset = null) {
 42 | 		const config = this._mergeConfig(opts);
 43 | 		const barPreset = preset || this.defaultPreset;
 44 | 
 45 | 		return new cliProgress.SingleBar(config, barPreset);
 46 | 	}
 47 | 
 48 | 	/**
 49 | 	 * Creates a new multi-bar container
 50 | 	 * @param {Object} opts - Custom options to override defaults
 51 | 	 * @param {Object} preset - Progress bar preset for styling
 52 | 	 * @returns {cliProgress.MultiBar} Configured multi-bar instance
 53 | 	 */
 54 | 	createMultiBar(opts = {}, preset = null) {
 55 | 		const config = this._mergeConfig(opts);
 56 | 		const barPreset = preset || this.defaultPreset;
 57 | 
 58 | 		return new cliProgress.MultiBar(config, barPreset);
 59 | 	}
 60 | 
 61 | 	/**
 62 | 	 * Merges custom options with defaults
 63 | 	 * @private
 64 | 	 * @param {Object} customOpts - Custom options to merge
 65 | 	 * @returns {Object} Merged configuration
 66 | 	 */
 67 | 	_mergeConfig(customOpts) {
 68 | 		return { ...this.defaultOptions, ...customOpts };
 69 | 	}
 70 | 
 71 | 	/**
 72 | 	 * Updates the default configuration
 73 | 	 * @param {Object} options - New default options
 74 | 	 */
 75 | 	setDefaultOptions(options) {
 76 | 		this.defaultOptions = { ...this.defaultOptions, ...options };
 77 | 	}
 78 | 
 79 | 	/**
 80 | 	 * Updates the default preset
 81 | 	 * @param {Object} preset - New default preset
 82 | 	 */
 83 | 	setDefaultPreset(preset) {
 84 | 		this.defaultPreset = preset;
 85 | 	}
 86 | }
 87 | 
 88 | // Create a default factory instance for backward compatibility
 89 | const defaultFactory = new ProgressBarFactory();
 90 | 
 91 | /**
 92 |  * Legacy function for creating a single progress bar
 93 |  * @deprecated Use ProgressBarFactory.createSingleBar() instead
 94 |  * @param {Object} opts - Progress bar options
 95 |  * @returns {cliProgress.SingleBar} Single progress bar instance
 96 |  */
 97 | export function newSingle(opts = {}) {
 98 | 	return defaultFactory.createSingleBar(opts);
 99 | }
100 | 
101 | /**
102 |  * Legacy function for creating a multi-bar
103 |  * @deprecated Use ProgressBarFactory.createMultiBar() instead
104 |  * @param {Object} opts - Progress bar options
105 |  * @returns {cliProgress.MultiBar} Multi-bar instance
106 |  */
107 | export function newMultiBar(opts = {}) {
108 | 	return defaultFactory.createMultiBar(opts);
109 | }
110 | 
111 | // Export presets for easy access
112 | export { PRESETS };
113 | 
114 | // Export the factory class as default
115 | export default ProgressBarFactory;
116 | 
```

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

```javascript
  1 | /**
  2 |  * tools/add-subtask.js
  3 |  * Tool for adding subtasks to existing tasks
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	createErrorResponse,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import { addSubtaskDirect } from '../core/task-master-core.js';
 13 | import { findTasksPath } from '../core/utils/path-utils.js';
 14 | import { resolveTag } from '../../../scripts/modules/utils.js';
 15 | 
 16 | /**
 17 |  * Register the addSubtask tool with the MCP server
 18 |  * @param {Object} server - FastMCP server instance
 19 |  */
 20 | export function registerAddSubtaskTool(server) {
 21 | 	server.addTool({
 22 | 		name: 'add_subtask',
 23 | 		description: 'Add a subtask to an existing task',
 24 | 		parameters: z.object({
 25 | 			id: z.string().describe('Parent task ID (required)'),
 26 | 			taskId: z
 27 | 				.string()
 28 | 				.optional()
 29 | 				.describe('Existing task ID to convert to subtask'),
 30 | 			title: z
 31 | 				.string()
 32 | 				.optional()
 33 | 				.describe('Title for the new subtask (when creating a new subtask)'),
 34 | 			description: z
 35 | 				.string()
 36 | 				.optional()
 37 | 				.describe('Description for the new subtask'),
 38 | 			details: z
 39 | 				.string()
 40 | 				.optional()
 41 | 				.describe('Implementation details for the new subtask'),
 42 | 			status: z
 43 | 				.string()
 44 | 				.optional()
 45 | 				.describe("Status for the new subtask (default: 'pending')"),
 46 | 			dependencies: z
 47 | 				.string()
 48 | 				.optional()
 49 | 				.describe('Comma-separated list of dependency IDs for the new subtask'),
 50 | 			file: z
 51 | 				.string()
 52 | 				.optional()
 53 | 				.describe(
 54 | 					'Absolute path to the tasks file (default: tasks/tasks.json)'
 55 | 				),
 56 | 			skipGenerate: z
 57 | 				.boolean()
 58 | 				.optional()
 59 | 				.describe('Skip regenerating task files'),
 60 | 			projectRoot: z
 61 | 				.string()
 62 | 				.describe('The directory of the project. Must be an absolute path.'),
 63 | 			tag: z.string().optional().describe('Tag context to operate on')
 64 | 		}),
 65 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 66 | 			try {
 67 | 				const resolvedTag = resolveTag({
 68 | 					projectRoot: args.projectRoot,
 69 | 					tag: args.tag
 70 | 				});
 71 | 				log.info(`Adding subtask with args: ${JSON.stringify(args)}`);
 72 | 
 73 | 				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
 74 | 				let tasksJsonPath;
 75 | 				try {
 76 | 					tasksJsonPath = findTasksPath(
 77 | 						{ projectRoot: args.projectRoot, file: args.file },
 78 | 						log
 79 | 					);
 80 | 				} catch (error) {
 81 | 					log.error(`Error finding tasks.json: ${error.message}`);
 82 | 					return createErrorResponse(
 83 | 						`Failed to find tasks.json: ${error.message}`
 84 | 					);
 85 | 				}
 86 | 
 87 | 				const result = await addSubtaskDirect(
 88 | 					{
 89 | 						tasksJsonPath: tasksJsonPath,
 90 | 						id: args.id,
 91 | 						taskId: args.taskId,
 92 | 						title: args.title,
 93 | 						description: args.description,
 94 | 						details: args.details,
 95 | 						status: args.status,
 96 | 						dependencies: args.dependencies,
 97 | 						skipGenerate: args.skipGenerate,
 98 | 						projectRoot: args.projectRoot,
 99 | 						tag: resolvedTag
100 | 					},
101 | 					log,
102 | 					{ session }
103 | 				);
104 | 
105 | 				if (result.success) {
106 | 					log.info(`Subtask added successfully: ${result.data.message}`);
107 | 				} else {
108 | 					log.error(`Failed to add subtask: ${result.error.message}`);
109 | 				}
110 | 
111 | 				return handleApiResult(
112 | 					result,
113 | 					log,
114 | 					'Error adding subtask',
115 | 					undefined,
116 | 					args.projectRoot
117 | 				);
118 | 			} catch (error) {
119 | 				log.error(`Error in addSubtask tool: ${error.message}`);
120 | 				return createErrorResponse(error.message);
121 | 			}
122 | 		})
123 | 	});
124 | }
125 | 
```

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

```javascript
  1 | /**
  2 |  * Provider Registry - Singleton for managing AI providers
  3 |  *
  4 |  * This module implements a singleton registry that allows dynamic registration
  5 |  * of AI providers at runtime, while maintaining compatibility with the existing
  6 |  * static PROVIDERS object in ai-services-unified.js.
  7 |  */
  8 | 
  9 | // Singleton instance
 10 | let instance = null;
 11 | 
 12 | /**
 13 |  * Provider Registry class - Manages dynamic provider registration
 14 |  */
 15 | class ProviderRegistry {
 16 | 	constructor() {
 17 | 		// Private provider map
 18 | 		this._providers = new Map();
 19 | 
 20 | 		// Flag to track initialization
 21 | 		this._initialized = false;
 22 | 	}
 23 | 
 24 | 	/**
 25 | 	 * Get the singleton instance
 26 | 	 * @returns {ProviderRegistry} The singleton instance
 27 | 	 */
 28 | 	static getInstance() {
 29 | 		if (!instance) {
 30 | 			instance = new ProviderRegistry();
 31 | 		}
 32 | 		return instance;
 33 | 	}
 34 | 
 35 | 	/**
 36 | 	 * Initialize the registry
 37 | 	 * @returns {ProviderRegistry} The singleton instance
 38 | 	 */
 39 | 	initialize() {
 40 | 		if (this._initialized) {
 41 | 			return this;
 42 | 		}
 43 | 
 44 | 		this._initialized = true;
 45 | 		return this;
 46 | 	}
 47 | 
 48 | 	/**
 49 | 	 * Register a provider with the registry
 50 | 	 * @param {string} providerName - The name of the provider
 51 | 	 * @param {object} provider - The provider instance
 52 | 	 * @param {object} options - Additional options for registration
 53 | 	 * @returns {ProviderRegistry} The singleton instance for chaining
 54 | 	 */
 55 | 	registerProvider(providerName, provider, options = {}) {
 56 | 		if (!providerName || typeof providerName !== 'string') {
 57 | 			throw new Error('Provider name must be a non-empty string');
 58 | 		}
 59 | 
 60 | 		if (!provider) {
 61 | 			throw new Error('Provider instance is required');
 62 | 		}
 63 | 
 64 | 		// Validate that provider implements the required interface
 65 | 		if (
 66 | 			typeof provider.generateText !== 'function' ||
 67 | 			typeof provider.streamText !== 'function' ||
 68 | 			typeof provider.generateObject !== 'function'
 69 | 		) {
 70 | 			throw new Error('Provider must implement BaseAIProvider interface');
 71 | 		}
 72 | 
 73 | 		// Add provider to the registry
 74 | 		this._providers.set(providerName, {
 75 | 			instance: provider,
 76 | 			options,
 77 | 			registeredAt: new Date()
 78 | 		});
 79 | 
 80 | 		return this;
 81 | 	}
 82 | 
 83 | 	/**
 84 | 	 * Check if a provider exists in the registry
 85 | 	 * @param {string} providerName - The name of the provider
 86 | 	 * @returns {boolean} True if the provider exists
 87 | 	 */
 88 | 	hasProvider(providerName) {
 89 | 		return this._providers.has(providerName);
 90 | 	}
 91 | 
 92 | 	/**
 93 | 	 * Get a provider from the registry
 94 | 	 * @param {string} providerName - The name of the provider
 95 | 	 * @returns {object|null} The provider instance or null if not found
 96 | 	 */
 97 | 	getProvider(providerName) {
 98 | 		const providerEntry = this._providers.get(providerName);
 99 | 		return providerEntry ? providerEntry.instance : null;
100 | 	}
101 | 
102 | 	/**
103 | 	 * Get all registered providers
104 | 	 * @returns {Map} Map of all registered providers
105 | 	 */
106 | 	getAllProviders() {
107 | 		return new Map(this._providers);
108 | 	}
109 | 
110 | 	/**
111 | 	 * Remove a provider from the registry
112 | 	 * @param {string} providerName - The name of the provider
113 | 	 * @returns {boolean} True if the provider was removed
114 | 	 */
115 | 	unregisterProvider(providerName) {
116 | 		if (this._providers.has(providerName)) {
117 | 			this._providers.delete(providerName);
118 | 			return true;
119 | 		}
120 | 		return false;
121 | 	}
122 | 
123 | 	/**
124 | 	 * Reset the registry (primarily for testing)
125 | 	 */
126 | 	reset() {
127 | 		this._providers.clear();
128 | 		this._initialized = false;
129 | 	}
130 | }
131 | 
132 | ProviderRegistry.getInstance().initialize(); // Ensure singleton is initialized on import
133 | // Export singleton getter
134 | export default ProviderRegistry;
135 | 
```

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

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

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

```javascript
  1 | /**
  2 |  * Configuration classes and schemas for PRD parsing
  3 |  */
  4 | 
  5 | import { z } from 'zod';
  6 | import { TASK_PRIORITY_OPTIONS } from '../../../../src/constants/task-priority.js';
  7 | import { getCurrentTag, isSilentMode, log } from '../../utils.js';
  8 | import { Duration } from '../../../../src/utils/timeout-manager.js';
  9 | import { hasCodebaseAnalysis } from '../../config-manager.js';
 10 | 
 11 | // ============================================================================
 12 | // SCHEMAS
 13 | // ============================================================================
 14 | 
 15 | // Define the Zod schema for a SINGLE task object
 16 | export const prdSingleTaskSchema = z.object({
 17 | 	id: z.number(),
 18 | 	title: z.string().min(1),
 19 | 	description: z.string().min(1),
 20 | 	details: z.string(),
 21 | 	testStrategy: z.string(),
 22 | 	priority: z.enum(TASK_PRIORITY_OPTIONS),
 23 | 	dependencies: z.array(z.number()),
 24 | 	status: z.string()
 25 | });
 26 | 
 27 | // Define the Zod schema for the ENTIRE expected AI response object
 28 | export const prdResponseSchema = z.object({
 29 | 	tasks: z.array(prdSingleTaskSchema),
 30 | 	metadata: z.object({
 31 | 		projectName: z.string(),
 32 | 		totalTasks: z.number(),
 33 | 		sourceFile: z.string(),
 34 | 		generatedAt: z.string()
 35 | 	})
 36 | });
 37 | 
 38 | // ============================================================================
 39 | // CONFIGURATION CLASSES
 40 | // ============================================================================
 41 | 
 42 | /**
 43 |  * Configuration object for PRD parsing
 44 |  */
 45 | export class PrdParseConfig {
 46 | 	constructor(prdPath, tasksPath, numTasks, options = {}) {
 47 | 		this.prdPath = prdPath;
 48 | 		this.tasksPath = tasksPath;
 49 | 		this.numTasks = numTasks;
 50 | 		this.force = options.force || false;
 51 | 		this.append = options.append || false;
 52 | 		this.research = options.research || false;
 53 | 		this.reportProgress = options.reportProgress;
 54 | 		this.mcpLog = options.mcpLog;
 55 | 		this.session = options.session;
 56 | 		this.projectRoot = options.projectRoot;
 57 | 		this.tag = options.tag;
 58 | 		this.streamingTimeout =
 59 | 			options.streamingTimeout || Duration.seconds(180).milliseconds;
 60 | 
 61 | 		// Derived values
 62 | 		this.targetTag = this.tag || getCurrentTag(this.projectRoot) || 'master';
 63 | 		this.isMCP = !!this.mcpLog;
 64 | 		this.outputFormat = this.isMCP && !this.reportProgress ? 'json' : 'text';
 65 | 
 66 | 		// Feature flag: Temporarily disable streaming, use generateObject instead
 67 | 		// TODO: Re-enable streaming once issues are resolved
 68 | 		const ENABLE_STREAMING = false;
 69 | 
 70 | 		this.useStreaming =
 71 | 			ENABLE_STREAMING &&
 72 | 			(typeof this.reportProgress === 'function' ||
 73 | 				this.outputFormat === 'text');
 74 | 	}
 75 | 
 76 | 	/**
 77 | 	 * Check if codebase analysis is available (Claude Code or Gemini CLI)
 78 | 	 */
 79 | 	hasCodebaseAnalysis() {
 80 | 		return hasCodebaseAnalysis(this.research, this.projectRoot, this.session);
 81 | 	}
 82 | }
 83 | 
 84 | /**
 85 |  * Logging configuration and utilities
 86 |  */
 87 | export class LoggingConfig {
 88 | 	constructor(mcpLog, reportProgress) {
 89 | 		this.isMCP = !!mcpLog;
 90 | 		this.outputFormat = this.isMCP && !reportProgress ? 'json' : 'text';
 91 | 
 92 | 		this.logFn = mcpLog || {
 93 | 			info: (...args) => log('info', ...args),
 94 | 			warn: (...args) => log('warn', ...args),
 95 | 			error: (...args) => log('error', ...args),
 96 | 			debug: (...args) => log('debug', ...args),
 97 | 			success: (...args) => log('success', ...args)
 98 | 		};
 99 | 	}
100 | 
101 | 	report(message, level = 'info') {
102 | 		if (this.logFn && typeof this.logFn[level] === 'function') {
103 | 			this.logFn[level](message);
104 | 		} else if (!isSilentMode() && this.outputFormat === 'text') {
105 | 			log(level, message);
106 | 		}
107 | 	}
108 | }
109 | 
```

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

```yaml
  1 | name: Extension CI
  2 | 
  3 | on:
  4 |   push:
  5 |     branches:
  6 |       - main
  7 |       - next
  8 |     paths:
  9 |       - 'apps/extension/**'
 10 |       - '.github/workflows/extension-ci.yml'
 11 |   pull_request:
 12 |     branches:
 13 |       - main
 14 |       - next
 15 |     paths:
 16 |       - 'apps/extension/**'
 17 |       - '.github/workflows/extension-ci.yml'
 18 | 
 19 | permissions:
 20 |   contents: read
 21 | 
 22 | jobs:
 23 |   setup:
 24 |     runs-on: ubuntu-latest
 25 |     steps:
 26 |       - uses: actions/checkout@v4
 27 |         with:
 28 |           fetch-depth: 0
 29 | 
 30 |       - uses: actions/setup-node@v4
 31 |         with:
 32 |           node-version: 20
 33 | 
 34 |       - name: Cache node_modules
 35 |         uses: actions/cache@v4
 36 |         with:
 37 |           path: |
 38 |             node_modules
 39 |             */*/node_modules
 40 |           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
 41 |           restore-keys: |
 42 |             ${{ runner.os }}-node-
 43 | 
 44 |       - name: Install Extension Dependencies
 45 |         working-directory: apps/extension
 46 |         run: npm ci
 47 |         timeout-minutes: 5
 48 | 
 49 |   typecheck:
 50 |     needs: setup
 51 |     runs-on: ubuntu-latest
 52 |     steps:
 53 |       - uses: actions/checkout@v4
 54 | 
 55 |       - uses: actions/setup-node@v4
 56 |         with:
 57 |           node-version: 20
 58 | 
 59 | 
 60 |       - name: Restore node_modules
 61 |         uses: actions/cache@v4
 62 |         with:
 63 |           path: |
 64 |             node_modules
 65 |             */*/node_modules
 66 |           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
 67 |           restore-keys: |
 68 |             ${{ runner.os }}-node-
 69 | 
 70 |       - name: Install if cache miss
 71 |         working-directory: apps/extension
 72 |         run: npm ci
 73 |         timeout-minutes: 3
 74 | 
 75 |       - name: Type Check Extension
 76 |         working-directory: apps/extension
 77 |         run: npm run check-types
 78 |         env:
 79 |           FORCE_COLOR: 1
 80 | 
 81 |   build:
 82 |     needs: setup
 83 |     runs-on: ubuntu-latest
 84 |     steps:
 85 |       - uses: actions/checkout@v4
 86 | 
 87 |       - uses: actions/setup-node@v4
 88 |         with:
 89 |           node-version: 20
 90 | 
 91 | 
 92 |       - name: Restore node_modules
 93 |         uses: actions/cache@v4
 94 |         with:
 95 |           path: |
 96 |             node_modules
 97 |             */*/node_modules
 98 |           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
 99 |           restore-keys: |
100 |             ${{ runner.os }}-node-
101 | 
102 |       - name: Install if cache miss
103 |         working-directory: apps/extension
104 |         run: npm ci
105 |         timeout-minutes: 3
106 | 
107 |       - name: Build Extension
108 |         working-directory: apps/extension
109 |         run: npm run build
110 |         env:
111 |           FORCE_COLOR: 1
112 | 
113 |       - name: Package Extension
114 |         working-directory: apps/extension
115 |         run: npm run package
116 |         env:
117 |           FORCE_COLOR: 1
118 | 
119 |       - name: Verify Package Contents
120 |         working-directory: apps/extension
121 |         run: |
122 |           echo "Checking vsix-build contents..."
123 |           ls -la vsix-build/
124 |           echo "Checking dist contents..."
125 |           ls -la vsix-build/dist/
126 |           echo "Checking package.json exists..."
127 |           test -f vsix-build/package.json
128 | 
129 |       - name: Create VSIX Package (Test)
130 |         working-directory: apps/extension/vsix-build
131 |         run: npx vsce package --no-dependencies
132 |         env:
133 |           FORCE_COLOR: 1
134 | 
135 |       - name: Upload Extension Artifact
136 |         uses: actions/upload-artifact@v4
137 |         with:
138 |           name: extension-package
139 |           path: |
140 |             apps/extension/vsix-build/*.vsix
141 |             apps/extension/dist/
142 |           retention-days: 30
143 | 
144 | 
```

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

```javascript
  1 | /**
  2 |  * Direct function wrapper for removeSubtask
  3 |  */
  4 | 
  5 | import { removeSubtask } from '../../../../scripts/modules/task-manager.js';
  6 | import {
  7 | 	enableSilentMode,
  8 | 	disableSilentMode
  9 | } from '../../../../scripts/modules/utils.js';
 10 | 
 11 | /**
 12 |  * Remove a subtask from its parent task
 13 |  * @param {Object} args - Function arguments
 14 |  * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 15 |  * @param {string} args.id - Subtask ID in format "parentId.subtaskId" (required)
 16 |  * @param {boolean} [args.convert] - Whether to convert the subtask to a standalone task
 17 |  * @param {boolean} [args.skipGenerate] - Skip regenerating task files
 18 |  * @param {string} args.projectRoot - Project root path (for MCP/env fallback)
 19 |  * @param {string} args.tag - Tag for the task (optional)
 20 |  * @param {Object} log - Logger object
 21 |  * @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
 22 |  */
 23 | export async function removeSubtaskDirect(args, log) {
 24 | 	// Destructure expected args
 25 | 	const { tasksJsonPath, id, convert, skipGenerate, projectRoot, tag } = args;
 26 | 	try {
 27 | 		// Enable silent mode to prevent console logs from interfering with JSON response
 28 | 		enableSilentMode();
 29 | 
 30 | 		log.info(`Removing subtask with args: ${JSON.stringify(args)}`);
 31 | 
 32 | 		// Check if tasksJsonPath was provided
 33 | 		if (!tasksJsonPath) {
 34 | 			log.error('removeSubtaskDirect called without tasksJsonPath');
 35 | 			disableSilentMode(); // Disable before returning
 36 | 			return {
 37 | 				success: false,
 38 | 				error: {
 39 | 					code: 'MISSING_ARGUMENT',
 40 | 					message: 'tasksJsonPath is required'
 41 | 				}
 42 | 			};
 43 | 		}
 44 | 
 45 | 		if (!id) {
 46 | 			disableSilentMode(); // Disable before returning
 47 | 			return {
 48 | 				success: false,
 49 | 				error: {
 50 | 					code: 'INPUT_VALIDATION_ERROR',
 51 | 					message:
 52 | 						'Subtask ID is required and must be in format "parentId.subtaskId"'
 53 | 				}
 54 | 			};
 55 | 		}
 56 | 
 57 | 		// Validate subtask ID format
 58 | 		if (!id.includes('.')) {
 59 | 			disableSilentMode(); // Disable before returning
 60 | 			return {
 61 | 				success: false,
 62 | 				error: {
 63 | 					code: 'INPUT_VALIDATION_ERROR',
 64 | 					message: `Invalid subtask ID format: ${id}. Expected format: "parentId.subtaskId"`
 65 | 				}
 66 | 			};
 67 | 		}
 68 | 
 69 | 		// Use provided path
 70 | 		const tasksPath = tasksJsonPath;
 71 | 
 72 | 		// Convert convertToTask to a boolean
 73 | 		const convertToTask = convert === true;
 74 | 
 75 | 		// Determine if we should generate files
 76 | 		const generateFiles = !skipGenerate;
 77 | 
 78 | 		log.info(
 79 | 			`Removing subtask ${id} (convertToTask: ${convertToTask}, generateFiles: ${generateFiles})`
 80 | 		);
 81 | 
 82 | 		// Use the provided tasksPath
 83 | 		const result = await removeSubtask(
 84 | 			tasksPath,
 85 | 			id,
 86 | 			convertToTask,
 87 | 			generateFiles,
 88 | 			{
 89 | 				projectRoot,
 90 | 				tag
 91 | 			}
 92 | 		);
 93 | 
 94 | 		// Restore normal logging
 95 | 		disableSilentMode();
 96 | 
 97 | 		if (convertToTask && result) {
 98 | 			// Return info about the converted task
 99 | 			return {
100 | 				success: true,
101 | 				data: {
102 | 					message: `Subtask ${id} successfully converted to task #${result.id}`,
103 | 					task: result
104 | 				}
105 | 			};
106 | 		} else {
107 | 			// Return simple success message for deletion
108 | 			return {
109 | 				success: true,
110 | 				data: {
111 | 					message: `Subtask ${id} successfully removed`
112 | 				}
113 | 			};
114 | 		}
115 | 	} catch (error) {
116 | 		// Ensure silent mode is disabled even if an outer error occurs
117 | 		disableSilentMode();
118 | 
119 | 		log.error(`Error in removeSubtaskDirect: ${error.message}`);
120 | 		return {
121 | 			success: false,
122 | 			error: {
123 | 				code: 'CORE_FUNCTION_ERROR',
124 | 				message: error.message
125 | 			}
126 | 		};
127 | 	}
128 | }
129 | 
```

--------------------------------------------------------------------------------
/src/ui/confirm.js:
--------------------------------------------------------------------------------

```javascript
  1 | import chalk from 'chalk';
  2 | import boxen from 'boxen';
  3 | 
  4 | /**
  5 |  * Confirm removing profile rules (destructive operation)
  6 |  * @param {string[]} profiles - Array of profile names to remove
  7 |  * @returns {Promise<boolean>} - Promise resolving to true if user confirms, false otherwise
  8 |  */
  9 | async function confirmProfilesRemove(profiles) {
 10 | 	const profileList = profiles
 11 | 		.map((b) => b.charAt(0).toUpperCase() + b.slice(1))
 12 | 		.join(', ');
 13 | 	console.log(
 14 | 		boxen(
 15 | 			chalk.yellow(
 16 | 				`WARNING: This will selectively remove Task Master components for: ${profileList}.
 17 | 
 18 | What will be removed:
 19 | • Task Master specific rule files (e.g., cursor_rules.mdc, taskmaster.mdc, etc.)
 20 | • Task Master MCP server configuration (if no other MCP servers exist)
 21 | 
 22 | What will be preserved:
 23 | • Your existing custom rule files
 24 | • Other MCP server configurations
 25 | • The profile directory itself (unless completely empty after removal)
 26 | 
 27 | The .[profile] directory will only be removed if ALL of the following are true:
 28 | • All rules in the directory were Task Master rules (no custom rules)
 29 | • No other files or folders exist in the profile directory
 30 | • The MCP configuration was completely removed (no other servers)
 31 | 
 32 | Are you sure you want to proceed?`
 33 | 			),
 34 | 			{ padding: 1, borderColor: 'yellow', borderStyle: 'round' }
 35 | 		)
 36 | 	);
 37 | 	const inquirer = await import('inquirer');
 38 | 	const { confirm } = await inquirer.default.prompt([
 39 | 		{
 40 | 			type: 'confirm',
 41 | 			name: 'confirm',
 42 | 			message: 'Type y to confirm selective removal, or n to abort:',
 43 | 			default: false
 44 | 		}
 45 | 	]);
 46 | 	return confirm;
 47 | }
 48 | 
 49 | /**
 50 |  * Confirm removing ALL remaining profile rules (extremely critical operation)
 51 |  * @param {string[]} profiles - Array of profile names to remove
 52 |  * @param {string[]} remainingProfiles - Array of profiles that would be left after removal
 53 |  * @returns {Promise<boolean>} - Promise resolving to true if user confirms, false otherwise
 54 |  */
 55 | async function confirmRemoveAllRemainingProfiles(profiles, remainingProfiles) {
 56 | 	const profileList = profiles
 57 | 		.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
 58 | 		.join(', ');
 59 | 
 60 | 	console.log(
 61 | 		boxen(
 62 | 			chalk.red.bold(
 63 | 				`⚠️  CRITICAL WARNING: REMOVING ALL TASK MASTER RULE PROFILES ⚠️\n\n` +
 64 | 					`You are about to remove Task Master components for: ${profileList}\n` +
 65 | 					`This will leave your project with NO Task Master rule profiles remaining!\n\n` +
 66 | 					`What will be removed:\n` +
 67 | 					`• All Task Master specific rule files\n` +
 68 | 					`• Task Master MCP server configurations\n` +
 69 | 					`• Profile directories (only if completely empty after removal)\n\n` +
 70 | 					`What will be preserved:\n` +
 71 | 					`• Your existing custom rule files\n` +
 72 | 					`• Other MCP server configurations\n` +
 73 | 					`• Profile directories with custom content\n\n` +
 74 | 					`This could impact Task Master functionality but will preserve your custom configurations.\n\n` +
 75 | 					`Are you absolutely sure you want to proceed?`
 76 | 			),
 77 | 			{
 78 | 				padding: 1,
 79 | 				borderColor: 'red',
 80 | 				borderStyle: 'double',
 81 | 				title: '🚨 CRITICAL OPERATION',
 82 | 				titleAlignment: 'center'
 83 | 			}
 84 | 		)
 85 | 	);
 86 | 
 87 | 	const inquirer = await import('inquirer');
 88 | 	const { confirm } = await inquirer.default.prompt([
 89 | 		{
 90 | 			type: 'confirm',
 91 | 			name: 'confirm',
 92 | 			message:
 93 | 				'Type y to confirm removing ALL Task Master rule profiles, or n to abort:',
 94 | 			default: false
 95 | 		}
 96 | 	]);
 97 | 	return confirm;
 98 | }
 99 | 
100 | export { confirmProfilesRemove, confirmRemoveAllRemainingProfiles };
101 | 
```

--------------------------------------------------------------------------------
/scripts/modules/task-manager/update-single-task-status.js:
--------------------------------------------------------------------------------

```javascript
  1 | import chalk from 'chalk';
  2 | 
  3 | import { log } from '../utils.js';
  4 | import { isValidTaskStatus } from '../../../src/constants/task-status.js';
  5 | 
  6 | /**
  7 |  * Update the status of a single task
  8 |  * @param {string} tasksPath - Path to the tasks.json file
  9 |  * @param {string} taskIdInput - Task ID to update
 10 |  * @param {string} newStatus - New status
 11 |  * @param {Object} data - Tasks data
 12 |  * @param {boolean} showUi - Whether to show UI elements
 13 |  */
 14 | async function updateSingleTaskStatus(
 15 | 	tasksPath,
 16 | 	taskIdInput,
 17 | 	newStatus,
 18 | 	data,
 19 | 	showUi = true
 20 | ) {
 21 | 	if (!isValidTaskStatus(newStatus)) {
 22 | 		throw new Error(
 23 | 			`Error: Invalid status value: ${newStatus}. Use one of: ${TASK_STATUS_OPTIONS.join(', ')}`
 24 | 		);
 25 | 	}
 26 | 
 27 | 	// Check if it's a subtask (e.g., "1.2")
 28 | 	if (taskIdInput.includes('.')) {
 29 | 		const [parentId, subtaskId] = taskIdInput
 30 | 			.split('.')
 31 | 			.map((id) => parseInt(id, 10));
 32 | 
 33 | 		// Find the parent task
 34 | 		const parentTask = data.tasks.find((t) => t.id === parentId);
 35 | 		if (!parentTask) {
 36 | 			throw new Error(`Parent task ${parentId} not found`);
 37 | 		}
 38 | 
 39 | 		// Find the subtask
 40 | 		if (!parentTask.subtasks) {
 41 | 			throw new Error(`Parent task ${parentId} has no subtasks`);
 42 | 		}
 43 | 
 44 | 		const subtask = parentTask.subtasks.find((st) => st.id === subtaskId);
 45 | 		if (!subtask) {
 46 | 			throw new Error(
 47 | 				`Subtask ${subtaskId} not found in parent task ${parentId}`
 48 | 			);
 49 | 		}
 50 | 
 51 | 		// Update the subtask status
 52 | 		const oldStatus = subtask.status || 'pending';
 53 | 		subtask.status = newStatus;
 54 | 
 55 | 		log(
 56 | 			'info',
 57 | 			`Updated subtask ${parentId}.${subtaskId} status from '${oldStatus}' to '${newStatus}'`
 58 | 		);
 59 | 
 60 | 		// Check if all subtasks are done (if setting to 'done')
 61 | 		if (
 62 | 			newStatus.toLowerCase() === 'done' ||
 63 | 			newStatus.toLowerCase() === 'completed'
 64 | 		) {
 65 | 			const allSubtasksDone = parentTask.subtasks.every(
 66 | 				(st) => st.status === 'done' || st.status === 'completed'
 67 | 			);
 68 | 
 69 | 			// Suggest updating parent task if all subtasks are done
 70 | 			if (
 71 | 				allSubtasksDone &&
 72 | 				parentTask.status !== 'done' &&
 73 | 				parentTask.status !== 'completed'
 74 | 			) {
 75 | 				// Only show suggestion in CLI mode
 76 | 				if (showUi) {
 77 | 					console.log(
 78 | 						chalk.yellow(
 79 | 							`All subtasks of parent task ${parentId} are now marked as done.`
 80 | 						)
 81 | 					);
 82 | 					console.log(
 83 | 						chalk.yellow(
 84 | 							`Consider updating the parent task status with: task-master set-status --id=${parentId} --status=done`
 85 | 						)
 86 | 					);
 87 | 				}
 88 | 			}
 89 | 		}
 90 | 	} else {
 91 | 		// Handle regular task
 92 | 		const taskId = parseInt(taskIdInput, 10);
 93 | 		const task = data.tasks.find((t) => t.id === taskId);
 94 | 
 95 | 		if (!task) {
 96 | 			throw new Error(`Task ${taskId} not found`);
 97 | 		}
 98 | 
 99 | 		// Update the task status
100 | 		const oldStatus = task.status || 'pending';
101 | 		task.status = newStatus;
102 | 
103 | 		log(
104 | 			'info',
105 | 			`Updated task ${taskId} status from '${oldStatus}' to '${newStatus}'`
106 | 		);
107 | 
108 | 		// If marking as done, also mark all subtasks as done
109 | 		if (
110 | 			(newStatus.toLowerCase() === 'done' ||
111 | 				newStatus.toLowerCase() === 'completed') &&
112 | 			task.subtasks &&
113 | 			task.subtasks.length > 0
114 | 		) {
115 | 			const pendingSubtasks = task.subtasks.filter(
116 | 				(st) => st.status !== 'done' && st.status !== 'completed'
117 | 			);
118 | 
119 | 			if (pendingSubtasks.length > 0) {
120 | 				log(
121 | 					'info',
122 | 					`Also marking ${pendingSubtasks.length} subtasks as '${newStatus}'`
123 | 				);
124 | 
125 | 				pendingSubtasks.forEach((subtask) => {
126 | 					subtask.status = newStatus;
127 | 				});
128 | 			}
129 | 		}
130 | 	}
131 | }
132 | 
133 | export default updateSingleTaskStatus;
134 | 
```

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

```yaml
  1 | name: CI
  2 | 
  3 | on:
  4 |   push:
  5 |     branches:
  6 |       - main
  7 |       - next
  8 |   pull_request:
  9 |     branches:
 10 |       - main
 11 |       - next
 12 |   workflow_dispatch:
 13 | 
 14 | concurrency:
 15 |   group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
 16 |   cancel-in-progress: true
 17 | 
 18 | permissions:
 19 |   contents: read
 20 | 
 21 | env:
 22 |   DO_NOT_TRACK: 1
 23 |   NODE_ENV: development
 24 | 
 25 | jobs:
 26 |   # Fast checks that can run in parallel
 27 |   format-check:
 28 |     name: Format Check
 29 |     runs-on: ubuntu-latest
 30 |     steps:
 31 |       - uses: actions/checkout@v4
 32 |         with:
 33 |           fetch-depth: 2
 34 | 
 35 |       - uses: actions/setup-node@v4
 36 |         with:
 37 |           node-version: 20
 38 |           cache: "npm"
 39 | 
 40 |       - name: Install dependencies
 41 |         run: npm install --frozen-lockfile --prefer-offline
 42 |         timeout-minutes: 5
 43 | 
 44 |       - name: Format Check
 45 |         run: npm run format-check
 46 |         env:
 47 |           FORCE_COLOR: 1
 48 | 
 49 |   typecheck:
 50 |     name: Typecheck
 51 |     timeout-minutes: 10
 52 |     runs-on: ubuntu-latest
 53 |     steps:
 54 |       - uses: actions/checkout@v4
 55 |         with:
 56 |           fetch-depth: 2
 57 | 
 58 |       - uses: actions/setup-node@v4
 59 |         with:
 60 |           node-version: 20
 61 |           cache: "npm"
 62 | 
 63 |       - name: Install dependencies
 64 |         run: npm install --frozen-lockfile --prefer-offline
 65 |         timeout-minutes: 5
 66 | 
 67 |       - name: Typecheck
 68 |         run: npm run turbo:typecheck
 69 |         env:
 70 |           FORCE_COLOR: 1
 71 | 
 72 |   # Build job to ensure everything compiles
 73 |   build:
 74 |     name: Build
 75 |     runs-on: ubuntu-latest
 76 |     steps:
 77 |       - uses: actions/checkout@v4
 78 |         with:
 79 |           fetch-depth: 2
 80 | 
 81 |       - uses: actions/setup-node@v4
 82 |         with:
 83 |           node-version: 20
 84 |           cache: "npm"
 85 | 
 86 |       - name: Install dependencies
 87 |         run: npm install --frozen-lockfile --prefer-offline
 88 |         timeout-minutes: 5
 89 | 
 90 |       - name: Build
 91 |         run: npm run turbo:build
 92 |         env:
 93 |           NODE_ENV: production
 94 |           FORCE_COLOR: 1
 95 |           TM_PUBLIC_BASE_DOMAIN: ${{ secrets.TM_PUBLIC_BASE_DOMAIN }}
 96 |           TM_PUBLIC_SUPABASE_URL: ${{ secrets.TM_PUBLIC_SUPABASE_URL }}
 97 |           TM_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.TM_PUBLIC_SUPABASE_ANON_KEY }}
 98 | 
 99 |       - name: Upload build artifacts
100 |         uses: actions/upload-artifact@v4
101 |         with:
102 |           name: build-artifacts
103 |           path: dist/
104 |           retention-days: 1
105 | 
106 |   test:
107 |     name: Test
108 |     timeout-minutes: 15
109 |     runs-on: ubuntu-latest
110 |     needs: [format-check, typecheck, build]
111 |     steps:
112 |       - uses: actions/checkout@v4
113 |         with:
114 |           fetch-depth: 2
115 | 
116 |       - uses: actions/setup-node@v4
117 |         with:
118 |           node-version: 20
119 |           cache: "npm"
120 | 
121 |       - name: Install dependencies
122 |         run: npm install --frozen-lockfile --prefer-offline
123 |         timeout-minutes: 5
124 | 
125 |       - name: Download build artifacts
126 |         uses: actions/download-artifact@v4
127 |         with:
128 |           name: build-artifacts
129 |           path: dist/
130 | 
131 |       - name: Run Tests
132 |         run: |
133 |           npm run test:coverage -- --coverageThreshold '{"global":{"branches":0,"functions":0,"lines":0,"statements":0}}' --detectOpenHandles --forceExit
134 |         env:
135 |           NODE_ENV: test
136 |           CI: true
137 |           FORCE_COLOR: 1
138 | 
139 |       - name: Upload Test Results
140 |         if: always()
141 |         uses: actions/upload-artifact@v4
142 |         with:
143 |           name: test-results
144 |           path: |
145 |             test-results
146 |             coverage
147 |             junit.xml
148 |           retention-days: 30
149 | 
```

--------------------------------------------------------------------------------
/apps/extension/src/components/TaskDetails/useTaskDetails.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { useMemo } from 'react';
  2 | import { useTaskDetails as useTaskDetailsQuery } from '../../webview/hooks/useTaskQueries';
  3 | import type { TaskMasterTask } from '../../webview/types';
  4 | 
  5 | interface TaskFileData {
  6 | 	details?: string;
  7 | 	testStrategy?: string;
  8 | }
  9 | 
 10 | interface UseTaskDetailsProps {
 11 | 	taskId: string;
 12 | 	sendMessage: (message: any) => Promise<any>;
 13 | 	tasks: TaskMasterTask[];
 14 | }
 15 | 
 16 | export const useTaskDetails = ({
 17 | 	taskId,
 18 | 	sendMessage,
 19 | 	tasks
 20 | }: UseTaskDetailsProps) => {
 21 | 	// Parse task ID to determine if it's a subtask (e.g., "13.2")
 22 | 	const { isSubtask, parentId, subtaskIndex, taskIdForFetch } = useMemo(() => {
 23 | 		// Ensure taskId is a string
 24 | 		const taskIdStr = String(taskId);
 25 | 		const parts = taskIdStr.split('.');
 26 | 		if (parts.length === 2) {
 27 | 			return {
 28 | 				isSubtask: true,
 29 | 				parentId: parts[0],
 30 | 				subtaskIndex: parseInt(parts[1]) - 1, // Convert to 0-based index
 31 | 				taskIdForFetch: parts[0] // Always fetch parent task for subtasks
 32 | 			};
 33 | 		}
 34 | 		return {
 35 | 			isSubtask: false,
 36 | 			parentId: taskIdStr,
 37 | 			subtaskIndex: -1,
 38 | 			taskIdForFetch: taskIdStr
 39 | 		};
 40 | 	}, [taskId]);
 41 | 
 42 | 	// Use React Query to fetch full task details
 43 | 	const { data: fullTaskData, error: taskDetailsError } =
 44 | 		useTaskDetailsQuery(taskIdForFetch);
 45 | 
 46 | 	// Find current task from local state for immediate display
 47 | 	const { currentTask, parentTask } = useMemo(() => {
 48 | 		if (isSubtask) {
 49 | 			const parent = tasks.find((t) => t.id === parentId);
 50 | 			if (parent && parent.subtasks && parent.subtasks[subtaskIndex]) {
 51 | 				const subtask = parent.subtasks[subtaskIndex];
 52 | 				return { currentTask: subtask, parentTask: parent };
 53 | 			}
 54 | 		} else {
 55 | 			const task = tasks.find((t) => t.id === String(taskId));
 56 | 			if (task) {
 57 | 				return { currentTask: task, parentTask: null };
 58 | 			}
 59 | 		}
 60 | 		return { currentTask: null, parentTask: null };
 61 | 	}, [taskId, tasks, isSubtask, parentId, subtaskIndex]);
 62 | 
 63 | 	// Merge full task data from React Query with local state
 64 | 	const mergedCurrentTask = useMemo(() => {
 65 | 		if (!currentTask || !fullTaskData) return currentTask;
 66 | 
 67 | 		if (isSubtask && fullTaskData.subtasks) {
 68 | 			// Find the specific subtask in the full data
 69 | 			const subtaskData = fullTaskData.subtasks.find(
 70 | 				(st: any) =>
 71 | 					st.id === currentTask.id || st.id === parseInt(currentTask.id as any)
 72 | 			);
 73 | 			if (subtaskData) {
 74 | 				return { ...currentTask, ...subtaskData };
 75 | 			}
 76 | 		} else if (!isSubtask) {
 77 | 			// Merge parent task data
 78 | 			return { ...currentTask, ...fullTaskData };
 79 | 		}
 80 | 
 81 | 		return currentTask;
 82 | 	}, [currentTask, fullTaskData, isSubtask]);
 83 | 
 84 | 	// Extract task file data
 85 | 	const taskFileData: TaskFileData = useMemo(() => {
 86 | 		if (!mergedCurrentTask) return {};
 87 | 		return {
 88 | 			details: mergedCurrentTask.details || '',
 89 | 			testStrategy: mergedCurrentTask.testStrategy || ''
 90 | 		};
 91 | 	}, [mergedCurrentTask]);
 92 | 
 93 | 	// Get complexity score
 94 | 	const complexity = useMemo(() => {
 95 | 		if (mergedCurrentTask?.complexityScore !== undefined) {
 96 | 			return { score: mergedCurrentTask.complexityScore };
 97 | 		}
 98 | 		return null;
 99 | 	}, [mergedCurrentTask]);
100 | 
101 | 	// Function to refresh data after AI operations
102 | 	const refreshComplexityAfterAI = () => {
103 | 		// React Query will automatically refetch when mutations invalidate the query
104 | 		// No need for manual refresh
105 | 	};
106 | 
107 | 	return {
108 | 		currentTask: mergedCurrentTask,
109 | 		parentTask,
110 | 		isSubtask,
111 | 		taskFileData,
112 | 		taskFileDataError: taskDetailsError ? 'Failed to load task details' : null,
113 | 		complexity,
114 | 		refreshComplexityAfterAI
115 | 	};
116 | };
117 | 
```

--------------------------------------------------------------------------------
/tests/unit/kebab-case-validation.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * Kebab case validation tests
  3 |  */
  4 | 
  5 | import { jest } from '@jest/globals';
  6 | import { toKebabCase } from '../../scripts/modules/utils.js';
  7 | 
  8 | // Create a test implementation of detectCamelCaseFlags
  9 | function testDetectCamelCaseFlags(args) {
 10 | 	const camelCaseFlags = [];
 11 | 	for (const arg of args) {
 12 | 		if (arg.startsWith('--')) {
 13 | 			const flagName = arg.split('=')[0].slice(2); // Remove -- and anything after =
 14 | 
 15 | 			// Skip single-word flags - they can't be camelCase
 16 | 			if (!flagName.includes('-') && !/[A-Z]/.test(flagName)) {
 17 | 				continue;
 18 | 			}
 19 | 
 20 | 			// Check for camelCase pattern (lowercase followed by uppercase)
 21 | 			if (/[a-z][A-Z]/.test(flagName)) {
 22 | 				const kebabVersion = toKebabCase(flagName);
 23 | 				if (kebabVersion !== flagName) {
 24 | 					camelCaseFlags.push({
 25 | 						original: flagName,
 26 | 						kebabCase: kebabVersion
 27 | 					});
 28 | 				}
 29 | 			}
 30 | 		}
 31 | 	}
 32 | 	return camelCaseFlags;
 33 | }
 34 | 
 35 | describe('Kebab Case Validation', () => {
 36 | 	describe('toKebabCase', () => {
 37 | 		test('should convert camelCase to kebab-case', () => {
 38 | 			expect(toKebabCase('promptText')).toBe('prompt-text');
 39 | 			expect(toKebabCase('userID')).toBe('user-id');
 40 | 			expect(toKebabCase('numTasks')).toBe('num-tasks');
 41 | 		});
 42 | 
 43 | 		test('should handle already kebab-case strings', () => {
 44 | 			expect(toKebabCase('already-kebab-case')).toBe('already-kebab-case');
 45 | 			expect(toKebabCase('kebab-case')).toBe('kebab-case');
 46 | 		});
 47 | 
 48 | 		test('should handle single words', () => {
 49 | 			expect(toKebabCase('single')).toBe('single');
 50 | 			expect(toKebabCase('file')).toBe('file');
 51 | 		});
 52 | 	});
 53 | 
 54 | 	describe('detectCamelCaseFlags', () => {
 55 | 		test('should properly detect camelCase flags', () => {
 56 | 			const args = [
 57 | 				'node',
 58 | 				'task-master',
 59 | 				'add-task',
 60 | 				'--promptText=test',
 61 | 				'--userID=123'
 62 | 			];
 63 | 			const flags = testDetectCamelCaseFlags(args);
 64 | 
 65 | 			expect(flags).toHaveLength(2);
 66 | 			expect(flags).toContainEqual({
 67 | 				original: 'promptText',
 68 | 				kebabCase: 'prompt-text'
 69 | 			});
 70 | 			expect(flags).toContainEqual({
 71 | 				original: 'userID',
 72 | 				kebabCase: 'user-id'
 73 | 			});
 74 | 		});
 75 | 
 76 | 		test('should not flag kebab-case or lowercase flags', () => {
 77 | 			const args = [
 78 | 				'node',
 79 | 				'task-master',
 80 | 				'add-task',
 81 | 				'--prompt=test',
 82 | 				'--user-id=123'
 83 | 			];
 84 | 			const flags = testDetectCamelCaseFlags(args);
 85 | 
 86 | 			expect(flags).toHaveLength(0);
 87 | 		});
 88 | 
 89 | 		test('should not flag any single-word flags regardless of case', () => {
 90 | 			const args = [
 91 | 				'node',
 92 | 				'task-master',
 93 | 				'add-task',
 94 | 				'--prompt=test', // lowercase
 95 | 				'--PROMPT=test', // uppercase
 96 | 				'--Prompt=test', // mixed case
 97 | 				'--file=test', // lowercase
 98 | 				'--FILE=test', // uppercase
 99 | 				'--File=test' // mixed case
100 | 			];
101 | 			const flags = testDetectCamelCaseFlags(args);
102 | 
103 | 			expect(flags).toHaveLength(0);
104 | 		});
105 | 
106 | 		test('should handle mixed case flags correctly', () => {
107 | 			const args = [
108 | 				'node',
109 | 				'task-master',
110 | 				'add-task',
111 | 				'--prompt=test', // single word, should pass
112 | 				'--promptText=test', // camelCase, should flag
113 | 				'--prompt-text=test', // kebab-case, should pass
114 | 				'--ID=123', // single word, should pass
115 | 				'--userId=123', // camelCase, should flag
116 | 				'--user-id=123' // kebab-case, should pass
117 | 			];
118 | 
119 | 			const flags = testDetectCamelCaseFlags(args);
120 | 
121 | 			expect(flags).toHaveLength(2);
122 | 			expect(flags).toContainEqual({
123 | 				original: 'promptText',
124 | 				kebabCase: 'prompt-text'
125 | 			});
126 | 			expect(flags).toContainEqual({
127 | 				original: 'userId',
128 | 				kebabCase: 'user-id'
129 | 			});
130 | 		});
131 | 	});
132 | });
133 | 
```

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

```javascript
  1 | /**
  2 |  * Tests for the find-next-task.js module
  3 |  */
  4 | import { jest } from '@jest/globals';
  5 | import findNextTask from '../../../../../scripts/modules/task-manager/find-next-task.js';
  6 | 
  7 | describe('findNextTask', () => {
  8 | 	test('should return the highest priority task with all dependencies satisfied', () => {
  9 | 		const tasks = [
 10 | 			{
 11 | 				id: 1,
 12 | 				title: 'Setup Project',
 13 | 				status: 'done',
 14 | 				dependencies: [],
 15 | 				priority: 'high'
 16 | 			},
 17 | 			{
 18 | 				id: 2,
 19 | 				title: 'Implement Core Features',
 20 | 				status: 'pending',
 21 | 				dependencies: [1],
 22 | 				priority: 'high'
 23 | 			},
 24 | 			{
 25 | 				id: 3,
 26 | 				title: 'Create Documentation',
 27 | 				status: 'pending',
 28 | 				dependencies: [1],
 29 | 				priority: 'medium'
 30 | 			},
 31 | 			{
 32 | 				id: 4,
 33 | 				title: 'Deploy Application',
 34 | 				status: 'pending',
 35 | 				dependencies: [2, 3],
 36 | 				priority: 'high'
 37 | 			}
 38 | 		];
 39 | 
 40 | 		const nextTask = findNextTask(tasks);
 41 | 
 42 | 		expect(nextTask).toBeDefined();
 43 | 		expect(nextTask.id).toBe(2);
 44 | 		expect(nextTask.title).toBe('Implement Core Features');
 45 | 	});
 46 | 
 47 | 	test('should prioritize by priority level when dependencies are equal', () => {
 48 | 		const tasks = [
 49 | 			{
 50 | 				id: 1,
 51 | 				title: 'Setup Project',
 52 | 				status: 'done',
 53 | 				dependencies: [],
 54 | 				priority: 'high'
 55 | 			},
 56 | 			{
 57 | 				id: 2,
 58 | 				title: 'Low Priority Task',
 59 | 				status: 'pending',
 60 | 				dependencies: [1],
 61 | 				priority: 'low'
 62 | 			},
 63 | 			{
 64 | 				id: 3,
 65 | 				title: 'Medium Priority Task',
 66 | 				status: 'pending',
 67 | 				dependencies: [1],
 68 | 				priority: 'medium'
 69 | 			},
 70 | 			{
 71 | 				id: 4,
 72 | 				title: 'High Priority Task',
 73 | 				status: 'pending',
 74 | 				dependencies: [1],
 75 | 				priority: 'high'
 76 | 			}
 77 | 		];
 78 | 
 79 | 		const nextTask = findNextTask(tasks);
 80 | 
 81 | 		expect(nextTask.id).toBe(4);
 82 | 		expect(nextTask.priority).toBe('high');
 83 | 	});
 84 | 
 85 | 	test('should return null when all tasks are completed', () => {
 86 | 		const tasks = [
 87 | 			{
 88 | 				id: 1,
 89 | 				title: 'Setup Project',
 90 | 				status: 'done',
 91 | 				dependencies: [],
 92 | 				priority: 'high'
 93 | 			},
 94 | 			{
 95 | 				id: 2,
 96 | 				title: 'Implement Features',
 97 | 				status: 'done',
 98 | 				dependencies: [1],
 99 | 				priority: 'high'
100 | 			}
101 | 		];
102 | 
103 | 		const nextTask = findNextTask(tasks);
104 | 
105 | 		expect(nextTask).toBeNull();
106 | 	});
107 | 
108 | 	test('should return null when all pending tasks have unsatisfied dependencies', () => {
109 | 		const tasks = [
110 | 			{
111 | 				id: 1,
112 | 				title: 'Setup Project',
113 | 				status: 'pending',
114 | 				dependencies: [2],
115 | 				priority: 'high'
116 | 			},
117 | 			{
118 | 				id: 2,
119 | 				title: 'Implement Features',
120 | 				status: 'pending',
121 | 				dependencies: [1],
122 | 				priority: 'high'
123 | 			}
124 | 		];
125 | 
126 | 		const nextTask = findNextTask(tasks);
127 | 
128 | 		expect(nextTask).toBeNull();
129 | 	});
130 | 
131 | 	test('should handle empty tasks array', () => {
132 | 		const nextTask = findNextTask([]);
133 | 
134 | 		expect(nextTask).toBeNull();
135 | 	});
136 | 
137 | 	test('should consider subtask dependencies when finding next task', () => {
138 | 		const tasks = [
139 | 			{
140 | 				id: 1,
141 | 				title: 'Parent Task',
142 | 				status: 'in-progress',
143 | 				dependencies: [],
144 | 				priority: 'high',
145 | 				subtasks: [
146 | 					{
147 | 						id: 1,
148 | 						title: 'Subtask 1',
149 | 						status: 'done',
150 | 						dependencies: []
151 | 					},
152 | 					{
153 | 						id: 2,
154 | 						title: 'Subtask 2',
155 | 						status: 'pending',
156 | 						dependencies: []
157 | 					}
158 | 				]
159 | 			},
160 | 			{
161 | 				id: 2,
162 | 				title: 'Dependent Task',
163 | 				status: 'pending',
164 | 				dependencies: [1],
165 | 				priority: 'high'
166 | 			}
167 | 		];
168 | 
169 | 		const nextTask = findNextTask(tasks);
170 | 
171 | 		// Task 2 should not be returned because Task 1 is not completely done
172 | 		// (it has a pending subtask)
173 | 		expect(nextTask).not.toEqual(expect.objectContaining({ id: 2 }));
174 | 	});
175 | });
176 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/scope-up.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * scope-up.js
  3 |  * Direct function implementation for scoping up task complexity
  4 |  */
  5 | 
  6 | import { scopeUpTask } from '../../../../scripts/modules/task-manager.js';
  7 | import {
  8 | 	enableSilentMode,
  9 | 	disableSilentMode
 10 | } from '../../../../scripts/modules/utils.js';
 11 | import { createLogWrapper } from '../../tools/utils.js';
 12 | 
 13 | /**
 14 |  * Direct function wrapper for scoping up task complexity with error handling.
 15 |  *
 16 |  * @param {Object} args - Command arguments
 17 |  * @param {string} args.id - Comma-separated list of task IDs to scope up
 18 |  * @param {string} [args.strength='regular'] - Strength level (light, regular, heavy)
 19 |  * @param {string} [args.prompt] - Custom prompt for scoping adjustments
 20 |  * @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
 21 |  * @param {boolean} [args.research=false] - Whether to use research capabilities for scoping
 22 |  * @param {string} args.projectRoot - Project root path
 23 |  * @param {string} [args.tag] - Tag for the task context (optional)
 24 |  * @param {Object} log - Logger object
 25 |  * @param {Object} context - Additional context (session)
 26 |  * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 27 |  */
 28 | export async function scopeUpDirect(args, log, context = {}) {
 29 | 	// Destructure expected args
 30 | 	const {
 31 | 		tasksJsonPath,
 32 | 		id,
 33 | 		strength = 'regular',
 34 | 		prompt: customPrompt,
 35 | 		research = false,
 36 | 		projectRoot,
 37 | 		tag
 38 | 	} = args;
 39 | 	const { session } = context; // Destructure session from context
 40 | 
 41 | 	// Enable silent mode to prevent console logs from interfering with JSON response
 42 | 	enableSilentMode();
 43 | 
 44 | 	// Create logger wrapper using the utility
 45 | 	const mcpLog = createLogWrapper(log);
 46 | 
 47 | 	try {
 48 | 		// Check if tasksJsonPath was provided
 49 | 		if (!tasksJsonPath) {
 50 | 			log.error('scopeUpDirect called without tasksJsonPath');
 51 | 			disableSilentMode(); // Disable before returning
 52 | 			return {
 53 | 				success: false,
 54 | 				error: {
 55 | 					code: 'MISSING_ARGUMENT',
 56 | 					message: 'tasksJsonPath is required'
 57 | 				}
 58 | 			};
 59 | 		}
 60 | 
 61 | 		// Check required parameters
 62 | 		if (!id) {
 63 | 			log.error('Missing required parameter: id');
 64 | 			disableSilentMode();
 65 | 			return {
 66 | 				success: false,
 67 | 				error: {
 68 | 					code: 'MISSING_PARAMETER',
 69 | 					message: 'The id parameter is required for scoping up tasks'
 70 | 				}
 71 | 			};
 72 | 		}
 73 | 
 74 | 		// Parse task IDs - convert to numbers as expected by scopeUpTask
 75 | 		const taskIds = id.split(',').map((taskId) => parseInt(taskId.trim(), 10));
 76 | 
 77 | 		log.info(
 78 | 			`Scoping up tasks: ${taskIds.join(', ')}, strength: ${strength}, research: ${research}`
 79 | 		);
 80 | 
 81 | 		// Call the scopeUpTask function
 82 | 		const result = await scopeUpTask(
 83 | 			tasksJsonPath,
 84 | 			taskIds,
 85 | 			strength,
 86 | 			customPrompt,
 87 | 			{
 88 | 				session,
 89 | 				mcpLog,
 90 | 				projectRoot,
 91 | 				commandName: 'scope-up',
 92 | 				outputType: 'mcp',
 93 | 				tag,
 94 | 				research
 95 | 			},
 96 | 			'json' // outputFormat
 97 | 		);
 98 | 
 99 | 		// Restore normal logging
100 | 		disableSilentMode();
101 | 
102 | 		return {
103 | 			success: true,
104 | 			data: {
105 | 				updatedTasks: result.updatedTasks,
106 | 				tasksUpdated: result.updatedTasks.length,
107 | 				message: `Successfully scoped up ${result.updatedTasks.length} task(s)`,
108 | 				telemetryData: result.telemetryData
109 | 			}
110 | 		};
111 | 	} catch (error) {
112 | 		// Make sure to restore normal logging even if there's an error
113 | 		disableSilentMode();
114 | 
115 | 		log.error(`Error in scopeUpDirect: ${error.message}`);
116 | 		return {
117 | 			success: false,
118 | 			error: {
119 | 				code: error.code || 'SCOPE_UP_ERROR',
120 | 				message: error.message
121 | 			}
122 | 		};
123 | 	}
124 | }
125 | 
```

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

```javascript
 1 | /**
 2 |  * @fileoverview Type definitions for Claude Code AI SDK provider
 3 |  * These JSDoc types mirror the TypeScript interfaces from the original provider
 4 |  */
 5 | 
 6 | /**
 7 |  * Claude Code provider settings
 8 |  * @typedef {Object} ClaudeCodeSettings
 9 |  * @property {string} [pathToClaudeCodeExecutable='claude'] - Custom path to Claude Code CLI executable
10 |  * @property {string} [customSystemPrompt] - Custom system prompt to use
11 |  * @property {string} [appendSystemPrompt] - Append additional content to the system prompt
12 |  * @property {number} [maxTurns] - Maximum number of turns for the conversation
13 |  * @property {number} [maxThinkingTokens] - Maximum thinking tokens for the model
14 |  * @property {string} [cwd] - Working directory for CLI operations
15 |  * @property {'bun'|'deno'|'node'} [executable='node'] - JavaScript runtime to use
16 |  * @property {string[]} [executableArgs] - Additional arguments for the JavaScript runtime
17 |  * @property {'default'|'acceptEdits'|'bypassPermissions'|'plan'} [permissionMode='default'] - Permission mode for tool usage
18 |  * @property {string} [permissionPromptToolName] - Custom tool name for permission prompts
19 |  * @property {boolean} [continue] - Continue the most recent conversation
20 |  * @property {string} [resume] - Resume a specific session by ID
21 |  * @property {string[]} [allowedTools] - Tools to explicitly allow during execution (e.g., ['Read', 'LS', 'Bash(git log:*)'])
22 |  * @property {string[]} [disallowedTools] - Tools to disallow during execution (e.g., ['Write', 'Edit', 'Bash(rm:*)'])
23 |  * @property {Object.<string, MCPServerConfig>} [mcpServers] - MCP server configuration
24 |  * @property {boolean} [verbose] - Enable verbose logging for debugging
25 |  */
26 | 
27 | /**
28 |  * MCP Server configuration
29 |  * @typedef {Object} MCPServerConfig
30 |  * @property {'stdio'|'sse'} [type='stdio'] - Server type
31 |  * @property {string} command - Command to execute (for stdio type)
32 |  * @property {string[]} [args] - Arguments for the command
33 |  * @property {Object.<string, string>} [env] - Environment variables
34 |  * @property {string} url - URL for SSE type servers
35 |  * @property {Object.<string, string>} [headers] - Headers for SSE type servers
36 |  */
37 | 
38 | /**
39 |  * Model ID type - either 'opus', 'sonnet', or any string
40 |  * @typedef {'opus'|'sonnet'|string} ClaudeCodeModelId
41 |  */
42 | 
43 | /**
44 |  * Language model options
45 |  * @typedef {Object} ClaudeCodeLanguageModelOptions
46 |  * @property {ClaudeCodeModelId} id - The model ID
47 |  * @property {ClaudeCodeSettings} [settings] - Optional settings
48 |  */
49 | 
50 | /**
51 |  * Error metadata for Claude Code errors
52 |  * @typedef {Object} ClaudeCodeErrorMetadata
53 |  * @property {string} [code] - Error code
54 |  * @property {number} [exitCode] - Process exit code
55 |  * @property {string} [stderr] - Standard error output
56 |  * @property {string} [promptExcerpt] - Excerpt of the prompt that caused the error
57 |  */
58 | 
59 | /**
60 |  * Claude Code provider interface
61 |  * @typedef {Object} ClaudeCodeProvider
62 |  * @property {function(ClaudeCodeModelId, ClaudeCodeSettings=): Object} languageModel - Create a language model
63 |  * @property {function(ClaudeCodeModelId, ClaudeCodeSettings=): Object} chat - Alias for languageModel
64 |  * @property {function(string): never} textEmbeddingModel - Throws NoSuchModelError (not supported)
65 |  */
66 | 
67 | /**
68 |  * Claude Code provider settings
69 |  * @typedef {Object} ClaudeCodeProviderSettings
70 |  * @property {ClaudeCodeSettings} [defaultSettings] - Default settings to use for all models
71 |  */
72 | 
73 | export {}; // This ensures the file is treated as a module
74 | 
```

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | import fs from 'fs';
  3 | import path from 'path';
  4 | import os from 'os';
  5 | 
  6 | describe('OpenCode Profile Integration', () => {
  7 | 	let tempDir;
  8 | 
  9 | 	beforeEach(() => {
 10 | 		jest.clearAllMocks();
 11 | 
 12 | 		// Create a temporary directory for testing
 13 | 		tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-master-test-'));
 14 | 
 15 | 		// Spy on fs methods
 16 | 		jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
 17 | 		jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
 18 | 			if (filePath.toString().includes('AGENTS.md')) {
 19 | 				return 'Sample AGENTS.md content for OpenCode integration';
 20 | 			}
 21 | 			if (filePath.toString().includes('opencode.json')) {
 22 | 				return JSON.stringify({ mcpServers: {} }, null, 2);
 23 | 			}
 24 | 			return '{}';
 25 | 		});
 26 | 		jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
 27 | 		jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
 28 | 	});
 29 | 
 30 | 	afterEach(() => {
 31 | 		// Clean up the temporary directory
 32 | 		try {
 33 | 			fs.rmSync(tempDir, { recursive: true, force: true });
 34 | 		} catch (err) {
 35 | 			console.error(`Error cleaning up: ${err.message}`);
 36 | 		}
 37 | 	});
 38 | 
 39 | 	// Test function that simulates the OpenCode profile file copying behavior
 40 | 	function mockCreateOpenCodeStructure() {
 41 | 		// OpenCode profile copies AGENTS.md to AGENTS.md in project root (same name)
 42 | 		const sourceContent = 'Sample AGENTS.md content for OpenCode integration';
 43 | 		fs.writeFileSync(path.join(tempDir, 'AGENTS.md'), sourceContent);
 44 | 
 45 | 		// OpenCode profile creates opencode.json config file
 46 | 		const configContent = JSON.stringify({ mcpServers: {} }, null, 2);
 47 | 		fs.writeFileSync(path.join(tempDir, 'opencode.json'), configContent);
 48 | 	}
 49 | 
 50 | 	test('creates AGENTS.md file in project root', () => {
 51 | 		// Act
 52 | 		mockCreateOpenCodeStructure();
 53 | 
 54 | 		// Assert
 55 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
 56 | 			path.join(tempDir, 'AGENTS.md'),
 57 | 			'Sample AGENTS.md content for OpenCode integration'
 58 | 		);
 59 | 	});
 60 | 
 61 | 	test('creates opencode.json config file in project root', () => {
 62 | 		// Act
 63 | 		mockCreateOpenCodeStructure();
 64 | 
 65 | 		// Assert
 66 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
 67 | 			path.join(tempDir, 'opencode.json'),
 68 | 			JSON.stringify({ mcpServers: {} }, null, 2)
 69 | 		);
 70 | 	});
 71 | 
 72 | 	test('does not create any profile directories', () => {
 73 | 		// Act
 74 | 		mockCreateOpenCodeStructure();
 75 | 
 76 | 		// Assert - OpenCode profile should not create any directories
 77 | 		// Only the temp directory creation calls should exist
 78 | 		const mkdirCalls = fs.mkdirSync.mock.calls.filter(
 79 | 			(call) => !call[0].includes('task-master-test-')
 80 | 		);
 81 | 		expect(mkdirCalls).toHaveLength(0);
 82 | 	});
 83 | 
 84 | 	test('handles transformation of MCP config format', () => {
 85 | 		// This test simulates the transformation behavior that would happen in onPostConvert
 86 | 		const standardMcpConfig = {
 87 | 			mcpServers: {
 88 | 				'taskmaster-ai': {
 89 | 					command: 'node',
 90 | 					args: ['path/to/server.js'],
 91 | 					env: {
 92 | 						API_KEY: 'test-key'
 93 | 					}
 94 | 				}
 95 | 			}
 96 | 		};
 97 | 
 98 | 		const expectedOpenCodeConfig = {
 99 | 			$schema: 'https://opencode.ai/config.json',
100 | 			mcp: {
101 | 				'taskmaster-ai': {
102 | 					type: 'local',
103 | 					command: ['node', 'path/to/server.js'],
104 | 					enabled: true,
105 | 					environment: {
106 | 						API_KEY: 'test-key'
107 | 					}
108 | 				}
109 | 			}
110 | 		};
111 | 
112 | 		// Mock the transformation behavior
113 | 		fs.writeFileSync(
114 | 			path.join(tempDir, 'opencode.json'),
115 | 			JSON.stringify(expectedOpenCodeConfig, null, 2)
116 | 		);
117 | 
118 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
119 | 			path.join(tempDir, 'opencode.json'),
120 | 			JSON.stringify(expectedOpenCodeConfig, null, 2)
121 | 		);
122 | 	});
123 | });
124 | 
```

--------------------------------------------------------------------------------
/apps/cli/src/ui/components/next-task.component.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * @fileoverview Next task recommendation component
  3 |  * Displays detailed information about the recommended next task
  4 |  */
  5 | 
  6 | import chalk from 'chalk';
  7 | import boxen from 'boxen';
  8 | import type { Task } from '@tm/core/types';
  9 | 
 10 | /**
 11 |  * Next task display options
 12 |  */
 13 | export interface NextTaskDisplayOptions {
 14 | 	id: string | number;
 15 | 	title: string;
 16 | 	priority?: string;
 17 | 	status?: string;
 18 | 	dependencies?: (string | number)[];
 19 | 	description?: string;
 20 | }
 21 | 
 22 | /**
 23 |  * Display the recommended next task section
 24 |  */
 25 | export function displayRecommendedNextTask(
 26 | 	task: NextTaskDisplayOptions | undefined
 27 | ): void {
 28 | 	if (!task) {
 29 | 		// If no task available, show a message
 30 | 		console.log(
 31 | 			boxen(
 32 | 				chalk.yellow(
 33 | 					'No tasks available to work on. All tasks are either completed, blocked by dependencies, or in progress.'
 34 | 				),
 35 | 				{
 36 | 					padding: 1,
 37 | 					borderStyle: 'round',
 38 | 					borderColor: 'yellow',
 39 | 					title: '⚠ NO TASKS AVAILABLE ⚠',
 40 | 					titleAlignment: 'center'
 41 | 				}
 42 | 			)
 43 | 		);
 44 | 		return;
 45 | 	}
 46 | 
 47 | 	// Build the content for the next task box
 48 | 	const content = [];
 49 | 
 50 | 	// Task header with ID and title
 51 | 	content.push(
 52 | 		`🔥 ${chalk.hex('#FF8800').bold('Next Task to Work On:')} ${chalk.yellow(`#${task.id}`)}${chalk.hex('#FF8800').bold(` - ${task.title}`)}`
 53 | 	);
 54 | 	content.push('');
 55 | 
 56 | 	// Priority and Status line
 57 | 	const statusLine = [];
 58 | 	if (task.priority) {
 59 | 		const priorityColor =
 60 | 			task.priority === 'high'
 61 | 				? chalk.red
 62 | 				: task.priority === 'medium'
 63 | 					? chalk.yellow
 64 | 					: chalk.gray;
 65 | 		statusLine.push(`Priority: ${priorityColor.bold(task.priority)}`);
 66 | 	}
 67 | 	if (task.status) {
 68 | 		const statusDisplay =
 69 | 			task.status === 'pending'
 70 | 				? chalk.yellow('○ pending')
 71 | 				: task.status === 'in-progress'
 72 | 					? chalk.blue('▶ in-progress')
 73 | 					: chalk.gray(task.status);
 74 | 		statusLine.push(`Status: ${statusDisplay}`);
 75 | 	}
 76 | 	content.push(statusLine.join('  '));
 77 | 
 78 | 	// Dependencies
 79 | 	const depsDisplay =
 80 | 		!task.dependencies || task.dependencies.length === 0
 81 | 			? chalk.gray('None')
 82 | 			: chalk.cyan(task.dependencies.join(', '));
 83 | 	content.push(`Dependencies: ${depsDisplay}`);
 84 | 
 85 | 	// Description if available
 86 | 	if (task.description) {
 87 | 		content.push('');
 88 | 		content.push(`Description: ${chalk.white(task.description)}`);
 89 | 	}
 90 | 
 91 | 	// Action commands
 92 | 	content.push('');
 93 | 	content.push(
 94 | 		`${chalk.cyan('Start working:')} ${chalk.yellow(`task-master set-status --id=${task.id} --status=in-progress`)}`
 95 | 	);
 96 | 	content.push(
 97 | 		`${chalk.cyan('View details:')} ${chalk.yellow(`task-master show ${task.id}`)}`
 98 | 	);
 99 | 
100 | 	// Display in a styled box with orange border
101 | 	console.log(
102 | 		boxen(content.join('\n'), {
103 | 			padding: 1,
104 | 			margin: { top: 1, bottom: 1 },
105 | 			borderStyle: 'round',
106 | 			borderColor: '#FFA500', // Orange color
107 | 			title: chalk.hex('#FFA500')('⚡ RECOMMENDED NEXT TASK ⚡'),
108 | 			titleAlignment: 'center',
109 | 			width: process.stdout.columns * 0.97,
110 | 			fullscreen: false
111 | 		})
112 | 	);
113 | }
114 | 
115 | /**
116 |  * Get task description from the full task object
117 |  */
118 | export function getTaskDescription(task: Task): string | undefined {
119 | 	// Try to get description from the task
120 | 	// This could be from task.description or the first line of task.details
121 | 	if ('description' in task && task.description) {
122 | 		return task.description as string;
123 | 	}
124 | 
125 | 	if ('details' in task && task.details) {
126 | 		// Take first sentence or line from details
127 | 		const details = task.details as string;
128 | 		const firstLine = details.split('\n')[0];
129 | 		const firstSentence = firstLine.split('.')[0];
130 | 		return firstSentence;
131 | 	}
132 | 
133 | 	return undefined;
134 | }
135 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/scope-down.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * scope-down.js
  3 |  * Direct function implementation for scoping down task complexity
  4 |  */
  5 | 
  6 | import { scopeDownTask } from '../../../../scripts/modules/task-manager.js';
  7 | import {
  8 | 	enableSilentMode,
  9 | 	disableSilentMode
 10 | } from '../../../../scripts/modules/utils.js';
 11 | import { createLogWrapper } from '../../tools/utils.js';
 12 | 
 13 | /**
 14 |  * Direct function wrapper for scoping down task complexity with error handling.
 15 |  *
 16 |  * @param {Object} args - Command arguments
 17 |  * @param {string} args.id - Comma-separated list of task IDs to scope down
 18 |  * @param {string} [args.strength='regular'] - Strength level (light, regular, heavy)
 19 |  * @param {string} [args.prompt] - Custom prompt for scoping adjustments
 20 |  * @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
 21 |  * @param {boolean} [args.research=false] - Whether to use research capabilities for scoping
 22 |  * @param {string} args.projectRoot - Project root path
 23 |  * @param {string} [args.tag] - Tag for the task context (optional)
 24 |  * @param {Object} log - Logger object
 25 |  * @param {Object} context - Additional context (session)
 26 |  * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 27 |  */
 28 | export async function scopeDownDirect(args, log, context = {}) {
 29 | 	// Destructure expected args
 30 | 	const {
 31 | 		tasksJsonPath,
 32 | 		id,
 33 | 		strength = 'regular',
 34 | 		prompt: customPrompt,
 35 | 		research = false,
 36 | 		projectRoot,
 37 | 		tag
 38 | 	} = args;
 39 | 	const { session } = context; // Destructure session from context
 40 | 
 41 | 	// Enable silent mode to prevent console logs from interfering with JSON response
 42 | 	enableSilentMode();
 43 | 
 44 | 	// Create logger wrapper using the utility
 45 | 	const mcpLog = createLogWrapper(log);
 46 | 
 47 | 	try {
 48 | 		// Check if tasksJsonPath was provided
 49 | 		if (!tasksJsonPath) {
 50 | 			log.error('scopeDownDirect called without tasksJsonPath');
 51 | 			disableSilentMode(); // Disable before returning
 52 | 			return {
 53 | 				success: false,
 54 | 				error: {
 55 | 					code: 'MISSING_ARGUMENT',
 56 | 					message: 'tasksJsonPath is required'
 57 | 				}
 58 | 			};
 59 | 		}
 60 | 
 61 | 		// Check required parameters
 62 | 		if (!id) {
 63 | 			log.error('Missing required parameter: id');
 64 | 			disableSilentMode();
 65 | 			return {
 66 | 				success: false,
 67 | 				error: {
 68 | 					code: 'MISSING_PARAMETER',
 69 | 					message: 'The id parameter is required for scoping down tasks'
 70 | 				}
 71 | 			};
 72 | 		}
 73 | 
 74 | 		// Parse task IDs - convert to numbers as expected by scopeDownTask
 75 | 		const taskIds = id.split(',').map((taskId) => parseInt(taskId.trim(), 10));
 76 | 
 77 | 		log.info(
 78 | 			`Scoping down tasks: ${taskIds.join(', ')}, strength: ${strength}, research: ${research}`
 79 | 		);
 80 | 
 81 | 		// Call the scopeDownTask function
 82 | 		const result = await scopeDownTask(
 83 | 			tasksJsonPath,
 84 | 			taskIds,
 85 | 			strength,
 86 | 			customPrompt,
 87 | 			{
 88 | 				session,
 89 | 				mcpLog,
 90 | 				projectRoot,
 91 | 				commandName: 'scope-down',
 92 | 				outputType: 'mcp',
 93 | 				tag,
 94 | 				research
 95 | 			},
 96 | 			'json' // outputFormat
 97 | 		);
 98 | 
 99 | 		// Restore normal logging
100 | 		disableSilentMode();
101 | 
102 | 		return {
103 | 			success: true,
104 | 			data: {
105 | 				updatedTasks: result.updatedTasks,
106 | 				tasksUpdated: result.updatedTasks.length,
107 | 				message: `Successfully scoped down ${result.updatedTasks.length} task(s)`,
108 | 				telemetryData: result.telemetryData
109 | 			}
110 | 		};
111 | 	} catch (error) {
112 | 		// Make sure to restore normal logging even if there's an error
113 | 		disableSilentMode();
114 | 
115 | 		log.error(`Error in scopeDownDirect: ${error.message}`);
116 | 		return {
117 | 			success: false,
118 | 			error: {
119 | 				code: error.code || 'SCOPE_DOWN_ERROR',
120 | 				message: error.message
121 | 			}
122 | 		};
123 | 	}
124 | }
125 | 
```

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

```javascript
  1 | /**
  2 |  * Direct function wrapper for expandAllTasks
  3 |  */
  4 | 
  5 | import { expandAllTasks } from '../../../../scripts/modules/task-manager.js';
  6 | import {
  7 | 	enableSilentMode,
  8 | 	disableSilentMode
  9 | } from '../../../../scripts/modules/utils.js';
 10 | import { createLogWrapper } from '../../tools/utils.js';
 11 | 
 12 | /**
 13 |  * Expand all pending tasks with subtasks (Direct Function Wrapper)
 14 |  * @param {Object} args - Function arguments
 15 |  * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 16 |  * @param {number|string} [args.num] - Number of subtasks to generate
 17 |  * @param {boolean} [args.research] - Enable research-backed subtask generation
 18 |  * @param {string} [args.prompt] - Additional context to guide subtask generation
 19 |  * @param {boolean} [args.force] - Force regeneration of subtasks for tasks that already have them
 20 |  * @param {string} [args.projectRoot] - Project root path.
 21 |  * @param {string} [args.tag] - Tag for the task (optional)
 22 |  * @param {Object} log - Logger object from FastMCP
 23 |  * @param {Object} context - Context object containing session
 24 |  * @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
 25 |  */
 26 | export async function expandAllTasksDirect(args, log, context = {}) {
 27 | 	const { session } = context; // Extract session
 28 | 	// Destructure expected args, including projectRoot
 29 | 	const { tasksJsonPath, num, research, prompt, force, projectRoot, tag } =
 30 | 		args;
 31 | 
 32 | 	// Create logger wrapper using the utility
 33 | 	const mcpLog = createLogWrapper(log);
 34 | 
 35 | 	if (!tasksJsonPath) {
 36 | 		log.error('expandAllTasksDirect called without tasksJsonPath');
 37 | 		return {
 38 | 			success: false,
 39 | 			error: {
 40 | 				code: 'MISSING_ARGUMENT',
 41 | 				message: 'tasksJsonPath is required'
 42 | 			}
 43 | 		};
 44 | 	}
 45 | 
 46 | 	enableSilentMode(); // Enable silent mode for the core function call
 47 | 	try {
 48 | 		log.info(
 49 | 			`Calling core expandAllTasks with args: ${JSON.stringify({ num, research, prompt, force, projectRoot, tag })}`
 50 | 		);
 51 | 
 52 | 		// Parse parameters (ensure correct types)
 53 | 		const numSubtasks = num ? parseInt(num, 10) : undefined;
 54 | 		const useResearch = research === true;
 55 | 		const additionalContext = prompt || '';
 56 | 		const forceFlag = force === true;
 57 | 
 58 | 		// Call the core function, passing options and the context object { session, mcpLog, projectRoot }
 59 | 		const result = await expandAllTasks(
 60 | 			tasksJsonPath,
 61 | 			numSubtasks,
 62 | 			useResearch,
 63 | 			additionalContext,
 64 | 			forceFlag,
 65 | 			{ session, mcpLog, projectRoot, tag },
 66 | 			'json'
 67 | 		);
 68 | 
 69 | 		// Core function now returns a summary object including the *aggregated* telemetryData
 70 | 		return {
 71 | 			success: true,
 72 | 			data: {
 73 | 				message: `Expand all operation completed. Expanded: ${result.expandedCount}, Failed: ${result.failedCount}, Skipped: ${result.skippedCount}`,
 74 | 				details: {
 75 | 					expandedCount: result.expandedCount,
 76 | 					failedCount: result.failedCount,
 77 | 					skippedCount: result.skippedCount,
 78 | 					tasksToExpand: result.tasksToExpand
 79 | 				},
 80 | 				telemetryData: result.telemetryData // Pass the aggregated object
 81 | 			}
 82 | 		};
 83 | 	} catch (error) {
 84 | 		// Log the error using the MCP logger
 85 | 		log.error(`Error during core expandAllTasks execution: ${error.message}`);
 86 | 		// Optionally log stack trace if available and debug enabled
 87 | 		// if (error.stack && log.debug) { log.debug(error.stack); }
 88 | 
 89 | 		return {
 90 | 			success: false,
 91 | 			error: {
 92 | 				code: 'CORE_FUNCTION_ERROR', // Or a more specific code if possible
 93 | 				message: error.message
 94 | 			}
 95 | 		};
 96 | 	} finally {
 97 | 		disableSilentMode(); // IMPORTANT: Ensure silent mode is always disabled
 98 | 	}
 99 | }
100 | 
```

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

```javascript
  1 | /**
  2 |  * list-tags.js
  3 |  * Direct function implementation for listing all tags
  4 |  */
  5 | 
  6 | import { tags } from '../../../../scripts/modules/task-manager/tag-management.js';
  7 | import {
  8 | 	enableSilentMode,
  9 | 	disableSilentMode
 10 | } from '../../../../scripts/modules/utils.js';
 11 | import { createLogWrapper } from '../../tools/utils.js';
 12 | 
 13 | /**
 14 |  * Direct function wrapper for listing all tags with error handling.
 15 |  *
 16 |  * @param {Object} args - Command arguments
 17 |  * @param {boolean} [args.showMetadata=false] - Whether to include metadata in the output
 18 |  * @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
 19 |  * @param {string} [args.projectRoot] - Project root path
 20 |  * @param {Object} log - Logger object
 21 |  * @param {Object} context - Additional context (session)
 22 |  * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 23 |  */
 24 | export async function listTagsDirect(args, log, context = {}) {
 25 | 	// Destructure expected args
 26 | 	const { tasksJsonPath, showMetadata = false, projectRoot } = args;
 27 | 	const { session } = context;
 28 | 
 29 | 	// Enable silent mode to prevent console logs from interfering with JSON response
 30 | 	enableSilentMode();
 31 | 
 32 | 	// Create logger wrapper using the utility
 33 | 	const mcpLog = createLogWrapper(log);
 34 | 
 35 | 	try {
 36 | 		// Check if tasksJsonPath was provided
 37 | 		if (!tasksJsonPath) {
 38 | 			log.error('listTagsDirect called without tasksJsonPath');
 39 | 			disableSilentMode();
 40 | 			return {
 41 | 				success: false,
 42 | 				error: {
 43 | 					code: 'MISSING_ARGUMENT',
 44 | 					message: 'tasksJsonPath is required'
 45 | 				}
 46 | 			};
 47 | 		}
 48 | 
 49 | 		log.info('Listing all tags');
 50 | 
 51 | 		// Prepare options
 52 | 		const options = {
 53 | 			showMetadata
 54 | 		};
 55 | 
 56 | 		// Call the tags function
 57 | 		const result = await tags(
 58 | 			tasksJsonPath,
 59 | 			options,
 60 | 			{
 61 | 				session,
 62 | 				mcpLog,
 63 | 				projectRoot
 64 | 			},
 65 | 			'json' // outputFormat - use 'json' to suppress CLI UI
 66 | 		);
 67 | 
 68 | 		// Transform the result to remove full task data and provide summary info
 69 | 		const tagsSummary = result.tags.map((tag) => {
 70 | 			const tasks = tag.tasks || [];
 71 | 
 72 | 			// Calculate status breakdown
 73 | 			const statusBreakdown = tasks.reduce((acc, task) => {
 74 | 				const status = task.status || 'pending';
 75 | 				acc[status] = (acc[status] || 0) + 1;
 76 | 				return acc;
 77 | 			}, {});
 78 | 
 79 | 			// Calculate subtask counts
 80 | 			const subtaskCounts = tasks.reduce(
 81 | 				(acc, task) => {
 82 | 					if (task.subtasks && task.subtasks.length > 0) {
 83 | 						acc.totalSubtasks += task.subtasks.length;
 84 | 						task.subtasks.forEach((subtask) => {
 85 | 							const subStatus = subtask.status || 'pending';
 86 | 							acc.subtasksByStatus[subStatus] =
 87 | 								(acc.subtasksByStatus[subStatus] || 0) + 1;
 88 | 						});
 89 | 					}
 90 | 					return acc;
 91 | 				},
 92 | 				{ totalSubtasks: 0, subtasksByStatus: {} }
 93 | 			);
 94 | 
 95 | 			return {
 96 | 				name: tag.name,
 97 | 				isCurrent: tag.isCurrent,
 98 | 				taskCount: tasks.length,
 99 | 				completedTasks: tag.completedTasks,
100 | 				statusBreakdown,
101 | 				subtaskCounts,
102 | 				created: tag.created,
103 | 				description: tag.description
104 | 			};
105 | 		});
106 | 
107 | 		// Restore normal logging
108 | 		disableSilentMode();
109 | 
110 | 		return {
111 | 			success: true,
112 | 			data: {
113 | 				tags: tagsSummary,
114 | 				currentTag: result.currentTag,
115 | 				totalTags: result.totalTags,
116 | 				message: `Found ${result.totalTags} tag(s)`
117 | 			}
118 | 		};
119 | 	} catch (error) {
120 | 		// Make sure to restore normal logging even if there's an error
121 | 		disableSilentMode();
122 | 
123 | 		log.error(`Error in listTagsDirect: ${error.message}`);
124 | 		return {
125 | 			success: false,
126 | 			error: {
127 | 				code: error.code || 'LIST_TAGS_ERROR',
128 | 				message: error.message
129 | 			}
130 | 		};
131 | 	}
132 | }
133 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/tools/set-task-status.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * tools/setTaskStatus.js
  3 |  * Tool to set the status of a task
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	handleApiResult,
  9 | 	createErrorResponse,
 10 | 	withNormalizedProjectRoot
 11 | } from './utils.js';
 12 | import {
 13 | 	setTaskStatusDirect,
 14 | 	nextTaskDirect
 15 | } from '../core/task-master-core.js';
 16 | import {
 17 | 	findTasksPath,
 18 | 	findComplexityReportPath
 19 | } from '../core/utils/path-utils.js';
 20 | import { TASK_STATUS_OPTIONS } from '../../../src/constants/task-status.js';
 21 | import { resolveTag } from '../../../scripts/modules/utils.js';
 22 | 
 23 | /**
 24 |  * Register the setTaskStatus tool with the MCP server
 25 |  * @param {Object} server - FastMCP server instance
 26 |  */
 27 | export function registerSetTaskStatusTool(server) {
 28 | 	server.addTool({
 29 | 		name: 'set_task_status',
 30 | 		description: 'Set the status of one or more tasks or subtasks.',
 31 | 		parameters: z.object({
 32 | 			id: z
 33 | 				.string()
 34 | 				.describe(
 35 | 					"Task ID or subtask ID (e.g., '15', '15.2'). Can be comma-separated to update multiple tasks/subtasks at once."
 36 | 				),
 37 | 			status: z
 38 | 				.enum(TASK_STATUS_OPTIONS)
 39 | 				.describe(
 40 | 					"New status to set (e.g., 'pending', 'done', 'in-progress', 'review', 'deferred', 'cancelled'."
 41 | 				),
 42 | 			file: z.string().optional().describe('Absolute path to the tasks file'),
 43 | 			complexityReport: z
 44 | 				.string()
 45 | 				.optional()
 46 | 				.describe(
 47 | 					'Path to the complexity report file (relative to project root or absolute)'
 48 | 				),
 49 | 			projectRoot: z
 50 | 				.string()
 51 | 				.describe('The directory of the project. Must be an absolute path.'),
 52 | 			tag: z.string().optional().describe('Optional tag context to operate on')
 53 | 		}),
 54 | 		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
 55 | 			try {
 56 | 				log.info(
 57 | 					`Setting status of task(s) ${args.id} to: ${args.status} ${
 58 | 						args.tag ? `in tag: ${args.tag}` : 'in current tag'
 59 | 					}`
 60 | 				);
 61 | 				const resolvedTag = resolveTag({
 62 | 					projectRoot: args.projectRoot,
 63 | 					tag: args.tag
 64 | 				});
 65 | 				// Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot)
 66 | 				let tasksJsonPath;
 67 | 				try {
 68 | 					tasksJsonPath = findTasksPath(
 69 | 						{ projectRoot: args.projectRoot, file: args.file },
 70 | 						log
 71 | 					);
 72 | 				} catch (error) {
 73 | 					log.error(`Error finding tasks.json: ${error.message}`);
 74 | 					return createErrorResponse(
 75 | 						`Failed to find tasks.json: ${error.message}`
 76 | 					);
 77 | 				}
 78 | 
 79 | 				let complexityReportPath;
 80 | 				try {
 81 | 					complexityReportPath = findComplexityReportPath(
 82 | 						{
 83 | 							projectRoot: args.projectRoot,
 84 | 							complexityReport: args.complexityReport,
 85 | 							tag: resolvedTag
 86 | 						},
 87 | 						log
 88 | 					);
 89 | 				} catch (error) {
 90 | 					log.error(`Error finding complexity report: ${error.message}`);
 91 | 				}
 92 | 
 93 | 				const result = await setTaskStatusDirect(
 94 | 					{
 95 | 						tasksJsonPath: tasksJsonPath,
 96 | 						id: args.id,
 97 | 						status: args.status,
 98 | 						complexityReportPath,
 99 | 						projectRoot: args.projectRoot,
100 | 						tag: resolvedTag
101 | 					},
102 | 					log,
103 | 					{ session }
104 | 				);
105 | 
106 | 				if (result.success) {
107 | 					log.info(
108 | 						`Successfully updated status for task(s) ${args.id} to "${args.status}": ${result.data.message}`
109 | 					);
110 | 				} else {
111 | 					log.error(
112 | 						`Failed to update task status: ${result.error?.message || 'Unknown error'}`
113 | 					);
114 | 				}
115 | 
116 | 				return handleApiResult(
117 | 					result,
118 | 					log,
119 | 					'Error setting task status',
120 | 					undefined,
121 | 					args.projectRoot
122 | 				);
123 | 			} catch (error) {
124 | 				log.error(`Error in setTaskStatus tool: ${error.message}`);
125 | 				return createErrorResponse(
126 | 					`Error setting task status: ${error.message}`
127 | 				);
128 | 			}
129 | 		})
130 | 	});
131 | }
132 | 
```

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

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

--------------------------------------------------------------------------------
/src/ai-providers/custom-sdk/grok-cli/message-converter.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * @fileoverview Message format conversion utilities for Grok CLI provider
  3 |  */
  4 | 
  5 | /**
  6 |  * @typedef {import('./types.js').GrokCliMessage} GrokCliMessage
  7 |  */
  8 | 
  9 | /**
 10 |  * Convert AI SDK messages to Grok CLI compatible format
 11 |  * @param {Array<Object>} messages - AI SDK message array
 12 |  * @returns {Array<GrokCliMessage>} Grok CLI compatible messages
 13 |  */
 14 | export function convertToGrokCliMessages(messages) {
 15 | 	return messages.map((message) => {
 16 | 		// Handle different message content types
 17 | 		let content = '';
 18 | 
 19 | 		if (typeof message.content === 'string') {
 20 | 			content = message.content;
 21 | 		} else if (Array.isArray(message.content)) {
 22 | 			// Handle multi-part content (text and images)
 23 | 			content = message.content
 24 | 				.filter((part) => part.type === 'text')
 25 | 				.map((part) => part.text)
 26 | 				.join('\n');
 27 | 		} else if (message.content && typeof message.content === 'object') {
 28 | 			// Handle object content
 29 | 			content = message.content.text || JSON.stringify(message.content);
 30 | 		}
 31 | 
 32 | 		return {
 33 | 			role: message.role,
 34 | 			content: content.trim()
 35 | 		};
 36 | 	});
 37 | }
 38 | 
 39 | /**
 40 |  * Convert Grok CLI response to AI SDK format
 41 |  * @param {string} responseText - Raw response text from Grok CLI (JSONL format)
 42 |  * @returns {Object} AI SDK compatible response object
 43 |  */
 44 | export function convertFromGrokCliResponse(responseText) {
 45 | 	try {
 46 | 		// Grok CLI outputs JSONL format - each line is a separate JSON message
 47 | 		const lines = responseText
 48 | 			.trim()
 49 | 			.split('\n')
 50 | 			.filter((line) => line.trim());
 51 | 
 52 | 		// Parse each line as JSON and find assistant messages
 53 | 		const messages = [];
 54 | 		for (const line of lines) {
 55 | 			try {
 56 | 				const message = JSON.parse(line);
 57 | 				messages.push(message);
 58 | 			} catch (parseError) {
 59 | 				// Skip invalid JSON lines
 60 | 				continue;
 61 | 			}
 62 | 		}
 63 | 
 64 | 		// Find the last assistant message
 65 | 		const assistantMessage = messages
 66 | 			.filter((msg) => msg.role === 'assistant')
 67 | 			.pop();
 68 | 
 69 | 		if (assistantMessage && assistantMessage.content) {
 70 | 			return {
 71 | 				text: assistantMessage.content,
 72 | 				usage: assistantMessage.usage
 73 | 					? {
 74 | 							promptTokens: assistantMessage.usage.prompt_tokens || 0,
 75 | 							completionTokens: assistantMessage.usage.completion_tokens || 0,
 76 | 							totalTokens: assistantMessage.usage.total_tokens || 0
 77 | 						}
 78 | 					: undefined
 79 | 			};
 80 | 		}
 81 | 
 82 | 		// Fallback: if no assistant message found, return the raw text
 83 | 		return {
 84 | 			text: responseText.trim(),
 85 | 			usage: undefined
 86 | 		};
 87 | 	} catch (error) {
 88 | 		// If parsing fails completely, treat as plain text response
 89 | 		return {
 90 | 			text: responseText.trim(),
 91 | 			usage: undefined
 92 | 		};
 93 | 	}
 94 | }
 95 | 
 96 | /**
 97 |  * Create a prompt string for Grok CLI from messages
 98 |  * @param {Array<Object>} messages - AI SDK message array
 99 |  * @returns {string} Formatted prompt string
100 |  */
101 | export function createPromptFromMessages(messages) {
102 | 	const grokMessages = convertToGrokCliMessages(messages);
103 | 
104 | 	// Create a conversation-style prompt
105 | 	const prompt = grokMessages
106 | 		.map((message) => {
107 | 			switch (message.role) {
108 | 				case 'system':
109 | 					return `System: ${message.content}`;
110 | 				case 'user':
111 | 					return `User: ${message.content}`;
112 | 				case 'assistant':
113 | 					return `Assistant: ${message.content}`;
114 | 				default:
115 | 					return `${message.role}: ${message.content}`;
116 | 			}
117 | 		})
118 | 		.join('\n\n');
119 | 
120 | 	return prompt;
121 | }
122 | 
123 | /**
124 |  * Escape shell arguments for safe CLI execution
125 |  * @param {string} arg - Argument to escape
126 |  * @returns {string} Shell-escaped argument
127 |  */
128 | export function escapeShellArg(arg) {
129 | 	if (typeof arg !== 'string') {
130 | 		arg = String(arg);
131 | 	}
132 | 
133 | 	// Replace single quotes with '\''
134 | 	return "'" + arg.replace(/'/g, "'\\''") + "'";
135 | }
136 | 
```

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

```typescript
  1 | /**
  2 |  * Claude executor implementation for Task Master
  3 |  */
  4 | 
  5 | import { spawn } from 'child_process';
  6 | import type { Task } from '../types/index.js';
  7 | import type {
  8 | 	ExecutorType,
  9 | 	ExecutionResult,
 10 | 	ClaudeExecutorConfig
 11 | } from './types.js';
 12 | import { BaseExecutor } from './base-executor.js';
 13 | 
 14 | export class ClaudeExecutor extends BaseExecutor {
 15 | 	private claudeConfig: ClaudeExecutorConfig;
 16 | 	private currentProcess: any = null;
 17 | 
 18 | 	constructor(projectRoot: string, config: ClaudeExecutorConfig = {}) {
 19 | 		super(projectRoot, config);
 20 | 		this.claudeConfig = {
 21 | 			command: config.command || 'claude',
 22 | 			systemPrompt:
 23 | 				config.systemPrompt ||
 24 | 				'You are a helpful AI assistant helping to complete a software development task.',
 25 | 			additionalFlags: config.additionalFlags || []
 26 | 		};
 27 | 	}
 28 | 
 29 | 	getType(): ExecutorType {
 30 | 		return 'claude';
 31 | 	}
 32 | 
 33 | 	async isAvailable(): Promise<boolean> {
 34 | 		return new Promise((resolve) => {
 35 | 			const checkProcess = spawn('which', [this.claudeConfig.command!], {
 36 | 				shell: true
 37 | 			});
 38 | 
 39 | 			checkProcess.on('close', (code) => {
 40 | 				resolve(code === 0);
 41 | 			});
 42 | 
 43 | 			checkProcess.on('error', () => {
 44 | 				resolve(false);
 45 | 			});
 46 | 		});
 47 | 	}
 48 | 
 49 | 	async execute(task: Task): Promise<ExecutionResult> {
 50 | 		const startTime = new Date().toISOString();
 51 | 
 52 | 		try {
 53 | 			// Check if Claude is available
 54 | 			const isAvailable = await this.isAvailable();
 55 | 			if (!isAvailable) {
 56 | 				return this.createResult(
 57 | 					task.id,
 58 | 					false,
 59 | 					undefined,
 60 | 					`Claude CLI not found. Please ensure 'claude' command is available in PATH.`
 61 | 				);
 62 | 			}
 63 | 
 64 | 			// Format the task into a prompt
 65 | 			const taskPrompt = this.formatTaskPrompt(task);
 66 | 			const fullPrompt = `${this.claudeConfig.systemPrompt}\n\nHere is the task to complete:\n\n${taskPrompt}`;
 67 | 
 68 | 			// Execute Claude with the task details
 69 | 			const result = await this.runClaude(fullPrompt, task.id);
 70 | 
 71 | 			return {
 72 | 				...result,
 73 | 				startTime,
 74 | 				endTime: new Date().toISOString()
 75 | 			};
 76 | 		} catch (error: any) {
 77 | 			this.logger.error(`Failed to execute task ${task.id}:`, error);
 78 | 			return this.createResult(
 79 | 				task.id,
 80 | 				false,
 81 | 				undefined,
 82 | 				error.message || 'Unknown error occurred'
 83 | 			);
 84 | 		}
 85 | 	}
 86 | 
 87 | 	private runClaude(prompt: string, taskId: string): Promise<ExecutionResult> {
 88 | 		return new Promise((resolve) => {
 89 | 			const args = [prompt, ...this.claudeConfig.additionalFlags!];
 90 | 
 91 | 			this.logger.info(`Executing Claude for task ${taskId}`);
 92 | 			this.logger.debug(
 93 | 				`Command: ${this.claudeConfig.command} ${args.join(' ')}`
 94 | 			);
 95 | 
 96 | 			this.currentProcess = spawn(this.claudeConfig.command!, args, {
 97 | 				cwd: this.projectRoot,
 98 | 				shell: false,
 99 | 				stdio: 'inherit' // Let Claude handle its own I/O
100 | 			});
101 | 
102 | 			this.currentProcess.on('close', (code: number) => {
103 | 				this.currentProcess = null;
104 | 
105 | 				if (code === 0) {
106 | 					resolve(
107 | 						this.createResult(
108 | 							taskId,
109 | 							true,
110 | 							'Claude session completed successfully'
111 | 						)
112 | 					);
113 | 				} else {
114 | 					resolve(
115 | 						this.createResult(
116 | 							taskId,
117 | 							false,
118 | 							undefined,
119 | 							`Claude exited with code ${code}`
120 | 						)
121 | 					);
122 | 				}
123 | 			});
124 | 
125 | 			this.currentProcess.on('error', (error: any) => {
126 | 				this.currentProcess = null;
127 | 				this.logger.error(`Claude process error:`, error);
128 | 				resolve(
129 | 					this.createResult(
130 | 						taskId,
131 | 						false,
132 | 						undefined,
133 | 						`Failed to spawn Claude: ${error.message}`
134 | 					)
135 | 				);
136 | 			});
137 | 		});
138 | 	}
139 | 
140 | 	async stop(): Promise<void> {
141 | 		if (this.currentProcess) {
142 | 			this.logger.info('Stopping Claude process...');
143 | 			this.currentProcess.kill('SIGTERM');
144 | 			this.currentProcess = null;
145 | 		}
146 | 	}
147 | }
148 | 
```

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | import fs from 'fs';
  3 | import path from 'path';
  4 | import { rooProfile } from '../../../src/profiles/roo.js';
  5 | import { COMMON_TOOL_MAPPINGS } from '../../../src/profiles/base-profile.js';
  6 | 
  7 | describe('Roo Profile Initialization Functionality', () => {
  8 | 	let rooProfileContent;
  9 | 
 10 | 	beforeAll(() => {
 11 | 		// Read the roo.js profile file content once for all tests
 12 | 		const rooJsPath = path.join(process.cwd(), 'src', 'profiles', 'roo.js');
 13 | 		rooProfileContent = fs.readFileSync(rooJsPath, 'utf8');
 14 | 	});
 15 | 
 16 | 	test('roo.js uses factory pattern with correct configuration', () => {
 17 | 		// Check for explicit, non-default values in the source file
 18 | 		expect(rooProfileContent).toContain("name: 'roo'");
 19 | 		expect(rooProfileContent).toContain("displayName: 'Roo Code'");
 20 | 		expect(rooProfileContent).toContain(
 21 | 			'toolMappings: COMMON_TOOL_MAPPINGS.ROO_STYLE'
 22 | 		);
 23 | 
 24 | 		// Check the final computed properties on the profile object
 25 | 		expect(rooProfile.profileName).toBe('roo');
 26 | 		expect(rooProfile.displayName).toBe('Roo Code');
 27 | 		expect(rooProfile.profileDir).toBe('.roo'); // default
 28 | 		expect(rooProfile.rulesDir).toBe('.roo/rules'); // default
 29 | 		expect(rooProfile.mcpConfig).toBe(true); // default
 30 | 	});
 31 | 
 32 | 	test('roo.js uses custom ROO_STYLE tool mappings', () => {
 33 | 		// Check that the profile uses the correct, non-standard tool mappings
 34 | 		expect(rooProfileContent).toContain(
 35 | 			'toolMappings: COMMON_TOOL_MAPPINGS.ROO_STYLE'
 36 | 		);
 37 | 
 38 | 		// Verify the result: roo uses custom tool names
 39 | 		expect(rooProfile.conversionConfig.toolNames.edit_file).toBe('apply_diff');
 40 | 		expect(rooProfile.conversionConfig.toolNames.search).toBe('search_files');
 41 | 	});
 42 | 
 43 | 	test('roo.js profile ensures Roo directory structure via onAddRulesProfile', () => {
 44 | 		// Check if onAddRulesProfile function exists
 45 | 		expect(rooProfileContent).toContain(
 46 | 			'onAddRulesProfile(targetDir, assetsDir)'
 47 | 		);
 48 | 
 49 | 		// Check for the general copy of assets/roocode which includes .roo base structure
 50 | 		expect(rooProfileContent).toContain(
 51 | 			"const sourceDir = path.join(assetsDir, 'roocode');"
 52 | 		);
 53 | 		expect(rooProfileContent).toContain(
 54 | 			'copyRecursiveSync(sourceDir, targetDir);'
 55 | 		);
 56 | 
 57 | 		// Check for the specific .roo modes directory handling
 58 | 		expect(rooProfileContent).toContain(
 59 | 			"const rooModesDir = path.join(sourceDir, '.roo');"
 60 | 		);
 61 | 
 62 | 		// Check for import of ROO_MODES from profiles.js instead of local definition
 63 | 		expect(rooProfileContent).toContain(
 64 | 			"import { ROO_MODES } from '../constants/profiles.js';"
 65 | 		);
 66 | 	});
 67 | 
 68 | 	test('roo.js profile copies .roomodes file via onAddRulesProfile', () => {
 69 | 		expect(rooProfileContent).toContain(
 70 | 			'onAddRulesProfile(targetDir, assetsDir)'
 71 | 		);
 72 | 
 73 | 		// Check for the specific .roomodes copy logic
 74 | 		expect(rooProfileContent).toContain(
 75 | 			"const roomodesSrc = path.join(sourceDir, '.roomodes');"
 76 | 		);
 77 | 		expect(rooProfileContent).toContain(
 78 | 			"const roomodesDest = path.join(targetDir, '.roomodes');"
 79 | 		);
 80 | 		expect(rooProfileContent).toContain(
 81 | 			'fs.copyFileSync(roomodesSrc, roomodesDest);'
 82 | 		);
 83 | 	});
 84 | 
 85 | 	test('roo.js profile copies mode-specific rule files via onAddRulesProfile', () => {
 86 | 		expect(rooProfileContent).toContain(
 87 | 			'onAddRulesProfile(targetDir, assetsDir)'
 88 | 		);
 89 | 		expect(rooProfileContent).toContain('for (const mode of ROO_MODES)');
 90 | 
 91 | 		// Check for the specific mode rule file copy logic
 92 | 		expect(rooProfileContent).toContain(
 93 | 			'const src = path.join(rooModesDir, `rules-${mode}`, `${mode}-rules`);'
 94 | 		);
 95 | 		expect(rooProfileContent).toContain(
 96 | 			"const dest = path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`);"
 97 | 		);
 98 | 	});
 99 | });
100 | 
```
Page 9/52FirstPrevNextLast