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

--------------------------------------------------------------------------------
/apps/docs/archive/ai-development-workflow.mdx:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: "AI Development Workflow"
  3 | description: "Learn how Task Master and Cursor AI work together to streamline your development workflow"
  4 | ---
  5 | 
  6 | <Tip>The Cursor agent is pre-configured (via the rules file) to follow this workflow</Tip>
  7 | 
  8 | <AccordionGroup>
  9 |   <Accordion title="1. Task Discovery and Selection">
 10 |     Ask the agent to list available tasks:
 11 | 
 12 |     ```
 13 |     What tasks are available to work on next?
 14 |     ```
 15 | 
 16 |     The agent will:
 17 | 
 18 |     - Run `task-master list` to see all tasks
 19 |     - Run `task-master next` to determine the next task to work on
 20 |     - Analyze dependencies to determine which tasks are ready to be worked on
 21 |     - Prioritize tasks based on priority level and ID order
 22 |     - Suggest the next task(s) to implement
 23 |   </Accordion>
 24 | 
 25 |   <Accordion title="2. Task Implementation">
 26 |     When implementing a task, the agent will:
 27 | 
 28 |     - Reference the task's details section for implementation specifics
 29 |     - Consider dependencies on previous tasks
 30 |     - Follow the project's coding standards
 31 |     - Create appropriate tests based on the task's testStrategy
 32 | 
 33 |     You can ask:
 34 | 
 35 |     ```
 36 |     Let's implement task 3. What does it involve?
 37 |     ```
 38 |   </Accordion>
 39 | 
 40 |   <Accordion title="3. Task Verification">
 41 |     Before marking a task as complete, verify it according to:
 42 | 
 43 |     - The task's specified testStrategy
 44 |     - Any automated tests in the codebase
 45 |     - Manual verification if required
 46 |   </Accordion>
 47 | 
 48 |   <Accordion title="4. Task Completion">
 49 |     When a task is completed, tell the agent:
 50 | 
 51 |     ```
 52 |     Task 3 is now complete. Please update its status.
 53 |     ```
 54 | 
 55 |     The agent will execute:
 56 | 
 57 |     ```bash
 58 |     task-master set-status --id=3 --status=done
 59 |     ```
 60 |   </Accordion>
 61 | 
 62 |   <Accordion title="5. Handling Implementation Drift">
 63 |     If during implementation, you discover that:
 64 | 
 65 |     - The current approach differs significantly from what was planned
 66 |     - Future tasks need to be modified due to current implementation choices
 67 |     - New dependencies or requirements have emerged
 68 | 
 69 |     Tell the agent:
 70 | 
 71 |     ```
 72 |     We've changed our approach. We're now using Express instead of Fastify. Please update all future tasks to reflect this change.
 73 |     ```
 74 | 
 75 |     The agent will execute:
 76 | 
 77 |     ```bash
 78 |     task-master update --from=4 --prompt="Now we are using Express instead of Fastify."
 79 |     ```
 80 | 
 81 |     This will rewrite or re-scope subsequent tasks in tasks.json while preserving completed work.
 82 |   </Accordion>
 83 | 
 84 |   <Accordion title="6. Breaking Down Complex Tasks">
 85 |     For complex tasks that need more granularity:
 86 | 
 87 |     ```
 88 |     Task 5 seems complex. Can you break it down into subtasks?
 89 |     ```
 90 | 
 91 |     The agent will execute:
 92 | 
 93 |     ```bash
 94 |     task-master expand --id=5 --num=3
 95 |     ```
 96 | 
 97 |     You can provide additional context:
 98 | 
 99 |     ```
100 |     Please break down task 5 with a focus on security considerations.
101 |     ```
102 | 
103 |     The agent will execute:
104 | 
105 |     ```bash
106 |     task-master expand --id=5 --prompt="Focus on security aspects"
107 |     ```
108 | 
109 |     You can also expand all pending tasks:
110 | 
111 |     ```
112 |     Please break down all pending tasks into subtasks.
113 |     ```
114 | 
115 |     The agent will execute:
116 | 
117 |     ```bash
118 |     task-master expand --all
119 |     ```
120 | 
121 |     For research-backed subtask generation using Perplexity AI:
122 | 
123 |     ```
124 |     Please break down task 5 using research-backed generation.
125 |     ```
126 | 
127 |     The agent will execute:
128 | 
129 |     ```bash
130 |     task-master expand --id=5 --research
131 |     ```
132 |   </Accordion>
133 | </AccordionGroup>
134 | 
135 | ## Example Cursor AI Interactions
136 | 
137 | <AccordionGroup>
138 |   <Accordion title="Starting a new project">
139 |     ```
140 |     I've just initialized a new project with Claude Task Master. I have a PRD at scripts/prd.txt.
141 |     Can you help me parse it and set up the initial tasks?
142 |     ```
143 |   </Accordion>
144 |   <Accordion title="Working on tasks">
145 |     ```
146 |     What's the next task I should work on? Please consider dependencies and priorities.
147 |     ```
148 |   </Accordion>
149 |   <Accordion title="Implementing a specific task">
150 |     ```
151 |     I'd like to implement task 4. Can you help me understand what needs to be done and how to approach it?
152 |     ```
153 |   </Accordion>
154 |   <Accordion title="Managing subtasks">
155 |     ```
156 |     I need to regenerate the subtasks for task 3 with a different approach. Can you help me clear and regenerate them?
157 |     ```
158 |   </Accordion>
159 |   <Accordion title="Handling changes">
160 |     ```
161 |     We've decided to use MongoDB instead of PostgreSQL. Can you update all future tasks to reflect this change?
162 |     ```
163 |   </Accordion>
164 |   <Accordion title="Completing work">
165 |     ```
166 |     I've finished implementing the authentication system described in task 2. All tests are passing.
167 |     Please mark it as complete and tell me what I should work on next.
168 |     ```
169 |   </Accordion>
170 |   <Accordion title="Analyzing complexity">
171 |     ```
172 |     Can you analyze the complexity of our tasks to help me understand which ones need to be broken down further?
173 |     ```
174 |   </Accordion>
175 |   <Accordion title="Viewing complexity report">
176 |     ```
177 |     Can you show me the complexity report in a more readable format?
178 |     ```
179 |   </Accordion>
180 | </AccordionGroup>
181 | 
```

--------------------------------------------------------------------------------
/apps/extension/src/webview/reducers/appReducer.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Main application state reducer
  3 |  */
  4 | 
  5 | import type { AppState, AppAction } from '../types';
  6 | import { logger } from '../utils/logger';
  7 | 
  8 | export const appReducer = (state: AppState, action: AppAction): AppState => {
  9 | 	logger.debug(
 10 | 		'Reducer action:',
 11 | 		action.type,
 12 | 		'payload' in action ? action.payload : 'no payload'
 13 | 	);
 14 | 	switch (action.type) {
 15 | 		case 'SET_TASKS':
 16 | 			const newTasks = Array.isArray(action.payload) ? action.payload : [];
 17 | 			logger.debug('SET_TASKS reducer - updating tasks:', {
 18 | 				oldCount: state.tasks.length,
 19 | 				newCount: newTasks.length,
 20 | 				newTasks
 21 | 			});
 22 | 			return {
 23 | 				...state,
 24 | 				tasks: newTasks,
 25 | 				loading: false,
 26 | 				error: undefined
 27 | 			};
 28 | 		case 'SET_LOADING':
 29 | 			return { ...state, loading: action.payload };
 30 | 		case 'SET_ERROR':
 31 | 			return { ...state, error: action.payload, loading: false };
 32 | 		case 'CLEAR_ERROR':
 33 | 			return { ...state, error: undefined };
 34 | 		case 'INCREMENT_REQUEST_ID':
 35 | 			return { ...state, requestId: state.requestId + 1 };
 36 | 		case 'UPDATE_TASK_STATUS': {
 37 | 			const { taskId, newStatus } = action.payload;
 38 | 			return {
 39 | 				...state,
 40 | 				tasks: state.tasks.map((task) =>
 41 | 					task.id === taskId ? { ...task, status: newStatus } : task
 42 | 				)
 43 | 			};
 44 | 		}
 45 | 		case 'UPDATE_TASK_CONTENT': {
 46 | 			const { taskId, updates } = action.payload;
 47 | 			return {
 48 | 				...state,
 49 | 				tasks: state.tasks.map((task) =>
 50 | 					task.id === taskId ? { ...task, ...updates } : task
 51 | 				)
 52 | 			};
 53 | 		}
 54 | 		case 'SET_CONNECTION_STATUS':
 55 | 			return {
 56 | 				...state,
 57 | 				isConnected: action.payload.isConnected,
 58 | 				connectionStatus: action.payload.status
 59 | 			};
 60 | 		case 'SET_EDITING_TASK':
 61 | 			return {
 62 | 				...state,
 63 | 				editingTask: action.payload
 64 | 			};
 65 | 		case 'SET_POLLING_STATUS':
 66 | 			return {
 67 | 				...state,
 68 | 				polling: {
 69 | 					...state.polling,
 70 | 					isActive: action.payload.isActive,
 71 | 					errorCount: action.payload.errorCount ?? state.polling.errorCount,
 72 | 					lastUpdate: action.payload.isActive
 73 | 						? Date.now()
 74 | 						: state.polling.lastUpdate
 75 | 				}
 76 | 			};
 77 | 		case 'SET_USER_INTERACTING':
 78 | 			return {
 79 | 				...state,
 80 | 				polling: {
 81 | 					...state.polling,
 82 | 					isUserInteracting: action.payload
 83 | 				}
 84 | 			};
 85 | 		case 'TASKS_UPDATED_FROM_POLLING':
 86 | 			return {
 87 | 				...state,
 88 | 				tasks: Array.isArray(action.payload) ? action.payload : [],
 89 | 				polling: {
 90 | 					...state.polling,
 91 | 					lastUpdate: Date.now()
 92 | 				}
 93 | 			};
 94 | 		case 'SET_NETWORK_STATUS':
 95 | 			return {
 96 | 				...state,
 97 | 				polling: {
 98 | 					...state.polling,
 99 | 					isOfflineMode: action.payload.isOfflineMode,
100 | 					connectionStatus: action.payload.connectionStatus,
101 | 					reconnectAttempts:
102 | 						action.payload.reconnectAttempts !== undefined
103 | 							? action.payload.reconnectAttempts
104 | 							: state.polling.reconnectAttempts,
105 | 					maxReconnectAttempts:
106 | 						action.payload.maxReconnectAttempts !== undefined
107 | 							? action.payload.maxReconnectAttempts
108 | 							: state.polling.maxReconnectAttempts,
109 | 					lastSuccessfulConnection:
110 | 						action.payload.lastSuccessfulConnection !== undefined
111 | 							? action.payload.lastSuccessfulConnection
112 | 							: state.polling.lastSuccessfulConnection
113 | 				}
114 | 			};
115 | 		case 'LOAD_CACHED_TASKS':
116 | 			return {
117 | 				...state,
118 | 				tasks: Array.isArray(action.payload) ? action.payload : []
119 | 			};
120 | 		case 'ADD_TOAST':
121 | 			return {
122 | 				...state,
123 | 				toastNotifications: [...state.toastNotifications, action.payload]
124 | 			};
125 | 		case 'REMOVE_TOAST':
126 | 			return {
127 | 				...state,
128 | 				toastNotifications: state.toastNotifications.filter(
129 | 					(notification) => notification.id !== action.payload
130 | 				)
131 | 			};
132 | 		case 'CLEAR_ALL_TOASTS':
133 | 			return { ...state, toastNotifications: [] };
134 | 		case 'NAVIGATE_TO_TASK':
135 | 			logger.debug('📍 Reducer: Navigating to task:', action.payload);
136 | 			return {
137 | 				...state,
138 | 				currentView: 'task-details',
139 | 				selectedTaskId: action.payload
140 | 			};
141 | 		case 'NAVIGATE_TO_KANBAN':
142 | 			logger.debug('📍 Reducer: Navigating to kanban');
143 | 			return { ...state, currentView: 'kanban', selectedTaskId: undefined };
144 | 		case 'NAVIGATE_TO_CONFIG':
145 | 			logger.debug('📍 Reducer: Navigating to config');
146 | 			return { ...state, currentView: 'config', selectedTaskId: undefined };
147 | 		case 'SET_CURRENT_TAG':
148 | 			return {
149 | 				...state,
150 | 				currentTag: action.payload
151 | 			};
152 | 		case 'SET_AVAILABLE_TAGS':
153 | 			return {
154 | 				...state,
155 | 				availableTags: action.payload
156 | 			};
157 | 		case 'SET_TAG_DATA':
158 | 			return {
159 | 				...state,
160 | 				currentTag: action.payload.currentTag,
161 | 				availableTags: action.payload.availableTags
162 | 			};
163 | 		default:
164 | 			return state;
165 | 	}
166 | };
167 | 
168 | export const initialState: AppState = {
169 | 	tasks: [],
170 | 	loading: true,
171 | 	requestId: 0,
172 | 	isConnected: false,
173 | 	connectionStatus: 'Connecting...',
174 | 	editingTask: { taskId: null },
175 | 	polling: {
176 | 		isActive: false,
177 | 		errorCount: 0,
178 | 		lastUpdate: undefined,
179 | 		isUserInteracting: false,
180 | 		isOfflineMode: false,
181 | 		reconnectAttempts: 0,
182 | 		maxReconnectAttempts: 0,
183 | 		lastSuccessfulConnection: undefined,
184 | 		connectionStatus: 'online'
185 | 	},
186 | 	toastNotifications: [],
187 | 	currentView: 'kanban',
188 | 	selectedTaskId: undefined,
189 | 	// Tag-related state
190 | 	currentTag: 'master',
191 | 	availableTags: ['master']
192 | };
193 | 
```

--------------------------------------------------------------------------------
/src/prompts/add-task.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 | 	"id": "add-task",
 3 | 	"version": "1.0.0",
 4 | 	"description": "Generate a new task based on description",
 5 | 	"metadata": {
 6 | 		"author": "system",
 7 | 		"created": "2024-01-01T00:00:00Z",
 8 | 		"updated": "2024-01-01T00:00:00Z",
 9 | 		"tags": ["task-creation", "generation"]
10 | 	},
11 | 	"parameters": {
12 | 		"prompt": {
13 | 			"type": "string",
14 | 			"required": true,
15 | 			"description": "User's task description"
16 | 		},
17 | 		"newTaskId": {
18 | 			"type": "number",
19 | 			"required": true,
20 | 			"description": "ID for the new task"
21 | 		},
22 | 		"existingTasks": {
23 | 			"type": "array",
24 | 			"description": "List of existing tasks for context"
25 | 		},
26 | 		"gatheredContext": {
27 | 			"type": "string",
28 | 			"description": "Context gathered from codebase analysis"
29 | 		},
30 | 		"contextFromArgs": {
31 | 			"type": "string",
32 | 			"description": "Additional context from manual args"
33 | 		},
34 | 		"priority": {
35 | 			"type": "string",
36 | 			"default": "medium",
37 | 			"enum": ["high", "medium", "low"],
38 | 			"description": "Task priority"
39 | 		},
40 | 		"dependencies": {
41 | 			"type": "array",
42 | 			"description": "Task dependency IDs"
43 | 		},
44 | 		"useResearch": {
45 | 			"type": "boolean",
46 | 			"default": false,
47 | 			"description": "Use research mode"
48 | 		},
49 | 		"hasCodebaseAnalysis": {
50 | 			"type": "boolean",
51 | 			"required": false,
52 | 			"default": false,
53 | 			"description": "Whether codebase analysis is available"
54 | 		},
55 | 		"projectRoot": {
56 | 			"type": "string",
57 | 			"required": false,
58 | 			"default": "",
59 | 			"description": "Project root path for context"
60 | 		}
61 | 	},
62 | 	"prompts": {
63 | 		"default": {
64 | 			"system": "You are a helpful assistant that creates well-structured tasks for a software development project. Generate a single new task based on the user's description, adhering strictly to the provided JSON schema. Pay special attention to dependencies between tasks, ensuring the new task correctly references any tasks it depends on.\n\nWhen determining dependencies for a new task, follow these principles:\n1. Select dependencies based on logical requirements - what must be completed before this task can begin.\n2. Prioritize task dependencies that are semantically related to the functionality being built.\n3. Consider both direct dependencies (immediately prerequisite) and indirect dependencies.\n4. Avoid adding unnecessary dependencies - only include tasks that are genuinely prerequisite.\n5. Consider the current status of tasks - prefer completed tasks as dependencies when possible.\n6. Pay special attention to foundation tasks (1-5) but don't automatically include them without reason.\n7. Recent tasks (higher ID numbers) may be more relevant for newer functionality.\n\nThe dependencies array should contain task IDs (numbers) of prerequisite tasks.{{#if useResearch}}\n\nResearch current best practices and technologies relevant to this task.{{/if}}",
65 | 			"user": "{{#if hasCodebaseAnalysis}}## IMPORTANT: Codebase Analysis Required\n\nYou have access to powerful codebase analysis tools. Before generating the task:\n\n1. Use the Glob tool to explore the project structure (e.g., \"**/*.js\", \"**/*.json\", \"**/README.md\")\n2. Use the Grep tool to search for existing implementations, patterns, and technologies\n3. Use the Read tool to examine key files like package.json, main entry points, and relevant source files\n4. Analyze the current implementation to understand what already exists\n\nBased on your analysis:\n- Identify existing components/features that relate to this new task\n- Understand the technology stack, frameworks, and patterns in use\n- Generate implementation details that align with the project's current architecture\n- Reference specific files, functions, or patterns from the codebase in your details\n\nProject Root: {{projectRoot}}\n\n{{/if}}You are generating the details for Task #{{newTaskId}}. Based on the user's request: \"{{prompt}}\", create a comprehensive new task for a software development project.\n      \n      {{gatheredContext}}\n      \n      {{#if useResearch}}Research current best practices, technologies, and implementation patterns relevant to this task. {{/if}}Based on the information about existing tasks provided above, include appropriate dependencies in the \"dependencies\" array. Only include task IDs that this new task directly depends on.\n      \n      Return your answer as a single JSON object matching the schema precisely:\n      \n      {\n        \"title\": \"Task title goes here\",\n        \"description\": \"A concise one or two sentence description of what the task involves\",\n    \"details\": \"Detailed implementation steps, considerations, code examples, or technical approach\",\n    \"testStrategy\": \"Specific steps to verify correct implementation and functionality\",\n    \"dependencies\": [1, 3] // Example: IDs of tasks that must be completed before this task\n  }\n      \n      Make sure the details and test strategy are comprehensive and specific{{#if useResearch}}, incorporating current best practices from your research{{/if}}. DO NOT include the task ID in the title.\n      {{#if contextFromArgs}}{{contextFromArgs}}{{/if}}"
66 | 		}
67 | 	}
68 | }
69 | 
```

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

```javascript
  1 | /**
  2 |  * Tests for the addSubtask function
  3 |  */
  4 | import { jest } from '@jest/globals';
  5 | 
  6 | // Mock dependencies before importing the module
  7 | const mockUtils = {
  8 | 	readJSON: jest.fn(),
  9 | 	writeJSON: jest.fn(),
 10 | 	log: jest.fn(),
 11 | 	getCurrentTag: jest.fn()
 12 | };
 13 | const mockTaskManager = {
 14 | 	isTaskDependentOn: jest.fn()
 15 | };
 16 | const mockGenerateTaskFiles = jest.fn();
 17 | 
 18 | jest.unstable_mockModule(
 19 | 	'../../../../../scripts/modules/utils.js',
 20 | 	() => mockUtils
 21 | );
 22 | jest.unstable_mockModule(
 23 | 	'../../../../../scripts/modules/task-manager.js',
 24 | 	() => mockTaskManager
 25 | );
 26 | jest.unstable_mockModule(
 27 | 	'../../../../../scripts/modules/task-manager/generate-task-files.js',
 28 | 	() => ({
 29 | 		default: mockGenerateTaskFiles
 30 | 	})
 31 | );
 32 | 
 33 | const addSubtask = (
 34 | 	await import('../../../../../scripts/modules/task-manager/add-subtask.js')
 35 | ).default;
 36 | 
 37 | describe('addSubtask function', () => {
 38 | 	const multiTagData = {
 39 | 		master: {
 40 | 			tasks: [{ id: 1, title: 'Master Task', subtasks: [] }],
 41 | 			metadata: { description: 'Master tasks' }
 42 | 		},
 43 | 		'feature-branch': {
 44 | 			tasks: [{ id: 1, title: 'Feature Task', subtasks: [] }],
 45 | 			metadata: { description: 'Feature tasks' }
 46 | 		}
 47 | 	};
 48 | 
 49 | 	beforeEach(() => {
 50 | 		jest.clearAllMocks();
 51 | 		mockTaskManager.isTaskDependentOn.mockReturnValue(false);
 52 | 	});
 53 | 
 54 | 	test('should add a new subtask and preserve other tags', async () => {
 55 | 		const context = { projectRoot: '/fake/root', tag: 'feature-branch' };
 56 | 		const newSubtaskData = { title: 'My New Subtask' };
 57 | 		mockUtils.readJSON.mockReturnValueOnce({
 58 | 			tasks: [{ id: 1, title: 'Feature Task', subtasks: [] }],
 59 | 			metadata: { description: 'Feature tasks' }
 60 | 		});
 61 | 
 62 | 		await addSubtask('tasks.json', '1', null, newSubtaskData, true, context);
 63 | 
 64 | 		expect(mockUtils.writeJSON).toHaveBeenCalledWith(
 65 | 			'tasks.json',
 66 | 			expect.any(Object),
 67 | 			'/fake/root',
 68 | 			'feature-branch'
 69 | 		);
 70 | 		const writtenData = mockUtils.writeJSON.mock.calls[0][1];
 71 | 		const parentTask = writtenData.tasks.find((t) => t.id === 1);
 72 | 		expect(parentTask.subtasks).toHaveLength(1);
 73 | 		expect(parentTask.subtasks[0].title).toBe('My New Subtask');
 74 | 	});
 75 | 
 76 | 	test('should add a new subtask to a parent task', async () => {
 77 | 		mockUtils.readJSON.mockReturnValueOnce({
 78 | 			tasks: [{ id: 1, title: 'Parent Task', subtasks: [] }]
 79 | 		});
 80 | 		const context = {};
 81 | 		const newSubtask = await addSubtask(
 82 | 			'tasks.json',
 83 | 			'1',
 84 | 			null,
 85 | 			{ title: 'New Subtask' },
 86 | 			true,
 87 | 			context
 88 | 		);
 89 | 		expect(newSubtask).toBeDefined();
 90 | 		expect(newSubtask.id).toBe(1);
 91 | 		expect(newSubtask.parentTaskId).toBe(1);
 92 | 		expect(mockUtils.writeJSON).toHaveBeenCalled();
 93 | 		const writeCallArgs = mockUtils.writeJSON.mock.calls[0][1]; // data is the second arg now
 94 | 		const parentTask = writeCallArgs.tasks.find((t) => t.id === 1);
 95 | 		expect(parentTask.subtasks).toHaveLength(1);
 96 | 		expect(parentTask.subtasks[0].title).toBe('New Subtask');
 97 | 		expect(mockGenerateTaskFiles).toHaveBeenCalled();
 98 | 	});
 99 | 
100 | 	test('should convert an existing task to a subtask', async () => {
101 | 		mockUtils.readJSON.mockReturnValueOnce({
102 | 			tasks: [
103 | 				{ id: 1, title: 'Parent Task', subtasks: [] },
104 | 				{ id: 2, title: 'Existing Task 2', subtasks: [] }
105 | 			]
106 | 		});
107 | 		const context = {};
108 | 		const convertedSubtask = await addSubtask(
109 | 			'tasks.json',
110 | 			'1',
111 | 			'2',
112 | 			null,
113 | 			true,
114 | 			context
115 | 		);
116 | 		expect(convertedSubtask.id).toBe(1);
117 | 		expect(convertedSubtask.parentTaskId).toBe(1);
118 | 		expect(convertedSubtask.title).toBe('Existing Task 2');
119 | 		expect(mockUtils.writeJSON).toHaveBeenCalled();
120 | 		const writeCallArgs = mockUtils.writeJSON.mock.calls[0][1];
121 | 		const parentTask = writeCallArgs.tasks.find((t) => t.id === 1);
122 | 		expect(parentTask.subtasks).toHaveLength(1);
123 | 		expect(parentTask.subtasks[0].title).toBe('Existing Task 2');
124 | 	});
125 | 
126 | 	test('should throw an error if parent task does not exist', async () => {
127 | 		mockUtils.readJSON.mockReturnValueOnce({
128 | 			tasks: [{ id: 1, title: 'Task 1', subtasks: [] }]
129 | 		});
130 | 		const context = {};
131 | 		await expect(
132 | 			addSubtask(
133 | 				'tasks.json',
134 | 				'99',
135 | 				null,
136 | 				{ title: 'New Subtask' },
137 | 				true,
138 | 				context
139 | 			)
140 | 		).rejects.toThrow('Parent task with ID 99 not found');
141 | 	});
142 | 
143 | 	test('should throw an error if trying to convert a non-existent task', async () => {
144 | 		mockUtils.readJSON.mockReturnValueOnce({
145 | 			tasks: [{ id: 1, title: 'Parent Task', subtasks: [] }]
146 | 		});
147 | 		const context = {};
148 | 		await expect(
149 | 			addSubtask('tasks.json', '1', '99', null, true, context)
150 | 		).rejects.toThrow('Task with ID 99 not found');
151 | 	});
152 | 
153 | 	test('should throw an error for circular dependency', async () => {
154 | 		mockUtils.readJSON.mockReturnValueOnce({
155 | 			tasks: [
156 | 				{ id: 1, title: 'Parent Task', subtasks: [] },
157 | 				{ id: 2, title: 'Child Task', subtasks: [] }
158 | 			]
159 | 		});
160 | 		mockTaskManager.isTaskDependentOn.mockImplementation(
161 | 			(tasks, parentTask, existingTaskIdNum) => {
162 | 				return parentTask.id === 1 && existingTaskIdNum === 2;
163 | 			}
164 | 		);
165 | 		const context = {};
166 | 		await expect(
167 | 			addSubtask('tasks.json', '1', '2', null, true, context)
168 | 		).rejects.toThrow(
169 | 			'Cannot create circular dependency: task 1 is already a subtask or dependent of task 2'
170 | 		);
171 | 	});
172 | });
173 | 
```

--------------------------------------------------------------------------------
/packages/tm-core/src/logger/logger.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * @fileoverview Core logger implementation
  3 |  */
  4 | 
  5 | import chalk from 'chalk';
  6 | 
  7 | export enum LogLevel {
  8 | 	SILENT = 0,
  9 | 	ERROR = 1,
 10 | 	WARN = 2,
 11 | 	INFO = 3,
 12 | 	DEBUG = 4
 13 | }
 14 | 
 15 | export interface LoggerConfig {
 16 | 	level?: LogLevel;
 17 | 	silent?: boolean;
 18 | 	prefix?: string;
 19 | 	timestamp?: boolean;
 20 | 	colors?: boolean;
 21 | 	// MCP mode silences all output
 22 | 	mcpMode?: boolean;
 23 | }
 24 | 
 25 | export class Logger {
 26 | 	private config: Required<LoggerConfig>;
 27 | 	private static readonly DEFAULT_CONFIG: Required<LoggerConfig> = {
 28 | 		level: LogLevel.WARN,
 29 | 		silent: false,
 30 | 		prefix: '',
 31 | 		timestamp: false,
 32 | 		colors: true,
 33 | 		mcpMode: false
 34 | 	};
 35 | 
 36 | 	constructor(config: LoggerConfig = {}) {
 37 | 		// Check environment variables
 38 | 		const envConfig: LoggerConfig = {};
 39 | 
 40 | 		// Check for MCP mode
 41 | 		if (
 42 | 			process.env.MCP_MODE === 'true' ||
 43 | 			process.env.TASK_MASTER_MCP === 'true'
 44 | 		) {
 45 | 			envConfig.mcpMode = true;
 46 | 		}
 47 | 
 48 | 		// Check for silent mode
 49 | 		if (
 50 | 			process.env.TASK_MASTER_SILENT === 'true' ||
 51 | 			process.env.TM_SILENT === 'true'
 52 | 		) {
 53 | 			envConfig.silent = true;
 54 | 		}
 55 | 
 56 | 		// Check for log level
 57 | 		if (process.env.TASK_MASTER_LOG_LEVEL || process.env.TM_LOG_LEVEL) {
 58 | 			const levelStr = (
 59 | 				process.env.TASK_MASTER_LOG_LEVEL ||
 60 | 				process.env.TM_LOG_LEVEL ||
 61 | 				''
 62 | 			).toUpperCase();
 63 | 			if (levelStr in LogLevel) {
 64 | 				envConfig.level = LogLevel[levelStr as keyof typeof LogLevel];
 65 | 			}
 66 | 		}
 67 | 
 68 | 		// Check for no colors
 69 | 		if (
 70 | 			process.env.NO_COLOR === 'true' ||
 71 | 			process.env.TASK_MASTER_NO_COLOR === 'true'
 72 | 		) {
 73 | 			envConfig.colors = false;
 74 | 		}
 75 | 
 76 | 		// Merge configs: defaults < constructor < environment
 77 | 		this.config = {
 78 | 			...Logger.DEFAULT_CONFIG,
 79 | 			...config,
 80 | 			...envConfig
 81 | 		};
 82 | 
 83 | 		// MCP mode overrides everything to be silent
 84 | 		if (this.config.mcpMode) {
 85 | 			this.config.silent = true;
 86 | 		}
 87 | 	}
 88 | 
 89 | 	/**
 90 | 	 * Check if logging is enabled for a given level
 91 | 	 */
 92 | 	private shouldLog(level: LogLevel): boolean {
 93 | 		if (this.config.silent || this.config.mcpMode) {
 94 | 			return false;
 95 | 		}
 96 | 		return level <= this.config.level;
 97 | 	}
 98 | 
 99 | 	/**
100 | 	 * Format a log message
101 | 	 */
102 | 	private formatMessage(
103 | 		level: LogLevel,
104 | 		message: string,
105 | 		...args: any[]
106 | 	): string {
107 | 		let formatted = '';
108 | 
109 | 		// Add timestamp if enabled
110 | 		if (this.config.timestamp) {
111 | 			const timestamp = new Date().toISOString();
112 | 			formatted += this.config.colors
113 | 				? chalk.gray(`[${timestamp}] `)
114 | 				: `[${timestamp}] `;
115 | 		}
116 | 
117 | 		// Add prefix if configured
118 | 		if (this.config.prefix) {
119 | 			formatted += this.config.colors
120 | 				? chalk.cyan(`[${this.config.prefix}] `)
121 | 				: `[${this.config.prefix}] `;
122 | 		}
123 | 
124 | 		// Skip level indicator for cleaner output
125 | 		// We can still color the message based on level
126 | 		if (this.config.colors) {
127 | 			switch (level) {
128 | 				case LogLevel.ERROR:
129 | 					message = chalk.red(message);
130 | 					break;
131 | 				case LogLevel.WARN:
132 | 					message = chalk.yellow(message);
133 | 					break;
134 | 				case LogLevel.INFO:
135 | 					// Info stays default color
136 | 					break;
137 | 				case LogLevel.DEBUG:
138 | 					message = chalk.gray(message);
139 | 					break;
140 | 			}
141 | 		}
142 | 
143 | 		// Add the message
144 | 		formatted += message;
145 | 
146 | 		// Add any additional arguments
147 | 		if (args.length > 0) {
148 | 			formatted +=
149 | 				' ' +
150 | 				args
151 | 					.map((arg) =>
152 | 						typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
153 | 					)
154 | 					.join(' ');
155 | 		}
156 | 
157 | 		return formatted;
158 | 	}
159 | 
160 | 	/**
161 | 	 * Log an error message
162 | 	 */
163 | 	error(message: string, ...args: any[]): void {
164 | 		if (!this.shouldLog(LogLevel.ERROR)) return;
165 | 		console.error(this.formatMessage(LogLevel.ERROR, message, ...args));
166 | 	}
167 | 
168 | 	/**
169 | 	 * Log a warning message
170 | 	 */
171 | 	warn(message: string, ...args: any[]): void {
172 | 		if (!this.shouldLog(LogLevel.WARN)) return;
173 | 		console.warn(this.formatMessage(LogLevel.WARN, message, ...args));
174 | 	}
175 | 
176 | 	/**
177 | 	 * Log an info message
178 | 	 */
179 | 	info(message: string, ...args: any[]): void {
180 | 		if (!this.shouldLog(LogLevel.INFO)) return;
181 | 		console.log(this.formatMessage(LogLevel.INFO, message, ...args));
182 | 	}
183 | 
184 | 	/**
185 | 	 * Log a debug message
186 | 	 */
187 | 	debug(message: string, ...args: any[]): void {
188 | 		if (!this.shouldLog(LogLevel.DEBUG)) return;
189 | 		console.log(this.formatMessage(LogLevel.DEBUG, message, ...args));
190 | 	}
191 | 
192 | 	/**
193 | 	 * Log a message without any formatting (raw output)
194 | 	 * Useful for CLI output that should appear as-is
195 | 	 */
196 | 	log(message: string, ...args: any[]): void {
197 | 		if (this.config.silent || this.config.mcpMode) return;
198 | 
199 | 		if (args.length > 0) {
200 | 			console.log(message, ...args);
201 | 		} else {
202 | 			console.log(message);
203 | 		}
204 | 	}
205 | 
206 | 	/**
207 | 	 * Update logger configuration
208 | 	 */
209 | 	setConfig(config: Partial<LoggerConfig>): void {
210 | 		this.config = {
211 | 			...this.config,
212 | 			...config
213 | 		};
214 | 
215 | 		// MCP mode always overrides to silent
216 | 		if (this.config.mcpMode) {
217 | 			this.config.silent = true;
218 | 		}
219 | 	}
220 | 
221 | 	/**
222 | 	 * Get current configuration
223 | 	 */
224 | 	getConfig(): Readonly<Required<LoggerConfig>> {
225 | 		return { ...this.config };
226 | 	}
227 | 
228 | 	/**
229 | 	 * Create a child logger with a prefix
230 | 	 */
231 | 	child(prefix: string, config?: Partial<LoggerConfig>): Logger {
232 | 		const childPrefix = this.config.prefix
233 | 			? `${this.config.prefix}:${prefix}`
234 | 			: prefix;
235 | 
236 | 		return new Logger({
237 | 			...this.config,
238 | 			...config,
239 | 			prefix: childPrefix
240 | 		});
241 | 	}
242 | }
243 | 
```

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

```javascript
  1 | /**
  2 |  * add-tag.js
  3 |  * Direct function implementation for creating a new tag
  4 |  */
  5 | 
  6 | import {
  7 | 	createTag,
  8 | 	createTagFromBranch
  9 | } from '../../../../scripts/modules/task-manager/tag-management.js';
 10 | import {
 11 | 	enableSilentMode,
 12 | 	disableSilentMode
 13 | } from '../../../../scripts/modules/utils.js';
 14 | import { createLogWrapper } from '../../tools/utils.js';
 15 | 
 16 | /**
 17 |  * Direct function wrapper for creating a new tag with error handling.
 18 |  *
 19 |  * @param {Object} args - Command arguments
 20 |  * @param {string} args.name - Name of the new tag to create
 21 |  * @param {boolean} [args.copyFromCurrent=false] - Whether to copy tasks from current tag
 22 |  * @param {string} [args.copyFromTag] - Specific tag to copy tasks from
 23 |  * @param {boolean} [args.fromBranch=false] - Create tag name from current git branch
 24 |  * @param {string} [args.description] - Optional description for the tag
 25 |  * @param {string} [args.tasksJsonPath] - Path to the tasks.json file (resolved by tool)
 26 |  * @param {string} [args.projectRoot] - Project root path
 27 |  * @param {Object} log - Logger object
 28 |  * @param {Object} context - Additional context (session)
 29 |  * @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
 30 |  */
 31 | export async function addTagDirect(args, log, context = {}) {
 32 | 	// Destructure expected args
 33 | 	const {
 34 | 		tasksJsonPath,
 35 | 		name,
 36 | 		copyFromCurrent = false,
 37 | 		copyFromTag,
 38 | 		fromBranch = false,
 39 | 		description,
 40 | 		projectRoot
 41 | 	} = args;
 42 | 	const { session } = context;
 43 | 
 44 | 	// Enable silent mode to prevent console logs from interfering with JSON response
 45 | 	enableSilentMode();
 46 | 
 47 | 	// Create logger wrapper using the utility
 48 | 	const mcpLog = createLogWrapper(log);
 49 | 
 50 | 	try {
 51 | 		// Check if tasksJsonPath was provided
 52 | 		if (!tasksJsonPath) {
 53 | 			log.error('addTagDirect called without tasksJsonPath');
 54 | 			disableSilentMode();
 55 | 			return {
 56 | 				success: false,
 57 | 				error: {
 58 | 					code: 'MISSING_ARGUMENT',
 59 | 					message: 'tasksJsonPath is required'
 60 | 				}
 61 | 			};
 62 | 		}
 63 | 
 64 | 		// Handle --from-branch option
 65 | 		if (fromBranch) {
 66 | 			log.info('Creating tag from current git branch');
 67 | 
 68 | 			// Import git utilities
 69 | 			const gitUtils = await import(
 70 | 				'../../../../scripts/modules/utils/git-utils.js'
 71 | 			);
 72 | 
 73 | 			// Check if we're in a git repository
 74 | 			if (!(await gitUtils.isGitRepository(projectRoot))) {
 75 | 				log.error('Not in a git repository');
 76 | 				disableSilentMode();
 77 | 				return {
 78 | 					success: false,
 79 | 					error: {
 80 | 						code: 'NOT_GIT_REPO',
 81 | 						message: 'Not in a git repository. Cannot use fromBranch option.'
 82 | 					}
 83 | 				};
 84 | 			}
 85 | 
 86 | 			// Get current git branch
 87 | 			const currentBranch = await gitUtils.getCurrentBranch(projectRoot);
 88 | 			if (!currentBranch) {
 89 | 				log.error('Could not determine current git branch');
 90 | 				disableSilentMode();
 91 | 				return {
 92 | 					success: false,
 93 | 					error: {
 94 | 						code: 'NO_CURRENT_BRANCH',
 95 | 						message: 'Could not determine current git branch.'
 96 | 					}
 97 | 				};
 98 | 			}
 99 | 
100 | 			// Prepare options for branch-based tag creation
101 | 			const branchOptions = {
102 | 				copyFromCurrent,
103 | 				copyFromTag,
104 | 				description:
105 | 					description || `Tag created from git branch "${currentBranch}"`
106 | 			};
107 | 
108 | 			// Call the createTagFromBranch function
109 | 			const result = await createTagFromBranch(
110 | 				tasksJsonPath,
111 | 				currentBranch,
112 | 				branchOptions,
113 | 				{
114 | 					session,
115 | 					mcpLog,
116 | 					projectRoot
117 | 				},
118 | 				'json' // outputFormat - use 'json' to suppress CLI UI
119 | 			);
120 | 
121 | 			// Restore normal logging
122 | 			disableSilentMode();
123 | 
124 | 			return {
125 | 				success: true,
126 | 				data: {
127 | 					branchName: result.branchName,
128 | 					tagName: result.tagName,
129 | 					created: result.created,
130 | 					mappingUpdated: result.mappingUpdated,
131 | 					message: `Successfully created tag "${result.tagName}" from git branch "${result.branchName}"`
132 | 				}
133 | 			};
134 | 		} else {
135 | 			// Check required parameters for regular tag creation
136 | 			if (!name || typeof name !== 'string') {
137 | 				log.error('Missing required parameter: name');
138 | 				disableSilentMode();
139 | 				return {
140 | 					success: false,
141 | 					error: {
142 | 						code: 'MISSING_PARAMETER',
143 | 						message: 'Tag name is required and must be a string'
144 | 					}
145 | 				};
146 | 			}
147 | 
148 | 			log.info(`Creating new tag: ${name}`);
149 | 
150 | 			// Prepare options
151 | 			const options = {
152 | 				copyFromCurrent,
153 | 				copyFromTag,
154 | 				description
155 | 			};
156 | 
157 | 			// Call the createTag function
158 | 			const result = await createTag(
159 | 				tasksJsonPath,
160 | 				name,
161 | 				options,
162 | 				{
163 | 					session,
164 | 					mcpLog,
165 | 					projectRoot
166 | 				},
167 | 				'json' // outputFormat - use 'json' to suppress CLI UI
168 | 			);
169 | 
170 | 			// Restore normal logging
171 | 			disableSilentMode();
172 | 
173 | 			return {
174 | 				success: true,
175 | 				data: {
176 | 					tagName: result.tagName,
177 | 					created: result.created,
178 | 					tasksCopied: result.tasksCopied,
179 | 					sourceTag: result.sourceTag,
180 | 					description: result.description,
181 | 					message: `Successfully created tag "${result.tagName}"`
182 | 				}
183 | 			};
184 | 		}
185 | 	} catch (error) {
186 | 		// Make sure to restore normal logging even if there's an error
187 | 		disableSilentMode();
188 | 
189 | 		log.error(`Error in addTagDirect: ${error.message}`);
190 | 		return {
191 | 			success: false,
192 | 			error: {
193 | 				code: error.code || 'ADD_TAG_ERROR',
194 | 				message: error.message
195 | 			}
196 | 		};
197 | 	}
198 | }
199 | 
```

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

```typescript
  1 | import React from 'react';
  2 | import { ExternalLink, Terminal, MessageSquare, Plus } from 'lucide-react';
  3 | import { TaskMasterLogo } from '../../components/TaskMasterLogo';
  4 | 
  5 | interface EmptyStateProps {
  6 | 	currentTag: string;
  7 | }
  8 | 
  9 | export const EmptyState: React.FC<EmptyStateProps> = ({ currentTag }) => {
 10 | 	return (
 11 | 		<div className="flex items-center justify-center h-full overflow-auto">
 12 | 			<div className="max-w-2xl mx-auto text-center p-8">
 13 | 				{/* Empty state illustration */}
 14 | 				<div className="mb-8 max-w-96 mx-auto">
 15 | 					<TaskMasterLogo className="w-32 h-32 mx-auto text-vscode-foreground/20" />
 16 | 				</div>
 17 | 
 18 | 				<h2 className="text-2xl font-semibold mb-2 text-vscode-foreground">
 19 | 					No tasks in "{currentTag}" tag
 20 | 				</h2>
 21 | 				<p className="text-vscode-foreground/70 mb-8">
 22 | 					Get started by adding tasks to this tag using the commands below
 23 | 				</p>
 24 | 
 25 | 				{/* Command suggestions */}
 26 | 				<div className="space-y-4 text-left">
 27 | 					<div className="bg-vscode-editor-background/50 border border-vscode-panel-border rounded-lg p-4">
 28 | 						<div className="flex items-center gap-2 mb-2">
 29 | 							<Terminal className="w-4 h-4 text-vscode-terminal-ansiGreen" />
 30 | 							<h3 className="font-medium">CLI Commands</h3>
 31 | 						</div>
 32 | 						<div className="space-y-2">
 33 | 							<div className="bg-vscode-editor-background rounded p-2 font-mono text-sm">
 34 | 								<span className="text-vscode-terminal-ansiYellow">
 35 | 									task-master
 36 | 								</span>{' '}
 37 | 								<span className="text-vscode-terminal-ansiCyan">parse-prd</span>{' '}
 38 | 								<span className="text-vscode-foreground/70">
 39 | 									&lt;path-to-prd&gt;
 40 | 								</span>{' '}
 41 | 								<span className="text-vscode-terminal-ansiMagenta">
 42 | 									--append
 43 | 								</span>
 44 | 								<div className="text-xs text-vscode-foreground/50 mt-1">
 45 | 									Parse a PRD and append tasks to current tag
 46 | 								</div>
 47 | 							</div>
 48 | 							<div className="bg-vscode-editor-background rounded p-2 font-mono text-sm">
 49 | 								<span className="text-vscode-terminal-ansiYellow">
 50 | 									task-master
 51 | 								</span>{' '}
 52 | 								<span className="text-vscode-terminal-ansiCyan">add-task</span>{' '}
 53 | 								<span className="text-vscode-terminal-ansiMagenta">
 54 | 									--prompt
 55 | 								</span>{' '}
 56 | 								<span className="text-vscode-foreground/70">
 57 | 									"Your task description"
 58 | 								</span>
 59 | 								<div className="text-xs text-vscode-foreground/50 mt-1">
 60 | 									Add a single task with AI assistance
 61 | 								</div>
 62 | 							</div>
 63 | 							<div className="bg-vscode-editor-background rounded p-2 font-mono text-sm">
 64 | 								<span className="text-vscode-terminal-ansiYellow">
 65 | 									task-master
 66 | 								</span>{' '}
 67 | 								<span className="text-vscode-terminal-ansiCyan">add-task</span>{' '}
 68 | 								<span className="text-vscode-terminal-ansiMagenta">--help</span>
 69 | 								<div className="text-xs text-vscode-foreground/50 mt-1">
 70 | 									View all options for adding tasks
 71 | 								</div>
 72 | 							</div>
 73 | 						</div>
 74 | 					</div>
 75 | 
 76 | 					<div className="bg-vscode-editor-background/50 border border-vscode-panel-border rounded-lg p-4">
 77 | 						<div className="flex items-center gap-2 mb-2">
 78 | 							<MessageSquare className="w-4 h-4 text-vscode-textLink-foreground" />
 79 | 							<h3 className="font-medium">MCP Examples</h3>
 80 | 						</div>
 81 | 						<div className="space-y-2 text-sm">
 82 | 							<div className="flex items-start gap-2">
 83 | 								<Plus className="w-4 h-4 mt-0.5 text-vscode-foreground/50" />
 84 | 								<div>
 85 | 									<div className="text-vscode-foreground">
 86 | 										"Add a task to tag {currentTag}: Implement user
 87 | 										authentication"
 88 | 									</div>
 89 | 								</div>
 90 | 							</div>
 91 | 							<div className="flex items-start gap-2">
 92 | 								<Plus className="w-4 h-4 mt-0.5 text-vscode-foreground/50" />
 93 | 								<div>
 94 | 									<div className="text-vscode-foreground">
 95 | 										"Parse this PRD and add tasks to {currentTag}: [paste PRD
 96 | 										content]"
 97 | 									</div>
 98 | 								</div>
 99 | 							</div>
100 | 							<div className="flex items-start gap-2">
101 | 								<Plus className="w-4 h-4 mt-0.5 text-vscode-foreground/50" />
102 | 								<div>
103 | 									<div className="text-vscode-foreground">
104 | 										"Create 5 tasks for building a REST API in tag {currentTag}"
105 | 									</div>
106 | 								</div>
107 | 							</div>
108 | 						</div>
109 | 					</div>
110 | 
111 | 					{/* Documentation link */}
112 | 					<div className="flex justify-center pt-4">
113 | 						<a
114 | 							href="https://docs.task-master.dev"
115 | 							className="inline-flex items-center gap-2 text-vscode-textLink-foreground hover:text-vscode-textLink-activeForeground transition-colors"
116 | 							onClick={(e) => {
117 | 								e.preventDefault();
118 | 								// Use VS Code API to open external link
119 | 								if (window.acquireVsCodeApi) {
120 | 									const vscode = window.acquireVsCodeApi();
121 | 									vscode.postMessage({
122 | 										type: 'openExternal',
123 | 										url: 'https://docs.task-master.dev'
124 | 									});
125 | 								}
126 | 							}}
127 | 						>
128 | 							<ExternalLink className="w-4 h-4" />
129 | 							<span className="text-sm font-medium">
130 | 								View TaskMaster Documentation
131 | 							</span>
132 | 						</a>
133 | 					</div>
134 | 				</div>
135 | 			</div>
136 | 		</div>
137 | 	);
138 | };
139 | 
```

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | import fs from 'fs';
  3 | import path from 'path';
  4 | import os from 'os';
  5 | 
  6 | // Mock external modules
  7 | jest.mock('child_process', () => ({
  8 | 	execSync: jest.fn()
  9 | }));
 10 | 
 11 | // Mock console methods
 12 | jest.mock('console', () => ({
 13 | 	log: jest.fn(),
 14 | 	info: jest.fn(),
 15 | 	warn: jest.fn(),
 16 | 	error: jest.fn(),
 17 | 	clear: jest.fn()
 18 | }));
 19 | 
 20 | describe('Roo Integration', () => {
 21 | 	let tempDir;
 22 | 
 23 | 	beforeEach(() => {
 24 | 		jest.clearAllMocks();
 25 | 
 26 | 		// Create a temporary directory for testing
 27 | 		tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-master-test-'));
 28 | 
 29 | 		// Spy on fs methods
 30 | 		jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
 31 | 		jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
 32 | 			if (filePath.toString().includes('.roomodes')) {
 33 | 				return 'Existing roomodes content';
 34 | 			}
 35 | 			if (filePath.toString().includes('-rules')) {
 36 | 				return 'Existing mode rules content';
 37 | 			}
 38 | 			return '{}';
 39 | 		});
 40 | 		jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
 41 | 		jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
 42 | 	});
 43 | 
 44 | 	afterEach(() => {
 45 | 		// Clean up the temporary directory
 46 | 		try {
 47 | 			fs.rmSync(tempDir, { recursive: true, force: true });
 48 | 		} catch (err) {
 49 | 			console.error(`Error cleaning up: ${err.message}`);
 50 | 		}
 51 | 	});
 52 | 
 53 | 	// Test function that simulates the createProjectStructure behavior for Roo files
 54 | 	function mockCreateRooStructure() {
 55 | 		// Create main .roo directory
 56 | 		fs.mkdirSync(path.join(tempDir, '.roo'), { recursive: true });
 57 | 
 58 | 		// Create rules directory
 59 | 		fs.mkdirSync(path.join(tempDir, '.roo', 'rules'), { recursive: true });
 60 | 
 61 | 		// Create mode-specific rule directories
 62 | 		const rooModes = [
 63 | 			'architect',
 64 | 			'ask',
 65 | 			'orchestrator',
 66 | 			'code',
 67 | 			'debug',
 68 | 			'test'
 69 | 		];
 70 | 		for (const mode of rooModes) {
 71 | 			fs.mkdirSync(path.join(tempDir, '.roo', `rules-${mode}`), {
 72 | 				recursive: true
 73 | 			});
 74 | 			fs.writeFileSync(
 75 | 				path.join(tempDir, '.roo', `rules-${mode}`, `${mode}-rules`),
 76 | 				`Content for ${mode} rules`
 77 | 			);
 78 | 		}
 79 | 
 80 | 		// Create additional directories
 81 | 		fs.mkdirSync(path.join(tempDir, '.roo', 'config'), { recursive: true });
 82 | 		fs.mkdirSync(path.join(tempDir, '.roo', 'templates'), { recursive: true });
 83 | 		fs.mkdirSync(path.join(tempDir, '.roo', 'logs'), { recursive: true });
 84 | 
 85 | 		// Copy .roomodes file
 86 | 		fs.writeFileSync(path.join(tempDir, '.roomodes'), 'Roomodes file content');
 87 | 	}
 88 | 
 89 | 	test('creates all required .roo directories', () => {
 90 | 		// Act
 91 | 		mockCreateRooStructure();
 92 | 
 93 | 		// Assert
 94 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(tempDir, '.roo'), {
 95 | 			recursive: true
 96 | 		});
 97 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
 98 | 			path.join(tempDir, '.roo', 'rules'),
 99 | 			{ recursive: true }
100 | 		);
101 | 
102 | 		// Verify all mode directories are created
103 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
104 | 			path.join(tempDir, '.roo', 'rules-architect'),
105 | 			{ recursive: true }
106 | 		);
107 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
108 | 			path.join(tempDir, '.roo', 'rules-ask'),
109 | 			{ recursive: true }
110 | 		);
111 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
112 | 			path.join(tempDir, '.roo', 'rules-orchestrator'),
113 | 			{ recursive: true }
114 | 		);
115 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
116 | 			path.join(tempDir, '.roo', 'rules-code'),
117 | 			{ recursive: true }
118 | 		);
119 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
120 | 			path.join(tempDir, '.roo', 'rules-debug'),
121 | 			{ recursive: true }
122 | 		);
123 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
124 | 			path.join(tempDir, '.roo', 'rules-test'),
125 | 			{ recursive: true }
126 | 		);
127 | 	});
128 | 
129 | 	test('creates rule files for all modes', () => {
130 | 		// Act
131 | 		mockCreateRooStructure();
132 | 
133 | 		// Assert - check all rule files are created
134 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
135 | 			path.join(tempDir, '.roo', 'rules-architect', 'architect-rules'),
136 | 			expect.any(String)
137 | 		);
138 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
139 | 			path.join(tempDir, '.roo', 'rules-ask', 'ask-rules'),
140 | 			expect.any(String)
141 | 		);
142 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
143 | 			path.join(tempDir, '.roo', 'rules-orchestrator', 'orchestrator-rules'),
144 | 			expect.any(String)
145 | 		);
146 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
147 | 			path.join(tempDir, '.roo', 'rules-code', 'code-rules'),
148 | 			expect.any(String)
149 | 		);
150 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
151 | 			path.join(tempDir, '.roo', 'rules-debug', 'debug-rules'),
152 | 			expect.any(String)
153 | 		);
154 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
155 | 			path.join(tempDir, '.roo', 'rules-test', 'test-rules'),
156 | 			expect.any(String)
157 | 		);
158 | 	});
159 | 
160 | 	test('creates .roomodes file in project root', () => {
161 | 		// Act
162 | 		mockCreateRooStructure();
163 | 
164 | 		// Assert
165 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
166 | 			path.join(tempDir, '.roomodes'),
167 | 			expect.any(String)
168 | 		);
169 | 	});
170 | 
171 | 	test('creates additional required Roo directories', () => {
172 | 		// Act
173 | 		mockCreateRooStructure();
174 | 
175 | 		// Assert
176 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
177 | 			path.join(tempDir, '.roo', 'config'),
178 | 			{ recursive: true }
179 | 		);
180 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
181 | 			path.join(tempDir, '.roo', 'templates'),
182 | 			{ recursive: true }
183 | 		);
184 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
185 | 			path.join(tempDir, '.roo', 'logs'),
186 | 			{ recursive: true }
187 | 		);
188 | 	});
189 | });
190 | 
```

--------------------------------------------------------------------------------
/apps/extension/src/components/TaskDetails/DetailsSection.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import type React from 'react';
  2 | import { CollapsibleSection } from '@/components/ui/CollapsibleSection';
  3 | 
  4 | interface MarkdownRendererProps {
  5 | 	content: string;
  6 | 	className?: string;
  7 | }
  8 | 
  9 | const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({
 10 | 	content,
 11 | 	className = ''
 12 | }) => {
 13 | 	const parseMarkdown = (text: string) => {
 14 | 		const parts = [];
 15 | 		const lines = text.split('\n');
 16 | 		let currentBlock = [];
 17 | 		let inCodeBlock = false;
 18 | 		let codeLanguage = '';
 19 | 
 20 | 		for (let i = 0; i < lines.length; i++) {
 21 | 			const line = lines[i];
 22 | 
 23 | 			if (line.startsWith('```')) {
 24 | 				if (inCodeBlock) {
 25 | 					if (currentBlock.length > 0) {
 26 | 						parts.push({
 27 | 							type: 'code',
 28 | 							content: currentBlock.join('\n'),
 29 | 							language: codeLanguage
 30 | 						});
 31 | 						currentBlock = [];
 32 | 					}
 33 | 					inCodeBlock = false;
 34 | 					codeLanguage = '';
 35 | 				} else {
 36 | 					if (currentBlock.length > 0) {
 37 | 						parts.push({
 38 | 							type: 'text',
 39 | 							content: currentBlock.join('\n')
 40 | 						});
 41 | 						currentBlock = [];
 42 | 					}
 43 | 					inCodeBlock = true;
 44 | 					codeLanguage = line.substring(3).trim();
 45 | 				}
 46 | 			} else {
 47 | 				currentBlock.push(line);
 48 | 			}
 49 | 		}
 50 | 
 51 | 		if (currentBlock.length > 0) {
 52 | 			parts.push({
 53 | 				type: inCodeBlock ? 'code' : 'text',
 54 | 				content: currentBlock.join('\n'),
 55 | 				language: codeLanguage
 56 | 			});
 57 | 		}
 58 | 
 59 | 		return parts;
 60 | 	};
 61 | 
 62 | 	const parts = parseMarkdown(content);
 63 | 
 64 | 	return (
 65 | 		<div className={className}>
 66 | 			{parts.map((part, index) => {
 67 | 				if (part.type === 'code') {
 68 | 					return (
 69 | 						<pre
 70 | 							key={index}
 71 | 							className="bg-vscode-editor-background rounded-md p-4 overflow-x-auto mb-4 border border-vscode-editor-lineHighlightBorder"
 72 | 						>
 73 | 							<code className="text-sm text-vscode-editor-foreground font-mono">
 74 | 								{part.content}
 75 | 							</code>
 76 | 						</pre>
 77 | 					);
 78 | 				}
 79 | 				return (
 80 | 					<div key={index} className="whitespace-pre-wrap mb-4 last:mb-0">
 81 | 						{part.content.split('\n').map((line, lineIndex) => {
 82 | 							const bulletMatch = line.match(/^(\s*)([-*])\s(.+)$/);
 83 | 							if (bulletMatch) {
 84 | 								const indent = bulletMatch[1].length;
 85 | 								return (
 86 | 									<div
 87 | 										key={lineIndex}
 88 | 										className="flex gap-2 mb-1"
 89 | 										style={{ paddingLeft: `${indent * 16}px` }}
 90 | 									>
 91 | 										<span className="text-vscode-foreground/60">•</span>
 92 | 										<span className="flex-1">{bulletMatch[3]}</span>
 93 | 									</div>
 94 | 								);
 95 | 							}
 96 | 
 97 | 							const numberedMatch = line.match(/^(\s*)(\d+\.)\s(.+)$/);
 98 | 							if (numberedMatch) {
 99 | 								const indent = numberedMatch[1].length;
100 | 								return (
101 | 									<div
102 | 										key={lineIndex}
103 | 										className="flex gap-2 mb-1"
104 | 										style={{ paddingLeft: `${indent * 16}px` }}
105 | 									>
106 | 										<span className="text-vscode-foreground/60 font-mono">
107 | 											{numberedMatch[2]}
108 | 										</span>
109 | 										<span className="flex-1">{numberedMatch[3]}</span>
110 | 									</div>
111 | 								);
112 | 							}
113 | 
114 | 							const headingMatch = line.match(/^(#{1,6})\s(.+)$/);
115 | 							if (headingMatch) {
116 | 								const level = headingMatch[1].length;
117 | 								const headingLevel = Math.min(level + 2, 6);
118 | 								const headingClassName =
119 | 									'font-semibold text-vscode-foreground mb-2 mt-4 first:mt-0';
120 | 
121 | 								switch (headingLevel) {
122 | 									case 3:
123 | 										return (
124 | 											<h3 key={lineIndex} className={headingClassName}>
125 | 												{headingMatch[2]}
126 | 											</h3>
127 | 										);
128 | 									case 4:
129 | 										return (
130 | 											<h4 key={lineIndex} className={headingClassName}>
131 | 												{headingMatch[2]}
132 | 											</h4>
133 | 										);
134 | 									case 5:
135 | 										return (
136 | 											<h5 key={lineIndex} className={headingClassName}>
137 | 												{headingMatch[2]}
138 | 											</h5>
139 | 										);
140 | 									case 6:
141 | 										return (
142 | 											<h6 key={lineIndex} className={headingClassName}>
143 | 												{headingMatch[2]}
144 | 											</h6>
145 | 										);
146 | 									default:
147 | 										return (
148 | 											<h3 key={lineIndex} className={headingClassName}>
149 | 												{headingMatch[2]}
150 | 											</h3>
151 | 										);
152 | 								}
153 | 							}
154 | 
155 | 							if (line.trim() === '') {
156 | 								return <div key={lineIndex} className="h-2" />;
157 | 							}
158 | 
159 | 							return (
160 | 								<div key={lineIndex} className="mb-2 last:mb-0">
161 | 									{line}
162 | 								</div>
163 | 							);
164 | 						})}
165 | 					</div>
166 | 				);
167 | 			})}
168 | 		</div>
169 | 	);
170 | };
171 | 
172 | interface DetailsSectionProps {
173 | 	title: string;
174 | 	content?: string;
175 | 	error?: string | null;
176 | 	emptyMessage?: string;
177 | 	defaultExpanded?: boolean;
178 | }
179 | 
180 | export const DetailsSection: React.FC<DetailsSectionProps> = ({
181 | 	title,
182 | 	content,
183 | 	error,
184 | 	emptyMessage = 'No details available',
185 | 	defaultExpanded = false
186 | }) => {
187 | 	return (
188 | 		<CollapsibleSection title={title} defaultExpanded={defaultExpanded}>
189 | 			<div className={title.toLowerCase().replace(/\s+/g, '-') + '-content'}>
190 | 				{error ? (
191 | 					<div className="text-sm text-red-400 py-2">
192 | 						Error loading {title.toLowerCase()}: {error}
193 | 					</div>
194 | 				) : content !== undefined && content !== '' ? (
195 | 					<MarkdownRenderer content={content} />
196 | 				) : (
197 | 					<div className="text-sm text-vscode-foreground/50 py-2">
198 | 						{emptyMessage}
199 | 					</div>
200 | 				)}
201 | 			</div>
202 | 		</CollapsibleSection>
203 | 	);
204 | };
205 | 
```

--------------------------------------------------------------------------------
/src/utils/timeout-manager.js:
--------------------------------------------------------------------------------

```javascript
  1 | import { StreamingError, STREAMING_ERROR_CODES } from './stream-parser.js';
  2 | 
  3 | /**
  4 |  * Utility class for managing timeouts in async operations
  5 |  * Reduces code duplication for timeout handling patterns
  6 |  */
  7 | export class TimeoutManager {
  8 | 	/**
  9 | 	 * Wraps a promise with a timeout that will reject if not resolved in time
 10 | 	 *
 11 | 	 * @param {Promise} promise - The promise to wrap with timeout
 12 | 	 * @param {number} timeoutMs - Timeout duration in milliseconds
 13 | 	 * @param {string} operationName - Name of the operation for error messages
 14 | 	 * @returns {Promise} The result of the promise or throws timeout error
 15 | 	 *
 16 | 	 * @example
 17 | 	 * const result = await TimeoutManager.withTimeout(
 18 | 	 *   fetchData(),
 19 | 	 *   5000,
 20 | 	 *   'Data fetch operation'
 21 | 	 * );
 22 | 	 */
 23 | 	static async withTimeout(promise, timeoutMs, operationName = 'Operation') {
 24 | 		let timeoutHandle;
 25 | 
 26 | 		const timeoutPromise = new Promise((_, reject) => {
 27 | 			timeoutHandle = setTimeout(() => {
 28 | 				reject(
 29 | 					new StreamingError(
 30 | 						`${operationName} timed out after ${timeoutMs / 1000} seconds`,
 31 | 						STREAMING_ERROR_CODES.STREAM_PROCESSING_FAILED
 32 | 					)
 33 | 				);
 34 | 			}, timeoutMs);
 35 | 		});
 36 | 
 37 | 		try {
 38 | 			// Race between the actual promise and the timeout
 39 | 			const result = await Promise.race([promise, timeoutPromise]);
 40 | 			// Clear timeout if promise resolved first
 41 | 			clearTimeout(timeoutHandle);
 42 | 			return result;
 43 | 		} catch (error) {
 44 | 			// Always clear timeout on error
 45 | 			clearTimeout(timeoutHandle);
 46 | 			throw error;
 47 | 		}
 48 | 	}
 49 | 
 50 | 	/**
 51 | 	 * Wraps a promise with a timeout, but returns undefined instead of throwing on timeout
 52 | 	 * Useful for optional operations that shouldn't fail the main flow
 53 | 	 *
 54 | 	 * @param {Promise} promise - The promise to wrap with timeout
 55 | 	 * @param {number} timeoutMs - Timeout duration in milliseconds
 56 | 	 * @param {*} defaultValue - Value to return on timeout (default: undefined)
 57 | 	 * @returns {Promise} The result of the promise or defaultValue on timeout
 58 | 	 *
 59 | 	 * @example
 60 | 	 * const usage = await TimeoutManager.withSoftTimeout(
 61 | 	 *   getUsageStats(),
 62 | 	 *   1000,
 63 | 	 *   { tokens: 0 }
 64 | 	 * );
 65 | 	 */
 66 | 	static async withSoftTimeout(promise, timeoutMs, defaultValue = undefined) {
 67 | 		let timeoutHandle;
 68 | 
 69 | 		const timeoutPromise = new Promise((resolve) => {
 70 | 			timeoutHandle = setTimeout(() => {
 71 | 				resolve(defaultValue);
 72 | 			}, timeoutMs);
 73 | 		});
 74 | 
 75 | 		try {
 76 | 			const result = await Promise.race([promise, timeoutPromise]);
 77 | 			clearTimeout(timeoutHandle);
 78 | 			return result;
 79 | 		} catch (error) {
 80 | 			// On error, clear timeout and return default value
 81 | 			clearTimeout(timeoutHandle);
 82 | 			return defaultValue;
 83 | 		}
 84 | 	}
 85 | 
 86 | 	/**
 87 | 	 * Creates a reusable timeout controller for multiple operations
 88 | 	 * Useful when you need to apply the same timeout to multiple promises
 89 | 	 *
 90 | 	 * @param {number} timeoutMs - Timeout duration in milliseconds
 91 | 	 * @param {string} operationName - Base name for operations
 92 | 	 * @returns {Object} Controller with wrap method
 93 | 	 *
 94 | 	 * @example
 95 | 	 * const controller = TimeoutManager.createController(60000, 'AI Service');
 96 | 	 * const result1 = await controller.wrap(service.call1(), 'call 1');
 97 | 	 * const result2 = await controller.wrap(service.call2(), 'call 2');
 98 | 	 */
 99 | 	static createController(timeoutMs, operationName = 'Operation') {
100 | 		return {
101 | 			timeoutMs,
102 | 			operationName,
103 | 
104 | 			async wrap(promise, specificName = null) {
105 | 				const fullName = specificName
106 | 					? `${operationName} - ${specificName}`
107 | 					: operationName;
108 | 				return TimeoutManager.withTimeout(promise, timeoutMs, fullName);
109 | 			},
110 | 
111 | 			async wrapSoft(promise, defaultValue = undefined) {
112 | 				return TimeoutManager.withSoftTimeout(promise, timeoutMs, defaultValue);
113 | 			}
114 | 		};
115 | 	}
116 | 
117 | 	/**
118 | 	 * Checks if an error is a timeout error from this manager
119 | 	 *
120 | 	 * @param {Error} error - The error to check
121 | 	 * @returns {boolean} True if this is a timeout error
122 | 	 */
123 | 	static isTimeoutError(error) {
124 | 		return (
125 | 			error instanceof StreamingError &&
126 | 			error.code === STREAMING_ERROR_CODES.STREAM_PROCESSING_FAILED &&
127 | 			error.message.includes('timed out')
128 | 		);
129 | 	}
130 | }
131 | 
132 | /**
133 |  * Duration helper class for more readable timeout specifications
134 |  */
135 | export class Duration {
136 | 	constructor(value, unit = 'ms') {
137 | 		this.milliseconds = this._toMilliseconds(value, unit);
138 | 	}
139 | 
140 | 	static milliseconds(value) {
141 | 		return new Duration(value, 'ms');
142 | 	}
143 | 
144 | 	static seconds(value) {
145 | 		return new Duration(value, 's');
146 | 	}
147 | 
148 | 	static minutes(value) {
149 | 		return new Duration(value, 'm');
150 | 	}
151 | 
152 | 	static hours(value) {
153 | 		return new Duration(value, 'h');
154 | 	}
155 | 
156 | 	get seconds() {
157 | 		return this.milliseconds / 1000;
158 | 	}
159 | 
160 | 	get minutes() {
161 | 		return this.milliseconds / 60000;
162 | 	}
163 | 
164 | 	get hours() {
165 | 		return this.milliseconds / 3600000;
166 | 	}
167 | 
168 | 	toString() {
169 | 		if (this.milliseconds < 1000) {
170 | 			return `${this.milliseconds}ms`;
171 | 		} else if (this.milliseconds < 60000) {
172 | 			return `${this.seconds}s`;
173 | 		} else if (this.milliseconds < 3600000) {
174 | 			return `${Math.floor(this.minutes)}m ${Math.floor(this.seconds % 60)}s`;
175 | 		} else {
176 | 			return `${Math.floor(this.hours)}h ${Math.floor(this.minutes % 60)}m`;
177 | 		}
178 | 	}
179 | 
180 | 	_toMilliseconds(value, unit) {
181 | 		const conversions = {
182 | 			ms: 1,
183 | 			s: 1000,
184 | 			m: 60000,
185 | 			h: 3600000
186 | 		};
187 | 		return value * (conversions[unit] || 1);
188 | 	}
189 | }
190 | 
```

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

```javascript
  1 | import { jest } from '@jest/globals';
  2 | import fs from 'fs';
  3 | import path from 'path';
  4 | import os from 'os';
  5 | 
  6 | // Mock external modules
  7 | jest.mock('child_process', () => ({
  8 | 	execSync: jest.fn()
  9 | }));
 10 | 
 11 | // Mock console methods
 12 | jest.mock('console', () => ({
 13 | 	log: jest.fn(),
 14 | 	info: jest.fn(),
 15 | 	warn: jest.fn(),
 16 | 	error: jest.fn(),
 17 | 	clear: jest.fn()
 18 | }));
 19 | 
 20 | describe('Kilo Integration', () => {
 21 | 	let tempDir;
 22 | 
 23 | 	beforeEach(() => {
 24 | 		jest.clearAllMocks();
 25 | 
 26 | 		// Create a temporary directory for testing
 27 | 		tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-master-test-'));
 28 | 
 29 | 		// Spy on fs methods
 30 | 		jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
 31 | 		jest.spyOn(fs, 'readFileSync').mockImplementation((filePath) => {
 32 | 			if (filePath.toString().includes('.kilocodemodes')) {
 33 | 				return 'Existing kilocodemodes content';
 34 | 			}
 35 | 			if (filePath.toString().includes('-rules')) {
 36 | 				return 'Existing mode rules content';
 37 | 			}
 38 | 			return '{}';
 39 | 		});
 40 | 		jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
 41 | 		jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {});
 42 | 	});
 43 | 
 44 | 	afterEach(() => {
 45 | 		// Clean up the temporary directory
 46 | 		try {
 47 | 			fs.rmSync(tempDir, { recursive: true, force: true });
 48 | 		} catch (err) {
 49 | 			console.error(`Error cleaning up: ${err.message}`);
 50 | 		}
 51 | 	});
 52 | 
 53 | 	// Test function that simulates the createProjectStructure behavior for Kilo files
 54 | 	function mockCreateKiloStructure() {
 55 | 		// Create main .kilo directory
 56 | 		fs.mkdirSync(path.join(tempDir, '.kilo'), { recursive: true });
 57 | 
 58 | 		// Create rules directory
 59 | 		fs.mkdirSync(path.join(tempDir, '.kilo', 'rules'), { recursive: true });
 60 | 
 61 | 		// Create mode-specific rule directories
 62 | 		const kiloModes = [
 63 | 			'architect',
 64 | 			'ask',
 65 | 			'orchestrator',
 66 | 			'code',
 67 | 			'debug',
 68 | 			'test'
 69 | 		];
 70 | 		for (const mode of kiloModes) {
 71 | 			fs.mkdirSync(path.join(tempDir, '.kilo', `rules-${mode}`), {
 72 | 				recursive: true
 73 | 			});
 74 | 			fs.writeFileSync(
 75 | 				path.join(tempDir, '.kilo', `rules-${mode}`, `${mode}-rules`),
 76 | 				`Content for ${mode} rules`
 77 | 			);
 78 | 		}
 79 | 
 80 | 		// Create additional directories
 81 | 		fs.mkdirSync(path.join(tempDir, '.kilo', 'config'), { recursive: true });
 82 | 		fs.mkdirSync(path.join(tempDir, '.kilo', 'templates'), { recursive: true });
 83 | 		fs.mkdirSync(path.join(tempDir, '.kilo', 'logs'), { recursive: true });
 84 | 
 85 | 		// Copy .kilocodemodes file
 86 | 		fs.writeFileSync(
 87 | 			path.join(tempDir, '.kilocodemodes'),
 88 | 			'Kilocodemodes file content'
 89 | 		);
 90 | 	}
 91 | 
 92 | 	test('creates all required .kilo directories', () => {
 93 | 		// Act
 94 | 		mockCreateKiloStructure();
 95 | 
 96 | 		// Assert
 97 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(tempDir, '.kilo'), {
 98 | 			recursive: true
 99 | 		});
100 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
101 | 			path.join(tempDir, '.kilo', 'rules'),
102 | 			{ recursive: true }
103 | 		);
104 | 
105 | 		// Verify all mode directories are created
106 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
107 | 			path.join(tempDir, '.kilo', 'rules-architect'),
108 | 			{ recursive: true }
109 | 		);
110 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
111 | 			path.join(tempDir, '.kilo', 'rules-ask'),
112 | 			{ recursive: true }
113 | 		);
114 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
115 | 			path.join(tempDir, '.kilo', 'rules-orchestrator'),
116 | 			{ recursive: true }
117 | 		);
118 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
119 | 			path.join(tempDir, '.kilo', 'rules-code'),
120 | 			{ recursive: true }
121 | 		);
122 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
123 | 			path.join(tempDir, '.kilo', 'rules-debug'),
124 | 			{ recursive: true }
125 | 		);
126 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
127 | 			path.join(tempDir, '.kilo', 'rules-test'),
128 | 			{ recursive: true }
129 | 		);
130 | 	});
131 | 
132 | 	test('creates rule files for all modes', () => {
133 | 		// Act
134 | 		mockCreateKiloStructure();
135 | 
136 | 		// Assert - check all rule files are created
137 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
138 | 			path.join(tempDir, '.kilo', 'rules-architect', 'architect-rules'),
139 | 			expect.any(String)
140 | 		);
141 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
142 | 			path.join(tempDir, '.kilo', 'rules-ask', 'ask-rules'),
143 | 			expect.any(String)
144 | 		);
145 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
146 | 			path.join(tempDir, '.kilo', 'rules-orchestrator', 'orchestrator-rules'),
147 | 			expect.any(String)
148 | 		);
149 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
150 | 			path.join(tempDir, '.kilo', 'rules-code', 'code-rules'),
151 | 			expect.any(String)
152 | 		);
153 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
154 | 			path.join(tempDir, '.kilo', 'rules-debug', 'debug-rules'),
155 | 			expect.any(String)
156 | 		);
157 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
158 | 			path.join(tempDir, '.kilo', 'rules-test', 'test-rules'),
159 | 			expect.any(String)
160 | 		);
161 | 	});
162 | 
163 | 	test('creates .kilocodemodes file in project root', () => {
164 | 		// Act
165 | 		mockCreateKiloStructure();
166 | 
167 | 		// Assert
168 | 		expect(fs.writeFileSync).toHaveBeenCalledWith(
169 | 			path.join(tempDir, '.kilocodemodes'),
170 | 			expect.any(String)
171 | 		);
172 | 	});
173 | 
174 | 	test('creates additional required Kilo directories', () => {
175 | 		// Act
176 | 		mockCreateKiloStructure();
177 | 
178 | 		// Assert
179 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
180 | 			path.join(tempDir, '.kilo', 'config'),
181 | 			{ recursive: true }
182 | 		);
183 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
184 | 			path.join(tempDir, '.kilo', 'templates'),
185 | 			{ recursive: true }
186 | 		);
187 | 		expect(fs.mkdirSync).toHaveBeenCalledWith(
188 | 			path.join(tempDir, '.kilo', 'logs'),
189 | 			{ recursive: true }
190 | 		);
191 | 	});
192 | });
193 | 
```

--------------------------------------------------------------------------------
/apps/extension/package.mjs:
--------------------------------------------------------------------------------

```
  1 | import { execSync } from 'child_process';
  2 | import path from 'path';
  3 | import { fileURLToPath } from 'url';
  4 | import fs from 'fs-extra';
  5 | 
  6 | // --- Configuration ---
  7 | const __filename = fileURLToPath(import.meta.url);
  8 | const __dirname = path.dirname(__filename);
  9 | 
 10 | const packageDir = path.resolve(__dirname, 'vsix-build');
 11 | // --- End Configuration ---
 12 | 
 13 | try {
 14 | 	console.log('🚀 Starting packaging process...');
 15 | 
 16 | 	// 1. Build Project
 17 | 	console.log('\nBuilding JavaScript...');
 18 | 	execSync('npm run build:js', { stdio: 'inherit' });
 19 | 	console.log('\nBuilding CSS...');
 20 | 	execSync('npm run build:css', { stdio: 'inherit' });
 21 | 
 22 | 	// 2. Prepare Clean Directory
 23 | 	console.log(`\nPreparing clean directory at: ${packageDir}`);
 24 | 	fs.emptyDirSync(packageDir);
 25 | 
 26 | 	// 3. Copy Build Artifacts (excluding source maps)
 27 | 	console.log('Copying build artifacts...');
 28 | 	const distDir = path.resolve(__dirname, 'dist');
 29 | 	const targetDistDir = path.resolve(packageDir, 'dist');
 30 | 	fs.ensureDirSync(targetDistDir);
 31 | 
 32 | 	// Only copy the files we need (exclude .map files)
 33 | 	const filesToCopy = ['extension.js', 'index.js', 'index.css', 'sidebar.js'];
 34 | 	for (const file of filesToCopy) {
 35 | 		const srcFile = path.resolve(distDir, file);
 36 | 		const destFile = path.resolve(targetDistDir, file);
 37 | 		if (fs.existsSync(srcFile)) {
 38 | 			fs.copySync(srcFile, destFile);
 39 | 			console.log(`  - Copied dist/${file}`);
 40 | 		}
 41 | 	}
 42 | 
 43 | 	// 4. Copy additional files
 44 | 	const additionalFiles = ['README.md', 'CHANGELOG.md', 'AGENTS.md'];
 45 | 	for (const file of additionalFiles) {
 46 | 		if (fs.existsSync(path.resolve(__dirname, file))) {
 47 | 			fs.copySync(
 48 | 				path.resolve(__dirname, file),
 49 | 				path.resolve(packageDir, file)
 50 | 			);
 51 | 			console.log(`  - Copied ${file}`);
 52 | 		}
 53 | 	}
 54 | 
 55 | 	// 5. Sync versions and prepare the final package.json
 56 | 	console.log('Syncing versions and preparing the final package.json...');
 57 | 
 58 | 	// Read current versions
 59 | 	const devPackagePath = path.resolve(__dirname, 'package.json');
 60 | 	const publishPackagePath = path.resolve(__dirname, 'package.publish.json');
 61 | 
 62 | 	const devPackage = JSON.parse(fs.readFileSync(devPackagePath, 'utf8'));
 63 | 	const publishPackage = JSON.parse(
 64 | 		fs.readFileSync(publishPackagePath, 'utf8')
 65 | 	);
 66 | 
 67 | 	// Handle RC versions for VS Code Marketplace
 68 | 	let finalVersion = devPackage.version;
 69 | 	if (finalVersion.includes('-rc.')) {
 70 | 		console.log(
 71 | 			'  - Detected RC version, transforming for VS Code Marketplace...'
 72 | 		);
 73 | 
 74 | 		// Extract base version and RC number
 75 | 		const baseVersion = finalVersion.replace(/-rc\.\d+$/, '');
 76 | 		const rcMatch = finalVersion.match(/rc\.(\d+)/);
 77 | 		const rcNumber = rcMatch ? parseInt(rcMatch[1]) : 0;
 78 | 
 79 | 		// For each RC iteration, increment the patch version
 80 | 		// This ensures unique versions in VS Code Marketplace
 81 | 		if (rcNumber > 0) {
 82 | 			const [major, minor, patch] = baseVersion.split('.').map(Number);
 83 | 			finalVersion = `${major}.${minor}.${patch + rcNumber}`;
 84 | 			console.log(
 85 | 				`  - RC version mapping: ${devPackage.version} → ${finalVersion}`
 86 | 			);
 87 | 		} else {
 88 | 			finalVersion = baseVersion;
 89 | 			console.log(
 90 | 				`  - RC version mapping: ${devPackage.version} → ${finalVersion}`
 91 | 			);
 92 | 		}
 93 | 	}
 94 | 
 95 | 	// Check if versions need updating
 96 | 	if (publishPackage.version !== finalVersion) {
 97 | 		console.log(
 98 | 			`  - Version sync needed: ${publishPackage.version} → ${finalVersion}`
 99 | 		);
100 | 		publishPackage.version = finalVersion;
101 | 
102 | 		// Update the source package.publish.json file with the final version
103 | 		fs.writeFileSync(
104 | 			publishPackagePath,
105 | 			JSON.stringify(publishPackage, null, '\t') + '\n'
106 | 		);
107 | 		console.log(`  - Updated package.publish.json version to ${finalVersion}`);
108 | 	} else {
109 | 		console.log(`  - Versions already in sync: ${finalVersion}`);
110 | 	}
111 | 
112 | 	// Copy the (now synced) package.publish.json as package.json
113 | 	fs.copySync(publishPackagePath, path.resolve(packageDir, 'package.json'));
114 | 	console.log('  - Copied package.publish.json as package.json');
115 | 
116 | 	// 6. Copy .vscodeignore if it exists
117 | 	if (fs.existsSync(path.resolve(__dirname, '.vscodeignore'))) {
118 | 		fs.copySync(
119 | 			path.resolve(__dirname, '.vscodeignore'),
120 | 			path.resolve(packageDir, '.vscodeignore')
121 | 		);
122 | 		console.log('  - Copied .vscodeignore');
123 | 	}
124 | 
125 | 	// 7. Copy LICENSE if it exists
126 | 	if (fs.existsSync(path.resolve(__dirname, 'LICENSE'))) {
127 | 		fs.copySync(
128 | 			path.resolve(__dirname, 'LICENSE'),
129 | 			path.resolve(packageDir, 'LICENSE')
130 | 		);
131 | 		console.log('  - Copied LICENSE');
132 | 	}
133 | 
134 | 	// 7a. Copy assets directory if it exists
135 | 	const assetsDir = path.resolve(__dirname, 'assets');
136 | 	if (fs.existsSync(assetsDir)) {
137 | 		const targetAssetsDir = path.resolve(packageDir, 'assets');
138 | 		fs.copySync(assetsDir, targetAssetsDir);
139 | 		console.log('  - Copied assets directory');
140 | 	}
141 | 
142 | 	// Small delay to ensure file system operations complete
143 | 	await new Promise((resolve) => setTimeout(resolve, 100));
144 | 
145 | 	// 8. Final step - manual packaging
146 | 	console.log('\n✅ Build preparation complete!');
147 | 	console.log('\nTo create the VSIX package, run:');
148 | 	console.log(
149 | 		'\x1b[36m%s\x1b[0m',
150 | 		`cd vsix-build && npx vsce package --no-dependencies`
151 | 	);
152 | 
153 | 	// Use the transformed version for output
154 | 	console.log(
155 | 		`\nYour extension will be packaged to: vsix-build/task-master-${finalVersion}.vsix`
156 | 	);
157 | } catch (error) {
158 | 	console.error('\n❌ Packaging failed!');
159 | 	console.error(error.message);
160 | 	process.exit(1);
161 | }
162 | 
```

--------------------------------------------------------------------------------
/src/profiles/opencode.js:
--------------------------------------------------------------------------------

```javascript
  1 | // Opencode profile for rule-transformer
  2 | import path from 'path';
  3 | import fs from 'fs';
  4 | import { log } from '../../scripts/modules/utils.js';
  5 | import { createProfile } from './base-profile.js';
  6 | 
  7 | /**
  8 |  * Transform standard MCP config format to OpenCode format
  9 |  * @param {Object} mcpConfig - Standard MCP configuration object
 10 |  * @returns {Object} - Transformed OpenCode configuration object
 11 |  */
 12 | function transformToOpenCodeFormat(mcpConfig) {
 13 | 	const openCodeConfig = {
 14 | 		$schema: 'https://opencode.ai/config.json'
 15 | 	};
 16 | 
 17 | 	// Transform mcpServers to mcp
 18 | 	if (mcpConfig.mcpServers) {
 19 | 		openCodeConfig.mcp = {};
 20 | 
 21 | 		for (const [serverName, serverConfig] of Object.entries(
 22 | 			mcpConfig.mcpServers
 23 | 		)) {
 24 | 			// Transform server configuration
 25 | 			const transformedServer = {
 26 | 				type: 'local'
 27 | 			};
 28 | 
 29 | 			// Combine command and args into single command array
 30 | 			if (serverConfig.command && serverConfig.args) {
 31 | 				transformedServer.command = [
 32 | 					serverConfig.command,
 33 | 					...serverConfig.args
 34 | 				];
 35 | 			} else if (serverConfig.command) {
 36 | 				transformedServer.command = [serverConfig.command];
 37 | 			}
 38 | 
 39 | 			// Add enabled flag
 40 | 			transformedServer.enabled = true;
 41 | 
 42 | 			// Transform env to environment
 43 | 			if (serverConfig.env) {
 44 | 				transformedServer.environment = serverConfig.env;
 45 | 			}
 46 | 
 47 | 			// update with transformed config
 48 | 			openCodeConfig.mcp[serverName] = transformedServer;
 49 | 		}
 50 | 	}
 51 | 
 52 | 	return openCodeConfig;
 53 | }
 54 | 
 55 | /**
 56 |  * Lifecycle function called after MCP config generation to transform to OpenCode format
 57 |  * @param {string} targetDir - Target project directory
 58 |  * @param {string} assetsDir - Assets directory (unused for OpenCode)
 59 |  */
 60 | function onPostConvertRulesProfile(targetDir, assetsDir) {
 61 | 	const openCodeConfigPath = path.join(targetDir, 'opencode.json');
 62 | 
 63 | 	if (!fs.existsSync(openCodeConfigPath)) {
 64 | 		log('debug', '[OpenCode] No opencode.json found to transform');
 65 | 		return;
 66 | 	}
 67 | 
 68 | 	try {
 69 | 		// Read the generated standard MCP config
 70 | 		const mcpConfigContent = fs.readFileSync(openCodeConfigPath, 'utf8');
 71 | 		const mcpConfig = JSON.parse(mcpConfigContent);
 72 | 
 73 | 		// Check if it's already in OpenCode format (has $schema)
 74 | 		if (mcpConfig.$schema) {
 75 | 			log(
 76 | 				'info',
 77 | 				'[OpenCode] opencode.json already in OpenCode format, skipping transformation'
 78 | 			);
 79 | 			return;
 80 | 		}
 81 | 
 82 | 		// Transform to OpenCode format
 83 | 		const openCodeConfig = transformToOpenCodeFormat(mcpConfig);
 84 | 
 85 | 		// Write back the transformed config with proper formatting
 86 | 		fs.writeFileSync(
 87 | 			openCodeConfigPath,
 88 | 			JSON.stringify(openCodeConfig, null, 2) + '\n'
 89 | 		);
 90 | 
 91 | 		log('info', '[OpenCode] Transformed opencode.json to OpenCode format');
 92 | 		log(
 93 | 			'debug',
 94 | 			`[OpenCode] Added schema, renamed mcpServers->mcp, combined command+args, added type/enabled, renamed env->environment`
 95 | 		);
 96 | 	} catch (error) {
 97 | 		log(
 98 | 			'error',
 99 | 			`[OpenCode] Failed to transform opencode.json: ${error.message}`
100 | 		);
101 | 	}
102 | }
103 | 
104 | /**
105 |  * Lifecycle function called when removing OpenCode profile
106 |  * @param {string} targetDir - Target project directory
107 |  */
108 | function onRemoveRulesProfile(targetDir) {
109 | 	const openCodeConfigPath = path.join(targetDir, 'opencode.json');
110 | 
111 | 	if (!fs.existsSync(openCodeConfigPath)) {
112 | 		log('debug', '[OpenCode] No opencode.json found to clean up');
113 | 		return;
114 | 	}
115 | 
116 | 	try {
117 | 		// Read the current config
118 | 		const configContent = fs.readFileSync(openCodeConfigPath, 'utf8');
119 | 		const config = JSON.parse(configContent);
120 | 
121 | 		// Check if it has the mcp section and taskmaster-ai server
122 | 		if (config.mcp && config.mcp['taskmaster-ai']) {
123 | 			// Remove taskmaster-ai server
124 | 			delete config.mcp['taskmaster-ai'];
125 | 
126 | 			// Check if there are other MCP servers
127 | 			const remainingServers = Object.keys(config.mcp);
128 | 
129 | 			if (remainingServers.length === 0) {
130 | 				// No other servers, remove entire mcp section
131 | 				delete config.mcp;
132 | 			}
133 | 
134 | 			// Check if config is now empty (only has $schema)
135 | 			const remainingKeys = Object.keys(config).filter(
136 | 				(key) => key !== '$schema'
137 | 			);
138 | 
139 | 			if (remainingKeys.length === 0) {
140 | 				// Config only has schema left, remove entire file
141 | 				fs.rmSync(openCodeConfigPath, { force: true });
142 | 				log('info', '[OpenCode] Removed empty opencode.json file');
143 | 			} else {
144 | 				// Write back the modified config
145 | 				fs.writeFileSync(
146 | 					openCodeConfigPath,
147 | 					JSON.stringify(config, null, 2) + '\n'
148 | 				);
149 | 				log(
150 | 					'info',
151 | 					'[OpenCode] Removed TaskMaster from opencode.json, preserved other configurations'
152 | 				);
153 | 			}
154 | 		} else {
155 | 			log('debug', '[OpenCode] TaskMaster not found in opencode.json');
156 | 		}
157 | 	} catch (error) {
158 | 		log(
159 | 			'error',
160 | 			`[OpenCode] Failed to clean up opencode.json: ${error.message}`
161 | 		);
162 | 	}
163 | }
164 | 
165 | // Create and export opencode profile using the base factory
166 | export const opencodeProfile = createProfile({
167 | 	name: 'opencode',
168 | 	displayName: 'OpenCode',
169 | 	url: 'opencode.ai',
170 | 	docsUrl: 'opencode.ai/docs/',
171 | 	profileDir: '.', // Root directory
172 | 	rulesDir: '.', // Root directory for AGENTS.md
173 | 	mcpConfigName: 'opencode.json', // Override default 'mcp.json'
174 | 	includeDefaultRules: false,
175 | 	fileMap: {
176 | 		'AGENTS.md': 'AGENTS.md'
177 | 	},
178 | 	onPostConvert: onPostConvertRulesProfile,
179 | 	onRemove: onRemoveRulesProfile
180 | });
181 | 
182 | // Export lifecycle functions separately to avoid naming conflicts
183 | export { onPostConvertRulesProfile, onRemoveRulesProfile };
184 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/update-subtask-by-id.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * update-subtask-by-id.js
  3 |  * Direct function implementation for appending information to a specific subtask
  4 |  */
  5 | 
  6 | import { updateSubtaskById } from '../../../../scripts/modules/task-manager.js';
  7 | import {
  8 | 	enableSilentMode,
  9 | 	disableSilentMode,
 10 | 	isSilentMode
 11 | } from '../../../../scripts/modules/utils.js';
 12 | import { createLogWrapper } from '../../tools/utils.js';
 13 | 
 14 | /**
 15 |  * Direct function wrapper for updateSubtaskById with error handling.
 16 |  *
 17 |  * @param {Object} args - Command arguments containing id, prompt, useResearch, tasksJsonPath, and projectRoot.
 18 |  * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 19 |  * @param {string} args.id - Subtask ID in format "parent.sub".
 20 |  * @param {string} args.prompt - Information to append to the subtask.
 21 |  * @param {boolean} [args.research] - Whether to use research role.
 22 |  * @param {string} [args.projectRoot] - Project root path.
 23 |  * @param {string} [args.tag] - Tag for the task (optional)
 24 |  * @param {Object} log - Logger object.
 25 |  * @param {Object} context - Context object containing session data.
 26 |  * @returns {Promise<Object>} - Result object with success status and data/error information.
 27 |  */
 28 | export async function updateSubtaskByIdDirect(args, log, context = {}) {
 29 | 	const { session } = context;
 30 | 	// Destructure expected args, including projectRoot
 31 | 	const { tasksJsonPath, id, prompt, research, projectRoot, tag } = args;
 32 | 
 33 | 	const logWrapper = createLogWrapper(log);
 34 | 
 35 | 	try {
 36 | 		logWrapper.info(
 37 | 			`Updating subtask by ID via direct function. ID: ${id}, ProjectRoot: ${projectRoot}`
 38 | 		);
 39 | 
 40 | 		// Check if tasksJsonPath was provided
 41 | 		if (!tasksJsonPath) {
 42 | 			const errorMessage = 'tasksJsonPath is required but was not provided.';
 43 | 			logWrapper.error(errorMessage);
 44 | 			return {
 45 | 				success: false,
 46 | 				error: { code: 'MISSING_ARGUMENT', message: errorMessage }
 47 | 			};
 48 | 		}
 49 | 
 50 | 		// Basic validation for ID format (e.g., '5.2')
 51 | 		if (!id || typeof id !== 'string' || !id.includes('.')) {
 52 | 			const errorMessage =
 53 | 				'Invalid subtask ID format. Must be in format "parentId.subtaskId" (e.g., "5.2").';
 54 | 			logWrapper.error(errorMessage);
 55 | 			return {
 56 | 				success: false,
 57 | 				error: { code: 'INVALID_SUBTASK_ID', message: errorMessage }
 58 | 			};
 59 | 		}
 60 | 
 61 | 		if (!prompt) {
 62 | 			const errorMessage =
 63 | 				'No prompt specified. Please provide the information to append.';
 64 | 			logWrapper.error(errorMessage);
 65 | 			return {
 66 | 				success: false,
 67 | 				error: { code: 'MISSING_PROMPT', message: errorMessage }
 68 | 			};
 69 | 		}
 70 | 
 71 | 		// Validate subtask ID format
 72 | 		const subtaskId = id;
 73 | 		if (typeof subtaskId !== 'string' && typeof subtaskId !== 'number') {
 74 | 			const errorMessage = `Invalid subtask ID type: ${typeof subtaskId}. Subtask ID must be a string or number.`;
 75 | 			log.error(errorMessage);
 76 | 			return {
 77 | 				success: false,
 78 | 				error: { code: 'INVALID_SUBTASK_ID_TYPE', message: errorMessage }
 79 | 			};
 80 | 		}
 81 | 
 82 | 		const subtaskIdStr = String(subtaskId);
 83 | 		if (!subtaskIdStr.includes('.')) {
 84 | 			const errorMessage = `Invalid subtask ID format: ${subtaskIdStr}. Subtask ID must be in format "parentId.subtaskId" (e.g., "5.2").`;
 85 | 			log.error(errorMessage);
 86 | 			return {
 87 | 				success: false,
 88 | 				error: { code: 'INVALID_SUBTASK_ID_FORMAT', message: errorMessage }
 89 | 			};
 90 | 		}
 91 | 
 92 | 		// Use the provided path
 93 | 		const tasksPath = tasksJsonPath;
 94 | 		const useResearch = research === true;
 95 | 
 96 | 		log.info(
 97 | 			`Updating subtask with ID ${subtaskIdStr} with prompt "${prompt}" and research: ${useResearch}`
 98 | 		);
 99 | 
100 | 		const wasSilent = isSilentMode();
101 | 		if (!wasSilent) {
102 | 			enableSilentMode();
103 | 		}
104 | 
105 | 		try {
106 | 			// Execute core updateSubtaskById function
107 | 			const coreResult = await updateSubtaskById(
108 | 				tasksPath,
109 | 				subtaskIdStr,
110 | 				prompt,
111 | 				useResearch,
112 | 				{
113 | 					mcpLog: logWrapper,
114 | 					session,
115 | 					projectRoot,
116 | 					tag,
117 | 					commandName: 'update-subtask',
118 | 					outputType: 'mcp'
119 | 				},
120 | 				'json'
121 | 			);
122 | 
123 | 			if (!coreResult || coreResult.updatedSubtask === null) {
124 | 				const message = `Subtask ${id} or its parent task not found.`;
125 | 				logWrapper.error(message);
126 | 				return {
127 | 					success: false,
128 | 					error: { code: 'SUBTASK_NOT_FOUND', message: message }
129 | 				};
130 | 			}
131 | 
132 | 			// Subtask updated successfully
133 | 			const successMessage = `Successfully updated subtask with ID ${subtaskIdStr}`;
134 | 			logWrapper.success(successMessage);
135 | 			return {
136 | 				success: true,
137 | 				data: {
138 | 					message: `Successfully updated subtask with ID ${subtaskIdStr}`,
139 | 					subtaskId: subtaskIdStr,
140 | 					parentId: subtaskIdStr.split('.')[0],
141 | 					subtask: coreResult.updatedSubtask,
142 | 					tasksPath,
143 | 					useResearch,
144 | 					telemetryData: coreResult.telemetryData,
145 | 					tagInfo: coreResult.tagInfo
146 | 				}
147 | 			};
148 | 		} catch (error) {
149 | 			logWrapper.error(`Error updating subtask by ID: ${error.message}`);
150 | 			return {
151 | 				success: false,
152 | 				error: {
153 | 					code: 'UPDATE_SUBTASK_CORE_ERROR',
154 | 					message: error.message || 'Unknown error updating subtask'
155 | 				}
156 | 			};
157 | 		} finally {
158 | 			if (!wasSilent && isSilentMode()) {
159 | 				disableSilentMode();
160 | 			}
161 | 		}
162 | 	} catch (error) {
163 | 		logWrapper.error(
164 | 			`Setup error in updateSubtaskByIdDirect: ${error.message}`
165 | 		);
166 | 		if (isSilentMode()) disableSilentMode();
167 | 		return {
168 | 			success: false,
169 | 			error: {
170 | 				code: 'DIRECT_FUNCTION_SETUP_ERROR',
171 | 				message: error.message || 'Unknown setup error'
172 | 			}
173 | 		};
174 | 	}
175 | }
176 | 
```

--------------------------------------------------------------------------------
/src/progress/parse-prd-tracker.js:
--------------------------------------------------------------------------------

```javascript
  1 | import chalk from 'chalk';
  2 | import { newMultiBar } from './cli-progress-factory.js';
  3 | import { BaseProgressTracker } from './base-progress-tracker.js';
  4 | import {
  5 | 	createProgressHeader,
  6 | 	createProgressRow,
  7 | 	createBorder
  8 | } from './tracker-ui.js';
  9 | import {
 10 | 	getCliPriorityIndicators,
 11 | 	getPriorityIndicator,
 12 | 	getStatusBarPriorityIndicators,
 13 | 	getPriorityColors
 14 | } from '../ui/indicators.js';
 15 | 
 16 | // Get centralized priority indicators
 17 | const PRIORITY_INDICATORS = getCliPriorityIndicators();
 18 | const PRIORITY_DOTS = getStatusBarPriorityIndicators();
 19 | const PRIORITY_COLORS = getPriorityColors();
 20 | 
 21 | // Constants
 22 | const CONSTANTS = {
 23 | 	DEBOUNCE_DELAY: 100,
 24 | 	MAX_TITLE_LENGTH: 57,
 25 | 	TRUNCATED_LENGTH: 54,
 26 | 	TASK_ID_PAD_START: 3,
 27 | 	TASK_ID_PAD_END: 4,
 28 | 	PRIORITY_PAD_END: 3,
 29 | 	VALID_PRIORITIES: ['high', 'medium', 'low'],
 30 | 	DEFAULT_PRIORITY: 'medium'
 31 | };
 32 | 
 33 | /**
 34 |  * Helper class to manage update debouncing
 35 |  */
 36 | class UpdateDebouncer {
 37 | 	constructor(delay = CONSTANTS.DEBOUNCE_DELAY) {
 38 | 		this.delay = delay;
 39 | 		this.pendingTimeout = null;
 40 | 	}
 41 | 
 42 | 	debounce(callback) {
 43 | 		this.clear();
 44 | 		this.pendingTimeout = setTimeout(() => {
 45 | 			callback();
 46 | 			this.pendingTimeout = null;
 47 | 		}, this.delay);
 48 | 	}
 49 | 
 50 | 	clear() {
 51 | 		if (this.pendingTimeout) {
 52 | 			clearTimeout(this.pendingTimeout);
 53 | 			this.pendingTimeout = null;
 54 | 		}
 55 | 	}
 56 | 
 57 | 	hasPending() {
 58 | 		return this.pendingTimeout !== null;
 59 | 	}
 60 | }
 61 | 
 62 | /**
 63 |  * Helper class to manage priority counts
 64 |  */
 65 | class PriorityManager {
 66 | 	constructor() {
 67 | 		this.priorities = { high: 0, medium: 0, low: 0 };
 68 | 	}
 69 | 
 70 | 	increment(priority) {
 71 | 		const normalized = this.normalize(priority);
 72 | 		this.priorities[normalized]++;
 73 | 		return normalized;
 74 | 	}
 75 | 
 76 | 	normalize(priority) {
 77 | 		const lowercased = priority
 78 | 			? priority.toLowerCase()
 79 | 			: CONSTANTS.DEFAULT_PRIORITY;
 80 | 		return CONSTANTS.VALID_PRIORITIES.includes(lowercased)
 81 | 			? lowercased
 82 | 			: CONSTANTS.DEFAULT_PRIORITY;
 83 | 	}
 84 | 
 85 | 	getCounts() {
 86 | 		return { ...this.priorities };
 87 | 	}
 88 | }
 89 | 
 90 | /**
 91 |  * Helper class for formatting task display elements
 92 |  */
 93 | class TaskFormatter {
 94 | 	static formatTitle(title, taskNumber) {
 95 | 		if (!title) return `Task ${taskNumber}`;
 96 | 		return title.length > CONSTANTS.MAX_TITLE_LENGTH
 97 | 			? title.substring(0, CONSTANTS.TRUNCATED_LENGTH) + '...'
 98 | 			: title;
 99 | 	}
100 | 
101 | 	static formatPriority(priority) {
102 | 		return getPriorityIndicator(priority, false).padEnd(
103 | 			CONSTANTS.PRIORITY_PAD_END,
104 | 			' '
105 | 		);
106 | 	}
107 | 
108 | 	static formatTaskId(taskNumber) {
109 | 		return taskNumber
110 | 			.toString()
111 | 			.padStart(CONSTANTS.TASK_ID_PAD_START, ' ')
112 | 			.padEnd(CONSTANTS.TASK_ID_PAD_END, ' ');
113 | 	}
114 | }
115 | 
116 | /**
117 |  * Tracks progress for PRD parsing operations with multibar display
118 |  */
119 | class ParsePrdTracker extends BaseProgressTracker {
120 | 	_initializeCustomProperties(options) {
121 | 		this.append = options.append;
122 | 		this.priorityManager = new PriorityManager();
123 | 		this.debouncer = new UpdateDebouncer();
124 | 		this.headerShown = false;
125 | 	}
126 | 
127 | 	_getTimeTokensBarFormat() {
128 | 		return `{clock} {elapsed} | ${PRIORITY_DOTS.high} {high}  ${PRIORITY_DOTS.medium} {medium}  ${PRIORITY_DOTS.low} {low} | Tokens (I/O): {in}/{out} | Est: {remaining}`;
129 | 	}
130 | 
131 | 	_getProgressBarFormat() {
132 | 		return 'Tasks {tasks} |{bar}| {percentage}%';
133 | 	}
134 | 
135 | 	_getCustomTimeTokensPayload() {
136 | 		return this.priorityManager.getCounts();
137 | 	}
138 | 
139 | 	addTaskLine(taskNumber, title, priority = 'medium') {
140 | 		if (!this.multibar || this.isFinished) return;
141 | 
142 | 		this._ensureHeaderShown();
143 | 		const normalizedPriority = this._updateTaskCounters(taskNumber, priority);
144 | 
145 | 		// Immediately update the time/tokens bar to show the new priority count
146 | 		this._updateTimeTokensBar();
147 | 
148 | 		this.debouncer.debounce(() => {
149 | 			this._updateProgressDisplay(taskNumber, title, normalizedPriority);
150 | 		});
151 | 	}
152 | 
153 | 	_ensureHeaderShown() {
154 | 		if (!this.headerShown) {
155 | 			this.headerShown = true;
156 | 			createProgressHeader(
157 | 				this.multibar,
158 | 				' TASK | PRI | TITLE',
159 | 				'------+-----+----------------------------------------------------------------'
160 | 			);
161 | 		}
162 | 	}
163 | 
164 | 	_updateTaskCounters(taskNumber, priority) {
165 | 		const normalizedPriority = this.priorityManager.increment(priority);
166 | 		this.completedUnits = taskNumber;
167 | 		return normalizedPriority;
168 | 	}
169 | 
170 | 	_updateProgressDisplay(taskNumber, title, normalizedPriority) {
171 | 		this.progressBar.update(this.completedUnits, {
172 | 			tasks: `${this.completedUnits}/${this.numUnits}`
173 | 		});
174 | 
175 | 		const displayTitle = TaskFormatter.formatTitle(title, taskNumber);
176 | 		const priorityDisplay = TaskFormatter.formatPriority(normalizedPriority);
177 | 		const taskIdCentered = TaskFormatter.formatTaskId(taskNumber);
178 | 
179 | 		createProgressRow(
180 | 			this.multibar,
181 | 			` ${taskIdCentered} | ${priorityDisplay} | {title}`,
182 | 			{ title: displayTitle }
183 | 		);
184 | 
185 | 		createBorder(
186 | 			this.multibar,
187 | 			'------+-----+----------------------------------------------------------------'
188 | 		);
189 | 
190 | 		this._updateTimeTokensBar();
191 | 	}
192 | 
193 | 	finish() {
194 | 		// Flush any pending updates before finishing
195 | 		if (this.debouncer.hasPending()) {
196 | 			this.debouncer.clear();
197 | 			this._updateTimeTokensBar();
198 | 		}
199 | 		this.cleanup();
200 | 		super.finish();
201 | 	}
202 | 
203 | 	/**
204 | 	 * Override cleanup to handle pending updates
205 | 	 */
206 | 	_performCustomCleanup() {
207 | 		this.debouncer.clear();
208 | 	}
209 | 
210 | 	getSummary() {
211 | 		return {
212 | 			...super.getSummary(),
213 | 			taskPriorities: this.priorityManager.getCounts(),
214 | 			actionVerb: this.append ? 'appended' : 'generated'
215 | 		};
216 | 	}
217 | }
218 | 
219 | export function createParsePrdTracker(options = {}) {
220 | 	return new ParsePrdTracker(options);
221 | }
222 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/initialize-project.js:
--------------------------------------------------------------------------------

```javascript
  1 | import { initializeProject } from '../../../../scripts/init.js'; // Import core function and its logger if needed separately
  2 | import {
  3 | 	enableSilentMode,
  4 | 	disableSilentMode
  5 | 	// isSilentMode // Not used directly here
  6 | } from '../../../../scripts/modules/utils.js';
  7 | import os from 'os'; // Import os module for home directory check
  8 | import { RULE_PROFILES } from '../../../../src/constants/profiles.js';
  9 | import { convertAllRulesToProfileRules } from '../../../../src/utils/rule-transformer.js';
 10 | 
 11 | /**
 12 |  * Direct function wrapper for initializing a project.
 13 |  * Derives target directory from session, sets CWD, and calls core init logic.
 14 |  * @param {object} args - Arguments containing initialization options (addAliases, initGit, storeTasksInGit, skipInstall, yes, projectRoot, rules)
 15 |  * @param {object} log - The FastMCP logger instance.
 16 |  * @param {object} context - The context object, must contain { session }.
 17 |  * @returns {Promise<{success: boolean, data?: any, error?: {code: string, message: string}}>} - Standard result object.
 18 |  */
 19 | export async function initializeProjectDirect(args, log, context = {}) {
 20 | 	const { session } = context; // Keep session if core logic needs it
 21 | 	const homeDir = os.homedir();
 22 | 
 23 | 	log.info(`Args received in direct function: ${JSON.stringify(args)}`);
 24 | 
 25 | 	// --- Determine Target Directory ---
 26 | 	// TRUST the projectRoot passed from the tool layer via args
 27 | 	// The HOF in the tool layer already normalized and validated it came from a reliable source (args or session)
 28 | 	const targetDirectory = args.projectRoot;
 29 | 
 30 | 	// --- Validate the targetDirectory (basic sanity checks) ---
 31 | 	if (
 32 | 		!targetDirectory ||
 33 | 		typeof targetDirectory !== 'string' || // Ensure it's a string
 34 | 		targetDirectory === '/' ||
 35 | 		targetDirectory === homeDir
 36 | 	) {
 37 | 		log.error(
 38 | 			`Invalid target directory received from tool layer: '${targetDirectory}'`
 39 | 		);
 40 | 		return {
 41 | 			success: false,
 42 | 			error: {
 43 | 				code: 'INVALID_TARGET_DIRECTORY',
 44 | 				message: `Cannot initialize project: Invalid target directory '${targetDirectory}' received. Please ensure a valid workspace/folder is open or specified.`,
 45 | 				details: `Received args.projectRoot: ${args.projectRoot}` // Show what was received
 46 | 			}
 47 | 		};
 48 | 	}
 49 | 
 50 | 	// --- Proceed with validated targetDirectory ---
 51 | 	log.info(`Validated target directory for initialization: ${targetDirectory}`);
 52 | 
 53 | 	const originalCwd = process.cwd();
 54 | 	let resultData;
 55 | 	let success = false;
 56 | 	let errorResult = null;
 57 | 
 58 | 	log.info(
 59 | 		`Temporarily changing CWD to ${targetDirectory} for initialization.`
 60 | 	);
 61 | 	process.chdir(targetDirectory); // Change CWD to the HOF-provided root
 62 | 
 63 | 	enableSilentMode();
 64 | 	try {
 65 | 		// Construct options ONLY from the relevant flags in args
 66 | 		// The core initializeProject operates in the current CWD, which we just set
 67 | 		const options = {
 68 | 			addAliases: args.addAliases,
 69 | 			initGit: args.initGit,
 70 | 			storeTasksInGit: args.storeTasksInGit,
 71 | 			skipInstall: args.skipInstall,
 72 | 			yes: true // Force yes mode
 73 | 		};
 74 | 
 75 | 		// Handle rules option with MCP-specific defaults
 76 | 		if (Array.isArray(args.rules) && args.rules.length > 0) {
 77 | 			options.rules = args.rules;
 78 | 			options.rulesExplicitlyProvided = true;
 79 | 			log.info(`Including rules: ${args.rules.join(', ')}`);
 80 | 		} else {
 81 | 			// For MCP initialization, default to Cursor profile only
 82 | 			options.rules = ['cursor'];
 83 | 			options.rulesExplicitlyProvided = true;
 84 | 			log.info(`No rule profiles specified, defaulting to: Cursor`);
 85 | 		}
 86 | 
 87 | 		log.info(`Initializing project with options: ${JSON.stringify(options)}`);
 88 | 		const result = await initializeProject(options); // Call core logic
 89 | 
 90 | 		resultData = {
 91 | 			message: 'Project initialized successfully.',
 92 | 			next_step:
 93 | 				'Now that the project is initialized, the next step is to create the tasks by parsing a PRD. This will create the tasks folder and the initial task files (tasks folder will be created when parse-prd is run). The parse-prd tool will require a prd.txt file as input (typically found in .taskmaster/docs/ directory). You can create a prd.txt file by asking the user about their idea, and then using the .taskmaster/templates/example_prd.txt file as a template to generate a prd.txt file in .taskmaster/docs/. You may skip all of this if the user already has a prd.txt file. You can THEN use the parse-prd tool to create the tasks. So: step 1 after initialization is to create a prd.txt file in .taskmaster/docs/prd.txt or confirm the user already has one. Step 2 is to use the parse-prd tool to create the tasks. Do not bother looking for tasks after initialization, just use the parse-prd tool to create the tasks after creating a prd.txt from which to parse the tasks. You do NOT need to reinitialize the project to parse-prd.',
 94 | 			...result
 95 | 		};
 96 | 		success = true;
 97 | 		log.info(
 98 | 			`Project initialization completed successfully in ${targetDirectory}.`
 99 | 		);
100 | 	} catch (error) {
101 | 		log.error(`Core initializeProject failed: ${error.message}`);
102 | 		errorResult = {
103 | 			code: 'INITIALIZATION_FAILED',
104 | 			message: `Core project initialization failed: ${error.message}`,
105 | 			details: error.stack
106 | 		};
107 | 		success = false;
108 | 	} finally {
109 | 		disableSilentMode();
110 | 		log.info(`Restoring original CWD: ${originalCwd}`);
111 | 		process.chdir(originalCwd);
112 | 	}
113 | 
114 | 	if (success) {
115 | 		return { success: true, data: resultData };
116 | 	} else {
117 | 		return { success: false, error: errorResult };
118 | 	}
119 | }
120 | 
```

--------------------------------------------------------------------------------
/mcp-server/src/core/task-master-core.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * task-master-core.js
  3 |  * Central module that imports and re-exports all direct function implementations
  4 |  * for improved organization and maintainability.
  5 |  */
  6 | 
  7 | // Import direct function implementations
  8 | import { listTasksDirect } from './direct-functions/list-tasks.js';
  9 | import { getCacheStatsDirect } from './direct-functions/cache-stats.js';
 10 | import { parsePRDDirect } from './direct-functions/parse-prd.js';
 11 | import { updateTasksDirect } from './direct-functions/update-tasks.js';
 12 | import { updateTaskByIdDirect } from './direct-functions/update-task-by-id.js';
 13 | import { updateSubtaskByIdDirect } from './direct-functions/update-subtask-by-id.js';
 14 | import { generateTaskFilesDirect } from './direct-functions/generate-task-files.js';
 15 | import { setTaskStatusDirect } from './direct-functions/set-task-status.js';
 16 | import { showTaskDirect } from './direct-functions/show-task.js';
 17 | import { nextTaskDirect } from './direct-functions/next-task.js';
 18 | import { expandTaskDirect } from './direct-functions/expand-task.js';
 19 | import { addTaskDirect } from './direct-functions/add-task.js';
 20 | import { addSubtaskDirect } from './direct-functions/add-subtask.js';
 21 | import { removeSubtaskDirect } from './direct-functions/remove-subtask.js';
 22 | import { analyzeTaskComplexityDirect } from './direct-functions/analyze-task-complexity.js';
 23 | import { clearSubtasksDirect } from './direct-functions/clear-subtasks.js';
 24 | import { expandAllTasksDirect } from './direct-functions/expand-all-tasks.js';
 25 | import { removeDependencyDirect } from './direct-functions/remove-dependency.js';
 26 | import { validateDependenciesDirect } from './direct-functions/validate-dependencies.js';
 27 | import { fixDependenciesDirect } from './direct-functions/fix-dependencies.js';
 28 | import { complexityReportDirect } from './direct-functions/complexity-report.js';
 29 | import { addDependencyDirect } from './direct-functions/add-dependency.js';
 30 | import { removeTaskDirect } from './direct-functions/remove-task.js';
 31 | import { initializeProjectDirect } from './direct-functions/initialize-project.js';
 32 | import { modelsDirect } from './direct-functions/models.js';
 33 | import { moveTaskDirect } from './direct-functions/move-task.js';
 34 | import { moveTaskCrossTagDirect } from './direct-functions/move-task-cross-tag.js';
 35 | import { researchDirect } from './direct-functions/research.js';
 36 | import { addTagDirect } from './direct-functions/add-tag.js';
 37 | import { deleteTagDirect } from './direct-functions/delete-tag.js';
 38 | import { listTagsDirect } from './direct-functions/list-tags.js';
 39 | import { useTagDirect } from './direct-functions/use-tag.js';
 40 | import { renameTagDirect } from './direct-functions/rename-tag.js';
 41 | import { copyTagDirect } from './direct-functions/copy-tag.js';
 42 | import { scopeUpDirect } from './direct-functions/scope-up.js';
 43 | import { scopeDownDirect } from './direct-functions/scope-down.js';
 44 | 
 45 | // Re-export utility functions
 46 | export { findTasksPath } from './utils/path-utils.js';
 47 | 
 48 | // Use Map for potential future enhancements like introspection or dynamic dispatch
 49 | export const directFunctions = new Map([
 50 | 	['listTasksDirect', listTasksDirect],
 51 | 	['getCacheStatsDirect', getCacheStatsDirect],
 52 | 	['parsePRDDirect', parsePRDDirect],
 53 | 	['updateTasksDirect', updateTasksDirect],
 54 | 	['updateTaskByIdDirect', updateTaskByIdDirect],
 55 | 	['updateSubtaskByIdDirect', updateSubtaskByIdDirect],
 56 | 	['generateTaskFilesDirect', generateTaskFilesDirect],
 57 | 	['setTaskStatusDirect', setTaskStatusDirect],
 58 | 	['showTaskDirect', showTaskDirect],
 59 | 	['nextTaskDirect', nextTaskDirect],
 60 | 	['expandTaskDirect', expandTaskDirect],
 61 | 	['addTaskDirect', addTaskDirect],
 62 | 	['addSubtaskDirect', addSubtaskDirect],
 63 | 	['removeSubtaskDirect', removeSubtaskDirect],
 64 | 	['analyzeTaskComplexityDirect', analyzeTaskComplexityDirect],
 65 | 	['clearSubtasksDirect', clearSubtasksDirect],
 66 | 	['expandAllTasksDirect', expandAllTasksDirect],
 67 | 	['removeDependencyDirect', removeDependencyDirect],
 68 | 	['validateDependenciesDirect', validateDependenciesDirect],
 69 | 	['fixDependenciesDirect', fixDependenciesDirect],
 70 | 	['complexityReportDirect', complexityReportDirect],
 71 | 	['addDependencyDirect', addDependencyDirect],
 72 | 	['removeTaskDirect', removeTaskDirect],
 73 | 	['initializeProjectDirect', initializeProjectDirect],
 74 | 	['modelsDirect', modelsDirect],
 75 | 	['moveTaskDirect', moveTaskDirect],
 76 | 	['moveTaskCrossTagDirect', moveTaskCrossTagDirect],
 77 | 	['researchDirect', researchDirect],
 78 | 	['addTagDirect', addTagDirect],
 79 | 	['deleteTagDirect', deleteTagDirect],
 80 | 	['listTagsDirect', listTagsDirect],
 81 | 	['useTagDirect', useTagDirect],
 82 | 	['renameTagDirect', renameTagDirect],
 83 | 	['copyTagDirect', copyTagDirect],
 84 | 	['scopeUpDirect', scopeUpDirect],
 85 | 	['scopeDownDirect', scopeDownDirect]
 86 | ]);
 87 | 
 88 | // Re-export all direct function implementations
 89 | export {
 90 | 	listTasksDirect,
 91 | 	getCacheStatsDirect,
 92 | 	parsePRDDirect,
 93 | 	updateTasksDirect,
 94 | 	updateTaskByIdDirect,
 95 | 	updateSubtaskByIdDirect,
 96 | 	generateTaskFilesDirect,
 97 | 	setTaskStatusDirect,
 98 | 	showTaskDirect,
 99 | 	nextTaskDirect,
100 | 	expandTaskDirect,
101 | 	addTaskDirect,
102 | 	addSubtaskDirect,
103 | 	removeSubtaskDirect,
104 | 	analyzeTaskComplexityDirect,
105 | 	clearSubtasksDirect,
106 | 	expandAllTasksDirect,
107 | 	removeDependencyDirect,
108 | 	validateDependenciesDirect,
109 | 	fixDependenciesDirect,
110 | 	complexityReportDirect,
111 | 	addDependencyDirect,
112 | 	removeTaskDirect,
113 | 	initializeProjectDirect,
114 | 	modelsDirect,
115 | 	moveTaskDirect,
116 | 	moveTaskCrossTagDirect,
117 | 	researchDirect,
118 | 	addTagDirect,
119 | 	deleteTagDirect,
120 | 	listTagsDirect,
121 | 	useTagDirect,
122 | 	renameTagDirect,
123 | 	copyTagDirect,
124 | 	scopeUpDirect,
125 | 	scopeDownDirect
126 | };
127 | 
```

--------------------------------------------------------------------------------
/docs/providers/gemini-cli.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Gemini CLI Provider
  2 | 
  3 | The Gemini CLI provider allows you to use Google's Gemini models through the Gemini CLI tool, leveraging your existing Gemini subscription and OAuth authentication.
  4 | 
  5 | ## Why Use Gemini CLI?
  6 | 
  7 | The primary benefit of using the `gemini-cli` provider is to leverage your existing Personal Gemini Code Assist license/usage Google offers for free, or Gemini Code Assist Standard/Enterprise subscription you may already have, via OAuth configured through the Gemini CLI. This is ideal for users who:
  8 | 
  9 | - Have an active Gemini Code Assist license (including those using the free tier offere by Google)
 10 | - Want to use OAuth authentication instead of managing API keys
 11 | - Have already configured authentication via `gemini` OAuth login
 12 | 
 13 | ## Installation
 14 | 
 15 | The provider is already included in Task Master. However, you need to install the Gemini CLI tool:
 16 | 
 17 | ```bash
 18 | # Install gemini CLI globally
 19 | npm install -g @google/gemini-cli
 20 | ```
 21 | 
 22 | ## Authentication
 23 | 
 24 | ### Primary Method: CLI Authentication (Recommended)
 25 | 
 26 | The Gemini CLI provider is designed to use your pre-configured OAuth authentication:
 27 | 
 28 | ```bash
 29 | # Launch Gemini CLI and go through the authentication procedure
 30 | gemini
 31 | ```
 32 | 
 33 | For OAuth use, select `Login with Google` - This will open a browser window for OAuth authentication. Once authenticated, Task Master will automatically use these credentials when you select the `gemini-cli` provider and models.
 34 | 
 35 | ### Alternative Method: API Key
 36 | 
 37 | While the primary use case is OAuth authentication, you can also use an API key if needed:
 38 | 
 39 | ```bash
 40 | export GEMINI_API_KEY="your-gemini-api-key"
 41 | ```
 42 | 
 43 | **Note:** If you want to use API keys, consider using the standard `google` provider instead, as `gemini-cli` is specifically designed for OAuth/subscription users.
 44 | 
 45 | More details on authentication steps and options can be found in the [gemini-cli GitHub README](https://github.com/google-gemini/gemini-cli).
 46 | 
 47 | ## Configuration
 48 | 
 49 | Use the `task-master init` command to run through the guided initialization:
 50 | 
 51 | ```bash
 52 | task-master init
 53 | ```
 54 | 
 55 | **OR**
 56 | 
 57 | Configure `gemini-cli` as a provider using the Task Master models command:
 58 | 
 59 | ```bash
 60 | # Set gemini-cli as your main provider with gemini-2.5-pro
 61 | task-master models --set-main gemini-2.5-pro --gemini-cli
 62 | 
 63 | # Or use the faster gemini-2.5-flash model
 64 | task-master models --set-main gemini-2.5-flash --gemini-cli
 65 | ```
 66 | 
 67 | You can also manually edit your `.taskmaster/config.json`:
 68 | 
 69 | ```json
 70 | {
 71 |   "models": {
 72 |     "main": {
 73 |       "provider": "gemini-cli",
 74 |       "modelId": "gemini-2.5-pro",
 75 |       "maxTokens": 65536,
 76 |       "temperature": 0.2
 77 |     },
 78 |     "research": {
 79 |       "provider": "gemini-cli",
 80 |       "modelId": "gemini-2.5-pro",
 81 |       "maxTokens": 65536,
 82 |       "temperature": 0.1
 83 |     },
 84 |     "fallback": {
 85 |       "provider": "gemini-cli",
 86 |       "modelId": "gemini-2.5-flash",
 87 |       "maxTokens": 65536,
 88 |       "temperature": 0.2
 89 |     }
 90 |   },
 91 |   "global": {
 92 |     "logLevel": "info",
 93 |     "debug": false,
 94 |     "defaultNumTasks": 10,
 95 |     "defaultSubtasks": 5,
 96 |     "defaultPriority": "medium",
 97 |     "projectName": "Taskmaster",
 98 |     "ollamaBaseURL": "http://localhost:11434/api",
 99 |     "bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com",
100 |     "responseLanguage": "English",
101 |     "defaultTag": "master",
102 |     "azureOpenaiBaseURL": "https://your-endpoint.openai.azure.com/"
103 |   },
104 |   "claudeCode": {}
105 | }
106 | ```
107 | 
108 | ### Available Models
109 | 
110 | The gemini-cli provider supports only two models:
111 | - `gemini-2.5-pro` - High performance model (1M token context window, 65,536 max output tokens)
112 | - `gemini-2.5-flash` - Fast, efficient model (1M token context window, 65,536 max output tokens)
113 | 
114 | ## Usage Examples
115 | 
116 | ### Basic Usage
117 | 
118 | Once gemini-cli is installed and authenticated, and Task Master  simply use Task Master as normal:
119 | 
120 | ```bash
121 | # The provider will automatically use your OAuth credentials
122 | task-master parse-prd my-prd.txt
123 | ```
124 | 
125 | ## Troubleshooting
126 | 
127 | ### "Authentication failed" Error
128 | 
129 | If you get an authentication error:
130 | 
131 | 1. **Primary solution**: Run `gemini` to authenticate with your Google account - use `/auth` slash command in **gemini-cli** to change authentication method if desired.
132 | 2. **Check authentication status**: Run `gemini` and use `/about` to verify your Auth Method and GCP Project if applicable.
133 | 3. **If using API key** (not recommended): Ensure `GEMINI_API_KEY` env variable is set correctly, see the gemini-cli README.md for more info.
134 | 
135 | ### "Model not found" Error
136 | 
137 | The gemini-cli provider only supports two models:
138 | - `gemini-2.5-pro`
139 | - `gemini-2.5-flash`
140 | 
141 | If you need other Gemini models, use the standard `google` provider with an API key instead.
142 | 
143 | ### Gemini CLI Not Found
144 | 
145 | If you get a "gemini: command not found" error:
146 | 
147 | ```bash
148 | # Install the Gemini CLI globally
149 | npm install -g @google/gemini-cli
150 | 
151 | # Verify installation
152 | gemini --version
153 | ```
154 | 
155 | ## Important Notes
156 | 
157 | - **OAuth vs API Key**: This provider is specifically designed for users who want to use OAuth authentication via gemini-cli. If you prefer using API keys, consider using the standard `google` provider instead.
158 | - **Limited Model Support**: Only `gemini-2.5-pro` and `gemini-2.5-flash` are available through gemini-cli.
159 | - **Subscription Benefits**: Using OAuth authentication allows you to leverage any subscription benefits associated with your Google account.
160 | - The provider uses the `ai-sdk-provider-gemini-cli` npm package internally.
161 | - Supports all standard Task Master features: text generation, streaming, and structured object generation.
```

--------------------------------------------------------------------------------
/mcp-server/src/core/direct-functions/update-task-by-id.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * update-task-by-id.js
  3 |  * Direct function implementation for updating a single task by ID with new information
  4 |  */
  5 | 
  6 | import { updateTaskById } from '../../../../scripts/modules/task-manager.js';
  7 | import {
  8 | 	enableSilentMode,
  9 | 	disableSilentMode,
 10 | 	isSilentMode
 11 | } from '../../../../scripts/modules/utils.js';
 12 | import { createLogWrapper } from '../../tools/utils.js';
 13 | 
 14 | /**
 15 |  * Direct function wrapper for updateTaskById with error handling.
 16 |  *
 17 |  * @param {Object} args - Command arguments containing id, prompt, useResearch, tasksJsonPath, and projectRoot.
 18 |  * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 19 |  * @param {string} args.id - Task ID (or subtask ID like "1.2").
 20 |  * @param {string} args.prompt - New information/context prompt.
 21 |  * @param {boolean} [args.research] - Whether to use research role.
 22 |  * @param {boolean} [args.append] - Whether to append timestamped information instead of full update.
 23 |  * @param {string} [args.projectRoot] - Project root path.
 24 |  * @param {string} [args.tag] - Tag for the task (optional)
 25 |  * @param {Object} log - Logger object.
 26 |  * @param {Object} context - Context object containing session data.
 27 |  * @returns {Promise<Object>} - Result object with success status and data/error information.
 28 |  */
 29 | export async function updateTaskByIdDirect(args, log, context = {}) {
 30 | 	const { session } = context;
 31 | 	// Destructure expected args, including projectRoot
 32 | 	const { tasksJsonPath, id, prompt, research, append, projectRoot, tag } =
 33 | 		args;
 34 | 
 35 | 	const logWrapper = createLogWrapper(log);
 36 | 
 37 | 	try {
 38 | 		logWrapper.info(
 39 | 			`Updating task by ID via direct function. ID: ${id}, ProjectRoot: ${projectRoot}`
 40 | 		);
 41 | 
 42 | 		// Check if tasksJsonPath was provided
 43 | 		if (!tasksJsonPath) {
 44 | 			const errorMessage = 'tasksJsonPath is required but was not provided.';
 45 | 			logWrapper.error(errorMessage);
 46 | 			return {
 47 | 				success: false,
 48 | 				error: { code: 'MISSING_ARGUMENT', message: errorMessage }
 49 | 			};
 50 | 		}
 51 | 
 52 | 		// Check required parameters (id and prompt)
 53 | 		if (!id) {
 54 | 			const errorMessage =
 55 | 				'No task ID specified. Please provide a task ID to update.';
 56 | 			logWrapper.error(errorMessage);
 57 | 			return {
 58 | 				success: false,
 59 | 				error: { code: 'MISSING_TASK_ID', message: errorMessage }
 60 | 			};
 61 | 		}
 62 | 
 63 | 		if (!prompt) {
 64 | 			const errorMessage =
 65 | 				'No prompt specified. Please provide a prompt with new information for the task update.';
 66 | 			logWrapper.error(errorMessage);
 67 | 			return {
 68 | 				success: false,
 69 | 				error: { code: 'MISSING_PROMPT', message: errorMessage }
 70 | 			};
 71 | 		}
 72 | 
 73 | 		// Parse taskId - handle both string and number values
 74 | 		let taskId;
 75 | 		if (typeof id === 'string') {
 76 | 			// Handle subtask IDs (e.g., "5.2")
 77 | 			if (id.includes('.')) {
 78 | 				taskId = id; // Keep as string for subtask IDs
 79 | 			} else {
 80 | 				// Parse as integer for main task IDs
 81 | 				taskId = parseInt(id, 10);
 82 | 				if (Number.isNaN(taskId)) {
 83 | 					const errorMessage = `Invalid task ID: ${id}. Task ID must be a positive integer or subtask ID (e.g., "5.2").`;
 84 | 					logWrapper.error(errorMessage);
 85 | 					return {
 86 | 						success: false,
 87 | 						error: { code: 'INVALID_TASK_ID', message: errorMessage }
 88 | 					};
 89 | 				}
 90 | 			}
 91 | 		} else {
 92 | 			taskId = id;
 93 | 		}
 94 | 
 95 | 		// Use the provided path
 96 | 		const tasksPath = tasksJsonPath;
 97 | 
 98 | 		// Get research flag
 99 | 		const useResearch = research === true;
100 | 
101 | 		logWrapper.info(
102 | 			`Updating task with ID ${taskId} with prompt "${prompt}" and research: ${useResearch}`
103 | 		);
104 | 
105 | 		const wasSilent = isSilentMode();
106 | 		if (!wasSilent) {
107 | 			enableSilentMode();
108 | 		}
109 | 
110 | 		try {
111 | 			// Execute core updateTaskById function with proper parameters
112 | 			const coreResult = await updateTaskById(
113 | 				tasksPath,
114 | 				taskId,
115 | 				prompt,
116 | 				useResearch,
117 | 				{
118 | 					mcpLog: logWrapper,
119 | 					session,
120 | 					projectRoot,
121 | 					tag,
122 | 					commandName: 'update-task',
123 | 					outputType: 'mcp'
124 | 				},
125 | 				'json',
126 | 				append || false
127 | 			);
128 | 
129 | 			// Check if the core function returned null or an object without success
130 | 			if (!coreResult || coreResult.updatedTask === null) {
131 | 				// Core function logs the reason, just return success with info
132 | 				const message = `Task ${taskId} was not updated (likely already completed).`;
133 | 				logWrapper.info(message);
134 | 				return {
135 | 					success: true,
136 | 					data: {
137 | 						message: message,
138 | 						taskId: taskId,
139 | 						updated: false,
140 | 						telemetryData: coreResult?.telemetryData,
141 | 						tagInfo: coreResult?.tagInfo
142 | 					}
143 | 				};
144 | 			}
145 | 
146 | 			// Task was updated successfully
147 | 			const successMessage = `Successfully updated task with ID ${taskId} based on the prompt`;
148 | 			logWrapper.success(successMessage);
149 | 			return {
150 | 				success: true,
151 | 				data: {
152 | 					message: successMessage,
153 | 					taskId: taskId,
154 | 					tasksPath: tasksPath,
155 | 					useResearch: useResearch,
156 | 					updated: true,
157 | 					updatedTask: coreResult.updatedTask,
158 | 					telemetryData: coreResult.telemetryData,
159 | 					tagInfo: coreResult.tagInfo
160 | 				}
161 | 			};
162 | 		} catch (error) {
163 | 			logWrapper.error(`Error updating task by ID: ${error.message}`);
164 | 			return {
165 | 				success: false,
166 | 				error: {
167 | 					code: 'UPDATE_TASK_CORE_ERROR',
168 | 					message: error.message || 'Unknown error updating task'
169 | 				}
170 | 			};
171 | 		} finally {
172 | 			if (!wasSilent && isSilentMode()) {
173 | 				disableSilentMode();
174 | 			}
175 | 		}
176 | 	} catch (error) {
177 | 		logWrapper.error(`Setup error in updateTaskByIdDirect: ${error.message}`);
178 | 		if (isSilentMode()) disableSilentMode();
179 | 		return {
180 | 			success: false,
181 | 			error: {
182 | 				code: 'DIRECT_FUNCTION_SETUP_ERROR',
183 | 				message: error.message || 'Unknown setup error'
184 | 			}
185 | 		};
186 | 	}
187 | }
188 | 
```

--------------------------------------------------------------------------------
/.github/workflows/claude-docs-updater.yml:
--------------------------------------------------------------------------------

```yaml
  1 | name: Claude Documentation Updater
  2 | 
  3 | on:
  4 |   workflow_dispatch:
  5 |     inputs:
  6 |       commit_sha:
  7 |         description: 'The commit SHA that triggered this update'
  8 |         required: true
  9 |         type: string
 10 |       commit_message:
 11 |         description: 'The commit message'
 12 |         required: true
 13 |         type: string
 14 |       changed_files:
 15 |         description: 'List of changed files'
 16 |         required: true
 17 |         type: string
 18 |       commit_diff:
 19 |         description: 'Diff summary of changes'
 20 |         required: true
 21 |         type: string
 22 | 
 23 | jobs:
 24 |   update-docs:
 25 |     runs-on: ubuntu-latest
 26 |     permissions:
 27 |       contents: write
 28 |       pull-requests: write
 29 |       issues: write
 30 |     steps:
 31 |       - name: Checkout repository
 32 |         uses: actions/checkout@v4
 33 |         with:
 34 |           ref: next
 35 |           fetch-depth: 0 # Need full history to checkout specific commit
 36 | 
 37 |       - name: Create docs update branch
 38 |         id: create-branch
 39 |         run: |
 40 |           BRANCH_NAME="docs/auto-update-$(date +%Y%m%d-%H%M%S)"
 41 |           git checkout -b $BRANCH_NAME
 42 |           echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
 43 | 
 44 |       - name: Run Claude Code to Update Documentation
 45 |         uses: anthropics/claude-code-action@beta
 46 |         with:
 47 |           anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
 48 |           timeout_minutes: "30"
 49 |           mode: "agent"
 50 |           github_token: ${{ secrets.GITHUB_TOKEN }}
 51 |           experimental_allowed_domains: |
 52 |             .anthropic.com
 53 |             .github.com
 54 |             api.github.com
 55 |             .githubusercontent.com
 56 |             registry.npmjs.org
 57 |             .task-master.dev
 58 |           base_branch: "next"
 59 |           direct_prompt: |
 60 |             You are a documentation specialist. Analyze the recent changes pushed to the 'next' branch and update the documentation accordingly.
 61 | 
 62 |             Recent changes:
 63 |             - Commit: ${{ inputs.commit_message }}
 64 |             - Changed files:
 65 |             ${{ inputs.changed_files }}
 66 | 
 67 |             - Changes summary:
 68 |             ${{ inputs.commit_diff }}
 69 | 
 70 |             Your task:
 71 |             1. Analyze the changes to understand what functionality was added, modified, or removed
 72 |             2. Check if these changes require documentation updates in apps/docs/
 73 |             3. If documentation updates are needed:
 74 |                - Update relevant documentation files in apps/docs/
 75 |                - Ensure examples are updated if APIs changed
 76 |                - Update any configuration documentation if config options changed
 77 |                - Add new documentation pages if new features were added
 78 |                - Update the changelog or release notes if applicable
 79 |             4. If no documentation updates are needed, skip creating changes
 80 | 
 81 |             Guidelines:
 82 |             - Focus only on user-facing changes that need documentation
 83 |             - Keep documentation clear, concise, and helpful
 84 |             - Include code examples where appropriate
 85 |             - Maintain consistent documentation style with existing docs
 86 |             - Don't document internal implementation details unless they affect users
 87 |             - Update navigation/menu files if new pages are added
 88 | 
 89 |             Only make changes if the documentation truly needs updating based on the code changes.
 90 | 
 91 |       - name: Check if changes were made
 92 |         id: check-changes
 93 |         run: |
 94 |           if git diff --quiet; then
 95 |             echo "has_changes=false" >> $GITHUB_OUTPUT
 96 |           else
 97 |             echo "has_changes=true" >> $GITHUB_OUTPUT
 98 |             git add -A
 99 |             git config --local user.email "github-actions[bot]@users.noreply.github.com"
100 |             git config --local user.name "github-actions[bot]"
101 |             git commit -m "docs: auto-update documentation based on changes in next branch
102 | 
103 |             This PR was automatically generated to update documentation based on recent changes.
104 |             
105 |             Original commit: ${{ inputs.commit_message }}
106 |             
107 |             Co-authored-by: Claude <[email protected]>"
108 |           fi
109 | 
110 |       - name: Push changes and create PR
111 |         if: steps.check-changes.outputs.has_changes == 'true'
112 |         env:
113 |           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
114 |         run: |
115 |           git push origin ${{ steps.create-branch.outputs.branch_name }}
116 | 
117 |           # Create PR using GitHub CLI
118 |           gh pr create \
119 |             --title "docs: update documentation for recent changes" \
120 |             --body "## 📚 Documentation Update
121 | 
122 |             This PR automatically updates documentation based on recent changes merged to the \`next\` branch.
123 | 
124 |             ### Original Changes
125 |             **Commit:** ${{ inputs.commit_sha }}
126 |             **Message:** ${{ inputs.commit_message }}
127 |             
128 |             ### Changed Files in Original Commit
129 |             \`\`\`
130 |             ${{ inputs.changed_files }}
131 |             \`\`\`
132 | 
133 |             ### Documentation Updates
134 |             This PR includes documentation updates to reflect the changes above. Please review to ensure:
135 |             - [ ] Documentation accurately reflects the changes
136 |             - [ ] Examples are correct and working
137 |             - [ ] No important details are missing
138 |             - [ ] Style is consistent with existing documentation
139 | 
140 |             ---
141 |             *This PR was automatically generated by Claude Code GitHub Action*" \
142 |             --base next \
143 |             --head ${{ steps.create-branch.outputs.branch_name }} \
144 |             --label "documentation" \
145 |             --label "automated"
146 | 
```
Page 13/52FirstPrevNextLast