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

# Directory Structure

```
├── .changeset
│   ├── config.json
│   └── README.md
├── .claude
│   ├── 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/task-manager/list-tasks.js:
--------------------------------------------------------------------------------

```javascript
import chalk from 'chalk';
import boxen from 'boxen';
import Table from 'cli-table3';

import {
	log,
	readJSON,
	truncate,
	readComplexityReport,
	addComplexityToTask
} from '../utils.js';
import findNextTask from './find-next-task.js';

import {
	displayBanner,
	getStatusWithColor,
	formatDependenciesWithStatus,
	getComplexityWithColor,
	createProgressBar
} from '../ui.js';
import { createTmCore } from '@tm/core';

/**
 * List all tasks
 * @param {string} tasksPath - Path to the tasks.json file
 * @param {string} statusFilter - Filter by status (single status or comma-separated list, e.g., 'pending' or 'blocked,deferred')
 * @param {string} reportPath - Path to the complexity report
 * @param {boolean} withSubtasks - Whether to show subtasks
 * @param {string} outputFormat - Output format (text or json)
 * @param {Object} context - Context object (required)
 * @param {string} context.projectRoot - Project root path
 * @param {string} context.tag - Tag for the task
 * @returns {Object} - Task list result for json format
 */
async function listTasks(
	tasksPath,
	statusFilter,
	reportPath = null,
	withSubtasks = false,
	outputFormat = 'text',
	context = {}
) {
	const { projectRoot, tag } = context;
	try {
		// BRIDGE: Initialize tm-core for unified task access
		let data;
		try {
			const tmCore = await createTmCore({
				projectPath: projectRoot || process.cwd()
			});

			// Load tasks via tm-core tasks domain (supports both file and API storage)
			const result = await tmCore.tasks.list({ tag });
			data = { tasks: result.tasks };

			log(
				'debug',
				`Loaded ${result.tasks.length} tasks via tm-core (${result.storageType} storage)`
			);
		} catch (storageError) {
			log(
				'warn',
				`TmCore failed, falling back to legacy readJSON: ${storageError.message}`
			);
			// Fallback to old readJSON if tm-core fails
			data = readJSON(tasksPath, projectRoot, tag);
		}

		if (!data || !data.tasks) {
			throw new Error(`No valid tasks found in ${tasksPath}`);
		}

		// Add complexity scores to tasks if report exists
		// `reportPath` is already tag-aware (resolved at the CLI boundary).
		const complexityReport = readComplexityReport(reportPath);
		// Apply complexity scores to tasks
		if (complexityReport && complexityReport.complexityAnalysis) {
			data.tasks.forEach((task) => addComplexityToTask(task, complexityReport));
		}

		// Filter tasks by status if specified - now supports comma-separated statuses
		let filteredTasks;
		if (statusFilter && statusFilter.toLowerCase() !== 'all') {
			// Handle comma-separated statuses
			const allowedStatuses = statusFilter
				.split(',')
				.map((s) => s.trim().toLowerCase())
				.filter((s) => s.length > 0); // Remove empty strings

			filteredTasks = data.tasks.filter(
				(task) =>
					task.status && allowedStatuses.includes(task.status.toLowerCase())
			);
		} else {
			// Default to all tasks if no filter or filter is 'all'
			filteredTasks = data.tasks;
		}

		// Calculate completion statistics
		const totalTasks = data.tasks.length;
		const completedTasks = data.tasks.filter(
			(task) => task.status === 'done' || task.status === 'completed'
		).length;
		const completionPercentage =
			totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0;

		// Count statuses for tasks
		const doneCount = completedTasks;
		const inProgressCount = data.tasks.filter(
			(task) => task.status === 'in-progress'
		).length;
		const pendingCount = data.tasks.filter(
			(task) => task.status === 'pending'
		).length;
		const blockedCount = data.tasks.filter(
			(task) => task.status === 'blocked'
		).length;
		const deferredCount = data.tasks.filter(
			(task) => task.status === 'deferred'
		).length;
		const cancelledCount = data.tasks.filter(
			(task) => task.status === 'cancelled'
		).length;
		const reviewCount = data.tasks.filter(
			(task) => task.status === 'review'
		).length;

		// Count subtasks and their statuses
		let totalSubtasks = 0;
		let completedSubtasks = 0;
		let inProgressSubtasks = 0;
		let pendingSubtasks = 0;
		let blockedSubtasks = 0;
		let deferredSubtasks = 0;
		let cancelledSubtasks = 0;
		let reviewSubtasks = 0;

		data.tasks.forEach((task) => {
			if (task.subtasks && task.subtasks.length > 0) {
				totalSubtasks += task.subtasks.length;
				completedSubtasks += task.subtasks.filter(
					(st) => st.status === 'done' || st.status === 'completed'
				).length;
				inProgressSubtasks += task.subtasks.filter(
					(st) => st.status === 'in-progress'
				).length;
				pendingSubtasks += task.subtasks.filter(
					(st) => st.status === 'pending'
				).length;
				blockedSubtasks += task.subtasks.filter(
					(st) => st.status === 'blocked'
				).length;
				deferredSubtasks += task.subtasks.filter(
					(st) => st.status === 'deferred'
				).length;
				cancelledSubtasks += task.subtasks.filter(
					(st) => st.status === 'cancelled'
				).length;
				reviewSubtasks += task.subtasks.filter(
					(st) => st.status === 'review'
				).length;
			}
		});

		const subtaskCompletionPercentage =
			totalSubtasks > 0 ? (completedSubtasks / totalSubtasks) * 100 : 0;

		// Calculate dependency statistics (moved up to be available for all output formats)
		const completedTaskIds = new Set(
			data.tasks
				.filter((t) => t.status === 'done' || t.status === 'completed')
				.map((t) => t.id)
		);

		const tasksWithNoDeps = data.tasks.filter(
			(t) =>
				t.status !== 'done' &&
				t.status !== 'completed' &&
				(!t.dependencies || t.dependencies.length === 0)
		).length;

		const tasksWithAllDepsSatisfied = data.tasks.filter(
			(t) =>
				t.status !== 'done' &&
				t.status !== 'completed' &&
				t.dependencies &&
				t.dependencies.length > 0 &&
				t.dependencies.every((depId) => completedTaskIds.has(depId))
		).length;

		const tasksWithUnsatisfiedDeps = data.tasks.filter(
			(t) =>
				t.status !== 'done' &&
				t.status !== 'completed' &&
				t.dependencies &&
				t.dependencies.length > 0 &&
				!t.dependencies.every((depId) => completedTaskIds.has(depId))
		).length;

		// Calculate total tasks ready to work on (no deps + satisfied deps)
		const tasksReadyToWork = tasksWithNoDeps + tasksWithAllDepsSatisfied;

		// Calculate most depended-on tasks
		const dependencyCount = {};
		data.tasks.forEach((task) => {
			if (task.dependencies && task.dependencies.length > 0) {
				task.dependencies.forEach((depId) => {
					dependencyCount[depId] = (dependencyCount[depId] || 0) + 1;
				});
			}
		});

		// Find the most depended-on task
		let mostDependedOnTaskId = null;
		let maxDependents = 0;

		for (const [taskId, count] of Object.entries(dependencyCount)) {
			if (count > maxDependents) {
				maxDependents = count;
				mostDependedOnTaskId = parseInt(taskId);
			}
		}

		// Get the most depended-on task
		const mostDependedOnTask =
			mostDependedOnTaskId !== null
				? data.tasks.find((t) => t.id === mostDependedOnTaskId)
				: null;

		// Calculate average dependencies per task
		const totalDependencies = data.tasks.reduce(
			(sum, task) => sum + (task.dependencies ? task.dependencies.length : 0),
			0
		);
		const avgDependenciesPerTask = totalDependencies / data.tasks.length;

		// Find next task to work on, passing the complexity report
		const nextItem = findNextTask(data.tasks, complexityReport);

		// For JSON output, return structured data
		if (outputFormat === 'json') {
			// *** Modification: Remove 'details' field for JSON output ***
			const tasksWithoutDetails = filteredTasks.map((task) => {
				// <-- USES filteredTasks!
				// Omit 'details' from the parent task
				const { details, ...taskRest } = task;

				// If subtasks exist, omit 'details' from them too
				if (taskRest.subtasks && Array.isArray(taskRest.subtasks)) {
					taskRest.subtasks = taskRest.subtasks.map((subtask) => {
						const { details: subtaskDetails, ...subtaskRest } = subtask;
						return subtaskRest;
					});
				}
				return taskRest;
			});
			// *** End of Modification ***

			return {
				tasks: tasksWithoutDetails, // <--- THIS IS THE ARRAY BEING RETURNED
				filter: statusFilter || 'all', // Return the actual filter used
				stats: {
					total: totalTasks,
					completed: doneCount,
					inProgress: inProgressCount,
					pending: pendingCount,
					blocked: blockedCount,
					deferred: deferredCount,
					cancelled: cancelledCount,
					review: reviewCount,
					completionPercentage,
					subtasks: {
						total: totalSubtasks,
						completed: completedSubtasks,
						inProgress: inProgressSubtasks,
						pending: pendingSubtasks,
						blocked: blockedSubtasks,
						deferred: deferredSubtasks,
						cancelled: cancelledSubtasks,
						completionPercentage: subtaskCompletionPercentage
					}
				}
			};
		}

		// For markdown-readme output, return formatted markdown
		if (outputFormat === 'markdown-readme') {
			return generateMarkdownOutput(data, filteredTasks, {
				totalTasks,
				completedTasks,
				completionPercentage,
				doneCount,
				inProgressCount,
				pendingCount,
				blockedCount,
				deferredCount,
				cancelledCount,
				totalSubtasks,
				completedSubtasks,
				subtaskCompletionPercentage,
				inProgressSubtasks,
				pendingSubtasks,
				blockedSubtasks,
				deferredSubtasks,
				cancelledSubtasks,
				reviewSubtasks,
				tasksWithNoDeps,
				tasksReadyToWork,
				tasksWithUnsatisfiedDeps,
				mostDependedOnTask,
				mostDependedOnTaskId,
				maxDependents,
				avgDependenciesPerTask,
				complexityReport,
				withSubtasks,
				nextItem
			});
		}

		// For compact output, return minimal one-line format
		if (outputFormat === 'compact') {
			return renderCompactOutput(filteredTasks, withSubtasks);
		}

		// ... existing code for text output ...

		// Calculate status breakdowns as percentages of total
		const taskStatusBreakdown = {
			'in-progress': totalTasks > 0 ? (inProgressCount / totalTasks) * 100 : 0,
			pending: totalTasks > 0 ? (pendingCount / totalTasks) * 100 : 0,
			blocked: totalTasks > 0 ? (blockedCount / totalTasks) * 100 : 0,
			deferred: totalTasks > 0 ? (deferredCount / totalTasks) * 100 : 0,
			cancelled: totalTasks > 0 ? (cancelledCount / totalTasks) * 100 : 0,
			review: totalTasks > 0 ? (reviewCount / totalTasks) * 100 : 0
		};

		const subtaskStatusBreakdown = {
			'in-progress':
				totalSubtasks > 0 ? (inProgressSubtasks / totalSubtasks) * 100 : 0,
			pending: totalSubtasks > 0 ? (pendingSubtasks / totalSubtasks) * 100 : 0,
			blocked: totalSubtasks > 0 ? (blockedSubtasks / totalSubtasks) * 100 : 0,
			deferred:
				totalSubtasks > 0 ? (deferredSubtasks / totalSubtasks) * 100 : 0,
			cancelled:
				totalSubtasks > 0 ? (cancelledSubtasks / totalSubtasks) * 100 : 0,
			review: totalSubtasks > 0 ? (reviewSubtasks / totalSubtasks) * 100 : 0
		};

		// Create progress bars with status breakdowns
		const taskProgressBar = createProgressBar(
			completionPercentage,
			30,
			taskStatusBreakdown
		);
		const subtaskProgressBar = createProgressBar(
			subtaskCompletionPercentage,
			30,
			subtaskStatusBreakdown
		);

		// Get terminal width - more reliable method
		let terminalWidth;
		try {
			// Try to get the actual terminal columns
			terminalWidth = process.stdout.columns;
		} catch (e) {
			// Fallback if columns cannot be determined
			log('debug', 'Could not determine terminal width, using default');
		}
		// Ensure we have a reasonable default if detection fails
		terminalWidth = terminalWidth || 80;

		// Ensure terminal width is at least a minimum value to prevent layout issues
		terminalWidth = Math.max(terminalWidth, 80);

		// Create dashboard content
		const projectDashboardContent =
			chalk.white.bold('Project Dashboard') +
			'\n' +
			`Tasks Progress: ${chalk.greenBright(taskProgressBar)} ${completionPercentage.toFixed(0)}%\n` +
			`Done: ${chalk.green(doneCount)}  In Progress: ${chalk.blue(inProgressCount)}  Pending: ${chalk.yellow(pendingCount)}  Blocked: ${chalk.red(blockedCount)}  Deferred: ${chalk.gray(deferredCount)}  Cancelled: ${chalk.gray(cancelledCount)}\n\n` +
			`Subtasks Progress: ${chalk.cyan(subtaskProgressBar)} ${subtaskCompletionPercentage.toFixed(0)}%\n` +
			`Completed: ${chalk.green(completedSubtasks)}/${totalSubtasks}  In Progress: ${chalk.blue(inProgressSubtasks)}  Pending: ${chalk.yellow(pendingSubtasks)}  Blocked: ${chalk.red(blockedSubtasks)}  Deferred: ${chalk.gray(deferredSubtasks)}  Cancelled: ${chalk.gray(cancelledSubtasks)}\n\n` +
			chalk.cyan.bold('Priority Breakdown:') +
			'\n' +
			`${chalk.red('•')} ${chalk.white('High priority:')} ${data.tasks.filter((t) => t.priority === 'high').length}\n` +
			`${chalk.yellow('•')} ${chalk.white('Medium priority:')} ${data.tasks.filter((t) => t.priority === 'medium').length}\n` +
			`${chalk.green('•')} ${chalk.white('Low priority:')} ${data.tasks.filter((t) => t.priority === 'low').length}`;

		const dependencyDashboardContent =
			chalk.white.bold('Dependency Status & Next Task') +
			'\n' +
			chalk.cyan.bold('Dependency Metrics:') +
			'\n' +
			`${chalk.green('•')} ${chalk.white('Tasks with no dependencies:')} ${tasksWithNoDeps}\n` +
			`${chalk.green('•')} ${chalk.white('Tasks ready to work on:')} ${tasksReadyToWork}\n` +
			`${chalk.yellow('•')} ${chalk.white('Tasks blocked by dependencies:')} ${tasksWithUnsatisfiedDeps}\n` +
			`${chalk.magenta('•')} ${chalk.white('Most depended-on task:')} ${mostDependedOnTask ? chalk.cyan(`#${mostDependedOnTaskId} (${maxDependents} dependents)`) : chalk.gray('None')}\n` +
			`${chalk.blue('•')} ${chalk.white('Avg dependencies per task:')} ${avgDependenciesPerTask.toFixed(1)}\n\n` +
			chalk.cyan.bold('Next Task to Work On:') +
			'\n' +
			`ID: ${chalk.cyan(nextItem ? nextItem.id : 'N/A')} - ${nextItem ? chalk.white.bold(truncate(nextItem.title, 40)) : chalk.yellow('No task available')}
` +
			`Priority: ${nextItem ? chalk.white(nextItem.priority || 'medium') : ''}  Dependencies: ${nextItem ? formatDependenciesWithStatus(nextItem.dependencies, data.tasks, true, complexityReport) : ''}
` +
			`Complexity: ${nextItem && nextItem.complexityScore ? getComplexityWithColor(nextItem.complexityScore) : chalk.gray('N/A')}`;

		// Calculate width for side-by-side display
		// Box borders, padding take approximately 4 chars on each side
		const minDashboardWidth = 50; // Minimum width for dashboard
		const minDependencyWidth = 50; // Minimum width for dependency dashboard
		const totalMinWidth = minDashboardWidth + minDependencyWidth + 4; // Extra 4 chars for spacing

		// If terminal is wide enough, show boxes side by side with responsive widths
		if (terminalWidth >= totalMinWidth) {
			// Calculate widths proportionally for each box - use exact 50% width each
			const availableWidth = terminalWidth;
			const halfWidth = Math.floor(availableWidth / 2);

			// Account for border characters (2 chars on each side)
			const boxContentWidth = halfWidth - 4;

			// Create boxen options with precise widths
			const dashboardBox = boxen(projectDashboardContent, {
				padding: 1,
				borderColor: 'blue',
				borderStyle: 'round',
				width: boxContentWidth,
				dimBorder: false
			});

			const dependencyBox = boxen(dependencyDashboardContent, {
				padding: 1,
				borderColor: 'magenta',
				borderStyle: 'round',
				width: boxContentWidth,
				dimBorder: false
			});

			// Create a better side-by-side layout with exact spacing
			const dashboardLines = dashboardBox.split('\n');
			const dependencyLines = dependencyBox.split('\n');

			// Make sure both boxes have the same height
			const maxHeight = Math.max(dashboardLines.length, dependencyLines.length);

			// For each line of output, pad the dashboard line to exactly halfWidth chars
			// This ensures the dependency box starts at exactly the right position
			const combinedLines = [];
			for (let i = 0; i < maxHeight; i++) {
				// Get the dashboard line (or empty string if we've run out of lines)
				const dashLine = i < dashboardLines.length ? dashboardLines[i] : '';
				// Get the dependency line (or empty string if we've run out of lines)
				const depLine = i < dependencyLines.length ? dependencyLines[i] : '';

				// Remove any trailing spaces from dashLine before padding to exact width
				const trimmedDashLine = dashLine.trimEnd();
				// Pad the dashboard line to exactly halfWidth chars with no extra spaces
				const paddedDashLine = trimmedDashLine.padEnd(halfWidth, ' ');

				// Join the lines with no space in between
				combinedLines.push(paddedDashLine + depLine);
			}

			// Join all lines and output
			console.log(combinedLines.join('\n'));
		} else {
			// Terminal too narrow, show boxes stacked vertically
			const dashboardBox = boxen(projectDashboardContent, {
				padding: 1,
				borderColor: 'blue',
				borderStyle: 'round',
				margin: { top: 0, bottom: 1 }
			});

			const dependencyBox = boxen(dependencyDashboardContent, {
				padding: 1,
				borderColor: 'magenta',
				borderStyle: 'round',
				margin: { top: 0, bottom: 1 }
			});

			// Display stacked vertically
			console.log(dashboardBox);
			console.log(dependencyBox);
		}

		if (filteredTasks.length === 0) {
			console.log(
				boxen(
					statusFilter
						? chalk.yellow(`No tasks with status '${statusFilter}' found`)
						: chalk.yellow('No tasks found'),
					{ padding: 1, borderColor: 'yellow', borderStyle: 'round' }
				)
			);
			return;
		}

		// COMPLETELY REVISED TABLE APPROACH
		// Define percentage-based column widths and calculate actual widths
		// Adjust percentages based on content type and user requirements

		// Adjust ID width if showing subtasks (subtask IDs are longer: e.g., "1.2")
		const idWidthPct = withSubtasks ? 10 : 7;

		// Calculate max status length to accommodate "in-progress"
		const statusWidthPct = 15;

		// Increase priority column width as requested
		const priorityWidthPct = 12;

		// Make dependencies column smaller as requested (-20%)
		const depsWidthPct = 20;

		const complexityWidthPct = 10;

		// Calculate title/description width as remaining space (+20% from dependencies reduction)
		const titleWidthPct =
			100 -
			idWidthPct -
			statusWidthPct -
			priorityWidthPct -
			depsWidthPct -
			complexityWidthPct;

		// Allow 10 characters for borders and padding
		const availableWidth = terminalWidth - 10;

		// Calculate actual column widths based on percentages
		const idWidth = Math.floor(availableWidth * (idWidthPct / 100));
		const statusWidth = Math.floor(availableWidth * (statusWidthPct / 100));
		const priorityWidth = Math.floor(availableWidth * (priorityWidthPct / 100));
		const depsWidth = Math.floor(availableWidth * (depsWidthPct / 100));
		const complexityWidth = Math.floor(
			availableWidth * (complexityWidthPct / 100)
		);
		const titleWidth = Math.floor(availableWidth * (titleWidthPct / 100));

		// Create a table with correct borders and spacing
		const table = new Table({
			head: [
				chalk.cyan.bold('ID'),
				chalk.cyan.bold('Title'),
				chalk.cyan.bold('Status'),
				chalk.cyan.bold('Priority'),
				chalk.cyan.bold('Dependencies'),
				chalk.cyan.bold('Complexity')
			],
			colWidths: [
				idWidth,
				titleWidth,
				statusWidth,
				priorityWidth,
				depsWidth,
				complexityWidth // Added complexity column width
			],
			style: {
				head: [], // No special styling for header
				border: [], // No special styling for border
				compact: false // Use default spacing
			},
			wordWrap: true,
			wrapOnWordBoundary: true
		});

		// Process tasks for the table
		filteredTasks.forEach((task) => {
			// Format dependencies with status indicators (colored)
			let depText = 'None';
			if (task.dependencies && task.dependencies.length > 0) {
				// Use the proper formatDependenciesWithStatus function for colored status
				depText = formatDependenciesWithStatus(
					task.dependencies,
					data.tasks,
					true,
					complexityReport
				);
			} else {
				depText = chalk.gray('None');
			}

			// Clean up any ANSI codes or confusing characters
			const cleanTitle = task.title.replace(/\n/g, ' ');

			// Get priority color
			const priorityColor =
				{
					high: chalk.red,
					medium: chalk.yellow,
					low: chalk.gray
				}[task.priority || 'medium'] || chalk.white;

			// Format status
			const status = getStatusWithColor(task.status, true);

			// Add the row without truncating dependencies
			table.push([
				task.id.toString(),
				truncate(cleanTitle, titleWidth - 3),
				status,
				priorityColor(truncate(task.priority || 'medium', priorityWidth - 2)),
				depText,
				task.complexityScore
					? getComplexityWithColor(task.complexityScore)
					: chalk.gray('N/A')
			]);

			// Add subtasks if requested
			if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
				task.subtasks.forEach((subtask) => {
					// Format subtask dependencies with status indicators
					let subtaskDepText = 'None';
					if (subtask.dependencies && subtask.dependencies.length > 0) {
						// Handle both subtask-to-subtask and subtask-to-task dependencies
						const formattedDeps = subtask.dependencies
							.map((depId) => {
								// Check if it's a dependency on another subtask
								if (typeof depId === 'number' && depId < 100) {
									const foundSubtask = task.subtasks.find(
										(st) => st.id === depId
									);
									if (foundSubtask) {
										const isDone =
											foundSubtask.status === 'done' ||
											foundSubtask.status === 'completed';
										const isInProgress = foundSubtask.status === 'in-progress';

										// Use consistent color formatting instead of emojis
										if (isDone) {
											return chalk.green.bold(`${task.id}.${depId}`);
										} else if (isInProgress) {
											return chalk.hex('#FFA500').bold(`${task.id}.${depId}`);
										} else {
											return chalk.red.bold(`${task.id}.${depId}`);
										}
									}
								}
								// Default to regular task dependency
								const depTask = data.tasks.find((t) => t.id === depId);
								if (depTask) {
									// Add complexity to depTask before checking status
									addComplexityToTask(depTask, complexityReport);
									const isDone =
										depTask.status === 'done' || depTask.status === 'completed';
									const isInProgress = depTask.status === 'in-progress';
									// Use the same color scheme as in formatDependenciesWithStatus
									if (isDone) {
										return chalk.green.bold(`${depId}`);
									} else if (isInProgress) {
										return chalk.hex('#FFA500').bold(`${depId}`);
									} else {
										return chalk.red.bold(`${depId}`);
									}
								}
								return chalk.cyan(depId.toString());
							})
							.join(', ');

						subtaskDepText = formattedDeps || chalk.gray('None');
					}

					// Add the subtask row without truncating dependencies
					table.push([
						`${task.id}.${subtask.id}`,
						chalk.dim(`└─ ${truncate(subtask.title, titleWidth - 5)}`),
						getStatusWithColor(subtask.status, true),
						chalk.dim('-'),
						subtaskDepText,
						subtask.complexityScore
							? chalk.gray(`${subtask.complexityScore}`)
							: chalk.gray('N/A')
					]);
				});
			}
		});

		// Ensure we output the table even if it had to wrap
		try {
			console.log(table.toString());
		} catch (err) {
			log('error', `Error rendering table: ${err.message}`);

			// Fall back to simpler output
			console.log(
				chalk.yellow(
					'\nFalling back to simple task list due to terminal width constraints:'
				)
			);
			filteredTasks.forEach((task) => {
				console.log(
					`${chalk.cyan(task.id)}: ${chalk.white(task.title)} - ${getStatusWithColor(task.status)}`
				);
			});
		}

		// Show filter info if applied
		if (statusFilter) {
			console.log(chalk.yellow(`\nFiltered by status: ${statusFilter}`));
			console.log(
				chalk.yellow(`Showing ${filteredTasks.length} of ${totalTasks} tasks`)
			);
		}

		// Define priority colors
		const priorityColors = {
			high: chalk.red.bold,
			medium: chalk.yellow,
			low: chalk.gray
		};

		// Show next task box in a prominent color
		if (nextItem) {
			// Prepare subtasks section if they exist (Only tasks have .subtasks property)
			let subtasksSection = '';
			// Check if the nextItem is a top-level task before looking for subtasks
			const parentTaskForSubtasks = data.tasks.find(
				(t) => String(t.id) === String(nextItem.id)
			); // Find the original task object
			if (
				parentTaskForSubtasks &&
				parentTaskForSubtasks.subtasks &&
				parentTaskForSubtasks.subtasks.length > 0
			) {
				subtasksSection = `\n\n${chalk.white.bold('Subtasks:')}\n`;
				subtasksSection += parentTaskForSubtasks.subtasks
					.map((subtask) => {
						// Add complexity to subtask before display
						addComplexityToTask(subtask, complexityReport);
						// Using a more simplified format for subtask status display
						const status = subtask.status || 'pending';
						const statusColors = {
							done: chalk.green,
							completed: chalk.green,
							pending: chalk.yellow,
							'in-progress': chalk.blue,
							deferred: chalk.gray,
							blocked: chalk.red,
							cancelled: chalk.gray
						};
						const statusColor =
							statusColors[status.toLowerCase()] || chalk.white;
						// Ensure subtask ID is displayed correctly using parent ID from the original task object
						return `${chalk.cyan(`${parentTaskForSubtasks.id}.${subtask.id}`)} [${statusColor(status)}] ${subtask.title}`;
					})
					.join('\n');
			}

			console.log(
				boxen(
					chalk.hex('#FF8800').bold(
						// Use nextItem.id and nextItem.title
						`🔥 Next Task to Work On: #${nextItem.id} - ${nextItem.title}`
					) +
						'\n\n' +
						// Use nextItem.priority, nextItem.status, nextItem.dependencies
						`${chalk.white('Priority:')} ${priorityColors[nextItem.priority || 'medium'](nextItem.priority || 'medium')}   ${chalk.white('Status:')} ${getStatusWithColor(nextItem.status, true)}\n` +
						`${chalk.white('Dependencies:')} ${nextItem.dependencies && nextItem.dependencies.length > 0 ? formatDependenciesWithStatus(nextItem.dependencies, data.tasks, true, complexityReport) : chalk.gray('None')}\n\n` +
						// Use nextTask.description (Note: findNextTask doesn't return description, need to fetch original task/subtask for this)
						// *** Fetching original item for description and details ***
						`${chalk.white('Description:')} ${getWorkItemDescription(nextItem, data.tasks)}` +
						subtasksSection + // <-- Subtasks are handled above now
						'\n\n' +
						// Use nextItem.id
						`${chalk.cyan('Start working:')} ${chalk.yellow(`task-master set-status --id=${nextItem.id} --status=in-progress`)}\n` +
						// Use nextItem.id
						`${chalk.cyan('View details:')} ${chalk.yellow(`task-master show ${nextItem.id}`)}`,
					{
						padding: { left: 2, right: 2, top: 1, bottom: 1 },
						borderColor: '#FF8800',
						borderStyle: 'round',
						margin: { top: 1, bottom: 1 },
						title: '⚡ RECOMMENDED NEXT TASK ⚡',
						titleAlignment: 'center',
						width: terminalWidth - 4,
						fullscreen: false
					}
				)
			);
		} else {
			console.log(
				boxen(
					chalk.hex('#FF8800').bold('No eligible next task found') +
						'\n\n' +
						'All pending tasks have dependencies that are not yet completed, or all tasks are done.',
					{
						padding: 1,
						borderColor: '#FF8800',
						borderStyle: 'round',
						margin: { top: 1, bottom: 1 },
						title: '⚡ NEXT TASK ⚡',
						titleAlignment: 'center',
						width: terminalWidth - 4 // Use full terminal width minus a small margin
					}
				)
			);
		}

		// Show next steps
		console.log(
			boxen(
				chalk.white.bold('Suggested Next Steps:') +
					'\n\n' +
					`${chalk.cyan('1.')} Run ${chalk.yellow('task-master next')} to see what to work on next\n` +
					`${chalk.cyan('2.')} Run ${chalk.yellow('task-master expand --id=<id>')} to break down a task into subtasks\n` +
					`${chalk.cyan('3.')} Run ${chalk.yellow('task-master set-status --id=<id> --status=done')} to mark a task as complete`,
				{
					padding: 1,
					borderColor: 'gray',
					borderStyle: 'round',
					margin: { top: 1 }
				}
			)
		);
	} catch (error) {
		log('error', `Error listing tasks: ${error.message}`);

		if (outputFormat === 'json') {
			// Return structured error for JSON output
			throw {
				code: 'TASK_LIST_ERROR',
				message: error.message,
				details: error.stack
			};
		}

		console.error(chalk.red(`Error: ${error.message}`));
		process.exit(1);
	}
}

// *** Helper function to get description for task or subtask ***
function getWorkItemDescription(item, allTasks) {
	if (!item) return 'N/A';
	if (item.parentId) {
		// It's a subtask
		const parent = allTasks.find((t) => t.id === item.parentId);
		const subtask = parent?.subtasks?.find(
			(st) => `${parent.id}.${st.id}` === item.id
		);
		return subtask?.description || 'No description available.';
	} else {
		// It's a top-level task
		const task = allTasks.find((t) => String(t.id) === String(item.id));
		return task?.description || 'No description available.';
	}
}

/**
 * Generate markdown-formatted output for README files
 * @param {Object} data - Full tasks data
 * @param {Array} filteredTasks - Filtered tasks array
 * @param {Object} stats - Statistics object
 * @returns {string} - Formatted markdown string
 */
function generateMarkdownOutput(data, filteredTasks, stats) {
	const {
		totalTasks,
		completedTasks,
		completionPercentage,
		doneCount,
		inProgressCount,
		pendingCount,
		blockedCount,
		deferredCount,
		cancelledCount,
		totalSubtasks,
		completedSubtasks,
		subtaskCompletionPercentage,
		inProgressSubtasks,
		pendingSubtasks,
		blockedSubtasks,
		deferredSubtasks,
		cancelledSubtasks,
		tasksWithNoDeps,
		tasksReadyToWork,
		tasksWithUnsatisfiedDeps,
		mostDependedOnTask,
		mostDependedOnTaskId,
		maxDependents,
		avgDependenciesPerTask,
		complexityReport,
		withSubtasks,
		nextItem
	} = stats;

	let markdown = '';

	// Create progress bars for markdown (using Unicode block characters)
	const createMarkdownProgressBar = (percentage, width = 20) => {
		const filled = Math.round((percentage / 100) * width);
		const empty = width - filled;
		return '█'.repeat(filled) + '░'.repeat(empty);
	};

	const taskProgressBar = createMarkdownProgressBar(completionPercentage, 20);
	const subtaskProgressBar = createMarkdownProgressBar(
		subtaskCompletionPercentage,
		20
	);

	// Dashboard section
	// markdown += '```\n';
	markdown += '| Project Dashboard |  |\n';
	markdown += '| :-                |:-|\n';
	markdown += `| Task Progress     | ${taskProgressBar} ${Math.round(completionPercentage)}% |\n`;
	markdown += `| Done | ${doneCount} |\n`;
	markdown += `| In Progress | ${inProgressCount} |\n`;
	markdown += `| Pending | ${pendingCount} |\n`;
	markdown += `| Deferred | ${deferredCount} |\n`;
	markdown += `| Cancelled | ${cancelledCount} |\n`;
	markdown += `|-|-|\n`;
	markdown += `| Subtask Progress | ${subtaskProgressBar} ${Math.round(subtaskCompletionPercentage)}% |\n`;
	markdown += `| Completed | ${completedSubtasks} |\n`;
	markdown += `| In Progress | ${inProgressSubtasks} |\n`;
	markdown += `| Pending | ${pendingSubtasks} |\n`;

	markdown += '\n\n';

	// Tasks table
	markdown +=
		'| ID | Title | Status | Priority | Dependencies | Complexity |\n';
	markdown +=
		'| :- | :-    | :-     | :-       | :-           | :-         |\n';

	// Helper function to format status with symbols
	const getStatusSymbol = (status) => {
		switch (status) {
			case 'done':
			case 'completed':
				return '✓&nbsp;done';
			case 'in-progress':
				return '►&nbsp;in-progress';
			case 'pending':
				return '○&nbsp;pending';
			case 'blocked':
				return '⭕&nbsp;blocked';
			case 'deferred':
				return 'x&nbsp;deferred';
			case 'cancelled':
				return 'x&nbsp;cancelled';
			case 'review':
				return '?&nbsp;review';
			default:
				return status || 'pending';
		}
	};

	// Helper function to format dependencies without color codes
	const formatDependenciesForMarkdown = (deps, allTasks) => {
		if (!deps || deps.length === 0) return 'None';
		return deps
			.map((depId) => {
				const depTask = allTasks.find((t) => t.id === depId);
				return depTask ? depId.toString() : depId.toString();
			})
			.join(', ');
	};

	// Process all tasks
	filteredTasks.forEach((task) => {
		const taskTitle = task.title; // No truncation for README
		const statusSymbol = getStatusSymbol(task.status);
		const priority = task.priority || 'medium';
		const deps = formatDependenciesForMarkdown(task.dependencies, data.tasks);
		const complexity = task.complexityScore
			? `● ${task.complexityScore}`
			: 'N/A';

		markdown += `| ${task.id} | ${taskTitle} | ${statusSymbol} | ${priority} | ${deps} | ${complexity} |\n`;

		// Add subtasks if requested
		if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
			task.subtasks.forEach((subtask) => {
				const subtaskTitle = `${subtask.title}`; // No truncation
				const subtaskStatus = getStatusSymbol(subtask.status);
				const subtaskDeps = formatDependenciesForMarkdown(
					subtask.dependencies,
					data.tasks
				);
				const subtaskComplexity = subtask.complexityScore
					? subtask.complexityScore.toString()
					: 'N/A';

				markdown += `| ${task.id}.${subtask.id} | ${subtaskTitle} | ${subtaskStatus} | -            | ${subtaskDeps} | ${subtaskComplexity} |\n`;
			});
		}
	});

	return markdown;
}

/**
 * Format dependencies for compact output with truncation and coloring
 * @param {Array} dependencies - Array of dependency IDs
 * @returns {string} - Formatted dependency string with arrow prefix
 */
function formatCompactDependencies(dependencies) {
	if (!dependencies || dependencies.length === 0) {
		return '';
	}

	if (dependencies.length > 5) {
		const visible = dependencies.slice(0, 5).join(',');
		const remaining = dependencies.length - 5;
		return ` → ${chalk.cyan(visible)}${chalk.gray('... (+' + remaining + ' more)')}`;
	} else {
		return ` → ${chalk.cyan(dependencies.join(','))}`;
	}
}

/**
 * Format a single task in compact one-line format
 * @param {Object} task - Task object
 * @param {number} maxTitleLength - Maximum title length before truncation
 * @returns {string} - Formatted task line
 */
function formatCompactTask(task, maxTitleLength = 50) {
	const status = task.status || 'pending';
	const priority = task.priority || 'medium';
	const title = truncate(task.title || 'Untitled', maxTitleLength);

	// Use colored status from existing function
	const coloredStatus = getStatusWithColor(status, true);

	// Color priority based on level
	const priorityColors = {
		high: chalk.red,
		medium: chalk.yellow,
		low: chalk.gray
	};
	const priorityColor = priorityColors[priority] || chalk.white;

	// Format dependencies using shared helper
	const depsText = formatCompactDependencies(task.dependencies);

	return `${chalk.cyan(task.id)} ${coloredStatus} ${chalk.white(title)} ${priorityColor('(' + priority + ')')}${depsText}`;
}

/**
 * Format a subtask in compact format with indentation
 * @param {Object} subtask - Subtask object
 * @param {string|number} parentId - Parent task ID
 * @param {number} maxTitleLength - Maximum title length before truncation
 * @returns {string} - Formatted subtask line
 */
function formatCompactSubtask(subtask, parentId, maxTitleLength = 47) {
	const status = subtask.status || 'pending';
	const title = truncate(subtask.title || 'Untitled', maxTitleLength);

	// Use colored status from existing function
	const coloredStatus = getStatusWithColor(status, true);

	// Format dependencies using shared helper
	const depsText = formatCompactDependencies(subtask.dependencies);

	return `  ${chalk.cyan(parentId + '.' + subtask.id)} ${coloredStatus} ${chalk.dim(title)}${depsText}`;
}

/**
 * Render complete compact output
 * @param {Array} filteredTasks - Tasks to display
 * @param {boolean} withSubtasks - Whether to include subtasks
 * @returns {void} - Outputs directly to console
 */
function renderCompactOutput(filteredTasks, withSubtasks) {
	if (filteredTasks.length === 0) {
		console.log('No tasks found');
		return;
	}

	const output = [];

	filteredTasks.forEach((task) => {
		output.push(formatCompactTask(task));

		if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
			task.subtasks.forEach((subtask) => {
				output.push(formatCompactSubtask(subtask, task.id));
			});
		}
	});

	console.log(output.join('\n'));
}

export default listTasks;

```

--------------------------------------------------------------------------------
/.taskmaster/docs/tdd-workflow-phase-1-core-rails.md:
--------------------------------------------------------------------------------

```markdown
# Phase 1: Core Rails - Autonomous TDD Workflow

## Objective
Implement the core autonomous TDD workflow with safe git operations, test generation/execution, and commit gating.

## Scope
- WorkflowOrchestrator with event stream
- GitAdapter and TestResultValidator
- Subtask loop (RED → GREEN → COMMIT)
- CLI commands for AI agent orchestration
- MCP tools for AI agent orchestration
- Test result validation (AI reports, TaskMaster validates)
- Commit creation with enhanced metadata
- Branch/tag mapping
- Global storage for state and activity logs
- Framework-agnostic design (AI runs tests, not TaskMaster)
- Run report persistence

## Key Design Decisions

### Global Storage (`~/.taskmaster/`)
- **Why:** Keeps project directory clean, client-friendly, no tooling evidence in PRs
- **What:** All runtime state, logs, and throwaway artifacts
- **Where:** `~/.taskmaster/projects/<project-path>/runs/<run-id>/`

### Dual System: State + Activity Log
- **State (`state.json`):** For orchestration, tells AI what to do next, mutable
- **Activity Log (`activity.jsonl`):** For debugging/audit, append-only event stream
- **Separation:** Optimizes for different use cases (fast reads vs. complete history)

### Enhanced Commit Messages
- **Why:** Enables future task-checker bot validation without external dependencies
- **What:** Embeds task ID, phase, tag, test counts, coverage in commit body
- **Benefit:** PR contains full context for review and automated validation

### Worktree Support
- **Why:** Enables parallel autonomous agents on different branches
- **How:** Each worktree has independent global state directory
- **Isolation:** No conflicts, complete separation

### Framework-Agnostic Test Execution
- **AI runs tests:** AI agent knows project context and test framework (npm test, pytest, go test)
- **TaskMaster validates:** Only checks that RED fails and GREEN passes
- **No framework detection:** TaskMaster doesn't need to know Jest vs Vitest vs pytest
- **Trust but verify:** AI reports results, TaskMaster validates they make sense
- **Language agnostic:** Works with any language/framework without TaskMaster changes

### AI Agent Orchestration Model
- **Who executes:** User's AI agent (Claude Code, Cursor, Windsurf, etc.) - not TaskMaster
- **TaskMaster's role:** Workflow orchestration, validation, commit creation
- **AI agent's role:** Code generation, test execution, result reporting
- **Communication:** Via CLI commands or MCP tools
- **State-driven:** AI agent reads `state.json` to know what to do next

**Separation of Concerns:**

| TaskMaster Responsibilities | AI Agent Responsibilities |
|----------------------------|---------------------------|
| Workflow state machine | Generate tests |
| Validate phase transitions | Run tests (knows test framework) |
| Create commits with metadata | Implement code |
| Store activity logs | Report test results |
| Manage git operations | Understand project context |
| Track progress | Choose appropriate test commands |

**Flow:**
```
AI Agent                                    TaskMaster
   │                                            │
   ├──► tm autopilot start 1                    │
   │                                            ├──► Creates state, branch
   │                                            ├──► Returns: "next action: RED phase for 1.1"
   │                                            │
   ├──► tm autopilot next                       │
   │                                            ├──► Reads state.json
   │                                            ├──► Returns: { phase: "red", subtask: "1.1", context: {...} }
   │                                            │
   │    Generate tests (AI does this)           │
   │    npm test (AI runs this)                 │
   │    Results: 3 failed, 0 passed             │
   │                                            │
   ├──► tm autopilot complete red 1.1 \         │
   │    --results="failed:3,passed:0"           │
   │                                            ├──► Validates: tests failed ✓
   │                                            ├──► Updates state to GREEN
   │                                            ├──► Returns: "next action: GREEN phase"
   │                                            │
   ├──► tm autopilot next                       │
   │                                            ├──► Returns: { phase: "green", subtask: "1.1" }
   │                                            │
   │    Implement code (AI does this)           │
   │    npm test (AI runs this)                 │
   │    Results: 3 passed, 0 failed             │
   │                                            │
   ├──► tm autopilot complete green 1.1 \       │
   │    --results="passed:3,failed:0"           │
   │                                            ├──► Validates: tests passed ✓
   │                                            ├──► Updates state to COMMIT
   │                                            ├──► Returns: "next action: COMMIT phase"
   │                                            │
   ├──► tm autopilot commit 1.1                 │
   │                                            ├──► Detects changed files (git status)
   │                                            ├──► Stages files
   │                                            ├──► Creates commit with metadata
   │                                            ├──► Updates state to next subtask
   │                                            ├──► Returns: { sha: "a1b2c3d", nextAction: {...} }
   │                                            │
   └──► Loop continues...                        │
```

**Key principle:** AI agent is the domain expert (knows the codebase, frameworks, tools). TaskMaster is the workflow expert (knows TDD process, state management, git operations).

## Deliverables

### 1. WorkflowOrchestrator (`packages/tm-core/src/services/workflow-orchestrator.ts`)

**Responsibilities:**
- State machine driving phases: Preflight → Branch/Tag → SubtaskIter → Finalize
- Event emission for progress tracking
- Coordination of Git, Test, and Executor adapters
- Run state persistence

**API:**
```typescript
class WorkflowOrchestrator {
  async executeTask(taskId: string, options: AutopilotOptions): Promise<RunResult>
  async resume(runId: string): Promise<RunResult>
  on(event: string, handler: (data: any) => void): void

  // Events emitted:
  // - 'phase:start' { phase, timestamp }
  // - 'phase:complete' { phase, status, timestamp }
  // - 'subtask:start' { subtaskId, phase }
  // - 'subtask:complete' { subtaskId, phase, status }
  // - 'test:run' { subtaskId, phase, results }
  // - 'commit:created' { subtaskId, sha, message }
  // - 'error' { phase, error, recoverable }
}
```

**State Machine Phases:**
1. Preflight - validate environment
2. BranchSetup - create branch, set tag
3. SubtaskLoop - for each subtask: RED → GREEN → COMMIT
4. Finalize - full test suite, coverage check
5. Complete - run report, cleanup

### 2. GitAdapter (`packages/tm-core/src/services/git-adapter.ts`)

**Responsibilities:**
- All git operations with safety checks
- Branch name generation from tag/task
- Confirmation gates for destructive operations

**API:**
```typescript
class GitAdapter {
  async isWorkingTreeClean(): Promise<boolean>
  async getCurrentBranch(): Promise<string>
  async getDefaultBranch(): Promise<string>
  async createBranch(name: string): Promise<void>
  async checkoutBranch(name: string): Promise<void>
  async commit(message: string, files?: string[]): Promise<string>
  async push(branch: string, remote?: string): Promise<void>

  // Safety checks
  async assertNotOnDefaultBranch(): Promise<void>
  async assertCleanOrConfirm(): Promise<void>

  // Branch naming
  generateBranchName(tag: string, taskId: string, slug: string): string
}
```

**Guardrails:**
- Never allow commits on default branch
- Always check working tree before branch creation
- Confirm destructive operations unless `--no-confirm` flag

### 3. Test Result Validator (`packages/tm-core/src/services/test-result-validator.ts`)

**Responsibilities:**
- Validate test results reported by AI agent
- Ensure RED phase has failing tests
- Ensure GREEN phase has passing tests
- Enforce coverage thresholds (if provided)

**API:**
```typescript
class TestResultValidator {
  async validateRedPhase(results: TestResults): Promise<ValidationResult>
  async validateGreenPhase(results: TestResults, coverage?: number): Promise<ValidationResult>
  async meetsThresholds(coverage: number): Promise<boolean>
}

interface TestResults {
  passed: number
  failed: number
  skipped?: number
  total: number
}

interface ValidationResult {
  valid: boolean
  message: string
  suggestion?: string
}
```

**Validation Logic:**
```typescript
async function validateRedPhase(results: TestResults): ValidationResult {
  if (results.failed === 0) {
    return {
      valid: false,
      message: "RED phase requires failing tests. All tests passed.",
      suggestion: "Verify tests are checking expected behavior. Tests should fail before implementation."
    }
  }

  if (results.passed > 0) {
    return {
      valid: true,
      message: `RED phase valid: ${results.failed} failing, ${results.passed} passing (existing tests)`,
      warning: "Some tests passing - ensure new tests are failing"
    }
  }

  return {
    valid: true,
    message: `RED phase complete: ${results.failed} tests failing as expected`
  }
}

async function validateGreenPhase(results: TestResults): ValidationResult {
  if (results.failed > 0) {
    return {
      valid: false,
      message: `GREEN phase incomplete: ${results.failed} tests still failing`,
      suggestion: "Continue implementing until all tests pass or retry GREEN phase"
    }
  }

  return {
    valid: true,
    message: `GREEN phase complete: ${results.passed} tests passing`
  }
}
```

**Note:** AI agent is responsible for:
- Running test commands (knows npm test vs pytest vs go test)
- Parsing test output
- Reporting results to TaskMaster

TaskMaster only validates the reported numbers make sense for the phase.

### 4. Test Generation Integration

**Use Surgical Test Generator:**
- Load prompt from `.claude/agents/surgical-test-generator.md`
- Compose with task/subtask context
- Generate tests via executor (Claude)
- Write test files to detected locations

**Prompt Composition:**
```typescript
async function composeRedPrompt(subtask: Subtask, context: ProjectContext): Promise<string> {
  const systemPrompts = [
    loadFile('.cursor/rules/git_workflow.mdc'),
    loadFile('.cursor/rules/test_workflow.mdc'),
    loadFile('.claude/agents/surgical-test-generator.md')
  ]

  const taskContext = formatTaskContext(subtask)
  const instruction = formatRedInstruction(subtask, context)

  return [
    ...systemPrompts,
    '<TASK CONTEXT>',
    taskContext,
    '<INSTRUCTION>',
    instruction
  ].join('\n\n')
}
```

### 5. Subtask Loop Implementation

**RED Phase:**
1. TaskMaster returns RED action with subtask context
2. AI agent generates tests (TaskMaster not involved)
3. AI agent writes test files (TaskMaster not involved)
4. AI agent runs tests using project's test command (e.g., npm test)
5. AI agent reports results: `tm autopilot complete red <id> --results="failed:3,passed:0"`
6. TaskMaster validates: tests should have failures
7. If validation fails (tests passed), return error with suggestion
8. If validation passes, update state to GREEN, store results in activity log
9. Return next action (GREEN phase)

**GREEN Phase:**
1. TaskMaster returns GREEN action with subtask context
2. AI agent implements code (TaskMaster not involved)
3. AI agent runs tests using project's test command
4. AI agent reports results: `tm autopilot complete green <id> --results="passed:5,failed:0" --coverage="85"`
5. TaskMaster validates: all tests should pass
6. If validation fails (tests still failing):
   - Increment attempt counter
   - If under max attempts: return GREEN action again with attempt number
   - If max attempts reached: save state, emit pause event, return resumable checkpoint
7. If validation passes: update state to COMMIT, store results in activity log
8. Return next action (COMMIT phase)

**COMMIT Phase:**
1. TaskMaster receives commit command: `tm autopilot commit <id>`
2. Detect changed files: `git status --porcelain`
3. Validate coverage meets thresholds (if provided and threshold configured)
4. Generate conventional commit message with task metadata
5. Stage files: `git add <files>`
6. Create commit: `git commit -m "<message>"`
7. Update subtask status to 'done' in tasks.json
8. Log commit event to activity.jsonl
9. Update state to next subtask's RED phase
10. Return next action

**Key changes from original design:**
- AI agent runs all test commands (framework agnostic)
- TaskMaster only validates reported results
- No test framework detection needed
- No test execution by TaskMaster
- AI agent is trusted to report accurate results

### 6. Branch & Tag Management

**Integration with existing tag system:**
- Use `scripts/modules/task-manager/tag-management.js`
- Explicit tag switching when branch created
- Store branch ↔ tag mapping in run state

**Branch Naming:**
- Pattern from config: `{tag}/task-{id}-{slug}`
- Default: `analytics/task-42-user-metrics`
- Sanitize: lowercase, replace spaces with hyphens

### 7. Global Storage & State Management

**Philosophy:**
- All runtime state, logs, and throwaway artifacts stored globally in `~/.taskmaster/`
- Project directory stays clean - only code changes and tasks.json versioned
- Enables single-player autonomous mode without polluting PRs
- Client-friendly: no evidence of tooling in source code

**Global directory structure:**
```
~/.taskmaster/
├── projects/
│   └── <project-path-normalized>/
│       ├── runs/
│       │   └── <tag>__task-<id>__<timestamp>/
│       │       ├── manifest.json          # run metadata
│       │       ├── activity.jsonl         # event stream (debugging)
│       │       ├── state.json             # resumable checkpoint
│       │       ├── commits.txt            # commit SHAs
│       │       └── test-results/
│       │           ├── subtask-1.1-red.json
│       │           ├── subtask-1.1-green.json
│       │           └── final-suite.json
│       └── tags/
│           └── <tag-name>/
│               └── current-run.json       # active run pointer
└── cache/
    └── templates/                          # shared templates
```

**Project path normalization:**
```typescript
function getProjectStoragePath(projectRoot: string): string {
  const normalized = projectRoot
    .replace(/\//g, '-')
    .replace(/^-/, '')

  return path.join(os.homedir(), '.taskmaster', 'projects', normalized)
  // Example: ~/.taskmaster/projects/-Volumes-Workspace-contrib-task-master-claude-task-master
}
```

**Run ID generation:**
```typescript
function generateRunId(tag: string, taskId: string): string {
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
  return `${tag}__task-${taskId}__${timestamp}`
  // Example: tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z
}
```

**manifest.json:**
```json
{
  "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z",
  "projectRoot": "/Volumes/Workspace/contrib/task-master/claude-task-master",
  "taskId": "1",
  "tag": "tdd-workflow-phase-0",
  "branch": "tdd-phase-0-implementation",
  "startTime": "2025-10-07T14:30:00Z",
  "endTime": null,
  "status": "in-progress",
  "currentPhase": "subtask-loop",
  "currentSubtask": "1.2",
  "subtasksCompleted": ["1.1"],
  "subtasksFailed": [],
  "totalCommits": 1
}
```

**state.json** (orchestration state):
```json
{
  "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z",
  "taskId": "1",
  "tag": "tdd-workflow-phase-0",
  "branch": "tdd-phase-0-implementation",
  "currentSubtask": "1.2",
  "currentPhase": "green",
  "attemptNumber": 1,
  "maxAttempts": 3,
  "completedSubtasks": ["1.1"],
  "pendingSubtasks": ["1.2", "1.3", "1.4"],
  "nextAction": {
    "type": "implement",
    "subtask": "1.2",
    "phase": "green",
    "context": {
      "testFile": "src/__tests__/preflight.test.ts",
      "failingTests": [
        "should detect test runner from package.json",
        "should validate git working tree"
      ],
      "implementationFiles": ["src/services/preflight-checker.ts"]
    }
  },
  "lastUpdated": "2025-10-07T14:31:45Z",
  "canResume": true
}
```

**activity.jsonl** (append-only event log):
```jsonl
{"ts":"2025-10-07T14:30:00Z","event":"phase:start","phase":"preflight","status":"ok"}
{"ts":"2025-10-07T14:30:15Z","event":"phase:complete","phase":"preflight","checks":{"git":true,"test":true,"tools":true}}
{"ts":"2025-10-07T14:30:20Z","event":"branch:created","branch":"tdd-phase-0-implementation"}
{"ts":"2025-10-07T14:30:22Z","event":"tag:switched","from":"master","to":"tdd-workflow-phase-0"}
{"ts":"2025-10-07T14:30:25Z","event":"subtask:start","subtaskId":"1.1","phase":"red"}
{"ts":"2025-10-07T14:31:10Z","event":"test:generated","files":["src/__tests__/autopilot.test.ts"],"testCount":3}
{"ts":"2025-10-07T14:31:15Z","event":"test:run","subtaskId":"1.1","phase":"red","passed":0,"failed":3,"status":"expected"}
{"ts":"2025-10-07T14:31:20Z","event":"phase:transition","from":"red","to":"green"}
{"ts":"2025-10-07T14:32:45Z","event":"code:modified","files":["src/commands/autopilot.ts"],"linesChanged":"+58,-0"}
{"ts":"2025-10-07T14:33:00Z","event":"test:run","subtaskId":"1.1","phase":"green","attempt":1,"passed":3,"failed":0,"status":"success"}
{"ts":"2025-10-07T14:33:15Z","event":"commit:created","subtaskId":"1.1","sha":"a1b2c3d","message":"feat(cli): add autopilot command skeleton (task 1.1)"}
{"ts":"2025-10-07T14:33:20Z","event":"subtask:complete","subtaskId":"1.1","duration":"180s"}
```

**current-run.json** (active run pointer):
```json
{
  "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z",
  "taskId": "1",
  "tag": "tdd-workflow-phase-0",
  "startTime": "2025-10-07T14:30:00Z",
  "status": "in-progress"
}
```

**What stays in project (versioned):**
```
<project>/
├── .taskmaster/
│   ├── tasks/
│   │   └── tasks.json              # ✅ Versioned (task definitions)
│   └── config.json                 # ✅ Versioned (shared config)
└── .gitignore                       # Add: .taskmaster/state/, .taskmaster/reports/
```

**State vs Activity Log:**

| State File (state.json) | Activity Log (activity.jsonl) |
|------------------------|-------------------------------|
| Current position | Full history |
| What to do next | What happened |
| Mutable (updated) | Immutable (append-only) |
| For orchestration | For debugging/audit |
| Single JSON object | Line-delimited JSON |
| Small (~2KB) | Can grow large |

**Resume logic:**
```typescript
async function resumeWorkflow(): Promise<void> {
  // 1. Find active run
  const currentRun = await loadJSON('~/.taskmaster/projects/<project>/tags/<tag>/current-run.json')

  // 2. Load state from that run
  const state = await loadJSON(`~/.taskmaster/projects/<project>/runs/${currentRun.runId}/state.json`)

  // 3. Continue from checkpoint
  return orchestrator.resumeFrom(state)
}
```

### 8. Enhanced Commit Message Format

**Purpose:**
- Embed task context in commits for future validation
- Enable task-checker bot to verify alignment
- Provide audit trail without needing external logs in PR

**Commit message template:**
```
{type}({scope}): {summary} (task {taskId})

{detailed description}

Task: #{taskId} - {taskTitle}
Phase: {phaseName}
Tag: {tagName}

Tests: {testCount} passing
Coverage: {coveragePercent}% lines
```

**Example commit:**
```
feat(cli): add autopilot command skeleton (task 1.1)

Implements AutopilotCommand class with Commander.js integration.
Adds argument parsing for task ID and dry-run flag. Includes basic
command registration and help text following existing CLI patterns.

Task: #1.1 - Create command structure
Phase: Phase 0 - Spike
Tag: tdd-workflow-phase-0

Tests: 3 passing
Coverage: 92% lines
```

**Conventional commit types:**
- `feat` - New feature or capability
- `fix` - Bug fix
- `test` - Test-only changes
- `refactor` - Code restructuring without behavior change
- `docs` - Documentation updates
- `chore` - Build/tooling changes

**Scope determination:**
```typescript
function determineScope(files: string[]): string {
  // Extract common scope from changed files
  const scopes = files.map(f => {
    if (f.startsWith('apps/cli/')) return 'cli'
    if (f.startsWith('packages/tm-core/')) return 'core'
    if (f.startsWith('packages/tm-mcp/')) return 'mcp'
    return 'misc'
  })

  // Use most common scope
  return mode(scopes)
}
```

**Commit validation (future task-checker bot):**
```typescript
async function validateCommit(commit: Commit, task: Task): Promise<ValidationResult> {
  const taskId = extractTaskId(commit.message)  // "1.1"
  const task = await loadTask(taskId)

  return aiChecker.validate({
    commitDiff: commit.diff,
    commitMessage: commit.message,
    taskDescription: task.description,
    acceptanceCriteria: task.acceptanceCriteria,
    testStrategy: task.testStrategy
  })
}
```

### 9. CLI Commands for AI Agent Orchestration

**New CLI commands** (all under `tm autopilot` namespace):

```bash
# Start workflow - creates branch, initializes state
tm autopilot start <taskId> [options]
  --branch <name>       # Override branch name
  --no-confirm          # Skip confirmations
  --max-attempts <n>    # Override max GREEN attempts

# Get next action from state
tm autopilot next [options]
  --json                # Output as JSON for parsing

# Complete a phase and report test results
tm autopilot complete <phase> <subtaskId> --results="<passed:n,failed:n>" [options]
  # phase: red | green
  --results <passed:n,failed:n>  # Required: test results from AI
  --coverage <percentage>        # Optional: coverage percentage
  --files <file1,file2>          # Optional: files changed (auto-detected if omitted)

# Create commit (called by AI after GREEN passes)
tm autopilot commit <subtaskId> [options]
  --message <msg>       # Override commit message

# Resume from interrupted run
tm autopilot resume [options]
  --run-id <id>         # Specific run to resume

# Get current status
tm autopilot status
  --json                # Output as JSON

# Watch activity log in real-time
tm autopilot watch

# Abort current run
tm autopilot abort [options]
  --cleanup             # Delete branch and state
```

**Command details:**

**`tm autopilot start <taskId>`**
- Creates global state directory
- Creates feature branch
- Switches tag
- Initializes state.json with first subtask
- Returns next action (RED phase for first subtask)

**`tm autopilot next`**
- Reads `~/.taskmaster/projects/<project>/tags/<tag>/current-run.json`
- Reads `~/.taskmaster/projects/<project>/runs/<run-id>/state.json`
- Returns next action with full context

Output:
```json
{
  "action": "red",
  "subtask": {
    "id": "1.1",
    "title": "Create command structure",
    "description": "...",
    "testStrategy": "..."
  },
  "context": {
    "projectRoot": "/path/to/project",
    "testPattern": "**/*.test.ts",
    "existingTests": []
  },
  "instructions": "Generate tests for this subtask. Tests should fail initially."
}
```

**`tm autopilot complete <phase> <subtaskId>`**
- Receives test results from AI agent
- Validates phase completion:
  - **RED**: Ensures reported results show failures
  - **GREEN**: Ensures reported results show all tests passing
- Updates state to next phase
- Logs event to activity.jsonl with test results
- Returns next action

**Examples:**
```bash
# After AI generates tests and runs them
tm autopilot complete red 1.1 --results="failed:3,passed:0"

# After AI implements code and runs tests
tm autopilot complete green 1.1 --results="passed:3,failed:0" --coverage="92"

# With existing passing tests
tm autopilot complete red 1.1 --results="failed:3,passed:12"
```

**`tm autopilot commit <subtaskId>`**
- Generates commit message from template
- Stages files
- Creates commit with enhanced message
- Updates subtask status to 'done'
- Updates state to next subtask
- Returns next action

**`tm autopilot status`**
```json
{
  "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z",
  "taskId": "1",
  "currentSubtask": "1.2",
  "currentPhase": "green",
  "attemptNumber": 1,
  "progress": {
    "completed": ["1.1"],
    "current": "1.2",
    "remaining": ["1.3", "1.4"]
  },
  "commits": 1,
  "startTime": "2025-10-07T14:30:00Z",
  "duration": "5m 30s"
}
```

### 10. MCP Tools for AI Agent Orchestration

**New MCP tools** (add to `packages/tm-mcp/src/tools/`):

```typescript
// autopilot_start
{
  name: "autopilot_start",
  description: "Start autonomous TDD workflow for a task",
  parameters: {
    taskId: string,
    options?: {
      branch?: string,
      maxAttempts?: number
    }
  },
  returns: {
    runId: string,
    branch: string,
    nextAction: NextAction
  }
}

// autopilot_next
{
  name: "autopilot_next",
  description: "Get next action from workflow state",
  parameters: {
    projectRoot?: string  // defaults to current
  },
  returns: {
    action: "red" | "green" | "commit" | "complete",
    subtask: Subtask,
    context: Context,
    instructions: string
  }
}

// autopilot_complete_phase
{
  name: "autopilot_complete_phase",
  description: "Report test results and validate phase completion",
  parameters: {
    phase: "red" | "green",
    subtaskId: string,
    testResults: {
      passed: number,
      failed: number,
      skipped?: number
    },
    coverage?: number,  // Optional coverage percentage
    files?: string[]    // Optional, auto-detected if not provided
  },
  returns: {
    validated: boolean,
    message: string,
    suggestion?: string,
    nextAction: NextAction
  }
}

// autopilot_commit
{
  name: "autopilot_commit",
  description: "Create commit for completed subtask",
  parameters: {
    subtaskId: string,
    files?: string[],
    message?: string  // Override
  },
  returns: {
    commitSha: string,
    message: string,
    nextAction: NextAction
  }
}

// autopilot_status
{
  name: "autopilot_status",
  description: "Get current workflow status",
  parameters: {
    projectRoot?: string
  },
  returns: {
    runId: string,
    taskId: string,
    currentSubtask: string,
    currentPhase: string,
    progress: Progress,
    commits: number
  }
}

// autopilot_resume
{
  name: "autopilot_resume",
  description: "Resume interrupted workflow",
  parameters: {
    runId?: string  // defaults to current
  },
  returns: {
    resumed: boolean,
    nextAction: NextAction
  }
}
```

**MCP tool usage example (Claude Code session):**

```javascript
// AI agent calls MCP tools
const { runId, nextAction } = await mcp.autopilot_start({ taskId: "1" })

while (nextAction.action !== "complete") {
  const action = await mcp.autopilot_next()

  if (action.action === "red") {
    // AI generates tests
    const tests = await generateTests(action.subtask, action.context)
    await writeFiles(tests)

    // AI runs tests (using project's test command)
    const testOutput = await runCommand("npm test")  // or pytest, go test, etc.
    const results = parseTestOutput(testOutput)

    // Report results to TaskMaster
    const validation = await mcp.autopilot_complete_phase({
      phase: "red",
      subtaskId: action.subtask.id,
      testResults: {
        passed: results.passed,
        failed: results.failed,
        skipped: results.skipped
      }
    })

    if (!validation.validated) {
      console.error(validation.message)
      // Handle validation failure
    }
  }

  if (action.action === "green") {
    // AI implements code
    const impl = await implementCode(action.subtask, action.context)
    await writeFiles(impl)

    // AI runs tests again
    const testOutput = await runCommand("npm test")
    const results = parseTestOutput(testOutput)
    const coverage = parseCoverage(testOutput)

    // Report results to TaskMaster
    const validation = await mcp.autopilot_complete_phase({
      phase: "green",
      subtaskId: action.subtask.id,
      testResults: {
        passed: results.passed,
        failed: results.failed
      },
      coverage: coverage.lines
    })

    if (!validation.validated) {
      console.log(validation.message, validation.suggestion)
      // Retry or handle failure
    }
  }

  if (action.action === "commit") {
    // TaskMaster creates the commit
    const { commitSha, nextAction: next } = await mcp.autopilot_commit({
      subtaskId: action.subtask.id
    })

    nextAction = next
  }
}
```

### 11. AI Agent Instructions (CLAUDE.md integration)

Add to `.claude/CLAUDE.md` or `.cursor/rules/`:

````markdown
## TaskMaster Autonomous Workflow

When working on tasks with `tm autopilot`:

1. **Start workflow:**
   ```bash
   tm autopilot start <taskId>
   ```

2. **Loop until complete:**
   ```bash
   # Get next action
   NEXT=$(tm autopilot next --json)
   ACTION=$(echo $NEXT | jq -r '.action')
   SUBTASK=$(echo $NEXT | jq -r '.subtask.id')

   case $ACTION in
     red)
       # 1. Generate tests based on instructions
       # 2. Write test files
       # 3. Run tests yourself (you know the test command)
       npm test  # or pytest, go test, cargo test, etc.

       # 4. Report results to TaskMaster
       tm autopilot complete red $SUBTASK --results="failed:3,passed:0"
       ;;

     green)
       # 1. Implement code to pass tests
       # 2. Write implementation files
       # 3. Run tests yourself
       npm test

       # 4. Report results to TaskMaster (include coverage if available)
       tm autopilot complete green $SUBTASK --results="passed:3,failed:0" --coverage="92"
       ;;

     commit)
       # TaskMaster handles git operations
       tm autopilot commit $SUBTASK
       ;;

     complete)
       echo "Workflow complete!"
       ;;
   esac
   ```

3. **State is preserved** - you can stop/resume anytime with `tm autopilot resume`

**Important:** You are responsible for:
- Running test commands (TaskMaster doesn't know your test framework)
- Parsing test output (passed/failed counts)
- Reporting accurate results

**Via MCP:**
Use `autopilot_*` tools for the same workflow with better integration.
````

**Example AI agent prompt:**

```markdown
You are working autonomously on Task Master tasks using the autopilot workflow.

Instructions:
1. Call `tm autopilot next --json` to get your next action
2. Read the action type and context
3. Execute the action:
   - **RED**:
     * Generate tests that fail initially
     * Run tests: `npm test` (or appropriate test command)
     * Report: `tm autopilot complete red <id> --results="failed:n,passed:n"`
   - **GREEN**:
     * Implement code to pass the tests
     * Run tests: `npm test`
     * Report: `tm autopilot complete green <id> --results="passed:n,failed:n" --coverage="nn"`
   - **COMMIT**:
     * Call: `tm autopilot commit <id>` (TaskMaster handles git)
4. Repeat until action is "complete"

Always:
- Follow TDD principles (RED → GREEN → COMMIT)
- YOU run the tests (TaskMaster doesn't know test frameworks)
- Report accurate test results (passed/failed counts)
- Write minimal code to pass tests
- Check `tm autopilot status` if unsure of current state

You are responsible for:
- Knowing which test command to run (npm test, pytest, go test, etc.)
- Parsing test output to get pass/fail counts
- Understanding the project's testing framework
- Running tests after generating/implementing code

TaskMaster is responsible for:
- Validating your reported results make sense (RED should fail, GREEN should pass)
- Creating properly formatted git commits
- Managing workflow state and transitions
```

## Success Criteria
- Can execute a simple task end-to-end without manual intervention
- All commits made on feature branch, never on default branch
- Tests are generated before implementation (RED → GREEN order enforced)
- Only commits when tests pass and coverage meets threshold
- Run state is persisted and can be inspected post-run
- Clear error messages when things go wrong
- Orchestrator events allow CLI to show live progress

## Configuration

**Add to `.taskmaster/config.json` (versioned):**
```json
{
  "autopilot": {
    "enabled": true,
    "requireCleanWorkingTree": true,
    "commitTemplate": "{type}({scope}): {msg} (task {taskId})",
    "defaultCommitType": "feat",
    "maxGreenAttempts": 3,
    "testTimeout": 300000,
    "storage": {
      "location": "global",
      "basePath": "~/.taskmaster"
    }
  },
  "test": {
    "runner": "auto",
    "coverageThresholds": {
      "lines": 80,
      "branches": 80,
      "functions": 80,
      "statements": 80
    },
    "targetedRunPattern": "**/*.test.js"
  },
  "git": {
    "branchPattern": "{tag}/task-{id}-{slug}",
    "defaultRemote": "origin"
  }
}
```

**Update `.gitignore` (keep project clean):**
```gitignore
# TaskMaster runtime artifacts (stored globally, not needed in repo)
.taskmaster/state/
.taskmaster/reports/

# Keep these versioned
!.taskmaster/tasks/
!.taskmaster/config.json
!.taskmaster/docs/
!.taskmaster/templates/
```

## Out of Scope (defer to Phase 2)
- PR creation (gh integration)
- Resume functionality (`--resume` flag)
- Lint/format step
- Multiple executor support (only Claude)

## Implementation Order

### Phase 1A: Infrastructure (Week 1)
1. Global storage utilities (path normalization, run ID generation)
2. Activity log writer (append-only JSONL)
3. State manager (load/save/update state.json)
4. GitAdapter with safety checks
5. TestResultValidator (validate RED/GREEN phase results)

### Phase 1B: Orchestration Core (Week 1-2)
6. WorkflowOrchestrator state machine skeleton
7. State transitions (Preflight → BranchSetup → SubtaskLoop → Finalize)
8. Event emitter for activity logging
9. Enhanced commit message generator

### Phase 1C: TDD Loop (Week 2)
10. RED phase validator (ensure tests fail)
11. GREEN phase validator (ensure tests pass)
12. COMMIT phase implementation (staging, committing)
13. Subtask progression logic

### Phase 1D: CLI Interface (Week 2-3)
14. `tm autopilot start` command
15. `tm autopilot next` command
16. `tm autopilot complete` command
17. `tm autopilot commit` command
18. `tm autopilot status` command
19. `tm autopilot resume` command

### Phase 1E: MCP Interface (Week 3)
20. `autopilot_start` tool
21. `autopilot_next` tool
22. `autopilot_complete_phase` tool
23. `autopilot_commit` tool
24. `autopilot_status` tool
25. `autopilot_resume` tool

### Phase 1F: Integration (Week 3)
26. AI agent instruction templates
27. Error handling and recovery
28. Integration tests
29. Documentation

## Testing Strategy
- Unit tests for global storage (path normalization, state management)
- Unit tests for activity log (JSONL append, parsing)
- Unit tests for each adapter (mock git/test commands)
- Integration tests with real git repo (temporary directory)
- End-to-end test with sample task in test project
- Verify no commits on default branch (security test)
- Verify commit gating works (force test failure, ensure no commit)
- Verify enhanced commit messages include task context
- Test resume from state checkpoint
- Verify project directory stays clean (no runtime artifacts)

## Dependencies
- Phase 0 completed (CLI skeleton, preflight checks)
- Existing TaskService and executor infrastructure
- Surgical Test Generator prompt file exists

## Estimated Effort
2-3 weeks

## Risks & Mitigations
- **Risk:** Test generation produces invalid/wrong tests
  - **Mitigation:** Use Surgical Test Generator prompt, add manual review step in early iterations

- **Risk:** Implementation attempts timeout/fail repeatedly
  - **Mitigation:** Max attempts with pause/resume; store state for manual intervention

- **Risk:** Coverage parsing fails on different test frameworks
  - **Mitigation:** Start with one framework (vitest), add parsers incrementally

- **Risk:** Git operations fail (conflicts, permissions)
  - **Mitigation:** Detailed error messages, save state before destructive ops

## Validation
Test with:
- Simple task (1 subtask, clear requirements)
- Medium task (3 subtasks with dependencies)
- Task requiring multiple GREEN attempts
- Task with dirty working tree (should error)
- Task on default branch (should error)
- Project without test command (should error with helpful message)
- Verify global storage created in `~/.taskmaster/projects/<project>/`
- Verify activity log is valid JSONL and streamable
- Verify state.json allows resumption
- Verify commit messages include task metadata
- Verify project directory contains no runtime artifacts after run
- Test with multiple worktrees (independent state per worktree)

```

--------------------------------------------------------------------------------
/packages/tm-core/src/modules/git/adapters/git-adapter.test.ts:
--------------------------------------------------------------------------------

```typescript
import os from 'os';
import path from 'path';
import {
	afterEach,
	beforeEach,
	describe,
	expect,
	it,
	jest
} from '@jest/globals';
import fs from 'fs-extra';
import { GitAdapter } from '../../../../../packages/tm-core/src/git/git-adapter.js';

describe('GitAdapter - Repository Detection and Validation', () => {
	let testDir;
	let gitAdapter;

	beforeEach(async () => {
		// Create temporary test directory
		testDir = path.join(os.tmpdir(), `git-test-${Date.now()}`);
		await fs.ensureDir(testDir);
	});

	afterEach(async () => {
		// Clean up test directory
		await fs.remove(testDir);
	});

	describe('isGitRepository', () => {
		it('should return false for non-git directory', async () => {
			gitAdapter = new GitAdapter(testDir);

			const isRepo = await gitAdapter.isGitRepository();

			expect(isRepo).toBe(false);
		});

		it('should return true for git repository', async () => {
			// Initialize real git repo
			await fs.ensureDir(path.join(testDir, '.git'));
			await fs.ensureDir(path.join(testDir, '.git', 'objects'));
			await fs.ensureDir(path.join(testDir, '.git', 'refs'));
			await fs.writeFile(
				path.join(testDir, '.git', 'HEAD'),
				'ref: refs/heads/main\n'
			);

			gitAdapter = new GitAdapter(testDir);

			const isRepo = await gitAdapter.isGitRepository();

			expect(isRepo).toBe(true);
		});

		it('should detect git repository in subdirectory', async () => {
			// Initialize real git repo in parent
			await fs.ensureDir(path.join(testDir, '.git'));
			await fs.ensureDir(path.join(testDir, '.git', 'objects'));
			await fs.ensureDir(path.join(testDir, '.git', 'refs'));
			await fs.writeFile(
				path.join(testDir, '.git', 'HEAD'),
				'ref: refs/heads/main\n'
			);

			// Create subdirectory
			const subDir = path.join(testDir, 'src', 'components');
			await fs.ensureDir(subDir);

			gitAdapter = new GitAdapter(subDir);

			const isRepo = await gitAdapter.isGitRepository();

			expect(isRepo).toBe(true);
		});

		it('should handle directory with .git file (submodule)', async () => {
			// Create .git file (used in submodules/worktrees)
			await fs.writeFile(path.join(testDir, '.git'), 'gitdir: /path/to/git');

			gitAdapter = new GitAdapter(testDir);

			const isRepo = await gitAdapter.isGitRepository();

			expect(isRepo).toBe(true);
		});

		it('should return false if .git is neither file nor directory', async () => {
			gitAdapter = new GitAdapter(testDir);

			const isRepo = await gitAdapter.isGitRepository();

			expect(isRepo).toBe(false);
		});
	});

	describe('validateGitInstallation', () => {
		it('should validate git is installed', async () => {
			gitAdapter = new GitAdapter(testDir);

			await expect(gitAdapter.validateGitInstallation()).resolves.not.toThrow();
		});

		it('should throw error if git version check fails', async () => {
			gitAdapter = new GitAdapter(testDir);

			// Mock simple-git to throw error
			const mockGit = {
				version: jest.fn().mockRejectedValue(new Error('git not found'))
			};
			gitAdapter.git = mockGit;

			await expect(gitAdapter.validateGitInstallation()).rejects.toThrow(
				'git not found'
			);
		});

		it('should return git version info', async () => {
			gitAdapter = new GitAdapter(testDir);

			const versionInfo = await gitAdapter.getGitVersion();

			expect(versionInfo).toBeDefined();
			expect(versionInfo.major).toBeGreaterThan(0);
		});
	});

	describe('getRepositoryRoot', () => {
		it('should return repository root path', async () => {
			// Initialize real git repo
			await fs.ensureDir(path.join(testDir, '.git'));
			await fs.ensureDir(path.join(testDir, '.git', 'objects'));
			await fs.ensureDir(path.join(testDir, '.git', 'refs'));
			await fs.writeFile(
				path.join(testDir, '.git', 'HEAD'),
				'ref: refs/heads/main\n'
			);

			gitAdapter = new GitAdapter(testDir);

			const root = await gitAdapter.getRepositoryRoot();

			// Resolve both paths to handle symlinks (e.g., /var vs /private/var on macOS)
			expect(await fs.realpath(root)).toBe(await fs.realpath(testDir));
		});

		it('should find repository root from subdirectory', async () => {
			// Initialize real git repo in parent
			await fs.ensureDir(path.join(testDir, '.git'));
			await fs.ensureDir(path.join(testDir, '.git', 'objects'));
			await fs.ensureDir(path.join(testDir, '.git', 'refs'));
			await fs.writeFile(
				path.join(testDir, '.git', 'HEAD'),
				'ref: refs/heads/main\n'
			);

			// Create subdirectory
			const subDir = path.join(testDir, 'src', 'components');
			await fs.ensureDir(subDir);

			gitAdapter = new GitAdapter(subDir);

			const root = await gitAdapter.getRepositoryRoot();

			// Resolve both paths to handle symlinks (e.g., /var vs /private/var on macOS)
			expect(await fs.realpath(root)).toBe(await fs.realpath(testDir));
		});

		it('should throw error if not in git repository', async () => {
			gitAdapter = new GitAdapter(testDir);

			await expect(gitAdapter.getRepositoryRoot()).rejects.toThrow(
				'not a git repository'
			);
		});
	});

	describe('validateRepository', () => {
		it('should validate repository is in good state', async () => {
			// Initialize git repo
			await fs.ensureDir(path.join(testDir, '.git'));
			await fs.ensureDir(path.join(testDir, '.git', 'refs'));
			await fs.ensureDir(path.join(testDir, '.git', 'objects'));
			await fs.writeFile(
				path.join(testDir, '.git', 'HEAD'),
				'ref: refs/heads/main\n'
			);

			gitAdapter = new GitAdapter(testDir);

			await expect(gitAdapter.validateRepository()).resolves.not.toThrow();
		});

		it('should throw error for non-git directory', async () => {
			gitAdapter = new GitAdapter(testDir);

			await expect(gitAdapter.validateRepository()).rejects.toThrow(
				'not a git repository'
			);
		});

		it('should detect corrupted repository', async () => {
			// Create .git directory but make it empty (corrupted)
			await fs.ensureDir(path.join(testDir, '.git'));

			gitAdapter = new GitAdapter(testDir);

			// This should either succeed or throw a specific error
			// depending on simple-git's behavior
			try {
				await gitAdapter.validateRepository();
			} catch (error) {
				expect(error.message).toMatch(/repository|git/i);
			}
		});
	});

	describe('ensureGitRepository', () => {
		it('should not throw if in valid git repository', async () => {
			// Initialize git repo
			await fs.ensureDir(path.join(testDir, '.git'));

			gitAdapter = new GitAdapter(testDir);

			await expect(gitAdapter.ensureGitRepository()).resolves.not.toThrow();
		});

		it('should throw error if not in git repository', async () => {
			gitAdapter = new GitAdapter(testDir);

			await expect(gitAdapter.ensureGitRepository()).rejects.toThrow(
				'not a git repository'
			);
		});

		it('should provide helpful error message', async () => {
			gitAdapter = new GitAdapter(testDir);

			try {
				await gitAdapter.ensureGitRepository();
				fail('Should have thrown error');
			} catch (error) {
				expect(error.message).toContain('not a git repository');
				expect(error.message).toContain(testDir);
			}
		});
	});

	describe('constructor', () => {
		it('should create GitAdapter with project path', () => {
			gitAdapter = new GitAdapter(testDir);

			expect(gitAdapter).toBeDefined();
			expect(gitAdapter.projectPath).toBe(testDir);
		});

		it('should normalize project path', () => {
			const unnormalizedPath = path.join(testDir, '..', path.basename(testDir));
			gitAdapter = new GitAdapter(unnormalizedPath);

			expect(gitAdapter.projectPath).toBe(testDir);
		});

		it('should initialize simple-git instance', () => {
			gitAdapter = new GitAdapter(testDir);

			expect(gitAdapter.git).toBeDefined();
		});

		it('should throw error for invalid path', () => {
			expect(() => new GitAdapter('')).toThrow('Project path is required');
		});

		it('should throw error for non-absolute path', () => {
			expect(() => new GitAdapter('./relative/path')).toThrow('absolute');
		});
	});

	describe('error handling', () => {
		it('should provide clear error for permission denied', async () => {
			// Create .git but make it inaccessible
			await fs.ensureDir(path.join(testDir, '.git'));

			gitAdapter = new GitAdapter(testDir);

			try {
				await fs.chmod(path.join(testDir, '.git'), 0o000);

				await gitAdapter.isGitRepository();
			} catch (error) {
				// Error handling
			} finally {
				// Restore permissions
				await fs.chmod(path.join(testDir, '.git'), 0o755);
			}
		});

		it('should handle symbolic links correctly', async () => {
			// Create actual git repo
			const realRepo = path.join(testDir, 'real-repo');
			await fs.ensureDir(path.join(realRepo, '.git'));

			// Create symlink
			const symlinkPath = path.join(testDir, 'symlink-repo');
			try {
				await fs.symlink(realRepo, symlinkPath);

				gitAdapter = new GitAdapter(symlinkPath);

				const isRepo = await gitAdapter.isGitRepository();

				expect(isRepo).toBe(true);
			} catch (error) {
				// Skip test on platforms without symlink support
				if (error.code !== 'EPERM') {
					throw error;
				}
			}
		});
	});

	describe('integration with simple-git', () => {
		it('should use simple-git for git operations', () => {
			gitAdapter = new GitAdapter(testDir);

			// Check that git instance is from simple-git
			expect(typeof gitAdapter.git.status).toBe('function');
			expect(typeof gitAdapter.git.branch).toBe('function');
		});

		it('should pass correct working directory to simple-git', () => {
			gitAdapter = new GitAdapter(testDir);

			// simple-git should be initialized with testDir
			expect(gitAdapter.git._executor).toBeDefined();
		});
	});
});

describe('GitAdapter - Working Tree Status', () => {
	let testDir;
	let gitAdapter;
	let simpleGit;

	beforeEach(async () => {
		testDir = path.join(os.tmpdir(), `git-status-test-${Date.now()}`);
		await fs.ensureDir(testDir);

		// Initialize actual git repo
		simpleGit = (await import('simple-git')).default;
		const git = simpleGit(testDir);
		await git.init();
		await git.addConfig('user.name', 'Test User');
		await git.addConfig('user.email', '[email protected]');

		gitAdapter = new GitAdapter(testDir);
	});

	afterEach(async () => {
		await fs.remove(testDir);
	});

	describe('isWorkingTreeClean', () => {
		it('should return true for clean working tree', async () => {
			const isClean = await gitAdapter.isWorkingTreeClean();
			expect(isClean).toBe(true);
		});

		it('should return false when files are modified', async () => {
			// Create and commit a file
			await fs.writeFile(path.join(testDir, 'test.txt'), 'initial');
			const git = simpleGit(testDir);
			await git.add('test.txt');
			await git.commit('initial commit', undefined, { '--no-gpg-sign': null });

			// Modify the file
			await fs.writeFile(path.join(testDir, 'test.txt'), 'modified');

			const isClean = await gitAdapter.isWorkingTreeClean();
			expect(isClean).toBe(false);
		});

		it('should return false when untracked files exist', async () => {
			await fs.writeFile(path.join(testDir, 'untracked.txt'), 'content');

			const isClean = await gitAdapter.isWorkingTreeClean();
			expect(isClean).toBe(false);
		});

		it('should return false when files are staged', async () => {
			await fs.writeFile(path.join(testDir, 'staged.txt'), 'content');
			const git = simpleGit(testDir);
			await git.add('staged.txt');

			const isClean = await gitAdapter.isWorkingTreeClean();
			expect(isClean).toBe(false);
		});
	});

	describe('getStatus', () => {
		it('should return status for clean repo', async () => {
			const status = await gitAdapter.getStatus();

			expect(status).toBeDefined();
			expect(status.modified).toEqual([]);
			expect(status.not_added).toEqual([]);
			expect(status.deleted).toEqual([]);
			expect(status.created).toEqual([]);
		});

		it('should detect modified files', async () => {
			// Create and commit
			await fs.writeFile(path.join(testDir, 'test.txt'), 'initial');
			const git = simpleGit(testDir);
			await git.add('test.txt');
			await git.commit('initial', undefined, { '--no-gpg-sign': null });

			// Modify
			await fs.writeFile(path.join(testDir, 'test.txt'), 'modified');

			const status = await gitAdapter.getStatus();
			expect(status.modified).toContain('test.txt');
		});

		it('should detect untracked files', async () => {
			await fs.writeFile(path.join(testDir, 'untracked.txt'), 'content');

			const status = await gitAdapter.getStatus();
			expect(status.not_added).toContain('untracked.txt');
		});

		it('should detect staged files', async () => {
			await fs.writeFile(path.join(testDir, 'staged.txt'), 'content');
			const git = simpleGit(testDir);
			await git.add('staged.txt');

			const status = await gitAdapter.getStatus();
			expect(status.created).toContain('staged.txt');
		});

		it('should detect deleted files', async () => {
			// Create and commit
			await fs.writeFile(path.join(testDir, 'deleted.txt'), 'content');
			const git = simpleGit(testDir);
			await git.add('deleted.txt');
			await git.commit('add file', undefined, { '--no-gpg-sign': null });

			// Delete
			await fs.remove(path.join(testDir, 'deleted.txt'));

			const status = await gitAdapter.getStatus();
			expect(status.deleted).toContain('deleted.txt');
		});
	});

	describe('hasUncommittedChanges', () => {
		it('should return false for clean repo', async () => {
			const hasChanges = await gitAdapter.hasUncommittedChanges();
			expect(hasChanges).toBe(false);
		});

		it('should return true for modified files', async () => {
			await fs.writeFile(path.join(testDir, 'test.txt'), 'initial');
			const git = simpleGit(testDir);
			await git.add('test.txt');
			await git.commit('initial', undefined, { '--no-gpg-sign': null });

			await fs.writeFile(path.join(testDir, 'test.txt'), 'modified');

			const hasChanges = await gitAdapter.hasUncommittedChanges();
			expect(hasChanges).toBe(true);
		});

		it('should return true for staged changes', async () => {
			await fs.writeFile(path.join(testDir, 'staged.txt'), 'content');
			const git = simpleGit(testDir);
			await git.add('staged.txt');

			const hasChanges = await gitAdapter.hasUncommittedChanges();
			expect(hasChanges).toBe(true);
		});
	});

	describe('hasStagedChanges', () => {
		it('should return false when no staged changes', async () => {
			const hasStaged = await gitAdapter.hasStagedChanges();
			expect(hasStaged).toBe(false);
		});

		it('should return true when files are staged', async () => {
			await fs.writeFile(path.join(testDir, 'staged.txt'), 'content');
			const git = simpleGit(testDir);
			await git.add('staged.txt');

			const hasStaged = await gitAdapter.hasStagedChanges();
			expect(hasStaged).toBe(true);
		});

		it('should return false for unstaged changes only', async () => {
			await fs.writeFile(path.join(testDir, 'test.txt'), 'initial');
			const git = simpleGit(testDir);
			await git.add('test.txt');
			await git.commit('initial', undefined, { '--no-gpg-sign': null });

			await fs.writeFile(path.join(testDir, 'test.txt'), 'modified');

			const hasStaged = await gitAdapter.hasStagedChanges();
			expect(hasStaged).toBe(false);
		});
	});

	describe('hasUntrackedFiles', () => {
		it('should return false when no untracked files', async () => {
			const hasUntracked = await gitAdapter.hasUntrackedFiles();
			expect(hasUntracked).toBe(false);
		});

		it('should return true when untracked files exist', async () => {
			await fs.writeFile(path.join(testDir, 'untracked.txt'), 'content');

			const hasUntracked = await gitAdapter.hasUntrackedFiles();
			expect(hasUntracked).toBe(true);
		});

		it('should not count staged files as untracked', async () => {
			await fs.writeFile(path.join(testDir, 'staged.txt'), 'content');
			const git = simpleGit(testDir);
			await git.add('staged.txt');

			const hasUntracked = await gitAdapter.hasUntrackedFiles();
			expect(hasUntracked).toBe(false);
		});
	});

	describe('getStatusSummary', () => {
		it('should provide summary for clean repo', async () => {
			const summary = await gitAdapter.getStatusSummary();

			expect(summary).toBeDefined();
			expect(summary.isClean).toBe(true);
			expect(summary.totalChanges).toBe(0);
		});

		it('should count all types of changes', async () => {
			// Create committed file
			await fs.writeFile(path.join(testDir, 'committed.txt'), 'content');
			const git = simpleGit(testDir);
			await git.add('committed.txt');
			await git.commit('initial', undefined, { '--no-gpg-sign': null });

			// Modify it
			await fs.writeFile(path.join(testDir, 'committed.txt'), 'modified');

			// Add untracked
			await fs.writeFile(path.join(testDir, 'untracked.txt'), 'content');

			// Add staged
			await fs.writeFile(path.join(testDir, 'staged.txt'), 'content');
			await git.add('staged.txt');

			const summary = await gitAdapter.getStatusSummary();

			expect(summary.isClean).toBe(false);
			expect(summary.totalChanges).toBeGreaterThan(0);
			expect(summary.modified).toBeGreaterThan(0);
			expect(summary.untracked).toBeGreaterThan(0);
			expect(summary.staged).toBeGreaterThan(0);
		});
	});

	describe('ensureCleanWorkingTree', () => {
		it('should not throw for clean repo', async () => {
			await expect(gitAdapter.ensureCleanWorkingTree()).resolves.not.toThrow();
		});

		it('should throw for dirty repo', async () => {
			await fs.writeFile(path.join(testDir, 'dirty.txt'), 'content');

			await expect(gitAdapter.ensureCleanWorkingTree()).rejects.toThrow(
				'working tree is not clean'
			);
		});

		it('should provide details about changes in error', async () => {
			await fs.writeFile(path.join(testDir, 'modified.txt'), 'content');

			try {
				await gitAdapter.ensureCleanWorkingTree();
				fail('Should have thrown');
			} catch (error) {
				expect(error.message).toContain('working tree is not clean');
			}
		});
	});

	describe('GitAdapter - Branch Operations', () => {
		let testDir;
		let gitAdapter;
		let simpleGit;

		beforeEach(async () => {
			testDir = path.join(os.tmpdir(), `git-branch-test-${Date.now()}`);
			await fs.ensureDir(testDir);

			// Initialize actual git repo with initial commit
			simpleGit = (await import('simple-git')).default;
			const git = simpleGit(testDir);
			await git.init();
			await git.addConfig('user.name', 'Test User');
			await git.addConfig('user.email', '[email protected]');

			// Create initial commit
			await fs.writeFile(path.join(testDir, 'README.md'), '# Test Repo');
			await git.add('README.md');
			await git.commit('Initial commit', undefined, { '--no-gpg-sign': null });

			// Rename master to main for consistency
			try {
				await git.branch(['-m', 'master', 'main']);
			} catch (error) {
				// Branch might already be main, ignore error
			}

			gitAdapter = new GitAdapter(testDir);
		});

		afterEach(async () => {
			if (await fs.pathExists(testDir)) {
				await fs.remove(testDir);
			}
		});

		describe('getCurrentBranch', () => {
			it('should return current branch name', async () => {
				const branch = await gitAdapter.getCurrentBranch();
				expect(branch).toBe('main');
			});

			it('should return updated branch after checkout', async () => {
				const git = simpleGit(testDir);
				await git.checkoutLocalBranch('feature');

				const branch = await gitAdapter.getCurrentBranch();
				expect(branch).toBe('feature');
			});
		});

		describe('listBranches', () => {
			it('should list all branches', async () => {
				const git = simpleGit(testDir);
				await git.checkoutLocalBranch('feature-a');
				await git.checkout('main');
				await git.checkoutLocalBranch('feature-b');

				const branches = await gitAdapter.listBranches();
				expect(branches).toContain('main');
				expect(branches).toContain('feature-a');
				expect(branches).toContain('feature-b');
				expect(branches.length).toBeGreaterThanOrEqual(3);
			});

			it('should return empty array if only on detached HEAD', async () => {
				const git = simpleGit(testDir);
				const log = await git.log();
				await git.checkout(log.latest.hash);

				const branches = await gitAdapter.listBranches();
				expect(Array.isArray(branches)).toBe(true);
			});
		});

		describe('branchExists', () => {
			it('should return true for existing branch', async () => {
				const exists = await gitAdapter.branchExists('main');
				expect(exists).toBe(true);
			});

			it('should return false for non-existing branch', async () => {
				const exists = await gitAdapter.branchExists('nonexistent');
				expect(exists).toBe(false);
			});

			it('should detect newly created branches', async () => {
				const git = simpleGit(testDir);
				await git.checkoutLocalBranch('new-feature');

				const exists = await gitAdapter.branchExists('new-feature');
				expect(exists).toBe(true);
			});
		});

		describe('createBranch', () => {
			it('should create a new branch', async () => {
				await gitAdapter.createBranch('new-branch');

				const exists = await gitAdapter.branchExists('new-branch');
				expect(exists).toBe(true);
			});

			it('should throw error if branch already exists', async () => {
				await gitAdapter.createBranch('existing-branch');

				await expect(
					gitAdapter.createBranch('existing-branch')
				).rejects.toThrow();
			});

			it('should not switch to new branch by default', async () => {
				await gitAdapter.createBranch('new-branch');

				const current = await gitAdapter.getCurrentBranch();
				expect(current).toBe('main');
			});

			it('should throw if working tree is dirty when checkout is requested', async () => {
				await fs.writeFile(path.join(testDir, 'dirty.txt'), 'content');

				await expect(
					gitAdapter.createBranch('new-branch', { checkout: true })
				).rejects.toThrow('working tree is not clean');
			});
		});

		describe('checkoutBranch', () => {
			it('should checkout existing branch', async () => {
				const git = simpleGit(testDir);
				await git.checkoutLocalBranch('feature');
				await git.checkout('main');

				await gitAdapter.checkoutBranch('feature');

				const current = await gitAdapter.getCurrentBranch();
				expect(current).toBe('feature');
			});

			it('should throw error for non-existing branch', async () => {
				await expect(
					gitAdapter.checkoutBranch('nonexistent')
				).rejects.toThrow();
			});

			it('should throw if working tree is dirty', async () => {
				const git = simpleGit(testDir);
				await git.checkoutLocalBranch('feature');
				await git.checkout('main');

				await fs.writeFile(path.join(testDir, 'dirty.txt'), 'content');

				await expect(gitAdapter.checkoutBranch('feature')).rejects.toThrow(
					'working tree is not clean'
				);
			});

			it('should allow force checkout with force flag', async () => {
				const git = simpleGit(testDir);
				await git.checkoutLocalBranch('feature');
				await git.checkout('main');

				await fs.writeFile(path.join(testDir, 'dirty.txt'), 'content');

				await gitAdapter.checkoutBranch('feature', { force: true });

				const current = await gitAdapter.getCurrentBranch();
				expect(current).toBe('feature');
			});
		});

		describe('createAndCheckoutBranch', () => {
			it('should create and checkout new branch', async () => {
				await gitAdapter.createAndCheckoutBranch('new-feature');

				const current = await gitAdapter.getCurrentBranch();
				expect(current).toBe('new-feature');

				const exists = await gitAdapter.branchExists('new-feature');
				expect(exists).toBe(true);
			});

			it('should throw if branch already exists', async () => {
				const git = simpleGit(testDir);
				await git.checkoutLocalBranch('existing');
				await git.checkout('main');

				await expect(
					gitAdapter.createAndCheckoutBranch('existing')
				).rejects.toThrow();
			});

			it('should throw if working tree is dirty', async () => {
				await fs.writeFile(path.join(testDir, 'dirty.txt'), 'content');

				await expect(
					gitAdapter.createAndCheckoutBranch('new-feature')
				).rejects.toThrow('working tree is not clean');
			});
		});

		describe('deleteBranch', () => {
			it('should delete existing branch', async () => {
				const git = simpleGit(testDir);
				await git.checkoutLocalBranch('to-delete');
				await git.checkout('main');

				await gitAdapter.deleteBranch('to-delete');

				const exists = await gitAdapter.branchExists('to-delete');
				expect(exists).toBe(false);
			});

			it('should throw error when deleting current branch', async () => {
				await expect(gitAdapter.deleteBranch('main')).rejects.toThrow();
			});

			it('should throw error for non-existing branch', async () => {
				await expect(gitAdapter.deleteBranch('nonexistent')).rejects.toThrow();
			});

			it('should force delete with force flag', async () => {
				const git = simpleGit(testDir);
				await git.checkoutLocalBranch('unmerged');
				await fs.writeFile(path.join(testDir, 'unmerged.txt'), 'content');
				await git.add('unmerged.txt');
				await git.commit('Unmerged commit', undefined, {
					'--no-gpg-sign': null
				});
				await git.checkout('main');

				await gitAdapter.deleteBranch('unmerged', { force: true });

				const exists = await gitAdapter.branchExists('unmerged');
				expect(exists).toBe(false);
			});
		});
	});

	describe('GitAdapter - Commit Operations', () => {
		let testDir;
		let gitAdapter;
		let simpleGit;

		beforeEach(async () => {
			testDir = path.join(os.tmpdir(), `git-commit-test-${Date.now()}`);
			await fs.ensureDir(testDir);

			// Initialize actual git repo with initial commit
			simpleGit = (await import('simple-git')).default;
			const git = simpleGit(testDir);
			await git.init();
			await git.addConfig('user.name', 'Test User');
			await git.addConfig('user.email', '[email protected]');

			// Create initial commit
			await fs.writeFile(path.join(testDir, 'README.md'), '# Test Repo');
			await git.add('README.md');
			await git.commit('Initial commit', undefined, { '--no-gpg-sign': null });

			// Rename master to main for consistency
			try {
				await git.branch(['-m', 'master', 'main']);
			} catch (error) {
				// Branch might already be main, ignore error
			}

			gitAdapter = new GitAdapter(testDir);
		});

		afterEach(async () => {
			if (await fs.pathExists(testDir)) {
				await fs.remove(testDir);
			}
		});

		describe('stageFiles', () => {
			it('should stage single file', async () => {
				await fs.writeFile(path.join(testDir, 'new.txt'), 'content');
				await gitAdapter.stageFiles(['new.txt']);

				const status = await gitAdapter.getStatus();
				expect(status.staged).toContain('new.txt');
			});

			it('should stage multiple files', async () => {
				await fs.writeFile(path.join(testDir, 'file1.txt'), 'content1');
				await fs.writeFile(path.join(testDir, 'file2.txt'), 'content2');
				await gitAdapter.stageFiles(['file1.txt', 'file2.txt']);

				const status = await gitAdapter.getStatus();
				expect(status.staged).toContain('file1.txt');
				expect(status.staged).toContain('file2.txt');
			});

			it('should stage all files with dot', async () => {
				await fs.writeFile(path.join(testDir, 'file1.txt'), 'content1');
				await fs.writeFile(path.join(testDir, 'file2.txt'), 'content2');
				await gitAdapter.stageFiles(['.']);

				const status = await gitAdapter.getStatus();
				expect(status.staged.length).toBeGreaterThanOrEqual(2);
			});
		});

		describe('unstageFiles', () => {
			it('should unstage single file', async () => {
				await fs.writeFile(path.join(testDir, 'staged.txt'), 'content');
				const git = simpleGit(testDir);
				await git.add('staged.txt');

				await gitAdapter.unstageFiles(['staged.txt']);

				const status = await gitAdapter.getStatus();
				expect(status.staged).not.toContain('staged.txt');
			});

			it('should unstage multiple files', async () => {
				await fs.writeFile(path.join(testDir, 'file1.txt'), 'content1');
				await fs.writeFile(path.join(testDir, 'file2.txt'), 'content2');
				const git = simpleGit(testDir);
				await git.add(['file1.txt', 'file2.txt']);

				await gitAdapter.unstageFiles(['file1.txt', 'file2.txt']);

				const status = await gitAdapter.getStatus();
				expect(status.staged).not.toContain('file1.txt');
				expect(status.staged).not.toContain('file2.txt');
			});
		});

		describe('createCommit', () => {
			it('should create commit with simple message', async () => {
				await fs.writeFile(path.join(testDir, 'new.txt'), 'content');
				await gitAdapter.stageFiles(['new.txt']);

				await gitAdapter.createCommit('Add new file');

				const git = simpleGit(testDir);
				const log = await git.log();
				expect(log.latest.message).toBe('Add new file');
			});

			it('should create commit with metadata', async () => {
				await fs.writeFile(path.join(testDir, 'new.txt'), 'content');
				await gitAdapter.stageFiles(['new.txt']);

				const metadata = {
					taskId: '2.4',
					phase: 'implementation',
					timestamp: new Date().toISOString()
				};
				await gitAdapter.createCommit('Add new file', { metadata });

				const commit = await gitAdapter.getLastCommit();
				expect(commit.message).toContain('Add new file');
				expect(commit.message).toContain('[taskId:2.4]');
				expect(commit.message).toContain('[phase:implementation]');
			});

			it('should throw if no staged changes', async () => {
				await expect(gitAdapter.createCommit('Empty commit')).rejects.toThrow();
			});

			it('should allow empty commits with allowEmpty flag', async () => {
				await gitAdapter.createCommit('Empty commit', { allowEmpty: true });

				const git = simpleGit(testDir);
				const log = await git.log();
				expect(log.latest.message).toBe('Empty commit');
			});

			it('should throw if on default branch without force', async () => {
				await fs.writeFile(path.join(testDir, 'new.txt'), 'content');
				await gitAdapter.stageFiles(['new.txt']);

				await expect(
					gitAdapter.createCommit('Add new file', {
						enforceNonDefaultBranch: true
					})
				).rejects.toThrow('cannot commit to default branch');
			});

			it('should allow commit on default branch with force', async () => {
				await fs.writeFile(path.join(testDir, 'new.txt'), 'content');
				await gitAdapter.stageFiles(['new.txt']);

				await gitAdapter.createCommit('Add new file', {
					enforceNonDefaultBranch: true,
					force: true
				});

				const git = simpleGit(testDir);
				const log = await git.log();
				expect(log.latest.message).toBe('Add new file');
			});

			it('should allow commit on feature branch with enforcement', async () => {
				// Create and checkout feature branch
				await gitAdapter.createAndCheckoutBranch('feature-branch');

				await fs.writeFile(path.join(testDir, 'new.txt'), 'content');
				await gitAdapter.stageFiles(['new.txt']);

				await gitAdapter.createCommit('Add new file', {
					enforceNonDefaultBranch: true
				});

				const git = simpleGit(testDir);
				const log = await git.log();
				expect(log.latest.message).toBe('Add new file');
			});
		});

		describe('getCommitLog', () => {
			it('should get recent commits', async () => {
				const log = await gitAdapter.getCommitLog();
				expect(log.length).toBeGreaterThan(0);
				expect(log[0].message.trim()).toBe('Initial commit');
			});

			it('should limit number of commits', async () => {
				// Create additional commits
				for (let i = 1; i <= 5; i++) {
					await fs.writeFile(path.join(testDir, `file${i}.txt`), `content${i}`);
					await gitAdapter.stageFiles([`file${i}.txt`]);
					await gitAdapter.createCommit(`Commit ${i}`);
				}

				const log = await gitAdapter.getCommitLog({ maxCount: 3 });
				expect(log.length).toBe(3);
			});

			it('should return commits with hash and date', async () => {
				const log = await gitAdapter.getCommitLog();
				expect(log[0]).toHaveProperty('hash');
				expect(log[0]).toHaveProperty('date');
				expect(log[0]).toHaveProperty('message');
				expect(log[0]).toHaveProperty('author_name');
				expect(log[0]).toHaveProperty('author_email');
			});
		});

		describe('getLastCommit', () => {
			it('should get the last commit', async () => {
				await fs.writeFile(path.join(testDir, 'new.txt'), 'content');
				await gitAdapter.stageFiles(['new.txt']);
				await gitAdapter.createCommit('Latest commit');

				const commit = await gitAdapter.getLastCommit();
				expect(commit.message.trim()).toBe('Latest commit');
			});

			it('should return commit with metadata if present', async () => {
				await fs.writeFile(path.join(testDir, 'new.txt'), 'content');
				await gitAdapter.stageFiles(['new.txt']);
				const metadata = { taskId: '2.4' };
				await gitAdapter.createCommit('With metadata', { metadata });

				const commit = await gitAdapter.getLastCommit();
				expect(commit.message).toContain('[taskId:2.4]');
			});
		});
	});

	describe('GitAdapter - Default Branch Detection and Protection', () => {
		let testDir;
		let gitAdapter;
		let simpleGit;

		beforeEach(async () => {
			testDir = path.join(os.tmpdir(), `git-default-branch-test-${Date.now()}`);
			await fs.ensureDir(testDir);

			// Initialize actual git repo with initial commit
			simpleGit = (await import('simple-git')).default;
			const git = simpleGit(testDir);
			await git.init();
			await git.addConfig('user.name', 'Test User');
			await git.addConfig('user.email', '[email protected]');

			// Create initial commit
			await fs.writeFile(path.join(testDir, 'README.md'), '# Test Repo');
			await git.add('README.md');
			await git.commit('Initial commit', undefined, { '--no-gpg-sign': null });

			// Rename master to main for consistency
			try {
				await git.branch(['-m', 'master', 'main']);
			} catch (error) {
				// Branch might already be main, ignore error
			}

			gitAdapter = new GitAdapter(testDir);
		});

		afterEach(async () => {
			if (await fs.pathExists(testDir)) {
				await fs.remove(testDir);
			}
		});

		describe('getDefaultBranch', () => {
			it('should detect main as default branch', async () => {
				const defaultBranch = await gitAdapter.getDefaultBranch();
				expect(defaultBranch).toBe('main');
			});

			it('should detect master if renamed back', async () => {
				const git = simpleGit(testDir);
				await git.branch(['-m', 'main', 'master']);

				const defaultBranch = await gitAdapter.getDefaultBranch();
				expect(defaultBranch).toBe('master');
			});
		});

		describe('isDefaultBranch', () => {
			it('should return true for main branch', async () => {
				const isDefault = await gitAdapter.isDefaultBranch('main');
				expect(isDefault).toBe(true);
			});

			it('should return true for master branch', async () => {
				const isDefault = await gitAdapter.isDefaultBranch('master');
				expect(isDefault).toBe(true);
			});

			it('should return true for develop branch', async () => {
				const isDefault = await gitAdapter.isDefaultBranch('develop');
				expect(isDefault).toBe(true);
			});

			it('should return false for feature branch', async () => {
				const isDefault = await gitAdapter.isDefaultBranch('feature-branch');
				expect(isDefault).toBe(false);
			});
		});

		describe('isOnDefaultBranch', () => {
			it('should return true when on main', async () => {
				const onDefault = await gitAdapter.isOnDefaultBranch();
				expect(onDefault).toBe(true);
			});

			it('should return false when on feature branch', async () => {
				await gitAdapter.createAndCheckoutBranch('feature-branch');

				const onDefault = await gitAdapter.isOnDefaultBranch();
				expect(onDefault).toBe(false);
			});
		});

		describe('ensureNotOnDefaultBranch', () => {
			it('should throw when on main branch', async () => {
				await expect(gitAdapter.ensureNotOnDefaultBranch()).rejects.toThrow(
					'currently on default branch'
				);
			});

			it('should not throw when on feature branch', async () => {
				await gitAdapter.createAndCheckoutBranch('feature-branch');

				await expect(
					gitAdapter.ensureNotOnDefaultBranch()
				).resolves.not.toThrow();
			});
		});
	});

	describe('GitAdapter - Push Operations', () => {
		let testDir;
		let gitAdapter;

		beforeEach(async () => {
			testDir = path.join(os.tmpdir(), `git-push-test-${Date.now()}`);
			await fs.ensureDir(testDir);

			const simpleGit = (await import('simple-git')).default;
			const git = simpleGit(testDir);
			await git.init();
			await git.addConfig('user.name', 'Test User');
			await git.addConfig('user.email', '[email protected]');

			await fs.writeFile(path.join(testDir, 'README.md'), '# Test Repo');
			await git.add('README.md');
			await git.commit('Initial commit', undefined, { '--no-gpg-sign': null });

			try {
				await git.branch(['-m', 'master', 'main']);
			} catch (error) {}

			gitAdapter = new GitAdapter(testDir);
		});

		afterEach(async () => {
			if (await fs.pathExists(testDir)) {
				await fs.remove(testDir);
			}
		});

		describe('hasRemote', () => {
			it('should return false when no remotes exist', async () => {
				const hasRemote = await gitAdapter.hasRemote();
				expect(hasRemote).toBe(false);
			});
		});

		describe('getRemotes', () => {
			it('should return empty array when no remotes', async () => {
				const remotes = await gitAdapter.getRemotes();
				expect(remotes).toEqual([]);
			});
		});
	});
});

```
Page 39/50FirstPrevNextLast