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

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/apps/cli/src/commands/context.command.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * @fileoverview Context command for managing org/brief selection
  3 |  * Provides a clean interface for workspace context management
  4 |  */
  5 | 
  6 | import { Command } from 'commander';
  7 | import chalk from 'chalk';
  8 | import inquirer from 'inquirer';
  9 | import ora, { Ora } from 'ora';
 10 | import {
 11 | 	AuthManager,
 12 | 	AuthenticationError,
 13 | 	type UserContext
 14 | } from '@tm/core/auth';
 15 | import * as ui from '../utils/ui.js';
 16 | 
 17 | /**
 18 |  * Result type from context command
 19 |  */
 20 | export interface ContextResult {
 21 | 	success: boolean;
 22 | 	action: 'show' | 'select-org' | 'select-brief' | 'clear' | 'set';
 23 | 	context?: UserContext;
 24 | 	message?: string;
 25 | }
 26 | 
 27 | /**
 28 |  * ContextCommand extending Commander's Command class
 29 |  * Manages user's workspace context (org/brief selection)
 30 |  */
 31 | export class ContextCommand extends Command {
 32 | 	private authManager: AuthManager;
 33 | 	private lastResult?: ContextResult;
 34 | 
 35 | 	constructor(name?: string) {
 36 | 		super(name || 'context');
 37 | 
 38 | 		// Initialize auth manager
 39 | 		this.authManager = AuthManager.getInstance();
 40 | 
 41 | 		// Configure the command
 42 | 		this.description(
 43 | 			'Manage workspace context (organization and brief selection)'
 44 | 		);
 45 | 
 46 | 		// Add subcommands
 47 | 		this.addOrgCommand();
 48 | 		this.addBriefCommand();
 49 | 		this.addClearCommand();
 50 | 		this.addSetCommand();
 51 | 
 52 | 		// Accept optional positional argument for brief ID or Hamster URL
 53 | 		this.argument('[briefOrUrl]', 'Brief ID or Hamster brief URL');
 54 | 
 55 | 		// Default action: if an argument is provided, resolve and set context; else show
 56 | 		this.action(async (briefOrUrl?: string) => {
 57 | 			if (briefOrUrl && briefOrUrl.trim().length > 0) {
 58 | 				await this.executeSetFromBriefInput(briefOrUrl.trim());
 59 | 				return;
 60 | 			}
 61 | 			await this.executeShow();
 62 | 		});
 63 | 	}
 64 | 
 65 | 	/**
 66 | 	 * Add org selection subcommand
 67 | 	 */
 68 | 	private addOrgCommand(): void {
 69 | 		this.command('org')
 70 | 			.description('Select an organization')
 71 | 			.action(async () => {
 72 | 				await this.executeSelectOrg();
 73 | 			});
 74 | 	}
 75 | 
 76 | 	/**
 77 | 	 * Add brief selection subcommand
 78 | 	 */
 79 | 	private addBriefCommand(): void {
 80 | 		this.command('brief')
 81 | 			.description('Select a brief within the current organization')
 82 | 			.action(async () => {
 83 | 				await this.executeSelectBrief();
 84 | 			});
 85 | 	}
 86 | 
 87 | 	/**
 88 | 	 * Add clear subcommand
 89 | 	 */
 90 | 	private addClearCommand(): void {
 91 | 		this.command('clear')
 92 | 			.description('Clear all context selections')
 93 | 			.action(async () => {
 94 | 				await this.executeClear();
 95 | 			});
 96 | 	}
 97 | 
 98 | 	/**
 99 | 	 * Add set subcommand for direct context setting
100 | 	 */
101 | 	private addSetCommand(): void {
102 | 		this.command('set')
103 | 			.description('Set context directly')
104 | 			.option('--org <id>', 'Organization ID')
105 | 			.option('--org-name <name>', 'Organization name')
106 | 			.option('--brief <id>', 'Brief ID')
107 | 			.option('--brief-name <name>', 'Brief name')
108 | 			.action(async (options) => {
109 | 				await this.executeSet(options);
110 | 			});
111 | 	}
112 | 
113 | 	/**
114 | 	 * Execute show current context
115 | 	 */
116 | 	private async executeShow(): Promise<void> {
117 | 		try {
118 | 			const result = this.displayContext();
119 | 			this.setLastResult(result);
120 | 		} catch (error: any) {
121 | 			this.handleError(error);
122 | 			process.exit(1);
123 | 		}
124 | 	}
125 | 
126 | 	/**
127 | 	 * Display current context
128 | 	 */
129 | 	private displayContext(): ContextResult {
130 | 		// Check authentication first
131 | 		if (!this.authManager.isAuthenticated()) {
132 | 			console.log(chalk.yellow('✗ Not authenticated'));
133 | 			console.log(chalk.gray('\n  Run "tm auth login" to authenticate first'));
134 | 
135 | 			return {
136 | 				success: false,
137 | 				action: 'show',
138 | 				message: 'Not authenticated'
139 | 			};
140 | 		}
141 | 
142 | 		const context = this.authManager.getContext();
143 | 
144 | 		console.log(chalk.cyan('\n🌍 Workspace Context\n'));
145 | 
146 | 		if (context && (context.orgId || context.briefId)) {
147 | 			if (context.orgName || context.orgId) {
148 | 				console.log(chalk.green('✓ Organization'));
149 | 				if (context.orgName) {
150 | 					console.log(chalk.white(`  ${context.orgName}`));
151 | 				}
152 | 				if (context.orgId) {
153 | 					console.log(chalk.gray(`  ID: ${context.orgId}`));
154 | 				}
155 | 			}
156 | 
157 | 			if (context.briefName || context.briefId) {
158 | 				console.log(chalk.green('\n✓ Brief'));
159 | 				if (context.briefName) {
160 | 					console.log(chalk.white(`  ${context.briefName}`));
161 | 				}
162 | 				if (context.briefId) {
163 | 					console.log(chalk.gray(`  ID: ${context.briefId}`));
164 | 				}
165 | 			}
166 | 
167 | 			if (context.updatedAt) {
168 | 				console.log(
169 | 					chalk.gray(
170 | 						`\n  Last updated: ${new Date(context.updatedAt).toLocaleString()}`
171 | 					)
172 | 				);
173 | 			}
174 | 
175 | 			return {
176 | 				success: true,
177 | 				action: 'show',
178 | 				context,
179 | 				message: 'Context loaded'
180 | 			};
181 | 		} else {
182 | 			console.log(chalk.yellow('✗ No context selected'));
183 | 			console.log(
184 | 				chalk.gray('\n  Run "tm context org" to select an organization')
185 | 			);
186 | 			console.log(chalk.gray('  Run "tm context brief" to select a brief'));
187 | 
188 | 			return {
189 | 				success: true,
190 | 				action: 'show',
191 | 				message: 'No context selected'
192 | 			};
193 | 		}
194 | 	}
195 | 
196 | 	/**
197 | 	 * Execute org selection
198 | 	 */
199 | 	private async executeSelectOrg(): Promise<void> {
200 | 		try {
201 | 			// Check authentication
202 | 			if (!this.authManager.isAuthenticated()) {
203 | 				ui.displayError('Not authenticated. Run "tm auth login" first.');
204 | 				process.exit(1);
205 | 			}
206 | 
207 | 			const result = await this.selectOrganization();
208 | 			this.setLastResult(result);
209 | 
210 | 			if (!result.success) {
211 | 				process.exit(1);
212 | 			}
213 | 		} catch (error: any) {
214 | 			this.handleError(error);
215 | 			process.exit(1);
216 | 		}
217 | 	}
218 | 
219 | 	/**
220 | 	 * Select an organization interactively
221 | 	 */
222 | 	private async selectOrganization(): Promise<ContextResult> {
223 | 		const spinner = ora('Fetching organizations...').start();
224 | 
225 | 		try {
226 | 			// Fetch organizations from API
227 | 			const organizations = await this.authManager.getOrganizations();
228 | 			spinner.stop();
229 | 
230 | 			if (organizations.length === 0) {
231 | 				ui.displayWarning('No organizations available');
232 | 				return {
233 | 					success: false,
234 | 					action: 'select-org',
235 | 					message: 'No organizations available'
236 | 				};
237 | 			}
238 | 
239 | 			// Prompt for selection
240 | 			const { selectedOrg } = await inquirer.prompt([
241 | 				{
242 | 					type: 'list',
243 | 					name: 'selectedOrg',
244 | 					message: 'Select an organization:',
245 | 					choices: organizations.map((org) => ({
246 | 						name: org.name,
247 | 						value: org
248 | 					}))
249 | 				}
250 | 			]);
251 | 
252 | 			// Update context
253 | 			await this.authManager.updateContext({
254 | 				orgId: selectedOrg.id,
255 | 				orgName: selectedOrg.name,
256 | 				// Clear brief when changing org
257 | 				briefId: undefined,
258 | 				briefName: undefined
259 | 			});
260 | 
261 | 			ui.displaySuccess(`Selected organization: ${selectedOrg.name}`);
262 | 
263 | 			return {
264 | 				success: true,
265 | 				action: 'select-org',
266 | 				context: this.authManager.getContext() || undefined,
267 | 				message: `Selected organization: ${selectedOrg.name}`
268 | 			};
269 | 		} catch (error) {
270 | 			spinner.fail('Failed to fetch organizations');
271 | 			throw error;
272 | 		}
273 | 	}
274 | 
275 | 	/**
276 | 	 * Execute brief selection
277 | 	 */
278 | 	private async executeSelectBrief(): Promise<void> {
279 | 		try {
280 | 			// Check authentication
281 | 			if (!this.authManager.isAuthenticated()) {
282 | 				ui.displayError('Not authenticated. Run "tm auth login" first.');
283 | 				process.exit(1);
284 | 			}
285 | 
286 | 			// Check if org is selected
287 | 			const context = this.authManager.getContext();
288 | 			if (!context?.orgId) {
289 | 				ui.displayError(
290 | 					'No organization selected. Run "tm context org" first.'
291 | 				);
292 | 				process.exit(1);
293 | 			}
294 | 
295 | 			const result = await this.selectBrief(context.orgId);
296 | 			this.setLastResult(result);
297 | 
298 | 			if (!result.success) {
299 | 				process.exit(1);
300 | 			}
301 | 		} catch (error: any) {
302 | 			this.handleError(error);
303 | 			process.exit(1);
304 | 		}
305 | 	}
306 | 
307 | 	/**
308 | 	 * Select a brief within the current organization
309 | 	 */
310 | 	private async selectBrief(orgId: string): Promise<ContextResult> {
311 | 		const spinner = ora('Fetching briefs...').start();
312 | 
313 | 		try {
314 | 			// Fetch briefs from API
315 | 			const briefs = await this.authManager.getBriefs(orgId);
316 | 			spinner.stop();
317 | 
318 | 			if (briefs.length === 0) {
319 | 				ui.displayWarning('No briefs available in this organization');
320 | 				return {
321 | 					success: false,
322 | 					action: 'select-brief',
323 | 					message: 'No briefs available'
324 | 				};
325 | 			}
326 | 
327 | 			// Prompt for selection
328 | 			const { selectedBrief } = await inquirer.prompt([
329 | 				{
330 | 					type: 'list',
331 | 					name: 'selectedBrief',
332 | 					message: 'Select a brief:',
333 | 					choices: [
334 | 						{ name: '(No brief - organization level)', value: null },
335 | 						...briefs.map((brief) => ({
336 | 							name: `Brief ${brief.id} (${new Date(brief.createdAt).toLocaleDateString()})`,
337 | 							value: brief
338 | 						}))
339 | 					]
340 | 				}
341 | 			]);
342 | 
343 | 			if (selectedBrief) {
344 | 				// Update context with brief
345 | 				const briefName = `Brief ${selectedBrief.id.slice(0, 8)}`;
346 | 				await this.authManager.updateContext({
347 | 					briefId: selectedBrief.id,
348 | 					briefName: briefName
349 | 				});
350 | 
351 | 				ui.displaySuccess(`Selected brief: ${briefName}`);
352 | 
353 | 				return {
354 | 					success: true,
355 | 					action: 'select-brief',
356 | 					context: this.authManager.getContext() || undefined,
357 | 					message: `Selected brief: ${selectedBrief.name}`
358 | 				};
359 | 			} else {
360 | 				// Clear brief selection
361 | 				await this.authManager.updateContext({
362 | 					briefId: undefined,
363 | 					briefName: undefined
364 | 				});
365 | 
366 | 				ui.displaySuccess('Cleared brief selection (organization level)');
367 | 
368 | 				return {
369 | 					success: true,
370 | 					action: 'select-brief',
371 | 					context: this.authManager.getContext() || undefined,
372 | 					message: 'Cleared brief selection'
373 | 				};
374 | 			}
375 | 		} catch (error) {
376 | 			spinner.fail('Failed to fetch briefs');
377 | 			throw error;
378 | 		}
379 | 	}
380 | 
381 | 	/**
382 | 	 * Execute clear context
383 | 	 */
384 | 	private async executeClear(): Promise<void> {
385 | 		try {
386 | 			// Check authentication
387 | 			if (!this.authManager.isAuthenticated()) {
388 | 				ui.displayError('Not authenticated. Run "tm auth login" first.');
389 | 				process.exit(1);
390 | 			}
391 | 
392 | 			const result = await this.clearContext();
393 | 			this.setLastResult(result);
394 | 
395 | 			if (!result.success) {
396 | 				process.exit(1);
397 | 			}
398 | 		} catch (error: any) {
399 | 			this.handleError(error);
400 | 			process.exit(1);
401 | 		}
402 | 	}
403 | 
404 | 	/**
405 | 	 * Clear all context selections
406 | 	 */
407 | 	private async clearContext(): Promise<ContextResult> {
408 | 		try {
409 | 			await this.authManager.clearContext();
410 | 			ui.displaySuccess('Context cleared');
411 | 
412 | 			return {
413 | 				success: true,
414 | 				action: 'clear',
415 | 				message: 'Context cleared'
416 | 			};
417 | 		} catch (error) {
418 | 			ui.displayError(`Failed to clear context: ${(error as Error).message}`);
419 | 
420 | 			return {
421 | 				success: false,
422 | 				action: 'clear',
423 | 				message: `Failed to clear context: ${(error as Error).message}`
424 | 			};
425 | 		}
426 | 	}
427 | 
428 | 	/**
429 | 	 * Execute set context with options
430 | 	 */
431 | 	private async executeSet(options: any): Promise<void> {
432 | 		try {
433 | 			// Check authentication
434 | 			if (!this.authManager.isAuthenticated()) {
435 | 				ui.displayError('Not authenticated. Run "tm auth login" first.');
436 | 				process.exit(1);
437 | 			}
438 | 
439 | 			const result = await this.setContext(options);
440 | 			this.setLastResult(result);
441 | 
442 | 			if (!result.success) {
443 | 				process.exit(1);
444 | 			}
445 | 		} catch (error: any) {
446 | 			this.handleError(error);
447 | 			process.exit(1);
448 | 		}
449 | 	}
450 | 
451 | 	/**
452 | 	 * Execute setting context from a brief ID or Hamster URL
453 | 	 */
454 | 	private async executeSetFromBriefInput(briefOrUrl: string): Promise<void> {
455 | 		let spinner: Ora | undefined;
456 | 		try {
457 | 			// Check authentication
458 | 			if (!this.authManager.isAuthenticated()) {
459 | 				ui.displayError('Not authenticated. Run "tm auth login" first.');
460 | 				process.exit(1);
461 | 			}
462 | 
463 | 			spinner = ora('Resolving brief...');
464 | 			spinner.start();
465 | 
466 | 			// Extract brief ID
467 | 			const briefId = this.extractBriefId(briefOrUrl);
468 | 			if (!briefId) {
469 | 				spinner.fail('Could not extract a brief ID from the provided input');
470 | 				ui.displayError(
471 | 					`Provide a valid brief ID or a Hamster brief URL, e.g. https://${process.env.TM_PUBLIC_BASE_DOMAIN}/home/hamster/briefs/<id>`
472 | 				);
473 | 				process.exit(1);
474 | 			}
475 | 
476 | 			// Fetch brief and resolve its organization
477 | 			const brief = await this.authManager.getBrief(briefId);
478 | 			if (!brief) {
479 | 				spinner.fail('Brief not found or you do not have access');
480 | 				process.exit(1);
481 | 			}
482 | 
483 | 			// Fetch org to get a friendly name (optional)
484 | 			let orgName: string | undefined;
485 | 			try {
486 | 				const org = await this.authManager.getOrganization(brief.accountId);
487 | 				orgName = org?.name;
488 | 			} catch {
489 | 				// Non-fatal if org lookup fails
490 | 			}
491 | 
492 | 			// Update context: set org and brief
493 | 			const briefName = `Brief ${brief.id.slice(0, 8)}`;
494 | 			await this.authManager.updateContext({
495 | 				orgId: brief.accountId,
496 | 				orgName,
497 | 				briefId: brief.id,
498 | 				briefName
499 | 			});
500 | 
501 | 			spinner.succeed('Context set from brief');
502 | 			console.log(
503 | 				chalk.gray(
504 | 					`  Organization: ${orgName || brief.accountId}\n  Brief: ${briefName}`
505 | 				)
506 | 			);
507 | 
508 | 			this.setLastResult({
509 | 				success: true,
510 | 				action: 'set',
511 | 				context: this.authManager.getContext() || undefined,
512 | 				message: 'Context set from brief'
513 | 			});
514 | 		} catch (error: any) {
515 | 			try {
516 | 				if (spinner?.isSpinning) spinner.stop();
517 | 			} catch {}
518 | 			this.handleError(error);
519 | 			process.exit(1);
520 | 		}
521 | 	}
522 | 
523 | 	/**
524 | 	 * Extract a brief ID from raw input (ID or Hamster URL)
525 | 	 */
526 | 	private extractBriefId(input: string): string | null {
527 | 		const raw = input?.trim() ?? '';
528 | 		if (!raw) return null;
529 | 
530 | 		const parseUrl = (s: string): URL | null => {
531 | 			try {
532 | 				return new URL(s);
533 | 			} catch {}
534 | 			try {
535 | 				return new URL(`https://${s}`);
536 | 			} catch {}
537 | 			return null;
538 | 		};
539 | 
540 | 		const fromParts = (path: string): string | null => {
541 | 			const parts = path.split('/').filter(Boolean);
542 | 			const briefsIdx = parts.lastIndexOf('briefs');
543 | 			const candidate =
544 | 				briefsIdx >= 0 && parts.length > briefsIdx + 1
545 | 					? parts[briefsIdx + 1]
546 | 					: parts[parts.length - 1];
547 | 			return candidate?.trim() || null;
548 | 		};
549 | 
550 | 		// 1) URL (absolute or scheme‑less)
551 | 		const url = parseUrl(raw);
552 | 		if (url) {
553 | 			const qId = url.searchParams.get('id') || url.searchParams.get('briefId');
554 | 			const candidate = (qId || fromParts(url.pathname)) ?? null;
555 | 			if (candidate) {
556 | 				// Light sanity check; let API be the final validator
557 | 				if (this.isLikelyId(candidate) || candidate.length >= 8)
558 | 					return candidate;
559 | 			}
560 | 		}
561 | 
562 | 		// 2) Looks like a path without scheme
563 | 		if (raw.includes('/')) {
564 | 			const candidate = fromParts(raw);
565 | 			if (candidate && (this.isLikelyId(candidate) || candidate.length >= 8)) {
566 | 				return candidate;
567 | 			}
568 | 		}
569 | 
570 | 		// 3) Fallback: raw token
571 | 		return raw;
572 | 	}
573 | 
574 | 	/**
575 | 	 * Heuristic to check if a string looks like a brief ID (UUID-like)
576 | 	 */
577 | 	private isLikelyId(value: string): boolean {
578 | 		const uuidRegex =
579 | 			/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
580 | 		const ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/i; // ULID
581 | 		const slugRegex = /^[A-Za-z0-9_-]{16,}$/; // general token
582 | 		return (
583 | 			uuidRegex.test(value) || ulidRegex.test(value) || slugRegex.test(value)
584 | 		);
585 | 	}
586 | 
587 | 	/**
588 | 	 * Set context directly from options
589 | 	 */
590 | 	private async setContext(options: any): Promise<ContextResult> {
591 | 		try {
592 | 			const context: Partial<UserContext> = {};
593 | 
594 | 			if (options.org) {
595 | 				context.orgId = options.org;
596 | 			}
597 | 			if (options.orgName) {
598 | 				context.orgName = options.orgName;
599 | 			}
600 | 			if (options.brief) {
601 | 				context.briefId = options.brief;
602 | 			}
603 | 			if (options.briefName) {
604 | 				context.briefName = options.briefName;
605 | 			}
606 | 
607 | 			if (Object.keys(context).length === 0) {
608 | 				ui.displayWarning('No context options provided');
609 | 				return {
610 | 					success: false,
611 | 					action: 'set',
612 | 					message: 'No context options provided'
613 | 				};
614 | 			}
615 | 
616 | 			await this.authManager.updateContext(context);
617 | 			ui.displaySuccess('Context updated');
618 | 
619 | 			// Display what was set
620 | 			if (context.orgName || context.orgId) {
621 | 				console.log(
622 | 					chalk.gray(`  Organization: ${context.orgName || context.orgId}`)
623 | 				);
624 | 			}
625 | 			if (context.briefName || context.briefId) {
626 | 				console.log(
627 | 					chalk.gray(`  Brief: ${context.briefName || context.briefId}`)
628 | 				);
629 | 			}
630 | 
631 | 			return {
632 | 				success: true,
633 | 				action: 'set',
634 | 				context: this.authManager.getContext() || undefined,
635 | 				message: 'Context updated'
636 | 			};
637 | 		} catch (error) {
638 | 			ui.displayError(`Failed to set context: ${(error as Error).message}`);
639 | 
640 | 			return {
641 | 				success: false,
642 | 				action: 'set',
643 | 				message: `Failed to set context: ${(error as Error).message}`
644 | 			};
645 | 		}
646 | 	}
647 | 
648 | 	/**
649 | 	 * Handle errors
650 | 	 */
651 | 	private handleError(error: any): void {
652 | 		if (error instanceof AuthenticationError) {
653 | 			console.error(chalk.red(`\n✗ ${error.message}`));
654 | 
655 | 			if (error.code === 'NOT_AUTHENTICATED') {
656 | 				ui.displayWarning('Please authenticate first: tm auth login');
657 | 			}
658 | 		} else {
659 | 			const msg = error?.message ?? String(error);
660 | 			console.error(chalk.red(`Error: ${msg}`));
661 | 
662 | 			if (error.stack && process.env.DEBUG) {
663 | 				console.error(chalk.gray(error.stack));
664 | 			}
665 | 		}
666 | 	}
667 | 
668 | 	/**
669 | 	 * Set the last result for programmatic access
670 | 	 */
671 | 	private setLastResult(result: ContextResult): void {
672 | 		this.lastResult = result;
673 | 	}
674 | 
675 | 	/**
676 | 	 * Get the last result (for programmatic usage)
677 | 	 */
678 | 	getLastResult(): ContextResult | undefined {
679 | 		return this.lastResult;
680 | 	}
681 | 
682 | 	/**
683 | 	 * Get current context (for programmatic usage)
684 | 	 */
685 | 	getContext(): UserContext | null {
686 | 		return this.authManager.getContext();
687 | 	}
688 | 
689 | 	/**
690 | 	 * Clean up resources
691 | 	 */
692 | 	async cleanup(): Promise<void> {
693 | 		// No resources to clean up for context command
694 | 	}
695 | 
696 | 	/**
697 | 	 * Static method to register this command on an existing program
698 | 	 */
699 | 	static registerOn(program: Command): Command {
700 | 		const contextCommand = new ContextCommand();
701 | 		program.addCommand(contextCommand);
702 | 		return contextCommand;
703 | 	}
704 | 
705 | 	/**
706 | 	 * Alternative registration that returns the command for chaining
707 | 	 */
708 | 	static register(program: Command, name?: string): ContextCommand {
709 | 		const contextCommand = new ContextCommand(name);
710 | 		program.addCommand(contextCommand);
711 | 		return contextCommand;
712 | 	}
713 | }
714 | 
```

--------------------------------------------------------------------------------
/tests/unit/scripts/modules/task-manager/move-task-cross-tag.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | import { jest } from '@jest/globals';
  2 | 
  3 | // --- Mocks ---
  4 | jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
  5 | 	readJSON: jest.fn(),
  6 | 	writeJSON: jest.fn(),
  7 | 	log: jest.fn(),
  8 | 	setTasksForTag: jest.fn(),
  9 | 	truncate: jest.fn((t) => t),
 10 | 	isSilentMode: jest.fn(() => false),
 11 | 	traverseDependencies: jest.fn((sourceTasks, allTasks, options = {}) => {
 12 | 		// Mock realistic dependency behavior for testing
 13 | 		const { direction = 'forward' } = options;
 14 | 
 15 | 		if (direction === 'forward') {
 16 | 			// For forward dependencies: return tasks that the source tasks depend on
 17 | 			const result = [];
 18 | 			sourceTasks.forEach((task) => {
 19 | 				if (task.dependencies && Array.isArray(task.dependencies)) {
 20 | 					result.push(...task.dependencies);
 21 | 				}
 22 | 			});
 23 | 			return result;
 24 | 		} else if (direction === 'reverse') {
 25 | 			// For reverse dependencies: return tasks that depend on the source tasks
 26 | 			const sourceIds = sourceTasks.map((t) => t.id);
 27 | 			const normalizedSourceIds = sourceIds.map((id) => String(id));
 28 | 			const result = [];
 29 | 			allTasks.forEach((task) => {
 30 | 				if (task.dependencies && Array.isArray(task.dependencies)) {
 31 | 					const hasDependency = task.dependencies.some((depId) =>
 32 | 						normalizedSourceIds.includes(String(depId))
 33 | 					);
 34 | 					if (hasDependency) {
 35 | 						result.push(task.id);
 36 | 					}
 37 | 				}
 38 | 			});
 39 | 			return result;
 40 | 		}
 41 | 		return [];
 42 | 	})
 43 | }));
 44 | 
 45 | jest.unstable_mockModule(
 46 | 	'../../../../../scripts/modules/task-manager/generate-task-files.js',
 47 | 	() => ({
 48 | 		default: jest.fn().mockResolvedValue()
 49 | 	})
 50 | );
 51 | 
 52 | jest.unstable_mockModule(
 53 | 	'../../../../../scripts/modules/task-manager.js',
 54 | 	() => ({
 55 | 		isTaskDependentOn: jest.fn(() => false)
 56 | 	})
 57 | );
 58 | 
 59 | jest.unstable_mockModule(
 60 | 	'../../../../../scripts/modules/dependency-manager.js',
 61 | 	() => ({
 62 | 		validateCrossTagMove: jest.fn(),
 63 | 		findCrossTagDependencies: jest.fn(),
 64 | 		getDependentTaskIds: jest.fn(),
 65 | 		validateSubtaskMove: jest.fn()
 66 | 	})
 67 | );
 68 | 
 69 | const { readJSON, writeJSON, log } = await import(
 70 | 	'../../../../../scripts/modules/utils.js'
 71 | );
 72 | 
 73 | const {
 74 | 	validateCrossTagMove,
 75 | 	findCrossTagDependencies,
 76 | 	getDependentTaskIds,
 77 | 	validateSubtaskMove
 78 | } = await import('../../../../../scripts/modules/dependency-manager.js');
 79 | 
 80 | const { moveTasksBetweenTags, getAllTasksWithTags } = await import(
 81 | 	'../../../../../scripts/modules/task-manager/move-task.js'
 82 | );
 83 | 
 84 | describe('Cross-Tag Task Movement', () => {
 85 | 	let mockRawData;
 86 | 	let mockTasksPath;
 87 | 	let mockContext;
 88 | 
 89 | 	beforeEach(() => {
 90 | 		jest.clearAllMocks();
 91 | 
 92 | 		// Setup mock data
 93 | 		mockRawData = {
 94 | 			backlog: {
 95 | 				tasks: [
 96 | 					{ id: 1, title: 'Task 1', dependencies: [2] },
 97 | 					{ id: 2, title: 'Task 2', dependencies: [] },
 98 | 					{ id: 3, title: 'Task 3', dependencies: [1] }
 99 | 				]
100 | 			},
101 | 			'in-progress': {
102 | 				tasks: [{ id: 4, title: 'Task 4', dependencies: [] }]
103 | 			},
104 | 			done: {
105 | 				tasks: [{ id: 5, title: 'Task 5', dependencies: [4] }]
106 | 			}
107 | 		};
108 | 
109 | 		mockTasksPath = '/test/path/tasks.json';
110 | 		mockContext = { projectRoot: '/test/project' };
111 | 
112 | 		// Mock readJSON to return our test data
113 | 		readJSON.mockImplementation((path, projectRoot, tag) => {
114 | 			return { ...mockRawData[tag], tag, _rawTaggedData: mockRawData };
115 | 		});
116 | 
117 | 		writeJSON.mockResolvedValue();
118 | 		log.mockImplementation(() => {});
119 | 	});
120 | 
121 | 	afterEach(() => {
122 | 		jest.clearAllMocks();
123 | 	});
124 | 
125 | 	describe('getAllTasksWithTags', () => {
126 | 		it('should return all tasks with tag information', () => {
127 | 			const allTasks = getAllTasksWithTags(mockRawData);
128 | 
129 | 			expect(allTasks).toHaveLength(5);
130 | 			expect(allTasks.find((t) => t.id === 1).tag).toBe('backlog');
131 | 			expect(allTasks.find((t) => t.id === 4).tag).toBe('in-progress');
132 | 			expect(allTasks.find((t) => t.id === 5).tag).toBe('done');
133 | 		});
134 | 	});
135 | 
136 | 	describe('validateCrossTagMove', () => {
137 | 		it('should allow move when no dependencies exist', () => {
138 | 			const task = { id: 2, dependencies: [] };
139 | 			const allTasks = getAllTasksWithTags(mockRawData);
140 | 
141 | 			validateCrossTagMove.mockReturnValue({ canMove: true, conflicts: [] });
142 | 			const result = validateCrossTagMove(
143 | 				task,
144 | 				'backlog',
145 | 				'in-progress',
146 | 				allTasks
147 | 			);
148 | 
149 | 			expect(result.canMove).toBe(true);
150 | 			expect(result.conflicts).toHaveLength(0);
151 | 		});
152 | 
153 | 		it('should block move when cross-tag dependencies exist', () => {
154 | 			const task = { id: 1, dependencies: [2] };
155 | 			const allTasks = getAllTasksWithTags(mockRawData);
156 | 
157 | 			validateCrossTagMove.mockReturnValue({
158 | 				canMove: false,
159 | 				conflicts: [{ taskId: 1, dependencyId: 2, dependencyTag: 'backlog' }]
160 | 			});
161 | 			const result = validateCrossTagMove(
162 | 				task,
163 | 				'backlog',
164 | 				'in-progress',
165 | 				allTasks
166 | 			);
167 | 
168 | 			expect(result.canMove).toBe(false);
169 | 			expect(result.conflicts).toHaveLength(1);
170 | 			expect(result.conflicts[0].dependencyId).toBe(2);
171 | 		});
172 | 	});
173 | 
174 | 	describe('findCrossTagDependencies', () => {
175 | 		it('should find cross-tag dependencies for multiple tasks', () => {
176 | 			const sourceTasks = [
177 | 				{ id: 1, dependencies: [2] },
178 | 				{ id: 3, dependencies: [1] }
179 | 			];
180 | 			const allTasks = getAllTasksWithTags(mockRawData);
181 | 
182 | 			findCrossTagDependencies.mockReturnValue([
183 | 				{ taskId: 1, dependencyId: 2, dependencyTag: 'backlog' },
184 | 				{ taskId: 3, dependencyId: 1, dependencyTag: 'backlog' }
185 | 			]);
186 | 			const conflicts = findCrossTagDependencies(
187 | 				sourceTasks,
188 | 				'backlog',
189 | 				'in-progress',
190 | 				allTasks
191 | 			);
192 | 
193 | 			expect(conflicts).toHaveLength(2);
194 | 			expect(
195 | 				conflicts.some((c) => c.taskId === 1 && c.dependencyId === 2)
196 | 			).toBe(true);
197 | 			expect(
198 | 				conflicts.some((c) => c.taskId === 3 && c.dependencyId === 1)
199 | 			).toBe(true);
200 | 		});
201 | 	});
202 | 
203 | 	describe('getDependentTaskIds', () => {
204 | 		it('should return dependent task IDs', () => {
205 | 			const sourceTasks = [{ id: 1, dependencies: [2] }];
206 | 			const crossTagDependencies = [
207 | 				{ taskId: 1, dependencyId: 2, dependencyTag: 'backlog' }
208 | 			];
209 | 			const allTasks = getAllTasksWithTags(mockRawData);
210 | 
211 | 			getDependentTaskIds.mockReturnValue([2]);
212 | 			const dependentTaskIds = getDependentTaskIds(
213 | 				sourceTasks,
214 | 				crossTagDependencies,
215 | 				allTasks
216 | 			);
217 | 
218 | 			expect(dependentTaskIds).toContain(2);
219 | 		});
220 | 	});
221 | 
222 | 	// New test: ensure with-dependencies only traverses tasks from the source tag
223 | 	it('should scope dependency traversal to source tag when using --with-dependencies', async () => {
224 | 		findCrossTagDependencies.mockReturnValue([]);
225 | 		validateSubtaskMove.mockImplementation(() => {});
226 | 
227 | 		const result = await moveTasksBetweenTags(
228 | 			mockTasksPath,
229 | 			[1], // backlog:1 depends on backlog:2
230 | 			'backlog',
231 | 			'in-progress',
232 | 			{ withDependencies: true },
233 | 			mockContext
234 | 		);
235 | 
236 | 		// Write should include backlog:2 moved, and must NOT traverse or fetch dependencies from the target tag
237 | 		expect(writeJSON).toHaveBeenCalledWith(
238 | 			mockTasksPath,
239 | 			expect.objectContaining({
240 | 				'in-progress': expect.objectContaining({
241 | 					tasks: expect.arrayContaining([
242 | 						expect.objectContaining({ id: 1 }),
243 | 						expect.objectContaining({ id: 2 }) // the backlog:2 now moved
244 | 						// ensure existing in-progress:2 remains (by id) but we don't double-add or fetch deps from it
245 | 					])
246 | 				})
247 | 			}),
248 | 			mockContext.projectRoot,
249 | 			null
250 | 		);
251 | 	});
252 | 
253 | 	describe('moveTasksBetweenTags', () => {
254 | 		it('should move tasks without dependencies successfully', async () => {
255 | 			// Mock the dependency functions to return no conflicts
256 | 			findCrossTagDependencies.mockReturnValue([]);
257 | 			validateSubtaskMove.mockImplementation(() => {});
258 | 
259 | 			const result = await moveTasksBetweenTags(
260 | 				mockTasksPath,
261 | 				[2],
262 | 				'backlog',
263 | 				'in-progress',
264 | 				{},
265 | 				mockContext
266 | 			);
267 | 
268 | 			expect(result.message).toContain('Successfully moved 1 tasks');
269 | 			expect(writeJSON).toHaveBeenCalledWith(
270 | 				mockTasksPath,
271 | 				expect.any(Object),
272 | 				mockContext.projectRoot,
273 | 				null
274 | 			);
275 | 		});
276 | 
277 | 		it('should throw error for cross-tag dependencies by default', async () => {
278 | 			const mockDependency = {
279 | 				taskId: 1,
280 | 				dependencyId: 2,
281 | 				dependencyTag: 'backlog'
282 | 			};
283 | 			findCrossTagDependencies.mockReturnValue([mockDependency]);
284 | 			validateSubtaskMove.mockImplementation(() => {});
285 | 
286 | 			await expect(
287 | 				moveTasksBetweenTags(
288 | 					mockTasksPath,
289 | 					[1],
290 | 					'backlog',
291 | 					'in-progress',
292 | 					{},
293 | 					mockContext
294 | 				)
295 | 			).rejects.toThrow(
296 | 				'Cannot move tasks: 1 cross-tag dependency conflicts found'
297 | 			);
298 | 
299 | 			expect(writeJSON).not.toHaveBeenCalled();
300 | 		});
301 | 
302 | 		it('should move with dependencies when --with-dependencies is used', async () => {
303 | 			const mockDependency = {
304 | 				taskId: 1,
305 | 				dependencyId: 2,
306 | 				dependencyTag: 'backlog'
307 | 			};
308 | 			findCrossTagDependencies.mockReturnValue([mockDependency]);
309 | 			getDependentTaskIds.mockReturnValue([2]);
310 | 			validateSubtaskMove.mockImplementation(() => {});
311 | 
312 | 			const result = await moveTasksBetweenTags(
313 | 				mockTasksPath,
314 | 				[1],
315 | 				'backlog',
316 | 				'in-progress',
317 | 				{ withDependencies: true },
318 | 				mockContext
319 | 			);
320 | 
321 | 			expect(result.message).toContain('Successfully moved 2 tasks');
322 | 			expect(writeJSON).toHaveBeenCalledWith(
323 | 				mockTasksPath,
324 | 				expect.objectContaining({
325 | 					backlog: expect.objectContaining({
326 | 						tasks: expect.arrayContaining([
327 | 							expect.objectContaining({
328 | 								id: 3,
329 | 								title: 'Task 3',
330 | 								dependencies: [1]
331 | 							})
332 | 						])
333 | 					}),
334 | 					'in-progress': expect.objectContaining({
335 | 						tasks: expect.arrayContaining([
336 | 							expect.objectContaining({
337 | 								id: 4,
338 | 								title: 'Task 4',
339 | 								dependencies: []
340 | 							}),
341 | 							expect.objectContaining({
342 | 								id: 1,
343 | 								title: 'Task 1',
344 | 								dependencies: [2],
345 | 								metadata: expect.objectContaining({
346 | 									moveHistory: expect.arrayContaining([
347 | 										expect.objectContaining({
348 | 											fromTag: 'backlog',
349 | 											toTag: 'in-progress',
350 | 											timestamp: expect.any(String)
351 | 										})
352 | 									])
353 | 								})
354 | 							}),
355 | 							expect.objectContaining({
356 | 								id: 2,
357 | 								title: 'Task 2',
358 | 								dependencies: [],
359 | 								metadata: expect.objectContaining({
360 | 									moveHistory: expect.arrayContaining([
361 | 										expect.objectContaining({
362 | 											fromTag: 'backlog',
363 | 											toTag: 'in-progress',
364 | 											timestamp: expect.any(String)
365 | 										})
366 | 									])
367 | 								})
368 | 							})
369 | 						])
370 | 					}),
371 | 					done: expect.objectContaining({
372 | 						tasks: expect.arrayContaining([
373 | 							expect.objectContaining({
374 | 								id: 5,
375 | 								title: 'Task 5',
376 | 								dependencies: [4]
377 | 							})
378 | 						])
379 | 					})
380 | 				}),
381 | 				mockContext.projectRoot,
382 | 				null
383 | 			);
384 | 		});
385 | 
386 | 		it('should break dependencies when --ignore-dependencies is used', async () => {
387 | 			const mockDependency = {
388 | 				taskId: 1,
389 | 				dependencyId: 2,
390 | 				dependencyTag: 'backlog'
391 | 			};
392 | 			findCrossTagDependencies.mockReturnValue([mockDependency]);
393 | 			validateSubtaskMove.mockImplementation(() => {});
394 | 
395 | 			const result = await moveTasksBetweenTags(
396 | 				mockTasksPath,
397 | 				[2],
398 | 				'backlog',
399 | 				'in-progress',
400 | 				{ ignoreDependencies: true },
401 | 				mockContext
402 | 			);
403 | 
404 | 			expect(result.message).toContain('Successfully moved 1 tasks');
405 | 			expect(writeJSON).toHaveBeenCalledWith(
406 | 				mockTasksPath,
407 | 				expect.objectContaining({
408 | 					backlog: expect.objectContaining({
409 | 						tasks: expect.arrayContaining([
410 | 							expect.objectContaining({
411 | 								id: 1,
412 | 								title: 'Task 1',
413 | 								dependencies: [2] // Dependencies not actually removed in current implementation
414 | 							}),
415 | 							expect.objectContaining({
416 | 								id: 3,
417 | 								title: 'Task 3',
418 | 								dependencies: [1]
419 | 							})
420 | 						])
421 | 					}),
422 | 					'in-progress': expect.objectContaining({
423 | 						tasks: expect.arrayContaining([
424 | 							expect.objectContaining({
425 | 								id: 4,
426 | 								title: 'Task 4',
427 | 								dependencies: []
428 | 							}),
429 | 							expect.objectContaining({
430 | 								id: 2,
431 | 								title: 'Task 2',
432 | 								dependencies: [],
433 | 								metadata: expect.objectContaining({
434 | 									moveHistory: expect.arrayContaining([
435 | 										expect.objectContaining({
436 | 											fromTag: 'backlog',
437 | 											toTag: 'in-progress',
438 | 											timestamp: expect.any(String)
439 | 										})
440 | 									])
441 | 								})
442 | 							})
443 | 						])
444 | 					}),
445 | 					done: expect.objectContaining({
446 | 						tasks: expect.arrayContaining([
447 | 							expect.objectContaining({
448 | 								id: 5,
449 | 								title: 'Task 5',
450 | 								dependencies: [4]
451 | 							})
452 | 						])
453 | 					})
454 | 				}),
455 | 				mockContext.projectRoot,
456 | 				null
457 | 			);
458 | 		});
459 | 
460 | 		it('should create target tag if it does not exist', async () => {
461 | 			findCrossTagDependencies.mockReturnValue([]);
462 | 			validateSubtaskMove.mockImplementation(() => {});
463 | 
464 | 			const result = await moveTasksBetweenTags(
465 | 				mockTasksPath,
466 | 				[2],
467 | 				'backlog',
468 | 				'new-tag',
469 | 				{},
470 | 				mockContext
471 | 			);
472 | 
473 | 			expect(result.message).toContain('Successfully moved 1 tasks');
474 | 			expect(result.message).toContain('new-tag');
475 | 			expect(writeJSON).toHaveBeenCalledWith(
476 | 				mockTasksPath,
477 | 				expect.objectContaining({
478 | 					backlog: expect.objectContaining({
479 | 						tasks: expect.arrayContaining([
480 | 							expect.objectContaining({
481 | 								id: 1,
482 | 								title: 'Task 1',
483 | 								dependencies: [2]
484 | 							}),
485 | 							expect.objectContaining({
486 | 								id: 3,
487 | 								title: 'Task 3',
488 | 								dependencies: [1]
489 | 							})
490 | 						])
491 | 					}),
492 | 					'new-tag': expect.objectContaining({
493 | 						tasks: expect.arrayContaining([
494 | 							expect.objectContaining({
495 | 								id: 2,
496 | 								title: 'Task 2',
497 | 								dependencies: [],
498 | 								metadata: expect.objectContaining({
499 | 									moveHistory: expect.arrayContaining([
500 | 										expect.objectContaining({
501 | 											fromTag: 'backlog',
502 | 											toTag: 'new-tag',
503 | 											timestamp: expect.any(String)
504 | 										})
505 | 									])
506 | 								})
507 | 							})
508 | 						])
509 | 					}),
510 | 					'in-progress': expect.objectContaining({
511 | 						tasks: expect.arrayContaining([
512 | 							expect.objectContaining({
513 | 								id: 4,
514 | 								title: 'Task 4',
515 | 								dependencies: []
516 | 							})
517 | 						])
518 | 					}),
519 | 					done: expect.objectContaining({
520 | 						tasks: expect.arrayContaining([
521 | 							expect.objectContaining({
522 | 								id: 5,
523 | 								title: 'Task 5',
524 | 								dependencies: [4]
525 | 							})
526 | 						])
527 | 					})
528 | 				}),
529 | 				mockContext.projectRoot,
530 | 				null
531 | 			);
532 | 		});
533 | 
534 | 		it('should throw error for subtask movement', async () => {
535 | 			const subtaskError = 'Cannot move subtask 1.2 directly between tags';
536 | 			validateSubtaskMove.mockImplementation(() => {
537 | 				throw new Error(subtaskError);
538 | 			});
539 | 
540 | 			await expect(
541 | 				moveTasksBetweenTags(
542 | 					mockTasksPath,
543 | 					['1.2'],
544 | 					'backlog',
545 | 					'in-progress',
546 | 					{},
547 | 					mockContext
548 | 				)
549 | 			).rejects.toThrow(subtaskError);
550 | 
551 | 			expect(writeJSON).not.toHaveBeenCalled();
552 | 		});
553 | 
554 | 		it('should throw error for invalid task IDs', async () => {
555 | 			findCrossTagDependencies.mockReturnValue([]);
556 | 			validateSubtaskMove.mockImplementation(() => {});
557 | 
558 | 			await expect(
559 | 				moveTasksBetweenTags(
560 | 					mockTasksPath,
561 | 					[999], // Non-existent task
562 | 					'backlog',
563 | 					'in-progress',
564 | 					{},
565 | 					mockContext
566 | 				)
567 | 			).rejects.toThrow('Task 999 not found in source tag "backlog"');
568 | 
569 | 			expect(writeJSON).not.toHaveBeenCalled();
570 | 		});
571 | 
572 | 		it('should throw error for invalid source tag', async () => {
573 | 			findCrossTagDependencies.mockReturnValue([]);
574 | 			validateSubtaskMove.mockImplementation(() => {});
575 | 
576 | 			await expect(
577 | 				moveTasksBetweenTags(
578 | 					mockTasksPath,
579 | 					[1],
580 | 					'non-existent-tag',
581 | 					'in-progress',
582 | 					{},
583 | 					mockContext
584 | 				)
585 | 			).rejects.toThrow('Source tag "non-existent-tag" not found or invalid');
586 | 
587 | 			expect(writeJSON).not.toHaveBeenCalled();
588 | 		});
589 | 
590 | 		it('should handle string dependencies correctly during cross-tag move', async () => {
591 | 			// Setup mock data with string dependencies
592 | 			mockRawData = {
593 | 				backlog: {
594 | 					tasks: [
595 | 						{ id: 1, title: 'Task 1', dependencies: ['2'] }, // String dependency
596 | 						{ id: 2, title: 'Task 2', dependencies: [] },
597 | 						{ id: 3, title: 'Task 3', dependencies: ['1'] } // String dependency
598 | 					]
599 | 				},
600 | 				'in-progress': {
601 | 					tasks: [{ id: 4, title: 'Task 4', dependencies: [] }]
602 | 				}
603 | 			};
604 | 
605 | 			// Mock readJSON to return our test data
606 | 			readJSON.mockImplementation((path, projectRoot, tag) => {
607 | 				return { ...mockRawData[tag], tag, _rawTaggedData: mockRawData };
608 | 			});
609 | 
610 | 			findCrossTagDependencies.mockReturnValue([]);
611 | 			validateSubtaskMove.mockImplementation(() => {});
612 | 
613 | 			const result = await moveTasksBetweenTags(
614 | 				mockTasksPath,
615 | 				['1'], // String task ID
616 | 				'backlog',
617 | 				'in-progress',
618 | 				{},
619 | 				mockContext
620 | 			);
621 | 
622 | 			expect(result.message).toContain('Successfully moved 1 tasks');
623 | 			expect(writeJSON).toHaveBeenCalledWith(
624 | 				mockTasksPath,
625 | 				expect.objectContaining({
626 | 					backlog: expect.objectContaining({
627 | 						tasks: expect.arrayContaining([
628 | 							expect.objectContaining({
629 | 								id: 2,
630 | 								title: 'Task 2',
631 | 								dependencies: []
632 | 							}),
633 | 							expect.objectContaining({
634 | 								id: 3,
635 | 								title: 'Task 3',
636 | 								dependencies: ['1'] // Should remain as string
637 | 							})
638 | 						])
639 | 					}),
640 | 					'in-progress': expect.objectContaining({
641 | 						tasks: expect.arrayContaining([
642 | 							expect.objectContaining({
643 | 								id: 1,
644 | 								title: 'Task 1',
645 | 								dependencies: ['2'], // Should remain as string
646 | 								metadata: expect.objectContaining({
647 | 									moveHistory: expect.arrayContaining([
648 | 										expect.objectContaining({
649 | 											fromTag: 'backlog',
650 | 											toTag: 'in-progress',
651 | 											timestamp: expect.any(String)
652 | 										})
653 | 									])
654 | 								})
655 | 							})
656 | 						])
657 | 					})
658 | 				}),
659 | 				mockContext.projectRoot,
660 | 				null
661 | 			);
662 | 		});
663 | 	});
664 | });
665 | 
```

--------------------------------------------------------------------------------
/tests/integration/move-task-simple.integration.test.js:
--------------------------------------------------------------------------------

```javascript
  1 | import { jest } from '@jest/globals';
  2 | import path from 'path';
  3 | import mockFs from 'mock-fs';
  4 | import fs from 'fs';
  5 | import { fileURLToPath } from 'url';
  6 | 
  7 | // Import the actual move task functionality
  8 | import moveTask, {
  9 | 	moveTasksBetweenTags
 10 | } from '../../scripts/modules/task-manager/move-task.js';
 11 | import { readJSON, writeJSON } from '../../scripts/modules/utils.js';
 12 | 
 13 | // Mock console to avoid conflicts with mock-fs
 14 | const originalConsole = { ...console };
 15 | beforeAll(() => {
 16 | 	global.console = {
 17 | 		...console,
 18 | 		log: jest.fn(),
 19 | 		error: jest.fn(),
 20 | 		warn: jest.fn(),
 21 | 		info: jest.fn()
 22 | 	};
 23 | });
 24 | 
 25 | afterAll(() => {
 26 | 	global.console = originalConsole;
 27 | });
 28 | 
 29 | // Get __dirname equivalent for ES modules
 30 | const __filename = fileURLToPath(import.meta.url);
 31 | const __dirname = path.dirname(__filename);
 32 | 
 33 | describe('Cross-Tag Task Movement Simple Integration Tests', () => {
 34 | 	const testDataDir = path.join(__dirname, 'fixtures');
 35 | 	const testTasksPath = path.join(testDataDir, 'tasks.json');
 36 | 
 37 | 	// Test data structure with proper tagged format
 38 | 	const testData = {
 39 | 		backlog: {
 40 | 			tasks: [
 41 | 				{ id: 1, title: 'Task 1', dependencies: [], status: 'pending' },
 42 | 				{ id: 2, title: 'Task 2', dependencies: [], status: 'pending' }
 43 | 			]
 44 | 		},
 45 | 		'in-progress': {
 46 | 			tasks: [
 47 | 				{ id: 3, title: 'Task 3', dependencies: [], status: 'in-progress' }
 48 | 			]
 49 | 		}
 50 | 	};
 51 | 
 52 | 	beforeEach(() => {
 53 | 		// Set up mock file system with test data
 54 | 		mockFs({
 55 | 			[testDataDir]: {
 56 | 				'tasks.json': JSON.stringify(testData, null, 2)
 57 | 			}
 58 | 		});
 59 | 	});
 60 | 
 61 | 	afterEach(() => {
 62 | 		// Clean up mock file system
 63 | 		mockFs.restore();
 64 | 	});
 65 | 
 66 | 	describe('Real Module Integration Tests', () => {
 67 | 		it('should move task within same tag using actual moveTask function', async () => {
 68 | 			// Test moving Task 1 from position 1 to position 5 within backlog tag
 69 | 			const result = await moveTask(
 70 | 				testTasksPath,
 71 | 				'1',
 72 | 				'5',
 73 | 				false, // Don't generate files for this test
 74 | 				{ tag: 'backlog' }
 75 | 			);
 76 | 
 77 | 			// Verify the move operation was successful
 78 | 			expect(result).toBeDefined();
 79 | 			expect(result.message).toContain('Moved task 1 to new ID 5');
 80 | 
 81 | 			// Read the updated data to verify the move actually happened
 82 | 			const updatedData = readJSON(testTasksPath, null, 'backlog');
 83 | 			const rawData = updatedData._rawTaggedData || updatedData;
 84 | 			const backlogTasks = rawData.backlog.tasks;
 85 | 
 86 | 			// Verify Task 1 is no longer at position 1
 87 | 			const taskAtPosition1 = backlogTasks.find((t) => t.id === 1);
 88 | 			expect(taskAtPosition1).toBeUndefined();
 89 | 
 90 | 			// Verify Task 1 is now at position 5
 91 | 			const taskAtPosition5 = backlogTasks.find((t) => t.id === 5);
 92 | 			expect(taskAtPosition5).toBeDefined();
 93 | 			expect(taskAtPosition5.title).toBe('Task 1');
 94 | 			expect(taskAtPosition5.status).toBe('pending');
 95 | 		});
 96 | 
 97 | 		it('should move tasks between tags using moveTasksBetweenTags function', async () => {
 98 | 			// Test moving Task 1 from backlog to in-progress tag
 99 | 			const result = await moveTasksBetweenTags(
100 | 				testTasksPath,
101 | 				['1'], // Task IDs to move (as strings)
102 | 				'backlog', // Source tag
103 | 				'in-progress', // Target tag
104 | 				{ withDependencies: false, ignoreDependencies: false },
105 | 				{ projectRoot: testDataDir }
106 | 			);
107 | 
108 | 			// Verify the cross-tag move operation was successful
109 | 			expect(result).toBeDefined();
110 | 			expect(result.message).toContain(
111 | 				'Successfully moved 1 tasks from "backlog" to "in-progress"'
112 | 			);
113 | 			expect(result.movedTasks).toHaveLength(1);
114 | 			expect(result.movedTasks[0].id).toBe('1');
115 | 			expect(result.movedTasks[0].fromTag).toBe('backlog');
116 | 			expect(result.movedTasks[0].toTag).toBe('in-progress');
117 | 
118 | 			// Read the updated data to verify the move actually happened
119 | 			const updatedData = readJSON(testTasksPath, null, 'backlog');
120 | 			// readJSON returns resolved data, so we need to access the raw tagged data
121 | 			const rawData = updatedData._rawTaggedData || updatedData;
122 | 			const backlogTasks = rawData.backlog?.tasks || [];
123 | 			const inProgressTasks = rawData['in-progress']?.tasks || [];
124 | 
125 | 			// Verify Task 1 is no longer in backlog
126 | 			const taskInBacklog = backlogTasks.find((t) => t.id === 1);
127 | 			expect(taskInBacklog).toBeUndefined();
128 | 
129 | 			// Verify Task 1 is now in in-progress
130 | 			const taskInProgress = inProgressTasks.find((t) => t.id === 1);
131 | 			expect(taskInProgress).toBeDefined();
132 | 			expect(taskInProgress.title).toBe('Task 1');
133 | 			expect(taskInProgress.status).toBe('pending');
134 | 		});
135 | 
136 | 		it('should handle subtask movement restrictions', async () => {
137 | 			// Create data with subtasks
138 | 			const dataWithSubtasks = {
139 | 				backlog: {
140 | 					tasks: [
141 | 						{
142 | 							id: 1,
143 | 							title: 'Task 1',
144 | 							dependencies: [],
145 | 							status: 'pending',
146 | 							subtasks: [
147 | 								{ id: '1.1', title: 'Subtask 1.1', status: 'pending' },
148 | 								{ id: '1.2', title: 'Subtask 1.2', status: 'pending' }
149 | 							]
150 | 						}
151 | 					]
152 | 				},
153 | 				'in-progress': {
154 | 					tasks: [
155 | 						{ id: 2, title: 'Task 2', dependencies: [], status: 'in-progress' }
156 | 					]
157 | 				}
158 | 			};
159 | 
160 | 			// Write subtask data to mock file system
161 | 			mockFs({
162 | 				[testDataDir]: {
163 | 					'tasks.json': JSON.stringify(dataWithSubtasks, null, 2)
164 | 				}
165 | 			});
166 | 
167 | 			// Try to move a subtask directly - this should actually work (converts subtask to task)
168 | 			const result = await moveTask(
169 | 				testTasksPath,
170 | 				'1.1', // Subtask ID
171 | 				'5', // New task ID
172 | 				false,
173 | 				{ tag: 'backlog' }
174 | 			);
175 | 
176 | 			// Verify the subtask was converted to a task
177 | 			expect(result).toBeDefined();
178 | 			expect(result.message).toContain('Converted subtask 1.1 to task 5');
179 | 
180 | 			// Verify the subtask was removed from the parent and converted to a standalone task
181 | 			const updatedData = readJSON(testTasksPath, null, 'backlog');
182 | 			const rawData = updatedData._rawTaggedData || updatedData;
183 | 			const task1 = rawData.backlog?.tasks?.find((t) => t.id === 1);
184 | 			const newTask5 = rawData.backlog?.tasks?.find((t) => t.id === 5);
185 | 
186 | 			expect(task1).toBeDefined();
187 | 			expect(task1.subtasks).toHaveLength(1); // Only 1.2 remains
188 | 			expect(task1.subtasks[0].id).toBe(2);
189 | 
190 | 			expect(newTask5).toBeDefined();
191 | 			expect(newTask5.title).toBe('Subtask 1.1');
192 | 			expect(newTask5.status).toBe('pending');
193 | 		});
194 | 
195 | 		it('should handle missing source tag errors', async () => {
196 | 			// Try to move from a non-existent tag
197 | 			await expect(
198 | 				moveTasksBetweenTags(
199 | 					testTasksPath,
200 | 					['1'],
201 | 					'non-existent-tag', // Source tag doesn't exist
202 | 					'in-progress',
203 | 					{ withDependencies: false, ignoreDependencies: false },
204 | 					{ projectRoot: testDataDir }
205 | 				)
206 | 			).rejects.toThrow();
207 | 		});
208 | 
209 | 		it('should handle missing task ID errors', async () => {
210 | 			// Try to move a non-existent task
211 | 			await expect(
212 | 				moveTask(
213 | 					testTasksPath,
214 | 					'999', // Non-existent task ID
215 | 					'5',
216 | 					false,
217 | 					{ tag: 'backlog' }
218 | 				)
219 | 			).rejects.toThrow();
220 | 		});
221 | 
222 | 		it('should handle ignoreDependencies option correctly', async () => {
223 | 			// Create data with dependencies
224 | 			const dataWithDependencies = {
225 | 				backlog: {
226 | 					tasks: [
227 | 						{ id: 1, title: 'Task 1', dependencies: [2], status: 'pending' },
228 | 						{ id: 2, title: 'Task 2', dependencies: [], status: 'pending' }
229 | 					]
230 | 				},
231 | 				'in-progress': {
232 | 					tasks: [
233 | 						{ id: 3, title: 'Task 3', dependencies: [], status: 'in-progress' }
234 | 					]
235 | 				}
236 | 			};
237 | 
238 | 			// Write dependency data to mock file system
239 | 			mockFs({
240 | 				[testDataDir]: {
241 | 					'tasks.json': JSON.stringify(dataWithDependencies, null, 2)
242 | 				}
243 | 			});
244 | 
245 | 			// Move Task 1 while ignoring its dependencies
246 | 			const result = await moveTasksBetweenTags(
247 | 				testTasksPath,
248 | 				['1'], // Only Task 1
249 | 				'backlog',
250 | 				'in-progress',
251 | 				{ withDependencies: false, ignoreDependencies: true },
252 | 				{ projectRoot: testDataDir }
253 | 			);
254 | 
255 | 			expect(result).toBeDefined();
256 | 			expect(result.movedTasks).toHaveLength(1);
257 | 
258 | 			// Verify Task 1 moved but Task 2 stayed
259 | 			const updatedData = readJSON(testTasksPath, null, 'backlog');
260 | 			const rawData = updatedData._rawTaggedData || updatedData;
261 | 			expect(rawData.backlog.tasks).toHaveLength(1); // Task 2 remains
262 | 			expect(rawData['in-progress'].tasks).toHaveLength(2); // Task 3 + Task 1
263 | 
264 | 			// Verify Task 1 has no dependencies (they were ignored)
265 | 			const movedTask = rawData['in-progress'].tasks.find((t) => t.id === 1);
266 | 			expect(movedTask.dependencies).toEqual([]);
267 | 		});
268 | 	});
269 | 
270 | 	describe('Complex Dependency Scenarios', () => {
271 | 		beforeAll(() => {
272 | 			// Document the mock-fs limitation for complex dependency scenarios
273 | 			console.warn(
274 | 				'⚠️  Complex dependency tests are skipped due to mock-fs limitations. ' +
275 | 					'These tests require real filesystem operations for proper dependency resolution. ' +
276 | 					'Consider using real temporary filesystem setup for these scenarios.'
277 | 			);
278 | 		});
279 | 
280 | 		it.skip('should handle dependency conflicts during cross-tag moves', async () => {
281 | 			// For now, skip this test as the mock setup is not working correctly
282 | 			// TODO: Fix mock-fs setup for complex dependency scenarios
283 | 		});
284 | 
285 | 		it.skip('should handle withDependencies option correctly', async () => {
286 | 			// For now, skip this test as the mock setup is not working correctly
287 | 			// TODO: Fix mock-fs setup for complex dependency scenarios
288 | 		});
289 | 	});
290 | 
291 | 	describe('Complex Dependency Integration Tests with Mock-fs', () => {
292 | 		const complexTestData = {
293 | 			backlog: {
294 | 				tasks: [
295 | 					{ id: 1, title: 'Task 1', dependencies: [2, 3], status: 'pending' },
296 | 					{ id: 2, title: 'Task 2', dependencies: [4], status: 'pending' },
297 | 					{ id: 3, title: 'Task 3', dependencies: [], status: 'pending' },
298 | 					{ id: 4, title: 'Task 4', dependencies: [], status: 'pending' }
299 | 				]
300 | 			},
301 | 			'in-progress': {
302 | 				tasks: [
303 | 					{ id: 5, title: 'Task 5', dependencies: [], status: 'in-progress' }
304 | 				]
305 | 			}
306 | 		};
307 | 
308 | 		beforeEach(() => {
309 | 			// Set up mock file system with complex dependency data
310 | 			mockFs({
311 | 				[testDataDir]: {
312 | 					'tasks.json': JSON.stringify(complexTestData, null, 2)
313 | 				}
314 | 			});
315 | 		});
316 | 
317 | 		afterEach(() => {
318 | 			// Clean up mock file system
319 | 			mockFs.restore();
320 | 		});
321 | 
322 | 		it('should handle dependency conflicts during cross-tag moves using actual move functions', async () => {
323 | 			// Test moving Task 1 which has dependencies on Tasks 2 and 3
324 | 			// This should fail because Task 1 depends on Tasks 2 and 3 which are in the same tag
325 | 			await expect(
326 | 				moveTasksBetweenTags(
327 | 					testTasksPath,
328 | 					['1'], // Task 1 with dependencies
329 | 					'backlog',
330 | 					'in-progress',
331 | 					{ withDependencies: false, ignoreDependencies: false },
332 | 					{ projectRoot: testDataDir }
333 | 				)
334 | 			).rejects.toThrow(
335 | 				'Cannot move tasks: 2 cross-tag dependency conflicts found'
336 | 			);
337 | 		});
338 | 
339 | 		it('should handle withDependencies option correctly using actual move functions', async () => {
340 | 			// Test moving Task 1 with its dependencies (Tasks 2 and 3)
341 | 			// Task 2 also depends on Task 4, so all 4 tasks should move
342 | 			const result = await moveTasksBetweenTags(
343 | 				testTasksPath,
344 | 				['1'], // Task 1
345 | 				'backlog',
346 | 				'in-progress',
347 | 				{ withDependencies: true, ignoreDependencies: false },
348 | 				{ projectRoot: testDataDir }
349 | 			);
350 | 
351 | 			// Verify the move operation was successful
352 | 			expect(result).toBeDefined();
353 | 			expect(result.message).toContain(
354 | 				'Successfully moved 4 tasks from "backlog" to "in-progress"'
355 | 			);
356 | 			expect(result.movedTasks).toHaveLength(4); // Task 1 + Tasks 2, 3, 4
357 | 
358 | 			// Read the updated data to verify all dependent tasks moved
359 | 			const updatedData = readJSON(testTasksPath, null, 'backlog');
360 | 			const rawData = updatedData._rawTaggedData || updatedData;
361 | 
362 | 			// Verify all tasks moved from backlog
363 | 			expect(rawData.backlog?.tasks || []).toHaveLength(0); // All tasks moved
364 | 
365 | 			// Verify all tasks are now in in-progress
366 | 			expect(rawData['in-progress']?.tasks || []).toHaveLength(5); // Task 5 + Tasks 1, 2, 3, 4
367 | 
368 | 			// Verify dependency relationships are preserved
369 | 			const task1 = rawData['in-progress']?.tasks?.find((t) => t.id === 1);
370 | 			const task2 = rawData['in-progress']?.tasks?.find((t) => t.id === 2);
371 | 			const task3 = rawData['in-progress']?.tasks?.find((t) => t.id === 3);
372 | 			const task4 = rawData['in-progress']?.tasks?.find((t) => t.id === 4);
373 | 
374 | 			expect(task1?.dependencies).toEqual([2, 3]);
375 | 			expect(task2?.dependencies).toEqual([4]);
376 | 			expect(task3?.dependencies).toEqual([]);
377 | 			expect(task4?.dependencies).toEqual([]);
378 | 		});
379 | 
380 | 		it('should handle circular dependency detection using actual move functions', async () => {
381 | 			// Create data with circular dependencies
382 | 			const circularData = {
383 | 				backlog: {
384 | 					tasks: [
385 | 						{ id: 1, title: 'Task 1', dependencies: [2], status: 'pending' },
386 | 						{ id: 2, title: 'Task 2', dependencies: [3], status: 'pending' },
387 | 						{ id: 3, title: 'Task 3', dependencies: [1], status: 'pending' } // Circular dependency
388 | 					]
389 | 				},
390 | 				'in-progress': {
391 | 					tasks: [
392 | 						{ id: 4, title: 'Task 4', dependencies: [], status: 'in-progress' }
393 | 					]
394 | 				}
395 | 			};
396 | 
397 | 			// Set up mock file system with circular dependency data
398 | 			mockFs({
399 | 				[testDataDir]: {
400 | 					'tasks.json': JSON.stringify(circularData, null, 2)
401 | 				}
402 | 			});
403 | 
404 | 			// Attempt to move Task 1 with dependencies should fail due to circular dependency
405 | 			await expect(
406 | 				moveTasksBetweenTags(
407 | 					testTasksPath,
408 | 					['1'],
409 | 					'backlog',
410 | 					'in-progress',
411 | 					{ withDependencies: true, ignoreDependencies: false },
412 | 					{ projectRoot: testDataDir }
413 | 				)
414 | 			).rejects.toThrow();
415 | 		});
416 | 
417 | 		it('should handle nested dependency chains using actual move functions', async () => {
418 | 			// Create data with nested dependency chains
419 | 			const nestedData = {
420 | 				backlog: {
421 | 					tasks: [
422 | 						{ id: 1, title: 'Task 1', dependencies: [2], status: 'pending' },
423 | 						{ id: 2, title: 'Task 2', dependencies: [3], status: 'pending' },
424 | 						{ id: 3, title: 'Task 3', dependencies: [4], status: 'pending' },
425 | 						{ id: 4, title: 'Task 4', dependencies: [], status: 'pending' }
426 | 					]
427 | 				},
428 | 				'in-progress': {
429 | 					tasks: [
430 | 						{ id: 5, title: 'Task 5', dependencies: [], status: 'in-progress' }
431 | 					]
432 | 				}
433 | 			};
434 | 
435 | 			// Set up mock file system with nested dependency data
436 | 			mockFs({
437 | 				[testDataDir]: {
438 | 					'tasks.json': JSON.stringify(nestedData, null, 2)
439 | 				}
440 | 			});
441 | 
442 | 			// Test moving Task 1 with all its nested dependencies
443 | 			const result = await moveTasksBetweenTags(
444 | 				testTasksPath,
445 | 				['1'], // Task 1
446 | 				'backlog',
447 | 				'in-progress',
448 | 				{ withDependencies: true, ignoreDependencies: false },
449 | 				{ projectRoot: testDataDir }
450 | 			);
451 | 
452 | 			// Verify the move operation was successful
453 | 			expect(result).toBeDefined();
454 | 			expect(result.message).toContain(
455 | 				'Successfully moved 4 tasks from "backlog" to "in-progress"'
456 | 			);
457 | 			expect(result.movedTasks).toHaveLength(4); // Tasks 1, 2, 3, 4
458 | 
459 | 			// Read the updated data to verify all tasks moved
460 | 			const updatedData = readJSON(testTasksPath, null, 'backlog');
461 | 			const rawData = updatedData._rawTaggedData || updatedData;
462 | 
463 | 			// Verify all tasks moved from backlog
464 | 			expect(rawData.backlog?.tasks || []).toHaveLength(0); // All tasks moved
465 | 
466 | 			// Verify all tasks are now in in-progress
467 | 			expect(rawData['in-progress']?.tasks || []).toHaveLength(5); // Task 5 + Tasks 1, 2, 3, 4
468 | 
469 | 			// Verify dependency relationships are preserved
470 | 			const task1 = rawData['in-progress']?.tasks?.find((t) => t.id === 1);
471 | 			const task2 = rawData['in-progress']?.tasks?.find((t) => t.id === 2);
472 | 			const task3 = rawData['in-progress']?.tasks?.find((t) => t.id === 3);
473 | 			const task4 = rawData['in-progress']?.tasks?.find((t) => t.id === 4);
474 | 
475 | 			expect(task1?.dependencies).toEqual([2]);
476 | 			expect(task2?.dependencies).toEqual([3]);
477 | 			expect(task3?.dependencies).toEqual([4]);
478 | 			expect(task4?.dependencies).toEqual([]);
479 | 		});
480 | 
481 | 		it('should handle cross-tag dependency resolution using actual move functions', async () => {
482 | 			// Create data with cross-tag dependencies
483 | 			const crossTagData = {
484 | 				backlog: {
485 | 					tasks: [
486 | 						{ id: 1, title: 'Task 1', dependencies: [5], status: 'pending' }, // Depends on task in in-progress
487 | 						{ id: 2, title: 'Task 2', dependencies: [], status: 'pending' }
488 | 					]
489 | 				},
490 | 				'in-progress': {
491 | 					tasks: [
492 | 						{ id: 5, title: 'Task 5', dependencies: [], status: 'in-progress' }
493 | 					]
494 | 				}
495 | 			};
496 | 
497 | 			// Set up mock file system with cross-tag dependency data
498 | 			mockFs({
499 | 				[testDataDir]: {
500 | 					'tasks.json': JSON.stringify(crossTagData, null, 2)
501 | 				}
502 | 			});
503 | 
504 | 			// Test moving Task 1 which depends on Task 5 in another tag
505 | 			const result = await moveTasksBetweenTags(
506 | 				testTasksPath,
507 | 				['1'], // Task 1
508 | 				'backlog',
509 | 				'in-progress',
510 | 				{ withDependencies: false, ignoreDependencies: false },
511 | 				{ projectRoot: testDataDir }
512 | 			);
513 | 
514 | 			// Verify the move operation was successful
515 | 			expect(result).toBeDefined();
516 | 			expect(result.message).toContain(
517 | 				'Successfully moved 1 tasks from "backlog" to "in-progress"'
518 | 			);
519 | 
520 | 			// Read the updated data to verify the move actually happened
521 | 			const updatedData = readJSON(testTasksPath, null, 'backlog');
522 | 			const rawData = updatedData._rawTaggedData || updatedData;
523 | 
524 | 			// Verify Task 1 is no longer in backlog
525 | 			const taskInBacklog = rawData.backlog?.tasks?.find((t) => t.id === 1);
526 | 			expect(taskInBacklog).toBeUndefined();
527 | 
528 | 			// Verify Task 1 is now in in-progress with its dependency preserved
529 | 			const taskInProgress = rawData['in-progress']?.tasks?.find(
530 | 				(t) => t.id === 1
531 | 			);
532 | 			expect(taskInProgress).toBeDefined();
533 | 			expect(taskInProgress.title).toBe('Task 1');
534 | 			expect(taskInProgress.dependencies).toEqual([5]); // Cross-tag dependency preserved
535 | 		});
536 | 	});
537 | });
538 | 
```

--------------------------------------------------------------------------------
/apps/extension/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Change Log
  2 | 
  3 | ## 0.25.4
  4 | 
  5 | ### Patch Changes
  6 | 
  7 | - Updated dependencies [[`af53525`](https://github.com/eyaltoledano/claude-task-master/commit/af53525cbc660a595b67d4bb90d906911c71f45d)]:
  8 |   - [email protected]
  9 | 
 10 | ## 0.25.3
 11 | 
 12 | ### Patch Changes
 13 | 
 14 | - Updated dependencies [[`044a7bf`](https://github.com/eyaltoledano/claude-task-master/commit/044a7bfc98049298177bc655cf341d7a8b6a0011)]:
 15 |   - [email protected]
 16 | 
 17 | ## 0.25.2
 18 | 
 19 | ### Patch Changes
 20 | 
 21 | - Updated dependencies [[`f487736`](https://github.com/eyaltoledano/claude-task-master/commit/f487736670ef8c484059f676293777eabb249c9e), [`c911608`](https://github.com/eyaltoledano/claude-task-master/commit/c911608f60454253f4e024b57ca84e5a5a53f65c), [`1a18794`](https://github.com/eyaltoledano/claude-task-master/commit/1a1879483b86c118a4e46c02cbf4acebfcf6bcf9)]:
 22 |   - [email protected]
 23 | 
 24 | ## 0.25.2-rc.1
 25 | 
 26 | ### Patch Changes
 27 | 
 28 | - Updated dependencies [[`1a18794`](https://github.com/eyaltoledano/claude-task-master/commit/1a1879483b86c118a4e46c02cbf4acebfcf6bcf9)]:
 29 |   - [email protected]
 30 | 
 31 | ## 0.25.2-rc.0
 32 | 
 33 | ### Patch Changes
 34 | 
 35 | - Updated dependencies [[`f487736`](https://github.com/eyaltoledano/claude-task-master/commit/f487736670ef8c484059f676293777eabb249c9e)]:
 36 |   - [email protected]
 37 | 
 38 | ## 0.25.0
 39 | 
 40 | ### Minor Changes
 41 | 
 42 | - [#1200](https://github.com/eyaltoledano/claude-task-master/pull/1200) [`fce8414`](https://github.com/eyaltoledano/claude-task-master/commit/fce841490a9ebbf1801a42dd8a29397379cf1142) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Add "Start Task" button to VS Code extension for seamless Claude Code integration
 43 | 
 44 |   You can now click a "Start Task" button directly in the Task Master extension which will open a new terminal and automatically execute the task using Claude Code. This provides a seamless workflow from viewing tasks in the extension to implementing them without leaving VS Code.
 45 | 
 46 | - [#1201](https://github.com/eyaltoledano/claude-task-master/pull/1201) [`83af314`](https://github.com/eyaltoledano/claude-task-master/commit/83af314879fc0e563581161c60d2bd089899313e) Thanks [@losolosol](https://github.com/losolosol)! - Added a Start Build button to the VSCODE Task Properties Right Panel
 47 | 
 48 | ### Patch Changes
 49 | 
 50 | - [#1229](https://github.com/eyaltoledano/claude-task-master/pull/1229) [`674d1f6`](https://github.com/eyaltoledano/claude-task-master/commit/674d1f6de7ea98116b61bdae6198bafe6c4e7c1a) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix MCP not connecting to new Taskmaster version
 51 | 
 52 | - Updated dependencies [[`4e12643`](https://github.com/eyaltoledano/claude-task-master/commit/4e126430a092fb54afb035514fb3d46115714f97), [`fce8414`](https://github.com/eyaltoledano/claude-task-master/commit/fce841490a9ebbf1801a42dd8a29397379cf1142), [`fce8414`](https://github.com/eyaltoledano/claude-task-master/commit/fce841490a9ebbf1801a42dd8a29397379cf1142), [`fce8414`](https://github.com/eyaltoledano/claude-task-master/commit/fce841490a9ebbf1801a42dd8a29397379cf1142), [`a621ff0`](https://github.com/eyaltoledano/claude-task-master/commit/a621ff05eafb51a147a9aabd7b37ddc0e45b0869), [`e6de285`](https://github.com/eyaltoledano/claude-task-master/commit/e6de285ceacb0a397e952a63435cd32a9c731515), [`fce8414`](https://github.com/eyaltoledano/claude-task-master/commit/fce841490a9ebbf1801a42dd8a29397379cf1142)]:
 53 |   - [email protected]
 54 | 
 55 | ## 0.25.0-rc.0
 56 | 
 57 | ### Minor Changes
 58 | 
 59 | - [#1201](https://github.com/eyaltoledano/claude-task-master/pull/1201) [`83af314`](https://github.com/eyaltoledano/claude-task-master/commit/83af314879fc0e563581161c60d2bd089899313e) Thanks [@losolosol](https://github.com/losolosol)! - Added a Start Build button to the VSCODE Task Properties Right Panel
 60 | 
 61 | ### Patch Changes
 62 | 
 63 | - Updated dependencies [[`137ef36`](https://github.com/eyaltoledano/claude-task-master/commit/137ef362789a9cdfdb1925e35e0438c1fa6c69ee)]:
 64 |   - [email protected]
 65 | 
 66 | ## 0.24.2
 67 | 
 68 | ### Patch Changes
 69 | 
 70 | - Updated dependencies [[`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f), [`df26c65`](https://github.com/eyaltoledano/claude-task-master/commit/df26c65632000874a73504963b08f18c46283144), [`37af0f1`](https://github.com/eyaltoledano/claude-task-master/commit/37af0f191227a68d119b7f89a377bf932ee3ac66), [`c4f92f6`](https://github.com/eyaltoledano/claude-task-master/commit/c4f92f6a0aee3435c56eb8d27d9aa9204284833e), [`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f), [`4dad2fd`](https://github.com/eyaltoledano/claude-task-master/commit/4dad2fd613ceac56a65ae9d3c1c03092b8860ac9)]:
 71 |   - [email protected]
 72 | 
 73 | ## 0.24.2-rc.1
 74 | 
 75 | ### Patch Changes
 76 | 
 77 | - Updated dependencies [[`c4f92f6`](https://github.com/eyaltoledano/claude-task-master/commit/c4f92f6a0aee3435c56eb8d27d9aa9204284833e)]:
 78 |   - [email protected]
 79 | 
 80 | ## 0.24.2-rc.0
 81 | 
 82 | ### Patch Changes
 83 | 
 84 | - Updated dependencies [[`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f), [`37af0f1`](https://github.com/eyaltoledano/claude-task-master/commit/37af0f191227a68d119b7f89a377bf932ee3ac66), [`8783708`](https://github.com/eyaltoledano/claude-task-master/commit/8783708e5e3389890a78fcf685d3da0580e73b3f), [`4dad2fd`](https://github.com/eyaltoledano/claude-task-master/commit/4dad2fd613ceac56a65ae9d3c1c03092b8860ac9)]:
 85 |   - [email protected]
 86 | 
 87 | ## 0.24.1
 88 | 
 89 | ### Patch Changes
 90 | 
 91 | - Updated dependencies [[`8933557`](https://github.com/eyaltoledano/claude-task-master/commit/89335578ffffc65504b2055c0c85aa7521e5e79b), [`db720a9`](https://github.com/eyaltoledano/claude-task-master/commit/db720a954d390bb44838cd021b8813dde8f3d8de)]:
 92 |   - [email protected]
 93 | 
 94 | ## 0.24.0
 95 | 
 96 | ### Minor Changes
 97 | 
 98 | - [#1100](https://github.com/eyaltoledano/claude-task-master/pull/1100) [`30ca144`](https://github.com/eyaltoledano/claude-task-master/commit/30ca144231c36a6c63911f20adc225d38fb15a2f) Thanks [@vedovelli](https://github.com/vedovelli)! - Display current task ID on task details page
 99 | 
100 | ### Patch Changes
101 | 
102 | - Updated dependencies [[`04e11b5`](https://github.com/eyaltoledano/claude-task-master/commit/04e11b5e828597c0ba5b82ca7d5fb6f933e4f1e8), [`fc47714`](https://github.com/eyaltoledano/claude-task-master/commit/fc477143400fd11d953727bf1b4277af5ad308d1), [`782728f`](https://github.com/eyaltoledano/claude-task-master/commit/782728ff95aa2e3b766d48273b57f6c6753e8573), [`3dee60d`](https://github.com/eyaltoledano/claude-task-master/commit/3dee60dc3d566e3cff650accb30f994b8bb3a15e), [`e3ed4d7`](https://github.com/eyaltoledano/claude-task-master/commit/e3ed4d7c14b56894d7da675eb2b757423bea8f9d), [`04e11b5`](https://github.com/eyaltoledano/claude-task-master/commit/04e11b5e828597c0ba5b82ca7d5fb6f933e4f1e8), [`95640dc`](https://github.com/eyaltoledano/claude-task-master/commit/95640dcde87ce7879858c0a951399fb49f3b6397), [`311b243`](https://github.com/eyaltoledano/claude-task-master/commit/311b2433e23c771c8d3a4d3f5ac577302b8321e5)]:
103 |   - [email protected]
104 | 
105 | ## 0.24.0-rc.0
106 | 
107 | ### Minor Changes
108 | 
109 | - [#1040](https://github.com/eyaltoledano/claude-task-master/pull/1040) [`fc47714`](https://github.com/eyaltoledano/claude-task-master/commit/fc477143400fd11d953727bf1b4277af5ad308d1) Thanks [@DomVidja](https://github.com/DomVidja)! - "Add Kilo Code profile integration with custom modes and MCP configuration"
110 | 
111 | - [#1100](https://github.com/eyaltoledano/claude-task-master/pull/1100) [`30ca144`](https://github.com/eyaltoledano/claude-task-master/commit/30ca144231c36a6c63911f20adc225d38fb15a2f) Thanks [@vedovelli](https://github.com/vedovelli)! - Display current task ID on task details page
112 | 
113 | ### Patch Changes
114 | 
115 | - Updated dependencies [[`04e11b5`](https://github.com/eyaltoledano/claude-task-master/commit/04e11b5e828597c0ba5b82ca7d5fb6f933e4f1e8), [`fc47714`](https://github.com/eyaltoledano/claude-task-master/commit/fc477143400fd11d953727bf1b4277af5ad308d1), [`782728f`](https://github.com/eyaltoledano/claude-task-master/commit/782728ff95aa2e3b766d48273b57f6c6753e8573), [`3dee60d`](https://github.com/eyaltoledano/claude-task-master/commit/3dee60dc3d566e3cff650accb30f994b8bb3a15e), [`e3ed4d7`](https://github.com/eyaltoledano/claude-task-master/commit/e3ed4d7c14b56894d7da675eb2b757423bea8f9d), [`04e11b5`](https://github.com/eyaltoledano/claude-task-master/commit/04e11b5e828597c0ba5b82ca7d5fb6f933e4f1e8), [`95640dc`](https://github.com/eyaltoledano/claude-task-master/commit/95640dcde87ce7879858c0a951399fb49f3b6397), [`311b243`](https://github.com/eyaltoledano/claude-task-master/commit/311b2433e23c771c8d3a4d3f5ac577302b8321e5)]:
116 |   - [email protected]
117 | 
118 | ## 0.23.1
119 | 
120 | ### Patch Changes
121 | 
122 | - [#1090](https://github.com/eyaltoledano/claude-task-master/pull/1090) [`a464e55`](https://github.com/eyaltoledano/claude-task-master/commit/a464e550b886ef81b09df80588fe5881bce83d93) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix issues with some users not being able to connect to Taskmaster MCP server while using the extension
123 | 
124 | - Updated dependencies [[`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`e495b2b`](https://github.com/eyaltoledano/claude-task-master/commit/e495b2b55950ee54c7d0f1817d8530e28bd79c05), [`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b), [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7)]:
125 |   - [email protected]
126 | 
127 | ## 0.23.1-rc.1
128 | 
129 | ### Patch Changes
130 | 
131 | - Updated dependencies [[`75c514c`](https://github.com/eyaltoledano/claude-task-master/commit/75c514cf5b2ca47f95c0ad7fa92654a4f2a6be4b)]:
132 |   - [email protected]
133 | 
134 | ## 0.23.1-rc.0
135 | 
136 | ### Patch Changes
137 | 
138 | - [#1090](https://github.com/eyaltoledano/claude-task-master/pull/1090) [`a464e55`](https://github.com/eyaltoledano/claude-task-master/commit/a464e550b886ef81b09df80588fe5881bce83d93) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix issues with some users not being able to connect to Taskmaster MCP server while using the extension
139 | 
140 | - Updated dependencies [[`4357af3`](https://github.com/eyaltoledano/claude-task-master/commit/4357af3f13859d90bca8795215e5d5f1d94abde5), [`36468f3`](https://github.com/eyaltoledano/claude-task-master/commit/36468f3c93faf4035a5c442ccbc501077f3440f1), [`4bb6370`](https://github.com/eyaltoledano/claude-task-master/commit/4bb63706b80c28d1b2d782ba868a725326f916c7)]:
141 |   - [email protected]
142 | 
143 | ## 0.23.0
144 | 
145 | ### Minor Changes
146 | 
147 | - [#1064](https://github.com/eyaltoledano/claude-task-master/pull/1064) [`b82d858`](https://github.com/eyaltoledano/claude-task-master/commit/b82d858f81a1e702ad59d84d5ae8a2ca84359a83) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - 🎉 **Introducing TaskMaster Extension!**
148 | 
149 |   We're thrilled to launch the first version of our Code extension, bringing the power of TaskMaster directly into your favorite code editor. While this is our initial release and we've kept things focused, it already packs powerful features to supercharge your development workflow.
150 | 
151 |   ## ✨ Key Features
152 | 
153 |   ### 📋 Visual Task Management
154 |   - **Kanban Board View**: Visualize all your tasks in an intuitive board layout directly in VS Code
155 |   - **Drag & Drop**: Easily change task status by dragging cards between columns
156 |   - **Real-time Updates**: See changes instantly as you work through your project
157 | 
158 |   ### 🏷️ Multi-Context Support
159 |   - **Tag Switching**: Seamlessly switch between different project contexts/tags
160 |   - **Isolated Workflows**: Keep different features or experiments organized separately
161 | 
162 |   ### 🤖 AI-Powered Task Updates
163 |   - **Smart Updates**: Use TaskMaster's AI capabilities to update tasks and subtasks
164 |   - **Context-Aware**: Leverages your existing TaskMaster configuration and models
165 | 
166 |   ### 📊 Rich Task Information
167 |   - **Complexity Scores**: See task complexity ratings at a glance
168 |   - **Subtask Visualization**: Expand tasks to view and manage subtasks
169 |   - **Dependency Graphs**: Understand task relationships and dependencies visually
170 | 
171 |   ### ⚙️ Configuration Management
172 |   - **Visual Config Editor**: View and understand your `.taskmaster/config.json` settings
173 |   - **Easy Access**: No more manual JSON editing for common configuration tasks
174 | 
175 |   ### 🚀 Quick Actions
176 |   - **Status Updates**: Change task status with a single click
177 |   - **Task Details**: Access full task information without leaving VS Code
178 |   - **Integrated Commands**: All TaskMaster commands available through the command palette
179 | 
180 |   ## 🎯 What's Next?
181 | 
182 |   This is just the beginning! We wanted to get a solid foundation into your hands quickly. The extension will evolve rapidly with your feedback, adding more advanced features, better visualizations, and deeper integration with your development workflow.
183 | 
184 |   Thank you for being part of the TaskMaster journey. Your workflow has never looked better! 🚀
185 | 
186 | ## 0.23.0-rc.1
187 | 
188 | ### Minor Changes
189 | 
190 | - [#1064](https://github.com/eyaltoledano/claude-task-master/pull/1064) [`b82d858`](https://github.com/eyaltoledano/claude-task-master/commit/b82d858f81a1e702ad59d84d5ae8a2ca84359a83) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - 🎉 **Introducing TaskMaster Extension!**
191 | 
192 |   We're thrilled to launch the first version of our Code extension, bringing the power of TaskMaster directly into your favorite code editor. While this is our initial release and we've kept things focused, it already packs powerful features to supercharge your development workflow.
193 | 
194 |   ## ✨ Key Features
195 | 
196 |   ### 📋 Visual Task Management
197 |   - **Kanban Board View**: Visualize all your tasks in an intuitive board layout directly in VS Code
198 |   - **Drag & Drop**: Easily change task status by dragging cards between columns
199 |   - **Real-time Updates**: See changes instantly as you work through your project
200 | 
201 |   ### 🏷️ Multi-Context Support
202 |   - **Tag Switching**: Seamlessly switch between different project contexts/tags
203 |   - **Isolated Workflows**: Keep different features or experiments organized separately
204 | 
205 |   ### 🤖 AI-Powered Task Updates
206 |   - **Smart Updates**: Use TaskMaster's AI capabilities to update tasks and subtasks
207 |   - **Context-Aware**: Leverages your existing TaskMaster configuration and models
208 | 
209 |   ### 📊 Rich Task Information
210 |   - **Complexity Scores**: See task complexity ratings at a glance
211 |   - **Subtask Visualization**: Expand tasks to view and manage subtasks
212 |   - **Dependency Graphs**: Understand task relationships and dependencies visually
213 | 
214 |   ### ⚙️ Configuration Management
215 |   - **Visual Config Editor**: View and understand your `.taskmaster/config.json` settings
216 |   - **Easy Access**: No more manual JSON editing for common configuration tasks
217 | 
218 |   ### 🚀 Quick Actions
219 |   - **Status Updates**: Change task status with a single click
220 |   - **Task Details**: Access full task information without leaving VS Code
221 |   - **Integrated Commands**: All TaskMaster commands available through the command palette
222 | 
223 |   ## 🎯 What's Next?
224 | 
225 |   This is just the beginning! We wanted to get a solid foundation into your hands quickly. The extension will evolve rapidly with your feedback, adding more advanced features, better visualizations, and deeper integration with your development workflow.
226 | 
227 |   Thank you for being part of the TaskMaster journey. Your workflow has never looked better! 🚀
228 | 
229 | ## 0.23.0-rc.0
230 | 
231 | ### Minor Changes
232 | 
233 | - [#997](https://github.com/eyaltoledano/claude-task-master/pull/997) [`64302dc`](https://github.com/eyaltoledano/claude-task-master/commit/64302dc1918f673fcdac05b29411bf76ffe93505) Thanks [@DavidMaliglowka](https://github.com/DavidMaliglowka)! - 🎉 **Introducing TaskMaster Extension!**
234 | 
235 |   We're thrilled to launch the first version of our Code extension, bringing the power of TaskMaster directly into your favorite code editor. While this is our initial release and we've kept things focused, it already packs powerful features to supercharge your development workflow.
236 | 
237 |   ## ✨ Key Features
238 | 
239 |   ### 📋 Visual Task Management
240 |   - **Kanban Board View**: Visualize all your tasks in an intuitive board layout directly in VS Code
241 |   - **Drag & Drop**: Easily change task status by dragging cards between columns
242 |   - **Real-time Updates**: See changes instantly as you work through your project
243 | 
244 |   ### 🏷️ Multi-Context Support
245 |   - **Tag Switching**: Seamlessly switch between different project contexts/tags
246 |   - **Isolated Workflows**: Keep different features or experiments organized separately
247 | 
248 |   ### 🤖 AI-Powered Task Updates
249 |   - **Smart Updates**: Use TaskMaster's AI capabilities to update tasks and subtasks
250 |   - **Context-Aware**: Leverages your existing TaskMaster configuration and models
251 | 
252 |   ### 📊 Rich Task Information
253 |   - **Complexity Scores**: See task complexity ratings at a glance
254 |   - **Subtask Visualization**: Expand tasks to view and manage subtasks
255 |   - **Dependency Graphs**: Understand task relationships and dependencies visually
256 | 
257 |   ### ⚙️ Configuration Management
258 |   - **Visual Config Editor**: View and understand your `.taskmaster/config.json` settings
259 |   - **Easy Access**: No more manual JSON editing for common configuration tasks
260 | 
261 |   ### 🚀 Quick Actions
262 |   - **Status Updates**: Change task status with a single click
263 |   - **Task Details**: Access full task information without leaving VS Code
264 |   - **Integrated Commands**: All TaskMaster commands available through the command palette
265 | 
266 |   ## 🎯 What's Next?
267 | 
268 |   This is just the beginning! We wanted to get a solid foundation into your hands quickly. The extension will evolve rapidly with your feedback, adding more advanced features, better visualizations, and deeper integration with your development workflow.
269 | 
270 |   Thank you for being part of the TaskMaster journey. Your workflow has never looked better! 🚀
271 | 
```

--------------------------------------------------------------------------------
/scripts/modules/task-manager/update-tasks.js:
--------------------------------------------------------------------------------

```javascript
  1 | import path from 'path';
  2 | import chalk from 'chalk';
  3 | import boxen from 'boxen';
  4 | import Table from 'cli-table3';
  5 | import { z } from 'zod'; // Keep Zod for post-parsing validation
  6 | 
  7 | import {
  8 | 	log as consoleLog,
  9 | 	readJSON,
 10 | 	writeJSON,
 11 | 	truncate,
 12 | 	isSilentMode
 13 | } from '../utils.js';
 14 | 
 15 | import {
 16 | 	getStatusWithColor,
 17 | 	startLoadingIndicator,
 18 | 	stopLoadingIndicator,
 19 | 	displayAiUsageSummary
 20 | } from '../ui.js';
 21 | 
 22 | import { getDebugFlag, hasCodebaseAnalysis } from '../config-manager.js';
 23 | import { getPromptManager } from '../prompt-manager.js';
 24 | import generateTaskFiles from './generate-task-files.js';
 25 | import { generateTextService } from '../ai-services-unified.js';
 26 | import { getModelConfiguration } from './models.js';
 27 | import { ContextGatherer } from '../utils/contextGatherer.js';
 28 | import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
 29 | import { flattenTasksWithSubtasks, findProjectRoot } from '../utils.js';
 30 | 
 31 | // Zod schema for validating the structure of tasks AFTER parsing
 32 | const updatedTaskSchema = z
 33 | 	.object({
 34 | 		id: z.number().int(),
 35 | 		title: z.string(),
 36 | 		description: z.string(),
 37 | 		status: z.string(),
 38 | 		dependencies: z.array(z.union([z.number().int(), z.string()])),
 39 | 		priority: z.string().nullable(),
 40 | 		details: z.string().nullable(),
 41 | 		testStrategy: z.string().nullable(),
 42 | 		subtasks: z.array(z.any()).nullable() // Keep subtasks flexible for now
 43 | 	})
 44 | 	.strip(); // Allow potential extra fields during parsing if needed, then validate structure
 45 | 
 46 | // Preprocessing schema that adds defaults before validation
 47 | const preprocessTaskSchema = z.preprocess((task) => {
 48 | 	// Ensure task is an object
 49 | 	if (typeof task !== 'object' || task === null) {
 50 | 		return {};
 51 | 	}
 52 | 
 53 | 	// Return task with defaults for missing fields
 54 | 	return {
 55 | 		...task,
 56 | 		// Add defaults for required fields if missing
 57 | 		id: task.id ?? 0,
 58 | 		title: task.title ?? 'Untitled Task',
 59 | 		description: task.description ?? '',
 60 | 		status: task.status ?? 'pending',
 61 | 		dependencies: Array.isArray(task.dependencies) ? task.dependencies : [],
 62 | 		// Optional fields - preserve undefined/null distinction
 63 | 		priority: task.hasOwnProperty('priority') ? task.priority : null,
 64 | 		details: task.hasOwnProperty('details') ? task.details : null,
 65 | 		testStrategy: task.hasOwnProperty('testStrategy')
 66 | 			? task.testStrategy
 67 | 			: null,
 68 | 		subtasks: Array.isArray(task.subtasks)
 69 | 			? task.subtasks
 70 | 			: task.subtasks === null
 71 | 				? null
 72 | 				: []
 73 | 	};
 74 | }, updatedTaskSchema);
 75 | 
 76 | const updatedTaskArraySchema = z.array(updatedTaskSchema);
 77 | const preprocessedTaskArraySchema = z.array(preprocessTaskSchema);
 78 | 
 79 | /**
 80 |  * Parses an array of task objects from AI's text response.
 81 |  * @param {string} text - Response text from AI.
 82 |  * @param {number} expectedCount - Expected number of tasks.
 83 |  * @param {Function | Object} logFn - The logging function or MCP log object.
 84 |  * @param {boolean} isMCP - Flag indicating if logFn is MCP logger.
 85 |  * @returns {Array} Parsed and validated tasks array.
 86 |  * @throws {Error} If parsing or validation fails.
 87 |  */
 88 | function parseUpdatedTasksFromText(text, expectedCount, logFn, isMCP) {
 89 | 	const report = (level, ...args) => {
 90 | 		if (isMCP) {
 91 | 			if (typeof logFn[level] === 'function') logFn[level](...args);
 92 | 			else logFn.info(...args);
 93 | 		} else if (!isSilentMode()) {
 94 | 			// Check silent mode for consoleLog
 95 | 			consoleLog(level, ...args);
 96 | 		}
 97 | 	};
 98 | 
 99 | 	report(
100 | 		'info',
101 | 		'Attempting to parse updated tasks array from text response...'
102 | 	);
103 | 	if (!text || text.trim() === '')
104 | 		throw new Error('AI response text is empty.');
105 | 
106 | 	let cleanedResponse = text.trim();
107 | 	const originalResponseForDebug = cleanedResponse;
108 | 	let parseMethodUsed = 'raw'; // Track which method worked
109 | 
110 | 	// --- NEW Step 1: Try extracting between [] first ---
111 | 	const firstBracketIndex = cleanedResponse.indexOf('[');
112 | 	const lastBracketIndex = cleanedResponse.lastIndexOf(']');
113 | 	let potentialJsonFromArray = null;
114 | 
115 | 	if (firstBracketIndex !== -1 && lastBracketIndex > firstBracketIndex) {
116 | 		potentialJsonFromArray = cleanedResponse.substring(
117 | 			firstBracketIndex,
118 | 			lastBracketIndex + 1
119 | 		);
120 | 		// Basic check to ensure it's not just "[]" or malformed
121 | 		if (potentialJsonFromArray.length <= 2) {
122 | 			potentialJsonFromArray = null; // Ignore empty array
123 | 		}
124 | 	}
125 | 
126 | 	// If [] extraction yielded something, try parsing it immediately
127 | 	if (potentialJsonFromArray) {
128 | 		try {
129 | 			const testParse = JSON.parse(potentialJsonFromArray);
130 | 			// It worked! Use this as the primary cleaned response.
131 | 			cleanedResponse = potentialJsonFromArray;
132 | 			parseMethodUsed = 'brackets';
133 | 		} catch (e) {
134 | 			report(
135 | 				'info',
136 | 				'Content between [] looked promising but failed initial parse. Proceeding to other methods.'
137 | 			);
138 | 			// Reset cleanedResponse to original if bracket parsing failed
139 | 			cleanedResponse = originalResponseForDebug;
140 | 		}
141 | 	}
142 | 
143 | 	// --- Step 2: If bracket parsing didn't work or wasn't applicable, try code block extraction ---
144 | 	if (parseMethodUsed === 'raw') {
145 | 		// Only look for ```json blocks now
146 | 		const codeBlockMatch = cleanedResponse.match(
147 | 			/```json\s*([\s\S]*?)\s*```/i // Only match ```json
148 | 		);
149 | 		if (codeBlockMatch) {
150 | 			cleanedResponse = codeBlockMatch[1].trim();
151 | 			parseMethodUsed = 'codeblock';
152 | 			report('info', 'Extracted JSON content from JSON Markdown code block.');
153 | 		} else {
154 | 			report('info', 'No JSON code block found.');
155 | 			// --- Step 3: If code block failed, try stripping prefixes ---
156 | 			const commonPrefixes = [
157 | 				'json\n',
158 | 				'javascript\n', // Keep checking common prefixes just in case
159 | 				'python\n',
160 | 				'here are the updated tasks:',
161 | 				'here is the updated json:',
162 | 				'updated tasks:',
163 | 				'updated json:',
164 | 				'response:',
165 | 				'output:'
166 | 			];
167 | 			let prefixFound = false;
168 | 			for (const prefix of commonPrefixes) {
169 | 				if (cleanedResponse.toLowerCase().startsWith(prefix)) {
170 | 					cleanedResponse = cleanedResponse.substring(prefix.length).trim();
171 | 					parseMethodUsed = 'prefix';
172 | 					report('info', `Stripped prefix: "${prefix.trim()}"`);
173 | 					prefixFound = true;
174 | 					break;
175 | 				}
176 | 			}
177 | 			if (!prefixFound) {
178 | 				report(
179 | 					'warn',
180 | 					'Response does not appear to contain [], JSON code block, or known prefix. Attempting raw parse.'
181 | 				);
182 | 			}
183 | 		}
184 | 	}
185 | 
186 | 	// --- Step 4: Attempt final parse ---
187 | 	let parsedTasks;
188 | 	try {
189 | 		parsedTasks = JSON.parse(cleanedResponse);
190 | 	} catch (parseError) {
191 | 		report('error', `Failed to parse JSON array: ${parseError.message}`);
192 | 		report(
193 | 			'error',
194 | 			`Extraction method used: ${parseMethodUsed}` // Log which method failed
195 | 		);
196 | 		report(
197 | 			'error',
198 | 			`Problematic JSON string (first 500 chars): ${cleanedResponse.substring(0, 500)}`
199 | 		);
200 | 		report(
201 | 			'error',
202 | 			`Original Raw Response (first 500 chars): ${originalResponseForDebug.substring(0, 500)}`
203 | 		);
204 | 		throw new Error(
205 | 			`Failed to parse JSON response array: ${parseError.message}`
206 | 		);
207 | 	}
208 | 
209 | 	// --- Step 5 & 6: Validate Array structure and Zod schema ---
210 | 	if (!Array.isArray(parsedTasks)) {
211 | 		report(
212 | 			'error',
213 | 			`Parsed content is not an array. Type: ${typeof parsedTasks}`
214 | 		);
215 | 		report(
216 | 			'error',
217 | 			`Parsed content sample: ${JSON.stringify(parsedTasks).substring(0, 200)}`
218 | 		);
219 | 		throw new Error('Parsed AI response is not a valid JSON array.');
220 | 	}
221 | 
222 | 	report('info', `Successfully parsed ${parsedTasks.length} potential tasks.`);
223 | 	if (expectedCount && parsedTasks.length !== expectedCount) {
224 | 		report(
225 | 			'warn',
226 | 			`Expected ${expectedCount} tasks, but parsed ${parsedTasks.length}.`
227 | 		);
228 | 	}
229 | 
230 | 	// Log missing fields for debugging before preprocessing
231 | 	let hasWarnings = false;
232 | 	parsedTasks.forEach((task, index) => {
233 | 		const missingFields = [];
234 | 		if (!task.hasOwnProperty('id')) missingFields.push('id');
235 | 		if (!task.hasOwnProperty('status')) missingFields.push('status');
236 | 		if (!task.hasOwnProperty('dependencies'))
237 | 			missingFields.push('dependencies');
238 | 
239 | 		if (missingFields.length > 0) {
240 | 			hasWarnings = true;
241 | 			report(
242 | 				'warn',
243 | 				`Task ${index} is missing fields: ${missingFields.join(', ')} - will use defaults`
244 | 			);
245 | 		}
246 | 	});
247 | 
248 | 	if (hasWarnings) {
249 | 		report(
250 | 			'warn',
251 | 			'Some tasks were missing required fields. Applying defaults...'
252 | 		);
253 | 	}
254 | 
255 | 	// Use the preprocessing schema to add defaults and validate
256 | 	const preprocessResult = preprocessedTaskArraySchema.safeParse(parsedTasks);
257 | 
258 | 	if (!preprocessResult.success) {
259 | 		// This should rarely happen now since preprocessing adds defaults
260 | 		report('error', 'Failed to validate task array even after preprocessing.');
261 | 		preprocessResult.error.errors.forEach((err) => {
262 | 			report('error', `  - Path '${err.path.join('.')}': ${err.message}`);
263 | 		});
264 | 
265 | 		throw new Error(
266 | 			`AI response failed validation: ${preprocessResult.error.message}`
267 | 		);
268 | 	}
269 | 
270 | 	report('info', 'Successfully validated and transformed task structure.');
271 | 	return preprocessResult.data.slice(
272 | 		0,
273 | 		expectedCount || preprocessResult.data.length
274 | 	);
275 | }
276 | 
277 | /**
278 |  * Update tasks based on new context using the unified AI service.
279 |  * @param {string} tasksPath - Path to the tasks.json file
280 |  * @param {number} fromId - Task ID to start updating from
281 |  * @param {string} prompt - Prompt with new context
282 |  * @param {boolean} [useResearch=false] - Whether to use the research AI role.
283 |  * @param {Object} context - Context object containing session and mcpLog.
284 |  * @param {Object} [context.session] - Session object from MCP server.
285 |  * @param {Object} [context.mcpLog] - MCP logger object.
286 |  * @param {string} [context.tag] - Tag for the task
287 |  * @param {string} [outputFormat='text'] - Output format ('text' or 'json').
288 |  */
289 | async function updateTasks(
290 | 	tasksPath,
291 | 	fromId,
292 | 	prompt,
293 | 	useResearch = false,
294 | 	context = {},
295 | 	outputFormat = 'text' // Default to text for CLI
296 | ) {
297 | 	const { session, mcpLog, projectRoot: providedProjectRoot, tag } = context;
298 | 	// Use mcpLog if available, otherwise use the imported consoleLog function
299 | 	const logFn = mcpLog || consoleLog;
300 | 	// Flag to easily check which logger type we have
301 | 	const isMCP = !!mcpLog;
302 | 
303 | 	if (isMCP)
304 | 		logFn.info(`updateTasks called with context: session=${!!session}`);
305 | 	else logFn('info', `updateTasks called`); // CLI log
306 | 
307 | 	try {
308 | 		if (isMCP) logFn.info(`Updating tasks from ID ${fromId}`);
309 | 		else
310 | 			logFn(
311 | 				'info',
312 | 				`Updating tasks from ID ${fromId} with prompt: "${prompt}"`
313 | 			);
314 | 
315 | 		// Determine project root
316 | 		const projectRoot = providedProjectRoot || findProjectRoot();
317 | 		if (!projectRoot) {
318 | 			throw new Error('Could not determine project root directory');
319 | 		}
320 | 
321 | 		// --- Task Loading/Filtering (Updated to pass projectRoot and tag) ---
322 | 		const data = readJSON(tasksPath, projectRoot, tag);
323 | 		if (!data || !data.tasks)
324 | 			throw new Error(`No valid tasks found in ${tasksPath}`);
325 | 		const tasksToUpdate = data.tasks.filter(
326 | 			(task) => task.id >= fromId && task.status !== 'done'
327 | 		);
328 | 		if (tasksToUpdate.length === 0) {
329 | 			if (isMCP)
330 | 				logFn.info(`No tasks to update (ID >= ${fromId} and not 'done').`);
331 | 			else
332 | 				logFn('info', `No tasks to update (ID >= ${fromId} and not 'done').`);
333 | 			if (outputFormat === 'text') console.log(/* yellow message */);
334 | 			return; // Nothing to do
335 | 		}
336 | 		// --- End Task Loading/Filtering ---
337 | 
338 | 		// --- Context Gathering ---
339 | 		let gatheredContext = '';
340 | 		try {
341 | 			const contextGatherer = new ContextGatherer(projectRoot, tag);
342 | 			const allTasksFlat = flattenTasksWithSubtasks(data.tasks);
343 | 			const fuzzySearch = new FuzzyTaskSearch(allTasksFlat, 'update');
344 | 			const searchResults = fuzzySearch.findRelevantTasks(prompt, {
345 | 				maxResults: 5,
346 | 				includeSelf: true
347 | 			});
348 | 			const relevantTaskIds = fuzzySearch.getTaskIds(searchResults);
349 | 
350 | 			const tasksToUpdateIds = tasksToUpdate.map((t) => t.id.toString());
351 | 			const finalTaskIds = [
352 | 				...new Set([...tasksToUpdateIds, ...relevantTaskIds])
353 | 			];
354 | 
355 | 			if (finalTaskIds.length > 0) {
356 | 				const contextResult = await contextGatherer.gather({
357 | 					tasks: finalTaskIds,
358 | 					format: 'research'
359 | 				});
360 | 				gatheredContext = contextResult.context || '';
361 | 			}
362 | 		} catch (contextError) {
363 | 			logFn(
364 | 				'warn',
365 | 				`Could not gather additional context: ${contextError.message}`
366 | 			);
367 | 		}
368 | 		// --- End Context Gathering ---
369 | 
370 | 		// --- Display Tasks to Update (CLI Only - Unchanged) ---
371 | 		if (outputFormat === 'text') {
372 | 			// Show the tasks that will be updated
373 | 			const table = new Table({
374 | 				head: [
375 | 					chalk.cyan.bold('ID'),
376 | 					chalk.cyan.bold('Title'),
377 | 					chalk.cyan.bold('Status')
378 | 				],
379 | 				colWidths: [5, 70, 20]
380 | 			});
381 | 
382 | 			tasksToUpdate.forEach((task) => {
383 | 				table.push([
384 | 					task.id,
385 | 					truncate(task.title, 57),
386 | 					getStatusWithColor(task.status)
387 | 				]);
388 | 			});
389 | 
390 | 			console.log(
391 | 				boxen(chalk.white.bold(`Updating ${tasksToUpdate.length} tasks`), {
392 | 					padding: 1,
393 | 					borderColor: 'blue',
394 | 					borderStyle: 'round',
395 | 					margin: { top: 1, bottom: 0 }
396 | 				})
397 | 			);
398 | 
399 | 			console.log(table.toString());
400 | 
401 | 			// Display a message about how completed subtasks are handled
402 | 			console.log(
403 | 				boxen(
404 | 					chalk.cyan.bold('How Completed Subtasks Are Handled:') +
405 | 						'\n\n' +
406 | 						chalk.white(
407 | 							'• Subtasks marked as "done" or "completed" will be preserved\n'
408 | 						) +
409 | 						chalk.white(
410 | 							'• New subtasks will build upon what has already been completed\n'
411 | 						) +
412 | 						chalk.white(
413 | 							'• If completed work needs revision, a new subtask will be created instead of modifying done items\n'
414 | 						) +
415 | 						chalk.white(
416 | 							'• This approach maintains a clear record of completed work and new requirements'
417 | 						),
418 | 					{
419 | 						padding: 1,
420 | 						borderColor: 'blue',
421 | 						borderStyle: 'round',
422 | 						margin: { top: 1, bottom: 1 }
423 | 					}
424 | 				)
425 | 			);
426 | 		}
427 | 		// --- End Display Tasks ---
428 | 
429 | 		// --- Build Prompts (Using PromptManager) ---
430 | 		// Load prompts using PromptManager
431 | 		const promptManager = getPromptManager();
432 | 		const { systemPrompt, userPrompt } = await promptManager.loadPrompt(
433 | 			'update-tasks',
434 | 			{
435 | 				tasks: tasksToUpdate,
436 | 				updatePrompt: prompt,
437 | 				useResearch,
438 | 				projectContext: gatheredContext,
439 | 				hasCodebaseAnalysis: hasCodebaseAnalysis(
440 | 					useResearch,
441 | 					projectRoot,
442 | 					session
443 | 				),
444 | 				projectRoot: projectRoot
445 | 			}
446 | 		);
447 | 		// --- End Build Prompts ---
448 | 
449 | 		// --- AI Call ---
450 | 		let loadingIndicator = null;
451 | 		let aiServiceResponse = null;
452 | 
453 | 		if (!isMCP && outputFormat === 'text') {
454 | 			loadingIndicator = startLoadingIndicator('Updating tasks with AI...\n');
455 | 		}
456 | 
457 | 		try {
458 | 			// Determine role based on research flag
459 | 			const serviceRole = useResearch ? 'research' : 'main';
460 | 
461 | 			// Call the unified AI service
462 | 			aiServiceResponse = await generateTextService({
463 | 				role: serviceRole,
464 | 				session: session,
465 | 				projectRoot: projectRoot,
466 | 				systemPrompt: systemPrompt,
467 | 				prompt: userPrompt,
468 | 				commandName: 'update-tasks',
469 | 				outputType: isMCP ? 'mcp' : 'cli'
470 | 			});
471 | 
472 | 			if (loadingIndicator)
473 | 				stopLoadingIndicator(loadingIndicator, 'AI update complete.');
474 | 
475 | 			// Use the mainResult (text) for parsing
476 | 			const parsedUpdatedTasks = parseUpdatedTasksFromText(
477 | 				aiServiceResponse.mainResult,
478 | 				tasksToUpdate.length,
479 | 				logFn,
480 | 				isMCP
481 | 			);
482 | 
483 | 			// --- Update Tasks Data (Updated writeJSON call) ---
484 | 			if (!Array.isArray(parsedUpdatedTasks)) {
485 | 				// Should be caught by parser, but extra check
486 | 				throw new Error(
487 | 					'Parsed AI response for updated tasks was not an array.'
488 | 				);
489 | 			}
490 | 			if (isMCP)
491 | 				logFn.info(
492 | 					`Received ${parsedUpdatedTasks.length} updated tasks from AI.`
493 | 				);
494 | 			else
495 | 				logFn(
496 | 					'info',
497 | 					`Received ${parsedUpdatedTasks.length} updated tasks from AI.`
498 | 				);
499 | 			// Create a map for efficient lookup
500 | 			const updatedTasksMap = new Map(
501 | 				parsedUpdatedTasks.map((task) => [task.id, task])
502 | 			);
503 | 
504 | 			let actualUpdateCount = 0;
505 | 			data.tasks.forEach((task, index) => {
506 | 				if (updatedTasksMap.has(task.id)) {
507 | 					// Only update if the task was part of the set sent to AI
508 | 					const updatedTask = updatedTasksMap.get(task.id);
509 | 					// Merge the updated task with the existing one to preserve fields like subtasks
510 | 					data.tasks[index] = {
511 | 						...task, // Keep all existing fields
512 | 						...updatedTask, // Override with updated fields
513 | 						// Ensure subtasks field is preserved if not provided by AI
514 | 						subtasks:
515 | 							updatedTask.subtasks !== undefined
516 | 								? updatedTask.subtasks
517 | 								: task.subtasks
518 | 					};
519 | 					actualUpdateCount++;
520 | 				}
521 | 			});
522 | 			if (isMCP)
523 | 				logFn.info(
524 | 					`Applied updates to ${actualUpdateCount} tasks in the dataset.`
525 | 				);
526 | 			else
527 | 				logFn(
528 | 					'info',
529 | 					`Applied updates to ${actualUpdateCount} tasks in the dataset.`
530 | 				);
531 | 
532 | 			// Fix: Pass projectRoot and currentTag to writeJSON
533 | 			writeJSON(tasksPath, data, projectRoot, tag);
534 | 			if (isMCP)
535 | 				logFn.info(
536 | 					`Successfully updated ${actualUpdateCount} tasks in ${tasksPath}`
537 | 				);
538 | 			else
539 | 				logFn(
540 | 					'success',
541 | 					`Successfully updated ${actualUpdateCount} tasks in ${tasksPath}`
542 | 				);
543 | 			// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
544 | 
545 | 			if (outputFormat === 'text' && aiServiceResponse.telemetryData) {
546 | 				displayAiUsageSummary(aiServiceResponse.telemetryData, 'cli');
547 | 			}
548 | 
549 | 			return {
550 | 				success: true,
551 | 				updatedTasks: parsedUpdatedTasks,
552 | 				telemetryData: aiServiceResponse.telemetryData,
553 | 				tagInfo: aiServiceResponse.tagInfo
554 | 			};
555 | 		} catch (error) {
556 | 			if (loadingIndicator) stopLoadingIndicator(loadingIndicator);
557 | 			if (isMCP) logFn.error(`Error during AI service call: ${error.message}`);
558 | 			else logFn('error', `Error during AI service call: ${error.message}`);
559 | 			if (error.message.includes('API key')) {
560 | 				if (isMCP)
561 | 					logFn.error(
562 | 						'Please ensure API keys are configured correctly in .env or mcp.json.'
563 | 					);
564 | 				else
565 | 					logFn(
566 | 						'error',
567 | 						'Please ensure API keys are configured correctly in .env or mcp.json.'
568 | 					);
569 | 			}
570 | 			throw error;
571 | 		} finally {
572 | 			if (loadingIndicator) stopLoadingIndicator(loadingIndicator);
573 | 		}
574 | 	} catch (error) {
575 | 		// --- General Error Handling (Unchanged) ---
576 | 		if (isMCP) logFn.error(`Error updating tasks: ${error.message}`);
577 | 		else logFn('error', `Error updating tasks: ${error.message}`);
578 | 		if (outputFormat === 'text') {
579 | 			console.error(chalk.red(`Error: ${error.message}`));
580 | 			if (getDebugFlag(session)) {
581 | 				console.error(error);
582 | 			}
583 | 			process.exit(1);
584 | 		} else {
585 | 			throw error; // Re-throw for MCP/programmatic callers
586 | 		}
587 | 		// --- End General Error Handling ---
588 | 	}
589 | }
590 | 
591 | export default updateTasks;
592 | 
```

--------------------------------------------------------------------------------
/README-task-master.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Task Master
  2 | 
  3 | ### by [@eyaltoledano](https://x.com/eyaltoledano)
  4 | 
  5 | A task management system for AI-driven development with Claude, designed to work seamlessly with Cursor AI.
  6 | 
  7 | ## Requirements
  8 | 
  9 | - Node.js 14.0.0 or higher
 10 | - Anthropic API key (Claude API)
 11 | - Anthropic SDK version 0.39.0 or higher
 12 | - OpenAI SDK (for Perplexity API integration, optional)
 13 | 
 14 | ## Configuration
 15 | 
 16 | Taskmaster uses two primary configuration methods:
 17 | 
 18 | 1.  **`.taskmasterconfig` File (Project Root)**
 19 | 
 20 |     - Stores most settings: AI model selections (main, research, fallback), parameters (max tokens, temperature), logging level, default priority/subtasks, project name.
 21 |     - **Created and managed using `task-master models --setup` CLI command or the `models` MCP tool.**
 22 |     - Do not edit manually unless you know what you are doing.
 23 | 
 24 | 2.  **Environment Variables (`.env` file or MCP `env` block)**
 25 |     - Used **only** for sensitive **API Keys** (e.g., `ANTHROPIC_API_KEY`, `PERPLEXITY_API_KEY`, etc.) and specific endpoints (like `OLLAMA_BASE_URL`).
 26 |     - **For CLI:** Place keys in a `.env` file in your project root.
 27 |     - **For MCP/Cursor:** Place keys in the `env` section of your `.cursor/mcp.json` (or other MCP config according to the AI IDE or client you use) file under the `taskmaster-ai` server definition.
 28 | 
 29 | **Important:** Settings like model choices, max tokens, temperature, and log level are **no longer configured via environment variables.** Use the `task-master models` command or tool.
 30 | 
 31 | See the [Configuration Guide](docs/configuration.md) for full details.
 32 | 
 33 | ## Installation
 34 | 
 35 | ```bash
 36 | # Install globally
 37 | npm install -g task-master-ai
 38 | 
 39 | # OR install locally within your project
 40 | npm install task-master-ai
 41 | ```
 42 | 
 43 | ### Initialize a new project
 44 | 
 45 | ```bash
 46 | # If installed globally
 47 | task-master init
 48 | 
 49 | # If installed locally
 50 | npx task-master init
 51 | ```
 52 | 
 53 | This will prompt you for project details and set up a new project with the necessary files and structure.
 54 | 
 55 | ### Important Notes
 56 | 
 57 | 1. **ES Modules Configuration:**
 58 | 
 59 |    - This project uses ES Modules (ESM) instead of CommonJS.
 60 |    - This is set via `"type": "module"` in your package.json.
 61 |    - Use `import/export` syntax instead of `require()`.
 62 |    - Files should use `.js` or `.mjs` extensions.
 63 |    - To use a CommonJS module, either:
 64 |      - Rename it with `.cjs` extension
 65 |      - Use `await import()` for dynamic imports
 66 |    - If you need CommonJS throughout your project, remove `"type": "module"` from package.json, but Task Master scripts expect ESM.
 67 | 
 68 | 2. The Anthropic SDK version should be 0.39.0 or higher.
 69 | 
 70 | ## Quick Start with Global Commands
 71 | 
 72 | After installing the package globally, you can use these CLI commands from any directory:
 73 | 
 74 | ```bash
 75 | # Initialize a new project
 76 | task-master init
 77 | 
 78 | # Parse a PRD and generate tasks
 79 | task-master parse-prd your-prd.txt
 80 | 
 81 | # List all tasks
 82 | task-master list
 83 | 
 84 | # Show the next task to work on
 85 | task-master next
 86 | 
 87 | # Generate task files
 88 | task-master generate
 89 | ```
 90 | 
 91 | ## Troubleshooting
 92 | 
 93 | ### If `task-master init` doesn't respond:
 94 | 
 95 | Try running it with Node directly:
 96 | 
 97 | ```bash
 98 | node node_modules/claude-task-master/scripts/init.js
 99 | ```
100 | 
101 | Or clone the repository and run:
102 | 
103 | ```bash
104 | git clone https://github.com/eyaltoledano/claude-task-master.git
105 | cd claude-task-master
106 | node scripts/init.js
107 | ```
108 | 
109 | ## Task Structure
110 | 
111 | Tasks in tasks.json have the following structure:
112 | 
113 | - `id`: Unique identifier for the task (Example: `1`)
114 | - `title`: Brief, descriptive title of the task (Example: `"Initialize Repo"`)
115 | - `description`: Concise description of what the task involves (Example: `"Create a new repository, set up initial structure."`)
116 | - `status`: Current state of the task (Example: `"pending"`, `"done"`, `"deferred"`)
117 | - `dependencies`: IDs of tasks that must be completed before this task (Example: `[1, 2]`)
118 |   - Dependencies are displayed with status indicators (✅ for completed, ⏱️ for pending)
119 |   - This helps quickly identify which prerequisite tasks are blocking work
120 | - `priority`: Importance level of the task (Example: `"high"`, `"medium"`, `"low"`)
121 | - `details`: In-depth implementation instructions (Example: `"Use GitHub client ID/secret, handle callback, set session token."`)
122 | - `testStrategy`: Verification approach (Example: `"Deploy and call endpoint to confirm 'Hello World' response."`)
123 | - `subtasks`: List of smaller, more specific tasks that make up the main task (Example: `[{"id": 1, "title": "Configure OAuth", ...}]`)
124 | 
125 | ## Integrating with Cursor AI
126 | 
127 | Claude Task Master is designed to work seamlessly with [Cursor AI](https://www.cursor.so/), providing a structured workflow for AI-driven development.
128 | 
129 | ### Setup with Cursor
130 | 
131 | 1. After initializing your project, open it in Cursor
132 | 2. The `.cursor/rules/dev_workflow.mdc` file is automatically loaded by Cursor, providing the AI with knowledge about the task management system
133 | 3. Place your PRD document in the `scripts/` directory (e.g., `scripts/prd.txt`)
134 | 4. Open Cursor's AI chat and switch to Agent mode
135 | 
136 | ### Setting up MCP in Cursor
137 | 
138 | To enable enhanced task management capabilities directly within Cursor using the Model Control Protocol (MCP):
139 | 
140 | 1. Go to Cursor settings
141 | 2. Navigate to the MCP section
142 | 3. Click on "Add New MCP Server"
143 | 4. Configure with the following details:
144 |    - Name: "Task Master"
145 |    - Type: "Command"
146 |    - Command: "npx -y task-master-ai"
147 | 5. Save the settings
148 | 
149 | Once configured, you can interact with Task Master's task management commands directly through Cursor's interface, providing a more integrated experience.
150 | 
151 | ### Initial Task Generation
152 | 
153 | In Cursor's AI chat, instruct the agent to generate tasks from your PRD:
154 | 
155 | ```
156 | Please use the task-master parse-prd command to generate tasks from my PRD. The PRD is located at scripts/prd.txt.
157 | ```
158 | 
159 | The agent will execute:
160 | 
161 | ```bash
162 | task-master parse-prd scripts/prd.txt
163 | ```
164 | 
165 | This will:
166 | 
167 | - Parse your PRD document
168 | - Generate a structured `tasks.json` file with tasks, dependencies, priorities, and test strategies
169 | - The agent will understand this process due to the Cursor rules
170 | 
171 | ### Generate Individual Task Files
172 | 
173 | Next, ask the agent to generate individual task files:
174 | 
175 | ```
176 | Please generate individual task files from tasks.json
177 | ```
178 | 
179 | The agent will execute:
180 | 
181 | ```bash
182 | task-master generate
183 | ```
184 | 
185 | This creates individual task files in the `tasks/` directory (e.g., `task_001.txt`, `task_002.txt`), making it easier to reference specific tasks.
186 | 
187 | ## AI-Driven Development Workflow
188 | 
189 | The Cursor agent is pre-configured (via the rules file) to follow this workflow:
190 | 
191 | ### 1. Task Discovery and Selection
192 | 
193 | Ask the agent to list available tasks:
194 | 
195 | ```
196 | What tasks are available to work on next?
197 | ```
198 | 
199 | The agent will:
200 | 
201 | - Run `task-master list` to see all tasks
202 | - Run `task-master next` to determine the next task to work on
203 | - Analyze dependencies to determine which tasks are ready to be worked on
204 | - Prioritize tasks based on priority level and ID order
205 | - Suggest the next task(s) to implement
206 | 
207 | ### 2. Task Implementation
208 | 
209 | When implementing a task, the agent will:
210 | 
211 | - Reference the task's details section for implementation specifics
212 | - Consider dependencies on previous tasks
213 | - Follow the project's coding standards
214 | - Create appropriate tests based on the task's testStrategy
215 | 
216 | You can ask:
217 | 
218 | ```
219 | Let's implement task 3. What does it involve?
220 | ```
221 | 
222 | ### 3. Task Verification
223 | 
224 | Before marking a task as complete, verify it according to:
225 | 
226 | - The task's specified testStrategy
227 | - Any automated tests in the codebase
228 | - Manual verification if required
229 | 
230 | ### 4. Task Completion
231 | 
232 | When a task is completed, tell the agent:
233 | 
234 | ```
235 | Task 3 is now complete. Please update its status.
236 | ```
237 | 
238 | The agent will execute:
239 | 
240 | ```bash
241 | task-master set-status --id=3 --status=done
242 | ```
243 | 
244 | ### 5. Handling Implementation Drift
245 | 
246 | If during implementation, you discover that:
247 | 
248 | - The current approach differs significantly from what was planned
249 | - Future tasks need to be modified due to current implementation choices
250 | - New dependencies or requirements have emerged
251 | 
252 | Tell the agent:
253 | 
254 | ```
255 | We've changed our approach. We're now using Express instead of Fastify. Please update all future tasks to reflect this change.
256 | ```
257 | 
258 | The agent will execute:
259 | 
260 | ```bash
261 | task-master update --from=4 --prompt="Now we are using Express instead of Fastify."
262 | ```
263 | 
264 | This will rewrite or re-scope subsequent tasks in tasks.json while preserving completed work.
265 | 
266 | ### 6. Breaking Down Complex Tasks
267 | 
268 | For complex tasks that need more granularity:
269 | 
270 | ```
271 | Task 5 seems complex. Can you break it down into subtasks?
272 | ```
273 | 
274 | The agent will execute:
275 | 
276 | ```bash
277 | task-master expand --id=5 --num=3
278 | ```
279 | 
280 | You can provide additional context:
281 | 
282 | ```
283 | Please break down task 5 with a focus on security considerations.
284 | ```
285 | 
286 | The agent will execute:
287 | 
288 | ```bash
289 | task-master expand --id=5 --prompt="Focus on security aspects"
290 | ```
291 | 
292 | You can also expand all pending tasks:
293 | 
294 | ```
295 | Please break down all pending tasks into subtasks.
296 | ```
297 | 
298 | The agent will execute:
299 | 
300 | ```bash
301 | task-master expand --all
302 | ```
303 | 
304 | For research-backed subtask generation using Perplexity AI:
305 | 
306 | ```
307 | Please break down task 5 using research-backed generation.
308 | ```
309 | 
310 | The agent will execute:
311 | 
312 | ```bash
313 | task-master expand --id=5 --research
314 | ```
315 | 
316 | ## Command Reference
317 | 
318 | Here's a comprehensive reference of all available commands:
319 | 
320 | ### Parse PRD
321 | 
322 | ```bash
323 | # Parse a PRD file and generate tasks
324 | task-master parse-prd <prd-file.txt>
325 | 
326 | # Limit the number of tasks generated (default is 10)
327 | task-master parse-prd <prd-file.txt> --num-tasks=5
328 | 
329 | # Allow task master to determine the number of tasks based on complexity
330 | task-master parse-prd <prd-file.txt> --num-tasks=0
331 | ```
332 | 
333 | ### List Tasks
334 | 
335 | ```bash
336 | # List all tasks
337 | task-master list
338 | 
339 | # List tasks with a specific status
340 | task-master list --status=<status>
341 | 
342 | # List tasks with subtasks
343 | task-master list --with-subtasks
344 | 
345 | # List tasks with a specific status and include subtasks
346 | task-master list --status=<status> --with-subtasks
347 | ```
348 | 
349 | ### Show Next Task
350 | 
351 | ```bash
352 | # Show the next task to work on based on dependencies and status
353 | task-master next
354 | ```
355 | 
356 | ### Show Specific Task
357 | 
358 | ```bash
359 | # Show details of a specific task
360 | task-master show <id>
361 | # or
362 | task-master show --id=<id>
363 | 
364 | # View a specific subtask (e.g., subtask 2 of task 1)
365 | task-master show 1.2
366 | ```
367 | 
368 | ### Update Tasks
369 | 
370 | ```bash
371 | # Update tasks from a specific ID and provide context
372 | task-master update --from=<id> --prompt="<prompt>"
373 | ```
374 | 
375 | ### Generate Task Files
376 | 
377 | ```bash
378 | # Generate individual task files from tasks.json
379 | task-master generate
380 | ```
381 | 
382 | ### Set Task Status
383 | 
384 | ```bash
385 | # Set status of a single task
386 | task-master set-status --id=<id> --status=<status>
387 | 
388 | # Set status for multiple tasks
389 | task-master set-status --id=1,2,3 --status=<status>
390 | 
391 | # Set status for subtasks
392 | task-master set-status --id=1.1,1.2 --status=<status>
393 | ```
394 | 
395 | When marking a task as "done", all of its subtasks will automatically be marked as "done" as well.
396 | 
397 | ### Expand Tasks
398 | 
399 | ```bash
400 | # Expand a specific task with subtasks
401 | task-master expand --id=<id> --num=<number>
402 | 
403 | # Expand a task with a dynamic number of subtasks (ignoring complexity report)
404 | task-master expand --id=<id> --num=0
405 | 
406 | # Expand with additional context
407 | task-master expand --id=<id> --prompt="<context>"
408 | 
409 | # Expand all pending tasks
410 | task-master expand --all
411 | 
412 | # Force regeneration of subtasks for tasks that already have them
413 | task-master expand --all --force
414 | 
415 | # Research-backed subtask generation for a specific task
416 | task-master expand --id=<id> --research
417 | 
418 | # Research-backed generation for all tasks
419 | task-master expand --all --research
420 | ```
421 | 
422 | ### Clear Subtasks
423 | 
424 | ```bash
425 | # Clear subtasks from a specific task
426 | task-master clear-subtasks --id=<id>
427 | 
428 | # Clear subtasks from multiple tasks
429 | task-master clear-subtasks --id=1,2,3
430 | 
431 | # Clear subtasks from all tasks
432 | task-master clear-subtasks --all
433 | ```
434 | 
435 | ### Analyze Task Complexity
436 | 
437 | ```bash
438 | # Analyze complexity of all tasks
439 | task-master analyze-complexity
440 | 
441 | # Save report to a custom location
442 | task-master analyze-complexity --output=my-report.json
443 | 
444 | # Use a specific LLM model
445 | task-master analyze-complexity --model=claude-3-opus-20240229
446 | 
447 | # Set a custom complexity threshold (1-10)
448 | task-master analyze-complexity --threshold=6
449 | 
450 | # Use an alternative tasks file
451 | task-master analyze-complexity --file=custom-tasks.json
452 | 
453 | # Use Perplexity AI for research-backed complexity analysis
454 | task-master analyze-complexity --research
455 | ```
456 | 
457 | ### View Complexity Report
458 | 
459 | ```bash
460 | # Display the task complexity analysis report
461 | task-master complexity-report
462 | 
463 | # View a report at a custom location
464 | task-master complexity-report --file=my-report.json
465 | ```
466 | 
467 | ### Managing Task Dependencies
468 | 
469 | ```bash
470 | # Add a dependency to a task
471 | task-master add-dependency --id=<id> --depends-on=<id>
472 | 
473 | # Remove a dependency from a task
474 | task-master remove-dependency --id=<id> --depends-on=<id>
475 | 
476 | # Validate dependencies without fixing them
477 | task-master validate-dependencies
478 | 
479 | # Find and fix invalid dependencies automatically
480 | task-master fix-dependencies
481 | ```
482 | 
483 | ### Add a New Task
484 | 
485 | ```bash
486 | # Add a new task using AI
487 | task-master add-task --prompt="Description of the new task"
488 | 
489 | # Add a task with dependencies
490 | task-master add-task --prompt="Description" --dependencies=1,2,3
491 | 
492 | # Add a task with priority
493 | task-master add-task --prompt="Description" --priority=high
494 | ```
495 | 
496 | ## Feature Details
497 | 
498 | ### Analyzing Task Complexity
499 | 
500 | The `analyze-complexity` command:
501 | 
502 | - Analyzes each task using AI to assess its complexity on a scale of 1-10
503 | - Recommends optimal number of subtasks based on configured DEFAULT_SUBTASKS
504 | - Generates tailored prompts for expanding each task
505 | - Creates a comprehensive JSON report with ready-to-use commands
506 | - Saves the report to scripts/task-complexity-report.json by default
507 | 
508 | The generated report contains:
509 | 
510 | - Complexity analysis for each task (scored 1-10)
511 | - Recommended number of subtasks based on complexity
512 | - AI-generated expansion prompts customized for each task
513 | - Ready-to-run expansion commands directly within each task analysis
514 | 
515 | ### Viewing Complexity Report
516 | 
517 | The `complexity-report` command:
518 | 
519 | - Displays a formatted, easy-to-read version of the complexity analysis report
520 | - Shows tasks organized by complexity score (highest to lowest)
521 | - Provides complexity distribution statistics (low, medium, high)
522 | - Highlights tasks recommended for expansion based on threshold score
523 | - Includes ready-to-use expansion commands for each complex task
524 | - If no report exists, offers to generate one on the spot
525 | 
526 | ### Smart Task Expansion
527 | 
528 | The `expand` command automatically checks for and uses the complexity report:
529 | 
530 | When a complexity report exists:
531 | 
532 | - Tasks are automatically expanded using the recommended subtask count and prompts
533 | - When expanding all tasks, they're processed in order of complexity (highest first)
534 | - Research-backed generation is preserved from the complexity analysis
535 | - You can still override recommendations with explicit command-line options
536 | 
537 | Example workflow:
538 | 
539 | ```bash
540 | # Generate the complexity analysis report with research capabilities
541 | task-master analyze-complexity --research
542 | 
543 | # Review the report in a readable format
544 | task-master complexity-report
545 | 
546 | # Expand tasks using the optimized recommendations
547 | task-master expand --id=8
548 | # or expand all tasks
549 | task-master expand --all
550 | ```
551 | 
552 | ### Finding the Next Task
553 | 
554 | The `next` command:
555 | 
556 | - Identifies tasks that are pending/in-progress and have all dependencies satisfied
557 | - Prioritizes tasks by priority level, dependency count, and task ID
558 | - Displays comprehensive information about the selected task:
559 |   - Basic task details (ID, title, priority, dependencies)
560 |   - Implementation details
561 |   - Subtasks (if they exist)
562 | - Provides contextual suggested actions:
563 |   - Command to mark the task as in-progress
564 |   - Command to mark the task as done
565 |   - Commands for working with subtasks
566 | 
567 | ### Viewing Specific Task Details
568 | 
569 | The `show` command:
570 | 
571 | - Displays comprehensive details about a specific task or subtask
572 | - Shows task status, priority, dependencies, and detailed implementation notes
573 | - For parent tasks, displays all subtasks and their status
574 | - For subtasks, shows parent task relationship
575 | - Provides contextual action suggestions based on the task's state
576 | - Works with both regular tasks and subtasks (using the format taskId.subtaskId)
577 | 
578 | ## Best Practices for AI-Driven Development
579 | 
580 | 1. **Start with a detailed PRD**: The more detailed your PRD, the better the generated tasks will be.
581 | 
582 | 2. **Review generated tasks**: After parsing the PRD, review the tasks to ensure they make sense and have appropriate dependencies.
583 | 
584 | 3. **Analyze task complexity**: Use the complexity analysis feature to identify which tasks should be broken down further.
585 | 
586 | 4. **Follow the dependency chain**: Always respect task dependencies - the Cursor agent will help with this.
587 | 
588 | 5. **Update as you go**: If your implementation diverges from the plan, use the update command to keep future tasks aligned with your current approach.
589 | 
590 | 6. **Break down complex tasks**: Use the expand command to break down complex tasks into manageable subtasks.
591 | 
592 | 7. **Regenerate task files**: After any updates to tasks.json, regenerate the task files to keep them in sync.
593 | 
594 | 8. **Communicate context to the agent**: When asking the Cursor agent to help with a task, provide context about what you're trying to achieve.
595 | 
596 | 9. **Validate dependencies**: Periodically run the validate-dependencies command to check for invalid or circular dependencies.
597 | 
598 | ## Example Cursor AI Interactions
599 | 
600 | ### Starting a new project
601 | 
602 | ```
603 | I've just initialized a new project with Claude Task Master. I have a PRD at scripts/prd.txt.
604 | Can you help me parse it and set up the initial tasks?
605 | ```
606 | 
607 | ### Working on tasks
608 | 
609 | ```
610 | What's the next task I should work on? Please consider dependencies and priorities.
611 | ```
612 | 
613 | ### Implementing a specific task
614 | 
615 | ```
616 | I'd like to implement task 4. Can you help me understand what needs to be done and how to approach it?
617 | ```
618 | 
619 | ### Managing subtasks
620 | 
621 | ```
622 | I need to regenerate the subtasks for task 3 with a different approach. Can you help me clear and regenerate them?
623 | ```
624 | 
625 | ### Handling changes
626 | 
627 | ```
628 | We've decided to use MongoDB instead of PostgreSQL. Can you update all future tasks to reflect this change?
629 | ```
630 | 
631 | ### Completing work
632 | 
633 | ```
634 | I've finished implementing the authentication system described in task 2. All tests are passing.
635 | Please mark it as complete and tell me what I should work on next.
636 | ```
637 | 
638 | ### Analyzing complexity
639 | 
640 | ```
641 | Can you analyze the complexity of our tasks to help me understand which ones need to be broken down further?
642 | ```
643 | 
644 | ### Viewing complexity report
645 | 
646 | ```
647 | Can you show me the complexity report in a more readable format?
648 | ```
649 | 
```
Page 32/52FirstPrevNextLast