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

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/scripts/modules/ui.js:
--------------------------------------------------------------------------------

```javascript
   1 | /**
   2 |  * ui.js
   3 |  * User interface functions for the Task Master CLI
   4 |  */
   5 | 
   6 | import chalk from 'chalk';
   7 | import figlet from 'figlet';
   8 | import boxen from 'boxen';
   9 | import ora from 'ora';
  10 | import Table from 'cli-table3';
  11 | import gradient from 'gradient-string';
  12 | import readline from 'readline';
  13 | import {
  14 | 	log,
  15 | 	findTaskById,
  16 | 	readJSON,
  17 | 	truncate,
  18 | 	isSilentMode,
  19 | 	formatTaskId
  20 | } from './utils.js';
  21 | import fs from 'fs';
  22 | import {
  23 | 	findNextTask,
  24 | 	analyzeTaskComplexity,
  25 | 	readComplexityReport
  26 | } from './task-manager.js';
  27 | import { getProjectName, getDefaultSubtasks } from './config-manager.js';
  28 | import { TASK_STATUS_OPTIONS } from '../../src/constants/task-status.js';
  29 | import {
  30 | 	TASKMASTER_CONFIG_FILE,
  31 | 	TASKMASTER_TASKS_FILE
  32 | } from '../../src/constants/paths.js';
  33 | import { getTaskMasterVersion } from '../../src/utils/getVersion.js';
  34 | 
  35 | // Create a color gradient for the banner
  36 | const coolGradient = gradient(['#00b4d8', '#0077b6', '#03045e']);
  37 | const warmGradient = gradient(['#fb8b24', '#e36414', '#9a031e']);
  38 | 
  39 | /**
  40 |  * Display FYI notice about tagged task lists (only if migration occurred)
  41 |  * @param {Object} data - Data object that may contain _migrationHappened flag
  42 |  */
  43 | function displayTaggedTasksFYI(data) {
  44 | 	if (isSilentMode() || !data || !data._migrationHappened) return;
  45 | 
  46 | 	console.log(
  47 | 		boxen(
  48 | 			chalk.white.bold('FYI: ') +
  49 | 				chalk.gray('Taskmaster now supports separate task lists per tag. ') +
  50 | 				chalk.cyan(
  51 | 					'Use the --tag flag to create/read/update/filter tasks by tag.'
  52 | 				),
  53 | 			{
  54 | 				padding: { top: 0, bottom: 0, left: 2, right: 2 },
  55 | 				borderColor: 'cyan',
  56 | 				borderStyle: 'round',
  57 | 				margin: { top: 1, bottom: 1 }
  58 | 			}
  59 | 		)
  60 | 	);
  61 | }
  62 | 
  63 | /**
  64 |  * Display a small, non-intrusive indicator showing the current tag context
  65 |  * @param {string} tagName - The tag name to display
  66 |  * @param {Object} options - Display options
  67 |  * @param {boolean} [options.skipIfMaster=false] - Don't show indicator if tag is 'master'
  68 |  * @param {boolean} [options.dim=false] - Use dimmed styling
  69 |  */
  70 | function displayCurrentTagIndicator(tag, options = {}) {
  71 | 	if (isSilentMode()) return;
  72 | 
  73 | 	const { skipIfMaster = false, dim = false } = options;
  74 | 
  75 | 	// Skip display for master tag only if explicitly requested
  76 | 	if (skipIfMaster && tag === 'master') return;
  77 | 
  78 | 	// Create a small, tasteful tag indicator
  79 | 	const tagIcon = '🏷️';
  80 | 	const tagText = dim
  81 | 		? chalk.gray(`${tagIcon} tag: ${tag}`)
  82 | 		: chalk.dim(`${tagIcon} tag: `) + chalk.cyan(tag);
  83 | 
  84 | 	console.log(tagText);
  85 | }
  86 | 
  87 | /**
  88 |  * Display a fancy banner for the CLI
  89 |  */
  90 | function displayBanner() {
  91 | 	if (isSilentMode()) return;
  92 | 
  93 | 	// console.clear(); // Removing this to avoid clearing the terminal per command
  94 | 	const bannerText = figlet.textSync('Task Master', {
  95 | 		font: 'Standard',
  96 | 		horizontalLayout: 'default',
  97 | 		verticalLayout: 'default'
  98 | 	});
  99 | 
 100 | 	console.log(coolGradient(bannerText));
 101 | 
 102 | 	// Add creator credit line below the banner
 103 | 	console.log(
 104 | 		chalk.dim('by ') + chalk.cyan.underline('https://x.com/eyaltoledano')
 105 | 	);
 106 | 
 107 | 	// Read version directly from package.json
 108 | 	const version = getTaskMasterVersion();
 109 | 
 110 | 	console.log(
 111 | 		boxen(
 112 | 			chalk.white(
 113 | 				`${chalk.bold('Version:')} ${version}   ${chalk.bold('Project:')} ${getProjectName(null)}`
 114 | 			),
 115 | 			{
 116 | 				padding: 1,
 117 | 				margin: { top: 0, bottom: 1 },
 118 | 				borderStyle: 'round',
 119 | 				borderColor: 'cyan'
 120 | 			}
 121 | 		)
 122 | 	);
 123 | }
 124 | 
 125 | /**
 126 |  * Start a loading indicator with an animated spinner
 127 |  * @param {string} message - Message to display next to the spinner
 128 |  * @returns {Object} Spinner object
 129 |  */
 130 | function startLoadingIndicator(message) {
 131 | 	if (isSilentMode()) return null;
 132 | 
 133 | 	const spinner = ora({
 134 | 		text: message,
 135 | 		color: 'cyan'
 136 | 	}).start();
 137 | 
 138 | 	return spinner;
 139 | }
 140 | 
 141 | /**
 142 |  * Stop a loading indicator (basic stop, no success/fail indicator)
 143 |  * @param {Object} spinner - Spinner object to stop
 144 |  */
 145 | function stopLoadingIndicator(spinner) {
 146 | 	if (spinner && typeof spinner.stop === 'function') {
 147 | 		spinner.stop();
 148 | 	}
 149 | }
 150 | 
 151 | /**
 152 |  * Complete a loading indicator with success (shows checkmark)
 153 |  * @param {Object} spinner - Spinner object to complete
 154 |  * @param {string} message - Optional success message (defaults to current text)
 155 |  */
 156 | function succeedLoadingIndicator(spinner, message = null) {
 157 | 	if (spinner && typeof spinner.succeed === 'function') {
 158 | 		if (message) {
 159 | 			spinner.succeed(message);
 160 | 		} else {
 161 | 			spinner.succeed();
 162 | 		}
 163 | 	}
 164 | }
 165 | 
 166 | /**
 167 |  * Complete a loading indicator with failure (shows X)
 168 |  * @param {Object} spinner - Spinner object to fail
 169 |  * @param {string} message - Optional failure message (defaults to current text)
 170 |  */
 171 | function failLoadingIndicator(spinner, message = null) {
 172 | 	if (spinner && typeof spinner.fail === 'function') {
 173 | 		if (message) {
 174 | 			spinner.fail(message);
 175 | 		} else {
 176 | 			spinner.fail();
 177 | 		}
 178 | 	}
 179 | }
 180 | 
 181 | /**
 182 |  * Complete a loading indicator with warning (shows warning symbol)
 183 |  * @param {Object} spinner - Spinner object to warn
 184 |  * @param {string} message - Optional warning message (defaults to current text)
 185 |  */
 186 | function warnLoadingIndicator(spinner, message = null) {
 187 | 	if (spinner && typeof spinner.warn === 'function') {
 188 | 		if (message) {
 189 | 			spinner.warn(message);
 190 | 		} else {
 191 | 			spinner.warn();
 192 | 		}
 193 | 	}
 194 | }
 195 | 
 196 | /**
 197 |  * Complete a loading indicator with info (shows info symbol)
 198 |  * @param {Object} spinner - Spinner object to complete with info
 199 |  * @param {string} message - Optional info message (defaults to current text)
 200 |  */
 201 | function infoLoadingIndicator(spinner, message = null) {
 202 | 	if (spinner && typeof spinner.info === 'function') {
 203 | 		if (message) {
 204 | 			spinner.info(message);
 205 | 		} else {
 206 | 			spinner.info();
 207 | 		}
 208 | 	}
 209 | }
 210 | 
 211 | /**
 212 |  * Create a colored progress bar
 213 |  * @param {number} percent - The completion percentage
 214 |  * @param {number} length - The total length of the progress bar in characters
 215 |  * @param {Object} statusBreakdown - Optional breakdown of non-complete statuses (e.g., {pending: 20, 'in-progress': 10})
 216 |  * @returns {string} The formatted progress bar
 217 |  */
 218 | function createProgressBar(percent, length = 30, statusBreakdown = null) {
 219 | 	// Adjust the percent to treat deferred and cancelled as complete
 220 | 	const effectivePercent = statusBreakdown
 221 | 		? Math.min(
 222 | 				100,
 223 | 				percent +
 224 | 					(statusBreakdown.deferred || 0) +
 225 | 					(statusBreakdown.cancelled || 0)
 226 | 			)
 227 | 		: percent;
 228 | 
 229 | 	// Calculate how many characters to fill for "true completion"
 230 | 	const trueCompletedFilled = Math.round((percent * length) / 100);
 231 | 
 232 | 	// Calculate how many characters to fill for "effective completion" (including deferred/cancelled)
 233 | 	const effectiveCompletedFilled = Math.round(
 234 | 		(effectivePercent * length) / 100
 235 | 	);
 236 | 
 237 | 	// The "deferred/cancelled" section (difference between true and effective)
 238 | 	const deferredCancelledFilled =
 239 | 		effectiveCompletedFilled - trueCompletedFilled;
 240 | 
 241 | 	// Set the empty section (remaining after effective completion)
 242 | 	const empty = length - effectiveCompletedFilled;
 243 | 
 244 | 	// Determine color based on percentage for the completed section
 245 | 	let completedColor;
 246 | 	if (percent < 25) {
 247 | 		completedColor = chalk.red;
 248 | 	} else if (percent < 50) {
 249 | 		completedColor = chalk.hex('#FFA500'); // Orange
 250 | 	} else if (percent < 75) {
 251 | 		completedColor = chalk.yellow;
 252 | 	} else if (percent < 100) {
 253 | 		completedColor = chalk.green;
 254 | 	} else {
 255 | 		completedColor = chalk.hex('#006400'); // Dark green
 256 | 	}
 257 | 
 258 | 	// Create colored sections
 259 | 	const completedSection = completedColor('█'.repeat(trueCompletedFilled));
 260 | 
 261 | 	// Gray section for deferred/cancelled items
 262 | 	const deferredCancelledSection = chalk.gray(
 263 | 		'█'.repeat(deferredCancelledFilled)
 264 | 	);
 265 | 
 266 | 	// If we have a status breakdown, create a multi-colored remaining section
 267 | 	let remainingSection = '';
 268 | 
 269 | 	if (statusBreakdown && empty > 0) {
 270 | 		// Status colors (matching the statusConfig colors in getStatusWithColor)
 271 | 		const statusColors = {
 272 | 			pending: chalk.yellow,
 273 | 			'in-progress': chalk.hex('#FFA500'), // Orange
 274 | 			blocked: chalk.red,
 275 | 			review: chalk.magenta
 276 | 			// Deferred and cancelled are treated as part of the completed section
 277 | 		};
 278 | 
 279 | 		// Calculate proportions for each status
 280 | 		const totalRemaining = Object.entries(statusBreakdown)
 281 | 			.filter(
 282 | 				([status]) =>
 283 | 					!['deferred', 'cancelled', 'done', 'completed'].includes(status)
 284 | 			)
 285 | 			.reduce((sum, [_, val]) => sum + val, 0);
 286 | 
 287 | 		// If no remaining tasks with tracked statuses, just use gray
 288 | 		if (totalRemaining <= 0) {
 289 | 			remainingSection = chalk.gray('░'.repeat(empty));
 290 | 		} else {
 291 | 			// Track how many characters we've added
 292 | 			let addedChars = 0;
 293 | 
 294 | 			// Add each status section proportionally
 295 | 			for (const [status, percentage] of Object.entries(statusBreakdown)) {
 296 | 				// Skip statuses that are considered complete
 297 | 				if (['deferred', 'cancelled', 'done', 'completed'].includes(status))
 298 | 					continue;
 299 | 
 300 | 				// Calculate how many characters this status should fill
 301 | 				const statusChars = Math.round((percentage / totalRemaining) * empty);
 302 | 
 303 | 				// Make sure we don't exceed the total length due to rounding
 304 | 				const actualChars = Math.min(statusChars, empty - addedChars);
 305 | 
 306 | 				// Add colored section for this status
 307 | 				const colorFn = statusColors[status] || chalk.gray;
 308 | 				remainingSection += colorFn('░'.repeat(actualChars));
 309 | 
 310 | 				addedChars += actualChars;
 311 | 			}
 312 | 
 313 | 			// If we have any remaining space due to rounding, fill with gray
 314 | 			if (addedChars < empty) {
 315 | 				remainingSection += chalk.gray('░'.repeat(empty - addedChars));
 316 | 			}
 317 | 		}
 318 | 	} else {
 319 | 		// Default to gray for the empty section if no breakdown provided
 320 | 		remainingSection = chalk.gray('░'.repeat(empty));
 321 | 	}
 322 | 
 323 | 	// Effective percentage text color should reflect the highest category
 324 | 	const percentTextColor =
 325 | 		percent === 100
 326 | 			? chalk.hex('#006400') // Dark green for 100%
 327 | 			: effectivePercent === 100
 328 | 				? chalk.gray // Gray for 100% with deferred/cancelled
 329 | 				: completedColor; // Otherwise match the completed color
 330 | 
 331 | 	// Build the complete progress bar
 332 | 	return `${completedSection}${deferredCancelledSection}${remainingSection} ${percentTextColor(`${effectivePercent.toFixed(0)}%`)}`;
 333 | }
 334 | 
 335 | /**
 336 |  * Get a colored status string based on the status value
 337 |  * @param {string} status - Task status (e.g., "done", "pending", "in-progress")
 338 |  * @param {boolean} forTable - Whether the status is being displayed in a table
 339 |  * @returns {string} Colored status string
 340 |  */
 341 | function getStatusWithColor(status, forTable = false) {
 342 | 	if (!status) {
 343 | 		return chalk.gray('❓ unknown');
 344 | 	}
 345 | 
 346 | 	const statusConfig = {
 347 | 		done: { color: chalk.green, icon: '✓', tableIcon: '✓' },
 348 | 		completed: { color: chalk.green, icon: '✓', tableIcon: '✓' },
 349 | 		pending: { color: chalk.yellow, icon: '○', tableIcon: '⏱' },
 350 | 		'in-progress': { color: chalk.hex('#FFA500'), icon: '🔄', tableIcon: '►' },
 351 | 		deferred: { color: chalk.gray, icon: 'x', tableIcon: '⏱' },
 352 | 		blocked: { color: chalk.red, icon: '!', tableIcon: '✗' },
 353 | 		review: { color: chalk.magenta, icon: '?', tableIcon: '?' },
 354 | 		cancelled: { color: chalk.gray, icon: '❌', tableIcon: 'x' }
 355 | 	};
 356 | 
 357 | 	const config = statusConfig[status.toLowerCase()] || {
 358 | 		color: chalk.red,
 359 | 		icon: '❌',
 360 | 		tableIcon: '✗'
 361 | 	};
 362 | 
 363 | 	// Use simpler icons for table display to prevent border issues
 364 | 	if (forTable) {
 365 | 		// Use ASCII characters instead of Unicode for completely stable display
 366 | 		const simpleIcons = {
 367 | 			done: '✓',
 368 | 			completed: '✓',
 369 | 			pending: '○',
 370 | 			'in-progress': '►',
 371 | 			deferred: 'x',
 372 | 			blocked: '!', // Using plain x character for better compatibility
 373 | 			review: '?' // Using circled dot symbol
 374 | 		};
 375 | 		const simpleIcon = simpleIcons[status.toLowerCase()] || 'x';
 376 | 		return config.color(`${simpleIcon} ${status}`);
 377 | 	}
 378 | 
 379 | 	return config.color(`${config.icon} ${status}`);
 380 | }
 381 | 
 382 | /**
 383 |  * Format dependencies list with status indicators
 384 |  * @param {Array} dependencies - Array of dependency IDs
 385 |  * @param {Array} allTasks - Array of all tasks
 386 |  * @param {boolean} forConsole - Whether the output is for console display
 387 |  * @param {Object|null} complexityReport - Optional pre-loaded complexity report
 388 |  * @returns {string} Formatted dependencies string
 389 |  */
 390 | function formatDependenciesWithStatus(
 391 | 	dependencies,
 392 | 	allTasks,
 393 | 	forConsole = false,
 394 | 	complexityReport = null // Add complexityReport parameter
 395 | ) {
 396 | 	if (
 397 | 		!dependencies ||
 398 | 		!Array.isArray(dependencies) ||
 399 | 		dependencies.length === 0
 400 | 	) {
 401 | 		return forConsole ? chalk.gray('None') : 'None';
 402 | 	}
 403 | 
 404 | 	const formattedDeps = dependencies.map((depId) => {
 405 | 		const depIdStr = depId.toString(); // Ensure string format for display
 406 | 
 407 | 		// Check if it's already a fully qualified subtask ID (like "22.1")
 408 | 		if (depIdStr.includes('.')) {
 409 | 			const parts = depIdStr.split('.');
 410 | 			// Validate that it's a proper subtask format (parentId.subtaskId)
 411 | 			if (parts.length !== 2 || !parts[0] || !parts[1]) {
 412 | 				// Invalid format - treat as regular dependency
 413 | 				const numericDepId =
 414 | 					typeof depId === 'string' ? parseInt(depId, 10) : depId;
 415 | 				const depTaskResult = findTaskById(
 416 | 					allTasks,
 417 | 					numericDepId,
 418 | 					complexityReport
 419 | 				);
 420 | 				const depTask = depTaskResult.task;
 421 | 
 422 | 				if (!depTask) {
 423 | 					return forConsole
 424 | 						? chalk.red(`${depIdStr} (Not found)`)
 425 | 						: `${depIdStr} (Not found)`;
 426 | 				}
 427 | 
 428 | 				const status = depTask.status || 'pending';
 429 | 				const isDone =
 430 | 					status.toLowerCase() === 'done' ||
 431 | 					status.toLowerCase() === 'completed';
 432 | 				const isInProgress = status.toLowerCase() === 'in-progress';
 433 | 
 434 | 				if (forConsole) {
 435 | 					if (isDone) {
 436 | 						return chalk.green.bold(depIdStr);
 437 | 					} else if (isInProgress) {
 438 | 						return chalk.yellow.bold(depIdStr);
 439 | 					} else {
 440 | 						return chalk.red.bold(depIdStr);
 441 | 					}
 442 | 				}
 443 | 				return depIdStr;
 444 | 			}
 445 | 
 446 | 			const [parentId, subtaskId] = parts.map((id) => parseInt(id, 10));
 447 | 
 448 | 			// Find the parent task
 449 | 			const parentTask = allTasks.find((t) => t.id === parentId);
 450 | 			if (!parentTask || !parentTask.subtasks) {
 451 | 				return forConsole
 452 | 					? chalk.red(`${depIdStr} (Not found)`)
 453 | 					: `${depIdStr} (Not found)`;
 454 | 			}
 455 | 
 456 | 			// Find the subtask
 457 | 			const subtask = parentTask.subtasks.find((st) => st.id === subtaskId);
 458 | 			if (!subtask) {
 459 | 				return forConsole
 460 | 					? chalk.red(`${depIdStr} (Not found)`)
 461 | 					: `${depIdStr} (Not found)`;
 462 | 			}
 463 | 
 464 | 			// Format with status
 465 | 			const status = subtask.status || 'pending';
 466 | 			const isDone =
 467 | 				status.toLowerCase() === 'done' || status.toLowerCase() === 'completed';
 468 | 			const isInProgress = status.toLowerCase() === 'in-progress';
 469 | 
 470 | 			if (forConsole) {
 471 | 				if (isDone) {
 472 | 					return chalk.green.bold(depIdStr);
 473 | 				} else if (isInProgress) {
 474 | 					return chalk.hex('#FFA500').bold(depIdStr);
 475 | 				} else {
 476 | 					return chalk.red.bold(depIdStr);
 477 | 				}
 478 | 			}
 479 | 
 480 | 			// For plain text output (task files), return just the ID without any formatting or emoji
 481 | 			return depIdStr;
 482 | 		}
 483 | 
 484 | 		// If depId is a number less than 100, it's likely a reference to a subtask ID in the current task
 485 | 		// This case is typically handled elsewhere (in task-specific code) before calling this function
 486 | 
 487 | 		// For regular task dependencies (not subtasks)
 488 | 		// Convert string depId to number if needed
 489 | 		const numericDepId =
 490 | 			typeof depId === 'string' ? parseInt(depId, 10) : depId;
 491 | 
 492 | 		// Look up the task using the numeric ID
 493 | 		const depTaskResult = findTaskById(
 494 | 			allTasks,
 495 | 			numericDepId,
 496 | 			complexityReport
 497 | 		);
 498 | 		const depTask = depTaskResult.task; // Access the task object from the result
 499 | 
 500 | 		if (!depTask) {
 501 | 			return forConsole
 502 | 				? chalk.red(`${depIdStr} (Not found)`)
 503 | 				: `${depIdStr} (Not found)`;
 504 | 		}
 505 | 
 506 | 		// Format with status
 507 | 		const status = depTask.status || 'pending';
 508 | 		const isDone =
 509 | 			status.toLowerCase() === 'done' || status.toLowerCase() === 'completed';
 510 | 		const isInProgress = status.toLowerCase() === 'in-progress';
 511 | 
 512 | 		if (forConsole) {
 513 | 			if (isDone) {
 514 | 				return chalk.green.bold(depIdStr);
 515 | 			} else if (isInProgress) {
 516 | 				return chalk.yellow.bold(depIdStr);
 517 | 			} else {
 518 | 				return chalk.red.bold(depIdStr);
 519 | 			}
 520 | 		}
 521 | 
 522 | 		// For plain text output (task files), return just the ID without any formatting or emoji
 523 | 		return depIdStr;
 524 | 	});
 525 | 
 526 | 	return formattedDeps.join(', ');
 527 | }
 528 | 
 529 | /**
 530 |  * Display a comprehensive help guide
 531 |  */
 532 | function displayHelp() {
 533 | 	// Get terminal width - moved to top of function to make it available throughout
 534 | 	const terminalWidth = process.stdout.columns || 100; // Default to 100 if can't detect
 535 | 
 536 | 	console.log(
 537 | 		boxen(chalk.white.bold('Task Master CLI'), {
 538 | 			padding: 1,
 539 | 			borderColor: 'blue',
 540 | 			borderStyle: 'round',
 541 | 			margin: { top: 1, bottom: 1 }
 542 | 		})
 543 | 	);
 544 | 
 545 | 	// Command categories
 546 | 	const commandCategories = [
 547 | 		{
 548 | 			title: 'Project Setup & Configuration',
 549 | 			color: 'blue',
 550 | 			commands: [
 551 | 				{
 552 | 					name: 'init',
 553 | 					args: '[--name=<name>] [--description=<desc>] [-y]',
 554 | 					desc: 'Initialize a new project with Task Master structure'
 555 | 				},
 556 | 				{
 557 | 					name: 'models',
 558 | 					args: '',
 559 | 					desc: 'View current AI model configuration and available models'
 560 | 				},
 561 | 				{
 562 | 					name: 'models --setup',
 563 | 					args: '',
 564 | 					desc: 'Run interactive setup to configure AI models'
 565 | 				},
 566 | 				{
 567 | 					name: 'models --set-main',
 568 | 					args: '<model_id>',
 569 | 					desc: 'Set the primary model for task generation'
 570 | 				},
 571 | 				{
 572 | 					name: 'models --set-research',
 573 | 					args: '<model_id>',
 574 | 					desc: 'Set the model for research operations'
 575 | 				},
 576 | 				{
 577 | 					name: 'models --set-fallback',
 578 | 					args: '<model_id>',
 579 | 					desc: 'Set the fallback model (optional)'
 580 | 				}
 581 | 			]
 582 | 		},
 583 | 		{
 584 | 			title: 'Task Generation',
 585 | 			color: 'cyan',
 586 | 			commands: [
 587 | 				{
 588 | 					name: 'parse-prd',
 589 | 					args: '--input=<file.txt> [--num-tasks=10]',
 590 | 					desc: 'Generate tasks from a PRD document'
 591 | 				},
 592 | 				{
 593 | 					name: 'generate',
 594 | 					args: '',
 595 | 					desc: 'Create individual task files from tasks.json'
 596 | 				}
 597 | 			]
 598 | 		},
 599 | 		{
 600 | 			title: 'Task Management',
 601 | 			color: 'green',
 602 | 			commands: [
 603 | 				{
 604 | 					name: 'list',
 605 | 					args: '[--status=<status>] [--with-subtasks]',
 606 | 					desc: 'List all tasks with their status'
 607 | 				},
 608 | 				{
 609 | 					name: 'set-status',
 610 | 					args: '--id=<id> --status=<status>',
 611 | 					desc: `Update task status (${TASK_STATUS_OPTIONS.join(', ')})`
 612 | 				},
 613 | 				{
 614 | 					name: 'sync-readme',
 615 | 					args: '[--with-subtasks] [--status=<status>]',
 616 | 					desc: 'Export tasks to README.md with professional formatting'
 617 | 				},
 618 | 				{
 619 | 					name: 'update',
 620 | 					args: '--from=<id> --prompt="<context>"',
 621 | 					desc: 'Update multiple tasks based on new requirements'
 622 | 				},
 623 | 				{
 624 | 					name: 'update-task',
 625 | 					args: '--id=<id> --prompt="<context>"',
 626 | 					desc: 'Update a single specific task with new information'
 627 | 				},
 628 | 				{
 629 | 					name: 'update-subtask',
 630 | 					args: '--id=<parentId.subtaskId> --prompt="<context>"',
 631 | 					desc: 'Append additional information to a subtask'
 632 | 				},
 633 | 				{
 634 | 					name: 'add-task',
 635 | 					args: '--prompt="<text>" [--dependencies=<ids>] [--priority=<priority>]',
 636 | 					desc: 'Add a new task using AI'
 637 | 				},
 638 | 				{
 639 | 					name: 'remove-task',
 640 | 					args: '--id=<id> [-y]',
 641 | 					desc: 'Permanently remove a task or subtask'
 642 | 				}
 643 | 			]
 644 | 		},
 645 | 		{
 646 | 			title: 'Subtask Management',
 647 | 			color: 'yellow',
 648 | 			commands: [
 649 | 				{
 650 | 					name: 'add-subtask',
 651 | 					args: '--parent=<id> --title="<title>" [--description="<desc>"]',
 652 | 					desc: 'Add a new subtask to a parent task'
 653 | 				},
 654 | 				{
 655 | 					name: 'add-subtask',
 656 | 					args: '--parent=<id> --task-id=<id>',
 657 | 					desc: 'Convert an existing task into a subtask'
 658 | 				},
 659 | 				{
 660 | 					name: 'remove-subtask',
 661 | 					args: '--id=<parentId.subtaskId> [--convert]',
 662 | 					desc: 'Remove a subtask (optionally convert to standalone task)'
 663 | 				},
 664 | 				{
 665 | 					name: 'clear-subtasks',
 666 | 					args: '--id=<id>',
 667 | 					desc: 'Remove all subtasks from specified tasks'
 668 | 				},
 669 | 				{
 670 | 					name: 'clear-subtasks --all',
 671 | 					args: '',
 672 | 					desc: 'Remove subtasks from all tasks'
 673 | 				}
 674 | 			]
 675 | 		},
 676 | 		{
 677 | 			title: 'Task Analysis & Breakdown',
 678 | 			color: 'magenta',
 679 | 			commands: [
 680 | 				{
 681 | 					name: 'analyze-complexity',
 682 | 					args: '[--research] [--threshold=5]',
 683 | 					desc: 'Analyze tasks and generate expansion recommendations'
 684 | 				},
 685 | 				{
 686 | 					name: 'complexity-report',
 687 | 					args: '[--file=<path>]',
 688 | 					desc: 'Display the complexity analysis report'
 689 | 				},
 690 | 				{
 691 | 					name: 'expand',
 692 | 					args: '--id=<id> [--num=5] [--research] [--prompt="<context>"]',
 693 | 					desc: 'Break down tasks into detailed subtasks'
 694 | 				},
 695 | 				{
 696 | 					name: 'expand --all',
 697 | 					args: '[--force] [--research]',
 698 | 					desc: 'Expand all pending tasks with subtasks'
 699 | 				},
 700 | 				{
 701 | 					name: 'research',
 702 | 					args: '"<prompt>" [-i=<task_ids>] [-f=<file_paths>] [-c="<context>"] [--tree] [-s=<save_file>] [-d=<detail_level>]',
 703 | 					desc: 'Perform AI-powered research queries with project context'
 704 | 				}
 705 | 			]
 706 | 		},
 707 | 		{
 708 | 			title: 'Task Navigation & Viewing',
 709 | 			color: 'cyan',
 710 | 			commands: [
 711 | 				{
 712 | 					name: 'next',
 713 | 					args: '',
 714 | 					desc: 'Show the next task to work on based on dependencies'
 715 | 				},
 716 | 				{
 717 | 					name: 'show',
 718 | 					args: '<id>',
 719 | 					desc: 'Display detailed information about a specific task'
 720 | 				}
 721 | 			]
 722 | 		},
 723 | 		{
 724 | 			title: 'Tag Management',
 725 | 			color: 'magenta',
 726 | 			commands: [
 727 | 				{
 728 | 					name: 'tags',
 729 | 					args: '[--show-metadata]',
 730 | 					desc: 'List all available tags with task counts'
 731 | 				},
 732 | 				{
 733 | 					name: 'add-tag',
 734 | 					args: '<tagName> [--copy-from-current] [--copy-from=<tag>] [-d="<desc>"]',
 735 | 					desc: 'Create a new tag context for organizing tasks'
 736 | 				},
 737 | 				{
 738 | 					name: 'use-tag',
 739 | 					args: '<tagName>',
 740 | 					desc: 'Switch to a different tag context'
 741 | 				},
 742 | 				{
 743 | 					name: 'delete-tag',
 744 | 					args: '<tagName> [--yes]',
 745 | 					desc: 'Delete an existing tag and all its tasks'
 746 | 				},
 747 | 				{
 748 | 					name: 'rename-tag',
 749 | 					args: '<oldName> <newName>',
 750 | 					desc: 'Rename an existing tag'
 751 | 				},
 752 | 				{
 753 | 					name: 'copy-tag',
 754 | 					args: '<sourceName> <targetName> [-d="<desc>"]',
 755 | 					desc: 'Copy an existing tag to create a new tag with the same tasks'
 756 | 				}
 757 | 			]
 758 | 		},
 759 | 		{
 760 | 			title: 'Dependency Management',
 761 | 			color: 'blue',
 762 | 			commands: [
 763 | 				{
 764 | 					name: 'add-dependency',
 765 | 					args: '--id=<id> --depends-on=<id>',
 766 | 					desc: 'Add a dependency to a task'
 767 | 				},
 768 | 				{
 769 | 					name: 'remove-dependency',
 770 | 					args: '--id=<id> --depends-on=<id>',
 771 | 					desc: 'Remove a dependency from a task'
 772 | 				},
 773 | 				{
 774 | 					name: 'validate-dependencies',
 775 | 					args: '',
 776 | 					desc: 'Identify invalid dependencies without fixing them'
 777 | 				},
 778 | 				{
 779 | 					name: 'fix-dependencies',
 780 | 					args: '',
 781 | 					desc: 'Fix invalid dependencies automatically'
 782 | 				}
 783 | 			]
 784 | 		}
 785 | 	];
 786 | 
 787 | 	// Display each category
 788 | 	commandCategories.forEach((category) => {
 789 | 		console.log(
 790 | 			boxen(chalk[category.color].bold(category.title), {
 791 | 				padding: { left: 2, right: 2, top: 0, bottom: 0 },
 792 | 				margin: { top: 1, bottom: 0 },
 793 | 				borderColor: category.color,
 794 | 				borderStyle: 'round'
 795 | 			})
 796 | 		);
 797 | 
 798 | 		// Calculate dynamic column widths - adjust ratios as needed
 799 | 		const nameWidth = Math.max(25, Math.floor(terminalWidth * 0.2)); // 20% of width but min 25
 800 | 		const argsWidth = Math.max(40, Math.floor(terminalWidth * 0.35)); // 35% of width but min 40
 801 | 		const descWidth = Math.max(45, Math.floor(terminalWidth * 0.45) - 10); // 45% of width but min 45, minus some buffer
 802 | 
 803 | 		const commandTable = new Table({
 804 | 			colWidths: [nameWidth, argsWidth, descWidth],
 805 | 			chars: {
 806 | 				top: '',
 807 | 				'top-mid': '',
 808 | 				'top-left': '',
 809 | 				'top-right': '',
 810 | 				bottom: '',
 811 | 				'bottom-mid': '',
 812 | 				'bottom-left': '',
 813 | 				'bottom-right': '',
 814 | 				left: '',
 815 | 				'left-mid': '',
 816 | 				mid: '',
 817 | 				'mid-mid': '',
 818 | 				right: '',
 819 | 				'right-mid': '',
 820 | 				middle: ' '
 821 | 			},
 822 | 			style: { border: [], 'padding-left': 4 },
 823 | 			wordWrap: true
 824 | 		});
 825 | 
 826 | 		category.commands.forEach((cmd, index) => {
 827 | 			commandTable.push([
 828 | 				`${chalk.yellow.bold(cmd.name)}${chalk.reset('')}`,
 829 | 				`${chalk.white(cmd.args)}${chalk.reset('')}`,
 830 | 				`${chalk.dim(cmd.desc)}${chalk.reset('')}`
 831 | 			]);
 832 | 		});
 833 | 
 834 | 		console.log(commandTable.toString());
 835 | 		console.log('');
 836 | 	});
 837 | 
 838 | 	// Display configuration section
 839 | 	console.log(
 840 | 		boxen(chalk.cyan.bold('Configuration'), {
 841 | 			padding: { left: 2, right: 2, top: 0, bottom: 0 },
 842 | 			margin: { top: 1, bottom: 0 },
 843 | 			borderColor: 'cyan',
 844 | 			borderStyle: 'round'
 845 | 		})
 846 | 	);
 847 | 
 848 | 	// Get terminal width if not already defined
 849 | 	const configTerminalWidth = terminalWidth || process.stdout.columns || 100;
 850 | 
 851 | 	// Calculate dynamic column widths for config table
 852 | 	const configKeyWidth = Math.max(30, Math.floor(configTerminalWidth * 0.25));
 853 | 	const configDescWidth = Math.max(50, Math.floor(configTerminalWidth * 0.45));
 854 | 	const configValueWidth = Math.max(
 855 | 		30,
 856 | 		Math.floor(configTerminalWidth * 0.3) - 10
 857 | 	);
 858 | 
 859 | 	const configTable = new Table({
 860 | 		colWidths: [configKeyWidth, configDescWidth, configValueWidth],
 861 | 		chars: {
 862 | 			top: '',
 863 | 			'top-mid': '',
 864 | 			'top-left': '',
 865 | 			'top-right': '',
 866 | 			bottom: '',
 867 | 			'bottom-mid': '',
 868 | 			'bottom-left': '',
 869 | 			'bottom-right': '',
 870 | 			left: '',
 871 | 			'left-mid': '',
 872 | 			mid: '',
 873 | 			'mid-mid': '',
 874 | 			right: '',
 875 | 			'right-mid': '',
 876 | 			middle: ' '
 877 | 		},
 878 | 		style: { border: [], 'padding-left': 4 },
 879 | 		wordWrap: true
 880 | 	});
 881 | 
 882 | 	configTable.push(
 883 | 		[
 884 | 			`${chalk.yellow(TASKMASTER_CONFIG_FILE)}${chalk.reset('')}`,
 885 | 			`${chalk.white('AI model configuration file (project root)')}${chalk.reset('')}`,
 886 | 			`${chalk.dim('Managed by models cmd')}${chalk.reset('')}`
 887 | 		],
 888 | 		[
 889 | 			`${chalk.yellow('API Keys (.env)')}${chalk.reset('')}`,
 890 | 			`${chalk.white('API keys for AI providers (ANTHROPIC_API_KEY, etc.)')}${chalk.reset('')}`,
 891 | 			`${chalk.dim('Required in .env file')}${chalk.reset('')}`
 892 | 		],
 893 | 		[
 894 | 			`${chalk.yellow('MCP Keys (mcp.json)')}${chalk.reset('')}`,
 895 | 			`${chalk.white('API keys for Cursor integration')}${chalk.reset('')}`,
 896 | 			`${chalk.dim('Required in .cursor/')}${chalk.reset('')}`
 897 | 		]
 898 | 	);
 899 | 
 900 | 	console.log(configTable.toString());
 901 | 	console.log('');
 902 | 
 903 | 	// Show helpful hints
 904 | 	console.log(
 905 | 		boxen(
 906 | 			chalk.white.bold('Quick Start:') +
 907 | 				'\n\n' +
 908 | 				chalk.cyan('1. Create Project: ') +
 909 | 				chalk.white('task-master init') +
 910 | 				'\n' +
 911 | 				chalk.cyan('2. Setup Models: ') +
 912 | 				chalk.white('task-master models --setup') +
 913 | 				'\n' +
 914 | 				chalk.cyan('3. Parse PRD: ') +
 915 | 				chalk.white('task-master parse-prd --input=<prd-file>') +
 916 | 				'\n' +
 917 | 				chalk.cyan('4. List Tasks: ') +
 918 | 				chalk.white('task-master list') +
 919 | 				'\n' +
 920 | 				chalk.cyan('5. Find Next Task: ') +
 921 | 				chalk.white('task-master next'),
 922 | 			{
 923 | 				padding: 1,
 924 | 				borderColor: 'yellow',
 925 | 				borderStyle: 'round',
 926 | 				margin: { top: 1 },
 927 | 				width: Math.min(configTerminalWidth - 10, 100) // Limit width to terminal width minus padding, max 100
 928 | 			}
 929 | 		)
 930 | 	);
 931 | }
 932 | 
 933 | /**
 934 |  * Get colored complexity score
 935 |  * @param {number} score - Complexity score (1-10)
 936 |  * @returns {string} Colored complexity score
 937 |  */
 938 | function getComplexityWithColor(score) {
 939 | 	if (score <= 3) return chalk.green(`● ${score}`);
 940 | 	if (score <= 6) return chalk.yellow(`● ${score}`);
 941 | 	return chalk.red(`● ${score}`);
 942 | }
 943 | 
 944 | /**
 945 |  * Truncate a string to a maximum length and add ellipsis if needed
 946 |  * @param {string} str - The string to truncate
 947 |  * @param {number} maxLength - Maximum length
 948 |  * @returns {string} Truncated string
 949 |  */
 950 | function truncateString(str, maxLength) {
 951 | 	if (!str) return '';
 952 | 	if (str.length <= maxLength) return str;
 953 | 	return str.substring(0, maxLength - 3) + '...';
 954 | }
 955 | 
 956 | /**
 957 |  * Display the next task to work on
 958 |  * @param {string} tasksPath - Path to the tasks.json file
 959 |  * @param {string} complexityReportPath - Path to the complexity report file
 960 |  * @param {string} tag - Optional tag to override current tag resolution
 961 |  */
 962 | async function displayNextTask(
 963 | 	tasksPath,
 964 | 	complexityReportPath = null,
 965 | 	context = {}
 966 | ) {
 967 | 	// Extract parameters from context
 968 | 	const { projectRoot, tag } = context;
 969 | 
 970 | 	// Read the tasks file with proper projectRoot for tag resolution
 971 | 	const data = readJSON(tasksPath, projectRoot, tag);
 972 | 	if (!data || !data.tasks) {
 973 | 		log('error', 'No valid tasks found.');
 974 | 		process.exit(1);
 975 | 	}
 976 | 
 977 | 	// Read complexity report once
 978 | 	const complexityReport = readComplexityReport(complexityReportPath);
 979 | 
 980 | 	// Find the next task
 981 | 	const nextTask = findNextTask(data.tasks, complexityReport);
 982 | 
 983 | 	if (!nextTask) {
 984 | 		console.log(
 985 | 			boxen(
 986 | 				chalk.yellow('No eligible tasks found!\n\n') +
 987 | 					'All pending tasks have unsatisfied dependencies, or all tasks are completed.',
 988 | 				{
 989 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
 990 | 					borderColor: 'yellow',
 991 | 					borderStyle: 'round',
 992 | 					margin: { top: 1 }
 993 | 				}
 994 | 			)
 995 | 		);
 996 | 		return;
 997 | 	}
 998 | 
 999 | 	// Display the task in a nice format
1000 | 	console.log(
1001 | 		boxen(chalk.white.bold(`Next Task: #${nextTask.id} - ${nextTask.title}`), {
1002 | 			padding: { top: 0, bottom: 0, left: 1, right: 1 },
1003 | 			borderColor: 'blue',
1004 | 			borderStyle: 'round',
1005 | 			margin: { top: 1, bottom: 0 }
1006 | 		})
1007 | 	);
1008 | 
1009 | 	// Create a table with task details
1010 | 	const taskTable = new Table({
1011 | 		style: {
1012 | 			head: [],
1013 | 			border: [],
1014 | 			'padding-top': 0,
1015 | 			'padding-bottom': 0,
1016 | 			compact: true
1017 | 		},
1018 | 		chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' },
1019 | 		colWidths: [15, Math.min(75, process.stdout.columns - 20 || 60)],
1020 | 		wordWrap: true
1021 | 	});
1022 | 
1023 | 	// Priority with color
1024 | 	const priorityColors = {
1025 | 		high: chalk.red.bold,
1026 | 		medium: chalk.yellow,
1027 | 		low: chalk.gray
1028 | 	};
1029 | 	const priorityColor =
1030 | 		priorityColors[nextTask.priority || 'medium'] || chalk.white;
1031 | 
1032 | 	// Add task details to table
1033 | 	taskTable.push(
1034 | 		[chalk.cyan.bold('ID:'), nextTask.id.toString()],
1035 | 		[chalk.cyan.bold('Title:'), nextTask.title],
1036 | 		[
1037 | 			chalk.cyan.bold('Priority:'),
1038 | 			priorityColor(nextTask.priority || 'medium')
1039 | 		],
1040 | 		[
1041 | 			chalk.cyan.bold('Dependencies:'),
1042 | 			formatDependenciesWithStatus(
1043 | 				nextTask.dependencies,
1044 | 				data.tasks,
1045 | 				true,
1046 | 				complexityReport
1047 | 			)
1048 | 		],
1049 | 		[
1050 | 			chalk.cyan.bold('Complexity:'),
1051 | 			nextTask.complexityScore
1052 | 				? getComplexityWithColor(nextTask.complexityScore)
1053 | 				: chalk.gray('N/A')
1054 | 		],
1055 | 		[chalk.cyan.bold('Description:'), nextTask.description]
1056 | 	);
1057 | 
1058 | 	console.log(taskTable.toString());
1059 | 
1060 | 	// If task has details, show them in a separate box
1061 | 	if (nextTask.details && nextTask.details.trim().length > 0) {
1062 | 		console.log(
1063 | 			boxen(
1064 | 				chalk.white.bold('Implementation Details:') + '\n\n' + nextTask.details,
1065 | 				{
1066 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
1067 | 					borderColor: 'cyan',
1068 | 					borderStyle: 'round',
1069 | 					margin: { top: 1, bottom: 0 }
1070 | 				}
1071 | 			)
1072 | 		);
1073 | 	}
1074 | 
1075 | 	// Determine if the nextTask is a subtask
1076 | 	const isSubtask = !!nextTask.parentId;
1077 | 
1078 | 	// Show subtasks if they exist (only for parent tasks)
1079 | 	if (!isSubtask && nextTask.subtasks && nextTask.subtasks.length > 0) {
1080 | 		console.log(
1081 | 			boxen(chalk.white.bold('Subtasks'), {
1082 | 				padding: { top: 0, bottom: 0, left: 1, right: 1 },
1083 | 				margin: { top: 1, bottom: 0 },
1084 | 				borderColor: 'magenta',
1085 | 				borderStyle: 'round'
1086 | 			})
1087 | 		);
1088 | 
1089 | 		// Calculate available width for the subtask table
1090 | 		const availableWidth = process.stdout.columns - 10 || 100; // Default to 100 if can't detect
1091 | 
1092 | 		// Define percentage-based column widths
1093 | 		const idWidthPct = 8;
1094 | 		const statusWidthPct = 15;
1095 | 		const depsWidthPct = 25;
1096 | 		const titleWidthPct = 100 - idWidthPct - statusWidthPct - depsWidthPct;
1097 | 
1098 | 		// Calculate actual column widths
1099 | 		const idWidth = Math.floor(availableWidth * (idWidthPct / 100));
1100 | 		const statusWidth = Math.floor(availableWidth * (statusWidthPct / 100));
1101 | 		const depsWidth = Math.floor(availableWidth * (depsWidthPct / 100));
1102 | 		const titleWidth = Math.floor(availableWidth * (titleWidthPct / 100));
1103 | 
1104 | 		// Create a table for subtasks with improved handling
1105 | 		const subtaskTable = new Table({
1106 | 			head: [
1107 | 				chalk.magenta.bold('ID'),
1108 | 				chalk.magenta.bold('Status'),
1109 | 				chalk.magenta.bold('Title'),
1110 | 				chalk.magenta.bold('Deps')
1111 | 			],
1112 | 			colWidths: [idWidth, statusWidth, titleWidth, depsWidth],
1113 | 			style: {
1114 | 				head: [],
1115 | 				border: [],
1116 | 				'padding-top': 0,
1117 | 				'padding-bottom': 0,
1118 | 				compact: true
1119 | 			},
1120 | 			chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' },
1121 | 			wordWrap: true
1122 | 		});
1123 | 
1124 | 		// Add subtasks to table
1125 | 		nextTask.subtasks.forEach((st) => {
1126 | 			const statusColor =
1127 | 				{
1128 | 					done: chalk.green,
1129 | 					completed: chalk.green,
1130 | 					pending: chalk.yellow,
1131 | 					'in-progress': chalk.blue
1132 | 				}[st.status || 'pending'] || chalk.white;
1133 | 
1134 | 			// Format subtask dependencies
1135 | 			let subtaskDeps = 'None';
1136 | 			if (st.dependencies && st.dependencies.length > 0) {
1137 | 				// Format dependencies with correct notation
1138 | 				const formattedDeps = st.dependencies.map((depId) => {
1139 | 					if (typeof depId === 'number' && depId < 100) {
1140 | 						const foundSubtask = nextTask.subtasks.find(
1141 | 							(st) => st.id === depId
1142 | 						);
1143 | 						if (foundSubtask) {
1144 | 							const isDone =
1145 | 								foundSubtask.status === 'done' ||
1146 | 								foundSubtask.status === 'completed';
1147 | 							const isInProgress = foundSubtask.status === 'in-progress';
1148 | 
1149 | 							// Use consistent color formatting instead of emojis
1150 | 							if (isDone) {
1151 | 								return chalk.green.bold(`${nextTask.id}.${depId}`);
1152 | 							} else if (isInProgress) {
1153 | 								return chalk.hex('#FFA500').bold(`${nextTask.id}.${depId}`);
1154 | 							} else {
1155 | 								return chalk.red.bold(`${nextTask.id}.${depId}`);
1156 | 							}
1157 | 						}
1158 | 						return chalk.red(`${nextTask.id}.${depId} (Not found)`);
1159 | 					}
1160 | 					return depId;
1161 | 				});
1162 | 
1163 | 				// Join the formatted dependencies directly instead of passing to formatDependenciesWithStatus again
1164 | 				subtaskDeps =
1165 | 					formattedDeps.length === 1
1166 | 						? formattedDeps[0]
1167 | 						: formattedDeps.join(chalk.white(', '));
1168 | 			}
1169 | 
1170 | 			subtaskTable.push([
1171 | 				`${nextTask.id}.${st.id}`,
1172 | 				statusColor(st.status || 'pending'),
1173 | 				st.title,
1174 | 				subtaskDeps
1175 | 			]);
1176 | 		});
1177 | 
1178 | 		console.log(subtaskTable.toString());
1179 | 	}
1180 | 
1181 | 	// Suggest expanding if no subtasks (only for parent tasks without subtasks)
1182 | 	if (!isSubtask && (!nextTask.subtasks || nextTask.subtasks.length === 0)) {
1183 | 		console.log(
1184 | 			boxen(
1185 | 				chalk.yellow('No subtasks found. Consider breaking down this task:') +
1186 | 					'\n' +
1187 | 					chalk.white(
1188 | 						`Run: ${chalk.cyan(`task-master expand --id=${nextTask.id}`)}`
1189 | 					),
1190 | 				{
1191 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
1192 | 					borderColor: 'yellow',
1193 | 					borderStyle: 'round',
1194 | 					margin: { top: 1, bottom: 0 }
1195 | 				}
1196 | 			)
1197 | 		);
1198 | 	}
1199 | 
1200 | 	// Show action suggestions
1201 | 	let suggestedActionsContent = chalk.white.bold('Suggested Actions:') + '\n';
1202 | 	if (isSubtask) {
1203 | 		// Suggested actions for a subtask
1204 | 		suggestedActionsContent +=
1205 | 			`${chalk.cyan('1.')} Mark as in-progress: ${chalk.yellow(`task-master set-status --id=${nextTask.id} --status=in-progress`)}\n` +
1206 | 			`${chalk.cyan('2.')} Mark as done when completed: ${chalk.yellow(`task-master set-status --id=${nextTask.id} --status=done`)}\n` +
1207 | 			`${chalk.cyan('3.')} View parent task: ${chalk.yellow(`task-master show --id=${nextTask.parentId}`)}`;
1208 | 	} else {
1209 | 		// Suggested actions for a parent task
1210 | 		suggestedActionsContent +=
1211 | 			`${chalk.cyan('1.')} Mark as in-progress: ${chalk.yellow(`task-master set-status --id=${nextTask.id} --status=in-progress`)}\n` +
1212 | 			`${chalk.cyan('2.')} Mark as done when completed: ${chalk.yellow(`task-master set-status --id=${nextTask.id} --status=done`)}\n` +
1213 | 			(nextTask.subtasks && nextTask.subtasks.length > 0
1214 | 				? `${chalk.cyan('3.')} Update subtask status: ${chalk.yellow(`task-master set-status --id=${nextTask.id}.1 --status=done`)}` // Example: first subtask
1215 | 				: `${chalk.cyan('3.')} Break down into subtasks: ${chalk.yellow(`task-master expand --id=${nextTask.id}`)}`);
1216 | 	}
1217 | 
1218 | 	console.log(
1219 | 		boxen(suggestedActionsContent, {
1220 | 			padding: { top: 0, bottom: 0, left: 1, right: 1 },
1221 | 			borderColor: 'green',
1222 | 			borderStyle: 'round',
1223 | 			margin: { top: 1 }
1224 | 		})
1225 | 	);
1226 | 
1227 | 	// Show FYI notice if migration occurred
1228 | 	displayTaggedTasksFYI(data);
1229 | }
1230 | 
1231 | /**
1232 |  * Display a specific task by ID
1233 |  * @param {string} tasksPath - Path to the tasks.json file
1234 |  * @param {string|number} taskId - The ID of the task to display
1235 |  * @param {string} complexityReportPath - Path to the complexity report file
1236 |  * @param {string} [statusFilter] - Optional status to filter subtasks by
1237 |  * @param {object} context - Context object containing projectRoot and tag
1238 |  * @param {string} context.projectRoot - Project root path
1239 |  * @param {string} context.tag - Tag for the task
1240 |  */
1241 | async function displayTaskById(
1242 | 	tasksPath,
1243 | 	taskId,
1244 | 	complexityReportPath = null,
1245 | 	statusFilter = null,
1246 | 	context = {}
1247 | ) {
1248 | 	const { projectRoot, tag } = context;
1249 | 
1250 | 	// Read the tasks file with proper projectRoot for tag resolution
1251 | 	const data = readJSON(tasksPath, projectRoot, tag);
1252 | 	if (!data || !data.tasks) {
1253 | 		log('error', 'No valid tasks found.');
1254 | 		process.exit(1);
1255 | 	}
1256 | 
1257 | 	// Read complexity report once
1258 | 	const complexityReport = readComplexityReport(complexityReportPath);
1259 | 
1260 | 	// Find the task by ID, applying the status filter if provided
1261 | 	// Returns { task, originalSubtaskCount, originalSubtasks }
1262 | 	const { task, originalSubtaskCount, originalSubtasks } = findTaskById(
1263 | 		data.tasks,
1264 | 		taskId,
1265 | 		complexityReport,
1266 | 		statusFilter
1267 | 	);
1268 | 
1269 | 	if (!task) {
1270 | 		console.log(
1271 | 			boxen(chalk.yellow(`Task with ID ${taskId} not found!`), {
1272 | 				padding: { top: 0, bottom: 0, left: 1, right: 1 },
1273 | 				borderColor: 'yellow',
1274 | 				borderStyle: 'round',
1275 | 				margin: { top: 1 }
1276 | 			})
1277 | 		);
1278 | 		return;
1279 | 	}
1280 | 
1281 | 	// Handle subtask display specially (This logic remains the same)
1282 | 	if (task.isSubtask || task.parentTask) {
1283 | 		console.log(
1284 | 			boxen(
1285 | 				chalk.white.bold(
1286 | 					`Subtask: #${task.parentTask.id}.${task.id} - ${task.title}`
1287 | 				),
1288 | 				{
1289 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
1290 | 					borderColor: 'magenta',
1291 | 					borderStyle: 'round',
1292 | 					margin: { top: 1, bottom: 0 }
1293 | 				}
1294 | 			)
1295 | 		);
1296 | 
1297 | 		const subtaskTable = new Table({
1298 | 			style: {
1299 | 				head: [],
1300 | 				border: [],
1301 | 				'padding-top': 0,
1302 | 				'padding-bottom': 0,
1303 | 				compact: true
1304 | 			},
1305 | 			chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' },
1306 | 			colWidths: [15, Math.min(75, process.stdout.columns - 20 || 60)],
1307 | 			wordWrap: true
1308 | 		});
1309 | 		subtaskTable.push(
1310 | 			[chalk.cyan.bold('ID:'), `${task.parentTask.id}.${task.id}`],
1311 | 			[
1312 | 				chalk.cyan.bold('Parent Task:'),
1313 | 				`#${task.parentTask.id} - ${task.parentTask.title}`
1314 | 			],
1315 | 			[chalk.cyan.bold('Title:'), task.title],
1316 | 			[
1317 | 				chalk.cyan.bold('Status:'),
1318 | 				getStatusWithColor(task.status || 'pending', true)
1319 | 			],
1320 | 			[
1321 | 				chalk.cyan.bold('Complexity:'),
1322 | 				task.complexityScore
1323 | 					? getComplexityWithColor(task.complexityScore)
1324 | 					: chalk.gray('N/A')
1325 | 			],
1326 | 			[
1327 | 				chalk.cyan.bold('Description:'),
1328 | 				task.description || 'No description provided.'
1329 | 			]
1330 | 		);
1331 | 		console.log(subtaskTable.toString());
1332 | 
1333 | 		if (task.details && task.details.trim().length > 0) {
1334 | 			console.log(
1335 | 				boxen(
1336 | 					chalk.white.bold('Implementation Details:') + '\n\n' + task.details,
1337 | 					{
1338 | 						padding: { top: 0, bottom: 0, left: 1, right: 1 },
1339 | 						borderColor: 'cyan',
1340 | 						borderStyle: 'round',
1341 | 						margin: { top: 1, bottom: 0 }
1342 | 					}
1343 | 				)
1344 | 			);
1345 | 		}
1346 | 
1347 | 		console.log(
1348 | 			boxen(
1349 | 				chalk.white.bold('Suggested Actions:') +
1350 | 					'\n' +
1351 | 					`${chalk.cyan('1.')} Mark as in-progress: ${chalk.yellow(`task-master set-status --id=${task.parentTask.id}.${task.id} --status=in-progress`)}\n` +
1352 | 					`${chalk.cyan('2.')} Mark as done when completed: ${chalk.yellow(`task-master set-status --id=${task.parentTask.id}.${task.id} --status=done`)}\n` +
1353 | 					`${chalk.cyan('3.')} View parent task: ${chalk.yellow(`task-master show --id=${task.parentTask.id}`)}`,
1354 | 				{
1355 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
1356 | 					borderColor: 'green',
1357 | 					borderStyle: 'round',
1358 | 					margin: { top: 1 }
1359 | 				}
1360 | 			)
1361 | 		);
1362 | 		return; // Exit after displaying subtask details
1363 | 	}
1364 | 
1365 | 	// --- Display Regular Task Details ---
1366 | 	console.log(
1367 | 		boxen(chalk.white.bold(`Task: #${task.id} - ${task.title}`), {
1368 | 			padding: { top: 0, bottom: 0, left: 1, right: 1 },
1369 | 			borderColor: 'blue',
1370 | 			borderStyle: 'round',
1371 | 			margin: { top: 1, bottom: 0 }
1372 | 		})
1373 | 	);
1374 | 
1375 | 	const taskTable = new Table({
1376 | 		style: {
1377 | 			head: [],
1378 | 			border: [],
1379 | 			'padding-top': 0,
1380 | 			'padding-bottom': 0,
1381 | 			compact: true
1382 | 		},
1383 | 		chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' },
1384 | 		colWidths: [15, Math.min(75, process.stdout.columns - 20 || 60)],
1385 | 		wordWrap: true
1386 | 	});
1387 | 	const priorityColors = {
1388 | 		high: chalk.red.bold,
1389 | 		medium: chalk.yellow,
1390 | 		low: chalk.gray
1391 | 	};
1392 | 	const priorityColor =
1393 | 		priorityColors[task.priority || 'medium'] || chalk.white;
1394 | 	taskTable.push(
1395 | 		[chalk.cyan.bold('ID:'), task.id.toString()],
1396 | 		[chalk.cyan.bold('Title:'), task.title],
1397 | 		[
1398 | 			chalk.cyan.bold('Status:'),
1399 | 			getStatusWithColor(task.status || 'pending', true)
1400 | 		],
1401 | 		[chalk.cyan.bold('Priority:'), priorityColor(task.priority || 'medium')],
1402 | 		[
1403 | 			chalk.cyan.bold('Dependencies:'),
1404 | 			formatDependenciesWithStatus(
1405 | 				task.dependencies,
1406 | 				data.tasks,
1407 | 				true,
1408 | 				complexityReport
1409 | 			)
1410 | 		],
1411 | 		[
1412 | 			chalk.cyan.bold('Complexity:'),
1413 | 			task.complexityScore
1414 | 				? getComplexityWithColor(task.complexityScore)
1415 | 				: chalk.gray('N/A')
1416 | 		],
1417 | 		[chalk.cyan.bold('Description:'), task.description]
1418 | 	);
1419 | 	console.log(taskTable.toString());
1420 | 
1421 | 	if (task.details && task.details.trim().length > 0) {
1422 | 		console.log(
1423 | 			boxen(
1424 | 				chalk.white.bold('Implementation Details:') + '\n\n' + task.details,
1425 | 				{
1426 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
1427 | 					borderColor: 'cyan',
1428 | 					borderStyle: 'round',
1429 | 					margin: { top: 1, bottom: 0 }
1430 | 				}
1431 | 			)
1432 | 		);
1433 | 	}
1434 | 	if (task.testStrategy && task.testStrategy.trim().length > 0) {
1435 | 		console.log(
1436 | 			boxen(chalk.white.bold('Test Strategy:') + '\n\n' + task.testStrategy, {
1437 | 				padding: { top: 0, bottom: 0, left: 1, right: 1 },
1438 | 				borderColor: 'cyan',
1439 | 				borderStyle: 'round',
1440 | 				margin: { top: 1, bottom: 0 }
1441 | 			})
1442 | 		);
1443 | 	}
1444 | 
1445 | 	// --- Subtask Table Display (uses filtered list: task.subtasks) ---
1446 | 	if (task.subtasks && task.subtasks.length > 0) {
1447 | 		console.log(
1448 | 			boxen(chalk.white.bold('Subtasks'), {
1449 | 				padding: { top: 0, bottom: 0, left: 1, right: 1 },
1450 | 				margin: { top: 1, bottom: 0 },
1451 | 				borderColor: 'magenta',
1452 | 				borderStyle: 'round'
1453 | 			})
1454 | 		);
1455 | 
1456 | 		const availableWidth = process.stdout.columns - 10 || 100;
1457 | 		const idWidthPct = 10;
1458 | 		const statusWidthPct = 15;
1459 | 		const depsWidthPct = 25;
1460 | 		const titleWidthPct = 100 - idWidthPct - statusWidthPct - depsWidthPct;
1461 | 		const idWidth = Math.floor(availableWidth * (idWidthPct / 100));
1462 | 		const statusWidth = Math.floor(availableWidth * (statusWidthPct / 100));
1463 | 		const depsWidth = Math.floor(availableWidth * (depsWidthPct / 100));
1464 | 		const titleWidth = Math.floor(availableWidth * (titleWidthPct / 100));
1465 | 
1466 | 		const subtaskTable = new Table({
1467 | 			head: [
1468 | 				chalk.magenta.bold('ID'),
1469 | 				chalk.magenta.bold('Status'),
1470 | 				chalk.magenta.bold('Title'),
1471 | 				chalk.magenta.bold('Deps')
1472 | 			],
1473 | 			colWidths: [idWidth, statusWidth, titleWidth, depsWidth],
1474 | 			style: {
1475 | 				head: [],
1476 | 				border: [],
1477 | 				'padding-top': 0,
1478 | 				'padding-bottom': 0,
1479 | 				compact: true
1480 | 			},
1481 | 			chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' },
1482 | 			wordWrap: true
1483 | 		});
1484 | 
1485 | 		// Populate table with the potentially filtered subtasks
1486 | 		task.subtasks.forEach((st) => {
1487 | 			const statusColorMap = {
1488 | 				done: chalk.green,
1489 | 				completed: chalk.green,
1490 | 				pending: chalk.yellow,
1491 | 				'in-progress': chalk.blue
1492 | 			};
1493 | 			const statusColor = statusColorMap[st.status || 'pending'] || chalk.white;
1494 | 			let subtaskDeps = 'None';
1495 | 			if (st.dependencies && st.dependencies.length > 0) {
1496 | 				const formattedDeps = st.dependencies.map((depId) => {
1497 | 					// Use the original, unfiltered list for dependency status lookup
1498 | 					const sourceListForDeps = originalSubtasks || task.subtasks;
1499 | 					const foundDepSubtask =
1500 | 						typeof depId === 'number' && depId < 100
1501 | 							? sourceListForDeps.find((sub) => sub.id === depId)
1502 | 							: null;
1503 | 
1504 | 					if (foundDepSubtask) {
1505 | 						const isDone =
1506 | 							foundDepSubtask.status === 'done' ||
1507 | 							foundDepSubtask.status === 'completed';
1508 | 						const isInProgress = foundDepSubtask.status === 'in-progress';
1509 | 						const color = isDone
1510 | 							? chalk.green.bold
1511 | 							: isInProgress
1512 | 								? chalk.hex('#FFA500').bold
1513 | 								: chalk.red.bold;
1514 | 						return color(`${task.id}.${depId}`);
1515 | 					} else if (typeof depId === 'number' && depId < 100) {
1516 | 						return chalk.red(`${task.id}.${depId} (Not found)`);
1517 | 					}
1518 | 					return depId; // Assume it's a top-level task ID if not a number < 100
1519 | 				});
1520 | 				subtaskDeps =
1521 | 					formattedDeps.length === 1
1522 | 						? formattedDeps[0]
1523 | 						: formattedDeps.join(chalk.white(', '));
1524 | 			}
1525 | 			subtaskTable.push([
1526 | 				`${task.id}.${st.id}`,
1527 | 				statusColor(st.status || 'pending'),
1528 | 				st.title,
1529 | 				subtaskDeps
1530 | 			]);
1531 | 		});
1532 | 		console.log(subtaskTable.toString());
1533 | 
1534 | 		// Display filter summary line *immediately after the table* if a filter was applied
1535 | 		if (statusFilter && originalSubtaskCount !== null) {
1536 | 			console.log(
1537 | 				chalk.cyan(
1538 | 					`  Filtered by status: ${chalk.bold(statusFilter)}. Showing ${chalk.bold(task.subtasks.length)} of ${chalk.bold(originalSubtaskCount)} subtasks.`
1539 | 				)
1540 | 			);
1541 | 			// Add a newline for spacing before the progress bar if the filter line was shown
1542 | 			console.log();
1543 | 		}
1544 | 		// --- Conditional Messages for No Subtasks Shown ---
1545 | 	} else if (statusFilter && originalSubtaskCount === 0) {
1546 | 		// Case where filter applied, but the parent task had 0 subtasks originally
1547 | 		console.log(
1548 | 			boxen(
1549 | 				chalk.yellow(
1550 | 					`No subtasks found matching status: ${statusFilter} (Task has no subtasks)`
1551 | 				),
1552 | 				{
1553 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
1554 | 					margin: { top: 1, bottom: 0 },
1555 | 					borderColor: 'yellow',
1556 | 					borderStyle: 'round'
1557 | 				}
1558 | 			)
1559 | 		);
1560 | 	} else if (
1561 | 		statusFilter &&
1562 | 		originalSubtaskCount > 0 &&
1563 | 		task.subtasks.length === 0
1564 | 	) {
1565 | 		// Case where filter applied, original subtasks existed, but none matched
1566 | 		console.log(
1567 | 			boxen(
1568 | 				chalk.yellow(
1569 | 					`No subtasks found matching status: ${statusFilter} (out of ${originalSubtaskCount} total)`
1570 | 				),
1571 | 				{
1572 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
1573 | 					margin: { top: 1, bottom: 0 },
1574 | 					borderColor: 'yellow',
1575 | 					borderStyle: 'round'
1576 | 				}
1577 | 			)
1578 | 		);
1579 | 	} else if (
1580 | 		!statusFilter &&
1581 | 		(!originalSubtasks || originalSubtasks.length === 0)
1582 | 	) {
1583 | 		// Case where NO filter applied AND the task genuinely has no subtasks
1584 | 		// Use the authoritative originalSubtasks if it exists (from filtering), else check task.subtasks
1585 | 		const actualSubtasks = originalSubtasks || task.subtasks;
1586 | 		if (!actualSubtasks || actualSubtasks.length === 0) {
1587 | 			console.log(
1588 | 				boxen(
1589 | 					chalk.yellow('No subtasks found. Consider breaking down this task:') +
1590 | 						'\n' +
1591 | 						chalk.white(
1592 | 							`Run: ${chalk.cyan(`task-master expand --id=${task.id}`)}`
1593 | 						),
1594 | 					{
1595 | 						padding: { top: 0, bottom: 0, left: 1, right: 1 },
1596 | 						borderColor: 'yellow',
1597 | 						borderStyle: 'round',
1598 | 						margin: { top: 1, bottom: 0 }
1599 | 					}
1600 | 				)
1601 | 			);
1602 | 		}
1603 | 	}
1604 | 
1605 | 	// --- Subtask Progress Bar Display (uses originalSubtasks or task.subtasks) ---
1606 | 	// Determine the list to use for progress calculation (always the original if available and filtering happened)
1607 | 	const subtasksForProgress = originalSubtasks || task.subtasks; // Use original if filtering occurred, else the potentially empty task.subtasks
1608 | 
1609 | 	// Only show progress if there are actually subtasks
1610 | 	if (subtasksForProgress && subtasksForProgress.length > 0) {
1611 | 		const totalSubtasks = subtasksForProgress.length;
1612 | 		const completedSubtasks = subtasksForProgress.filter(
1613 | 			(st) => st.status === 'done' || st.status === 'completed'
1614 | 		).length;
1615 | 
1616 | 		// Count other statuses from the original/complete list
1617 | 		const inProgressSubtasks = subtasksForProgress.filter(
1618 | 			(st) => st.status === 'in-progress'
1619 | 		).length;
1620 | 		const pendingSubtasks = subtasksForProgress.filter(
1621 | 			(st) => st.status === 'pending'
1622 | 		).length;
1623 | 		const blockedSubtasks = subtasksForProgress.filter(
1624 | 			(st) => st.status === 'blocked'
1625 | 		).length;
1626 | 		const deferredSubtasks = subtasksForProgress.filter(
1627 | 			(st) => st.status === 'deferred'
1628 | 		).length;
1629 | 		const cancelledSubtasks = subtasksForProgress.filter(
1630 | 			(st) => st.status === 'cancelled'
1631 | 		).length;
1632 | 
1633 | 		const statusBreakdown = {
1634 | 			// Calculate breakdown based on the complete list
1635 | 			'in-progress': (inProgressSubtasks / totalSubtasks) * 100,
1636 | 			pending: (pendingSubtasks / totalSubtasks) * 100,
1637 | 			blocked: (blockedSubtasks / totalSubtasks) * 100,
1638 | 			deferred: (deferredSubtasks / totalSubtasks) * 100,
1639 | 			cancelled: (cancelledSubtasks / totalSubtasks) * 100
1640 | 		};
1641 | 		const completionPercentage = (completedSubtasks / totalSubtasks) * 100;
1642 | 
1643 | 		const availableWidth = process.stdout.columns || 80;
1644 | 		const boxPadding = 2;
1645 | 		const boxBorders = 2;
1646 | 		const percentTextLength = 5;
1647 | 		const progressBarLength = Math.max(
1648 | 			20,
1649 | 			Math.min(
1650 | 				60,
1651 | 				availableWidth - boxPadding - boxBorders - percentTextLength - 35
1652 | 			)
1653 | 		);
1654 | 
1655 | 		const statusCounts =
1656 | 			`${chalk.green('✓ Done:')} ${completedSubtasks}  ${chalk.hex('#FFA500')('► In Progress:')} ${inProgressSubtasks}  ${chalk.yellow('○ Pending:')} ${pendingSubtasks}\n` +
1657 | 			`${chalk.red('! Blocked:')} ${blockedSubtasks}  ${chalk.gray('⏱ Deferred:')} ${deferredSubtasks}  ${chalk.gray('✗ Cancelled:')} ${cancelledSubtasks}`;
1658 | 
1659 | 		console.log(
1660 | 			boxen(
1661 | 				chalk.white.bold('Subtask Progress:') +
1662 | 					'\n\n' +
1663 | 					`${chalk.cyan('Completed:')} ${completedSubtasks}/${totalSubtasks} (${completionPercentage.toFixed(1)}%)\n` +
1664 | 					`${statusCounts}\n` +
1665 | 					`${chalk.cyan('Progress:')} ${createProgressBar(completionPercentage, progressBarLength, statusBreakdown)}`,
1666 | 				{
1667 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
1668 | 					borderColor: 'blue',
1669 | 					borderStyle: 'round',
1670 | 					margin: { top: 1, bottom: 0 },
1671 | 					width: Math.min(availableWidth - 10, 100),
1672 | 					textAlignment: 'left'
1673 | 				}
1674 | 			)
1675 | 		);
1676 | 	}
1677 | 
1678 | 	// --- Suggested Actions ---
1679 | 	const actions = [];
1680 | 	let actionNumber = 1;
1681 | 
1682 | 	// Basic actions
1683 | 	actions.push(
1684 | 		`${chalk.cyan(`${actionNumber}.`)} Mark as in-progress: ${chalk.yellow(`task-master set-status --id=${task.id} --status=in-progress`)}`
1685 | 	);
1686 | 	actionNumber++;
1687 | 	actions.push(
1688 | 		`${chalk.cyan(`${actionNumber}.`)} Mark as done when completed: ${chalk.yellow(`task-master set-status --id=${task.id} --status=done`)}`
1689 | 	);
1690 | 	actionNumber++;
1691 | 
1692 | 	// Subtask-related action
1693 | 	if (subtasksForProgress && subtasksForProgress.length > 0) {
1694 | 		actions.push(
1695 | 			`${chalk.cyan(`${actionNumber}.`)} Update subtask status: ${chalk.yellow(`task-master set-status --id=${task.id}.1 --status=done`)}`
1696 | 		);
1697 | 	} else {
1698 | 		actions.push(
1699 | 			`${chalk.cyan(`${actionNumber}.`)} Break down into subtasks: ${chalk.yellow(`task-master expand --id=${task.id}`)}`
1700 | 		);
1701 | 	}
1702 | 	actionNumber++;
1703 | 
1704 | 	// Complexity-based scope adjustment actions
1705 | 	if (task.complexityScore) {
1706 | 		const complexityScore = task.complexityScore;
1707 | 		actions.push(
1708 | 			`${chalk.cyan(`${actionNumber}.`)} Re-analyze complexity: ${chalk.yellow(`task-master analyze-complexity --id=${task.id}`)}`
1709 | 		);
1710 | 		actionNumber++;
1711 | 
1712 | 		// Add scope adjustment suggestions based on current complexity
1713 | 		if (complexityScore >= 7) {
1714 | 			// High complexity - suggest scoping down
1715 | 			actions.push(
1716 | 				`${chalk.cyan(`${actionNumber}.`)} Scope down (simplify): ${chalk.yellow(`task-master scope-down --id=${task.id} --strength=regular`)}`
1717 | 			);
1718 | 			actionNumber++;
1719 | 			if (complexityScore >= 9) {
1720 | 				actions.push(
1721 | 					`${chalk.cyan(`${actionNumber}.`)} Heavy scope down: ${chalk.yellow(`task-master scope-down --id=${task.id} --strength=heavy`)}`
1722 | 				);
1723 | 				actionNumber++;
1724 | 			}
1725 | 		} else if (complexityScore <= 4) {
1726 | 			// Low complexity - suggest scoping up
1727 | 			actions.push(
1728 | 				`${chalk.cyan(`${actionNumber}.`)} Scope up (add detail): ${chalk.yellow(`task-master scope-up --id=${task.id} --strength=regular`)}`
1729 | 			);
1730 | 			actionNumber++;
1731 | 			if (complexityScore <= 2) {
1732 | 				actions.push(
1733 | 					`${chalk.cyan(`${actionNumber}.`)} Heavy scope up: ${chalk.yellow(`task-master scope-up --id=${task.id} --strength=heavy`)}`
1734 | 				);
1735 | 				actionNumber++;
1736 | 			}
1737 | 		} else {
1738 | 			// Medium complexity (5-6) - offer both options
1739 | 			actions.push(
1740 | 				`${chalk.cyan(`${actionNumber}.`)} Scope up/down: ${chalk.yellow(`task-master scope-up --id=${task.id} --strength=light`)} or ${chalk.yellow(`scope-down --id=${task.id} --strength=light`)}`
1741 | 			);
1742 | 			actionNumber++;
1743 | 		}
1744 | 	}
1745 | 
1746 | 	console.log(
1747 | 		boxen(chalk.white.bold('Suggested Actions:') + '\n' + actions.join('\n'), {
1748 | 			padding: { top: 0, bottom: 0, left: 1, right: 1 },
1749 | 			borderColor: 'green',
1750 | 			borderStyle: 'round',
1751 | 			margin: { top: 1 }
1752 | 		})
1753 | 	);
1754 | 
1755 | 	// Show FYI notice if migration occurred
1756 | 	displayTaggedTasksFYI(data);
1757 | }
1758 | 
1759 | /**
1760 |  * Display the complexity analysis report in a nice format
1761 |  * @param {string} reportPath - Path to the complexity report file
1762 |  */
1763 | async function displayComplexityReport(reportPath) {
1764 | 	// Check if the report exists
1765 | 	if (!fs.existsSync(reportPath)) {
1766 | 		console.log(
1767 | 			boxen(
1768 | 				chalk.yellow(`No complexity report found at ${reportPath}\n\n`) +
1769 | 					'Would you like to generate one now?',
1770 | 				{
1771 | 					padding: 1,
1772 | 					borderColor: 'yellow',
1773 | 					borderStyle: 'round',
1774 | 					margin: { top: 1 }
1775 | 				}
1776 | 			)
1777 | 		);
1778 | 
1779 | 		const rl = readline.createInterface({
1780 | 			input: process.stdin,
1781 | 			output: process.stdout
1782 | 		});
1783 | 
1784 | 		const answer = await new Promise((resolve) => {
1785 | 			rl.question(chalk.cyan('Generate complexity report? (y/n): '), resolve);
1786 | 		});
1787 | 		rl.close();
1788 | 
1789 | 		if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
1790 | 			// Call the analyze-complexity command
1791 | 			console.log(chalk.blue('Generating complexity report...'));
1792 | 			const tasksPath = TASKMASTER_TASKS_FILE;
1793 | 			if (!fs.existsSync(tasksPath)) {
1794 | 				console.error(
1795 | 					'❌ No tasks.json file found. Please run "task-master init" or create a tasks.json file.'
1796 | 				);
1797 | 				return null;
1798 | 			}
1799 | 
1800 | 			await analyzeTaskComplexity({
1801 | 				output: reportPath,
1802 | 				research: false, // Default to no research for speed
1803 | 				file: tasksPath
1804 | 			});
1805 | 			// Read the newly generated report
1806 | 			return displayComplexityReport(reportPath);
1807 | 		} else {
1808 | 			console.log(chalk.yellow('Report generation cancelled.'));
1809 | 			return;
1810 | 		}
1811 | 	}
1812 | 
1813 | 	// Read the report
1814 | 	let report;
1815 | 	try {
1816 | 		report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
1817 | 	} catch (error) {
1818 | 		log('error', `Error reading complexity report: ${error.message}`);
1819 | 		return;
1820 | 	}
1821 | 
1822 | 	// Display report header
1823 | 	console.log(
1824 | 		boxen(chalk.white.bold('Task Complexity Analysis Report'), {
1825 | 			padding: 1,
1826 | 			borderColor: 'blue',
1827 | 			borderStyle: 'round',
1828 | 			margin: { top: 1, bottom: 1 }
1829 | 		})
1830 | 	);
1831 | 
1832 | 	// Display metadata
1833 | 	const metaTable = new Table({
1834 | 		style: {
1835 | 			head: [],
1836 | 			border: [],
1837 | 			'padding-top': 0,
1838 | 			'padding-bottom': 0,
1839 | 			compact: true
1840 | 		},
1841 | 		chars: {
1842 | 			mid: '',
1843 | 			'left-mid': '',
1844 | 			'mid-mid': '',
1845 | 			'right-mid': ''
1846 | 		},
1847 | 		colWidths: [20, 50]
1848 | 	});
1849 | 
1850 | 	metaTable.push(
1851 | 		[
1852 | 			chalk.cyan.bold('Generated:'),
1853 | 			new Date(report.meta.generatedAt).toLocaleString()
1854 | 		],
1855 | 		[chalk.cyan.bold('Tasks Analyzed:'), report.meta.tasksAnalyzed],
1856 | 		[chalk.cyan.bold('Threshold Score:'), report.meta.thresholdScore],
1857 | 		[chalk.cyan.bold('Project:'), report.meta.projectName],
1858 | 		[
1859 | 			chalk.cyan.bold('Research-backed:'),
1860 | 			report.meta.usedResearch ? 'Yes' : 'No'
1861 | 		]
1862 | 	);
1863 | 
1864 | 	console.log(metaTable.toString());
1865 | 
1866 | 	// Sort tasks by complexity score (highest first)
1867 | 	const sortedTasks = [...report.complexityAnalysis].sort(
1868 | 		(a, b) => b.complexityScore - a.complexityScore
1869 | 	);
1870 | 
1871 | 	// Determine which tasks need expansion based on threshold
1872 | 	const tasksNeedingExpansion = sortedTasks.filter(
1873 | 		(task) => task.complexityScore >= report.meta.thresholdScore
1874 | 	);
1875 | 	const simpleTasks = sortedTasks.filter(
1876 | 		(task) => task.complexityScore < report.meta.thresholdScore
1877 | 	);
1878 | 
1879 | 	// Create progress bar to show complexity distribution
1880 | 	const complexityDistribution = [0, 0, 0]; // Low (0-4), Medium (5-7), High (8-10)
1881 | 	sortedTasks.forEach((task) => {
1882 | 		if (task.complexityScore < 5) complexityDistribution[0]++;
1883 | 		else if (task.complexityScore < 8) complexityDistribution[1]++;
1884 | 		else complexityDistribution[2]++;
1885 | 	});
1886 | 
1887 | 	const percentLow = Math.round(
1888 | 		(complexityDistribution[0] / sortedTasks.length) * 100
1889 | 	);
1890 | 	const percentMedium = Math.round(
1891 | 		(complexityDistribution[1] / sortedTasks.length) * 100
1892 | 	);
1893 | 	const percentHigh = Math.round(
1894 | 		(complexityDistribution[2] / sortedTasks.length) * 100
1895 | 	);
1896 | 
1897 | 	console.log(
1898 | 		boxen(
1899 | 			chalk.white.bold('Complexity Distribution\n\n') +
1900 | 				`${chalk.green.bold('Low (1-4):')} ${complexityDistribution[0]} tasks (${percentLow}%)\n` +
1901 | 				`${chalk.yellow.bold('Medium (5-7):')} ${complexityDistribution[1]} tasks (${percentMedium}%)\n` +
1902 | 				`${chalk.red.bold('High (8-10):')} ${complexityDistribution[2]} tasks (${percentHigh}%)`,
1903 | 			{
1904 | 				padding: 1,
1905 | 				borderColor: 'cyan',
1906 | 				borderStyle: 'round',
1907 | 				margin: { top: 1, bottom: 1 }
1908 | 			}
1909 | 		)
1910 | 	);
1911 | 
1912 | 	// Get terminal width
1913 | 	const terminalWidth = process.stdout.columns || 100; // Default to 100 if can't detect
1914 | 
1915 | 	// Calculate dynamic column widths
1916 | 	const idWidth = 12;
1917 | 	const titleWidth = Math.floor(terminalWidth * 0.25); // 25% of width
1918 | 	const scoreWidth = 8;
1919 | 	const subtasksWidth = 8;
1920 | 	// Command column gets the remaining space (minus some buffer for borders)
1921 | 	const commandWidth =
1922 | 		terminalWidth - idWidth - titleWidth - scoreWidth - subtasksWidth - 10;
1923 | 
1924 | 	// Create table with new column widths and word wrapping
1925 | 	const complexTable = new Table({
1926 | 		head: [
1927 | 			chalk.yellow.bold('ID'),
1928 | 			chalk.yellow.bold('Title'),
1929 | 			chalk.yellow.bold('Score'),
1930 | 			chalk.yellow.bold('Subtasks'),
1931 | 			chalk.yellow.bold('Expansion Command')
1932 | 		],
1933 | 		colWidths: [idWidth, titleWidth, scoreWidth, subtasksWidth, commandWidth],
1934 | 		style: { head: [], border: [] },
1935 | 		wordWrap: true,
1936 | 		wrapOnWordBoundary: true
1937 | 	});
1938 | 
1939 | 	// When adding rows, don't truncate the expansion command
1940 | 	tasksNeedingExpansion.forEach((task) => {
1941 | 		const expansionCommand = `task-master expand --id=${task.taskId} --num=${task.recommendedSubtasks}${task.expansionPrompt ? ` --prompt="${task.expansionPrompt}"` : ''}`;
1942 | 
1943 | 		complexTable.push([
1944 | 			task.taskId,
1945 | 			truncate(task.taskTitle, titleWidth - 3), // Still truncate title for readability
1946 | 			getComplexityWithColor(task.complexityScore),
1947 | 			task.recommendedSubtasks,
1948 | 			chalk.cyan(expansionCommand) // Don't truncate - allow wrapping
1949 | 		]);
1950 | 	});
1951 | 
1952 | 	console.log(complexTable.toString());
1953 | 
1954 | 	// Create table for simple tasks
1955 | 	if (simpleTasks.length > 0) {
1956 | 		console.log(
1957 | 			boxen(chalk.green.bold(`Simple Tasks (${simpleTasks.length})`), {
1958 | 				padding: { left: 2, right: 2, top: 0, bottom: 0 },
1959 | 				margin: { top: 1, bottom: 0 },
1960 | 				borderColor: 'green',
1961 | 				borderStyle: 'round'
1962 | 			})
1963 | 		);
1964 | 
1965 | 		const simpleTable = new Table({
1966 | 			head: [
1967 | 				chalk.green.bold('ID'),
1968 | 				chalk.green.bold('Title'),
1969 | 				chalk.green.bold('Score'),
1970 | 				chalk.green.bold('Reasoning')
1971 | 			],
1972 | 			colWidths: [5, 40, 8, 50],
1973 | 			style: { head: [], border: [] }
1974 | 		});
1975 | 
1976 | 		simpleTasks.forEach((task) => {
1977 | 			simpleTable.push([
1978 | 				task.taskId,
1979 | 				truncate(task.taskTitle, 37),
1980 | 				getComplexityWithColor(task.complexityScore),
1981 | 				truncate(task.reasoning, 47)
1982 | 			]);
1983 | 		});
1984 | 
1985 | 		console.log(simpleTable.toString());
1986 | 	}
1987 | 
1988 | 	// Show action suggestions
1989 | 	console.log(
1990 | 		boxen(
1991 | 			chalk.white.bold('Suggested Actions:') +
1992 | 				'\n\n' +
1993 | 				`${chalk.cyan('1.')} Expand all complex tasks: ${chalk.yellow(`task-master expand --all`)}\n` +
1994 | 				`${chalk.cyan('2.')} Expand a specific task: ${chalk.yellow(`task-master expand --id=<id>`)}\n` +
1995 | 				`${chalk.cyan('3.')} Regenerate with research: ${chalk.yellow(`task-master analyze-complexity --research`)}`,
1996 | 			{
1997 | 				padding: 1,
1998 | 				borderColor: 'cyan',
1999 | 				borderStyle: 'round',
2000 | 				margin: { top: 1 }
2001 | 			}
2002 | 		)
2003 | 	);
2004 | }
2005 | 
2006 | /**
2007 |  * Generate a prompt for complexity analysis
2008 |  * @param {Object} tasksData - Tasks data object containing tasks array
2009 |  * @returns {string} Generated prompt
2010 |  */
2011 | function generateComplexityAnalysisPrompt(tasksData) {
2012 | 	const defaultSubtasks = getDefaultSubtasks(null); // Use the getter
2013 | 	return `Analyze the complexity of the following tasks and provide recommendations for subtask breakdown:
2014 | 
2015 | ${tasksData.tasks
2016 | 	.map(
2017 | 		(task) => `
2018 | Task ID: ${task.id}
2019 | Title: ${task.title}
2020 | Description: ${task.description}
2021 | Details: ${task.details}
2022 | Dependencies: ${JSON.stringify(task.dependencies || [])}
2023 | Priority: ${task.priority || 'medium'}
2024 | `
2025 | 	)
2026 | 	.join('\n---\n')}
2027 | 
2028 | Analyze each task and return a JSON array with the following structure for each task:
2029 | [
2030 |   {
2031 |     "taskId": number,
2032 |     "taskTitle": string,
2033 |     "complexityScore": number (1-10),
2034 |     "recommendedSubtasks": number (${Math.max(3, defaultSubtasks - 1)}-${Math.min(8, defaultSubtasks + 2)}),
2035 |     "expansionPrompt": string (a specific prompt for generating good subtasks),
2036 |     "reasoning": string (brief explanation of your assessment)
2037 |   },
2038 |   ...
2039 | ]
2040 | 
2041 | IMPORTANT: Make sure to include an analysis for EVERY task listed above, with the correct taskId matching each task's ID.
2042 | `;
2043 | }
2044 | 
2045 | /**
2046 |  * Confirm overwriting existing tasks.json file
2047 |  * @param {string} tasksPath - Path to the tasks.json file
2048 |  * @returns {Promise<boolean>} - Promise resolving to true if user confirms, false otherwise
2049 |  */
2050 | async function confirmTaskOverwrite(tasksPath) {
2051 | 	console.log(
2052 | 		boxen(
2053 | 			chalk.yellow(
2054 | 				"It looks like you've already generated tasks for this project.\n"
2055 | 			) +
2056 | 				chalk.yellow(
2057 | 					'Executing this command will overwrite any existing tasks.'
2058 | 				),
2059 | 			{
2060 | 				padding: 1,
2061 | 				borderColor: 'yellow',
2062 | 				borderStyle: 'round',
2063 | 				margin: { top: 1 }
2064 | 			}
2065 | 		)
2066 | 	);
2067 | 
2068 | 	const rl = readline.createInterface({
2069 | 		input: process.stdin,
2070 | 		output: process.stdout
2071 | 	});
2072 | 
2073 | 	const answer = await new Promise((resolve) => {
2074 | 		rl.question(
2075 | 			chalk.cyan('Are you sure you wish to continue? (y/N): '),
2076 | 			resolve
2077 | 		);
2078 | 	});
2079 | 	rl.close();
2080 | 
2081 | 	return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
2082 | }
2083 | 
2084 | /**
2085 |  * Displays the API key status for different providers.
2086 |  * @param {Array<{provider: string, cli: boolean, mcp: boolean}>} statusReport - The report generated by getApiKeyStatusReport.
2087 |  */
2088 | function displayApiKeyStatus(statusReport) {
2089 | 	if (!statusReport || statusReport.length === 0) {
2090 | 		console.log(chalk.yellow('No API key status information available.'));
2091 | 		return;
2092 | 	}
2093 | 
2094 | 	const table = new Table({
2095 | 		head: [
2096 | 			chalk.cyan('Provider'),
2097 | 			chalk.cyan('CLI Key (.env)'),
2098 | 			chalk.cyan('MCP Key (mcp.json)')
2099 | 		],
2100 | 		colWidths: [15, 20, 25],
2101 | 		chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' }
2102 | 	});
2103 | 
2104 | 	statusReport.forEach(({ provider, cli, mcp }) => {
2105 | 		const cliStatus = cli ? chalk.green('✅ Found') : chalk.red('❌ Missing');
2106 | 		const mcpStatus = mcp ? chalk.green('✅ Found') : chalk.red('❌ Missing');
2107 | 		// Capitalize provider name for display
2108 | 		const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
2109 | 		table.push([providerName, cliStatus, mcpStatus]);
2110 | 	});
2111 | 
2112 | 	console.log(chalk.bold('\n🔑 API Key Status:'));
2113 | 	console.log(table.toString());
2114 | 	console.log(
2115 | 		chalk.gray(
2116 | 			`  Note: Some providers (e.g., Azure, Ollama) may require additional endpoint configuration in ${TASKMASTER_CONFIG_FILE}.`
2117 | 		)
2118 | 	);
2119 | }
2120 | 
2121 | // --- Formatting Helpers (Potentially move some to utils.js if reusable) ---
2122 | 
2123 | const formatSweScoreWithTertileStars = (score, allModels) => {
2124 | 	// ... (Implementation from previous version or refine) ...
2125 | 	if (score === null || score === undefined || score <= 0) return 'N/A';
2126 | 	const formattedPercentage = `${(score * 100).toFixed(1)}%`;
2127 | 
2128 | 	const validScores = allModels
2129 | 		.map((m) => m.sweScore)
2130 | 		.filter((s) => s !== null && s !== undefined && s > 0);
2131 | 	const sortedScores = [...validScores].sort((a, b) => b - a);
2132 | 	const n = sortedScores.length;
2133 | 	let stars = chalk.gray('☆☆☆');
2134 | 
2135 | 	if (n > 0) {
2136 | 		const topThirdIndex = Math.max(0, Math.floor(n / 3) - 1);
2137 | 		const midThirdIndex = Math.max(0, Math.floor((2 * n) / 3) - 1);
2138 | 		if (score >= sortedScores[topThirdIndex]) stars = chalk.yellow('★★★');
2139 | 		else if (score >= sortedScores[midThirdIndex])
2140 | 			stars = chalk.yellow('★★') + chalk.gray('☆');
2141 | 		else stars = chalk.yellow('★') + chalk.gray('☆☆');
2142 | 	}
2143 | 	return `${formattedPercentage} ${stars}`;
2144 | };
2145 | 
2146 | const formatCost = (costObj) => {
2147 | 	// ... (Implementation from previous version or refine) ...
2148 | 	if (!costObj) return 'N/A';
2149 | 	if (costObj.input === 0 && costObj.output === 0) {
2150 | 		return chalk.green('Free');
2151 | 	}
2152 | 	const formatSingleCost = (costValue) => {
2153 | 		if (costValue === null || costValue === undefined) return 'N/A';
2154 | 		const isInteger = Number.isInteger(costValue);
2155 | 		return `$${costValue.toFixed(isInteger ? 0 : 2)}`;
2156 | 	};
2157 | 	return `${formatSingleCost(costObj.input)} in, ${formatSingleCost(costObj.output)} out`;
2158 | };
2159 | 
2160 | // --- Display Functions ---
2161 | 
2162 | /**
2163 |  * Displays the currently configured active models.
2164 |  * @param {ConfigData} configData - The active configuration data.
2165 |  * @param {AvailableModel[]} allAvailableModels - Needed for SWE score tertiles.
2166 |  */
2167 | function displayModelConfiguration(configData, allAvailableModels = []) {
2168 | 	console.log(chalk.cyan.bold('\nActive Model Configuration:'));
2169 | 	const active = configData.activeModels;
2170 | 	const activeTable = new Table({
2171 | 		head: [
2172 | 			'Role',
2173 | 			'Provider',
2174 | 			'Model ID',
2175 | 			'SWE Score',
2176 | 			'Cost ($/1M tkns)'
2177 | 			// 'API Key Status' // Removed, handled by separate displayApiKeyStatus
2178 | 		].map((h) => chalk.cyan.bold(h)),
2179 | 		colWidths: [10, 14, 30, 18, 20 /*, 28 */], // Adjusted widths
2180 | 		style: { head: ['cyan', 'bold'] }
2181 | 	});
2182 | 
2183 | 	activeTable.push([
2184 | 		chalk.white('Main'),
2185 | 		active.main.provider,
2186 | 		active.main.modelId,
2187 | 		formatSweScoreWithTertileStars(active.main.sweScore, allAvailableModels),
2188 | 		formatCost(active.main.cost)
2189 | 		// getCombinedStatus(active.main.keyStatus) // Removed
2190 | 	]);
2191 | 	activeTable.push([
2192 | 		chalk.white('Research'),
2193 | 		active.research.provider,
2194 | 		active.research.modelId,
2195 | 		formatSweScoreWithTertileStars(
2196 | 			active.research.sweScore,
2197 | 			allAvailableModels
2198 | 		),
2199 | 		formatCost(active.research.cost)
2200 | 		// getCombinedStatus(active.research.keyStatus) // Removed
2201 | 	]);
2202 | 	if (active.fallback && active.fallback.provider && active.fallback.modelId) {
2203 | 		activeTable.push([
2204 | 			chalk.white('Fallback'),
2205 | 			active.fallback.provider,
2206 | 			active.fallback.modelId,
2207 | 			formatSweScoreWithTertileStars(
2208 | 				active.fallback.sweScore,
2209 | 				allAvailableModels
2210 | 			),
2211 | 			formatCost(active.fallback.cost)
2212 | 			// getCombinedStatus(active.fallback.keyStatus) // Removed
2213 | 		]);
2214 | 	} else {
2215 | 		activeTable.push([
2216 | 			chalk.white('Fallback'),
2217 | 			chalk.gray('-'),
2218 | 			chalk.gray('(Not Set)'),
2219 | 			chalk.gray('-'),
2220 | 			chalk.gray('-')
2221 | 			// chalk.gray('-') // Removed
2222 | 		]);
2223 | 	}
2224 | 	console.log(activeTable.toString());
2225 | }
2226 | 
2227 | /**
2228 |  * Displays the list of available models not currently configured.
2229 |  * @param {AvailableModel[]} availableModels - List of available models.
2230 |  */
2231 | function displayAvailableModels(availableModels) {
2232 | 	if (!availableModels || availableModels.length === 0) {
2233 | 		console.log(
2234 | 			chalk.gray('\n(No other models available or all are configured)')
2235 | 		);
2236 | 		return;
2237 | 	}
2238 | 
2239 | 	console.log(chalk.cyan.bold('\nOther Available Models:'));
2240 | 	const availableTable = new Table({
2241 | 		head: ['Provider', 'Model ID', 'SWE Score', 'Cost ($/1M tkns)'].map((h) =>
2242 | 			chalk.cyan.bold(h)
2243 | 		),
2244 | 		colWidths: [15, 40, 18, 25],
2245 | 		style: { head: ['cyan', 'bold'] }
2246 | 	});
2247 | 
2248 | 	availableModels.forEach((model) => {
2249 | 		availableTable.push([
2250 | 			model.provider,
2251 | 			model.modelId,
2252 | 			formatSweScoreWithTertileStars(model.sweScore, availableModels), // Pass itself for comparison
2253 | 			formatCost(model.cost)
2254 | 		]);
2255 | 	});
2256 | 	console.log(availableTable.toString());
2257 | 
2258 | 	// --- Suggested Actions Section (moved here from models command) ---
2259 | 	console.log(
2260 | 		boxen(
2261 | 			chalk.white.bold('Next Steps:') +
2262 | 				'\n' +
2263 | 				chalk.cyan(
2264 | 					`1. Set main model: ${chalk.yellow('task-master models --set-main <model_id>')}`
2265 | 				) +
2266 | 				'\n' +
2267 | 				chalk.cyan(
2268 | 					`2. Set research model: ${chalk.yellow('task-master models --set-research <model_id>')}`
2269 | 				) +
2270 | 				'\n' +
2271 | 				chalk.cyan(
2272 | 					`3. Set fallback model: ${chalk.yellow('task-master models --set-fallback <model_id>')}`
2273 | 				) +
2274 | 				'\n' +
2275 | 				chalk.cyan(
2276 | 					`4. Run interactive setup: ${chalk.yellow('task-master models --setup')}`
2277 | 				) +
2278 | 				'\n' +
2279 | 				chalk.cyan(
2280 | 					`5. Use custom ollama/openrouter models: ${chalk.yellow('task-master models --openrouter|ollama --set-main|research|fallback <model_id>')}`
2281 | 				),
2282 | 			{
2283 | 				padding: 1,
2284 | 				borderColor: 'yellow',
2285 | 				borderStyle: 'round',
2286 | 				margin: { top: 1 }
2287 | 			}
2288 | 		)
2289 | 	);
2290 | }
2291 | 
2292 | /**
2293 |  * Displays AI usage telemetry summary in the CLI.
2294 |  * @param {object} telemetryData - The telemetry data object.
2295 |  * @param {string} outputType - 'cli' or 'mcp' (though typically only called for 'cli').
2296 |  */
2297 | function displayAiUsageSummary(telemetryData, outputType = 'cli') {
2298 | 	if (
2299 | 		(outputType !== 'cli' && outputType !== 'text') ||
2300 | 		!telemetryData ||
2301 | 		isSilentMode()
2302 | 	) {
2303 | 		return; // Only display for CLI and if data exists and not in silent mode
2304 | 	}
2305 | 
2306 | 	const {
2307 | 		modelUsed,
2308 | 		providerName,
2309 | 		inputTokens,
2310 | 		outputTokens,
2311 | 		totalTokens,
2312 | 		totalCost,
2313 | 		commandName
2314 | 	} = telemetryData;
2315 | 
2316 | 	let summary = chalk.bold.blue('AI Usage Summary:') + '\n';
2317 | 	summary += chalk.gray(`  Command: ${commandName}\n`);
2318 | 	summary += chalk.gray(`  Provider: ${providerName}\n`);
2319 | 	summary += chalk.gray(`  Model: ${modelUsed}\n`);
2320 | 	summary += chalk.gray(
2321 | 		`  Tokens: ${totalTokens} (Input: ${inputTokens}, Output: ${outputTokens})\n`
2322 | 	);
2323 | 	summary += chalk.gray(`  Est. Cost: $${totalCost.toFixed(6)}`);
2324 | 
2325 | 	console.log(
2326 | 		boxen(summary, {
2327 | 			padding: 1,
2328 | 			margin: { top: 1 },
2329 | 			borderColor: 'blue',
2330 | 			borderStyle: 'round',
2331 | 			title: '💡 Telemetry',
2332 | 			titleAlignment: 'center'
2333 | 		})
2334 | 	);
2335 | }
2336 | 
2337 | /**
2338 |  * Display multiple tasks in a compact summary format with interactive drill-down
2339 |  * @param {string} tasksPath - Path to the tasks.json file
2340 |  * @param {Array<string>} taskIds - Array of task IDs to display
2341 |  * @param {string} complexityReportPath - Path to complexity report
2342 |  * @param {string} statusFilter - Optional status filter for subtasks
2343 |  * @param {Object} context - Context object containing projectRoot and tag
2344 |  * @param {string} [context.projectRoot] - Project root path
2345 |  * @param {string} [context.tag] - Tag for the task
2346 |  */
2347 | async function displayMultipleTasksSummary(
2348 | 	tasksPath,
2349 | 	taskIds,
2350 | 	complexityReportPath = null,
2351 | 	statusFilter = null,
2352 | 	context = {}
2353 | ) {
2354 | 	displayBanner();
2355 | 
2356 | 	// Extract projectRoot and tag from context
2357 | 	const projectRoot = context.projectRoot || null;
2358 | 	const tag = context.tag || null;
2359 | 
2360 | 	// Read the tasks file with proper projectRoot for tag resolution
2361 | 	const data = readJSON(tasksPath, projectRoot, tag);
2362 | 	if (!data || !data.tasks) {
2363 | 		log('error', 'No valid tasks found.');
2364 | 		process.exit(1);
2365 | 	}
2366 | 
2367 | 	// Read complexity report once
2368 | 	const complexityReport = readComplexityReport(complexityReportPath);
2369 | 
2370 | 	// Find all requested tasks
2371 | 	const foundTasks = [];
2372 | 	const notFoundIds = [];
2373 | 
2374 | 	taskIds.forEach((id) => {
2375 | 		const { task } = findTaskById(
2376 | 			data.tasks,
2377 | 			id,
2378 | 			complexityReport,
2379 | 			statusFilter
2380 | 		);
2381 | 		if (task) {
2382 | 			foundTasks.push(task);
2383 | 		} else {
2384 | 			notFoundIds.push(id);
2385 | 		}
2386 | 	});
2387 | 
2388 | 	// Show not found tasks
2389 | 	if (notFoundIds.length > 0) {
2390 | 		console.log(
2391 | 			boxen(chalk.yellow(`Tasks not found: ${notFoundIds.join(', ')}`), {
2392 | 				padding: { top: 0, bottom: 0, left: 1, right: 1 },
2393 | 				borderColor: 'yellow',
2394 | 				borderStyle: 'round',
2395 | 				margin: { top: 1, bottom: 1 }
2396 | 			})
2397 | 		);
2398 | 	}
2399 | 
2400 | 	if (foundTasks.length === 0) {
2401 | 		console.log(
2402 | 			boxen(chalk.red('No valid tasks found to display'), {
2403 | 				padding: { top: 0, bottom: 0, left: 1, right: 1 },
2404 | 				borderColor: 'red',
2405 | 				borderStyle: 'round',
2406 | 				margin: { top: 1 }
2407 | 			})
2408 | 		);
2409 | 		return;
2410 | 	}
2411 | 
2412 | 	// Display header
2413 | 	console.log(
2414 | 		boxen(
2415 | 			chalk.white.bold(
2416 | 				`Task Summary (${foundTasks.length} task${foundTasks.length === 1 ? '' : 's'})`
2417 | 			),
2418 | 			{
2419 | 				padding: { top: 0, bottom: 0, left: 1, right: 1 },
2420 | 				borderColor: 'blue',
2421 | 				borderStyle: 'round',
2422 | 				margin: { top: 1, bottom: 0 }
2423 | 			}
2424 | 		)
2425 | 	);
2426 | 
2427 | 	// Calculate terminal width for responsive layout
2428 | 	const terminalWidth = process.stdout.columns || 100;
2429 | 	const availableWidth = terminalWidth - 10;
2430 | 
2431 | 	// Create compact summary table
2432 | 	const summaryTable = new Table({
2433 | 		head: [
2434 | 			chalk.cyan.bold('ID'),
2435 | 			chalk.cyan.bold('Title'),
2436 | 			chalk.cyan.bold('Status'),
2437 | 			chalk.cyan.bold('Priority'),
2438 | 			chalk.cyan.bold('Subtasks'),
2439 | 			chalk.cyan.bold('Progress')
2440 | 		],
2441 | 		colWidths: [
2442 | 			Math.floor(availableWidth * 0.08), // ID: 8%
2443 | 			Math.floor(availableWidth * 0.35), // Title: 35%
2444 | 			Math.floor(availableWidth * 0.12), // Status: 12%
2445 | 			Math.floor(availableWidth * 0.1), // Priority: 10%
2446 | 			Math.floor(availableWidth * 0.15), // Subtasks: 15%
2447 | 			Math.floor(availableWidth * 0.2) // Progress: 20%
2448 | 		],
2449 | 		style: {
2450 | 			head: [],
2451 | 			border: [],
2452 | 			'padding-top': 0,
2453 | 			'padding-bottom': 0,
2454 | 			compact: true
2455 | 		},
2456 | 		chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' },
2457 | 		wordWrap: true
2458 | 	});
2459 | 
2460 | 	// Add each task to the summary table
2461 | 	foundTasks.forEach((task) => {
2462 | 		// Handle subtask case
2463 | 		if (task.isSubtask || task.parentTask) {
2464 | 			const parentId = task.parentTask ? task.parentTask.id : 'Unknown';
2465 | 			summaryTable.push([
2466 | 				`${parentId}.${task.id}`,
2467 | 				truncate(task.title, Math.floor(availableWidth * 0.35) - 3),
2468 | 				getStatusWithColor(task.status || 'pending', true),
2469 | 				chalk.gray('(subtask)'),
2470 | 				chalk.gray('N/A'),
2471 | 				chalk.gray('N/A')
2472 | 			]);
2473 | 			return;
2474 | 		}
2475 | 
2476 | 		// Handle regular task
2477 | 		const priorityColors = {
2478 | 			high: chalk.red.bold,
2479 | 			medium: chalk.yellow,
2480 | 			low: chalk.gray
2481 | 		};
2482 | 		const priorityColor =
2483 | 			priorityColors[task.priority || 'medium'] || chalk.white;
2484 | 
2485 | 		// Calculate subtask summary
2486 | 		let subtaskSummary = chalk.gray('None');
2487 | 		let progressBar = chalk.gray('N/A');
2488 | 
2489 | 		if (task.subtasks && task.subtasks.length > 0) {
2490 | 			const total = task.subtasks.length;
2491 | 			const completed = task.subtasks.filter(
2492 | 				(st) => st.status === 'done' || st.status === 'completed'
2493 | 			).length;
2494 | 			const inProgress = task.subtasks.filter(
2495 | 				(st) => st.status === 'in-progress'
2496 | 			).length;
2497 | 			const pending = task.subtasks.filter(
2498 | 				(st) => st.status === 'pending'
2499 | 			).length;
2500 | 
2501 | 			// Compact subtask count with status indicators
2502 | 			subtaskSummary = `${chalk.green(completed)}/${total}`;
2503 | 			if (inProgress > 0)
2504 | 				subtaskSummary += ` ${chalk.hex('#FFA500')(`+${inProgress}`)}`;
2505 | 			if (pending > 0) subtaskSummary += ` ${chalk.yellow(`(${pending})`)}`;
2506 | 
2507 | 			// Mini progress bar (shorter than usual)
2508 | 			const completionPercentage = (completed / total) * 100;
2509 | 			const barLength = 8; // Compact bar
2510 | 			const statusBreakdown = {
2511 | 				'in-progress': (inProgress / total) * 100,
2512 | 				pending: (pending / total) * 100
2513 | 			};
2514 | 			progressBar = createProgressBar(
2515 | 				completionPercentage,
2516 | 				barLength,
2517 | 				statusBreakdown
2518 | 			);
2519 | 		}
2520 | 
2521 | 		summaryTable.push([
2522 | 			task.id.toString(),
2523 | 			truncate(task.title, Math.floor(availableWidth * 0.35) - 3),
2524 | 			getStatusWithColor(task.status || 'pending', true),
2525 | 			priorityColor(task.priority || 'medium'),
2526 | 			subtaskSummary,
2527 | 			progressBar
2528 | 		]);
2529 | 	});
2530 | 
2531 | 	console.log(summaryTable.toString());
2532 | 
2533 | 	// Interactive drill-down prompt
2534 | 	if (foundTasks.length > 1) {
2535 | 		console.log(
2536 | 			boxen(
2537 | 				chalk.white.bold('Interactive Options:') +
2538 | 					'\n' +
2539 | 					chalk.cyan('• Press Enter to view available actions for all tasks') +
2540 | 					'\n' +
2541 | 					chalk.cyan(
2542 | 						'• Type a task ID (e.g., "3" or "3.2") to view that specific task'
2543 | 					) +
2544 | 					'\n' +
2545 | 					chalk.cyan('• Type "q" to quit'),
2546 | 				{
2547 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
2548 | 					borderColor: 'green',
2549 | 					borderStyle: 'round',
2550 | 					margin: { top: 1 }
2551 | 				}
2552 | 			)
2553 | 		);
2554 | 
2555 | 		const rl = readline.createInterface({
2556 | 			input: process.stdin,
2557 | 			output: process.stdout
2558 | 		});
2559 | 
2560 | 		const choice = await new Promise((resolve) => {
2561 | 			rl.question(chalk.cyan('Your choice: '), resolve);
2562 | 		});
2563 | 		rl.close();
2564 | 
2565 | 		if (choice.toLowerCase() === 'q') {
2566 | 			return;
2567 | 		} else if (choice.trim() === '') {
2568 | 			// Show action menu for selected tasks
2569 | 			console.log(
2570 | 				boxen(
2571 | 					chalk.white.bold('Available Actions for Selected Tasks:') +
2572 | 						'\n' +
2573 | 						chalk.cyan('1.') +
2574 | 						' Mark all as in-progress' +
2575 | 						'\n' +
2576 | 						chalk.cyan('2.') +
2577 | 						' Mark all as done' +
2578 | 						'\n' +
2579 | 						chalk.cyan('3.') +
2580 | 						' Show next available task' +
2581 | 						'\n' +
2582 | 						chalk.cyan('4.') +
2583 | 						' Expand all tasks (generate subtasks)' +
2584 | 						'\n' +
2585 | 						chalk.cyan('5.') +
2586 | 						' View dependency relationships' +
2587 | 						'\n' +
2588 | 						chalk.cyan('6.') +
2589 | 						' Generate task files' +
2590 | 						'\n' +
2591 | 						chalk.gray('Or type a task ID to view details'),
2592 | 					{
2593 | 						padding: { top: 0, bottom: 0, left: 1, right: 1 },
2594 | 						borderColor: 'blue',
2595 | 						borderStyle: 'round',
2596 | 						margin: { top: 1 }
2597 | 					}
2598 | 				)
2599 | 			);
2600 | 
2601 | 			const rl2 = readline.createInterface({
2602 | 				input: process.stdin,
2603 | 				output: process.stdout
2604 | 			});
2605 | 
2606 | 			const actionChoice = await new Promise((resolve) => {
2607 | 				rl2.question(chalk.cyan('Choose action (1-6): '), resolve);
2608 | 			});
2609 | 			rl2.close();
2610 | 
2611 | 			const taskIdList = foundTasks.map((t) => t.id).join(',');
2612 | 
2613 | 			switch (actionChoice.trim()) {
2614 | 				case '1':
2615 | 					console.log(
2616 | 						chalk.blue(
2617 | 							`\n→ Command: task-master set-status --id=${taskIdList} --status=in-progress`
2618 | 						)
2619 | 					);
2620 | 					console.log(
2621 | 						chalk.green(
2622 | 							'✓ Copy and run this command to mark all tasks as in-progress'
2623 | 						)
2624 | 					);
2625 | 					break;
2626 | 				case '2':
2627 | 					console.log(
2628 | 						chalk.blue(
2629 | 							`\n→ Command: task-master set-status --id=${taskIdList} --status=done`
2630 | 						)
2631 | 					);
2632 | 					console.log(
2633 | 						chalk.green('✓ Copy and run this command to mark all tasks as done')
2634 | 					);
2635 | 					break;
2636 | 				case '3':
2637 | 					console.log(chalk.blue(`\n→ Command: task-master next`));
2638 | 					console.log(
2639 | 						chalk.green(
2640 | 							'✓ Copy and run this command to see the next available task'
2641 | 						)
2642 | 					);
2643 | 					break;
2644 | 				case '4':
2645 | 					console.log(
2646 | 						chalk.blue(
2647 | 							`\n→ Command: task-master expand --id=${taskIdList} --research`
2648 | 						)
2649 | 					);
2650 | 					console.log(
2651 | 						chalk.green(
2652 | 							'✓ Copy and run this command to expand all selected tasks into subtasks'
2653 | 						)
2654 | 					);
2655 | 					break;
2656 | 				case '5': {
2657 | 					// Show dependency visualization
2658 | 					console.log(chalk.white.bold('\nDependency Relationships:'));
2659 | 					let hasDependencies = false;
2660 | 					foundTasks.forEach((task) => {
2661 | 						if (task.dependencies && task.dependencies.length > 0) {
2662 | 							console.log(
2663 | 								chalk.cyan(
2664 | 									`Task ${task.id} depends on: ${task.dependencies.join(', ')}`
2665 | 								)
2666 | 							);
2667 | 							hasDependencies = true;
2668 | 						}
2669 | 					});
2670 | 					if (!hasDependencies) {
2671 | 						console.log(chalk.gray('No dependencies found for selected tasks'));
2672 | 					}
2673 | 					break;
2674 | 				}
2675 | 				case '6':
2676 | 					console.log(chalk.blue(`\n→ Command: task-master generate`));
2677 | 					console.log(
2678 | 						chalk.green('✓ Copy and run this command to generate task files')
2679 | 					);
2680 | 					break;
2681 | 				default:
2682 | 					if (actionChoice.trim().length > 0) {
2683 | 						console.log(chalk.yellow(`Invalid choice: ${actionChoice.trim()}`));
2684 | 						console.log(chalk.gray('Please choose 1-6 or type a task ID'));
2685 | 					}
2686 | 			}
2687 | 		} else {
2688 | 			// Show specific task
2689 | 			await displayTaskById(
2690 | 				tasksPath,
2691 | 				choice.trim(),
2692 | 				complexityReportPath,
2693 | 				statusFilter,
2694 | 				context
2695 | 			);
2696 | 		}
2697 | 	} else {
2698 | 		// Single task - show suggested actions
2699 | 		const task = foundTasks[0];
2700 | 		console.log(
2701 | 			boxen(
2702 | 				chalk.white.bold('Suggested Actions:') +
2703 | 					'\n' +
2704 | 					`${chalk.cyan('1.')} View full details: ${chalk.yellow(`task-master show ${task.id}`)}\n` +
2705 | 					`${chalk.cyan('2.')} Mark as in-progress: ${chalk.yellow(`task-master set-status --id=${task.id} --status=in-progress`)}\n` +
2706 | 					`${chalk.cyan('3.')} Mark as done: ${chalk.yellow(`task-master set-status --id=${task.id} --status=done`)}`,
2707 | 				{
2708 | 					padding: { top: 0, bottom: 0, left: 1, right: 1 },
2709 | 					borderColor: 'green',
2710 | 					borderStyle: 'round',
2711 | 					margin: { top: 1 }
2712 | 				}
2713 | 			)
2714 | 		);
2715 | 	}
2716 | }
2717 | 
2718 | /**
2719 |  * Display context analysis results with beautiful formatting
2720 |  * @param {Object} analysisData - Analysis data from ContextGatherer
2721 |  * @param {string} semanticQuery - The original query used for semantic search
2722 |  * @param {number} contextSize - Size of gathered context in characters
2723 |  */
2724 | function displayContextAnalysis(analysisData, semanticQuery, contextSize) {
2725 | 	if (isSilentMode() || !analysisData) return;
2726 | 
2727 | 	const { highRelevance, mediumRelevance, recentTasks, allRelevantTasks } =
2728 | 		analysisData;
2729 | 
2730 | 	// Create the context analysis display
2731 | 	let analysisContent = chalk.white.bold('Context Analysis') + '\n\n';
2732 | 
2733 | 	// Query info
2734 | 	analysisContent +=
2735 | 		chalk.gray('Query: ') + chalk.white(`"${semanticQuery}"`) + '\n';
2736 | 	analysisContent +=
2737 | 		chalk.gray('Context size: ') +
2738 | 		chalk.cyan(`${contextSize.toLocaleString()} characters`) +
2739 | 		'\n';
2740 | 	analysisContent +=
2741 | 		chalk.gray('Tasks found: ') +
2742 | 		chalk.yellow(`${allRelevantTasks.length} relevant tasks`) +
2743 | 		'\n\n';
2744 | 
2745 | 	// High relevance matches
2746 | 	if (highRelevance.length > 0) {
2747 | 		analysisContent += chalk.green.bold('🎯 High Relevance Matches:') + '\n';
2748 | 		highRelevance.slice(0, 3).forEach((task) => {
2749 | 			analysisContent +=
2750 | 				chalk.green(`  • Task ${task.id}: ${truncate(task.title, 50)}`) + '\n';
2751 | 		});
2752 | 		if (highRelevance.length > 3) {
2753 | 			analysisContent +=
2754 | 				chalk.green(
2755 | 					`  • ... and ${highRelevance.length - 3} more high relevance tasks`
2756 | 				) + '\n';
2757 | 		}
2758 | 		analysisContent += '\n';
2759 | 	}
2760 | 
2761 | 	// Medium relevance matches
2762 | 	if (mediumRelevance.length > 0) {
2763 | 		analysisContent += chalk.yellow.bold('📋 Medium Relevance Matches:') + '\n';
2764 | 		mediumRelevance.slice(0, 3).forEach((task) => {
2765 | 			analysisContent +=
2766 | 				chalk.yellow(`  • Task ${task.id}: ${truncate(task.title, 50)}`) + '\n';
2767 | 		});
2768 | 		if (mediumRelevance.length > 3) {
2769 | 			analysisContent +=
2770 | 				chalk.yellow(
2771 | 					`  • ... and ${mediumRelevance.length - 3} more medium relevance tasks`
2772 | 				) + '\n';
2773 | 		}
2774 | 		analysisContent += '\n';
2775 | 	}
2776 | 
2777 | 	// Recent tasks (if they contributed)
2778 | 	const recentTasksNotInRelevance = recentTasks.filter(
2779 | 		(task) =>
2780 | 			!highRelevance.some((hr) => hr.id === task.id) &&
2781 | 			!mediumRelevance.some((mr) => mr.id === task.id)
2782 | 	);
2783 | 
2784 | 	if (recentTasksNotInRelevance.length > 0) {
2785 | 		analysisContent += chalk.cyan.bold('🕒 Recent Tasks (for context):') + '\n';
2786 | 		recentTasksNotInRelevance.slice(0, 2).forEach((task) => {
2787 | 			analysisContent +=
2788 | 				chalk.cyan(`  • Task ${task.id}: ${truncate(task.title, 50)}`) + '\n';
2789 | 		});
2790 | 		if (recentTasksNotInRelevance.length > 2) {
2791 | 			analysisContent +=
2792 | 				chalk.cyan(
2793 | 					`  • ... and ${recentTasksNotInRelevance.length - 2} more recent tasks`
2794 | 				) + '\n';
2795 | 		}
2796 | 	}
2797 | 
2798 | 	console.log(
2799 | 		boxen(analysisContent, {
2800 | 			padding: { top: 1, bottom: 1, left: 2, right: 2 },
2801 | 			margin: { top: 1, bottom: 0 },
2802 | 			borderStyle: 'round',
2803 | 			borderColor: 'blue',
2804 | 			title: chalk.blue('🔍 Context Gathering'),
2805 | 			titleAlignment: 'center'
2806 | 		})
2807 | 	);
2808 | }
2809 | 
2810 | // Export UI functions
2811 | export {
2812 | 	displayBanner,
2813 | 	displayTaggedTasksFYI,
2814 | 	startLoadingIndicator,
2815 | 	stopLoadingIndicator,
2816 | 	createProgressBar,
2817 | 	getStatusWithColor,
2818 | 	formatDependenciesWithStatus,
2819 | 	displayHelp,
2820 | 	getComplexityWithColor,
2821 | 	displayNextTask,
2822 | 	displayTaskById,
2823 | 	displayComplexityReport,
2824 | 	generateComplexityAnalysisPrompt,
2825 | 	confirmTaskOverwrite,
2826 | 	displayApiKeyStatus,
2827 | 	displayModelConfiguration,
2828 | 	displayAvailableModels,
2829 | 	displayAiUsageSummary,
2830 | 	displayMultipleTasksSummary,
2831 | 	succeedLoadingIndicator,
2832 | 	failLoadingIndicator,
2833 | 	warnLoadingIndicator,
2834 | 	infoLoadingIndicator,
2835 | 	displayContextAnalysis,
2836 | 	displayCurrentTagIndicator,
2837 | 	formatTaskIdForDisplay
2838 | };
2839 | 
2840 | /**
2841 |  * Display enhanced error message for cross-tag dependency conflicts
2842 |  * @param {Array} conflicts - Array of cross-tag dependency conflicts
2843 |  * @param {string} sourceTag - Source tag name
2844 |  * @param {string} targetTag - Target tag name
2845 |  * @param {string} sourceIds - Source task IDs (comma-separated)
2846 |  */
2847 | export function displayCrossTagDependencyError(
2848 | 	conflicts,
2849 | 	sourceTag,
2850 | 	targetTag,
2851 | 	sourceIds
2852 | ) {
2853 | 	console.log(
2854 | 		chalk.red(`\n❌ Cannot move tasks from "${sourceTag}" to "${targetTag}"`)
2855 | 	);
2856 | 	console.log(chalk.yellow(`\nCross-tag dependency conflicts detected:`));
2857 | 
2858 | 	if (conflicts.length > 0) {
2859 | 		conflicts.forEach((conflict) => {
2860 | 			console.log(`  • ${conflict.message}`);
2861 | 		});
2862 | 	}
2863 | 
2864 | 	console.log(chalk.cyan(`\nResolution options:`));
2865 | 	console.log(
2866 | 		`  1. Move with dependencies: task-master move --from=${sourceIds} --from-tag=${sourceTag} --to-tag=${targetTag} --with-dependencies`
2867 | 	);
2868 | 	console.log(
2869 | 		`  2. Break dependencies: task-master move --from=${sourceIds} --from-tag=${sourceTag} --to-tag=${targetTag} --ignore-dependencies`
2870 | 	);
2871 | 	console.log(
2872 | 		`  3. Validate and fix dependencies: task-master validate-dependencies && task-master fix-dependencies`
2873 | 	);
2874 | 	if (conflicts.length > 0) {
2875 | 		console.log(
2876 | 			`  4. Move dependencies first: task-master move --from=${conflicts.map((c) => c.dependencyId).join(',')} --from-tag=${conflicts[0].dependencyTag} --to-tag=${targetTag}`
2877 | 		);
2878 | 	}
2879 | }
2880 | 
2881 | /**
2882 |  * Helper function to format task ID for display, handling edge cases with explicit labels
2883 |  * Builds on the existing formatTaskId utility but adds user-friendly display for edge cases
2884 |  * @param {*} taskId - The task ID to format
2885 |  * @returns {string} Formatted task ID for display
2886 |  */
2887 | function formatTaskIdForDisplay(taskId) {
2888 | 	if (taskId === null) return 'null';
2889 | 	if (taskId === undefined) return 'undefined';
2890 | 	if (taskId === '') return '(empty)';
2891 | 
2892 | 	// Use existing formatTaskId for normal cases, with fallback to 'unknown'
2893 | 	return formatTaskId(taskId) || 'unknown';
2894 | }
2895 | 
2896 | /**
2897 |  * Display enhanced error message for subtask movement restriction
2898 |  * @param {string} taskId - The subtask ID that cannot be moved
2899 |  * @param {string} sourceTag - Source tag name
2900 |  * @param {string} targetTag - Target tag name
2901 |  */
2902 | export function displaySubtaskMoveError(taskId, sourceTag, targetTag) {
2903 | 	// Handle null/undefined taskId but preserve the actual value for display
2904 | 	const displayTaskId = formatTaskIdForDisplay(taskId);
2905 | 
2906 | 	// Safe taskId for operations that need a valid string
2907 | 	const safeTaskId = taskId || 'unknown';
2908 | 
2909 | 	// Validate taskId format before splitting
2910 | 	let parentId = safeTaskId;
2911 | 	if (safeTaskId.includes('.')) {
2912 | 		const parts = safeTaskId.split('.');
2913 | 		// Check if it's a valid subtask format (parentId.subtaskId)
2914 | 		if (parts.length === 2 && parts[0] && parts[1]) {
2915 | 			parentId = parts[0];
2916 | 		} else {
2917 | 			// Invalid format - log warning and use the original taskId
2918 | 			console.log(
2919 | 				chalk.yellow(
2920 | 					`\n⚠️  Warning: Unexpected taskId format "${safeTaskId}". Using as-is for command suggestions.`
2921 | 				)
2922 | 			);
2923 | 			parentId = safeTaskId;
2924 | 		}
2925 | 	}
2926 | 
2927 | 	console.log(
2928 | 		chalk.red(`\n❌ Cannot move subtask ${displayTaskId} directly between tags`)
2929 | 	);
2930 | 	console.log(chalk.yellow(`\nSubtask movement restriction:`));
2931 | 	console.log(`  • Subtasks cannot be moved directly between tags`);
2932 | 	console.log(`  • They must be promoted to full tasks first`);
2933 | 	console.log(`  • Source tag: "${sourceTag}"`);
2934 | 	console.log(`  • Target tag: "${targetTag}"`);
2935 | 
2936 | 	console.log(chalk.cyan(`\nResolution options:`));
2937 | 	console.log(
2938 | 		`  1. Promote subtask to full task: task-master remove-subtask --id=${displayTaskId} --convert`
2939 | 	);
2940 | 	console.log(
2941 | 		`  2. Then move the promoted task: task-master move --from=${parentId} --from-tag=${sourceTag} --to-tag=${targetTag}`
2942 | 	);
2943 | 	console.log(
2944 | 		`  3. Or move the parent task with all subtasks: task-master move --from=${parentId} --from-tag=${sourceTag} --to-tag=${targetTag} --with-dependencies`
2945 | 	);
2946 | }
2947 | 
2948 | /**
2949 |  * Display enhanced error message for invalid tag combinations
2950 |  * @param {string} sourceTag - Source tag name
2951 |  * @param {string} targetTag - Target tag name
2952 |  * @param {string} reason - Reason for the error
2953 |  */
2954 | export function displayInvalidTagCombinationError(
2955 | 	sourceTag,
2956 | 	targetTag,
2957 | 	reason
2958 | ) {
2959 | 	console.log(chalk.red(`\n❌ Invalid tag combination`));
2960 | 	console.log(chalk.yellow(`\nError details:`));
2961 | 	console.log(`  • Source tag: "${sourceTag}"`);
2962 | 	console.log(`  • Target tag: "${targetTag}"`);
2963 | 	console.log(`  • Reason: ${reason}`);
2964 | 
2965 | 	console.log(chalk.cyan(`\nResolution options:`));
2966 | 	console.log(`  1. Use different tags for cross-tag moves`);
2967 | 	console.log(
2968 | 		`  2. Use within-tag move: task-master move --from=<id> --to=<id> --tag=${sourceTag}`
2969 | 	);
2970 | 	console.log(`  3. Check available tags: task-master tags`);
2971 | }
2972 | 
2973 | /**
2974 |  * Display helpful hints for dependency validation commands
2975 |  * @param {string} context - Context for the hints (e.g., 'before-move', 'after-error')
2976 |  */
2977 | export function displayDependencyValidationHints(context = 'general') {
2978 | 	const hints = {
2979 | 		'before-move': [
2980 | 			'💡 Tip: Run "task-master validate-dependencies" to check for dependency issues before moving tasks',
2981 | 			'💡 Tip: Use "task-master fix-dependencies" to automatically resolve common dependency problems',
2982 | 			'💡 Tip: Consider using --with-dependencies flag to move dependent tasks together'
2983 | 		],
2984 | 		'after-error': [
2985 | 			'🔧 Quick fix: Run "task-master validate-dependencies" to identify specific issues',
2986 | 			'🔧 Quick fix: Use "task-master fix-dependencies" to automatically resolve problems',
2987 | 			'🔧 Quick fix: Check "task-master show <id>" to see task dependencies before moving'
2988 | 		],
2989 | 		general: [
2990 | 			'💡 Use "task-master validate-dependencies" to check for dependency issues',
2991 | 			'💡 Use "task-master fix-dependencies" to automatically resolve problems',
2992 | 			'💡 Use "task-master show <id>" to view task dependencies',
2993 | 			'💡 Use --with-dependencies flag to move dependent tasks together'
2994 | 		]
2995 | 	};
2996 | 
2997 | 	const relevantHints = hints[context] || hints.general;
2998 | 
2999 | 	console.log(chalk.cyan(`\nHelpful hints:`));
3000 | 	// Convert to Set to ensure only unique hints are displayed
3001 | 	const uniqueHints = new Set(relevantHints);
3002 | 	uniqueHints.forEach((hint) => {
3003 | 		console.log(`  ${hint}`);
3004 | 	});
3005 | }
3006 | 
```
Page 49/52FirstPrevNextLast