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

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/packages/tm-core/src/modules/storage/adapters/api-storage.ts:
--------------------------------------------------------------------------------

```typescript
   1 | /**
   2 |  * @fileoverview API-based storage implementation using repository pattern
   3 |  * This provides storage via repository abstraction for flexibility
   4 |  */
   5 | 
   6 | import type { SupabaseClient } from '@supabase/supabase-js';
   7 | import {
   8 | 	ERROR_CODES,
   9 | 	TaskMasterError
  10 | } from '../../../common/errors/task-master-error.js';
  11 | import type {
  12 | 	IStorage,
  13 | 	LoadTasksOptions,
  14 | 	StorageStats,
  15 | 	UpdateStatusResult
  16 | } from '../../../common/interfaces/storage.interface.js';
  17 | import { getLogger } from '../../../common/logger/factory.js';
  18 | import type {
  19 | 	Task,
  20 | 	TaskMetadata,
  21 | 	TaskStatus,
  22 | 	TaskTag
  23 | } from '../../../common/types/index.js';
  24 | import { AuthManager } from '../../auth/managers/auth-manager.js';
  25 | import { BriefsDomain } from '../../briefs/briefs-domain.js';
  26 | import {
  27 | 	type ExpandTaskResult,
  28 | 	TaskExpansionService
  29 | } from '../../integration/services/task-expansion.service.js';
  30 | import { TaskRetrievalService } from '../../integration/services/task-retrieval.service.js';
  31 | import { SupabaseRepository } from '../../tasks/repositories/supabase/index.js';
  32 | import type { TaskRepository } from '../../tasks/repositories/task-repository.interface.js';
  33 | import { ApiClient } from '../utils/api-client.js';
  34 | 
  35 | /**
  36 |  * API storage configuration
  37 |  */
  38 | export interface ApiStorageConfig {
  39 | 	/** Supabase client instance */
  40 | 	supabaseClient?: SupabaseClient;
  41 | 	/** Custom repository implementation */
  42 | 	repository?: TaskRepository;
  43 | 	/** Project ID for scoping */
  44 | 	projectId: string;
  45 | 	/** Enable request retries */
  46 | 	enableRetry?: boolean;
  47 | 	/** Maximum retry attempts */
  48 | 	maxRetries?: number;
  49 | }
  50 | 
  51 | /**
  52 |  * Response from the update task with prompt API endpoint
  53 |  */
  54 | interface UpdateTaskWithPromptResponse {
  55 | 	success: boolean;
  56 | 	task: {
  57 | 		id: string;
  58 | 		displayId: string | null;
  59 | 		title: string;
  60 | 		description: string | null;
  61 | 		status: string;
  62 | 		priority: string | null;
  63 | 	};
  64 | 	message: string;
  65 | }
  66 | 
  67 | /**
  68 |  * ApiStorage implementation using repository pattern
  69 |  * Provides flexibility to swap between different backend implementations
  70 |  */
  71 | export class ApiStorage implements IStorage {
  72 | 	private readonly repository: TaskRepository;
  73 | 	private readonly projectId: string;
  74 | 	private readonly enableRetry: boolean;
  75 | 	private readonly maxRetries: number;
  76 | 	private initialized = false;
  77 | 	private tagsCache: Map<string, TaskTag> = new Map();
  78 | 	private apiClient?: ApiClient;
  79 | 	private expansionService?: TaskExpansionService;
  80 | 	private retrievalService?: TaskRetrievalService;
  81 | 	private readonly logger = getLogger('ApiStorage');
  82 | 
  83 | 	constructor(config: ApiStorageConfig) {
  84 | 		this.validateConfig(config);
  85 | 
  86 | 		// Use provided repository or create Supabase repository
  87 | 		if (config.repository) {
  88 | 			this.repository = config.repository;
  89 | 		} else if (config.supabaseClient) {
  90 | 			// TODO: SupabaseRepository doesn't implement all TaskRepository methods yet
  91 | 			// Cast for now until full implementation is complete
  92 | 			this.repository = new SupabaseRepository(
  93 | 				config.supabaseClient
  94 | 			) as unknown as TaskRepository;
  95 | 		} else {
  96 | 			throw new TaskMasterError(
  97 | 				'Either repository or supabaseClient must be provided',
  98 | 				ERROR_CODES.MISSING_CONFIGURATION
  99 | 			);
 100 | 		}
 101 | 
 102 | 		this.projectId = config.projectId;
 103 | 		this.enableRetry = config.enableRetry ?? true;
 104 | 		this.maxRetries = config.maxRetries ?? 3;
 105 | 	}
 106 | 
 107 | 	/**
 108 | 	 * Validate API storage configuration
 109 | 	 */
 110 | 	private validateConfig(config: ApiStorageConfig): void {
 111 | 		if (!config.projectId) {
 112 | 			throw new TaskMasterError(
 113 | 				'Project ID is required for API storage',
 114 | 				ERROR_CODES.MISSING_CONFIGURATION
 115 | 			);
 116 | 		}
 117 | 
 118 | 		if (!config.repository && !config.supabaseClient) {
 119 | 			throw new TaskMasterError(
 120 | 				'Either repository or supabaseClient must be provided',
 121 | 				ERROR_CODES.MISSING_CONFIGURATION
 122 | 			);
 123 | 		}
 124 | 	}
 125 | 
 126 | 	/**
 127 | 	 * Initialize the API storage
 128 | 	 */
 129 | 	async initialize(): Promise<void> {
 130 | 		if (this.initialized) return;
 131 | 
 132 | 		try {
 133 | 			// Load initial tags
 134 | 			await this.loadTagsIntoCache();
 135 | 			this.initialized = true;
 136 | 		} catch (error) {
 137 | 			throw new TaskMasterError(
 138 | 				'Failed to initialize API storage',
 139 | 				ERROR_CODES.STORAGE_ERROR,
 140 | 				{ operation: 'initialize' },
 141 | 				error as Error
 142 | 			);
 143 | 		}
 144 | 	}
 145 | 
 146 | 	/**
 147 | 	 * Get the storage type
 148 | 	 */
 149 | 	getStorageType(): 'api' {
 150 | 		return 'api';
 151 | 	}
 152 | 
 153 | 	/**
 154 | 	 * Get the current brief name
 155 | 	 * @returns The brief name if a brief is selected, null otherwise
 156 | 	 */
 157 | 	getCurrentBriefName(): string | null {
 158 | 		const authManager = AuthManager.getInstance();
 159 | 		const context = authManager.getContext();
 160 | 		return context?.briefName || null;
 161 | 	}
 162 | 
 163 | 	/**
 164 | 	 * Get all briefs (tags) with detailed statistics including task counts
 165 | 	 * In API storage, tags are called "briefs"
 166 | 	 * Delegates to BriefsDomain for brief statistics calculation
 167 | 	 */
 168 | 	async getTagsWithStats(): Promise<{
 169 | 		tags: Array<{
 170 | 			name: string;
 171 | 			isCurrent: boolean;
 172 | 			taskCount: number;
 173 | 			completedTasks: number;
 174 | 			statusBreakdown: Record<string, number>;
 175 | 			subtaskCounts?: {
 176 | 				totalSubtasks: number;
 177 | 				subtasksByStatus: Record<string, number>;
 178 | 			};
 179 | 			created?: string;
 180 | 			description?: string;
 181 | 			status?: string;
 182 | 			briefId?: string;
 183 | 		}>;
 184 | 		currentTag: string | null;
 185 | 		totalTags: number;
 186 | 	}> {
 187 | 		await this.ensureInitialized();
 188 | 
 189 | 		try {
 190 | 			// Delegate to BriefsDomain which owns brief operations
 191 | 			const briefsDomain = new BriefsDomain();
 192 | 			return await briefsDomain.getBriefsWithStats(
 193 | 				this.repository,
 194 | 				this.projectId
 195 | 			);
 196 | 		} catch (error) {
 197 | 			throw new TaskMasterError(
 198 | 				'Failed to get tags with stats from API',
 199 | 				ERROR_CODES.STORAGE_ERROR,
 200 | 				{ operation: 'getTagsWithStats' },
 201 | 				error as Error
 202 | 			);
 203 | 		}
 204 | 	}
 205 | 
 206 | 	/**
 207 | 	 * Load tags into cache
 208 | 	 * In our API-based system, "tags" represent briefs
 209 | 	 */
 210 | 	private async loadTagsIntoCache(): Promise<void> {
 211 | 		try {
 212 | 			const authManager = AuthManager.getInstance();
 213 | 			const context = authManager.getContext();
 214 | 
 215 | 			// If we have a selected brief, create a virtual "tag" for it
 216 | 			if (context?.briefId) {
 217 | 				// Create a virtual tag representing the current brief
 218 | 				const briefTag: TaskTag = {
 219 | 					name: context.briefId,
 220 | 					tasks: [], // Will be populated when tasks are loaded
 221 | 					metadata: {
 222 | 						briefId: context.briefId,
 223 | 						briefName: context.briefName,
 224 | 						organizationId: context.orgId
 225 | 					}
 226 | 				};
 227 | 
 228 | 				this.tagsCache.clear();
 229 | 				this.tagsCache.set(context.briefId, briefTag);
 230 | 			}
 231 | 		} catch (error) {
 232 | 			// If no brief is selected, that's okay - user needs to select one first
 233 | 			console.debug('No brief selected, starting with empty cache');
 234 | 		}
 235 | 	}
 236 | 
 237 | 	/**
 238 | 	 * Load tasks from API
 239 | 	 * In our system, the tag parameter represents a brief ID
 240 | 	 */
 241 | 	async loadTasks(tag?: string, options?: LoadTasksOptions): Promise<Task[]> {
 242 | 		await this.ensureInitialized();
 243 | 
 244 | 		try {
 245 | 			const context =
 246 | 				AuthManager.getInstance().ensureBriefSelected('loadTasks');
 247 | 
 248 | 			// Load tasks from the current brief context with filters pushed to repository
 249 | 			const tasks = await this.retryOperation(() =>
 250 | 				this.repository.getTasks(this.projectId, options)
 251 | 			);
 252 | 
 253 | 			// Update the tag cache with the loaded task IDs
 254 | 			const briefTag = this.tagsCache.get(context.briefId);
 255 | 			if (briefTag) {
 256 | 				briefTag.tasks = tasks.map((task) => task.id);
 257 | 			}
 258 | 
 259 | 			return tasks;
 260 | 		} catch (error) {
 261 | 			this.wrapError(error, 'Failed to load tasks from API', {
 262 | 				operation: 'loadTasks',
 263 | 				tag,
 264 | 				context: 'brief-based loading'
 265 | 			});
 266 | 		}
 267 | 	}
 268 | 
 269 | 	/**
 270 | 	 * Save tasks to API
 271 | 	 */
 272 | 	async saveTasks(tasks: Task[], tag?: string): Promise<void> {
 273 | 		await this.ensureInitialized();
 274 | 
 275 | 		try {
 276 | 			if (tag) {
 277 | 				// Update tag with task IDs
 278 | 				const tagData = this.tagsCache.get(tag) || {
 279 | 					name: tag,
 280 | 					tasks: [],
 281 | 					metadata: {}
 282 | 				};
 283 | 				tagData.tasks = tasks.map((t) => t.id);
 284 | 
 285 | 				// Save or update tag
 286 | 				if (this.tagsCache.has(tag)) {
 287 | 					await this.repository.updateTag(this.projectId, tag, tagData);
 288 | 				} else {
 289 | 					await this.repository.createTag(this.projectId, tagData);
 290 | 				}
 291 | 
 292 | 				this.tagsCache.set(tag, tagData);
 293 | 			}
 294 | 
 295 | 			// Save tasks using bulk operation
 296 | 			await this.retryOperation(() =>
 297 | 				this.repository.bulkCreateTasks(this.projectId, tasks)
 298 | 			);
 299 | 		} catch (error) {
 300 | 			throw new TaskMasterError(
 301 | 				'Failed to save tasks to API',
 302 | 				ERROR_CODES.STORAGE_ERROR,
 303 | 				{ operation: 'saveTasks', tag, taskCount: tasks.length },
 304 | 				error as Error
 305 | 			);
 306 | 		}
 307 | 	}
 308 | 
 309 | 	/**
 310 | 	 * Load a single task by ID (supports UUID or display ID like HAM-123)
 311 | 	 */
 312 | 	async loadTask(taskId: string, tag?: string): Promise<Task | null> {
 313 | 		await this.ensureInitialized();
 314 | 
 315 | 		try {
 316 | 			const retrievalService = this.getRetrievalService();
 317 | 			return await this.retryOperation(() => retrievalService.getTask(taskId));
 318 | 		} catch (error) {
 319 | 			this.wrapError(error, 'Failed to load task from API', {
 320 | 				operation: 'loadTask',
 321 | 				taskId,
 322 | 				tag
 323 | 			});
 324 | 		}
 325 | 	}
 326 | 
 327 | 	/**
 328 | 	 * Save a single task
 329 | 	 */
 330 | 	async saveTask(task: Task, tag?: string): Promise<void> {
 331 | 		await this.ensureInitialized();
 332 | 
 333 | 		try {
 334 | 			// Check if task exists
 335 | 			const existing = await this.repository.getTask(this.projectId, task.id);
 336 | 
 337 | 			if (existing) {
 338 | 				await this.retryOperation(() =>
 339 | 					this.repository.updateTask(this.projectId, task.id, task)
 340 | 				);
 341 | 			} else {
 342 | 				await this.retryOperation(() =>
 343 | 					this.repository.createTask(this.projectId, task)
 344 | 				);
 345 | 			}
 346 | 
 347 | 			// Update tag if specified
 348 | 			if (tag) {
 349 | 				const tagData = this.tagsCache.get(tag);
 350 | 				if (tagData && !tagData.tasks.includes(task.id)) {
 351 | 					tagData.tasks.push(task.id);
 352 | 					await this.repository.updateTag(this.projectId, tag, tagData);
 353 | 				}
 354 | 			}
 355 | 		} catch (error) {
 356 | 			throw new TaskMasterError(
 357 | 				'Failed to save task to API',
 358 | 				ERROR_CODES.STORAGE_ERROR,
 359 | 				{ operation: 'saveTask', taskId: task.id, tag },
 360 | 				error as Error
 361 | 			);
 362 | 		}
 363 | 	}
 364 | 
 365 | 	/**
 366 | 	 * Delete a task
 367 | 	 */
 368 | 	async deleteTask(taskId: string, tag?: string): Promise<void> {
 369 | 		await this.ensureInitialized();
 370 | 
 371 | 		try {
 372 | 			await this.retryOperation(() =>
 373 | 				this.repository.deleteTask(this.projectId, taskId)
 374 | 			);
 375 | 
 376 | 			// Remove from tag if specified
 377 | 			if (tag) {
 378 | 				const tagData = this.tagsCache.get(tag);
 379 | 				if (tagData) {
 380 | 					tagData.tasks = tagData.tasks.filter((id) => id !== taskId);
 381 | 					await this.repository.updateTag(this.projectId, tag, tagData);
 382 | 				}
 383 | 			}
 384 | 		} catch (error) {
 385 | 			throw new TaskMasterError(
 386 | 				'Failed to delete task from API',
 387 | 				ERROR_CODES.STORAGE_ERROR,
 388 | 				{ operation: 'deleteTask', taskId, tag },
 389 | 				error as Error
 390 | 			);
 391 | 		}
 392 | 	}
 393 | 
 394 | 	/**
 395 | 	 * List available tags (briefs in our system)
 396 | 	 */
 397 | 	async listTags(): Promise<string[]> {
 398 | 		await this.ensureInitialized();
 399 | 
 400 | 		try {
 401 | 			const authManager = AuthManager.getInstance();
 402 | 			const context = authManager.getContext();
 403 | 
 404 | 			// In our API-based system, we only have one "tag" at a time - the current brief
 405 | 			if (context?.briefId) {
 406 | 				// Ensure the current brief is in our cache
 407 | 				await this.loadTagsIntoCache();
 408 | 				return [context.briefId];
 409 | 			}
 410 | 
 411 | 			// No brief selected, return empty array
 412 | 			return [];
 413 | 		} catch (error) {
 414 | 			throw new TaskMasterError(
 415 | 				'Failed to list tags from API',
 416 | 				ERROR_CODES.STORAGE_ERROR,
 417 | 				{ operation: 'listTags' },
 418 | 				error as Error
 419 | 			);
 420 | 		}
 421 | 	}
 422 | 
 423 | 	/**
 424 | 	 * Load metadata
 425 | 	 */
 426 | 	async loadMetadata(tag?: string): Promise<TaskMetadata | null> {
 427 | 		await this.ensureInitialized();
 428 | 
 429 | 		try {
 430 | 			if (tag) {
 431 | 				const tagData = this.tagsCache.get(tag);
 432 | 				return (tagData?.metadata as TaskMetadata) || null;
 433 | 			}
 434 | 
 435 | 			// Return global metadata if no tag specified
 436 | 			// This could be stored in a special system tag
 437 | 			const systemTag = await this.repository.getTag(this.projectId, '_system');
 438 | 			return (systemTag?.metadata as TaskMetadata) || null;
 439 | 		} catch (error) {
 440 | 			throw new TaskMasterError(
 441 | 				'Failed to load metadata from API',
 442 | 				ERROR_CODES.STORAGE_ERROR,
 443 | 				{ operation: 'loadMetadata', tag },
 444 | 				error as Error
 445 | 			);
 446 | 		}
 447 | 	}
 448 | 
 449 | 	/**
 450 | 	 * Save metadata
 451 | 	 */
 452 | 	async saveMetadata(metadata: TaskMetadata, tag?: string): Promise<void> {
 453 | 		await this.ensureInitialized();
 454 | 
 455 | 		try {
 456 | 			if (tag) {
 457 | 				const tagData = this.tagsCache.get(tag) || {
 458 | 					name: tag,
 459 | 					tasks: [],
 460 | 					metadata: {}
 461 | 				};
 462 | 				tagData.metadata = metadata as any;
 463 | 
 464 | 				if (this.tagsCache.has(tag)) {
 465 | 					await this.repository.updateTag(this.projectId, tag, tagData);
 466 | 				} else {
 467 | 					await this.repository.createTag(this.projectId, tagData);
 468 | 				}
 469 | 
 470 | 				this.tagsCache.set(tag, tagData);
 471 | 			} else {
 472 | 				// Save to system tag
 473 | 				const systemTag: TaskTag = {
 474 | 					name: '_system',
 475 | 					tasks: [],
 476 | 					metadata: metadata as any
 477 | 				};
 478 | 
 479 | 				const existing = await this.repository.getTag(
 480 | 					this.projectId,
 481 | 					'_system'
 482 | 				);
 483 | 				if (existing) {
 484 | 					await this.repository.updateTag(this.projectId, '_system', systemTag);
 485 | 				} else {
 486 | 					await this.repository.createTag(this.projectId, systemTag);
 487 | 				}
 488 | 			}
 489 | 		} catch (error) {
 490 | 			throw new TaskMasterError(
 491 | 				'Failed to save metadata to API',
 492 | 				ERROR_CODES.STORAGE_ERROR,
 493 | 				{ operation: 'saveMetadata', tag },
 494 | 				error as Error
 495 | 			);
 496 | 		}
 497 | 	}
 498 | 
 499 | 	/**
 500 | 	 * Check if storage exists
 501 | 	 */
 502 | 	async exists(): Promise<boolean> {
 503 | 		try {
 504 | 			await this.initialize();
 505 | 			return true;
 506 | 		} catch {
 507 | 			return false;
 508 | 		}
 509 | 	}
 510 | 
 511 | 	/**
 512 | 	 * Append tasks to existing storage
 513 | 	 */
 514 | 	async appendTasks(tasks: Task[], tag?: string): Promise<void> {
 515 | 		await this.ensureInitialized();
 516 | 
 517 | 		try {
 518 | 			// Use bulk create - repository should handle duplicates
 519 | 			await this.retryOperation(() =>
 520 | 				this.repository.bulkCreateTasks(this.projectId, tasks)
 521 | 			);
 522 | 
 523 | 			// Update tag if specified
 524 | 			if (tag) {
 525 | 				const tagData = this.tagsCache.get(tag) || {
 526 | 					name: tag,
 527 | 					tasks: [],
 528 | 					metadata: {}
 529 | 				};
 530 | 
 531 | 				const newTaskIds = tasks.map((t) => t.id);
 532 | 				tagData.tasks = [...new Set([...tagData.tasks, ...newTaskIds])];
 533 | 
 534 | 				if (this.tagsCache.has(tag)) {
 535 | 					await this.repository.updateTag(this.projectId, tag, tagData);
 536 | 				} else {
 537 | 					await this.repository.createTag(this.projectId, tagData);
 538 | 				}
 539 | 
 540 | 				this.tagsCache.set(tag, tagData);
 541 | 			}
 542 | 		} catch (error) {
 543 | 			throw new TaskMasterError(
 544 | 				'Failed to append tasks to API',
 545 | 				ERROR_CODES.STORAGE_ERROR,
 546 | 				{ operation: 'appendTasks', tag, taskCount: tasks.length },
 547 | 				error as Error
 548 | 			);
 549 | 		}
 550 | 	}
 551 | 
 552 | 	/**
 553 | 	 * Update a specific task
 554 | 	 */
 555 | 	async updateTask(
 556 | 		taskId: string,
 557 | 		updates: Partial<Task>,
 558 | 		tag?: string
 559 | 	): Promise<void> {
 560 | 		await this.ensureInitialized();
 561 | 
 562 | 		try {
 563 | 			await this.retryOperation(() =>
 564 | 				this.repository.updateTask(this.projectId, taskId, updates)
 565 | 			);
 566 | 		} catch (error) {
 567 | 			throw new TaskMasterError(
 568 | 				'Failed to update task via API',
 569 | 				ERROR_CODES.STORAGE_ERROR,
 570 | 				{ operation: 'updateTask', taskId, tag },
 571 | 				error as Error
 572 | 			);
 573 | 		}
 574 | 	}
 575 | 
 576 | 	/**
 577 | 	 * Update task with AI-powered prompt
 578 | 	 * Sends prompt to backend for server-side AI processing
 579 | 	 */
 580 | 	async updateTaskWithPrompt(
 581 | 		taskId: string,
 582 | 		prompt: string,
 583 | 		tag?: string,
 584 | 		options?: { useResearch?: boolean; mode?: 'append' | 'update' | 'rewrite' }
 585 | 	): Promise<void> {
 586 | 		await this.ensureInitialized();
 587 | 
 588 | 		const mode = options?.mode ?? 'append';
 589 | 
 590 | 		try {
 591 | 			// Use the API client - all auth, error handling, etc. is centralized
 592 | 			const apiClient = this.getApiClient();
 593 | 
 594 | 			const result = await apiClient.patch<UpdateTaskWithPromptResponse>(
 595 | 				`/ai/api/v1/tasks/${taskId}/prompt`,
 596 | 				{ prompt, mode }
 597 | 			);
 598 | 
 599 | 			if (!result.success) {
 600 | 				// API returned success: false
 601 | 				throw new Error(
 602 | 					result.message ||
 603 | 						`Update failed for task ${taskId}. The server did not provide details.`
 604 | 				);
 605 | 			}
 606 | 
 607 | 			// Log success with task details
 608 | 			this.logger.info(
 609 | 				`Successfully updated task ${result.task.displayId || result.task.id} using AI prompt (mode: ${mode})`
 610 | 			);
 611 | 			this.logger.info(`  Title: ${result.task.title}`);
 612 | 			this.logger.info(`  Status: ${result.task.status}`);
 613 | 			if (result.message) {
 614 | 				this.logger.info(`  ${result.message}`);
 615 | 			}
 616 | 		} catch (error) {
 617 | 			// If it's already a TaskMasterError, just add context and re-throw
 618 | 			if (error instanceof TaskMasterError) {
 619 | 				throw error.withContext({
 620 | 					operation: 'updateTaskWithPrompt',
 621 | 					taskId,
 622 | 					tag,
 623 | 					promptLength: prompt.length,
 624 | 					mode
 625 | 				});
 626 | 			}
 627 | 
 628 | 			// For other errors, wrap them
 629 | 			const errorMessage =
 630 | 				error instanceof Error ? error.message : String(error);
 631 | 			throw new TaskMasterError(
 632 | 				errorMessage,
 633 | 				ERROR_CODES.STORAGE_ERROR,
 634 | 				{
 635 | 					operation: 'updateTaskWithPrompt',
 636 | 					taskId,
 637 | 					tag,
 638 | 					promptLength: prompt.length,
 639 | 					mode
 640 | 				},
 641 | 				error as Error
 642 | 			);
 643 | 		}
 644 | 	}
 645 | 
 646 | 	/**
 647 | 	 * Expand task into subtasks with AI-powered generation
 648 | 	 * Sends task to backend for server-side AI processing
 649 | 	 */
 650 | 	async expandTaskWithPrompt(
 651 | 		taskId: string,
 652 | 		_tag?: string,
 653 | 		options?: {
 654 | 			numSubtasks?: number;
 655 | 			useResearch?: boolean;
 656 | 			additionalContext?: string;
 657 | 			force?: boolean;
 658 | 		}
 659 | 	): Promise<ExpandTaskResult> {
 660 | 		await this.ensureInitialized();
 661 | 
 662 | 		const expansionService = this.getExpansionService();
 663 | 		return await expansionService.expandTask(taskId, options);
 664 | 	}
 665 | 
 666 | 	/**
 667 | 	 * Update task or subtask status by ID - for API storage
 668 | 	 */
 669 | 	async updateTaskStatus(
 670 | 		taskId: string,
 671 | 		newStatus: TaskStatus,
 672 | 		tag?: string
 673 | 	): Promise<UpdateStatusResult> {
 674 | 		await this.ensureInitialized();
 675 | 
 676 | 		try {
 677 | 			AuthManager.getInstance().ensureBriefSelected('updateTaskStatus');
 678 | 
 679 | 			const existingTask = await this.retryOperation(() =>
 680 | 				this.repository.getTask(this.projectId, taskId)
 681 | 			);
 682 | 
 683 | 			if (!existingTask) {
 684 | 				throw new Error(`Task ${taskId} not found`);
 685 | 			}
 686 | 
 687 | 			const oldStatus = existingTask.status;
 688 | 			if (oldStatus === newStatus) {
 689 | 				return {
 690 | 					success: true,
 691 | 					oldStatus,
 692 | 					newStatus,
 693 | 					taskId
 694 | 				};
 695 | 			}
 696 | 
 697 | 			// Update the task/subtask status
 698 | 			await this.retryOperation(() =>
 699 | 				this.repository.updateTask(this.projectId, taskId, {
 700 | 					status: newStatus,
 701 | 					updatedAt: new Date().toISOString()
 702 | 				})
 703 | 			);
 704 | 
 705 | 			// Note: Parent status auto-adjustment is handled by the backend API service
 706 | 			// which has its own business logic for managing task relationships
 707 | 
 708 | 			return {
 709 | 				success: true,
 710 | 				oldStatus,
 711 | 				newStatus,
 712 | 				taskId
 713 | 			};
 714 | 		} catch (error) {
 715 | 			this.wrapError(error, 'Failed to update task status via API', {
 716 | 				operation: 'updateTaskStatus',
 717 | 				taskId,
 718 | 				newStatus,
 719 | 				tag
 720 | 			});
 721 | 		}
 722 | 	}
 723 | 
 724 | 	/**
 725 | 	 * Get all available tags
 726 | 	 */
 727 | 	async getAllTags(): Promise<string[]> {
 728 | 		return this.listTags();
 729 | 	}
 730 | 
 731 | 	/**
 732 | 	 * Create a new tag (brief)
 733 | 	 * Not supported with API storage - users must create briefs via web interface
 734 | 	 */
 735 | 	async createTag(
 736 | 		tagName: string,
 737 | 		_options?: { copyFrom?: string; description?: string }
 738 | 	): Promise<void> {
 739 | 		throw new TaskMasterError(
 740 | 			'Tag creation is not supported with API storage. Please create briefs through Hamster Studio.',
 741 | 			ERROR_CODES.NOT_IMPLEMENTED,
 742 | 			{ storageType: 'api', operation: 'createTag', tagName }
 743 | 		);
 744 | 	}
 745 | 
 746 | 	/**
 747 | 	 * Delete all tasks for a tag
 748 | 	 */
 749 | 	async deleteTag(tag: string): Promise<void> {
 750 | 		await this.ensureInitialized();
 751 | 
 752 | 		try {
 753 | 			await this.retryOperation(() =>
 754 | 				this.repository.deleteTag(this.projectId, tag)
 755 | 			);
 756 | 
 757 | 			this.tagsCache.delete(tag);
 758 | 		} catch (error) {
 759 | 			throw new TaskMasterError(
 760 | 				'Failed to delete tag via API',
 761 | 				ERROR_CODES.STORAGE_ERROR,
 762 | 				{ operation: 'deleteTag', tag },
 763 | 				error as Error
 764 | 			);
 765 | 		}
 766 | 	}
 767 | 
 768 | 	/**
 769 | 	 * Rename a tag
 770 | 	 */
 771 | 	async renameTag(oldTag: string, newTag: string): Promise<void> {
 772 | 		await this.ensureInitialized();
 773 | 
 774 | 		try {
 775 | 			const tagData = this.tagsCache.get(oldTag);
 776 | 			if (!tagData) {
 777 | 				throw new Error(`Tag ${oldTag} not found`);
 778 | 			}
 779 | 
 780 | 			// Create new tag with same data
 781 | 			const newTagData = { ...tagData, name: newTag };
 782 | 			await this.repository.createTag(this.projectId, newTagData);
 783 | 
 784 | 			// Delete old tag
 785 | 			await this.repository.deleteTag(this.projectId, oldTag);
 786 | 
 787 | 			// Update cache
 788 | 			this.tagsCache.delete(oldTag);
 789 | 			this.tagsCache.set(newTag, newTagData);
 790 | 		} catch (error) {
 791 | 			throw new TaskMasterError(
 792 | 				'Failed to rename tag via API',
 793 | 				ERROR_CODES.STORAGE_ERROR,
 794 | 				{ operation: 'renameTag', oldTag, newTag },
 795 | 				error as Error
 796 | 			);
 797 | 		}
 798 | 	}
 799 | 
 800 | 	/**
 801 | 	 * Copy a tag
 802 | 	 */
 803 | 	async copyTag(sourceTag: string, targetTag: string): Promise<void> {
 804 | 		await this.ensureInitialized();
 805 | 
 806 | 		try {
 807 | 			const sourceData = this.tagsCache.get(sourceTag);
 808 | 			if (!sourceData) {
 809 | 				throw new Error(`Source tag ${sourceTag} not found`);
 810 | 			}
 811 | 
 812 | 			// Create new tag with copied data
 813 | 			const targetData = { ...sourceData, name: targetTag };
 814 | 			await this.repository.createTag(this.projectId, targetData);
 815 | 
 816 | 			// Update cache
 817 | 			this.tagsCache.set(targetTag, targetData);
 818 | 		} catch (error) {
 819 | 			throw new TaskMasterError(
 820 | 				'Failed to copy tag via API',
 821 | 				ERROR_CODES.STORAGE_ERROR,
 822 | 				{ operation: 'copyTag', sourceTag, targetTag },
 823 | 				error as Error
 824 | 			);
 825 | 		}
 826 | 	}
 827 | 
 828 | 	/**
 829 | 	 * Get storage statistics
 830 | 	 */
 831 | 	async getStats(): Promise<StorageStats> {
 832 | 		await this.ensureInitialized();
 833 | 
 834 | 		try {
 835 | 			const tasks = await this.repository.getTasks(this.projectId);
 836 | 			const tags = await this.repository.getTags(this.projectId);
 837 | 
 838 | 			const tagStats = tags.map((tag) => ({
 839 | 				tag: tag.name,
 840 | 				taskCount: tag.tasks.length,
 841 | 				lastModified: new Date().toISOString() // TODO: Get actual last modified from tag data
 842 | 			}));
 843 | 
 844 | 			return {
 845 | 				totalTasks: tasks.length,
 846 | 				totalTags: tags.length,
 847 | 				storageSize: 0, // Not applicable for API storage
 848 | 				lastModified: new Date().toISOString(),
 849 | 				tagStats
 850 | 			};
 851 | 		} catch (error) {
 852 | 			throw new TaskMasterError(
 853 | 				'Failed to get stats from API',
 854 | 				ERROR_CODES.STORAGE_ERROR,
 855 | 				{ operation: 'getStats' },
 856 | 				error as Error
 857 | 			);
 858 | 		}
 859 | 	}
 860 | 
 861 | 	/**
 862 | 	 * Create backup
 863 | 	 */
 864 | 	async backup(): Promise<string> {
 865 | 		await this.ensureInitialized();
 866 | 
 867 | 		try {
 868 | 			// Export all data
 869 | 			await this.repository.getTasks(this.projectId);
 870 | 			await this.repository.getTags(this.projectId);
 871 | 
 872 | 			// TODO: In a real implementation, this would:
 873 | 			// 1. Create backup data structure with tasks and tags
 874 | 			// 2. Save the backup to a storage service
 875 | 			// For now, return a backup identifier
 876 | 			return `backup-${this.projectId}-${Date.now()}`;
 877 | 		} catch (error) {
 878 | 			throw new TaskMasterError(
 879 | 				'Failed to create backup via API',
 880 | 				ERROR_CODES.STORAGE_ERROR,
 881 | 				{ operation: 'backup' },
 882 | 				error as Error
 883 | 			);
 884 | 		}
 885 | 	}
 886 | 
 887 | 	/**
 888 | 	 * Restore from backup
 889 | 	 */
 890 | 	async restore(backupId: string): Promise<void> {
 891 | 		await this.ensureInitialized();
 892 | 
 893 | 		// This would restore from a backup service
 894 | 		// Implementation depends on backup strategy
 895 | 		throw new TaskMasterError(
 896 | 			'Restore not implemented for API storage',
 897 | 			ERROR_CODES.NOT_IMPLEMENTED,
 898 | 			{ operation: 'restore', backupId }
 899 | 		);
 900 | 	}
 901 | 
 902 | 	/**
 903 | 	 * Clear all data
 904 | 	 */
 905 | 	async clear(): Promise<void> {
 906 | 		await this.ensureInitialized();
 907 | 
 908 | 		try {
 909 | 			// Delete all tasks
 910 | 			const tasks = await this.repository.getTasks(this.projectId);
 911 | 			if (tasks.length > 0) {
 912 | 				await this.repository.bulkDeleteTasks(
 913 | 					this.projectId,
 914 | 					tasks.map((t) => t.id)
 915 | 				);
 916 | 			}
 917 | 
 918 | 			// Delete all tags
 919 | 			const tags = await this.repository.getTags(this.projectId);
 920 | 			for (const tag of tags) {
 921 | 				await this.repository.deleteTag(this.projectId, tag.name);
 922 | 			}
 923 | 
 924 | 			// Clear cache
 925 | 			this.tagsCache.clear();
 926 | 		} catch (error) {
 927 | 			throw new TaskMasterError(
 928 | 				'Failed to clear data via API',
 929 | 				ERROR_CODES.STORAGE_ERROR,
 930 | 				{ operation: 'clear' },
 931 | 				error as Error
 932 | 			);
 933 | 		}
 934 | 	}
 935 | 
 936 | 	/**
 937 | 	 * Close connection
 938 | 	 */
 939 | 	async close(): Promise<void> {
 940 | 		this.initialized = false;
 941 | 		this.tagsCache.clear();
 942 | 	}
 943 | 
 944 | 	/**
 945 | 	 * Ensure storage is initialized
 946 | 	 */
 947 | 	private async ensureInitialized(): Promise<void> {
 948 | 		if (!this.initialized) {
 949 | 			await this.initialize();
 950 | 		}
 951 | 	}
 952 | 
 953 | 	/**
 954 | 	 * Get or create API client instance with auth
 955 | 	 */
 956 | 	private getApiClient(): ApiClient {
 957 | 		if (!this.apiClient) {
 958 | 			const apiEndpoint =
 959 | 				process.env.TM_BASE_DOMAIN || process.env.TM_PUBLIC_BASE_DOMAIN;
 960 | 
 961 | 			if (!apiEndpoint) {
 962 | 				throw new TaskMasterError(
 963 | 					'API endpoint not configured. Please set TM_PUBLIC_BASE_DOMAIN environment variable.',
 964 | 					ERROR_CODES.MISSING_CONFIGURATION,
 965 | 					{ operation: 'getApiClient' }
 966 | 				);
 967 | 			}
 968 | 
 969 | 			const context =
 970 | 				AuthManager.getInstance().ensureBriefSelected('getApiClient');
 971 | 			const authManager = AuthManager.getInstance();
 972 | 
 973 | 			this.apiClient = new ApiClient({
 974 | 				baseUrl: apiEndpoint,
 975 | 				authManager,
 976 | 				accountId: context.orgId
 977 | 			});
 978 | 		}
 979 | 
 980 | 		return this.apiClient;
 981 | 	}
 982 | 
 983 | 	/**
 984 | 	 * Get or create TaskExpansionService instance
 985 | 	 */
 986 | 	private getExpansionService(): TaskExpansionService {
 987 | 		if (!this.expansionService) {
 988 | 			const apiClient = this.getApiClient();
 989 | 			const authManager = AuthManager.getInstance();
 990 | 
 991 | 			this.expansionService = new TaskExpansionService(
 992 | 				this.repository,
 993 | 				this.projectId,
 994 | 				apiClient,
 995 | 				authManager
 996 | 			);
 997 | 		}
 998 | 
 999 | 		return this.expansionService;
1000 | 	}
1001 | 
1002 | 	/**
1003 | 	 * Get or create TaskRetrievalService instance
1004 | 	 */
1005 | 	private getRetrievalService(): TaskRetrievalService {
1006 | 		if (!this.retrievalService) {
1007 | 			const apiClient = this.getApiClient();
1008 | 			const authManager = AuthManager.getInstance();
1009 | 
1010 | 			this.retrievalService = new TaskRetrievalService(
1011 | 				this.repository,
1012 | 				this.projectId,
1013 | 				apiClient,
1014 | 				authManager
1015 | 			);
1016 | 		}
1017 | 
1018 | 		return this.retrievalService;
1019 | 	}
1020 | 
1021 | 	/**
1022 | 	 * Retry an operation with exponential backoff
1023 | 	 */
1024 | 	private async retryOperation<T>(
1025 | 		operation: () => Promise<T>,
1026 | 		attempt = 1
1027 | 	): Promise<T> {
1028 | 		try {
1029 | 			return await operation();
1030 | 		} catch (error) {
1031 | 			if (this.enableRetry && attempt < this.maxRetries) {
1032 | 				const delay = Math.pow(2, attempt) * 1000;
1033 | 				await new Promise((resolve) => setTimeout(resolve, delay));
1034 | 				return this.retryOperation(operation, attempt + 1);
1035 | 			}
1036 | 			throw error;
1037 | 		}
1038 | 	}
1039 | 
1040 | 	/**
1041 | 	 * Wrap an error unless it's already a NO_BRIEF_SELECTED error
1042 | 	 */
1043 | 	private wrapError(
1044 | 		error: unknown,
1045 | 		message: string,
1046 | 		context: Record<string, unknown>
1047 | 	): never {
1048 | 		// If it's already a NO_BRIEF_SELECTED error, don't wrap it
1049 | 		if (
1050 | 			error instanceof TaskMasterError &&
1051 | 			error.is(ERROR_CODES.NO_BRIEF_SELECTED)
1052 | 		) {
1053 | 			throw error;
1054 | 		}
1055 | 
1056 | 		throw new TaskMasterError(
1057 | 			message,
1058 | 			ERROR_CODES.STORAGE_ERROR,
1059 | 			context,
1060 | 			error as Error
1061 | 		);
1062 | 	}
1063 | }
1064 | 
```

--------------------------------------------------------------------------------
/docs/models.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Available Models as of November 18, 2025
  2 | 
  3 | ## Main Models
  4 | 
  5 | | Provider    | Model Name                                     | SWE Score | Input Cost | Output Cost |
  6 | | ----------- | ---------------------------------------------- | --------- | ---------- | ----------- |
  7 | | anthropic   | claude-sonnet-4-20250514                       | 0.727     | 3          | 15          |
  8 | | anthropic   | claude-opus-4-20250514                         | 0.725     | 15         | 75          |
  9 | | anthropic   | claude-3-7-sonnet-20250219                     | 0.623     | 3          | 15          |
 10 | | anthropic   | claude-3-5-sonnet-20241022                     | 0.49      | 3          | 15          |
 11 | | anthropic   | claude-sonnet-4-5-20250929                     | 0.73      | 3          | 15          |
 12 | | anthropic   | claude-haiku-4-5-20251001                      | 0.45      | 1          | 5           |
 13 | | claude-code | opus                                           | 0.725     | 0          | 0           |
 14 | | claude-code | sonnet                                         | 0.727     | 0          | 0           |
 15 | | claude-code | haiku                                          | 0.45      | 0          | 0           |
 16 | | codex-cli   | gpt-5                                          | 0.749     | 0          | 0           |
 17 | | codex-cli   | gpt-5-codex                                    | 0.749     | 0          | 0           |
 18 | | mcp         | mcp-sampling                                   | —         | 0          | 0           |
 19 | | gemini-cli  | gemini-3-pro-preview                           | 0.762     | 0          | 0           |
 20 | | gemini-cli  | gemini-2.5-pro                                 | 0.72      | 0          | 0           |
 21 | | gemini-cli  | gemini-2.5-flash                               | 0.71      | 0          | 0           |
 22 | | grok-cli    | grok-4-latest                                  | 0.7       | 0          | 0           |
 23 | | grok-cli    | grok-3-latest                                  | 0.65      | 0          | 0           |
 24 | | grok-cli    | grok-3-fast                                    | 0.6       | 0          | 0           |
 25 | | grok-cli    | grok-3-mini-fast                               | 0.55      | 0          | 0           |
 26 | | openai      | gpt-4o                                         | 0.332     | 2.5        | 10          |
 27 | | openai      | o1                                             | 0.489     | 15         | 60          |
 28 | | openai      | o3                                             | 0.5       | 2          | 8           |
 29 | | openai      | o3-mini                                        | 0.493     | 1.1        | 4.4         |
 30 | | openai      | o4-mini                                        | 0.45      | 1.1        | 4.4         |
 31 | | openai      | o1-mini                                        | 0.4       | 1.1        | 4.4         |
 32 | | openai      | o1-pro                                         | —         | 150        | 600         |
 33 | | openai      | gpt-4-5-preview                                | 0.38      | 75         | 150         |
 34 | | openai      | gpt-4-1-mini                                   | —         | 0.4        | 1.6         |
 35 | | openai      | gpt-4-1-nano                                   | —         | 0.1        | 0.4         |
 36 | | openai      | gpt-4o-mini                                    | 0.3       | 0.15       | 0.6         |
 37 | | openai      | gpt-5                                          | 0.749     | 5          | 20          |
 38 | | google      | gemini-3-pro-preview                           | 0.762     | 2          | 12          |
 39 | | google      | gemini-2.5-pro-preview-05-06                   | 0.638     | —          | —           |
 40 | | google      | gemini-2.5-pro-preview-03-25                   | 0.638     | —          | —           |
 41 | | google      | gemini-2.5-flash-preview-04-17                 | 0.604     | —          | —           |
 42 | | google      | gemini-2.0-flash                               | 0.518     | 0.15       | 0.6         |
 43 | | google      | gemini-2.0-flash-lite                          | —         | —          | —           |
 44 | | xai         | grok-3                                         | —         | 3          | 15          |
 45 | | xai         | grok-3-fast                                    | —         | 5          | 25          |
 46 | | xai         | grok-4                                         | —         | 3          | 15          |
 47 | | groq        | moonshotai/kimi-k2-instruct                    | 0.66      | 1          | 3           |
 48 | | groq        | llama-3.3-70b-versatile                        | 0.55      | 0.59       | 0.79        |
 49 | | groq        | llama-3.1-8b-instant                           | 0.32      | 0.05       | 0.08        |
 50 | | groq        | llama-4-scout                                  | 0.45      | 0.11       | 0.34        |
 51 | | groq        | llama-4-maverick                               | 0.52      | 0.5        | 0.77        |
 52 | | groq        | mixtral-8x7b-32768                             | 0.35      | 0.24       | 0.24        |
 53 | | groq        | qwen-qwq-32b-preview                           | 0.4       | 0.18       | 0.18        |
 54 | | groq        | deepseek-r1-distill-llama-70b                  | 0.52      | 0.75       | 0.99        |
 55 | | groq        | gemma2-9b-it                                   | 0.3       | 0.2        | 0.2         |
 56 | | groq        | whisper-large-v3                               | —         | 0.11       | 0           |
 57 | | perplexity  | sonar-pro                                      | —         | 3          | 15          |
 58 | | perplexity  | sonar-reasoning-pro                            | 0.211     | 2          | 8           |
 59 | | perplexity  | sonar-reasoning                                | 0.211     | 1          | 5           |
 60 | | openrouter  | google/gemini-2.5-flash-preview-05-20          | —         | 0.15       | 0.6         |
 61 | | openrouter  | google/gemini-2.5-flash-preview-05-20:thinking | —         | 0.15       | 3.5         |
 62 | | openrouter  | google/gemini-2.5-pro-exp-03-25                | —         | 0          | 0           |
 63 | | openrouter  | deepseek/deepseek-chat-v3-0324                 | —         | 0.27       | 1.1         |
 64 | | openrouter  | openai/gpt-4.1                                 | —         | 2          | 8           |
 65 | | openrouter  | openai/gpt-4.1-mini                            | —         | 0.4        | 1.6         |
 66 | | openrouter  | openai/gpt-4.1-nano                            | —         | 0.1        | 0.4         |
 67 | | openrouter  | openai/o3                                      | —         | 10         | 40          |
 68 | | openrouter  | openai/codex-mini                              | —         | 1.5        | 6           |
 69 | | openrouter  | openai/gpt-4o-mini                             | —         | 0.15       | 0.6         |
 70 | | openrouter  | openai/o4-mini                                 | 0.45      | 1.1        | 4.4         |
 71 | | openrouter  | openai/o4-mini-high                            | —         | 1.1        | 4.4         |
 72 | | openrouter  | openai/o1-pro                                  | —         | 150        | 600         |
 73 | | openrouter  | meta-llama/llama-3.3-70b-instruct              | —         | 120        | 600         |
 74 | | openrouter  | meta-llama/llama-4-maverick                    | —         | 0.18       | 0.6         |
 75 | | openrouter  | meta-llama/llama-4-scout                       | —         | 0.08       | 0.3         |
 76 | | openrouter  | qwen/qwen-max                                  | —         | 1.6        | 6.4         |
 77 | | openrouter  | qwen/qwen-turbo                                | —         | 0.05       | 0.2         |
 78 | | openrouter  | qwen/qwen3-235b-a22b                           | —         | 0.14       | 2           |
 79 | | openrouter  | mistralai/mistral-small-3.1-24b-instruct       | —         | 0.1        | 0.3         |
 80 | | openrouter  | mistralai/devstral-small                       | —         | 0.1        | 0.3         |
 81 | | openrouter  | mistralai/mistral-nemo                         | —         | 0.03       | 0.07        |
 82 | | zai         | glm-4.6                                        | 0.68      | 0.6        | 2.2         |
 83 | | zai         | glm-4.5                                        | 0.65      | 0.6        | 2.2         |
 84 | | zai         | glm-4.5-air                                    | 0.62      | 0.2        | 1.1         |
 85 | | zai-coding  | glm-4.6                                        | 0.68      | 0          | 0           |
 86 | | zai-coding  | glm-4.5                                        | 0.65      | 0          | 0           |
 87 | | zai-coding  | glm-4.5-air                                    | 0.62      | 0          | 0           |
 88 | | ollama      | gpt-oss:latest                                 | 0.607     | 0          | 0           |
 89 | | ollama      | gpt-oss:20b                                    | 0.607     | 0          | 0           |
 90 | | ollama      | gpt-oss:120b                                   | 0.624     | 0          | 0           |
 91 | | ollama      | devstral:latest                                | —         | 0          | 0           |
 92 | | ollama      | qwen3:latest                                   | —         | 0          | 0           |
 93 | | ollama      | qwen3:14b                                      | —         | 0          | 0           |
 94 | | ollama      | qwen3:32b                                      | —         | 0          | 0           |
 95 | | ollama      | mistral-small3.1:latest                        | —         | 0          | 0           |
 96 | | ollama      | llama3.3:latest                                | —         | 0          | 0           |
 97 | | ollama      | phi4:latest                                    | —         | 0          | 0           |
 98 | | azure       | gpt-4o                                         | 0.332     | 2.5        | 10          |
 99 | | azure       | gpt-4o-mini                                    | 0.3       | 0.15       | 0.6         |
100 | | azure       | gpt-4-1                                        | —         | 2          | 10          |
101 | | bedrock     | us.anthropic.claude-3-haiku-20240307-v1:0      | 0.4       | 0.25       | 1.25        |
102 | | bedrock     | us.anthropic.claude-3-opus-20240229-v1:0       | 0.725     | 15         | 75          |
103 | | bedrock     | us.anthropic.claude-3-5-sonnet-20240620-v1:0   | 0.49      | 3          | 15          |
104 | | bedrock     | us.anthropic.claude-3-5-sonnet-20241022-v2:0   | 0.49      | 3          | 15          |
105 | | bedrock     | us.anthropic.claude-3-7-sonnet-20250219-v1:0   | 0.623     | 3          | 15          |
106 | | bedrock     | us.anthropic.claude-3-5-haiku-20241022-v1:0    | 0.4       | 0.8        | 4           |
107 | | bedrock     | us.anthropic.claude-opus-4-20250514-v1:0       | 0.725     | 15         | 75          |
108 | | bedrock     | us.anthropic.claude-sonnet-4-20250514-v1:0     | 0.727     | 3          | 15          |
109 | 
110 | ## Research Models
111 | 
112 | | Provider    | Model Name                                   | SWE Score | Input Cost | Output Cost |
113 | | ----------- | -------------------------------------------- | --------- | ---------- | ----------- |
114 | | claude-code | opus                                         | 0.725     | 0          | 0           |
115 | | claude-code | sonnet                                       | 0.727     | 0          | 0           |
116 | | claude-code | haiku                                        | 0.45      | 0          | 0           |
117 | | codex-cli   | gpt-5                                        | 0.749     | 0          | 0           |
118 | | codex-cli   | gpt-5-codex                                  | 0.749     | 0          | 0           |
119 | | mcp         | mcp-sampling                                 | —         | 0          | 0           |
120 | | gemini-cli  | gemini-3-pro-preview                         | 0.762     | 0          | 0           |
121 | | gemini-cli  | gemini-2.5-pro                               | 0.72      | 0          | 0           |
122 | | gemini-cli  | gemini-2.5-flash                             | 0.71      | 0          | 0           |
123 | | grok-cli    | grok-4-latest                                | 0.7       | 0          | 0           |
124 | | grok-cli    | grok-3-latest                                | 0.65      | 0          | 0           |
125 | | grok-cli    | grok-3-fast                                  | 0.6       | 0          | 0           |
126 | | grok-cli    | grok-3-mini-fast                             | 0.55      | 0          | 0           |
127 | | openai      | gpt-4o-search-preview                        | 0.33      | 2.5        | 10          |
128 | | openai      | gpt-4o-mini-search-preview                   | 0.3       | 0.15       | 0.6         |
129 | | google      | gemini-3-pro-preview                         | 0.762     | 2          | 12          |
130 | | xai         | grok-3                                       | —         | 3          | 15          |
131 | | xai         | grok-3-fast                                  | —         | 5          | 25          |
132 | | xai         | grok-4                                       | —         | 3          | 15          |
133 | | groq        | llama-3.3-70b-versatile                      | 0.55      | 0.59       | 0.79        |
134 | | groq        | llama-4-scout                                | 0.45      | 0.11       | 0.34        |
135 | | groq        | llama-4-maverick                             | 0.52      | 0.5        | 0.77        |
136 | | groq        | qwen-qwq-32b-preview                         | 0.4       | 0.18       | 0.18        |
137 | | groq        | deepseek-r1-distill-llama-70b                | 0.52      | 0.75       | 0.99        |
138 | | perplexity  | sonar-pro                                    | —         | 3          | 15          |
139 | | perplexity  | sonar                                        | —         | 1          | 1           |
140 | | perplexity  | sonar-deep-research                          | 0.211     | 2          | 8           |
141 | | perplexity  | sonar-reasoning-pro                          | 0.211     | 2          | 8           |
142 | | perplexity  | sonar-reasoning                              | 0.211     | 1          | 5           |
143 | | zai         | glm-4.6                                      | 0.68      | 0.6        | 2.2         |
144 | | zai         | glm-4.5                                      | 0.65      | 0.6        | 2.2         |
145 | | zai         | glm-4.5-air                                  | 0.62      | 0.2        | 1.1         |
146 | | zai-coding  | glm-4.6                                      | 0.68      | 0          | 0           |
147 | | zai-coding  | glm-4.5                                      | 0.65      | 0          | 0           |
148 | | zai-coding  | glm-4.5-air                                  | 0.62      | 0          | 0           |
149 | | bedrock     | us.anthropic.claude-3-opus-20240229-v1:0     | 0.725     | 15         | 75          |
150 | | bedrock     | us.anthropic.claude-3-5-sonnet-20240620-v1:0 | 0.49      | 3          | 15          |
151 | | bedrock     | us.anthropic.claude-3-5-sonnet-20241022-v2:0 | 0.49      | 3          | 15          |
152 | | bedrock     | us.anthropic.claude-3-7-sonnet-20250219-v1:0 | 0.623     | 3          | 15          |
153 | | bedrock     | us.anthropic.claude-opus-4-20250514-v1:0     | 0.725     | 15         | 75          |
154 | | bedrock     | us.anthropic.claude-sonnet-4-20250514-v1:0   | 0.727     | 3          | 15          |
155 | | bedrock     | us.deepseek.r1-v1:0                          | —         | 1.35       | 5.4         |
156 | 
157 | ## Fallback Models
158 | 
159 | | Provider    | Model Name                                     | SWE Score | Input Cost | Output Cost |
160 | | ----------- | ---------------------------------------------- | --------- | ---------- | ----------- |
161 | | anthropic   | claude-sonnet-4-20250514                       | 0.727     | 3          | 15          |
162 | | anthropic   | claude-opus-4-20250514                         | 0.725     | 15         | 75          |
163 | | anthropic   | claude-3-7-sonnet-20250219                     | 0.623     | 3          | 15          |
164 | | anthropic   | claude-3-5-sonnet-20241022                     | 0.49      | 3          | 15          |
165 | | anthropic   | claude-sonnet-4-5-20250929                     | 0.73      | 3          | 15          |
166 | | anthropic   | claude-haiku-4-5-20251001                      | 0.45      | 1          | 5           |
167 | | claude-code | opus                                           | 0.725     | 0          | 0           |
168 | | claude-code | sonnet                                         | 0.727     | 0          | 0           |
169 | | claude-code | haiku                                          | 0.45      | 0          | 0           |
170 | | codex-cli   | gpt-5                                          | 0.749     | 0          | 0           |
171 | | codex-cli   | gpt-5-codex                                    | 0.749     | 0          | 0           |
172 | | mcp         | mcp-sampling                                   | —         | 0          | 0           |
173 | | gemini-cli  | gemini-3-pro-preview                           | 0.762     | 0          | 0           |
174 | | gemini-cli  | gemini-2.5-pro                                 | 0.72      | 0          | 0           |
175 | | gemini-cli  | gemini-2.5-flash                               | 0.71      | 0          | 0           |
176 | | grok-cli    | grok-4-latest                                  | 0.7       | 0          | 0           |
177 | | grok-cli    | grok-3-latest                                  | 0.65      | 0          | 0           |
178 | | grok-cli    | grok-3-fast                                    | 0.6       | 0          | 0           |
179 | | grok-cli    | grok-3-mini-fast                               | 0.55      | 0          | 0           |
180 | | openai      | gpt-4o                                         | 0.332     | 2.5        | 10          |
181 | | openai      | o3                                             | 0.5       | 2          | 8           |
182 | | openai      | o4-mini                                        | 0.45      | 1.1        | 4.4         |
183 | | openai      | gpt-5                                          | 0.749     | 5          | 20          |
184 | | google      | gemini-3-pro-preview                           | 0.762     | 2          | 12          |
185 | | google      | gemini-2.5-pro-preview-05-06                   | 0.638     | —          | —           |
186 | | google      | gemini-2.5-pro-preview-03-25                   | 0.638     | —          | —           |
187 | | google      | gemini-2.5-flash-preview-04-17                 | 0.604     | —          | —           |
188 | | google      | gemini-2.0-flash                               | 0.518     | 0.15       | 0.6         |
189 | | google      | gemini-2.0-flash-lite                          | —         | —          | —           |
190 | | xai         | grok-3                                         | —         | 3          | 15          |
191 | | xai         | grok-3-fast                                    | —         | 5          | 25          |
192 | | xai         | grok-4                                         | —         | 3          | 15          |
193 | | groq        | moonshotai/kimi-k2-instruct                    | 0.66      | 1          | 3           |
194 | | groq        | llama-3.3-70b-versatile                        | 0.55      | 0.59       | 0.79        |
195 | | groq        | llama-3.1-8b-instant                           | 0.32      | 0.05       | 0.08        |
196 | | groq        | llama-4-scout                                  | 0.45      | 0.11       | 0.34        |
197 | | groq        | llama-4-maverick                               | 0.52      | 0.5        | 0.77        |
198 | | groq        | mixtral-8x7b-32768                             | 0.35      | 0.24       | 0.24        |
199 | | groq        | qwen-qwq-32b-preview                           | 0.4       | 0.18       | 0.18        |
200 | | groq        | gemma2-9b-it                                   | 0.3       | 0.2        | 0.2         |
201 | | perplexity  | sonar-reasoning-pro                            | 0.211     | 2          | 8           |
202 | | perplexity  | sonar-reasoning                                | 0.211     | 1          | 5           |
203 | | openrouter  | google/gemini-2.5-flash-preview-05-20          | —         | 0.15       | 0.6         |
204 | | openrouter  | google/gemini-2.5-flash-preview-05-20:thinking | —         | 0.15       | 3.5         |
205 | | openrouter  | google/gemini-2.5-pro-exp-03-25                | —         | 0          | 0           |
206 | | openrouter  | openai/gpt-4.1                                 | —         | 2          | 8           |
207 | | openrouter  | openai/gpt-4.1-mini                            | —         | 0.4        | 1.6         |
208 | | openrouter  | openai/gpt-4.1-nano                            | —         | 0.1        | 0.4         |
209 | | openrouter  | openai/o3                                      | —         | 10         | 40          |
210 | | openrouter  | openai/codex-mini                              | —         | 1.5        | 6           |
211 | | openrouter  | openai/gpt-4o-mini                             | —         | 0.15       | 0.6         |
212 | | openrouter  | openai/o4-mini                                 | 0.45      | 1.1        | 4.4         |
213 | | openrouter  | openai/o4-mini-high                            | —         | 1.1        | 4.4         |
214 | | openrouter  | openai/o1-pro                                  | —         | 150        | 600         |
215 | | openrouter  | meta-llama/llama-3.3-70b-instruct              | —         | 120        | 600         |
216 | | openrouter  | meta-llama/llama-4-maverick                    | —         | 0.18       | 0.6         |
217 | | openrouter  | meta-llama/llama-4-scout                       | —         | 0.08       | 0.3         |
218 | | openrouter  | qwen/qwen-max                                  | —         | 1.6        | 6.4         |
219 | | openrouter  | qwen/qwen-turbo                                | —         | 0.05       | 0.2         |
220 | | openrouter  | qwen/qwen3-235b-a22b                           | —         | 0.14       | 2           |
221 | | openrouter  | mistralai/mistral-small-3.1-24b-instruct       | —         | 0.1        | 0.3         |
222 | | openrouter  | mistralai/mistral-nemo                         | —         | 0.03       | 0.07        |
223 | | zai         | glm-4.6                                        | 0.68      | 0.6        | 2.2         |
224 | | zai         | glm-4.5                                        | 0.65      | 0.6        | 2.2         |
225 | | zai         | glm-4.5-air                                    | 0.62      | 0.2        | 1.1         |
226 | | zai-coding  | glm-4.6                                        | 0.68      | 0          | 0           |
227 | | zai-coding  | glm-4.5                                        | 0.65      | 0          | 0           |
228 | | zai-coding  | glm-4.5-air                                    | 0.62      | 0          | 0           |
229 | | ollama      | gpt-oss:latest                                 | 0.607     | 0          | 0           |
230 | | ollama      | gpt-oss:20b                                    | 0.607     | 0          | 0           |
231 | | ollama      | gpt-oss:120b                                   | 0.624     | 0          | 0           |
232 | | ollama      | devstral:latest                                | —         | 0          | 0           |
233 | | ollama      | qwen3:latest                                   | —         | 0          | 0           |
234 | | ollama      | qwen3:14b                                      | —         | 0          | 0           |
235 | | ollama      | qwen3:32b                                      | —         | 0          | 0           |
236 | | ollama      | mistral-small3.1:latest                        | —         | 0          | 0           |
237 | | ollama      | llama3.3:latest                                | —         | 0          | 0           |
238 | | ollama      | phi4:latest                                    | —         | 0          | 0           |
239 | | azure       | gpt-4o                                         | 0.332     | 2.5        | 10          |
240 | | azure       | gpt-4o-mini                                    | 0.3       | 0.15       | 0.6         |
241 | | azure       | gpt-4-1                                        | —         | 2          | 10          |
242 | | bedrock     | us.anthropic.claude-3-haiku-20240307-v1:0      | 0.4       | 0.25       | 1.25        |
243 | | bedrock     | us.anthropic.claude-3-opus-20240229-v1:0       | 0.725     | 15         | 75          |
244 | | bedrock     | us.anthropic.claude-3-5-sonnet-20240620-v1:0   | 0.49      | 3          | 15          |
245 | | bedrock     | us.anthropic.claude-3-5-sonnet-20241022-v2:0   | 0.49      | 3          | 15          |
246 | | bedrock     | us.anthropic.claude-3-7-sonnet-20250219-v1:0   | 0.623     | 3          | 15          |
247 | | bedrock     | us.anthropic.claude-3-5-haiku-20241022-v1:0    | 0.4       | 0.8        | 4           |
248 | | bedrock     | us.anthropic.claude-opus-4-20250514-v1:0       | 0.725     | 15         | 75          |
249 | | bedrock     | us.anthropic.claude-sonnet-4-20250514-v1:0     | 0.727     | 3          | 15          |
250 | 
251 | ## Unsupported Models
252 | 
253 | | Provider   | Model Name                                    | Reason                                                                                                                                                                    |
254 | | ---------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
255 | | openrouter | deepseek/deepseek-chat-v3-0324:free           | Free OpenRouter models are not supported due to severe rate limits, lack of tool use support, and other reliability issues that make them impractical for production use. |
256 | | openrouter | mistralai/mistral-small-3.1-24b-instruct:free | Free OpenRouter models are not supported due to severe rate limits, lack of tool use support, and other reliability issues that make them impractical for production use. |
257 | | openrouter | thudm/glm-4-32b:free                          | Free OpenRouter models are not supported due to severe rate limits, lack of tool use support, and other reliability issues that make them impractical for production use. |
258 | 
```

--------------------------------------------------------------------------------
/scripts/modules/task-manager/scope-adjustment.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * scope-adjustment.js
  3 |  * Core logic for dynamic task complexity adjustment (scope-up and scope-down)
  4 |  */
  5 | 
  6 | import { z } from 'zod';
  7 | import {
  8 | 	log,
  9 | 	readJSON,
 10 | 	writeJSON,
 11 | 	getCurrentTag,
 12 | 	readComplexityReport,
 13 | 	findTaskInComplexityReport
 14 | } from '../utils.js';
 15 | import {
 16 | 	generateObjectService,
 17 | 	generateTextService
 18 | } from '../ai-services-unified.js';
 19 | import { findTaskById, taskExists } from '../task-manager.js';
 20 | import analyzeTaskComplexity from './analyze-task-complexity.js';
 21 | import { findComplexityReportPath } from '../../../src/utils/path-utils.js';
 22 | 
 23 | /**
 24 |  * Valid strength levels for scope adjustments
 25 |  */
 26 | const VALID_STRENGTHS = ['light', 'regular', 'heavy'];
 27 | 
 28 | /**
 29 |  * Statuses that should be preserved during subtask regeneration
 30 |  * These represent work that has been started or intentionally set by the user
 31 |  */
 32 | const PRESERVE_STATUSES = [
 33 | 	'done',
 34 | 	'in-progress',
 35 | 	'review',
 36 | 	'cancelled',
 37 | 	'deferred',
 38 | 	'blocked'
 39 | ];
 40 | 
 41 | /**
 42 |  * Statuses that should be regenerated during subtask regeneration
 43 |  * These represent work that hasn't been started yet
 44 |  */
 45 | const REGENERATE_STATUSES = ['pending'];
 46 | 
 47 | /**
 48 |  * Validates strength parameter
 49 |  * @param {string} strength - The strength level to validate
 50 |  * @returns {boolean} True if valid, false otherwise
 51 |  */
 52 | export function validateStrength(strength) {
 53 | 	return VALID_STRENGTHS.includes(strength);
 54 | }
 55 | 
 56 | /**
 57 |  * Re-analyzes the complexity of a single task after scope adjustment
 58 |  * @param {Object} task - The task to analyze
 59 |  * @param {string} tasksPath - Path to tasks.json
 60 |  * @param {Object} context - Context containing projectRoot, tag, session
 61 |  * @returns {Promise<number|null>} New complexity score or null if analysis failed
 62 |  */
 63 | async function reanalyzeTaskComplexity(task, tasksPath, context) {
 64 | 	const { projectRoot, tag, session } = context;
 65 | 
 66 | 	try {
 67 | 		// Create a minimal tasks data structure for analysis
 68 | 		const tasksForAnalysis = {
 69 | 			tasks: [task],
 70 | 			metadata: { analyzedAt: new Date().toISOString() }
 71 | 		};
 72 | 
 73 | 		// Find the complexity report path for this tag
 74 | 		const complexityReportPath = findComplexityReportPath(
 75 | 			null,
 76 | 			{ projectRoot, tag },
 77 | 			null
 78 | 		);
 79 | 
 80 | 		if (!complexityReportPath) {
 81 | 			log('warn', 'No complexity report found - cannot re-analyze complexity');
 82 | 			return null;
 83 | 		}
 84 | 
 85 | 		// Use analyze-task-complexity to re-analyze just this task
 86 | 		const analysisOptions = {
 87 | 			file: tasksPath,
 88 | 			output: complexityReportPath,
 89 | 			id: task.id.toString(), // Analyze only this specific task
 90 | 			projectRoot,
 91 | 			tag,
 92 | 			_filteredTasksData: tasksForAnalysis, // Pass pre-filtered data
 93 | 			_originalTaskCount: 1
 94 | 		};
 95 | 
 96 | 		// Run the analysis with proper context
 97 | 		await analyzeTaskComplexity(analysisOptions, { session });
 98 | 
 99 | 		// Read the updated complexity report to get the new score
100 | 		const updatedReport = readComplexityReport(complexityReportPath);
101 | 		if (updatedReport) {
102 | 			const taskAnalysis = findTaskInComplexityReport(updatedReport, task.id);
103 | 			if (taskAnalysis) {
104 | 				log(
105 | 					'info',
106 | 					`Re-analyzed task ${task.id} complexity: ${taskAnalysis.complexityScore}/10`
107 | 				);
108 | 				return taskAnalysis.complexityScore;
109 | 			}
110 | 		}
111 | 
112 | 		log(
113 | 			'warn',
114 | 			`Could not find updated complexity analysis for task ${task.id}`
115 | 		);
116 | 		return null;
117 | 	} catch (error) {
118 | 		log('error', `Failed to re-analyze task complexity: ${error.message}`);
119 | 		return null;
120 | 	}
121 | }
122 | 
123 | /**
124 |  * Gets the current complexity score for a task from the complexity report
125 |  * @param {number} taskId - Task ID to look up
126 |  * @param {Object} context - Context containing projectRoot, tag
127 |  * @returns {number|null} Current complexity score or null if not found
128 |  */
129 | function getCurrentComplexityScore(taskId, context) {
130 | 	const { projectRoot, tag } = context;
131 | 
132 | 	try {
133 | 		// Find the complexity report path for this tag
134 | 		const complexityReportPath = findComplexityReportPath(
135 | 			null,
136 | 			{ projectRoot, tag },
137 | 			null
138 | 		);
139 | 
140 | 		if (!complexityReportPath) {
141 | 			return null;
142 | 		}
143 | 
144 | 		// Read the current complexity report
145 | 		const complexityReport = readComplexityReport(complexityReportPath);
146 | 		if (!complexityReport) {
147 | 			return null;
148 | 		}
149 | 
150 | 		// Find this task's current complexity
151 | 		const taskAnalysis = findTaskInComplexityReport(complexityReport, taskId);
152 | 		return taskAnalysis ? taskAnalysis.complexityScore : null;
153 | 	} catch (error) {
154 | 		log('debug', `Could not read current complexity score: ${error.message}`);
155 | 		return null;
156 | 	}
157 | }
158 | 
159 | /**
160 |  * Regenerates subtasks for a task based on new complexity while preserving completed work
161 |  * @param {Object} task - The updated task object
162 |  * @param {string} tasksPath - Path to tasks.json
163 |  * @param {Object} context - Context containing projectRoot, tag, session
164 |  * @param {string} direction - Direction of scope change (up/down) for logging
165 |  * @param {string} strength - Strength level ('light', 'regular', 'heavy')
166 |  * @param {number|null} originalComplexity - Original complexity score for smarter adjustments
167 |  * @returns {Promise<Object>} Object with updated task and regeneration info
168 |  */
169 | async function regenerateSubtasksForComplexity(
170 | 	task,
171 | 	tasksPath,
172 | 	context,
173 | 	direction,
174 | 	strength = 'regular',
175 | 	originalComplexity = null
176 | ) {
177 | 	const { projectRoot, tag, session } = context;
178 | 
179 | 	// Check if task has subtasks
180 | 	if (
181 | 		!task.subtasks ||
182 | 		!Array.isArray(task.subtasks) ||
183 | 		task.subtasks.length === 0
184 | 	) {
185 | 		return {
186 | 			updatedTask: task,
187 | 			regenerated: false,
188 | 			preserved: 0,
189 | 			generated: 0
190 | 		};
191 | 	}
192 | 
193 | 	// Identify subtasks to preserve vs regenerate
194 | 	const preservedSubtasks = task.subtasks.filter((subtask) =>
195 | 		PRESERVE_STATUSES.includes(subtask.status)
196 | 	);
197 | 	const pendingSubtasks = task.subtasks.filter((subtask) =>
198 | 		REGENERATE_STATUSES.includes(subtask.status)
199 | 	);
200 | 
201 | 	// If no pending subtasks, nothing to regenerate
202 | 	if (pendingSubtasks.length === 0) {
203 | 		return {
204 | 			updatedTask: task,
205 | 			regenerated: false,
206 | 			preserved: preservedSubtasks.length,
207 | 			generated: 0
208 | 		};
209 | 	}
210 | 
211 | 	// Calculate appropriate number of total subtasks based on direction, complexity, strength, and original complexity
212 | 	let targetSubtaskCount;
213 | 	const preservedCount = preservedSubtasks.length;
214 | 	const currentPendingCount = pendingSubtasks.length;
215 | 
216 | 	// Use original complexity to inform decisions (if available)
217 | 	const complexityFactor = originalComplexity
218 | 		? Math.max(0.5, originalComplexity / 10)
219 | 		: 1.0;
220 | 	const complexityInfo = originalComplexity
221 | 		? ` (original complexity: ${originalComplexity}/10)`
222 | 		: '';
223 | 
224 | 	if (direction === 'up') {
225 | 		// Scope up: More subtasks for increased complexity
226 | 		if (strength === 'light') {
227 | 			const base = Math.max(
228 | 				5,
229 | 				preservedCount + Math.ceil(currentPendingCount * 1.1)
230 | 			);
231 | 			targetSubtaskCount = Math.ceil(base * (0.8 + 0.4 * complexityFactor));
232 | 		} else if (strength === 'regular') {
233 | 			const base = Math.max(
234 | 				6,
235 | 				preservedCount + Math.ceil(currentPendingCount * 1.3)
236 | 			);
237 | 			targetSubtaskCount = Math.ceil(base * (0.8 + 0.4 * complexityFactor));
238 | 		} else {
239 | 			// heavy
240 | 			const base = Math.max(
241 | 				8,
242 | 				preservedCount + Math.ceil(currentPendingCount * 1.6)
243 | 			);
244 | 			targetSubtaskCount = Math.ceil(base * (0.8 + 0.6 * complexityFactor));
245 | 		}
246 | 	} else {
247 | 		// Scope down: Fewer subtasks for decreased complexity
248 | 		// High complexity tasks get reduced more aggressively
249 | 		const aggressiveFactor =
250 | 			originalComplexity >= 8 ? 0.7 : originalComplexity >= 6 ? 0.85 : 1.0;
251 | 
252 | 		if (strength === 'light') {
253 | 			const base = Math.max(
254 | 				3,
255 | 				preservedCount + Math.ceil(currentPendingCount * 0.8)
256 | 			);
257 | 			targetSubtaskCount = Math.ceil(base * aggressiveFactor);
258 | 		} else if (strength === 'regular') {
259 | 			const base = Math.max(
260 | 				3,
261 | 				preservedCount + Math.ceil(currentPendingCount * 0.5)
262 | 			);
263 | 			targetSubtaskCount = Math.ceil(base * aggressiveFactor);
264 | 		} else {
265 | 			// heavy
266 | 			// Heavy scope-down should be much more aggressive - aim for only core functionality
267 | 			// Very high complexity tasks (9-10) get reduced to almost nothing
268 | 			const ultraAggressiveFactor =
269 | 				originalComplexity >= 9 ? 0.3 : originalComplexity >= 7 ? 0.5 : 0.7;
270 | 			const base = Math.max(
271 | 				2,
272 | 				preservedCount + Math.ceil(currentPendingCount * 0.25)
273 | 			);
274 | 			targetSubtaskCount = Math.max(1, Math.ceil(base * ultraAggressiveFactor));
275 | 		}
276 | 	}
277 | 
278 | 	log(
279 | 		'debug',
280 | 		`Complexity-aware subtask calculation${complexityInfo}: ${currentPendingCount} pending -> target ${targetSubtaskCount} total`
281 | 	);
282 | 	log(
283 | 		'debug',
284 | 		`Complexity-aware calculation${complexityInfo}: ${currentPendingCount} pending -> ${targetSubtaskCount} total subtasks (${strength} ${direction})`
285 | 	);
286 | 
287 | 	const newSubtasksNeeded = Math.max(1, targetSubtaskCount - preservedCount);
288 | 
289 | 	try {
290 | 		// Generate new subtasks using AI to match the new complexity level
291 | 		const systemPrompt = `You are an expert project manager who creates task breakdowns that match complexity levels.`;
292 | 
293 | 		const prompt = `Based on this updated task, generate ${newSubtasksNeeded} NEW subtasks that reflect the ${direction === 'up' ? 'increased' : 'decreased'} complexity level:
294 | 
295 | **Task Title**: ${task.title}
296 | **Task Description**: ${task.description}
297 | **Implementation Details**: ${task.details}
298 | **Test Strategy**: ${task.testStrategy}
299 | 
300 | **Complexity Direction**: This task was recently scoped ${direction} (${strength} strength) to ${direction === 'up' ? 'increase' : 'decrease'} complexity.
301 | ${originalComplexity ? `**Original Complexity**: ${originalComplexity}/10 - consider this when determining appropriate scope level.` : ''}
302 | 
303 | ${preservedCount > 0 ? `**Preserved Subtasks**: ${preservedCount} existing subtasks with work already done will be kept.` : ''}
304 | 
305 | Generate subtasks that:
306 | ${
307 | 	direction === 'up'
308 | 		? strength === 'heavy'
309 | 			? `- Add comprehensive implementation steps with advanced features
310 | - Include extensive error handling, validation, and edge cases
311 | - Cover multiple integration scenarios and advanced testing
312 | - Provide thorough documentation and optimization approaches`
313 | 			: strength === 'regular'
314 | 				? `- Add more detailed implementation steps
315 | - Include additional error handling and validation
316 | - Cover more edge cases and advanced features
317 | - Provide more comprehensive testing approaches`
318 | 				: `- Add some additional implementation details
319 | - Include basic error handling considerations
320 | - Cover a few common edge cases
321 | - Enhance testing approaches slightly`
322 | 		: strength === 'heavy'
323 | 			? `- Focus ONLY on absolutely essential core functionality
324 | - Strip out ALL non-critical features (error handling, advanced testing, etc.)
325 | - Provide only the minimum viable implementation
326 | - Eliminate any complex integrations or advanced scenarios
327 | - Aim for the simplest possible working solution`
328 | 			: strength === 'regular'
329 | 				? `- Focus on core functionality only
330 | - Simplify implementation steps
331 | - Remove non-essential features
332 | - Streamline to basic requirements`
333 | 				: `- Focus mainly on core functionality
334 | - Slightly simplify implementation steps
335 | - Remove some non-essential features
336 | - Streamline most requirements`
337 | }
338 | 
339 | Return a JSON object with a "subtasks" array. Each subtask should have:
340 | - id: Sequential NUMBER starting from 1 (e.g., 1, 2, 3 - NOT "1", "2", "3")
341 | - title: Clear, specific title
342 | - description: Detailed description
343 | - dependencies: Array of dependency IDs as STRINGS (use format ["${task.id}.1", "${task.id}.2"] for siblings, or empty array [] for no dependencies)
344 | - details: Implementation guidance
345 | - status: "pending"
346 | - testStrategy: Testing approach
347 | 
348 | IMPORTANT: 
349 | - The 'id' field must be a NUMBER, not a string!
350 | - Dependencies must be strings, not numbers!
351 | 
352 | Ensure the JSON is valid and properly formatted.`;
353 | 
354 | 		// Define subtask schema
355 | 		const subtaskSchema = z.object({
356 | 			subtasks: z.array(
357 | 				z.object({
358 | 					id: z.int().positive(),
359 | 					title: z.string().min(5),
360 | 					description: z.string().min(10),
361 | 					dependencies: z.array(z.string()),
362 | 					details: z.string().min(20),
363 | 					status: z.string(),
364 | 					testStrategy: z.string()
365 | 				})
366 | 			)
367 | 		});
368 | 
369 | 		const aiResult = await generateObjectService({
370 | 			role: context.research ? 'research' : 'main',
371 | 			session: context.session,
372 | 			systemPrompt,
373 | 			prompt,
374 | 			schema: subtaskSchema,
375 | 			objectName: 'subtask_regeneration',
376 | 			commandName: context.commandName || `subtask-regen-${direction}`,
377 | 			outputType: context.outputType || 'cli'
378 | 		});
379 | 
380 | 		const generatedSubtasks = aiResult.mainResult.subtasks || [];
381 | 
382 | 		// Post-process generated subtasks to ensure defaults
383 | 		const processedGeneratedSubtasks = generatedSubtasks.map((subtask) => ({
384 | 			...subtask,
385 | 			status: subtask.status || 'pending',
386 | 			testStrategy: subtask.testStrategy || ''
387 | 		}));
388 | 
389 | 		// Ensure new subtasks have unique sequential IDs after the preserved ones
390 | 		const maxPreservedId = preservedSubtasks.reduce(
391 | 			(max, st) => Math.max(max, st.id || 0),
392 | 			0
393 | 		);
394 | 		let nextId = maxPreservedId + 1;
395 | 		const idMapping = new Map();
396 | 		const normalizedGeneratedSubtasks = processedGeneratedSubtasks
397 | 			.map((st) => {
398 | 				const originalId = st.id;
399 | 				const newId = nextId++;
400 | 				idMapping.set(originalId, newId);
401 | 				return {
402 | 					...st,
403 | 					id: newId
404 | 				};
405 | 			})
406 | 			.map((st) => ({
407 | 				...st,
408 | 				dependencies: (st.dependencies || []).map((dep) => {
409 | 					if (typeof dep !== 'string' || !dep.startsWith(`${task.id}.`)) {
410 | 						return dep;
411 | 					}
412 | 					const [, siblingIdPart] = dep.split('.');
413 | 					const originalSiblingId = Number.parseInt(siblingIdPart, 10);
414 | 					const remappedSiblingId = idMapping.get(originalSiblingId);
415 | 					return remappedSiblingId ? `${task.id}.${remappedSiblingId}` : dep;
416 | 				})
417 | 			}));
418 | 
419 | 		// Update task with preserved subtasks + newly generated ones
420 | 		task.subtasks = [...preservedSubtasks, ...normalizedGeneratedSubtasks];
421 | 
422 | 		return {
423 | 			updatedTask: task,
424 | 			regenerated: true,
425 | 			preserved: preservedSubtasks.length,
426 | 			generated: normalizedGeneratedSubtasks.length
427 | 		};
428 | 	} catch (error) {
429 | 		log(
430 | 			'warn',
431 | 			`Failed to regenerate subtasks for task ${task.id}: ${error.message}`
432 | 		);
433 | 		// Don't fail the whole operation if subtask regeneration fails
434 | 		return {
435 | 			updatedTask: task,
436 | 			regenerated: false,
437 | 			preserved: preservedSubtasks.length,
438 | 			generated: 0,
439 | 			error: error.message
440 | 		};
441 | 	}
442 | }
443 | 
444 | /**
445 |  * Generates AI prompt for scope adjustment
446 |  * @param {Object} task - The task to adjust
447 |  * @param {string} direction - 'up' or 'down'
448 |  * @param {string} strength - 'light', 'regular', or 'heavy'
449 |  * @param {string} customPrompt - Optional custom instructions
450 |  * @returns {string} The generated prompt
451 |  */
452 | function generateScopePrompt(task, direction, strength, customPrompt) {
453 | 	const isUp = direction === 'up';
454 | 	const strengthDescriptions = {
455 | 		light: isUp ? 'minor enhancements' : 'slight simplifications',
456 | 		regular: isUp
457 | 			? 'moderate complexity increases'
458 | 			: 'moderate simplifications',
459 | 		heavy: isUp ? 'significant complexity additions' : 'major simplifications'
460 | 	};
461 | 
462 | 	let basePrompt = `You are tasked with adjusting the complexity of a task. 
463 | 
464 | CURRENT TASK:
465 | Title: ${task.title}
466 | Description: ${task.description}
467 | Details: ${task.details}
468 | Test Strategy: ${task.testStrategy || 'Not specified'}
469 | 
470 | ADJUSTMENT REQUIREMENTS:
471 | - Direction: ${isUp ? 'INCREASE' : 'DECREASE'} complexity
472 | - Strength: ${strength} (${strengthDescriptions[strength]})
473 | - Preserve the core purpose and functionality of the task
474 | - Maintain consistency with the existing task structure`;
475 | 
476 | 	if (isUp) {
477 | 		basePrompt += `
478 | - Add more detailed requirements, edge cases, or advanced features
479 | - Include additional implementation considerations
480 | - Enhance error handling and validation requirements
481 | - Expand testing strategies with more comprehensive scenarios`;
482 | 	} else {
483 | 		basePrompt += `
484 | - Focus on core functionality and essential requirements
485 | - Remove or simplify non-essential features  
486 | - Streamline implementation details
487 | - Simplify testing to focus on basic functionality`;
488 | 	}
489 | 
490 | 	if (customPrompt) {
491 | 		basePrompt += `\n\nCUSTOM INSTRUCTIONS:\n${customPrompt}`;
492 | 	}
493 | 
494 | 	basePrompt += `\n\nReturn a JSON object with the updated task containing these fields:
495 | - title: Updated task title
496 | - description: Updated task description  
497 | - details: Updated implementation details
498 | - testStrategy: Updated test strategy
499 | - priority: Task priority ('low', 'medium', or 'high')
500 | 
501 | Ensure the JSON is valid and properly formatted.`;
502 | 
503 | 	return basePrompt;
504 | }
505 | 
506 | /**
507 |  * Adjusts task complexity using AI
508 |  * @param {Object} task - The task to adjust
509 |  * @param {string} direction - 'up' or 'down'
510 |  * @param {string} strength - 'light', 'regular', or 'heavy'
511 |  * @param {string} customPrompt - Optional custom instructions
512 |  * @param {Object} context - Context object with projectRoot, tag, etc.
513 |  * @returns {Promise<Object>} Updated task data and telemetry
514 |  */
515 | async function adjustTaskComplexity(
516 | 	task,
517 | 	direction,
518 | 	strength,
519 | 	customPrompt,
520 | 	context
521 | ) {
522 | 	const systemPrompt = `You are an expert software project manager who helps adjust task complexity while maintaining clarity and actionability.`;
523 | 
524 | 	const prompt = generateScopePrompt(task, direction, strength, customPrompt);
525 | 
526 | 	// Define the task schema for structured response using Zod
527 | 	const taskSchema = z.object({
528 | 		title: z
529 | 			.string()
530 | 			.min(1)
531 | 			.describe('Updated task title reflecting scope adjustment'),
532 | 		description: z
533 | 			.string()
534 | 			.min(1)
535 | 			.describe('Updated task description with adjusted scope'),
536 | 		details: z
537 | 			.string()
538 | 			.min(1)
539 | 			.describe('Updated implementation details with adjusted complexity'),
540 | 		testStrategy: z
541 | 			.string()
542 | 			.min(1)
543 | 			.describe('Updated testing approach for the adjusted scope'),
544 | 		priority: z.enum(['low', 'medium', 'high']).describe('Task priority level')
545 | 	});
546 | 
547 | 	const aiResult = await generateObjectService({
548 | 		role: context.research ? 'research' : 'main',
549 | 		session: context.session,
550 | 		systemPrompt,
551 | 		prompt,
552 | 		schema: taskSchema,
553 | 		objectName: 'updated_task',
554 | 		commandName: context.commandName || `scope-${direction}`,
555 | 		outputType: context.outputType || 'cli'
556 | 	});
557 | 
558 | 	const updatedTaskData = aiResult.mainResult;
559 | 
560 | 	// Ensure priority has a value (in case AI didn't provide one)
561 | 	const processedTaskData = {
562 | 		...updatedTaskData,
563 | 		priority: updatedTaskData.priority || task.priority || 'medium'
564 | 	};
565 | 
566 | 	return {
567 | 		updatedTask: {
568 | 			...task,
569 | 			...processedTaskData
570 | 		},
571 | 		telemetryData: aiResult.telemetryData
572 | 	};
573 | }
574 | 
575 | /**
576 |  * Increases task complexity (scope-up)
577 |  * @param {string} tasksPath - Path to tasks.json file
578 |  * @param {Array<number>} taskIds - Array of task IDs to scope up
579 |  * @param {string} strength - Strength level ('light', 'regular', 'heavy')
580 |  * @param {string} customPrompt - Optional custom instructions
581 |  * @param {Object} context - Context object with projectRoot, tag, etc.
582 |  * @param {string} outputFormat - Output format ('text' or 'json')
583 |  * @returns {Promise<Object>} Results of the scope-up operation
584 |  */
585 | export async function scopeUpTask(
586 | 	tasksPath,
587 | 	taskIds,
588 | 	strength = 'regular',
589 | 	customPrompt = null,
590 | 	context = {},
591 | 	outputFormat = 'text'
592 | ) {
593 | 	// Validate inputs
594 | 	if (!validateStrength(strength)) {
595 | 		throw new Error(
596 | 			`Invalid strength level: ${strength}. Must be one of: ${VALID_STRENGTHS.join(', ')}`
597 | 		);
598 | 	}
599 | 
600 | 	const { projectRoot = '.', tag = 'master' } = context;
601 | 
602 | 	// Read tasks data
603 | 	const data = readJSON(tasksPath, projectRoot, tag);
604 | 	const tasks = data?.tasks || [];
605 | 
606 | 	// Validate all task IDs exist
607 | 	for (const taskId of taskIds) {
608 | 		if (!taskExists(tasks, taskId)) {
609 | 			throw new Error(`Task with ID ${taskId} not found`);
610 | 		}
611 | 	}
612 | 
613 | 	const updatedTasks = [];
614 | 	let combinedTelemetryData = null;
615 | 
616 | 	// Process each task
617 | 	for (const taskId of taskIds) {
618 | 		const taskResult = findTaskById(tasks, taskId);
619 | 		const task = taskResult.task;
620 | 		if (!task) {
621 | 			throw new Error(`Task with ID ${taskId} not found`);
622 | 		}
623 | 
624 | 		if (outputFormat === 'text') {
625 | 			log('info', `Scoping up task ${taskId}: ${task.title}`);
626 | 		}
627 | 
628 | 		// Get original complexity score (if available)
629 | 		const originalComplexity = getCurrentComplexityScore(taskId, context);
630 | 		if (originalComplexity && outputFormat === 'text') {
631 | 			log('info', `Original complexity: ${originalComplexity}/10`);
632 | 		}
633 | 
634 | 		const adjustResult = await adjustTaskComplexity(
635 | 			task,
636 | 			'up',
637 | 			strength,
638 | 			customPrompt,
639 | 			context
640 | 		);
641 | 
642 | 		// Regenerate subtasks based on new complexity while preserving completed work
643 | 		const subtaskResult = await regenerateSubtasksForComplexity(
644 | 			adjustResult.updatedTask,
645 | 			tasksPath,
646 | 			context,
647 | 			'up',
648 | 			strength,
649 | 			originalComplexity
650 | 		);
651 | 
652 | 		// Log subtask regeneration info if in text mode
653 | 		if (outputFormat === 'text' && subtaskResult.regenerated) {
654 | 			log(
655 | 				'info',
656 | 				`Regenerated ${subtaskResult.generated} pending subtasks (preserved ${subtaskResult.preserved} completed)`
657 | 			);
658 | 		}
659 | 
660 | 		// Update task in data
661 | 		const taskIndex = data.tasks.findIndex((t) => t.id === taskId);
662 | 		if (taskIndex !== -1) {
663 | 			data.tasks[taskIndex] = subtaskResult.updatedTask;
664 | 			updatedTasks.push(subtaskResult.updatedTask);
665 | 		}
666 | 
667 | 		// Re-analyze complexity after scoping (if we have a session for AI calls)
668 | 		if (context.session && originalComplexity) {
669 | 			try {
670 | 				// Write the updated task first so complexity analysis can read it
671 | 				writeJSON(tasksPath, data, projectRoot, tag);
672 | 
673 | 				// Re-analyze complexity
674 | 				const newComplexity = await reanalyzeTaskComplexity(
675 | 					subtaskResult.updatedTask,
676 | 					tasksPath,
677 | 					context
678 | 				);
679 | 				if (newComplexity && outputFormat === 'text') {
680 | 					const complexityChange = newComplexity - originalComplexity;
681 | 					const arrow =
682 | 						complexityChange > 0 ? '↗️' : complexityChange < 0 ? '↘️' : '➡️';
683 | 					log(
684 | 						'info',
685 | 						`New complexity: ${originalComplexity}/10 ${arrow} ${newComplexity}/10 (${complexityChange > 0 ? '+' : ''}${complexityChange})`
686 | 					);
687 | 				}
688 | 			} catch (error) {
689 | 				if (outputFormat === 'text') {
690 | 					log('warn', `Could not re-analyze complexity: ${error.message}`);
691 | 				}
692 | 			}
693 | 		}
694 | 
695 | 		// Combine telemetry data
696 | 		if (adjustResult.telemetryData) {
697 | 			if (!combinedTelemetryData) {
698 | 				combinedTelemetryData = { ...adjustResult.telemetryData };
699 | 			} else {
700 | 				// Sum up costs and tokens
701 | 				combinedTelemetryData.inputTokens +=
702 | 					adjustResult.telemetryData.inputTokens || 0;
703 | 				combinedTelemetryData.outputTokens +=
704 | 					adjustResult.telemetryData.outputTokens || 0;
705 | 				combinedTelemetryData.totalTokens +=
706 | 					adjustResult.telemetryData.totalTokens || 0;
707 | 				combinedTelemetryData.totalCost +=
708 | 					adjustResult.telemetryData.totalCost || 0;
709 | 			}
710 | 		}
711 | 	}
712 | 
713 | 	// Write updated data
714 | 	writeJSON(tasksPath, data, projectRoot, tag);
715 | 
716 | 	if (outputFormat === 'text') {
717 | 		log('info', `Successfully scoped up ${updatedTasks.length} task(s)`);
718 | 	}
719 | 
720 | 	return {
721 | 		updatedTasks,
722 | 		telemetryData: combinedTelemetryData
723 | 	};
724 | }
725 | 
726 | /**
727 |  * Decreases task complexity (scope-down)
728 |  * @param {string} tasksPath - Path to tasks.json file
729 |  * @param {Array<number>} taskIds - Array of task IDs to scope down
730 |  * @param {string} strength - Strength level ('light', 'regular', 'heavy')
731 |  * @param {string} customPrompt - Optional custom instructions
732 |  * @param {Object} context - Context object with projectRoot, tag, etc.
733 |  * @param {string} outputFormat - Output format ('text' or 'json')
734 |  * @returns {Promise<Object>} Results of the scope-down operation
735 |  */
736 | export async function scopeDownTask(
737 | 	tasksPath,
738 | 	taskIds,
739 | 	strength = 'regular',
740 | 	customPrompt = null,
741 | 	context = {},
742 | 	outputFormat = 'text'
743 | ) {
744 | 	// Validate inputs
745 | 	if (!validateStrength(strength)) {
746 | 		throw new Error(
747 | 			`Invalid strength level: ${strength}. Must be one of: ${VALID_STRENGTHS.join(', ')}`
748 | 		);
749 | 	}
750 | 
751 | 	const { projectRoot = '.', tag = 'master' } = context;
752 | 
753 | 	// Read tasks data
754 | 	const data = readJSON(tasksPath, projectRoot, tag);
755 | 	const tasks = data?.tasks || [];
756 | 
757 | 	// Validate all task IDs exist
758 | 	for (const taskId of taskIds) {
759 | 		if (!taskExists(tasks, taskId)) {
760 | 			throw new Error(`Task with ID ${taskId} not found`);
761 | 		}
762 | 	}
763 | 
764 | 	const updatedTasks = [];
765 | 	let combinedTelemetryData = null;
766 | 
767 | 	// Process each task
768 | 	for (const taskId of taskIds) {
769 | 		const taskResult = findTaskById(tasks, taskId);
770 | 		const task = taskResult.task;
771 | 		if (!task) {
772 | 			throw new Error(`Task with ID ${taskId} not found`);
773 | 		}
774 | 
775 | 		if (outputFormat === 'text') {
776 | 			log('info', `Scoping down task ${taskId}: ${task.title}`);
777 | 		}
778 | 
779 | 		// Get original complexity score (if available)
780 | 		const originalComplexity = getCurrentComplexityScore(taskId, context);
781 | 		if (originalComplexity && outputFormat === 'text') {
782 | 			log('info', `Original complexity: ${originalComplexity}/10`);
783 | 		}
784 | 
785 | 		const adjustResult = await adjustTaskComplexity(
786 | 			task,
787 | 			'down',
788 | 			strength,
789 | 			customPrompt,
790 | 			context
791 | 		);
792 | 
793 | 		// Regenerate subtasks based on new complexity while preserving completed work
794 | 		const subtaskResult = await regenerateSubtasksForComplexity(
795 | 			adjustResult.updatedTask,
796 | 			tasksPath,
797 | 			context,
798 | 			'down',
799 | 			strength,
800 | 			originalComplexity
801 | 		);
802 | 
803 | 		// Log subtask regeneration info if in text mode
804 | 		if (outputFormat === 'text' && subtaskResult.regenerated) {
805 | 			log(
806 | 				'info',
807 | 				`Regenerated ${subtaskResult.generated} pending subtasks (preserved ${subtaskResult.preserved} completed)`
808 | 			);
809 | 		}
810 | 
811 | 		// Update task in data
812 | 		const taskIndex = data.tasks.findIndex((t) => t.id === taskId);
813 | 		if (taskIndex !== -1) {
814 | 			data.tasks[taskIndex] = subtaskResult.updatedTask;
815 | 			updatedTasks.push(subtaskResult.updatedTask);
816 | 		}
817 | 
818 | 		// Re-analyze complexity after scoping (if we have a session for AI calls)
819 | 		if (context.session && originalComplexity) {
820 | 			try {
821 | 				// Write the updated task first so complexity analysis can read it
822 | 				writeJSON(tasksPath, data, projectRoot, tag);
823 | 
824 | 				// Re-analyze complexity
825 | 				const newComplexity = await reanalyzeTaskComplexity(
826 | 					subtaskResult.updatedTask,
827 | 					tasksPath,
828 | 					context
829 | 				);
830 | 				if (newComplexity && outputFormat === 'text') {
831 | 					const complexityChange = newComplexity - originalComplexity;
832 | 					const arrow =
833 | 						complexityChange > 0 ? '↗️' : complexityChange < 0 ? '↘️' : '➡️';
834 | 					log(
835 | 						'info',
836 | 						`New complexity: ${originalComplexity}/10 ${arrow} ${newComplexity}/10 (${complexityChange > 0 ? '+' : ''}${complexityChange})`
837 | 					);
838 | 				}
839 | 			} catch (error) {
840 | 				if (outputFormat === 'text') {
841 | 					log('warn', `Could not re-analyze complexity: ${error.message}`);
842 | 				}
843 | 			}
844 | 		}
845 | 
846 | 		// Combine telemetry data
847 | 		if (adjustResult.telemetryData) {
848 | 			if (!combinedTelemetryData) {
849 | 				combinedTelemetryData = { ...adjustResult.telemetryData };
850 | 			} else {
851 | 				// Sum up costs and tokens
852 | 				combinedTelemetryData.inputTokens +=
853 | 					adjustResult.telemetryData.inputTokens || 0;
854 | 				combinedTelemetryData.outputTokens +=
855 | 					adjustResult.telemetryData.outputTokens || 0;
856 | 				combinedTelemetryData.totalTokens +=
857 | 					adjustResult.telemetryData.totalTokens || 0;
858 | 				combinedTelemetryData.totalCost +=
859 | 					adjustResult.telemetryData.totalCost || 0;
860 | 			}
861 | 		}
862 | 	}
863 | 
864 | 	// Write updated data
865 | 	writeJSON(tasksPath, data, projectRoot, tag);
866 | 
867 | 	if (outputFormat === 'text') {
868 | 		log('info', `Successfully scoped down ${updatedTasks.length} task(s)`);
869 | 	}
870 | 
871 | 	return {
872 | 		updatedTasks,
873 | 		telemetryData: combinedTelemetryData
874 | 	};
875 | }
876 | 
```
Page 48/69FirstPrevNextLast