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

# Directory Structure

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

# Files

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

```javascript
import { newMultiBar } from './cli-progress-factory.js';

/**
 * Base class for progress trackers, handling common logic for time, tokens, estimation, and multibar management.
 */
export class BaseProgressTracker {
	constructor(options = {}) {
		this.numUnits = options.numUnits || 1;
		this.unitName = options.unitName || 'unit'; // e.g., 'task', 'subtask'
		this.startTime = null;
		this.completedUnits = 0;
		this.tokensIn = 0;
		this.tokensOut = 0;
		this.isEstimate = true; // For token display

		// Time estimation properties
		this.bestAvgTimePerUnit = null;
		this.lastEstimateTime = null;
		this.lastEstimateSeconds = 0;

		// UI components
		this.multibar = null;
		this.timeTokensBar = null;
		this.progressBar = null;
		this._timerInterval = null;

		// State flags
		this.isStarted = false;
		this.isFinished = false;

		// Allow subclasses to define custom properties
		this._initializeCustomProperties(options);
	}

	/**
	 * Protected method for subclasses to initialize custom properties.
	 * @protected
	 */
	_initializeCustomProperties(options) {
		// Subclasses can override this
	}

	/**
	 * Get the pluralized form of the unit name for safe property keys.
	 * @returns {string} Pluralized unit name
	 */
	get unitNamePlural() {
		return `${this.unitName}s`;
	}

	start() {
		if (this.isStarted || this.isFinished) return;

		this.isStarted = true;
		this.startTime = Date.now();

		this.multibar = newMultiBar();

		// Create time/tokens bar using subclass-provided format
		this.timeTokensBar = this.multibar.create(
			1,
			0,
			{},
			{
				format: this._getTimeTokensBarFormat(),
				barsize: 1,
				hideCursor: true,
				clearOnComplete: false
			}
		);

		// Create main progress bar using subclass-provided format
		this.progressBar = this.multibar.create(
			this.numUnits,
			0,
			{},
			{
				format: this._getProgressBarFormat(),
				barCompleteChar: '\u2588',
				barIncompleteChar: '\u2591'
			}
		);

		this._updateTimeTokensBar();
		this.progressBar.update(0, { [this.unitNamePlural]: `0/${this.numUnits}` });

		// Start timer
		this._timerInterval = setInterval(() => this._updateTimeTokensBar(), 1000);

		// Allow subclasses to add custom bars or setup
		this._setupCustomUI();
	}

	/**
	 * Protected method for subclasses to add custom UI elements after start.
	 * @protected
	 */
	_setupCustomUI() {
		// Subclasses can override this
	}

	/**
	 * Protected method to get the format for the time/tokens bar.
	 * @protected
	 * @returns {string} Format string for the time/tokens bar.
	 */
	_getTimeTokensBarFormat() {
		return `{clock} {elapsed} | Tokens (I/O): {in}/{out} | Est: {remaining}`;
	}

	/**
	 * Protected method to get the format for the main progress bar.
	 * @protected
	 * @returns {string} Format string for the progress bar.
	 */
	_getProgressBarFormat() {
		return `${this.unitName.charAt(0).toUpperCase() + this.unitName.slice(1)}s {${this.unitNamePlural}} |{bar}| {percentage}%`;
	}

	updateTokens(tokensIn, tokensOut, isEstimate = false) {
		this.tokensIn = tokensIn || 0;
		this.tokensOut = tokensOut || 0;
		this.isEstimate = isEstimate;
		this._updateTimeTokensBar();
	}

	_updateTimeTokensBar() {
		if (!this.timeTokensBar || this.isFinished) return;

		const elapsed = this._formatElapsedTime();
		const remaining = this._estimateRemainingTime();
		const tokensLabel = this.isEstimate ? '~ Tokens (I/O)' : 'Tokens (I/O)';

		this.timeTokensBar.update(1, {
			clock: '⏱️',
			elapsed,
			in: this.tokensIn,
			out: this.tokensOut,
			remaining,
			tokensLabel,
			// Subclasses can add more payload here via override
			...this._getCustomTimeTokensPayload()
		});
	}

	/**
	 * Protected method for subclasses to provide custom payload for time/tokens bar.
	 * @protected
	 * @returns {Object} Custom payload object.
	 */
	_getCustomTimeTokensPayload() {
		return {};
	}

	_formatElapsedTime() {
		if (!this.startTime) return '0m 00s';
		const seconds = Math.floor((Date.now() - this.startTime) / 1000);
		const minutes = Math.floor(seconds / 60);
		const remainingSeconds = seconds % 60;
		return `${minutes}m ${remainingSeconds.toString().padStart(2, '0')}s`;
	}

	_estimateRemainingTime() {
		const progress = this._getProgressFraction();
		if (progress >= 1) return '~0s';

		const now = Date.now();
		const elapsed = (now - this.startTime) / 1000;

		if (progress === 0) return '~calculating...';

		const avgTimePerUnit = elapsed / progress;

		if (
			this.bestAvgTimePerUnit === null ||
			avgTimePerUnit < this.bestAvgTimePerUnit
		) {
			this.bestAvgTimePerUnit = avgTimePerUnit;
		}

		const remainingUnits = this.numUnits * (1 - progress);
		let estimatedSeconds = Math.ceil(remainingUnits * this.bestAvgTimePerUnit);

		// Stabilization logic
		if (this.lastEstimateTime) {
			const elapsedSinceEstimate = Math.floor(
				(now - this.lastEstimateTime) / 1000
			);
			const countdownSeconds = Math.max(
				0,
				this.lastEstimateSeconds - elapsedSinceEstimate
			);
			if (countdownSeconds === 0) return '~0s';
			estimatedSeconds = Math.min(estimatedSeconds, countdownSeconds);
		}

		this.lastEstimateTime = now;
		this.lastEstimateSeconds = estimatedSeconds;

		return `~${this._formatDuration(estimatedSeconds)}`;
	}

	/**
	 * Protected method for subclasses to calculate current progress fraction (0-1).
	 * Defaults to simple completedUnits / numUnits.
	 * @protected
	 * @returns {number} Progress fraction (can be fractional for subtasks).
	 */
	_getProgressFraction() {
		return this.completedUnits / this.numUnits;
	}

	_formatDuration(seconds) {
		if (seconds < 60) return `${seconds}s`;
		const minutes = Math.floor(seconds / 60);
		const remainingSeconds = seconds % 60;
		if (minutes < 60) {
			return remainingSeconds > 0
				? `${minutes}m ${remainingSeconds}s`
				: `${minutes}m`;
		}
		const hours = Math.floor(minutes / 60);
		const remainingMinutes = minutes % 60;
		return `${hours}h ${remainingMinutes}m`;
	}

	getElapsedTime() {
		return this.startTime ? Date.now() - this.startTime : 0;
	}

	stop() {
		if (this.isFinished) return;

		this.isFinished = true;

		if (this._timerInterval) {
			clearInterval(this._timerInterval);
			this._timerInterval = null;
		}

		if (this.multibar) {
			this._updateTimeTokensBar();
			this.multibar.stop();
		}

		// Ensure cleanup is called to prevent memory leaks
		this.cleanup();
	}

	getSummary() {
		return {
			completedUnits: this.completedUnits,
			elapsedTime: this.getElapsedTime()
			// Subclasses should extend this
		};
	}

	/**
	 * Cleanup method to ensure proper resource disposal and prevent memory leaks.
	 * Should be called when the progress tracker is no longer needed.
	 */
	cleanup() {
		// Stop any active timers
		if (this._timerInterval) {
			clearInterval(this._timerInterval);
			this._timerInterval = null;
		}

		// Stop and clear multibar
		if (this.multibar) {
			try {
				this.multibar.stop();
			} catch (error) {
				// Ignore errors during cleanup
			}
			this.multibar = null;
		}

		// Clear progress bar references
		this.timeTokensBar = null;
		this.progressBar = null;

		// Reset state
		this.isStarted = false;
		this.isFinished = true;

		// Allow subclasses to perform custom cleanup
		this._performCustomCleanup();
	}

	/**
	 * Protected method for subclasses to perform custom cleanup.
	 * @protected
	 */
	_performCustomCleanup() {
		// Subclasses can override this
	}
}

```

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

```javascript
/**
 * expand-task.js
 * Direct function implementation for expanding a task into subtasks
 */

import expandTask from '../../../../scripts/modules/task-manager/expand-task.js';
import {
	readJSON,
	writeJSON,
	enableSilentMode,
	disableSilentMode,
	isSilentMode
} from '../../../../scripts/modules/utils.js';
import path from 'path';
import fs from 'fs';
import { createLogWrapper } from '../../tools/utils.js';

/**
 * Direct function wrapper for expanding a task into subtasks with error handling.
 *
 * @param {Object} args - Command arguments
 * @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
 * @param {string} args.id - The ID of the task to expand.
 * @param {number|string} [args.num] - Number of subtasks to generate.
 * @param {boolean} [args.research] - Enable research role for subtask generation.
 * @param {string} [args.prompt] - Additional context to guide subtask generation.
 * @param {boolean} [args.force] - Force expansion even if subtasks exist.
 * @param {string} [args.projectRoot] - Project root directory.
 * @param {string} [args.tag] - Tag for the task
 * @param {Object} log - Logger object
 * @param {Object} context - Context object containing session
 * @param {Object} [context.session] - MCP Session object
 * @returns {Promise<Object>} - Task expansion result { success: boolean, data?: any, error?: { code: string, message: string } }
 */
export async function expandTaskDirect(args, log, context = {}) {
	const { session } = context; // Extract session
	// Destructure expected args, including projectRoot
	const {
		tasksJsonPath,
		id,
		num,
		research,
		prompt,
		force,
		projectRoot,
		tag,
		complexityReportPath
	} = args;

	// Log session root data for debugging
	log.info(
		`Session data in expandTaskDirect: ${JSON.stringify({
			hasSession: !!session,
			sessionKeys: session ? Object.keys(session) : [],
			roots: session?.roots,
			rootsStr: JSON.stringify(session?.roots)
		})}`
	);

	// Check if tasksJsonPath was provided
	if (!tasksJsonPath) {
		log.error('expandTaskDirect called without tasksJsonPath');
		return {
			success: false,
			error: {
				code: 'MISSING_ARGUMENT',
				message: 'tasksJsonPath is required'
			}
		};
	}

	// Use provided path
	const tasksPath = tasksJsonPath;

	log.info(`[expandTaskDirect] Using tasksPath: ${tasksPath}`);

	// Validate task ID
	const taskId = id ? parseInt(id, 10) : null;
	if (!taskId) {
		log.error('Task ID is required');
		return {
			success: false,
			error: {
				code: 'INPUT_VALIDATION_ERROR',
				message: 'Task ID is required'
			}
		};
	}

	// Process other parameters
	const numSubtasks = num ? parseInt(num, 10) : undefined;
	const useResearch = research === true;
	const additionalContext = prompt || '';
	const forceFlag = force === true;

	try {
		log.info(
			`[expandTaskDirect] Expanding task ${taskId} into ${numSubtasks || 'default'} subtasks. Research: ${useResearch}, Force: ${forceFlag}`
		);

		// Read tasks data
		log.info(`[expandTaskDirect] Attempting to read JSON from: ${tasksPath}`);
		const data = readJSON(tasksPath, projectRoot);
		log.info(
			`[expandTaskDirect] Result of readJSON: ${data ? 'Data read successfully' : 'readJSON returned null or undefined'}`
		);

		if (!data || !data.tasks) {
			log.error(
				`[expandTaskDirect] readJSON failed or returned invalid data for path: ${tasksPath}`
			);
			return {
				success: false,
				error: {
					code: 'INVALID_TASKS_FILE',
					message: `No valid tasks found in ${tasksPath}. readJSON returned: ${JSON.stringify(data)}`
				}
			};
		}

		// Find the specific task
		log.info(`[expandTaskDirect] Searching for task ID ${taskId} in data`);
		const task = data.tasks.find((t) => t.id === taskId);
		log.info(`[expandTaskDirect] Task found: ${task ? 'Yes' : 'No'}`);

		if (!task) {
			return {
				success: false,
				error: {
					code: 'TASK_NOT_FOUND',
					message: `Task with ID ${taskId} not found`
				}
			};
		}

		// Check if task is completed
		if (task.status === 'done' || task.status === 'completed') {
			return {
				success: false,
				error: {
					code: 'TASK_COMPLETED',
					message: `Task ${taskId} is already marked as ${task.status} and cannot be expanded`
				}
			};
		}

		// Check for existing subtasks and force flag
		const hasExistingSubtasks = task.subtasks && task.subtasks.length > 0;
		if (hasExistingSubtasks && !forceFlag) {
			log.info(
				`Task ${taskId} already has ${task.subtasks.length} subtasks. Use --force to overwrite.`
			);
			return {
				success: true,
				data: {
					message: `Task ${taskId} already has subtasks. Expansion skipped.`,
					task,
					subtasksAdded: 0,
					hasExistingSubtasks
				}
			};
		}

		// If force flag is set, clear existing subtasks
		if (hasExistingSubtasks && forceFlag) {
			log.info(
				`Force flag set. Clearing existing subtasks for task ${taskId}.`
			);
			task.subtasks = [];
		}

		// Keep a copy of the task before modification
		const originalTask = JSON.parse(JSON.stringify(task));

		// Tracking subtasks count before expansion
		const subtasksCountBefore = task.subtasks ? task.subtasks.length : 0;

		// Directly modify the data instead of calling the CLI function
		if (!task.subtasks) {
			task.subtasks = [];
		}

		// Save tasks.json with potentially empty subtasks array and proper context
		writeJSON(tasksPath, data, projectRoot, tag);

		// Create logger wrapper using the utility
		const mcpLog = createLogWrapper(log);

		let wasSilent; // Declare wasSilent outside the try block
		// Process the request
		try {
			// Enable silent mode to prevent console logs from interfering with JSON response
			wasSilent = isSilentMode(); // Assign inside the try block
			if (!wasSilent) enableSilentMode();

			// Call the core expandTask function with the wrapped logger and projectRoot
			const coreResult = await expandTask(
				tasksPath,
				taskId,
				numSubtasks,
				useResearch,
				additionalContext,
				{
					complexityReportPath,
					mcpLog,
					session,
					projectRoot,
					commandName: 'expand-task',
					outputType: 'mcp',
					tag
				},
				forceFlag
			);

			// Restore normal logging
			if (!wasSilent && isSilentMode()) disableSilentMode();

			// Read the updated data
			const updatedData = readJSON(tasksPath, projectRoot);
			const updatedTask = updatedData.tasks.find((t) => t.id === taskId);

			// Calculate how many subtasks were added
			const subtasksAdded = updatedTask.subtasks
				? updatedTask.subtasks.length - subtasksCountBefore
				: 0;

			// Return the result, including telemetryData
			log.info(
				`Successfully expanded task ${taskId} with ${subtasksAdded} new subtasks`
			);
			return {
				success: true,
				data: {
					task: coreResult.task,
					subtasksAdded,
					hasExistingSubtasks,
					telemetryData: coreResult.telemetryData,
					tagInfo: coreResult.tagInfo
				}
			};
		} catch (error) {
			// Make sure to restore normal logging even if there's an error
			if (!wasSilent && isSilentMode()) disableSilentMode();

			log.error(`Error expanding task: ${error.message}`);
			return {
				success: false,
				error: {
					code: 'CORE_FUNCTION_ERROR',
					message: error.message || 'Failed to expand task'
				}
			};
		}
	} catch (error) {
		log.error(`Error expanding task: ${error.message}`);
		return {
			success: false,
			error: {
				code: 'CORE_FUNCTION_ERROR',
				message: error.message || 'Failed to expand task'
			}
		};
	}
}

```

--------------------------------------------------------------------------------
/apps/extension/src/webview/index.css:
--------------------------------------------------------------------------------

```css
@import "tailwindcss";

/* shadcn/ui CSS variables */
@theme {
	/* VS Code CSS variables will be injected here */
	/* color-scheme: var(--vscode-theme-kind, light); */

	/* shadcn/ui variables - adapted for VS Code */
	--color-background: var(--vscode-editor-background);
	--color-sidebar-background: var(--vscode-sideBar-background);
	--color-foreground: var(--vscode-foreground);
	--color-card: var(--vscode-editor-background);
	--color-card-foreground: var(--vscode-foreground);
	--color-popover: var(--vscode-editor-background);
	--color-popover-foreground: var(--vscode-foreground);
	--color-primary: var(--vscode-button-background);
	--color-primary-foreground: var(--vscode-button-foreground);
	--color-secondary: var(--vscode-button-secondaryBackground);
	--color-secondary-foreground: var(--vscode-button-secondaryForeground);
	--color-widget-background: var(--vscode-editorWidget-background);
	--color-widget-border: var(--vscode-editorWidget-border);
	--color-code-snippet-background: var(--vscode-textPreformat-background);
	--color-code-snippet-text: var(--vscode-textPreformat-foreground);
	--font-editor-font: var(--vscode-editor-font-family);
	--font-editor-size: var(--vscode-editor-font-size);
	--color-input-background: var(--vscode-input-background);
	--color-input-foreground: var(--vscode-input-foreground);
	--color-accent: var(--vscode-focusBorder);
	--color-accent-foreground: var(--vscode-foreground);
	--color-destructive: var(--vscode-errorForeground);
	--color-destructive-foreground: var(--vscode-foreground);
	--color-border: var(--vscode-panel-border);
	--color-ring: var(--vscode-focusBorder);
	--color-link: var(--vscode-editorLink-foreground);
	--color-link-hover: var(--vscode-editorLink-activeForeground);
	--color-textSeparator-foreground: var(--vscode-textSeparator-foreground);
	--radius: 0.5rem;

	/* VS Code specific color mappings for Tailwind utilities */
	--color-vscode-foreground: var(--vscode-foreground);
	--color-vscode-button-background: var(--vscode-button-background);
	--color-vscode-button-foreground: var(--vscode-button-foreground);
	--color-vscode-button-hoverBackground: var(--vscode-button-hoverBackground);
	--color-vscode-editor-background: var(--vscode-editor-background);
	--color-vscode-input-background: var(--vscode-input-background);
	--color-vscode-input-foreground: var(--vscode-input-foreground);
	--color-vscode-dropdown-background: var(--vscode-dropdown-background);
	--color-vscode-dropdown-foreground: var(--vscode-dropdown-foreground);
	--color-vscode-dropdown-border: var(--vscode-dropdown-border);
	--color-vscode-focusBorder: var(--vscode-focusBorder);
	--color-vscode-panel-border: var(--vscode-panel-border);
	--color-vscode-sideBar-background: var(--vscode-sideBar-background);
	--color-vscode-sideBar-foreground: var(--vscode-sideBar-foreground);
	--color-vscode-sideBarTitle-foreground: var(--vscode-sideBarTitle-foreground);
	--color-vscode-testing-iconPassed: var(--vscode-testing-iconPassed);
	--color-vscode-testing-iconFailed: var(--vscode-testing-iconFailed);
	--color-vscode-errorForeground: var(--vscode-errorForeground);
	--color-vscode-editorWidget-background: var(--vscode-editorWidget-background);
	--color-vscode-editorWidget-border: var(--vscode-editorWidget-border);
	--color-vscode-list-hoverBackground: var(--vscode-list-hoverBackground);
	--color-vscode-list-activeSelectionBackground: var(
		--vscode-list-activeSelectionBackground
	);
	--color-vscode-list-activeSelectionForeground: var(
		--vscode-list-activeSelectionForeground
	);
	--color-vscode-badge-background: var(--vscode-badge-background);
	--color-vscode-badge-foreground: var(--vscode-badge-foreground);
	--color-vscode-textLink-foreground: var(--vscode-textLink-foreground);
	--color-vscode-textLink-activeForeground: var(
		--vscode-textLink-activeForeground
	);
	--color-vscode-icon-foreground: var(--vscode-icon-foreground);
	--color-vscode-descriptionForeground: var(--vscode-descriptionForeground);
	--color-vscode-disabledForeground: var(--vscode-disabledForeground);
}

/* Reset body to match VS Code styles instead of Tailwind defaults */
@layer base {
	html,
	body {
		height: 100%;
		margin: 0 !important;
		padding: 0 !important;
		overflow: hidden;
	}

	body {
		background-color: var(--vscode-editor-background) !important;
		color: var(--vscode-foreground) !important;
		font-family: var(--vscode-font-family) !important;
		font-size: var(--vscode-font-size) !important;
		font-weight: var(--vscode-font-weight) !important;
		line-height: 1.4 !important;
	}

	/* Ensure root container takes full space */
	#root {
		height: 100vh;
		width: 100vw;
		display: flex;
		flex-direction: column;
		overflow: hidden;
	}

	/* Override any conflicting Tailwind defaults for VS Code integration */
	* {
		box-sizing: border-box;
	}

	/* Ensure buttons and inputs use VS Code styling */
	button,
	input,
	select,
	textarea {
		font-family: inherit;
	}
}

/* Enhanced scrollbar styling for Kanban board */
::-webkit-scrollbar {
	width: 8px;
	height: 8px;
}

::-webkit-scrollbar-track {
	background: var(--vscode-scrollbarSlider-background, rgba(255, 255, 255, 0.1));
	border-radius: 4px;
}

::-webkit-scrollbar-thumb {
	background: var(
		--vscode-scrollbarSlider-hoverBackground,
		rgba(255, 255, 255, 0.2)
	);
	border-radius: 4px;
	border: 1px solid transparent;
	background-clip: padding-box;
}

::-webkit-scrollbar-thumb:hover {
	background: var(
		--vscode-scrollbarSlider-activeBackground,
		rgba(255, 255, 255, 0.3)
	);
}

::-webkit-scrollbar-corner {
	background: var(--vscode-scrollbarSlider-background, rgba(255, 255, 255, 0.1));
}

/* Kanban specific styles */
@layer components {
	.kanban-container {
		scrollbar-gutter: stable;
	}

	/* Smooth scrolling for better UX */
	.kanban-container {
		scroll-behavior: smooth;
	}

	/* Ensure proper touch scrolling on mobile */
	.kanban-container {
		-webkit-overflow-scrolling: touch;
	}

	/* Add subtle shadow for depth */
	.kanban-column {
		box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
	}

	/* Enhanced scrolling for column content areas */
	.kanban-column > div[style*="overflow-y"] {
		scrollbar-width: thin;
		scrollbar-color: var(
				--vscode-scrollbarSlider-hoverBackground,
				rgba(255, 255, 255, 0.2)
			)
			var(--vscode-scrollbarSlider-background, rgba(255, 255, 255, 0.1));
	}

	/* Card hover effects */
	.kanban-card {
		transition: all 0.2s ease-in-out;
	}

	.kanban-card:hover {
		transform: translateY(-1px);
	}

	/* Focus indicators for accessibility */
	.kanban-card:focus-visible {
		outline: 2px solid var(--vscode-focusBorder);
		outline-offset: 2px;
	}
}

/* Line clamp utility for text truncation */
@layer utilities {
	.line-clamp-2 {
		overflow: hidden;
		display: -webkit-box;
		-webkit-box-orient: vertical;
		-webkit-line-clamp: 2;
	}

	.line-clamp-3 {
		overflow: hidden;
		display: -webkit-box;
		-webkit-box-orient: vertical;
		-webkit-line-clamp: 3;
	}

	/* Custom scrollbar utilities */
	.scrollbar-thin {
		scrollbar-width: thin;
	}

	.scrollbar-track-transparent {
		scrollbar-color: var(
				--vscode-scrollbarSlider-hoverBackground,
				rgba(255, 255, 255, 0.2)
			)
			transparent;
	}
}

/* Dark mode adjustments */
@media (prefers-color-scheme: dark) {
	::-webkit-scrollbar-track {
		background: rgba(255, 255, 255, 0.05);
	}

	::-webkit-scrollbar-thumb {
		background: rgba(255, 255, 255, 0.15);
	}

	::-webkit-scrollbar-thumb:hover {
		background: rgba(255, 255, 255, 0.25);
	}
}

```

--------------------------------------------------------------------------------
/src/ui/indicators.js:
--------------------------------------------------------------------------------

```javascript
/**
 * indicators.js
 * UI functions for displaying priority and complexity indicators in different contexts
 */

import chalk from 'chalk';
import { TASK_PRIORITY_OPTIONS } from '../constants/task-priority.js';

// Extract priority values for cleaner object keys
const [HIGH, MEDIUM, LOW] = TASK_PRIORITY_OPTIONS;

// Cache for generated indicators
const INDICATOR_CACHE = new Map();

/**
 * Base configuration for indicator systems
 */
class IndicatorConfig {
	constructor(name, levels, colors, thresholds = null) {
		this.name = name;
		this.levels = levels;
		this.colors = colors;
		this.thresholds = thresholds;
	}

	getColor(level) {
		return this.colors[level] || chalk.gray;
	}

	getLevelFromScore(score) {
		if (!this.thresholds) {
			throw new Error(`${this.name} does not support score-based levels`);
		}

		if (score >= 7) return this.levels[0]; // high
		if (score <= 3) return this.levels[2]; // low
		return this.levels[1]; // medium
	}
}

/**
 * Visual style definitions
 */
const VISUAL_STYLES = {
	cli: {
		filled: '●', // ●
		empty: '○' // ○
	},
	statusBar: {
		high: '⋮', // ⋮
		medium: ':', // :
		low: '.' // .
	},
	mcp: {
		high: '🔴', // 🔴
		medium: '🟠', // 🟠
		low: '🟢' // 🟢
	}
};

/**
 * Priority configuration
 */
const PRIORITY_CONFIG = new IndicatorConfig('priority', [HIGH, MEDIUM, LOW], {
	[HIGH]: chalk.hex('#CC0000'),
	[MEDIUM]: chalk.hex('#FF8800'),
	[LOW]: chalk.yellow
});

/**
 * Generates CLI indicator with intensity
 */
function generateCliIndicator(intensity, color) {
	const filled = VISUAL_STYLES.cli.filled;
	const empty = VISUAL_STYLES.cli.empty;

	let indicator = '';
	for (let i = 0; i < 3; i++) {
		if (i < intensity) {
			indicator += color(filled);
		} else {
			indicator += chalk.white(empty);
		}
	}
	return indicator;
}

/**
 * Get intensity level from priority/complexity level
 */
function getIntensityFromLevel(level, levels) {
	const index = levels.indexOf(level);
	return 3 - index; // high=3, medium=2, low=1
}

/**
 * Generic cached indicator getter
 * @param {string} cacheKey - Cache key for the indicators
 * @param {Function} generator - Function to generate the indicators
 * @returns {Object} Cached or newly generated indicators
 */
function getCachedIndicators(cacheKey, generator) {
	if (INDICATOR_CACHE.has(cacheKey)) {
		return INDICATOR_CACHE.get(cacheKey);
	}

	const indicators = generator();
	INDICATOR_CACHE.set(cacheKey, indicators);
	return indicators;
}

/**
 * Get priority indicators for MCP context (single emojis)
 * @returns {Object} Priority to emoji mapping
 */
export function getMcpPriorityIndicators() {
	return getCachedIndicators('mcp-priority-all', () => ({
		[HIGH]: VISUAL_STYLES.mcp.high,
		[MEDIUM]: VISUAL_STYLES.mcp.medium,
		[LOW]: VISUAL_STYLES.mcp.low
	}));
}

/**
 * Get priority indicators for CLI context (colored dots with visual hierarchy)
 * @returns {Object} Priority to colored dot string mapping
 */
export function getCliPriorityIndicators() {
	return getCachedIndicators('cli-priority-all', () => {
		const indicators = {};
		PRIORITY_CONFIG.levels.forEach((level) => {
			const intensity = getIntensityFromLevel(level, PRIORITY_CONFIG.levels);
			const color = PRIORITY_CONFIG.getColor(level);
			indicators[level] = generateCliIndicator(intensity, color);
		});
		return indicators;
	});
}

/**
 * Get priority indicators for status bars (simplified single character versions)
 * @returns {Object} Priority to single character indicator mapping
 */
export function getStatusBarPriorityIndicators() {
	return getCachedIndicators('statusbar-priority-all', () => {
		const indicators = {};
		PRIORITY_CONFIG.levels.forEach((level, index) => {
			const style =
				index === 0
					? VISUAL_STYLES.statusBar.high
					: index === 1
						? VISUAL_STYLES.statusBar.medium
						: VISUAL_STYLES.statusBar.low;
			const color = PRIORITY_CONFIG.getColor(level);
			indicators[level] = color(style);
		});
		return indicators;
	});
}

/**
 * Get priority colors for consistent styling
 * @returns {Object} Priority to chalk color function mapping
 */
export function getPriorityColors() {
	return {
		[HIGH]: PRIORITY_CONFIG.colors[HIGH],
		[MEDIUM]: PRIORITY_CONFIG.colors[MEDIUM],
		[LOW]: PRIORITY_CONFIG.colors[LOW]
	};
}

/**
 * Get priority indicators based on context
 * @param {boolean} isMcp - Whether this is for MCP context (true) or CLI context (false)
 * @returns {Object} Priority to indicator mapping
 */
export function getPriorityIndicators(isMcp = false) {
	return isMcp ? getMcpPriorityIndicators() : getCliPriorityIndicators();
}

/**
 * Get a specific priority indicator
 * @param {string} priority - The priority level ('high', 'medium', 'low')
 * @param {boolean} isMcp - Whether this is for MCP context
 * @returns {string} The indicator string for the priority
 */
export function getPriorityIndicator(priority, isMcp = false) {
	const indicators = getPriorityIndicators(isMcp);
	return indicators[priority] || indicators[MEDIUM];
}

// ============================================================================
// Complexity Indicators
// ============================================================================

/**
 * Complexity configuration
 */
const COMPLEXITY_CONFIG = new IndicatorConfig(
	'complexity',
	['high', 'medium', 'low'],
	{
		high: chalk.hex('#CC0000'),
		medium: chalk.hex('#FF8800'),
		low: chalk.green
	},
	{
		high: (score) => score >= 7,
		medium: (score) => score >= 4 && score <= 6,
		low: (score) => score <= 3
	}
);

/**
 * Get complexity indicators for CLI context (colored dots with visual hierarchy)
 * Complexity scores: 1-3 (low), 4-6 (medium), 7-10 (high)
 * @returns {Object} Complexity level to colored dot string mapping
 */
export function getCliComplexityIndicators() {
	return getCachedIndicators('cli-complexity-all', () => {
		const indicators = {};
		COMPLEXITY_CONFIG.levels.forEach((level) => {
			const intensity = getIntensityFromLevel(level, COMPLEXITY_CONFIG.levels);
			const color = COMPLEXITY_CONFIG.getColor(level);
			indicators[level] = generateCliIndicator(intensity, color);
		});
		return indicators;
	});
}

/**
 * Get complexity indicators for status bars (simplified single character versions)
 * @returns {Object} Complexity level to single character indicator mapping
 */
export function getStatusBarComplexityIndicators() {
	return getCachedIndicators('statusbar-complexity-all', () => {
		const indicators = {};
		COMPLEXITY_CONFIG.levels.forEach((level, index) => {
			const style =
				index === 0
					? VISUAL_STYLES.statusBar.high
					: index === 1
						? VISUAL_STYLES.statusBar.medium
						: VISUAL_STYLES.statusBar.low;
			const color = COMPLEXITY_CONFIG.getColor(level);
			indicators[level] = color(style);
		});
		return indicators;
	});
}

/**
 * Get complexity colors for consistent styling
 * @returns {Object} Complexity level to chalk color function mapping
 */
export function getComplexityColors() {
	return { ...COMPLEXITY_CONFIG.colors };
}

/**
 * Get a specific complexity indicator based on score
 * @param {number} score - The complexity score (1-10)
 * @param {boolean} statusBar - Whether to return status bar version (single char)
 * @returns {string} The indicator string for the complexity level
 */
export function getComplexityIndicator(score, statusBar = false) {
	const level = COMPLEXITY_CONFIG.getLevelFromScore(score);
	const indicators = statusBar
		? getStatusBarComplexityIndicators()
		: getCliComplexityIndicators();
	return indicators[level];
}

```

--------------------------------------------------------------------------------
/scripts/modules/task-manager/expand-all-tasks.js:
--------------------------------------------------------------------------------

```javascript
import { log, readJSON, isSilentMode, findProjectRoot } from '../utils.js';
import {
	startLoadingIndicator,
	stopLoadingIndicator,
	displayAiUsageSummary
} from '../ui.js';
import expandTask from './expand-task.js';
import { getDebugFlag } from '../config-manager.js';
import { aggregateTelemetry } from '../utils.js';
import chalk from 'chalk';
import boxen from 'boxen';

/**
 * Expand all eligible pending or in-progress tasks using the expandTask function.
 * @param {string} tasksPath - Path to the tasks.json file
 * @param {number} [numSubtasks] - Optional: Target number of subtasks per task.
 * @param {boolean} [useResearch=false] - Whether to use the research AI role.
 * @param {string} [additionalContext=''] - Optional additional context.
 * @param {boolean} [force=false] - Force expansion even if tasks already have subtasks.
 * @param {Object} context - Context object containing session and mcpLog.
 * @param {Object} [context.session] - Session object from MCP.
 * @param {Object} [context.mcpLog] - MCP logger object.
 * @param {string} [context.projectRoot] - Project root path
 * @param {string} [context.tag] - Tag for the task
 * @param {string} [context.complexityReportPath] - Path to the complexity report file
 * @param {string} [outputFormat='text'] - Output format ('text' or 'json'). MCP calls should use 'json'.
 * @returns {Promise<{success: boolean, expandedCount: number, failedCount: number, skippedCount: number, tasksToExpand: number, telemetryData: Array<Object>}>} - Result summary.
 */
async function expandAllTasks(
	tasksPath,
	numSubtasks, // Keep this signature, expandTask handles defaults
	useResearch = false,
	additionalContext = '',
	force = false, // Keep force here for the filter logic
	context = {},
	outputFormat = 'text' // Assume text default for CLI
) {
	const {
		session,
		mcpLog,
		projectRoot: providedProjectRoot,
		tag,
		complexityReportPath
	} = context;
	const isMCPCall = !!mcpLog; // Determine if called from MCP

	const projectRoot = providedProjectRoot || findProjectRoot();
	if (!projectRoot) {
		throw new Error('Could not determine project root directory');
	}

	// Use mcpLog if available, otherwise use the default console log wrapper respecting silent mode
	const logger =
		mcpLog ||
		(outputFormat === 'json'
			? {
					// Basic logger for JSON output mode
					info: (msg) => {},
					warn: (msg) => {},
					error: (msg) => console.error(`ERROR: ${msg}`), // Still log errors
					debug: (msg) => {}
				}
			: {
					// CLI logger respecting silent mode
					info: (msg) => !isSilentMode() && log('info', msg),
					warn: (msg) => !isSilentMode() && log('warn', msg),
					error: (msg) => !isSilentMode() && log('error', msg),
					debug: (msg) =>
						!isSilentMode() && getDebugFlag(session) && log('debug', msg)
				});

	let loadingIndicator = null;
	let expandedCount = 0;
	let failedCount = 0;
	let tasksToExpandCount = 0;
	const allTelemetryData = []; // Still collect individual data first

	if (!isMCPCall && outputFormat === 'text') {
		loadingIndicator = startLoadingIndicator(
			'Analyzing tasks for expansion...'
		);
	}

	try {
		logger.info(`Reading tasks from ${tasksPath}`);
		const data = readJSON(tasksPath, projectRoot, tag);
		if (!data || !data.tasks) {
			throw new Error(`Invalid tasks data in ${tasksPath}`);
		}

		// --- Restore Original Filtering Logic ---
		const tasksToExpand = data.tasks.filter(
			(task) =>
				(task.status === 'pending' || task.status === 'in-progress') && // Include 'in-progress'
				(!task.subtasks || task.subtasks.length === 0 || force) // Check subtasks/force here
		);
		tasksToExpandCount = tasksToExpand.length; // Get the count from the filtered array
		logger.info(`Found ${tasksToExpandCount} tasks eligible for expansion.`);
		// --- End Restored Filtering Logic ---

		if (loadingIndicator) {
			stopLoadingIndicator(loadingIndicator, 'Analysis complete.');
		}

		if (tasksToExpandCount === 0) {
			logger.info('No tasks eligible for expansion.');
			// --- Fix: Restore success: true and add message ---
			return {
				success: true, // Indicate overall success despite no action
				expandedCount: 0,
				failedCount: 0,
				skippedCount: 0,
				tasksToExpand: 0,
				telemetryData: allTelemetryData,
				message: 'No tasks eligible for expansion.'
			};
			// --- End Fix ---
		}

		// Iterate over the already filtered tasks
		for (const task of tasksToExpand) {
			// Start indicator for individual task expansion in CLI mode
			let taskIndicator = null;
			if (!isMCPCall && outputFormat === 'text') {
				taskIndicator = startLoadingIndicator(`Expanding task ${task.id}...`);
			}

			try {
				// Call the refactored expandTask function AND capture result
				const result = await expandTask(
					tasksPath,
					task.id,
					numSubtasks,
					useResearch,
					additionalContext,
					{
						...context,
						projectRoot,
						tag: data.tag || tag,
						complexityReportPath
					}, // Pass the whole context object with projectRoot and resolved tag
					force
				);
				expandedCount++;

				// Collect individual telemetry data
				if (result && result.telemetryData) {
					allTelemetryData.push(result.telemetryData);
				}

				if (taskIndicator) {
					stopLoadingIndicator(taskIndicator, `Task ${task.id} expanded.`);
				}
				logger.info(`Successfully expanded task ${task.id}.`);
			} catch (error) {
				failedCount++;
				if (taskIndicator) {
					stopLoadingIndicator(
						taskIndicator,
						`Failed to expand task ${task.id}.`,
						false
					);
				}
				logger.error(`Failed to expand task ${task.id}: ${error.message}`);
				// Continue to the next task
			}
		}

		// --- AGGREGATION AND DISPLAY ---
		logger.info(
			`Expansion complete: ${expandedCount} expanded, ${failedCount} failed.`
		);

		// Aggregate the collected telemetry data
		const aggregatedTelemetryData = aggregateTelemetry(
			allTelemetryData,
			'expand-all-tasks'
		);

		if (outputFormat === 'text') {
			const summaryContent =
				`${chalk.white.bold('Expansion Summary:')}\n\n` +
				`${chalk.cyan('-')} Attempted: ${chalk.bold(tasksToExpandCount)}\n` +
				`${chalk.green('-')} Expanded:  ${chalk.bold(expandedCount)}\n` +
				// Skipped count is always 0 now due to pre-filtering
				`${chalk.gray('-')} Skipped:   ${chalk.bold(0)}\n` +
				`${chalk.red('-')} Failed:    ${chalk.bold(failedCount)}`;

			console.log(
				boxen(summaryContent, {
					padding: 1,
					margin: { top: 1 },
					borderColor: failedCount > 0 ? 'red' : 'green', // Red if failures, green otherwise
					borderStyle: 'round'
				})
			);
		}

		if (outputFormat === 'text' && aggregatedTelemetryData) {
			displayAiUsageSummary(aggregatedTelemetryData, 'cli');
		}

		// Return summary including the AGGREGATED telemetry data
		return {
			success: true,
			expandedCount,
			failedCount,
			skippedCount: 0,
			tasksToExpand: tasksToExpandCount,
			telemetryData: aggregatedTelemetryData
		};
	} catch (error) {
		if (loadingIndicator)
			stopLoadingIndicator(loadingIndicator, 'Error.', false);
		logger.error(`Error during expand all operation: ${error.message}`);
		if (!isMCPCall && getDebugFlag(session)) {
			console.error(error); // Log full stack in debug CLI mode
		}
		// Re-throw error for the caller to handle, the direct function will format it
		throw error; // Let direct function wrapper handle formatting
		/* Original re-throw:
		throw new Error(`Failed to expand all tasks: ${error.message}`);
		*/
	}
}

export default expandAllTasks;

```

--------------------------------------------------------------------------------
/tests/unit/ai-providers/openai.test.js:
--------------------------------------------------------------------------------

```javascript
/**
 * Tests for OpenAI Provider - Token parameter handling for GPT-5
 *
 * This test suite covers:
 * 1. Correct identification of GPT-5 models requiring max_completion_tokens
 * 2. Token parameter preparation for different model types
 * 3. Validation of maxTokens parameter
 * 4. Integer coercion of token values
 */

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

// Mock the utils module to prevent logging during tests
jest.mock('../../../scripts/modules/utils.js', () => ({
	log: jest.fn()
}));

// Import the provider
import { OpenAIProvider } from '../../../src/ai-providers/openai.js';

describe('OpenAIProvider', () => {
	let provider;

	beforeEach(() => {
		provider = new OpenAIProvider();
		jest.clearAllMocks();
	});

	describe('requiresMaxCompletionTokens', () => {
		it('should return true for GPT-5 models', () => {
			expect(provider.requiresMaxCompletionTokens('gpt-5')).toBe(true);
			expect(provider.requiresMaxCompletionTokens('gpt-5-mini')).toBe(true);
			expect(provider.requiresMaxCompletionTokens('gpt-5-nano')).toBe(true);
			expect(provider.requiresMaxCompletionTokens('gpt-5-turbo')).toBe(true);
		});

		it('should return false for non-GPT-5 models', () => {
			expect(provider.requiresMaxCompletionTokens('gpt-4')).toBe(false);
			expect(provider.requiresMaxCompletionTokens('gpt-4o')).toBe(false);
			expect(provider.requiresMaxCompletionTokens('gpt-3.5-turbo')).toBe(false);
			expect(provider.requiresMaxCompletionTokens('o1')).toBe(false);
			expect(provider.requiresMaxCompletionTokens('o1-mini')).toBe(false);
		});

		it('should handle null/undefined modelId', () => {
			expect(provider.requiresMaxCompletionTokens(null)).toBeFalsy();
			expect(provider.requiresMaxCompletionTokens(undefined)).toBeFalsy();
			expect(provider.requiresMaxCompletionTokens('')).toBeFalsy();
		});
	});

	describe('prepareTokenParam', () => {
		it('should return max_completion_tokens for GPT-5 models', () => {
			const result = provider.prepareTokenParam('gpt-5', 1000);
			expect(result).toEqual({ max_completion_tokens: 1000 });
		});

		it('should return maxTokens for non-GPT-5 models', () => {
			const result = provider.prepareTokenParam('gpt-4', 1000);
			expect(result).toEqual({ maxTokens: 1000 });
		});

		it('should coerce token value to integer', () => {
			// Float values
			const result1 = provider.prepareTokenParam('gpt-5', 1000.7);
			expect(result1).toEqual({ max_completion_tokens: 1000 });

			const result2 = provider.prepareTokenParam('gpt-4', 1000.7);
			expect(result2).toEqual({ maxTokens: 1000 });

			// String float
			const result3 = provider.prepareTokenParam('gpt-5', '1000.7');
			expect(result3).toEqual({ max_completion_tokens: 1000 });

			// String integers (common CLI input path)
			expect(provider.prepareTokenParam('gpt-5', '1000')).toEqual({
				max_completion_tokens: 1000
			});
			expect(provider.prepareTokenParam('gpt-4', '1000')).toEqual({
				maxTokens: 1000
			});
		});

		it('should return empty object for undefined maxTokens', () => {
			const result = provider.prepareTokenParam('gpt-5', undefined);
			expect(result).toEqual({});
		});

		it('should handle edge cases', () => {
			// Test with 0 (should still pass through as 0)
			const result1 = provider.prepareTokenParam('gpt-5', 0);
			expect(result1).toEqual({ max_completion_tokens: 0 });

			// Test with string number
			const result2 = provider.prepareTokenParam('gpt-5', '100');
			expect(result2).toEqual({ max_completion_tokens: 100 });

			// Test with negative number (will be floored, validation happens elsewhere)
			const result3 = provider.prepareTokenParam('gpt-4', -10.5);
			expect(result3).toEqual({ maxTokens: -11 });
		});
	});

	describe('validateOptionalParams', () => {
		it('should accept valid maxTokens values', () => {
			expect(() =>
				provider.validateOptionalParams({ maxTokens: 1000 })
			).not.toThrow();
			expect(() =>
				provider.validateOptionalParams({ maxTokens: 1 })
			).not.toThrow();
			expect(() =>
				provider.validateOptionalParams({ maxTokens: '1000' })
			).not.toThrow();
		});

		it('should reject invalid maxTokens values', () => {
			expect(() => provider.validateOptionalParams({ maxTokens: 0 })).toThrow(
				Error
			);
			expect(() => provider.validateOptionalParams({ maxTokens: -1 })).toThrow(
				Error
			);
			expect(() => provider.validateOptionalParams({ maxTokens: NaN })).toThrow(
				Error
			);
			expect(() =>
				provider.validateOptionalParams({ maxTokens: Infinity })
			).toThrow(Error);
			expect(() =>
				provider.validateOptionalParams({ maxTokens: 'invalid' })
			).toThrow(Error);
		});

		it('should accept valid temperature values', () => {
			expect(() =>
				provider.validateOptionalParams({ temperature: 0 })
			).not.toThrow();
			expect(() =>
				provider.validateOptionalParams({ temperature: 0.5 })
			).not.toThrow();
			expect(() =>
				provider.validateOptionalParams({ temperature: 1 })
			).not.toThrow();
		});

		it('should reject invalid temperature values', () => {
			expect(() =>
				provider.validateOptionalParams({ temperature: -0.1 })
			).toThrow(Error);
			expect(() =>
				provider.validateOptionalParams({ temperature: 1.1 })
			).toThrow(Error);
		});
	});

	describe('getRequiredApiKeyName', () => {
		it('should return OPENAI_API_KEY', () => {
			expect(provider.getRequiredApiKeyName()).toBe('OPENAI_API_KEY');
		});
	});

	describe('getClient', () => {
		it('should throw error if API key is missing', () => {
			expect(() => provider.getClient({})).toThrow(Error);
		});

		it('should create client with apiKey only', () => {
			const params = {
				apiKey: 'sk-test-123'
			};

			// The getClient method should return a function
			const client = provider.getClient(params);
			expect(typeof client).toBe('function');

			// The client function should be callable and return a model object
			const model = client('gpt-4');
			expect(model).toBeDefined();
			expect(model.modelId).toBe('gpt-4');
		});

		it('should create client with apiKey and baseURL', () => {
			const params = {
				apiKey: 'sk-test-456',
				baseURL: 'https://api.openai.example'
			};

			// Should not throw when baseURL is provided
			const client = provider.getClient(params);
			expect(typeof client).toBe('function');

			// The client function should be callable and return a model object
			const model = client('gpt-5');
			expect(model).toBeDefined();
			expect(model.modelId).toBe('gpt-5');
		});

		it('should return the same client instance for the same parameters', () => {
			const params = {
				apiKey: 'sk-test-789'
			};

			// Multiple calls with same params should work
			const client1 = provider.getClient(params);
			const client2 = provider.getClient(params);

			expect(typeof client1).toBe('function');
			expect(typeof client2).toBe('function');

			// Both clients should be able to create models
			const model1 = client1('gpt-4');
			const model2 = client2('gpt-4');
			expect(model1.modelId).toBe('gpt-4');
			expect(model2.modelId).toBe('gpt-4');
		});

		it('should handle different model IDs correctly', () => {
			const client = provider.getClient({ apiKey: 'sk-test-models' });

			// Test with different models
			const gpt4 = client('gpt-4');
			expect(gpt4.modelId).toBe('gpt-4');

			const gpt5 = client('gpt-5');
			expect(gpt5.modelId).toBe('gpt-5');

			const gpt35 = client('gpt-3.5-turbo');
			expect(gpt35.modelId).toBe('gpt-3.5-turbo');
		});
	});

	describe('name property', () => {
		it('should have OpenAI as the provider name', () => {
			expect(provider.name).toBe('OpenAI');
		});
	});
});

```

--------------------------------------------------------------------------------
/docs/cross-tag-task-movement.md:
--------------------------------------------------------------------------------

```markdown
# Cross-Tag Task Movement

Task Master now supports moving tasks between different tag contexts, allowing you to organize your work across multiple project contexts, feature branches, or development phases.

## Overview

Cross-tag task movement enables you to:
- Move tasks between different tag contexts (e.g., from "backlog" to "in-progress")
- Handle cross-tag dependencies intelligently
- Maintain task relationships across different contexts
- Organize work across multiple project phases

## Basic Usage

### Within-Tag Moves

Move tasks within the same tag context:

```bash
# Move a single task
task-master move --from=5 --to=7

# Move a subtask
task-master move --from=5.2 --to=7.3

# Move multiple tasks
task-master move --from=5,6,7 --to=10,11,12
```

### Cross-Tag Moves

Move tasks between different tag contexts:

```bash
# Basic cross-tag move
task-master move --from=5 --from-tag=backlog --to-tag=in-progress

# Move multiple tasks
task-master move --from=5,6,7 --from-tag=backlog --to-tag=done
```

## Dependency Resolution

When moving tasks between tags, you may encounter cross-tag dependencies. Task Master provides several options to handle these:

### Move with Dependencies

Move the main task along with all its dependent tasks:

```bash
task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies
```

This ensures that all dependent tasks are moved together, maintaining the task relationships.

### Break Dependencies

Break cross-tag dependencies and move only the specified task:

```bash
task-master move --from=5 --from-tag=backlog --to-tag=in-progress --ignore-dependencies
```

This removes the dependency relationships and moves only the specified task.

### Force Move

Note: Force moves are no longer supported. Instead, use one of these options:

- `--with-dependencies` — move dependents together
- `--ignore-dependencies` — break cross-tag dependencies

⚠️ **Warning**: This may break dependency relationships and should be used with caution.

## Error Handling

Task Master provides enhanced error messages with specific resolution suggestions:

### Cross-Tag Dependency Conflicts

When you encounter dependency conflicts, you'll see:

```text
❌ Cannot move tasks from "backlog" to "in-progress"

Cross-tag dependency conflicts detected:
  • Task 5 depends on 2 (in backlog)
  • Task 6 depends on 3 (in done)

Resolution options:
  1. Move with dependencies: task-master move --from=5,6 --from-tag=backlog --to-tag=in-progress --with-dependencies
  2. Break dependencies: task-master move --from=5,6 --from-tag=backlog --to-tag=in-progress --ignore-dependencies
  3. Validate and fix dependencies: task-master validate-dependencies && task-master fix-dependencies
  4. Move dependencies first: task-master move --from=2,3 --from-tag=backlog --to-tag=in-progress
  5. After deciding, re-run the move with either --with-dependencies or --ignore-dependencies
```

### Subtask Movement Restrictions

Subtasks cannot be moved directly between tags:

```text
❌ Cannot move subtask 5.2 directly between tags

Subtask movement restriction:
  • Subtasks cannot be moved directly between tags
  • They must be promoted to full tasks first

Resolution options:
  1. Promote subtask to full task: task-master remove-subtask --id=5.2 --convert
  2. Then move the promoted task: task-master move --from=5 --from-tag=backlog --to-tag=in-progress
  3. Or move the parent task with all subtasks: task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies
```

### Invalid Tag Combinations

When source and target tags are the same:

```text
❌ Invalid tag combination

Error details:
  • Source tag: "backlog"
  • Target tag: "backlog"
  • Reason: Source and target tags are identical

Resolution options:
  1. Use different tags for cross-tag moves
  2. Use within-tag move: task-master move --from=<id> --to=<id> --tag=backlog
  3. Check available tags: task-master tags
```

## Best Practices

### 1. Check Dependencies First

Before moving tasks, validate your dependencies:

```bash
# Check for dependency issues
task-master validate-dependencies

# Fix common dependency problems
task-master fix-dependencies
```

### 2. Use Appropriate Flags

- **`--with-dependencies`**: When you want to maintain task relationships
- **`--ignore-dependencies`**: When you want to break cross-tag dependencies

### 3. Organize by Context

Use tags to organize work by:
- **Development phases**: `backlog`, `in-progress`, `review`, `done`
- **Feature branches**: `feature-auth`, `feature-dashboard`
- **Team members**: `alice-tasks`, `bob-tasks`
- **Project versions**: `v1.0`, `v2.0`

### 4. Handle Subtasks Properly

For subtasks, either:
1. Promote the subtask to a full task first
2. Move the parent task with all subtasks using `--with-dependencies`

## Advanced Usage

### Multiple Task Movement

Move multiple tasks at once:

```bash
# Move multiple tasks with dependencies
task-master move --from=5,6,7 --from-tag=backlog --to-tag=in-progress --with-dependencies

# Move multiple tasks, breaking dependencies
task-master move --from=5,6,7 --from-tag=backlog --to-tag=in-progress --ignore-dependencies
```

### Tag Creation

Target tags are created automatically if they don't exist:

```bash
# This will create the "new-feature" tag if it doesn't exist
task-master move --from=5 --from-tag=backlog --to-tag=new-feature
```

### Current Tag Fallback

If `--from-tag` is not provided, the current tag is used:

```bash
# Uses current tag as source
task-master move --from=5 --to-tag=in-progress
```

## MCP Integration

The cross-tag move functionality is also available through MCP tools:

```javascript
// Move task with dependencies
await moveTask({
  from: "5",
  fromTag: "backlog", 
  toTag: "in-progress",
  withDependencies: true
});

// Break dependencies
await moveTask({
  from: "5",
  fromTag: "backlog",
  toTag: "in-progress", 
  ignoreDependencies: true
});
```

## Troubleshooting

### Common Issues

1. **"Source tag not found"**: Check available tags with `task-master tags`
2. **"Task not found"**: Verify task IDs with `task-master list`
3. **"Cross-tag dependency conflicts"**: Use dependency resolution flags
4. **"Cannot move subtask"**: Promote subtask first or move parent task

### Getting Help

```bash
# Show move command help
task-master move --help

# Check available tags
task-master tags

# Validate dependencies
task-master validate-dependencies

# Fix dependency issues
task-master fix-dependencies
```

## Examples

### Scenario 1: Moving from Backlog to In-Progress

```bash
# Check for dependencies first
task-master validate-dependencies

# Move with dependencies
task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies
```

### Scenario 2: Breaking Dependencies

```bash
# Move task, breaking cross-tag dependencies
task-master move --from=5 --from-tag=backlog --to-tag=done --ignore-dependencies
```

### Scenario 3: Force Move

Choose one of these options explicitly:

```bash
# Move together with dependencies
task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies

# Or break dependencies
task-master move --from=5 --from-tag=backlog --to-tag=in-progress --ignore-dependencies
```

### Scenario 4: Moving Subtasks

```bash
# Option 1: Promote subtask first
task-master remove-subtask --id=5.2 --convert
task-master move --from=5 --from-tag=backlog --to-tag=in-progress

# Option 2: Move parent with all subtasks
task-master move --from=5 --from-tag=backlog --to-tag=in-progress --with-dependencies
```

```

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

```javascript
import path from 'path';
import * as fs from 'fs';
import { readJSON, writeJSON, log, findTaskById } from '../utils.js';
import generateTaskFiles from './generate-task-files.js';
import taskExists from './task-exists.js';

/**
 * Removes one or more tasks or subtasks from the tasks file
 * @param {string} tasksPath - Path to the tasks file
 * @param {string} taskIds - Comma-separated string of task/subtask IDs to remove (e.g., '5,6.1,7')
 * @param {Object} context - Context object containing projectRoot and tag information
 * @param {string} [context.projectRoot] - Project root path
 * @param {string} [context.tag] - Tag for the task
 * @returns {Object} Result object with success status, messages, and removed task info
 */
async function removeTask(tasksPath, taskIds, context = {}) {
	const { projectRoot, tag } = context;
	const results = {
		success: true,
		messages: [],
		errors: [],
		removedTasks: []
	};
	const taskIdsToRemove = taskIds
		.split(',')
		.map((id) => id.trim())
		.filter(Boolean); // Remove empty strings if any

	if (taskIdsToRemove.length === 0) {
		results.success = false;
		results.errors.push('No valid task IDs provided.');
		return results;
	}

	try {
		// Read the tasks file ONCE before the loop, preserving the full tagged structure
		const rawData = readJSON(tasksPath, projectRoot, tag); // Read raw data
		if (!rawData) {
			throw new Error(`Could not read tasks file at ${tasksPath}`);
		}

		// Use the full tagged data if available, otherwise use the data as is
		const fullTaggedData = rawData._rawTaggedData || rawData;

		if (!fullTaggedData[tag] || !fullTaggedData[tag].tasks) {
			throw new Error(`Tag '${tag}' not found or has no tasks.`);
		}

		const tasks = fullTaggedData[tag].tasks; // Work with tasks from the correct tag

		const tasksToDeleteFiles = []; // Collect IDs of main tasks whose files should be deleted

		for (const taskId of taskIdsToRemove) {
			// Check if the task ID exists *before* attempting removal
			if (!taskExists(tasks, taskId)) {
				const errorMsg = `Task with ID ${taskId} in tag '${tag}' not found or already removed.`;
				results.errors.push(errorMsg);
				results.success = false; // Mark overall success as false if any error occurs
				continue; // Skip to the next ID
			}

			try {
				// Handle subtask removal (e.g., '5.2')
				if (typeof taskId === 'string' && taskId.includes('.')) {
					const [parentTaskId, subtaskId] = taskId
						.split('.')
						.map((id) => parseInt(id, 10));

					// Find the parent task
					const parentTask = tasks.find((t) => t.id === parentTaskId);
					if (!parentTask || !parentTask.subtasks) {
						throw new Error(
							`Parent task ${parentTaskId} or its subtasks not found for subtask ${taskId}`
						);
					}

					// Find the subtask to remove
					const subtaskIndex = parentTask.subtasks.findIndex(
						(st) => st.id === subtaskId
					);
					if (subtaskIndex === -1) {
						throw new Error(
							`Subtask ${subtaskId} not found in parent task ${parentTaskId}`
						);
					}

					// Store the subtask info before removal
					const removedSubtask = {
						...parentTask.subtasks[subtaskIndex],
						parentTaskId: parentTaskId
					};
					results.removedTasks.push(removedSubtask);

					// Remove the subtask from the parent
					parentTask.subtasks.splice(subtaskIndex, 1);

					results.messages.push(
						`Successfully removed subtask ${taskId} from tag '${tag}'`
					);
				}
				// Handle main task removal
				else {
					const taskIdNum = parseInt(taskId, 10);
					const taskIndex = tasks.findIndex((t) => t.id === taskIdNum);
					if (taskIndex === -1) {
						throw new Error(`Task with ID ${taskId} not found in tag '${tag}'`);
					}

					// Store the task info before removal
					const removedTask = tasks[taskIndex];
					results.removedTasks.push(removedTask);
					tasksToDeleteFiles.push(taskIdNum); // Add to list for file deletion

					// Remove the task from the main array
					tasks.splice(taskIndex, 1);

					results.messages.push(
						`Successfully removed task ${taskId} from tag '${tag}'`
					);
				}
			} catch (innerError) {
				// Catch errors specific to processing *this* ID
				const errorMsg = `Error processing ID ${taskId}: ${innerError.message}`;
				results.errors.push(errorMsg);
				results.success = false;
				log('warn', errorMsg); // Log as warning and continue with next ID
			}
		} // End of loop through taskIdsToRemove

		// --- Post-Loop Operations ---

		// Only proceed with cleanup and saving if at least one task was potentially removed
		if (results.removedTasks.length > 0) {
			const allRemovedIds = new Set(
				taskIdsToRemove.map((id) =>
					typeof id === 'string' && id.includes('.') ? id : parseInt(id, 10)
				)
			);

			// Update the tasks in the current tag of the full data structure
			fullTaggedData[tag].tasks = tasks;

			// Remove dependencies from all tags
			for (const tagName in fullTaggedData) {
				if (
					Object.prototype.hasOwnProperty.call(fullTaggedData, tagName) &&
					fullTaggedData[tagName] &&
					fullTaggedData[tagName].tasks
				) {
					const currentTagTasks = fullTaggedData[tagName].tasks;
					currentTagTasks.forEach((task) => {
						if (task.dependencies) {
							task.dependencies = task.dependencies.filter(
								(depId) => !allRemovedIds.has(depId)
							);
						}
						if (task.subtasks) {
							task.subtasks.forEach((subtask) => {
								if (subtask.dependencies) {
									subtask.dependencies = subtask.dependencies.filter(
										(depId) =>
											!allRemovedIds.has(`${task.id}.${depId}`) &&
											!allRemovedIds.has(depId)
									);
								}
							});
						}
					});
				}
			}

			// Save the updated raw data structure
			writeJSON(tasksPath, fullTaggedData, projectRoot, tag);

			// Delete task files AFTER saving tasks.json
			for (const taskIdNum of tasksToDeleteFiles) {
				const taskFileName = path.join(
					path.dirname(tasksPath),
					`task_${taskIdNum.toString().padStart(3, '0')}.txt`
				);
				if (fs.existsSync(taskFileName)) {
					try {
						fs.unlinkSync(taskFileName);
						results.messages.push(`Deleted task file: ${taskFileName}`);
					} catch (unlinkError) {
						const unlinkMsg = `Failed to delete task file ${taskFileName}: ${unlinkError.message}`;
						results.errors.push(unlinkMsg);
						results.success = false;
						log('warn', unlinkMsg);
					}
				}
			}

			// Generate updated task files ONCE, with context
			// try {
			// 	await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
			// 		projectRoot,
			// 		tag
			// 	});
			// 	results.messages.push('Task files regenerated successfully.');
			// } catch (genError) {
			// 	const genErrMsg = `Failed to regenerate task files: ${genError.message}`;
			// 	results.errors.push(genErrMsg);
			// 	results.success = false;
			// 	log('warn', genErrMsg);
			// }
		} else if (results.errors.length === 0) {
			results.messages.push('No tasks found matching the provided IDs.');
		}

		// Consolidate messages for final output
		const finalMessage = results.messages.join('\n');
		const finalError = results.errors.join('\n');

		return {
			success: results.success,
			message: finalMessage || 'No tasks were removed.',
			error: finalError || null,
			removedTasks: results.removedTasks
		};
	} catch (error) {
		// Catch errors from reading file or other initial setup
		log('error', `Error removing tasks: ${error.message}`);
		return {
			success: false,
			message: '',
			error: `Operation failed: ${error.message}`,
			removedTasks: []
		};
	}
}

export default removeTask;

```

--------------------------------------------------------------------------------
/apps/cli/src/commands/set-status.command.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview SetStatusCommand using Commander's native class pattern
 * Extends Commander.Command for better integration with the framework
 */

import { Command } from 'commander';
import chalk from 'chalk';
import boxen from 'boxen';
import {
	createTaskMasterCore,
	type TaskMasterCore,
	type TaskStatus
} from '@tm/core';
import type { StorageType } from '@tm/core/types';

/**
 * Valid task status values for validation
 */
const VALID_TASK_STATUSES: TaskStatus[] = [
	'pending',
	'in-progress',
	'done',
	'deferred',
	'cancelled',
	'blocked',
	'review'
];

/**
 * Options interface for the set-status command
 */
export interface SetStatusCommandOptions {
	id?: string;
	status?: TaskStatus;
	format?: 'text' | 'json';
	silent?: boolean;
	project?: string;
}

/**
 * Result type from set-status command
 */
export interface SetStatusResult {
	success: boolean;
	updatedTasks: Array<{
		taskId: string;
		oldStatus: TaskStatus;
		newStatus: TaskStatus;
	}>;
	storageType: Exclude<StorageType, 'auto'>;
}

/**
 * SetStatusCommand extending Commander's Command class
 * This is a thin presentation layer over @tm/core
 */
export class SetStatusCommand extends Command {
	private tmCore?: TaskMasterCore;
	private lastResult?: SetStatusResult;

	constructor(name?: string) {
		super(name || 'set-status');

		// Configure the command
		this.description('Update the status of one or more tasks')
			.requiredOption(
				'-i, --id <id>',
				'Task ID(s) to update (comma-separated for multiple, supports subtasks like 5.2)'
			)
			.requiredOption(
				'-s, --status <status>',
				`New status (${VALID_TASK_STATUSES.join(', ')})`
			)
			.option('-f, --format <format>', 'Output format (text, json)', 'text')
			.option('--silent', 'Suppress output (useful for programmatic usage)')
			.option('-p, --project <path>', 'Project root directory', process.cwd())
			.action(async (options: SetStatusCommandOptions) => {
				await this.executeCommand(options);
			});
	}

	/**
	 * Execute the set-status command
	 */
	private async executeCommand(
		options: SetStatusCommandOptions
	): Promise<void> {
		try {
			// Validate required options
			if (!options.id) {
				console.error(chalk.red('Error: Task ID is required. Use -i or --id'));
				process.exit(1);
			}

			if (!options.status) {
				console.error(
					chalk.red('Error: Status is required. Use -s or --status')
				);
				process.exit(1);
			}

			// Validate status
			if (!VALID_TASK_STATUSES.includes(options.status)) {
				console.error(
					chalk.red(
						`Error: Invalid status "${options.status}". Valid options: ${VALID_TASK_STATUSES.join(', ')}`
					)
				);
				process.exit(1);
			}

			// Initialize TaskMaster core
			this.tmCore = await createTaskMasterCore({
				projectPath: options.project || process.cwd()
			});

			// Parse task IDs (handle comma-separated values)
			const taskIds = options.id.split(',').map((id) => id.trim());

			// Update each task
			const updatedTasks: Array<{
				taskId: string;
				oldStatus: TaskStatus;
				newStatus: TaskStatus;
			}> = [];

			for (const taskId of taskIds) {
				try {
					const result = await this.tmCore.updateTaskStatus(
						taskId,
						options.status
					);
					updatedTasks.push({
						taskId: result.taskId,
						oldStatus: result.oldStatus,
						newStatus: result.newStatus
					});
				} catch (error) {
					const errorMessage =
						error instanceof Error ? error.message : String(error);

					if (!options.silent) {
						console.error(
							chalk.red(`Failed to update task ${taskId}: ${errorMessage}`)
						);
					}
					if (options.format === 'json') {
						console.log(
							JSON.stringify({
								success: false,
								error: errorMessage,
								taskId,
								timestamp: new Date().toISOString()
							})
						);
					}
					process.exit(1);
				}
			}

			// Store result for potential reuse
			this.lastResult = {
				success: true,
				updatedTasks,
				storageType: this.tmCore.getStorageType() as Exclude<
					StorageType,
					'auto'
				>
			};

			// Display results
			this.displayResults(this.lastResult, options);
		} catch (error) {
			const errorMessage =
				error instanceof Error ? error.message : 'Unknown error occurred';

			if (!options.silent) {
				console.error(chalk.red(`Error: ${errorMessage}`));
			}

			if (options.format === 'json') {
				console.log(JSON.stringify({ success: false, error: errorMessage }));
			}

			process.exit(1);
		} finally {
			// Clean up resources
			if (this.tmCore) {
				await this.tmCore.close();
			}
		}
	}

	/**
	 * Display results based on format
	 */
	private displayResults(
		result: SetStatusResult,
		options: SetStatusCommandOptions
	): void {
		const format = options.format || 'text';

		switch (format) {
			case 'json':
				console.log(JSON.stringify(result, null, 2));
				break;

			case 'text':
			default:
				if (!options.silent) {
					this.displayTextResults(result);
				}
				break;
		}
	}

	/**
	 * Display results in text format
	 */
	private displayTextResults(result: SetStatusResult): void {
		if (result.updatedTasks.length === 1) {
			// Single task update
			const update = result.updatedTasks[0];
			console.log(
				boxen(
					chalk.white.bold(`✅ Successfully updated task ${update.taskId}`) +
						'\n\n' +
						`${chalk.blue('From:')} ${this.getStatusDisplay(update.oldStatus)}\n` +
						`${chalk.blue('To:')}   ${this.getStatusDisplay(update.newStatus)}`,
					{
						padding: 1,
						borderColor: 'green',
						borderStyle: 'round',
						margin: { top: 1 }
					}
				)
			);
		} else {
			// Multiple task updates
			console.log(
				boxen(
					chalk.white.bold(
						`✅ Successfully updated ${result.updatedTasks.length} tasks`
					) +
						'\n\n' +
						result.updatedTasks
							.map(
								(update) =>
									`${chalk.cyan(update.taskId)}: ${this.getStatusDisplay(update.oldStatus)} → ${this.getStatusDisplay(update.newStatus)}`
							)
							.join('\n'),
					{
						padding: 1,
						borderColor: 'green',
						borderStyle: 'round',
						margin: { top: 1 }
					}
				)
			);
		}

		// Show storage info
		console.log(chalk.gray(`\nUsing ${result.storageType} storage`));
	}

	/**
	 * Get colored status display
	 */
	private getStatusDisplay(status: TaskStatus): string {
		const statusColors: Record<TaskStatus, (text: string) => string> = {
			pending: chalk.yellow,
			'in-progress': chalk.blue,
			done: chalk.green,
			deferred: chalk.gray,
			cancelled: chalk.red,
			blocked: chalk.red,
			review: chalk.magenta,
			completed: chalk.green
		};

		const colorFn = statusColors[status] || chalk.white;
		return colorFn(status);
	}

	/**
	 * Get the last command result (useful for testing or chaining)
	 */
	getLastResult(): SetStatusResult | undefined {
		return this.lastResult;
	}

	/**
	 * Static method to register this command on an existing program
	 * This is for gradual migration - allows commands.js to use this
	 */
	static registerOn(program: Command): Command {
		const setStatusCommand = new SetStatusCommand();
		program.addCommand(setStatusCommand);
		return setStatusCommand;
	}

	/**
	 * Alternative registration that returns the command for chaining
	 * Can also configure the command name if needed
	 */
	static register(program: Command, name?: string): SetStatusCommand {
		const setStatusCommand = new SetStatusCommand(name);
		program.addCommand(setStatusCommand);
		return setStatusCommand;
	}
}

/**
 * Factory function to create and configure the set-status command
 */
export function createSetStatusCommand(): SetStatusCommand {
	return new SetStatusCommand();
}

```

--------------------------------------------------------------------------------
/packages/tm-core/src/auth/auth-manager.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Authentication manager for Task Master CLI
 */

import {
	AuthCredentials,
	OAuthFlowOptions,
	AuthenticationError,
	AuthConfig,
	UserContext
} from './types.js';
import { CredentialStore } from './credential-store.js';
import { OAuthService } from './oauth-service.js';
import { SupabaseAuthClient } from '../clients/supabase-client.js';
import {
	OrganizationService,
	type Organization,
	type Brief,
	type RemoteTask
} from '../services/organization.service.js';
import { getLogger } from '../logger/index.js';

/**
 * Authentication manager class
 */
export class AuthManager {
	private static instance: AuthManager | null = null;
	private credentialStore: CredentialStore;
	private oauthService: OAuthService;
	private supabaseClient: SupabaseAuthClient;
	private organizationService?: OrganizationService;

	private constructor(config?: Partial<AuthConfig>) {
		this.credentialStore = CredentialStore.getInstance(config);
		this.supabaseClient = new SupabaseAuthClient();
		this.oauthService = new OAuthService(this.credentialStore, config);

		// Initialize Supabase client with session restoration
		this.initializeSupabaseSession();
	}

	/**
	 * Initialize Supabase session from stored credentials
	 */
	private async initializeSupabaseSession(): Promise<void> {
		try {
			await this.supabaseClient.initialize();
		} catch (error) {
			// Log but don't throw - session might not exist yet
			const logger = getLogger('AuthManager');
			logger.debug('No existing session to restore');
		}
	}

	/**
	 * Get singleton instance
	 */
	static getInstance(config?: Partial<AuthConfig>): AuthManager {
		if (!AuthManager.instance) {
			AuthManager.instance = new AuthManager(config);
		} else if (config) {
			// Warn if config is provided after initialization
			const logger = getLogger('AuthManager');
			logger.warn(
				'getInstance called with config after initialization; config is ignored.'
			);
		}
		return AuthManager.instance;
	}

	/**
	 * Reset the singleton instance (useful for testing)
	 */
	static resetInstance(): void {
		AuthManager.instance = null;
		CredentialStore.resetInstance();
	}

	/**
	 * Get stored authentication credentials
	 */
	getCredentials(): AuthCredentials | null {
		return this.credentialStore.getCredentials();
	}

	/**
	 * Start OAuth 2.0 Authorization Code Flow with browser handling
	 */
	async authenticateWithOAuth(
		options: OAuthFlowOptions = {}
	): Promise<AuthCredentials> {
		return this.oauthService.authenticate(options);
	}

	/**
	 * Get the authorization URL (for browser opening)
	 */
	getAuthorizationUrl(): string | null {
		return this.oauthService.getAuthorizationUrl();
	}

	/**
	 * Refresh authentication token using Supabase session
	 */
	async refreshToken(): Promise<AuthCredentials> {
		try {
			// Use Supabase's built-in session refresh
			const session = await this.supabaseClient.refreshSession();

			if (!session) {
				throw new AuthenticationError(
					'Failed to refresh session',
					'REFRESH_FAILED'
				);
			}

			// Get existing credentials to preserve context
			const existingCredentials = this.credentialStore.getCredentials({
				allowExpired: true
			});

			// Update authentication data from session
			const newAuthData: AuthCredentials = {
				token: session.access_token,
				refreshToken: session.refresh_token,
				userId: session.user.id,
				email: session.user.email,
				expiresAt: session.expires_at
					? new Date(session.expires_at * 1000).toISOString()
					: undefined,
				savedAt: new Date().toISOString(),
				selectedContext: existingCredentials?.selectedContext
			};

			this.credentialStore.saveCredentials(newAuthData);
			return newAuthData;
		} catch (error) {
			if (error instanceof AuthenticationError) {
				throw error;
			}
			throw new AuthenticationError(
				`Token refresh failed: ${(error as Error).message}`,
				'REFRESH_FAILED'
			);
		}
	}

	/**
	 * Logout and clear credentials
	 */
	async logout(): Promise<void> {
		try {
			// First try to sign out from Supabase to revoke tokens
			await this.supabaseClient.signOut();
		} catch (error) {
			// Log but don't throw - we still want to clear local credentials
			getLogger('AuthManager').warn('Failed to sign out from Supabase:', error);
		}

		// Always clear local credentials (removes auth.json file)
		this.credentialStore.clearCredentials();
	}

	/**
	 * Check if authenticated
	 */
	isAuthenticated(): boolean {
		return this.credentialStore.hasValidCredentials();
	}

	/**
	 * Get the current user context (org/brief selection)
	 */
	getContext(): UserContext | null {
		const credentials = this.getCredentials();
		return credentials?.selectedContext || null;
	}

	/**
	 * Update the user context (org/brief selection)
	 */
	async updateContext(context: Partial<UserContext>): Promise<void> {
		const credentials = this.getCredentials();
		if (!credentials) {
			throw new AuthenticationError('Not authenticated', 'NOT_AUTHENTICATED');
		}

		// Merge with existing context
		const existingContext = credentials.selectedContext || {};
		const newContext: UserContext = {
			...existingContext,
			...context,
			updatedAt: new Date().toISOString()
		};

		// Save updated credentials with new context
		const updatedCredentials: AuthCredentials = {
			...credentials,
			selectedContext: newContext
		};

		this.credentialStore.saveCredentials(updatedCredentials);
	}

	/**
	 * Clear the user context
	 */
	async clearContext(): Promise<void> {
		const credentials = this.getCredentials();
		if (!credentials) {
			throw new AuthenticationError('Not authenticated', 'NOT_AUTHENTICATED');
		}

		// Remove context from credentials
		const { selectedContext, ...credentialsWithoutContext } = credentials;
		this.credentialStore.saveCredentials(credentialsWithoutContext);
	}

	/**
	 * Get the organization service instance
	 * Uses the Supabase client with the current session or token
	 */
	private async getOrganizationService(): Promise<OrganizationService> {
		if (!this.organizationService) {
			// First check if we have credentials with a token
			const credentials = this.getCredentials();
			if (!credentials || !credentials.token) {
				throw new AuthenticationError('Not authenticated', 'NOT_AUTHENTICATED');
			}

			// Initialize session if needed (this will load from our storage adapter)
			await this.supabaseClient.initialize();

			// Use the SupabaseAuthClient which now has the session
			const supabaseClient = this.supabaseClient.getClient();
			this.organizationService = new OrganizationService(supabaseClient as any);
		}
		return this.organizationService;
	}

	/**
	 * Get all organizations for the authenticated user
	 */
	async getOrganizations(): Promise<Organization[]> {
		const service = await this.getOrganizationService();
		return service.getOrganizations();
	}

	/**
	 * Get all briefs for a specific organization
	 */
	async getBriefs(orgId: string): Promise<Brief[]> {
		const service = await this.getOrganizationService();
		return service.getBriefs(orgId);
	}

	/**
	 * Get a specific organization by ID
	 */
	async getOrganization(orgId: string): Promise<Organization | null> {
		const service = await this.getOrganizationService();
		return service.getOrganization(orgId);
	}

	/**
	 * Get a specific brief by ID
	 */
	async getBrief(briefId: string): Promise<Brief | null> {
		const service = await this.getOrganizationService();
		return service.getBrief(briefId);
	}

	/**
	 * Get all tasks for a specific brief
	 */
	async getTasks(briefId: string): Promise<RemoteTask[]> {
		const service = await this.getOrganizationService();
		return service.getTasks(briefId);
	}
}

```

--------------------------------------------------------------------------------
/apps/docs/getting-started/contribute.mdx:
--------------------------------------------------------------------------------

```markdown
# Contributing to Task Master

Thank you for your interest in contributing to Task Master! We're excited to work with you and appreciate your help in making this project better. 🚀

## 🤝 Our Collaborative Approach

We're a **PR-friendly team** that values collaboration:

- ✅ **We review PRs quickly** - Usually within hours, not days
- ✅ **We're super reactive** - Expect fast feedback and engagement
- ✅ **We sometimes take over PRs** - If your contribution is valuable but needs cleanup, we might jump in to help finish it
- ✅ **We're open to all contributions** - From bug fixes to major features

**We don't mind AI-generated code**, but we do expect you to:

- ✅ **Review and understand** what the AI generated
- ✅ **Test the code thoroughly** before submitting
- ✅ **Ensure it's well-written** and follows our patterns
- ❌ **Don't submit "AI slop"** - untested, unreviewed AI output

> **Why this matters**: We spend significant time reviewing PRs. Help us help you by submitting quality contributions that save everyone time!

## 🚀 Quick Start for Contributors

### 1. Fork and Clone

```bash
git clone https://github.com/YOUR_USERNAME/claude-task-master.git
cd claude-task-master
npm install
```

### 2. Create a Feature Branch

**Important**: Always target the `next` branch, not `main`:

```bash
git checkout next
git pull origin next
git checkout -b feature/your-feature-name
```

### 3. Make Your Changes

Follow our development guidelines below.

### 4. Test Everything Yourself

**Before submitting your PR**, ensure:

```bash
# Run all tests
npm test

# Check formatting
npm run format-check

# Fix formatting if needed
npm run format
```

### 5. Create a Changeset

**Required for most changes**:

```bash
npm run changeset
```

See the [Changeset Guidelines](#changeset-guidelines) below for details.

### 6. Submit Your PR

- Target the `next` branch
- Write a clear description
- Reference any related issues

## 📋 Development Guidelines

### Branch Strategy

- **`main`**: Production-ready code
- **`next`**: Development branch - **target this for PRs**
- **Feature branches**: `feature/description` or `fix/description`

### Code Quality Standards

1. **Write tests** for new functionality
2. **Follow existing patterns** in the codebase
3. **Add JSDoc comments** for functions
4. **Keep functions focused** and single-purpose

### Testing Requirements

Your PR **must pass all CI checks**:

- ✅ **Unit tests**: `npm test`
- ✅ **Format check**: `npm run format-check`

**Test your changes locally first** - this saves review time and shows you care about quality.

## 📦 Changeset Guidelines

We use [Changesets](https://github.com/changesets/changesets) to manage versioning and generate changelogs.

### When to Create a Changeset

**Always create a changeset for**:

- ✅ New features
- ✅ Bug fixes
- ✅ Breaking changes
- ✅ Performance improvements
- ✅ User-facing documentation updates
- ✅ Dependency updates that affect functionality

**Skip changesets for**:

- ❌ Internal documentation only
- ❌ Test-only changes
- ❌ Code formatting/linting
- ❌ Development tooling that doesn't affect users

### How to Create a Changeset

1. **After making your changes**:

   ```bash
   npm run changeset
   ```

2. **Choose the bump type**:

   - **Major**: Breaking changes
   - **Minor**: New features
   - **Patch**: Bug fixes, docs, performance improvements

3. **Write a clear summary**:

   ```
   Add support for custom AI models in MCP configuration
   ```

4. **Commit the changeset file** with your changes:
   ```bash
   git add .changeset/*.md
   git commit -m "feat: add custom AI model support"
   ```

### Changeset vs Git Commit Messages

- **Changeset summary**: User-facing, goes in CHANGELOG.md
- **Git commit**: Developer-facing, explains the technical change

Example:

```bash
# Changeset summary (user-facing)
"Add support for custom Ollama models"

# Git commit message (developer-facing)
"feat(models): implement custom Ollama model validation

- Add model validation for custom Ollama endpoints
- Update configuration schema to support custom models
- Add tests for new validation logic"
```

## 🔧 Development Setup

### Prerequisites

- Node.js 18+
- npm or yarn

### Environment Setup

1. **Copy environment template**:

   ```bash
   cp .env.example .env
   ```

2. **Add your API keys** (for testing AI features):
   ```bash
   ANTHROPIC_API_KEY=your_key_here
   OPENAI_API_KEY=your_key_here
   # Add others as needed
   ```

### Running Tests

```bash
# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run with coverage
npm run test:coverage

# Run E2E tests
npm run test:e2e
```

### Code Formatting

We use Prettier for consistent formatting:

```bash
# Check formatting
npm run format-check

# Fix formatting
npm run format
```

## 📝 PR Guidelines

### Before Submitting

- [ ] **Target the `next` branch**
- [ ] **Test everything locally**
- [ ] **Run the full test suite**
- [ ] **Check code formatting**
- [ ] **Create a changeset** (if needed)
- [ ] **Re-read your changes** - ensure they're clean and well-thought-out

### PR Description Template

```markdown
## Description

Brief description of what this PR does.

## Type of Change

- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Testing

- [ ] I have tested this locally
- [ ] All existing tests pass
- [ ] I have added tests for new functionality

## Changeset

- [ ] I have created a changeset (or this change doesn't need one)

## Additional Notes

Any additional context or notes for reviewers.
```

### What We Look For

✅ **Good PRs**:

- Clear, focused changes
- Comprehensive testing
- Good commit messages
- Proper changeset (when needed)
- Self-reviewed code

❌ **Avoid**:

- Massive PRs that change everything
- Untested code
- Formatting issues
- Missing changesets for user-facing changes
- AI-generated code that wasn't reviewed

## 🏗️ Project Structure

```
claude-task-master/
├── bin/                    # CLI executables
├── mcp-server/            # MCP server implementation
├── scripts/               # Core task management logic
├── src/                   # Shared utilities and providers and well refactored code (we are slowly moving everything here)
├── tests/                 # Test files
├── docs/                  # Documentation
└── .cursor/               # Cursor IDE rules and configuration
└── assets/							   # Assets like rules and configuration for all IDEs
```

### Key Areas for Contribution

- **CLI Commands**: `scripts/modules/commands.js`
- **MCP Tools**: `mcp-server/src/tools/`
- **Core Logic**: `scripts/modules/task-manager/`
- **AI Providers**: `src/ai-providers/`
- **Tests**: `tests/`

## 🐛 Reporting Issues

### Bug Reports

Include:

- Task Master version
- Node.js version
- Operating system
- Steps to reproduce
- Expected vs actual behavior
- Error messages/logs

### Feature Requests

Include:

- Clear description of the feature
- Use case/motivation
- Proposed implementation (if you have ideas)
- Willingness to contribute

## 💬 Getting Help

- **Discord**: [Join our community](https://discord.gg/taskmasterai)
- **Issues**: [GitHub Issues](https://github.com/eyaltoledano/claude-task-master/issues)
- **Discussions**: [GitHub Discussions](https://github.com/eyaltoledano/claude-task-master/discussions)

## 📄 License

By contributing, you agree that your contributions will be licensed under the same license as the project (MIT with Commons Clause).

---

**Thank you for contributing to Task Master!** 🎉

Your contributions help make AI-driven development more accessible and efficient for everyone.
```

--------------------------------------------------------------------------------
/packages/tm-core/src/storage/storage-factory.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Storage factory for creating appropriate storage implementations
 */

import type { IStorage } from '../interfaces/storage.interface.js';
import type {
	IConfiguration,
	RuntimeStorageConfig,
	StorageSettings
} from '../interfaces/configuration.interface.js';
import { FileStorage } from './file-storage/index.js';
import { ApiStorage } from './api-storage.js';
import { ERROR_CODES, TaskMasterError } from '../errors/task-master-error.js';
import { AuthManager } from '../auth/auth-manager.js';
import { getLogger } from '../logger/index.js';
import { SupabaseAuthClient } from '../clients/supabase-client.js';

/**
 * Factory for creating storage implementations based on configuration
 */
export class StorageFactory {
	/**
	 * Create a storage implementation from runtime storage config
	 * This is the preferred method when you have a RuntimeStorageConfig
	 * @param storageConfig - Runtime storage configuration
	 * @param projectPath - Project root path (for file storage)
	 * @returns Storage implementation
	 */
	static createFromStorageConfig(
		storageConfig: RuntimeStorageConfig,
		projectPath: string
	): IStorage {
		// Wrap the storage config in the expected format, including projectPath
		// This ensures ApiStorage receives the projectPath for projectId
		return StorageFactory.create(
			{ storage: storageConfig, projectPath } as Partial<IConfiguration>,
			projectPath
		);
	}

	/**
	 * Create a storage implementation based on configuration
	 * @param config - Configuration object
	 * @param projectPath - Project root path (for file storage)
	 * @returns Storage implementation
	 */
	static create(
		config: Partial<IConfiguration>,
		projectPath: string
	): IStorage {
		const storageType = config.storage?.type || 'auto';

		const logger = getLogger('StorageFactory');

		switch (storageType) {
			case 'file':
				logger.debug('📁 Using local file storage');
				return StorageFactory.createFileStorage(projectPath, config);

			case 'api':
				if (!StorageFactory.isHamsterAvailable(config)) {
					const missing: string[] = [];
					if (!config.storage?.apiEndpoint) missing.push('apiEndpoint');
					if (!config.storage?.apiAccessToken) missing.push('apiAccessToken');

					// Check if authenticated via AuthManager
					const authManager = AuthManager.getInstance();
					if (!authManager.isAuthenticated()) {
						throw new TaskMasterError(
							`API storage not fully configured (${missing.join(', ') || 'credentials missing'}). Run: tm auth login, or set the missing field(s).`,
							ERROR_CODES.MISSING_CONFIGURATION,
							{ storageType: 'api', missing }
						);
					}
					// Use auth token from AuthManager
					const credentials = authManager.getCredentials();
					if (credentials) {
						// Merge with existing storage config, ensuring required fields
						const nextStorage: StorageSettings = {
							...(config.storage as StorageSettings),
							type: 'api',
							apiAccessToken: credentials.token,
							apiEndpoint:
								config.storage?.apiEndpoint ||
								process.env.HAMSTER_API_URL ||
								'https://tryhamster.com/api'
						};
						config.storage = nextStorage;
					}
				}
				logger.info('☁️  Using API storage');
				return StorageFactory.createApiStorage(config);

			case 'auto':
				// Auto-detect based on authentication status
				const authManager = AuthManager.getInstance();

				// First check if API credentials are explicitly configured
				if (StorageFactory.isHamsterAvailable(config)) {
					logger.info('☁️  Using API storage (configured)');
					return StorageFactory.createApiStorage(config);
				}

				// Then check if authenticated via AuthManager
				if (authManager.isAuthenticated()) {
					const credentials = authManager.getCredentials();
					if (credentials) {
						// Configure API storage with auth credentials
						const nextStorage: StorageSettings = {
							...(config.storage as StorageSettings),
							type: 'api',
							apiAccessToken: credentials.token,
							apiEndpoint:
								config.storage?.apiEndpoint ||
								process.env.HAMSTER_API_URL ||
								'https://tryhamster.com/api'
						};
						config.storage = nextStorage;
						logger.info('☁️  Using API storage (authenticated)');
						return StorageFactory.createApiStorage(config);
					}
				}

				// Default to file storage
				logger.debug('📁 Using local file storage');
				return StorageFactory.createFileStorage(projectPath, config);

			default:
				throw new TaskMasterError(
					`Unknown storage type: ${storageType}`,
					ERROR_CODES.INVALID_INPUT,
					{ storageType }
				);
		}
	}

	/**
	 * Create file storage implementation
	 */
	private static createFileStorage(
		projectPath: string,
		config: Partial<IConfiguration>
	): FileStorage {
		const basePath = config.storage?.basePath || projectPath;
		return new FileStorage(basePath);
	}

	/**
	 * Create API storage implementation
	 */
	private static createApiStorage(config: Partial<IConfiguration>): ApiStorage {
		// Use our SupabaseAuthClient instead of creating a raw Supabase client
		const supabaseAuthClient = new SupabaseAuthClient();
		const supabaseClient = supabaseAuthClient.getClient();

		return new ApiStorage({
			supabaseClient,
			projectId: config.projectPath || '',
			enableRetry: config.retry?.retryOnNetworkError,
			maxRetries: config.retry?.retryAttempts
		});
	}

	/**
	 * Detect optimal storage type based on available configuration
	 */
	static detectOptimalStorage(config: Partial<IConfiguration>): 'file' | 'api' {
		// If API credentials are provided, prefer API storage (Hamster)
		if (config.storage?.apiEndpoint && config.storage?.apiAccessToken) {
			return 'api';
		}

		// Default to file storage
		return 'file';
	}

	/**
	 * Validate storage configuration
	 */
	static validateStorageConfig(config: Partial<IConfiguration>): {
		isValid: boolean;
		errors: string[];
	} {
		const errors: string[] = [];
		const storageType = config.storage?.type;

		if (!storageType) {
			errors.push('Storage type is not specified');
			return { isValid: false, errors };
		}

		switch (storageType) {
			case 'api':
				if (!config.storage?.apiEndpoint) {
					errors.push('API endpoint is required for API storage');
				}
				if (!config.storage?.apiAccessToken) {
					errors.push('API access token is required for API storage');
				}
				break;

			case 'file':
				// File storage doesn't require additional config
				break;

			case 'auto':
				// Auto storage is valid - it will determine the actual type at runtime
				// No specific validation needed as it will fall back to file if API not configured
				break;

			default:
				errors.push(`Unknown storage type: ${storageType}`);
		}

		return {
			isValid: errors.length === 0,
			errors
		};
	}

	/**
	 * Check if Hamster (API storage) is available
	 */
	static isHamsterAvailable(config: Partial<IConfiguration>): boolean {
		return !!(config.storage?.apiEndpoint && config.storage?.apiAccessToken);
	}

	/**
	 * Create a storage implementation with fallback
	 * Tries API storage first, falls back to file storage
	 */
	static async createWithFallback(
		config: Partial<IConfiguration>,
		projectPath: string
	): Promise<IStorage> {
		// Try API storage if configured
		if (StorageFactory.isHamsterAvailable(config)) {
			try {
				const apiStorage = StorageFactory.createApiStorage(config);
				await apiStorage.initialize();
				return apiStorage;
			} catch (error) {
				const logger = getLogger('StorageFactory');
				logger.warn(
					'Failed to initialize API storage, falling back to file storage:',
					error
				);
			}
		}

		// Fallback to file storage
		return StorageFactory.createFileStorage(projectPath, config);
	}
}

```

--------------------------------------------------------------------------------
/mcp-server/src/core/utils/path-utils.js:
--------------------------------------------------------------------------------

```javascript
import path from 'path';
import {
	findTasksPath as coreFindTasksPath,
	findPRDPath as coreFindPrdPath,
	findComplexityReportPath as coreFindComplexityReportPath,
	findProjectRoot as coreFindProjectRoot,
	normalizeProjectRoot
} from '../../../../src/utils/path-utils.js';
import { PROJECT_MARKERS } from '../../../../src/constants/paths.js';

/**
 * MCP-specific path utilities that extend core path utilities with session support
 * This module handles session-specific path resolution for the MCP server
 */

/**
 * Silent logger for MCP context to prevent console output
 */
const silentLogger = {
	info: () => {},
	warn: () => {},
	error: () => {},
	debug: () => {},
	success: () => {}
};

/**
 * Cache for last found project root to improve performance
 */
export const lastFoundProjectRoot = null;

/**
 * Find PRD file with MCP support
 * @param {string} [explicitPath] - Explicit path to PRD file (highest priority)
 * @param {Object} [args] - Arguments object for context
 * @param {Object} [log] - Logger object to prevent console logging
 * @returns {string|null} - Resolved path to PRD file or null if not found
 */
export function findPrdPath(explicitPath, args = null, log = silentLogger) {
	return coreFindPrdPath(explicitPath, args, log);
}

/**
 * Resolve tasks.json path from arguments
 * Prioritizes explicit path parameter, then uses fallback logic
 * @param {Object} args - Arguments object containing projectRoot and optional file path
 * @param {Object} [log] - Logger object to prevent console logging
 * @returns {string|null} - Resolved path to tasks.json or null if not found
 */
export function resolveTasksPath(args, log = silentLogger) {
	// Get explicit path from args.file if provided
	const explicitPath = args?.file;
	const rawProjectRoot = args?.projectRoot;

	// If explicit path is provided and absolute, use it directly
	if (explicitPath && path.isAbsolute(explicitPath)) {
		return explicitPath;
	}

	// Normalize project root if provided
	const projectRoot = rawProjectRoot
		? normalizeProjectRoot(rawProjectRoot)
		: null;

	// If explicit path is relative, resolve it relative to normalized projectRoot
	if (explicitPath && projectRoot) {
		return path.resolve(projectRoot, explicitPath);
	}

	// Use core findTasksPath with explicit path and normalized projectRoot context
	if (projectRoot) {
		return coreFindTasksPath(explicitPath, { projectRoot }, log);
	}

	// Fallback to core function without projectRoot context
	return coreFindTasksPath(explicitPath, null, log);
}

/**
 * Resolve PRD path from arguments
 * @param {Object} args - Arguments object containing projectRoot and optional input path
 * @param {Object} [log] - Logger object to prevent console logging
 * @returns {string|null} - Resolved path to PRD file or null if not found
 */
export function resolvePrdPath(args, log = silentLogger) {
	// Get explicit path from args.input if provided
	const explicitPath = args?.input;
	const rawProjectRoot = args?.projectRoot;

	// If explicit path is provided and absolute, use it directly
	if (explicitPath && path.isAbsolute(explicitPath)) {
		return explicitPath;
	}

	// Normalize project root if provided
	const projectRoot = rawProjectRoot
		? normalizeProjectRoot(rawProjectRoot)
		: null;

	// If explicit path is relative, resolve it relative to normalized projectRoot
	if (explicitPath && projectRoot) {
		return path.resolve(projectRoot, explicitPath);
	}

	// Use core findPRDPath with explicit path and normalized projectRoot context
	if (projectRoot) {
		return coreFindPrdPath(explicitPath, { projectRoot }, log);
	}

	// Fallback to core function without projectRoot context
	return coreFindPrdPath(explicitPath, null, log);
}

/**
 * Resolve complexity report path from arguments
 * @param {Object} args - Arguments object containing projectRoot and optional complexityReport path
 * @param {Object} [log] - Logger object to prevent console logging
 * @returns {string|null} - Resolved path to complexity report or null if not found
 */
export function resolveComplexityReportPath(args, log = silentLogger) {
	// Get explicit path from args.complexityReport if provided
	const explicitPath = args?.complexityReport;
	const rawProjectRoot = args?.projectRoot;
	const tag = args?.tag;

	// If explicit path is provided and absolute, use it directly
	if (explicitPath && path.isAbsolute(explicitPath)) {
		return explicitPath;
	}

	// Normalize project root if provided
	const projectRoot = rawProjectRoot
		? normalizeProjectRoot(rawProjectRoot)
		: null;

	// If explicit path is relative, resolve it relative to normalized projectRoot
	if (explicitPath && projectRoot) {
		return path.resolve(projectRoot, explicitPath);
	}

	// Use core findComplexityReportPath with explicit path and normalized projectRoot context
	if (projectRoot) {
		return coreFindComplexityReportPath(
			explicitPath,
			{ projectRoot, tag },
			log
		);
	}

	// Fallback to core function without projectRoot context
	return coreFindComplexityReportPath(explicitPath, null, log);
}

/**
 * Resolve any project-relative path from arguments
 * @param {string} relativePath - Relative path to resolve
 * @param {Object} args - Arguments object containing projectRoot
 * @returns {string} - Resolved absolute path
 */
export function resolveProjectPath(relativePath, args) {
	// Ensure we have a projectRoot from args
	if (!args?.projectRoot) {
		throw new Error('projectRoot is required in args to resolve project paths');
	}

	// Normalize the project root to prevent double .taskmaster paths
	const projectRoot = normalizeProjectRoot(args.projectRoot);

	// If already absolute, return as-is
	if (path.isAbsolute(relativePath)) {
		return relativePath;
	}

	// Resolve relative to normalized projectRoot
	return path.resolve(projectRoot, relativePath);
}

/**
 * Find project root using core utility
 * @param {string} [startDir] - Directory to start searching from
 * @returns {string|null} - Project root path or null if not found
 */
export function findProjectRoot(startDir) {
	return coreFindProjectRoot(startDir);
}

// MAIN EXPORTS FOR MCP TOOLS - these are the functions MCP tools should use

/**
 * Find tasks.json path from arguments - primary MCP function
 * @param {Object} args - Arguments object containing projectRoot and optional file path
 * @param {Object} [log] - Log function to prevent console logging
 * @returns {string|null} - Resolved path to tasks.json or null if not found
 */
export function findTasksPath(args, log = silentLogger) {
	return resolveTasksPath(args, log);
}

/**
 * Find complexity report path from arguments - primary MCP function
 * @param {Object} args - Arguments object containing projectRoot and optional complexityReport path
 * @param {Object} [log] - Log function to prevent console logging
 * @returns {string|null} - Resolved path to complexity report or null if not found
 */
export function findComplexityReportPath(args, log = silentLogger) {
	return resolveComplexityReportPath(args, log);
}

/**
 * Find PRD path - primary MCP function
 * @param {string} [explicitPath] - Explicit path to PRD file
 * @param {Object} [args] - Arguments object for context (not used in current implementation)
 * @param {Object} [log] - Logger object to prevent console logging
 * @returns {string|null} - Resolved path to PRD file or null if not found
 */
export function findPRDPath(explicitPath, args = null, log = silentLogger) {
	return findPrdPath(explicitPath, args, log);
}

// Legacy aliases for backward compatibility - DEPRECATED
export const findTasksJsonPath = findTasksPath;
export const findComplexityReportJsonPath = findComplexityReportPath;

// Re-export PROJECT_MARKERS for MCP tools that import it from this module
export { PROJECT_MARKERS };

```

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

```javascript
import chalk from 'chalk';
import {
	StreamingError,
	STREAMING_ERROR_CODES
} from '../../../../src/utils/stream-parser.js';
import { TimeoutManager } from '../../../../src/utils/timeout-manager.js';
import { getDebugFlag, getDefaultPriority } from '../../config-manager.js';

// Import configuration classes
import { PrdParseConfig, LoggingConfig } from './parse-prd-config.js';

// Import helper functions
import {
	readPrdContent,
	loadExistingTasks,
	validateFileOperations,
	processTasks,
	saveTasksToFile,
	buildPrompts,
	displayCliSummary,
	displayNonStreamingCliOutput
} from './parse-prd-helpers.js';

// Import handlers
import { handleStreamingService } from './parse-prd-streaming.js';
import { handleNonStreamingService } from './parse-prd-non-streaming.js';

// ============================================================================
// MAIN PARSING FUNCTIONS (Simplified after refactoring)
// ============================================================================

/**
 * Shared parsing logic for both streaming and non-streaming
 * @param {PrdParseConfig} config - Configuration object
 * @param {Function} serviceHandler - Handler function for AI service
 * @param {boolean} isStreaming - Whether this is streaming mode
 * @returns {Promise<Object>} Result object with success status and telemetry
 */
async function parsePRDCore(config, serviceHandler, isStreaming) {
	const logger = new LoggingConfig(config.mcpLog, config.reportProgress);

	logger.report(
		`Parsing PRD file: ${config.prdPath}, Force: ${config.force}, Append: ${config.append}, Research: ${config.research}`,
		'debug'
	);

	try {
		// Load existing tasks
		const { existingTasks, nextId } = loadExistingTasks(
			config.tasksPath,
			config.targetTag
		);

		// Validate operations
		validateFileOperations({
			existingTasks,
			targetTag: config.targetTag,
			append: config.append,
			force: config.force,
			isMCP: config.isMCP,
			logger
		});

		// Read PRD content and build prompts
		const prdContent = readPrdContent(config.prdPath);
		const prompts = await buildPrompts(config, prdContent, nextId);

		// Call the appropriate service handler
		const serviceResult = await serviceHandler(
			config,
			prompts,
			config.numTasks
		);

		// Process tasks
		const defaultPriority = getDefaultPriority(config.projectRoot) || 'medium';
		const processedNewTasks = processTasks(
			serviceResult.parsedTasks,
			nextId,
			existingTasks,
			defaultPriority
		);

		// Combine with existing if appending
		const finalTasks = config.append
			? [...existingTasks, ...processedNewTasks]
			: processedNewTasks;

		// Save to file
		saveTasksToFile(config.tasksPath, finalTasks, config.targetTag, logger);

		// Handle completion reporting
		await handleCompletionReporting(
			config,
			serviceResult,
			processedNewTasks,
			finalTasks,
			nextId,
			isStreaming
		);

		return {
			success: true,
			tasksPath: config.tasksPath,
			telemetryData: serviceResult.aiServiceResponse?.telemetryData,
			tagInfo: serviceResult.aiServiceResponse?.tagInfo
		};
	} catch (error) {
		logger.report(`Error parsing PRD: ${error.message}`, 'error');

		if (!config.isMCP) {
			console.error(chalk.red(`Error: ${error.message}`));
			if (getDebugFlag(config.projectRoot)) {
				console.error(error);
			}
		}
		throw error;
	}
}

/**
 * Handle completion reporting for both CLI and MCP
 * @param {PrdParseConfig} config - Configuration object
 * @param {Object} serviceResult - Result from service handler
 * @param {Array} processedNewTasks - New tasks that were processed
 * @param {Array} finalTasks - All tasks after processing
 * @param {number} nextId - Next available task ID
 * @param {boolean} isStreaming - Whether this was streaming mode
 */
async function handleCompletionReporting(
	config,
	serviceResult,
	processedNewTasks,
	finalTasks,
	nextId,
	isStreaming
) {
	const { aiServiceResponse, estimatedInputTokens, estimatedOutputTokens } =
		serviceResult;

	// MCP progress reporting
	if (config.reportProgress) {
		const hasValidTelemetry =
			aiServiceResponse?.telemetryData &&
			(aiServiceResponse.telemetryData.inputTokens > 0 ||
				aiServiceResponse.telemetryData.outputTokens > 0);

		let completionMessage;
		if (hasValidTelemetry) {
			const cost = aiServiceResponse.telemetryData.totalCost || 0;
			const currency = aiServiceResponse.telemetryData.currency || 'USD';
			completionMessage = `✅ Task Generation Completed | Tokens (I/O): ${aiServiceResponse.telemetryData.inputTokens}/${aiServiceResponse.telemetryData.outputTokens} | Cost: ${currency === 'USD' ? '$' : currency}${cost.toFixed(4)}`;
		} else {
			const outputTokens = isStreaming ? estimatedOutputTokens : 'unknown';
			completionMessage = `✅ Task Generation Completed | ~Tokens (I/O): ${estimatedInputTokens}/${outputTokens} | Cost: ~$0.00`;
		}

		await config.reportProgress({
			progress: config.numTasks,
			total: config.numTasks,
			message: completionMessage
		});
	}

	// CLI output
	if (config.outputFormat === 'text' && !config.isMCP) {
		if (isStreaming && serviceResult.summary) {
			await displayCliSummary({
				processedTasks: processedNewTasks,
				nextId,
				summary: serviceResult.summary,
				prdPath: config.prdPath,
				tasksPath: config.tasksPath,
				usedFallback: serviceResult.usedFallback,
				aiServiceResponse
			});
		} else if (!isStreaming) {
			displayNonStreamingCliOutput({
				processedTasks: processedNewTasks,
				research: config.research,
				finalTasks,
				tasksPath: config.tasksPath,
				aiServiceResponse
			});
		}
	}
}

/**
 * Parse PRD with streaming progress reporting
 */
async function parsePRDWithStreaming(
	prdPath,
	tasksPath,
	numTasks,
	options = {}
) {
	const config = new PrdParseConfig(prdPath, tasksPath, numTasks, options);
	return parsePRDCore(config, handleStreamingService, true);
}

/**
 * Parse PRD without streaming (fallback)
 */
async function parsePRDWithoutStreaming(
	prdPath,
	tasksPath,
	numTasks,
	options = {}
) {
	const config = new PrdParseConfig(prdPath, tasksPath, numTasks, options);
	return parsePRDCore(config, handleNonStreamingService, false);
}

/**
 * Main entry point - decides between streaming and non-streaming
 */
async function parsePRD(prdPath, tasksPath, numTasks, options = {}) {
	const config = new PrdParseConfig(prdPath, tasksPath, numTasks, options);

	if (config.useStreaming) {
		try {
			return await parsePRDWithStreaming(prdPath, tasksPath, numTasks, options);
		} catch (streamingError) {
			// Check if this is a streaming-specific error (including timeout)
			const isStreamingError =
				streamingError instanceof StreamingError ||
				streamingError.code === STREAMING_ERROR_CODES.NOT_ASYNC_ITERABLE ||
				streamingError.code ===
					STREAMING_ERROR_CODES.STREAM_PROCESSING_FAILED ||
				streamingError.code === STREAMING_ERROR_CODES.STREAM_NOT_ITERABLE ||
				TimeoutManager.isTimeoutError(streamingError);

			if (isStreamingError) {
				const logger = new LoggingConfig(config.mcpLog, config.reportProgress);

				// Show fallback message
				if (config.outputFormat === 'text' && !config.isMCP) {
					console.log(
						chalk.yellow(
							`⚠️  Streaming operation ${streamingError.message.includes('timed out') ? 'timed out' : 'failed'}. Falling back to non-streaming mode...`
						)
					);
				} else {
					logger.report(
						`Streaming failed (${streamingError.message}), falling back to non-streaming mode...`,
						'warn'
					);
				}

				// Fallback to non-streaming
				return await parsePRDWithoutStreaming(
					prdPath,
					tasksPath,
					numTasks,
					options
				);
			} else {
				throw streamingError;
			}
		}
	} else {
		return await parsePRDWithoutStreaming(
			prdPath,
			tasksPath,
			numTasks,
			options
		);
	}
}

export default parsePRD;

```

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

```javascript
/**
 * Tests for the clear-subtasks.js module
 */
import { jest } from '@jest/globals';

// Mock the dependencies before importing the module under test
jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
	readJSON: jest.fn(),
	writeJSON: jest.fn(),
	log: jest.fn(),
	CONFIG: {
		model: 'mock-claude-model',
		maxTokens: 4000,
		temperature: 0.7,
		debug: false
	},
	findTaskById: jest.fn(),
	isSilentMode: jest.fn(() => false),
	truncate: jest.fn((text) => text),
	ensureTagMetadata: jest.fn()
}));

jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({
	displayBanner: jest.fn()
}));

jest.unstable_mockModule(
	'../../../../../scripts/modules/task-manager/generate-task-files.js',
	() => ({
		default: jest.fn().mockResolvedValue()
	})
);

// Mock external UI libraries
jest.unstable_mockModule('chalk', () => ({
	default: {
		white: {
			bold: jest.fn((text) => text)
		},
		cyan: Object.assign(
			jest.fn((text) => text),
			{
				bold: jest.fn((text) => text)
			}
		),
		green: jest.fn((text) => text),
		yellow: jest.fn((text) => text),
		bold: jest.fn((text) => text)
	}
}));

jest.unstable_mockModule('boxen', () => ({
	default: jest.fn((text) => text)
}));

jest.unstable_mockModule('cli-table3', () => ({
	default: jest.fn().mockImplementation(() => ({
		push: jest.fn(),
		toString: jest.fn(() => 'mocked table')
	}))
}));

// Mock process.exit to prevent Jest worker crashes
const mockExit = jest.spyOn(process, 'exit').mockImplementation((code) => {
	throw new Error(`process.exit called with "${code}"`);
});

// Import the mocked modules
const { readJSON, writeJSON, log, findTaskById, ensureTagMetadata } =
	await import('../../../../../scripts/modules/utils.js');
const generateTaskFiles = (
	await import(
		'../../../../../scripts/modules/task-manager/generate-task-files.js'
	)
).default;

// Import the module under test
const { default: clearSubtasks } = await import(
	'../../../../../scripts/modules/task-manager/clear-subtasks.js'
);

describe('clearSubtasks', () => {
	const sampleTasks = {
		master: {
			tasks: [
				{ id: 1, title: 'Task 1', subtasks: [] },
				{ id: 2, title: 'Task 2', subtasks: [] },
				{
					id: 3,
					title: 'Task 3',
					subtasks: [{ id: 1, title: 'Subtask 3.1' }]
				},
				{
					id: 4,
					title: 'Task 4',
					subtasks: [{ id: 1, title: 'Subtask 4.1' }]
				}
			]
		}
	};

	beforeEach(() => {
		jest.clearAllMocks();
		mockExit.mockClear();
		readJSON.mockImplementation((tasksPath, projectRoot, tag) => {
			// Ensure tag contract is honoured
			expect(tag).toBeDefined();
			expect(tag).toBe('master');
			// Create a deep copy to avoid mutation issues between tests
			const sampleTasksCopy = JSON.parse(JSON.stringify(sampleTasks));
			// Return the data for the 'master' tag, which is what the tests use
			return {
				...sampleTasksCopy.master,
				tag: tag || 'master',
				_rawTaggedData: sampleTasksCopy
			};
		});
		writeJSON.mockResolvedValue();
		generateTaskFiles.mockResolvedValue();
		log.mockImplementation(() => {});
	});

	test('should clear subtasks from a specific task', () => {
		// Arrange
		const taskId = '3';
		const tasksPath = 'tasks/tasks.json';
		const context = { tag: 'master' };

		// Act
		clearSubtasks(tasksPath, taskId, context);

		// Assert
		expect(readJSON).toHaveBeenCalledWith(tasksPath, undefined, 'master');
		expect(writeJSON).toHaveBeenCalledWith(
			tasksPath,
			expect.objectContaining({
				_rawTaggedData: expect.objectContaining({
					master: expect.objectContaining({
						tasks: expect.arrayContaining([
							expect.objectContaining({
								id: 3,
								subtasks: [] // Should be empty
							})
						])
					})
				})
			}),
			undefined,
			'master'
		);
	});

	test('should clear subtasks from multiple tasks when given comma-separated IDs', () => {
		// Arrange
		const taskIds = '3,4';
		const tasksPath = 'tasks/tasks.json';
		const context = { tag: 'master' };

		// Act
		clearSubtasks(tasksPath, taskIds, context);

		// Assert
		expect(readJSON).toHaveBeenCalledWith(tasksPath, undefined, 'master');
		expect(writeJSON).toHaveBeenCalledWith(
			tasksPath,
			expect.objectContaining({
				_rawTaggedData: expect.objectContaining({
					master: expect.objectContaining({
						tasks: expect.arrayContaining([
							expect.objectContaining({ id: 3, subtasks: [] }),
							expect.objectContaining({ id: 4, subtasks: [] })
						])
					})
				})
			}),
			undefined,
			'master'
		);
	});

	test('should handle tasks with no subtasks', () => {
		// Arrange
		const taskId = '1'; // Task 1 already has no subtasks
		const tasksPath = 'tasks/tasks.json';
		const context = { tag: 'master' };

		// Act
		clearSubtasks(tasksPath, taskId, context);

		// Assert
		expect(readJSON).toHaveBeenCalledWith(tasksPath, undefined, 'master');
		// Should not write the file if no changes were made
		expect(writeJSON).not.toHaveBeenCalled();
		expect(generateTaskFiles).not.toHaveBeenCalled();
	});

	test('should handle non-existent task IDs gracefully', () => {
		// Arrange
		const taskId = '99'; // Non-existent task
		const tasksPath = 'tasks/tasks.json';
		const context = { tag: 'master' };

		// Act
		clearSubtasks(tasksPath, taskId, context);

		// Assert
		expect(readJSON).toHaveBeenCalledWith(tasksPath, undefined, 'master');
		expect(log).toHaveBeenCalledWith('error', 'Task 99 not found');
		// Should not write the file if no changes were made
		expect(writeJSON).not.toHaveBeenCalled();
		expect(generateTaskFiles).not.toHaveBeenCalled();
	});

	test('should handle multiple task IDs including both valid and non-existent IDs', () => {
		// Arrange
		const taskIds = '3,99'; // Mix of valid and invalid IDs
		const tasksPath = 'tasks/tasks.json';
		const context = { tag: 'master' };

		// Act
		clearSubtasks(tasksPath, taskIds, context);

		// Assert
		expect(readJSON).toHaveBeenCalledWith(tasksPath, undefined, 'master');
		expect(log).toHaveBeenCalledWith('error', 'Task 99 not found');
		// Since task 3 has subtasks that should be cleared, writeJSON should be called
		expect(writeJSON).toHaveBeenCalledWith(
			tasksPath,
			expect.objectContaining({
				tasks: expect.arrayContaining([
					expect.objectContaining({ id: 3, subtasks: [] })
				]),
				tag: 'master',
				_rawTaggedData: expect.objectContaining({
					master: expect.objectContaining({
						tasks: expect.arrayContaining([
							expect.objectContaining({ id: 3, subtasks: [] })
						])
					})
				})
			}),
			undefined,
			'master'
		);
	});

	test('should handle file read errors', () => {
		// Arrange
		readJSON.mockImplementation(() => {
			throw new Error('File read failed');
		});

		// Act & Assert
		expect(() => {
			clearSubtasks('tasks/tasks.json', '3', { tag: 'master' });
		}).toThrow('File read failed');
	});

	test('should handle invalid tasks data', () => {
		// Arrange
		readJSON.mockReturnValue(null);

		// Act & Assert
		expect(() => {
			clearSubtasks('tasks/tasks.json', '3', { tag: 'master' });
		}).toThrow('process.exit called');

		expect(log).toHaveBeenCalledWith('error', 'No valid tasks found.');
	});

	test('should handle file write errors', () => {
		// Arrange
		// Ensure task 3 has subtasks to clear so writeJSON gets called
		readJSON.mockReturnValue({
			...sampleTasks.master,
			tag: 'master',
			_rawTaggedData: sampleTasks,
			tasks: [
				...sampleTasks.master.tasks.slice(0, 2),
				{
					...sampleTasks.master.tasks[2],
					subtasks: [{ id: 1, title: 'Subtask to clear' }]
				},
				...sampleTasks.master.tasks.slice(3)
			]
		});

		writeJSON.mockImplementation(() => {
			throw new Error('File write failed');
		});

		// Act & Assert
		expect(() => {
			clearSubtasks('tasks/tasks.json', '3', { tag: 'master' });
		}).toThrow('File write failed');
	});
});

```

--------------------------------------------------------------------------------
/.taskmaster/docs/research/2025-06-14_how-can-i-improve-the-scope-up-and-scope-down-comm.md:
--------------------------------------------------------------------------------

```markdown
---
title: Research Session
query: "how can i improve the scope-up and scope-down command"
date: 6/14/2025
time: 4:35:03 PM
timestamp: 2025-06-14T20:35:03.231Z
exchanges: 2
---

# Research Session

## Initial Query

**Question:** how can i improve the scope-up and scope-down command

**Response:**

## Improving the `scope-up` and `scope-down` Commands

**Clarify Command Purpose and User Experience**

- Ensure the commands have clear, intuitive behavior. For example, `scope-up` should consistently broaden the context or task view, while `scope-down` should reliably narrow it.
- Provide immediate, user-friendly feedback after each command, indicating what changed (e.g., "Now viewing parent task and all subtasks" or "Focused on subtask: X").
- Document the commands with examples in your `docs/command-reference.md` and `docs/examples.md` to help users understand their effects and use cases.

**Enhance Context Awareness**

- Make the commands context-sensitive: if the user is already at the top or bottom of the scope hierarchy, return a helpful message instead of failing silently.
- Allow optional arguments to specify the exact level or target (e.g., `scope-up 2` to move up two levels, or `scope-down <subtask-id>` to jump directly to a subtask).

**Robust Error Handling and Validation**

- Implement checks to prevent invalid operations, such as attempting to scope up from the root or scope down when no subtasks exist.
- Provide clear error messages and suggestions for next steps, improving user trust and reducing confusion[1][5].

**Integrate with Task and Tag Systems**

- Ensure `scope-up` and `scope-down` interact smoothly with your tagging and task hierarchy features. For example, scoping up from a subtask should show the parent task and its context, while scoping down should allow selection from available subtasks or related tags.
- Consider supporting batch operations, such as scoping down into all subtasks and displaying them in a summarized view.

**Improve Performance and Synchronization**

- Optimize the underlying logic to minimize delays, especially in large task trees. Use asynchronous operations where possible to keep the UI responsive.
- If the commands trigger data loading or context switching, ensure synchronization so that subsequent commands operate on the correct, updated context[2][4].

**Testing and Stakeholder Feedback**

- Add or expand unit and integration tests for these commands (see `tests/unit/commands.test.js` and `integration/cli/commands.test.js`) to cover edge cases and ensure reliability.
- Gather feedback from users and stakeholders to identify pain points and desired enhancements, then iterate on the command design and implementation[1][5].

**Example Improvements Table**

| Area                | Current Issue                | Suggested Improvement                        |
|---------------------|-----------------------------|----------------------------------------------|
| User Feedback       | Minimal or unclear output    | Add explicit, contextual messages            |
| Error Handling      | Silent failures              | Informative errors and recovery suggestions  |
| Flexibility         | Only single-level movement   | Support multi-level and targeted navigation  |
| Integration         | Limited context awareness    | Sync with tags, parent/child relationships   |
| Performance         | Slow on large hierarchies    | Optimize data access and UI updates          |

By focusing on these areas, you can make `scope-up` and `scope-down` more robust, user-friendly, and aligned with best practices in scope management and command design[1][5].

---

## Follow-up 1

**Question:** how might i do this relative to the tag system?

**Response:**

## Integrating Scope-Up and Scope-Down with the Tag System

**Conceptual Mapping**

- In your project, *scope-up* and *scope-down* commands currently operate on the task hierarchy, broadening or narrowing the context by moving between parent and child tasks.
- The tag system provides a parallel, orthogonal way to group and filter tasks—tags can represent categories, priorities, or any custom grouping, independent of strict parent/child relationships.

**How to Make Scope Commands Tag-Aware**

- **Scoping by Tag**: Allow users to "scope-down" into a tag, viewing only tasks associated with that tag. Conversely, "scope-up" could move from a specific tag context back to the broader task list or to a parent tag grouping if you support tag hierarchies.
- **Tag-Scoped Views**: When a tag is active as the current scope, all navigation and operations (listing, editing, etc.) are limited to tasks with that tag. This is similar to how some systems let you set the "Tag Scope" to filter navigation and search to only tagged items[1][5].
- **Combining Hierarchy and Tags**: Enable compound scoping, such as "all subtasks of Task X that also have Tag Y." This can be implemented by intersecting the results of hierarchical and tag-based queries.

**Implementation Strategies**

- **Scope Stack or Context Object**: Maintain a stack or context object that tracks both the current task scope and any active tag filters. Each scope change (up/down or tag in/out) updates this context.
- **Commands**:
  - `scope-down --tag <tag>`: Narrows the view to tasks with the specified tag.
  - `scope-up --tag`: Removes the tag filter, broadening the view.
  - `scope-down <subtask-id> --tag <tag>`: Narrows to a specific subtask *and* applies a tag filter.
- **Tag Hierarchies (Optional)**: If your tag system supports parent/child tags or tag groups, allow "scope-up" to move from a specific tag to its parent tag or group, and "scope-down" to move into a child tag[4][5].

**Example User Flows**

- *Scoping Down by Tag*:  
  User runs `scope-down --tag urgent` → Only tasks tagged "urgent" are shown.
- *Combining Task and Tag*:  
  User runs `scope-down <task-id> --tag frontend` → Only subtasks of `<task-id>` that are tagged "frontend" are shown.
- *Scoping Up from Tag*:  
  User runs `scope-up --tag` → Removes the tag filter, returning to the previous broader context.

**Technical Considerations**

- **Efficient Filtering**: Use indexed queries or in-memory filtering to efficiently combine tag and hierarchy filters, especially for large task sets.
- **Feedback and UI**: Always display the current scope context (e.g., "Viewing tasks tagged 'urgent' under 'Feature X'") so users understand what they're seeing.
- **Extensibility**: Design the scope system so new types of scopes (e.g., by status, assignee) can be added in the future, not just tags and hierarchy.

**Relevant Precedents**

- Tag-based file systems and management tools often implement "scopes" as dynamic groups defined by tag criteria, allowing users to build up complex views by combining tags and other filters[5].
- Some systems allow scoping into tag management itself, where you can create, edit, or view tags and their associated objects, which could be mirrored in your CLI or UI[4].

**Summary Table**

| Command Example                  | Resulting Scope                                  |
|----------------------------------|--------------------------------------------------|
| `scope-down --tag bug`           | All tasks tagged "bug"                           |
| `scope-down <task-id> --tag api` | Subtasks of `<task-id>` tagged "api"             |
| `scope-up --tag`                 | Removes tag filter, returns to previous scope    |
| `scope-down --tag frontend`      | All tasks tagged "frontend"                      |

By integrating tag-based scoping, you make your scope commands more flexible and powerful, supporting both hierarchical and ad-hoc organizational strategies[1][5].


---

*Generated by Task Master Research Command*  
*Timestamp: 2025-06-14T20:35:03.231Z*

```

--------------------------------------------------------------------------------
/packages/tm-core/src/task-master-core.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview TaskMasterCore facade - main entry point for tm-core functionality
 */

import { ConfigManager } from './config/config-manager.js';
import {
	TaskService,
	type TaskListResult as ListTasksResult,
	type GetTaskListOptions
} from './services/task-service.js';
import {
	TaskExecutionService,
	type StartTaskOptions,
	type StartTaskResult,
	type ConflictCheckResult
} from './services/task-execution-service.js';
import { ERROR_CODES, TaskMasterError } from './errors/task-master-error.js';
import type { IConfiguration } from './interfaces/configuration.interface.js';
import type {
	Task,
	TaskStatus,
	TaskFilter,
	StorageType
} from './types/index.js';
import {
	ExecutorService,
	type ExecutorServiceOptions,
	type ExecutionResult,
	type ExecutorType
} from './executors/index.js';

/**
 * Options for creating TaskMasterCore instance
 */
export interface TaskMasterCoreOptions {
	projectPath: string;
	configuration?: Partial<IConfiguration>;
}

/**
 * Re-export result types from services
 */
export type { TaskListResult as ListTasksResult } from './services/task-service.js';
export type { GetTaskListOptions } from './services/task-service.js';
export type {
	StartTaskOptions,
	StartTaskResult,
	ConflictCheckResult
} from './services/task-execution-service.js';

/**
 * TaskMasterCore facade class
 * Provides simplified API for all tm-core operations
 */
export class TaskMasterCore {
	private configManager: ConfigManager;
	private taskService: TaskService;
	private taskExecutionService: TaskExecutionService;
	private executorService: ExecutorService | null = null;

	/**
	 * Create and initialize a new TaskMasterCore instance
	 * This is the ONLY way to create a TaskMasterCore
	 *
	 * @param options - Configuration options for TaskMasterCore
	 * @returns Fully initialized TaskMasterCore instance
	 */
	static async create(options: TaskMasterCoreOptions): Promise<TaskMasterCore> {
		const instance = new TaskMasterCore();
		await instance.initialize(options);
		return instance;
	}

	/**
	 * Private constructor - use TaskMasterCore.create() instead
	 * This ensures the TaskMasterCore is always properly initialized
	 */
	private constructor() {
		// Services will be initialized in the initialize() method
		this.configManager = null as any;
		this.taskService = null as any;
		this.taskExecutionService = null as any;
	}

	/**
	 * Initialize by loading services
	 * Private - only called by the factory method
	 */
	private async initialize(options: TaskMasterCoreOptions): Promise<void> {
		if (!options.projectPath) {
			throw new TaskMasterError(
				'Project path is required',
				ERROR_CODES.MISSING_CONFIGURATION
			);
		}

		try {
			// Create config manager using factory method
			this.configManager = await ConfigManager.create(options.projectPath);

			// Apply configuration overrides if provided
			if (options.configuration) {
				await this.configManager.updateConfig(options.configuration);
			}

			// Create task service
			this.taskService = new TaskService(this.configManager);
			await this.taskService.initialize();

			// Create task execution service
			this.taskExecutionService = new TaskExecutionService(this.taskService);
		} catch (error) {
			throw new TaskMasterError(
				'Failed to initialize TaskMasterCore',
				ERROR_CODES.INTERNAL_ERROR,
				{ operation: 'initialize' },
				error as Error
			);
		}
	}

	/**
	 * Get list of tasks with optional filtering
	 * @deprecated Use getTaskList() instead
	 */
	async listTasks(options?: {
		tag?: string;
		filter?: TaskFilter;
		includeSubtasks?: boolean;
	}): Promise<ListTasksResult> {
		return this.getTaskList(options);
	}

	/**
	 * Get list of tasks with optional filtering
	 */
	async getTaskList(options?: GetTaskListOptions): Promise<ListTasksResult> {
		return this.taskService.getTaskList(options);
	}

	/**
	 * Get a specific task by ID
	 */
	async getTask(taskId: string, tag?: string): Promise<Task | null> {
		return this.taskService.getTask(taskId, tag);
	}

	/**
	 * Get tasks by status
	 */
	async getTasksByStatus(
		status: TaskStatus | TaskStatus[],
		tag?: string
	): Promise<Task[]> {
		return this.taskService.getTasksByStatus(status, tag);
	}

	/**
	 * Get task statistics
	 */
	async getTaskStats(tag?: string): Promise<{
		total: number;
		byStatus: Record<TaskStatus, number>;
		withSubtasks: number;
		blocked: number;
	}> {
		const stats = await this.taskService.getTaskStats(tag);
		// Remove storageType from the return to maintain backward compatibility
		const { storageType, ...restStats } = stats;
		return restStats;
	}

	/**
	 * Get next available task
	 */
	async getNextTask(tag?: string): Promise<Task | null> {
		return this.taskService.getNextTask(tag);
	}

	/**
	 * Get current storage type
	 */
	getStorageType(): StorageType {
		return this.taskService.getStorageType();
	}

	/**
	 * Get current active tag
	 */
	getActiveTag(): string {
		return this.configManager.getActiveTag();
	}

	/**
	 * Set active tag
	 */
	async setActiveTag(tag: string): Promise<void> {
		await this.configManager.setActiveTag(tag);
	}

	// ==================== Task Execution Methods ====================

	/**
	 * Start working on a task with comprehensive business logic
	 */
	async startTask(
		taskId: string,
		options: StartTaskOptions = {}
	): Promise<StartTaskResult> {
		return this.taskExecutionService.startTask(taskId, options);
	}

	/**
	 * Check if a task can be started (no conflicts)
	 */
	async canStartTask(taskId: string, force = false): Promise<boolean> {
		return this.taskExecutionService.canStartTask(taskId, force);
	}

	/**
	 * Check for existing in-progress tasks and determine conflicts
	 */
	async checkInProgressConflicts(
		targetTaskId: string
	): Promise<ConflictCheckResult> {
		return this.taskExecutionService.checkInProgressConflicts(targetTaskId);
	}

	/**
	 * Get task with subtask resolution
	 */
	async getTaskWithSubtask(
		taskId: string
	): Promise<{ task: Task | null; subtask?: any; subtaskId?: string }> {
		return this.taskExecutionService.getTaskWithSubtask(taskId);
	}

	/**
	 * Get the next available task to start
	 */
	async getNextAvailableTask(): Promise<string | null> {
		return this.taskExecutionService.getNextAvailableTask();
	}

	// ==================== Executor Service Methods ====================

	/**
	 * Initialize executor service (lazy initialization)
	 */
	private getExecutorService(): ExecutorService {
		if (!this.executorService) {
			const executorOptions: ExecutorServiceOptions = {
				projectRoot: this.configManager.getProjectRoot()
			};
			this.executorService = new ExecutorService(executorOptions);
		}
		return this.executorService;
	}

	/**
	 * Execute a task
	 */
	async executeTask(
		task: Task,
		executorType?: ExecutorType
	): Promise<ExecutionResult> {
		const executor = this.getExecutorService();
		return executor.executeTask(task, executorType);
	}

	/**
	 * Stop the current task execution
	 */
	async stopCurrentTask(): Promise<void> {
		if (this.executorService) {
			await this.executorService.stopCurrentTask();
		}
	}

	/**
	 * Update task status
	 */
	async updateTaskStatus(
		taskId: string | number,
		newStatus: TaskStatus,
		tag?: string
	): Promise<{
		success: boolean;
		oldStatus: TaskStatus;
		newStatus: TaskStatus;
		taskId: string;
	}> {
		return this.taskService.updateTaskStatus(taskId, newStatus, tag);
	}

	/**
	 * Close and cleanup resources
	 */
	async close(): Promise<void> {
		// Stop any running executors
		if (this.executorService) {
			await this.executorService.stopCurrentTask();
		}
		// TaskService handles storage cleanup internally
	}
}

/**
 * Factory function to create TaskMasterCore instance
 */
export async function createTaskMasterCore(
	options: TaskMasterCoreOptions
): Promise<TaskMasterCore> {
	return TaskMasterCore.create(options);
}

```

--------------------------------------------------------------------------------
/packages/tm-core/src/auth/credential-store.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Credential storage and management
 */

import fs from 'fs';
import path from 'path';
import { AuthCredentials, AuthenticationError, AuthConfig } from './types.js';
import { getAuthConfig } from './config.js';
import { getLogger } from '../logger/index.js';

/**
 * CredentialStore manages the persistence and retrieval of authentication credentials.
 *
 * Runtime vs Persisted Shape:
 * - When retrieved (getCredentials): expiresAt is normalized to number (milliseconds since epoch)
 * - When persisted (saveCredentials): expiresAt is stored as ISO string for readability
 *
 * This normalization ensures consistent runtime behavior while maintaining
 * human-readable persisted format in the auth.json file.
 */
export class CredentialStore {
	private static instance: CredentialStore | null = null;
	private logger = getLogger('CredentialStore');
	private config: AuthConfig;
	// Clock skew tolerance for expiry checks (30 seconds)
	private readonly CLOCK_SKEW_MS = 30_000;

	private constructor(config?: Partial<AuthConfig>) {
		this.config = getAuthConfig(config);
	}

	/**
	 * Get the singleton instance of CredentialStore
	 */
	static getInstance(config?: Partial<AuthConfig>): CredentialStore {
		if (!CredentialStore.instance) {
			CredentialStore.instance = new CredentialStore(config);
		} else if (config) {
			// Warn if config is provided after initialization
			const logger = getLogger('CredentialStore');
			logger.warn(
				'getInstance called with config after initialization; config is ignored.'
			);
		}
		return CredentialStore.instance;
	}

	/**
	 * Reset the singleton instance (useful for testing)
	 */
	static resetInstance(): void {
		CredentialStore.instance = null;
	}

	/**
	 * Get stored authentication credentials
	 * @returns AuthCredentials with expiresAt as number (milliseconds) for runtime use
	 */
	getCredentials(options?: { allowExpired?: boolean }): AuthCredentials | null {
		try {
			if (!fs.existsSync(this.config.configFile)) {
				return null;
			}

			const authData = JSON.parse(
				fs.readFileSync(this.config.configFile, 'utf-8')
			) as AuthCredentials;

			// Normalize/migrate timestamps to numeric (handles both number and ISO string)
			let expiresAtMs: number | undefined;
			if (typeof authData.expiresAt === 'number') {
				expiresAtMs = Number.isFinite(authData.expiresAt)
					? authData.expiresAt
					: undefined;
			} else if (typeof authData.expiresAt === 'string') {
				const parsed = Date.parse(authData.expiresAt);
				expiresAtMs = Number.isNaN(parsed) ? undefined : parsed;
			} else {
				expiresAtMs = undefined;
			}

			// Validate expiration time for tokens
			if (expiresAtMs === undefined) {
				this.logger.warn('No valid expiration time provided for token');
				return null;
			}

			// Update the authData with normalized timestamp
			authData.expiresAt = expiresAtMs;

			// Check if the token has expired (with clock skew tolerance)
			const now = Date.now();
			const allowExpired = options?.allowExpired ?? false;
			if (now >= expiresAtMs - this.CLOCK_SKEW_MS && !allowExpired) {
				this.logger.warn(
					'Authentication token has expired or is about to expire',
					{
						expiresAt: authData.expiresAt,
						currentTime: new Date(now).toISOString(),
						skewWindow: `${this.CLOCK_SKEW_MS / 1000}s`
					}
				);
				return null;
			}

			// Return valid token
			return authData;
		} catch (error) {
			this.logger.error(
				`Failed to read auth credentials: ${(error as Error).message}`
			);

			// Quarantine corrupt file to prevent repeated errors
			try {
				if (fs.existsSync(this.config.configFile)) {
					const corruptFile = `${this.config.configFile}.corrupt-${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
					fs.renameSync(this.config.configFile, corruptFile);
					this.logger.warn(`Quarantined corrupt auth file to: ${corruptFile}`);
				}
			} catch (quarantineError) {
				// If we can't quarantine, log but don't throw
				this.logger.debug(
					`Could not quarantine corrupt file: ${(quarantineError as Error).message}`
				);
			}

			return null;
		}
	}

	/**
	 * Save authentication credentials
	 * @param authData - Credentials with expiresAt as number or string (will be persisted as ISO string)
	 */
	saveCredentials(authData: AuthCredentials): void {
		try {
			// Ensure directory exists
			if (!fs.existsSync(this.config.configDir)) {
				fs.mkdirSync(this.config.configDir, { recursive: true, mode: 0o700 });
			}

			// Add timestamp without mutating caller's object
			authData = { ...authData, savedAt: new Date().toISOString() };

			// Validate and normalize expiresAt timestamp
			if (authData.expiresAt !== undefined) {
				let validTimestamp: number | undefined;

				if (typeof authData.expiresAt === 'number') {
					validTimestamp = Number.isFinite(authData.expiresAt)
						? authData.expiresAt
						: undefined;
				} else if (typeof authData.expiresAt === 'string') {
					const parsed = Date.parse(authData.expiresAt);
					validTimestamp = Number.isNaN(parsed) ? undefined : parsed;
				}

				if (validTimestamp === undefined) {
					throw new AuthenticationError(
						`Invalid expiresAt format: ${authData.expiresAt}`,
						'SAVE_FAILED'
					);
				}

				// Store as ISO string for consistency
				authData.expiresAt = new Date(validTimestamp).toISOString();
			}

			// Save credentials atomically with secure permissions
			const tempFile = `${this.config.configFile}.tmp`;
			fs.writeFileSync(tempFile, JSON.stringify(authData, null, 2), {
				mode: 0o600
			});
			fs.renameSync(tempFile, this.config.configFile);
		} catch (error) {
			throw new AuthenticationError(
				`Failed to save auth credentials: ${(error as Error).message}`,
				'SAVE_FAILED',
				error
			);
		}
	}

	/**
	 * Clear stored credentials
	 */
	clearCredentials(): void {
		try {
			if (fs.existsSync(this.config.configFile)) {
				fs.unlinkSync(this.config.configFile);
			}
		} catch (error) {
			throw new AuthenticationError(
				`Failed to clear credentials: ${(error as Error).message}`,
				'CLEAR_FAILED',
				error
			);
		}
	}

	/**
	 * Check if credentials exist and are valid
	 */
	hasValidCredentials(): boolean {
		const credentials = this.getCredentials({ allowExpired: false });
		return credentials !== null;
	}

	/**
	 * Get configuration
	 */
	getConfig(): AuthConfig {
		return { ...this.config };
	}

	/**
	 * Clean up old corrupt auth files
	 * Removes corrupt files older than the specified age
	 */
	cleanupCorruptFiles(maxAgeMs: number = 7 * 24 * 60 * 60 * 1000): void {
		try {
			const dir = path.dirname(this.config.configFile);
			const baseName = path.basename(this.config.configFile);
			const prefix = `${baseName}.corrupt-`;

			if (!fs.existsSync(dir)) {
				return;
			}

			const entries = fs.readdirSync(dir, { withFileTypes: true });
			const now = Date.now();

			for (const entry of entries) {
				if (!entry.isFile()) continue;
				const file = entry.name;

				// Check if file matches pattern: baseName.corrupt-{timestamp}
				if (!file.startsWith(prefix)) continue;
				const suffix = file.slice(prefix.length);
				if (!/^\d+$/.test(suffix)) continue; // Fixed regex, not from variable input

				const filePath = path.join(dir, file);
				try {
					const stats = fs.statSync(filePath);
					const age = now - stats.mtimeMs;

					if (age > maxAgeMs) {
						fs.unlinkSync(filePath);
						this.logger.debug(`Cleaned up old corrupt file: ${file}`);
					}
				} catch (error) {
					// Ignore errors for individual file cleanup
					this.logger.debug(
						`Could not clean up corrupt file ${file}: ${(error as Error).message}`
					);
				}
			}
		} catch (error) {
			// Log but don't throw - this is a cleanup operation
			this.logger.debug(
				`Error during corrupt file cleanup: ${(error as Error).message}`
			);
		}
	}
}

```

--------------------------------------------------------------------------------
/src/utils/create-mcp-config.js:
--------------------------------------------------------------------------------

```javascript
import fs from 'fs';
import path from 'path';
import { log } from '../../scripts/modules/utils.js';

// Return JSON with existing mcp.json formatting style
function formatJSONWithTabs(obj) {
	let json = JSON.stringify(obj, null, '\t');

	json = json.replace(
		/(\[\n\t+)([^[\]]+?)(\n\t+\])/g,
		(match, openBracket, content, closeBracket) => {
			// Only convert to single line if content doesn't contain nested objects/arrays
			if (!content.includes('{') && !content.includes('[')) {
				const singleLineContent = content
					.replace(/\n\t+/g, ' ')
					.replace(/\s+/g, ' ')
					.trim();
				return `[${singleLineContent}]`;
			}
			return match;
		}
	);

	return json;
}

// Structure matches project conventions (see scripts/init.js)
export function setupMCPConfiguration(projectRoot, mcpConfigPath) {
	// Handle null mcpConfigPath (e.g., for Claude/Codex profiles)
	if (!mcpConfigPath) {
		log(
			'debug',
			'[MCP Config] No mcpConfigPath provided, skipping MCP configuration setup'
		);
		return;
	}

	// Build the full path to the MCP config file
	const mcpPath = path.join(projectRoot, mcpConfigPath);
	const configDir = path.dirname(mcpPath);

	log('info', `Setting up MCP configuration at ${mcpPath}...`);

	// New MCP config to be added - references the installed package
	const newMCPServer = {
		'task-master-ai': {
			command: 'npx',
			args: ['-y', 'task-master-ai'],
			env: {
				ANTHROPIC_API_KEY: 'YOUR_ANTHROPIC_API_KEY_HERE',
				PERPLEXITY_API_KEY: 'YOUR_PERPLEXITY_API_KEY_HERE',
				OPENAI_API_KEY: 'YOUR_OPENAI_KEY_HERE',
				GOOGLE_API_KEY: 'YOUR_GOOGLE_KEY_HERE',
				XAI_API_KEY: 'YOUR_XAI_KEY_HERE',
				OPENROUTER_API_KEY: 'YOUR_OPENROUTER_KEY_HERE',
				MISTRAL_API_KEY: 'YOUR_MISTRAL_KEY_HERE',
				AZURE_OPENAI_API_KEY: 'YOUR_AZURE_KEY_HERE',
				OLLAMA_API_KEY: 'YOUR_OLLAMA_API_KEY_HERE'
			}
		}
	};

	// Create config directory if it doesn't exist
	if (!fs.existsSync(configDir)) {
		fs.mkdirSync(configDir, { recursive: true });
	}

	if (fs.existsSync(mcpPath)) {
		log(
			'info',
			'MCP configuration file already exists, checking for existing task-master-ai...'
		);
		try {
			// Read existing config
			const mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
			// Initialize mcpServers if it doesn't exist
			if (!mcpConfig.mcpServers) {
				mcpConfig.mcpServers = {};
			}
			// Check if any existing server configuration already has task-master-ai in its args
			const hasMCPString = Object.values(mcpConfig.mcpServers).some(
				(server) =>
					server.args &&
					Array.isArray(server.args) &&
					server.args.some(
						(arg) => typeof arg === 'string' && arg.includes('task-master-ai')
					)
			);
			if (hasMCPString) {
				log(
					'info',
					'Found existing task-master-ai MCP configuration in mcp.json, leaving untouched'
				);
				return; // Exit early, don't modify the existing configuration
			}
			// Add the task-master-ai server if it doesn't exist
			if (!mcpConfig.mcpServers['task-master-ai']) {
				mcpConfig.mcpServers['task-master-ai'] = newMCPServer['task-master-ai'];
				log(
					'info',
					'Added task-master-ai server to existing MCP configuration'
				);
			} else {
				log('info', 'task-master-ai server already configured in mcp.json');
			}
			// Write the updated configuration
			fs.writeFileSync(mcpPath, formatJSONWithTabs(mcpConfig) + '\n');
			log('success', 'Updated MCP configuration file');
		} catch (error) {
			log('error', `Failed to update MCP configuration: ${error.message}`);
			// Create a backup before potentially modifying
			const backupPath = `${mcpPath}.backup-${Date.now()}`;
			if (fs.existsSync(mcpPath)) {
				fs.copyFileSync(mcpPath, backupPath);
				log('info', `Created backup of existing mcp.json at ${backupPath}`);
			}
			// Create new configuration
			const newMCPConfig = {
				mcpServers: newMCPServer
			};
			fs.writeFileSync(mcpPath, formatJSONWithTabs(newMCPConfig) + '\n');
			log(
				'warn',
				'Created new MCP configuration file (backup of original file was created if it existed)'
			);
		}
	} else {
		// If mcp.json doesn't exist, create it
		const newMCPConfig = {
			mcpServers: newMCPServer
		};
		fs.writeFileSync(mcpPath, formatJSONWithTabs(newMCPConfig) + '\n');
		log('success', `Created MCP configuration file at ${mcpPath}`);
	}

	// Add note to console about MCP integration
	log('info', 'MCP server will use the installed task-master-ai package');
}

/**
 * Remove Task Master MCP server configuration from an existing mcp.json file
 * Only removes Task Master entries, preserving other MCP servers
 * @param {string} projectRoot - Target project directory
 * @param {string} mcpConfigPath - Relative path to MCP config file (e.g., '.cursor/mcp.json')
 * @returns {Object} Result object with success status and details
 */
export function removeTaskMasterMCPConfiguration(projectRoot, mcpConfigPath) {
	// Handle null mcpConfigPath (e.g., for Claude/Codex profiles)
	if (!mcpConfigPath) {
		return {
			success: true,
			removed: false,
			deleted: false,
			error: null,
			hasOtherServers: false
		};
	}

	const mcpPath = path.join(projectRoot, mcpConfigPath);

	let result = {
		success: false,
		removed: false,
		deleted: false,
		error: null,
		hasOtherServers: false
	};

	if (!fs.existsSync(mcpPath)) {
		result.success = true;
		result.removed = false;
		log('debug', `[MCP Config] MCP config file does not exist: ${mcpPath}`);
		return result;
	}

	try {
		// Read existing config
		const mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));

		if (!mcpConfig.mcpServers) {
			result.success = true;
			result.removed = false;
			log('debug', `[MCP Config] No mcpServers section found in: ${mcpPath}`);
			return result;
		}

		// Check if Task Master is configured
		const hasTaskMaster =
			mcpConfig.mcpServers['task-master-ai'] ||
			Object.values(mcpConfig.mcpServers).some(
				(server) =>
					server.args &&
					Array.isArray(server.args) &&
					server.args.some(
						(arg) => typeof arg === 'string' && arg.includes('task-master-ai')
					)
			);

		if (!hasTaskMaster) {
			result.success = true;
			result.removed = false;
			log(
				'debug',
				`[MCP Config] Task Master not found in MCP config: ${mcpPath}`
			);
			return result;
		}

		// Remove task-master-ai server
		delete mcpConfig.mcpServers['task-master-ai'];

		// Also remove any servers that have task-master-ai in their args
		Object.keys(mcpConfig.mcpServers).forEach((serverName) => {
			const server = mcpConfig.mcpServers[serverName];
			if (
				server.args &&
				Array.isArray(server.args) &&
				server.args.some(
					(arg) => typeof arg === 'string' && arg.includes('task-master-ai')
				)
			) {
				delete mcpConfig.mcpServers[serverName];
				log(
					'debug',
					`[MCP Config] Removed server '${serverName}' containing task-master-ai`
				);
			}
		});

		// Check if there are other MCP servers remaining
		const remainingServers = Object.keys(mcpConfig.mcpServers);
		result.hasOtherServers = remainingServers.length > 0;

		if (result.hasOtherServers) {
			// Write back the modified config with remaining servers
			fs.writeFileSync(mcpPath, formatJSONWithTabs(mcpConfig) + '\n');
			result.success = true;
			result.removed = true;
			result.deleted = false;
			log(
				'info',
				`[MCP Config] Removed Task Master from MCP config, preserving other servers: ${remainingServers.join(', ')}`
			);
		} else {
			// No other servers, delete the entire file
			fs.rmSync(mcpPath, { force: true });
			result.success = true;
			result.removed = true;
			result.deleted = true;
			log(
				'info',
				`[MCP Config] Removed MCP config file (no other servers remaining): ${mcpPath}`
			);
		}
	} catch (error) {
		result.error = error.message;
		log(
			'error',
			`[MCP Config] Failed to remove Task Master from MCP config: ${error.message}`
		);
	}

	return result;
}

```
Page 13/38FirstPrevNextLast