#
tokens: 34334/50000 1/975 files (page 61/69)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 61 of 69. 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
│   ├── commands
│   │   └── dedupe.md
│   └── TM_COMMANDS_GUIDE.md
├── .claude-plugin
│   └── marketplace.json
├── .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
│   │   └── validate-changesets.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
│   │   ├── autonomous-tdd-git-workflow.md
│   │   ├── 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
│   │   ├── tdd-workflow-phase-0-spike.md
│   │   ├── tdd-workflow-phase-1-core-rails.md
│   │   ├── tdd-workflow-phase-1-orchestrator.md
│   │   ├── tdd-workflow-phase-2-pr-resumability.md
│   │   ├── tdd-workflow-phase-3-extensibility-guardrails.md
│   │   ├── test-prd.txt
│   │   └── tm-core-phase-1.txt
│   ├── reports
│   │   ├── task-complexity-report_autonomous-tdd-git-workflow.json
│   │   ├── task-complexity-report_cc-kiro-hooks.json
│   │   ├── task-complexity-report_tdd-phase-1-core-rails.json
│   │   ├── task-complexity-report_tdd-workflow-phase-0.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_rpg.md
│       └── example_prd.md
├── .vscode
│   ├── extensions.json
│   └── settings.json
├── apps
│   ├── cli
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── command-registry.ts
│   │   │   ├── commands
│   │   │   │   ├── auth.command.ts
│   │   │   │   ├── autopilot
│   │   │   │   │   ├── abort.command.ts
│   │   │   │   │   ├── commit.command.ts
│   │   │   │   │   ├── complete.command.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── next.command.ts
│   │   │   │   │   ├── resume.command.ts
│   │   │   │   │   ├── shared.ts
│   │   │   │   │   ├── start.command.ts
│   │   │   │   │   └── status.command.ts
│   │   │   │   ├── briefs.command.ts
│   │   │   │   ├── context.command.ts
│   │   │   │   ├── export.command.ts
│   │   │   │   ├── list.command.ts
│   │   │   │   ├── models
│   │   │   │   │   ├── custom-providers.ts
│   │   │   │   │   ├── fetchers.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── prompts.ts
│   │   │   │   │   ├── setup.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── next.command.ts
│   │   │   │   ├── set-status.command.ts
│   │   │   │   ├── show.command.ts
│   │   │   │   ├── start.command.ts
│   │   │   │   └── tags.command.ts
│   │   │   ├── index.ts
│   │   │   ├── lib
│   │   │   │   └── model-management.ts
│   │   │   ├── types
│   │   │   │   └── tag-management.d.ts
│   │   │   ├── ui
│   │   │   │   ├── components
│   │   │   │   │   ├── cardBox.component.ts
│   │   │   │   │   ├── dashboard.component.ts
│   │   │   │   │   ├── header.component.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── next-task.component.ts
│   │   │   │   │   ├── suggested-steps.component.ts
│   │   │   │   │   └── task-detail.component.ts
│   │   │   │   ├── display
│   │   │   │   │   ├── messages.ts
│   │   │   │   │   └── tables.ts
│   │   │   │   ├── formatters
│   │   │   │   │   ├── complexity-formatters.ts
│   │   │   │   │   ├── dependency-formatters.ts
│   │   │   │   │   ├── priority-formatters.ts
│   │   │   │   │   ├── status-formatters.spec.ts
│   │   │   │   │   └── status-formatters.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── layout
│   │   │   │       ├── helpers.spec.ts
│   │   │   │       └── helpers.ts
│   │   │   └── utils
│   │   │       ├── auth-helpers.ts
│   │   │       ├── auto-update.ts
│   │   │       ├── brief-selection.ts
│   │   │       ├── display-helpers.ts
│   │   │       ├── error-handler.ts
│   │   │       ├── index.ts
│   │   │       ├── project-root.ts
│   │   │       ├── task-status.ts
│   │   │       ├── ui.spec.ts
│   │   │       └── ui.ts
│   │   ├── tests
│   │   │   ├── integration
│   │   │   │   └── commands
│   │   │   │       └── autopilot
│   │   │   │           └── workflow.test.ts
│   │   │   └── unit
│   │   │       ├── commands
│   │   │       │   ├── autopilot
│   │   │       │   │   └── shared.test.ts
│   │   │       │   ├── list.command.spec.ts
│   │   │       │   └── show.command.spec.ts
│   │   │       └── ui
│   │   │           └── dashboard.component.spec.ts
│   │   ├── tsconfig.json
│   │   └── vitest.config.ts
│   ├── 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
│   │   │   ├── rpg-method.mdx
│   │   │   └── task-structure.mdx
│   │   ├── CHANGELOG.md
│   │   ├── command-reference.mdx
│   │   ├── configuration.mdx
│   │   ├── docs.json
│   │   ├── favicon.svg
│   │   ├── getting-started
│   │   │   ├── api-keys.mdx
│   │   │   ├── 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
│   │   ├── tdd-workflow
│   │   │   ├── ai-agent-integration.mdx
│   │   │   └── quickstart.mdx
│   │   ├── 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
│   └── mcp
│       ├── CHANGELOG.md
│       ├── package.json
│       ├── src
│       │   ├── index.ts
│       │   ├── shared
│       │   │   ├── types.ts
│       │   │   └── utils.ts
│       │   └── tools
│       │       ├── autopilot
│       │       │   ├── abort.tool.ts
│       │       │   ├── commit.tool.ts
│       │       │   ├── complete.tool.ts
│       │       │   ├── finalize.tool.ts
│       │       │   ├── index.ts
│       │       │   ├── next.tool.ts
│       │       │   ├── resume.tool.ts
│       │       │   ├── start.tool.ts
│       │       │   └── status.tool.ts
│       │       ├── README-ZOD-V3.md
│       │       └── tasks
│       │           ├── get-task.tool.ts
│       │           ├── get-tasks.tool.ts
│       │           └── index.ts
│       ├── tsconfig.json
│       └── vitest.config.ts
├── assets
│   ├── .windsurfrules
│   ├── AGENTS.md
│   ├── claude
│   │   └── TM_COMMANDS_GUIDE.md
│   ├── config.json
│   ├── env.example
│   ├── example_prd_rpg.txt
│   ├── example_prd.txt
│   ├── GEMINI.md
│   ├── 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_CODE_PLUGIN.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
│   ├── claude-code-integration.md
│   ├── CLI-COMMANDER-PATTERN.md
│   ├── command-reference.md
│   ├── configuration.md
│   ├── contributor-docs
│   │   ├── testing-roo-integration.md
│   │   └── worktree-setup.md
│   ├── cross-tag-task-movement.md
│   ├── examples
│   │   ├── claude-code-usage.md
│   │   └── codex-cli-usage.md
│   ├── examples.md
│   ├── licensing.md
│   ├── mcp-provider-guide.md
│   ├── mcp-provider.md
│   ├── migration-guide.md
│   ├── models.md
│   ├── providers
│   │   ├── codex-cli.md
│   │   └── gemini-cli.md
│   ├── README.md
│   ├── scripts
│   │   └── models-json-to-markdown.js
│   ├── task-structure.md
│   └── tutorial.md
├── images
│   ├── hamster-hiring.png
│   └── 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
│       │   │   ├── 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
│       │   │   ├── 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
│           ├── index.js
│           ├── initialize-project.js
│           ├── list-tags.js
│           ├── models.js
│           ├── move-task.js
│           ├── next-task.js
│           ├── parse-prd.js
│           ├── README-ZOD-V3.md
│           ├── 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
│           ├── tool-registry.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
│   ├── ai-sdk-provider-grok-cli
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── errors.test.ts
│   │   │   ├── errors.ts
│   │   │   ├── grok-cli-language-model.ts
│   │   │   ├── grok-cli-provider.test.ts
│   │   │   ├── grok-cli-provider.ts
│   │   │   ├── index.ts
│   │   │   ├── json-extractor.test.ts
│   │   │   ├── json-extractor.ts
│   │   │   ├── message-converter.test.ts
│   │   │   ├── message-converter.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   ├── build-config
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src
│   │   │   └── tsdown.base.ts
│   │   └── tsconfig.json
│   ├── claude-code-plugin
│   │   ├── .claude-plugin
│   │   │   └── plugin.json
│   │   ├── .gitignore
│   │   ├── agents
│   │   │   ├── task-checker.md
│   │   │   ├── task-executor.md
│   │   │   └── task-orchestrator.md
│   │   ├── CHANGELOG.md
│   │   ├── commands
│   │   │   ├── add-dependency.md
│   │   │   ├── add-subtask.md
│   │   │   ├── add-task.md
│   │   │   ├── analyze-complexity.md
│   │   │   ├── analyze-project.md
│   │   │   ├── auto-implement-tasks.md
│   │   │   ├── command-pipeline.md
│   │   │   ├── complexity-report.md
│   │   │   ├── convert-task-to-subtask.md
│   │   │   ├── expand-all-tasks.md
│   │   │   ├── expand-task.md
│   │   │   ├── fix-dependencies.md
│   │   │   ├── generate-tasks.md
│   │   │   ├── help.md
│   │   │   ├── init-project-quick.md
│   │   │   ├── init-project.md
│   │   │   ├── install-taskmaster.md
│   │   │   ├── learn.md
│   │   │   ├── list-tasks-by-status.md
│   │   │   ├── list-tasks-with-subtasks.md
│   │   │   ├── list-tasks.md
│   │   │   ├── next-task.md
│   │   │   ├── parse-prd-with-research.md
│   │   │   ├── parse-prd.md
│   │   │   ├── project-status.md
│   │   │   ├── quick-install-taskmaster.md
│   │   │   ├── remove-all-subtasks.md
│   │   │   ├── remove-dependency.md
│   │   │   ├── remove-subtask.md
│   │   │   ├── remove-subtasks.md
│   │   │   ├── remove-task.md
│   │   │   ├── setup-models.md
│   │   │   ├── show-task.md
│   │   │   ├── smart-workflow.md
│   │   │   ├── sync-readme.md
│   │   │   ├── tm-main.md
│   │   │   ├── to-cancelled.md
│   │   │   ├── to-deferred.md
│   │   │   ├── to-done.md
│   │   │   ├── to-in-progress.md
│   │   │   ├── to-pending.md
│   │   │   ├── to-review.md
│   │   │   ├── update-single-task.md
│   │   │   ├── update-task.md
│   │   │   ├── update-tasks-from-id.md
│   │   │   ├── validate-dependencies.md
│   │   │   └── view-models.md
│   │   ├── mcp.json
│   │   └── package.json
│   ├── tm-bridge
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── add-tag-bridge.ts
│   │   │   ├── bridge-types.ts
│   │   │   ├── bridge-utils.ts
│   │   │   ├── expand-bridge.ts
│   │   │   ├── index.ts
│   │   │   ├── tags-bridge.ts
│   │   │   ├── update-bridge.ts
│   │   │   └── use-tag-bridge.ts
│   │   └── tsconfig.json
│   └── tm-core
│       ├── .gitignore
│       ├── CHANGELOG.md
│       ├── docs
│       │   └── listTasks-architecture.md
│       ├── package.json
│       ├── POC-STATUS.md
│       ├── README.md
│       ├── src
│       │   ├── common
│       │   │   ├── constants
│       │   │   │   ├── index.ts
│       │   │   │   ├── paths.ts
│       │   │   │   └── providers.ts
│       │   │   ├── errors
│       │   │   │   ├── index.ts
│       │   │   │   └── task-master-error.ts
│       │   │   ├── interfaces
│       │   │   │   ├── configuration.interface.ts
│       │   │   │   ├── index.ts
│       │   │   │   └── storage.interface.ts
│       │   │   ├── logger
│       │   │   │   ├── factory.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── logger.spec.ts
│       │   │   │   └── logger.ts
│       │   │   ├── mappers
│       │   │   │   ├── TaskMapper.test.ts
│       │   │   │   └── TaskMapper.ts
│       │   │   ├── types
│       │   │   │   ├── database.types.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── legacy.ts
│       │   │   │   └── repository-types.ts
│       │   │   └── utils
│       │   │       ├── git-utils.ts
│       │   │       ├── id-generator.ts
│       │   │       ├── index.ts
│       │   │       ├── path-helpers.ts
│       │   │       ├── path-normalizer.spec.ts
│       │   │       ├── path-normalizer.ts
│       │   │       ├── project-root-finder.spec.ts
│       │   │       ├── project-root-finder.ts
│       │   │       ├── run-id-generator.spec.ts
│       │   │       └── run-id-generator.ts
│       │   ├── index.ts
│       │   ├── modules
│       │   │   ├── ai
│       │   │   │   ├── index.ts
│       │   │   │   ├── interfaces
│       │   │   │   │   └── ai-provider.interface.ts
│       │   │   │   └── providers
│       │   │   │       ├── base-provider.ts
│       │   │   │       └── index.ts
│       │   │   ├── auth
│       │   │   │   ├── auth-domain.spec.ts
│       │   │   │   ├── auth-domain.ts
│       │   │   │   ├── config.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── managers
│       │   │   │   │   ├── auth-manager.spec.ts
│       │   │   │   │   └── auth-manager.ts
│       │   │   │   ├── services
│       │   │   │   │   ├── context-store.ts
│       │   │   │   │   ├── oauth-service.ts
│       │   │   │   │   ├── organization.service.ts
│       │   │   │   │   ├── supabase-session-storage.spec.ts
│       │   │   │   │   └── supabase-session-storage.ts
│       │   │   │   └── types.ts
│       │   │   ├── briefs
│       │   │   │   ├── briefs-domain.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── services
│       │   │   │   │   └── brief-service.ts
│       │   │   │   ├── types.ts
│       │   │   │   └── utils
│       │   │   │       └── url-parser.ts
│       │   │   ├── commands
│       │   │   │   └── index.ts
│       │   │   ├── config
│       │   │   │   ├── config-domain.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── managers
│       │   │   │   │   ├── config-manager.spec.ts
│       │   │   │   │   └── config-manager.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
│       │   │   ├── dependencies
│       │   │   │   └── index.ts
│       │   │   ├── execution
│       │   │   │   ├── executors
│       │   │   │   │   ├── base-executor.ts
│       │   │   │   │   ├── claude-executor.ts
│       │   │   │   │   └── executor-factory.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── services
│       │   │   │   │   └── executor-service.ts
│       │   │   │   └── types.ts
│       │   │   ├── git
│       │   │   │   ├── adapters
│       │   │   │   │   ├── git-adapter.test.ts
│       │   │   │   │   └── git-adapter.ts
│       │   │   │   ├── git-domain.ts
│       │   │   │   ├── index.ts
│       │   │   │   └── services
│       │   │   │       ├── branch-name-generator.spec.ts
│       │   │   │       ├── branch-name-generator.ts
│       │   │   │       ├── commit-message-generator.test.ts
│       │   │   │       ├── commit-message-generator.ts
│       │   │   │       ├── scope-detector.test.ts
│       │   │   │       ├── scope-detector.ts
│       │   │   │       ├── template-engine.test.ts
│       │   │   │       └── template-engine.ts
│       │   │   ├── integration
│       │   │   │   ├── clients
│       │   │   │   │   ├── index.ts
│       │   │   │   │   └── supabase-client.ts
│       │   │   │   ├── integration-domain.ts
│       │   │   │   └── services
│       │   │   │       ├── export.service.ts
│       │   │   │       ├── task-expansion.service.ts
│       │   │   │       └── task-retrieval.service.ts
│       │   │   ├── reports
│       │   │   │   ├── index.ts
│       │   │   │   ├── managers
│       │   │   │   │   └── complexity-report-manager.ts
│       │   │   │   └── types.ts
│       │   │   ├── storage
│       │   │   │   ├── adapters
│       │   │   │   │   ├── activity-logger.ts
│       │   │   │   │   ├── api-storage.ts
│       │   │   │   │   └── file-storage
│       │   │   │   │       ├── file-operations.ts
│       │   │   │   │       ├── file-storage.ts
│       │   │   │   │       ├── format-handler.ts
│       │   │   │   │       ├── index.ts
│       │   │   │   │       └── path-resolver.ts
│       │   │   │   ├── index.ts
│       │   │   │   ├── services
│       │   │   │   │   └── storage-factory.ts
│       │   │   │   └── utils
│       │   │   │       └── api-client.ts
│       │   │   ├── tasks
│       │   │   │   ├── entities
│       │   │   │   │   └── task.entity.ts
│       │   │   │   ├── parser
│       │   │   │   │   └── index.ts
│       │   │   │   ├── repositories
│       │   │   │   │   ├── supabase
│       │   │   │   │   │   ├── dependency-fetcher.ts
│       │   │   │   │   │   ├── index.ts
│       │   │   │   │   │   └── supabase-repository.ts
│       │   │   │   │   └── task-repository.interface.ts
│       │   │   │   ├── services
│       │   │   │   │   ├── preflight-checker.service.ts
│       │   │   │   │   ├── tag.service.ts
│       │   │   │   │   ├── task-execution-service.ts
│       │   │   │   │   ├── task-loader.service.ts
│       │   │   │   │   └── task-service.ts
│       │   │   │   └── tasks-domain.ts
│       │   │   ├── ui
│       │   │   │   └── index.ts
│       │   │   └── workflow
│       │   │       ├── managers
│       │   │       │   ├── workflow-state-manager.spec.ts
│       │   │       │   └── workflow-state-manager.ts
│       │   │       ├── orchestrators
│       │   │       │   ├── workflow-orchestrator.test.ts
│       │   │       │   └── workflow-orchestrator.ts
│       │   │       ├── services
│       │   │       │   ├── test-result-validator.test.ts
│       │   │       │   ├── test-result-validator.ts
│       │   │       │   ├── test-result-validator.types.ts
│       │   │       │   ├── workflow-activity-logger.ts
│       │   │       │   └── workflow.service.ts
│       │   │       ├── types.ts
│       │   │       └── workflow-domain.ts
│       │   ├── subpath-exports.test.ts
│       │   ├── tm-core.ts
│       │   └── utils
│       │       └── time.utils.ts
│       ├── tests
│       │   ├── auth
│       │   │   └── auth-refresh.test.ts
│       │   ├── integration
│       │   │   ├── auth-token-refresh.test.ts
│       │   │   ├── list-tasks.test.ts
│       │   │   └── storage
│       │   │       └── activity-logger.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
│   ├── create-worktree.sh
│   ├── dev.js
│   ├── init.js
│   ├── list-worktrees.sh
│   ├── modules
│   │   ├── ai-services-unified.js
│   │   ├── bridge-utils.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
├── sonar-project.properties
├── src
│   ├── ai-providers
│   │   ├── anthropic.js
│   │   ├── azure.js
│   │   ├── base-provider.js
│   │   ├── bedrock.js
│   │   ├── claude-code.js
│   │   ├── codex-cli.js
│   │   ├── gemini-cli.js
│   │   ├── google-vertex.js
│   │   ├── google.js
│   │   ├── grok-cli.js
│   │   ├── groq.js
│   │   ├── index.js
│   │   ├── lmstudio.js
│   │   ├── ollama.js
│   │   ├── openai-compatible.js
│   │   ├── openai.js
│   │   ├── openrouter.js
│   │   ├── perplexity.js
│   │   ├── xai.js
│   │   ├── zai-coding.js
│   │   └── zai.js
│   ├── constants
│   │   ├── commands.js
│   │   ├── paths.js
│   │   ├── profiles.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
│   ├── schemas
│   │   ├── add-task.js
│   │   ├── analyze-complexity.js
│   │   ├── base-schemas.js
│   │   ├── expand-task.js
│   │   ├── parse-prd.js
│   │   ├── registry.js
│   │   ├── update-subtask.js
│   │   ├── update-task.js
│   │   └── update-tasks.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
│   ├── fixtures
│   │   ├── .taskmasterconfig
│   │   ├── sample-claude-response.js
│   │   ├── sample-prd.txt
│   │   └── sample-tasks.js
│   ├── helpers
│   │   └── tool-counts.js
│   ├── integration
│   │   ├── claude-code-error-handling.test.js
│   │   ├── 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
│   │   └── providers
│   │       └── temperature-support.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
│       │   ├── base-provider.test.js
│       │   ├── claude-code.test.js
│       │   ├── codex-cli.test.js
│       │   ├── gemini-cli.test.js
│       │   ├── lmstudio.test.js
│       │   ├── mcp-components.test.js
│       │   ├── openai-compatible.test.js
│       │   ├── openai.test.js
│       │   ├── provider-registry.test.js
│       │   ├── zai-coding.test.js
│       │   ├── zai-provider.test.js
│       │   ├── zai-schema-introspection.test.js
│       │   └── zai.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
│       │       └── tool-registration.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
│       │   └── prompt-migration.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
│       │       │   ├── models-baseurl.test.js
│       │       │   ├── move-task-cross-tag.test.js
│       │       │   ├── move-task.test.js
│       │       │   ├── parse-prd-schema.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
└── update-task-migration-plan.md
```

# Files

--------------------------------------------------------------------------------
/scripts/modules/dependency-manager.js:
--------------------------------------------------------------------------------

```javascript
   1 | /**
   2 |  * dependency-manager.js
   3 |  * Manages task dependencies and relationships
   4 |  */
   5 | 
   6 | import path from 'path';
   7 | import chalk from 'chalk';
   8 | import boxen from 'boxen';
   9 | 
  10 | import {
  11 | 	log,
  12 | 	readJSON,
  13 | 	writeJSON,
  14 | 	taskExists,
  15 | 	formatTaskId,
  16 | 	findCycles,
  17 | 	traverseDependencies,
  18 | 	isSilentMode
  19 | } from './utils.js';
  20 | 
  21 | import { displayBanner } from './ui.js';
  22 | 
  23 | import generateTaskFiles from './task-manager/generate-task-files.js';
  24 | 
  25 | /**
  26 |  * Structured error class for dependency operations
  27 |  */
  28 | class DependencyError extends Error {
  29 | 	constructor(code, message, data = {}) {
  30 | 		super(message);
  31 | 		this.name = 'DependencyError';
  32 | 		this.code = code;
  33 | 		this.data = data;
  34 | 	}
  35 | }
  36 | 
  37 | /**
  38 |  * Error codes for dependency operations
  39 |  */
  40 | const DEPENDENCY_ERROR_CODES = {
  41 | 	CANNOT_MOVE_SUBTASK: 'CANNOT_MOVE_SUBTASK',
  42 | 	INVALID_TASK_ID: 'INVALID_TASK_ID',
  43 | 	INVALID_SOURCE_TAG: 'INVALID_SOURCE_TAG',
  44 | 	INVALID_TARGET_TAG: 'INVALID_TARGET_TAG'
  45 | };
  46 | 
  47 | /**
  48 |  * Add a dependency to a task
  49 |  * @param {string} tasksPath - Path to the tasks.json file
  50 |  * @param {number|string} taskId - ID of the task to add dependency to
  51 |  * @param {number|string} dependencyId - ID of the task to add as dependency
  52 |  * @param {Object} context - Context object containing projectRoot and tag information
  53 |  * @param {string} [context.projectRoot] - Project root path
  54 |  * @param {string} [context.tag] - Tag for the task
  55 |  */
  56 | async function addDependency(tasksPath, taskId, dependencyId, context = {}) {
  57 | 	log('info', `Adding dependency ${dependencyId} to task ${taskId}...`);
  58 | 
  59 | 	const data = readJSON(tasksPath, context.projectRoot, context.tag);
  60 | 	if (!data || !data.tasks) {
  61 | 		log('error', 'No valid tasks found in tasks.json');
  62 | 		process.exit(1);
  63 | 	}
  64 | 
  65 | 	// Format the task and dependency IDs correctly
  66 | 	const formattedTaskId =
  67 | 		typeof taskId === 'string' && taskId.includes('.')
  68 | 			? taskId
  69 | 			: parseInt(taskId, 10);
  70 | 
  71 | 	const formattedDependencyId = formatTaskId(dependencyId);
  72 | 
  73 | 	// Check if the dependency task or subtask actually exists
  74 | 	if (!taskExists(data.tasks, formattedDependencyId)) {
  75 | 		log(
  76 | 			'error',
  77 | 			`Dependency target ${formattedDependencyId} does not exist in tasks.json`
  78 | 		);
  79 | 		process.exit(1);
  80 | 	}
  81 | 
  82 | 	// Find the task to update
  83 | 	let targetTask = null;
  84 | 	let isSubtask = false;
  85 | 
  86 | 	if (typeof formattedTaskId === 'string' && formattedTaskId.includes('.')) {
  87 | 		// Handle dot notation for subtasks (e.g., "1.2")
  88 | 		const [parentId, subtaskId] = formattedTaskId
  89 | 			.split('.')
  90 | 			.map((id) => parseInt(id, 10));
  91 | 		const parentTask = data.tasks.find((t) => t.id === parentId);
  92 | 
  93 | 		if (!parentTask) {
  94 | 			log('error', `Parent task ${parentId} not found.`);
  95 | 			process.exit(1);
  96 | 		}
  97 | 
  98 | 		if (!parentTask.subtasks) {
  99 | 			log('error', `Parent task ${parentId} has no subtasks.`);
 100 | 			process.exit(1);
 101 | 		}
 102 | 
 103 | 		targetTask = parentTask.subtasks.find((s) => s.id === subtaskId);
 104 | 		isSubtask = true;
 105 | 
 106 | 		if (!targetTask) {
 107 | 			log('error', `Subtask ${formattedTaskId} not found.`);
 108 | 			process.exit(1);
 109 | 		}
 110 | 	} else {
 111 | 		// Regular task (not a subtask)
 112 | 		targetTask = data.tasks.find((t) => t.id === formattedTaskId);
 113 | 
 114 | 		if (!targetTask) {
 115 | 			log('error', `Task ${formattedTaskId} not found.`);
 116 | 			process.exit(1);
 117 | 		}
 118 | 	}
 119 | 
 120 | 	// Initialize dependencies array if it doesn't exist
 121 | 	if (!targetTask.dependencies) {
 122 | 		targetTask.dependencies = [];
 123 | 	}
 124 | 
 125 | 	// Check if dependency already exists
 126 | 	if (
 127 | 		targetTask.dependencies.some((d) => {
 128 | 			// Convert both to strings for comparison to handle both numeric and string IDs
 129 | 			return String(d) === String(formattedDependencyId);
 130 | 		})
 131 | 	) {
 132 | 		log(
 133 | 			'warn',
 134 | 			`Dependency ${formattedDependencyId} already exists in task ${formattedTaskId}.`
 135 | 		);
 136 | 		return;
 137 | 	}
 138 | 
 139 | 	// Check if the task is trying to depend on itself - compare full IDs (including subtask parts)
 140 | 	if (String(formattedTaskId) === String(formattedDependencyId)) {
 141 | 		log('error', `Task ${formattedTaskId} cannot depend on itself.`);
 142 | 		process.exit(1);
 143 | 	}
 144 | 
 145 | 	// For subtasks of the same parent, we need to make sure we're not treating it as a self-dependency
 146 | 	// Check if we're dealing with subtasks with the same parent task
 147 | 	let isSelfDependency = false;
 148 | 
 149 | 	if (
 150 | 		typeof formattedTaskId === 'string' &&
 151 | 		typeof formattedDependencyId === 'string' &&
 152 | 		formattedTaskId.includes('.') &&
 153 | 		formattedDependencyId.includes('.')
 154 | 	) {
 155 | 		const [taskParentId] = formattedTaskId.split('.');
 156 | 		const [depParentId] = formattedDependencyId.split('.');
 157 | 
 158 | 		// Only treat it as a self-dependency if both the parent ID and subtask ID are identical
 159 | 		isSelfDependency = formattedTaskId === formattedDependencyId;
 160 | 
 161 | 		// Log for debugging
 162 | 		log(
 163 | 			'debug',
 164 | 			`Adding dependency between subtasks: ${formattedTaskId} depends on ${formattedDependencyId}`
 165 | 		);
 166 | 		log(
 167 | 			'debug',
 168 | 			`Parent IDs: ${taskParentId} and ${depParentId}, Self-dependency check: ${isSelfDependency}`
 169 | 		);
 170 | 	}
 171 | 
 172 | 	if (isSelfDependency) {
 173 | 		log('error', `Subtask ${formattedTaskId} cannot depend on itself.`);
 174 | 		process.exit(1);
 175 | 	}
 176 | 
 177 | 	// Check for circular dependencies
 178 | 	const dependencyChain = [formattedTaskId];
 179 | 	if (
 180 | 		!isCircularDependency(data.tasks, formattedDependencyId, dependencyChain)
 181 | 	) {
 182 | 		// Add the dependency
 183 | 		targetTask.dependencies.push(formattedDependencyId);
 184 | 
 185 | 		// Sort dependencies numerically or by parent task ID first, then subtask ID
 186 | 		targetTask.dependencies.sort((a, b) => {
 187 | 			if (typeof a === 'number' && typeof b === 'number') {
 188 | 				return a - b;
 189 | 			} else if (typeof a === 'string' && typeof b === 'string') {
 190 | 				const [aParent, aChild] = a.split('.').map(Number);
 191 | 				const [bParent, bChild] = b.split('.').map(Number);
 192 | 				return aParent !== bParent ? aParent - bParent : aChild - bChild;
 193 | 			} else if (typeof a === 'number') {
 194 | 				return -1; // Numbers come before strings
 195 | 			} else {
 196 | 				return 1; // Strings come after numbers
 197 | 			}
 198 | 		});
 199 | 
 200 | 		// Save changes
 201 | 		writeJSON(tasksPath, data, context.projectRoot, context.tag);
 202 | 		log(
 203 | 			'success',
 204 | 			`Added dependency ${formattedDependencyId} to task ${formattedTaskId}`
 205 | 		);
 206 | 
 207 | 		// Display a more visually appealing success message
 208 | 		if (!isSilentMode()) {
 209 | 			console.log(
 210 | 				boxen(
 211 | 					chalk.green(`Successfully added dependency:\n\n`) +
 212 | 						`Task ${chalk.bold(formattedTaskId)} now depends on ${chalk.bold(formattedDependencyId)}`,
 213 | 					{
 214 | 						padding: 1,
 215 | 						borderColor: 'green',
 216 | 						borderStyle: 'round',
 217 | 						margin: { top: 1 }
 218 | 					}
 219 | 				)
 220 | 			);
 221 | 		}
 222 | 
 223 | 		// Generate updated task files
 224 | 		// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
 225 | 
 226 | 		log('info', 'Task files regenerated with updated dependencies.');
 227 | 	} else {
 228 | 		log(
 229 | 			'error',
 230 | 			`Cannot add dependency ${formattedDependencyId} to task ${formattedTaskId} as it would create a circular dependency.`
 231 | 		);
 232 | 		process.exit(1);
 233 | 	}
 234 | }
 235 | 
 236 | /**
 237 |  * Remove a dependency from a task
 238 |  * @param {string} tasksPath - Path to the tasks.json file
 239 |  * @param {number|string} taskId - ID of the task to remove dependency from
 240 |  * @param {number|string} dependencyId - ID of the task to remove as dependency
 241 |  * @param {Object} context - Context object containing projectRoot and tag information
 242 |  * @param {string} [context.projectRoot] - Project root path
 243 |  * @param {string} [context.tag] - Tag for the task
 244 |  */
 245 | async function removeDependency(tasksPath, taskId, dependencyId, context = {}) {
 246 | 	log('info', `Removing dependency ${dependencyId} from task ${taskId}...`);
 247 | 
 248 | 	// Read tasks file
 249 | 	const data = readJSON(tasksPath, context.projectRoot, context.tag);
 250 | 	if (!data || !data.tasks) {
 251 | 		log('error', 'No valid tasks found.');
 252 | 		process.exit(1);
 253 | 	}
 254 | 
 255 | 	// Format the task and dependency IDs correctly
 256 | 	const formattedTaskId =
 257 | 		typeof taskId === 'string' && taskId.includes('.')
 258 | 			? taskId
 259 | 			: parseInt(taskId, 10);
 260 | 
 261 | 	const formattedDependencyId = formatTaskId(dependencyId);
 262 | 
 263 | 	// Find the task to update
 264 | 	let targetTask = null;
 265 | 	let isSubtask = false;
 266 | 
 267 | 	if (typeof formattedTaskId === 'string' && formattedTaskId.includes('.')) {
 268 | 		// Handle dot notation for subtasks (e.g., "1.2")
 269 | 		const [parentId, subtaskId] = formattedTaskId
 270 | 			.split('.')
 271 | 			.map((id) => parseInt(id, 10));
 272 | 		const parentTask = data.tasks.find((t) => t.id === parentId);
 273 | 
 274 | 		if (!parentTask) {
 275 | 			log('error', `Parent task ${parentId} not found.`);
 276 | 			process.exit(1);
 277 | 		}
 278 | 
 279 | 		if (!parentTask.subtasks) {
 280 | 			log('error', `Parent task ${parentId} has no subtasks.`);
 281 | 			process.exit(1);
 282 | 		}
 283 | 
 284 | 		targetTask = parentTask.subtasks.find((s) => s.id === subtaskId);
 285 | 		isSubtask = true;
 286 | 
 287 | 		if (!targetTask) {
 288 | 			log('error', `Subtask ${formattedTaskId} not found.`);
 289 | 			process.exit(1);
 290 | 		}
 291 | 	} else {
 292 | 		// Regular task (not a subtask)
 293 | 		targetTask = data.tasks.find((t) => t.id === formattedTaskId);
 294 | 
 295 | 		if (!targetTask) {
 296 | 			log('error', `Task ${formattedTaskId} not found.`);
 297 | 			process.exit(1);
 298 | 		}
 299 | 	}
 300 | 
 301 | 	// Check if the task has any dependencies
 302 | 	if (!targetTask.dependencies || targetTask.dependencies.length === 0) {
 303 | 		log(
 304 | 			'info',
 305 | 			`Task ${formattedTaskId} has no dependencies, nothing to remove.`
 306 | 		);
 307 | 		return;
 308 | 	}
 309 | 
 310 | 	// Normalize the dependency ID for comparison to handle different formats
 311 | 	const normalizedDependencyId = String(formattedDependencyId);
 312 | 
 313 | 	// Check if the dependency exists by comparing string representations
 314 | 	const dependencyIndex = targetTask.dependencies.findIndex((dep) => {
 315 | 		// Direct string comparison (handles both numeric IDs and dot notation)
 316 | 		const depStr = String(dep);
 317 | 		if (depStr === normalizedDependencyId) {
 318 | 			return true;
 319 | 		}
 320 | 
 321 | 		// For subtasks: handle numeric dependencies that might be references to other subtasks
 322 | 		// in the same parent (e.g., subtask 1.2 depending on subtask 1.1 stored as just "1")
 323 | 		if (typeof dep === 'number' && dep < 100 && isSubtask) {
 324 | 			const [parentId] = formattedTaskId.split('.');
 325 | 			const fullSubtaskRef = `${parentId}.${dep}`;
 326 | 			if (fullSubtaskRef === normalizedDependencyId) {
 327 | 				return true;
 328 | 			}
 329 | 		}
 330 | 
 331 | 		return false;
 332 | 	});
 333 | 
 334 | 	if (dependencyIndex === -1) {
 335 | 		log(
 336 | 			'info',
 337 | 			`Task ${formattedTaskId} does not depend on ${formattedDependencyId}, no changes made.`
 338 | 		);
 339 | 		return;
 340 | 	}
 341 | 
 342 | 	// Remove the dependency
 343 | 	targetTask.dependencies.splice(dependencyIndex, 1);
 344 | 
 345 | 	// Save the updated tasks
 346 | 	writeJSON(tasksPath, data, context.projectRoot, context.tag);
 347 | 
 348 | 	// Success message
 349 | 	log(
 350 | 		'success',
 351 | 		`Removed dependency: Task ${formattedTaskId} no longer depends on ${formattedDependencyId}`
 352 | 	);
 353 | 
 354 | 	if (!isSilentMode()) {
 355 | 		// Display a more visually appealing success message
 356 | 		console.log(
 357 | 			boxen(
 358 | 				chalk.green(`Successfully removed dependency:\n\n`) +
 359 | 					`Task ${chalk.bold(formattedTaskId)} no longer depends on ${chalk.bold(formattedDependencyId)}`,
 360 | 				{
 361 | 					padding: 1,
 362 | 					borderColor: 'green',
 363 | 					borderStyle: 'round',
 364 | 					margin: { top: 1 }
 365 | 				}
 366 | 			)
 367 | 		);
 368 | 	}
 369 | 
 370 | 	// Regenerate task files
 371 | 	// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
 372 | }
 373 | 
 374 | /**
 375 |  * Check if adding a dependency would create a circular dependency
 376 |  * @param {Array} tasks - Array of all tasks
 377 |  * @param {number|string} taskId - ID of task to check
 378 |  * @param {Array} chain - Chain of dependencies to check
 379 |  * @returns {boolean} True if circular dependency would be created
 380 |  */
 381 | function isCircularDependency(tasks, taskId, chain = []) {
 382 | 	// Convert taskId to string for comparison
 383 | 	const taskIdStr = String(taskId);
 384 | 
 385 | 	// If we've seen this task before in the chain, we have a circular dependency
 386 | 	if (chain.some((id) => String(id) === taskIdStr)) {
 387 | 		return true;
 388 | 	}
 389 | 
 390 | 	// Find the task or subtask
 391 | 	let task = null;
 392 | 	let parentIdForSubtask = null;
 393 | 
 394 | 	// Check if this is a subtask reference (e.g., "1.2")
 395 | 	if (taskIdStr.includes('.')) {
 396 | 		const [parentId, subtaskId] = taskIdStr.split('.').map(Number);
 397 | 		const parentTask = tasks.find((t) => t.id === parentId);
 398 | 		parentIdForSubtask = parentId; // Store parent ID if it's a subtask
 399 | 
 400 | 		if (parentTask && parentTask.subtasks) {
 401 | 			task = parentTask.subtasks.find((st) => st.id === subtaskId);
 402 | 		}
 403 | 	} else {
 404 | 		// Regular task - handle both string and numeric task IDs
 405 | 		const taskIdNum = parseInt(taskIdStr, 10);
 406 | 		task = tasks.find((t) => t.id === taskIdNum || String(t.id) === taskIdStr);
 407 | 	}
 408 | 
 409 | 	if (!task) {
 410 | 		return false; // Task doesn't exist, can't create circular dependency
 411 | 	}
 412 | 
 413 | 	// No dependencies, can't create circular dependency
 414 | 	if (!task.dependencies || task.dependencies.length === 0) {
 415 | 		return false;
 416 | 	}
 417 | 
 418 | 	// Check each dependency recursively
 419 | 	const newChain = [...chain, taskIdStr]; // Use taskIdStr for consistency
 420 | 	return task.dependencies.some((depId) => {
 421 | 		let normalizedDepId = String(depId);
 422 | 		// Normalize relative subtask dependencies
 423 | 		if (typeof depId === 'number' && parentIdForSubtask !== null) {
 424 | 			// If the current task is a subtask AND the dependency is a number,
 425 | 			// assume it refers to a sibling subtask.
 426 | 			normalizedDepId = `${parentIdForSubtask}.${depId}`;
 427 | 		}
 428 | 		// Pass the normalized ID to the recursive call
 429 | 		return isCircularDependency(tasks, normalizedDepId, newChain);
 430 | 	});
 431 | }
 432 | 
 433 | /**
 434 |  * Validate task dependencies
 435 |  * @param {Array} tasks - Array of all tasks
 436 |  * @returns {Object} Validation result with valid flag and issues array
 437 |  */
 438 | function validateTaskDependencies(tasks) {
 439 | 	const issues = [];
 440 | 
 441 | 	// Check each task's dependencies
 442 | 	tasks.forEach((task) => {
 443 | 		if (!task.dependencies) {
 444 | 			return; // No dependencies to validate
 445 | 		}
 446 | 
 447 | 		task.dependencies.forEach((depId) => {
 448 | 			// Check for self-dependencies
 449 | 			if (String(depId) === String(task.id)) {
 450 | 				issues.push({
 451 | 					type: 'self',
 452 | 					taskId: task.id,
 453 | 					message: `Task ${task.id} depends on itself`
 454 | 				});
 455 | 				return;
 456 | 			}
 457 | 
 458 | 			// Check if dependency exists
 459 | 			if (!taskExists(tasks, depId)) {
 460 | 				issues.push({
 461 | 					type: 'missing',
 462 | 					taskId: task.id,
 463 | 					dependencyId: depId,
 464 | 					message: `Task ${task.id} depends on non-existent task ${depId}`
 465 | 				});
 466 | 			}
 467 | 		});
 468 | 
 469 | 		// Check for circular dependencies
 470 | 		if (isCircularDependency(tasks, task.id)) {
 471 | 			issues.push({
 472 | 				type: 'circular',
 473 | 				taskId: task.id,
 474 | 				message: `Task ${task.id} is part of a circular dependency chain`
 475 | 			});
 476 | 		}
 477 | 
 478 | 		// Check subtask dependencies if they exist
 479 | 		if (task.subtasks && task.subtasks.length > 0) {
 480 | 			task.subtasks.forEach((subtask) => {
 481 | 				if (!subtask.dependencies) {
 482 | 					return; // No dependencies to validate
 483 | 				}
 484 | 
 485 | 				// Create a full subtask ID for reference
 486 | 				const fullSubtaskId = `${task.id}.${subtask.id}`;
 487 | 
 488 | 				subtask.dependencies.forEach((depId) => {
 489 | 					// Check for self-dependencies in subtasks
 490 | 					if (
 491 | 						String(depId) === String(fullSubtaskId) ||
 492 | 						(typeof depId === 'number' && depId === subtask.id)
 493 | 					) {
 494 | 						issues.push({
 495 | 							type: 'self',
 496 | 							taskId: fullSubtaskId,
 497 | 							message: `Subtask ${fullSubtaskId} depends on itself`
 498 | 						});
 499 | 						return;
 500 | 					}
 501 | 
 502 | 					// Check if dependency exists
 503 | 					if (!taskExists(tasks, depId)) {
 504 | 						issues.push({
 505 | 							type: 'missing',
 506 | 							taskId: fullSubtaskId,
 507 | 							dependencyId: depId,
 508 | 							message: `Subtask ${fullSubtaskId} depends on non-existent task/subtask ${depId}`
 509 | 						});
 510 | 					}
 511 | 				});
 512 | 
 513 | 				// Check for circular dependencies in subtasks
 514 | 				if (isCircularDependency(tasks, fullSubtaskId)) {
 515 | 					issues.push({
 516 | 						type: 'circular',
 517 | 						taskId: fullSubtaskId,
 518 | 						message: `Subtask ${fullSubtaskId} is part of a circular dependency chain`
 519 | 					});
 520 | 				}
 521 | 			});
 522 | 		}
 523 | 	});
 524 | 
 525 | 	return {
 526 | 		valid: issues.length === 0,
 527 | 		issues
 528 | 	};
 529 | }
 530 | 
 531 | /**
 532 |  * Remove duplicate dependencies from tasks
 533 |  * @param {Object} tasksData - Tasks data object with tasks array
 534 |  * @returns {Object} Updated tasks data with duplicates removed
 535 |  */
 536 | function removeDuplicateDependencies(tasksData) {
 537 | 	const tasks = tasksData.tasks.map((task) => {
 538 | 		if (!task.dependencies) {
 539 | 			return task;
 540 | 		}
 541 | 
 542 | 		// Convert to Set and back to array to remove duplicates
 543 | 		const uniqueDeps = [...new Set(task.dependencies)];
 544 | 		return {
 545 | 			...task,
 546 | 			dependencies: uniqueDeps
 547 | 		};
 548 | 	});
 549 | 
 550 | 	return {
 551 | 		...tasksData,
 552 | 		tasks
 553 | 	};
 554 | }
 555 | 
 556 | /**
 557 |  * Clean up invalid subtask dependencies
 558 |  * @param {Object} tasksData - Tasks data object with tasks array
 559 |  * @returns {Object} Updated tasks data with invalid subtask dependencies removed
 560 |  */
 561 | function cleanupSubtaskDependencies(tasksData) {
 562 | 	const tasks = tasksData.tasks.map((task) => {
 563 | 		// Handle task's own dependencies
 564 | 		if (task.dependencies) {
 565 | 			task.dependencies = task.dependencies.filter((depId) => {
 566 | 				// Keep only dependencies that exist
 567 | 				return taskExists(tasksData.tasks, depId);
 568 | 			});
 569 | 		}
 570 | 
 571 | 		// Handle subtask dependencies
 572 | 		if (task.subtasks) {
 573 | 			task.subtasks = task.subtasks.map((subtask) => {
 574 | 				if (!subtask.dependencies) {
 575 | 					return subtask;
 576 | 				}
 577 | 
 578 | 				// Filter out dependencies to non-existent subtasks
 579 | 				subtask.dependencies = subtask.dependencies.filter((depId) => {
 580 | 					return taskExists(tasksData.tasks, depId);
 581 | 				});
 582 | 
 583 | 				return subtask;
 584 | 			});
 585 | 		}
 586 | 
 587 | 		return task;
 588 | 	});
 589 | 
 590 | 	return {
 591 | 		...tasksData,
 592 | 		tasks
 593 | 	};
 594 | }
 595 | 
 596 | /**
 597 |  * Validate dependencies in task files
 598 |  * @param {string} tasksPath - Path to tasks.json
 599 |  * @param {Object} options - Options object, including context
 600 |  */
 601 | async function validateDependenciesCommand(tasksPath, options = {}) {
 602 | 	const { context = {} } = options;
 603 | 	log('info', 'Checking for invalid dependencies in task files...');
 604 | 
 605 | 	// Read tasks data
 606 | 	const data = readJSON(tasksPath, context.projectRoot, context.tag);
 607 | 	if (!data || !data.tasks) {
 608 | 		log('error', 'No valid tasks found in tasks.json');
 609 | 		process.exit(1);
 610 | 	}
 611 | 
 612 | 	// Count of tasks and subtasks for reporting
 613 | 	const taskCount = data.tasks.length;
 614 | 	let subtaskCount = 0;
 615 | 	data.tasks.forEach((task) => {
 616 | 		if (task.subtasks && Array.isArray(task.subtasks)) {
 617 | 			subtaskCount += task.subtasks.length;
 618 | 		}
 619 | 	});
 620 | 
 621 | 	log(
 622 | 		'info',
 623 | 		`Analyzing dependencies for ${taskCount} tasks and ${subtaskCount} subtasks...`
 624 | 	);
 625 | 
 626 | 	try {
 627 | 		// Directly call the validation function
 628 | 		const validationResult = validateTaskDependencies(data.tasks);
 629 | 
 630 | 		if (!validationResult.valid) {
 631 | 			log(
 632 | 				'error',
 633 | 				`Dependency validation failed. Found ${validationResult.issues.length} issue(s):`
 634 | 			);
 635 | 			validationResult.issues.forEach((issue) => {
 636 | 				let errorMsg = `  [${issue.type.toUpperCase()}] Task ${issue.taskId}: ${issue.message}`;
 637 | 				if (issue.dependencyId) {
 638 | 					errorMsg += ` (Dependency: ${issue.dependencyId})`;
 639 | 				}
 640 | 				log('error', errorMsg); // Log each issue as an error
 641 | 			});
 642 | 
 643 | 			// Optionally exit if validation fails, depending on desired behavior
 644 | 			// process.exit(1); // Uncomment if validation failure should stop the process
 645 | 
 646 | 			// Display summary box even on failure, showing issues found
 647 | 			if (!isSilentMode()) {
 648 | 				console.log(
 649 | 					boxen(
 650 | 						chalk.red(`Dependency Validation FAILED\n\n`) +
 651 | 							`${chalk.cyan('Tasks checked:')} ${taskCount}\n` +
 652 | 							`${chalk.cyan('Subtasks checked:')} ${subtaskCount}\n` +
 653 | 							`${chalk.red('Issues found:')} ${validationResult.issues.length}`, // Display count from result
 654 | 						{
 655 | 							padding: 1,
 656 | 							borderColor: 'red',
 657 | 							borderStyle: 'round',
 658 | 							margin: { top: 1, bottom: 1 }
 659 | 						}
 660 | 					)
 661 | 				);
 662 | 			}
 663 | 		} else {
 664 | 			log(
 665 | 				'success',
 666 | 				'No invalid dependencies found - all dependencies are valid'
 667 | 			);
 668 | 
 669 | 			// Show validation summary - only if not in silent mode
 670 | 			if (!isSilentMode()) {
 671 | 				console.log(
 672 | 					boxen(
 673 | 						chalk.green(`All Dependencies Are Valid\n\n`) +
 674 | 							`${chalk.cyan('Tasks checked:')} ${taskCount}\n` +
 675 | 							`${chalk.cyan('Subtasks checked:')} ${subtaskCount}\n` +
 676 | 							`${chalk.cyan('Total dependencies verified:')} ${countAllDependencies(data.tasks)}`,
 677 | 						{
 678 | 							padding: 1,
 679 | 							borderColor: 'green',
 680 | 							borderStyle: 'round',
 681 | 							margin: { top: 1, bottom: 1 }
 682 | 						}
 683 | 					)
 684 | 				);
 685 | 			}
 686 | 		}
 687 | 	} catch (error) {
 688 | 		log('error', 'Error validating dependencies:', error);
 689 | 		process.exit(1);
 690 | 	}
 691 | }
 692 | 
 693 | /**
 694 |  * Helper function to count all dependencies across tasks and subtasks
 695 |  * @param {Array} tasks - All tasks
 696 |  * @returns {number} - Total number of dependencies
 697 |  */
 698 | function countAllDependencies(tasks) {
 699 | 	let count = 0;
 700 | 
 701 | 	tasks.forEach((task) => {
 702 | 		// Count main task dependencies
 703 | 		if (task.dependencies && Array.isArray(task.dependencies)) {
 704 | 			count += task.dependencies.length;
 705 | 		}
 706 | 
 707 | 		// Count subtask dependencies
 708 | 		if (task.subtasks && Array.isArray(task.subtasks)) {
 709 | 			task.subtasks.forEach((subtask) => {
 710 | 				if (subtask.dependencies && Array.isArray(subtask.dependencies)) {
 711 | 					count += subtask.dependencies.length;
 712 | 				}
 713 | 			});
 714 | 		}
 715 | 	});
 716 | 
 717 | 	return count;
 718 | }
 719 | 
 720 | /**
 721 |  * Fixes invalid dependencies in tasks.json
 722 |  * @param {string} tasksPath - Path to tasks.json
 723 |  * @param {Object} options - Options object, including context
 724 |  */
 725 | async function fixDependenciesCommand(tasksPath, options = {}) {
 726 | 	const { context = {} } = options;
 727 | 	log('info', 'Checking for and fixing invalid dependencies in tasks.json...');
 728 | 
 729 | 	try {
 730 | 		// Read tasks data
 731 | 		const data = readJSON(tasksPath, context.projectRoot, context.tag);
 732 | 		if (!data || !data.tasks) {
 733 | 			log('error', 'No valid tasks found in tasks.json');
 734 | 			process.exit(1);
 735 | 		}
 736 | 
 737 | 		// Create a deep copy of the original data for comparison
 738 | 		const originalData = JSON.parse(JSON.stringify(data));
 739 | 
 740 | 		// Track fixes for reporting
 741 | 		const stats = {
 742 | 			nonExistentDependenciesRemoved: 0,
 743 | 			selfDependenciesRemoved: 0,
 744 | 			duplicateDependenciesRemoved: 0,
 745 | 			circularDependenciesFixed: 0,
 746 | 			tasksFixed: 0,
 747 | 			subtasksFixed: 0
 748 | 		};
 749 | 
 750 | 		// First phase: Remove duplicate dependencies in tasks
 751 | 		data.tasks.forEach((task) => {
 752 | 			if (task.dependencies && Array.isArray(task.dependencies)) {
 753 | 				const uniqueDeps = new Set();
 754 | 				const originalLength = task.dependencies.length;
 755 | 				task.dependencies = task.dependencies.filter((depId) => {
 756 | 					const depIdStr = String(depId);
 757 | 					if (uniqueDeps.has(depIdStr)) {
 758 | 						log(
 759 | 							'info',
 760 | 							`Removing duplicate dependency from task ${task.id}: ${depId}`
 761 | 						);
 762 | 						stats.duplicateDependenciesRemoved++;
 763 | 						return false;
 764 | 					}
 765 | 					uniqueDeps.add(depIdStr);
 766 | 					return true;
 767 | 				});
 768 | 				if (task.dependencies.length < originalLength) {
 769 | 					stats.tasksFixed++;
 770 | 				}
 771 | 			}
 772 | 
 773 | 			// Check for duplicates in subtasks
 774 | 			if (task.subtasks && Array.isArray(task.subtasks)) {
 775 | 				task.subtasks.forEach((subtask) => {
 776 | 					if (subtask.dependencies && Array.isArray(subtask.dependencies)) {
 777 | 						const uniqueDeps = new Set();
 778 | 						const originalLength = subtask.dependencies.length;
 779 | 						subtask.dependencies = subtask.dependencies.filter((depId) => {
 780 | 							let depIdStr = String(depId);
 781 | 							if (typeof depId === 'number' && depId < 100) {
 782 | 								depIdStr = `${task.id}.${depId}`;
 783 | 							}
 784 | 							if (uniqueDeps.has(depIdStr)) {
 785 | 								log(
 786 | 									'info',
 787 | 									`Removing duplicate dependency from subtask ${task.id}.${subtask.id}: ${depId}`
 788 | 								);
 789 | 								stats.duplicateDependenciesRemoved++;
 790 | 								return false;
 791 | 							}
 792 | 							uniqueDeps.add(depIdStr);
 793 | 							return true;
 794 | 						});
 795 | 						if (subtask.dependencies.length < originalLength) {
 796 | 							stats.subtasksFixed++;
 797 | 						}
 798 | 					}
 799 | 				});
 800 | 			}
 801 | 		});
 802 | 
 803 | 		// Create validity maps for tasks and subtasks
 804 | 		const validTaskIds = new Set(data.tasks.map((t) => t.id));
 805 | 		const validSubtaskIds = new Set();
 806 | 		data.tasks.forEach((task) => {
 807 | 			if (task.subtasks && Array.isArray(task.subtasks)) {
 808 | 				task.subtasks.forEach((subtask) => {
 809 | 					validSubtaskIds.add(`${task.id}.${subtask.id}`);
 810 | 				});
 811 | 			}
 812 | 		});
 813 | 
 814 | 		// Second phase: Remove invalid task dependencies (non-existent tasks)
 815 | 		data.tasks.forEach((task) => {
 816 | 			if (task.dependencies && Array.isArray(task.dependencies)) {
 817 | 				const originalLength = task.dependencies.length;
 818 | 				task.dependencies = task.dependencies.filter((depId) => {
 819 | 					const isSubtask = typeof depId === 'string' && depId.includes('.');
 820 | 
 821 | 					if (isSubtask) {
 822 | 						// Check if the subtask exists
 823 | 						if (!validSubtaskIds.has(depId)) {
 824 | 							log(
 825 | 								'info',
 826 | 								`Removing invalid subtask dependency from task ${task.id}: ${depId} (subtask does not exist)`
 827 | 							);
 828 | 							stats.nonExistentDependenciesRemoved++;
 829 | 							return false;
 830 | 						}
 831 | 						return true;
 832 | 					} else {
 833 | 						// Check if the task exists
 834 | 						const numericId =
 835 | 							typeof depId === 'string' ? parseInt(depId, 10) : depId;
 836 | 						if (!validTaskIds.has(numericId)) {
 837 | 							log(
 838 | 								'info',
 839 | 								`Removing invalid task dependency from task ${task.id}: ${depId} (task does not exist)`
 840 | 							);
 841 | 							stats.nonExistentDependenciesRemoved++;
 842 | 							return false;
 843 | 						}
 844 | 						return true;
 845 | 					}
 846 | 				});
 847 | 
 848 | 				if (task.dependencies.length < originalLength) {
 849 | 					stats.tasksFixed++;
 850 | 				}
 851 | 			}
 852 | 
 853 | 			// Check subtask dependencies for invalid references
 854 | 			if (task.subtasks && Array.isArray(task.subtasks)) {
 855 | 				task.subtasks.forEach((subtask) => {
 856 | 					if (subtask.dependencies && Array.isArray(subtask.dependencies)) {
 857 | 						const originalLength = subtask.dependencies.length;
 858 | 						const subtaskId = `${task.id}.${subtask.id}`;
 859 | 
 860 | 						// First check for self-dependencies
 861 | 						const hasSelfDependency = subtask.dependencies.some((depId) => {
 862 | 							if (typeof depId === 'string' && depId.includes('.')) {
 863 | 								return depId === subtaskId;
 864 | 							} else if (typeof depId === 'number' && depId < 100) {
 865 | 								return depId === subtask.id;
 866 | 							}
 867 | 							return false;
 868 | 						});
 869 | 
 870 | 						if (hasSelfDependency) {
 871 | 							subtask.dependencies = subtask.dependencies.filter((depId) => {
 872 | 								const normalizedDepId =
 873 | 									typeof depId === 'number' && depId < 100
 874 | 										? `${task.id}.${depId}`
 875 | 										: String(depId);
 876 | 
 877 | 								if (normalizedDepId === subtaskId) {
 878 | 									log(
 879 | 										'info',
 880 | 										`Removing self-dependency from subtask ${subtaskId}`
 881 | 									);
 882 | 									stats.selfDependenciesRemoved++;
 883 | 									return false;
 884 | 								}
 885 | 								return true;
 886 | 							});
 887 | 						}
 888 | 
 889 | 						// Then check for non-existent dependencies
 890 | 						subtask.dependencies = subtask.dependencies.filter((depId) => {
 891 | 							if (typeof depId === 'string' && depId.includes('.')) {
 892 | 								if (!validSubtaskIds.has(depId)) {
 893 | 									log(
 894 | 										'info',
 895 | 										`Removing invalid subtask dependency from subtask ${subtaskId}: ${depId} (subtask does not exist)`
 896 | 									);
 897 | 									stats.nonExistentDependenciesRemoved++;
 898 | 									return false;
 899 | 								}
 900 | 								return true;
 901 | 							}
 902 | 
 903 | 							// Handle numeric dependencies
 904 | 							const numericId =
 905 | 								typeof depId === 'number' ? depId : parseInt(depId, 10);
 906 | 
 907 | 							// Small numbers likely refer to subtasks in the same task
 908 | 							if (numericId < 100) {
 909 | 								const fullSubtaskId = `${task.id}.${numericId}`;
 910 | 
 911 | 								if (!validSubtaskIds.has(fullSubtaskId)) {
 912 | 									log(
 913 | 										'info',
 914 | 										`Removing invalid subtask dependency from subtask ${subtaskId}: ${numericId}`
 915 | 									);
 916 | 									stats.nonExistentDependenciesRemoved++;
 917 | 									return false;
 918 | 								}
 919 | 
 920 | 								return true;
 921 | 							}
 922 | 
 923 | 							// Otherwise it's a task reference
 924 | 							if (!validTaskIds.has(numericId)) {
 925 | 								log(
 926 | 									'info',
 927 | 									`Removing invalid task dependency from subtask ${subtaskId}: ${numericId}`
 928 | 								);
 929 | 								stats.nonExistentDependenciesRemoved++;
 930 | 								return false;
 931 | 							}
 932 | 
 933 | 							return true;
 934 | 						});
 935 | 
 936 | 						if (subtask.dependencies.length < originalLength) {
 937 | 							stats.subtasksFixed++;
 938 | 						}
 939 | 					}
 940 | 				});
 941 | 			}
 942 | 		});
 943 | 
 944 | 		// Third phase: Check for circular dependencies
 945 | 		log('info', 'Checking for circular dependencies...');
 946 | 
 947 | 		// Build the dependency map for subtasks
 948 | 		const subtaskDependencyMap = new Map();
 949 | 		data.tasks.forEach((task) => {
 950 | 			if (task.subtasks && Array.isArray(task.subtasks)) {
 951 | 				task.subtasks.forEach((subtask) => {
 952 | 					const subtaskId = `${task.id}.${subtask.id}`;
 953 | 
 954 | 					if (subtask.dependencies && Array.isArray(subtask.dependencies)) {
 955 | 						const normalizedDeps = subtask.dependencies.map((depId) => {
 956 | 							if (typeof depId === 'string' && depId.includes('.')) {
 957 | 								return depId;
 958 | 							} else if (typeof depId === 'number' && depId < 100) {
 959 | 								return `${task.id}.${depId}`;
 960 | 							}
 961 | 							return String(depId);
 962 | 						});
 963 | 						subtaskDependencyMap.set(subtaskId, normalizedDeps);
 964 | 					} else {
 965 | 						subtaskDependencyMap.set(subtaskId, []);
 966 | 					}
 967 | 				});
 968 | 			}
 969 | 		});
 970 | 
 971 | 		// Check for and fix circular dependencies
 972 | 		for (const [subtaskId, dependencies] of subtaskDependencyMap.entries()) {
 973 | 			const visited = new Set();
 974 | 			const recursionStack = new Set();
 975 | 
 976 | 			// Detect cycles
 977 | 			const cycleEdges = findCycles(
 978 | 				subtaskId,
 979 | 				subtaskDependencyMap,
 980 | 				visited,
 981 | 				recursionStack
 982 | 			);
 983 | 
 984 | 			if (cycleEdges.length > 0) {
 985 | 				const [taskId, subtaskNum] = subtaskId
 986 | 					.split('.')
 987 | 					.map((part) => Number(part));
 988 | 				const task = data.tasks.find((t) => t.id === taskId);
 989 | 
 990 | 				if (task && task.subtasks) {
 991 | 					const subtask = task.subtasks.find((st) => st.id === subtaskNum);
 992 | 
 993 | 					if (subtask && subtask.dependencies) {
 994 | 						const originalLength = subtask.dependencies.length;
 995 | 
 996 | 						const edgesToRemove = cycleEdges.map((edge) => {
 997 | 							if (edge.includes('.')) {
 998 | 								const [depTaskId, depSubtaskId] = edge
 999 | 									.split('.')
1000 | 									.map((part) => Number(part));
1001 | 
1002 | 								if (depTaskId === taskId) {
1003 | 									return depSubtaskId;
1004 | 								}
1005 | 
1006 | 								return edge;
1007 | 							}
1008 | 
1009 | 							return Number(edge);
1010 | 						});
1011 | 
1012 | 						subtask.dependencies = subtask.dependencies.filter((depId) => {
1013 | 							const normalizedDepId =
1014 | 								typeof depId === 'number' && depId < 100
1015 | 									? `${taskId}.${depId}`
1016 | 									: String(depId);
1017 | 
1018 | 							if (
1019 | 								edgesToRemove.includes(depId) ||
1020 | 								edgesToRemove.includes(normalizedDepId)
1021 | 							) {
1022 | 								log(
1023 | 									'info',
1024 | 									`Breaking circular dependency: Removing ${normalizedDepId} from subtask ${subtaskId}`
1025 | 								);
1026 | 								stats.circularDependenciesFixed++;
1027 | 								return false;
1028 | 							}
1029 | 							return true;
1030 | 						});
1031 | 
1032 | 						if (subtask.dependencies.length < originalLength) {
1033 | 							stats.subtasksFixed++;
1034 | 						}
1035 | 					}
1036 | 				}
1037 | 			}
1038 | 		}
1039 | 
1040 | 		// Check if any changes were made by comparing with original data
1041 | 		const dataChanged = JSON.stringify(data) !== JSON.stringify(originalData);
1042 | 
1043 | 		if (dataChanged) {
1044 | 			// Save the changes
1045 | 			writeJSON(tasksPath, data, context.projectRoot, context.tag);
1046 | 			log('success', 'Fixed dependency issues in tasks.json');
1047 | 
1048 | 			// Regenerate task files
1049 | 			log('info', 'Regenerating task files to reflect dependency changes...');
1050 | 			// await generateTaskFiles(tasksPath, path.dirname(tasksPath));
1051 | 		} else {
1052 | 			log('info', 'No changes needed to fix dependencies');
1053 | 		}
1054 | 
1055 | 		// Show detailed statistics report
1056 | 		const totalFixedAll =
1057 | 			stats.nonExistentDependenciesRemoved +
1058 | 			stats.selfDependenciesRemoved +
1059 | 			stats.duplicateDependenciesRemoved +
1060 | 			stats.circularDependenciesFixed;
1061 | 
1062 | 		if (!isSilentMode()) {
1063 | 			if (totalFixedAll > 0) {
1064 | 				log('success', `Fixed ${totalFixedAll} dependency issues in total!`);
1065 | 
1066 | 				console.log(
1067 | 					boxen(
1068 | 						chalk.green(`Dependency Fixes Summary:\n\n`) +
1069 | 							`${chalk.cyan('Invalid dependencies removed:')} ${stats.nonExistentDependenciesRemoved}\n` +
1070 | 							`${chalk.cyan('Self-dependencies removed:')} ${stats.selfDependenciesRemoved}\n` +
1071 | 							`${chalk.cyan('Duplicate dependencies removed:')} ${stats.duplicateDependenciesRemoved}\n` +
1072 | 							`${chalk.cyan('Circular dependencies fixed:')} ${stats.circularDependenciesFixed}\n\n` +
1073 | 							`${chalk.cyan('Tasks fixed:')} ${stats.tasksFixed}\n` +
1074 | 							`${chalk.cyan('Subtasks fixed:')} ${stats.subtasksFixed}\n`,
1075 | 						{
1076 | 							padding: 1,
1077 | 							borderColor: 'green',
1078 | 							borderStyle: 'round',
1079 | 							margin: { top: 1, bottom: 1 }
1080 | 						}
1081 | 					)
1082 | 				);
1083 | 			} else {
1084 | 				log(
1085 | 					'success',
1086 | 					'No dependency issues found - all dependencies are valid'
1087 | 				);
1088 | 
1089 | 				console.log(
1090 | 					boxen(
1091 | 						chalk.green(`All Dependencies Are Valid\n\n`) +
1092 | 							`${chalk.cyan('Tasks checked:')} ${data.tasks.length}\n` +
1093 | 							`${chalk.cyan('Total dependencies verified:')} ${countAllDependencies(data.tasks)}`,
1094 | 						{
1095 | 							padding: 1,
1096 | 							borderColor: 'green',
1097 | 							borderStyle: 'round',
1098 | 							margin: { top: 1, bottom: 1 }
1099 | 						}
1100 | 					)
1101 | 				);
1102 | 			}
1103 | 		}
1104 | 	} catch (error) {
1105 | 		log('error', 'Error in fix-dependencies command:', error);
1106 | 		process.exit(1);
1107 | 	}
1108 | }
1109 | 
1110 | /**
1111 |  * Ensure at least one subtask in each task has no dependencies
1112 |  * @param {Object} tasksData - The tasks data object with tasks array
1113 |  * @returns {boolean} - True if any changes were made
1114 |  */
1115 | function ensureAtLeastOneIndependentSubtask(tasksData) {
1116 | 	if (!tasksData || !tasksData.tasks || !Array.isArray(tasksData.tasks)) {
1117 | 		return false;
1118 | 	}
1119 | 
1120 | 	let changesDetected = false;
1121 | 
1122 | 	tasksData.tasks.forEach((task) => {
1123 | 		if (
1124 | 			!task.subtasks ||
1125 | 			!Array.isArray(task.subtasks) ||
1126 | 			task.subtasks.length === 0
1127 | 		) {
1128 | 			return;
1129 | 		}
1130 | 
1131 | 		// Check if any subtask has no dependencies
1132 | 		const hasIndependentSubtask = task.subtasks.some(
1133 | 			(st) =>
1134 | 				!st.dependencies ||
1135 | 				!Array.isArray(st.dependencies) ||
1136 | 				st.dependencies.length === 0
1137 | 		);
1138 | 
1139 | 		if (!hasIndependentSubtask) {
1140 | 			// Find the first subtask and clear its dependencies
1141 | 			if (task.subtasks.length > 0) {
1142 | 				const firstSubtask = task.subtasks[0];
1143 | 				log(
1144 | 					'debug',
1145 | 					`Ensuring at least one independent subtask: Clearing dependencies for subtask ${task.id}.${firstSubtask.id}`
1146 | 				);
1147 | 				firstSubtask.dependencies = [];
1148 | 				changesDetected = true;
1149 | 			}
1150 | 		}
1151 | 	});
1152 | 
1153 | 	return changesDetected;
1154 | }
1155 | 
1156 | /**
1157 |  * Validate and fix dependencies across all tasks and subtasks
1158 |  * This function is designed to be called after any task modification
1159 |  * @param {Object} tasksData - The tasks data object with tasks array
1160 |  * @param {string} tasksPath - Optional path to save the changes
1161 |  * @param {string} projectRoot - Optional project root for tag context
1162 |  * @param {string} tag - Optional tag for tag context
1163 |  * @returns {boolean} - True if any changes were made
1164 |  */
1165 | function validateAndFixDependencies(
1166 | 	tasksData,
1167 | 	tasksPath = null,
1168 | 	projectRoot = null,
1169 | 	tag = null
1170 | ) {
1171 | 	if (!tasksData || !tasksData.tasks || !Array.isArray(tasksData.tasks)) {
1172 | 		log('error', 'Invalid tasks data');
1173 | 		return false;
1174 | 	}
1175 | 
1176 | 	log('debug', 'Validating and fixing dependencies...');
1177 | 
1178 | 	// Create a deep copy for comparison
1179 | 	const originalData = JSON.parse(JSON.stringify(tasksData));
1180 | 
1181 | 	// 1. Remove duplicate dependencies from tasks and subtasks
1182 | 	tasksData.tasks = tasksData.tasks.map((task) => {
1183 | 		// Handle task dependencies
1184 | 		if (task.dependencies) {
1185 | 			const uniqueDeps = [...new Set(task.dependencies)];
1186 | 			task.dependencies = uniqueDeps;
1187 | 		}
1188 | 
1189 | 		// Handle subtask dependencies
1190 | 		if (task.subtasks) {
1191 | 			task.subtasks = task.subtasks.map((subtask) => {
1192 | 				if (subtask.dependencies) {
1193 | 					const uniqueDeps = [...new Set(subtask.dependencies)];
1194 | 					subtask.dependencies = uniqueDeps;
1195 | 				}
1196 | 				return subtask;
1197 | 			});
1198 | 		}
1199 | 		return task;
1200 | 	});
1201 | 
1202 | 	// 2. Remove invalid task dependencies (non-existent tasks)
1203 | 	tasksData.tasks.forEach((task) => {
1204 | 		// Clean up task dependencies
1205 | 		if (task.dependencies) {
1206 | 			task.dependencies = task.dependencies.filter((depId) => {
1207 | 				// Remove self-dependencies
1208 | 				if (String(depId) === String(task.id)) {
1209 | 					return false;
1210 | 				}
1211 | 				// Remove non-existent dependencies
1212 | 				return taskExists(tasksData.tasks, depId);
1213 | 			});
1214 | 		}
1215 | 
1216 | 		// Clean up subtask dependencies
1217 | 		if (task.subtasks) {
1218 | 			task.subtasks.forEach((subtask) => {
1219 | 				if (subtask.dependencies) {
1220 | 					subtask.dependencies = subtask.dependencies.filter((depId) => {
1221 | 						// Handle numeric subtask references
1222 | 						if (typeof depId === 'number' && depId < 100) {
1223 | 							const fullSubtaskId = `${task.id}.${depId}`;
1224 | 							return taskExists(tasksData.tasks, fullSubtaskId);
1225 | 						}
1226 | 						// Handle full task/subtask references
1227 | 						return taskExists(tasksData.tasks, depId);
1228 | 					});
1229 | 				}
1230 | 			});
1231 | 		}
1232 | 	});
1233 | 
1234 | 	// 3. Ensure at least one subtask has no dependencies in each task
1235 | 	tasksData.tasks.forEach((task) => {
1236 | 		if (task.subtasks && task.subtasks.length > 0) {
1237 | 			const hasIndependentSubtask = task.subtasks.some(
1238 | 				(st) =>
1239 | 					!st.dependencies ||
1240 | 					!Array.isArray(st.dependencies) ||
1241 | 					st.dependencies.length === 0
1242 | 			);
1243 | 
1244 | 			if (!hasIndependentSubtask) {
1245 | 				task.subtasks[0].dependencies = [];
1246 | 			}
1247 | 		}
1248 | 	});
1249 | 
1250 | 	// Check if any changes were made by comparing with original data
1251 | 	const changesDetected =
1252 | 		JSON.stringify(tasksData) !== JSON.stringify(originalData);
1253 | 
1254 | 	// Save changes if needed
1255 | 	if (tasksPath && changesDetected) {
1256 | 		try {
1257 | 			writeJSON(tasksPath, tasksData, projectRoot, tag);
1258 | 			log('debug', 'Saved dependency fixes to tasks.json');
1259 | 		} catch (error) {
1260 | 			log('error', 'Failed to save dependency fixes to tasks.json', error);
1261 | 		}
1262 | 	}
1263 | 
1264 | 	return changesDetected;
1265 | }
1266 | 
1267 | /**
1268 |  * Recursively find all dependencies for a set of tasks with depth limiting
1269 |  * Recursively find all dependencies for a set of tasks with depth limiting
1270 |  *
1271 |  * @note This function depends on the traverseDependencies utility from utils.js
1272 |  * for the actual dependency traversal logic.
1273 |  *
1274 |  * @param {Array} sourceTasks - Array of source tasks to find dependencies for
1275 |  * @param {Array} allTasks - Array of all available tasks
1276 |  * @param {Object} options - Options object
1277 |  * @param {number} options.maxDepth - Maximum recursion depth (default: 50)
1278 |  * @param {boolean} options.includeSelf - Whether to include self-references (default: false)
1279 |  * @returns {Array} Array of all dependency task IDs
1280 |  */
1281 | function findAllDependenciesRecursively(sourceTasks, allTasks, options = {}) {
1282 | 	if (!Array.isArray(sourceTasks)) {
1283 | 		throw new Error('Source tasks parameter must be an array');
1284 | 	}
1285 | 	if (!Array.isArray(allTasks)) {
1286 | 		throw new Error('All tasks parameter must be an array');
1287 | 	}
1288 | 	return traverseDependencies(sourceTasks, allTasks, {
1289 | 		...options,
1290 | 		direction: 'forward',
1291 | 		logger: { warn: log.warn || console.warn }
1292 | 	});
1293 | }
1294 | 
1295 | /**
1296 |  * Find dependency task by ID, handling various ID formats
1297 |  * @param {string|number} depId - Dependency ID to find
1298 |  * @param {string} taskId - ID of the task that has this dependency
1299 |  * @param {Array} allTasks - Array of all tasks to search
1300 |  * @returns {Object|null} Found dependency task or null
1301 |  */
1302 | /**
1303 |  * Find a subtask within a parent task's subtasks array
1304 |  * @param {string} parentId - The parent task ID
1305 |  * @param {string|number} subtaskId - The subtask ID to find
1306 |  * @param {Array} allTasks - Array of all tasks to search in
1307 |  * @param {boolean} useStringComparison - Whether to use string comparison for subtaskId
1308 |  * @returns {Object|null} The found subtask with full ID or null if not found
1309 |  */
1310 | function findSubtaskInParent(
1311 | 	parentId,
1312 | 	subtaskId,
1313 | 	allTasks,
1314 | 	useStringComparison = false
1315 | ) {
1316 | 	// Convert parentId to numeric for proper comparison with top-level task IDs
1317 | 	const numericParentId = parseInt(parentId, 10);
1318 | 	const parentTask = allTasks.find((t) => t.id === numericParentId);
1319 | 
1320 | 	if (parentTask && parentTask.subtasks && Array.isArray(parentTask.subtasks)) {
1321 | 		const foundSubtask = parentTask.subtasks.find((subtask) =>
1322 | 			useStringComparison
1323 | 				? String(subtask.id) === String(subtaskId)
1324 | 				: subtask.id === subtaskId
1325 | 		);
1326 | 		if (foundSubtask) {
1327 | 			// Return a task-like object that represents the subtask with full ID
1328 | 			return {
1329 | 				...foundSubtask,
1330 | 				id: `${parentId}.${foundSubtask.id}`
1331 | 			};
1332 | 		}
1333 | 	}
1334 | 
1335 | 	return null;
1336 | }
1337 | 
1338 | function findDependencyTask(depId, taskId, allTasks) {
1339 | 	if (!depId) {
1340 | 		return null;
1341 | 	}
1342 | 
1343 | 	// Convert depId to string for consistent comparison
1344 | 	const depIdStr = String(depId);
1345 | 
1346 | 	// Find the dependency task - handle both top-level and subtask IDs
1347 | 	let depTask = null;
1348 | 
1349 | 	// First try exact match (for top-level tasks)
1350 | 	depTask = allTasks.find((t) => String(t.id) === depIdStr);
1351 | 
1352 | 	// If not found and it's a subtask reference (contains dot), find the parent task first
1353 | 	if (!depTask && depIdStr.includes('.')) {
1354 | 		const [parentId, subtaskId] = depIdStr.split('.');
1355 | 		depTask = findSubtaskInParent(parentId, subtaskId, allTasks, true);
1356 | 	}
1357 | 
1358 | 	// If still not found, try numeric comparison for relative subtask references
1359 | 	if (!depTask && !isNaN(depId)) {
1360 | 		const numericId = parseInt(depId, 10);
1361 | 		// For subtasks, this might be a relative reference within the same parent
1362 | 		if (taskId && typeof taskId === 'string' && taskId.includes('.')) {
1363 | 			const [parentId] = taskId.split('.');
1364 | 			depTask = findSubtaskInParent(parentId, numericId, allTasks, false);
1365 | 		}
1366 | 	}
1367 | 
1368 | 	return depTask;
1369 | }
1370 | 
1371 | /**
1372 |  * Check if a task has cross-tag dependencies
1373 |  * @param {Object} task - Task to check
1374 |  * @param {string} targetTag - Target tag name
1375 |  * @param {Array} allTasks - Array of all tasks from all tags
1376 |  * @returns {Array} Array of cross-tag dependency conflicts
1377 |  */
1378 | function findTaskCrossTagConflicts(task, targetTag, allTasks) {
1379 | 	const conflicts = [];
1380 | 
1381 | 	// Validate task.dependencies is an array before processing
1382 | 	if (!Array.isArray(task.dependencies) || task.dependencies.length === 0) {
1383 | 		return conflicts;
1384 | 	}
1385 | 
1386 | 	// Filter out null/undefined dependencies and check each valid dependency
1387 | 	const validDependencies = task.dependencies.filter((depId) => depId != null);
1388 | 
1389 | 	validDependencies.forEach((depId) => {
1390 | 		const depTask = findDependencyTask(depId, task.id, allTasks);
1391 | 
1392 | 		if (depTask && depTask.tag !== targetTag) {
1393 | 			conflicts.push({
1394 | 				taskId: task.id,
1395 | 				dependencyId: depId,
1396 | 				dependencyTag: depTask.tag,
1397 | 				message: `Task ${task.id} depends on ${depId} (in ${depTask.tag})`
1398 | 			});
1399 | 		}
1400 | 	});
1401 | 
1402 | 	return conflicts;
1403 | }
1404 | 
1405 | function validateCrossTagMove(task, sourceTag, targetTag, allTasks) {
1406 | 	// Parameter validation
1407 | 	if (!task || typeof task !== 'object') {
1408 | 		throw new Error('Task parameter must be a valid object');
1409 | 	}
1410 | 
1411 | 	if (!sourceTag || typeof sourceTag !== 'string') {
1412 | 		throw new Error('Source tag must be a valid string');
1413 | 	}
1414 | 
1415 | 	if (!targetTag || typeof targetTag !== 'string') {
1416 | 		throw new Error('Target tag must be a valid string');
1417 | 	}
1418 | 
1419 | 	if (!Array.isArray(allTasks)) {
1420 | 		throw new Error('All tasks parameter must be an array');
1421 | 	}
1422 | 
1423 | 	const conflicts = findTaskCrossTagConflicts(task, targetTag, allTasks);
1424 | 
1425 | 	return {
1426 | 		canMove: conflicts.length === 0,
1427 | 		conflicts
1428 | 	};
1429 | }
1430 | 
1431 | /**
1432 |  * Find all cross-tag dependencies for a set of tasks
1433 |  * @param {Array} sourceTasks - Array of tasks to check
1434 |  * @param {string} sourceTag - Source tag name
1435 |  * @param {string} targetTag - Target tag name
1436 |  * @param {Array} allTasks - Array of all tasks from all tags
1437 |  * @returns {Array} Array of cross-tag dependency conflicts
1438 |  */
1439 | function findCrossTagDependencies(sourceTasks, sourceTag, targetTag, allTasks) {
1440 | 	// Parameter validation
1441 | 	if (!Array.isArray(sourceTasks)) {
1442 | 		throw new Error('Source tasks parameter must be an array');
1443 | 	}
1444 | 
1445 | 	if (!sourceTag || typeof sourceTag !== 'string') {
1446 | 		throw new Error('Source tag must be a valid string');
1447 | 	}
1448 | 
1449 | 	if (!targetTag || typeof targetTag !== 'string') {
1450 | 		throw new Error('Target tag must be a valid string');
1451 | 	}
1452 | 
1453 | 	if (!Array.isArray(allTasks)) {
1454 | 		throw new Error('All tasks parameter must be an array');
1455 | 	}
1456 | 
1457 | 	const conflicts = [];
1458 | 
1459 | 	sourceTasks.forEach((task) => {
1460 | 		// Validate task object and dependencies array
1461 | 		if (
1462 | 			!task ||
1463 | 			typeof task !== 'object' ||
1464 | 			!Array.isArray(task.dependencies) ||
1465 | 			task.dependencies.length === 0
1466 | 		) {
1467 | 			return;
1468 | 		}
1469 | 
1470 | 		// Use the shared helper function to find conflicts for this task
1471 | 		const taskConflicts = findTaskCrossTagConflicts(task, targetTag, allTasks);
1472 | 		conflicts.push(...taskConflicts);
1473 | 	});
1474 | 
1475 | 	return conflicts;
1476 | }
1477 | 
1478 | /**
1479 |  * Helper function to find all tasks that depend on a given task (reverse dependencies)
1480 |  * @param {string|number} taskId - The task ID to find dependencies for
1481 |  * @param {Array} allTasks - Array of all tasks to search
1482 |  * @param {Set} dependentTaskIds - Set to add found dependencies to
1483 |  */
1484 | function findTasksThatDependOn(taskId, allTasks, dependentTaskIds) {
1485 | 	// Find the task object for the given ID
1486 | 	const sourceTask = allTasks.find((t) => t.id === taskId);
1487 | 	if (!sourceTask) {
1488 | 		return;
1489 | 	}
1490 | 
1491 | 	// Use the shared utility for reverse dependency traversal
1492 | 	const reverseDeps = traverseDependencies([sourceTask], allTasks, {
1493 | 		direction: 'reverse',
1494 | 		includeSelf: false,
1495 | 		logger: { warn: log.warn || console.warn }
1496 | 	});
1497 | 
1498 | 	// Add all found reverse dependencies to the dependentTaskIds set
1499 | 	reverseDeps.forEach((depId) => dependentTaskIds.add(depId));
1500 | }
1501 | 
1502 | /**
1503 |  * Helper function to check if a task depends on a source task
1504 |  * @param {Object} task - Task to check for dependencies
1505 |  * @param {Object} sourceTask - Source task to check dependency against
1506 |  * @returns {boolean} True if task depends on source task
1507 |  */
1508 | function taskDependsOnSource(task, sourceTask) {
1509 | 	if (!task || !Array.isArray(task.dependencies)) {
1510 | 		return false;
1511 | 	}
1512 | 
1513 | 	const sourceTaskIdStr = String(sourceTask.id);
1514 | 
1515 | 	return task.dependencies.some((depId) => {
1516 | 		if (!depId) return false;
1517 | 
1518 | 		const depIdStr = String(depId);
1519 | 
1520 | 		// Exact match
1521 | 		if (depIdStr === sourceTaskIdStr) {
1522 | 			return true;
1523 | 		}
1524 | 
1525 | 		// Handle subtask references
1526 | 		if (
1527 | 			sourceTaskIdStr &&
1528 | 			typeof sourceTaskIdStr === 'string' &&
1529 | 			sourceTaskIdStr.includes('.')
1530 | 		) {
1531 | 			// If source is a subtask, check if dependency references the parent
1532 | 			const [parentId] = sourceTaskIdStr.split('.');
1533 | 			if (depIdStr === parentId) {
1534 | 				return true;
1535 | 			}
1536 | 		}
1537 | 
1538 | 		// Handle relative subtask references
1539 | 		if (
1540 | 			depIdStr &&
1541 | 			typeof depIdStr === 'string' &&
1542 | 			depIdStr.includes('.') &&
1543 | 			sourceTaskIdStr &&
1544 | 			typeof sourceTaskIdStr === 'string' &&
1545 | 			sourceTaskIdStr.includes('.')
1546 | 		) {
1547 | 			const [depParentId] = depIdStr.split('.');
1548 | 			const [sourceParentId] = sourceTaskIdStr.split('.');
1549 | 			if (depParentId === sourceParentId) {
1550 | 				// Both are subtasks of the same parent, check if they reference each other
1551 | 				const depSubtaskNum = parseInt(depIdStr.split('.')[1], 10);
1552 | 				const sourceSubtaskNum = parseInt(sourceTaskIdStr.split('.')[1], 10);
1553 | 				if (depSubtaskNum === sourceSubtaskNum) {
1554 | 					return true;
1555 | 				}
1556 | 			}
1557 | 		}
1558 | 
1559 | 		return false;
1560 | 	});
1561 | }
1562 | 
1563 | /**
1564 |  * Helper function to check if any subtasks of a task depend on source tasks
1565 |  * @param {Object} task - Task to check subtasks of
1566 |  * @param {Array} sourceTasks - Array of source tasks to check dependencies against
1567 |  * @returns {boolean} True if any subtasks depend on source tasks
1568 |  */
1569 | function subtasksDependOnSource(task, sourceTasks) {
1570 | 	if (!task.subtasks || !Array.isArray(task.subtasks)) {
1571 | 		return false;
1572 | 	}
1573 | 
1574 | 	return task.subtasks.some((subtask) => {
1575 | 		// Check if this subtask depends on any source task
1576 | 		const subtaskDependsOnSource = sourceTasks.some((sourceTask) =>
1577 | 			taskDependsOnSource(subtask, sourceTask)
1578 | 		);
1579 | 
1580 | 		if (subtaskDependsOnSource) {
1581 | 			return true;
1582 | 		}
1583 | 
1584 | 		// Recursively check if any nested subtasks depend on source tasks
1585 | 		if (subtask.subtasks && Array.isArray(subtask.subtasks)) {
1586 | 			return subtasksDependOnSource(subtask, sourceTasks);
1587 | 		}
1588 | 
1589 | 		return false;
1590 | 	});
1591 | }
1592 | 
1593 | /**
1594 |  * Get all dependent task IDs for a set of cross-tag dependencies
1595 |  * @param {Array} sourceTasks - Array of source tasks
1596 |  * @param {Array} crossTagDependencies - Array of cross-tag dependency conflicts
1597 |  * @param {Array} allTasks - Array of all tasks from all tags
1598 |  * @returns {Array} Array of dependent task IDs to move
1599 |  */
1600 | function getDependentTaskIds(sourceTasks, crossTagDependencies, allTasks) {
1601 | 	// Enhanced parameter validation
1602 | 	if (!Array.isArray(sourceTasks)) {
1603 | 		throw new Error('Source tasks parameter must be an array');
1604 | 	}
1605 | 
1606 | 	if (!Array.isArray(crossTagDependencies)) {
1607 | 		throw new Error('Cross tag dependencies parameter must be an array');
1608 | 	}
1609 | 
1610 | 	if (!Array.isArray(allTasks)) {
1611 | 		throw new Error('All tasks parameter must be an array');
1612 | 	}
1613 | 
1614 | 	// Use the shared recursive dependency finder
1615 | 	const dependentTaskIds = new Set(
1616 | 		findAllDependenciesRecursively(sourceTasks, allTasks, {
1617 | 			includeSelf: false
1618 | 		})
1619 | 	);
1620 | 
1621 | 	// Add immediate dependency IDs from conflicts and find their dependencies recursively
1622 | 	const conflictTasksToProcess = [];
1623 | 	crossTagDependencies.forEach((conflict) => {
1624 | 		if (conflict && conflict.dependencyId) {
1625 | 			const depId =
1626 | 				typeof conflict.dependencyId === 'string'
1627 | 					? parseInt(conflict.dependencyId, 10)
1628 | 					: conflict.dependencyId;
1629 | 			if (!isNaN(depId)) {
1630 | 				dependentTaskIds.add(depId);
1631 | 				// Find the task object for recursive dependency finding
1632 | 				const depTask = allTasks.find((t) => t.id === depId);
1633 | 				if (depTask) {
1634 | 					conflictTasksToProcess.push(depTask);
1635 | 				}
1636 | 			}
1637 | 		}
1638 | 	});
1639 | 
1640 | 	// Find dependencies of conflict tasks
1641 | 	if (conflictTasksToProcess.length > 0) {
1642 | 		const conflictDependencies = findAllDependenciesRecursively(
1643 | 			conflictTasksToProcess,
1644 | 			allTasks,
1645 | 			{ includeSelf: false }
1646 | 		);
1647 | 		conflictDependencies.forEach((depId) => dependentTaskIds.add(depId));
1648 | 	}
1649 | 
1650 | 	// For --with-dependencies, we also need to find all dependencies of the source tasks
1651 | 	sourceTasks.forEach((sourceTask) => {
1652 | 		if (sourceTask && sourceTask.id) {
1653 | 			// Find all tasks that this source task depends on (forward dependencies) - already handled above
1654 | 
1655 | 			// Find all tasks that depend on this source task (reverse dependencies)
1656 | 			findTasksThatDependOn(sourceTask.id, allTasks, dependentTaskIds);
1657 | 		}
1658 | 	});
1659 | 
1660 | 	// Also include any tasks that depend on the source tasks
1661 | 	sourceTasks.forEach((sourceTask) => {
1662 | 		if (!sourceTask || typeof sourceTask !== 'object' || !sourceTask.id) {
1663 | 			return; // Skip invalid source tasks
1664 | 		}
1665 | 
1666 | 		allTasks.forEach((task) => {
1667 | 			// Validate task and dependencies array
1668 | 			if (
1669 | 				!task ||
1670 | 				typeof task !== 'object' ||
1671 | 				!Array.isArray(task.dependencies)
1672 | 			) {
1673 | 				return;
1674 | 			}
1675 | 
1676 | 			// Check if this task depends on the source task
1677 | 			const hasDependency = taskDependsOnSource(task, sourceTask);
1678 | 
1679 | 			// Check if any subtasks of this task depend on the source task
1680 | 			const subtasksHaveDependency = subtasksDependOnSource(task, [sourceTask]);
1681 | 
1682 | 			if (hasDependency || subtasksHaveDependency) {
1683 | 				dependentTaskIds.add(task.id);
1684 | 			}
1685 | 		});
1686 | 	});
1687 | 
1688 | 	return Array.from(dependentTaskIds);
1689 | }
1690 | 
1691 | /**
1692 |  * Validate subtask movement - block direct cross-tag subtask moves
1693 |  * @param {string} taskId - Task ID to validate
1694 |  * @param {string} sourceTag - Source tag name
1695 |  * @param {string} targetTag - Target tag name
1696 |  * @throws {Error} If subtask movement is attempted
1697 |  */
1698 | function validateSubtaskMove(taskId, sourceTag, targetTag) {
1699 | 	// Parameter validation
1700 | 	if (!taskId || typeof taskId !== 'string') {
1701 | 		throw new DependencyError(
1702 | 			DEPENDENCY_ERROR_CODES.INVALID_TASK_ID,
1703 | 			'Task ID must be a valid string'
1704 | 		);
1705 | 	}
1706 | 
1707 | 	if (!sourceTag || typeof sourceTag !== 'string') {
1708 | 		throw new DependencyError(
1709 | 			DEPENDENCY_ERROR_CODES.INVALID_SOURCE_TAG,
1710 | 			'Source tag must be a valid string'
1711 | 		);
1712 | 	}
1713 | 
1714 | 	if (!targetTag || typeof targetTag !== 'string') {
1715 | 		throw new DependencyError(
1716 | 			DEPENDENCY_ERROR_CODES.INVALID_TARGET_TAG,
1717 | 			'Target tag must be a valid string'
1718 | 		);
1719 | 	}
1720 | 
1721 | 	if (taskId.includes('.')) {
1722 | 		throw new DependencyError(
1723 | 			DEPENDENCY_ERROR_CODES.CANNOT_MOVE_SUBTASK,
1724 | 			`Cannot move subtask ${taskId} directly between tags.
1725 | 
1726 | First promote it to a full task using:
1727 |   task-master remove-subtask --id=${taskId} --convert`,
1728 | 			{
1729 | 				taskId,
1730 | 				sourceTag,
1731 | 				targetTag
1732 | 			}
1733 | 		);
1734 | 	}
1735 | }
1736 | 
1737 | /**
1738 |  * Check if a task can be moved with its dependencies
1739 |  * @param {string} taskId - Task ID to check
1740 |  * @param {string} sourceTag - Source tag name
1741 |  * @param {string} targetTag - Target tag name
1742 |  * @param {Array} allTasks - Array of all tasks from all tags
1743 |  * @returns {Object} Object with canMove boolean and dependentTaskIds array
1744 |  */
1745 | function canMoveWithDependencies(taskId, sourceTag, targetTag, allTasks) {
1746 | 	// Parameter validation
1747 | 	if (!taskId || typeof taskId !== 'string') {
1748 | 		throw new Error('Task ID must be a valid string');
1749 | 	}
1750 | 
1751 | 	if (!sourceTag || typeof sourceTag !== 'string') {
1752 | 		throw new Error('Source tag must be a valid string');
1753 | 	}
1754 | 
1755 | 	if (!targetTag || typeof targetTag !== 'string') {
1756 | 		throw new Error('Target tag must be a valid string');
1757 | 	}
1758 | 
1759 | 	if (!Array.isArray(allTasks)) {
1760 | 		throw new Error('All tasks parameter must be an array');
1761 | 	}
1762 | 
1763 | 	// Enhanced task lookup to handle subtasks properly
1764 | 	let sourceTask = null;
1765 | 
1766 | 	// Check if it's a subtask ID (e.g., "1.2")
1767 | 	if (taskId.includes('.')) {
1768 | 		const [parentId, subtaskId] = taskId
1769 | 			.split('.')
1770 | 			.map((id) => parseInt(id, 10));
1771 | 		const parentTask = allTasks.find(
1772 | 			(t) => t.id === parentId && t.tag === sourceTag
1773 | 		);
1774 | 
1775 | 		if (
1776 | 			parentTask &&
1777 | 			parentTask.subtasks &&
1778 | 			Array.isArray(parentTask.subtasks)
1779 | 		) {
1780 | 			const subtask = parentTask.subtasks.find((st) => st.id === subtaskId);
1781 | 			if (subtask) {
1782 | 				// Create a copy of the subtask with parent context
1783 | 				sourceTask = {
1784 | 					...subtask,
1785 | 					parentTask: {
1786 | 						id: parentTask.id,
1787 | 						title: parentTask.title,
1788 | 						status: parentTask.status
1789 | 					},
1790 | 					isSubtask: true
1791 | 				};
1792 | 			}
1793 | 		}
1794 | 	} else {
1795 | 		// Regular task lookup - handle both string and numeric IDs
1796 | 		sourceTask = allTasks.find((t) => {
1797 | 			const taskIdNum = parseInt(taskId, 10);
1798 | 			return (t.id === taskIdNum || t.id === taskId) && t.tag === sourceTag;
1799 | 		});
1800 | 	}
1801 | 
1802 | 	if (!sourceTask) {
1803 | 		return {
1804 | 			canMove: false,
1805 | 			dependentTaskIds: [],
1806 | 			conflicts: [],
1807 | 			error: 'Task not found'
1808 | 		};
1809 | 	}
1810 | 
1811 | 	const validation = validateCrossTagMove(
1812 | 		sourceTask,
1813 | 		sourceTag,
1814 | 		targetTag,
1815 | 		allTasks
1816 | 	);
1817 | 
1818 | 	// Fix contradictory logic: return canMove: false when conflicts exist
1819 | 	if (validation.canMove) {
1820 | 		return {
1821 | 			canMove: true,
1822 | 			dependentTaskIds: [],
1823 | 			conflicts: []
1824 | 		};
1825 | 	}
1826 | 
1827 | 	// When conflicts exist, return canMove: false with conflicts and dependent task IDs
1828 | 	const dependentTaskIds = getDependentTaskIds(
1829 | 		[sourceTask],
1830 | 		validation.conflicts,
1831 | 		allTasks
1832 | 	);
1833 | 
1834 | 	return {
1835 | 		canMove: false,
1836 | 		dependentTaskIds,
1837 | 		conflicts: validation.conflicts
1838 | 	};
1839 | }
1840 | 
1841 | export {
1842 | 	addDependency,
1843 | 	removeDependency,
1844 | 	isCircularDependency,
1845 | 	validateTaskDependencies,
1846 | 	validateDependenciesCommand,
1847 | 	fixDependenciesCommand,
1848 | 	removeDuplicateDependencies,
1849 | 	cleanupSubtaskDependencies,
1850 | 	ensureAtLeastOneIndependentSubtask,
1851 | 	validateAndFixDependencies,
1852 | 	findDependencyTask,
1853 | 	findTaskCrossTagConflicts,
1854 | 	validateCrossTagMove,
1855 | 	findCrossTagDependencies,
1856 | 	getDependentTaskIds,
1857 | 	validateSubtaskMove,
1858 | 	canMoveWithDependencies,
1859 | 	findAllDependenciesRecursively,
1860 | 	DependencyError,
1861 | 	DEPENDENCY_ERROR_CODES
1862 | };
1863 | 
```
Page 61/69FirstPrevNextLast