diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 00000000..2cba423e --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,22 @@ +{ + "mode": "pre", + "tag": "rc", + "initialVersions": { + "task-master-ai": "0.29.0", + "@tm/cli": "", + "docs": "0.0.6", + "extension": "0.25.6", + "@tm/mcp": "0.28.0-rc.2", + "@tm/ai-sdk-provider-grok-cli": "", + "@tm/build-config": "", + "@tm/claude-code-plugin": "0.0.2", + "@tm/core": "" + }, + "changesets": [ + "dirty-hairs-know", + "fix-parent-directory-traversal", + "fix-warning-box-alignment", + "light-owls-stay", + "metal-rocks-help" + ] +} diff --git a/.changeset/some-dodos-wonder.md b/.changeset/some-dodos-wonder.md new file mode 100644 index 00000000..3af01c7c --- /dev/null +++ b/.changeset/some-dodos-wonder.md @@ -0,0 +1,36 @@ +--- +"task-master-ai": minor +--- + +Add autonomous TDD workflow automation system with new `tm autopilot` commands and MCP tools for AI-driven test-driven development. + +**New CLI Commands:** + +- `tm autopilot start ` - Initialize TDD workflow +- `tm autopilot next` - Get next action in workflow +- `tm autopilot status` - Check workflow progress +- `tm autopilot complete` - Advance phase with test results +- `tm autopilot commit` - Save progress with metadata +- `tm autopilot resume` - Continue from checkpoint +- `tm autopilot abort` - Cancel workflow + +**New MCP Tools:** +Seven new autopilot tools for programmatic control: `autopilot_start`, `autopilot_next`, `autopilot_status`, `autopilot_complete_phase`, `autopilot_commit`, `autopilot_resume`, `autopilot_abort` + +**Features:** + +- Complete RED → GREEN → COMMIT cycle enforcement +- Intelligent commit message generation with metadata +- Activity logging and state persistence +- Configurable workflow settings via `.taskmaster/config.json` +- Comprehensive AI agent integration documentation + +**Documentation:** + +- AI Agent Integration Guide (2,800+ lines) +- TDD Quick Start Guide +- Example prompts and integration patterns + +> **Learn more:** [TDD Workflow Quickstart Guide](https://dev.task-master.dev/tdd-workflow/quickstart) + +This release enables AI agents to autonomously execute test-driven development workflows with full state management and recovery capabilities. diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 0f96eb68..0a308455 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -1,10 +1,3 @@ reviews: - profile: assertive + profile: chill poem: false - auto_review: - base_branches: - - rc - - beta - - alpha - - production - - next \ No newline at end of file diff --git a/.gitignore b/.gitignore index d5a2b42b..be9395b4 100644 --- a/.gitignore +++ b/.gitignore @@ -96,4 +96,7 @@ apps/extension/.vscode-test/ apps/extension/vsix-build/ # turbo -.turbo \ No newline at end of file +.turbo + +# TaskMaster Workflow State (now stored in ~/.taskmaster/sessions/) +# No longer needed in .gitignore as state is stored globally \ No newline at end of file diff --git a/.taskmaster/config.json b/.taskmaster/config.json index c1c0f929..1a3f929a 100644 --- a/.taskmaster/config.json +++ b/.taskmaster/config.json @@ -1,8 +1,8 @@ { "models": { "main": { - "provider": "anthropic", - "modelId": "claude-sonnet-4-20250514", + "provider": "claude-code", + "modelId": "sonnet", "maxTokens": 64000, "temperature": 0.2 }, @@ -35,6 +35,7 @@ "defaultTag": "master" }, "claudeCode": {}, + "codexCli": {}, "grokCli": { "timeout": 120000, "workingDirectory": null, diff --git a/.taskmaster/docs/autonomous-tdd-git-workflow.md b/.taskmaster/docs/autonomous-tdd-git-workflow.md new file mode 100644 index 00000000..3234c241 --- /dev/null +++ b/.taskmaster/docs/autonomous-tdd-git-workflow.md @@ -0,0 +1,912 @@ +## Summary + +- Put the existing git and test workflows on rails: a repeatable, automated process that can run autonomously, with guardrails and a compact TUI for visibility. + +- Flow: for a selected task, create a branch named with the tag + task id → generate tests for the first subtask (red) using the Surgical Test Generator → implement code (green) → verify tests → commit → repeat per subtask → final verify → push → open PR against the default branch. + +- Build on existing rules: .cursor/rules/git_workflow.mdc, .cursor/rules/test_workflow.mdc, .claude/agents/surgical-test-generator.md, and existing CLI/core services. + +## Goals + +- Deterministic, resumable automation to execute the TDD loop per subtask with minimal human intervention. + +- Strong guardrails: never commit to the default branch; only commit when tests pass; enforce status transitions; persist logs/state for debuggability. + +- Visibility: a compact terminal UI (like lazygit) to pick tag, view tasks, and start work; right-side pane opens an executor terminal (via tmux) for agent coding. + +- Extensible: framework-agnostic test generation via the Surgical Test Generator; detect and use the repo’s test command for execution with coverage thresholds. + +## Non‑Goals (initial) + +- Full multi-language runner parity beyond detection and executing the project’s test command. + +- Complex GUI; start with CLI/TUI + tmux pane. IDE/extension can hook into the same state later. + +- Rich executor selection UX (codex/gemini/claude) — we’ll prompt per run; defaults can come later. + +## Success Criteria + +- One command can autonomously complete a task's subtasks via TDD and open a PR when done. + +- All commits made on a branch that includes the tag and task id (see Branch Naming); no commits to the default branch directly. + +- Every subtask iteration: failing tests added first (red), then code added to pass them (green), commit only after green. + +- End-to-end logs + artifacts stored in .taskmaster/reports/runs//. + +## Success Metrics (Phase 1) + +- **Adoption**: 80% of tasks in a pilot repo completed via `tm autopilot` +- **Safety**: 0 commits to default branch; 100% of commits have green tests +- **Efficiency**: Average time from task start to PR < 30min for simple subtasks +- **Reliability**: < 5% of runs require manual intervention (timeout/conflicts) + +## User Stories + +- As a developer, I can run tm autopilot and watch a structured, safe workflow execute. + +- As a reviewer, I can inspect commits per subtask, and a PR summarizing the work when the task completes. + +- As an operator, I can see current step, active subtask, tests status, and logs in a compact CLI view and read a final run report. + +## Example Workflow Traces + +### Happy Path: Complete a 3-subtask feature + +```bash +# Developer starts +$ tm autopilot 42 +→ Checks preflight: ✓ clean tree, ✓ npm test detected +→ Creates branch: analytics/task-42-user-metrics +→ Subtask 42.1: "Add metrics schema" + RED: generates test_metrics_schema.test.js → 3 failures + GREEN: implements schema.js → all pass + COMMIT: "feat(metrics): add metrics schema (task 42.1)" +→ Subtask 42.2: "Add collection endpoint" + RED: generates test_metrics_endpoint.test.js → 5 failures + GREEN: implements api/metrics.js → all pass + COMMIT: "feat(metrics): add collection endpoint (task 42.2)" +→ Subtask 42.3: "Add dashboard widget" + RED: generates test_metrics_widget.test.js → 4 failures + GREEN: implements components/MetricsWidget.jsx → all pass + COMMIT: "feat(metrics): add dashboard widget (task 42.3)" +→ Final: all 3 subtasks complete + ✓ Run full test suite → all pass + ✓ Coverage check → 85% (meets 80% threshold) + PUSH: confirms with user → pushed to origin + PR: opens #123 "Task #42 [analytics]: User metrics tracking" + +✓ Task 42 complete. PR: https://github.com/org/repo/pull/123 + Run report: .taskmaster/reports/runs/2025-01-15-142033/ +``` + +### Error Recovery: Failing tests timeout + +```bash +$ tm autopilot 42 +→ Subtask 42.2 GREEN phase: attempt 1 fails (2 tests still red) +→ Subtask 42.2 GREEN phase: attempt 2 fails (1 test still red) +→ Subtask 42.2 GREEN phase: attempt 3 fails (1 test still red) + +⚠️ Paused: Could not achieve green state after 3 attempts +📋 State saved to: .taskmaster/reports/runs/2025-01-15-142033/ + Last error: "POST /api/metrics returns 500 instead of 201" + +Next steps: + - Review diff: git diff HEAD + - Inspect logs: cat .taskmaster/reports/runs/2025-01-15-142033/log.jsonl + - Check test output: cat .taskmaster/reports/runs/2025-01-15-142033/test-results/subtask-42.2-green-attempt3.json + - Resume after manual fix: tm autopilot --resume + +# Developer manually fixes the issue, then: +$ tm autopilot --resume +→ Resuming subtask 42.2 GREEN phase + GREEN: all tests pass + COMMIT: "feat(metrics): add collection endpoint (task 42.2)" +→ Continuing to subtask 42.3... +``` + +### Dry Run: Preview before execution + +```bash +$ tm autopilot 42 --dry-run +Autopilot Plan for Task #42 [analytics]: User metrics tracking +───────────────────────────────────────────────────────────── +Preflight: + ✓ Working tree is clean + ✓ Test command detected: npm test + ✓ Tools available: git, gh, node, npm + ✓ Current branch: main (will create new branch) + +Branch & Tag: + → Create branch: analytics/task-42-user-metrics + → Set active tag: analytics + +Subtasks (3 pending): + 1. 42.1: Add metrics schema + - RED: generate tests in src/__tests__/schema.test.js + - GREEN: implement src/schema.js + - COMMIT: "feat(metrics): add metrics schema (task 42.1)" + + 2. 42.2: Add collection endpoint [depends on 42.1] + - RED: generate tests in src/api/__tests__/metrics.test.js + - GREEN: implement src/api/metrics.js + - COMMIT: "feat(metrics): add collection endpoint (task 42.2)" + + 3. 42.3: Add dashboard widget [depends on 42.2] + - RED: generate tests in src/components/__tests__/MetricsWidget.test.jsx + - GREEN: implement src/components/MetricsWidget.jsx + - COMMIT: "feat(metrics): add dashboard widget (task 42.3)" + +Finalization: + → Run full test suite with coverage + → Push branch to origin (will confirm) + → Create PR targeting main + +Run without --dry-run to execute. +``` + +## High‑Level Workflow + +1) Pre‑flight + + - Verify clean working tree or confirm staging/commit policy (configurable). + + - Detect repo type and the project’s test command (e.g., npm test, pnpm test, pytest, go test). + + - Validate tools: git, gh (optional for PR), node/npm, and (if used) claude CLI. + + - Load TaskMaster state and selected task; if no subtasks exist, automatically run “expand” before working. + +2) Branch & Tag Setup + + - Checkout default branch and update (optional), then create a branch using Branch Naming (below). + + - Map branch ↔ tag via existing tag management; explicitly set active tag to the branch’s tag. + +3) Subtask Loop (for each pending/in-progress subtask in dependency order) + + - Select next eligible subtask using tm-core TaskService getNextTask() and subtask eligibility logic. + + - Red: generate or update failing tests for the subtask + + - Use the Surgical Test Generator system prompt .claude/agents/surgical-test-generator.md) to produce high-signal tests following project conventions. + + - Run tests to confirm red; record results. If not red (already passing), skip to next subtask or escalate. + + - Green: implement code to pass tests + + - Use executor to implement changes (initial: claude CLI prompt with focused context). + + - Re-run tests until green or timeout/backoff policy triggers. + + - Commit: when green + + - Commit tests + code with conventional commit message. Optionally update subtask status to done. + + - Persist run step metadata/logs. + +4) Finalization + + - Run full test suite and coverage (if configured); optionally lint/format. + + - Commit any final adjustments. + + - Push branch (ask user to confirm); create PR (via gh pr create) targeting the default branch. Title format: Task # []: . + +5) Post‑Run + + - Update task status if desired (e.g., review). + + - Persist run report (JSON + markdown summary) to .taskmaster/reports/runs/<run-id>/. + +## Guardrails + +- Never commit to the default branch. + +- Commit only if all tests (targeted and suite) pass; allow override flags. + +- Enforce 80% coverage thresholds (lines/branches/functions/statements) by default; configurable. + +- Timebox/model ops and retries; if not green within N attempts, pause with actionable state for resume. + +- Always log actions, commands, and outcomes; include dry-run mode. + +- Ask before branch creation, pushing, and opening a PR unless --no-confirm is set. + +## Integration Points (Current Repo) + +- CLI: apps/cli provides command structure and UI components. + + - New command: tm autopilot (alias: task-master autopilot). + + - Reuse UI components under apps/cli/src/ui/components/ for headers/task details/next-task. + +- Core services: packages/tm-core + + - TaskService for selection, status, tags. + + - TaskExecutionService for prompt formatting and executor prep. + + - Executors: claude executor and ExecutorFactory to run external tools. + + - Proposed new: WorkflowOrchestrator to drive the autonomous loop and emit progress events. + +- Tag/Git utilities: scripts/modules/utils/git-utils.js and scripts/modules/task-manager/tag-management.js for branch→tag mapping and explicit tag switching. + +- Rules: .cursor/rules/git_workflow.mdc and .cursor/rules/test_workflow.mdc to steer behavior and ensure consistency. + +- Test generation prompt: .claude/agents/surgical-test-generator.md. + +## Proposed Components + +- Orchestrator (tm-core): WorkflowOrchestrator (new) + + - State machine driving phases: Preflight → Branch/Tag → SubtaskIter (Red/Green/Commit) → Finalize → PR. + + - Exposes an evented API (progress events) that the CLI can render. + + - Stores run state artifacts. + +- Test Runner Adapter + + - Detects and runs tests via the project’s test command (e.g., npm test), with targeted runs where feasible. + + - API: runTargeted(files/pattern), runAll(), report summary (failures, duration, coverage), enforce 80% threshold by default. + +- Git/PR Adapter + + - Encapsulates git ops: branch create/checkout, add/commit, push. + + - Optional gh integration to open PR; fallback to instructions if gh unavailable. + + - Confirmation gates for branch creation and pushes. + +- Prompt/Exec Adapter + + - Uses existing executor service to call the selected coding assistant (initially claude) with tight prompts: task/subtask context, surgical tests first, then minimal code to green. + +- Run State + Reporting + + - JSONL log of steps, timestamps, commands, test results. + + - Markdown summary for PR description and post-run artifact. + +## CLI UX (MVP) + +- Command: tm autopilot [taskId] + + - Flags: --dry-run, --no-push, --no-pr, --no-confirm, --force, --max-attempts <n>, --runner <auto|custom>, --commit-scope <scope> + + - Output: compact header (project, tag, branch), current phase, subtask line, last test summary, next actions. + +- Resume: If interrupted, tm autopilot --resume picks up from last checkpoint in run state. + +### TUI with tmux (Linear Execution) + +- Left pane: Tag selector, task list (status/priority), start/expand shortcuts; "Start" triggers the next task or a selected task. + +- Right pane: Executor terminal (tmux split) that runs the coding agent (claude-code/codex). Autopilot can hand over to the right pane during green. + +- MCP integration: use MCP tools for task queries/updates and for shell/test invocations where available. + +## TUI Layout (tmux-based) + +### Pane Structure + +``` +┌─────────────────────────────────────┬──────────────────────────────────┐ +│ Task Navigator (left) │ Executor Terminal (right) │ +│ │ │ +│ Project: my-app │ $ tm autopilot --executor-mode │ +│ Branch: analytics/task-42 │ > Running subtask 42.2 GREEN... │ +│ Tag: analytics │ > Implementing endpoint... │ +│ │ > Tests: 3 passed, 0 failed │ +│ Tasks: │ > Ready to commit │ +│ → 42 [in-progress] User metrics │ │ +│ → 42.1 [done] Schema │ [Live output from Claude Code] │ +│ → 42.2 [active] Endpoint ◀ │ │ +│ → 42.3 [pending] Dashboard │ │ +│ │ │ +│ [s] start [p] pause [q] quit │ │ +└─────────────────────────────────────┴──────────────────────────────────┘ +``` + +### Implementation Notes + +- **Left pane**: `apps/cli/src/ui/tui/navigator.ts` (new, uses `blessed` or `ink`) +- **Right pane**: spawned via `tmux split-window -h` running `tm autopilot --executor-mode` +- **Communication**: shared state file `.taskmaster/state/current-run.json` + file watching or event stream +- **Keybindings**: + - `s` - Start selected task + - `p` - Pause/resume current run + - `q` - Quit (with confirmation if run active) + - `↑/↓` - Navigate task list + - `Enter` - Expand/collapse subtasks + +## Prompt Composition (Detailed) + +### System Prompt Assembly + +Prompts are composed in three layers: + +1. **Base rules** (loaded in order from `.cursor/rules/` and `.claude/agents/`): + - `git_workflow.mdc` → git commit conventions, branch policy, PR guidelines + - `test_workflow.mdc` → TDD loop requirements, coverage thresholds, test structure + - `surgical-test-generator.md` → test generation methodology, project-specific test patterns + +2. **Task context injection**: + ``` + You are implementing: + Task #42 [analytics]: User metrics tracking + Subtask 42.2: Add collection endpoint + + Description: + Implement POST /api/metrics endpoint to collect user metrics events + + Acceptance criteria: + - POST /api/metrics accepts { userId, eventType, timestamp } + - Validates input schema (reject missing/invalid fields) + - Persists to database + - Returns 201 on success with created record + - Returns 400 on validation errors + + Dependencies: + - Subtask 42.1 (metrics schema) is complete + + Current phase: RED (generate failing tests) + Test command: npm test + Test file convention: src/**/*.test.js (vitest framework detected) + Branch: analytics/task-42-user-metrics + Project language: JavaScript (Node.js) + ``` + +3. **Phase-specific instructions**: + - **RED phase**: "Generate minimal failing tests for this subtask. Do NOT implement any production code. Only create test files. Confirm tests fail with clear error messages indicating missing implementation." + - **GREEN phase**: "Implement minimal code to pass the failing tests. Follow existing project patterns in `src/`. Only modify files necessary for this subtask. Keep changes focused and reviewable." + +### Example Full Prompt (RED Phase) + +```markdown +<SYSTEM PROMPT> +[Contents of .cursor/rules/git_workflow.mdc] +[Contents of .cursor/rules/test_workflow.mdc] +[Contents of .claude/agents/surgical-test-generator.md] + +<TASK CONTEXT> +You are implementing: +Task #42.2: Add collection endpoint + +Description: +Implement POST /api/metrics endpoint to collect user metrics events + +Acceptance criteria: +- POST /api/metrics accepts { userId, eventType, timestamp } +- Validates input schema (reject missing/invalid fields) +- Persists to database using MetricsSchema from subtask 42.1 +- Returns 201 on success with created record +- Returns 400 on validation errors with details + +Dependencies: Subtask 42.1 (metrics schema) is complete + +<INSTRUCTION> +Generate failing tests for this subtask. Follow project conventions: +- Test file: src/api/__tests__/metrics.test.js +- Framework: vitest (detected from package.json) +- Test cases to cover: + * POST /api/metrics with valid payload → should return 201 (will fail: endpoint not implemented) + * POST /api/metrics with missing userId → should return 400 (will fail: validation not implemented) + * POST /api/metrics with invalid timestamp → should return 400 (will fail: validation not implemented) + * POST /api/metrics should persist to database → should save record (will fail: persistence not implemented) + +Do NOT implement the endpoint code yet. Only create test file(s). +Confirm tests fail with messages like "Cannot POST /api/metrics" or "endpoint not defined". + +Output format: +1. File path to create: src/api/__tests__/metrics.test.js +2. Complete test code +3. Command to run: npm test src/api/__tests__/metrics.test.js +``` + +### Example Full Prompt (GREEN Phase) + +```markdown +<SYSTEM PROMPT> +[Contents of .cursor/rules/git_workflow.mdc] +[Contents of .cursor/rules/test_workflow.mdc] + +<TASK CONTEXT> +Task #42.2: Add collection endpoint +[same context as RED phase] + +<CURRENT STATE> +Tests created in RED phase: +- src/api/__tests__/metrics.test.js +- 5 tests written, all failing as expected + +Test output: +``` +FAIL src/api/__tests__/metrics.test.js + POST /api/metrics + ✗ should return 201 with valid payload (endpoint not found) + ✗ should return 400 with missing userId (endpoint not found) + ✗ should return 400 with invalid timestamp (endpoint not found) + ✗ should persist to database (endpoint not found) +``` + +<INSTRUCTION> +Implement minimal code to make all tests pass. + +Guidelines: +- Create/modify file: src/api/metrics.js +- Use existing patterns from src/api/ (e.g., src/api/users.js for reference) +- Import MetricsSchema from subtask 42.1 (src/models/schema.js) +- Implement validation, persistence, and response handling +- Follow project error handling conventions +- Keep implementation focused on this subtask only + +After implementation: +1. Run tests: npm test src/api/__tests__/metrics.test.js +2. Confirm all 5 tests pass +3. Report results + +Output format: +1. File(s) created/modified +2. Implementation code +3. Test command and results +``` + +### Prompt Loading Configuration + +See `.taskmaster/config.json` → `prompts` section for paths and load order. + +## Configuration Schema + +### .taskmaster/config.json + +```json +{ + "autopilot": { + "enabled": true, + "requireCleanWorkingTree": true, + "commitTemplate": "{type}({scope}): {msg}", + "defaultCommitType": "feat", + "maxGreenAttempts": 3, + "testTimeout": 300000 + }, + "test": { + "runner": "auto", + "coverageThresholds": { + "lines": 80, + "branches": 80, + "functions": 80, + "statements": 80 + }, + "targetedRunPattern": "**/*.test.js" + }, + "git": { + "branchPattern": "{tag}/task-{id}-{slug}", + "pr": { + "enabled": true, + "base": "default", + "bodyTemplate": ".taskmaster/templates/pr-body.md" + } + }, + "prompts": { + "rulesPath": ".cursor/rules", + "testGeneratorPath": ".claude/agents/surgical-test-generator.md", + "loadOrder": ["git_workflow.mdc", "test_workflow.mdc"] + } +} +``` + +### Configuration Fields + +#### autopilot +- `enabled` (boolean): Enable/disable autopilot functionality +- `requireCleanWorkingTree` (boolean): Require clean git state before starting +- `commitTemplate` (string): Template for commit messages (tokens: `{type}`, `{scope}`, `{msg}`) +- `defaultCommitType` (string): Default commit type (feat, fix, chore, etc.) +- `maxGreenAttempts` (number): Maximum retry attempts to achieve green tests (default: 3) +- `testTimeout` (number): Timeout in milliseconds per test run (default: 300000 = 5min) + +#### test +- `runner` (string): Test runner detection mode (`"auto"` or explicit command like `"npm test"`) +- `coverageThresholds` (object): Minimum coverage percentages required + - `lines`, `branches`, `functions`, `statements` (number): Threshold percentages (0-100) +- `targetedRunPattern` (string): Glob pattern for targeted subtask test runs + +#### git +- `branchPattern` (string): Branch naming pattern (tokens: `{tag}`, `{id}`, `{slug}`) +- `pr.enabled` (boolean): Enable automatic PR creation +- `pr.base` (string): Target branch for PRs (`"default"` uses repo default, or specify like `"main"`) +- `pr.bodyTemplate` (string): Path to PR body template file (optional) + +#### prompts +- `rulesPath` (string): Directory containing rule files (e.g., `.cursor/rules`) +- `testGeneratorPath` (string): Path to test generator prompt file +- `loadOrder` (array): Order to load rule files from `rulesPath` + +### Environment Variables + +```bash +# Required for executor +ANTHROPIC_API_KEY=sk-ant-... # Claude API key + +# Optional: for PR creation +GITHUB_TOKEN=ghp_... # GitHub personal access token + +# Optional: for other executors (future) +OPENAI_API_KEY=sk-... +GOOGLE_API_KEY=... +``` + +## Run Artifacts & Observability + +### Per-Run Artifact Structure + +Each autopilot run creates a timestamped directory with complete traceability: + +``` +.taskmaster/reports/runs/2025-01-15-142033/ +├── manifest.json # run metadata (task id, start/end time, status) +├── log.jsonl # timestamped event stream +├── commits.txt # list of commit SHAs made during run +├── test-results/ +│ ├── subtask-42.1-red.json +│ ├── subtask-42.1-green.json +│ ├── subtask-42.2-red.json +│ ├── subtask-42.2-green-attempt1.json +│ ├── subtask-42.2-green-attempt2.json +│ ├── subtask-42.2-green-attempt3.json +│ └── final-suite.json +└── pr.md # generated PR body +``` + +### manifest.json Format + +```json +{ + "runId": "2025-01-15-142033", + "taskId": "42", + "tag": "analytics", + "branch": "analytics/task-42-user-metrics", + "startTime": "2025-01-15T14:20:33Z", + "endTime": "2025-01-15T14:45:12Z", + "status": "completed", + "subtasksCompleted": ["42.1", "42.2", "42.3"], + "subtasksFailed": [], + "totalCommits": 3, + "prUrl": "https://github.com/org/repo/pull/123", + "finalCoverage": { + "lines": 85.3, + "branches": 82.1, + "functions": 88.9, + "statements": 85.0 + } +} +``` + +### log.jsonl Format + +Event stream in JSON Lines format for easy parsing and debugging: + +```jsonl +{"ts":"2025-01-15T14:20:33Z","phase":"preflight","status":"ok","details":{"testCmd":"npm test","gitClean":true}} +{"ts":"2025-01-15T14:20:45Z","phase":"branch","status":"ok","branch":"analytics/task-42-user-metrics"} +{"ts":"2025-01-15T14:21:00Z","phase":"red","subtask":"42.1","status":"ok","tests":{"failed":3,"passed":0}} +{"ts":"2025-01-15T14:22:15Z","phase":"green","subtask":"42.1","status":"ok","tests":{"passed":3,"failed":0},"attempts":2} +{"ts":"2025-01-15T14:22:20Z","phase":"commit","subtask":"42.1","status":"ok","sha":"a1b2c3d","message":"feat(metrics): add metrics schema (task 42.1)"} +{"ts":"2025-01-15T14:23:00Z","phase":"red","subtask":"42.2","status":"ok","tests":{"failed":5,"passed":0}} +{"ts":"2025-01-15T14:25:30Z","phase":"green","subtask":"42.2","status":"error","tests":{"passed":3,"failed":2},"attempts":3,"error":"Max attempts reached"} +{"ts":"2025-01-15T14:25:35Z","phase":"pause","reason":"max_attempts","nextAction":"manual_review"} +``` + +### Test Results Format + +Each test run stores detailed results: + +```json +{ + "subtask": "42.2", + "phase": "green", + "attempt": 3, + "timestamp": "2025-01-15T14:25:30Z", + "command": "npm test src/api/__tests__/metrics.test.js", + "exitCode": 1, + "duration": 2340, + "summary": { + "total": 5, + "passed": 3, + "failed": 2, + "skipped": 0 + }, + "failures": [ + { + "test": "POST /api/metrics should return 201 with valid payload", + "error": "Expected status 201, got 500", + "stack": "..." + } + ], + "coverage": { + "lines": 78.5, + "branches": 75.0, + "functions": 80.0, + "statements": 78.5 + } +} +``` + +## Execution Model + +### Orchestration vs Direct Execution + +The autopilot system uses an **orchestration model** rather than direct code execution: + +**Orchestrator Role** (tm-core WorkflowOrchestrator): +- Maintains state machine tracking current phase (RED/GREEN/COMMIT) per subtask +- Validates preconditions (tests pass, git state clean, etc.) +- Returns "work units" describing what needs to be done next +- Records completion and advances to next phase +- Persists state for resumability + +**Executor Role** (Claude Code/AI session via MCP): +- Queries orchestrator for next work unit +- Executes the work (generates tests, writes code, runs tests, makes commits) +- Reports results back to orchestrator +- Handles file operations and tool invocations + +**Why This Approach?** +- Leverages existing AI capabilities (Claude Code) rather than duplicating them +- MCP protocol provides clean separation between state management and execution +- Allows human oversight and intervention at each phase +- Simpler to implement: orchestrator is pure state logic, no code generation needed +- Enables multiple executor types (Claude Code, other AI tools, human developers) + +**Example Flow**: +```typescript +// Claude Code (via MCP) queries orchestrator +const workUnit = await orchestrator.getNextWorkUnit('42'); +// => { +// phase: 'RED', +// subtask: '42.1', +// action: 'Generate failing tests for metrics schema', +// context: { title, description, dependencies, testFile: 'src/__tests__/schema.test.js' } +// } + +// Claude Code executes the work (writes test file, runs tests) +// Then reports back +await orchestrator.completeWorkUnit('42', '42.1', 'RED', { + success: true, + testsCreated: ['src/__tests__/schema.test.js'], + testsFailed: 3 +}); + +// Query again for next phase +const nextWorkUnit = await orchestrator.getNextWorkUnit('42'); +// => { phase: 'GREEN', subtask: '42.1', action: 'Implement code to pass tests', ... } +``` + +## Design Decisions + +### Why commit per subtask instead of per task? + +**Decision**: Commit after each subtask's green state, not after the entire task. + +**Rationale**: +- Atomic commits make code review easier (reviewers can see logical progression) +- Easier to revert a single subtask if it causes issues downstream +- Matches the TDD loop's natural checkpoint and cognitive boundary +- Provides resumability points if the run is interrupted + +**Trade-off**: More commits per task (can use squash-merge in PRs if desired) + +### Why not support parallel subtask execution? + +**Decision**: Sequential subtask execution in Phase 1; parallel execution deferred to Phase 3. + +**Rationale**: +- Subtasks often have implicit dependencies (e.g., schema before endpoint, endpoint before UI) +- Simpler orchestrator state machine (less complexity = faster to ship) +- Parallel execution requires explicit dependency DAG and conflict resolution +- Can be added in Phase 3 once core workflow is proven stable + +**Trade-off**: Slower for truly independent subtasks (mitigated by keeping subtasks small and focused) + +### Why require 80% coverage by default? + +**Decision**: Enforce 80% coverage threshold (lines/branches/functions/statements) before allowing commits. + +**Rationale**: +- Industry standard baseline for production code quality +- Forces test generation to be comprehensive, not superficial +- Configurable per project via `.taskmaster/config.json` if too strict +- Prevents "green tests" that only test happy paths + +**Trade-off**: May require more test generation iterations; can be lowered per project + +### Why use tmux instead of a rich GUI? + +**Decision**: MVP uses tmux split panes for TUI, not Electron/web-based GUI. + +**Rationale**: +- Tmux is universally available on dev machines; no installation burden +- Terminal-first workflows match developer mental model (no context switching) +- Simpler to implement and maintain; can add GUI later via extensions +- State stored in files allows IDE/extension integration without coupling + +**Trade-off**: Less visual polish than GUI; requires tmux familiarity + +### Why not support multiple executors (codex/gemini/claude) in Phase 1? + +**Decision**: Start with Claude executor only; add others in Phase 2+. + +**Rationale**: +- Reduces scope and complexity for initial delivery +- Claude Code already integrated with existing executor service +- Executor abstraction already exists; adding more is straightforward later +- Different executors may need different prompt strategies (requires experimentation) + +**Trade-off**: Users locked to Claude initially; can work around with manual executor selection + +## Risks and Mitigations + +- Model hallucination/large diffs: restrict prompt scope; enforce minimal changes; show diff previews (optional) before commit. + +- Flaky tests: allow retries, isolate targeted runs for speed, then full suite before commit. + +- Environment variability: detect runners/tools; provide fallbacks and actionable errors. + +- PR creation fails: still push and print manual commands; persist PR body to reuse. + +## Open Questions + +1) Slugging rules for branch names; any length limits or normalization beyond {slug} token sanitize? + +2) PR body standard sections beyond run report (e.g., checklist, coverage table)? + +3) Default executor prompt fine-tuning once codex/gemini integration is available. + +4) Where to store persistent TUI state (pane layout, last selection) in .taskmaster/state.json? + +## Branch Naming + +- Include both the tag and the task id in the branch name to make lineage explicit. + +- Default pattern: <tag>/task-<id>[-slug] (e.g., master/task-12, tag-analytics/task-4-user-auth). + +- Configurable via .taskmaster/config.json: git.branchPattern supports tokens {tag}, {id}, {slug}. + +## PR Base Branch + +- Use the repository’s default branch (detected via git) unless overridden. + +- Title format: Task #<id> [<tag>]: <title>. + +## RPG Mapping (Repository Planning Graph) + +Functional nodes (capabilities): + +- Autopilot Orchestration → drives TDD loop and lifecycle + +- Test Generation (Surgical) → produces failing tests from subtask context + +- Test Execution + Coverage → runs suite, enforces thresholds + +- Git/Branch/PR Management → safe operations and PR creation + +- TUI/Terminal Integration → interactive control and visibility via tmux + +- MCP Integration → structured task/status/context operations + +Structural nodes (code organization): + +- packages/tm-core: + + - services/workflow-orchestrator.ts (new) + + - services/test-runner-adapter.ts (new) + + - services/git-adapter.ts (new) + + - existing: task-service.ts, task-execution-service.ts, executors/* + +- apps/cli: + + - src/commands/autopilot.command.ts (new) + + - src/ui/tui/ (new tmux/TUI helpers) + +- scripts/modules: + + - reuse utils/git-utils.js, task-manager/tag-management.js + +- .claude/agents/: + + - surgical-test-generator.md + +Edges (data/control flow): + +- Autopilot → Test Generation → Test Execution → Git Commit → loop + +- Autopilot → Git Adapter (branch, tag, PR) + +- Autopilot → TUI (event stream) → tmux pane control + +- Autopilot → MCP tools for task/status updates + +- Test Execution → Coverage gate → Autopilot decision + +Topological traversal (implementation order): + +1) Git/Test adapters (foundations) + +2) Orchestrator skeleton + events + +3) CLI autopilot command and dry-run + +4) Surgical test-gen integration and execution gate + +5) PR creation, run reports, resumability + +## Phased Roadmap + +- Phase 0: Spike + + - Implement CLI skeleton tm autopilot with dry-run showing planned steps from a real task + subtasks. + + - Detect test runner (package.json) and git state; render a preflight report. + +- Phase 1: Core Rails (State Machine & Orchestration) + + - Implement WorkflowOrchestrator in tm-core as a **state machine** that tracks TDD phases per subtask. + + - Orchestrator **guides** the current AI session (Claude Code/MCP client) rather than executing code itself. + + - Add Git/Test adapters for status checks and validation (not direct execution). + + - WorkflowOrchestrator API: + - `getNextWorkUnit(taskId)` → returns next phase to execute (RED/GREEN/COMMIT) with context + - `completeWorkUnit(taskId, subtaskId, phase, result)` → records completion and advances state + - `getRunState(taskId)` → returns current progress and resumability data + + - MCP integration: expose work unit endpoints so Claude Code can query "what to do next" and report back. + + - Branch/tag mapping via existing tag-management APIs. + + - Run report persisted under .taskmaster/reports/runs/ with state checkpoints for resumability. + +- Phase 2: PR + Resumability + + - Add gh PR creation with well-formed body using the run report. + + - Introduce resumable checkpoints and --resume flag. + + - Add coverage enforcement and optional lint/format step. + +- Phase 3: Extensibility + Guardrails + + - Add support for basic pytest/go test adapters. + + - Add safeguards: diff preview mode, manual confirm gates, aggressive minimal-change prompts. + + - Optional: small TUI panel and extension panel leveraging the same run state file. + +## References (Repo) + +- Test Workflow: .cursor/rules/test_workflow.mdc + +- Git Workflow: .cursor/rules/git_workflow.mdc + +- CLI: apps/cli/src/commands/start.command.ts, apps/cli/src/ui/components/*.ts + +- Core Services: packages/tm-core/src/services/task-service.ts, task-execution-service.ts + +- Executors: packages/tm-core/src/executors/* + +- Git Utilities: scripts/modules/utils/git-utils.js + +- Tag Management: scripts/modules/task-manager/tag-management.js + + - Surgical Test Generator: .claude/agents/surgical-test-generator.md + diff --git a/.taskmaster/docs/tdd-workflow-phase-0-spike.md b/.taskmaster/docs/tdd-workflow-phase-0-spike.md new file mode 100644 index 00000000..ebb8b679 --- /dev/null +++ b/.taskmaster/docs/tdd-workflow-phase-0-spike.md @@ -0,0 +1,130 @@ +# Phase 0: Spike - Autonomous TDD Workflow ✅ COMPLETE + +## Objective +Validate feasibility and build foundational understanding before full implementation. + +## Status +**COMPLETED** - All deliverables implemented and validated. + +See `apps/cli/src/commands/autopilot.command.ts` for implementation. + +## Scope +- Implement CLI skeleton `tm autopilot` with dry-run mode +- Show planned steps from a real task with subtasks +- Detect test runner from package.json +- Detect git state and render preflight report + +## Deliverables + +### 1. CLI Command Skeleton +- Create `apps/cli/src/commands/autopilot.command.ts` +- Support `tm autopilot <taskId>` command +- Implement `--dry-run` flag +- Basic help text and usage information + +### 2. Preflight Detection System +- Detect test runner from package.json (npm test, pnpm test, etc.) +- Check git working tree state (clean/dirty) +- Validate required tools are available (git, gh, node/npm) +- Detect default branch + +### 3. Dry-Run Execution Plan Display +Display planned execution for a task including: +- Preflight checks status +- Branch name that would be created +- Tag that would be set +- List of subtasks in execution order +- For each subtask: + - RED phase: test file that would be created + - GREEN phase: implementation files that would be modified + - COMMIT: commit message that would be used +- Finalization steps: test suite run, coverage check, push, PR creation + +### 4. Task Loading & Validation +- Load task from TaskMaster state +- Validate task exists and has subtasks +- If no subtasks, show message about needing to expand first +- Show dependency order for subtasks + +## Example Output + +```bash +$ tm autopilot 42 --dry-run + +Autopilot Plan for Task #42 [analytics]: User metrics tracking +───────────────────────────────────────────────────────────── + +Preflight Checks: + ✓ Working tree is clean + ✓ Test command detected: npm test + ✓ Tools available: git, gh, node, npm + ✓ Current branch: main (will create new branch) + ✓ Task has 3 subtasks ready to execute + +Branch & Tag: + → Will create branch: analytics/task-42-user-metrics + → Will set active tag: analytics + +Execution Plan (3 subtasks): + + 1. Subtask 42.1: Add metrics schema + RED: Generate tests → src/__tests__/schema.test.js + GREEN: Implement code → src/schema.js + COMMIT: "feat(metrics): add metrics schema (task 42.1)" + + 2. Subtask 42.2: Add collection endpoint [depends on 42.1] + RED: Generate tests → src/api/__tests__/metrics.test.js + GREEN: Implement code → src/api/metrics.js + COMMIT: "feat(metrics): add collection endpoint (task 42.2)" + + 3. Subtask 42.3: Add dashboard widget [depends on 42.2] + RED: Generate tests → src/components/__tests__/MetricsWidget.test.jsx + GREEN: Implement code → src/components/MetricsWidget.jsx + COMMIT: "feat(metrics): add dashboard widget (task 42.3)" + +Finalization: + → Run full test suite with coverage (threshold: 80%) + → Push branch to origin (will confirm) + → Create PR targeting main + +Estimated commits: 3 +Estimated duration: ~20-30 minutes (depends on implementation complexity) + +Run without --dry-run to execute. +``` + +## Success Criteria +- Dry-run output is clear and matches expected workflow +- Preflight detection works correctly on the project repo +- Task loading integrates with existing TaskMaster state +- No actual git operations or file modifications occur in dry-run mode + +## Out of Scope +- Actual test generation +- Actual code implementation +- Git operations (branch creation, commits, push) +- PR creation +- Test execution + +## Implementation Notes +- Reuse existing `TaskService` from `packages/tm-core` +- Use existing git utilities from `scripts/modules/utils/git-utils.js` +- Load task/subtask data from `.taskmaster/tasks/tasks.json` +- Detect test command via package.json → scripts.test field + +## Dependencies +- Existing TaskMaster CLI structure +- Existing task storage format +- Git utilities + +## Estimated Effort +2-3 days + +## Validation +Test dry-run mode with: +- Task with 1 subtask +- Task with multiple subtasks +- Task with dependencies between subtasks +- Task without subtasks (should show warning) +- Dirty git working tree (should warn) +- Missing tools (should error with helpful message) diff --git a/.taskmaster/docs/tdd-workflow-phase-1-core-rails.md b/.taskmaster/docs/tdd-workflow-phase-1-core-rails.md new file mode 100644 index 00000000..39e58c3d --- /dev/null +++ b/.taskmaster/docs/tdd-workflow-phase-1-core-rails.md @@ -0,0 +1,1144 @@ +# Phase 1: Core Rails - Autonomous TDD Workflow + +## Objective +Implement the core autonomous TDD workflow with safe git operations, test generation/execution, and commit gating. + +## Scope +- WorkflowOrchestrator with event stream +- GitAdapter and TestResultValidator +- Subtask loop (RED → GREEN → COMMIT) +- CLI commands for AI agent orchestration +- MCP tools for AI agent orchestration +- Test result validation (AI reports, TaskMaster validates) +- Commit creation with enhanced metadata +- Branch/tag mapping +- Global storage for state and activity logs +- Framework-agnostic design (AI runs tests, not TaskMaster) +- Run report persistence + +## Key Design Decisions + +### Global Storage (`~/.taskmaster/`) +- **Why:** Keeps project directory clean, client-friendly, no tooling evidence in PRs +- **What:** All runtime state, logs, and throwaway artifacts +- **Where:** `~/.taskmaster/projects/<project-path>/runs/<run-id>/` + +### Dual System: State + Activity Log +- **State (`state.json`):** For orchestration, tells AI what to do next, mutable +- **Activity Log (`activity.jsonl`):** For debugging/audit, append-only event stream +- **Separation:** Optimizes for different use cases (fast reads vs. complete history) + +### Enhanced Commit Messages +- **Why:** Enables future task-checker bot validation without external dependencies +- **What:** Embeds task ID, phase, tag, test counts, coverage in commit body +- **Benefit:** PR contains full context for review and automated validation + +### Worktree Support +- **Why:** Enables parallel autonomous agents on different branches +- **How:** Each worktree has independent global state directory +- **Isolation:** No conflicts, complete separation + +### Framework-Agnostic Test Execution +- **AI runs tests:** AI agent knows project context and test framework (npm test, pytest, go test) +- **TaskMaster validates:** Only checks that RED fails and GREEN passes +- **No framework detection:** TaskMaster doesn't need to know Jest vs Vitest vs pytest +- **Trust but verify:** AI reports results, TaskMaster validates they make sense +- **Language agnostic:** Works with any language/framework without TaskMaster changes + +### AI Agent Orchestration Model +- **Who executes:** User's AI agent (Claude Code, Cursor, Windsurf, etc.) - not TaskMaster +- **TaskMaster's role:** Workflow orchestration, validation, commit creation +- **AI agent's role:** Code generation, test execution, result reporting +- **Communication:** Via CLI commands or MCP tools +- **State-driven:** AI agent reads `state.json` to know what to do next + +**Separation of Concerns:** + +| TaskMaster Responsibilities | AI Agent Responsibilities | +|----------------------------|---------------------------| +| Workflow state machine | Generate tests | +| Validate phase transitions | Run tests (knows test framework) | +| Create commits with metadata | Implement code | +| Store activity logs | Report test results | +| Manage git operations | Understand project context | +| Track progress | Choose appropriate test commands | + +**Flow:** +``` +AI Agent TaskMaster + │ │ + ├──► tm autopilot start 1 │ + │ ├──► Creates state, branch + │ ├──► Returns: "next action: RED phase for 1.1" + │ │ + ├──► tm autopilot next │ + │ ├──► Reads state.json + │ ├──► Returns: { phase: "red", subtask: "1.1", context: {...} } + │ │ + │ Generate tests (AI does this) │ + │ npm test (AI runs this) │ + │ Results: 3 failed, 0 passed │ + │ │ + ├──► tm autopilot complete red 1.1 \ │ + │ --results="failed:3,passed:0" │ + │ ├──► Validates: tests failed ✓ + │ ├──► Updates state to GREEN + │ ├──► Returns: "next action: GREEN phase" + │ │ + ├──► tm autopilot next │ + │ ├──► Returns: { phase: "green", subtask: "1.1" } + │ │ + │ Implement code (AI does this) │ + │ npm test (AI runs this) │ + │ Results: 3 passed, 0 failed │ + │ │ + ├──► tm autopilot complete green 1.1 \ │ + │ --results="passed:3,failed:0" │ + │ ├──► Validates: tests passed ✓ + │ ├──► Updates state to COMMIT + │ ├──► Returns: "next action: COMMIT phase" + │ │ + ├──► tm autopilot commit 1.1 │ + │ ├──► Detects changed files (git status) + │ ├──► Stages files + │ ├──► Creates commit with metadata + │ ├──► Updates state to next subtask + │ ├──► Returns: { sha: "a1b2c3d", nextAction: {...} } + │ │ + └──► Loop continues... │ +``` + +**Key principle:** AI agent is the domain expert (knows the codebase, frameworks, tools). TaskMaster is the workflow expert (knows TDD process, state management, git operations). + +## Deliverables + +### 1. WorkflowOrchestrator (`packages/tm-core/src/services/workflow-orchestrator.ts`) + +**Responsibilities:** +- State machine driving phases: Preflight → Branch/Tag → SubtaskIter → Finalize +- Event emission for progress tracking +- Coordination of Git, Test, and Executor adapters +- Run state persistence + +**API:** +```typescript +class WorkflowOrchestrator { + async executeTask(taskId: string, options: AutopilotOptions): Promise<RunResult> + async resume(runId: string): Promise<RunResult> + on(event: string, handler: (data: any) => void): void + + // Events emitted: + // - 'phase:start' { phase, timestamp } + // - 'phase:complete' { phase, status, timestamp } + // - 'subtask:start' { subtaskId, phase } + // - 'subtask:complete' { subtaskId, phase, status } + // - 'test:run' { subtaskId, phase, results } + // - 'commit:created' { subtaskId, sha, message } + // - 'error' { phase, error, recoverable } +} +``` + +**State Machine Phases:** +1. Preflight - validate environment +2. BranchSetup - create branch, set tag +3. SubtaskLoop - for each subtask: RED → GREEN → COMMIT +4. Finalize - full test suite, coverage check +5. Complete - run report, cleanup + +### 2. GitAdapter (`packages/tm-core/src/services/git-adapter.ts`) + +**Responsibilities:** +- All git operations with safety checks +- Branch name generation from tag/task +- Confirmation gates for destructive operations + +**API:** +```typescript +class GitAdapter { + async isWorkingTreeClean(): Promise<boolean> + async getCurrentBranch(): Promise<string> + async getDefaultBranch(): Promise<string> + async createBranch(name: string): Promise<void> + async checkoutBranch(name: string): Promise<void> + async commit(message: string, files?: string[]): Promise<string> + async push(branch: string, remote?: string): Promise<void> + + // Safety checks + async assertNotOnDefaultBranch(): Promise<void> + async assertCleanOrConfirm(): Promise<void> + + // Branch naming + generateBranchName(tag: string, taskId: string, slug: string): string +} +``` + +**Guardrails:** +- Never allow commits on default branch +- Always check working tree before branch creation +- Confirm destructive operations unless `--no-confirm` flag + +### 3. Test Result Validator (`packages/tm-core/src/services/test-result-validator.ts`) + +**Responsibilities:** +- Validate test results reported by AI agent +- Ensure RED phase has failing tests +- Ensure GREEN phase has passing tests +- Enforce coverage thresholds (if provided) + +**API:** +```typescript +class TestResultValidator { + async validateRedPhase(results: TestResults): Promise<ValidationResult> + async validateGreenPhase(results: TestResults, coverage?: number): Promise<ValidationResult> + async meetsThresholds(coverage: number): Promise<boolean> +} + +interface TestResults { + passed: number + failed: number + skipped?: number + total: number +} + +interface ValidationResult { + valid: boolean + message: string + suggestion?: string +} +``` + +**Validation Logic:** +```typescript +async function validateRedPhase(results: TestResults): ValidationResult { + if (results.failed === 0) { + return { + valid: false, + message: "RED phase requires failing tests. All tests passed.", + suggestion: "Verify tests are checking expected behavior. Tests should fail before implementation." + } + } + + if (results.passed > 0) { + return { + valid: true, + message: `RED phase valid: ${results.failed} failing, ${results.passed} passing (existing tests)`, + warning: "Some tests passing - ensure new tests are failing" + } + } + + return { + valid: true, + message: `RED phase complete: ${results.failed} tests failing as expected` + } +} + +async function validateGreenPhase(results: TestResults): ValidationResult { + if (results.failed > 0) { + return { + valid: false, + message: `GREEN phase incomplete: ${results.failed} tests still failing`, + suggestion: "Continue implementing until all tests pass or retry GREEN phase" + } + } + + return { + valid: true, + message: `GREEN phase complete: ${results.passed} tests passing` + } +} +``` + +**Note:** AI agent is responsible for: +- Running test commands (knows npm test vs pytest vs go test) +- Parsing test output +- Reporting results to TaskMaster + +TaskMaster only validates the reported numbers make sense for the phase. + +### 4. Test Generation Integration + +**Use Surgical Test Generator:** +- Load prompt from `.claude/agents/surgical-test-generator.md` +- Compose with task/subtask context +- Generate tests via executor (Claude) +- Write test files to detected locations + +**Prompt Composition:** +```typescript +async function composeRedPrompt(subtask: Subtask, context: ProjectContext): Promise<string> { + const systemPrompts = [ + loadFile('.cursor/rules/git_workflow.mdc'), + loadFile('.cursor/rules/test_workflow.mdc'), + loadFile('.claude/agents/surgical-test-generator.md') + ] + + const taskContext = formatTaskContext(subtask) + const instruction = formatRedInstruction(subtask, context) + + return [ + ...systemPrompts, + '<TASK CONTEXT>', + taskContext, + '<INSTRUCTION>', + instruction + ].join('\n\n') +} +``` + +### 5. Subtask Loop Implementation + +**RED Phase:** +1. TaskMaster returns RED action with subtask context +2. AI agent generates tests (TaskMaster not involved) +3. AI agent writes test files (TaskMaster not involved) +4. AI agent runs tests using project's test command (e.g., npm test) +5. AI agent reports results: `tm autopilot complete red <id> --results="failed:3,passed:0"` +6. TaskMaster validates: tests should have failures +7. If validation fails (tests passed), return error with suggestion +8. If validation passes, update state to GREEN, store results in activity log +9. Return next action (GREEN phase) + +**GREEN Phase:** +1. TaskMaster returns GREEN action with subtask context +2. AI agent implements code (TaskMaster not involved) +3. AI agent runs tests using project's test command +4. AI agent reports results: `tm autopilot complete green <id> --results="passed:5,failed:0" --coverage="85"` +5. TaskMaster validates: all tests should pass +6. If validation fails (tests still failing): + - Increment attempt counter + - If under max attempts: return GREEN action again with attempt number + - If max attempts reached: save state, emit pause event, return resumable checkpoint +7. If validation passes: update state to COMMIT, store results in activity log +8. Return next action (COMMIT phase) + +**COMMIT Phase:** +1. TaskMaster receives commit command: `tm autopilot commit <id>` +2. Detect changed files: `git status --porcelain` +3. Validate coverage meets thresholds (if provided and threshold configured) +4. Generate conventional commit message with task metadata +5. Stage files: `git add <files>` +6. Create commit: `git commit -m "<message>"` +7. Update subtask status to 'done' in tasks.json +8. Log commit event to activity.jsonl +9. Update state to next subtask's RED phase +10. Return next action + +**Key changes from original design:** +- AI agent runs all test commands (framework agnostic) +- TaskMaster only validates reported results +- No test framework detection needed +- No test execution by TaskMaster +- AI agent is trusted to report accurate results + +### 6. Branch & Tag Management + +**Integration with existing tag system:** +- Use `scripts/modules/task-manager/tag-management.js` +- Explicit tag switching when branch created +- Store branch ↔ tag mapping in run state + +**Branch Naming:** +- Pattern from config: `{tag}/task-{id}-{slug}` +- Default: `analytics/task-42-user-metrics` +- Sanitize: lowercase, replace spaces with hyphens + +### 7. Global Storage & State Management + +**Philosophy:** +- All runtime state, logs, and throwaway artifacts stored globally in `~/.taskmaster/` +- Project directory stays clean - only code changes and tasks.json versioned +- Enables single-player autonomous mode without polluting PRs +- Client-friendly: no evidence of tooling in source code + +**Global directory structure:** +``` +~/.taskmaster/ +├── projects/ +│ └── <project-path-normalized>/ +│ ├── runs/ +│ │ └── <tag>__task-<id>__<timestamp>/ +│ │ ├── manifest.json # run metadata +│ │ ├── activity.jsonl # event stream (debugging) +│ │ ├── state.json # resumable checkpoint +│ │ ├── commits.txt # commit SHAs +│ │ └── test-results/ +│ │ ├── subtask-1.1-red.json +│ │ ├── subtask-1.1-green.json +│ │ └── final-suite.json +│ └── tags/ +│ └── <tag-name>/ +│ └── current-run.json # active run pointer +└── cache/ + └── templates/ # shared templates +``` + +**Project path normalization:** +```typescript +function getProjectStoragePath(projectRoot: string): string { + const normalized = projectRoot + .replace(/\//g, '-') + .replace(/^-/, '') + + return path.join(os.homedir(), '.taskmaster', 'projects', normalized) + // Example: ~/.taskmaster/projects/-Volumes-Workspace-contrib-task-master-claude-task-master +} +``` + +**Run ID generation:** +```typescript +function generateRunId(tag: string, taskId: string): string { + const timestamp = new Date().toISOString().replace(/[:.]/g, '-') + return `${tag}__task-${taskId}__${timestamp}` + // Example: tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z +} +``` + +**manifest.json:** +```json +{ + "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z", + "projectRoot": "/Volumes/Workspace/contrib/task-master/claude-task-master", + "taskId": "1", + "tag": "tdd-workflow-phase-0", + "branch": "tdd-phase-0-implementation", + "startTime": "2025-10-07T14:30:00Z", + "endTime": null, + "status": "in-progress", + "currentPhase": "subtask-loop", + "currentSubtask": "1.2", + "subtasksCompleted": ["1.1"], + "subtasksFailed": [], + "totalCommits": 1 +} +``` + +**state.json** (orchestration state): +```json +{ + "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z", + "taskId": "1", + "tag": "tdd-workflow-phase-0", + "branch": "tdd-phase-0-implementation", + "currentSubtask": "1.2", + "currentPhase": "green", + "attemptNumber": 1, + "maxAttempts": 3, + "completedSubtasks": ["1.1"], + "pendingSubtasks": ["1.2", "1.3", "1.4"], + "nextAction": { + "type": "implement", + "subtask": "1.2", + "phase": "green", + "context": { + "testFile": "src/__tests__/preflight.test.ts", + "failingTests": [ + "should detect test runner from package.json", + "should validate git working tree" + ], + "implementationFiles": ["src/services/preflight-checker.ts"] + } + }, + "lastUpdated": "2025-10-07T14:31:45Z", + "canResume": true +} +``` + +**activity.jsonl** (append-only event log): +```jsonl +{"ts":"2025-10-07T14:30:00Z","event":"phase:start","phase":"preflight","status":"ok"} +{"ts":"2025-10-07T14:30:15Z","event":"phase:complete","phase":"preflight","checks":{"git":true,"test":true,"tools":true}} +{"ts":"2025-10-07T14:30:20Z","event":"branch:created","branch":"tdd-phase-0-implementation"} +{"ts":"2025-10-07T14:30:22Z","event":"tag:switched","from":"master","to":"tdd-workflow-phase-0"} +{"ts":"2025-10-07T14:30:25Z","event":"subtask:start","subtaskId":"1.1","phase":"red"} +{"ts":"2025-10-07T14:31:10Z","event":"test:generated","files":["src/__tests__/autopilot.test.ts"],"testCount":3} +{"ts":"2025-10-07T14:31:15Z","event":"test:run","subtaskId":"1.1","phase":"red","passed":0,"failed":3,"status":"expected"} +{"ts":"2025-10-07T14:31:20Z","event":"phase:transition","from":"red","to":"green"} +{"ts":"2025-10-07T14:32:45Z","event":"code:modified","files":["src/commands/autopilot.ts"],"linesChanged":"+58,-0"} +{"ts":"2025-10-07T14:33:00Z","event":"test:run","subtaskId":"1.1","phase":"green","attempt":1,"passed":3,"failed":0,"status":"success"} +{"ts":"2025-10-07T14:33:15Z","event":"commit:created","subtaskId":"1.1","sha":"a1b2c3d","message":"feat(cli): add autopilot command skeleton (task 1.1)"} +{"ts":"2025-10-07T14:33:20Z","event":"subtask:complete","subtaskId":"1.1","duration":"180s"} +``` + +**current-run.json** (active run pointer): +```json +{ + "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z", + "taskId": "1", + "tag": "tdd-workflow-phase-0", + "startTime": "2025-10-07T14:30:00Z", + "status": "in-progress" +} +``` + +**What stays in project (versioned):** +``` +<project>/ +├── .taskmaster/ +│ ├── tasks/ +│ │ └── tasks.json # ✅ Versioned (task definitions) +│ └── config.json # ✅ Versioned (shared config) +└── .gitignore # Add: .taskmaster/state/, .taskmaster/reports/ +``` + +**State vs Activity Log:** + +| State File (state.json) | Activity Log (activity.jsonl) | +|------------------------|-------------------------------| +| Current position | Full history | +| What to do next | What happened | +| Mutable (updated) | Immutable (append-only) | +| For orchestration | For debugging/audit | +| Single JSON object | Line-delimited JSON | +| Small (~2KB) | Can grow large | + +**Resume logic:** +```typescript +async function resumeWorkflow(): Promise<void> { + // 1. Find active run + const currentRun = await loadJSON('~/.taskmaster/projects/<project>/tags/<tag>/current-run.json') + + // 2. Load state from that run + const state = await loadJSON(`~/.taskmaster/projects/<project>/runs/${currentRun.runId}/state.json`) + + // 3. Continue from checkpoint + return orchestrator.resumeFrom(state) +} +``` + +### 8. Enhanced Commit Message Format + +**Purpose:** +- Embed task context in commits for future validation +- Enable task-checker bot to verify alignment +- Provide audit trail without needing external logs in PR + +**Commit message template:** +``` +{type}({scope}): {summary} (task {taskId}) + +{detailed description} + +Task: #{taskId} - {taskTitle} +Phase: {phaseName} +Tag: {tagName} + +Tests: {testCount} passing +Coverage: {coveragePercent}% lines +``` + +**Example commit:** +``` +feat(cli): add autopilot command skeleton (task 1.1) + +Implements AutopilotCommand class with Commander.js integration. +Adds argument parsing for task ID and dry-run flag. Includes basic +command registration and help text following existing CLI patterns. + +Task: #1.1 - Create command structure +Phase: Phase 0 - Spike +Tag: tdd-workflow-phase-0 + +Tests: 3 passing +Coverage: 92% lines +``` + +**Conventional commit types:** +- `feat` - New feature or capability +- `fix` - Bug fix +- `test` - Test-only changes +- `refactor` - Code restructuring without behavior change +- `docs` - Documentation updates +- `chore` - Build/tooling changes + +**Scope determination:** +```typescript +function determineScope(files: string[]): string { + // Extract common scope from changed files + const scopes = files.map(f => { + if (f.startsWith('apps/cli/')) return 'cli' + if (f.startsWith('packages/tm-core/')) return 'core' + if (f.startsWith('packages/tm-mcp/')) return 'mcp' + return 'misc' + }) + + // Use most common scope + return mode(scopes) +} +``` + +**Commit validation (future task-checker bot):** +```typescript +async function validateCommit(commit: Commit, task: Task): Promise<ValidationResult> { + const taskId = extractTaskId(commit.message) // "1.1" + const task = await loadTask(taskId) + + return aiChecker.validate({ + commitDiff: commit.diff, + commitMessage: commit.message, + taskDescription: task.description, + acceptanceCriteria: task.acceptanceCriteria, + testStrategy: task.testStrategy + }) +} +``` + +### 9. CLI Commands for AI Agent Orchestration + +**New CLI commands** (all under `tm autopilot` namespace): + +```bash +# Start workflow - creates branch, initializes state +tm autopilot start <taskId> [options] + --branch <name> # Override branch name + --no-confirm # Skip confirmations + --max-attempts <n> # Override max GREEN attempts + +# Get next action from state +tm autopilot next [options] + --json # Output as JSON for parsing + +# Complete a phase and report test results +tm autopilot complete <phase> <subtaskId> --results="<passed:n,failed:n>" [options] + # phase: red | green + --results <passed:n,failed:n> # Required: test results from AI + --coverage <percentage> # Optional: coverage percentage + --files <file1,file2> # Optional: files changed (auto-detected if omitted) + +# Create commit (called by AI after GREEN passes) +tm autopilot commit <subtaskId> [options] + --message <msg> # Override commit message + +# Resume from interrupted run +tm autopilot resume [options] + --run-id <id> # Specific run to resume + +# Get current status +tm autopilot status + --json # Output as JSON + +# Watch activity log in real-time +tm autopilot watch + +# Abort current run +tm autopilot abort [options] + --cleanup # Delete branch and state +``` + +**Command details:** + +**`tm autopilot start <taskId>`** +- Creates global state directory +- Creates feature branch +- Switches tag +- Initializes state.json with first subtask +- Returns next action (RED phase for first subtask) + +**`tm autopilot next`** +- Reads `~/.taskmaster/projects/<project>/tags/<tag>/current-run.json` +- Reads `~/.taskmaster/projects/<project>/runs/<run-id>/state.json` +- Returns next action with full context + +Output: +```json +{ + "action": "red", + "subtask": { + "id": "1.1", + "title": "Create command structure", + "description": "...", + "testStrategy": "..." + }, + "context": { + "projectRoot": "/path/to/project", + "testPattern": "**/*.test.ts", + "existingTests": [] + }, + "instructions": "Generate tests for this subtask. Tests should fail initially." +} +``` + +**`tm autopilot complete <phase> <subtaskId>`** +- Receives test results from AI agent +- Validates phase completion: + - **RED**: Ensures reported results show failures + - **GREEN**: Ensures reported results show all tests passing +- Updates state to next phase +- Logs event to activity.jsonl with test results +- Returns next action + +**Examples:** +```bash +# After AI generates tests and runs them +tm autopilot complete red 1.1 --results="failed:3,passed:0" + +# After AI implements code and runs tests +tm autopilot complete green 1.1 --results="passed:3,failed:0" --coverage="92" + +# With existing passing tests +tm autopilot complete red 1.1 --results="failed:3,passed:12" +``` + +**`tm autopilot commit <subtaskId>`** +- Generates commit message from template +- Stages files +- Creates commit with enhanced message +- Updates subtask status to 'done' +- Updates state to next subtask +- Returns next action + +**`tm autopilot status`** +```json +{ + "runId": "tdd-workflow-phase-0__task-1__2025-10-07T14-30-00-000Z", + "taskId": "1", + "currentSubtask": "1.2", + "currentPhase": "green", + "attemptNumber": 1, + "progress": { + "completed": ["1.1"], + "current": "1.2", + "remaining": ["1.3", "1.4"] + }, + "commits": 1, + "startTime": "2025-10-07T14:30:00Z", + "duration": "5m 30s" +} +``` + +### 10. MCP Tools for AI Agent Orchestration + +**New MCP tools** (add to `packages/tm-mcp/src/tools/`): + +```typescript +// autopilot_start +{ + name: "autopilot_start", + description: "Start autonomous TDD workflow for a task", + parameters: { + taskId: string, + options?: { + branch?: string, + maxAttempts?: number + } + }, + returns: { + runId: string, + branch: string, + nextAction: NextAction + } +} + +// autopilot_next +{ + name: "autopilot_next", + description: "Get next action from workflow state", + parameters: { + projectRoot?: string // defaults to current + }, + returns: { + action: "red" | "green" | "commit" | "complete", + subtask: Subtask, + context: Context, + instructions: string + } +} + +// autopilot_complete_phase +{ + name: "autopilot_complete_phase", + description: "Report test results and validate phase completion", + parameters: { + phase: "red" | "green", + subtaskId: string, + testResults: { + passed: number, + failed: number, + skipped?: number + }, + coverage?: number, // Optional coverage percentage + files?: string[] // Optional, auto-detected if not provided + }, + returns: { + validated: boolean, + message: string, + suggestion?: string, + nextAction: NextAction + } +} + +// autopilot_commit +{ + name: "autopilot_commit", + description: "Create commit for completed subtask", + parameters: { + subtaskId: string, + files?: string[], + message?: string // Override + }, + returns: { + commitSha: string, + message: string, + nextAction: NextAction + } +} + +// autopilot_status +{ + name: "autopilot_status", + description: "Get current workflow status", + parameters: { + projectRoot?: string + }, + returns: { + runId: string, + taskId: string, + currentSubtask: string, + currentPhase: string, + progress: Progress, + commits: number + } +} + +// autopilot_resume +{ + name: "autopilot_resume", + description: "Resume interrupted workflow", + parameters: { + runId?: string // defaults to current + }, + returns: { + resumed: boolean, + nextAction: NextAction + } +} +``` + +**MCP tool usage example (Claude Code session):** + +```javascript +// AI agent calls MCP tools +const { runId, nextAction } = await mcp.autopilot_start({ taskId: "1" }) + +while (nextAction.action !== "complete") { + const action = await mcp.autopilot_next() + + if (action.action === "red") { + // AI generates tests + const tests = await generateTests(action.subtask, action.context) + await writeFiles(tests) + + // AI runs tests (using project's test command) + const testOutput = await runCommand("npm test") // or pytest, go test, etc. + const results = parseTestOutput(testOutput) + + // Report results to TaskMaster + const validation = await mcp.autopilot_complete_phase({ + phase: "red", + subtaskId: action.subtask.id, + testResults: { + passed: results.passed, + failed: results.failed, + skipped: results.skipped + } + }) + + if (!validation.validated) { + console.error(validation.message) + // Handle validation failure + } + } + + if (action.action === "green") { + // AI implements code + const impl = await implementCode(action.subtask, action.context) + await writeFiles(impl) + + // AI runs tests again + const testOutput = await runCommand("npm test") + const results = parseTestOutput(testOutput) + const coverage = parseCoverage(testOutput) + + // Report results to TaskMaster + const validation = await mcp.autopilot_complete_phase({ + phase: "green", + subtaskId: action.subtask.id, + testResults: { + passed: results.passed, + failed: results.failed + }, + coverage: coverage.lines + }) + + if (!validation.validated) { + console.log(validation.message, validation.suggestion) + // Retry or handle failure + } + } + + if (action.action === "commit") { + // TaskMaster creates the commit + const { commitSha, nextAction: next } = await mcp.autopilot_commit({ + subtaskId: action.subtask.id + }) + + nextAction = next + } +} +``` + +### 11. AI Agent Instructions (CLAUDE.md integration) + +Add to `.claude/CLAUDE.md` or `.cursor/rules/`: + +````markdown +## TaskMaster Autonomous Workflow + +When working on tasks with `tm autopilot`: + +1. **Start workflow:** + ```bash + tm autopilot start <taskId> + ``` + +2. **Loop until complete:** + ```bash + # Get next action + NEXT=$(tm autopilot next --json) + ACTION=$(echo $NEXT | jq -r '.action') + SUBTASK=$(echo $NEXT | jq -r '.subtask.id') + + case $ACTION in + red) + # 1. Generate tests based on instructions + # 2. Write test files + # 3. Run tests yourself (you know the test command) + npm test # or pytest, go test, cargo test, etc. + + # 4. Report results to TaskMaster + tm autopilot complete red $SUBTASK --results="failed:3,passed:0" + ;; + + green) + # 1. Implement code to pass tests + # 2. Write implementation files + # 3. Run tests yourself + npm test + + # 4. Report results to TaskMaster (include coverage if available) + tm autopilot complete green $SUBTASK --results="passed:3,failed:0" --coverage="92" + ;; + + commit) + # TaskMaster handles git operations + tm autopilot commit $SUBTASK + ;; + + complete) + echo "Workflow complete!" + ;; + esac + ``` + +3. **State is preserved** - you can stop/resume anytime with `tm autopilot resume` + +**Important:** You are responsible for: +- Running test commands (TaskMaster doesn't know your test framework) +- Parsing test output (passed/failed counts) +- Reporting accurate results + +**Via MCP:** +Use `autopilot_*` tools for the same workflow with better integration. +```` + +**Example AI agent prompt:** + +```markdown +You are working autonomously on Task Master tasks using the autopilot workflow. + +Instructions: +1. Call `tm autopilot next --json` to get your next action +2. Read the action type and context +3. Execute the action: + - **RED**: + * Generate tests that fail initially + * Run tests: `npm test` (or appropriate test command) + * Report: `tm autopilot complete red <id> --results="failed:n,passed:n"` + - **GREEN**: + * Implement code to pass the tests + * Run tests: `npm test` + * Report: `tm autopilot complete green <id> --results="passed:n,failed:n" --coverage="nn"` + - **COMMIT**: + * Call: `tm autopilot commit <id>` (TaskMaster handles git) +4. Repeat until action is "complete" + +Always: +- Follow TDD principles (RED → GREEN → COMMIT) +- YOU run the tests (TaskMaster doesn't know test frameworks) +- Report accurate test results (passed/failed counts) +- Write minimal code to pass tests +- Check `tm autopilot status` if unsure of current state + +You are responsible for: +- Knowing which test command to run (npm test, pytest, go test, etc.) +- Parsing test output to get pass/fail counts +- Understanding the project's testing framework +- Running tests after generating/implementing code + +TaskMaster is responsible for: +- Validating your reported results make sense (RED should fail, GREEN should pass) +- Creating properly formatted git commits +- Managing workflow state and transitions +``` + +## Success Criteria +- Can execute a simple task end-to-end without manual intervention +- All commits made on feature branch, never on default branch +- Tests are generated before implementation (RED → GREEN order enforced) +- Only commits when tests pass and coverage meets threshold +- Run state is persisted and can be inspected post-run +- Clear error messages when things go wrong +- Orchestrator events allow CLI to show live progress + +## Configuration + +**Add to `.taskmaster/config.json` (versioned):** +```json +{ + "autopilot": { + "enabled": true, + "requireCleanWorkingTree": true, + "commitTemplate": "{type}({scope}): {msg} (task {taskId})", + "defaultCommitType": "feat", + "maxGreenAttempts": 3, + "testTimeout": 300000, + "storage": { + "location": "global", + "basePath": "~/.taskmaster" + } + }, + "test": { + "runner": "auto", + "coverageThresholds": { + "lines": 80, + "branches": 80, + "functions": 80, + "statements": 80 + }, + "targetedRunPattern": "**/*.test.js" + }, + "git": { + "branchPattern": "{tag}/task-{id}-{slug}", + "defaultRemote": "origin" + } +} +``` + +**Update `.gitignore` (keep project clean):** +```gitignore +# TaskMaster runtime artifacts (stored globally, not needed in repo) +.taskmaster/state/ +.taskmaster/reports/ + +# Keep these versioned +!.taskmaster/tasks/ +!.taskmaster/config.json +!.taskmaster/docs/ +!.taskmaster/templates/ +``` + +## Out of Scope (defer to Phase 2) +- PR creation (gh integration) +- Resume functionality (`--resume` flag) +- Lint/format step +- Multiple executor support (only Claude) + +## Implementation Order + +### Phase 1A: Infrastructure (Week 1) +1. Global storage utilities (path normalization, run ID generation) +2. Activity log writer (append-only JSONL) +3. State manager (load/save/update state.json) +4. GitAdapter with safety checks +5. TestResultValidator (validate RED/GREEN phase results) + +### Phase 1B: Orchestration Core (Week 1-2) +6. WorkflowOrchestrator state machine skeleton +7. State transitions (Preflight → BranchSetup → SubtaskLoop → Finalize) +8. Event emitter for activity logging +9. Enhanced commit message generator + +### Phase 1C: TDD Loop (Week 2) +10. RED phase validator (ensure tests fail) +11. GREEN phase validator (ensure tests pass) +12. COMMIT phase implementation (staging, committing) +13. Subtask progression logic + +### Phase 1D: CLI Interface (Week 2-3) +14. `tm autopilot start` command +15. `tm autopilot next` command +16. `tm autopilot complete` command +17. `tm autopilot commit` command +18. `tm autopilot status` command +19. `tm autopilot resume` command + +### Phase 1E: MCP Interface (Week 3) +20. `autopilot_start` tool +21. `autopilot_next` tool +22. `autopilot_complete_phase` tool +23. `autopilot_commit` tool +24. `autopilot_status` tool +25. `autopilot_resume` tool + +### Phase 1F: Integration (Week 3) +26. AI agent instruction templates +27. Error handling and recovery +28. Integration tests +29. Documentation + +## Testing Strategy +- Unit tests for global storage (path normalization, state management) +- Unit tests for activity log (JSONL append, parsing) +- Unit tests for each adapter (mock git/test commands) +- Integration tests with real git repo (temporary directory) +- End-to-end test with sample task in test project +- Verify no commits on default branch (security test) +- Verify commit gating works (force test failure, ensure no commit) +- Verify enhanced commit messages include task context +- Test resume from state checkpoint +- Verify project directory stays clean (no runtime artifacts) + +## Dependencies +- Phase 0 completed (CLI skeleton, preflight checks) +- Existing TaskService and executor infrastructure +- Surgical Test Generator prompt file exists + +## Estimated Effort +2-3 weeks + +## Risks & Mitigations +- **Risk:** Test generation produces invalid/wrong tests + - **Mitigation:** Use Surgical Test Generator prompt, add manual review step in early iterations + +- **Risk:** Implementation attempts timeout/fail repeatedly + - **Mitigation:** Max attempts with pause/resume; store state for manual intervention + +- **Risk:** Coverage parsing fails on different test frameworks + - **Mitigation:** Start with one framework (vitest), add parsers incrementally + +- **Risk:** Git operations fail (conflicts, permissions) + - **Mitigation:** Detailed error messages, save state before destructive ops + +## Validation +Test with: +- Simple task (1 subtask, clear requirements) +- Medium task (3 subtasks with dependencies) +- Task requiring multiple GREEN attempts +- Task with dirty working tree (should error) +- Task on default branch (should error) +- Project without test command (should error with helpful message) +- Verify global storage created in `~/.taskmaster/projects/<project>/` +- Verify activity log is valid JSONL and streamable +- Verify state.json allows resumption +- Verify commit messages include task metadata +- Verify project directory contains no runtime artifacts after run +- Test with multiple worktrees (independent state per worktree) diff --git a/.taskmaster/docs/tdd-workflow-phase-1-orchestrator.md b/.taskmaster/docs/tdd-workflow-phase-1-orchestrator.md new file mode 100644 index 00000000..7eee991a --- /dev/null +++ b/.taskmaster/docs/tdd-workflow-phase-1-orchestrator.md @@ -0,0 +1,369 @@ +# Phase 1: Core Rails - State Machine & Orchestration + +## Objective +Build the WorkflowOrchestrator as a state machine that guides AI sessions through TDD workflow, rather than directly executing code. + +## Architecture Overview + +### Execution Model +The orchestrator acts as a **state manager and guide**, not a code executor: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Claude Code (MCP Client) │ +│ - Queries "what to do next" │ +│ - Executes work (writes tests, code, runs commands) │ +│ - Reports completion │ +└────────────────┬────────────────────────────────────────────┘ + │ MCP Protocol + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ WorkflowOrchestrator (tm-core) │ +│ - Maintains state machine (RED → GREEN → COMMIT) │ +│ - Returns work units with context │ +│ - Validates preconditions │ +│ - Records progress │ +│ - Persists state for resumability │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Why This Approach? +1. **Separation of Concerns**: State management separate from code execution +2. **Leverage Existing Tools**: Uses Claude Code's capabilities instead of reimplementing +3. **Human-in-the-Loop**: Easy to inspect state and intervene at any phase +4. **Simpler Implementation**: Orchestrator is pure logic, no AI model integration needed +5. **Flexible Executors**: Any tool (Claude Code, human, other AI) can execute work units + +## Core Components + +### 1. WorkflowOrchestrator Service +**Location**: `packages/tm-core/src/services/workflow-orchestrator.service.ts` + +**Responsibilities**: +- Track current phase (RED/GREEN/COMMIT) per subtask +- Generate work units with context for each phase +- Validate phase completion criteria +- Advance state machine on successful completion +- Handle errors and retry logic +- Persist run state for resumability + +**API**: +```typescript +interface WorkflowOrchestrator { + // Start a new autopilot run + startRun(taskId: string, options?: RunOptions): Promise<RunContext>; + + // Get next work unit to execute + getNextWorkUnit(runId: string): Promise<WorkUnit | null>; + + // Report work unit completion + completeWorkUnit( + runId: string, + workUnitId: string, + result: WorkUnitResult + ): Promise<void>; + + // Get current run state + getRunState(runId: string): Promise<RunState>; + + // Pause/resume + pauseRun(runId: string): Promise<void>; + resumeRun(runId: string): Promise<void>; +} + +interface WorkUnit { + id: string; // Unique work unit ID + phase: 'RED' | 'GREEN' | 'COMMIT'; + subtaskId: string; // e.g., "42.1" + action: string; // Human-readable description + context: WorkUnitContext; // All info needed to execute + preconditions: Precondition[]; // Checks before execution +} + +interface WorkUnitContext { + taskId: string; + taskTitle: string; + subtaskTitle: string; + subtaskDescription: string; + dependencies: string[]; // Completed subtask IDs + testCommand: string; // e.g., "npm test" + + // Phase-specific context + redPhase?: { + testFile: string; // Where to create test + testFramework: string; // e.g., "vitest" + acceptanceCriteria: string[]; + }; + + greenPhase?: { + testFile: string; // Test to make pass + implementationHints: string[]; + expectedFiles: string[]; // Files likely to modify + }; + + commitPhase?: { + commitMessage: string; // Pre-generated message + filesToCommit: string[]; // Files modified in RED+GREEN + }; +} + +interface WorkUnitResult { + success: boolean; + phase: 'RED' | 'GREEN' | 'COMMIT'; + + // RED phase results + testsCreated?: string[]; + testsFailed?: number; + + // GREEN phase results + testsPassed?: number; + filesModified?: string[]; + attempts?: number; + + // COMMIT phase results + commitSha?: string; + + // Common + error?: string; + logs?: string; +} + +interface RunState { + runId: string; + taskId: string; + status: 'running' | 'paused' | 'completed' | 'failed'; + currentPhase: 'RED' | 'GREEN' | 'COMMIT'; + currentSubtask: string; + completedSubtasks: string[]; + failedSubtasks: string[]; + startTime: Date; + lastUpdateTime: Date; + + // Resumability + checkpoint: { + subtaskId: string; + phase: 'RED' | 'GREEN' | 'COMMIT'; + attemptNumber: number; + }; +} +``` + +### 2. State Machine Logic + +**Phase Transitions**: +``` +START → RED(subtask 1) → GREEN(subtask 1) → COMMIT(subtask 1) + ↓ + RED(subtask 2) ← ─ ─ ─ ┘ + ↓ + GREEN(subtask 2) + ↓ + COMMIT(subtask 2) + ↓ + (repeat for remaining subtasks) + ↓ + FINALIZE → END +``` + +**Phase Rules**: +- **RED**: Can only transition to GREEN if tests created and failing +- **GREEN**: Can only transition to COMMIT if tests passing (attempt < maxAttempts) +- **COMMIT**: Can only transition to next RED if commit successful +- **FINALIZE**: Can only start if all subtasks completed + +**Preconditions**: +- RED: No uncommitted changes (or staged from previous GREEN that failed) +- GREEN: RED phase complete, tests exist and are failing +- COMMIT: GREEN phase complete, all tests passing, coverage meets threshold + +### 3. MCP Integration + +**New MCP Tools** (expose WorkflowOrchestrator via MCP): +```typescript +// Start an autopilot run +mcp__task_master_ai__autopilot_start(taskId: string, dryRun?: boolean) + +// Get next work unit +mcp__task_master_ai__autopilot_next_work_unit(runId: string) + +// Complete current work unit +mcp__task_master_ai__autopilot_complete_work_unit( + runId: string, + workUnitId: string, + result: WorkUnitResult +) + +// Get run state +mcp__task_master_ai__autopilot_get_state(runId: string) + +// Pause/resume +mcp__task_master_ai__autopilot_pause(runId: string) +mcp__task_master_ai__autopilot_resume(runId: string) +``` + +### 4. Git/Test Adapters + +**GitAdapter** (`packages/tm-core/src/services/git-adapter.service.ts`): +- Check working tree status +- Validate branch state +- Read git config (user, remote, default branch) +- **Does NOT execute** git commands (that's executor's job) + +**TestAdapter** (`packages/tm-core/src/services/test-adapter.service.ts`): +- Detect test framework from package.json +- Parse test output (failures, passes, coverage) +- Validate coverage thresholds +- **Does NOT run** tests (that's executor's job) + +### 5. Run State Persistence + +**Storage Location**: `.taskmaster/reports/runs/<runId>/` + +**Files**: +- `state.json` - Current run state (for resumability) +- `log.jsonl` - Event stream (timestamped work unit completions) +- `manifest.json` - Run metadata +- `work-units.json` - All work units generated for this run + +**Example `state.json`**: +```json +{ + "runId": "2025-01-15-142033", + "taskId": "42", + "status": "paused", + "currentPhase": "GREEN", + "currentSubtask": "42.2", + "completedSubtasks": ["42.1"], + "failedSubtasks": [], + "checkpoint": { + "subtaskId": "42.2", + "phase": "GREEN", + "attemptNumber": 2 + }, + "startTime": "2025-01-15T14:20:33Z", + "lastUpdateTime": "2025-01-15T14:35:12Z" +} +``` + +## Implementation Plan + +### Step 1: WorkflowOrchestrator Skeleton +- [ ] Create `workflow-orchestrator.service.ts` with interfaces +- [ ] Implement state machine logic (phase transitions) +- [ ] Add run state persistence (state.json, log.jsonl) +- [ ] Write unit tests for state machine + +### Step 2: Work Unit Generation +- [ ] Implement `getNextWorkUnit()` with context assembly +- [ ] Generate RED phase work units (test file paths, criteria) +- [ ] Generate GREEN phase work units (implementation hints) +- [ ] Generate COMMIT phase work units (commit messages) + +### Step 3: Git/Test Adapters +- [ ] Create GitAdapter for status checks only +- [ ] Create TestAdapter for output parsing only +- [ ] Add precondition validation using adapters +- [ ] Write adapter unit tests + +### Step 4: MCP Integration +- [ ] Add MCP tool definitions in `packages/mcp-server/src/tools/` +- [ ] Wire up WorkflowOrchestrator to MCP tools +- [ ] Test MCP tools via Claude Code +- [ ] Document MCP workflow in CLAUDE.md + +### Step 5: CLI Integration +- [ ] Update `autopilot.command.ts` to call WorkflowOrchestrator +- [ ] Add `--interactive` mode that shows work units and waits for completion +- [ ] Add `--resume` flag to continue paused runs +- [ ] Test end-to-end flow + +### Step 6: Integration Testing +- [ ] Create test task with 2-3 subtasks +- [ ] Run autopilot start → get work unit → complete → repeat +- [ ] Verify state persistence and resumability +- [ ] Test failure scenarios (test failures, git issues) + +## Success Criteria +- [ ] WorkflowOrchestrator can generate work units for all phases +- [ ] MCP tools allow Claude Code to query and complete work units +- [ ] State persists correctly between work unit completions +- [ ] Run can be paused and resumed from checkpoint +- [ ] Adapters validate preconditions without executing commands +- [ ] End-to-end: Claude Code can complete a simple task via work units + +## Out of Scope (Phase 1) +- Actual git operations (branch creation, commits) - executor handles this +- Actual test execution - executor handles this +- PR creation - deferred to Phase 2 +- TUI interface - deferred to Phase 3 +- Coverage enforcement - deferred to Phase 2 + +## Example Usage Flow + +```bash +# Terminal 1: Claude Code session +$ claude + +# In Claude Code (via MCP): +> Start autopilot for task 42 +[Calls mcp__task_master_ai__autopilot_start(42)] +→ Run started: run-2025-01-15-142033 + +> Get next work unit +[Calls mcp__task_master_ai__autopilot_next_work_unit(run-2025-01-15-142033)] +→ Work unit: RED phase for subtask 42.1 +→ Action: Generate failing tests for metrics schema +→ Test file: src/__tests__/schema.test.js +→ Framework: vitest + +> [Claude Code creates test file, runs tests] + +> Complete work unit +[Calls mcp__task_master_ai__autopilot_complete_work_unit( + run-2025-01-15-142033, + workUnit-42.1-RED, + { success: true, testsCreated: ['src/__tests__/schema.test.js'], testsFailed: 3 } +)] +→ Work unit completed. State saved. + +> Get next work unit +[Calls mcp__task_master_ai__autopilot_next_work_unit(run-2025-01-15-142033)] +→ Work unit: GREEN phase for subtask 42.1 +→ Action: Implement code to pass failing tests +→ Test file: src/__tests__/schema.test.js +→ Expected implementation: src/schema.js + +> [Claude Code implements schema.js, runs tests, confirms all pass] + +> Complete work unit +[...] +→ Work unit completed. Ready for COMMIT. + +> Get next work unit +[...] +→ Work unit: COMMIT phase for subtask 42.1 +→ Commit message: "feat(metrics): add metrics schema (task 42.1)" +→ Files to commit: src/__tests__/schema.test.js, src/schema.js + +> [Claude Code stages files and commits] + +> Complete work unit +[...] +→ Subtask 42.1 complete! Moving to 42.2... +``` + +## Dependencies +- Existing TaskService (task loading, status updates) +- Existing PreflightChecker (environment validation) +- Existing TaskLoaderService (dependency ordering) +- MCP server infrastructure + +## Estimated Effort +7-10 days + +## Next Phase +Phase 2 will add: +- PR creation via gh CLI +- Coverage enforcement +- Enhanced error recovery +- Full resumability testing diff --git a/.taskmaster/docs/tdd-workflow-phase-2-pr-resumability.md b/.taskmaster/docs/tdd-workflow-phase-2-pr-resumability.md new file mode 100644 index 00000000..871812c9 --- /dev/null +++ b/.taskmaster/docs/tdd-workflow-phase-2-pr-resumability.md @@ -0,0 +1,433 @@ +# Phase 2: PR + Resumability - Autonomous TDD Workflow + +## Objective +Add PR creation with GitHub CLI integration, resumable checkpoints for interrupted runs, and enhanced guardrails with coverage enforcement. + +## Scope +- GitHub PR creation via `gh` CLI +- Well-formed PR body using run report +- Resumable checkpoints and `--resume` flag +- Coverage enforcement before finalization +- Optional lint/format step +- Enhanced error recovery + +## Deliverables + +### 1. PR Creation Integration + +**PRAdapter** (`packages/tm-core/src/services/pr-adapter.ts`): +```typescript +class PRAdapter { + async isGHAvailable(): Promise<boolean> + async createPR(options: PROptions): Promise<PRResult> + async getPRTemplate(runReport: RunReport): Promise<string> + + // Fallback for missing gh CLI + async getManualPRInstructions(options: PROptions): Promise<string> +} + +interface PROptions { + branch: string + base: string + title: string + body: string + draft?: boolean +} + +interface PRResult { + url: string + number: number +} +``` + +**PR Title Format:** +``` +Task #<id> [<tag>]: <title> +``` + +Example: `Task #42 [analytics]: User metrics tracking` + +**PR Body Template:** + +Located at `.taskmaster/templates/pr-body.md`: + +```markdown +## Summary + +Implements Task #42 from TaskMaster autonomous workflow. + +**Branch:** {branch} +**Tag:** {tag} +**Subtasks completed:** {subtaskCount} + +{taskDescription} + +## Subtasks + +{subtasksList} + +## Test Coverage + +| Metric | Coverage | +|--------|----------| +| Lines | {lines}% | +| Branches | {branches}% | +| Functions | {functions}% | +| Statements | {statements}% | + +**All subtasks passed with {totalTests} tests.** + +## Commits + +{commitsList} + +## Run Report + +Full execution report: `.taskmaster/reports/runs/{runId}/` + +--- + +🤖 Generated with [Task Master](https://github.com/cline/task-master) autonomous TDD workflow +``` + +**Token replacement:** +- `{branch}` → branch name +- `{tag}` → active tag +- `{subtaskCount}` → number of completed subtasks +- `{taskDescription}` → task description from TaskMaster +- `{subtasksList}` → markdown list of subtask titles +- `{lines}`, `{branches}`, `{functions}`, `{statements}` → coverage percentages +- `{totalTests}` → total test count +- `{commitsList}` → markdown list of commit SHAs and messages +- `{runId}` → run ID timestamp + +### 2. GitHub CLI Integration + +**Detection:** +```bash +which gh +``` + +If not found, show fallback instructions: +```bash +✓ Branch pushed: analytics/task-42-user-metrics +✗ gh CLI not found - cannot create PR automatically + +To create PR manually: + gh pr create \ + --base main \ + --head analytics/task-42-user-metrics \ + --title "Task #42 [analytics]: User metrics tracking" \ + --body-file .taskmaster/reports/runs/2025-01-15-142033/pr.md + +Or visit: + https://github.com/org/repo/compare/main...analytics/task-42-user-metrics +``` + +**Confirmation gate:** +```bash +Ready to create PR: + Title: Task #42 [analytics]: User metrics tracking + Base: main + Head: analytics/task-42-user-metrics + +Create PR? [Y/n] +``` + +Unless `--no-confirm` flag is set. + +### 3. Resumable Workflow + +**State Checkpoint** (`state.json`): +```json +{ + "runId": "2025-01-15-142033", + "taskId": "42", + "phase": "subtask-loop", + "currentSubtask": "42.2", + "currentPhase": "green", + "attempts": 2, + "completedSubtasks": ["42.1"], + "commits": ["a1b2c3d"], + "branch": "analytics/task-42-user-metrics", + "tag": "analytics", + "canResume": true, + "pausedAt": "2025-01-15T14:25:35Z", + "pausedReason": "max_attempts_reached", + "nextAction": "manual_review_required" +} +``` + +**Resume Command:** +```bash +$ tm autopilot --resume + +Resuming run: 2025-01-15-142033 + Task: #42 [analytics] User metrics tracking + Branch: analytics/task-42-user-metrics + Last subtask: 42.2 (GREEN phase, attempt 2/3 failed) + Paused: 5 minutes ago + +Reason: Could not achieve green state after 3 attempts +Last error: POST /api/metrics returns 500 instead of 201 + +Resume from subtask 42.2 GREEN phase? [Y/n] +``` + +**Resume logic:** +1. Load state from `.taskmaster/reports/runs/<runId>/state.json` +2. Verify branch still exists and is checked out +3. Verify no uncommitted changes (unless `--force`) +4. Continue from last checkpoint phase +5. Update state file as execution progresses + +**Multiple interrupted runs:** +```bash +$ tm autopilot --resume + +Found 2 resumable runs: + 1. 2025-01-15-142033 - Task #42 (paused 5 min ago at subtask 42.2 GREEN) + 2. 2025-01-14-103022 - Task #38 (paused 2 hours ago at subtask 38.3 RED) + +Select run to resume [1-2]: +``` + +### 4. Coverage Enforcement + +**Coverage Check Phase** (before finalization): +```typescript +async function enforceCoverage(runId: string): Promise<void> { + const testResults = await testRunner.runAll() + const coverage = await testRunner.getCoverage() + + const thresholds = config.test.coverageThresholds + const failures = [] + + if (coverage.lines < thresholds.lines) { + failures.push(`Lines: ${coverage.lines}% < ${thresholds.lines}%`) + } + // ... check branches, functions, statements + + if (failures.length > 0) { + throw new CoverageError( + `Coverage thresholds not met:\n${failures.join('\n')}` + ) + } + + // Store coverage in run report + await storeRunArtifact(runId, 'coverage.json', coverage) +} +``` + +**Handling coverage failures:** +```bash +⚠️ Coverage check failed: + Lines: 78.5% < 80% + Branches: 75.0% < 80% + +Options: + 1. Add more tests and resume + 2. Lower thresholds in .taskmaster/config.json + 3. Skip coverage check: tm autopilot --resume --skip-coverage + +Run paused. Fix coverage and resume with: + tm autopilot --resume +``` + +### 5. Optional Lint/Format Step + +**Configuration:** +```json +{ + "autopilot": { + "finalization": { + "lint": { + "enabled": true, + "command": "npm run lint", + "fix": true, + "failOnError": false + }, + "format": { + "enabled": true, + "command": "npm run format", + "commitChanges": true + } + } + } +} +``` + +**Execution:** +```bash +Finalization Steps: + + ✓ All tests passing (12 tests, 0 failures) + ✓ Coverage thresholds met (85% lines, 82% branches) + + LINT Running linter... ⏳ + LINT ✓ No lint errors + + FORMAT Running formatter... ⏳ + FORMAT ✓ Formatted 3 files + FORMAT ✓ Committed formatting changes: "chore: auto-format code" + + PUSH Pushing to origin... ⏳ + PUSH ✓ Pushed analytics/task-42-user-metrics + + PR Creating pull request... ⏳ + PR ✓ Created PR #123 + https://github.com/org/repo/pull/123 +``` + +### 6. Enhanced Error Recovery + +**Pause Points:** +- Max GREEN attempts reached (current) +- Coverage check failed (new) +- Lint errors (if `failOnError: true`) +- Git push failed (new) +- PR creation failed (new) + +**Each pause saves:** +- Full state checkpoint +- Last command output +- Suggested next actions +- Resume instructions + +**Automatic recovery attempts:** +- Git push: retry up to 3 times with backoff +- PR creation: fall back to manual instructions +- Lint: auto-fix if enabled, otherwise pause + +### 7. Finalization Phase Enhancement + +**Updated workflow:** +1. Run full test suite +2. Check coverage thresholds → pause if failed +3. Run lint (if enabled) → pause if failed and `failOnError: true` +4. Run format (if enabled) → auto-commit changes +5. Confirm push (unless `--no-confirm`) +6. Push branch → retry on failure +7. Generate PR body from template +8. Create PR via gh → fall back to manual instructions +9. Update task status to 'review' (configurable) +10. Save final run report + +**Final output:** +```bash +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +✅ Task #42 [analytics]: User metrics tracking - COMPLETE + + Branch: analytics/task-42-user-metrics + Subtasks completed: 3/3 + Commits: 3 + Total tests: 12 (12 passed, 0 failed) + Coverage: 85% lines, 82% branches, 88% functions, 85% statements + + PR #123: https://github.com/org/repo/pull/123 + + Run report: .taskmaster/reports/runs/2025-01-15-142033/ + +Next steps: + - Review PR and request changes if needed + - Merge when ready + - Task status updated to 'review' + +Completed in 24 minutes +``` + +## CLI Updates + +**New flags:** +- `--resume` → Resume from last checkpoint +- `--skip-coverage` → Skip coverage checks +- `--skip-lint` → Skip lint step +- `--skip-format` → Skip format step +- `--skip-pr` → Push branch but don't create PR +- `--draft-pr` → Create draft PR instead of ready-for-review + +## Configuration Updates + +**Add to `.taskmaster/config.json`:** +```json +{ + "autopilot": { + "finalization": { + "lint": { + "enabled": false, + "command": "npm run lint", + "fix": true, + "failOnError": false + }, + "format": { + "enabled": false, + "command": "npm run format", + "commitChanges": true + }, + "updateTaskStatus": "review" + } + }, + "git": { + "pr": { + "enabled": true, + "base": "default", + "bodyTemplate": ".taskmaster/templates/pr-body.md", + "draft": false + }, + "pushRetries": 3, + "pushRetryDelay": 5000 + } +} +``` + +## Success Criteria +- Can create PR automatically with well-formed body +- Can resume interrupted runs from any checkpoint +- Coverage checks prevent low-quality code from being merged +- Clear error messages and recovery paths for all failure modes +- Run reports include full PR context for review + +## Out of Scope (defer to Phase 3) +- Multiple test framework support (pytest, go test) +- Diff preview before commits +- TUI panel implementation +- Extension/IDE integration + +## Testing Strategy +- Mock `gh` CLI for PR creation tests +- Test resume from each possible pause point +- Test coverage failure scenarios +- Test lint/format integration with mock commands +- End-to-end test with PR creation on test repo + +## Dependencies +- Phase 1 completed (core workflow) +- GitHub CLI (`gh`) installed (optional, fallback provided) +- Test framework supports coverage output + +## Estimated Effort +1-2 weeks + +## Risks & Mitigations +- **Risk:** GitHub CLI auth issues + - **Mitigation:** Clear auth setup docs, fallback to manual instructions + +- **Risk:** PR body template doesn't match all project needs + - **Mitigation:** Make template customizable via config path + +- **Risk:** Resume state gets corrupted + - **Mitigation:** Validate state on load, provide --force-reset option + +- **Risk:** Coverage calculation differs between runs + - **Mitigation:** Store coverage with each test run for comparison + +## Validation +Test with: +- Successful PR creation end-to-end +- Resume from GREEN attempt failure +- Resume from coverage failure +- Resume from lint failure +- Missing `gh` CLI (fallback to manual) +- Lint/format integration enabled +- Multiple interrupted runs (selection UI) diff --git a/.taskmaster/docs/tdd-workflow-phase-3-extensibility-guardrails.md b/.taskmaster/docs/tdd-workflow-phase-3-extensibility-guardrails.md new file mode 100644 index 00000000..2bd8b38c --- /dev/null +++ b/.taskmaster/docs/tdd-workflow-phase-3-extensibility-guardrails.md @@ -0,0 +1,534 @@ +# Phase 3: Extensibility + Guardrails - Autonomous TDD Workflow + +## Objective +Add multi-language/framework support, enhanced safety guardrails, TUI interface, and extensibility for IDE/editor integration. + +## Scope +- Multi-language test runner support (pytest, go test, etc.) +- Enhanced safety: diff preview, confirmation gates, minimal-change prompts +- Optional TUI panel with tmux integration +- State-based extension API for IDE integration +- Parallel subtask execution (experimental) + +## Deliverables + +### 1. Multi-Language Test Runner Support + +**Extend TestRunnerAdapter:** +```typescript +class TestRunnerAdapter { + // Existing methods... + + async detectLanguage(): Promise<Language> + async detectFramework(language: Language): Promise<Framework> + async getFrameworkAdapter(framework: Framework): Promise<FrameworkAdapter> +} + +enum Language { + JavaScript = 'javascript', + TypeScript = 'typescript', + Python = 'python', + Go = 'go', + Rust = 'rust' +} + +enum Framework { + Vitest = 'vitest', + Jest = 'jest', + Pytest = 'pytest', + GoTest = 'gotest', + CargoTest = 'cargotest' +} + +interface FrameworkAdapter { + runTargeted(pattern: string): Promise<TestResults> + runAll(): Promise<TestResults> + parseCoverage(output: string): Promise<CoverageReport> + getTestFilePattern(): string + getTestFileExtension(): string +} +``` + +**Framework-specific adapters:** + +**PytestAdapter** (`packages/tm-core/src/services/test-adapters/pytest-adapter.ts`): +```typescript +class PytestAdapter implements FrameworkAdapter { + async runTargeted(pattern: string): Promise<TestResults> { + const output = await exec(`pytest ${pattern} --json-report`) + return this.parseResults(output) + } + + async runAll(): Promise<TestResults> { + const output = await exec('pytest --cov --json-report') + return this.parseResults(output) + } + + parseCoverage(output: string): Promise<CoverageReport> { + // Parse pytest-cov XML output + } + + getTestFilePattern(): string { + return '**/test_*.py' + } + + getTestFileExtension(): string { + return '.py' + } +} +``` + +**GoTestAdapter** (`packages/tm-core/src/services/test-adapters/gotest-adapter.ts`): +```typescript +class GoTestAdapter implements FrameworkAdapter { + async runTargeted(pattern: string): Promise<TestResults> { + const output = await exec(`go test ${pattern} -json`) + return this.parseResults(output) + } + + async runAll(): Promise<TestResults> { + const output = await exec('go test ./... -coverprofile=coverage.out -json') + return this.parseResults(output) + } + + parseCoverage(output: string): Promise<CoverageReport> { + // Parse go test coverage output + } + + getTestFilePattern(): string { + return '**/*_test.go' + } + + getTestFileExtension(): string { + return '_test.go' + } +} +``` + +**Detection Logic:** +```typescript +async function detectFramework(): Promise<Framework> { + // Check for package.json + if (await exists('package.json')) { + const pkg = await readJSON('package.json') + if (pkg.devDependencies?.vitest) return Framework.Vitest + if (pkg.devDependencies?.jest) return Framework.Jest + } + + // Check for Python files + if (await exists('pytest.ini') || await exists('setup.py')) { + return Framework.Pytest + } + + // Check for Go files + if (await exists('go.mod')) { + return Framework.GoTest + } + + // Check for Rust files + if (await exists('Cargo.toml')) { + return Framework.CargoTest + } + + throw new Error('Could not detect test framework') +} +``` + +### 2. Enhanced Safety Guardrails + +**Diff Preview Mode:** +```bash +$ tm autopilot 42 --preview-diffs + +[2/3] Subtask 42.2: Add collection endpoint + + RED ✓ Tests created: src/api/__tests__/metrics.test.js + + GREEN Implementing code... + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Proposed changes (src/api/metrics.js): + + + import { MetricsSchema } from '../models/schema.js' + + + + export async function createMetric(data) { + + const validated = MetricsSchema.parse(data) + + const result = await db.metrics.create(validated) + + return result + + } + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Apply these changes? [Y/n/e(dit)/s(kip)] + Y - Apply and continue + n - Reject and retry GREEN phase + e - Open in editor for manual changes + s - Skip this subtask +``` + +**Minimal Change Enforcement:** + +Add to system prompt: +```markdown +CRITICAL: Make MINIMAL changes to pass the failing tests. +- Only modify files directly related to the subtask +- Do not refactor existing code unless absolutely necessary +- Do not add features beyond the acceptance criteria +- Keep changes under 50 lines per file when possible +- Prefer composition over modification +``` + +**Change Size Warnings:** +```bash +⚠️ Large change detected: + Files modified: 5 + Lines changed: +234, -12 + +This subtask was expected to be small (~50 lines). +Consider: + - Breaking into smaller subtasks + - Reviewing acceptance criteria + - Checking for unintended changes + +Continue anyway? [y/N] +``` + +### 3. TUI Interface with tmux + +**Layout:** +``` +┌──────────────────────────────────┬─────────────────────────────────┐ +│ Task Navigator (left) │ Executor Terminal (right) │ +│ │ │ +│ Project: my-app │ $ tm autopilot --executor-mode │ +│ Branch: analytics/task-42 │ > Running subtask 42.2 GREEN... │ +│ Tag: analytics │ > Implementing endpoint... │ +│ │ > Tests: 3 passed, 0 failed │ +│ Tasks: │ > Ready to commit │ +│ → 42 [in-progress] User metrics │ │ +│ → 42.1 [done] Schema │ [Live output from executor] │ +│ → 42.2 [active] Endpoint ◀ │ │ +│ → 42.3 [pending] Dashboard │ │ +│ │ │ +│ [s] start [p] pause [q] quit │ │ +└──────────────────────────────────┴─────────────────────────────────┘ +``` + +**Implementation:** + +**TUI Navigator** (`apps/cli/src/ui/tui/navigator.ts`): +```typescript +import blessed from 'blessed' + +class AutopilotTUI { + private screen: blessed.Widgets.Screen + private taskList: blessed.Widgets.ListElement + private statusBox: blessed.Widgets.BoxElement + private executorPane: string // tmux pane ID + + async start(taskId?: string) { + // Create blessed screen + this.screen = blessed.screen() + + // Create task list widget + this.taskList = blessed.list({ + label: 'Tasks', + keys: true, + vi: true, + style: { selected: { bg: 'blue' } } + }) + + // Spawn tmux pane for executor + this.executorPane = await this.spawnExecutorPane() + + // Watch state file for updates + this.watchStateFile() + + // Handle keybindings + this.setupKeybindings() + } + + private async spawnExecutorPane(): Promise<string> { + const paneId = await exec('tmux split-window -h -P -F "#{pane_id}"') + await exec(`tmux send-keys -t ${paneId} "tm autopilot --executor-mode" Enter`) + return paneId.trim() + } + + private watchStateFile() { + watch('.taskmaster/state/current-run.json', (event, filename) => { + this.updateDisplay() + }) + } + + private setupKeybindings() { + this.screen.key(['s'], () => this.startTask()) + this.screen.key(['p'], () => this.pauseTask()) + this.screen.key(['q'], () => this.quit()) + this.screen.key(['up', 'down'], () => this.navigateTasks()) + } +} +``` + +**Executor Mode:** +```bash +$ tm autopilot 42 --executor-mode + +# Runs in executor pane, writes state to shared file +# Left pane reads state file and updates display +``` + +**State File** (`.taskmaster/state/current-run.json`): +```json +{ + "runId": "2025-01-15-142033", + "taskId": "42", + "status": "running", + "currentPhase": "green", + "currentSubtask": "42.2", + "lastOutput": "Implementing endpoint...", + "testsStatus": { + "passed": 3, + "failed": 0 + } +} +``` + +### 4. Extension API for IDE Integration + +**State-based API:** + +Expose run state via JSON files that IDEs can read: +- `.taskmaster/state/current-run.json` - live run state +- `.taskmaster/reports/runs/<runId>/manifest.json` - run metadata +- `.taskmaster/reports/runs/<runId>/log.jsonl` - event stream + +**WebSocket API (optional):** +```typescript +// packages/tm-core/src/services/autopilot-server.ts +class AutopilotServer { + private wss: WebSocketServer + + start(port: number = 7890) { + this.wss = new WebSocketServer({ port }) + + this.wss.on('connection', (ws) => { + // Send current state + ws.send(JSON.stringify(this.getCurrentState())) + + // Stream events + this.orchestrator.on('*', (event) => { + ws.send(JSON.stringify(event)) + }) + }) + } +} +``` + +**Usage from IDE extension:** +```typescript +// VS Code extension example +const ws = new WebSocket('ws://localhost:7890') + +ws.on('message', (data) => { + const event = JSON.parse(data) + + if (event.type === 'subtask:complete') { + vscode.window.showInformationMessage( + `Subtask ${event.subtaskId} completed` + ) + } +}) +``` + +### 5. Parallel Subtask Execution (Experimental) + +**Dependency Analysis:** +```typescript +class SubtaskScheduler { + async buildDependencyGraph(subtasks: Subtask[]): Promise<DAG> { + const graph = new DAG() + + for (const subtask of subtasks) { + graph.addNode(subtask.id) + + for (const depId of subtask.dependencies) { + graph.addEdge(depId, subtask.id) + } + } + + return graph + } + + async getParallelBatches(graph: DAG): Promise<Subtask[][]> { + const batches: Subtask[][] = [] + const completed = new Set<string>() + + while (completed.size < graph.size()) { + const ready = graph.nodes.filter(node => + !completed.has(node.id) && + node.dependencies.every(dep => completed.has(dep)) + ) + + batches.push(ready) + ready.forEach(node => completed.add(node.id)) + } + + return batches + } +} +``` + +**Parallel Execution:** +```bash +$ tm autopilot 42 --parallel + +[Batch 1] Running 2 subtasks in parallel: + → 42.1: Add metrics schema + → 42.4: Add API documentation + + 42.1 RED ✓ Tests created + 42.4 RED ✓ Tests created + + 42.1 GREEN ✓ Implementation complete + 42.4 GREEN ✓ Implementation complete + + 42.1 COMMIT ✓ Committed: a1b2c3d + 42.4 COMMIT ✓ Committed: e5f6g7h + +[Batch 2] Running 2 subtasks in parallel (depend on 42.1): + → 42.2: Add collection endpoint + → 42.3: Add dashboard widget + ... +``` + +**Conflict Detection:** +```typescript +async function detectConflicts(subtasks: Subtask[]): Promise<Conflict[]> { + const conflicts: Conflict[] = [] + + for (let i = 0; i < subtasks.length; i++) { + for (let j = i + 1; j < subtasks.length; j++) { + const filesA = await predictAffectedFiles(subtasks[i]) + const filesB = await predictAffectedFiles(subtasks[j]) + + const overlap = filesA.filter(f => filesB.includes(f)) + + if (overlap.length > 0) { + conflicts.push({ + subtasks: [subtasks[i].id, subtasks[j].id], + files: overlap + }) + } + } + } + + return conflicts +} +``` + +### 6. Advanced Configuration + +**Add to `.taskmaster/config.json`:** +```json +{ + "autopilot": { + "safety": { + "previewDiffs": false, + "maxChangeLinesPerFile": 100, + "warnOnLargeChanges": true, + "requireConfirmOnLargeChanges": true + }, + "parallel": { + "enabled": false, + "maxConcurrent": 3, + "detectConflicts": true + }, + "tui": { + "enabled": false, + "tmuxSession": "taskmaster-autopilot" + }, + "api": { + "enabled": false, + "port": 7890, + "allowRemote": false + } + }, + "test": { + "frameworks": { + "python": { + "runner": "pytest", + "coverageCommand": "pytest --cov", + "testPattern": "**/test_*.py" + }, + "go": { + "runner": "go test", + "coverageCommand": "go test ./... -coverprofile=coverage.out", + "testPattern": "**/*_test.go" + } + } + } +} +``` + +## CLI Updates + +**New commands:** +```bash +tm autopilot <taskId> --tui # Launch TUI interface +tm autopilot <taskId> --parallel # Enable parallel execution +tm autopilot <taskId> --preview-diffs # Show diffs before applying +tm autopilot <taskId> --executor-mode # Run as executor pane +tm autopilot-server start # Start WebSocket API +``` + +## Success Criteria +- Supports Python projects with pytest +- Supports Go projects with go test +- Diff preview prevents unwanted changes +- TUI provides better visibility for long-running tasks +- IDE extensions can integrate via state files or WebSocket +- Parallel execution reduces total time for independent subtasks + +## Out of Scope +- Full Electron/web GUI +- AI executor selection UI (defer to Phase 4) +- Multi-repository support +- Remote execution on cloud runners + +## Testing Strategy +- Test with Python project (pytest) +- Test with Go project (go test) +- Test diff preview UI with mock changes +- Test parallel execution with independent subtasks +- Test conflict detection with overlapping file changes +- Test TUI with mock tmux environment + +## Dependencies +- Phase 2 completed (PR + resumability) +- tmux installed (for TUI) +- blessed or ink library (for TUI rendering) + +## Estimated Effort +3-4 weeks + +## Risks & Mitigations +- **Risk:** Parallel execution causes git conflicts + - **Mitigation:** Conservative conflict detection, sequential fallback + +- **Risk:** TUI adds complexity and maintenance burden + - **Mitigation:** Keep TUI optional, state-based design allows alternatives + +- **Risk:** Framework adapters hard to maintain across versions + - **Mitigation:** Abstract common parsing logic, document adapter interface + +- **Risk:** Diff preview slows down workflow + - **Mitigation:** Make optional, use --preview-diffs flag only when needed + +## Validation +Test with: +- Python project with pytest and pytest-cov +- Go project with go test +- Large changes requiring confirmation +- Parallel execution with 3+ independent subtasks +- TUI with task selection and live status updates +- VS Code extension reading state files diff --git a/.taskmaster/reports/task-complexity-report_autonomous-tdd-git-workflow.json b/.taskmaster/reports/task-complexity-report_autonomous-tdd-git-workflow.json new file mode 100644 index 00000000..cae8097b --- /dev/null +++ b/.taskmaster/reports/task-complexity-report_autonomous-tdd-git-workflow.json @@ -0,0 +1,197 @@ +{ + "meta": { + "generatedAt": "2025-10-07T09:46:06.248Z", + "tasksAnalyzed": 23, + "totalTasks": 23, + "analysisCount": 23, + "thresholdScore": 5, + "projectName": "Taskmaster", + "usedResearch": false + }, + "complexityAnalysis": [ + { + "taskId": 31, + "taskTitle": "Create WorkflowOrchestrator service foundation", + "complexityScore": 7, + "recommendedSubtasks": 5, + "expansionPrompt": "Break down the WorkflowOrchestrator foundation into its core architectural components: phase management system, event emitter infrastructure, state management interfaces, service integration, and lifecycle control methods. Each subtask should focus on a specific architectural concern with clear interfaces and testable units.", + "reasoning": "This is a foundational service requiring state machine implementation, event-driven architecture, and integration with existing services. The complexity is high due to the need for robust phase management, error handling, and service orchestration patterns." + }, + { + "taskId": 32, + "taskTitle": "Implement GitAdapter for repository operations", + "complexityScore": 6, + "recommendedSubtasks": 4, + "expansionPrompt": "Decompose the GitAdapter implementation into: TypeScript wrapper creation around existing git-utils.js, core git operation methods with comprehensive error handling, branch naming pattern system with token replacement, and confirmation gates for destructive operations. Focus on type safety and existing code integration.", + "reasoning": "Moderate-high complexity due to TypeScript integration over existing JavaScript utilities, branch pattern implementation, and safety mechanisms. The existing git-utils.js provides a solid foundation, reducing complexity." + }, + { + "taskId": 33, + "taskTitle": "Create TestRunnerAdapter for framework detection and execution", + "complexityScore": 8, + "recommendedSubtasks": 6, + "expansionPrompt": "Break down TestRunnerAdapter into framework detection logic, test execution engine with process management, Jest-specific result parsing, Vitest-specific result parsing, unified result interfaces, and final integration. Each framework parser should be separate to handle their unique output formats.", + "reasoning": "High complexity due to multiple framework support (Jest, Vitest), child process management, result parsing from different formats, coverage reporting, and timeout handling. Each framework has unique output formats requiring specialized parsers." + }, + { + "taskId": 34, + "taskTitle": "Implement autopilot CLI command structure", + "complexityScore": 5, + "recommendedSubtasks": 4, + "expansionPrompt": "Structure the autopilot command into: basic command setup with Commander.js integration, comprehensive flag handling and validation system, preflight check validation with environment validation, and WorkflowOrchestrator integration with dry-run execution planning. Follow existing CLI patterns from the codebase.", + "reasoning": "Moderate complexity involving CLI structure, flag handling, and integration with WorkflowOrchestrator. The existing CLI patterns and Commander.js usage in the codebase provide good guidance, reducing implementation complexity." + }, + { + "taskId": 35, + "taskTitle": "Integrate surgical test generator with WorkflowOrchestrator", + "complexityScore": 6, + "recommendedSubtasks": 4, + "expansionPrompt": "Decompose the test generation integration into: TaskExecutionService enhancement for test generation mode, TestGenerationService creation using executor framework, prompt composition system for rule integration, and framework-specific test pattern support. Leverage existing executor patterns from the codebase.", + "reasoning": "Moderate-high complexity due to integration with existing services, prompt composition system, and framework-specific test generation. The existing executor framework and TaskExecutionService provide good integration points." + }, + { + "taskId": 36, + "taskTitle": "Implement subtask TDD loop execution", + "complexityScore": 9, + "recommendedSubtasks": 7, + "expansionPrompt": "Break down the TDD loop into: SubtaskExecutor class architecture, RED phase test generation, GREEN phase code generation, COMMIT phase with conventional commits, retry mechanism for GREEN phase, timeout and backoff policies, and TaskService integration. Each phase should be independently testable.", + "reasoning": "Very high complexity due to implementing the complete TDD red-green-commit cycle with AI integration, retry logic, timeout handling, and git operations. This is the core autonomous workflow requiring robust error handling and state management." + }, + { + "taskId": 37, + "taskTitle": "Add configuration schema for autopilot settings", + "complexityScore": 4, + "recommendedSubtasks": 3, + "expansionPrompt": "Expand configuration support into: extending configuration interfaces with autopilot settings, updating ConfigManager validation logic, and implementing default configuration values. Build on existing configuration patterns and maintain backward compatibility.", + "reasoning": "Low-moderate complexity involving schema extension and validation logic. The existing configuration system provides clear patterns to follow, making this primarily an extension task rather than new architecture." + }, + { + "taskId": 38, + "taskTitle": "Implement run state persistence and logging", + "complexityScore": 6, + "recommendedSubtasks": 5, + "expansionPrompt": "Structure run state management into: RunStateManager service class creation, run directory structure and manifest creation, JSONL event logging system, test result and commit tracking storage, and state checkpointing with resume functionality. Focus on data integrity and structured logging.", + "reasoning": "Moderate-high complexity due to file system operations, structured logging, state serialization, and resume functionality. Requires careful design of data formats and error handling for persistence operations." + }, + { + "taskId": 39, + "taskTitle": "Add GitHub PR creation with run reports", + "complexityScore": 5, + "recommendedSubtasks": 4, + "expansionPrompt": "Decompose PR creation into: PRAdapter service foundation with interfaces, GitHub CLI integration and command execution, PR body generation from run data and test results, and custom PR template system with configuration support. Leverage existing git-utils.js patterns for CLI integration.", + "reasoning": "Moderate complexity involving GitHub CLI integration, report generation, and template systems. The existing git-utils.js provides patterns for CLI tool integration, reducing implementation complexity." + }, + { + "taskId": 40, + "taskTitle": "Implement task dependency resolution for subtask ordering", + "complexityScore": 6, + "recommendedSubtasks": 4, + "expansionPrompt": "Break down dependency resolution into: dependency resolution algorithm with cycle detection, topological sorting for subtask ordering, task eligibility checking system, and TaskService integration. Implement graph algorithms for dependency management with proper error handling.", + "reasoning": "Moderate-high complexity due to graph algorithm implementation, cycle detection, and integration with existing task management. Requires careful design of dependency resolution logic and edge case handling." + }, + { + "taskId": 41, + "taskTitle": "Create resume functionality for interrupted runs", + "complexityScore": 7, + "recommendedSubtasks": 5, + "expansionPrompt": "Structure resume functionality into: checkpoint creation in RunStateManager, state restoration logic with validation, state validation for safe resume operations, CLI flag implementation for resume command, and partial phase resume functionality. Focus on data integrity and workflow consistency.", + "reasoning": "High complexity due to state serialization/deserialization, workflow restoration, validation logic, and CLI integration. Requires robust error handling and state consistency checks for reliable resume operations." + }, + { + "taskId": 42, + "taskTitle": "Add coverage threshold enforcement", + "complexityScore": 5, + "recommendedSubtasks": 4, + "expansionPrompt": "Decompose coverage enforcement into: coverage report parsing from Jest/Vitest, configurable threshold validation logic, coverage gates integration in workflow phases, and detailed coverage failure reporting system. Build on existing TestRunnerAdapter patterns.", + "reasoning": "Moderate complexity involving coverage report parsing, validation logic, and workflow integration. The existing TestRunnerAdapter provides good foundation for extending coverage capabilities." + }, + { + "taskId": 43, + "taskTitle": "Implement tmux-based TUI navigator", + "complexityScore": 8, + "recommendedSubtasks": 6, + "expansionPrompt": "Break down TUI implementation into: framework selection and basic structure setup, left pane interface layout with status indicators, tmux integration and terminal coordination, navigation system with keybindings, real-time status updates system, and comprehensive event handling with UX polish. Each component should be independently testable.", + "reasoning": "High complexity due to terminal UI framework integration, tmux session management, real-time updates, keyboard event handling, and terminal interface design. Requires expertise in terminal UI libraries and tmux integration." + }, + { + "taskId": 44, + "taskTitle": "Add prompt composition system for context-aware test generation", + "complexityScore": 6, + "recommendedSubtasks": 4, + "expansionPrompt": "Structure prompt composition into: PromptComposer service foundation, template processing engine with token replacement, rule loading system with precedence handling, and context injection with phase-specific prompt generation. Focus on flexible template system and rule management.", + "reasoning": "Moderate-high complexity due to template processing, rule precedence systems, and context injection logic. Requires careful design of template syntax and rule loading mechanisms." + }, + { + "taskId": 45, + "taskTitle": "Implement tag-branch mapping and automatic tag switching", + "complexityScore": 5, + "recommendedSubtasks": 3, + "expansionPrompt": "Decompose tag-branch mapping into: GitAdapter enhancement with branch-to-tag extraction logic, automatic tag switching workflow integration, and branch-to-tag mapping persistence with validation. Build on existing git-utils.js and tag management functionality.", + "reasoning": "Moderate complexity involving pattern matching, tag management integration, and workflow automation. The existing git-utils.js and tag management systems provide good foundation for implementation." + }, + { + "taskId": 46, + "taskTitle": "Add comprehensive error handling and recovery", + "complexityScore": 7, + "recommendedSubtasks": 5, + "expansionPrompt": "Structure error handling into: error classification system with specific error types, recovery suggestion engine with actionable recommendations, error context management and preservation, force flag implementation with selective bypass, and logging/reporting system integration. Focus on actionable error messages and automated recovery where possible.", + "reasoning": "High complexity due to comprehensive error taxonomy, recovery automation, context preservation, and integration across all workflow components. Requires deep understanding of failure modes and recovery strategies." + }, + { + "taskId": 47, + "taskTitle": "Implement conventional commit message generation", + "complexityScore": 4, + "recommendedSubtasks": 3, + "expansionPrompt": "Break down commit message generation into: template system creation with variable substitution, commit type auto-detection based on task content and file changes, and validation with GitAdapter integration. Follow conventional commit standards and integrate with existing git operations.", + "reasoning": "Low-moderate complexity involving template processing, pattern matching for commit type detection, and validation logic. Well-defined conventional commit standards provide clear implementation guidance." + }, + { + "taskId": 48, + "taskTitle": "Add multi-framework test execution support", + "complexityScore": 7, + "recommendedSubtasks": 5, + "expansionPrompt": "Expand test framework support into: framework detection system for multiple languages, common adapter interface design, Python pytest adapter implementation, Go and Rust adapter implementations, and integration with existing TestRunnerAdapter. Each language adapter should follow the unified interface pattern.", + "reasoning": "High complexity due to multi-language support, framework detection across different ecosystems, and adapter pattern implementation. Each language has unique testing conventions and output formats." + }, + { + "taskId": 49, + "taskTitle": "Implement workflow event streaming for real-time monitoring", + "complexityScore": 6, + "recommendedSubtasks": 4, + "expansionPrompt": "Structure event streaming into: WorkflowOrchestrator EventEmitter enhancement, structured event format with metadata, event persistence to run logs, and optional WebSocket streaming for external monitoring. Focus on event consistency and real-time delivery.", + "reasoning": "Moderate-high complexity due to event-driven architecture, structured event formats, persistence integration, and WebSocket implementation. Requires careful design of event schemas and delivery mechanisms." + }, + { + "taskId": 50, + "taskTitle": "Add intelligent test targeting for faster feedback", + "complexityScore": 7, + "recommendedSubtasks": 5, + "expansionPrompt": "Decompose test targeting into: file change detection system, test dependency analysis engine, framework-specific targeting adapters, test impact calculation algorithm, and fallback integration with TestRunnerAdapter. Focus on accuracy and performance optimization.", + "reasoning": "High complexity due to dependency analysis, impact calculation algorithms, framework-specific targeting, and integration with existing test execution. Requires sophisticated analysis of code relationships and test dependencies." + }, + { + "taskId": 51, + "taskTitle": "Implement dry-run visualization with execution timeline", + "complexityScore": 6, + "recommendedSubtasks": 4, + "expansionPrompt": "Structure dry-run visualization into: timeline calculation engine with duration estimates, estimation algorithms based on task complexity, ASCII art progress visualization with formatting, and resource validation with preflight checks. Focus on accurate planning and clear visual presentation.", + "reasoning": "Moderate-high complexity due to timeline calculation, estimation algorithms, ASCII visualization, and resource validation. Requires understanding of workflow timing and visual formatting for terminal output." + }, + { + "taskId": 52, + "taskTitle": "Add autopilot workflow integration tests", + "complexityScore": 8, + "recommendedSubtasks": 6, + "expansionPrompt": "Structure integration testing into: isolated test environment infrastructure, mock integrations and service stubs, end-to-end workflow test scenarios, performance benchmarking and resource monitoring, test isolation and parallelization strategies, and comprehensive result validation and reporting. Focus on realistic test scenarios and reliable automation.", + "reasoning": "High complexity due to end-to-end testing requirements, mock service integration, performance testing, isolation mechanisms, and comprehensive validation. Requires sophisticated test infrastructure and scenario design." + }, + { + "taskId": 53, + "taskTitle": "Finalize autopilot documentation and examples", + "complexityScore": 3, + "recommendedSubtasks": 4, + "expansionPrompt": "Structure documentation into: comprehensive autopilot documentation covering setup and usage, example PRD files and templates for different project types, troubleshooting guide for common issues and solutions, and demo materials with workflow visualization. Focus on clarity and practical examples.", + "reasoning": "Low complexity involving documentation writing, example creation, and demo material production. The main challenge is ensuring accuracy and completeness rather than technical implementation." + } + ] +} diff --git a/.taskmaster/reports/task-complexity-report_tdd-phase-1-core-rails.json b/.taskmaster/reports/task-complexity-report_tdd-phase-1-core-rails.json new file mode 100644 index 00000000..4602a8a3 --- /dev/null +++ b/.taskmaster/reports/task-complexity-report_tdd-phase-1-core-rails.json @@ -0,0 +1,93 @@ +{ + "meta": { + "generatedAt": "2025-10-09T12:47:27.960Z", + "tasksAnalyzed": 10, + "totalTasks": 10, + "analysisCount": 10, + "thresholdScore": 5, + "projectName": "Taskmaster", + "usedResearch": false + }, + "complexityAnalysis": [ + { + "taskId": 1, + "taskTitle": "Design and Implement Global Storage System", + "complexityScore": 7, + "recommendedSubtasks": 6, + "expansionPrompt": "Break down the global storage system implementation into: 1) Path normalization utilities with cross-platform support, 2) Run ID generation and validation, 3) Manifest.json structure and management, 4) Activity.jsonl append-only logging, 5) State.json mutable checkpoint handling, and 6) Directory structure creation and cleanup. Focus on robust error handling, atomic operations, and isolation between different runs.", + "reasoning": "Complex system requiring cross-platform path handling, multiple file formats (JSON/JSONL), atomic operations, and state management. The existing codebase shows sophisticated file operations infrastructure but this extends beyond current patterns. Implementation involves filesystem operations, concurrency concerns, and data integrity." + }, + { + "taskId": 2, + "taskTitle": "Build GitAdapter with Safety Checks", + "complexityScore": 8, + "recommendedSubtasks": 7, + "expansionPrompt": "Decompose GitAdapter into: 1) Git repository detection and validation, 2) Working tree status checking with detailed reporting, 3) Branch operations (create, checkout, list) with safety guards, 4) Commit operations with metadata embedding, 5) Default branch detection and protection logic, 6) Push operations with conflict handling, and 7) Branch name generation from patterns. Emphasize safety checks, confirmation gates, and comprehensive error messages.", + "reasoning": "High complexity due to git operations safety requirements, multiple git commands integration, error handling for various git states, and safety mechanisms. The PRD emphasizes never allowing commits on default branch and requiring clean working tree - critical safety features that need robust implementation." + }, + { + "taskId": 3, + "taskTitle": "Implement Test Result Validator", + "complexityScore": 5, + "recommendedSubtasks": 4, + "expansionPrompt": "Split test validation into: 1) Input validation and schema definition for test results, 2) RED phase validation logic (ensuring failures exist), 3) GREEN phase validation logic (ensuring all tests pass), and 4) Coverage threshold validation with configurable limits. Include comprehensive validation messages and suggestions for common failure scenarios.", + "reasoning": "Moderate complexity focused on business logic validation. The validator is framework-agnostic (only validates reported numbers), has clear validation rules, and well-defined input/output. The existing codebase shows validation patterns that can be leveraged." + }, + { + "taskId": 4, + "taskTitle": "Develop WorkflowOrchestrator State Machine", + "complexityScore": 9, + "recommendedSubtasks": 8, + "expansionPrompt": "Structure the orchestrator into: 1) State machine definition and transitions (Preflight → BranchSetup → SubtaskLoop → Finalize), 2) Event emission system with comprehensive event types, 3) State persistence and recovery mechanisms, 4) Phase coordination and validation, 5) Subtask iteration and progress tracking, 6) Error handling and recovery strategies, 7) Resume functionality from checkpoints, and 8) Integration points for Git, Test, and other adapters.", + "reasoning": "Very high complexity as the central coordination component. Must orchestrate multiple adapters, handle state transitions, event emission, persistence, and recovery. The state machine needs to be robust, resumable, and coordinate all other components. Critical for the entire workflow's reliability." + }, + { + "taskId": 5, + "taskTitle": "Create Enhanced Commit Message Generator", + "complexityScore": 4, + "recommendedSubtasks": 3, + "expansionPrompt": "Organize commit message generation into: 1) Template parsing and variable substitution with configurable templates, 2) Scope detection from changed files with intelligent categorization, and 3) Metadata embedding (task context, test results, coverage) with conventional commits compliance. Ensure messages are parseable and contain all required task metadata.", + "reasoning": "Relatively straightforward text processing and template system. The conventional commits format is well-defined, and the metadata requirements are clear. The existing package.json shows commander dependency for CLI patterns that can be leveraged." + }, + { + "taskId": 6, + "taskTitle": "Implement Subtask TDD Loop", + "complexityScore": 8, + "recommendedSubtasks": 6, + "expansionPrompt": "Break down the TDD loop into: 1) RED phase orchestration with test generation coordination, 2) GREEN phase orchestration with implementation guidance, 3) COMMIT phase with file staging and commit creation, 4) Attempt tracking and maximum retry logic, 5) Phase transition validation and state updates, and 6) Activity logging for all phase transitions. Focus on robust state management and clear error recovery paths.", + "reasoning": "High complexity due to coordinating multiple phases, state transitions, retry logic, and integration with multiple adapters (Git, Test, State). This is the core workflow execution engine requiring careful orchestration and error handling." + }, + { + "taskId": 7, + "taskTitle": "Build CLI Commands for AI Agent Orchestration", + "complexityScore": 6, + "recommendedSubtasks": 5, + "expansionPrompt": "Structure CLI commands into: 1) Command registration and argument parsing setup, 2) `start` and `resume` commands with initialization logic, 3) `next` and `status` commands with JSON output formatting, 4) `complete` command with result validation integration, and 5) `commit` and `abort` commands with git operation coordination. Ensure consistent JSON output for machine parsing and comprehensive error handling.", + "reasoning": "Moderate complexity leveraging existing CLI infrastructure. The codebase shows commander usage patterns and CLI structure. Main complexity is in JSON output formatting, argument validation, and integration with the orchestrator component." + }, + { + "taskId": 8, + "taskTitle": "Develop MCP Tools for AI Agent Integration", + "complexityScore": 6, + "recommendedSubtasks": 5, + "expansionPrompt": "Organize MCP tools into: 1) Tool schema definition and parameter validation, 2) `autopilot_start` and `autopilot_resume` tool implementation, 3) `autopilot_next` and `autopilot_status` tools with context provision, 4) `autopilot_complete_phase` tool with validation integration, and 5) `autopilot_commit` tool with git operations. Ensure parity with CLI functionality and proper error handling.", + "reasoning": "Moderate complexity building on existing MCP infrastructure. The codebase shows extensive MCP tooling patterns. Main work is adapting CLI functionality to MCP interface patterns and ensuring consistent behavior between CLI and MCP interfaces." + }, + { + "taskId": 9, + "taskTitle": "Write AI Agent Integration Documentation and Templates", + "complexityScore": 2, + "recommendedSubtasks": 2, + "expansionPrompt": "Structure documentation into: 1) Comprehensive workflow documentation with step-by-step examples, command usage, and integration patterns, and 2) Template creation for CLAUDE.md integration, example prompts, and troubleshooting guides. Focus on clear examples and practical integration guidance.", + "reasoning": "Low complexity documentation task. Requires understanding of the implemented system but primarily involves writing clear instructions and examples. The existing codebase shows good documentation patterns that can be followed." + }, + { + "taskId": 10, + "taskTitle": "Implement Configuration System and Project Hygiene", + "complexityScore": 5, + "recommendedSubtasks": 4, + "expansionPrompt": "Structure configuration into: 1) Configuration schema definition with comprehensive validation using ajv, 2) Default configuration setup and loading mechanisms, 3) Gitignore management and project directory hygiene rules, and 4) Configuration validation and error reporting. Ensure configurations are validated on startup and provide clear error messages for invalid settings.", + "reasoning": "Moderate complexity involving schema validation, file operations, and configuration management. The package.json shows ajv dependency is available. Configuration systems require careful validation and user-friendly error reporting, but follow established patterns." + } + ] +} diff --git a/.taskmaster/reports/task-complexity-report_tdd-workflow-phase-0.json b/.taskmaster/reports/task-complexity-report_tdd-workflow-phase-0.json new file mode 100644 index 00000000..8e927529 --- /dev/null +++ b/.taskmaster/reports/task-complexity-report_tdd-workflow-phase-0.json @@ -0,0 +1,93 @@ +{ + "meta": { + "generatedAt": "2025-10-07T14:16:40.283Z", + "tasksAnalyzed": 10, + "totalTasks": 10, + "analysisCount": 10, + "thresholdScore": 5, + "projectName": "Taskmaster", + "usedResearch": false + }, + "complexityAnalysis": [ + { + "taskId": 1, + "taskTitle": "Create autopilot command CLI skeleton", + "complexityScore": 4, + "recommendedSubtasks": 3, + "expansionPrompt": "Break down the autopilot command creation into: 1) Create AutopilotCommand class extending Commander.Command with proper argument parsing and options, 2) Implement command structure with help text and validation following existing patterns, 3) Add basic registration method and placeholder action handler", + "reasoning": "Medium complexity due to following established patterns in the codebase. The command-registry.ts and start.command.ts provide clear templates for implementation. Main complexity is argument parsing and option validation." + }, + { + "taskId": 2, + "taskTitle": "Implement preflight detection system", + "complexityScore": 7, + "recommendedSubtasks": 5, + "expansionPrompt": "Create PreflightChecker with these subtasks: 1) Package.json test script detection and validation, 2) Git working tree status checking using system commands, 3) Tool availability validation (git, gh, node/npm), 4) Default branch detection via git commands, 5) Structured result reporting with success/failure indicators and error messages", + "reasoning": "High complexity due to system integration requirements. Needs to interact with multiple external tools (git, npm, gh), parse various file formats, and handle different system configurations. Error handling for missing tools adds complexity." + }, + { + "taskId": 3, + "taskTitle": "Implement task loading and validation", + "complexityScore": 5, + "recommendedSubtasks": 3, + "expansionPrompt": "Implement task loading: 1) Use existing TaskService from @tm/core to load tasks by ID with proper error handling, 2) Validate task structure including subtask existence and dependency validation, 3) Provide user-friendly error messages for missing tasks or need to expand subtasks first", + "reasoning": "Medium-high complexity. While leveraging existing TaskService reduces implementation effort, the validation logic for subtasks and dependencies requires careful handling of edge cases. Task structure validation adds complexity." + }, + { + "taskId": 4, + "taskTitle": "Create execution plan display logic", + "complexityScore": 6, + "recommendedSubtasks": 4, + "expansionPrompt": "Build ExecutionPlanDisplay: 1) Create display formatter using boxen and chalk for consistent CLI styling, 2) Format preflight check results with color-coded status indicators, 3) Display subtask execution order with RED/GREEN/COMMIT phase visualization, 4) Show branch/tag info and finalization steps with duration estimates", + "reasoning": "Moderate-high complexity due to complex formatting requirements and dependency on multiple other components. The display needs to coordinate information from preflight, task validation, and execution planning. CLI styling consistency adds complexity." + }, + { + "taskId": 5, + "taskTitle": "Implement branch and tag planning", + "complexityScore": 3, + "recommendedSubtasks": 2, + "expansionPrompt": "Create BranchPlanner: 1) Implement branch name generation using pattern <tag>/task-<id>-<slug> with kebab-case conversion and special character handling, 2) Add TaskMaster config integration to determine active tag and handle existing branch conflicts", + "reasoning": "Low-medium complexity. String manipulation and naming convention implementation is straightforward. The main complexity is handling edge cases with special characters and existing branch conflicts." + }, + { + "taskId": 6, + "taskTitle": "Create subtask execution order calculation", + "complexityScore": 8, + "recommendedSubtasks": 4, + "expansionPrompt": "Implement dependency resolution: 1) Build dependency graph from subtask data with proper parsing, 2) Implement topological sort algorithm for execution order, 3) Add circular dependency detection with clear error reporting, 4) Create parallel execution grouping for independent subtasks", + "reasoning": "High complexity due to graph algorithms and dependency resolution. Topological sorting, circular dependency detection, and parallel grouping require algorithmic sophistication. Edge cases in dependency chains add significant complexity." + }, + { + "taskId": 7, + "taskTitle": "Implement TDD phase planning for subtasks", + "complexityScore": 6, + "recommendedSubtasks": 4, + "expansionPrompt": "Create TDDPhasePlanner: 1) Implement test file path detection for common project structures (src/, tests/, __tests__), 2) Parse implementation files from subtask details and descriptions, 3) Generate conventional commit messages for RED/GREEN/COMMIT phases, 4) Add implementation complexity estimation based on subtask content", + "reasoning": "Moderate-high complexity due to project structure detection and file path inference. Conventional commit message generation and complexity estimation require understanding of different project layouts and parsing subtask content effectively." + }, + { + "taskId": 8, + "taskTitle": "Add finalization steps planning", + "complexityScore": 4, + "recommendedSubtasks": 3, + "expansionPrompt": "Create FinalizationPlanner: 1) Implement test suite execution planning with coverage threshold detection from package.json, 2) Add git operations planning (branch push, PR creation) using existing git patterns, 3) Create duration estimation algorithm based on subtask count and complexity metrics", + "reasoning": "Medium complexity. Building on existing git utilities and test command detection reduces complexity. Main challenges are coverage threshold parsing and duration estimation algorithms." + }, + { + "taskId": 9, + "taskTitle": "Integrate command with existing CLI infrastructure", + "complexityScore": 3, + "recommendedSubtasks": 2, + "expansionPrompt": "Complete CLI integration: 1) Add AutopilotCommand to command-registry.ts following existing patterns and update command metadata, 2) Test command registration and help system integration with proper cleanup and error handling", + "reasoning": "Low-medium complexity. The command-registry.ts provides a clear pattern to follow. Main work is registration and ensuring proper integration with existing CLI infrastructure. Well-established patterns reduce complexity." + }, + { + "taskId": 10, + "taskTitle": "Add comprehensive error handling and edge cases", + "complexityScore": 7, + "recommendedSubtasks": 5, + "expansionPrompt": "Implement error handling: 1) Add missing task and invalid task structure error handling with helpful messages, 2) Handle git state errors (dirty working tree, missing tools), 3) Add dependency validation errors (circular, invalid references), 4) Implement missing tool detection with installation guidance, 5) Create user-friendly error messages following existing CLI patterns", + "reasoning": "High complexity due to comprehensive error scenarios. Each component (preflight, task loading, dependency resolution) has multiple failure modes that need proper handling. Providing helpful error messages and recovery suggestions adds complexity." + } + ] +} diff --git a/.taskmaster/state.json b/.taskmaster/state.json index d53c4466..e694c7d7 100644 --- a/.taskmaster/state.json +++ b/.taskmaster/state.json @@ -1,6 +1,6 @@ { - "currentTag": "master", - "lastSwitched": "2025-09-12T22:25:27.535Z", + "currentTag": "tdd-phase-1-core-rails", + "lastSwitched": "2025-10-09T12:41:40.367Z", "branchTagMapping": { "v017-adds": "v017-adds", "next": "next" diff --git a/.taskmaster/tasks/tasks.json b/.taskmaster/tasks/tasks.json index 87c0c264..a8e96b66 100644 --- a/.taskmaster/tasks/tasks.json +++ b/.taskmaster/tasks/tasks.json @@ -27,9 +27,7 @@ "title": "Implement Basic Task Operations", "description": "Create core functionality for managing tasks including listing, creating, updating, and deleting tasks.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "priority": "high", "details": "Implement the following task operations:\n- List tasks with filtering options\n- Create new tasks with required fields\n- Update existing task properties\n- Delete tasks\n- Change task status (pending/done/deferred)\n- Handle dependencies between tasks\n- Manage task priorities", "testStrategy": "Test each operation with valid and invalid inputs. Verify that dependencies are properly tracked and that status changes are reflected correctly in the tasks.json file." @@ -39,10 +37,7 @@ "title": "Create Task File Generation System", "description": "Implement the system for generating individual task files from the tasks.json data structure.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "priority": "medium", "details": "Build the task file generation system including:\n- Create task file templates\n- Implement generation of task files from tasks.json\n- Add bi-directional synchronization between task files and tasks.json\n- Implement proper file naming and organization\n- Handle updates to task files reflecting back to tasks.json", "testStrategy": "Generate task files from sample tasks.json data and verify the content matches the expected format. Test synchronization by modifying task files and ensuring changes are reflected in tasks.json.", @@ -60,9 +55,7 @@ "title": "Implement Task File Generation Logic", "description": "Develop the core functionality to generate individual task files from the tasks.json data structure. This includes reading the tasks.json file, iterating through each task, applying the template to each task's data, and writing the resulting content to appropriately named files in the tasks directory. Ensure proper error handling for file operations and data validation.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Successfully reads tasks from tasks.json\n- Correctly applies template to each task's data\n- Generates files with proper naming convention (e.g., task_001.txt)\n- Creates the tasks directory if it doesn't exist\n- Handles errors gracefully (file not found, permission issues, etc.)\n- Validates task data before generation to prevent errors\n- Logs generation process with appropriate verbosity levels" }, { @@ -70,9 +63,7 @@ "title": "Implement File Naming and Organization System", "description": "Create a consistent system for naming and organizing task files. Implement a function that generates standardized filenames based on task IDs (e.g., task_001.txt for task ID 1). Design the directory structure for storing task files according to the PRD specification. Ensure the system handles task ID formatting consistently and prevents filename collisions.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Generates consistent filenames based on task IDs with proper zero-padding\n- Creates and maintains the correct directory structure as specified in the PRD\n- Handles special characters or edge cases in task IDs appropriately\n- Prevents filename collisions between different tasks\n- Provides utility functions for converting between task IDs and filenames\n- Maintains backward compatibility if the naming scheme needs to evolve" }, { @@ -80,10 +71,7 @@ "title": "Implement Task File to JSON Synchronization", "description": "Develop functionality to read modified task files and update the corresponding entries in tasks.json. This includes parsing the task file format, extracting structured data, validating the changes, and updating the tasks.json file accordingly. Ensure the system can handle concurrent modifications and resolve conflicts appropriately.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- Successfully parses task files to extract structured data\n- Validates parsed data against the task model schema\n- Updates tasks.json with changes from task files\n- Handles conflicts when the same task is modified in both places\n- Preserves task relationships and dependencies during synchronization\n- Provides clear error messages for parsing or validation failures\n- Updates the \"updatedAt\" timestamp in tasks.json metadata" }, { @@ -91,11 +79,7 @@ "title": "Implement Change Detection and Update Handling", "description": "Create a system to detect changes in task files and tasks.json, and handle updates bidirectionally. This includes implementing file watching or comparison mechanisms, determining which version is newer, and applying changes in the appropriate direction. Ensure the system handles edge cases like deleted files, new tasks, and conflicting changes.", "status": "done", - "dependencies": [ - 1, - 3, - 4 - ], + "dependencies": [1, 3, 4], "acceptanceCriteria": "- Detects changes in both task files and tasks.json\n- Determines which version is newer based on modification timestamps or content\n- Applies changes in the appropriate direction (file to JSON or JSON to file)\n- Handles edge cases like deleted files, new tasks, and renamed tasks\n- Provides options for manual conflict resolution when necessary\n- Maintains data integrity during the synchronization process\n- Includes a command to force synchronization in either direction\n- Logs all synchronization activities for troubleshooting\n\nEach of these subtasks addresses a specific component of the task file generation system, following a logical progression from template design to bidirectional synchronization. The dependencies ensure that prerequisites are completed before dependent work begins, and the acceptance criteria provide clear guidelines for verifying each subtask's completion.", "details": "\n\n<info added on 2025-05-01T21:59:10.551Z>\n{\n \"id\": 5,\n \"title\": \"Implement Change Detection and Update Handling\",\n \"description\": \"Create a system to detect changes in task files and tasks.json, and handle updates bidirectionally. This includes implementing file watching or comparison mechanisms, determining which version is newer, and applying changes in the appropriate direction. Ensure the system handles edge cases like deleted files, new tasks, and conflicting changes.\",\n \"status\": \"done\",\n \"dependencies\": [\n 1,\n 3,\n 4,\n 2\n ],\n \"acceptanceCriteria\": \"- Detects changes in both task files and tasks.json\\n- Determines which version is newer based on modification timestamps or content\\n- Applies changes in the appropriate direction (file to JSON or JSON to file)\\n- Handles edge cases like deleted files, new tasks, and renamed tasks\\n- Provides options for manual conflict resolution when necessary\\n- Maintains data integrity during the synchronization process\\n- Includes a command to force synchronization in either direction\\n- Logs all synchronization activities for troubleshooting\\n\\nEach of these subtasks addresses a specific component of the task file generation system, following a logical progression from template design to bidirectional synchronization. The dependencies ensure that prerequisites are completed before dependent work begins, and the acceptance criteria provide clear guidelines for verifying each subtask's completion.\",\n \"details\": \"[2025-05-01 21:59:07] Adding another note via MCP test.\"\n}\n</info added on 2025-05-01T21:59:10.551Z>" } @@ -106,9 +90,7 @@ "title": "Integrate Anthropic Claude API", "description": "Set up the integration with Claude API for AI-powered task generation and expansion.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "priority": "high", "details": "Implement Claude API integration including:\n- API authentication using environment variables\n- Create prompt templates for various operations\n- Implement response handling and parsing\n- Add error management with retries and exponential backoff\n- Implement token usage tracking\n- Create configurable model parameters", "testStrategy": "Test API connectivity with sample prompts. Verify authentication works correctly with different API keys. Test error handling by simulating API failures.", @@ -126,9 +108,7 @@ "title": "Develop Prompt Template System", "description": "Create a flexible prompt template system for Claude API interactions. Implement a PromptTemplate class that can handle variable substitution, system and user messages, and proper formatting according to Claude's requirements. Include templates for different operations (task generation, task expansion, etc.) with appropriate instructions and constraints for each use case.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- PromptTemplate class supports variable substitution\n- System and user message separation is properly implemented\n- Templates exist for all required operations (task generation, expansion, etc.)\n- Templates include appropriate constraints and formatting instructions\n- Template system is unit tested with various inputs" }, { @@ -136,9 +116,7 @@ "title": "Implement Response Handling and Parsing", "description": "Create a response handling system that processes Claude API responses. Implement JSON parsing for structured outputs, error detection in responses, and extraction of relevant information. Build utility functions to transform Claude's responses into the application's data structures. Include validation to ensure responses meet expected formats.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Response parsing functions handle both JSON and text formats\n- Error detection identifies malformed or unexpected responses\n- Utility functions transform responses into task data structures\n- Validation ensures responses meet expected schemas\n- Edge cases like empty or partial responses are handled gracefully" }, { @@ -146,10 +124,7 @@ "title": "Build Error Management with Retry Logic", "description": "Implement a robust error handling system for Claude API interactions. Create middleware that catches API errors, network issues, and timeout problems. Implement exponential backoff retry logic that increases wait time between retries. Add configurable retry limits and timeout settings. Include detailed logging for troubleshooting API issues.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- All API errors are caught and handled appropriately\n- Exponential backoff retry logic is implemented\n- Retry limits and timeouts are configurable\n- Detailed error logging provides actionable information\n- System degrades gracefully when API is unavailable\n- Unit tests verify retry behavior with mocked API failures" }, { @@ -157,10 +132,7 @@ "title": "Implement Token Usage Tracking", "description": "Create a token tracking system to monitor Claude API usage. Implement functions to count tokens in prompts and responses. Build a logging system that records token usage per operation. Add reporting capabilities to show token usage trends and costs. Implement configurable limits to prevent unexpected API costs.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- Token counting functions accurately estimate usage\n- Usage logging records tokens per operation type\n- Reporting functions show usage statistics and estimated costs\n- Configurable limits can prevent excessive API usage\n- Warning system alerts when approaching usage thresholds\n- Token tracking data is persisted between application runs" }, { @@ -168,9 +140,7 @@ "title": "Create Model Parameter Configuration System", "description": "Implement a flexible system for configuring Claude model parameters. Create a configuration module that manages model selection, temperature, top_p, max_tokens, and other parameters. Build functions to customize parameters based on operation type. Add validation to ensure parameters are within acceptable ranges. Include preset configurations for different use cases (creative, precise, etc.).", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Configuration module manages all Claude model parameters\n- Parameter customization functions exist for different operations\n- Validation ensures parameters are within acceptable ranges\n- Preset configurations exist for different use cases\n- Parameters can be overridden at runtime when needed\n- Documentation explains parameter effects and recommended values\n- Unit tests verify parameter validation and configuration loading" } ] @@ -180,9 +150,7 @@ "title": "Build PRD Parsing System", "description": "Create the system for parsing Product Requirements Documents into structured task lists.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "priority": "high", "details": "Implement PRD parsing functionality including:\n- PRD file reading from specified path\n- Prompt engineering for effective PRD parsing\n- Convert PRD content to task structure via Claude API\n- Implement intelligent dependency inference\n- Add priority assignment logic\n- Handle large PRDs by chunking if necessary", "testStrategy": "Test with sample PRDs of varying complexity. Verify that generated tasks accurately reflect the requirements in the PRD. Check that dependencies and priorities are logically assigned.", @@ -208,9 +176,7 @@ "title": "Implement PRD to Task Conversion System", "description": "Develop the core functionality that sends PRD content to Claude API and converts the response into the task data structure. This includes sending the engineered prompts with PRD content to Claude, parsing the structured response, and transforming it into valid task objects that conform to the task model. Implement validation to ensure the generated tasks meet all requirements.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Successfully sends PRD content to Claude API with appropriate prompts\n- Parses Claude's response into structured task objects\n- Validates generated tasks against the task model schema\n- Handles API errors and response parsing failures gracefully\n- Generates unique and sequential task IDs" }, { @@ -218,10 +184,7 @@ "title": "Build Intelligent Dependency Inference System", "description": "Create an algorithm that analyzes the generated tasks and infers logical dependencies between them. The system should identify which tasks must be completed before others based on the content and context of each task. Implement both explicit dependency detection (from Claude's output) and implicit dependency inference (based on task relationships and logical ordering).", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- Correctly identifies explicit dependencies mentioned in task descriptions\n- Infers implicit dependencies based on task context and relationships\n- Prevents circular dependencies in the task graph\n- Provides confidence scores for inferred dependencies\n- Allows for manual override/adjustment of detected dependencies" }, { @@ -229,10 +192,7 @@ "title": "Implement Priority Assignment Logic", "description": "Develop a system that assigns appropriate priorities (high, medium, low) to tasks based on their content, dependencies, and position in the PRD. Create algorithms that analyze task descriptions, identify critical path tasks, and consider factors like technical risk and business value. Implement both automated priority assignment and manual override capabilities.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- Assigns priorities based on multiple factors (dependencies, critical path, risk)\n- Identifies foundation/infrastructure tasks as high priority\n- Balances priorities across the project (not everything is high priority)\n- Provides justification for priority assignments\n- Allows for manual adjustment of priorities" }, { @@ -240,10 +200,7 @@ "title": "Implement PRD Chunking for Large Documents", "description": "Create a system that can handle large PRDs by breaking them into manageable chunks for processing. Implement intelligent document segmentation that preserves context across chunks, tracks section relationships, and maintains coherence in the generated tasks. Develop a mechanism to reassemble and deduplicate tasks generated from different chunks into a unified task list.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- Successfully processes PRDs larger than Claude's context window\n- Intelligently splits documents at logical boundaries (sections, chapters)\n- Preserves context when processing individual chunks\n- Reassembles tasks from multiple chunks into a coherent task list\n- Detects and resolves duplicate or overlapping tasks\n- Maintains correct dependency relationships across chunks" } ] @@ -253,9 +210,7 @@ "title": "Implement Task Expansion with Claude", "description": "Create functionality to expand tasks into subtasks using Claude's AI capabilities.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "priority": "medium", "details": "Build task expansion functionality including:\n- Create subtask generation prompts\n- Implement workflow for expanding a task into subtasks\n- Add context-aware expansion capabilities\n- Implement parent-child relationship management\n- Allow specification of number of subtasks to generate\n- Provide mechanism to regenerate unsatisfactory subtasks", "testStrategy": "Test expanding various types of tasks into subtasks. Verify that subtasks are properly linked to parent tasks. Check that context is properly incorporated into generated subtasks.", @@ -281,9 +236,7 @@ "title": "Implement Context-Aware Expansion Capabilities", "description": "Enhance the task expansion functionality to incorporate project context when generating subtasks. Develop a system to gather relevant information from the project, such as related tasks, dependencies, and previously completed work. Implement logic to include this context in the Claude prompts to improve the relevance and quality of generated subtasks.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- System automatically gathers context from related tasks and dependencies\n- Project metadata is incorporated into expansion prompts\n- Implementation details from dependent tasks are included in context\n- Context gathering is configurable (amount and type of context)\n- Generated subtasks show awareness of existing project structure and patterns\n- Context gathering has reasonable performance even with large task collections" }, { @@ -291,9 +244,7 @@ "title": "Build Parent-Child Relationship Management", "description": "Implement the data structure and operations for managing parent-child relationships between tasks and subtasks. Create functions to establish these relationships in the tasks.json file, update the task model to support subtask arrays, and develop utilities to navigate, filter, and display task hierarchies. Ensure all basic task operations (update, delete, etc.) properly handle subtask relationships.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- Task model is updated to include subtasks array\n- Subtasks have proper ID format (parent.sequence)\n- Parent tasks track their subtasks with proper references\n- Task listing command shows hierarchical structure\n- Completing all subtasks automatically updates parent task status\n- Deleting a parent task properly handles orphaned subtasks\n- Task file generation includes subtask information" }, { @@ -301,10 +252,7 @@ "title": "Implement Subtask Regeneration Mechanism", "description": "Create functionality that allows users to regenerate unsatisfactory subtasks. Implement a command that can target specific subtasks for regeneration, preserve satisfactory subtasks, and incorporate feedback to improve the new generation. Design the system to maintain proper parent-child relationships and task IDs during regeneration.", "status": "done", - "dependencies": [ - 1, - 4 - ], + "dependencies": [1, 4], "acceptanceCriteria": "- Command `node scripts/dev.js regenerate --id=<subtask_id>` is implemented\n- Option to regenerate all subtasks for a parent (`--all`)\n- Feedback parameter allows user to guide regeneration (`--feedback=\"...\"`)\n- Original subtask details are preserved in prompt context\n- Regenerated subtasks maintain proper ID sequence\n- Task relationships remain intact after regeneration\n- Command provides clear before/after comparison of subtasks" } ] @@ -314,10 +262,7 @@ "title": "Develop Implementation Drift Handling", "description": "Create system to handle changes in implementation that affect future tasks.", "status": "done", - "dependencies": [ - 3, - 7 - ], + "dependencies": [3, 7], "priority": "medium", "details": "Implement drift handling including:\n- Add capability to update future tasks based on completed work\n- Implement task rewriting based on new context\n- Create dependency chain updates when tasks change\n- Preserve completed work while updating future tasks\n- Add command to analyze and suggest updates to future tasks", "testStrategy": "Simulate implementation changes and test the system's ability to update future tasks appropriately. Verify that completed tasks remain unchanged while pending tasks are updated correctly.", @@ -351,9 +296,7 @@ "title": "Implement Completed Work Preservation", "description": "Develop a mechanism to ensure that updates to future tasks don't affect completed work. This includes creating a versioning system for tasks, tracking task history, and implementing safeguards to prevent modifications to completed tasks. The system should maintain a record of task changes while ensuring that completed work remains stable.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- Implementation of task versioning to track changes\n- Safeguards that prevent modifications to tasks marked as \"done\"\n- System to store and retrieve task history\n- Clear visual indicators in the CLI for tasks that have been modified\n- Ability to view the original version of a modified task\n- Unit tests for completed work preservation" }, { @@ -361,9 +304,7 @@ "title": "Create Update Analysis and Suggestion Command", "description": "Implement a CLI command that analyzes the current state of tasks, identifies potential drift between completed and pending tasks, and suggests updates. This command should provide a comprehensive report of potential inconsistencies and offer recommendations for task updates without automatically applying them. It should include options to apply all suggested changes, select specific changes to apply, or ignore suggestions.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- New CLI command \"analyze-drift\" implemented\n- Comprehensive analysis of potential implementation drift\n- Detailed report of suggested task updates\n- Interactive mode to select which suggestions to apply\n- Batch mode to apply all suggested changes\n- Option to export suggestions to a file for review\n- Documentation of the command usage and options\n- Integration tests that verify the end-to-end workflow" } ] @@ -425,9 +366,7 @@ "title": "Create Research-Backed Subtask Generation", "description": "Enhance subtask generation with research capabilities from Perplexity API.", "status": "done", - "dependencies": [ - 7 - ], + "dependencies": [7], "priority": "low", "details": "Implement research-backed generation including:\n- Create specialized research prompts for different domains\n- Implement context enrichment from research results\n- Add domain-specific knowledge incorporation\n- Create more detailed subtask generation with best practices\n- Include references to relevant libraries and tools", "testStrategy": "Compare subtasks generated with and without research backing. Verify that research-backed subtasks include more specific technical details and best practices.", @@ -461,9 +400,7 @@ "title": "Implement Domain-Specific Knowledge Incorporation", "description": "Develop a system to incorporate domain-specific knowledge into the subtask generation process. This should include identifying key domain concepts, technical requirements, and industry standards from the research results. Create a knowledge base structure that organizes domain information and can be referenced during subtask generation.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- Domain knowledge extraction function that identifies key technical concepts\n- Knowledge base structure for organizing domain-specific information\n- Integration with the subtask generation prompt to incorporate relevant domain knowledge\n- Support for technical terminology and concept explanation in generated subtasks\n- Mechanism to link domain concepts to specific implementation recommendations\n- Tests verifying improved technical accuracy in generated subtasks" }, { @@ -471,10 +408,7 @@ "title": "Enhance Subtask Generation with Technical Details", "description": "Extend the existing subtask generation functionality to incorporate research findings and produce more technically detailed subtasks. This includes modifying the Claude prompt templates to leverage the enriched context, implementing specific sections for technical approach, implementation notes, and potential challenges. Ensure generated subtasks include concrete technical details rather than generic steps.", "status": "done", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "acceptanceCriteria": "- Enhanced prompt templates for Claude that incorporate research-backed context\n- Generated subtasks include specific technical approaches and implementation details\n- Each subtask contains references to relevant tools, libraries, or frameworks\n- Implementation notes section with code patterns or architectural recommendations\n- Potential challenges and mitigation strategies are included where appropriate\n- Comparative tests showing improvement over baseline subtask generation" }, { @@ -482,9 +416,7 @@ "title": "Implement Reference and Resource Inclusion", "description": "Create a system to include references to relevant libraries, tools, documentation, and other resources in generated subtasks. This should extract specific references from research results, validate their relevance, and format them as actionable links or citations within subtasks. Implement a verification step to ensure referenced resources are current and applicable.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- Reference extraction function that identifies tools, libraries, and resources from research\n- Validation mechanism to verify reference relevance and currency\n- Formatting system for including references in subtask descriptions\n- Support for different reference types (GitHub repos, documentation, articles, etc.)\n- Optional version specification for referenced libraries and tools\n- Tests verifying that included references are relevant and accessible" } ] @@ -494,9 +426,7 @@ "title": "Implement Batch Operations", "description": "Add functionality for performing operations on multiple tasks simultaneously.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "priority": "medium", "details": "Create batch operations including:\n- Implement multi-task status updates\n- Add bulk subtask generation\n- Create task filtering and querying capabilities\n- Implement advanced dependency management\n- Add batch prioritization\n- Create commands for operating on filtered task sets", "testStrategy": "Test batch operations with various filters and operations. Verify that operations are applied correctly to all matching tasks. Test with large task sets to ensure performance.", @@ -506,9 +436,7 @@ "title": "Implement Multi-Task Status Update Functionality", "description": "Create a command-line interface command that allows users to update the status of multiple tasks simultaneously. Implement the backend logic to process batch status changes, validate the requested changes, and update the tasks.json file accordingly. The implementation should include options for filtering tasks by various criteria (ID ranges, status, priority, etc.) and applying status changes to the filtered set.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- Command accepts parameters for filtering tasks (e.g., `--status=pending`, `--priority=high`, `--id=1,2,3-5`)\n- Command accepts a parameter for the new status value (e.g., `--new-status=done`)\n- All matching tasks are updated in the tasks.json file\n- Command provides a summary of changes made (e.g., \"Updated 5 tasks from 'pending' to 'done'\")\n- Command handles errors gracefully (e.g., invalid status values, no matching tasks)\n- Changes are persisted correctly to tasks.json" }, { @@ -516,10 +444,7 @@ "title": "Develop Bulk Subtask Generation System", "description": "Create functionality to generate multiple subtasks across several parent tasks at once. This should include a command-line interface that accepts filtering parameters to select parent tasks and either a template for subtasks or an AI-assisted generation option. The system should validate parent tasks, generate appropriate subtasks with proper ID assignments, and update the tasks.json file.", "status": "done", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "acceptanceCriteria": "- Command accepts parameters for filtering parent tasks\n- Command supports template-based subtask generation with variable substitution\n- Command supports AI-assisted subtask generation using Claude API\n- Generated subtasks have proper IDs following the parent.sequence format (e.g., 1.1, 1.2)\n- Subtasks inherit appropriate properties from parent tasks (e.g., dependencies)\n- Generated subtasks are added to the tasks.json file\n- Task files are regenerated to include the new subtasks\n- Command provides a summary of subtasks created" }, { @@ -535,9 +460,7 @@ "title": "Create Advanced Dependency Management System", "description": "Implement batch operations for managing dependencies between tasks. This includes commands for adding, removing, and updating dependencies across multiple tasks simultaneously. The system should validate dependency changes to prevent circular dependencies, update the tasks.json file, and regenerate task files to reflect the changes.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- Command for adding dependencies to multiple tasks at once\n- Command for removing dependencies from multiple tasks\n- Command for replacing dependencies across multiple tasks\n- Validation to prevent circular dependencies\n- Validation to ensure referenced tasks exist\n- Automatic update of affected task files\n- Summary report of dependency changes made\n- Error handling for invalid dependency operations" }, { @@ -545,9 +468,7 @@ "title": "Implement Batch Task Prioritization and Command System", "description": "Create a system for batch prioritization of tasks and a command framework for operating on filtered task sets. This includes commands for changing priorities of multiple tasks at once and a generic command execution system that can apply custom operations to filtered task sets. The implementation should include a plugin architecture that allows for extending the system with new batch operations.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- Command for changing priorities of multiple tasks at once\n- Support for relative priority changes (e.g., increase/decrease priority)\n- Generic command execution framework that works with the filtering system\n- Plugin architecture for registering new batch operations\n- At least three example plugins (e.g., batch tagging, batch assignment, batch export)\n- Command for executing arbitrary operations on filtered task sets\n- Documentation for creating new batch operation plugins\n- Performance testing with large task sets (100+ tasks)" } ] @@ -557,11 +478,7 @@ "title": "Develop Project Initialization System", "description": "Create functionality for initializing new projects with task structure and configuration.", "status": "done", - "dependencies": [ - 1, - 3, - 4 - ], + "dependencies": [1, 3, 4], "priority": "medium", "details": "Implement project initialization including:\n- Create project templating system\n- Implement interactive setup wizard\n- Add environment configuration generation\n- Create initial directory structure\n- Generate example tasks.json\n- Set up default configuration", "testStrategy": "Test project initialization in empty directories. Verify that all required files and directories are created correctly. Test the interactive setup with various inputs.", @@ -571,9 +488,7 @@ "title": "Create Project Template Structure", "description": "Design and implement a flexible project template system that will serve as the foundation for new project initialization. This should include creating a base directory structure, template files (e.g., default tasks.json, .env.example), and a configuration file to define customizable aspects of the template.", "status": "done", - "dependencies": [ - 4 - ], + "dependencies": [4], "acceptanceCriteria": "- A `templates` directory is created with at least one default project template" }, { @@ -581,9 +496,7 @@ "title": "Implement Interactive Setup Wizard", "description": "Develop an interactive command-line wizard using a library like Inquirer.js to guide users through the project initialization process. The wizard should prompt for project name, description, initial task structure, and other configurable options defined in the template configuration.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- Interactive wizard prompts for essential project information" }, { @@ -599,9 +512,7 @@ "title": "Implement Directory Structure Creation", "description": "Develop the logic to create the initial directory structure for new projects based on the selected template and user inputs. This should include creating necessary subdirectories (e.g., tasks/, scripts/, .cursor/rules/) and copying template files to appropriate locations.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Directory structure is created according to the template specification" }, { @@ -627,10 +538,7 @@ "title": "Create Cursor Rules Implementation", "description": "Develop the Cursor AI integration rules and documentation.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "priority": "medium", "details": "Implement Cursor rules including:\n- Create dev_workflow.mdc documentation\n- Implement cursor_rules.mdc\n- Add self_improve.mdc\n- Design rule integration documentation\n- Set up .cursor directory structure\n- Document how Cursor AI should interact with the system", "testStrategy": "Review rules documentation for clarity and completeness. Test with Cursor AI to verify the rules are properly interpreted and followed.", @@ -648,9 +556,7 @@ "title": "Create dev_workflow.mdc Documentation", "description": "Develop the dev_workflow.mdc file that documents the development workflow for Cursor AI. This file should outline how Cursor AI should assist with task discovery, implementation, and verification within the project. Include specific examples of commands and interactions that demonstrate the optimal workflow.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- dev_workflow.mdc file created in .cursor/rules directory\n- Document clearly explains the development workflow with Cursor AI\n- Workflow documentation includes task discovery process\n- Implementation guidance for Cursor AI is detailed\n- Verification procedures are documented\n- Examples of typical interactions are provided" }, { @@ -658,9 +564,7 @@ "title": "Implement cursor_rules.mdc", "description": "Create the cursor_rules.mdc file that defines specific rules and guidelines for how Cursor AI should interact with the codebase. This should include code style preferences, architectural patterns to follow, documentation requirements, and any project-specific conventions that Cursor AI should adhere to when generating or modifying code.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- cursor_rules.mdc file created in .cursor/rules directory\n- Rules document clearly defines code style guidelines\n- Architectural patterns and principles are specified\n- Documentation requirements for generated code are outlined\n- Project-specific naming conventions are documented\n- Rules for handling dependencies and imports are defined\n- Guidelines for test implementation are included" }, { @@ -668,10 +572,7 @@ "title": "Add self_improve.mdc Documentation", "description": "Develop the self_improve.mdc file that instructs Cursor AI on how to continuously improve its assistance capabilities within the project context. This document should outline how Cursor AI should learn from feedback, adapt to project evolution, and enhance its understanding of the codebase over time.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- self_improve.mdc file created in .cursor/rules directory\n- Document outlines feedback incorporation mechanisms\n- Guidelines for adapting to project evolution are included\n- Instructions for enhancing codebase understanding over time\n- Strategies for improving code suggestions based on past interactions\n- Methods for refining prompt responses based on user feedback\n- Approach for maintaining consistency with evolving project patterns" }, { @@ -679,11 +580,7 @@ "title": "Create Cursor AI Integration Documentation", "description": "Develop comprehensive documentation on how Cursor AI integrates with the task management system. This should include detailed instructions on how Cursor AI should interpret tasks.json, individual task files, and how it should assist with implementation. Document the specific commands and workflows that Cursor AI should understand and support.", "status": "done", - "dependencies": [ - 1, - 3, - 4 - ], + "dependencies": [1, 3, 4], "acceptanceCriteria": "- Integration documentation created and stored in an appropriate location\n- Documentation explains how Cursor AI should interpret tasks.json structure\n- Guidelines for Cursor AI to understand task dependencies and priorities\n- Instructions for Cursor AI to assist with task implementation\n- Documentation of specific commands Cursor AI should recognize\n- Examples of effective prompts for working with the task system\n- Troubleshooting section for common Cursor AI integration issues\n- Documentation references all created rule files and explains their purpose" } ] @@ -693,9 +590,7 @@ "title": "Develop Agent Workflow Guidelines", "description": "Create comprehensive guidelines for how AI agents should interact with the task system.", "status": "done", - "dependencies": [ - 13 - ], + "dependencies": [13], "priority": "medium", "details": "Create agent workflow guidelines including:\n- Document task discovery workflow\n- Create task selection guidelines\n- Implement implementation guidance\n- Add verification procedures\n- Define how agents should prioritize work\n- Create guidelines for handling dependencies", "testStrategy": "Review guidelines with actual AI agents to verify they can follow the procedures. Test various scenarios to ensure the guidelines cover all common workflows.", @@ -713,9 +608,7 @@ "title": "Implement Task Selection Algorithm", "description": "Develop an algorithm for AI agents to select the most appropriate task to work on based on priority, dependencies, and current project status. This should include logic for evaluating task urgency, managing blocked tasks, and optimizing workflow efficiency. Implement the algorithm in JavaScript and integrate it with the existing task management system.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- JavaScript module implementing the task selection algorithm" }, { @@ -731,9 +624,7 @@ "title": "Develop Verification Procedure Framework", "description": "Create a flexible framework for defining and executing verification procedures for completed tasks. This should include a DSL (Domain Specific Language) for specifying acceptance criteria, automated test generation where possible, and integration with popular testing frameworks. Implement hooks for both automated and manual verification steps.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- JavaScript module implementing the verification procedure framework" }, { @@ -741,10 +632,7 @@ "title": "Implement Dynamic Task Prioritization System", "description": "Develop a system that dynamically adjusts task priorities based on project progress, dependencies, and external factors. This should include an algorithm for recalculating priorities, a mechanism for propagating priority changes through dependency chains, and an API for external systems to influence priorities. Implement this as a background process that periodically updates the tasks.json file.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- Node.js module implementing the dynamic prioritization system" } ] @@ -754,9 +642,7 @@ "title": "Optimize Agent Integration with Cursor and dev.js Commands", "description": "Document and enhance existing agent interaction patterns through Cursor rules and dev.js commands.", "status": "done", - "dependencies": [ - 14 - ], + "dependencies": [14], "priority": "medium", "details": "Optimize agent integration including:\n- Document and improve existing agent interaction patterns in Cursor rules\n- Enhance integration between Cursor agent capabilities and dev.js commands\n- Improve agent workflow documentation in cursor rules (dev_workflow.mdc, cursor_rules.mdc)\n- Add missing agent-specific features to existing commands\n- Leverage existing infrastructure rather than building a separate system", "testStrategy": "Test the enhanced commands with AI agents to verify they can correctly interpret and use them. Verify that agents can effectively interact with the task system using the documented patterns in Cursor rules.", @@ -790,10 +676,7 @@ "title": "Improve Agent Workflow Documentation in Cursor Rules", "description": "Enhance the agent workflow documentation in dev_workflow.mdc and cursor_rules.mdc to provide clear guidance on how agents should interact with the task system. Include example interactions and best practices for agents.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- Enhanced agent workflow documentation in Cursor rules" }, { @@ -809,10 +692,7 @@ "title": "Create Agent Usage Examples and Patterns", "description": "Develop a set of example interactions and usage patterns that demonstrate how agents should effectively use the task system. Include these examples in the documentation to guide future agent implementations.", "status": "done", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "acceptanceCriteria": "- Comprehensive set of agent usage examples and patterns" } ] @@ -822,9 +702,7 @@ "title": "Create Configuration Management System", "description": "Implement robust configuration handling with environment variables and .env files.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "priority": "high", "details": "Build configuration management including:\n- Environment variable handling\n- .env file support\n- Configuration validation\n- Sensible defaults with overrides\n- Create .env.example template\n- Add configuration documentation\n- Implement secure handling of API keys", "testStrategy": "Test configuration loading from various sources (environment variables, .env files). Verify that validation correctly identifies invalid configurations. Test that defaults are applied when values are missing.", @@ -842,9 +720,7 @@ "title": "Implement .env File Support", "description": "Add support for loading configuration from .env files using dotenv or a similar library. Implement file detection, parsing, and merging with existing environment variables. Handle multiple environments (.env.development, .env.production, etc.) and implement proper error handling for file reading issues.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Integration with dotenv or equivalent library\n- Support for multiple environment-specific .env files (.env.development, .env.production)\n- Proper error handling for missing or malformed .env files\n- Priority order established (process.env overrides .env values)\n- Unit tests verifying .env file loading and overriding behavior" }, { @@ -852,9 +728,7 @@ "title": "Implement Configuration Validation", "description": "Create a validation system for configuration values using a schema validation library like Joi, Zod, or Ajv. Define schemas for all configuration categories (API keys, file paths, feature flags, etc.). Implement validation that runs at startup and provides clear error messages for invalid configurations.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Schema validation implemented for all configuration values\n- Type checking and format validation for different value types\n- Comprehensive error messages that clearly identify validation failures\n- Support for custom validation rules for complex configuration requirements\n- Unit tests covering validation of valid and invalid configurations" }, { @@ -862,10 +736,7 @@ "title": "Create Configuration Defaults and Override System", "description": "Implement a system of sensible defaults for all configuration values with the ability to override them via environment variables or .env files. Create a unified configuration object that combines defaults, .env values, and environment variables with proper precedence. Implement a caching mechanism to avoid repeated environment lookups.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- Default configuration values defined for all settings\n- Clear override precedence (env vars > .env files > defaults)\n- Configuration object accessible throughout the application\n- Caching mechanism to improve performance\n- Unit tests verifying override behavior works correctly" }, { @@ -873,11 +744,7 @@ "title": "Create .env.example Template", "description": "Generate a comprehensive .env.example file that documents all supported environment variables, their purpose, format, and default values. Include comments explaining the purpose of each variable and provide examples. Ensure sensitive values are not included but have clear placeholders.", "status": "done", - "dependencies": [ - 1, - 3, - 4 - ], + "dependencies": [1, 3, 4], "acceptanceCriteria": "- Complete .env.example file with all supported variables\n- Detailed comments explaining each variable's purpose and format\n- Clear placeholders for sensitive values (API_KEY=your-api-key-here)\n- Categorization of variables by function (API, logging, features, etc.)\n- Documentation on how to use the .env.example file" }, { @@ -885,11 +752,7 @@ "title": "Implement Secure API Key Handling", "description": "Create a secure mechanism for handling sensitive configuration values like API keys. Implement masking of sensitive values in logs and error messages. Add validation for API key formats and implement a mechanism to detect and warn about insecure storage of API keys (e.g., committed to git). Add support for key rotation and refresh.", "status": "done", - "dependencies": [ - 1, - 3, - 4 - ], + "dependencies": [1, 3, 4], "acceptanceCriteria": "- Secure storage of API keys and sensitive configuration\n- Masking of sensitive values in logs and error messages\n- Validation of API key formats (length, character set, etc.)\n- Warning system for potentially insecure configuration practices\n- Support for key rotation without application restart\n- Unit tests verifying secure handling of sensitive configuration\n\nThese subtasks provide a comprehensive approach to implementing the configuration management system with a focus on security, validation, and developer experience. The tasks are sequenced to build upon each other logically, starting with basic environment variable support and progressing to more advanced features like secure API key handling." } ] @@ -899,9 +762,7 @@ "title": "Implement Comprehensive Logging System", "description": "Create a flexible logging system with configurable levels and output formats.", "status": "done", - "dependencies": [ - 16 - ], + "dependencies": [16], "priority": "medium", "details": "Implement logging system including:\n- Multiple log levels (debug, info, warn, error)\n- Configurable output destinations\n- Command execution logging\n- API interaction logging\n- Error tracking\n- Performance metrics\n- Log file rotation", "testStrategy": "Test logging at different verbosity levels. Verify that logs contain appropriate information for debugging. Test log file rotation with large volumes of logs.", @@ -919,9 +780,7 @@ "title": "Implement Configurable Output Destinations", "description": "Extend the logging framework to support multiple output destinations simultaneously. Implement adapters for console output, file output, and potentially other destinations (like remote logging services). Create a configuration system that allows specifying which log levels go to which destinations. Ensure thread-safe writing to prevent log corruption.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Abstract destination interface that can be implemented by different output types\n- Console output adapter with color-coding based on log level\n- File output adapter with proper file handling and path configuration\n- Configuration options to route specific log levels to specific destinations\n- Ability to add custom output destinations through the adapter pattern\n- Tests verifying logs are correctly routed to configured destinations" }, { @@ -929,9 +788,7 @@ "title": "Implement Command and API Interaction Logging", "description": "Create specialized logging functionality for command execution and API interactions. For commands, log the command name, arguments, options, and execution status. For API interactions, log request details (URL, method, headers), response status, and timing information. Implement sanitization to prevent logging sensitive data like API keys or passwords.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Command logger that captures command execution details\n- API logger that records request/response details with timing information\n- Data sanitization to mask sensitive information in logs\n- Configuration options to control verbosity of command and API logs\n- Integration with existing command execution flow\n- Tests verifying proper logging of commands and API calls" }, { @@ -939,9 +796,7 @@ "title": "Implement Error Tracking and Performance Metrics", "description": "Enhance the logging system to provide detailed error tracking and performance metrics. For errors, capture stack traces, error codes, and contextual information. For performance metrics, implement timing utilities to measure execution duration of key operations. Create a consistent format for these specialized log types to enable easier analysis.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Error logging with full stack trace capture and error context\n- Performance timer utility for measuring operation duration\n- Standard format for error and performance log entries\n- Ability to track related errors through correlation IDs\n- Configuration options for performance logging thresholds\n- Unit tests for error tracking and performance measurement" }, { @@ -959,15 +814,7 @@ "title": "Create Comprehensive User Documentation", "description": "Develop complete user documentation including README, examples, and troubleshooting guides.", "status": "done", - "dependencies": [ - 1, - 3, - 4, - 7, - 11, - 12, - 16 - ], + "dependencies": [1, 3, 4, 7, 11, 12, 16], "priority": "medium", "details": "Create user documentation including:\n- Detailed README with installation and usage instructions\n- Command reference documentation\n- Configuration guide\n- Example workflows\n- Troubleshooting guides\n- API integration documentation\n- Best practices\n- Advanced usage scenarios", "testStrategy": "Review documentation for clarity and completeness. Have users unfamiliar with the system attempt to follow the documentation and note any confusion or issues.", @@ -977,9 +824,7 @@ "title": "Create Detailed README with Installation and Usage Instructions", "description": "Develop a comprehensive README.md file that serves as the primary documentation entry point. Include project overview, installation steps for different environments, basic usage examples, and links to other documentation sections. Structure the README with clear headings, code blocks for commands, and screenshots where helpful.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- README includes project overview, features list, and system requirements\n- Installation instructions cover all supported platforms with step-by-step commands\n- Basic usage examples demonstrate core functionality with command syntax\n- Configuration section explains environment variables and .env file usage\n- Documentation includes badges for version, license, and build status\n- All sections are properly formatted with Markdown for readability" }, { @@ -987,9 +832,7 @@ "title": "Develop Command Reference Documentation", "description": "Create detailed documentation for all CLI commands, their options, arguments, and examples. Organize commands by functionality category, include syntax diagrams, and provide real-world examples for each command. Document all global options and environment variables that affect command behavior.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- All commands are documented with syntax, options, and arguments\n- Each command includes at least 2 practical usage examples\n- Commands are organized into logical categories (task management, AI integration, etc.)\n- Global options are documented with their effects on command execution\n- Exit codes and error messages are documented for troubleshooting\n- Documentation includes command output examples" }, { @@ -1005,9 +848,7 @@ "title": "Develop Example Workflows and Use Cases", "description": "Create detailed documentation of common workflows and use cases, showing how to use the tool effectively for different scenarios. Include step-by-step guides with command sequences, expected outputs, and explanations. Cover basic to advanced workflows, including PRD parsing, task expansion, and implementation drift handling.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- At least 5 complete workflow examples from initialization to completion\n- Each workflow includes all commands in sequence with expected outputs\n- Screenshots or terminal recordings illustrate the workflows\n- Explanation of decision points and alternatives within workflows\n- Advanced use cases demonstrate integration with development processes\n- Examples show how to handle common edge cases and errors" }, { @@ -1015,10 +856,7 @@ "title": "Create Troubleshooting Guide and FAQ", "description": "Develop a comprehensive troubleshooting guide that addresses common issues, error messages, and their solutions. Include a FAQ section covering common questions about usage, configuration, and best practices. Document known limitations and workarounds for edge cases.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- All error messages are documented with causes and solutions\n- Common issues are organized by category (installation, configuration, execution)\n- FAQ covers at least 15 common questions with detailed answers\n- Troubleshooting decision trees help users diagnose complex issues\n- Known limitations and edge cases are clearly documented\n- Recovery procedures for data corruption or API failures are included" }, { @@ -1036,12 +874,7 @@ "title": "Implement Error Handling and Recovery", "description": "Create robust error handling throughout the system with helpful error messages and recovery options.", "status": "done", - "dependencies": [ - 1, - 3, - 16, - 17 - ], + "dependencies": [1, 3, 16, 17], "priority": "high", "details": "Implement error handling including:\n- Consistent error message format\n- Helpful error messages with recovery suggestions\n- API error handling with retries\n- File system error recovery\n- Data validation errors with specific feedback\n- Command syntax error guidance\n- System state recovery after failures", "testStrategy": "Deliberately trigger various error conditions and verify that the system handles them gracefully. Check that error messages are helpful and provide clear guidance on how to resolve issues.", @@ -1067,9 +900,7 @@ "title": "Develop File System Error Recovery Mechanisms", "description": "Implement error handling and recovery mechanisms for file system operations, focusing on tasks.json and individual task files. This should include handling of file not found errors, permission issues, and data corruption scenarios. Implement automatic backups and recovery procedures to ensure data integrity.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- File system operations are wrapped with comprehensive error handling" }, { @@ -1077,10 +908,7 @@ "title": "Enhance Data Validation with Detailed Error Feedback", "description": "Improve the existing data validation system to provide more specific and actionable error messages. Implement detailed validation checks for all user inputs and task data, with clear error messages that pinpoint the exact issue and how to resolve it. This should cover task creation, updates, and any data imported from external sources.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- Enhanced validation checks are implemented for all task properties and user inputs" }, { @@ -1096,10 +924,7 @@ "title": "Develop System State Recovery After Critical Failures", "description": "Implement a system state recovery mechanism to handle critical failures that could leave the task management system in an inconsistent state. This should include creating periodic snapshots of the system state, implementing a recovery procedure to restore from these snapshots, and providing tools for manual intervention if automatic recovery fails.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "acceptanceCriteria": "- Periodic snapshots of the tasks.json and related state are automatically created" } ] @@ -1109,9 +934,7 @@ "title": "Create Token Usage Tracking and Cost Management", "description": "Implement system for tracking API token usage and managing costs.", "status": "done", - "dependencies": [ - 17 - ], + "dependencies": [17], "priority": "medium", "details": "Implement token tracking including:\n- Track token usage for all API calls\n- Implement configurable usage limits\n- Add reporting on token consumption\n- Create cost estimation features\n- Implement caching to reduce API calls\n- Add token optimization for prompts\n- Create usage alerts when approaching limits", "testStrategy": "Track token usage across various operations and verify accuracy. Test that limits properly prevent excessive usage. Verify that caching reduces token consumption for repeated operations.", @@ -1137,9 +960,7 @@ "title": "Implement Token Usage Reporting and Cost Estimation", "description": "Develop a reporting module that generates detailed token usage reports. Include breakdowns by API, user, and time period. Implement cost estimation features by integrating current pricing information for each API. Create both command-line and programmatic interfaces for generating reports and estimates.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- CLI command for generating usage reports with various filters" }, { @@ -1155,9 +976,7 @@ "title": "Develop Token Usage Alert System", "description": "Create an alert system that monitors token usage in real-time and sends notifications when usage approaches or exceeds defined thresholds. Implement multiple notification channels (e.g., email, Slack, system logs) and allow for customizable alert rules. Integrate this system with the existing logging and reporting modules.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- Real-time monitoring of token usage against configured limits" } ] @@ -1167,11 +986,7 @@ "title": "Refactor dev.js into Modular Components", "description": "Restructure the monolithic dev.js file into separate modular components to improve code maintainability, readability, and testability while preserving all existing functionality.", "status": "done", - "dependencies": [ - 3, - 16, - 17 - ], + "dependencies": [3, 16, 17], "priority": "high", "details": "This task involves breaking down the current dev.js file into logical modules with clear responsibilities:\n\n1. Create the following module files:\n - commands.js: Handle all CLI command definitions and execution logic\n - ai-services.js: Encapsulate all AI service interactions (OpenAI, etc.)\n - task-manager.js: Manage task operations (create, read, update, delete)\n - ui.js: Handle all console output formatting, colors, and user interaction\n - utils.js: Contain helper functions, utilities, and shared code\n\n2. Refactor dev.js to serve as the entry point that:\n - Imports and initializes all modules\n - Handles command-line argument parsing\n - Sets up the execution environment\n - Orchestrates the flow between modules\n\n3. Ensure proper dependency injection between modules to avoid circular dependencies\n\n4. Maintain consistent error handling across modules\n\n5. Update import/export statements throughout the codebase\n\n6. Document each module with clear JSDoc comments explaining purpose and usage\n\n7. Ensure configuration and logging systems are properly integrated into each module\n\nThe refactoring should not change any existing functionality - this is purely a code organization task.", "testStrategy": "Testing should verify that functionality remains identical after refactoring:\n\n1. Automated Testing:\n - Create unit tests for each new module to verify individual functionality\n - Implement integration tests that verify modules work together correctly\n - Test each command to ensure it works exactly as before\n\n2. Manual Testing:\n - Execute all existing CLI commands and verify outputs match pre-refactoring behavior\n - Test edge cases like error handling and invalid inputs\n - Verify that configuration options still work as expected\n\n3. Code Quality Verification:\n - Run linting tools to ensure code quality standards are maintained\n - Check for any circular dependencies between modules\n - Verify that each module has a single, clear responsibility\n\n4. Performance Testing:\n - Compare execution time before and after refactoring to ensure no performance regression\n\n5. Documentation Check:\n - Verify that each module has proper documentation\n - Ensure README is updated if necessary to reflect architectural changes", @@ -1189,9 +1004,7 @@ "title": "Create Core Module Structure and Entry Point Refactoring", "description": "Create the skeleton structure for all module files (commands.js, ai-services.js, task-manager.js, ui.js, utils.js) with proper export statements. Refactor dev.js to serve as the entry point that imports and orchestrates these modules. Implement the basic initialization flow and command-line argument parsing in the new structure.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- All module files created with appropriate JSDoc headers explaining purpose" }, { @@ -1207,9 +1020,7 @@ "title": "Implement Error Handling and Complete Module Migration", "description": "Establish a consistent error handling pattern across all modules. Complete the migration of remaining functionality from dev.js to the appropriate modules. Ensure all edge cases, error scenarios, and helper functions are properly moved and integrated. Update all import/export statements throughout the codebase to reference the new module structure.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "acceptanceCriteria": "- Consistent error handling pattern implemented across all modules" }, { @@ -1217,9 +1028,7 @@ "title": "Test, Document, and Finalize Modular Structure", "description": "Perform comprehensive testing of the refactored codebase to ensure all functionality works as expected. Add detailed JSDoc comments to all modules, functions, and significant code blocks. Create or update developer documentation explaining the new modular structure, module responsibilities, and how they interact. Perform a final code review to ensure code quality, consistency, and adherence to best practices.", "status": "done", - "dependencies": [ - "21.4" - ], + "dependencies": ["21.4"], "acceptanceCriteria": "- All existing functionality works exactly as before" } ] @@ -1229,9 +1038,7 @@ "title": "Create Comprehensive Test Suite for Task Master CLI", "description": "Develop a complete testing infrastructure for the Task Master CLI that includes unit, integration, and end-to-end tests to verify all core functionality and error handling.", "status": "done", - "dependencies": [ - 21 - ], + "dependencies": [21], "priority": "high", "details": "Implement a comprehensive test suite using Jest as the testing framework. The test suite should be organized into three main categories:\n\n1. Unit Tests:\n - Create tests for all utility functions and core logic components\n - Test task creation, parsing, and manipulation functions\n - Test data storage and retrieval functions\n - Test formatting and display functions\n\n2. Integration Tests:\n - Test all CLI commands (create, expand, update, list, etc.)\n - Verify command options and parameters work correctly\n - Test interactions between different components\n - Test configuration loading and application settings\n\n3. End-to-End Tests:\n - Test complete workflows (e.g., creating a task, expanding it, updating status)\n - Test error scenarios and recovery\n - Test edge cases like handling large numbers of tasks\n\nImplement proper mocking for:\n- Claude API interactions (using Jest mock functions)\n- File system operations (using mock-fs or similar)\n- User input/output (using mock stdin/stdout)\n\nEnsure tests cover both successful operations and error handling paths. Set up continuous integration to run tests automatically. Create fixtures for common test data and scenarios. Include test coverage reporting to identify untested code paths.", "testStrategy": "Verification will involve:\n\n1. Code Review:\n - Verify test organization follows the unit/integration/end-to-end structure\n - Check that all major functions have corresponding tests\n - Verify mocks are properly implemented for external dependencies\n\n2. Test Coverage Analysis:\n - Run test coverage tools to ensure at least 80% code coverage\n - Verify critical paths have 100% coverage\n - Identify any untested code paths\n\n3. Test Quality Verification:\n - Manually review test cases to ensure they test meaningful behavior\n - Verify both positive and negative test cases exist\n - Check that tests are deterministic and don't have false positives/negatives\n\n4. CI Integration:\n - Verify tests run successfully in the CI environment\n - Ensure tests run in a reasonable amount of time\n - Check that test failures provide clear, actionable information\n\nThe task will be considered complete when all tests pass consistently, coverage meets targets, and the test suite can detect intentionally introduced bugs.", @@ -1249,9 +1056,7 @@ "title": "Implement Unit Tests for Core Components", "description": "Create a comprehensive set of unit tests for all utility functions, core logic components, and individual modules of the Task Master CLI. This includes tests for task creation, parsing, manipulation, data storage, retrieval, and formatting functions. Ensure all edge cases and error scenarios are covered.", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Unit tests are implemented for all utility functions in the project" }, { @@ -1259,9 +1064,7 @@ "title": "Develop Integration and End-to-End Tests", "description": "Create integration tests that verify the correct interaction between different components of the CLI, including command execution, option parsing, and data flow. Implement end-to-end tests that simulate complete user workflows, such as creating a task, expanding it, and updating its status. Include tests for error scenarios, recovery processes, and handling large numbers of tasks.", "status": "deferred", - "dependencies": [ - 1 - ], + "dependencies": [1], "acceptanceCriteria": "- Integration tests cover all CLI commands (create, expand, update, list, etc.)" } ] @@ -1271,9 +1074,7 @@ "title": "Complete MCP Server Implementation for Task Master using FastMCP", "description": "Finalize the MCP server functionality for Task Master by leveraging FastMCP's capabilities, transitioning from CLI-based execution to direct function imports, and optimizing performance, authentication, and context management. Ensure the server integrates seamlessly with Cursor via `mcp.json` and supports proper tool registration, efficient context handling, and transport type handling (focusing on stdio). Additionally, ensure the server can be instantiated properly when installed via `npx` or `npm i -g`. Evaluate and address gaps in the current implementation, including function imports, context management, caching, tool registration, and adherence to FastMCP best practices.", "status": "done", - "dependencies": [ - 22 - ], + "dependencies": [22], "priority": "medium", "details": "This task involves completing the Model Context Protocol (MCP) server implementation for Task Master using FastMCP. Key updates include:\n\n1. Transition from CLI-based execution (currently using `child_process.spawnSync`) to direct Task Master function imports for improved performance and reliability.\n2. Implement caching mechanisms for frequently accessed contexts to enhance performance, leveraging FastMCP's efficient transport mechanisms (e.g., stdio).\n3. Refactor context management to align with best practices for handling large context windows, metadata, and tagging.\n4. Refactor tool registration in `tools/index.js` to include clear descriptions and parameter definitions, leveraging FastMCP's decorator-based patterns for better integration.\n5. Enhance transport type handling to ensure proper stdio communication and compatibility with FastMCP.\n6. Ensure the MCP server can be instantiated and run correctly when installed globally via `npx` or `npm i -g`.\n7. Integrate the ModelContextProtocol SDK directly to streamline resource and tool registration, ensuring compatibility with FastMCP's transport mechanisms.\n8. Identify and address missing components or functionalities to meet FastMCP best practices, such as robust error handling, monitoring endpoints, and concurrency support.\n9. Update documentation to include examples of using the MCP server with FastMCP, detailed setup instructions, and client integration guides.\n10. Organize direct function implementations in a modular structure within the mcp-server/src/core/direct-functions/ directory for improved maintainability and organization.\n11. Follow consistent naming conventions: file names use kebab-case (like-this.js), direct functions use camelCase with Direct suffix (functionNameDirect), tool registration functions use camelCase with Tool suffix (registerToolNameTool), and MCP tool names exposed to clients use snake_case (tool_name).\n\nThe implementation must ensure compatibility with existing MCP clients and follow RESTful API design principles, while supporting concurrent requests and maintaining robust error handling.", "testStrategy": "Testing for the MCP server implementation will follow a comprehensive approach based on our established testing guidelines:\n\n## Test Organization\n\n1. **Unit Tests** (`tests/unit/mcp-server/`):\n - Test individual MCP server components in isolation\n - Mock all external dependencies including FastMCP SDK\n - Test each tool implementation separately\n - Test each direct function implementation in the direct-functions directory\n - Verify direct function imports work correctly\n - Test context management and caching mechanisms\n - Example files: `context-manager.test.js`, `tool-registration.test.js`, `direct-functions/list-tasks.test.js`\n\n2. **Integration Tests** (`tests/integration/mcp-server/`):\n - Test interactions between MCP server components\n - Verify proper tool registration with FastMCP\n - Test context flow between components\n - Validate error handling across module boundaries\n - Test the integration between direct functions and their corresponding MCP tools\n - Example files: `server-tool-integration.test.js`, `context-flow.test.js`\n\n3. **End-to-End Tests** (`tests/e2e/mcp-server/`):\n - Test complete MCP server workflows\n - Verify server instantiation via different methods (direct, npx, global install)\n - Test actual stdio communication with mock clients\n - Example files: `server-startup.e2e.test.js`, `client-communication.e2e.test.js`\n\n4. **Test Fixtures** (`tests/fixtures/mcp-server/`):\n - Sample context data\n - Mock tool definitions\n - Sample MCP requests and responses\n\n## Testing Approach\n\n### Module Mocking Strategy\n```javascript\n// Mock the FastMCP SDK\njest.mock('@model-context-protocol/sdk', () => ({\n MCPServer: jest.fn().mockImplementation(() => ({\n registerTool: jest.fn(),\n registerResource: jest.fn(),\n start: jest.fn().mockResolvedValue(undefined),\n stop: jest.fn().mockResolvedValue(undefined)\n })),\n MCPError: jest.fn().mockImplementation(function(message, code) {\n this.message = message;\n this.code = code;\n })\n}));\n\n// Import modules after mocks\nimport { MCPServer, MCPError } from '@model-context-protocol/sdk';\nimport { initMCPServer } from '../../scripts/mcp-server.js';\n```\n\n### Direct Function Testing\n- Test each direct function in isolation\n- Verify proper error handling and return formats\n- Test with various input parameters and edge cases\n- Verify integration with the task-master-core.js export hub\n\n### Context Management Testing\n- Test context creation, retrieval, and manipulation\n- Verify caching mechanisms work correctly\n- Test context windowing and metadata handling\n- Validate context persistence across server restarts\n\n### Direct Function Import Testing\n- Verify Task Master functions are imported correctly\n- Test performance improvements compared to CLI execution\n- Validate error handling with direct imports\n\n### Tool Registration Testing\n- Verify tools are registered with proper descriptions and parameters\n- Test decorator-based registration patterns\n- Validate tool execution with different input types\n\n### Error Handling Testing\n- Test all error paths with appropriate MCPError types\n- Verify error propagation to clients\n- Test recovery from various error conditions\n\n### Performance Testing\n- Benchmark response times with and without caching\n- Test memory usage under load\n- Verify concurrent request handling\n\n## Test Quality Guidelines\n\n- Follow TDD approach when possible\n- Maintain test independence and isolation\n- Use descriptive test names explaining expected behavior\n- Aim for 80%+ code coverage, with critical paths at 100%\n- Follow the mock-first-then-import pattern for all Jest mocks\n- Avoid testing implementation details that might change\n- Ensure tests don't depend on execution order\n\n## Specific Test Cases\n\n1. **Server Initialization**\n - Test server creation with various configuration options\n - Verify proper tool and resource registration\n - Test server startup and shutdown procedures\n\n2. **Context Operations**\n - Test context creation, retrieval, update, and deletion\n - Verify context windowing and truncation\n - Test context metadata and tagging\n\n3. **Tool Execution**\n - Test each tool with various input parameters\n - Verify proper error handling for invalid inputs\n - Test tool execution performance\n\n4. **MCP.json Integration**\n - Test creation and updating of .cursor/mcp.json\n - Verify proper server registration in mcp.json\n - Test handling of existing mcp.json files\n\n5. **Transport Handling**\n - Test stdio communication\n - Verify proper message formatting\n - Test error handling in transport layer\n\n6. **Direct Function Structure**\n - Test the modular organization of direct functions\n - Verify proper import/export through task-master-core.js\n - Test utility functions in the utils directory\n\nAll tests will be automated and integrated into the CI/CD pipeline to ensure consistent quality.", @@ -1291,9 +1092,7 @@ "id": 2, "title": "Implement Context Management System", "description": "Develop a robust context management system that can efficiently store, retrieve, and manipulate context data according to the MCP specification.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implementation steps:\n1. Design and implement data structures for context storage\n2. Create methods for context creation, retrieval, updating, and deletion\n3. Implement context windowing and truncation algorithms for handling size limits\n4. Add support for context metadata and tagging\n5. Create utilities for context serialization and deserialization\n6. Implement efficient indexing for quick context lookups\n7. Add support for context versioning and history\n8. Develop mechanisms for context persistence (in-memory, disk-based, or database)\n\nTesting approach:\n- Unit tests for all context operations (CRUD)\n- Performance tests for context retrieval with various sizes\n- Test context windowing and truncation with edge cases\n- Verify metadata handling and tagging functionality\n- Test persistence mechanisms with simulated failures", "status": "done", "parentTaskId": 23 @@ -1302,9 +1101,7 @@ "id": 3, "title": "Implement MCP Endpoints and API Handlers", "description": "Develop the complete API handlers for all required MCP endpoints, ensuring they follow the protocol specification and integrate with the context management system.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implementation steps:\n1. Implement the `/context` endpoint for:\n - GET: retrieving existing context\n - POST: creating new context\n - PUT: updating existing context\n - DELETE: removing context\n2. Implement the `/models` endpoint to list available models\n3. Develop the `/execute` endpoint for performing operations with context\n4. Create request validators for each endpoint\n5. Implement response formatters according to MCP specifications\n6. Add detailed error handling for each endpoint\n7. Set up proper HTTP status codes for different scenarios\n8. Implement pagination for endpoints that return lists\n\nTesting approach:\n- Unit tests for each endpoint handler\n- Integration tests with mock context data\n- Test various request formats and edge cases\n- Verify response formats match MCP specifications\n- Test error handling with invalid inputs\n- Benchmark endpoint performance", "status": "done", "parentTaskId": 23 @@ -1313,10 +1110,7 @@ "id": 6, "title": "Refactor MCP Server to Leverage ModelContextProtocol SDK", "description": "Integrate the ModelContextProtocol SDK directly into the MCP server implementation to streamline tool registration and resource handling.", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "Implementation steps:\n1. Replace manual tool registration with ModelContextProtocol SDK methods.\n2. Use SDK utilities to simplify resource and template management.\n3. Ensure compatibility with FastMCP's transport mechanisms.\n4. Update server initialization to include SDK-based configurations.\n\nTesting approach:\n- Verify SDK integration with all MCP endpoints.\n- Test resource and template registration using SDK methods.\n- Validate compatibility with existing MCP clients.\n- Benchmark performance improvements from SDK integration.\n\n<info added on 2025-03-31T18:49:14.439Z>\nThe subtask is being cancelled because FastMCP already serves as a higher-level abstraction over the Model Context Protocol SDK. Direct integration with the MCP SDK would be redundant and potentially counterproductive since:\n\n1. FastMCP already encapsulates the necessary SDK functionality for tool registration and resource handling\n2. The existing FastMCP abstractions provide a more streamlined developer experience\n3. Adding another layer of SDK integration would increase complexity without clear benefits\n4. The transport mechanisms in FastMCP are already optimized for the current architecture\n\nInstead, we should focus on extending and enhancing the existing FastMCP abstractions where needed, rather than attempting to bypass them with direct SDK integration.\n</info added on 2025-03-31T18:49:14.439Z>", "status": "done", "parentTaskId": 23 @@ -1325,9 +1119,7 @@ "id": 8, "title": "Implement Direct Function Imports and Replace CLI-based Execution", "description": "Refactor the MCP server implementation to use direct Task Master function imports instead of the current CLI-based execution using child_process.spawnSync. This will improve performance, reliability, and enable better error handling.", - "dependencies": [ - "23.13" - ], + "dependencies": ["23.13"], "details": "\n\n<info added on 2025-03-30T00:14:10.040Z>\n```\n# Refactoring Strategy for Direct Function Imports\n\n## Core Approach\n1. Create a clear separation between data retrieval/processing and presentation logic\n2. Modify function signatures to accept `outputFormat` parameter ('cli'|'json', default: 'cli')\n3. Implement early returns for JSON format to bypass CLI-specific code\n\n## Implementation Details for `listTasks`\n```javascript\nfunction listTasks(tasksPath, statusFilter, withSubtasks = false, outputFormat = 'cli') {\n try {\n // Existing data retrieval logic\n const filteredTasks = /* ... */;\n \n // Early return for JSON format\n if (outputFormat === 'json') return filteredTasks;\n \n // Existing CLI output logic\n } catch (error) {\n if (outputFormat === 'json') {\n throw {\n code: 'TASK_LIST_ERROR',\n message: error.message,\n details: error.stack\n };\n } else {\n console.error(error);\n process.exit(1);\n }\n }\n}\n```\n\n## Testing Strategy\n- Create integration tests in `tests/integration/mcp-server/`\n- Use FastMCP InMemoryTransport for direct client-server testing\n- Test both JSON and CLI output formats\n- Verify structure consistency with schema validation\n\n## Additional Considerations\n- Update JSDoc comments to document new parameters and return types\n- Ensure backward compatibility with default CLI behavior\n- Add JSON schema validation for consistent output structure\n- Apply similar pattern to other core functions (expandTask, updateTaskById, etc.)\n\n## Error Handling Improvements\n- Standardize error format for JSON returns:\n```javascript\n{\n code: 'ERROR_CODE',\n message: 'Human-readable message',\n details: {}, // Additional context when available\n stack: process.env.NODE_ENV === 'development' ? error.stack : undefined\n}\n```\n- Enrich JSON errors with error codes and debug info\n- Ensure validation failures return proper objects in JSON mode\n```\n</info added on 2025-03-30T00:14:10.040Z>", "status": "done", "parentTaskId": 23 @@ -1336,9 +1128,7 @@ "id": 9, "title": "Implement Context Management and Caching Mechanisms", "description": "Enhance the MCP server with proper context management and caching to improve performance and user experience, especially for frequently accessed data and contexts.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "1. Implement a context manager class that leverages FastMCP's Context object\n2. Add caching for frequently accessed task data with configurable TTL settings\n3. Implement context tagging for better organization of context data\n4. Add methods to efficiently handle large context windows\n5. Create helper functions for storing and retrieving context data\n6. Implement cache invalidation strategies for task updates\n7. Add cache statistics for monitoring performance\n8. Create unit tests for context management and caching functionality", "status": "done", "parentTaskId": 23 @@ -1347,10 +1137,7 @@ "id": 10, "title": "Enhance Tool Registration and Resource Management", "description": "Refactor tool registration to follow FastMCP best practices, using decorators and improving the overall structure. Implement proper resource management for task templates and other shared resources.", - "dependencies": [ - 1, - "23.8" - ], + "dependencies": [1, "23.8"], "details": "1. Update registerTaskMasterTools function to use FastMCP's decorator pattern\n2. Implement @mcp.tool() decorators for all existing tools\n3. Add proper type annotations and documentation for all tools\n4. Create resource handlers for task templates using @mcp.resource()\n5. Implement resource templates for common task patterns\n6. Update the server initialization to properly register all tools and resources\n7. Add validation for tool inputs using FastMCP's built-in validation\n8. Create comprehensive tests for tool registration and resource access\n\n<info added on 2025-03-31T18:35:21.513Z>\nHere is additional information to enhance the subtask regarding resources and resource templates in FastMCP:\n\nResources in FastMCP are used to expose static or dynamic data to LLM clients. For the Task Master MCP server, we should implement resources to provide:\n\n1. Task templates: Predefined task structures that can be used as starting points\n2. Workflow definitions: Reusable workflow patterns for common task sequences\n3. User preferences: Stored user settings for task management\n4. Project metadata: Information about active projects and their attributes\n\nResource implementation should follow this structure:\n\n```python\n@mcp.resource(\"tasks://templates/{template_id}\")\ndef get_task_template(template_id: str) -> dict:\n # Fetch and return the specified task template\n ...\n\n@mcp.resource(\"workflows://definitions/{workflow_id}\")\ndef get_workflow_definition(workflow_id: str) -> dict:\n # Fetch and return the specified workflow definition\n ...\n\n@mcp.resource(\"users://{user_id}/preferences\")\ndef get_user_preferences(user_id: str) -> dict:\n # Fetch and return user preferences\n ...\n\n@mcp.resource(\"projects://metadata\")\ndef get_project_metadata() -> List[dict]:\n # Fetch and return metadata for all active projects\n ...\n```\n\nResource templates in FastMCP allow for dynamic generation of resources based on patterns. For Task Master, we can implement:\n\n1. Dynamic task creation templates\n2. Customizable workflow templates\n3. User-specific resource views\n\nExample implementation:\n\n```python\n@mcp.resource(\"tasks://create/{task_type}\")\ndef get_task_creation_template(task_type: str) -> dict:\n # Generate and return a task creation template based on task_type\n ...\n\n@mcp.resource(\"workflows://custom/{user_id}/{workflow_name}\")\ndef get_custom_workflow_template(user_id: str, workflow_name: str) -> dict:\n # Generate and return a custom workflow template for the user\n ...\n\n@mcp.resource(\"users://{user_id}/dashboard\")\ndef get_user_dashboard(user_id: str) -> dict:\n # Generate and return a personalized dashboard view for the user\n ...\n```\n\nBest practices for integrating resources with Task Master functionality:\n\n1. Use resources to provide context and data for tools\n2. Implement caching for frequently accessed resources\n3. Ensure proper error handling and not-found cases for all resources\n4. Use resource templates to generate dynamic, personalized views of data\n5. Implement access control to ensure users only access authorized resources\n\nBy properly implementing these resources and resource templates, we can provide rich, contextual data to LLM clients, enhancing the Task Master's capabilities and user experience.\n</info added on 2025-03-31T18:35:21.513Z>", "status": "done", "parentTaskId": 23 @@ -1361,10 +1148,7 @@ "description": "Implement robust error handling using FastMCP's MCPError, including custom error types for different categories and standardized error responses.", "details": "1. Create custom error types extending MCPError for different categories (validation, auth, etc.)\\n2. Implement standardized error responses following MCP protocol\\n3. Add error handling middleware for all MCP endpoints\\n4. Ensure proper error propagation from tools to client\\n5. Add debug mode with detailed error information\\n6. Document error types and handling patterns", "status": "done", - "dependencies": [ - "23.1", - "23.3" - ], + "dependencies": ["23.1", "23.3"], "parentTaskId": 23 }, { @@ -1373,10 +1157,7 @@ "description": "Implement a comprehensive logging system for the MCP server with different log levels, structured logging format, and request/response tracking.", "details": "1. Design structured log format for consistent parsing\\n2. Implement different log levels (debug, info, warn, error)\\n3. Add request/response logging middleware\\n4. Implement correlation IDs for request tracking\\n5. Add performance metrics logging\\n6. Configure log output destinations (console, file)\\n7. Document logging patterns and usage", "status": "done", - "dependencies": [ - "23.1", - "23.3" - ], + "dependencies": ["23.1", "23.3"], "parentTaskId": 23 }, { @@ -1385,10 +1166,7 @@ "description": "Implement a comprehensive testing framework for the MCP server, including unit tests, integration tests, and end-to-end tests.", "details": "1. Set up Jest testing framework with proper configuration\\n2. Create MCPTestClient for testing FastMCP server interaction\\n3. Implement unit tests for individual tool functions\\n4. Create integration tests for end-to-end request/response cycles\\n5. Set up test fixtures and mock data\\n6. Implement test coverage reporting\\n7. Document testing guidelines and examples", "status": "done", - "dependencies": [ - "23.1", - "23.3" - ], + "dependencies": ["23.1", "23.3"], "parentTaskId": 23 }, { @@ -1397,10 +1175,7 @@ "description": "Implement functionality to create or update .cursor/mcp.json during project initialization, handling cases where: 1) If there's no mcp.json, create it with the appropriate configuration; 2) If there is an mcp.json, intelligently append to it without syntax errors like trailing commas", "details": "1. Create functionality to detect if .cursor/mcp.json exists in the project\\n2. Implement logic to create a new mcp.json file with proper structure if it doesn't exist\\n3. Add functionality to read and parse existing mcp.json if it exists\\n4. Create method to add a new taskmaster-ai server entry to the mcpServers object\\n5. Implement intelligent JSON merging that avoids trailing commas and syntax errors\\n6. Ensure proper formatting and indentation in the generated/updated JSON\\n7. Add validation to verify the updated configuration is valid JSON\\n8. Include this functionality in the init workflow\\n9. Add error handling for file system operations and JSON parsing\\n10. Document the mcp.json structure and integration process", "status": "done", - "dependencies": [ - "23.1", - "23.3" - ], + "dependencies": ["23.1", "23.3"], "parentTaskId": 23 }, { @@ -1409,11 +1184,7 @@ "description": "Add Server-Sent Events (SSE) capabilities to the MCP server to enable real-time updates and streaming of task execution progress, logs, and status changes to clients", "details": "1. Research and implement SSE protocol for the MCP server\\n2. Create dedicated SSE endpoints for event streaming\\n3. Implement event emitter pattern for internal event management\\n4. Add support for different event types (task status, logs, errors)\\n5. Implement client connection management with proper keep-alive handling\\n6. Add filtering capabilities to allow subscribing to specific event types\\n7. Create in-memory event buffer for clients reconnecting\\n8. Document SSE endpoint usage and client implementation examples\\n9. Add robust error handling for dropped connections\\n10. Implement rate limiting and backpressure mechanisms\\n11. Add authentication for SSE connections", "status": "done", - "dependencies": [ - "23.1", - "23.3", - "23.11" - ], + "dependencies": ["23.1", "23.3", "23.11"], "parentTaskId": 23 }, { @@ -1566,9 +1337,7 @@ "description": "Move existing direct function implementations from task-master-core.js to individual files in the new directory structure.", "details": "1. Identify all existing direct functions in task-master-core.js\n2. Create individual files for each function in mcp-server/src/core/direct-functions/\n3. Move the implementation to the new files, ensuring consistent error handling\n4. Update imports/exports in task-master-core.js\n5. Create unit tests for each individual function file\n6. Update documentation to reflect the new structure\n7. Ensure all MCP tools reference the functions through task-master-core.js\n8. Verify backward compatibility with existing code", "status": "done", - "dependencies": [ - "23.31" - ], + "dependencies": ["23.31"], "parentTaskId": 23 }, { @@ -1631,9 +1400,7 @@ "description": "Create MCP tool implementation for the add-dependency command", "details": "", "status": "done", - "dependencies": [ - "23.31" - ], + "dependencies": ["23.31"], "parentTaskId": 23 }, { @@ -1642,9 +1409,7 @@ "description": "Create MCP tool implementation for the remove-dependency command", "details": "", "status": "done", - "dependencies": [ - "23.31" - ], + "dependencies": ["23.31"], "parentTaskId": 23 }, { @@ -1653,11 +1418,7 @@ "description": "Create MCP tool implementation for the validate-dependencies command", "details": "", "status": "done", - "dependencies": [ - "23.31", - "23.39", - "23.40" - ], + "dependencies": ["23.31", "23.39", "23.40"], "parentTaskId": 23 }, { @@ -1666,10 +1427,7 @@ "description": "Create MCP tool implementation for the fix-dependencies command", "details": "", "status": "done", - "dependencies": [ - "23.31", - "23.41" - ], + "dependencies": ["23.31", "23.41"], "parentTaskId": 23 }, { @@ -1678,9 +1436,7 @@ "description": "Create MCP tool implementation for the complexity-report command", "details": "", "status": "done", - "dependencies": [ - "23.31" - ], + "dependencies": ["23.31"], "parentTaskId": 23 }, { @@ -1717,9 +1473,7 @@ "title": "Implement AI-Powered Test Generation Command", "description": "Create a new 'generate-test' command in Task Master that leverages AI to automatically produce Jest test files for tasks based on their descriptions and subtasks, utilizing Claude API for AI integration.", "status": "pending", - "dependencies": [ - 22 - ], + "dependencies": [22], "priority": "high", "details": "Implement a new command in the Task Master CLI that generates comprehensive Jest test files for tasks. The command should be callable as 'task-master generate-test --id=1' and should:\n\n1. Accept a task ID parameter to identify which task to generate tests for\n2. Retrieve the task and its subtasks from the task store\n3. Analyze the task description, details, and subtasks to understand implementation requirements\n4. Construct an appropriate prompt for the AI service using Claude API\n5. Process the AI response to create a well-formatted test file named 'task_XXX.test.ts' where XXX is the zero-padded task ID\n6. Include appropriate test cases that cover the main functionality described in the task\n7. Generate mocks for external dependencies identified in the task description\n8. Create assertions that validate the expected behavior\n9. Handle both parent tasks and subtasks appropriately (for subtasks, name the file 'task_XXX_YYY.test.ts' where YYY is the subtask ID)\n10. Include error handling for API failures, invalid task IDs, etc.\n11. Add appropriate documentation for the command in the help system\n\nThe implementation should utilize the Claude API for AI service integration and maintain consistency with the current command structure and error handling patterns. Consider using TypeScript for better type safety and integration with the Claude API.", "testStrategy": "Testing for this feature should include:\n\n1. Unit tests for the command handler function to verify it correctly processes arguments and options\n2. Mock tests for the Claude API integration to ensure proper prompt construction and response handling\n3. Integration tests that verify the end-to-end flow using a mock Claude API response\n4. Tests for error conditions including:\n - Invalid task IDs\n - Network failures when contacting the AI service\n - Malformed AI responses\n - File system permission issues\n5. Verification that generated test files follow Jest conventions and can be executed\n6. Tests for both parent task and subtask handling\n7. Manual verification of the quality of generated tests by running them against actual task implementations\n\nCreate a test fixture with sample tasks of varying complexity to evaluate the test generation capabilities across different scenarios. The tests should verify that the command outputs appropriate success/error messages to the console and creates files in the expected location with proper content structure.", @@ -1737,9 +1491,7 @@ "id": 2, "title": "Implement AI prompt construction and FastMCP integration", "description": "Develop the logic to analyze tasks, construct appropriate AI prompts, and interact with the AI service using FastMCP to generate test content.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implementation steps:\n1. Create a utility function to analyze task descriptions and subtasks for test requirements\n2. Implement a prompt builder that formats task information into an effective AI prompt\n3. Use FastMCP to send the prompt and receive the response\n4. Process the FastMCP response to extract the generated test code\n5. Implement error handling for FastMCP failures, rate limits, and malformed responses\n6. Add appropriate logging for the FastMCP interaction process\n\nTesting approach:\n- Test prompt construction with various task types\n- Test FastMCP integration with mocked responses\n- Test error handling for FastMCP failures\n- Test response processing with sample FastMCP outputs\n<info added on 2025-05-23T21:04:33.890Z>\n## AI Integration Implementation\n\n### AI Service Integration\n- Use the unified AI service layer, not FastMCP directly\n- Implement with `generateObjectService` from '../ai-services-unified.js'\n- Define Zod schema for structured test generation output:\n - testContent: Complete Jest test file content\n - fileName: Suggested filename for the test file\n - mockRequirements: External dependencies that need mocking\n\n### Prompt Construction\n- Create system prompt defining AI's role as test generator\n- Build user prompt with task context (ID, title, description, details)\n- Include test strategy and subtasks context in the prompt\n- Follow patterns from add-task.js for prompt structure\n\n### Task Analysis\n- Retrieve task data using `findTaskById()` from utils.js\n- Build context by analyzing task description, details, and testStrategy\n- Examine project structure for import patterns\n- Parse specific testing requirements from task.testStrategy field\n\n### File System Operations\n- Determine output path in same directory as tasks.json\n- Generate standardized filename based on task ID\n- Use fs.writeFileSync for writing test content to file\n\n### Error Handling & UI\n- Implement try/catch blocks for AI service calls\n- Display user-friendly error messages with chalk\n- Use loading indicators during AI processing\n- Support both research and main AI models\n\n### Telemetry\n- Pass through telemetryData from AI service response\n- Display AI usage summary for CLI output\n\n### Required Dependencies\n- generateObjectService from ai-services-unified.js\n- UI components (loading indicators, display functions)\n- Zod for schema validation\n- Chalk for formatted console output\n</info added on 2025-05-23T21:04:33.890Z>", "status": "pending", "parentTaskId": 24 @@ -1759,9 +1511,7 @@ "description": "Create MCP server tool support for the generate-test command to enable integration with Claude Code and other MCP clients.", "details": "Implementation steps:\n1. Create direct function wrapper in mcp-server/src/core/direct-functions/\n2. Create MCP tool registration in mcp-server/src/tools/\n3. Add tool to the main tools index\n4. Implement proper parameter validation and error handling\n5. Ensure telemetry data is properly passed through\n6. Add tool to MCP server registration\n\nThe MCP tool should support the same parameters as the CLI command:\n- id: Task ID to generate tests for\n- file: Path to tasks.json file\n- research: Whether to use research model\n- prompt: Additional context for test generation\n\nFollow the existing pattern from other MCP tools like add-task.js and expand-task.js.", "status": "pending", - "dependencies": [ - 3 - ], + "dependencies": [3], "parentTaskId": 24 }, { @@ -1770,9 +1520,7 @@ "description": "Enhance the init.js process to let users choose their preferred testing framework (Jest, Mocha, Vitest, etc.) and store this choice in .taskmasterconfig for use by the generate-test command.", "details": "Implementation requirements:\n\n1. **Add Testing Framework Prompt to init.js**:\n - Add interactive prompt asking users to choose testing framework\n - Support Jest (default), Mocha + Chai, Vitest, Ava, Jasmine\n - Include brief descriptions of each framework\n - Allow --testing-framework flag for non-interactive mode\n\n2. **Update .taskmasterconfig Template**:\n - Add testingFramework field to configuration file\n - Include default dependencies for each framework\n - Store framework-specific configuration options\n\n3. **Framework-Specific Setup**:\n - Generate appropriate config files (jest.config.js, vitest.config.ts, etc.)\n - Add framework dependencies to package.json suggestions\n - Create sample test file for the chosen framework\n\n4. **Integration Points**:\n - Ensure generate-test command reads testingFramework from config\n - Add validation to prevent conflicts between framework choices\n - Support switching frameworks later via models command or separate config command\n\nThis makes the generate-test command truly framework-agnostic and sets up the foundation for --with-test flags in other commands.\n<info added on 2025-05-23T21:22:02.048Z>\n# Implementation Plan for Testing Framework Integration\n\n## Code Structure\n\n### 1. Update init.js\n- Add testing framework prompt after addAliases prompt\n- Implement framework selection with descriptions\n- Support non-interactive mode with --testing-framework flag\n- Create setupTestingFramework() function to handle framework-specific setup\n\n### 2. Create New Module Files\n- Create `scripts/modules/testing-frameworks.js` for framework templates and setup\n- Add sample test generators for each supported framework\n- Implement config file generation for each framework\n\n### 3. Update Configuration Templates\n- Modify `assets/.taskmasterconfig` to include testing fields:\n ```json\n \"testingFramework\": \"{{testingFramework}}\",\n \"testingConfig\": {\n \"framework\": \"{{testingFramework}}\",\n \"setupFiles\": [],\n \"testDirectory\": \"tests\",\n \"testPattern\": \"**/*.test.js\",\n \"coverage\": {\n \"enabled\": false,\n \"threshold\": 80\n }\n }\n ```\n\n### 4. Create Framework-Specific Templates\n- `assets/jest.config.template.js`\n- `assets/vitest.config.template.ts`\n- `assets/.mocharc.template.json`\n- `assets/ava.config.template.js`\n- `assets/jasmine.json.template`\n\n### 5. Update commands.js\n- Add `--testing-framework <framework>` option to init command\n- Add validation for supported frameworks\n\n## Error Handling\n- Validate selected framework against supported list\n- Handle existing config files gracefully with warning/overwrite prompt\n- Provide recovery options if framework setup fails\n- Add conflict detection for multiple testing frameworks\n\n## Integration Points\n- Ensure generate-test command reads testingFramework from config\n- Prepare for future --with-test flag in other commands\n- Support framework switching via config command\n\n## Testing Requirements\n- Unit tests for framework selection logic\n- Integration tests for config file generation\n- Validation tests for each supported framework\n</info added on 2025-05-23T21:22:02.048Z>", "status": "pending", - "dependencies": [ - 3 - ], + "dependencies": [3], "parentTaskId": 24 } ] @@ -1782,9 +1530,7 @@ "title": "Implement 'add-subtask' Command for Task Hierarchy Management", "description": "Create a command-line interface command that allows users to manually add subtasks to existing tasks, establishing a parent-child relationship between tasks.", "status": "done", - "dependencies": [ - 3 - ], + "dependencies": [3], "priority": "medium", "details": "Implement the 'add-subtask' command that enables users to create hierarchical relationships between tasks. The command should:\n\n1. Accept parameters for the parent task ID and either the details for a new subtask or the ID of an existing task to convert to a subtask\n2. Validate that the parent task exists before proceeding\n3. If creating a new subtask, collect all necessary task information (title, description, due date, etc.)\n4. If converting an existing task, ensure it's not already a subtask of another task\n5. Update the data model to support parent-child relationships between tasks\n6. Modify the task storage mechanism to persist these relationships\n7. Ensure that when a parent task is marked complete, there's appropriate handling of subtasks (prompt user or provide options)\n8. Update the task listing functionality to display subtasks with appropriate indentation or visual hierarchy\n9. Implement proper error handling for cases like circular dependencies (a task cannot be a subtask of its own subtask)\n10. Document the command syntax and options in the help system", "testStrategy": "Testing should verify both the functionality and edge cases of the subtask implementation:\n\n1. Unit tests:\n - Test adding a new subtask to an existing task\n - Test converting an existing task to a subtask\n - Test validation logic for parent task existence\n - Test prevention of circular dependencies\n - Test error handling for invalid inputs\n\n2. Integration tests:\n - Verify subtask relationships are correctly persisted to storage\n - Verify subtasks appear correctly in task listings\n - Test the complete workflow from adding a subtask to viewing it in listings\n\n3. Edge cases:\n - Attempt to add a subtask to a non-existent parent\n - Attempt to make a task a subtask of itself\n - Attempt to create circular dependencies (A → B → A)\n - Test with a deep hierarchy of subtasks (A → B → C → D)\n - Test handling of subtasks when parent tasks are deleted\n - Verify behavior when marking parent tasks as complete\n\n4. Manual testing:\n - Verify command usability and clarity of error messages\n - Test the command with various parameter combinations", @@ -1802,9 +1548,7 @@ "id": 2, "title": "Implement Core addSubtask Function in task-manager.js", "description": "Create the core function that handles adding subtasks to parent tasks", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "1. Create a new addSubtask function in scripts/modules/task-manager.js\n2. Implement logic to validate that the parent task exists\n3. Add functionality to handle both creating new subtasks and converting existing tasks\n4. For new subtasks: collect task information and create a new task with parentId set\n5. For existing tasks: validate it's not already a subtask and update its parentId\n6. Add validation to prevent circular dependencies (a task cannot be a subtask of its own subtask)\n7. Update the parent task's subtasks array\n8. Ensure proper error handling with descriptive error messages\n9. Export the function for use by the command handler\n10. Write unit tests to verify all scenarios (new subtask, converting task, error cases)", "status": "done", "parentTaskId": 25 @@ -1822,9 +1566,7 @@ "id": 4, "title": "Create Unit Test for add-subtask", "description": "Develop comprehensive unit tests for the add-subtask functionality", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "1. Create a test file in tests/unit/ directory for the add-subtask functionality\n2. Write tests for the addSubtask function in task-manager.js\n3. Test all key scenarios: adding new subtasks, converting existing tasks to subtasks\n4. Test error cases: non-existent parent task, circular dependencies, invalid input\n5. Use Jest mocks to isolate the function from file system operations\n6. Test the command handler in isolation using mock functions\n7. Ensure test coverage for all branches and edge cases\n8. Document the testing approach for future reference", "status": "done", "parentTaskId": 25 @@ -1833,9 +1575,7 @@ "id": 5, "title": "Implement remove-subtask Command", "description": "Create functionality to remove a subtask from its parent, following the same approach as add-subtask", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "1. Create a removeSubtask function in scripts/modules/task-manager.js\n2. Implement logic to validate the subtask exists and is actually a subtask\n3. Add options to either delete the subtask completely or convert it to a standalone task\n4. Update the parent task's subtasks array to remove the reference\n5. If converting to standalone task, clear the parentId reference\n6. Implement the remove-subtask command in scripts/modules/commands.js following patterns from add-subtask\n7. Add appropriate validation and error messages\n8. Document the command in the help system\n9. Export the function in task-manager.js\n10. Ensure proper error handling for all scenarios", "status": "done", "parentTaskId": 25 @@ -1847,9 +1587,7 @@ "title": "Implement Context Foundation for AI Operations", "description": "Implement the foundation for context integration in Task Master, enabling AI operations to leverage file-based context, cursor rules, and basic code context to improve generated outputs.", "status": "pending", - "dependencies": [ - 7 - ], + "dependencies": [7], "priority": "high", "details": "Create a Phase 1 foundation for context integration in Task Master that provides immediate practical value:\n\n1. Add `--context-file` Flag to AI Commands:\n - Add a consistent `--context-file <file>` option to all AI-related commands (expand, update, add-task, etc.)\n - Implement file reading functionality that loads content from the specified file\n - Add content integration into Claude API prompts with appropriate formatting\n - Handle error conditions such as file not found gracefully\n - Update help documentation to explain the new option\n\n2. Implement Cursor Rules Integration for Context:\n - Create a `--context-rules <rules>` option for all AI commands\n - Implement functionality to extract content from specified .cursor/rules/*.mdc files\n - Support comma-separated lists of rule names and \"all\" option\n - Add validation and error handling for non-existent rules\n - Include helpful examples in command help output\n\n3. Implement Basic Context File Extraction Utility:\n - Create utility functions in utils.js for reading context from files\n - Add proper error handling and logging\n - Implement content validation to ensure reasonable size limits\n - Add content truncation if files exceed token limits\n - Create helper functions for formatting context additions properly\n\n4. Update Command Handler Logic:\n - Modify command handlers to support the new context options\n - Update prompt construction to incorporate context content\n - Ensure backwards compatibility with existing commands\n - Add logging for context inclusion to aid troubleshooting\n\nThe focus of this phase is to provide immediate value with straightforward implementations that enable users to include relevant context in their AI operations.", "testStrategy": "Testing should verify that the context foundation works as expected and adds value:\n\n1. Functional Tests:\n - Verify `--context-file` flag correctly reads and includes content from specified files\n - Test that `--context-rules` correctly extracts and formats content from cursor rules\n - Test with both existing and non-existent files/rules to verify error handling\n - Verify content truncation works appropriately for large files\n\n2. Integration Tests:\n - Test each AI-related command with context options\n - Verify context is properly included in API calls to Claude\n - Test combinations of multiple context options\n - Verify help documentation includes the new options\n\n3. Usability Testing:\n - Create test scenarios that show clear improvement in AI output quality with context\n - Compare outputs with and without context to measure impact\n - Document examples of effective context usage for the user documentation\n\n4. Error Handling:\n - Test invalid file paths and rule names\n - Test oversized context files\n - Verify appropriate error messages guide users to correct usage\n\nThe testing focus should be on proving immediate value to users while ensuring robust error handling.", @@ -1897,9 +1635,7 @@ "title": "Implement Context Enhancements for AI Operations", "description": "Enhance the basic context integration with more sophisticated code context extraction, task history awareness, and PRD integration to provide richer context for AI operations.", "status": "pending", - "dependencies": [ - 26 - ], + "dependencies": [26], "priority": "high", "details": "Building upon the foundational context implementation in Task #26, implement Phase 2 context enhancements:\n\n1. Add Code Context Extraction Feature:\n - Create a `--context-code <pattern>` option for all AI commands\n - Implement glob-based file matching to extract code from specified patterns\n - Create intelligent code parsing to extract most relevant sections (function signatures, classes, exports)\n - Implement token usage optimization by selecting key structural elements\n - Add formatting for code context with proper file paths and syntax indicators\n\n2. Implement Task History Context:\n - Add a `--context-tasks <ids>` option for AI commands\n - Support comma-separated task IDs and a \"similar\" option to find related tasks\n - Create functions to extract context from specified tasks or find similar tasks\n - Implement formatting for task context with clear section markers\n - Add validation and error handling for non-existent task IDs\n\n3. Add PRD Context Integration:\n - Create a `--context-prd <file>` option for AI commands\n - Implement PRD text extraction and intelligent summarization\n - Add formatting for PRD context with appropriate section markers\n - Integrate with the existing PRD parsing functionality from Task #6\n\n4. Improve Context Formatting and Integration:\n - Create a standardized context formatting system\n - Implement type-based sectioning for different context sources\n - Add token estimation for different context types to manage total prompt size\n - Enhance prompt templates to better integrate various context types\n\nThese enhancements will provide significantly richer context for AI operations, resulting in more accurate and relevant outputs while remaining practical to implement.", "testStrategy": "Testing should verify the enhanced context functionality:\n\n1. Code Context Testing:\n - Verify pattern matching works for different glob patterns\n - Test code extraction with various file types and sizes\n - Verify intelligent parsing correctly identifies important code elements\n - Test token optimization by comparing full file extraction vs. optimized extraction\n - Check code formatting in prompts sent to Claude API\n\n2. Task History Testing:\n - Test with different combinations of task IDs\n - Verify \"similar\" option correctly identifies relevant tasks\n - Test with non-existent task IDs to ensure proper error handling\n - Verify formatting and integration in prompts\n\n3. PRD Context Testing:\n - Test with various PRD files of different sizes\n - Verify summarization functions correctly when PRDs are too large\n - Test integration with prompts and formatting\n\n4. Performance Testing:\n - Measure the impact of context enrichment on command execution time\n - Test with large code bases to ensure reasonable performance\n - Verify token counting and optimization functions work as expected\n\n5. Quality Assessment:\n - Compare AI outputs with Phase 1 vs. Phase 2 context to measure improvements\n - Create test cases that specifically benefit from code context\n - Create test cases that benefit from task history context\n\nFocus testing on practical use cases that demonstrate clear improvements in AI-generated outputs.", @@ -1947,10 +1683,7 @@ "title": "Implement Advanced ContextManager System", "description": "Create a comprehensive ContextManager class to unify context handling with advanced features like context optimization, prioritization, and intelligent context selection.", "status": "pending", - "dependencies": [ - 26, - 27 - ], + "dependencies": [26, 27], "priority": "high", "details": "Building on Phase 1 and Phase 2 context implementations, develop Phase 3 advanced context management:\n\n1. Implement the ContextManager Class:\n - Create a unified `ContextManager` class that encapsulates all context functionality\n - Implement methods for gathering context from all supported sources\n - Create a configurable context priority system to favor more relevant context types\n - Add token management to ensure context fits within API limits\n - Implement caching for frequently used context to improve performance\n\n2. Create Context Optimization Pipeline:\n - Develop intelligent context optimization algorithms\n - Implement type-based truncation strategies (code vs. text)\n - Create relevance scoring to prioritize most useful context portions\n - Add token budget allocation that divides available tokens among context types\n - Implement dynamic optimization based on operation type\n\n3. Add Command Interface Enhancements:\n - Create the `--context-all` flag to include all available context\n - Add the `--context-max-tokens <tokens>` option to control token allocation\n - Implement unified context options across all AI commands\n - Add intelligent default values for different command types\n\n4. Integrate with AI Services:\n - Update the AI service integration to use the ContextManager\n - Create specialized context assembly for different AI operations\n - Add post-processing to capture new context from AI responses\n - Implement adaptive context selection based on operation success\n\n5. Add Performance Monitoring:\n - Create context usage statistics tracking\n - Implement logging for context selection decisions\n - Add warnings for context token limits\n - Create troubleshooting utilities for context-related issues\n\nThe ContextManager system should provide a powerful but easy-to-use interface for both users and developers, maintaining backward compatibility with earlier phases while adding substantial new capabilities.", "testStrategy": "Testing should verify both the functionality and performance of the advanced context management:\n\n1. Unit Testing:\n - Test all ContextManager class methods with various inputs\n - Verify optimization algorithms maintain critical information\n - Test caching mechanisms for correctness and efficiency\n - Verify token allocation and budgeting functions\n - Test each context source integration separately\n\n2. Integration Testing:\n - Verify ContextManager integration with AI services\n - Test with all AI-related commands\n - Verify backward compatibility with existing context options\n - Test context prioritization across multiple context types\n - Verify logging and error handling\n\n3. Performance Testing:\n - Benchmark context gathering and optimization times\n - Test with large and complex context sources\n - Measure impact of caching on repeated operations\n - Verify memory usage remains acceptable\n - Test with token limits of different sizes\n\n4. Quality Assessment:\n - Compare AI outputs using Phase 3 vs. earlier context handling\n - Measure improvements in context relevance and quality\n - Test complex scenarios requiring multiple context types\n - Quantify the impact on token efficiency\n\n5. User Experience Testing:\n - Verify CLI options are intuitive and well-documented\n - Test error messages are helpful for troubleshooting\n - Ensure log output provides useful insights\n - Test all convenience options like `--context-all`\n\nCreate automated test suites for regression testing of the complete context system.", @@ -2182,9 +1915,7 @@ "id": 2, "title": "Implement updateTask command in commands.js", "description": "Create a new command called 'updateTask' in commands.js that leverages the updateTaskById function to update a specific task by ID.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implementation steps:\n1. Create a new command object for 'updateTask' in commands.js following the Command pattern\n2. Define command parameters including a required taskId parameter\n3. Support all options from the existing update command:\n - Research flag for Perplexity integration\n - Formatting and refinement options\n - Task context options\n4. Implement the command handler function that calls the updateTaskById function from task-manager.js\n5. Add appropriate error handling to catch and display user-friendly error messages\n6. Ensure the command follows the same pattern as other commands in the codebase\n7. Implement proper validation of input parameters\n8. Format and return appropriate success/failure messages to the user\n\nTesting approach:\n- Unit test the command handler with various input combinations\n- Test error handling scenarios\n- Verify command options are correctly passed to the updateTaskById function", "status": "done", "parentTaskId": 34 @@ -2193,9 +1924,7 @@ "id": 3, "title": "Add comprehensive error handling and validation", "description": "Implement robust error handling and validation for the updateTask command to ensure proper user feedback and system stability.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implementation steps:\n1. Create custom error types for different failure scenarios (TaskNotFoundError, ValidationError, etc.)\n2. Implement input validation for the taskId parameter and all options\n3. Add proper error handling for AI service failures with appropriate fallback mechanisms\n4. Implement concurrency handling to prevent conflicts when multiple updates occur simultaneously\n5. Add comprehensive logging for debugging and auditing purposes\n6. Ensure all error messages are user-friendly and actionable\n7. Implement proper HTTP status codes for API responses if applicable\n8. Add validation to ensure the task exists before attempting updates\n\nTesting approach:\n- Test various error scenarios including invalid inputs, non-existent tasks, and API failures\n- Verify error messages are clear and helpful\n- Test concurrency scenarios with multiple simultaneous updates\n- Verify logging captures appropriate information for troubleshooting", "status": "done", "parentTaskId": 34 @@ -2204,10 +1933,7 @@ "id": 4, "title": "Write comprehensive tests for updateTask command", "description": "Create a comprehensive test suite for the updateTask command to ensure it works correctly in all scenarios and maintains backward compatibility.", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "Implementation steps:\n1. Create unit tests for the updateTaskById function in task-manager.js\n - Test finding and updating tasks with various IDs\n - Test preservation of completed subtasks\n - Test different update options combinations\n - Test error handling for non-existent tasks\n2. Create unit tests for the updateTask command in commands.js\n - Test command parameter parsing\n - Test option handling\n - Test error scenarios and messages\n3. Create integration tests that verify the end-to-end flow\n - Test the command with actual AI service integration\n - Test with mock AI responses for predictable testing\n4. Implement test fixtures and mocks for consistent testing\n5. Add performance tests to ensure the command is efficient\n6. Test edge cases such as empty tasks, tasks with many subtasks, etc.\n\nTesting approach:\n- Use Jest or similar testing framework\n- Implement mocks for external dependencies like AI services\n- Create test fixtures for consistent test data\n- Use snapshot testing for command output verification", "status": "done", "parentTaskId": 34 @@ -2286,9 +2012,7 @@ "id": 2, "title": "Update Source Code License Headers and Package Metadata", "description": "Add appropriate dual license headers to all source code files and update package metadata to reflect the new licensing structure.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implementation steps:\n1. Create a template for the new license header that references the dual license structure (BSL 1.1 / Apache 2.0).\n2. Systematically update all source code files to include the new license header, replacing any existing MIT headers.\n3. Update the license field in package.json to \"BSL 1.1 / Apache 2.0\".\n4. Update any other metadata files (composer.json, setup.py, etc.) that contain license information.\n5. Verify that any build scripts or tools that reference licensing information are updated.\n\nTesting approach:\n- Write a script to verify that all source files contain the new license header\n- Validate package.json and other metadata files have the correct license field\n- Ensure any build processes that depend on license information still function correctly\n- Run a sample build to confirm license information is properly included in any generated artifacts", "status": "done", "parentTaskId": 39 @@ -2297,9 +2021,7 @@ "id": 3, "title": "Update Documentation and Create License Explanation", "description": "Update project documentation to clearly explain the dual license structure and create comprehensive licensing guidance.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implementation steps:\n1. Update the README.md with a clear, concise explanation of the licensing terms:\n - Summary of what users can and cannot do with the code\n - Who holds commercial rights (Ralph & Eyal)\n - How to obtain commercial use permission\n - Links to the full license texts\n2. Create a dedicated LICENSING.md or similar document with detailed explanations of:\n - The rationale behind the dual licensing approach\n - Detailed examples of what constitutes commercial vs. non-commercial use\n - FAQs addressing common licensing questions\n3. Update any other documentation references to licensing throughout the project.\n4. Create visual aids (if appropriate) to help users understand the licensing structure.\n5. Ensure all documentation links to licensing information are updated.\n\nTesting approach:\n- Have non-technical stakeholders review the documentation for clarity and understanding\n- Verify all links to license files work correctly\n- Ensure the explanation is comprehensive but concise enough for users to understand quickly\n- Check that the documentation correctly addresses the most common use cases and questions", "status": "done", "parentTaskId": 39 @@ -2328,9 +2050,7 @@ "id": 2, "title": "Generate Implementation Plan with AI", "description": "Use an AI model (Claude or Perplexity) to generate an implementation plan based on the retrieved task content. The plan should outline the steps required to complete the task.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implement logic to switch between Claude and Perplexity APIs. Handle API authentication and rate limiting. Prompt the AI model with the task content and request a detailed implementation plan.", "status": "pending" }, @@ -2346,9 +2066,7 @@ "id": 4, "title": "Error Handling and Output", "description": "Implement error handling for all steps, including API failures and XML formatting errors. Output the formatted XML plan to the console or a file.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Add try-except blocks to handle potential exceptions. Log errors for debugging. Provide informative error messages to the user. Output the XML plan in a user-friendly format.", "status": "pending" } @@ -2376,9 +2094,7 @@ "id": 2, "title": "Graph Layout Algorithms", "description": "Develop or integrate algorithms to compute optimal node and edge placement for clear and readable graph layouts in a terminal environment.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Consider topological sorting, hierarchical, and force-directed layouts suitable for ASCII/Unicode rendering.\n<info added on 2025-05-23T21:02:49.434Z>\nCreate a new diagram-generator.js module in the scripts/modules/ directory following Task Master's module architecture pattern. The module should include:\n\n1. Core functions for generating Mermaid diagrams:\n - generateDependencyGraph(tasks, options) - creates flowchart showing task dependencies\n - generateSubtaskDiagram(task, options) - creates hierarchy diagram for subtasks\n - generateProjectFlow(tasks, options) - creates overall project workflow\n - generateGanttChart(tasks, options) - creates timeline visualization\n\n2. Integration with existing Task Master data structures:\n - Use the same task object format from task-manager.js\n - Leverage dependency analysis from dependency-manager.js\n - Support complexity scores from analyze-complexity functionality\n - Handle both main tasks and subtasks with proper ID notation (parentId.subtaskId)\n\n3. Layout algorithm considerations for Mermaid:\n - Topological sorting for dependency flows\n - Hierarchical layouts for subtask trees\n - Circular dependency detection and highlighting\n - Terminal width-aware formatting for ASCII fallback\n\n4. Export functions following the existing module pattern at the bottom of the file\n</info added on 2025-05-23T21:02:49.434Z>", "status": "pending" }, @@ -2394,9 +2110,7 @@ "id": 4, "title": "Color Coding Support", "description": "Add color coding to nodes and edges to visually distinguish types, statuses, or other attributes in the graph.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Use ANSI escape codes for color; provide options for colorblind-friendly palettes.\n<info added on 2025-05-23T21:03:35.762Z>\nIntegrate color coding with Task Master's existing status system:\n\n1. Extend getStatusWithColor() in ui.js to support diagram contexts:\n - Add 'diagram' parameter to determine rendering context\n - Modify color intensity for better visibility in graph elements\n\n2. Implement Task Master's established color scheme using ANSI codes:\n - Green (\\x1b[32m) for 'done'/'completed' tasks\n - Yellow (\\x1b[33m) for 'pending' tasks\n - Orange (\\x1b[38;5;208m) for 'in-progress' tasks\n - Red (\\x1b[31m) for 'blocked' tasks\n - Gray (\\x1b[90m) for 'deferred'/'cancelled' tasks\n - Magenta (\\x1b[35m) for 'review' tasks\n\n3. Create diagram-specific color functions:\n - getDependencyLineColor(fromTaskStatus, toTaskStatus) - color dependency arrows based on relationship status\n - getNodeBorderColor(task) - style node borders using priority/complexity indicators\n - getSubtaskGroupColor(parentTask) - visually group related subtasks\n\n4. Integrate complexity visualization:\n - Use getComplexityWithColor() for node background or border thickness\n - Map complexity scores to visual weight in the graph\n\n5. Ensure accessibility:\n - Add text-based indicators (symbols like ✓, ⚠, ⏳) alongside colors\n - Implement colorblind-friendly palettes as user-selectable option\n - Include shape variations for different statuses\n\n6. Follow existing ANSI patterns:\n - Maintain consistency with terminal UI color usage\n - Reuse color constants from the codebase\n\n7. Support graceful degradation:\n - Check terminal capabilities using existing detection\n - Provide monochrome fallbacks with distinctive patterns\n - Use bold/underline as alternatives when colors unavailable\n</info added on 2025-05-23T21:03:35.762Z>", "status": "pending" }, @@ -2412,9 +2126,7 @@ "id": 6, "title": "Filtering and Search Functionality", "description": "Enable users to filter nodes and edges by criteria such as name, type, or dependency depth.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Support command-line flags for filtering and interactive search if feasible.\n<info added on 2025-05-23T21:04:57.811Z>\nImplement MCP tool integration for task dependency visualization:\n\n1. Create task_diagram.js in mcp-server/src/tools/ following existing tool patterns\n2. Implement taskDiagramDirect.js in mcp-server/src/core/direct-functions/\n3. Use Zod schema for parameter validation:\n - diagramType (dependencies, subtasks, flow, gantt)\n - taskId (optional string)\n - format (mermaid, text, json)\n - includeComplexity (boolean)\n\n4. Structure response data with:\n - mermaidCode for client-side rendering\n - metadata (nodeCount, edgeCount, cycleWarnings)\n - support for both task-specific and project-wide diagrams\n\n5. Integrate with session management and project root handling\n6. Implement error handling using handleApiResult pattern\n7. Register the tool in tools/index.js\n\nMaintain compatibility with existing command-line flags for filtering and interactive search.\n</info added on 2025-05-23T21:04:57.811Z>", "status": "pending" }, @@ -2422,10 +2134,7 @@ "id": 7, "title": "Accessibility Features", "description": "Ensure the tool is accessible, including support for screen readers, high-contrast modes, and keyboard navigation.", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Provide alternative text output and ensure color is not the sole means of conveying information.\n<info added on 2025-05-23T21:05:54.584Z>\n# Accessibility and Export Integration\n\n## Accessibility Features\n- Provide alternative text output for visual elements\n- Ensure color is not the sole means of conveying information\n- Support keyboard navigation through the dependency graph\n- Add screen reader compatible node descriptions\n\n## Export Integration\n- Extend generateTaskFiles function in task-manager.js to include Mermaid diagram sections\n- Add Mermaid code blocks to task markdown files under ## Diagrams header\n- Follow existing task file generation patterns and markdown structure\n- Support multiple diagram types per task file:\n * Task dependencies (prerequisite relationships)\n * Subtask hierarchy visualization\n * Task flow context in project workflow\n- Integrate with existing fs module file writing operations\n- Add diagram export options to the generate command in commands.js\n- Support SVG and PNG export using Mermaid CLI when available\n- Implement error handling for diagram generation failures\n- Reference exported diagrams in task markdown with proper paths\n- Update CLI generate command with options like --include-diagrams\n</info added on 2025-05-23T21:05:54.584Z>", "status": "pending" }, @@ -2433,10 +2142,7 @@ "id": 8, "title": "Performance Optimization", "description": "Profile and optimize the tool for large graphs to ensure responsive rendering and low memory usage.", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Implement lazy loading, efficient data structures, and parallel processing where appropriate.\n<info added on 2025-05-23T21:06:14.533Z>\n# Mermaid Library Integration and Terminal-Specific Handling\n\n## Package Dependencies\n- Add mermaid package as an optional dependency in package.json for generating raw Mermaid diagram code\n- Consider mermaid-cli for SVG/PNG conversion capabilities\n- Evaluate terminal-image or similar libraries for terminals with image support\n- Explore ascii-art-ansi or box-drawing character libraries for text-only terminals\n\n## Terminal Capability Detection\n- Leverage existing terminal detection from ui.js to assess rendering capabilities\n- Implement detection for:\n - iTerm2 and other terminals with image protocol support\n - Terminals with Unicode/extended character support\n - Basic terminals requiring pure ASCII output\n\n## Rendering Strategy with Fallbacks\n1. Primary: Generate raw Mermaid code for user copy/paste\n2. Secondary: Render simplified ASCII tree/flow representation using box characters\n3. Tertiary: Present dependencies in tabular format for minimal terminals\n\n## Implementation Approach\n- Use dynamic imports for optional rendering libraries to maintain lightweight core\n- Implement graceful degradation when optional packages aren't available\n- Follow Task Master's philosophy of minimal dependencies\n- Ensure performance optimization through lazy loading where appropriate\n- Design modular rendering components that can be swapped based on terminal capabilities\n</info added on 2025-05-23T21:06:14.533Z>", "status": "pending" }, @@ -2444,12 +2150,7 @@ "id": 9, "title": "Documentation", "description": "Write comprehensive user and developer documentation covering installation, usage, configuration, and extension.", - "dependencies": [ - 1, - 3, - 4, - 7 - ], + "dependencies": [1, 3, 4, 7], "details": "Include examples, troubleshooting, and contribution guidelines.", "status": "pending" }, @@ -2457,12 +2158,7 @@ "id": 10, "title": "Testing and Validation", "description": "Develop automated tests for all major features, including CLI parsing, layout correctness, rendering, color coding, filtering, and cycle detection.", - "dependencies": [ - 1, - 3, - 4, - 7 - ], + "dependencies": [1, 3, 4, 7], "details": "Include unit, integration, and regression tests; validate accessibility and performance claims.\n<info added on 2025-05-23T21:08:36.329Z>\n# Documentation Tasks for Visual Task Dependency Graph\n\n## User Documentation\n1. Update README.md with diagram command documentation following existing command reference format\n2. Add examples to CLI command help text in commands.js matching patterns from other commands\n3. Create docs/diagrams.md with detailed usage guide including:\n - Command examples for each diagram type\n - Mermaid code samples and output\n - Terminal compatibility notes\n - Integration with task workflow examples\n - Troubleshooting section for common diagram rendering issues\n - Accessibility features and terminal fallback options\n\n## Developer Documentation\n1. Update MCP tool documentation to include the new task_diagram tool\n2. Add JSDoc comments to all new functions following existing code standards\n3. Create contributor documentation for extending diagram types\n4. Update API documentation for any new MCP interface endpoints\n\n## Integration Documentation\n1. Document integration with existing commands (analyze-complexity, generate, etc.)\n2. Provide examples showing how diagrams complement other Task Master features\n</info added on 2025-05-23T21:08:36.329Z>", "status": "pending" } @@ -2552,9 +2248,7 @@ "id": 2, "title": "Implement webhook authentication and security measures", "description": "Develop security mechanisms for webhook verification and payload signing", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implement signature verification using HMAC, rate limiting to prevent abuse, IP whitelisting options, and webhook secret management. Create a secure token system for webhook verification and implement TLS for all webhook communications.", "status": "pending" }, @@ -2570,10 +2264,7 @@ "id": 4, "title": "Build event processing and queuing system", "description": "Implement a robust system for processing and queuing events before webhook delivery", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "Create an event queue using a message broker (like RabbitMQ or Kafka) to handle high volumes of events. Implement event deduplication, prioritization, and persistence to ensure reliable delivery even during system failures.", "status": "pending" }, @@ -2581,9 +2272,7 @@ "id": 5, "title": "Develop webhook delivery and retry mechanism", "description": "Create a reliable system for webhook delivery with retry logic and failure handling", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Implement exponential backoff retry logic, configurable retry attempts, and dead letter queues for failed deliveries. Add monitoring for webhook delivery success rates and performance metrics. Include timeout handling for unresponsive webhook endpoints.", "status": "pending" }, @@ -2599,9 +2288,7 @@ "id": 7, "title": "Create webhook testing and simulation tools", "description": "Develop tools for testing webhook integrations and simulating event triggers", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Build a webhook testing console that allows manual triggering of events, viewing delivery history, and replaying failed webhooks. Create a webhook simulator for developers to test their endpoint implementations without generating real system events.", "status": "pending" } @@ -2612,9 +2299,7 @@ "title": "Implement GitHub Issue Import Feature", "description": "Implement a comprehensive LLM-powered 'import_task' command that can intelligently import tasks from GitHub Issues and Discussions. The system uses our existing ContextGatherer.js infrastructure to analyze the full context of GitHub content and automatically generate well-structured tasks with appropriate subtasks, priorities, and implementation details. This feature works in conjunction with the GitHub export feature (Task #97) to provide bidirectional linking between Task Master tasks and GitHub issues.", "status": "pending", - "dependencies": [ - 97 - ], + "dependencies": [97], "priority": "medium", "details": "Implement a new 'import_task' command that leverages LLM-powered analysis to create comprehensive tasks from GitHub Issues and Discussions. The system should be designed as an extensible import framework that can support multiple platforms in the future.\n\nCore functionality:\n1. **New Command Structure**: Create 'import_task' command with source-specific subcommands:\n - 'taskmaster import_task github <URL>' for GitHub imports\n - Future: 'taskmaster import_task gitlab <URL>', 'taskmaster import_task linear <URL>', etc.\n\n2. **Multi-Source GitHub Support**: Automatically detect and handle:\n - GitHub Issues: https://github.com/owner/repo/issues/123\n - GitHub Discussions: https://github.com/owner/repo/discussions/456\n - Auto-detect URL type and use appropriate API endpoints\n\n3. **LLM-Powered Context Analysis**: Integrate with ContextGatherer.js to:\n - Fetch complete GitHub content (main post + all comments/replies)\n - Analyze discussion threads and extract key insights\n - Identify references to our project components and codebase\n - Generate comprehensive task descriptions based on full context\n - Automatically create relevant subtasks from complex discussions\n - Determine appropriate priority levels based on content analysis\n - Suggest dependencies based on mentioned components/features\n\n4. **Smart Content Processing**: The LLM should:\n - Parse markdown content and preserve important formatting\n - Extract actionable items from discussion threads\n - Identify implementation requirements and technical details\n - Convert complex discussions into structured task breakdowns\n - Generate appropriate test strategies based on the scope\n - Preserve important context while creating focused task descriptions\n\n5. **Enhanced GitHub API Integration**:\n - Support GITHUB_API_KEY environment variable for authentication\n - Handle both public and private repository access\n - Fetch issue/discussion metadata (labels, assignees, status)\n - Retrieve complete comment threads with proper threading\n - Implement rate limiting and error handling\n\n6. **Rich Metadata Storage**:\n - Source platform and original URL\n - Import timestamp and LLM model version used\n - Content hash for change detection and sync capabilities\n - Participant information and discussion context\n - GitHub-specific metadata (labels, assignees, status)\n - **Use consistent metadata schema with export feature (Task #97)**\n\n7. **Future-Proof Architecture**:\n - Modular design supporting multiple import sources\n - Plugin-style architecture for new platforms\n - Extensible content type handling (issues, PRs, discussions, etc.)\n - Configurable LLM prompts for different content types\n\n8. **Bidirectional Integration**:\n - Maintain compatibility with GitHub export feature\n - Enable round-trip workflows (import → modify → export)\n - Preserve source linking for synchronization capabilities\n - Support identification of imported vs. native tasks\n\n9. **Error Handling and Validation**:\n - Validate GitHub URLs and accessibility\n - Handle API rate limiting gracefully\n - Provide meaningful error messages for various failure scenarios\n - Implement retry logic for transient failures\n - Validate LLM responses and handle generation errors\n\n10. **Configuration and Customization**:\n - Allow users to customize LLM prompts for task generation\n - Support different import strategies (full vs. summary)\n - Enable filtering of comments by date, author, or relevance\n - Provide options for manual review before task creation", "testStrategy": "Testing should cover the comprehensive LLM-powered import system:\n\n1. **Unit Tests**:\n - Test URL parsing for GitHub Issues and Discussions\n - Test GitHub API client with mocked responses\n - Test LLM integration with ContextGatherer.js\n - Test metadata schema consistency with export feature\n - Test content processing and task generation logic\n - Test error handling for various failure scenarios\n\n2. **Integration Tests**:\n - Test with real GitHub Issues and Discussions (public repos)\n - Test LLM-powered analysis with various content types\n - Test ContextGatherer integration with GitHub content\n - Test bidirectional compatibility with export feature\n - Test metadata structure and storage\n - Test with different GitHub content complexities\n\n3. **LLM and Context Analysis Tests**:\n - Test task generation quality with various GitHub content types\n - Test subtask creation from complex discussions\n - Test priority and dependency inference\n - Test handling of code references and technical discussions\n - Test content summarization and structure preservation\n - Validate LLM prompt effectiveness and response quality\n\n4. **Error Case Tests**:\n - Invalid or malformed GitHub URLs\n - Non-existent repositories or issues/discussions\n - API rate limit handling\n - Authentication failures for private repos\n - LLM service unavailability or errors\n - Network connectivity issues\n - Malformed or incomplete GitHub content\n\n5. **End-to-End Tests**:\n - Complete import workflow from GitHub URL to created task\n - Verify task quality and completeness\n - Test metadata preservation and linking\n - Test compatibility with existing task management features\n - Verify bidirectional workflow with export feature\n\n6. **Performance and Scalability Tests**:\n - Test with large GitHub discussions (many comments)\n - Test LLM processing time and resource usage\n - Test API rate limiting behavior\n - Test concurrent import operations\n\n7. **Future Platform Preparation Tests**:\n - Test modular architecture extensibility\n - Verify plugin-style platform addition capability\n - Test configuration system flexibility\n\nCreate comprehensive mock data for GitHub API responses including various issue/discussion types, comment structures, and edge cases. Use environment variables for test credentials and LLM service configuration.", @@ -2631,9 +2316,7 @@ "id": 2, "title": "Implement GitHub URL parsing and validation", "description": "Create a module to parse and validate GitHub issue URLs, extracting repository owner, repository name, and issue number.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Handle various GitHub URL formats (e.g., github.com/owner/repo/issues/123, github.com/owner/repo/pull/123). Implement validation to ensure the URL points to a valid issue or pull request. Return structured data with owner, repo, and issue number for valid URLs.", "status": "pending" }, @@ -2641,9 +2324,7 @@ "id": 3, "title": "Develop GitHub API client for issue fetching", "description": "Create a service to authenticate with GitHub and fetch issue details using the GitHub REST API.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implement authentication using GitHub Personal Access Tokens or OAuth. Handle API responses, including error cases (rate limiting, authentication failures, not found). Extract relevant issue data: title, description, labels, assignees, and comments.", "status": "pending" }, @@ -2651,9 +2332,7 @@ "id": 4, "title": "Create task formatter for GitHub issues", "description": "Develop a formatter to convert GitHub issue data into the application's task format.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Map GitHub issue fields to task fields (title, description, etc.). Convert GitHub markdown to the application's supported format. Handle special GitHub features like issue references and user mentions. Generate appropriate tags based on GitHub labels.", "status": "pending" }, @@ -2661,9 +2340,7 @@ "id": 5, "title": "Implement end-to-end import flow with UI", "description": "Create the user interface and workflow for importing GitHub issues, including progress indicators and error handling.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Design and implement UI for URL input and import confirmation. Show loading states during API calls. Display meaningful error messages for various failure scenarios. Allow users to review and modify imported task details before saving. Add automated tests for the entire import flow.", "status": "pending" }, @@ -2703,9 +2380,7 @@ "id": 10, "title": "Implement comprehensive GitHub API client", "description": "Create enhanced GitHub API client supporting both Issues and Discussions APIs with complete content fetching.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Extend existing API client to support GitHub Discussions API. Implement complete content fetching including all comments and replies. Add support for GITHUB_API_KEY environment variable. Handle threaded discussions and comment hierarchies. Implement robust error handling and rate limiting for both API types.", "status": "pending" }, @@ -2721,9 +2396,7 @@ "id": 12, "title": "Implement LLM-powered task generation", "description": "Create the core LLM integration that analyzes GitHub content and generates comprehensive tasks with subtasks.", - "dependencies": [ - 11 - ], + "dependencies": [11], "details": "Design LLM prompts for task generation from GitHub content. Implement automatic subtask creation from complex discussions. Add priority and dependency inference based on content analysis. Create test strategy generation from technical discussions. Implement quality validation for LLM-generated content. Add fallback mechanisms for LLM failures.", "status": "pending" }, @@ -2731,9 +2404,7 @@ "id": 13, "title": "Enhance metadata system for rich import context", "description": "Extend the metadata schema to store comprehensive import context and enable advanced features.", - "dependencies": [ - 12 - ], + "dependencies": [12], "details": "Extend existing metadata schema with import-specific fields. Add source platform, import timestamp, and LLM model tracking. Implement content hash storage for change detection. Store participant information and discussion context. Add support for custom metadata per platform type. Ensure backward compatibility with existing export feature metadata.", "status": "pending" }, @@ -2741,10 +2412,7 @@ "id": 14, "title": "Implement import_task command interface", "description": "Create the user-facing command interface for the new import_task system with GitHub support.", - "dependencies": [ - 12, - 13 - ], + "dependencies": [12, 13], "details": "Implement the main import_task command with GitHub subcommand. Add command-line argument parsing and validation. Create progress indicators for LLM processing. Implement user review and confirmation workflow. Add verbose output options for debugging. Create help documentation and usage examples.", "status": "pending" }, @@ -2752,9 +2420,7 @@ "id": 15, "title": "Add comprehensive testing and validation", "description": "Implement comprehensive testing suite covering all aspects of the LLM-powered import system.", - "dependencies": [ - 14 - ], + "dependencies": [14], "details": "Create unit tests for all new components. Implement integration tests with real GitHub content. Add LLM response validation and quality tests. Create performance tests for large discussions. Implement end-to-end workflow testing. Add mock data for consistent testing. Test bidirectional compatibility with export feature.", "status": "pending" } @@ -2782,9 +2448,7 @@ "id": 2, "title": "Implement AI integration for ICE scoring", "description": "Develop the AI component that will analyze tasks and generate ICE scores", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create prompts for the AI to evaluate Impact, Confidence, and Ease. Implement error handling for AI responses. Add caching to prevent redundant AI calls. Ensure the AI provides justification for each score component. Test with various task types to ensure consistent scoring.", "status": "pending" }, @@ -2800,9 +2464,7 @@ "id": 4, "title": "Implement CLI rendering for ICE analysis", "description": "Develop the command-line interface for displaying ICE analysis results", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Design a tabular format for displaying ICE scores in the terminal. Use color coding to highlight high/medium/low priority tasks. Implement filtering options (by score range, task type, etc.). Add sorting capabilities. Create a summary view that shows top N tasks by ICE score.", "status": "pending" }, @@ -2810,10 +2472,7 @@ "id": 5, "title": "Integrate with existing complexity reports", "description": "Connect the ICE analysis functionality with the existing complexity reporting system", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Modify the existing complexity report to include ICE scores. Ensure consistent formatting between complexity and ICE reports. Add cross-referencing between reports. Update the command-line help documentation. Test the integrated system with various project sizes and configurations.", "status": "pending" } @@ -2841,9 +2500,7 @@ "id": 2, "title": "Implement State Management for Task Expansion", "description": "Develop the state management logic to handle expanded task states, subtask creation, and context additions.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create state handlers for expanded/collapsed states, subtask array management, and context data. Implement proper validation for user inputs and error handling. Ensure state persistence across user sessions and synchronization with backend services.", "status": "pending" }, @@ -2867,10 +2524,7 @@ "id": 5, "title": "Integrate with Existing Task Systems", "description": "Ensure the enhanced actions card workflow integrates seamlessly with existing task management functionality.", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Connect the new UI components to existing backend APIs. Update data models if necessary to support new features. Ensure compatibility with existing task filters, search, and reporting features. Implement data migration plan for existing tasks if needed.", "status": "pending" }, @@ -2906,9 +2560,7 @@ "id": 2, "title": "Extract prompts into individual files", "description": "Identify all hardcoded prompts in the codebase and extract them into individual files in the prompts directory", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Search through the codebase for all hardcoded prompt strings. For each prompt, create a new file in the appropriate subdirectory with a descriptive name (e.g., 'taskBreakdownPrompt.js'). Format each file to export the prompt string as a constant. Add JSDoc comments to document the purpose and expected usage of each prompt.", "status": "pending" }, @@ -2916,9 +2568,7 @@ "id": 3, "title": "Update functions to import prompts", "description": "Modify all functions that use hardcoded prompts to import them from the centralized structure", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "For each function that previously used a hardcoded prompt, add an import statement to pull in the prompt from the centralized structure. Test each function after modification to ensure it still works correctly. Update any tests that might be affected by the refactoring. Create a pull request with the changes and document the new prompt structure in the project documentation.", "status": "pending" } @@ -2946,9 +2596,7 @@ "id": 2, "title": "Implement best practice verification", "description": "Build verification checks against established coding standards and best practices", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create a framework to compare code against established best practices for the specific language/framework. Include checks for naming conventions, function length, complexity metrics, comment coverage, and other industry-standard quality indicators.", "status": "pending" }, @@ -2956,9 +2604,7 @@ "id": 3, "title": "Develop AI integration for code analysis", "description": "Integrate AI capabilities to enhance code analysis and provide intelligent recommendations", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Connect to AI services (like OpenAI) to analyze code beyond rule-based checks. Configure the AI to understand context, project-specific patterns, and provide nuanced analysis that rule-based systems might miss.", "status": "pending" }, @@ -2966,9 +2612,7 @@ "id": 4, "title": "Create recommendation generation system", "description": "Build a system to generate actionable improvement recommendations based on analysis results", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Develop algorithms to transform analysis results into specific, actionable recommendations. Include priority levels, effort estimates, and potential impact assessments for each recommendation.", "status": "pending" }, @@ -2976,9 +2620,7 @@ "id": 5, "title": "Implement task creation functionality", "description": "Add capability to automatically create tasks from code quality recommendations", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Build functionality to convert recommendations into tasks in the project management system. Include appropriate metadata, assignee suggestions based on code ownership, and integration with existing workflow systems.", "status": "pending" }, @@ -2986,9 +2628,7 @@ "id": 6, "title": "Create comprehensive reporting interface", "description": "Develop a user interface to display analysis results and recommendations", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Build a dashboard showing code quality metrics, identified patterns, recommendations, and created tasks. Include filtering options, trend analysis over time, and the ability to drill down into specific issues with code snippets and explanations.", "status": "pending" } @@ -3017,9 +2657,7 @@ "id": 2, "title": "Develop coverage report parser and adapter system", "description": "Create a framework-agnostic system that can parse coverage reports from various testing tools and convert them to the standardized task-based format in tests.json.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "1. Research and document output formats for major coverage tools (Istanbul/nyc, Jest, Pytest, JaCoCo).\n2. Design a normalized intermediate coverage format that any test tool can map to.\n3. Implement adapter classes for each major testing framework that convert their reports to the intermediate format.\n4. Create a parser registry that can automatically detect and use the appropriate parser based on input format.\n5. Develop a mapping algorithm that associates coverage data with specific tasks based on file paths and code blocks.\n6. Implement file path normalization to handle different operating systems and environments.\n7. Add error handling for malformed or incomplete coverage reports.\n8. Create unit tests for each adapter using sample coverage reports.\n9. Implement a command-line interface for manual parsing and testing.\n10. Document the extension points for adding custom coverage tool adapters.", "status": "pending", "parentTaskId": 50 @@ -3028,9 +2666,7 @@ "id": 3, "title": "Build coverage tracking and update generator", "description": "Create a system that processes code coverage reports, maps them to tasks, and updates the tests.json file to maintain accurate coverage tracking over time.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "1. Implement a coverage processor that takes parsed coverage data and maps it to task IDs.\n2. Create algorithms to calculate aggregate coverage metrics at the task and subtask levels.\n3. Develop a change detection system that identifies when tests or code have changed and require updates.\n4. Implement incremental update logic to avoid reprocessing unchanged tests.\n5. Create a task-code association system that maps specific code blocks to tasks for granular tracking.\n6. Add historical tracking to monitor coverage trends over time.\n7. Implement hooks for CI/CD integration to automatically update coverage after test runs.\n8. Create a conflict resolution strategy for when multiple tests cover the same code areas.\n9. Add performance optimizations for large codebases and test suites.\n10. Develop unit tests that verify correct aggregation and mapping of coverage data.\n11. Document the update workflow with sequence diagrams and examples.", "status": "pending", "parentTaskId": 50 @@ -3039,10 +2675,7 @@ "id": 4, "title": "Implement CLI commands for coverage operations", "description": "Create a set of command-line interface tools that allow developers to view, analyze, and manage test coverage at the task level.", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "1. Design a cohesive CLI command structure with subcommands for different coverage operations.\n2. Implement 'coverage show' command to display test coverage for a specific task/subtask.\n3. Create 'coverage gaps' command to identify untested code related to a particular task.\n4. Develop 'coverage history' command to show how coverage has changed over time.\n5. Implement 'coverage generate' command that uses LLMs to suggest tests for uncovered code.\n6. Add filtering options to focus on specific test types or coverage thresholds.\n7. Create formatted output options (JSON, CSV, markdown tables) for integration with other tools.\n8. Implement colorized terminal output for better readability of coverage reports.\n9. Add batch processing capabilities for running operations across multiple tasks.\n10. Create comprehensive help documentation and examples for each command.\n11. Develop unit and integration tests for CLI commands.\n12. Document command usage patterns and example workflows.", "status": "pending", "parentTaskId": 50 @@ -3051,11 +2684,7 @@ "id": 5, "title": "Develop AI-powered test generation system", "description": "Create an intelligent system that uses LLMs to generate targeted tests for uncovered code sections within tasks, integrating with the existing task management workflow.", - "dependencies": [ - 1, - 3, - 4 - ], + "dependencies": [1, 3, 4], "details": "1. Design prompt templates for different test types (unit, integration, E2E) that incorporate task descriptions and code context.\n2. Implement code analysis to extract relevant context from uncovered code sections.\n3. Create a test generation pipeline that combines task metadata, code context, and coverage gaps.\n4. Develop strategies for maintaining test context across task changes and updates.\n5. Implement test quality evaluation to ensure generated tests are meaningful and effective.\n6. Create a feedback mechanism to improve prompts based on acceptance or rejection of generated tests.\n7. Add support for different testing frameworks and languages through templating.\n8. Implement caching to avoid regenerating similar tests.\n9. Create a workflow that integrates with the task management system to suggest tests alongside implementation requirements.\n10. Develop specialized generation modes for edge cases, regression tests, and performance tests.\n11. Add configuration options for controlling test generation style and coverage goals.\n12. Create comprehensive documentation on how to use and extend the test generation system.\n13. Implement evaluation metrics to track the effectiveness of AI-generated tests.", "status": "pending", "parentTaskId": 50 @@ -3094,9 +2723,7 @@ "id": 3, "title": "Build Research Command CLI Interface", "description": "Implement the Commander.js command structure for the 'research' command with all required options and parameters.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implementation details:\n1. Create a new command file `commands/research.js`\n2. Set up the Commander.js command structure with the following options:\n - Required search query parameter\n - `--task` or `-t` option for task/subtask ID\n - `--prompt` or `-p` option for custom research prompt\n - `--save` or `-s` option to save results to a file\n - `--copy` or `-c` option to copy results to clipboard\n - `--summary` or `-m` option to generate a summary\n - `--detail` or `-d` option to set research depth (default: medium)\n3. Implement command validation logic\n4. Connect the command to the Perplexity service created in subtask 1\n5. Integrate the context extraction logic from subtask 2\n6. Register the command in the main CLI application\n7. Add help text and examples\n\nTesting approach:\n- Test command registration and option parsing\n- Verify command validation logic works correctly\n- Test with various combinations of options\n- Ensure proper error messages for invalid inputs\n<info added on 2025-05-23T21:09:08.478Z>\nImplementation details:\n1. Create a new module `repl/research-chat.js` for the interactive research experience\n2. Implement REPL-style chat interface using inquirer with:\n - Persistent conversation history management\n - Context-aware prompting system\n - Command parsing for special instructions\n3. Implement REPL commands:\n - `/save` - Save conversation to file\n - `/task` - Associate with or load context from a task\n - `/help` - Show available commands and usage\n - `/exit` - End the research session\n - `/copy` - Copy last response to clipboard\n - `/summary` - Generate summary of conversation\n - `/detail` - Adjust research depth level\n4. Create context initialization system:\n - Task/subtask context loading\n - File content integration\n - System prompt configuration\n5. Integrate with ai-services-unified.js research mode\n6. Implement conversation state management:\n - Track message history\n - Maintain context window\n - Handle context pruning for long conversations\n7. Design consistent UI patterns using ui.js library\n8. Add entry point in main CLI application\n\nTesting approach:\n- Test REPL command parsing and execution\n- Verify context initialization with various inputs\n- Test conversation state management\n- Ensure proper error handling and recovery\n- Validate UI consistency across different terminal environments\n</info added on 2025-05-23T21:09:08.478Z>", "status": "pending", "parentTaskId": 51 @@ -3105,10 +2732,7 @@ "id": 4, "title": "Implement Results Processing and Output Formatting", "description": "Create functionality to process, format, and display research results in the terminal with options for saving, copying, and summarizing.", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "Implementation details:\n1. Create a new module `utils/researchFormatter.js`\n2. Implement terminal output formatting with:\n - Color-coded sections for better readability\n - Proper text wrapping for terminal width\n - Highlighting of key points\n3. Add functionality to save results to a file:\n - Create a `research-results` directory if it doesn't exist\n - Save results with timestamp and query in filename\n - Support multiple formats (text, markdown, JSON)\n4. Implement clipboard copying using a library like `clipboardy`\n5. Create a summarization function that extracts key points from research results\n6. Add progress indicators during API calls\n7. Implement pagination for long results\n\nTesting approach:\n- Test output formatting with various result lengths and content types\n- Verify file saving functionality creates proper files with correct content\n- Test clipboard functionality\n- Verify summarization produces useful results\n<info added on 2025-05-23T21:10:00.181Z>\nImplementation details:\n1. Create a new module `utils/chatFormatter.js` for REPL interface formatting\n2. Implement terminal output formatting for conversational display:\n - Color-coded messages distinguishing user inputs and AI responses\n - Proper text wrapping and indentation for readability\n - Support for markdown rendering in terminal\n - Visual indicators for system messages and status updates\n3. Implement streaming/progressive display of AI responses:\n - Character-by-character or chunk-by-chunk display\n - Cursor animations during response generation\n - Ability to interrupt long responses\n4. Design chat history visualization:\n - Scrollable history with clear message boundaries\n - Timestamp display options\n - Session identification\n5. Create specialized formatters for different content types:\n - Code blocks with syntax highlighting\n - Bulleted and numbered lists\n - Tables and structured data\n - Citations and references\n6. Implement export functionality:\n - Save conversations to markdown or text files\n - Export individual responses\n - Copy responses to clipboard\n7. Adapt existing ui.js patterns for conversational context:\n - Maintain consistent styling while supporting chat flow\n - Handle multi-turn context appropriately\n\nTesting approach:\n- Test streaming display with various response lengths and speeds\n- Verify markdown rendering accuracy for complex formatting\n- Test history navigation and scrolling functionality\n- Verify export features create properly formatted files\n- Test display on various terminal sizes and configurations\n- Verify handling of special characters and unicode\n</info added on 2025-05-23T21:10:00.181Z>", "status": "pending", "parentTaskId": 51 @@ -3117,10 +2741,7 @@ "id": 5, "title": "Implement Caching and Results Management System", "description": "Create a persistent caching system for research results and implement functionality to manage, retrieve, and reference previous research.", - "dependencies": [ - 1, - 4 - ], + "dependencies": [1, 4], "details": "Implementation details:\n1. Create a research results database using a simple JSON file or SQLite:\n - Store queries, timestamps, and results\n - Index by query and related task IDs\n2. Implement cache retrieval and validation:\n - Check for cached results before making API calls\n - Validate cache freshness with configurable TTL\n3. Add commands to manage research history:\n - List recent research queries\n - Retrieve past research by ID or search term\n - Clear cache or delete specific entries\n4. Create functionality to associate research results with tasks:\n - Add metadata linking research to specific tasks\n - Implement command to show all research related to a task\n5. Add configuration options for cache behavior in user settings\n6. Implement export/import functionality for research data\n\nTesting approach:\n- Test cache storage and retrieval with various queries\n- Verify cache invalidation works correctly\n- Test history management commands\n- Verify task association functionality\n- Test with large cache sizes to ensure performance\n<info added on 2025-05-23T21:10:28.544Z>\nImplementation details:\n1. Create a session management system for the REPL experience:\n - Generate and track unique session IDs\n - Store conversation history with timestamps\n - Maintain context and state between interactions\n2. Implement session persistence:\n - Save sessions to disk automatically\n - Load previous sessions on startup\n - Handle graceful recovery from crashes\n3. Build session browser and selector:\n - List available sessions with preview\n - Filter sessions by date, topic, or content\n - Enable quick switching between sessions\n4. Implement conversation state serialization:\n - Capture full conversation context\n - Preserve user preferences per session\n - Handle state migration during updates\n5. Add session sharing capabilities:\n - Export sessions to portable formats\n - Import sessions from files\n - Generate shareable links (if applicable)\n6. Create session management commands:\n - Create new sessions\n - Clone existing sessions\n - Archive or delete old sessions\n\nTesting approach:\n- Verify session persistence across application restarts\n- Test session recovery from simulated crashes\n- Validate state serialization with complex conversations\n- Ensure session switching maintains proper context\n- Test session import/export functionality\n- Verify performance with large conversation histories\n</info added on 2025-05-23T21:10:28.544Z>", "status": "cancelled", "parentTaskId": 51 @@ -3138,9 +2759,7 @@ "id": 7, "title": "Create REPL Command System", "description": "Implement a flexible command system for the research REPL that allows users to control the conversation flow, manage sessions, and access additional functionality.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Implementation details:\n1. Create a new module `repl/commands.js` for REPL command handling\n2. Implement a command parser that:\n - Detects commands starting with `/`\n - Parses arguments and options\n - Handles quoted strings and special characters\n3. Create a command registry system:\n - Register command handlers with descriptions\n - Support command aliases\n - Enable command discovery and help\n4. Implement core commands:\n - `/save [filename]` - Save conversation\n - `/task <taskId>` - Load task context\n - `/file <path>` - Include file content\n - `/help [command]` - Show help\n - `/exit` - End session\n - `/copy [n]` - Copy nth response\n - `/summary` - Generate conversation summary\n - `/detail <level>` - Set detail level\n - `/clear` - Clear conversation\n - `/project` - Refresh project context\n - `/session <id|new>` - Switch/create session\n5. Add command completion and suggestions\n6. Implement error handling for invalid commands\n7. Create a help system with examples\n\nTesting approach:\n- Test command parsing with various inputs\n- Verify command execution and error handling\n- Test command completion functionality\n- Verify help system provides useful information\n- Test with complex command sequences", "status": "pending", "parentTaskId": 51 @@ -3149,10 +2768,7 @@ "id": 8, "title": "Integrate with AI Services Unified", "description": "Integrate the research REPL with the existing ai-services-unified.js to leverage the unified AI service architecture with research mode.", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Implementation details:\n1. Update `repl/research-chat.js` to integrate with ai-services-unified.js\n2. Configure research mode in AI service:\n - Set appropriate system prompts\n - Configure temperature and other parameters\n - Enable streaming responses\n3. Implement context management:\n - Format conversation history for AI context\n - Include task and project context\n - Handle context window limitations\n4. Add support for different research styles:\n - Exploratory research with broader context\n - Focused research with specific questions\n - Comparative analysis between concepts\n5. Implement response handling:\n - Process streaming chunks\n - Format and display responses\n - Handle errors and retries\n6. Add configuration options for AI service selection\n7. Implement fallback mechanisms for service unavailability\n\nTesting approach:\n- Test integration with mocked AI services\n- Verify context formatting and management\n- Test streaming response handling\n- Verify error handling and recovery\n- Test with various research styles and queries", "status": "pending", "parentTaskId": 51 @@ -3181,9 +2797,7 @@ "id": 2, "title": "Implement AI integration for task suggestions", "description": "Develop the core functionality to generate task suggestions using AI based on existing tasks", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create an AI prompt template that effectively communicates the existing task context and request for suggestions. Implement error handling for API failures, rate limiting, and malformed responses. Include parameters for controlling suggestion quantity and specificity.", "status": "pending" }, @@ -3199,9 +2813,7 @@ "id": 4, "title": "Implement suggestion selection and task creation", "description": "Allow users to interactively select suggestions to convert into actual tasks", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Create an interactive selection interface where users can review suggestions, select which ones to create as tasks, and optionally modify them before creation. Implement batch creation capabilities and validation to ensure new tasks meet system requirements.", "status": "pending" }, @@ -3209,10 +2821,7 @@ "id": 5, "title": "Add configuration options and flag handling", "description": "Implement various configuration options and command flags for customizing suggestion behavior", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Create a comprehensive set of command flags for controlling suggestion quantity, specificity, format, and other parameters. Implement persistent configuration options that users can set as defaults. Document all available options and provide examples of common usage patterns.", "status": "pending" } @@ -3240,9 +2849,7 @@ "id": 2, "title": "Build context gathering mechanism", "description": "Develop a system to collect relevant context from parent task and existing subtasks", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create functions to extract information from the parent task including title, description, and metadata. Also gather information about any existing subtasks to provide context for AI suggestions. Format this data appropriately for the AI prompt.", "status": "pending" }, @@ -3258,9 +2865,7 @@ "id": 4, "title": "Create interactive CLI interface", "description": "Build a user-friendly command-line interface for the subtask suggestion feature", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Develop CLI commands and options for requesting subtask suggestions. Include interactive elements for selecting, modifying, or rejecting suggested subtasks. Ensure clear user feedback throughout the process.", "status": "pending" }, @@ -3268,9 +2873,7 @@ "id": 5, "title": "Implement subtask linking functionality", "description": "Create system to properly link suggested subtasks to their parent task", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Develop the database operations to save accepted subtasks and link them to the parent task. Include functionality for setting dependencies between subtasks. Ensure proper transaction handling to maintain data integrity.", "status": "pending" }, @@ -3316,9 +2919,7 @@ "id": 2, "title": "Design positional argument specification format", "description": "Create a specification for how positional arguments will be defined in command definitions, including their order, required/optional status, and type validation.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Define a clear syntax for specifying positional arguments in command definitions. Consider how to handle mixed positional and named arguments, default values, and type constraints. Document the specification with examples for different command types.", "status": "pending" }, @@ -3326,9 +2927,7 @@ "id": 3, "title": "Implement core positional argument parsing logic", "description": "Modify the argument parser to recognize and process positional arguments according to the specification, while maintaining compatibility with existing named arguments.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Update the parser to identify arguments without flags as positional, map them to the correct parameter based on order, and apply appropriate validation. Ensure the implementation handles missing required positional arguments and provides helpful error messages.", "status": "pending" }, @@ -3336,9 +2935,7 @@ "id": 4, "title": "Handle edge cases and error conditions", "description": "Implement robust handling for edge cases such as too many/few arguments, type mismatches, and ambiguous situations between positional and named arguments.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Create comprehensive error handling for scenarios like: providing both positional and named version of the same argument, incorrect argument types, missing required positional arguments, and excess positional arguments. Ensure error messages are clear and actionable for users.", "status": "pending" }, @@ -3346,10 +2943,7 @@ "id": 5, "title": "Update documentation and create usage examples", "description": "Update CLI documentation to explain positional argument support and provide clear examples showing how to use positional arguments with different commands.", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Revise user documentation to include positional argument syntax, update command reference with positional argument information, and create example command snippets showing both positional and named argument usage. Include a migration guide for users transitioning from named-only to positional arguments.", "status": "pending" } @@ -3419,9 +3013,7 @@ "id": 6, "title": "Create Help System and Interactive Documentation", "description": "Develop an in-CLI help system with examples and contextual assistance", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Implement a comprehensive help command with examples for each feature. Add contextual help that suggests relevant commands based on user actions. Create interactive tutorials for new users. Include command auto-completion suggestions and syntax highlighting for command examples.", "status": "pending" } @@ -3459,9 +3051,7 @@ "id": 2, "title": "Remove Manual Dependency Modifications", "description": "Eliminate any custom scripts or manual steps that alter dependencies outside of npm's standard workflow.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Refactor or delete code that manually installs, updates, or modifies dependencies, ensuring all dependency management is handled via npm.", "status": "done" }, @@ -3477,9 +3067,7 @@ "id": 4, "title": "Update Initialization and Installation Commands", "description": "Revise project setup scripts and documentation to reflect the new npm-based dependency management approach.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Ensure that all initialization commands (e.g., npm install) are up-to-date and remove references to deprecated manual steps.", "status": "done" }, @@ -3487,9 +3075,7 @@ "id": 5, "title": "Update Documentation", "description": "Revise project documentation to describe the new dependency management process and provide clear setup instructions.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Update README, onboarding guides, and any developer documentation to align with npm best practices.", "status": "done" }, @@ -3525,9 +3111,7 @@ "id": 2, "title": "Implement Mentor Profile Management", "description": "Develop the functionality for creating, editing, and managing mentor profiles in the system.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Build UI components for mentor profile creation and editing. Implement backend APIs for profile CRUD operations. Create expertise tagging system and availability calendar. Add profile verification and approval workflows for quality control.", "status": "pending" }, @@ -3535,9 +3119,7 @@ "id": 3, "title": "Develop Round-Table Discussion Framework", "description": "Create the core framework for hosting and managing round-table discussions between mentors and users.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Design the discussion room data model and state management. Implement discussion scheduling and participant management. Create discussion topic and agenda setting functionality. Develop discussion moderation tools and rules enforcement mechanisms.", "status": "pending" }, @@ -3545,9 +3127,7 @@ "id": 4, "title": "Implement LLM Integration for AI Mentors", "description": "Integrate LLM capabilities to simulate AI mentors that can participate in round-table discussions.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Select appropriate LLM models for mentor simulation. Develop prompt engineering templates for different mentor personas and expertise areas. Implement context management to maintain conversation coherence. Create fallback mechanisms for handling edge cases in discussions.", "status": "pending" }, @@ -3555,10 +3135,7 @@ "id": 5, "title": "Build Discussion Output Formatter", "description": "Create a system to format and present round-table discussion outputs in a structured, readable format.", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Design templates for discussion summaries and transcripts. Implement real-time formatting of ongoing discussions. Create exportable formats for discussion outcomes (PDF, markdown, etc.). Develop highlighting and annotation features for key insights.", "status": "pending" }, @@ -3566,9 +3143,7 @@ "id": 6, "title": "Integrate Mentor System with Task Management", "description": "Connect the mentor system with the existing task management functionality to enable task-specific mentoring.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Create APIs to link tasks with relevant mentors based on expertise. Implement functionality to initiate discussions around specific tasks. Develop mechanisms for mentors to provide feedback and guidance on tasks. Build notification system for task-related mentor interactions.", "status": "pending" }, @@ -3576,9 +3151,7 @@ "id": 7, "title": "Test and Optimize Round-Table Discussions", "description": "Conduct comprehensive testing of the round-table discussion feature and optimize for performance and user experience.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Perform load testing with multiple concurrent discussions. Test AI mentor responses for quality and relevance. Optimize LLM usage for cost efficiency. Conduct user testing sessions and gather feedback. Implement performance monitoring and analytics for ongoing optimization.", "status": "pending" } @@ -3607,9 +3180,7 @@ "id": 2, "title": "Implement CLI Command Parser for Model Management", "description": "Extend the CLI command parser to handle the new 'models' command and associated flags for model management.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "1. Update the CLI command parser to recognize the 'models' command\n2. Add support for '--set-main' and '--set-research' flags\n3. Implement validation for command arguments\n4. Create help text and usage examples for the models command\n5. Add error handling for invalid command usage\n6. Connect CLI parser to the configuration manager\n7. Implement command output formatting for model listings\n8. Testing approach: Create integration tests that verify CLI commands correctly interact with the configuration manager", "status": "done", "parentTaskId": 61 @@ -3618,9 +3189,7 @@ "id": 3, "title": "Integrate Vercel AI SDK and Create Client Factory", "description": "Set up Vercel AI SDK integration and implement a client factory pattern to create and manage AI model clients.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "1. Install Vercel AI SDK: `npm install @vercel/ai`\n2. Create an `ai-client-factory.js` module that implements the Factory pattern\n3. Define client creation functions for each supported model (Claude, OpenAI, Ollama, Gemini, OpenRouter, Perplexity, Grok)\n4. Implement error handling for missing API keys or configuration issues\n5. Add caching mechanism to reuse existing clients\n6. Create a unified interface for all clients regardless of the underlying model\n7. Implement client validation to ensure proper initialization\n8. Testing approach: Mock API responses to test client creation and error handling\n\n<info added on 2025-04-14T23:02:30.519Z>\nHere's additional information for the client factory implementation:\n\nFor the client factory implementation:\n\n1. Structure the factory with a modular approach:\n```javascript\n// ai-client-factory.js\nimport { createOpenAI } from '@ai-sdk/openai';\nimport { createAnthropic } from '@ai-sdk/anthropic';\nimport { createGoogle } from '@ai-sdk/google';\nimport { createPerplexity } from '@ai-sdk/perplexity';\n\nconst clientCache = new Map();\n\nexport function createClientInstance(providerName, options = {}) {\n // Implementation details below\n}\n```\n\n2. For OpenAI-compatible providers (Ollama), implement specific configuration:\n```javascript\ncase 'ollama':\n const ollamaBaseUrl = process.env.OLLAMA_BASE_URL || 'http://localhost:11434';\n return createOpenAI({\n baseURL: ollamaBaseUrl,\n apiKey: 'ollama', // Ollama doesn't require a real API key\n ...options\n });\n```\n\n3. Add provider-specific model mapping:\n```javascript\n// Model mapping helper\nconst getModelForProvider = (provider, requestedModel) => {\n const modelMappings = {\n openai: {\n default: 'gpt-3.5-turbo',\n // Add other mappings\n },\n anthropic: {\n default: 'claude-3-opus-20240229',\n // Add other mappings\n },\n // Add mappings for other providers\n };\n \n return (modelMappings[provider] && modelMappings[provider][requestedModel]) \n || modelMappings[provider]?.default \n || requestedModel;\n};\n```\n\n4. Implement caching with provider+model as key:\n```javascript\nexport function getClient(providerName, model) {\n const cacheKey = `${providerName}:${model || 'default'}`;\n \n if (clientCache.has(cacheKey)) {\n return clientCache.get(cacheKey);\n }\n \n const modelName = getModelForProvider(providerName, model);\n const client = createClientInstance(providerName, { model: modelName });\n clientCache.set(cacheKey, client);\n \n return client;\n}\n```\n\n5. Add detailed environment variable validation:\n```javascript\nfunction validateEnvironment(provider) {\n const requirements = {\n openai: ['OPENAI_API_KEY'],\n anthropic: ['ANTHROPIC_API_KEY'],\n google: ['GOOGLE_API_KEY'],\n perplexity: ['PERPLEXITY_API_KEY'],\n openrouter: ['OPENROUTER_API_KEY'],\n ollama: ['OLLAMA_BASE_URL'],\n xai: ['XAI_API_KEY']\n };\n \n const missing = requirements[provider]?.filter(env => !process.env[env]) || [];\n \n if (missing.length > 0) {\n throw new Error(`Missing environment variables for ${provider}: ${missing.join(', ')}`);\n }\n}\n```\n\n6. Add Jest test examples:\n```javascript\n// ai-client-factory.test.js\ndescribe('AI Client Factory', () => {\n beforeEach(() => {\n // Mock environment variables\n process.env.OPENAI_API_KEY = 'test-openai-key';\n process.env.ANTHROPIC_API_KEY = 'test-anthropic-key';\n // Add other mocks\n });\n \n test('creates OpenAI client with correct configuration', () => {\n const client = getClient('openai');\n expect(client).toBeDefined();\n // Add assertions for client configuration\n });\n \n test('throws error when environment variables are missing', () => {\n delete process.env.OPENAI_API_KEY;\n expect(() => getClient('openai')).toThrow(/Missing environment variables/);\n });\n \n // Add tests for other providers\n});\n```\n</info added on 2025-04-14T23:02:30.519Z>", "status": "done", "parentTaskId": 61 @@ -3629,9 +3198,7 @@ "id": 4, "title": "Develop Centralized AI Services Module", "description": "Create a centralized AI services module that abstracts all AI interactions through a unified interface, using the Decorator pattern for adding functionality like logging and retries.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "1. Create `ai-services.js` module to consolidate all AI model interactions\n2. Implement wrapper functions for text generation and streaming\n3. Add retry mechanisms for handling API rate limits and transient errors\n4. Implement logging for all AI interactions for observability\n5. Create model-specific adapters to normalize responses across different providers\n6. Add caching layer for frequently used responses to optimize performance\n7. Implement graceful fallback mechanisms when primary models fail\n8. Testing approach: Create unit tests with mocked responses to verify service behavior\n\n<info added on 2025-04-19T23:51:22.219Z>\nBased on the exploration findings, here's additional information for the AI services module refactoring:\n\nThe existing `ai-services.js` should be refactored to:\n\n1. Leverage the `ai-client-factory.js` for model instantiation while providing a higher-level service abstraction\n2. Implement a layered architecture:\n - Base service layer handling common functionality (retries, logging, caching)\n - Model-specific service implementations extending the base\n - Facade pattern to provide a unified API for all consumers\n\n3. Integration points:\n - Replace direct OpenAI client usage with factory-provided clients\n - Maintain backward compatibility with existing service consumers\n - Add service registration mechanism for new AI providers\n\n4. Performance considerations:\n - Implement request batching for high-volume operations\n - Add request priority queuing for critical vs non-critical operations\n - Implement circuit breaker pattern to prevent cascading failures\n\n5. Monitoring enhancements:\n - Add detailed telemetry for response times, token usage, and costs\n - Implement standardized error classification for better diagnostics\n\n6. Implementation sequence:\n - Start with abstract base service class\n - Refactor existing OpenAI implementations\n - Add adapter layer for new providers\n - Implement the unified facade\n</info added on 2025-04-19T23:51:22.219Z>", "status": "done", "parentTaskId": 61 @@ -3640,10 +3207,7 @@ "id": 5, "title": "Implement Environment Variable Management", "description": "Update environment variable handling to support multiple AI models and create documentation for configuration options.", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "1. Update `.env.example` with all required API keys for supported models\n2. Implement environment variable validation on startup\n3. Create clear error messages for missing or invalid environment variables\n4. Add support for model-specific configuration options\n5. Document all environment variables and their purposes\n6. Implement a check to ensure required API keys are present for selected models\n7. Add support for optional configuration parameters for each model\n8. Testing approach: Create tests that verify environment variable validation logic", "status": "done", "parentTaskId": 61 @@ -3652,10 +3216,7 @@ "id": 6, "title": "Implement Model Listing Command", "description": "Implement the 'task-master models' command to display currently configured models and available options.", - "dependencies": [ - 1, - 4 - ], + "dependencies": [1, 4], "details": "1. Create handler for the models command without flags\n2. Implement formatted output showing current model configuration\n3. Add color-coding for better readability using a library like chalk\n4. Include version information for each configured model\n5. Show API status indicators (connected/disconnected)\n6. Display usage examples for changing models\n7. Add support for verbose output with additional details\n8. Testing approach: Create integration tests that verify correct output formatting and content", "status": "done", "parentTaskId": 61 @@ -3664,10 +3225,7 @@ "id": 7, "title": "Implement Model Setting Commands", "description": "Implement the commands to set main and research models with proper validation and feedback.", - "dependencies": [ - 1, - 4 - ], + "dependencies": [1, 4], "details": "1. Create handlers for '--set-main' and '--set-research' flags\n2. Implement validation logic for model names\n3. Add clear error messages for invalid model selections\n4. Implement confirmation messages for successful model changes\n5. Add support for setting both models in a single command\n6. Implement dry-run option to validate without making changes\n7. Add verbose output option for debugging\n8. Testing approach: Create integration tests that verify model setting functionality with various inputs", "status": "done", "parentTaskId": 61 @@ -3676,10 +3234,7 @@ "id": 8, "title": "Update Main Task Processing Logic", "description": "Refactor the main task processing logic to use the new AI services module and support dynamic model selection.", - "dependencies": [ - 4, - "61.18" - ], + "dependencies": [4, "61.18"], "details": "1. Update task processing functions to use the centralized AI services\n2. Implement dynamic model selection based on configuration\n3. Add error handling for model-specific failures\n4. Implement graceful degradation when preferred models are unavailable\n5. Update prompts to be model-agnostic where possible\n6. Add telemetry for model performance monitoring\n7. Implement response validation to ensure quality across different models\n8. Testing approach: Create integration tests that verify task processing with different model configurations\n\n<info added on 2025-04-20T03:55:56.310Z>\nWhen updating the main task processing logic, implement the following changes to align with the new configuration system:\n\n1. Replace direct environment variable access with calls to the configuration manager:\n ```javascript\n // Before\n const apiKey = process.env.OPENAI_API_KEY;\n const modelId = process.env.MAIN_MODEL || \"gpt-4\";\n \n // After\n import { getMainProvider, getMainModelId, getMainMaxTokens, getMainTemperature } from './config-manager.js';\n \n const provider = getMainProvider();\n const modelId = getMainModelId();\n const maxTokens = getMainMaxTokens();\n const temperature = getMainTemperature();\n ```\n\n2. Implement model fallback logic using the configuration hierarchy:\n ```javascript\n async function processTaskWithFallback(task) {\n try {\n return await processWithModel(task, getMainModelId());\n } catch (error) {\n logger.warn(`Primary model failed: ${error.message}`);\n const fallbackModel = getMainFallbackModelId();\n if (fallbackModel) {\n return await processWithModel(task, fallbackModel);\n }\n throw error;\n }\n }\n ```\n\n3. Add configuration-aware telemetry points to track model usage and performance:\n ```javascript\n function trackModelPerformance(modelId, startTime, success) {\n const duration = Date.now() - startTime;\n telemetry.trackEvent('model_usage', {\n modelId,\n provider: getMainProvider(),\n duration,\n success,\n configVersion: getConfigVersion()\n });\n }\n ```\n\n4. Ensure all prompt templates are loaded through the configuration system rather than hardcoded:\n ```javascript\n const promptTemplate = getPromptTemplate('task_processing');\n const prompt = formatPrompt(promptTemplate, { task: taskData });\n ```\n</info added on 2025-04-20T03:55:56.310Z>", "status": "done", "parentTaskId": 61 @@ -3688,10 +3243,7 @@ "id": 9, "title": "Update Research Processing Logic", "description": "Refactor the research processing logic to use the new AI services module and support dynamic model selection for research operations.", - "dependencies": [ - 4, - "61.18" - ], + "dependencies": [4, "61.18"], "details": "1. Update research functions to use the centralized AI services\n2. Implement dynamic model selection for research operations\n3. Add specialized error handling for research-specific issues\n4. Optimize prompts for research-focused models\n5. Implement result caching for research operations\n6. Add support for model-specific research parameters\n7. Create fallback mechanisms for research operations\n8. Testing approach: Create integration tests that verify research functionality with different model configurations\n\n<info added on 2025-04-20T03:55:39.633Z>\nWhen implementing the refactored research processing logic, ensure the following:\n\n1. Replace direct environment variable access with the new configuration system:\n ```javascript\n // Old approach\n const apiKey = process.env.OPENAI_API_KEY;\n const model = \"gpt-4\";\n \n // New approach\n import { getResearchProvider, getResearchModelId, getResearchMaxTokens, \n getResearchTemperature } from './config-manager.js';\n \n const provider = getResearchProvider();\n const modelId = getResearchModelId();\n const maxTokens = getResearchMaxTokens();\n const temperature = getResearchTemperature();\n ```\n\n2. Implement model fallback chains using the configuration system:\n ```javascript\n async function performResearch(query) {\n try {\n return await callAIService({\n provider: getResearchProvider(),\n modelId: getResearchModelId(),\n maxTokens: getResearchMaxTokens(),\n temperature: getResearchTemperature()\n });\n } catch (error) {\n logger.warn(`Primary research model failed: ${error.message}`);\n return await callAIService({\n provider: getResearchProvider('fallback'),\n modelId: getResearchModelId('fallback'),\n maxTokens: getResearchMaxTokens('fallback'),\n temperature: getResearchTemperature('fallback')\n });\n }\n }\n ```\n\n3. Add support for dynamic parameter adjustment based on research type:\n ```javascript\n function getResearchParameters(researchType) {\n // Get base parameters\n const baseParams = {\n provider: getResearchProvider(),\n modelId: getResearchModelId(),\n maxTokens: getResearchMaxTokens(),\n temperature: getResearchTemperature()\n };\n \n // Adjust based on research type\n switch(researchType) {\n case 'deep':\n return {...baseParams, maxTokens: baseParams.maxTokens * 1.5};\n case 'creative':\n return {...baseParams, temperature: Math.min(baseParams.temperature + 0.2, 1.0)};\n case 'factual':\n return {...baseParams, temperature: Math.max(baseParams.temperature - 0.2, 0)};\n default:\n return baseParams;\n }\n }\n ```\n\n4. Ensure the caching mechanism uses configuration-based TTL settings:\n ```javascript\n const researchCache = new Cache({\n ttl: getResearchCacheTTL(),\n maxSize: getResearchCacheMaxSize()\n });\n ```\n</info added on 2025-04-20T03:55:39.633Z>", "status": "done", "parentTaskId": 61 @@ -3700,9 +3252,7 @@ "id": 10, "title": "Create Comprehensive Documentation and Examples", "description": "Develop comprehensive documentation for the new model management features, including examples, troubleshooting guides, and best practices.", - "dependencies": [ - 7 - ], + "dependencies": [7], "details": "1. Update README.md with new model management commands\n2. Create usage examples for all supported models\n3. Document environment variable requirements for each model\n4. Create troubleshooting guide for common issues\n5. Add performance considerations and best practices\n6. Document API key acquisition process for each supported service\n7. Create comparison chart of model capabilities and limitations\n8. Testing approach: Conduct user testing with the documentation to ensure clarity and completeness\n\n<info added on 2025-04-20T03:55:20.433Z>\n## Documentation Update for Configuration System Refactoring\n\n### Configuration System Architecture\n- Document the separation between environment variables and configuration file:\n - API keys: Sourced exclusively from environment variables (process.env or session.env)\n - All other settings: Centralized in `.taskmasterconfig` JSON file\n\n### `.taskmasterconfig` Structure\n```json\n{\n \"models\": {\n \"completion\": \"gpt-3.5-turbo\",\n \"chat\": \"gpt-4\",\n \"embedding\": \"text-embedding-ada-002\"\n },\n \"parameters\": {\n \"temperature\": 0.7,\n \"maxTokens\": 2000,\n \"topP\": 1\n },\n \"logging\": {\n \"enabled\": true,\n \"level\": \"info\"\n },\n \"defaults\": {\n \"outputFormat\": \"markdown\"\n }\n}\n```\n\n### Configuration Access Patterns\n- Document the getter functions in `config-manager.js`:\n - `getModelForRole(role)`: Returns configured model for a specific role\n - `getParameter(name)`: Retrieves model parameters\n - `getLoggingConfig()`: Access logging settings\n - Example usage: `const completionModel = getModelForRole('completion')`\n\n### Environment Variable Resolution\n- Explain the `resolveEnvVariable(key)` function:\n - Checks both process.env and session.env\n - Prioritizes session variables over process variables\n - Returns null if variable not found\n\n### Configuration Precedence\n- Document the order of precedence:\n 1. Command-line arguments (highest priority)\n 2. Session environment variables\n 3. Process environment variables\n 4. `.taskmasterconfig` settings\n 5. Hardcoded defaults (lowest priority)\n\n### Migration Guide\n- Steps for users to migrate from previous configuration approach\n- How to verify configuration is correctly loaded\n</info added on 2025-04-20T03:55:20.433Z>", "status": "done", "parentTaskId": 61 @@ -3713,9 +3263,7 @@ "description": "Update PRD processing logic (callClaude, processClaudeResponse, handleStreamingRequest in ai-services.js) to use the new `generateObjectService` from `ai-services-unified.js` with an appropriate Zod schema.", "details": "\n\n<info added on 2025-04-20T03:55:01.707Z>\nThe PRD parsing refactoring should align with the new configuration system architecture. When implementing this change:\n\n1. Replace direct environment variable access with `resolveEnvVariable` calls for API keys.\n\n2. Remove any hardcoded model names or parameters in the PRD processing functions. Instead, use the config-manager.js getters:\n - `getModelForRole('prd')` to determine the appropriate model\n - `getModelParameters('prd')` to retrieve temperature, maxTokens, etc.\n\n3. When constructing the generateObjectService call, ensure parameters are sourced from config:\n```javascript\nconst modelConfig = getModelParameters('prd');\nconst model = getModelForRole('prd');\n\nconst result = await generateObjectService({\n model,\n temperature: modelConfig.temperature,\n maxTokens: modelConfig.maxTokens,\n // other parameters as needed\n schema: prdSchema,\n // existing prompt/context parameters\n});\n```\n\n4. Update any logging to respect the logging configuration from config-manager (e.g., `isLoggingEnabled('ai')`)\n\n5. Ensure any default values previously hardcoded are now retrieved from the configuration system.\n</info added on 2025-04-20T03:55:01.707Z>", "status": "done", - "dependencies": [ - "61.23" - ], + "dependencies": ["61.23"], "parentTaskId": 61 }, { @@ -3724,9 +3272,7 @@ "description": "Update the `generateSubtasks` function in `ai-services.js` to use the new `generateObjectService` from `ai-services-unified.js` with a Zod schema for the subtask array.", "details": "\n\n<info added on 2025-04-20T03:54:45.542Z>\nThe refactoring should leverage the new configuration system:\n\n1. Replace direct model references with calls to config-manager.js getters:\n ```javascript\n const { getModelForRole, getModelParams } = require('./config-manager');\n \n // Instead of hardcoded models/parameters:\n const model = getModelForRole('subtask-generator');\n const modelParams = getModelParams('subtask-generator');\n ```\n\n2. Update API key handling to use the resolveEnvVariable pattern:\n ```javascript\n const { resolveEnvVariable } = require('./utils');\n const apiKey = resolveEnvVariable('OPENAI_API_KEY');\n ```\n\n3. When calling generateObjectService, pass the configuration parameters:\n ```javascript\n const result = await generateObjectService({\n schema: subtasksArraySchema,\n prompt: subtaskPrompt,\n model: model,\n temperature: modelParams.temperature,\n maxTokens: modelParams.maxTokens,\n // Other parameters from config\n });\n ```\n\n4. Add error handling that respects logging configuration:\n ```javascript\n const { isLoggingEnabled } = require('./config-manager');\n \n try {\n // Generation code\n } catch (error) {\n if (isLoggingEnabled('errors')) {\n console.error('Subtask generation error:', error);\n }\n throw error;\n }\n ```\n</info added on 2025-04-20T03:54:45.542Z>", "status": "done", - "dependencies": [ - "61.23" - ], + "dependencies": ["61.23"], "parentTaskId": 61 }, { @@ -3735,9 +3281,7 @@ "description": "Update the `generateSubtasksWithPerplexity` function in `ai-services.js` to first perform research (potentially keeping the Perplexity call separate or adapting it) and then use `generateObjectService` from `ai-services-unified.js` with research results included in the prompt.", "details": "\n\n<info added on 2025-04-20T03:54:26.882Z>\nThe refactoring should align with the new configuration system by:\n\n1. Replace direct environment variable access with `resolveEnvVariable` for API keys\n2. Use the config-manager.js getters to retrieve model parameters:\n - Replace hardcoded model names with `getModelForRole('research')`\n - Use `getParametersForRole('research')` to get temperature, maxTokens, etc.\n3. Implement proper error handling that respects the `getLoggingConfig()` settings\n4. Example implementation pattern:\n```javascript\nconst { getModelForRole, getParametersForRole, getLoggingConfig } = require('./config-manager');\nconst { resolveEnvVariable } = require('./environment-utils');\n\n// In the refactored function:\nconst researchModel = getModelForRole('research');\nconst { temperature, maxTokens } = getParametersForRole('research');\nconst apiKey = resolveEnvVariable('PERPLEXITY_API_KEY');\nconst { verbose } = getLoggingConfig();\n\n// Then use these variables in the API call configuration\n```\n5. Ensure the transition to generateObjectService maintains all existing functionality while leveraging the new configuration system\n</info added on 2025-04-20T03:54:26.882Z>", "status": "done", - "dependencies": [ - "61.23" - ], + "dependencies": ["61.23"], "parentTaskId": 61 }, { @@ -3746,9 +3290,7 @@ "description": "Update the `generateTaskDescriptionWithPerplexity` function in `ai-services.js` to first perform research and then use `generateObjectService` from `ai-services-unified.js` to generate the structured task description.", "details": "\n\n<info added on 2025-04-20T03:54:04.420Z>\nThe refactoring should incorporate the new configuration management system:\n\n1. Update imports to include the config-manager:\n```javascript\nconst { getModelForRole, getParametersForRole } = require('./config-manager');\n```\n\n2. Replace any hardcoded model selections or parameters with config-manager calls:\n```javascript\n// Replace direct model references like:\n// const model = \"perplexity-model-7b-online\" \n// With:\nconst model = getModelForRole('research');\nconst parameters = getParametersForRole('research');\n```\n\n3. For API key handling, use the resolveEnvVariable pattern:\n```javascript\nconst apiKey = resolveEnvVariable('PERPLEXITY_API_KEY');\n```\n\n4. When calling generateObjectService, pass the configuration-derived parameters:\n```javascript\nreturn generateObjectService({\n prompt: researchResults,\n schema: taskDescriptionSchema,\n role: 'taskDescription',\n // Config-driven parameters will be applied within generateObjectService\n});\n```\n\n5. Remove any hardcoded configuration values, ensuring all settings are retrieved from the centralized configuration system.\n</info added on 2025-04-20T03:54:04.420Z>", "status": "done", - "dependencies": [ - "61.23" - ], + "dependencies": ["61.23"], "parentTaskId": 61 }, { @@ -3757,9 +3299,7 @@ "description": "Update the logic that calls the AI after using `generateComplexityAnalysisPrompt` in `ai-services.js` to use the new `generateObjectService` from `ai-services-unified.js` with a Zod schema for the complexity report.", "details": "\n\n<info added on 2025-04-20T03:53:46.120Z>\nThe complexity analysis AI call should be updated to align with the new configuration system architecture. When refactoring to use `generateObjectService`, implement the following changes:\n\n1. Replace direct model references with calls to the appropriate config getter:\n ```javascript\n const modelName = getComplexityAnalysisModel(); // Use the specific getter from config-manager.js\n ```\n\n2. Retrieve AI parameters from the config system:\n ```javascript\n const temperature = getAITemperature('complexityAnalysis');\n const maxTokens = getAIMaxTokens('complexityAnalysis');\n ```\n\n3. When constructing the call to `generateObjectService`, pass these configuration values:\n ```javascript\n const result = await generateObjectService({\n prompt,\n schema: complexityReportSchema,\n modelName,\n temperature,\n maxTokens,\n sessionEnv: session?.env\n });\n ```\n\n4. Ensure API key resolution uses the `resolveEnvVariable` helper:\n ```javascript\n // Don't hardcode API keys or directly access process.env\n // The generateObjectService should handle this internally with resolveEnvVariable\n ```\n\n5. Add logging configuration based on settings:\n ```javascript\n const enableLogging = getAILoggingEnabled('complexityAnalysis');\n if (enableLogging) {\n // Use the logging mechanism defined in the configuration\n }\n ```\n</info added on 2025-04-20T03:53:46.120Z>", "status": "done", - "dependencies": [ - "61.23" - ], + "dependencies": ["61.23"], "parentTaskId": 61 }, { @@ -3768,9 +3308,7 @@ "description": "Update the logic that calls the AI after using `_buildAddTaskPrompt` in `ai-services.js` to use the new `generateObjectService` from `ai-services-unified.js` with a Zod schema for the single task object.", "details": "\n\n<info added on 2025-04-20T03:53:27.455Z>\nTo implement this refactoring, you'll need to:\n\n1. Replace direct AI calls with the new `generateObjectService` approach:\n ```javascript\n // OLD approach\n const aiResponse = await callLLM(prompt, modelName, temperature, maxTokens);\n const task = parseAIResponseToTask(aiResponse);\n \n // NEW approach using generateObjectService with config-manager\n import { generateObjectService } from '../services/ai-services-unified.js';\n import { getAIModelForRole, getAITemperature, getAIMaxTokens } from '../config/config-manager.js';\n import { taskSchema } from '../schemas/task-schema.js'; // Create this Zod schema for a single task\n \n const modelName = getAIModelForRole('taskCreation');\n const temperature = getAITemperature('taskCreation');\n const maxTokens = getAIMaxTokens('taskCreation');\n \n const task = await generateObjectService({\n prompt: _buildAddTaskPrompt(...),\n schema: taskSchema,\n modelName,\n temperature,\n maxTokens\n });\n ```\n\n2. Create a Zod schema for the task object in a new file `schemas/task-schema.js` that defines the expected structure.\n\n3. Ensure API key resolution uses the new pattern:\n ```javascript\n // This happens inside generateObjectService, but verify it uses:\n import { resolveEnvVariable } from '../config/config-manager.js';\n // Instead of direct process.env access\n ```\n\n4. Update any error handling to match the new service's error patterns.\n</info added on 2025-04-20T03:53:27.455Z>", "status": "done", - "dependencies": [ - "61.23" - ], + "dependencies": ["61.23"], "parentTaskId": 61 }, { @@ -3779,9 +3317,7 @@ "description": "Refactor functions like `sendChatWithContext` (and potentially related task update functions in `task-manager.js` if they make direct AI calls) to use `streamTextService` or `generateTextService` from `ai-services-unified.js`.", "details": "\n\n<info added on 2025-04-20T03:53:03.709Z>\nWhen refactoring `sendChatWithContext` and related functions, ensure they align with the new configuration system:\n\n1. Replace direct model references with config getter calls:\n ```javascript\n // Before\n const model = \"gpt-4\";\n \n // After\n import { getModelForRole } from './config-manager.js';\n const model = getModelForRole('chat'); // or appropriate role\n ```\n\n2. Extract AI parameters from config rather than hardcoding:\n ```javascript\n import { getAIParameters } from './config-manager.js';\n const { temperature, maxTokens } = getAIParameters('chat');\n ```\n\n3. When calling `streamTextService` or `generateTextService`, pass parameters from config:\n ```javascript\n await streamTextService({\n messages,\n model: getModelForRole('chat'),\n temperature: getAIParameters('chat').temperature,\n // other parameters as needed\n });\n ```\n\n4. For logging control, check config settings:\n ```javascript\n import { isLoggingEnabled } from './config-manager.js';\n \n if (isLoggingEnabled('aiCalls')) {\n console.log('AI request:', messages);\n }\n ```\n\n5. Ensure any default behaviors respect configuration defaults rather than hardcoded values.\n</info added on 2025-04-20T03:53:03.709Z>", "status": "done", - "dependencies": [ - "61.23" - ], + "dependencies": ["61.23"], "parentTaskId": 61 }, { @@ -3799,9 +3335,7 @@ "description": "Refactor the AI call within `updateSubtaskById` in `task-manager.js` (which generates additional information based on a prompt) to use the appropriate unified service function (e.g., `generateTextService`) from `ai-services-unified.js`.", "details": "\n\n<info added on 2025-04-20T03:52:28.196Z>\nThe `updateSubtaskById` function currently makes direct AI calls with hardcoded parameters. When refactoring to use the unified service:\n\n1. Replace direct OpenAI calls with `generateTextService` from `ai-services-unified.js`\n2. Use configuration parameters from `config-manager.js`:\n - Replace hardcoded model with `getMainModel()`\n - Use `getMainMaxTokens()` for token limits\n - Apply `getMainTemperature()` for response randomness\n3. Ensure prompt construction remains consistent but passes these dynamic parameters\n4. Handle API key resolution through the unified service (which uses `resolveEnvVariable`)\n5. Update error handling to work with the unified service response format\n6. If the function uses any logging, ensure it respects `getLoggingEnabled()` setting\n\nExample refactoring pattern:\n```javascript\n// Before\nconst completion = await openai.chat.completions.create({\n model: \"gpt-4\",\n temperature: 0.7,\n max_tokens: 1000,\n messages: [/* prompt messages */]\n});\n\n// After\nconst completion = await generateTextService({\n model: getMainModel(),\n temperature: getMainTemperature(),\n max_tokens: getMainMaxTokens(),\n messages: [/* prompt messages */]\n});\n```\n</info added on 2025-04-20T03:52:28.196Z>\n\n<info added on 2025-04-22T06:05:42.437Z>\n- When testing the non-streaming `generateTextService` call within `updateSubtaskById`, ensure that the function awaits the full response before proceeding with subtask updates. This allows you to validate that the unified service returns the expected structure (e.g., `completion.choices.message.content`) and that error handling logic correctly interprets any error objects or status codes returned by the service.\n\n- Mock or stub the `generateTextService` in unit tests to simulate both successful and failed completions. For example, verify that when the service returns a valid completion, the subtask is updated with the generated content, and when an error is returned, the error handling path is triggered and logged appropriately.\n\n- Confirm that the non-streaming mode does not emit partial results or require event-based handling; the function should only process the final, complete response.\n\n- Example test assertion:\n ```javascript\n // Mocked response from generateTextService\n const mockCompletion = {\n choices: [{ message: { content: \"Generated subtask details.\" } }]\n };\n generateTextService.mockResolvedValue(mockCompletion);\n\n // Call updateSubtaskById and assert the subtask is updated\n await updateSubtaskById(...);\n expect(subtask.details).toBe(\"Generated subtask details.\");\n ```\n\n- If the unified service supports both streaming and non-streaming modes, explicitly set or verify the `stream` parameter is `false` (or omitted) to ensure non-streaming behavior during these tests.\n</info added on 2025-04-22T06:05:42.437Z>\n\n<info added on 2025-04-22T06:20:19.747Z>\nWhen testing the non-streaming `generateTextService` call in `updateSubtaskById`, implement these verification steps:\n\n1. Add unit tests that verify proper parameter transformation between the old and new implementation:\n ```javascript\n test('should correctly transform parameters when calling generateTextService', async () => {\n // Setup mocks for config values\n jest.spyOn(configManager, 'getMainModel').mockReturnValue('gpt-4');\n jest.spyOn(configManager, 'getMainTemperature').mockReturnValue(0.7);\n jest.spyOn(configManager, 'getMainMaxTokens').mockReturnValue(1000);\n \n const generateTextServiceSpy = jest.spyOn(aiServices, 'generateTextService')\n .mockResolvedValue({ choices: [{ message: { content: 'test content' } }] });\n \n await updateSubtaskById(/* params */);\n \n // Verify the service was called with correct transformed parameters\n expect(generateTextServiceSpy).toHaveBeenCalledWith({\n model: 'gpt-4',\n temperature: 0.7,\n max_tokens: 1000,\n messages: expect.any(Array)\n });\n });\n ```\n\n2. Implement response validation to ensure the subtask content is properly extracted:\n ```javascript\n // In updateSubtaskById function\n try {\n const completion = await generateTextService({\n // parameters\n });\n \n // Validate response structure before using\n if (!completion?.choices?.[0]?.message?.content) {\n throw new Error('Invalid response structure from AI service');\n }\n \n // Continue with updating subtask\n } catch (error) {\n // Enhanced error handling\n }\n ```\n\n3. Add integration tests that verify the end-to-end flow with actual configuration values.\n</info added on 2025-04-22T06:20:19.747Z>\n\n<info added on 2025-04-22T06:23:23.247Z>\n<info added on 2025-04-22T06:35:14.892Z>\nWhen testing the non-streaming `generateTextService` call in `updateSubtaskById`, implement these specific verification steps:\n\n1. Create a dedicated test fixture that isolates the AI service interaction:\n ```javascript\n describe('updateSubtaskById AI integration', () => {\n beforeEach(() => {\n // Reset all mocks and spies\n jest.clearAllMocks();\n // Setup environment with controlled config values\n process.env.OPENAI_API_KEY = 'test-key';\n });\n \n // Test cases follow...\n });\n ```\n\n2. Test error propagation from the unified service:\n ```javascript\n test('should properly handle AI service errors', async () => {\n const mockError = new Error('Service unavailable');\n mockError.status = 503;\n jest.spyOn(aiServices, 'generateTextService').mockRejectedValue(mockError);\n \n // Capture console errors if needed\n const consoleSpy = jest.spyOn(console, 'error').mockImplementation();\n \n // Execute with error expectation\n await expect(updateSubtaskById(1, { prompt: 'test' })).rejects.toThrow();\n \n // Verify error was logged with appropriate context\n expect(consoleSpy).toHaveBeenCalledWith(\n expect.stringContaining('AI service error'),\n expect.objectContaining({ status: 503 })\n );\n });\n ```\n\n3. Verify that the function correctly preserves existing subtask content when appending new AI-generated information:\n ```javascript\n test('should preserve existing content when appending AI-generated details', async () => {\n // Setup mock subtask with existing content\n const mockSubtask = {\n id: 1,\n details: 'Existing details.\\n\\n'\n };\n \n // Mock database retrieval\n getSubtaskById.mockResolvedValue(mockSubtask);\n \n // Mock AI response\n generateTextService.mockResolvedValue({\n choices: [{ message: { content: 'New AI content.' } }]\n });\n \n await updateSubtaskById(1, { prompt: 'Enhance this subtask' });\n \n // Verify the update preserves existing content\n expect(updateSubtaskInDb).toHaveBeenCalledWith(\n 1,\n expect.objectContaining({\n details: expect.stringContaining('Existing details.\\n\\n<info added on')\n })\n );\n \n // Verify the new content was added\n expect(updateSubtaskInDb).toHaveBeenCalledWith(\n 1,\n expect.objectContaining({\n details: expect.stringContaining('New AI content.')\n })\n );\n });\n ```\n\n4. Test that the function correctly formats the timestamp and wraps the AI-generated content:\n ```javascript\n test('should format timestamp and wrap content correctly', async () => {\n // Mock date for consistent testing\n const mockDate = new Date('2025-04-22T10:00:00Z');\n jest.spyOn(global, 'Date').mockImplementation(() => mockDate);\n \n // Setup and execute test\n // ...\n \n // Verify correct formatting\n expect(updateSubtaskInDb).toHaveBeenCalledWith(\n expect.any(Number),\n expect.objectContaining({\n details: expect.stringMatching(\n /<info added on 2025-04-22T10:00:00\\.000Z>\\n.*\\n<\\/info added on 2025-04-22T10:00:00\\.000Z>/s\n )\n })\n );\n });\n ```\n\n5. Verify that the function correctly handles the case when no existing details are present:\n ```javascript\n test('should handle subtasks with no existing details', async () => {\n // Setup mock subtask with no details\n const mockSubtask = { id: 1 };\n getSubtaskById.mockResolvedValue(mockSubtask);\n \n // Execute test\n // ...\n \n // Verify details were initialized properly\n expect(updateSubtaskInDb).toHaveBeenCalledWith(\n 1,\n expect.objectContaining({\n details: expect.stringMatching(/^<info added on/)\n })\n );\n });\n ```\n</info added on 2025-04-22T06:35:14.892Z>\n</info added on 2025-04-22T06:23:23.247Z>", "status": "done", - "dependencies": [ - "61.23" - ], + "dependencies": ["61.23"], "parentTaskId": 61 }, { @@ -3908,9 +3442,7 @@ "title": "Implement Integration Tests for Unified AI Service", "description": "Implement integration tests for `ai-services-unified.js`. These tests should verify the correct routing to different provider modules based on configuration and ensure the unified service functions (`generateTextService`, `generateObjectService`, etc.) work correctly when called from modules like `task-manager.js`. [Updated: 5/2/2025] [Updated: 5/2/2025] [Updated: 5/2/2025] [Updated: 5/2/2025]", "status": "done", - "dependencies": [ - "61.18" - ], + "dependencies": ["61.18"], "details": "\n\n<info added on 2025-04-20T03:51:23.368Z>\nFor the integration tests of the Unified AI Service, consider the following implementation details:\n\n1. Setup test fixtures:\n - Create a mock `.taskmasterconfig` file with different provider configurations\n - Define test cases with various model selections and parameter settings\n - Use environment variable mocks only for API keys (e.g., `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`)\n\n2. Test configuration resolution:\n - Verify that `ai-services-unified.js` correctly retrieves settings from `config-manager.js`\n - Test that model selection follows the hierarchy defined in `.taskmasterconfig`\n - Ensure fallback mechanisms work when primary providers are unavailable\n\n3. Mock the provider modules:\n ```javascript\n jest.mock('../services/openai-service.js');\n jest.mock('../services/anthropic-service.js');\n ```\n\n4. Test specific scenarios:\n - Provider selection based on configured preferences\n - Parameter inheritance from config (temperature, maxTokens)\n - Error handling when API keys are missing\n - Proper routing when specific models are requested\n\n5. Verify integration with task-manager:\n ```javascript\n test('task-manager correctly uses unified AI service with config-based settings', async () => {\n // Setup mock config with specific settings\n mockConfigManager.getAIProviderPreference.mockReturnValue(['openai', 'anthropic']);\n mockConfigManager.getModelForRole.mockReturnValue('gpt-4');\n mockConfigManager.getParametersForModel.mockReturnValue({ temperature: 0.7, maxTokens: 2000 });\n \n // Verify task-manager uses these settings when calling the unified service\n // ...\n });\n ```\n\n6. Include tests for configuration changes at runtime and their effect on service behavior.\n</info added on 2025-04-20T03:51:23.368Z>\n\n<info added on 2025-05-02T18:41:13.374Z>\n]\n{\n \"id\": 31,\n \"title\": \"Implement Integration Test for Unified AI Service\",\n \"description\": \"Implement integration tests for `ai-services-unified.js`. These tests should verify the correct routing to different provider module based on configuration and ensure the unified service function (`generateTextService`, `generateObjectService`, etc.) work correctly when called from module like `task-manager.js`.\",\n \"details\": \"\\n\\n<info added on 2025-04-20T03:51:23.368Z>\\nFor the integration test of the Unified AI Service, consider the following implementation details:\\n\\n1. Setup test fixture:\\n - Create a mock `.taskmasterconfig` file with different provider configuration\\n - Define test case with various model selection and parameter setting\\n - Use environment variable mock only for API key (e.g., `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`)\\n\\n2. Test configuration resolution:\\n - Verify that `ai-services-unified.js` correctly retrieve setting from `config-manager.js`\\n - Test that model selection follow the hierarchy defined in `.taskmasterconfig`\\n - Ensure fallback mechanism work when primary provider are unavailable\\n\\n3. Mock the provider module:\\n ```javascript\\n jest.mock('../service/openai-service.js');\\n jest.mock('../service/anthropic-service.js');\\n ```\\n\\n4. Test specific scenario:\\n - Provider selection based on configured preference\\n - Parameter inheritance from config (temperature, maxToken)\\n - Error handling when API key are missing\\n - Proper routing when specific model are requested\\n\\n5. Verify integration with task-manager:\\n ```javascript\\n test('task-manager correctly use unified AI service with config-based setting', async () => {\\n // Setup mock config with specific setting\\n mockConfigManager.getAIProviderPreference.mockReturnValue(['openai', 'anthropic']);\\n mockConfigManager.getModelForRole.mockReturnValue('gpt-4');\\n mockConfigManager.getParameterForModel.mockReturnValue({ temperature: 0.7, maxToken: 2000 });\\n \\n // Verify task-manager use these setting when calling the unified service\\n // ...\\n });\\n ```\\n\\n6. Include test for configuration change at runtime and their effect on service behavior.\\n</info added on 2025-04-20T03:51:23.368Z>\\n[2024-01-15 10:30:45] A custom e2e script was created to test all the CLI command but that we'll need one to test the MCP too and that task 76 are dedicated to that\",\n \"status\": \"pending\",\n \"dependency\": [\n \"61.18\"\n ],\n \"parentTaskId\": 61\n}\n</info added on 2025-05-02T18:41:13.374Z>\n[2023-11-24 20:05:45] It's my birthday today\n[2023-11-24 20:05:46] add more low level details\n[2023-11-24 20:06:45] Additional low-level details for integration tests:\n\n- Ensure that each test case logs detailed output for each step, including configuration retrieval, provider selection, and API call results.\n- Implement a utility function to reset mocks and configurations between tests to avoid state leakage.\n- Use a combination of spies and mocks to verify that internal methods are called with expected arguments, especially for critical functions like `generateTextService`.\n- Consider edge cases such as empty configurations, invalid API keys, and network failures to ensure robustness.\n- Document each test case with expected outcomes and any assumptions made during the test design.\n- Leverage parallel test execution where possible to reduce test suite runtime, ensuring that tests are independent and do not interfere with each other.\n<info added on 2025-05-02T20:42:14.388Z>\n<info added on 2025-04-20T03:51:23.368Z>\nFor the integration tests of the Unified AI Service, consider the following implementation details:\n\n1. Setup test fixtures:\n - Create a mock `.taskmasterconfig` file with different provider configurations\n - Define test cases with various model selections and parameter settings\n - Use environment variable mocks only for API keys (e.g., `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`)\n\n2. Test configuration resolution:\n - Verify that `ai-services-unified.js` correctly retrieves settings from `config-manager.js`\n - Test that model selection follows the hierarchy defined in `.taskmasterconfig`\n - Ensure fallback mechanisms work when primary providers are unavailable\n\n3. Mock the provider modules:\n ```javascript\n jest.mock('../services/openai-service.js');\n jest.mock('../services/anthropic-service.js');\n ```\n\n4. Test specific scenarios:\n - Provider selection based on configured preferences\n - Parameter inheritance from config (temperature, maxTokens)\n - Error handling when API keys are missing\n - Proper routing when specific models are requested\n\n5. Verify integration with task-manager:\n ```javascript\n test('task-manager correctly uses unified AI service with config-based settings', async () => {\n // Setup mock config with specific settings\n mockConfigManager.getAIProviderPreference.mockReturnValue(['openai', 'anthropic']);\n mockConfigManager.getModelForRole.mockReturnValue('gpt-4');\n mockConfigManager.getParametersForModel.mockReturnValue({ temperature: 0.7, maxTokens: 2000 });\n \n // Verify task-manager uses these settings when calling the unified service\n // ...\n });\n ```\n\n6. Include tests for configuration changes at runtime and their effect on service behavior.\n</info added on 2025-04-20T03:51:23.368Z>\n\n<info added on 2025-05-02T18:41:13.374Z>\n]\n{\n \"id\": 31,\n \"title\": \"Implement Integration Test for Unified AI Service\",\n \"description\": \"Implement integration tests for `ai-services-unified.js`. These tests should verify the correct routing to different provider module based on configuration and ensure the unified service function (`generateTextService`, `generateObjectService`, etc.) work correctly when called from module like `task-manager.js`.\",\n \"details\": \"\\n\\n<info added on 2025-04-20T03:51:23.368Z>\\nFor the integration test of the Unified AI Service, consider the following implementation details:\\n\\n1. Setup test fixture:\\n - Create a mock `.taskmasterconfig` file with different provider configuration\\n - Define test case with various model selection and parameter setting\\n - Use environment variable mock only for API key (e.g., `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`)\\n\\n2. Test configuration resolution:\\n - Verify that `ai-services-unified.js` correctly retrieve setting from `config-manager.js`\\n - Test that model selection follow the hierarchy defined in `.taskmasterconfig`\\n - Ensure fallback mechanism work when primary provider are unavailable\\n\\n3. Mock the provider module:\\n ```javascript\\n jest.mock('../service/openai-service.js');\\n jest.mock('../service/anthropic-service.js');\\n ```\\n\\n4. Test specific scenario:\\n - Provider selection based on configured preference\\n - Parameter inheritance from config (temperature, maxToken)\\n - Error handling when API key are missing\\n - Proper routing when specific model are requested\\n\\n5. Verify integration with task-manager:\\n ```javascript\\n test('task-manager correctly use unified AI service with config-based setting', async () => {\\n // Setup mock config with specific setting\\n mockConfigManager.getAIProviderPreference.mockReturnValue(['openai', 'anthropic']);\\n mockConfigManager.getModelForRole.mockReturnValue('gpt-4');\\n mockConfigManager.getParameterForModel.mockReturnValue({ temperature: 0.7, maxToken: 2000 });\\n \\n // Verify task-manager use these setting when calling the unified service\\n // ...\\n });\\n ```\\n\\n6. Include test for configuration change at runtime and their effect on service behavior.\\n</info added on 2025-04-20T03:51:23.368Z>\\n[2024-01-15 10:30:45] A custom e2e script was created to test all the CLI command but that we'll need one to test the MCP too and that task 76 are dedicated to that\",\n \"status\": \"pending\",\n \"dependency\": [\n \"61.18\"\n ],\n \"parentTaskId\": 61\n}\n</info added on 2025-05-02T18:41:13.374Z>\n[2023-11-24 20:05:45] It's my birthday today\n[2023-11-24 20:05:46] add more low level details\n[2023-11-24 20:06:45] Additional low-level details for integration tests:\n\n- Ensure that each test case logs detailed output for each step, including configuration retrieval, provider selection, and API call results.\n- Implement a utility function to reset mocks and configurations between tests to avoid state leakage.\n- Use a combination of spies and mocks to verify that internal methods are called with expected arguments, especially for critical functions like `generateTextService`.\n- Consider edge cases such as empty configurations, invalid API keys, and network failures to ensure robustness.\n- Document each test case with expected outcomes and any assumptions made during the test design.\n- Leverage parallel test execution where possible to reduce test suite runtime, ensuring that tests are independent and do not interfere with each other.\n\n<info added on 2023-11-24T20:10:00.000Z>\n- Implement detailed logging for each API call, capturing request and response data to facilitate debugging.\n- Create a comprehensive test matrix to cover all possible combinations of provider configurations and model selections.\n- Use snapshot testing to verify that the output of `generateTextService` and `generateObjectService` remains consistent across code changes.\n- Develop a set of utility functions to simulate network latency and failures, ensuring the service handles such scenarios gracefully.\n- Regularly review and update test cases to reflect changes in the configuration management or provider APIs.\n- Ensure that all test data is anonymized and does not contain sensitive information.\n</info added on 2023-11-24T20:10:00.000Z>\n</info added on 2025-05-02T20:42:14.388Z>" }, { @@ -3919,9 +3451,7 @@ "description": "Update relevant documentation files (e.g., `architecture.mdc`, `taskmaster.mdc`, environment variable guides, README) to accurately reflect the new AI service architecture using `ai-services-unified.js`, provider modules, the Vercel AI SDK, and the updated configuration approach.", "details": "\n\n<info added on 2025-04-20T03:51:04.461Z>\nThe new AI architecture introduces a clear separation between sensitive credentials and configuration settings:\n\n## Environment Variables vs Configuration File\n\n- **Environment Variables (.env)**: \n - Store only sensitive API keys and credentials\n - Accessed via `resolveEnvVariable()` which checks both process.env and session.env\n - Example: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`\n - No model names, parameters, or non-sensitive settings should be here\n\n- **.taskmasterconfig File**:\n - Central location for all non-sensitive configuration\n - Structured JSON with clear sections for different aspects of the system\n - Contains:\n - Model mappings by role (e.g., `systemModels`, `userModels`)\n - Default parameters (temperature, maxTokens, etc.)\n - Logging preferences\n - Provider-specific settings\n - Accessed via getter functions from `config-manager.js` like:\n ```javascript\n import { getModelForRole, getDefaultTemperature } from './config-manager.js';\n \n // Usage examples\n const model = getModelForRole('system');\n const temp = getDefaultTemperature();\n ```\n\n## Implementation Notes\n- Document the structure of `.taskmasterconfig` with examples\n- Explain the migration path for users with existing setups\n- Include a troubleshooting section for common configuration issues\n- Add a configuration validation section explaining how the system verifies settings\n</info added on 2025-04-20T03:51:04.461Z>", "status": "done", - "dependencies": [ - "61.31" - ], + "dependencies": ["61.31"], "parentTaskId": 61 }, { @@ -3930,10 +3460,7 @@ "description": "After all other migration subtasks (refactoring, provider implementation, testing, documentation) are complete and verified, remove the old `ai-services.js` and `ai-client-factory.js` files from the `scripts/modules/` directory. Ensure no code still references them.", "details": "\n\n<info added on 2025-04-22T06:51:02.444Z>\nI'll provide additional technical information to enhance the \"Cleanup Old AI Service Files\" subtask:\n\n## Implementation Details\n\n**Pre-Cleanup Verification Steps:**\n- Run a comprehensive codebase search for any remaining imports or references to `ai-services.js` and `ai-client-factory.js` using grep or your IDE's search functionality[1][4]\n- Check for any dynamic imports that might not be caught by static analysis tools\n- Verify that all dependent modules have been properly migrated to the new AI service architecture\n\n**Cleanup Process:**\n- Create a backup of the files before deletion in case rollback is needed\n- Document the file removal in the migration changelog with timestamps and specific file paths[5]\n- Update any build configuration files that might reference these files (webpack configs, etc.)\n- Run a full test suite after removal to ensure no runtime errors occur[2]\n\n**Post-Cleanup Validation:**\n- Implement automated tests to verify the application functions correctly without the removed files\n- Monitor application logs and error reporting systems for 48-72 hours after deployment to catch any missed dependencies[3]\n- Perform a final code review to ensure clean architecture principles are maintained in the new implementation\n\n**Technical Considerations:**\n- Check for any circular dependencies that might have been created during the migration process\n- Ensure proper garbage collection by removing any cached instances of the old services\n- Verify that performance metrics remain stable after the removal of legacy code\n</info added on 2025-04-22T06:51:02.444Z>", "status": "done", - "dependencies": [ - "61.31", - "61.32" - ], + "dependencies": ["61.31", "61.32"], "parentTaskId": 61 }, { @@ -4069,9 +3596,7 @@ "id": 2, "title": "Implement conditional logic to bypass AI processing", "description": "Modify the update logic to check for the --simple flag and conditionally skip the AI processing pipeline when the flag is present.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "In the update handlers for both commands, add a condition to check if the --simple flag is set. If it is, create a path that bypasses the normal AI processing flow. This will require modifying the update functions to accept the flag parameter and branch the execution flow accordingly.", "status": "pending", "testStrategy": "Test that when the --simple flag is provided, the AI processing functions are not called, and when the flag is not provided, the normal AI processing flow is maintained." @@ -4089,9 +3614,7 @@ "id": 4, "title": "Add visual indicator for manual updates", "description": "Make simple updates visually distinguishable from AI-processed updates by adding a 'manual update' indicator or other visual differentiation.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Modify the update formatting to include a visual indicator (such as '[Manual Update]' prefix or different styling) when displaying updates that were created using the --simple flag. This will help users distinguish between AI-processed and manually entered updates.", "status": "pending", "testStrategy": "Check that updates made with the --simple flag are visually distinct from AI-processed updates when displayed in the task or subtask history." @@ -4100,10 +3623,7 @@ "id": 5, "title": "Implement storage of simple updates in history", "description": "Ensure that updates made with the --simple flag are properly saved to the task or subtask's history in the same way as AI-processed updates.", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Modify the storage logic to save the formatted simple updates to the task or subtask history. The storage format should be consistent with AI-processed updates, but include the manual indicator. Ensure that the update is properly associated with the correct task or subtask.", "status": "pending", "testStrategy": "Test that updates made with the --simple flag are correctly saved to the history and persist between application restarts." @@ -4112,9 +3632,7 @@ "id": 6, "title": "Update help documentation for the new flag", "description": "Update the help documentation for both update-task and update-subtask commands to include information about the new --simple flag.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Add clear descriptions of the --simple flag to the help text for both commands. The documentation should explain that the flag allows users to add timestamped notes without AI processing, directly using the text from the prompt. Include examples of how to use the flag.", "status": "pending", "testStrategy": "Verify that the help command correctly displays information about the --simple flag for both update commands." @@ -4123,11 +3641,7 @@ "id": 7, "title": "Implement integration tests for the simple update feature", "description": "Create comprehensive integration tests to verify that the --simple flag works correctly in both commands and integrates properly with the rest of the system.", - "dependencies": [ - 1, - 3, - 4 - ], + "dependencies": [1, 3, 4], "details": "Develop integration tests that verify the entire flow of using the --simple flag with both update commands. Tests should confirm that updates are correctly formatted, stored, and displayed. Include edge cases such as empty input, very long input, and special characters.", "status": "pending", "testStrategy": "Run integration tests that simulate user input with and without the --simple flag and verify the correct behavior in each case." @@ -4136,12 +3650,7 @@ "id": 8, "title": "Perform final validation and documentation", "description": "Conduct final validation of the feature across all use cases and update the user documentation to include the new functionality.", - "dependencies": [ - 1, - 3, - 4, - 7 - ], + "dependencies": [1, 3, 4, 7], "details": "Perform end-to-end testing of the feature to ensure it works correctly in all scenarios. Update the user documentation with detailed information about the new --simple flag, including its purpose, how to use it, and examples. Ensure that the documentation clearly explains the difference between AI-processed updates and simple updates.", "status": "pending", "testStrategy": "Manually test all use cases and review documentation for completeness and clarity." @@ -4171,9 +3680,7 @@ "id": 2, "title": "Ensure Package Scripts Compatibility with pnpm", "description": "Review and update package.json scripts to ensure they work seamlessly with pnpm's execution model. Confirm that any scripts responsible for showing a website or prompt during install behave identically with pnpm and npm. Ensure compatibility with 'module' package type and correct binary definitions.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Test all scripts using `pnpm run <script>`, address any pnpm-specific path or execution differences, and modify scripts as needed for compatibility. Pay special attention to any scripts that trigger a website or prompt during installation, ensuring they serve the same content as npm. Validate that scripts/init.js and binaries are referenced correctly for ESM ('module') projects.", "status": "done", "testStrategy": "Run all package scripts using pnpm and confirm expected behavior matches npm, especially for any website or UI shown during install. Validate correct execution of scripts/init.js and binary linking." @@ -4191,9 +3698,7 @@ "id": 4, "title": "Test Taskmaster Installation and Operation with pnpm", "description": "Thoroughly test Taskmaster's installation and CLI operation when installed via pnpm, both globally and locally. Confirm that any website or UI shown during installation is identical to npm. Validate that binaries and the init process (scripts/init.js) work as expected.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Perform global (`pnpm add -g taskmaster`) and local installations, verify CLI commands, and check for any pnpm-specific issues or incompatibilities. Ensure any installation UIs or websites appear identical to npm installations, including any website or prompt shown during install. Test that binaries 'task-master' and 'task-master-mcp' are linked and that scripts/init.js creates the correct structure and templates.", "status": "done", "testStrategy": "Document and resolve any errors encountered during installation or usage with pnpm. Compare the installation experience side-by-side with npm, including any website or UI shown during install. Validate directory and template setup as per scripts/init.js." @@ -4202,9 +3707,7 @@ "id": 5, "title": "Integrate pnpm into CI/CD Pipeline", "description": "Update CI/CD workflows to include pnpm in the test matrix, ensuring all tests pass when dependencies are installed with pnpm. Confirm that tests cover the 'module' package type, binaries, and init process.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Modify GitHub Actions or other CI configurations to use pnpm/action-setup, run tests with pnpm, and cache pnpm dependencies for efficiency. Ensure that CI covers CLI commands, binary linking, and the directory/template setup performed by scripts/init.js.", "status": "done", "testStrategy": "Confirm that CI passes for all supported package managers, including pnpm, and that pnpm-specific jobs are green. Validate that tests cover ESM usage, binaries, and init.js flows." @@ -4213,9 +3716,7 @@ "id": 6, "title": "Verify Installation UI/Website Consistency", "description": "Ensure any installation UIs, websites, or interactive prompts—including any website or prompt shown during install—appear and function identically when installing with pnpm compared to npm. Confirm that the experience is consistent for the 'module' package type and the init process.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Identify all user-facing elements during the installation process, including any website or prompt shown during install, and verify they are consistent across package managers. If a website is shown during installation, ensure it appears the same regardless of package manager used. Validate that any prompts or UIs triggered by scripts/init.js are identical.", "status": "done", "testStrategy": "Perform side-by-side installations with npm and pnpm, capturing screenshots of any UIs or websites for comparison. Test all interactive elements to ensure identical behavior, including any website or prompt shown during install and those from scripts/init.js." @@ -4224,9 +3725,7 @@ "id": 7, "title": "Test init.js Script with pnpm", "description": "Verify that the scripts/init.js file works correctly when Taskmaster is installed via pnpm, creating the proper directory structure and copying all required templates as defined in the project structure.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Test the init command to ensure it properly creates .cursor/rules, scripts, and tasks directories, copies templates (.env.example, .gitignore, rule files, dev.js), handles package.json merging, and sets up MCP config (.cursor/mcp.json) as per scripts/init.js.", "status": "done", "testStrategy": "Run the init command after installing with pnpm and verify all directories and files are created correctly. Compare the results with an npm installation to ensure identical behavior and structure." @@ -4235,9 +3734,7 @@ "id": 8, "title": "Verify Binary Links with pnpm", "description": "Ensure that the task-master and task-master-mcp binaries are properly defined in package.json, linked, and executable when installed via pnpm, in both global and local installations.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Check that the binaries defined in package.json are correctly linked in node_modules/.bin when installed with pnpm, and that they can be executed without errors. Validate that binaries work for ESM ('module') projects and are accessible after both global and local installs.", "status": "done", "testStrategy": "Install Taskmaster with pnpm and verify that the binaries are accessible and executable. Test both global and local installations, ensuring correct behavior for ESM projects." @@ -4267,9 +3764,7 @@ "id": 2, "title": "Add Yarn-Specific Configuration Files", "description": "Introduce Yarn-specific configuration files such as .yarnrc.yml if needed to optimize Yarn behavior and ensure consistent installs for 'module' package type and binary definitions.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Determine if Yarn v2+ (Berry) or classic requires additional configuration for the project, and add or update .yarnrc.yml or .yarnrc files accordingly. Ensure configuration supports ESM and binary linking.", "status": "done", "testStrategy": "Verify that Yarn respects the configuration by running installs and checking for expected behaviors (e.g., plug'n'play, nodeLinker settings, ESM support, binary linking)." @@ -4287,9 +3782,7 @@ "id": 4, "title": "Update Documentation for Yarn Installation and Usage", "description": "Revise installation and usage documentation to include clear instructions for installing and managing Taskmaster with Yarn. Clearly state that the installation process, including any website or UI shown, is identical to npm. Ensure documentation reflects the use of 'module' package type, binaries, and the init process as defined in scripts/init.js. If the installation process includes a website component or requires account setup, document the steps users must follow. If not, explicitly state that no website or account setup is required.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Add Yarn-specific installation commands, troubleshooting tips, and notes on version compatibility to the README and any relevant docs. Document that any installation website or prompt is the same as with npm. Include notes on the 'module' package type, binaries, and the directory/template setup performed by scripts/init.js. If website or account setup is required during installation, provide clear instructions; otherwise, confirm and document that no such steps are needed.", "status": "done", "testStrategy": "Review documentation for accuracy and clarity; have a user follow the Yarn instructions to verify successful installation and usage. Confirm that documentation explicitly states the identical experience for npm and Yarn, including any website or UI shown during install, and describes the init process and binaries. If website/account setup is required, verify that instructions are complete and accurate; if not, confirm this is documented." @@ -4298,9 +3791,7 @@ "id": 5, "title": "Implement and Test Package Manager Detection Logic", "description": "Update or add logic in the codebase to detect Yarn installations and handle Yarn-specific behaviors, ensuring feature parity across package managers. Ensure detection logic works for 'module' package type and binary definitions.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Modify detection logic to recognize Yarn (classic and berry), handle lockfile generation, and resolve any Yarn-specific package resolution or hoisting issues. Ensure detection logic supports ESM and binary linking.", "status": "done", "testStrategy": "Install Taskmaster using npm, pnpm, and Yarn (classic and berry), verifying that the application detects the package manager correctly and behaves consistently for ESM projects and binaries." @@ -4309,9 +3800,7 @@ "id": 6, "title": "Verify Installation UI/Website Consistency", "description": "Ensure any installation UIs, websites, or interactive prompts—including any website or prompt shown during install—appear and function identically when installing with Yarn compared to npm. Confirm that the experience is consistent for the 'module' package type and the init process. If the installation process includes a website or account setup, verify that all required website actions (e.g., account creation, login) are consistent and documented. If not, confirm and document that no website or account setup is needed.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Identify all user-facing elements during the installation process, including any website or prompt shown during install, and verify they are consistent across package managers. If a website is shown during installation or account setup is required, ensure it appears and functions the same regardless of package manager used, and document the steps. If not, confirm and document that no website or account setup is needed. Validate that any prompts or UIs triggered by scripts/init.js are identical.", "status": "done", "testStrategy": "Perform side-by-side installations with npm and Yarn, capturing screenshots of any UIs or websites for comparison. Test all interactive elements to ensure identical behavior, including any website or prompt shown during install and those from scripts/init.js. If website/account setup is required, verify and document the steps; if not, confirm this is documented." @@ -4320,9 +3809,7 @@ "id": 7, "title": "Test init.js Script with Yarn", "description": "Verify that the scripts/init.js file works correctly when Taskmaster is installed via Yarn, creating the proper directory structure and copying all required templates as defined in the project structure.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Test the init command to ensure it properly creates .cursor/rules, scripts, and tasks directories, copies templates (.env.example, .gitignore, rule files, dev.js), handles package.json merging, and sets up MCP config (.cursor/mcp.json) as per scripts/init.js.", "status": "done", "testStrategy": "Run the init command after installing with Yarn and verify all directories and files are created correctly. Compare the results with an npm installation to ensure identical behavior and structure." @@ -4331,9 +3818,7 @@ "id": 8, "title": "Verify Binary Links with Yarn", "description": "Ensure that the task-master and task-master-mcp binaries are properly defined in package.json, linked, and executable when installed via Yarn, in both global and local installations.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Check that the binaries defined in package.json are correctly linked in node_modules/.bin when installed with Yarn, and that they can be executed without errors. Validate that binaries work for ESM ('module') projects and are accessible after both global and local installs.", "status": "done", "testStrategy": "Install Taskmaster with Yarn and verify that the binaries are accessible and executable. Test both global and local installations, ensuring correct behavior for ESM projects." @@ -4371,9 +3856,7 @@ "id": 2, "title": "Update installation scripts for Bun compatibility", "description": "Modify the existing installation scripts to detect and support Bun as a runtime environment.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Add Bun detection logic to installation scripts. Update package management commands to use Bun equivalents where needed. Ensure all dependencies are compatible with Bun. Modify any Node.js-specific code to work with Bun's runtime.", "status": "done" }, @@ -4389,9 +3872,7 @@ "id": 4, "title": "Test Taskmaster installation with Bun", "description": "Perform comprehensive testing of the installation process using Bun across different operating systems.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Test installation on Windows, macOS, and Linux using Bun. Verify that all Taskmaster features work correctly when installed via Bun. Document any issues encountered and implement fixes as needed.", "status": "done" }, @@ -4399,9 +3880,7 @@ "id": 5, "title": "Test Taskmaster operation with Bun", "description": "Ensure all Taskmaster functionality works correctly when running under Bun.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Test all Taskmaster commands and features when running with Bun. Compare performance metrics between Node.js and Bun. Identify and fix any runtime issues specific to Bun. Ensure all plugins and extensions are compatible.", "status": "done" }, @@ -4409,9 +3888,7 @@ "id": 6, "title": "Update documentation for Bun support", "description": "Update all relevant documentation to include information about installing and running Taskmaster with Bun.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Add Bun installation instructions to README and documentation. Document any Bun-specific considerations or limitations. Update troubleshooting guides to include Bun-specific issues. Create examples showing Bun usage with Taskmaster.", "status": "done" } @@ -4451,9 +3928,7 @@ "id": 2, "title": "Extend JSON Output to All Relevant Commands and Ensure Schema Consistency", "description": "Apply the JSON output pattern established in subtask 1 to all other relevant Taskmaster CLI commands that display data (e.g., `list`, `status`, etc.). Ensure the JSON structure is consistent where applicable (e.g., task objects should have the same fields). Add help text mentioning the `--json` flag for each modified command.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "1. Create a JSON schema definition file at `src/schemas/task.json` to define the standard structure for task objects\n2. Modify the following command files to support the `--json` flag:\n - `src/commands/list.js`\n - `src/commands/status.js`\n - `src/commands/search.js`\n - `src/commands/summary.js`\n3. Refactor the `formatAsJson` utility to handle different data types (single task, task array, status object, etc.)\n4. Add a `validateJsonSchema` function in `src/utils/validation.js` to ensure output conforms to defined schemas\n5. Update each command's help text documentation to include the `--json` flag description\n6. Implement consistent error handling for JSON output (using a standard error object format)\n7. For list-type commands, ensure array outputs are properly formatted as JSON arrays", "status": "pending", "testStrategy": "1. Create unit tests for each modified command in their respective test files\n2. Test each command with the `--json` flag and validate output against the defined schemas\n3. Create specific test cases for edge conditions (empty lists, error states, etc.)\n4. Verify help text includes `--json` documentation for each command\n5. Test piping JSON output to tools like `jq` to confirm proper formatting\n6. Create integration tests that verify schema consistency across different commands" @@ -4471,9 +3946,7 @@ "id": 4, "title": "Implement Keybinding File Handling and Backup Logic", "description": "Implement the core logic within the `install-keybindings` command to read the target `keybindings.json` file. If it exists, create a backup. If it doesn't exist, create a new file with an empty JSON array `[]`. Prepare the structure to add new keybindings.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "1. Create a `KeybindingsManager` class in `src/utils/keybindings.js` with the following methods:\n - `checkFileExists(path)`: Verify if the keybindings file exists\n - `createBackup(path)`: Copy existing file to `keybindings.json.bak`\n - `readKeybindings(path)`: Read and parse the JSON file\n - `writeKeybindings(path, data)`: Serialize and write data to the file\n - `createEmptyFile(path)`: Create a new file with `[]` content\n2. In the command handler, use these methods to:\n - Check if the target file exists\n - Create a backup if it does (with timestamp in filename)\n - Read existing keybindings or create an empty file\n - Parse the JSON content with proper error handling\n3. Add a `--no-backup` flag to skip backup creation\n4. Implement verbose logging with a `--verbose` flag\n5. Handle all potential file system errors (permissions, disk space, etc.)\n6. Add a `--dry-run` option that shows what would be done without making changes", "status": "pending", "testStrategy": "1. Create unit tests for the `KeybindingsManager` class\n2. Test all file handling scenarios with mocked file system:\n - File exists with valid JSON\n - File exists with invalid JSON\n - File doesn't exist\n - File exists but is not writable\n - Backup creation succeeds/fails\n3. Test the `--no-backup` and `--dry-run` flags\n4. Verify error messages are clear and actionable\n5. Test with various mock file contents to ensure proper parsing" @@ -4482,9 +3955,7 @@ "id": 5, "title": "Add Taskmaster Keybindings, Prevent Duplicates, and Support Customization", "description": "Define the specific Taskmaster keybindings (e.g., next task to clipboard, status update, open agent chat) and implement the logic to merge them into the user's `keybindings.json` data. Prevent adding duplicate keybindings (based on command ID or key combination). Add support for custom key combinations via command flags.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "1. Define default Taskmaster keybindings in `src/config/default-keybindings.js` as an array of objects with:\n - `key`: Default key combination (e.g., `\"ctrl+alt+n\"`)\n - `command`: Cursor command ID (e.g., `\"taskmaster.nextTask\"`)\n - `when`: Context when keybinding is active (e.g., `\"editorTextFocus\"`)\n - `args`: Any command arguments as an object\n - `description`: Human-readable description of what the keybinding does\n2. Implement the following keybindings:\n - Next task to clipboard: `ctrl+alt+n`\n - Update task status: `ctrl+alt+u`\n - Open agent chat with task context: `ctrl+alt+a`\n - Show task details: `ctrl+alt+d`\n3. Add command-line options to customize each keybinding:\n - `--next-key=\"ctrl+alt+n\"`\n - `--update-key=\"ctrl+alt+u\"`\n - `--agent-key=\"ctrl+alt+a\"`\n - `--details-key=\"ctrl+alt+d\"`\n4. Implement a `mergeKeybindings(existing, new)` function that:\n - Checks for duplicates based on command ID\n - Checks for key combination conflicts\n - Warns about conflicts but allows override with `--force` flag\n - Preserves existing non-Taskmaster keybindings\n5. Add a `--reset` flag to remove all existing Taskmaster keybindings before adding new ones\n6. Add a `--list` option to display currently installed Taskmaster keybindings\n7. Implement an `--uninstall` option to remove all Taskmaster keybindings", "status": "pending", "testStrategy": "1. Create unit tests for the keybinding merging logic\n2. Test duplicate detection and conflict resolution\n3. Test each customization flag to verify it properly overrides defaults\n4. Test the `--reset`, `--list`, and `--uninstall` options\n5. Create integration tests with various starting keybindings.json states\n6. Manually verify the installed keybindings work in Cursor\n7. Test edge cases like:\n - All keybindings customized\n - Conflicting key combinations with `--force` and without\n - Empty initial keybindings file\n - File with existing Taskmaster keybindings" @@ -4513,9 +3984,7 @@ "id": 2, "title": "Implement task saving functionality", "description": "Develop the backend functionality to save manually created tasks to the database", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create API endpoints to handle task creation requests from the frontend. Implement data validation, error handling, and confirmation messages. Ensure the saved tasks appear in the task list view and can be edited or deleted like PRD-parsed tasks.", "status": "done" } @@ -4543,9 +4012,7 @@ "id": 2, "title": "Update CLI interface for task-specific complexity analysis", "description": "Extend the CLI to accept task IDs or ranges as parameters for the complexity analysis command", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Add new flags `--id/-i`, `--from/-f`, and `--to/-t` to the CLI that allow users to specify task IDs or ranges for targeted complexity analysis. Update the command parser, help documentation, and ensure proper validation of the provided values.", "status": "done" }, @@ -4553,9 +4020,7 @@ "id": 3, "title": "Integrate task-specific analysis with MCP tool", "description": "Update the MCP tool interface to support analyzing complexity for specific tasks or ranges", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Modify the MCP tool's API endpoints and UI components to allow users to select specific tasks or ranges for complexity analysis. Ensure the UI provides clear feedback about which tasks are being analyzed and update the visualization components to properly display partial analysis results.", "status": "done" }, @@ -4563,10 +4028,7 @@ "id": 4, "title": "Create comprehensive tests for task-specific complexity analysis", "description": "Develop test cases to verify the correct functioning of task-specific complexity analysis", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "Create unit and integration tests that verify the task-specific complexity analysis works correctly across both CLI and MCP interfaces. Include tests for edge cases such as invalid task IDs, tasks with dependencies outside the selected set, and performance tests for large task sets.", "status": "done" } @@ -4594,9 +4056,7 @@ "id": 2, "title": "Implement Mermaid diagram generation core functionality", "description": "Create the core logic to parse Mermaid syntax and generate diagram output", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Integrate with the Mermaid library to parse diagram syntax. Implement error handling for invalid syntax. Create the rendering pipeline to generate the diagram in memory before output. Support all standard Mermaid diagram types (flowchart, sequence, class, etc.). Include proper logging for the generation process.", "status": "pending" }, @@ -4612,9 +4072,7 @@ "id": 4, "title": "Create documentation and examples", "description": "Provide comprehensive documentation and examples for the 'diagram' command", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Write detailed command documentation with all options explained. Create example diagrams covering different diagram types. Include troubleshooting section for common errors. Add documentation on extending the command with custom themes or templates. Create integration examples showing how to use the command in workflows with other tools.", "status": "pending" } @@ -4653,9 +4111,7 @@ "id": 2, "title": "Design PDF template and layout", "description": "Create a template design for the project progress PDF including sections for summary, metrics, and dependency visualization", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Design should include header/footer, progress summary section, key metrics visualization, dependency diagram placement, and styling guidelines. Create a mockup of the final PDF output for approval.", "status": "pending" }, @@ -4663,9 +4119,7 @@ "id": 3, "title": "Implement project progress data collection module", "description": "Develop functionality to gather and process project data for the PDF report", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create functions to extract task completion percentages, milestone status, timeline adherence, and other relevant metrics from the project database. Include data transformation logic to prepare for PDF rendering.", "status": "pending" }, @@ -4673,10 +4127,7 @@ "id": 4, "title": "Integrate with dependency visualization system", "description": "Connect to the existing diagram command to generate visual representation of task dependencies", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "Implement adapter for the diagram command output to be compatible with the PDF generation library. Handle different scales of dependency chains and ensure proper rendering of complex relationships.", "status": "pending" }, @@ -4684,10 +4135,7 @@ "id": 5, "title": "Build PDF generation core functionality", "description": "Develop the main module that combines data and visualizations into a formatted PDF document", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Implement the core PDF generation logic using the selected library. Include functions for adding text sections, embedding visualizations, formatting tables, and applying the template design. Add pagination and document metadata.", "status": "pending" }, @@ -4755,9 +4203,7 @@ "id": 2, "title": "Implement conditional logic for research role detection", "description": "Create logic to detect when a conversation is in 'research mode' and should trigger the Google Search Grounding functionality.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Develop heuristics or machine learning-based detection to identify when a user's query requires research capabilities. Implement a decision tree that determines when to activate Google Search Grounding based on conversation context, explicit user requests for research, or specific keywords. Include configuration options to adjust sensitivity of the detection mechanism.", "status": "pending" }, @@ -4765,9 +4211,7 @@ "id": 3, "title": "Update supported models configuration", "description": "Modify the model configuration to specify which AI models can utilize the Google Search Grounding capability.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Update the model configuration files to include flags for Google Search Grounding compatibility. Create a registry of supported models with their specific parameters for optimal integration with the search API. Implement version checking to ensure compatibility between model versions and the Google Search Grounding API version.", "status": "pending" }, @@ -4775,10 +4219,7 @@ "id": 4, "title": "Create end-to-end testing suite for research functionality", "description": "Develop comprehensive tests to verify the correct operation of the Google Search Grounding integration in research contexts.", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "Build automated test cases that cover various research scenarios, including edge cases. Create mock responses for the Google Search API to enable testing without actual API calls. Implement integration tests that verify the entire flow from user query to research-enhanced response. Include performance benchmarks to ensure the integration doesn't significantly impact response times.", "status": "pending" } @@ -4806,9 +4247,7 @@ "id": 2, "title": "Implement FastMCP Server Launcher", "description": "Create a component that can programmatically launch and manage the FastMCP server process over stdio", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Develop a module that can spawn the FastMCP server as a child process, establish stdio communication channels, handle process lifecycle events, and implement proper cleanup procedures. Include error handling for process failures and timeout mechanisms.", "status": "pending" }, @@ -4816,9 +4255,7 @@ "id": 3, "title": "Develop Message Protocol Handler", "description": "Implement a handler that can serialize/deserialize messages according to the FastMCP protocol specification", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create a protocol handler that formats outgoing messages and parses incoming messages according to the FastMCP protocol. Implement validation for message format compliance and error handling for malformed messages. Support all required message types defined in the protocol.", "status": "pending" }, @@ -4826,9 +4263,7 @@ "id": 4, "title": "Create Request/Response Correlation Mechanism", "description": "Implement a system to track and correlate requests with their corresponding responses", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Develop a correlation mechanism using unique identifiers to match requests with their responses. Implement timeout handling for unresponded requests and proper error propagation. Design the API to support both synchronous and asynchronous request patterns.", "status": "pending" }, @@ -4836,10 +4271,7 @@ "id": 5, "title": "Build Test Assertion Framework", "description": "Create a set of assertion utilities specific to FastMCP server testing", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "details": "Develop assertion utilities that can validate server responses against expected values, verify timing constraints, and check for proper error handling. Include support for complex response validation patterns and detailed failure reporting.", "status": "pending" }, @@ -4847,9 +4279,7 @@ "id": 6, "title": "Implement Test Cases", "description": "Develop a comprehensive set of test cases covering all FastMCP server functionality", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Create test cases for basic server operations, error conditions, edge cases, and performance scenarios. Organize tests into logical groups and ensure proper isolation between test cases. Include documentation for each test explaining its purpose and expected outcomes.", "status": "pending" }, @@ -4886,9 +4316,7 @@ "id": 2, "title": "Implement secure telemetry transmission", "description": "Create a secure mechanism to transmit telemetry data to the external analytics endpoint", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implement HTTPS POST request functionality to securely send the telemetry payload to the closed-source analytics API. Include proper encryption in transit using TLS. Implement retry logic and graceful fallback mechanisms for handling transmission failures due to connectivity issues.\n<info added on 2025-05-14T17:52:40.647Z>\nTo securely send structured JSON telemetry payloads from a Node.js CLI tool to an external analytics backend, follow these steps:\n\n1. Use the Axios library for HTTPS POST requests. Install it with: npm install axios.\n2. Store sensitive configuration such as the analytics endpoint URL and any secret keys in environment variables (e.g., process.env.ANALYTICS_URL, process.env.ANALYTICS_KEY). Use dotenv or a similar library to load these securely.\n3. Construct the telemetry payload as a JSON object with the required fields: userId, commandName, modelUsed, inputTokens, outputTokens, totalTokens, totalCost, and timestamp (ISO 8601).\n4. Implement robust retry logic using the axios-retry package (npm install axios-retry). Configure exponential backoff with a recommended maximum of 3 retries and a base delay (e.g., 500ms).\n5. Ensure all requests use HTTPS to guarantee TLS encryption in transit. Axios automatically uses HTTPS when the endpoint URL starts with https://.\n6. Handle errors gracefully: catch all transmission errors, log them for diagnostics, and ensure failures do not interrupt or degrade the CLI user experience. Optionally, queue failed payloads for later retry if persistent connectivity issues occur.\n7. Example code snippet:\n\nrequire('dotenv').config();\nconst axios = require('axios');\nconst axiosRetry = require('axios-retry');\n\naxiosRetry(axios, {\n retries: 3,\n retryDelay: axiosRetry.exponentialDelay,\n retryCondition: (error) => axiosRetry.isNetworkOrIdempotentRequestError(error),\n});\n\nasync function sendTelemetry(payload) {\n try {\n await axios.post(process.env.ANALYTICS_URL, payload, {\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${process.env.ANALYTICS_KEY}`,\n },\n timeout: 5000,\n });\n } catch (error) {\n // Log error, do not throw to avoid impacting CLI UX\n console.error('Telemetry transmission failed:', error.message);\n // Optionally, queue payload for later retry\n }\n}\n\nconst telemetryPayload = {\n userId: 'user-123',\n commandName: 'expand',\n modelUsed: 'gpt-4',\n inputTokens: 100,\n outputTokens: 200,\n totalTokens: 300,\n totalCost: 0.0123,\n timestamp: new Date().toISOString(),\n};\n\nsendTelemetry(telemetryPayload);\n\n8. Best practices:\n- Never hardcode secrets or endpoint URLs in source code.\n- Use environment variables and restrict access permissions.\n- Validate all payload fields before transmission.\n- Ensure the CLI continues to function even if telemetry transmission fails.\n\nReferences: [1][2][3][5]\n</info added on 2025-05-14T17:52:40.647Z>\n<info added on 2025-05-14T17:57:18.218Z>\nUser ID Retrieval and Generation:\n\nThe telemetry system must securely retrieve the user ID from the .taskmasterconfig globals, where it should have been generated during the initialization phase. Implementation should:\n\n1. Check for an existing user ID in the .taskmasterconfig file before sending any telemetry data.\n2. If no user ID exists (for users who run AI commands without prior initialization or during upgrades), automatically generate a new UUID v4 and persist it to the .taskmasterconfig file.\n3. Implement a getOrCreateUserId() function that:\n - Reads from the global configuration file\n - Returns the existing ID if present\n - Generates a cryptographically secure UUID v4 if not present\n - Saves the newly generated ID to the configuration file\n - Handles file access errors gracefully\n\n4. Example implementation:\n```javascript\nconst fs = require('fs');\nconst path = require('path');\nconst { v4: uuidv4 } = require('uuid');\n\nfunction getOrCreateUserId() {\n const configPath = path.join(os.homedir(), '.taskmasterconfig');\n \n try {\n // Try to read existing config\n const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));\n \n if (config.userId) {\n return config.userId;\n }\n \n // No user ID found, generate and save\n config.userId = uuidv4();\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2));\n return config.userId;\n } catch (error) {\n // Handle case where config doesn't exist or is invalid\n const userId = uuidv4();\n const newConfig = { userId };\n \n try {\n fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2));\n } catch (writeError) {\n console.error('Failed to save user ID to config:', writeError.message);\n }\n \n return userId;\n }\n}\n```\n\n5. Ensure this function is called before constructing any telemetry payload to guarantee a consistent user ID across all telemetry events.\n</info added on 2025-05-14T17:57:18.218Z>\n<info added on 2025-05-15T18:45:32.123Z>\n**Invocation Point for Sending Telemetry:**\n* The primary invocation for sending the telemetry payload should occur in `scripts/modules/ai-services-unified.js`.\n* This should happen *after* the `telemetryData` object is fully constructed and *after* user consent (from subtask 77.3) has been confirmed.\n\n**Dedicated Module for Transmission Logic:**\n* The actual HTTPS POST request mechanism, including TLS encryption, retry logic, and graceful fallbacks, should be implemented in a new, separate module (e.g., `scripts/modules/telemetry-sender.js` or `scripts/utils/telemetry-client.js`).\n* This module will be imported and utilized by `scripts/modules/ai-services-unified.js`.\n\n**Key Considerations:**\n* Robust error handling must be in place for the telemetry transmission process; failures should be logged locally and must not disrupt core application functionality.\n* The entire telemetry sending process is contingent upon explicit user consent as outlined in subtask 77.3.\n\n**Implementation Plan:**\n1. Create a new module `scripts/utils/telemetry-client.js` with the following functions:\n - `sendTelemetryData(telemetryPayload)`: Main function that handles the HTTPS POST request\n - `isUserConsentGiven()`: Helper function to check if user has consented to telemetry\n - `logTelemetryError(error)`: Helper function for consistent error logging\n\n2. In `ai-services-unified.js`, after constructing the telemetryData object:\n ```javascript\n const telemetryClient = require('../utils/telemetry-client');\n \n // After telemetryData is constructed\n if (telemetryClient.isUserConsentGiven()) {\n // Non-blocking telemetry submission\n telemetryClient.sendTelemetryData(telemetryData)\n .catch(error => telemetryClient.logTelemetryError(error));\n }\n ```\n\n3. Ensure the telemetry-client module implements:\n - Axios with retry logic for robust HTTP requests\n - Proper TLS encryption via HTTPS\n - Comprehensive error handling\n - Configuration loading from environment variables\n - Validation of payload data before transmission\n</info added on 2025-05-15T18:45:32.123Z>", "status": "deferred", "testStrategy": "Test with mock endpoints to verify secure transmission and proper handling of various response scenarios" @@ -4906,10 +4334,7 @@ "id": 4, "title": "Integrate telemetry into Taskmaster commands", "description": "Integrate the telemetry utility across all relevant Taskmaster commands", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "Modify each Taskmaster command (expand, parse-prd, research, etc.) to call the logAiUsage utility after AI interactions. Ensure telemetry is only sent if user has provided consent. Implement the integration in a way that doesn't impact command performance or user experience.\n<info added on 2025-05-06T17:57:13.980Z>\nModify each Taskmaster command (expand, parse-prd, research, etc.) to call the logAiUsage utility after AI interactions. Ensure telemetry is only sent if user has provided consent. Implement the integration in a way that doesn't impact command performance or user experience.\n\nSuccessfully integrated telemetry calls into `addTask` (core) and `addTaskDirect` (MCP) functions by passing `commandName` and `outputType` parameters to the telemetry system. The `ai-services-unified.js` module now logs basic telemetry data, including calculated cost information, whenever the `add-task` command or tool is invoked. This integration respects user consent settings and maintains performance standards.\n</info added on 2025-05-06T17:57:13.980Z>", "status": "done", "testStrategy": "Integration tests to verify telemetry is correctly triggered across different commands with proper data" @@ -4918,10 +4343,7 @@ "id": 5, "title": "Implement usage summary display", "description": "Create an optional feature to display AI usage summary in the CLI output", - "dependencies": [ - 1, - 4 - ], + "dependencies": [1, 4], "details": "Develop functionality to display a concise summary of AI usage (tokens used, estimated cost) directly in the CLI output after command execution. Make this feature configurable through Taskmaster settings. Ensure the display is formatted clearly and doesn't clutter the main command output.", "status": "done", "testStrategy": "User acceptance testing to verify the summary display is clear, accurate, and properly configurable" @@ -5046,10 +4468,7 @@ "description": "Implement a separate module for handling telemetry transmission logic", "details": "Create a new module (e.g., `scripts/utils/telemetry-client.js`) that encapsulates all telemetry transmission functionality:\n\n1. Implement core functions:\n - `sendTelemetryData(telemetryPayload)`: Main function to handle HTTPS POST requests\n - `isUserConsentGiven()`: Helper to check if user has consented to telemetry\n - `logTelemetryError(error)`: Helper for consistent error logging\n\n2. Use Axios with retry logic:\n - Configure with exponential backoff (max 3 retries, 500ms base delay)\n - Implement proper TLS encryption via HTTPS\n - Set appropriate timeouts (5000ms recommended)\n\n3. Implement robust error handling:\n - Catch all transmission errors\n - Log failures locally without disrupting application flow\n - Ensure failures are transparent to users\n\n4. Configure securely:\n - Load endpoint URL and authentication from environment variables\n - Never hardcode secrets in source code\n - Validate payload data before transmission\n\n5. Integration with ai-services-unified.js:\n - Import the telemetry-client module\n - Call after telemetryData object is constructed\n - Only send if user consent is confirmed\n - Use non-blocking approach to avoid performance impact", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "parentTaskId": 77, "testStrategy": "Unit test with mock endpoints to verify proper transmission, error handling, and respect for user consent settings" } @@ -5078,9 +4497,7 @@ "id": 2, "title": "Modify Add-Task to Recursively Analyze Dependencies", "description": "Update the add-task functionality to recursively analyze and incorporate all task dependencies.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implement a recursive algorithm that identifies and incorporates all dependencies for a given task. Ensure it handles nested dependencies correctly.", "status": "done", "testStrategy": "Test with various dependency scenarios, including nested dependencies." @@ -5098,9 +4515,7 @@ "id": 4, "title": "Integrate with Existing Validation and Error Handling", "description": "Update the add-task functionality to integrate with existing validation and error handling mechanisms (from Task 87).", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Modify the code to provide clear feedback if dependencies cannot be resolved. Ensure that circular dependencies are detected and handled appropriately.", "status": "done", "testStrategy": "Test with invalid dependency scenarios to verify proper error handling." @@ -5109,9 +4524,7 @@ "id": 5, "title": "Optimize Performance for Large Projects", "description": "Optimize the add-task functionality to ensure efficient dependency resolution, especially for projects with a large number of tasks.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Profile and optimize the recursive dependency analysis algorithm. Implement caching or other performance improvements as needed.", "status": "done", "testStrategy": "Test with large sets of tasks to verify performance improvements." @@ -5134,10 +4547,7 @@ "title": "Implement Move Command for Tasks and Subtasks", "description": "Introduce a 'move' command to enable moving tasks or subtasks to a different id, facilitating conflict resolution by allowing teams to assign new ids as needed.", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "priority": "medium", "details": "The move command will consist of three core components: 1) Core Logic Function in scripts/modules/task-manager/move-task.js, 2) Direct Function Wrapper in mcp-server/src/core/direct-functions/move-task.js, and 3) MCP Tool in mcp-server/src/tools/move-task.js. The command will accept source and destination IDs, handling various scenarios including moving tasks to become subtasks, subtasks to become tasks, and subtasks between different parents. The implementation will handle edge cases such as invalid ids, non-existent parents, circular dependencies, and will properly update all dependencies.", "testStrategy": "Testing will follow a three-tier approach: 1) Unit tests for core functionality including moving tasks to subtasks, subtasks to tasks, subtasks between parents, dependency handling, and validation error cases; 2) Integration tests for the direct function with mock MCP environment and task file regeneration; 3) End-to-end tests for the full MCP tool call path. This will verify all scenarios including moving a task to a new id, moving a subtask under a different parent while preserving its hierarchy, and handling errors for invalid operations.", @@ -5155,9 +4565,7 @@ "id": 2, "title": "Implement edge case handling", "description": "Develop robust error handling for all potential edge cases in the move operation", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create validation functions to detect invalid task IDs, non-existent parent tasks, and circular dependencies. Handle special cases such as moving a task to become the first/last subtask, reordering within the same parent, preventing moving a task to itself, and preventing moving a parent to its own subtask. Implement proper error messages and status codes for each edge case, and ensure system stability if a move operation fails.", "status": "done", "parentTaskId": 91 @@ -5166,9 +4574,7 @@ "id": 3, "title": "Update CLI interface for move commands", "description": "Extend the command-line interface to support the new move functionality with appropriate flags and options", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create the Direct Function Wrapper in mcp-server/src/core/direct-functions/move-task.js to adapt the core logic for MCP, handling path resolution and parameter validation. Implement silent mode to prevent console output interfering with JSON responses. Create the MCP Tool in mcp-server/src/tools/move-task.js that exposes the functionality to Cursor, handles project root resolution, and includes proper Zod parameter definitions. Update MCP tool definition in .cursor/mcp.json and register the tool in mcp-server/src/tools/index.js.", "status": "done", "parentTaskId": 91 @@ -5177,9 +4583,7 @@ "id": 4, "title": "Ensure data integrity during moves", "description": "Implement safeguards to maintain data consistency and update all relationships during move operations", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implement dependency handling logic to update dependencies when converting between task/subtask, add appropriate parent dependencies when needed, and validate no circular dependencies are created. Create transaction-like operations to ensure atomic moves that either complete fully or roll back. Implement functions to update all affected task relationships after a move, and add verification steps to confirm data integrity post-move.", "status": "done", "parentTaskId": 91 @@ -5188,11 +4592,7 @@ "id": 5, "title": "Create comprehensive test suite", "description": "Develop and execute tests covering all move scenarios and edge cases", - "dependencies": [ - 1, - 3, - 4 - ], + "dependencies": [1, 3, 4], "details": "Create unit tests for core functionality including moving tasks to subtasks, subtasks to tasks, subtasks between parents, dependency handling, and validation error cases. Implement integration tests for the direct function with mock MCP environment and task file regeneration. Develop end-to-end tests for the full MCP tool call path. Ensure tests cover all identified edge cases and potential failure points, and verify data integrity after moves.", "status": "done", "parentTaskId": 91 @@ -5201,9 +4601,7 @@ "id": 6, "title": "Export and integrate the move function", "description": "Ensure the move function is properly exported and integrated with existing code", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Export the move function in scripts/modules/task-manager.js. Update task-master-core.js to include the direct function. Reuse validation logic from add-subtask.js and remove-subtask.js where appropriate. Follow silent mode implementation pattern from other direct functions and match parameter naming conventions in MCP tools.", "status": "done", "parentTaskId": 91 @@ -5215,11 +4613,7 @@ "title": "Implement Project Root Environment Variable Support in MCP Configuration", "description": "Add support for a 'TASK_MASTER_PROJECT_ROOT' environment variable in MCP configuration, allowing it to be set in both mcp.json and .env, with precedence over other methods. This will define the root directory for the MCP server and take precedence over all other project root resolution methods. The implementation should be backward compatible with existing workflows that don't use this variable.", "status": "done", - "dependencies": [ - 1, - 3, - 17 - ], + "dependencies": [1, 3, 17], "priority": "medium", "details": "Update the MCP server configuration system to support the TASK_MASTER_PROJECT_ROOT environment variable as the standard way to specify the project root directory. This provides better namespacing and avoids conflicts with other tools that might use a generic PROJECT_ROOT variable. Implement a clear precedence order for project root resolution:\n\n1. TASK_MASTER_PROJECT_ROOT environment variable (from shell or .env file)\n2. 'projectRoot' key in mcp_config.toml or mcp.json configuration files\n3. Existing resolution logic (CLI args, current working directory, etc.)\n\nModify the configuration loading logic to check for these sources in the specified order, ensuring backward compatibility. All MCP tools and components should use this standardized project root resolution logic. The TASK_MASTER_PROJECT_ROOT environment variable will be required because path resolution is delegated to the MCP client implementation, ensuring consistent behavior across different environments.\n\nImplementation steps:\n1. Identify all code locations where project root is determined (initialization, utility functions)\n2. Update configuration loaders to check for TASK_MASTER_PROJECT_ROOT in environment variables\n3. Add support for 'projectRoot' in configuration files as a fallback\n4. Refactor project root resolution logic to follow the new precedence rules\n5. Ensure all MCP tools and functions use the updated resolution logic\n6. Add comprehensive error handling for cases where TASK_MASTER_PROJECT_ROOT is not set or invalid\n7. Implement validation to ensure the specified directory exists and is accessible", "testStrategy": "1. Write unit tests to verify that the config loader correctly reads project root from environment variables and configuration files with the expected precedence:\n - Test TASK_MASTER_PROJECT_ROOT environment variable takes precedence when set\n - Test 'projectRoot' in configuration files is used when environment variable is absent\n - Test fallback to existing resolution logic when neither is specified\n\n2. Add integration tests to ensure that the MCP server and all tools use the correct project root:\n - Test server startup with TASK_MASTER_PROJECT_ROOT set to various valid and invalid paths\n - Test configuration file loading from the specified project root\n - Test path resolution for resources relative to the project root\n\n3. Test backward compatibility:\n - Verify existing workflows function correctly without the new variables\n - Ensure no regression in projects not using the new configuration options\n\n4. Manual testing:\n - Set TASK_MASTER_PROJECT_ROOT in shell environment and verify correct behavior\n - Set TASK_MASTER_PROJECT_ROOT in .env file and verify it's properly loaded\n - Configure 'projectRoot' in configuration files and test precedence\n - Test with invalid or non-existent directories to verify error handling", @@ -5281,10 +4675,7 @@ "details": "1. Create a new provider class in `src/ai-providers/google-vertex.js` that extends the existing BaseAIProvider, following the established structure used by other providers (e.g., google.js, openai.js).\n2. Integrate the Vercel AI SDK's `@ai-sdk/google-vertex` package. Use the default `vertex` provider for standard usage, and allow for custom configuration via `createVertex` for advanced scenarios (e.g., specifying project ID, location, and credentials).\n3. Implement all required interface methods (such as `getClient`, `generateText`, etc.) to ensure compatibility with the provider system. Reference the implementation patterns from other providers for consistency.\n4. Handle Vertex AI-specific configuration, including project ID, location, and Google Cloud authentication. Support both environment-based authentication and explicit service account credentials via `googleAuthOptions`.\n5. Implement robust error handling for Vertex-specific issues, including authentication failures and API errors, leveraging the system-wide error handling patterns.\n6. Update `src/ai-providers/index.js` to export the new provider, and add the 'vertex' entry to the PROVIDERS object in `scripts/modules/ai-services-unified.js`.\n7. Update documentation to provide clear setup instructions for Google Vertex AI, including required environment variables, service account setup, and configuration examples.\n8. Ensure the implementation is modular and maintainable, supporting future expansion for additional Vertex AI features or models.", "testStrategy": "- Write unit tests for the new provider class, covering all interface methods and configuration scenarios (default, custom, error cases).\n- Verify that the provider can successfully authenticate using both environment-based and explicit service account credentials.\n- Test integration with the provider system by selecting 'vertex' as the provider and generating text using supported Vertex AI models (e.g., Gemini).\n- Simulate authentication and API errors to confirm robust error handling and user feedback.\n- Confirm that the provider is correctly exported and available in the PROVIDERS object.\n- Review and validate the updated documentation for accuracy and completeness.", "status": "done", - "dependencies": [ - 19, - 94 - ], + "dependencies": [19, 94], "priority": "medium", "subtasks": [ { @@ -5300,9 +4691,7 @@ "id": 2, "title": "Integrate Vercel AI SDK Google Vertex Package", "description": "Integrate the `@ai-sdk/google-vertex` package, supporting both the default provider and custom configuration via `createVertex`.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Allow for standard usage with the default `vertex` provider and advanced scenarios using `createVertex` for custom project ID, location, and credentials as per SDK documentation.", "status": "done", "testStrategy": "Write unit tests to ensure both default and custom provider instances can be created and configured." @@ -5320,9 +4709,7 @@ "id": 4, "title": "Handle Vertex AI Configuration and Authentication", "description": "Implement support for Vertex AI-specific configuration, including project ID, location, and authentication via environment variables or explicit service account credentials.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Support both environment-based authentication and explicit credentials using `googleAuthOptions`, following Google Cloud and Vertex AI setup best practices.", "status": "done", "testStrategy": "Test with both environment variable-based and explicit service account authentication to ensure both methods work as expected." @@ -5331,9 +4718,7 @@ "id": 5, "title": "Update Exports, Documentation, and Error Handling", "description": "Export the new provider, update the PROVIDERS object, and document setup instructions, including robust error handling for Vertex-specific issues.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Update `src/ai-providers/index.js` and `scripts/modules/ai-services-unified.js`, and provide clear documentation for setup, configuration, and error handling patterns.", "status": "done", "testStrategy": "Verify the provider is available for import, documentation is accurate, and error handling works by simulating common failure scenarios." @@ -5347,10 +4732,7 @@ "details": "Implement the Azure OpenAI provider following the established provider pattern:\n\n1. **Create Azure Provider Class** (`src/ai-providers/azure.js`):\n - Extend BaseAIProvider class following the same pattern as openai.js and google.js\n - Import and use `createAzureOpenAI` from `@ai-sdk/azure` package\n - Implement required interface methods: `getClient()`, `validateConfig()`, and any other abstract methods\n - Handle Azure-specific configuration: endpoint URL, API key, and deployment name\n - Add proper error handling for missing or invalid Azure configuration\n\n2. **Configuration Management**:\n - Support environment variables: AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_OPENAI_DEPLOYMENT\n - Validate that both endpoint and API key are provided\n - Provide clear error messages for configuration issues\n - Follow the same configuration pattern as other providers\n\n3. **Integration Updates**:\n - Update `src/ai-providers/index.js` to export the new AzureProvider\n - Add 'azure' entry to the PROVIDERS object in `scripts/modules/ai-services-unified.js`\n - Ensure the provider is properly registered and accessible through the unified AI services\n\n4. **Error Handling**:\n - Implement Azure-specific error handling for authentication failures\n - Handle endpoint connectivity issues with helpful error messages\n - Validate deployment name and provide guidance for common configuration mistakes\n - Follow the established error handling patterns from Task 19\n\n5. **Documentation Updates**:\n - Update any provider documentation to include Azure OpenAI setup instructions\n - Add configuration examples for Azure OpenAI environment variables\n - Include troubleshooting guidance for common Azure-specific issues\n\nThe implementation should maintain consistency with existing provider implementations while handling Azure's unique authentication and endpoint requirements.", "testStrategy": "Verify the Azure OpenAI provider implementation through comprehensive testing:\n\n1. **Unit Testing**:\n - Test provider class instantiation and configuration validation\n - Verify getClient() method returns properly configured Azure OpenAI client\n - Test error handling for missing/invalid configuration parameters\n - Validate that the provider correctly extends BaseAIProvider\n\n2. **Integration Testing**:\n - Test provider registration in the unified AI services system\n - Verify the provider appears in the PROVIDERS object and is accessible\n - Test end-to-end functionality with valid Azure OpenAI credentials\n - Validate that the provider works with existing AI operation workflows\n\n3. **Configuration Testing**:\n - Test with various environment variable combinations\n - Verify proper error messages for missing endpoint or API key\n - Test with invalid endpoint URLs and ensure graceful error handling\n - Validate deployment name handling and error reporting\n\n4. **Manual Verification**:\n - Set up test Azure OpenAI credentials and verify successful connection\n - Test actual AI operations (like task expansion) using the Azure provider\n - Verify that the provider selection works correctly in the CLI\n - Confirm that error messages are helpful and actionable for users\n\n5. **Documentation Verification**:\n - Ensure all configuration examples work as documented\n - Verify that setup instructions are complete and accurate\n - Test troubleshooting guidance with common error scenarios", "status": "done", - "dependencies": [ - 19, - 26 - ], + "dependencies": [19, 26], "priority": "medium", "subtasks": [ { @@ -5367,9 +4749,7 @@ "id": 2, "title": "Implement Configuration Management", "description": "Add support for Azure OpenAI environment variables and configuration validation", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implement configuration management for Azure OpenAI provider that supports environment variables: AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, and AZURE_OPENAI_DEPLOYMENT. Add validation logic to ensure both endpoint and API key are provided. Create clear error messages for configuration issues. Follow the same configuration pattern as implemented in other providers. Ensure the validateConfig() method properly checks all required Azure configuration parameters.", "status": "done", "testStrategy": "Test configuration validation with various combinations of missing or invalid parameters. Verify environment variables are correctly loaded and applied to the provider configuration.", @@ -5379,9 +4759,7 @@ "id": 3, "title": "Update Provider Integration", "description": "Integrate the Azure provider into the existing AI provider system", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Update src/ai-providers/index.js to export the new AzureProvider class. Add 'azure' entry to the PROVIDERS object in scripts/modules/ai-services-unified.js. Ensure the provider is properly registered and accessible through the unified AI services. Test that the provider can be instantiated and used through the provider selection mechanism. Follow the same integration pattern used for existing providers.", "status": "done", "testStrategy": "Create integration tests that verify the Azure provider is correctly registered and can be selected through the provider system. Test that the provider is properly initialized when selected.", @@ -5391,9 +4769,7 @@ "id": 4, "title": "Implement Azure-Specific Error Handling", "description": "Add specialized error handling for Azure OpenAI-specific issues", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Implement Azure-specific error handling for authentication failures, endpoint connectivity issues, and deployment name validation. Provide helpful error messages that guide users to resolve common configuration mistakes. Follow the established error handling patterns from Task 19. Create custom error classes if needed for Azure-specific errors. Ensure errors are properly propagated and formatted for user display.", "status": "done", "testStrategy": "Test error handling by simulating various failure scenarios including authentication failures, invalid endpoints, and missing deployment names. Verify appropriate error messages are generated.", @@ -5403,11 +4779,7 @@ "id": 5, "title": "Update Documentation", "description": "Create comprehensive documentation for the Azure OpenAI provider integration", - "dependencies": [ - 1, - 3, - 4 - ], + "dependencies": [1, 3, 4], "details": "Update provider documentation to include Azure OpenAI setup instructions. Add configuration examples for Azure OpenAI environment variables. Include troubleshooting guidance for common Azure-specific issues. Document the required Azure resource creation process with references to Microsoft's documentation. Provide examples of valid configuration settings and explain each required parameter. Include information about Azure OpenAI model deployment requirements.", "status": "done", "testStrategy": "Review documentation for completeness, accuracy, and clarity. Ensure all configuration options are documented and examples are provided. Verify troubleshooting guidance addresses common issues identified during implementation.", @@ -5420,12 +4792,7 @@ "title": "Implement .taskmaster Directory Structure", "description": "Consolidate all Task Master-managed files in user projects into a clean, centralized .taskmaster/ directory structure to improve organization and keep user project directories clean, based on GitHub issue #275.", "status": "done", - "dependencies": [ - 1, - 3, - 4, - 17 - ], + "dependencies": [1, 3, 4, 17], "priority": "high", "details": "This task involves restructuring how Task Master organizes files within user projects to improve maintainability and keep project directories clean:\n\n1. Create a new `.taskmaster/` directory structure in user projects:\n - Move task files from `tasks/` to `.taskmaster/tasks/`\n - Move PRD files from `scripts/` to `.taskmaster/docs/`\n - Move analysis reports to `.taskmaster/reports/`\n - Move configuration from `.taskmasterconfig` to `.taskmaster/config.json`\n - Create `.taskmaster/templates/` for user templates\n\n2. Update all Task Master code that creates/reads user files:\n - Modify task file generation to use `.taskmaster/tasks/`\n - Update PRD file handling to use `.taskmaster/docs/`\n - Adjust report generation to save to `.taskmaster/reports/`\n - Update configuration loading to look for `.taskmaster/config.json`\n - Modify any path resolution logic in Task Master's codebase\n\n3. Ensure backward compatibility during migration:\n - Implement path fallback logic that checks both old and new locations\n - Add deprecation warnings when old paths are detected\n - Create a migration command to help users transition to the new structure\n - Preserve existing user data during migration\n\n4. Update the project initialization process:\n - Modify the init command to create the new `.taskmaster/` directory structure\n - Update default file creation to use new paths\n\n5. Benefits of the new structure:\n - Keeps user project directories clean and organized\n - Clearly separates Task Master files from user project files\n - Makes it easier to add Task Master to .gitignore if desired\n - Provides logical grouping of different file types\n\n6. Test thoroughly to ensure all functionality works with the new structure:\n - Verify all Task Master commands work with the new paths\n - Ensure backward compatibility functions correctly\n - Test migration process preserves all user data\n\n7. Update documentation:\n - Update README.md to reflect the new user file structure\n - Add migration guide for existing users\n - Document the benefits of the cleaner organization", "testStrategy": "1. Unit Testing:\n - Create unit tests for path resolution that verify both new and old paths work\n - Test configuration loading with both `.taskmasterconfig` and `.taskmaster/config.json`\n - Verify the migration command correctly moves files and preserves content\n - Test file creation in all new subdirectories\n\n2. Integration Testing:\n - Run all existing integration tests with the new directory structure\n - Verify that all Task Master commands function correctly with new paths\n - Test backward compatibility by running commands with old file structure\n\n3. Migration Testing:\n - Test the migration process on sample projects with existing tasks and files\n - Verify all tasks, PRDs, reports, and configurations are correctly moved\n - Ensure no data loss occurs during migration\n - Test migration with partial existing structures (e.g., only tasks/ exists)\n\n4. User Workflow Testing:\n - Test complete workflows: init → create tasks → generate reports → update PRDs\n - Verify all generated files go to correct locations in `.taskmaster/`\n - Test that user project directories remain clean\n\n5. Manual Testing:\n - Perform end-to-end testing with the new structure\n - Create, update, and delete tasks using the new structure\n - Generate reports and verify they're saved to `.taskmaster/reports/`\n\n6. Documentation Verification:\n - Review all documentation to ensure it accurately reflects the new user file structure\n - Verify the migration guide provides clear instructions\n\n7. Regression Testing:\n - Run the full test suite to ensure no regressions were introduced\n - Verify existing user projects continue to work during transition period", @@ -5443,9 +4810,7 @@ "id": 2, "title": "Update Task Master code for new user file paths", "description": "Modify all Task Master code that creates or reads user project files to use the new .taskmaster structure", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Update Task Master's file handling code to use the new paths: tasks in .taskmaster/tasks/, PRD files in .taskmaster/docs/, reports in .taskmaster/reports/, and config in .taskmaster/config.json. Modify path resolution logic throughout the Task Master codebase to reference the new user file locations.", "status": "done", "testStrategy": "Run unit tests to ensure all Task Master modules can properly create and access user files in new locations. Test configuration loading with the new path structure." @@ -5454,9 +4819,7 @@ "id": 3, "title": "Update task file generation system", "description": "Modify the task file generation system to use the new directory structure", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Update the task file generation system to create and read task files from .taskmaster/tasks/ instead of tasks/. Ensure all template paths are updated. Modify any path resolution logic specific to task file handling.", "status": "done", "testStrategy": "Test creating new tasks and verify they're saved to the correct location. Verify existing tasks can be read from the new location." @@ -5465,9 +4828,7 @@ "id": 4, "title": "Implement backward compatibility logic", "description": "Add fallback mechanisms to support both old and new file locations during transition", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Implement path fallback logic that checks both old and new locations when files aren't found. Add deprecation warnings when old paths are used, informing users about the new structure. Ensure error messages are clear about the transition.", "status": "done", "testStrategy": "Test with both old and new directory structures to verify fallback works correctly. Verify deprecation warnings appear when using old paths." @@ -5476,10 +4837,7 @@ "id": 5, "title": "Create migration command for users", "description": "Develop a Task Master command to help users transition their existing projects to the new structure", - "dependencies": [ - 1, - 4 - ], + "dependencies": [1, 4], "details": "Create a 'taskmaster migrate' command that automatically moves user files from old locations to the new .taskmaster structure. Move tasks/ to .taskmaster/tasks/, scripts/prd.txt to .taskmaster/docs/, reports to .taskmaster/reports/, and .taskmasterconfig to .taskmaster/config.json. Include backup functionality and validation to ensure migration completed successfully.", "status": "done", "testStrategy": "Test the migration command on various user project configurations. Verify it handles edge cases like missing directories or partial existing structures." @@ -5488,9 +4846,7 @@ "id": 6, "title": "Update project initialization process", "description": "Modify the init command to create the new directory structure for new projects", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Update the init command to create the .taskmaster directory and its subdirectories (tasks/, docs/, reports/, templates/). Modify default file creation to use the new paths. Ensure new projects are created with the correct structure from the start.", "status": "done", "testStrategy": "Test initializing new projects and verify the correct .taskmaster directory structure is created. Check that default configurations are properly set up in the new location." @@ -5508,9 +4864,7 @@ "id": 8, "title": "Update documentation and create migration guide", "description": "Update all documentation to reflect the new directory structure and provide migration guidance", - "dependencies": [ - 7 - ], + "dependencies": [7], "details": "Update README.md and other documentation to reflect the new .taskmaster structure for user projects. Create a comprehensive migration guide explaining the benefits of the new structure and how to migrate existing projects. Include examples of the new directory layout and explain how it keeps user project directories clean.", "status": "done", "testStrategy": "Review documentation for accuracy and completeness. Have users follow the migration guide to verify it's clear and effective." @@ -5542,10 +4896,7 @@ "details": "Implement a new 'export' command in the CLI that supports two primary modes: (1) generating individual task files on-demand (superseding the current automatic generation system), and (2) producing a comprehensive PDF export. The PDF should include: a first page with the output of 'tm list --with-subtasks', followed by individual pages for each task (using 'tm show <task_id>') and each subtask (using 'tm show <subtask_id>'). Integrate PDF generation using a robust library (e.g., pdfkit, Puppeteer, or jsPDF) to ensure high-quality output and proper pagination. Refactor or disable any existing automatic file generation logic to avoid performance overhead. Ensure the command supports flexible output paths and options for exporting only files, only PDF, or both. Update documentation and help output to reflect the new export capabilities. Consider concurrency and error handling for large projects. Ensure the export process is efficient and does not block the main CLI thread unnecessarily.", "testStrategy": "1. Run the 'export' command with various options and verify that task files are generated only on-demand, not automatically. 2. Generate a PDF export and confirm that the first page contains the correct 'tm list --with-subtasks' output, and that each subsequent page accurately reflects the output of 'tm show <task_id>' and 'tm show <subtask_id>' for all tasks and subtasks. 3. Test exporting in projects with large numbers of tasks and subtasks to ensure performance and correctness. 4. Attempt exports with invalid paths or missing data to verify robust error handling. 5. Confirm that no automatic file generation occurs during normal task operations. 6. Review CLI help output and documentation for accuracy regarding the new export functionality.", "status": "pending", - "dependencies": [ - 4, - 95 - ], + "dependencies": [4, 95], "priority": "medium", "subtasks": [ { @@ -5562,9 +4913,7 @@ "id": 2, "title": "Implement Export Command Infrastructure with On-Demand Task File Generation", "description": "Develop the CLI 'export' command infrastructure, enabling users to generate task files on-demand by invoking the preserved generateTaskFiles function only when requested.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Create the export command with options for output paths and modes (files, PDF, or both). Ensure generateTaskFiles is only called within this command and not elsewhere.", "status": "pending", "testStrategy": "Test the export command to confirm task files are generated only when explicitly requested and that output paths and options function as intended.", @@ -5584,9 +4933,7 @@ "id": 4, "title": "Update Documentation, Tests, and CLI Help for Export Workflow", "description": "Revise all relevant documentation, automated tests, and CLI help output to reflect the new export-based workflow and available options.", - "dependencies": [ - 3 - ], + "dependencies": [3], "details": "Update user guides, README files, and CLI help text. Add or modify tests to cover the new export command and its options. Ensure all documentation accurately describes the new workflow and usage.", "status": "pending", "testStrategy": "Review documentation for completeness and accuracy. Run all tests to ensure coverage of the new export command and verify CLI help output.", @@ -5626,10 +4973,7 @@ "id": 3, "title": "Integrate Git Module with Task System", "description": "Connect the Git branch commands to the task tracking system with minimal integration to update task statuses.", - "dependencies": [ - "1", - "2" - ], + "dependencies": ["1", "2"], "details": "Create the main entry point at `scripts/modules/git/index.js` that exposes the three core commands. Implement the simplest possible integration with the task system:\n1. When a branch is started, update the task status to 'in-progress'\n2. When a branch is finished, update the task status to 'completed'\n\nSkip all advanced integrations, error handling, and edge cases. Do not implement remote repository support beyond the absolute basics. Focus only on making the commands accessible through the main CLI interface.", "status": "pending", "testStrategy": "Test the complete workflow manually: create a task, start a branch for it, verify the task status changes, finish the branch, and verify the task status updates accordingly." @@ -5641,10 +4985,7 @@ "title": "Implement Standalone 'research' CLI Command for AI-Powered Queries", "description": "Develop a new 'task-master research' (alias 'tm research') CLI command for fast, context-aware AI research queries using the ai-services-unified.js infrastructure.", "status": "done", - "dependencies": [ - 4, - 16 - ], + "dependencies": [4, 16], "priority": "medium", "details": "- Add a new CLI command 'research' to commands.js, following established CLI patterns and conventions.\n- Command should accept a research query as the main parameter, with optional flags for task IDs (--tasks), file paths (--files), custom context (--context), output detail level (--detail), and result saving (--save).\n- Integrate with ai-services-unified.js, invoking its research mode to process the query and context, leveraging project file tree and task context as needed.\n- Implement logic to gather and inject relevant context from specified tasks, files, or custom input, and generate a project file tree snapshot if required.\n- Ensure output is formatted for terminal readability, including citations and references where available.\n- Support saving research results to a specified file if --save is provided.\n- Provide both brief and comprehensive output modes, controlled by a flag.\n- Ensure the command is non-interactive (one-shot execution) and complements the existing 'explore' command.\n- Update help documentation and usage examples for the new command.\n<info added on 2025-06-13T16:09:57.347Z>\nAdd research findings on interactive CLI best practices to inform implementation decisions for the research command and potential future interactive features:\n\n**Interactive CLI Best Practices Research Results:**\n\nKey findings for Node.js CLI development include empathic user-friendly design (prompting for missing arguments rather than failing), stateful context-aware interactions using XDG Base Directory Specification for config storage, and conversation threading with libraries like Inquirer.js for chaining prompts based on previous answers.\n\nImplementation recommendations: Use Inquirer.js for interactive flows, implement state management for user preferences across sessions, structure commands to support multi-step context-aware conversations, enhance output with color formatting using chalk, and design keyboard-friendly navigation without mouse requirements.\n\nSpecific patterns include branching conversations where follow-up prompts are triggered based on user selections, persistent context storage between CLI runs through file-based serialization, and rich UI elements like lists and checkboxes for improved accessibility.\n\nConsider applying these patterns to enhance the research command with optional interactive modes for query refinement and result exploration, while maintaining the primary non-interactive one-shot execution design.\n</info added on 2025-06-13T16:09:57.347Z>\n<info added on 2025-06-13T16:46:28.375Z>\nTesting append functionality: The research command implementation is progressing well. Successfully integrated enhanced follow-up functionality with conversation threading and save-to-task capabilities.\n</info added on 2025-06-13T16:46:28.375Z>\n<info added on 2025-06-14T10:15:00.000Z>\nRegular update test: Add information about the successful implementation of the append mode functionality which allows timestamped updates to tasks. This functionality enables research results to be appended to existing task details with proper timestamps, maintaining a chronological record of research activities and findings. The append mode integrates seamlessly with the existing task update mechanisms and follows the established patterns for task modification and file regeneration.\n</info added on 2025-06-14T10:15:00.000Z>", "testStrategy": "- Write unit and integration tests to verify correct parsing of command-line arguments and flags.\n- Test that the command invokes ai-services-unified.js in research mode with the correct parameters and context.\n- Validate that context from tasks, files, and custom input is correctly gathered and passed to the research engine.\n- Confirm that output is properly formatted, includes citations, and is displayed in the terminal as expected.\n- Test saving results to files and handling of file system errors.\n- Ensure the command works in both brief and comprehensive modes.\n- Verify that the command does not enter interactive mode and exits cleanly after execution.\n- Check help output and usage documentation for accuracy and completeness.\n- Test the append mode functionality for timestamped updates to task details.\n- Validate that research-to-task linking works correctly with proper task file updates.", @@ -5661,9 +5002,7 @@ "id": 2, "title": "Parameter and Flag Handling", "description": "Define and implement parsing for all arguments, flags, and options accepted by the 'research' command, including validation and default values.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Use a command-line parsing framework to handle parameters. Ensure support for optional and required arguments, order-independence, and clear error messages for invalid input.\n<info added on 2025-05-25T06:00:42.350Z>\n✅ **Parameter and Flag Handling Implementation Complete**\n\nSuccessfully implemented comprehensive parameter validation and processing for the research command:\n\n**✅ Implemented Features:**\n1. **Comprehensive Parameter Validation:**\n - Prompt validation (required, non-empty string)\n - Detail level validation (low, medium, high)\n - Task ID format validation (supports \"15\" and \"15.2\" formats)\n - File path validation (comma-separated, existence checks)\n - Save target validation (security checks for path traversal)\n\n2. **Advanced Parameter Processing:**\n - Comma-separated value parsing for task IDs and file paths\n - Whitespace trimming and normalization\n - Project root detection using `findProjectRoot()`\n - Relative/absolute path handling for files\n - Default value application\n\n3. **Informative Error Messages:**\n - Specific error messages for each validation failure\n - Clear examples of correct usage\n - Helpful suggestions for fixing errors\n - Much more informative than basic Commander.js errors\n\n4. **Structured Output:**\n - Creates validated parameters object with all processed values\n - Proper type conversion and normalization\n - Ready for consumption by core research function\n\n**✅ Validation Examples Tested:**\n- Missing prompt: Shows Commander.js error (expected behavior)\n- Invalid detail level: \"Error: Detail level must be one of: low, medium, high\"\n- Invalid task ID format: \"Error parsing task IDs: Invalid task ID format: \"invalid_format\". Expected format: \"15\" or \"15.2\"\"\n- Valid parameters: Successfully processes and creates structured parameter object\n\n**✅ Security Features:**\n- Path traversal protection for save targets\n- File existence validation\n- Input sanitization and trimming\n\nThe parameter validation is now production-ready and follows the same patterns used throughout the Task Master codebase. Ready to proceed to subtask 94.3 (Context Gathering Utility).\n</info added on 2025-05-25T06:00:42.350Z>", "status": "done" }, @@ -5681,10 +5020,7 @@ "description": "Implement the core research function in scripts/modules/task-manager/ following the add-task.js pattern", "details": "Create a new core function (e.g., research.js) in scripts/modules/task-manager/ that:\n- Accepts parameters: query, context options (task IDs, file paths, custom context), project tree flag, detail level\n- Implements context gathering using the contextGatherer utility from subtask 94.3\n- Integrates with ai-services-unified.js using research role\n- Handles both CLI and MCP output formats\n- Returns structured results with telemetry data\n- Follows the same parameter validation and error handling patterns as add-task.js\n<info added on 2025-05-25T06:29:01.194Z>\n✅ COMPLETED: Added loading spinner to research command\n\n**Implementation Details:**\n- Imported `startLoadingIndicator` and `stopLoadingIndicator` from ui.js\n- Added loading indicator that shows \"Researching with AI...\" during AI service calls\n- Properly wrapped AI service call in try/catch/finally blocks to ensure spinner stops on both success and error\n- Loading indicator only shows in CLI mode (outputFormat === 'text'), not in MCP mode\n- Follows the same pattern as add-task.js for consistent user experience\n\n**Testing:**\n- Tested with `node bin/task-master.js research \"What is TypeScript?\" --detail=low`\n- Confirmed spinner appears during AI processing and disappears when complete\n- Telemetry display works correctly after spinner stops\n\nThe research command now provides the same polished user experience as other AI-powered commands in the system.\n</info added on 2025-05-25T06:29:01.194Z>", "status": "done", - "dependencies": [ - "98.2", - "98.3" - ], + "dependencies": ["98.2", "98.3"], "parentTaskId": 98 }, { @@ -5693,9 +5029,7 @@ "description": "Create the MCP direct function wrapper in mcp-server/src/core/direct-functions/ following the add-task pattern", "details": "Create a new direct function (e.g., research.js) in mcp-server/src/core/direct-functions/ that:\n- Follows the addTaskDirect pattern for parameter handling and error management\n- Uses enableSilentMode/disableSilentMode to prevent console output interference\n- Creates logger wrapper using createLogWrapper utility\n- Validates required parameters (query, projectRoot)\n- Calls the core research function with proper context (session, mcpLog, projectRoot)\n- Returns standardized result object with success/error structure\n- Handles telemetry data propagation\n- Export and register in task-master-core.js", "status": "done", - "dependencies": [ - "98.4" - ], + "dependencies": ["98.4"], "parentTaskId": 98 }, { @@ -5704,9 +5038,7 @@ "description": "Create the MCP tool in mcp-server/src/tools/ following the add-task tool pattern", "details": "Create a new MCP tool (e.g., research.js) in mcp-server/src/tools/ that:\n- Defines zod schema for all research command parameters (query, id, files, context, project-tree, save, detail)\n- Uses withNormalizedProjectRoot HOF to handle project path normalization\n- Calls findTasksJsonPath to locate tasks.json file\n- Invokes the direct function with proper parameter mapping\n- Uses handleApiResult for standardized response formatting\n- Registers the tool as 'research' (snake_case) in the MCP server\n- Handles errors with createErrorResponse\n- Register in mcp-server/src/tools/index.js\n- Update .cursor/mcp.json with tool definition", "status": "done", - "dependencies": [ - "98.5" - ], + "dependencies": ["98.5"], "parentTaskId": 98 }, { @@ -5733,9 +5065,7 @@ "description": "Leverage the successfully implemented append mode functionality to enable seamless integration of research results into task details", "details": "Build upon the existing append mode functionality to enhance research-to-task integration:\n\n- Utilize the timestamped update mechanism for appending research findings to task details\n- Ensure research results maintain chronological order when appended to tasks\n- Integrate with the existing task modification and file regeneration patterns\n- Support both direct research appending and research reference linking\n- Maintain consistency with the established append mode formatting and timestamp patterns\n- Test integration with the existing task update workflows\n\nThis subtask leverages the already-implemented append mode infrastructure to provide a robust foundation for research result integration into the task management system.", "status": "done", - "dependencies": [ - "98.8" - ], + "dependencies": ["98.8"], "parentTaskId": 98 } ] @@ -5765,9 +5095,7 @@ "description": "Modify generateTasksFromPRD to embed PRD context and maintain source mappings", "details": "## Implementation Requirements\n\n### Core Modifications to generateTasksFromPRD:\n1. **Add PRD Context Embedding**\n - Modify task generation prompt to include relevant PRD excerpts\n - Ensure each generated task includes source section references\n - Preserve original PRD language and specifications in task metadata\n\n2. **Implement Context Windowing**\n - For large PRDs, implement intelligent context windowing\n - Include relevant sections for each task being generated\n - Maintain context relationships between related tasks\n\n3. **Enhanced Task Structure**\n - Add prdContext field to task objects\n - Include sourceSection, originalText, and relatedSections\n - Store contextWindow for later use in expansions\n\n### Technical Implementation:\n```javascript\n// Enhanced task generation with context\nconst generateTaskWithContext = async (prdSection, relatedSections, fullPRD) => {\n const contextWindow = buildContextWindow(prdSection, relatedSections, fullPRD);\n const prompt = `\n Generate a task based on this PRD section:\n \n PRIMARY SECTION:\n ${prdSection.content}\n \n RELATED CONTEXT:\n ${contextWindow}\n \n Ensure the task preserves all specific requirements and technical details.\n `;\n \n // Generate task with embedded context\n const task = await generateTask(prompt);\n task.prdContext = {\n sourceSection: prdSection.title,\n originalText: prdSection.content,\n relatedSections: relatedSections.map(s => s.title),\n contextWindow: contextWindow\n };\n \n return task;\n};\n```\n\n### Context Preservation Strategy:\n- Map each task to its source PRD sections\n- Preserve technical specifications and requirements language\n- Maintain relationships between interdependent features\n- Store context for later use in expansion phase\n\n### Integration with Existing Flow:\n- Modify existing generateTasksFromPRD function\n- Maintain backward compatibility with simple PRDs\n- Add new metadata fields without breaking existing structure\n- Ensure context is available for subsequent operations", "status": "pending", - "dependencies": [ - "99.1" - ], + "dependencies": ["99.1"], "parentTaskId": 99 }, { @@ -5776,9 +5104,7 @@ "description": "Add optional --expand-tasks flag and intelligent expansion using preserved PRD context", "details": "## Implementation Requirements\n\n### Core Features:\n1. **Add --expand-tasks CLI Flag**\n - Optional flag for parse-prd command\n - Triggers automatic expansion after initial task generation\n - Configurable expansion depth and strategy\n\n2. **Two-Pass Processing System**\n - First pass: Generate tasks with PRD context preservation\n - Second pass: Expand tasks using their embedded PRD context\n - Maintain context fidelity throughout the process\n\n3. **Context-Aware Expansion Logic**\n - Use preserved PRD context for each task's expansion\n - Include relevant PRD excerpts in expansion prompts\n - Ensure subtasks maintain fidelity to original specifications\n\n### Technical Implementation:\n```javascript\n// Enhanced parse-prd with expansion pipeline\nconst parsePRDWithExpansion = async (prdContent, options) => {\n // Phase 1: Analyze and generate tasks with context\n const prdAnalysis = await analyzePRDStructure(prdContent);\n const tasksWithContext = await generateTasksWithContext(prdAnalysis);\n \n // Phase 2: Expand tasks if requested\n if (options.expandTasks) {\n for (const task of tasksWithContext) {\n if (shouldExpandTask(task, prdAnalysis)) {\n const expandedSubtasks = await expandTaskWithPRDContext(task);\n task.subtasks = expandedSubtasks;\n }\n }\n }\n \n return tasksWithContext;\n};\n\n// Context-aware task expansion\nconst expandTaskWithPRDContext = async (task) => {\n const { prdContext } = task;\n const expansionPrompt = `\n Expand this task into detailed subtasks using the original PRD context:\n \n TASK: ${task.title}\n DESCRIPTION: ${task.description}\n \n ORIGINAL PRD CONTEXT:\n ${prdContext.originalText}\n \n RELATED SECTIONS:\n ${prdContext.contextWindow}\n \n Generate subtasks that preserve all technical details and requirements from the PRD.\n `;\n \n return await generateSubtasks(expansionPrompt);\n};\n```\n\n### CLI Integration:\n- Add --expand-tasks flag to parse-prd command\n- Add --expansion-depth option for controlling subtask levels\n- Add --preserve-detail flag for maximum context preservation\n- Maintain backward compatibility with existing parse-prd usage\n\n### Expansion Strategy:\n- Determine which tasks should be expanded based on complexity\n- Use PRD context to generate accurate, detailed subtasks\n- Preserve technical specifications and implementation details\n- Validate subtask accuracy against original PRD content\n\n### Performance Considerations:\n- Implement batching for large numbers of tasks\n- Add progress indicators for long-running expansions\n- Optimize context window sizes for efficiency\n- Cache PRD analysis results for reuse", "status": "pending", - "dependencies": [ - "99.2" - ], + "dependencies": ["99.2"], "parentTaskId": 99 }, { @@ -5787,9 +5113,7 @@ "description": "Modify existing expand-task functionality to leverage preserved PRD context for more accurate expansions", "details": "## Implementation Requirements\n\n### Core Enhancements to expand-task.js:\n1. **PRD Context Detection**\n - Check if task has embedded prdContext metadata\n - Extract relevant PRD sections for expansion\n - Fall back to existing expansion logic if no PRD context\n\n2. **Context-Enhanced Expansion Prompts**\n - Include original PRD excerpts in expansion prompts\n - Add related section context for comprehensive understanding\n - Preserve technical specifications and requirements language\n\n3. **Validation and Quality Assurance**\n - Validate generated subtasks against original PRD content\n - Ensure technical accuracy and requirement compliance\n - Flag potential discrepancies for review\n\n### Technical Implementation:\n```javascript\n// Enhanced expand-task with PRD context\nconst expandTaskWithContext = async (taskId, options, context) => {\n const task = await getTask(taskId);\n \n // Check for PRD context\n if (task.prdContext) {\n return await expandWithPRDContext(task, options);\n } else {\n // Fall back to existing expansion logic\n return await expandTaskStandard(task, options);\n }\n};\n\nconst expandWithPRDContext = async (task, options) => {\n const { prdContext } = task;\n \n const enhancedPrompt = `\n Expand this task into detailed subtasks using the original PRD context:\n \n TASK DETAILS:\n Title: ${task.title}\n Description: ${task.description}\n Current Details: ${task.details}\n \n ORIGINAL PRD CONTEXT:\n Source Section: ${prdContext.sourceSection}\n Original Requirements:\n ${prdContext.originalText}\n \n RELATED CONTEXT:\n ${prdContext.contextWindow}\n \n EXPANSION REQUIREMENTS:\n - Preserve all technical specifications from the PRD\n - Maintain requirement accuracy and completeness\n - Generate ${options.num || 'appropriate number of'} subtasks\n - Include implementation details that reflect PRD specifics\n \n Generate subtasks that are grounded in the original PRD content.\n `;\n \n const subtasks = await generateSubtasks(enhancedPrompt, options);\n \n // Add PRD context inheritance to subtasks\n subtasks.forEach(subtask => {\n subtask.prdContext = {\n inheritedFrom: task.id,\n sourceSection: prdContext.sourceSection,\n relevantExcerpt: extractRelevantExcerpt(prdContext, subtask)\n };\n });\n \n return subtasks;\n};\n```\n\n### Integration Points:\n1. **Modify existing expand-task.js**\n - Add PRD context detection logic\n - Enhance prompt generation with context\n - Maintain backward compatibility\n\n2. **Update expansion validation**\n - Add PRD compliance checking\n - Implement quality scoring for context fidelity\n - Flag potential accuracy issues\n\n3. **CLI and MCP Integration**\n - Update expand-task command to leverage PRD context\n - Add options for context-aware expansion\n - Maintain existing command interface\n\n### Context Inheritance Strategy:\n- Pass relevant PRD context to generated subtasks\n- Create context inheritance chain for nested expansions\n- Preserve source traceability throughout expansion tree\n- Enable future re-expansion with maintained context\n\n### Quality Assurance Features:\n- Semantic similarity checking between subtasks and PRD\n- Technical requirement compliance validation\n- Automated flagging of potential context drift\n- User feedback integration for continuous improvement", "status": "pending", - "dependencies": [ - "99.2" - ], + "dependencies": ["99.2"], "parentTaskId": 99 }, { @@ -5798,9 +5122,7 @@ "description": "Implement new command-line flags and MCP tool parameters for enhanced PRD parsing", "details": "## Implementation Requirements\n\n### New CLI Options for parse-prd:\n1. **--expand-tasks**\n - Automatically expand generated tasks using PRD context\n - Boolean flag, default false\n - Triggers in-flight expansion pipeline\n\n2. **--preserve-detail**\n - Maximum detail preservation mode\n - Boolean flag, default false\n - Ensures highest fidelity to PRD content\n\n3. **--adaptive-count**\n - Let AI determine optimal task count based on PRD complexity\n - Boolean flag, default false\n - Overrides --num-tasks when enabled\n\n4. **--context-window-size**\n - Control how much PRD context to include in expansions\n - Integer value, default 2000 characters\n - Balances context richness with performance\n\n5. **--expansion-depth**\n - Control how many levels deep to expand tasks\n - Integer value, default 1\n - Prevents excessive nesting\n\n### MCP Tool Parameter Updates:\n```javascript\n// Enhanced parse_prd MCP tool parameters\n{\n input: \"Path to PRD file\",\n output: \"Output path for tasks.json\",\n numTasks: \"Number of top-level tasks (overridden by adaptiveCount)\",\n expandTasks: \"Boolean - automatically expand tasks with PRD context\",\n preserveDetail: \"Boolean - maximum detail preservation mode\",\n adaptiveCount: \"Boolean - AI determines optimal task count\",\n contextWindowSize: \"Integer - context size for expansions\",\n expansionDepth: \"Integer - levels of expansion to perform\",\n research: \"Boolean - use research model for enhanced analysis\",\n force: \"Boolean - overwrite existing files\"\n}\n```\n\n### CLI Command Updates:\n```bash\n# Enhanced parse-prd command examples\ntask-master parse-prd prd.txt --expand-tasks --preserve-detail\ntask-master parse-prd prd.txt --adaptive-count --expansion-depth=2\ntask-master parse-prd prd.txt --context-window-size=3000 --research\n```\n\n### Implementation Details:\n1. **Update commands.js**\n - Add new option definitions\n - Update parse-prd command handler\n - Maintain backward compatibility\n\n2. **Update MCP tool definition**\n - Add new parameter schemas\n - Update tool description and examples\n - Ensure parameter validation\n\n3. **Parameter Processing Logic**\n - Validate parameter combinations\n - Set appropriate defaults\n - Handle conflicting options gracefully\n\n### Validation Rules:\n- expansion-depth must be positive integer ≤ 3\n- context-window-size must be between 500-5000 characters\n- adaptive-count overrides num-tasks when both specified\n- expand-tasks requires either adaptive-count or num-tasks > 5\n\n### Help Documentation Updates:\n- Update command help text with new options\n- Add usage examples for different scenarios\n- Document parameter interactions and constraints\n- Include performance considerations for large PRDs", "status": "pending", - "dependencies": [ - "99.3" - ], + "dependencies": ["99.3"], "parentTaskId": 99 }, { @@ -5809,10 +5131,7 @@ "description": "Create test suite for PRD analysis, context preservation, and expansion accuracy", "details": "## Implementation Requirements\n\n### Test Categories:\n1. **PRD Analysis Testing**\n - Test section identification with various PRD formats\n - Validate complexity scoring accuracy\n - Test boundary detection for different document structures\n - Verify context mapping correctness\n\n2. **Context Preservation Testing**\n - Validate PRD context embedding in generated tasks\n - Test context window generation and sizing\n - Verify source section mapping accuracy\n - Test context inheritance in subtasks\n\n3. **Expansion Accuracy Testing**\n - Compare PRD-grounded vs standard expansions\n - Measure semantic similarity between PRD and subtasks\n - Test technical requirement preservation\n - Validate expansion depth and quality\n\n4. **Integration Testing**\n - Test full parse-prd pipeline with expansion\n - Validate CLI option combinations\n - Test MCP tool parameter handling\n - Verify backward compatibility\n\n### Test Data Requirements:\n```javascript\n// Test PRD samples\nconst testPRDs = {\n simple: \"Basic PRD with minimal technical details\",\n complex: \"Detailed PRD with extensive technical specifications\",\n structured: \"Well-organized PRD with clear sections\",\n unstructured: \"Free-form PRD with mixed content\",\n technical: \"Highly technical PRD with specific requirements\",\n large: \"Very large PRD testing context window limits\"\n};\n```\n\n### Validation Metrics:\n1. **Detail Preservation Score**\n - Semantic similarity between PRD and generated tasks\n - Technical requirement coverage percentage\n - Specification accuracy rating\n\n2. **Context Fidelity Score**\n - Accuracy of source section mapping\n - Relevance of included context windows\n - Quality of context inheritance\n\n3. **Expansion Quality Score**\n - Subtask relevance to parent task and PRD\n - Technical accuracy of implementation details\n - Completeness of requirement coverage\n\n### Test Implementation:\n```javascript\n// Example test structure\ndescribe('Enhanced Parse-PRD', () => {\n describe('PRD Analysis', () => {\n test('should identify sections correctly', async () => {\n const analysis = await analyzePRDStructure(testPRDs.structured);\n expect(analysis.sections).toHaveLength(expectedSectionCount);\n expect(analysis.overallComplexity).toBeGreaterThan(0);\n });\n \n test('should calculate appropriate task count', async () => {\n const analysis = await analyzePRDStructure(testPRDs.complex);\n expect(analysis.recommendedTaskCount).toBeGreaterThan(10);\n });\n });\n \n describe('Context Preservation', () => {\n test('should embed PRD context in tasks', async () => {\n const tasks = await generateTasksWithContext(testPRDs.technical);\n tasks.forEach(task => {\n expect(task.prdContext).toBeDefined();\n expect(task.prdContext.sourceSection).toBeTruthy();\n expect(task.prdContext.originalText).toBeTruthy();\n });\n });\n });\n \n describe('Expansion Accuracy', () => {\n test('should generate relevant subtasks from PRD context', async () => {\n const task = createTestTaskWithPRDContext();\n const subtasks = await expandTaskWithPRDContext(task);\n \n const relevanceScore = calculateRelevanceScore(subtasks, task.prdContext);\n expect(relevanceScore).toBeGreaterThan(0.8);\n });\n });\n});\n```\n\n### Performance Testing:\n- Test with large PRDs (>10,000 words)\n- Measure processing time for different complexity levels\n- Test memory usage with extensive context preservation\n- Validate timeout handling for long-running operations\n\n### Quality Assurance Tools:\n- Automated semantic similarity checking\n- Technical requirement compliance validation\n- Context drift detection algorithms\n- User acceptance testing framework\n\n### Continuous Integration:\n- Add tests to existing CI pipeline\n- Set up performance benchmarking\n- Implement quality gates for PRD processing\n- Create regression testing for context preservation", "status": "pending", - "dependencies": [ - "99.4", - "99.5" - ], + "dependencies": ["99.4", "99.5"], "parentTaskId": 99 } ] @@ -5824,9 +5143,7 @@ "details": "## Core Problem Statement\n\nThe current help menu in `displayHelp()` function (ui.js:434-734) is hardcoded with static command information that can become outdated when:\n\n1. **Command Changes**: New options/flags are added to existing commands\n2. **New Commands**: New commands are added to commands.js but not reflected in help\n3. **Command Removal**: Commands are removed but help text remains\n4. **Inconsistent Documentation**: Help text doesn't match actual command behavior\n5. **Maintenance Burden**: Developers must remember to update help when modifying commands\n\n## Technical Implementation Requirements\n\n### 1. Command Introspection System\n- **Extract Command Metadata**: Parse Commander.js program instance to extract:\n - Command names and aliases\n - Command descriptions\n - All options/flags with their descriptions and default values\n - Required vs optional parameters\n - Argument specifications\n- **Command Categorization**: Implement intelligent categorization based on:\n - Command name patterns (e.g., 'add-*', 'remove-*', 'set-*')\n - Command descriptions containing keywords\n - Manual category overrides for edge cases\n- **Validation**: Ensure all registered commands are captured and categorized\n\n### 2. Dynamic Help Generation Engine\n- **Template System**: Create flexible templates for:\n - Category headers with consistent styling\n - Command entries with proper formatting\n - Option/flag documentation with type information\n - Example usage generation\n- **Formatting Logic**: Implement dynamic column width calculation based on:\n - Terminal width detection\n - Content length analysis\n - Responsive layout adjustments\n- **Content Optimization**: Handle text wrapping, truncation, and spacing automatically\n\n### 3. Enhanced Command Documentation\n- **Auto-Generated Examples**: Create realistic usage examples by:\n - Combining command names with common option patterns\n - Using project-specific values (task IDs, file paths)\n - Showing both simple and complex usage scenarios\n- **Option Details**: Display comprehensive option information:\n - Short and long flag variants (-f, --file)\n - Data types and format requirements\n - Default values and behavior\n - Required vs optional indicators\n- **Cross-References**: Add intelligent linking between related commands\n\n### 4. Integration Points\n- **Commands.js Integration**: \n - Access the programInstance after all commands are registered\n - Extract metadata without affecting command functionality\n - Handle edge cases like hidden commands or aliases\n- **UI.js Refactoring**:\n - Replace static commandCategories array with dynamic generation\n - Maintain existing visual styling and layout\n - Preserve terminal width responsiveness\n - Keep configuration and quick start sections\n\n### 5. Category Classification Logic\nImplement smart categorization rules:\n```javascript\nconst categoryRules = {\n 'Project Setup & Configuration': ['init', 'models'],\n 'Task Generation': ['parse-prd', 'generate'],\n 'Task Management': ['list', 'set-status', 'update', 'add-task', 'remove-task'],\n 'Subtask Management': ['add-subtask', 'remove-subtask', 'clear-subtasks'],\n 'Task Analysis & Breakdown': ['analyze-complexity', 'complexity-report', 'expand', 'research'],\n 'Task Navigation & Viewing': ['next', 'show'],\n 'Dependency Management': ['add-dependency', 'remove-dependency', 'validate-dependencies', 'fix-dependencies']\n};\n```\n\n### 6. Error Handling and Fallbacks\n- **Graceful Degradation**: Fall back to static help if introspection fails\n- **Missing Information**: Handle commands with incomplete metadata\n- **Performance Considerations**: Cache generated help content when possible\n- **Debug Mode**: Provide verbose output for troubleshooting categorization\n\n## Implementation Architecture\n\n### Core Functions to Implement:\n1. **`extractCommandMetadata(programInstance)`**\n - Parse Commander.js instance\n - Extract all command and option information\n - Return structured metadata object\n\n2. **`categorizeCommands(commandMetadata)`**\n - Apply categorization rules\n - Handle special cases and overrides\n - Return categorized command structure\n\n3. **`generateDynamicHelp(categorizedCommands)`**\n - Create formatted help content\n - Apply consistent styling\n - Handle responsive layout\n\n4. **`displayDynamicHelp(programInstance)`**\n - Replace current displayHelp() function\n - Integrate with existing banner and footer content\n - Maintain backward compatibility\n\n### File Structure Changes:\n- **ui.js**: Replace static help with dynamic generation\n- **commands.js**: Ensure all commands have proper descriptions and option documentation\n- **New utility functions**: Add command introspection helpers\n\n## Testing Requirements\n\n### Unit Tests:\n- Command metadata extraction accuracy\n- Categorization logic correctness\n- Help content generation formatting\n- Terminal width responsiveness\n\n### Integration Tests:\n- Full help menu generation from actual commands\n- Consistency between help and actual command behavior\n- Performance with large numbers of commands\n\n### Manual Testing:\n- Visual verification of help output\n- Terminal width adaptation testing\n- Comparison with current static help for completeness\n\n## Benefits\n\n1. **Automatic Synchronization**: Help always reflects actual command state\n2. **Reduced Maintenance**: No manual help updates needed for command changes\n3. **Consistency**: Guaranteed alignment between help and implementation\n4. **Extensibility**: Easy to add new categorization rules or formatting\n5. **Accuracy**: Eliminates human error in help documentation\n6. **Developer Experience**: Faster development with automatic documentation\n\n## Migration Strategy\n\n1. **Phase 1**: Implement introspection system alongside existing static help\n2. **Phase 2**: Add categorization and dynamic generation\n3. **Phase 3**: Replace static help with dynamic system\n4. **Phase 4**: Remove static command definitions and add validation tests\n\nThis implementation will create a self-documenting CLI that maintains accuracy and reduces the burden on developers to manually maintain help documentation.", "testStrategy": "", "status": "pending", - "dependencies": [ - 4 - ], + "dependencies": [4], "priority": "medium", "subtasks": [ { @@ -5844,9 +5161,7 @@ "description": "Implement smart categorization logic to group commands into logical categories for the help menu", "details": "## Implementation Requirements\n\n### Core Function: `categorizeCommands(commandMetadata)`\n\n**Location**: Add to `ui.js` or `help-utils.js` module\n\n**Functionality**:\n1. **Category Definition System**:\n - Define category rules with command name patterns\n - Support keyword-based categorization from descriptions\n - Allow manual overrides for edge cases\n - Maintain existing category structure for consistency\n\n2. **Categorization Rules**:\n```javascript\nconst categoryRules = {\n 'Project Setup & Configuration': {\n commands: ['init', 'models'],\n patterns: [/^models/, /^init/],\n keywords: ['setup', 'configure', 'initialization'],\n color: 'blue'\n },\n 'Task Generation': {\n commands: ['parse-prd', 'generate'],\n patterns: [/^parse/, /^generate/],\n keywords: ['create', 'generate', 'parse'],\n color: 'cyan'\n },\n 'Task Management': {\n commands: ['list', 'set-status', 'update', 'add-task', 'remove-task'],\n patterns: [/^(list|set-|update|add-|remove-)/, /status$/],\n keywords: ['manage', 'update', 'modify', 'status'],\n color: 'green'\n },\n 'Subtask Management': {\n commands: ['add-subtask', 'remove-subtask', 'clear-subtasks'],\n patterns: [/subtask/],\n keywords: ['subtask', 'sub-task'],\n color: 'yellow'\n },\n 'Task Analysis & Breakdown': {\n commands: ['analyze-complexity', 'complexity-report', 'expand', 'research'],\n patterns: [/^(analyze|complexity|expand|research)/],\n keywords: ['analyze', 'complexity', 'expand', 'research', 'breakdown'],\n color: 'magenta'\n },\n 'Task Navigation & Viewing': {\n commands: ['next', 'show'],\n patterns: [/^(next|show|view|display)/],\n keywords: ['view', 'show', 'display', 'navigate'],\n color: 'cyan'\n },\n 'Dependency Management': {\n commands: ['add-dependency', 'remove-dependency', 'validate-dependencies', 'fix-dependencies'],\n patterns: [/dependency|dependencies/],\n keywords: ['dependency', 'dependencies', 'depend'],\n color: 'blue'\n }\n};\n```\n\n3. **Categorization Algorithm**:\n - **Exact Match**: Check if command name is in category's command list\n - **Pattern Matching**: Test command name against regex patterns\n - **Keyword Analysis**: Search command description for category keywords\n - **Fallback Category**: Create \"Other Commands\" for uncategorized commands\n - **Priority System**: Handle commands that match multiple categories\n\n4. **Category Validation**:\n - Ensure all commands are categorized\n - Detect and warn about duplicate categorizations\n - Validate category color assignments\n - Check for empty categories\n\n### Technical Implementation:\n1. **Categorization Logic**:\n```javascript\nfunction categorizeCommands(commandMetadata) {\n const categorizedCommands = {};\n const uncategorized = [];\n \n // Initialize categories\n Object.keys(categoryRules).forEach(categoryName => {\n categorizedCommands[categoryName] = {\n ...categoryRules[categoryName],\n commands: []\n };\n });\n \n // Categorize each command\n Object.values(commandMetadata).forEach(command => {\n const category = findBestCategory(command);\n if (category) {\n categorizedCommands[category].commands.push(command);\n } else {\n uncategorized.push(command);\n }\n });\n \n // Handle uncategorized commands\n if (uncategorized.length > 0) {\n categorizedCommands['Other Commands'] = {\n color: 'gray',\n commands: uncategorized\n };\n }\n \n return categorizedCommands;\n}\n```\n\n2. **Best Category Detection**:\n - Score each category based on match strength\n - Prefer exact command name matches over patterns\n - Weight keyword matches by frequency and relevance\n - Return highest-scoring category\n\n3. **Dynamic Category Creation**:\n - Support adding new categories without code changes\n - Allow category rules to be loaded from configuration\n - Handle category inheritance and hierarchies\n\n### Testing Requirements:\n- Test categorization accuracy for all existing commands\n- Verify handling of new commands not in predefined lists\n- Test pattern matching and keyword detection\n- Validate category completeness and no duplicates", "status": "pending", - "dependencies": [ - 1 - ], + "dependencies": [1], "parentTaskId": 100 }, { @@ -5864,9 +5179,7 @@ "description": "Replace the static help system with the new dynamic help generation and ensure seamless integration", "details": "## Implementation Requirements\n\n### Core Integration Tasks:\n\n1. **Replace displayHelp() Function**:\n - **Location**: `ui.js` lines 434-734\n - **Action**: Replace static help content with dynamic generation\n - **Preserve**: Existing function signature and color scheme\n - **Enhance**: Add new parameters for filtering and customization\n\n2. **Update Function Signature**:\n```javascript\n// Current: displayHelp()\n// New: displayHelp(options = {})\nfunction displayHelp(options = {}) {\n const {\n category = null, // Filter by specific category\n command = null, // Show help for specific command\n search = null, // Search query for commands\n verbose = false, // Show detailed help\n noColor = false // Disable colors for accessibility\n } = options;\n \n // Dynamic help generation logic\n}\n```\n\n3. **Integration with commands.js**:\n - **Access Program Instance**: Get reference to Commander.js program\n - **Timing**: Ensure commands are fully registered before help generation\n - **Caching**: Cache command metadata to avoid repeated parsing\n\n4. **Update Help Command Registration**:\n```javascript\n// In commands.js, update help command\nprogram\n .command('help [command]')\n .description('Display help information')\n .option('-c, --category <category>', 'Show help for specific category')\n .option('-s, --search <query>', 'Search commands by keyword')\n .option('-v, --verbose', 'Show detailed help information')\n .option('--no-color', 'Disable colored output')\n .action(async (command, options) => {\n displayHelp({\n command,\n category: options.category,\n search: options.search,\n verbose: options.verbose,\n noColor: !options.color\n });\n });\n```\n\n5. **Fallback and Error Handling**:\n - **Graceful Degradation**: Fall back to static help if dynamic generation fails\n - **Error Recovery**: Handle malformed command definitions\n - **Performance**: Ensure help generation doesn't slow down CLI startup\n\n### Technical Implementation:\n\n1. **Program Instance Access**:\n```javascript\n// Method 1: Pass program instance to displayHelp\nfunction displayHelp(options = {}, programInstance = null) {\n if (!programInstance) {\n // Fallback to static help or error\n console.warn('Dynamic help unavailable, using static fallback');\n return displayStaticHelp();\n }\n \n const commandMetadata = extractCommandMetadata(programInstance);\n const categorizedCommands = categorizeCommands(commandMetadata);\n return generateHelpContent(categorizedCommands, options);\n}\n\n// Method 2: Global program reference\nlet globalProgramInstance = null;\nexport function setProgramInstance(program) {\n globalProgramInstance = program;\n}\n```\n\n2. **Initialization Sequence**:\n - Commands are registered in `commands.js`\n - Program instance is made available to help system\n - Help system caches command metadata on first use\n - Subsequent help calls use cached data\n\n3. **Backward Compatibility**:\n - Maintain existing `displayHelp()` calls without parameters\n - Preserve existing color scheme and formatting style\n - Keep same output structure for scripts that parse help output\n\n4. **Performance Optimization**:\n - Cache command metadata after first extraction\n - Lazy-load help content generation\n - Minimize impact on CLI startup time\n - Support incremental cache updates\n\n### Integration Points:\n\n1. **Update All Help Call Sites**:\n - Search codebase for `displayHelp()` calls\n - Update calls to pass program instance or use global reference\n - Test all help invocation paths\n\n2. **Enhanced Help Commands**:\n - `task-master help` - General help (existing behavior)\n - `task-master help <command>` - Command-specific help\n - `task-master help --category <cat>` - Category-specific help\n - `task-master help --search <query>` - Search-based help\n\n3. **Error Handling Integration**:\n - Update error messages to suggest relevant help commands\n - Provide contextual help suggestions based on failed commands\n - Integrate with existing error reporting system\n\n### Testing Requirements:\n\n1. **Integration Testing**:\n - Test help system with all existing commands\n - Verify backward compatibility with existing help calls\n - Test new help command options and parameters\n\n2. **Performance Testing**:\n - Measure help generation time with full command set\n - Test CLI startup time impact\n - Verify caching effectiveness\n\n3. **Compatibility Testing**:\n - Test with different terminal types and capabilities\n - Verify color output and no-color modes\n - Test with various screen sizes and widths\n\n4. **Error Scenario Testing**:\n - Test behavior with malformed command definitions\n - Verify fallback to static help when needed\n - Test graceful handling of missing metadata\n\n### Migration Strategy:\n\n1. **Phase 1**: Implement dynamic help system alongside existing static help\n2. **Phase 2**: Update help command to use dynamic system with fallback\n3. **Phase 3**: Replace all displayHelp() calls with dynamic version\n4. **Phase 4**: Remove static help content and cleanup old code\n5. **Phase 5**: Add enhanced help features (search, filtering, etc.)", "status": "pending", - "dependencies": [ - 3 - ], + "dependencies": [3], "parentTaskId": 100 }, { @@ -5875,9 +5188,7 @@ "description": "Implement advanced help features including command search, category filtering, and contextual help suggestions", "details": "## Implementation Requirements\n\n### Enhanced Help Features:\n\n1. **Command Search Functionality**:\n```javascript\nfunction searchCommands(query, commandMetadata) {\n const results = [];\n const searchTerms = query.toLowerCase().split(' ');\n \n Object.values(commandMetadata).forEach(command => {\n let score = 0;\n \n // Search in command name (highest weight)\n if (command.name.toLowerCase().includes(query.toLowerCase())) {\n score += 10;\n }\n \n // Search in description (medium weight)\n if (command.description && command.description.toLowerCase().includes(query.toLowerCase())) {\n score += 5;\n }\n \n // Search in option descriptions (lower weight)\n command.options?.forEach(option => {\n if (option.description.toLowerCase().includes(query.toLowerCase())) {\n score += 2;\n }\n });\n \n // Fuzzy matching for command names\n if (fuzzyMatch(command.name, query)) {\n score += 3;\n }\n \n if (score > 0) {\n results.push({ command, score });\n }\n });\n \n return results.sort((a, b) => b.score - a.score);\n}\n```\n\n2. **Category Filtering**:\n - Allow users to view help for specific categories only\n - Support partial category name matching\n - Provide category list when invalid category specified\n - Enable multiple category selection\n\n3. **Contextual Help Suggestions**:\n```javascript\nfunction suggestRelatedCommands(commandName, commandMetadata) {\n const suggestions = [];\n const command = commandMetadata[commandName];\n \n if (!command) return suggestions;\n \n // Find commands in same category\n const category = findCommandCategory(commandName);\n if (category) {\n suggestions.push(...getCategoryCommands(category));\n }\n \n // Find commands with similar names\n Object.keys(commandMetadata).forEach(name => {\n if (name !== commandName && similarity(name, commandName) > 0.6) {\n suggestions.push(commandMetadata[name]);\n }\n });\n \n // Find commands with related functionality\n const keywords = extractKeywords(command.description);\n keywords.forEach(keyword => {\n const related = findCommandsByKeyword(keyword, commandMetadata);\n suggestions.push(...related);\n });\n \n return deduplicateAndScore(suggestions);\n}\n```\n\n4. **Interactive Help Mode**:\n - Implement step-by-step help wizard\n - Guide users through command selection\n - Provide examples and use cases\n - Support help history and bookmarks\n\n### Advanced Features:\n\n1. **Help Caching and Performance**:\n```javascript\nclass HelpCache {\n constructor() {\n this.cache = new Map();\n this.lastUpdate = null;\n this.commandMetadata = null;\n }\n \n getHelp(key, generator) {\n if (this.cache.has(key) && !this.isStale()) {\n return this.cache.get(key);\n }\n \n const content = generator();\n this.cache.set(key, content);\n return content;\n }\n \n invalidate() {\n this.cache.clear();\n this.lastUpdate = Date.now();\n }\n \n isStale() {\n return Date.now() - this.lastUpdate > 300000; // 5 minutes\n }\n}\n```\n\n2. **Help Export and Documentation**:\n - Export help content to markdown format\n - Generate man pages from command metadata\n - Create HTML documentation\n - Support JSON export for API documentation\n\n3. **Accessibility Enhancements**:\n - Screen reader friendly output\n - High contrast mode support\n - Keyboard navigation for interactive help\n - Alternative text descriptions for visual elements\n\n4. **Internationalization Support**:\n - Support for multiple languages\n - Localized command descriptions\n - Cultural formatting preferences\n - RTL language support\n\n### Command-Specific Help Features:\n\n1. **Detailed Command Help**:\n```javascript\nfunction displayCommandHelp(commandName, commandMetadata) {\n const command = commandMetadata[commandName];\n if (!command) {\n console.error(`Command '${commandName}' not found.`);\n suggestSimilarCommands(commandName, commandMetadata);\n return;\n }\n \n console.log(chalk.cyan.bold(`\\\\n${command.name.toUpperCase()} COMMAND HELP\\\\n`));\n console.log(`Description: ${command.description}\\\\n`);\n \n // Usage examples\n if (command.examples) {\n console.log(chalk.yellow.bold('Examples:'));\n command.examples.forEach(example => {\n console.log(` ${chalk.green(example.command)}`);\n console.log(` ${example.description}\\\\n`);\n });\n }\n \n // Detailed options\n if (command.options && command.options.length > 0) {\n console.log(chalk.yellow.bold('Options:'));\n command.options.forEach(option => {\n console.log(` ${chalk.cyan(option.flags.padEnd(20))} ${option.description}`);\n if (option.defaultValue) {\n console.log(`${' '.repeat(22)}Default: ${option.defaultValue}`);\n }\n if (option.examples) {\n console.log(`${' '.repeat(22)}Example: ${option.examples[0]}`);\n }\n });\n }\n \n // Related commands\n const related = suggestRelatedCommands(commandName, commandMetadata);\n if (related.length > 0) {\n console.log(chalk.yellow.bold('\\\\nRelated Commands:'));\n related.slice(0, 5).forEach(cmd => {\n console.log(` ${chalk.cyan(cmd.name)} - ${cmd.description}`);\n });\n }\n}\n```\n\n2. **Usage Examples Generation**:\n - Auto-generate common usage patterns\n - Include real-world scenarios\n - Show before/after examples\n - Provide troubleshooting tips\n\n### Error Integration:\n\n1. **Smart Error Messages**:\n```javascript\nfunction enhanceErrorWithHelp(error, commandName, commandMetadata) {\n console.error(chalk.red(error.message));\n \n // Suggest correct usage\n if (commandMetadata[commandName]) {\n console.log(chalk.yellow('\\\\nCorrect usage:'));\n console.log(` ${formatCommandUsage(commandMetadata[commandName])}`);\n }\n \n // Suggest similar commands\n const suggestions = findSimilarCommands(commandName, commandMetadata);\n if (suggestions.length > 0) {\n console.log(chalk.yellow('\\\\nDid you mean:'));\n suggestions.slice(0, 3).forEach(cmd => {\n console.log(` ${chalk.cyan(cmd.name)} - ${cmd.description}`);\n });\n }\n \n // Provide help command\n console.log(chalk.gray(`\\\\nFor more help: task-master help ${commandName}`));\n}\n```\n\n### Testing Requirements:\n\n1. **Search Functionality Testing**:\n - Test search accuracy with various queries\n - Verify fuzzy matching and scoring\n - Test performance with large command sets\n - Validate search result ranking\n\n2. **Feature Integration Testing**:\n - Test all new help command options\n - Verify category filtering accuracy\n - Test contextual suggestions relevance\n - Validate caching behavior\n\n3. **Accessibility Testing**:\n - Test with screen readers\n - Verify keyboard navigation\n - Test color contrast and no-color modes\n - Validate output formatting\n\n4. **Performance Testing**:\n - Measure search response times\n - Test caching effectiveness\n - Verify memory usage with large datasets\n - Test concurrent help requests\n\n### Documentation Updates:\n\n1. **Update README**:\n - Document new help features\n - Provide usage examples\n - Update command reference\n - Add troubleshooting section\n\n2. **Create Help Documentation**:\n - Comprehensive help system guide\n - Advanced usage patterns\n - Customization options\n - Integration examples", "status": "pending", - "dependencies": [ - 4 - ], + "dependencies": [4], "parentTaskId": 100 }, { @@ -5916,9 +5227,7 @@ "description": "Implement intelligent content formatting to convert Task Master tasks into properly formatted GitHub issues", "details": "## Implementation Requirements\n\n### Core Formatting Service\n```javascript\n// scripts/modules/github/task-formatter.js\nclass TaskToGitHubFormatter {\n constructor(options = {}) {\n this.options = {\n includeTaskId: true,\n includeMetadata: true,\n convertSubtasksToChecklist: true,\n addTaskMasterReference: true,\n ...options\n };\n }\n \n formatTaskAsIssue(task, exportOptions = {}) {\n return {\n title: this.formatTitle(task, exportOptions),\n body: this.formatBody(task, exportOptions),\n labels: this.formatLabels(task, exportOptions),\n assignees: this.formatAssignees(task, exportOptions)\n };\n }\n}\n```\n\n### Title Formatting\n```javascript\nformatTitle(task, options) {\n let title = task.title;\n \n // Add task ID prefix if enabled\n if (this.options.includeTaskId && !options.hideTaskId) {\n title = `[Task ${task.id}] ${title}`;\n }\n \n // Add priority indicator for high priority tasks\n if (task.priority === 'high') {\n title = `🔥 ${title}`;\n }\n \n // Truncate if too long (GitHub limit is 256 characters)\n if (title.length > 250) {\n title = title.substring(0, 247) + '...';\n }\n \n return title;\n}\n```\n\n### Body Formatting\n```javascript\nformatBody(task, options) {\n let body = '';\n \n // Header with task metadata\n if (this.options.includeMetadata) {\n body += this.formatMetadataSection(task);\n }\n \n // Main description\n if (task.description) {\n body += `## Description\\n\\n${task.description}\\n\\n`;\n }\n \n // Implementation details\n if (task.details) {\n body += `## Implementation Details\\n\\n${this.formatDetails(task.details)}\\n\\n`;\n }\n \n // Test strategy\n if (task.testStrategy) {\n body += `## Test Strategy\\n\\n${task.testStrategy}\\n\\n`;\n }\n \n // Subtasks as checklist\n if (task.subtasks && task.subtasks.length > 0 && this.options.convertSubtasksToChecklist) {\n body += this.formatSubtasksSection(task.subtasks);\n }\n \n // Dependencies\n if (task.dependencies && task.dependencies.length > 0) {\n body += this.formatDependenciesSection(task.dependencies);\n }\n \n // Task Master reference\n if (this.options.addTaskMasterReference) {\n body += this.formatTaskMasterReference(task);\n }\n \n return body;\n}\n```\n\n### Metadata Section\n```javascript\nformatMetadataSection(task) {\n let metadata = '## Task Information\\n\\n';\n metadata += `| Field | Value |\\n`;\n metadata += `|-------|-------|\\n`;\n metadata += `| **Task ID** | ${task.id} |\\n`;\n metadata += `| **Priority** | ${this.formatPriority(task.priority)} |\\n`;\n metadata += `| **Status** | ${this.formatStatus(task.status)} |\\n`;\n \n if (task.dependencies && task.dependencies.length > 0) {\n metadata += `| **Dependencies** | ${task.dependencies.join(', ')} |\\n`;\n }\n \n if (task.complexityScore) {\n metadata += `| **Complexity** | ${task.complexityScore}/10 |\\n`;\n }\n \n metadata += '\\n';\n return metadata;\n}\n```\n\n### Subtasks Formatting\n```javascript\nformatSubtasksSection(subtasks) {\n let section = '## Subtasks\\n\\n';\n \n subtasks.forEach(subtask => {\n const checked = subtask.status === 'done' ? 'x' : ' ';\n section += `- [${checked}] **${subtask.title}**`;\n \n if (subtask.description) {\n section += ` - ${subtask.description}`;\n }\n \n section += '\\n';\n \n // Add subtask details as indented content\n if (subtask.details) {\n const indentedDetails = subtask.details\n .split('\\n')\n .map(line => ` ${line}`)\n .join('\\n');\n section += `${indentedDetails}\\n`;\n }\n });\n \n section += '\\n';\n return section;\n}\n```\n\n### Label Generation\n```javascript\nformatLabels(task, options) {\n const labels = [];\n \n // Always add task-master label\n labels.push('task-master');\n \n // Priority-based labels\n if (task.priority === 'high') {\n labels.push('priority:high');\n } else if (task.priority === 'low') {\n labels.push('priority:low');\n }\n \n // Status-based labels\n if (task.status === 'in-progress') {\n labels.push('in-progress');\n }\n \n // Complexity-based labels\n if (task.complexityScore >= 8) {\n labels.push('complexity:high');\n } else if (task.complexityScore <= 3) {\n labels.push('complexity:low');\n }\n \n // Custom labels from options\n if (options.labels) {\n labels.push(...options.labels);\n }\n \n return labels;\n}\n```\n\n### Markdown Conversion\n```javascript\nformatDetails(details) {\n // Convert Task Master specific formatting to GitHub markdown\n let formatted = details;\n \n // Convert code blocks\n formatted = formatted.replace(/```(\\w+)?\\n([\\s\\S]*?)```/g, (match, lang, code) => {\n return `\\`\\`\\`${lang || ''}\\n${code}\\`\\`\\``;\n });\n \n // Convert inline code\n formatted = formatted.replace(/`([^`]+)`/g, '`$1`');\n \n // Convert headers\n formatted = formatted.replace(/^(#{1,6})\\s+(.+)$/gm, '$1 $2');\n \n // Convert lists\n formatted = formatted.replace(/^\\s*[-*+]\\s+(.+)$/gm, '- $1');\n \n // Convert numbered lists\n formatted = formatted.replace(/^\\s*\\d+\\.\\s+(.+)$/gm, (match, content, offset, string) => {\n const lineNumber = string.substring(0, offset).split('\\n').length;\n return `${lineNumber}. ${content}`;\n });\n \n return formatted;\n}\n```\n\n### Task Master Reference\n```javascript\nformatTaskMasterReference(task) {\n return `\\n---\\n\\n*This issue was exported from Task Master*\\n\\n` +\n `**Original Task**: #${task.id}\\n` +\n `**Exported**: ${new Date().toISOString()}\\n` +\n `**Task Master Project**: ${this.getProjectName()}\\n`;\n}\n```\n\n### Template System\n```javascript\nclass IssueTemplateManager {\n constructor() {\n this.templates = {\n default: new DefaultTemplate(),\n bug: new BugTemplate(),\n feature: new FeatureTemplate(),\n epic: new EpicTemplate()\n };\n }\n \n applyTemplate(task, templateName, options) {\n const template = this.templates[templateName] || this.templates.default;\n return template.format(task, options);\n }\n}\n\nclass BugTemplate extends TaskToGitHubFormatter {\n formatTitle(task, options) {\n return `🐛 [BUG] ${task.title}`;\n }\n \n formatBody(task, options) {\n let body = '## Bug Report\\n\\n';\n body += `**Task ID**: ${task.id}\\n\\n`;\n \n if (task.description) {\n body += `### Description\\n${task.description}\\n\\n`;\n }\n \n if (task.details) {\n body += `### Steps to Reproduce\\n${task.details}\\n\\n`;\n }\n \n body += `### Expected Behavior\\n<!-- Describe what should happen -->\\n\\n`;\n body += `### Actual Behavior\\n<!-- Describe what actually happens -->\\n\\n`;\n \n return body + this.formatTaskMasterReference(task);\n }\n}\n```\n\n### Testing Requirements\n- Unit tests for all formatting methods\n- Test with various task structures (with/without subtasks, different priorities)\n- Markdown conversion accuracy testing\n- Template system testing\n- Character limit and truncation testing\n- Special character handling (emojis, unicode)\n- Large content handling and performance testing", "status": "pending", - "dependencies": [ - 1 - ], + "dependencies": [1], "parentTaskId": 101 }, { @@ -5936,9 +5245,7 @@ "description": "Implement the command-line interface and MCP tools for GitHub export functionality", "details": "## Implementation Requirements\n\n### CLI Command Implementation\n```javascript\n// In scripts/modules/commands.js\nprogram\n .command('github-export')\n .description('Export Task Master task to GitHub issue with bidirectional linking')\n .requiredOption('-i, --id <taskId>', 'Task ID to export')\n .requiredOption('-r, --repo <owner/repo>', 'Target GitHub repository (owner/repo format)')\n .option('-t, --token <token>', 'GitHub Personal Access Token (or use GITHUB_TOKEN env var)')\n .option('--title <title>', 'Override the GitHub issue title')\n .option('--labels <labels>', 'Comma-separated list of GitHub labels to add')\n .option('--assignees <assignees>', 'Comma-separated list of GitHub usernames to assign')\n .option('--milestone <milestone>', 'GitHub milestone number or title')\n .option('--template <template>', 'Issue template to use (bug, feature, epic, default)')\n .option('--include-subtasks', 'Include subtasks as checklist items in the issue')\n .option('--separate-subtasks', 'Create separate GitHub issues for each subtask')\n .option('--dry-run', 'Preview the issue content without actually creating it')\n .option('--force', 'Overwrite existing GitHub link if task is already linked')\n .option('--no-link-back', 'Do not add Task Master reference to the GitHub issue')\n .option('--sync', 'Enable automatic synchronization between task and issue')\n .action(async (options) => {\n try {\n await handleGitHubExport(options);\n } catch (error) {\n console.error(chalk.red('GitHub export failed:'), error.message);\n process.exit(1);\n }\n });\n```\n\n### Core Export Handler\n```javascript\n// scripts/modules/github/github-export-handler.js\nasync function handleGitHubExport(options) {\n const {\n id: taskId,\n repo: repository,\n token,\n title: titleOverride,\n labels,\n assignees,\n milestone,\n template = 'default',\n includeSubtasks,\n separateSubtasks,\n dryRun,\n force,\n linkBack = true,\n sync = false\n } = options;\n\n // Validate inputs\n await validateExportOptions(options);\n \n // Get task details\n const task = await getTask(taskId);\n if (!task) {\n throw new Error(`Task ${taskId} not found`);\n }\n \n // Check for existing GitHub link\n if (task.metadata?.githubIssue && !force) {\n const existingUrl = task.metadata.githubIssue.url;\n console.log(chalk.yellow(`Task ${taskId} is already linked to GitHub issue: ${existingUrl}`));\n console.log(chalk.gray('Use --force to overwrite the existing link'));\n return;\n }\n \n // Initialize GitHub service\n const githubToken = token || process.env.GITHUB_TOKEN;\n if (!githubToken) {\n throw new Error('GitHub token required. Use --token flag or set GITHUB_TOKEN environment variable');\n }\n \n const githubService = new GitHubExportService(githubToken);\n const formatter = new TaskToGitHubFormatter();\n const linkManager = new GitHubLinkManager(githubService);\n \n // Format task content\n const exportOptions = {\n titleOverride,\n labels: labels ? labels.split(',').map(l => l.trim()) : [],\n assignees: assignees ? assignees.split(',').map(a => a.trim()) : [],\n milestone,\n template,\n includeSubtasks,\n linkBack\n };\n \n const issueData = formatter.formatTaskAsIssue(task, exportOptions);\n \n // Dry run - just show what would be created\n if (dryRun) {\n console.log(chalk.cyan('\\\\n=== DRY RUN - GitHub Issue Preview ===\\\\n'));\n console.log(chalk.bold('Title:'), issueData.title);\n console.log(chalk.bold('\\\\nLabels:'), issueData.labels.join(', ') || 'None');\n console.log(chalk.bold('\\\\nAssignees:'), issueData.assignees.join(', ') || 'None');\n console.log(chalk.bold('\\\\nBody:'));\n console.log(issueData.body);\n console.log(chalk.cyan('\\\\n=== End Preview ===\\\\n'));\n return;\n }\n \n // Show progress\n console.log(chalk.blue(`Exporting task ${taskId} to GitHub repository ${repository}...`));\n \n // Export to GitHub\n const result = await githubService.exportTask(task, repository, exportOptions);\n \n if (result.success) {\n console.log(chalk.green(`✅ Successfully exported task ${taskId} to GitHub!`));\n console.log(chalk.cyan(`GitHub Issue: ${result.issueUrl}`));\n \n // Add bidirectional link\n await linkManager.addGitHubLinkToTask(taskId, result.issueUrl, result.issue.number, repository);\n \n if (linkBack) {\n await linkManager.updateGitHubIssueWithTaskReference(result.issueUrl, taskId, getProjectName());\n }\n \n if (sync) {\n await linkManager.enableSync(taskId);\n console.log(chalk.blue('🔄 Synchronization enabled for this task'));\n }\n \n // Handle subtasks if requested\n if (separateSubtasks && task.subtasks && task.subtasks.length > 0) {\n console.log(chalk.blue('\\\\nExporting subtasks as separate issues...'));\n await exportSubtasksAsSeparateIssues(task.subtasks, repository, githubService, linkManager);\n }\n \n } else {\n throw new Error(result.error || 'Export failed');\n }\n}\n```\n\n### MCP Tool Implementation\n```javascript\n// mcp-server/src/tools/github-export.js\nimport { githubExportDirect } from '../core/direct-functions/github-export-direct.js';\nimport { handleApiResult, withNormalizedProjectRoot } from './utils.js';\n\nexport function registerGitHubExportTool(server) {\n server.addTool({\n name: \"github_export_task\",\n description: \"Export a Task Master task to GitHub issue with bidirectional linking\",\n parameters: {\n type: \"object\",\n properties: {\n taskId: {\n type: \"string\",\n description: \"Task ID to export (required)\"\n },\n repository: {\n type: \"string\", \n description: \"GitHub repository in owner/repo format (required)\"\n },\n token: {\n type: \"string\",\n description: \"GitHub Personal Access Token (optional if GITHUB_TOKEN env var is set)\"\n },\n options: {\n type: \"object\",\n properties: {\n title: {\n type: \"string\",\n description: \"Override the GitHub issue title\"\n },\n labels: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"GitHub labels to add to the issue\"\n },\n assignees: {\n type: \"array\", \n items: { type: \"string\" },\n description: \"GitHub usernames to assign to the issue\"\n },\n milestone: {\n type: \"string\",\n description: \"GitHub milestone number or title\"\n },\n template: {\n type: \"string\",\n enum: [\"default\", \"bug\", \"feature\", \"epic\"],\n description: \"Issue template to use\"\n },\n includeSubtasks: {\n type: \"boolean\",\n description: \"Include subtasks as checklist items\"\n },\n separateSubtasks: {\n type: \"boolean\", \n description: \"Create separate issues for subtasks\"\n },\n dryRun: {\n type: \"boolean\",\n description: \"Preview without creating the issue\"\n },\n force: {\n type: \"boolean\",\n description: \"Overwrite existing GitHub link\"\n },\n linkBack: {\n type: \"boolean\",\n description: \"Add Task Master reference to GitHub issue\"\n },\n enableSync: {\n type: \"boolean\",\n description: \"Enable automatic synchronization\"\n }\n }\n },\n projectRoot: {\n type: \"string\",\n description: \"Project root directory path\"\n }\n },\n required: [\"taskId\", \"repository\"]\n },\n execute: withNormalizedProjectRoot(async (args, { log, session }) => {\n try {\n const result = await githubExportDirect(args, log, { session });\n return handleApiResult(result, log);\n } catch (error) {\n log(`GitHub export error: ${error.message}`);\n return {\n success: false,\n error: error.message\n };\n }\n })\n });\n}\n```\n\n### Direct Function Implementation\n```javascript\n// mcp-server/src/core/direct-functions/github-export-direct.js\nimport { handleGitHubExport } from '../../../../scripts/modules/github/github-export-handler.js';\nimport { createLogWrapper } from '../../tools/utils.js';\n\nexport async function githubExportDirect(args, log, context = {}) {\n const { session } = context;\n const mcpLog = createLogWrapper(log);\n \n try {\n // Prepare options for the core handler\n const options = {\n id: args.taskId,\n repo: args.repository,\n token: args.token,\n ...args.options,\n projectRoot: args.projectRoot\n };\n \n // Call the core export handler\n const result = await handleGitHubExport(options, {\n session,\n mcpLog,\n outputFormat: 'json' // Request JSON output for MCP\n });\n \n return {\n success: true,\n data: {\n taskId: args.taskId,\n repository: args.repository,\n issueUrl: result.issueUrl,\n issueNumber: result.issue.number,\n exportedAt: new Date().toISOString(),\n message: `Successfully exported task ${args.taskId} to GitHub issue #${result.issue.number}`\n }\n };\n \n } catch (error) {\n mcpLog(`GitHub export failed: ${error.message}`);\n return {\n success: false,\n error: error.message\n };\n }\n}\n```\n\n### Validation Functions\n```javascript\n// scripts/modules/github/validation.js\nasync function validateExportOptions(options) {\n const { id: taskId, repo: repository, token } = options;\n \n // Validate task ID\n if (!taskId || !/^\\\\d+(\\\\.\\\\d+)*$/.test(taskId)) {\n throw new Error('Invalid task ID format');\n }\n \n // Validate repository format\n if (!repository || !/^[a-zA-Z0-9._-]+\\\\/[a-zA-Z0-9._-]+$/.test(repository)) {\n throw new Error('Repository must be in owner/repo format');\n }\n \n // Validate GitHub token\n const githubToken = token || process.env.GITHUB_TOKEN;\n if (!githubToken) {\n throw new Error('GitHub token is required');\n }\n \n if (!/^gh[ps]_[a-zA-Z0-9]{36,}$/.test(githubToken)) {\n console.warn(chalk.yellow('Warning: GitHub token format appears invalid'));\n }\n \n // Validate labels format\n if (options.labels) {\n const labels = options.labels.split(',').map(l => l.trim());\n for (const label of labels) {\n if (label.length > 50) {\n throw new Error(`Label \"${label}\" is too long (max 50 characters)`);\n }\n }\n }\n \n // Validate assignees format\n if (options.assignees) {\n const assignees = options.assignees.split(',').map(a => a.trim());\n for (const assignee of assignees) {\n if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(assignee)) {\n throw new Error(`Invalid GitHub username: ${assignee}`);\n }\n }\n }\n}\n```\n\n### Help Integration\n```javascript\n// Add to help system\nconst githubExportHelp = {\n command: 'github-export',\n description: 'Export Task Master task to GitHub issue',\n usage: 'task-master github-export --id=<taskId> --repo=<owner/repo> [options]',\n examples: [\n {\n command: 'task-master github-export --id=42 --repo=myorg/myproject',\n description: 'Export task 42 to GitHub repository'\n },\n {\n command: 'task-master github-export --id=42 --repo=myorg/myproject --labels=\"bug,urgent\" --assignees=\"john,jane\"',\n description: 'Export with custom labels and assignees'\n },\n {\n command: 'task-master github-export --id=42 --repo=myorg/myproject --dry-run',\n description: 'Preview the GitHub issue without creating it'\n },\n {\n command: 'task-master github-export --id=42 --repo=myorg/myproject --template=bug --sync',\n description: 'Export using bug template with sync enabled'\n }\n ],\n options: [\n { flag: '--id <taskId>', description: 'Task ID to export (required)' },\n { flag: '--repo <owner/repo>', description: 'GitHub repository (required)' },\n { flag: '--token <token>', description: 'GitHub Personal Access Token' },\n { flag: '--title <title>', description: 'Override issue title' },\n { flag: '--labels <labels>', description: 'Comma-separated labels' },\n { flag: '--assignees <users>', description: 'Comma-separated assignees' },\n { flag: '--milestone <milestone>', description: 'GitHub milestone' },\n { flag: '--template <template>', description: 'Issue template (bug, feature, epic)' },\n { flag: '--include-subtasks', description: 'Include subtasks as checklist' },\n { flag: '--separate-subtasks', description: 'Create separate issues for subtasks' },\n { flag: '--dry-run', description: 'Preview without creating' },\n { flag: '--force', description: 'Overwrite existing GitHub link' },\n { flag: '--no-link-back', description: 'Skip Task Master reference in issue' },\n { flag: '--sync', description: 'Enable automatic synchronization' }\n ]\n};\n```\n\n### Testing Requirements\n- Unit tests for CLI option parsing and validation\n- Integration tests for MCP tool functionality\n- End-to-end tests with real GitHub repositories\n- Error handling tests for various failure scenarios\n- Dry-run functionality testing\n- Template system testing\n- Subtask export testing (both checklist and separate issues)\n- Authentication and authorization testing\n- Rate limiting and retry logic testing", "status": "pending", - "dependencies": [ - 3 - ], + "dependencies": [3], "parentTaskId": 101 }, { @@ -5947,9 +5254,7 @@ "description": "Implement thorough testing for the GitHub export system and create comprehensive documentation", "details": "## Implementation Requirements\n\n### Testing Strategy\n\n#### 1. Unit Tests\n```javascript\n// tests/unit/github-export.test.js\ndescribe('GitHub Export System', () => {\n describe('GitHubExportService', () => {\n test('should validate repository access', async () => {\n const service = new GitHubExportService('mock-token');\n const mockGitHub = jest.spyOn(service, 'validateRepositoryAccess');\n \n await service.exportTask(mockTask, 'owner', 'repo');\n expect(mockGitHub).toHaveBeenCalledWith('owner', 'repo');\n });\n \n test('should handle authentication errors', async () => {\n const service = new GitHubExportService('invalid-token');\n \n await expect(service.exportTask(mockTask, 'owner', 'repo'))\n .rejects.toThrow('Authentication failed');\n });\n \n test('should respect rate limits', async () => {\n const service = new GitHubExportService('valid-token');\n const rateLimiter = jest.spyOn(service.rateLimiter, 'removeTokens');\n \n await service.exportTask(mockTask, 'owner', 'repo');\n expect(rateLimiter).toHaveBeenCalled();\n });\n });\n \n describe('TaskToGitHubFormatter', () => {\n test('should format task title correctly', () => {\n const formatter = new TaskToGitHubFormatter();\n const task = { id: 42, title: 'Test Task', priority: 'high' };\n \n const result = formatter.formatTitle(task);\n expect(result).toBe('🔥 [Task 42] Test Task');\n });\n \n test('should truncate long titles', () => {\n const formatter = new TaskToGitHubFormatter();\n const longTitle = 'A'.repeat(300);\n const task = { id: 1, title: longTitle };\n \n const result = formatter.formatTitle(task);\n expect(result.length).toBeLessThanOrEqual(250);\n expect(result).toEndWith('...');\n });\n \n test('should format subtasks as checklist', () => {\n const formatter = new TaskToGitHubFormatter();\n const task = {\n id: 1,\n title: 'Parent Task',\n subtasks: [\n { title: 'Subtask 1', status: 'done' },\n { title: 'Subtask 2', status: 'pending' }\n ]\n };\n \n const result = formatter.formatBody(task);\n expect(result).toContain('- [x] **Subtask 1**');\n expect(result).toContain('- [ ] **Subtask 2**');\n });\n \n test('should generate appropriate labels', () => {\n const formatter = new TaskToGitHubFormatter();\n const task = { priority: 'high', complexityScore: 9 };\n \n const labels = formatter.formatLabels(task);\n expect(labels).toContain('task-master');\n expect(labels).toContain('priority:high');\n expect(labels).toContain('complexity:high');\n });\n });\n \n describe('GitHubLinkManager', () => {\n test('should add GitHub link to task metadata', async () => {\n const linkManager = new GitHubLinkManager(mockGitHubService);\n const taskId = '42';\n const issueUrl = 'https://github.com/owner/repo/issues/123';\n \n await linkManager.addGitHubLinkToTask(taskId, issueUrl, 123, 'owner/repo');\n \n const task = await getTask(taskId);\n expect(task.metadata.githubIssue).toBeDefined();\n expect(task.metadata.githubIssue.url).toBe(issueUrl);\n expect(task.metadata.githubIssue.number).toBe(123);\n });\n \n test('should validate GitHub links', async () => {\n const linkManager = new GitHubLinkManager(mockGitHubService);\n const linkInfo = {\n repository: 'owner/repo',\n number: 123,\n status: 'active'\n };\n \n mockGitHubService.getIssue.mockResolvedValue({ state: 'open' });\n \n const result = await linkManager.validateGitHubLink('42', linkInfo);\n expect(result.valid).toBe(true);\n expect(result.status).toBe('active');\n });\n });\n});\n```\n\n#### 2. Integration Tests\n```javascript\n// tests/integration/github-export-integration.test.js\ndescribe('GitHub Export Integration', () => {\n let testRepository;\n let githubToken;\n \n beforeAll(() => {\n githubToken = process.env.GITHUB_TEST_TOKEN;\n testRepository = process.env.GITHUB_TEST_REPO || 'taskmaster-test/test-repo';\n \n if (!githubToken) {\n throw new Error('GITHUB_TEST_TOKEN environment variable required for integration tests');\n }\n });\n \n test('should export task to real GitHub repository', async () => {\n const task = createTestTask();\n const service = new GitHubExportService(githubToken);\n \n const result = await service.exportTask(task, testRepository, {\n labels: ['test', 'automated'],\n template: 'default'\n });\n \n expect(result.success).toBe(true);\n expect(result.issueUrl).toMatch(/https:\\\\/\\\\/github\\\\.com\\\\/.+\\\\/issues\\\\/\\\\d+/);\n \n // Cleanup: Close the test issue\n await service.updateIssue(testRepository, result.issue.number, { state: 'closed' });\n });\n \n test('should handle repository permission errors', async () => {\n const task = createTestTask();\n const service = new GitHubExportService(githubToken);\n \n await expect(service.exportTask(task, 'private/inaccessible-repo'))\n .rejects.toThrow(/permission|access/i);\n });\n \n test('should respect GitHub API rate limits', async () => {\n const service = new GitHubExportService(githubToken);\n const tasks = Array.from({ length: 10 }, () => createTestTask());\n \n const startTime = Date.now();\n \n for (const task of tasks) {\n await service.exportTask(task, testRepository);\n }\n \n const endTime = Date.now();\n const duration = endTime - startTime;\n \n // Should take some time due to rate limiting\n expect(duration).toBeGreaterThan(1000);\n });\n});\n```\n\n#### 3. CLI Tests\n```javascript\n// tests/cli/github-export-cli.test.js\ndescribe('GitHub Export CLI', () => {\n test('should validate required options', async () => {\n const result = await runCLI(['github-export']);\n \n expect(result.exitCode).toBe(1);\n expect(result.stderr).toContain('required option');\n });\n \n test('should perform dry run correctly', async () => {\n const result = await runCLI([\n 'github-export',\n '--id=42',\n '--repo=owner/repo',\n '--dry-run'\n ]);\n \n expect(result.exitCode).toBe(0);\n expect(result.stdout).toContain('DRY RUN');\n expect(result.stdout).toContain('GitHub Issue Preview');\n });\n \n test('should handle invalid task ID', async () => {\n const result = await runCLI([\n 'github-export',\n '--id=invalid',\n '--repo=owner/repo'\n ]);\n \n expect(result.exitCode).toBe(1);\n expect(result.stderr).toContain('Invalid task ID');\n });\n \n test('should validate repository format', async () => {\n const result = await runCLI([\n 'github-export',\n '--id=42',\n '--repo=invalid-format'\n ]);\n \n expect(result.exitCode).toBe(1);\n expect(result.stderr).toContain('owner/repo format');\n });\n});\n```\n\n#### 4. MCP Tool Tests\n```javascript\n// tests/mcp/github-export-mcp.test.js\ndescribe('GitHub Export MCP Tool', () => {\n test('should export task via MCP', async () => {\n const args = {\n taskId: '42',\n repository: 'owner/repo',\n token: 'test-token',\n options: {\n labels: ['test'],\n dryRun: true\n }\n };\n \n const result = await githubExportDirect(args, mockLog, { session: mockSession });\n \n expect(result.success).toBe(true);\n expect(result.data.taskId).toBe('42');\n });\n \n test('should handle MCP tool errors', async () => {\n const args = {\n taskId: 'invalid',\n repository: 'owner/repo'\n };\n \n const result = await githubExportDirect(args, mockLog, { session: mockSession });\n \n expect(result.success).toBe(false);\n expect(result.error).toBeDefined();\n });\n});\n```\n\n### Mock Data and Utilities\n```javascript\n// tests/utils/github-mocks.js\nexport function createTestTask() {\n return {\n id: Math.floor(Math.random() * 1000),\n title: 'Test Task',\n description: 'This is a test task for GitHub export',\n details: 'Implementation details for the test task',\n priority: 'medium',\n status: 'pending',\n subtasks: [\n { title: 'Test Subtask 1', status: 'done' },\n { title: 'Test Subtask 2', status: 'pending' }\n ]\n };\n}\n\nexport function mockGitHubAPI() {\n return {\n getIssue: jest.fn(),\n createIssue: jest.fn(),\n updateIssue: jest.fn(),\n getRepository: jest.fn()\n };\n}\n\nexport function createMockGitHubResponse(issueNumber = 123) {\n return {\n id: issueNumber,\n number: issueNumber,\n title: 'Test Issue',\n body: 'Test issue body',\n state: 'open',\n html_url: `https://github.com/owner/repo/issues/${issueNumber}`,\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString()\n };\n}\n```\n\n### Documentation\n\n#### 1. User Guide\n```markdown\n# GitHub Export Feature\n\n## Overview\nThe GitHub Export feature allows you to create GitHub issues directly from your Task Master tasks, maintaining bidirectional links between tasks and issues.\n\n## Setup\n\n### 1. GitHub Token\nCreate a GitHub Personal Access Token with the following permissions:\n- `repo` (for private repositories)\n- `public_repo` (for public repositories)\n\nSet the token as an environment variable:\n```bash\nexport GITHUB_TOKEN=your_token_here\n```\n\n### 2. Basic Usage\n```bash\n# Export a task to GitHub\ntask-master github-export --id=42 --repo=myorg/myproject\n\n# Export with custom labels and assignees\ntask-master github-export --id=42 --repo=myorg/myproject \\\\\n --labels=\"bug,urgent\" --assignees=\"john,jane\"\n\n# Preview before creating\ntask-master github-export --id=42 --repo=myorg/myproject --dry-run\n```\n\n## Advanced Features\n\n### Templates\nUse predefined templates for different issue types:\n```bash\n# Bug report template\ntask-master github-export --id=42 --repo=myorg/myproject --template=bug\n\n# Feature request template\ntask-master github-export --id=42 --repo=myorg/myproject --template=feature\n```\n\n### Subtask Handling\n```bash\n# Include subtasks as checklist items\ntask-master github-export --id=42 --repo=myorg/myproject --include-subtasks\n\n# Create separate issues for each subtask\ntask-master github-export --id=42 --repo=myorg/myproject --separate-subtasks\n```\n\n### Synchronization\n```bash\n# Enable automatic synchronization\ntask-master github-export --id=42 --repo=myorg/myproject --sync\n```\n\n## Troubleshooting\n\n### Common Issues\n1. **Authentication Error**: Verify your GitHub token has the correct permissions\n2. **Repository Not Found**: Ensure the repository exists and you have access\n3. **Rate Limit Exceeded**: Wait for the rate limit to reset or use a different token\n\n### Link Management\n```bash\n# Validate all GitHub links\ntask-master github-link --validate\n\n# Sync specific task with GitHub\ntask-master github-link --sync 42\n\n# Remove GitHub link from task\ntask-master github-link --remove 42\n```\n```\n\n#### 2. API Documentation\n```markdown\n# GitHub Export API Reference\n\n## MCP Tool: github_export_task\n\n### Parameters\n- `taskId` (string, required): Task ID to export\n- `repository` (string, required): GitHub repository in owner/repo format\n- `token` (string, optional): GitHub Personal Access Token\n- `options` (object, optional): Export configuration\n\n### Options Object\n- `title` (string): Override issue title\n- `labels` (array): GitHub labels to add\n- `assignees` (array): GitHub usernames to assign\n- `milestone` (string): GitHub milestone\n- `template` (string): Issue template (bug, feature, epic, default)\n- `includeSubtasks` (boolean): Include subtasks as checklist\n- `separateSubtasks` (boolean): Create separate issues for subtasks\n- `dryRun` (boolean): Preview without creating\n- `force` (boolean): Overwrite existing GitHub link\n- `linkBack` (boolean): Add Task Master reference to issue\n- `enableSync` (boolean): Enable automatic synchronization\n\n### Response\n```json\n{\n \"success\": true,\n \"data\": {\n \"taskId\": \"42\",\n \"repository\": \"owner/repo\",\n \"issueUrl\": \"https://github.com/owner/repo/issues/123\",\n \"issueNumber\": 123,\n \"exportedAt\": \"2024-01-15T10:30:00.000Z\",\n \"message\": \"Successfully exported task 42 to GitHub issue #123\"\n }\n}\n```\n```\n\n### Performance Testing\n```javascript\n// tests/performance/github-export-performance.test.js\ndescribe('GitHub Export Performance', () => {\n test('should export large task within time limit', async () => {\n const largeTask = createLargeTask(); // Task with many subtasks and long content\n const service = new GitHubExportService('test-token');\n \n const startTime = performance.now();\n await service.exportTask(largeTask, 'owner/repo');\n const endTime = performance.now();\n \n expect(endTime - startTime).toBeLessThan(5000); // Should complete in under 5 seconds\n });\n \n test('should handle concurrent exports', async () => {\n const service = new GitHubExportService('test-token');\n const tasks = Array.from({ length: 5 }, () => createTestTask());\n \n const promises = tasks.map(task => service.exportTask(task, 'owner/repo'));\n const results = await Promise.all(promises);\n \n results.forEach(result => {\n expect(result.success).toBe(true);\n });\n });\n});\n```\n\n### Test Configuration\n```javascript\n// jest.config.js additions\nmodule.exports = {\n // ... existing config\n testEnvironment: 'node',\n setupFilesAfterEnv: ['<rootDir>/tests/setup/github-setup.js'],\n testMatch: [\n '**/tests/**/*.test.js',\n '**/tests/**/*.spec.js'\n ],\n collectCoverageFrom: [\n 'scripts/modules/github/**/*.js',\n 'mcp-server/src/tools/github-*.js',\n 'mcp-server/src/core/direct-functions/github-*.js'\n ],\n coverageThreshold: {\n global: {\n branches: 80,\n functions: 80,\n lines: 80,\n statements: 80\n }\n }\n};\n```\n\n### Continuous Integration\n```yaml\n# .github/workflows/github-export-tests.yml\nname: GitHub Export Tests\n\non: [push, pull_request]\n\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v3\n - uses: actions/setup-node@v3\n with:\n node-version: '18'\n - run: npm ci\n - run: npm run test:github-export\n env:\n GITHUB_TEST_TOKEN: ${{ secrets.GITHUB_TEST_TOKEN }}\n GITHUB_TEST_REPO: ${{ secrets.GITHUB_TEST_REPO }}\n```", "status": "pending", - "dependencies": [ - 4 - ], + "dependencies": [4], "parentTaskId": 101 } ] @@ -6061,11 +5366,7 @@ "title": "Implement Tagged Task Lists System for Multi-Context Task Management", "description": "Develop a comprehensive tagged task lists system enabling users to organize, filter, and manage tasks across multiple contexts (e.g., personal, branch, version) with full backward compatibility.", "status": "done", - "dependencies": [ - 3, - 11, - 19 - ], + "dependencies": [3, 11, 19], "priority": "medium", "details": "1. Extend the tasks.json schema to support a 'tags' structure, with 'master' as the default tag containing all existing tasks. Ensure seamless migration for users without tags.\n2. Add a 'defaultTag' configuration option to config.json in the global section, defaulting to 'master'.\n3. Implement tag management CLI commands: add-tag, delete, list, use (switch), rename, and copy. Each command should update the relevant data structures and persist changes.\n4. Update all existing task commands (list, add-task, set-status, etc.) to accept a --tag flag, filtering or applying actions within the specified tag context.\n5. Implement automatic tag creation from git branch names using a --from-branch flag, integrating with git APIs to detect current branch.\n6. Maintain the current tag state in .taskmaster/state.json with currentTag set to 'master' by default, ensuring session persistence and correct context switching.\n7. Guarantee backward compatibility: users without tags continue to operate in the 'master' context transparently.\n8. Provide comprehensive documentation and migration notes for users, and update help menus to reflect new tag-related features.", "testStrategy": "- Migrate an existing tasks.json and verify all tasks appear under the 'master' tag.\n- Create, delete, rename, and copy tags using add-tag and other commands; confirm correct data structure updates and persistence.\n- Switch between tags and verify task isolation and context switching.\n- Use --tag flag with all supported commands and confirm correct filtering and operation.\n- Test --from-branch flag by switching git branches and verifying tag creation and selection.\n- Simulate usage without tags to ensure backward compatibility.\n- Review documentation and help menus for accuracy and completeness.\n- Run automated and manual tests for all new and modified commands, including edge cases (e.g., duplicate tag names, tag deletion with tasks).", @@ -6083,9 +5384,7 @@ "id": 2, "title": "Implement Seamless Migration for Existing Users", "description": "Develop a migration script or logic to move existing tasks into the 'master' tag for users upgrading from previous versions.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Ensure no data loss and that users without tags continue to operate transparently.", "status": "done", "testStrategy": "Test migration on various legacy datasets and verify task integrity post-migration." @@ -6094,9 +5393,7 @@ "id": 3, "title": "Add 'defaultTag' Configuration Option to config.json Global Section", "description": "Introduce a 'defaultTag' field in the global section of config.json, defaulting to 'master', and update configuration handling logic.", - "dependencies": [ - 1 - ], + "dependencies": [1], "details": "Allow users to set and persist their preferred default tag in the global configuration section.\n<info added on 2025-06-11T20:46:57.669Z>\nAdded global.defaultTag configuration option to .taskmaster/config.json structure in assets/config.json. Implemented complete tags section including autoSwitchOnBranch and gitIntegration options. Created migrateConfigJson() function in utils.js to handle updating existing configuration files during the migration process. Configuration is automatically created and updated during the silent migration process to ensure seamless transition for existing users.\n</info added on 2025-06-11T20:46:57.669Z>", "status": "done", "testStrategy": "Check that the default tag is respected on startup and when creating new tasks." @@ -6105,10 +5402,7 @@ "id": 4, "title": "Develop Tag Management CLI Commands", "description": "Implement CLI commands for tag management: add-tag, delete, list, use (switch), rename, and copy, ensuring all changes are persisted.", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "details": "Each command should update the tasks.json and config files as needed. The primary command for creating tags should be 'add-tag' to maintain consistency with other task-master commands.\n<info added on 2025-06-12T07:14:51.761Z>\n✅ **COMPLETED: CLI Command Integration for Tag Management**\n\nSuccessfully implemented complete CLI command integration for all tag management functions with enhanced UX features:\n\n**Commands Implemented:**\n\n1. **`task-master tags [--show-metadata]`** - List all available tags\n - Shows tag names, task counts, completion status\n - Optional metadata display (creation date, description)\n - Dynamic table width that adapts to terminal size\n - Current tag indicator with visual highlighting\n\n2. **`task-master add-tag <name> [options]`** - Create new tags\n - `--copy-from-current` - Copy tasks from current tag\n - `--copy-from=<tag>` - Copy tasks from specified tag\n - `-d, --description <text>` - Set tag description\n - **Default behavior: Creates empty tags** (fixed from previous copying behavior)\n\n3. **`task-master delete-tag <name> [--yes]`** - Delete tags with enhanced safety\n - **Changed from `--force` to `--yes`** for consistency\n - **Double confirmation system** using inquirer:\n - First: Yes/No confirmation prompt\n - Second: Type tag name to confirm deletion\n - Visual warning box showing impact\n - Automatic current tag switching if deleting active tag\n\n4. **`task-master use-tag <name>`** - Switch tag contexts\n - Updates current tag in state.json\n - Validates tag existence before switching\n - Clear success messaging\n\n5. **`task-master rename-tag <old> <new>`** - Rename existing tags\n - Validates both source and target names\n - Updates current tag reference if renaming active tag\n\n6. **`task-master copy-tag <source> <target> [options]`** - Copy tags\n - `-d, --description <text>` - Set description for new tag\n - Deep copy of all tasks and metadata\n\n**Key Improvements Made:**\n\nEnhanced User Experience:\n- **Double confirmation for destructive operations** using inquirer prompts\n- **Consistent option naming** (`--yes` instead of `--force`)\n- **Dynamic table layouts** that use full terminal width\n- **Visual warning boxes** for dangerous operations\n- **Contextual help displays** on command errors\n\nTechnical Fixes:\n- **Fixed critical `_rawTaggedData` corruption bug** in readJSON/writeJSON cycle\n- **Dynamic task counting** instead of stored counters (eliminates sync issues)\n- **Master tag metadata enhancement** with creation dates and descriptions\n- **Proper error handling** with command-specific help displays\n\nCLI Integration:\n- **Added all commands to help menu** in ui.js under \"Tag Management\" section\n- **Comprehensive help functions** for each command with examples\n- **Error handlers with contextual help** for better user guidance\n- **Consistent command patterns** following established CLI conventions\n\n**Testing Completed:**\n- ✅ Created empty tags (default behavior)\n- ✅ Created tags with task copying (explicit flags)\n- ✅ Listed tags with and without metadata\n- ✅ Double confirmation for tag deletion\n- ✅ Tag switching and current tag persistence\n- ✅ Table width responsiveness\n- ✅ Master tag metadata enhancement\n- ✅ Error handling and help displays\n\n**Files Modified:**\n- `scripts/modules/commands.js` - Added all tag management commands\n- `scripts/modules/task-manager/tag-management.js` - Enhanced functions with inquirer\n- `scripts/modules/ui.js` - Added tag commands to help menu\n- Fixed critical data corruption bug in utils.js\n\nThe CLI integration is now complete and production-ready with enhanced safety features and improved user experience!\n</info added on 2025-06-12T07:14:51.761Z>", "status": "done", "testStrategy": "Unit test each CLI command for correct behavior and data persistence, specifically testing add-tag command." @@ -6117,9 +5411,7 @@ "id": 5, "title": "Update Task Commands to Support --tag Flag", "description": "Modify all existing task-related CLI commands (list, add-task, set-status, etc.) to accept a --tag flag, applying actions within the specified tag context.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Ensure commands filter or apply actions only to tasks within the selected tag.\n<info added on 2025-06-11T18:23:45.185Z>\nDependencies: [4, 13, 14] - Requires CLI commands foundation, MCP tools integration, and state management utilities to properly implement --tag flag support across both CLI and MCP interfaces.\n</info added on 2025-06-11T18:23:45.185Z>\n<info added on 2025-06-12T22:44:17.705Z>\n**CURRENT STATUS ANALYSIS - Commands Needing --tag Flag + projectRoot Fix**\n\nAfter fixing the migration bug in readJSON and updating `list` and `move` commands, here's the current status:\n\n**✅ COMPLETED:**\n- `list` command - Has projectRoot fix + tag support working\n- `move` command - Has projectRoot fix + tag support working \n\n**❌ STILL NEED BOTH --tag FLAG + projectRoot FIX:**\n\n**High Priority (Core Task Operations):**\n1. `show` - View specific tasks (needs tag context)\n2. `add-task` - Create tasks (needs tag context) \n3. `set-status` - Update task status (needs tag context)\n4. `next` - Find next task (needs tag context)\n\n**Medium Priority (Task Modification):**\n5. `update-task` - Update specific task (needs tag context)\n6. `update-subtask` - Update subtask (needs tag context)\n7. `add-subtask` - Add subtasks (needs tag context)\n8. `remove-task` - Remove tasks (needs tag context)\n9. `remove-subtask` - Remove subtasks (needs tag context)\n10. `clear-subtasks` - Clear subtasks (needs tag context)\n11. `expand` - Expand tasks (needs tag context)\n\n**Lower Priority (Dependencies & Analysis):**\n12. `add-dependency` - Add dependencies (needs tag context)\n13. `remove-dependency` - Remove dependencies (needs tag context)\n14. `validate-dependencies` - Validate deps (needs tag context)\n15. `fix-dependencies` - Fix deps (needs tag context)\n16. `generate` - Generate task files (needs tag context)\n17. `analyze-complexity` - Analyze complexity (needs tag context)\n18. `complexity-report` - View complexity report (needs tag context)\n\n**✅ DON'T NEED TAG SUPPORT:**\n- `init`, `models`, `parse-prd`, `research`, `migrate`, `sync-readme`\n- Tag management commands (they manage tags themselves)\n\n**NEXT STEPS:**\n1. Start with high-priority commands (`show`, `add-task`, `set-status`, `next`)\n2. Add `--tag` flag to each command\n3. Ensure `findProjectRoot()` is called and passed to underlying functions\n4. Update underlying functions to accept and use projectRoot parameter\n5. Test migration and tag resolution for each command\n\n**PATTERN TO FOLLOW:**\nSame pattern as `list` and `move` commands:\n- Add `--tag` option to CLI command\n- Call `findProjectRoot()` in action function\n- Pass `{ projectRoot }` context to underlying function\n- Update underlying function signature to accept context parameter\n- Pass projectRoot to readJSON/writeJSON calls\n</info added on 2025-06-12T22:44:17.705Z>\n<info added on 2025-06-12T22:47:22.415Z>\n**PROGRESS UPDATE - show Command Completed Successfully**\n\n✅ **COMPLETED: `show` command**\n- Added `--tag` flag support to CLI command\n- Fixed `findProjectRoot()` call and projectRoot passing\n- Updated `displayTaskById` function to accept context parameter with projectRoot\n- Updated `displayMultipleTasksSummary` function to accept context parameter\n- Fixed readJSON calls to include projectRoot for proper tag resolution and migration\n- **TESTED SUCCESSFULLY**: `task-master show 103` works perfectly with no errors\n\n**TECHNICAL DETAILS:**\n- CLI command now calls `findProjectRoot()` and passes `{ projectRoot, tag }` context\n- UI functions extract projectRoot from context and pass to `readJSON(tasksPath, projectRoot, tag)`\n- Migration logic now works correctly when viewing tasks\n- Both single task and multiple task views work properly\n\n**UPDATED STATUS - 1 of 4 High-Priority Commands Complete:**\n1. ✅ `show` - **COMPLETED** \n2. ❌ `add-task` - Create tasks (needs tag context)\n3. ❌ `set-status` - Update task status (needs tag context) \n4. ❌ `next` - Find next task (needs tag context)\n\n**NEXT ACTION:** Continue with `add-task` command following the same proven pattern:\n- Add `--tag` flag to CLI command\n- Call `findProjectRoot()` in action function \n- Pass `{ projectRoot, tag }` context to underlying function\n- Update underlying function to accept context and pass projectRoot to readJSON/writeJSON\n</info added on 2025-06-12T22:47:22.415Z>\n<info added on 2025-06-12T22:49:16.724Z>\n**PROGRESS UPDATE - add-task Command Completed Successfully**\n\n✅ **COMPLETED: `add-task` command**\n- Already had `--tag` flag support in CLI command\n- Already had `findProjectRoot()` call and projectRoot passing\n- Already had proper context object with `{ projectRoot, tag }`\n- Underlying `addTask` function already properly handles tag parameter and projectRoot\n- **TESTED SUCCESSFULLY**: `task-master add-task --prompt=\"Test task for tag support\" --priority=low` works perfectly with no errors\n\n**TECHNICAL DETAILS:**\n- CLI command already calls `findProjectRoot()` and passes `{ projectRoot, tag }` context\n- `addTask` function extracts projectRoot from context and passes to `readJSON(tasksPath, projectRoot)`\n- Migration logic works correctly when adding tasks\n- Tag resolution and context handling work properly\n\n**COMPLETED HIGH-PRIORITY COMMANDS:**\n1. ✅ `show` - **COMPLETED** \n2. ✅ `add-task` - **COMPLETED**\n3. ❌ `set-status` - Update task status (needs tag context)\n4. ❌ `next` - Find next task (needs tag context)\n\n**REMAINING WORK:**\nNext commands to fix: `set-status` and `next` commands following the same pattern.\n</info added on 2025-06-12T22:49:16.724Z>\n<info added on 2025-06-13T02:48:17.985Z>\n**FINAL PROGRESS UPDATE - Tag Management Issues Resolved**\n\n✅ **COMPLETED: All Tag Management Issues Fixed**\n\n**Major Issues Resolved:**\n1. **Rogue `\"created\"` property cleanup** - Fixed root-level `\"created\"` property in master tag that was outside metadata\n2. **Tags command error fixed** - Resolved undefined `taskCount` error by making calculation dynamic\n3. **Data corruption prevention** - Enhanced `writeJSON` to automatically filter rogue properties during write operations\n\n**Technical Fixes Applied:**\n- **Enhanced `writeJSON` function** to automatically clean up rogue `created` and `description` properties from tag objects\n- **Fixed `taskCount` calculation** to be dynamic (`tasks.length`) instead of hardcoded field\n- **Cleaned up existing corruption** in master tag through forced write operation\n- **All `created` properties** now properly located in `metadata` objects only\n\n**Commands Status Update:**\n✅ **COMPLETED HIGH-PRIORITY COMMANDS:**\n1. ✅ `show` - Added --tag flag + fixed projectRoot passing\n2. ✅ `add-task` - Already had proper tag support \n3. ✅ `list` - Already had proper tag support\n4. ✅ `move` - Already had proper tag support\n5. ✅ `tags` - Fixed errors and working perfectly\n\n**REMAINING COMMANDS TO FIX:**\n❌ `set-status` - Update task status (needs tag context)\n❌ `next` - Find next task (needs tag context)\n❌ `update-task` - Update specific task (needs tag context)\n❌ `update-subtask` - Update subtask (needs tag context)\n❌ `add-subtask` - Add subtasks (needs tag context)\n❌ `remove-task` - Remove tasks (needs tag context)\n❌ `remove-subtask` - Remove subtasks (needs tag context)\n\n**Data Integrity Status:**\n- ✅ Migration logic working correctly\n- ✅ Tag creation/deletion working with proper metadata\n- ✅ File corruption prevention active\n- ✅ Automatic cleanup during write operations\n- ✅ Tagged task lists system fully functional\n\n**Next Steps:** Continue with remaining commands (set-status, next, etc.) to complete task 103.5\n</info added on 2025-06-13T02:48:17.985Z>\n<info added on 2025-06-13T03:57:35.440Z>\n**CRITICAL BUG FIX & PROGRESS UPDATE**\n\n✅ **COMPLETED: Fixed critical tag-deletion bug** affecting `add-subtask` and likely other commands.\n- **Root Cause:** The core `writeJSON` function was not accepting `projectRoot` and `tag` parameters, causing it to overwrite the entire `tasks.json` file with only the data for the current tag, deleting all other tags.\n- **The Fix:** The `writeJSON` signature and logic have been corrected to properly accept `projectRoot` and `tag` context. It now correctly merges resolved tag data back into the full tagged data structure before writing, preserving data integrity.\n- **Impact:** This single, critical fix likely resolves the tag-deletion bug for all commands that modify the tasks file.\n\n**UPDATED COMMAND STATUS:**\nMany commands previously listed as \"remaining\" are now likely fixed due to the `writeJSON` correction.\n\n- ✅ `list`, `show`, `add-task`, `move`\n- ✅ `add-subtask` (tested and confirmed fixed)\n- ❓ **Likely Fixed (Pending Confirmation):** `set-status`, `remove-task`, `remove-subtask`, `clear-subtasks`, `update-task`, `update-subtask`, `expand`, `generate`, and all dependency commands.\n\n**NEXT STEPS:**\nSystematically test the \"Likely Fixed\" commands to confirm they no longer corrupt the `tasks.json` file. Then, implement the `--tag` flag for those that still need it.\n</info added on 2025-06-13T03:57:35.440Z>\n<info added on 2025-06-13T03:58:43.036Z>\n**PROGRESS UPDATE - `set-status` command verified**\n\n✅ **COMPLETED: `set-status` command is confirmed fixed.**\n- **Test:** Created a new tag, ran `set-status` on an existing task, and verified that the new tag was NOT deleted.\n- **Confirmation:** The underlying fix to the `writeJSON` function correctly preserves the full tagged data structure.\n\n**UPDATED COMMAND STATUS:**\n- ✅ `list`, `show`, `add-task`, `move`, `add-subtask`\n- ✅ `set-status` **(Newly Verified)**\n- ❓ **Likely Fixed (Pending Confirmation):** `remove-task`, `remove-subtask`, `clear-subtasks`, `update-task`, `update-subtask`, `expand`, `generate`, and all dependency commands.\n\n**NEXT STEPS:**\nContinue systematically testing the remaining commands. Next up is `remove-task`.\n</info added on 2025-06-13T03:58:43.036Z>\n<info added on 2025-06-13T04:01:04.367Z>\n**PROGRESS UPDATE - `remove-task` command fixed**\n\n✅ **COMPLETED: `remove-task` command has been fixed and is now fully tag-aware.**\n- **CLI Command:** Updated `remove-task` in `commands.js` to include the `--tag` option and pass `projectRoot` and `tag` context to the core function.\n- **Core Function:** Refactored the `removeTask` function in `scripts/modules/task-manager/remove-task.js`.\n - It now accepts a `context` object.\n - It reads the raw tagged data structure using `readJSON` with the correct context.\n - It operates only on the tasks within the specified (or current) tag.\n - It correctly updates the full `rawData` object before writing.\n - It calls `writeJSON` and `generateTaskFiles` with the correct context to prevent data corruption.\n- **Impact:** The `remove-task` command should no longer cause tag deletion or data corruption.\n\n**UPDATED COMMAND STATUS:**\n- ✅ `list`, `show`, `add-task`, `move`, `add-subtask`, `set-status`\n- ✅ `remove-task` **(Newly Fixed)**\n- ❓ **Likely Fixed (Pending Confirmation):** `remove-subtask`, `clear-subtasks`, `update-task`, `update-subtask`, `expand`, `generate`, and all dependency commands.\n\n**NEXT STEPS:**\nTest the `remove-task` command to verify the fix. Then continue with the remaining commands.\n</info added on 2025-06-13T04:01:04.367Z>\n<info added on 2025-06-13T04:13:22.909Z>\n**FINAL COMPLETION STATUS - All Critical Data Corruption Bugs Resolved**\n\nThe root cause of the tag deletion bug has been identified and fixed in the `generateTaskFiles` function. This function was incorrectly reading a single tag's data and then causing `validateAndFixDependencies` to overwrite the entire `tasks.json` file.\n\n**The Core Fix:**\n- `generateTaskFiles` has been refactored to be fully tag-aware\n- It now reads the complete raw data structure, preserving all tags\n- It performs its operations (validation, file generation) only on the tasks of the specified tag, without affecting other tags\n- This prevents the data corruption that was affecting `add-task`, `add-subtask`, and likely other commands\n\n**System Stability Achieved:**\nThe critical `writeJSON` and `generateTaskFiles` fixes have stabilized the entire system. All commands that modify `tasks.json` are now safe from data corruption.\n\n**Final Command Status - All Core Commands Working:**\n✅ `list`, `show`, `add-task`, `move`, `add-subtask`, `set-status`, `remove-task` - All confirmed working correctly without causing data loss\n✅ All other commands are presumed stable due to the core infrastructure fixes\n\n**Tagged Task List System Status: STABLE**\nThe tagged task list system is now considered stable and production-ready for all primary task modification commands. The --tag flag implementation is complete and functional across the command suite.\n</info added on 2025-06-13T04:13:22.909Z>\n<info added on 2025-06-13T07:01:11.925Z>\n**MAJOR MILESTONE ACHIEVED - Tagged Command Implementation Complete!**\n\n✅ **CRITICAL BUG FIXES COMPLETED:**\n- Fixed parse-prd data corruption bug that was deleting other tags\n- Enhanced tag preservation logic in both parse-prd.js and commands.js\n- Fixed confirmation logic to check tag-specific task existence (not just file existence)\n\n✅ **PARSE-PRD TAG SUPPORT - FULLY WORKING:**\n- parse-prd --tag=feature-name: Creates tasks in specific tag contexts\n- Non-existent tags are created automatically and silently\n- Existing tags are preserved during new tag creation\n- Append mode works correctly within tag contexts\n- Smart confirmation only prompts when target tag has existing tasks\n\n✅ **ANALYZE-COMPLEXITY TAG SUPPORT - FULLY WORKING:**\n- analyze-complexity --tag=branch: Generates tag-specific reports\n- File naming: master → task-complexity-report.json, others → task-complexity-report_tagname.json\n- complexity-report --tag=branch: Reads from correct tag-specific report file\n- Complete isolation between different tag contexts\n\n✅ **COMPREHENSIVE TESTING COMPLETED:**\n- Tested parse-prd with new tag creation (test-prd-tag)\n- Tested parse-prd with append mode (added 2 more tasks to existing 3)\n- Tested analyze-complexity with tag-specific report generation\n- Tested complexity-report with tag-specific report reading\n- Verified tag preservation and no data corruption across all operations\n\n✅ **CHANGESET & COMMIT COMPLETED:**\n- Updated changeset to describe new tag features (minor version bump)\n- Committed comprehensive changes with detailed commit message\n- All changes properly documented and versioned\n\n🚀 **SYSTEM IMPACT:**\nThe core tagged task lists system is now complete and production-ready! Users can:\n- Parse PRDs into separate contexts on the fly: parse-prd --tag=feature-branch\n- Generate isolated complexity reports: analyze-complexity --tag=experiment\n- Work across multiple project contexts without conflicts\n- Enable rapid prototyping and parallel development workflows\n\n**TAGGED COMMAND SUITE STATUS: COMPLETE**\nAll critical commands now support --tag flag with full data integrity preservation. The tagged task management system is stable and ready for advanced workflow integration.\n</info added on 2025-06-13T07:01:11.925Z>", "status": "done", "testStrategy": "Test each command with and without the --tag flag for correct scoping." @@ -6128,9 +5420,7 @@ "id": 6, "title": "Integrate Automatic Tag Creation from Git Branches", "description": "Implement logic to create tags based on git branch names using a --from-branch flag, integrating with git APIs to detect the current branch.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Enable seamless context switching between code branches and task tags. Use add-tag internally when creating tags from branch names.\n<info added on 2025-06-13T17:27:34.449Z>\n**Code Context Analysis Complete**\n\n**Current State:**\n- `state.json` has `branchTagMapping: {}` ready for storing git branch to tag mappings\n- `config.json` has `tags.enabledGitworkflow: false` and `tags.autoSwitchTagWithBranch: false` controls\n- Existing tag management functions in `scripts/modules/task-manager/tag-management.js` provide `createTag`, `useTag`, `switchCurrentTag` utilities\n- No existing git integration - need to add git CLI dependencies\n\n**Implementation Plan:**\n\n1. **Add Git Dependencies**: Add `simple-git` package for git operations (better than calling CLI directly)\n2. **Create Git Utilities Module**: `scripts/modules/utils/git-utils.js` with functions:\n - `getCurrentBranch()` - Get current git branch name\n - `isGitRepository()` - Check if we're in a git repo\n - `getBranchList()` - Get list of all branches\n - `onBranchChange()` - Hook for branch change detection\n\n3. **Enhance Tag Management**: Add git integration functions:\n - `createTagFromBranch(branchName)` - Create tag from git branch name\n - `autoSwitchTagForBranch()` - Auto-switch tag when branch changes\n - `updateBranchTagMapping()` - Update state.json mapping\n\n4. **Add CLI Commands**:\n - `--from-branch` flag for `add-tag` command\n - `task-master sync-git` command for manual git-tag synchronization\n\n5. **Configuration Integration**: \n - Check `config.tags.enabledGitworkflow` before git operations\n - Use `config.tags.autoSwitchTagWithBranch` for automatic switching\n\n**Next Steps**: Start with adding simple-git dependency and creating git utilities module.\n</info added on 2025-06-13T17:27:34.449Z>\n<info added on 2025-06-13T17:45:03.727Z>\n**Updated Implementation Strategy - Automatic Git Integration**\n\n**Revised Approach:**\n- Eliminate manual `sync-git` command for seamless user experience\n- Implement automatic git-tag synchronization following the established migration pattern\n- Integration occurs transparently during normal task operations without user intervention\n- Behavior controlled entirely through existing configuration flags\n\n**Updated Implementation Plan:**\n\n1. **Simplified Git Dependencies**: Keep `simple-git` package for git operations\n\n2. **Enhanced Git Utilities Module**: `scripts/modules/utils/git-utils.js` with streamlined functions:\n - `getCurrentBranch()` - Get current git branch name\n - `isGitRepository()` - Check if we're in a git repo\n - `shouldAutoSync()` - Check if git workflow is enabled and conditions are met\n\n3. **Automatic Integration Hook**: \n - Add `checkAndSyncGitTags()` function to utils.js\n - Integrate into `readJSON()` similar to migration system\n - Automatically create tags from branch names when conditions are met\n - Update branch-tag mappings in state.json transparently\n\n4. **Streamlined Tag Management**: Remove complex CLI additions:\n - No `--from-branch` flag needed for `add-tag`\n - No manual `sync-git` command\n - Automatic tag creation and switching based on git context\n\n5. **Configuration-Driven Behavior**:\n - `config.tags.enabledGitworkflow` enables/disables entire system\n - `config.tags.autoSwitchTagWithBranch` controls automatic tag switching\n - Silent operation when disabled, seamless when enabled\n\n**Benefits**: Zero-friction git integration that \"just works\" when enabled, following established project patterns for automatic system enhancements.\n</info added on 2025-06-13T17:45:03.727Z>\n<info added on 2025-06-13T17:50:24.997Z>\n**✅ IMPLEMENTATION COMPLETED**\n\n**Final Implementation Summary:**\n\n1. **Proper Module Organization**: \n - Moved `checkAndAutoSwitchGitTag` function to correct location in `scripts/modules/utils/git-utils.js`\n - Updated imports in `utils.js` to use the git-utils version\n - Maintains clean separation of concerns with git operations in dedicated module\n\n2. **Seamless Integration Architecture**:\n - Function automatically executes during `readJSON()` operations\n - Integrates with both migration system and normal tagged format processing\n - Zero user intervention required - works transparently in background\n\n3. **Smart Git-Tag Synchronization**:\n - Automatically switches to existing tags matching current branch names\n - Creates new tags for branches without corresponding tags\n - Updates `state.json` branch-tag mappings for persistent tracking\n - Validates branch names (excludes main/master/develop/dev/HEAD)\n\n4. **Configuration-Driven Operation**:\n - Controlled by `config.tags.enabledGitworkflow` and `config.tags.autoSwitchTagWithBranch` flags\n - Silent operation when disabled, seamless when enabled\n - Uses `console.debug` for error handling to avoid disrupting normal operations\n\n5. **MCP-Compatible Design**:\n - All functions require `projectRoot` parameter for MCP compatibility\n - Leverages existing git utility functions (`isGitRepository`, `getCurrentBranch`, `isValidBranchForTag`, `sanitizeBranchNameForTag`)\n - Follows established project patterns for automatic system enhancements\n\n**Status**: Implementation complete and ready for production use. Users can enable automatic git integration by configuring the appropriate flags in `.taskmaster/config.json`.\n</info added on 2025-06-13T17:50:24.997Z>\n<info added on 2025-06-14T01:01:52.559Z>\n**SCOPE SIMPLIFIED: Basic CLI Git Integration Only**\n\n**Implementation Status Changed:**\n- **CANCELLED**: All automatic git-tag synchronization features\n- **CANCELLED**: Configuration-driven git workflow automation \n- **CANCELLED**: Silent background git integration\n- **CANCELLED**: Branch-tag mapping persistence and auto-switching\n\n**COMPLETED: Core Git Utilities**\n- Git utilities module with MCP-compatible functions ready\n- Branch name sanitization and validation implemented\n- Git repository detection working\n\n**REMAINING WORK: CLI Integration**\n- Add `--from-branch` flag to `add-tag` command in `scripts/modules/commands.js`\n- Integrate existing git utilities with tag creation workflow\n- Enable `task-master add-tag --from-branch` command functionality\n\n**Final Simplified Scope:**\nSingle explicit command: `task-master add-tag --from-branch`\n- Detects current git branch name\n- Sanitizes branch name to valid tag format\n- Creates new tag with sanitized name\n- No automation, no background processes, no persistent mappings\n\n**Benefits**: Explicit user control, predictable behavior, simple implementation, easy debugging, clear separation of concerns between git and task management.\n\n**Next Step**: Implement CLI flag integration to complete the simplified git integration feature.\n</info added on 2025-06-14T01:01:52.559Z>", "status": "done", "testStrategy": "Test tag creation and switching in repositories with multiple branches." @@ -6139,9 +5429,7 @@ "id": 7, "title": "Update State Management for Current Tag Tracking", "description": "Ensure .taskmaster/state.json properly tracks the current tag with currentTag field set to 'master' by default during initialization.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Update initialization logic to create state.json with currentTag set to 'master', ensuring the state file accurately reflects the active tag across sessions.\n<info added on 2025-06-11T20:49:28.104Z>\nSTATE MANAGEMENT: Updated scripts/init.js to create state.json during initialization with proper initial state: currentTag: 'master', lastSwitched timestamp, branchTagMapping, migrationNoticeShown flag. createStateJson() function in utils.js handles state file creation during migration. State management integrated into complete migration system.\n</info added on 2025-06-11T20:49:28.104Z>", "status": "done", "testStrategy": "Verify state persistence after restarts and tag switches, confirm initialization creates proper currentTag field." @@ -6150,9 +5438,7 @@ "id": 8, "title": "Ensure Full Backward Compatibility", "description": "Guarantee that users without tags continue to operate in the 'master' context without disruption or required changes.", - "dependencies": [ - 7 - ], + "dependencies": [7], "details": "Test all workflows for legacy users and ensure no regressions.", "status": "done", "testStrategy": "Regression test with legacy data and workflows." @@ -6161,9 +5447,7 @@ "id": 9, "title": "Update Documentation and Help Menus", "description": "Revise user documentation, migration notes, and CLI help menus to reflect new tag-related features and usage patterns, specifically documenting the add-tag command.", - "dependencies": [ - 4 - ], + "dependencies": [4], "details": "Provide clear instructions and examples for all tag management features, ensuring add-tag command is properly documented with consistent naming.", "status": "done", "testStrategy": "Review documentation for completeness and clarity; user acceptance testing." @@ -6183,10 +5467,7 @@ "description": "Implement core tag management functions in scripts/modules/task-manager/ following the established pattern. Include functions for createTag, deleteTag, listTags, useTag, renameTag, copyTag, and tag resolution logic.", "details": "", "status": "done", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "parentTaskId": 103 }, { @@ -6195,9 +5476,7 @@ "description": "Create MCP direct function wrappers in mcp-server/src/core/direct-functions/ for all tag management operations, following the established pattern like add-task.js", "details": "<info added on 2025-06-13T14:32:43.186Z>\nSuccessfully implemented all 6 MCP direct function wrappers in `mcp-server/src/core/direct-functions/`:\n\n**Created Files:**\n1. **`add-tag.js`** - Direct function for creating new tags with options for copying tasks and descriptions\n2. **`delete-tag.js`** - Direct function for deleting tags with automatic confirmation bypass for MCP\n3. **`list-tags.js`** - Direct function for listing all tags with optional metadata display\n4. **`use-tag.js`** - Direct function for switching current tag context\n5. **`rename-tag.js`** - Direct function for renaming existing tags\n6. **`copy-tag.js`** - Direct function for copying tags with all tasks and metadata\n\n**Integration Completed:**\n- Updated `mcp-server/src/core/task-master-core.js` to import and export all new direct functions\n- Added all functions to the directFunctions Map for dynamic dispatch\n- Followed established patterns from existing direct functions (add-task.js, etc.)\n\n**Key Features Implemented:**\n- **Silent Mode Management**: All functions properly enable/disable silent mode for MCP responses\n- **Error Handling**: Comprehensive error handling with structured error codes and messages\n- **Parameter Validation**: Full validation of required parameters with helpful error messages\n- **Logger Integration**: Proper MCP logger wrapper integration using createLogWrapper utility\n- **JSON Output**: All functions return structured JSON responses suitable for MCP clients\n- **Context Support**: Full session and projectRoot context support for all operations\n\n**Technical Details:**\n- All functions accept `tasksJsonPath`, `projectRoot`, and operation-specific parameters\n- Proper async/await error handling with silent mode restoration in finally blocks\n- Consistent return format: `{ success: boolean, data?: any, error?: { code: string, message: string } }`\n- Integration with existing tag management functions from `scripts/modules/task-manager/tag-management.js`\n\nThe MCP direct function layer is now complete and ready for MCP tool integration in the next phase.\n</info added on 2025-06-13T14:32:43.186Z>", "status": "done", - "dependencies": [ - 11 - ], + "dependencies": [11], "parentTaskId": 103 }, { @@ -6206,9 +5485,7 @@ "description": "Implement MCP tools in mcp-server/src/tools/ for all tag management operations (add-tag, delete-tag, list-tags, use-tag, rename-tag, copy-tag), following the established pattern like add-task.js", "details": "", "status": "done", - "dependencies": [ - 12 - ], + "dependencies": [12], "parentTaskId": 103 }, { @@ -6217,10 +5494,7 @@ "description": "Implement utilities for reading/writing current tag state, tag resolution logic (currentTag from state -> --tag flag -> defaultTag fallback), and state file validation", "details": "", "status": "done", - "dependencies": [ - 3, - 7 - ], + "dependencies": [3, 7], "parentTaskId": 103 }, { @@ -6229,9 +5503,7 @@ "description": "Create specific migration logic to transform existing tasks.json format (array of tasks) to the new tagged format ({tags: {master: {tasks: [...]}}}). Include validation and rollback capabilities.", "details": "<info added on 2025-06-11T20:50:25.721Z>\nMIGRATION LOGIC: Implemented in scripts/modules/utils.js with performCompleteTagMigration(), migrateConfigJson(), createStateJson(), and markMigrationForNotice() functions. Silent migration triggers on readJSON() for tasks.json files. Migration notice system implemented in commands.js with displayTaggedTasksFYI() from ui.js. Complete 3-part migration: tasks.json + config.json + state.json all handled automatically.\n</info added on 2025-06-11T20:50:25.721Z>", "status": "done", - "dependencies": [ - 1 - ], + "dependencies": [1], "parentTaskId": 103 }, { @@ -6240,10 +5512,7 @@ "description": "Update all documentation in /docs to reflect the new tagged task lists architecture and migration system", "details": "Update docs to be aware of the new tagged structure: - Update command-reference.md with new tag-related commands - Update task-structure.md to explain tagged format - Update configuration.md with tagged system config - Update tutorial.md with tagged workflow - Update migration-guide.md for tagged migration - Ensure all examples reflect new structure\n<info added on 2025-06-11T21:12:52.662Z>\nCOMPLETED: All documentation files have been successfully updated to reflect the tagged task lists system. Key updates include:\n\n- docs/task-structure.md: Added complete tagged format explanation, data structure overview, migration details, and best practices\n- docs/configuration.md: Updated with tagged system configuration, state management, and new settings\n- docs/migration-guide.md: Added comprehensive tagged system migration process, verification steps, and team coordination guidelines\n- .cursor/rules/*.mdc files: Updated architecture.mdc, dev_workflow.mdc, taskmaster.mdc, tasks.mdc, utilities.mdc, new_features.mdc, git_workflow.mdc, and glossary.mdc to be aware of tagged system\n\nAll documentation now properly reflects Part 1 implementation and prepares for Part 2 features. Documentation is fully aligned with the new tagged task structure.\n</info added on 2025-06-11T21:12:52.662Z>", "status": "done", - "dependencies": [ - "103.8", - "103.9" - ], + "dependencies": ["103.8", "103.9"], "parentTaskId": 103 }, { @@ -6280,12 +5549,7 @@ "title": "Implement 'scope-up' and 'scope-down' CLI Commands for Dynamic Task Complexity Adjustment", "description": "Add new CLI commands 'scope-up' and 'scope-down' to enable users to dynamically increase or decrease the complexity of tasks or subtasks, with support for multiple IDs, strength levels, milestone-aware adjustments, and custom prompts for targeted modifications.", "status": "done", - "dependencies": [ - 3, - 11, - 19, - 94 - ], + "dependencies": [3, 11, 19, 94], "priority": "high", "details": "1. Extend the CLI (commands.js) to introduce 'scope-up' and 'scope-down' commands, following established subcommand patterns for consistency and discoverability. \n2. Accept comma-separated task/subtask IDs and an optional '--strength' flag (light|regular|heavy, defaulting to regular). Validate all inputs, ensuring referenced tasks/subtasks exist and strength is valid.\n3. Implement a '--prompt' flag that allows users to specify custom instructions for how to scope up/down tasks, including targeting specific parts of tasks/subtasks (e.g., removing unnecessary test sections, focusing on particular implementation aspects).\n4. Fetch current task details and associated milestone context to inform the adjustment logic.\n5. Implement core logic functions that:\n - Construct context-aware AI prompts for scaling complexity up or down, leveraging the current state, project phase, strength parameter, and any user-provided custom prompt.\n - Call the unified AI service (ai-services-unified.js) to generate new task/subtask content at the desired complexity level.\n - Replace existing task details and subtasks with the AI-generated output, preserving historical versions for rollback/comparison.\n - Ensure task dependency integrity and update tasks.json and related files accordingly.\n6. Integrate robust error handling for invalid IDs, parameters, or AI failures, and provide clear CLI output showing before/after differences.\n7. Add corresponding MCP tool equivalents for integrated environments and update documentation/help text to reflect new commands and usage patterns.\n8. Ensure compatibility with batch operations, milestone-based guidelines, and existing task management workflows.", "testStrategy": "- Write unit and integration tests for both 'scope-up' and 'scope-down' commands, covering single and multiple ID scenarios, all strength levels, custom prompts, and edge cases (e.g., non-existent IDs, invalid strength values).\n- Test various custom prompt scenarios, including prompts that target specific sections of tasks/subtasks for modification.\n- Simulate CLI usage to verify correct parsing, validation, and error handling.\n- Test AI prompt construction and output integration, ensuring that task complexity is adjusted as expected for each strength level, milestone context, and custom prompt.\n- Verify that historical data is preserved and that before/after summaries are accurate and clear.\n- Confirm that task dependencies remain intact and that batch operations work as intended.\n- Validate MCP tool integration and documentation updates.", @@ -6303,9 +5567,7 @@ "id": 2, "title": "Implement task/subtask retrieval and context gathering", "description": "Create functions to retrieve task/subtask details and gather relevant context information needed for complexity adjustment.", - "dependencies": [ - "104.1" - ], + "dependencies": ["104.1"], "details": "1. Implement a function to fetch task and subtask details by ID from tasks.json\n2. Create a function to gather milestone context for the specified tasks\n3. Implement validation to ensure all specified IDs exist and are valid\n4. Add error handling for missing or invalid tasks/subtasks\n5. Create a data structure to hold the original task state for later comparison\n6. Implement functions to determine if an ID refers to a task or subtask\n7. Add support for handling multiple IDs in a single command execution", "status": "done", "testStrategy": "Test task and subtask retrieval with various ID formats. Verify correct handling of both task and subtask IDs. Test error cases like non-existent IDs and malformed ID strings." @@ -6314,9 +5576,7 @@ "id": 3, "title": "Develop core complexity adjustment logic", "description": "Implement the core functions that construct AI prompts and process responses to adjust task complexity up or down.", - "dependencies": [ - "104.2" - ], + "dependencies": ["104.2"], "details": "1. Create separate handler functions for scope-up and scope-down operations\n2. Implement prompt construction logic that incorporates:\n - Current task/subtask details\n - Milestone context\n - Strength parameter (light/regular/heavy)\n - User-provided custom prompt\n3. Add integration with ai-services-unified.js to generate new content\n4. Implement response parsing to extract updated task details\n5. Create utility functions to determine how much to increase/decrease complexity based on strength\n6. Add special handling for different task components (description, implementation details, test strategy)", "status": "done", "testStrategy": "Test prompt construction with various inputs and strength levels. Mock AI service responses to test parsing logic. Verify that different strength levels produce appropriately scaled adjustments." @@ -6325,9 +5585,7 @@ "id": 4, "title": "Implement task update and persistence logic", "description": "Create functions to update tasks/subtasks with new content while preserving integrity and handling dependencies.", - "dependencies": [ - "104.3" - ], + "dependencies": ["104.3"], "details": "1. Implement functions to update task and subtask objects with new content\n2. Create logic to preserve task dependency integrity during updates\n3. Add version history tracking to store previous versions of tasks\n4. Implement file writing functions to persist changes to tasks.json\n5. Add validation to ensure updated tasks maintain required fields and format\n6. Implement rollback capability in case of errors\n7. Create utility functions to display before/after differences in the CLI\n8. Add support for batch updates when multiple IDs are provided", "status": "done", "testStrategy": "Test task updates with various scenarios including single tasks, subtasks, and multiple items. Verify that dependencies remain intact after updates. Test rollback functionality by simulating errors during the update process." @@ -6336,9 +5594,7 @@ "id": 5, "title": "Integrate with MCP tool and add comprehensive error handling", "description": "Add MCP tool equivalents for the new commands and implement robust error handling throughout the implementation.", - "dependencies": [ - "104.4" - ], + "dependencies": ["104.4"], "details": "1. Create MCP tool equivalents for scope-up and scope-down commands\n2. Implement comprehensive error handling for all potential failure points:\n - Network errors during AI service calls\n - Invalid task/subtask IDs\n - Malformed AI responses\n - File system errors\n3. Add clear user feedback with colorized output showing differences\n4. Implement progress indicators for longer operations\n5. Update documentation to include the new commands\n6. Add examples of common usage patterns to help text\n7. Ensure compatibility with existing task management workflows\n8. Add logging integration using the existing logging system\n<info added on 2025-08-02T14:00:38.363Z>\nMCP Integration Complete:\n\n✅ **Direct Functions Created:**\n- Created `mcp-server/src/core/direct-functions/scope-up.js` - Direct function wrapper for scope-up functionality\n- Created `mcp-server/src/core/direct-functions/scope-down.js` - Direct function wrapper for scope-down functionality\n- Both follow the exact same pattern as add-task direct function with proper error handling and silent mode\n\n✅ **MCP Tools Created:**\n- Created `mcp-server/src/tools/scope-up.js` - MCP tool for scope_up_task \n- Created `mcp-server/src/tools/scope-down.js` - MCP tool for scope_down_task\n- Both follow the exact same pattern as add-task tool with proper Zod schema validation\n\n✅ **Integration Updates:**\n- Added imports and exports to `mcp-server/src/core/task-master-core.js` for both direct functions\n- Added imports and registrations to `mcp-server/src/tools/index.js` in Group 5: Task Creation & Modification\n- Tools will be available as `mcp_task-master-ai_scope_up_task` and `mcp_task-master-ai_scope_down_task` after MCP server restart\n\n✅ **Tool Parameters:**\n- `id`: Comma-separated list of task IDs (required)\n- `strength`: light/regular/heavy (optional, defaults to regular) \n- `prompt`: Custom scoping instructions (optional)\n- `file`: Path to tasks.json (optional, auto-detected)\n- `projectRoot`: Absolute project path (required)\n- `tag`: Tag context (optional, uses current tag)\n- `research`: Enable research mode (optional, defaults to false)\n\n✅ **Error Handling:**\n- Comprehensive error handling for missing parameters, invalid task IDs, AI failures\n- Structured error responses with appropriate error codes\n- Silent mode handling to prevent CLI output interference\n\n**Status**: MCP integration complete. Requires MCP server restart for new tools to become available.\n</info added on 2025-08-02T14:00:38.363Z>", "status": "done", "testStrategy": "Test error handling by simulating various failure conditions. Verify that appropriate error messages are displayed. Test MCP tool integration and ensure consistent behavior between CLI and MCP interfaces. Perform end-to-end testing with real tasks to verify the complete workflow." @@ -6361,9 +5617,7 @@ "details": "Implement TTS functionality including:\n- Add --tts flag to all relevant taskmaster commands (list, show, generate, etc.)\n- Integrate with system TTS engines (Windows SAPI, macOS say command, Linux espeak/festival)\n- Create TTS configuration options in the configuration management system\n- Add voice selection options (male/female, different languages if available)\n- Implement audio output settings (volume, speed, pitch)\n- Add TTS-specific error handling for cases where TTS is unavailable\n- Create fallback behavior when TTS fails (silent failure or text output)\n- Support for reading task titles, descriptions, and status updates aloud\n- Add option to read entire task lists or individual task details\n- Implement TTS for command confirmations and error messages\n- Create TTS output formatting to make spoken text more natural (removing markdown, formatting numbers/dates appropriately)\n- Add configuration option to enable/disable TTS globally\n<info added on 2025-06-14T21:55:53.499Z>\nAdd comprehensive testing strategy for TTS functionality:\n\n**TTS Testing Requirements:**\n- Test TTS flag functionality across all commands (list, show, generate) with various voice configurations\n- Validate TTS engine integration on different platforms (Windows SAPI, macOS say, Linux espeak/festival)\n- Test voice selection options and audio output settings (volume, speed, pitch) with edge cases\n- Verify TTS error handling when engines are unavailable or fail\n- Test fallback behavior scenarios (silent failure vs text output)\n- Validate TTS output formatting for natural speech (markdown removal, number/date pronunciation)\n- Test global TTS enable/disable configuration settings\n- Verify TTS works correctly with task titles, descriptions, and status updates of varying lengths and complexity\n- Test TTS performance with large task lists and individual task details\n- Validate TTS for command confirmations and error messages across different error scenarios\n\n**Automated TTS Test Cases:**\n- Enable TTS flag and verify audio output generation without errors\n- Test each supported TTS engine with fallback when primary engine fails\n- Validate configuration persistence for TTS settings across application restarts\n- Test TTS with special characters, long text, and multilingual content\n- Verify TTS integration doesn't interfere with normal command execution or file operations\n- Test concurrent TTS operations and resource management\n- Validate TTS accessibility compliance and user experience consistency\n</info added on 2025-06-14T21:55:53.499Z>\n<info added on 2025-06-14T22:07:04.840Z>\n**Duplicate Save Prevention Testing for TTS Implementation:**\n\nSince TTS functionality involves configuration persistence and potential concurrent operations, implement specific tests to prevent duplicate saves in the TTS context:\n\n- Test TTS configuration saves to prevent duplicate entries in configuration files when users rapidly change voice settings, volume, or other audio parameters\n- Validate that TTS engine initialization doesn't create duplicate configuration entries when switching between different TTS engines (Windows SAPI, macOS say, Linux espeak)\n- Test concurrent TTS operations to ensure audio output settings aren't duplicated when multiple commands with --tts flag run simultaneously\n- Verify that TTS preference saves are atomic and don't result in corrupted or duplicate configuration data during rapid user interactions\n- Implement unique constraint checks for TTS configuration entries to prevent duplicate voice profiles or audio settings\n- Test TTS configuration persistence across application restarts to ensure settings aren't duplicated on reload\n- Validate that TTS error logging doesn't create duplicate log entries when TTS engines fail or fallback mechanisms activate\n- Test edge cases where users rapidly toggle global TTS enable/disable settings to prevent duplicate configuration states\n- Ensure TTS-related task metadata (like audio output preferences per task) doesn't create duplicate entries in tasks.json\n- Implement locking mechanisms for TTS configuration file operations to prevent race conditions during concurrent access\n\nThis testing should be integrated with the existing TTS test strategy to ensure robust duplicate prevention across all TTS-related save operations.\n</info added on 2025-06-14T22:07:04.840Z>\n<info added on 2025-06-14T22:08:10.995Z>\n**Claude API Integration Testing for TTS Commands:**\n\nAdd specific testing requirements for Claude API integration within the TTS implementation context:\n\n- Test Claude API connectivity when generating TTS-enabled task content to ensure API calls don't interfere with audio output generation\n- Validate Claude API authentication and error handling when TTS commands request AI-generated content with --tts flag enabled\n- Test Claude API response parsing and integration with TTS output formatting to ensure AI-generated text is properly converted for speech synthesis\n- Verify Claude API token usage tracking doesn't conflict with TTS configuration persistence mechanisms\n- Test concurrent operations where Claude API calls and TTS audio generation occur simultaneously\n- Validate Claude API retry and backoff logic works correctly when TTS commands fail and need to regenerate content\n- Test Claude API integration with task generation workflows that include TTS output requirements\n- Ensure Claude API error messages are properly formatted for TTS output when --tts flag is enabled\n- Test Claude API model parameter configuration persistence alongside TTS configuration settings\n- Validate that Claude API failures gracefully fallback without breaking TTS functionality for existing task content\n- Test environment variable handling for ANTHROPIC_API_KEY in conjunction with TTS engine configuration\n- Verify Claude API integration logging doesn't create conflicts with TTS error logging mechanisms\n\nThis testing should ensure seamless integration between Claude API functionality and TTS features without interference or duplicate save issues.\n</info added on 2025-06-14T22:08:10.995Z>\n<info added on 2025-06-14T22:10:22.106Z>\n**Final Duplicate Save Testing Protocol - Research Session 6/14/2025:**\n\n**Comprehensive Test Environment Setup:**\n- Create clean test environment with known state of tasks.json and TTS configuration files\n- Back up current tasks.json, TTS settings, and Claude API configuration before testing\n- Ensure all TTS engines and Claude API connectivity are functional for comprehensive testing\n\n**Duplicate Save Test Scenarios for TTS Implementation:**\n- Test saving TTS configuration with identical voice settings to verify no duplicate entries\n- Attempt simultaneous TTS configuration saves while audio output is active\n- Test Claude API task generation with --tts flag to ensure no duplicate task entries in tasks.json\n- Validate TTS preference persistence doesn't create duplicate configuration entries during rapid setting changes\n- Test concurrent TTS operations with multiple commands to verify no duplicate audio output settings\n- Simulate race conditions between TTS configuration saves and Claude API calls\n\n**Manual and Automated Test Execution:**\n- Execute TTS commands with --tts flag while monitoring tasks.json for duplicate entries\n- Test Claude API integration with TTS-enabled commands to verify single task creation\n- Validate TTS configuration file integrity after multiple rapid setting changes\n- Test TTS error logging to ensure no duplicate log entries during engine failures\n- Verify TTS-related task metadata saves don't create duplicate entries\n\n**Edge Case Testing for TTS Context:**\n- Test TTS configuration saves with minor variations (case sensitivity, whitespace) in voice names\n- Validate duplicate detection with similar TTS settings across different engines\n- Test large-scale TTS operations to ensure performance and correctness\n- Verify TTS global enable/disable toggle doesn't create duplicate configuration states\n\n**Validation and Documentation:**\n- Monitor TTS configuration files, tasks.json, and logs for any duplicate entries\n- Document test results in provided table format with TTS-specific scenarios\n- Verify error handling provides clear feedback for TTS-related duplicate save attempts\n- Confirm regression testing covers all TTS functionality without introducing new duplicate save issues\n- Complete stakeholder confirmation before closing duplicate save testing for TTS implementation\n</info added on 2025-06-14T22:10:22.106Z>", "testStrategy": "Test TTS functionality across different operating systems (Windows, macOS, Linux). Verify that the --tts flag works with all major commands. Test voice configuration options and ensure audio output settings are properly applied. Test error handling when TTS services are unavailable. Verify that text formatting for speech is natural and understandable. Test with various task content types including special characters, code snippets, and long descriptions. Ensure TTS can be disabled and enabled through configuration.", "status": "pending", - "dependencies": [ - 16 - ], + "dependencies": [16], "priority": "medium", "subtasks": [] } @@ -6440,9 +5694,7 @@ "details": "Implement dependency graph management using efficient data structures to support large graphs (500+ tasks). Integrate with Taskmaster MCP commands to fetch and monitor task dependencies and tags. Implement circular dependency detection algorithms. Use event-driven notifications for dependency status changes. Optimize for sub-second response times. Cache dependency data for performance.", "testStrategy": "Unit test dependency graph operations and circular dependency detection. Integration test with Taskmaster MCP mock server. Performance test with large dependency graphs. Validate event notifications on dependency changes.", "priority": "high", - "dependencies": [ - 1 - ], + "dependencies": [1], "status": "pending", "subtasks": [ { @@ -6458,9 +5710,7 @@ "id": 2, "title": "Integrate Taskmaster MCP Commands for Dependency Fetching and Monitoring", "description": "Connect the dependency monitor to Taskmaster MCP to fetch task dependencies and tags, and monitor their status in real-time.", - "dependencies": [ - "2.1" - ], + "dependencies": ["2.1"], "details": "Implement API integration with Taskmaster MCP commands to retrieve task dependency data and tags. Set up listeners or polling mechanisms to detect changes in dependencies and update the graph accordingly.", "status": "pending", "testStrategy": "Integration test with Taskmaster MCP mock server to validate data fetching and real-time monitoring." @@ -6469,9 +5719,7 @@ "id": 3, "title": "Implement Circular Dependency Detection Algorithms", "description": "Develop algorithms to detect circular dependencies within the task dependency graph to prevent execution deadlocks.", - "dependencies": [ - "2.1" - ], + "dependencies": ["2.1"], "details": "Use graph traversal techniques such as depth-first search (DFS) or Tarjan's algorithm to identify cycles in the dependency graph. Ensure detection is efficient to maintain sub-second response times.", "status": "pending", "testStrategy": "Unit test with various graph scenarios including cyclic and acyclic graphs to verify detection accuracy." @@ -6480,10 +5728,7 @@ "id": 4, "title": "Develop Event-Driven Notification System for Dependency Status Changes", "description": "Create an event-driven mechanism to notify relevant components or users when dependency statuses change.", - "dependencies": [ - "2.2", - "2.3" - ], + "dependencies": ["2.2", "2.3"], "details": "Implement event listeners that trigger notifications on dependency status updates, such as completion, failure, or changes in dependency relationships. Ensure notifications are timely and scalable.", "status": "pending", "testStrategy": "Integration test event notifications with simulated dependency status changes to verify correct triggering and delivery." @@ -6492,12 +5737,7 @@ "id": 5, "title": "Optimize Performance and Cache Dependency Data", "description": "Enhance the system to achieve sub-second response times by optimizing algorithms and caching dependency data effectively.", - "dependencies": [ - "2.1", - "2.2", - "2.3", - "2.4" - ], + "dependencies": ["2.1", "2.2", "2.3", "2.4"], "details": "Profile and optimize graph operations and data fetching. Implement caching strategies to reduce redundant computations and network calls. Validate that the system maintains performance under load with large dependency graphs.", "status": "pending", "testStrategy": "Performance test with large-scale graphs and high-frequency updates to ensure response time targets are met." @@ -6511,9 +5751,7 @@ "details": "Use a priority queue data structure to manage task execution order. Support up to 10 concurrent tasks with resource reservation and conflict detection. Integrate with Taskmaster MCP commands for task execution, cancellation, and status updates. Implement timeout and cancellation logic. Persist execution history in SQLite for analytics. Provide APIs for querying execution status and history.", "testStrategy": "Unit test queue management, priority scheduling, and concurrency control. Integration test with Taskmaster MCP commands. Simulate resource conflicts and timeouts. Verify execution history persistence and retrieval.", "priority": "high", - "dependencies": [ - 1 - ], + "dependencies": [1], "status": "pending", "subtasks": [ { @@ -6529,9 +5767,7 @@ "id": 2, "title": "Implement Parallel Task Execution with Resource Conflict Detection", "description": "Enable concurrent execution of up to 10 tasks while managing resource reservations and detecting conflicts to prevent resource contention.", - "dependencies": [ - "3.1" - ], + "dependencies": ["3.1"], "details": "Develop concurrency control to allow up to 10 parallel tasks. Implement resource reservation mechanisms and conflict detection algorithms to avoid simultaneous access to conflicting resources. Integrate with the priority queue to select tasks eligible for execution.", "status": "pending", "testStrategy": "Unit test concurrency limits and resource conflict detection logic. Simulate resource conflicts and verify that conflicting tasks are not executed concurrently." @@ -6540,9 +5776,7 @@ "id": 3, "title": "Integrate Taskmaster MCP Commands for Task Lifecycle Management", "description": "Integrate the execution manager with Taskmaster MCP commands to support task execution, cancellation, and status updates.", - "dependencies": [ - "3.1" - ], + "dependencies": ["3.1"], "details": "Implement interfaces to send commands to Taskmaster MCP for starting, cancelling, and querying task statuses. Ensure synchronization between the execution manager state and Taskmaster MCP responses.", "status": "pending", "testStrategy": "Integration test with Taskmaster MCP mock or real API. Verify correct command dispatch, response handling, and state synchronization." @@ -6551,10 +5785,7 @@ "id": 4, "title": "Implement Timeout and Cancellation Logic for Task Execution", "description": "Add timeout handling and cancellation support to manage long-running or stalled tasks effectively.", - "dependencies": [ - "3.2", - "3.3" - ], + "dependencies": ["3.2", "3.3"], "details": "Implement mechanisms to track task execution time and cancel tasks exceeding their timeout thresholds. Support manual cancellation requests and ensure proper cleanup and state updates upon cancellation.", "status": "pending", "testStrategy": "Unit test timeout triggers and cancellation flows. Simulate long-running tasks and verify timely cancellation and resource release." @@ -6563,9 +5794,7 @@ "id": 5, "title": "Persist Execution History and Provide Query APIs", "description": "Store task execution history in SQLite for analytics and provide APIs to query execution status and history.", - "dependencies": [ - "3.4" - ], + "dependencies": ["3.4"], "details": "Design a schema in SQLite to record task execution details including start time, end time, status, priority, and resource usage. Implement APIs to query current execution status and historical data for analytics and reporting.", "status": "pending", "testStrategy": "Unit test database persistence and retrieval operations. Integration test API endpoints for querying execution status and history." @@ -6579,10 +5808,7 @@ "details": "Design safety constraint schemas and validation logic using JSON Schema. Implement emergency stop and pause functionality with immediate effect. Integrate user approval workflows for critical tasks. Monitor resource usage and detect runaway automation. Implement file permission validation and backup mechanisms. Log all safety-related events for audit purposes.", "testStrategy": "Unit test safety constraint validation and emergency controls. Simulate safety violations and verify automatic responses. Test user approval workflows. Perform security testing for file protection and access control.", "priority": "high", - "dependencies": [ - 1, - 3 - ], + "dependencies": [1, 3], "status": "pending", "subtasks": [ { @@ -6598,9 +5824,7 @@ "id": 2, "title": "Implement Emergency Stop and Pause Functionality", "description": "Develop mechanisms to immediately halt or pause ongoing automation tasks upon emergency triggers.", - "dependencies": [ - "4.1" - ], + "dependencies": ["4.1"], "details": "Build emergency stop and pause controls that can be triggered manually or automatically. Ensure these controls take immediate effect to prevent unsafe operations and allow safe recovery or intervention.", "status": "pending", "testStrategy": "Simulate emergency conditions and verify that tasks stop or pause instantly and safely." @@ -6609,9 +5833,7 @@ "id": 3, "title": "Integrate User Approval Workflows for Critical Tasks", "description": "Create workflows requiring explicit user approval before executing high-risk or critical automation tasks.", - "dependencies": [ - "4.1" - ], + "dependencies": ["4.1"], "details": "Design and implement user interaction flows that prompt for approval on critical operations. Ensure integration with task execution logic to block unauthorized or unapproved actions.", "status": "pending", "testStrategy": "Test approval prompts, user responses, and enforcement of approval requirements before task execution." @@ -6620,9 +5842,7 @@ "id": 4, "title": "Implement File Protection and Backup Mechanisms", "description": "Develop validation for file permissions and automatic backup processes to protect critical files from unauthorized changes or loss.", - "dependencies": [ - "4.1" - ], + "dependencies": ["4.1"], "details": "Validate file access permissions before operations to prevent unauthorized modifications. Implement backup routines to save file states prior to changes, enabling recovery in case of violations or failures.", "status": "pending", "testStrategy": "Perform security testing on file access controls and verify backup creation and restoration processes." @@ -6631,10 +5851,7 @@ "id": 5, "title": "Monitor Resource Usage and Log Safety Events", "description": "Continuously monitor resource consumption to detect runaway automation and log all safety-related events for auditing.", - "dependencies": [ - "4.1", - "4.2" - ], + "dependencies": ["4.1", "4.2"], "details": "Implement monitoring tools to track CPU, memory, and other resource usage in real-time. Detect violations such as runaway tasks and trigger safety responses. Log all safety events including constraint violations, emergency stops, approvals, and file protections for audit trails.", "status": "pending", "testStrategy": "Simulate resource overuse and safety violations to verify detection and logging accuracy." @@ -6648,9 +5865,7 @@ "details": "Integrate with Kiro's file watcher for file system monitoring. Use Taskmaster MCP API to detect task status changes. Integrate Git event listeners for commits, branch changes, and merges. Connect to build system events for test and compilation results. Implement configurable event filters and pattern matching. Use debouncing and batching to optimize performance under high-frequency events.", "testStrategy": "Unit test event filtering, debouncing, and batching logic. Integration test with simulated file system, Git, and build events. Verify correct triggering of hooks and task execution.", "priority": "medium", - "dependencies": [ - 1 - ], + "dependencies": [1], "status": "pending", "subtasks": [ { @@ -6693,12 +5908,7 @@ "id": 5, "title": "Implement Configurable Event Filtering, Pattern Matching, Debouncing, and Batching", "description": "Develop filtering mechanisms and pattern matching for events, and implement debouncing and batching to optimize performance under high-frequency event loads.", - "dependencies": [ - "5.1", - "5.2", - "5.3", - "5.4" - ], + "dependencies": ["5.1", "5.2", "5.3", "5.4"], "details": "Create configurable filters to selectively process events based on criteria. Implement pattern matching for event attributes. Use debouncing to delay processing of rapid event bursts and batching to group events for efficient handling.", "status": "pending", "testStrategy": "Unit test filtering, pattern matching, debouncing, and batching logic. Integration test with high-frequency simulated events to validate performance optimization and correct hook triggering." @@ -6712,9 +5922,7 @@ "details": "Integrate with Kiro's agent communication system to intercept prompts. Analyze conversation content using natural language processing to suggest relevant tasks. Inject task context dynamically into AI conversations. Update task statuses based on agent responses. Implement relevance scoring and context-aware filtering for task suggestions.", "testStrategy": "Unit test prompt interception and context injection. Use mock AI conversations to validate task suggestion accuracy. Integration test with Kiro agent communication system. Verify task status updates triggered by prompts.", "priority": "medium", - "dependencies": [ - 1 - ], + "dependencies": [1], "status": "pending", "subtasks": [ { @@ -6730,9 +5938,7 @@ "id": 2, "title": "Implement Natural Language Processing for Task Suggestion", "description": "Analyze intercepted conversation content using NLP techniques to automatically suggest relevant tasks based on the context.", - "dependencies": [ - "6.1" - ], + "dependencies": ["6.1"], "details": "Use NLP models to parse and understand agent prompts and conversation transcripts. Extract intent and key entities to match against existing tasks. Implement relevance scoring and context-aware filtering to prioritize task suggestions.", "status": "pending", "testStrategy": "Unit test NLP parsing and intent extraction with diverse conversation samples. Validate task suggestion accuracy using mock AI conversations." @@ -6741,9 +5947,7 @@ "id": 3, "title": "Develop Dynamic Task Context Injection into AI Conversations", "description": "Create functionality to inject relevant task context dynamically into AI conversations to enhance agent understanding and response quality.", - "dependencies": [ - "6.1" - ], + "dependencies": ["6.1"], "details": "Design the context injection mechanism to append or embed task-related information into AI prompts or responses. Ensure context is relevant, concise, and updated in real-time based on conversation flow.", "status": "pending", "testStrategy": "Unit test context injection with simulated AI conversations. Verify that injected context improves task relevance without causing prompt overload." @@ -6752,10 +5956,7 @@ "id": 4, "title": "Implement Task Status Update Mechanism Based on Agent Responses", "description": "Build the logic to update task statuses automatically by interpreting agent responses and AI conversation outcomes.", - "dependencies": [ - "6.2", - "6.3" - ], + "dependencies": ["6.2", "6.3"], "details": "Monitor agent replies and AI outputs to detect task progress or completion signals. Map conversation cues to task status changes such as 'in progress', 'completed', or 'needs review'. Ensure updates are synchronized with the task management system.", "status": "pending", "testStrategy": "Unit test status update triggers with varied agent response scenarios. Integration test with task management API to confirm status synchronization." @@ -6764,9 +5965,7 @@ "id": 5, "title": "Implement Relevance Scoring and Context-Aware Filtering for Task Suggestions", "description": "Enhance task suggestion accuracy by applying relevance scoring algorithms and context-aware filters to prioritize and refine suggested tasks.", - "dependencies": [ - "6.2" - ], + "dependencies": ["6.2"], "details": "Develop scoring models that evaluate task relevance based on conversation context, task metadata, and historical data. Apply filters to exclude irrelevant or low-priority tasks. Continuously refine scoring criteria based on feedback and usage patterns.", "status": "pending", "testStrategy": "Unit test scoring and filtering logic with controlled datasets. Perform A/B testing to measure improvement in suggestion relevance." @@ -6780,9 +5979,7 @@ "details": "Monitor code changes via file system and version control integration. Track implementation progress and milestones automatically. Validate task acceptance criteria and mark completion. Propagate status updates through dependency chains. Detect and resolve conflicts in task updates. Integrate with version control systems for change tracking.", "testStrategy": "Unit test progress tracking and acceptance validation logic. Integration test with version control events and Taskmaster MCP updates. Simulate conflict scenarios and verify resolution mechanisms.", "priority": "medium", - "dependencies": [ - 1 - ], + "dependencies": [1], "status": "pending", "subtasks": [ { @@ -6798,9 +5995,7 @@ "id": 2, "title": "Develop Acceptance Criteria Validation and Task Completion Marking", "description": "Create logic to validate task acceptance criteria automatically upon code updates and mark tasks as completed when criteria are met.", - "dependencies": [ - "7.1" - ], + "dependencies": ["7.1"], "details": "Implement validation rules to check if code changes satisfy predefined acceptance criteria. Automate task status updates to 'completed' when criteria validation passes.", "status": "pending", "testStrategy": "Unit test acceptance criteria validation logic with various scenarios. Integration test with task status updates triggered by code changes." @@ -6809,9 +6004,7 @@ "id": 3, "title": "Implement Dependency Update Propagation Through Task Chains", "description": "Design and implement propagation of status updates through dependent tasks to maintain consistency across the dependency graph.", - "dependencies": [ - "7.2" - ], + "dependencies": ["7.2"], "details": "Use efficient data structures to manage dependency chains and propagate status changes downstream. Integrate with Taskmaster MCP to fetch and update dependency statuses.", "status": "pending", "testStrategy": "Unit test dependency propagation logic. Integration test with Taskmaster MCP mock server to verify correct update flows." @@ -6820,9 +6013,7 @@ "id": 4, "title": "Detect and Resolve Conflicts in Task Updates", "description": "Develop mechanisms to detect conflicts arising from concurrent or incompatible task updates and implement resolution strategies.", - "dependencies": [ - "7.1" - ], + "dependencies": ["7.1"], "details": "Implement conflict detection algorithms triggered by update hooks. Provide automated or manual conflict resolution workflows to maintain task integrity.", "status": "pending", "testStrategy": "Simulate conflict scenarios and verify detection accuracy. Test resolution mechanisms for correctness and robustness." @@ -6831,10 +6022,7 @@ "id": 5, "title": "Integrate Update-Based Hook Processor with Version Control Systems", "description": "Ensure seamless integration of the update-based hook processor with version control systems for effective change tracking and hook execution.", - "dependencies": [ - "7.1", - "7.4" - ], + "dependencies": ["7.1", "7.4"], "details": "Implement Git update hook scripts that invoke the processor before ref updates. Ensure hooks are executable and handle parameters such as refname and old/new commit hashes.", "status": "pending", "testStrategy": "Integration test with Git repositories to verify hook triggering on push events. Validate correct parameter handling and processor invocation." @@ -6848,10 +6036,7 @@ "details": "Use React with TypeScript for UI components. Implement WebSocket client for real-time updates. Use D3.js for interactive dependency chain visualization and Chart.js for analytics. Provide controls for pause/resume/stop automation, hook toggles, manual task triggers, safety configuration, and tag management. Style with CSS Modules. Ensure responsive and performant UI.", "testStrategy": "Unit test UI components with Jest and React Testing Library. Perform integration tests with backend WebSocket server. Conduct usability testing with sample users. Validate real-time updates and control responsiveness.", "priority": "medium", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "status": "pending", "subtasks": [ { @@ -6876,9 +6061,7 @@ "id": 3, "title": "Build Interactive Visualization Components", "description": "Develop interactive visualizations for dependency graphs (D3.js) and analytics charts (Chart.js) to display task dependencies, execution trends, and system health.", - "dependencies": [ - "8.2" - ], + "dependencies": ["8.2"], "details": "Implement a dependency graph visualization using D3.js for interactive exploration of task dependencies and status. Use Chart.js for rendering analytics (e.g., execution history, queue lengths, health metrics). Ensure visualizations are performant with large datasets and support user interactions like zoom, filter, and tooltips.", "status": "pending", "testStrategy": "Unit test visualization components for rendering accuracy and interaction handling. Integration test with live and simulated data streams. Validate performance with large datasets." @@ -6896,12 +6079,7 @@ "id": 5, "title": "Ensure Dashboard Performance, Responsiveness, and Maintainability", "description": "Optimize the dashboard for fast rendering, smooth interactions, and maintainable code structure.", - "dependencies": [ - "8.1", - "8.2", - "8.3", - "8.4" - ], + "dependencies": ["8.1", "8.2", "8.3", "8.4"], "details": "Profile and optimize dashboard performance, focusing on rendering speed, memory usage, and real-time update handling. Enforce TypeScript best practices: strict typing, consistent naming, and avoidance of 'any' type[1]. Use React hooks and functional components for state management. Apply CSS Modules for scoped, maintainable styles. Implement code splitting and lazy loading where beneficial.", "status": "pending", "testStrategy": "Performance test dashboard under heavy data load and frequent updates. Audit code for TypeScript and React best practices[1]. Conduct cross-browser and cross-device responsiveness testing." @@ -6915,11 +6093,7 @@ "details": "Implement KiroHookAdapter for hook registration, lifecycle, and event handling within Kiro IDE plugin architecture. Implement TaskmasterMCPAdapter for full MCP command support including task operations, tags, complexity analysis, and research features. Integrate file system monitoring and agent communication. Ensure error handling and retry logic for MCP communication. Integrate UI components into Kiro IDE interface.", "testStrategy": "Perform end-to-end integration testing with Kiro IDE and Taskmaster MCP servers. Validate all hook types and task operations. Test error handling and recovery. Conduct performance and compatibility testing within Kiro IDE.", "priority": "high", - "dependencies": [ - 1, - 3, - 7 - ], + "dependencies": [1, 3, 7], "status": "pending", "subtasks": [ { @@ -6935,9 +6109,7 @@ "id": 2, "title": "Implement TaskmasterMCPAdapter for MCP Command Support", "description": "Create the TaskmasterMCPAdapter to provide full support for MCP commands including task operations, tags, complexity analysis, and research features.", - "dependencies": [ - "9.1" - ], + "dependencies": ["9.1"], "details": "Develop the adapter to interface with Taskmaster MCP servers, supporting commands for task creation, updates, tagging, complexity metrics, and research functionalities. Ensure robust communication and command execution.", "status": "pending", "testStrategy": "Unit test MCP command implementations and command parsing. Integration test with Taskmaster MCP servers to validate command execution and response handling." @@ -6946,10 +6118,7 @@ "id": 3, "title": "Integrate File System Monitoring and Agent Communication", "description": "Integrate file system event monitoring and agent communication mechanisms to support real-time updates and interactions within Kiro IDE.", - "dependencies": [ - "9.1", - "9.2" - ], + "dependencies": ["9.1", "9.2"], "details": "Implement file watcher integration to detect file changes and trigger appropriate hooks or MCP commands. Establish reliable communication channels between Kiro IDE agents and backend services.", "status": "pending", "testStrategy": "Test file system event detection and propagation. Validate agent communication reliability and message handling under various scenarios." @@ -6958,10 +6127,7 @@ "id": 4, "title": "Implement Error Handling and Retry Logic for MCP Communication", "description": "Develop comprehensive error handling and retry mechanisms for all MCP communication to ensure robustness and fault tolerance.", - "dependencies": [ - "9.2", - "9.3" - ], + "dependencies": ["9.2", "9.3"], "details": "Design error detection, logging, and recovery strategies for MCP command failures. Implement retry policies with backoff and failure escalation to maintain stable integration.", "status": "pending", "testStrategy": "Simulate MCP communication failures and verify error handling paths. Test retry logic effectiveness and system recovery under intermittent failures." @@ -6970,12 +6136,7 @@ "id": 5, "title": "Integrate UI Components into Kiro IDE Interface", "description": "Integrate and embed UI components related to Kiro hooks and Taskmaster MCP commands into the Kiro IDE interface for user interaction.", - "dependencies": [ - "9.1", - "9.2", - "9.3", - "9.4" - ], + "dependencies": ["9.1", "9.2", "9.3", "9.4"], "details": "Develop UI elements to display hook statuses, task operations, tags, and complexity analysis results. Ensure seamless user experience and interaction within the Kiro IDE environment.", "status": "pending", "testStrategy": "Perform UI integration testing to validate component rendering and interaction. Conduct end-to-end tests to verify UI updates reflect backend state changes accurately." @@ -6989,10 +6150,7 @@ "details": "Build a React-based configuration editor UI with templates and validation using JSON Schema. Support editing of automation rules, safety profiles, resource limits, and tag inheritance. Implement import/export functionality for configuration sharing. Integrate version control for configuration history and rollback. Provide real-time validation feedback and error highlighting.", "testStrategy": "Unit test configuration editor components and validation logic. Integration test import/export and version control features. Perform user acceptance testing for usability and correctness.", "priority": "medium", - "dependencies": [ - 1, - 4 - ], + "dependencies": [1, 4], "status": "pending", "subtasks": [ { @@ -7008,9 +6166,7 @@ "id": 2, "title": "Implement JSON Schema-Based Validation and Real-Time Feedback", "description": "Integrate JSON Schema validation into the editor to validate configuration inputs dynamically and provide immediate error highlighting and feedback to users.", - "dependencies": [ - "10.1" - ], + "dependencies": ["10.1"], "details": "Use JSON Schema to define validation rules for automation rules, safety profiles, resource limits, and tag inheritance. Implement real-time validation that triggers on user input and displays errors inline within the editor UI.", "status": "pending", "testStrategy": "Unit test validation logic with various valid and invalid configuration samples. Verify error messages and UI error highlighting." @@ -7019,10 +6175,7 @@ "id": 3, "title": "Develop Import and Export Functionality for Configuration Sharing", "description": "Enable users to import existing configuration files and export current configurations for sharing and backup purposes.", - "dependencies": [ - "10.1", - "10.2" - ], + "dependencies": ["10.1", "10.2"], "details": "Implement file handling features to load configuration JSON files into the editor and export the current configuration state to JSON files. Ensure validation is performed on import and errors are reported to users.", "status": "pending", "testStrategy": "Integration test import/export workflows with valid and invalid files. Verify that imported configurations populate the editor correctly and exported files match the current state." @@ -7031,10 +6184,7 @@ "id": 4, "title": "Integrate Version Control for Configuration History and Rollback", "description": "Add version control capabilities to track configuration changes, view history, and rollback to previous versions within the editor.", - "dependencies": [ - "10.1", - "10.3" - ], + "dependencies": ["10.1", "10.3"], "details": "Implement integration with a version control system (e.g., Git or a custom solution) to save configuration snapshots. Provide UI components to browse history, compare versions, and revert changes. Ensure seamless user experience within the React editor.", "status": "pending", "testStrategy": "Integration test version control operations including commit, history browsing, diff viewing, and rollback. Verify data integrity and UI responsiveness." @@ -7043,10 +6193,7 @@ "id": 5, "title": "Support Editing of Automation Rules, Safety Profiles, Resource Limits, and Tag Inheritance", "description": "Extend the editor to support detailed editing capabilities for all configuration aspects including automation rules, safety constraints, resource limits, and tag contexts with inheritance.", - "dependencies": [ - "10.1", - "10.2" - ], + "dependencies": ["10.1", "10.2"], "details": "Design and implement UI forms and editors for each configuration domain. Ensure that editing respects validation rules and supports inheritance logic for tags. Provide user-friendly interfaces for complex configuration structures.", "status": "pending", "testStrategy": "Unit and integration test editing workflows for each configuration type. Validate inheritance behavior and constraint enforcement." @@ -7085,9 +6232,7 @@ "id": 2, "title": "Configure build and test infrastructure", "description": "Set up tsup build configuration for dual format support and Jest testing configuration", - "dependencies": [ - "115.1" - ], + "dependencies": ["115.1"], "details": "Create tsup.config.js with dual format configuration (ESM and CJS), entry points from src/index.ts, declaration files generation, and sourcemaps. Configure jest.config.js with TypeScript preset, ESM support, proper module name mapping, coverage thresholds (80%), and test environment setup. Create .gitignore for node_modules, dist, and coverage directories. Add npm scripts in package.json for build, test, test:watch, and test:coverage commands.\n<info added on 2025-08-06T10:50:49.396Z>\nBuild process successfully configured with tsup.config.ts (TypeScript configuration file instead of JavaScript) supporting dual format output and multiple entry points including submodules. Jest configuration established with comprehensive ESM support and path alias mapping. Created tests/setup.ts for centralized test environment configuration. Added ES2022 compilation target for modern JavaScript features. Enhanced .gitignore to exclude additional development-specific files beyond the basic directories.\n</info added on 2025-08-06T10:50:49.396Z>", "status": "done", "testStrategy": "Run 'npm run build' to verify tsup configuration works, execute 'npm test' with a simple test file to confirm Jest setup, check that both .mjs and .cjs files are generated in dist/" @@ -7096,9 +6241,7 @@ "id": 3, "title": "Create barrel export files for all directories", "description": "Implement index.ts files in each directory to enable clean imports throughout the package", - "dependencies": [ - "115.1" - ], + "dependencies": ["115.1"], "details": "Create index.ts in src/ that exports from all subdirectories. Create index.ts in each subdirectory (types/, interfaces/, providers/, parsers/, builders/, utils/, errors/) with appropriate exports. For now, add placeholder comments indicating what will be exported from each module. Ensure proper export syntax for TypeScript types and interfaces using 'export type' where appropriate. Structure exports to allow consumers to import like '@task-master/tm-core/types' or from the main entry point.\n<info added on 2025-08-06T10:51:56.837Z>\nImplementation complete. All barrel export files have been created successfully with:\n\n- Main src/index.ts exporting from all subdirectories with proper TypeScript syntax\n- Individual index.ts files in types/, providers/, storage/, parser/, utils/, and errors/ directories\n- Proper ES module syntax with .js extensions for TypeScript compatibility\n- Placeholder exports with @deprecated JSDoc tags to indicate future implementation\n- Clean module structure supporting both root imports and submodule imports like '@task-master/tm-core/types'\n- All files include appropriate documentation comments explaining their purpose\n</info added on 2025-08-06T10:51:56.837Z>", "status": "done", "testStrategy": "Compile with TypeScript to ensure all index.ts files are valid, verify no circular dependencies exist, check that imports from package root work correctly" @@ -7107,10 +6250,7 @@ "id": 4, "title": "Add development tooling and documentation", "description": "Set up development tools, linting, and initial documentation structure", - "dependencies": [ - "115.1", - "115.2" - ], + "dependencies": ["115.1", "115.2"], "details": "Create .eslintrc.js with TypeScript plugin and recommended rules for consistent code style. Add prettier configuration for code formatting. Create README.md with package overview, installation instructions, and usage examples (marked as 'coming soon'). Add CHANGELOG.md to track version changes. Create npm scripts for linting and formatting. Add pre-commit hooks configuration if needed. Document the dual ESM/CJS support in README.\n<info added on 2025-08-06T10:53:45.056Z>\nI'll analyze the user's request and the context to determine what new information should be added to the subtask's details.Successfully completed development tooling and documentation setup. Created .eslintrc.js with TypeScript plugin and comprehensive rules including no-explicit-any, consistent-type-imports, and proper TypeScript checks. Added .prettierrc.json with sensible defaults for consistent code formatting. Created comprehensive README.md with package overview, installation instructions, usage examples for both ESM and CommonJS, modular imports, architecture description, development setup, and detailed roadmap for tasks 116-125. Added CHANGELOG.md following Keep a Changelog format with current package status and planned features. All development tooling is configured and ready for use.\n</info added on 2025-08-06T10:53:45.056Z>", "status": "done", "testStrategy": "Run eslint on sample TypeScript files, verify prettier formats code consistently, ensure all npm scripts execute without errors" @@ -7119,12 +6259,7 @@ "id": 5, "title": "Validate package structure and prepare for development", "description": "Perform final validation of the package structure and ensure it's ready for implementation", - "dependencies": [ - "115.1", - "115.2", - "115.3", - "115.4" - ], + "dependencies": ["115.1", "115.2", "115.3", "115.4"], "details": "Run 'npm install' to ensure all dependencies are properly resolved. Execute 'tsc --noEmit' to verify TypeScript configuration is correct. Create a simple smoke test in tests/ that imports from the package to verify module resolution works. Ensure the package can be linked locally for testing in other projects. Verify that both CommonJS and ESM imports work correctly. Create a checklist in README for remaining implementation tasks based on tasks 116-125.\n<info added on 2025-08-06T11:02:21.457Z>\nSuccessfully validated package structure with comprehensive testing. All validations passed: npm install resolved dependencies without issues, TypeScript compilation (tsc --noEmit) showed no errors, and dual-format build (npm run build) successfully generated both ESM and CJS outputs with proper TypeScript declarations. Created and executed comprehensive smoke test suite covering all module imports, placeholder functionality, and type definitions - all 8 tests passing. Code quality tools (ESLint, Prettier) are properly configured and show no issues. Package is confirmed ready for local linking and supports both CommonJS and ESM import patterns. README updated with implementation checklist marking Task 115 as complete and clearly outlining remaining implementation tasks 116-125. Package structure validation is complete and development environment is fully prepared for core implementation phase.\n</info added on 2025-08-06T11:02:21.457Z>", "status": "done", "testStrategy": "Successfully run all build and test commands, verify package can be imported in both ESM and CJS test files, ensure TypeScript compilation produces no errors, confirm all directories contain appropriate index.ts files" @@ -7138,9 +6273,7 @@ "details": "Create types/index.ts with Task, Subtask, TaskMetadata interfaces and type literals (TaskStatus, TaskPriority, TaskComplexity). Create all interface files: storage.interface.ts with IStorage methods, ai-provider.interface.ts with IAIProvider and AIOptions, configuration.interface.ts with IConfiguration. Use strict typing throughout, no 'any' types allowed. Follow naming conventions: interfaces prefixed with 'I', type literals in PascalCase.", "testStrategy": "Compile with TypeScript to ensure no type errors, create mock implementations to verify interfaces are complete, use type checking in IDE to confirm all required properties are defined", "priority": "high", - "dependencies": [ - 115 - ], + "dependencies": [115], "status": "done", "subtasks": [ { @@ -7156,9 +6289,7 @@ "id": 2, "title": "Define Type Literals and Enums", "description": "Create all type literal definitions for TaskStatus, TaskPriority, and TaskComplexity in the types file", - "dependencies": [ - "116.1" - ], + "dependencies": ["116.1"], "details": "In types/index.ts, define type literals: TaskStatus = 'pending' | 'in-progress' | 'done' | 'deferred' | 'cancelled' | 'blocked'; TaskPriority = 'low' | 'medium' | 'high' | 'critical'; TaskComplexity = 'simple' | 'moderate' | 'complex' | 'very-complex'. Consider using const assertions for better type inference. Export all type literals.\n<info added on 2025-08-06T11:04:04.675Z>\nType literals were already implemented in subtask 116.1 as part of the comprehensive type system. The types/index.ts file includes all required type literals: TaskStatus with values 'pending' | 'in-progress' | 'done' | 'deferred' | 'cancelled' | 'blocked' | 'review', TaskPriority with values 'low' | 'medium' | 'high' | 'critical', and TaskComplexity with values 'simple' | 'moderate' | 'complex' | 'very-complex'. All type literals are properly exported and include comprehensive JSDoc documentation. TypeScript compilation verified the types work correctly.\n</info added on 2025-08-06T11:04:04.675Z>", "status": "done", "testStrategy": "Use TypeScript compiler to verify type literals work correctly, test with invalid values to ensure type checking catches errors" @@ -7167,9 +6298,7 @@ "id": 3, "title": "Create Storage Interface Definition", "description": "Create storage.interface.ts with IStorage interface defining all storage operation methods", - "dependencies": [ - "116.1" - ], + "dependencies": ["116.1"], "details": "Create interfaces/storage.interface.ts file. Define IStorage interface with methods: loadTasks(tag?: string): Promise<Task[]>; saveTasks(tasks: Task[], tag?: string): Promise<void>; appendTasks(tasks: Task[], tag?: string): Promise<void>; updateTask(taskId: string, updates: Partial<Task>, tag?: string): Promise<void>; deleteTask(taskId: string, tag?: string): Promise<void>; exists(tag?: string): Promise<boolean>. Import Task type from types/index.ts.\n<info added on 2025-08-06T11:05:00.573Z>\nImplementation completed successfully. Extended IStorage interface beyond original specification to include metadata operations (loadMetadata, saveMetadata), tag management (getAllTags, deleteTag, renameTag, copyTag), and lifecycle methods (initialize, close, getStats). Added StorageStats interface for monitoring storage metrics and StorageConfig interface for configuration options. Implemented BaseStorage abstract class that provides common functionality including task validation using validateTask method, tag sanitization with sanitizeTag to ensure valid filenames, and backup path generation through getBackupPath for data safety. The abstract class serves as a foundation for concrete storage implementations, reducing code duplication and ensuring consistent behavior across different storage backends. All methods properly typed with async/await patterns and comprehensive error handling considerations.\n</info added on 2025-08-06T11:05:00.573Z>", "status": "done", "testStrategy": "Create mock implementation of IStorage to verify all methods are properly typed, ensure Promise return types are correct" @@ -7178,9 +6307,7 @@ "id": 4, "title": "Create AI Provider Interface Definition", "description": "Create ai-provider.interface.ts with IAIProvider interface and AIOptions type", - "dependencies": [ - "116.1" - ], + "dependencies": ["116.1"], "details": "Create interfaces/ai-provider.interface.ts file. Define AIOptions interface with properties: temperature (number), maxTokens (number), stream (boolean), topP (number), frequencyPenalty (number). Define IAIProvider interface with methods: generateCompletion(prompt: string, options?: AIOptions): Promise<string>; calculateTokens(text: string): number; getName(): string; getModel(): string; getDefaultModel(): string; isAvailable(): Promise<boolean>.\n<info added on 2025-08-06T11:06:15.795Z>\nFile successfully updated with expanded interface implementation details including comprehensive method signatures and supporting interfaces: AIOptions with full parameter set (temperature, maxTokens, stream, model, topP, topK, frequencyPenalty, presencePenalty, stopSequences, systemPrompt), AIResponse structure with content and usage metadata, AIModel interface for model information, ProviderInfo for capability tracking, ProviderUsageStats for usage monitoring, AIProviderConfig for initialization, and additional interfaces for streaming support. Documented BaseAIProvider abstract class implementation with validation, usage tracking, and common utility methods. All interfaces properly typed with strict TypeScript patterns and async/await support. No compilation errors.\n</info added on 2025-08-06T11:06:15.795Z>", "status": "done", "testStrategy": "Create stub implementation to verify interface completeness, test optional parameters work correctly" @@ -7189,10 +6316,7 @@ "id": 5, "title": "Create Configuration Interface Definition", "description": "Create configuration.interface.ts with IConfiguration interface for all config options", - "dependencies": [ - "116.1", - "116.2" - ], + "dependencies": ["116.1", "116.2"], "details": "Create interfaces/configuration.interface.ts file. Define IConfiguration interface with properties: projectPath (string), aiProvider (string), apiKeys (Record<string, string>), models (object with main, research, fallback as strings), enableTags (boolean), defaultTag (string), maxConcurrentTasks (number), retryAttempts (number), retryDelay (number). Import necessary types from types/index.ts. Ensure all properties have appropriate types with no 'any' usage.\n<info added on 2025-08-06T11:07:43.367Z>\nImplementation completed successfully. Created comprehensive configuration system with:\n\n- Core IConfiguration interface with all required properties: projectPath, aiProvider, apiKeys, models configuration, providers settings, tasks management, tags configuration, storage options, retry behavior, logging preferences, and security settings\n- Supporting interfaces for each configuration section: ModelConfig for AI model selection, ProviderConfig for API provider settings, TaskSettings for task management options, TagSettings for tag-based organization, StorageSettings for persistence configuration, RetrySettings for error handling, LoggingSettings for debugging options, SecuritySettings for API key management\n- Configuration management system with IConfigurationFactory for creating configs from various sources (file, environment, defaults) and IConfigurationManager for runtime config operations including loading, saving, validation, watching for changes, and merging configurations\n- Validation system with ConfigValidationResult interface for detailed error reporting, ConfigSchema for JSON schema validation, and EnvironmentConfig for environment variable mapping\n- DEFAULT_CONFIG_VALUES constant providing sensible defaults for all configuration options\n- All interfaces properly typed with strict TypeScript typing, no 'any' usage, proper imports from types/index\n- Successfully exported all interfaces through main index.ts for package consumers\n- TypeScript compilation confirmed passing without any type errors\n</info added on 2025-08-06T11:07:43.367Z>", "status": "done", "testStrategy": "Create sample configuration objects to verify interface covers all needed options, test with partial configs to ensure optional properties work" @@ -7206,9 +6330,7 @@ "details": "Implement FileStorage class in storage/file-storage.ts following Repository pattern. Constructor accepts projectPath, private basePath property set to {projectPath}/.taskmaster. Implement all IStorage methods: loadTasks, saveTasks, appendTasks, updateTask, deleteTask, exists. Handle file operations with proper error handling (ENOENT returns empty arrays). Use JSON format with tasks array and metadata object containing version and lastModified. Create getTasksPath method to handle tag-based file paths.", "testStrategy": "Unit test all FileStorage methods with mock file system, test error scenarios (missing files, invalid JSON), verify tag-based path generation, test concurrent operations, ensure proper directory creation", "priority": "high", - "dependencies": [ - 116 - ], + "dependencies": [116], "status": "done", "subtasks": [ { @@ -7224,9 +6346,7 @@ "id": 2, "title": "Implement file path management and helper methods", "description": "Create internal helper methods for managing file paths and ensuring directory structure exists", - "dependencies": [ - "117.1" - ], + "dependencies": ["117.1"], "details": "Implement private getTasksPath(tag?: string) method that returns path to tasks file based on optional tag parameter. If tag provided, return `${basePath}/tasks/${tag}.json`, otherwise `${basePath}/tasks/tasks.json`. Create private ensureDirectoryExists() method that creates .taskmaster and tasks directories if they don't exist using fs.mkdir with recursive option. Add private method for safe JSON parsing with error handling.", "status": "done", "testStrategy": "Test getTasksPath with and without tags, verify directory creation works recursively, test JSON parsing with valid and invalid data" @@ -7235,9 +6355,7 @@ "id": 3, "title": "Implement read operations: loadTasks and exists", "description": "Implement methods for reading tasks from the file system with proper error handling", - "dependencies": [ - "117.2" - ], + "dependencies": ["117.2"], "details": "Implement loadTasks(tag?: string) method: use getTasksPath to get file path, read file using fs.readFile, parse JSON content, return tasks array from parsed data. Handle ENOENT error by returning empty array. Handle JSON parse errors appropriately. Implement exists(tag?: string) method: use fs.access to check if file exists at getTasksPath location, return boolean result.", "status": "done", "testStrategy": "Test loadTasks with existing files, missing files (ENOENT), corrupted JSON files. Test exists method with present and absent files" @@ -7246,9 +6364,7 @@ "id": 4, "title": "Implement write operations: saveTasks and appendTasks", "description": "Implement methods for persisting tasks to the file system with metadata", - "dependencies": [ - "117.3" - ], + "dependencies": ["117.3"], "details": "Implement saveTasks(tasks: Task[], tag?: string) method: ensure directory exists, create data object with tasks array and metadata object containing version (e.g., '1.0.0') and lastModified (ISO timestamp). Write to file using fs.writeFile with JSON.stringify and proper formatting. Implement appendTasks(tasks: Task[], tag?: string) method: load existing tasks, merge with new tasks (avoiding duplicates by ID), call saveTasks with merged array.", "status": "done", "testStrategy": "Test saveTasks creates files with correct structure, verify metadata is included, test appendTasks merges correctly without duplicates" @@ -7257,9 +6373,7 @@ "id": 5, "title": "Implement update and delete operations", "description": "Implement methods for modifying and removing individual tasks with atomic operations", - "dependencies": [ - "117.4" - ], + "dependencies": ["117.4"], "details": "Implement updateTask(taskId: string, updates: Partial<Task>, tag?: string) method: load tasks, find task by ID, merge updates using object spread, save updated tasks array. Return boolean indicating success. Implement deleteTask(taskId: string, tag?: string) method: load tasks, filter out task with matching ID, save filtered array. Return boolean indicating if task was found and deleted. Ensure both operations are atomic using temporary files if needed.", "status": "done", "testStrategy": "Test updateTask with existing and non-existing tasks, verify partial updates work correctly. Test deleteTask removes correct task, handles missing tasks gracefully" @@ -7273,9 +6387,7 @@ "details": "Convert existing base-provider.js to TypeScript abstract class BaseProvider implementing IAIProvider. Add protected properties for apiKey and model. Create abstract methods: generateCompletion, calculateTokens, getName, getModel, getDefaultModel. Apply Template Method pattern for common provider logic like error handling and retry logic. Ensure proper type safety throughout.", "testStrategy": "Create MockProvider extending BaseProvider to test abstract class functionality, verify all abstract methods are properly defined, test error handling and common logic", "priority": "high", - "dependencies": [ - 116 - ], + "dependencies": [116], "status": "done", "subtasks": [ { @@ -7291,9 +6403,7 @@ "id": 2, "title": "Implement BaseProvider abstract class with core properties", "description": "Create the abstract BaseProvider class implementing IAIProvider with protected properties for apiKey and model configuration", - "dependencies": [ - "118.1" - ], + "dependencies": ["118.1"], "details": "In base-provider.ts, define abstract class BaseProvider implements IAIProvider with protected properties: apiKey: string, model: string, maxRetries: number = 3, retryDelay: number = 1000. Add constructor that accepts BaseProviderConfig interface with apiKey and optional model. Implement getModel() method to return current model.\n<info added on 2025-08-06T12:28:45.485Z>\nI've reviewed the existing BaseAIProvider interface in the interfaces file. The task requires creating a separate BaseProvider abstract class in base-provider.ts that implements the IAIProvider interface, with specific protected properties and configuration. This appears to be a deliberate architectural decision to have a more concrete base class with built-in retry logic and configuration management that all provider implementations will extend.\n</info added on 2025-08-06T12:28:45.485Z>\n<info added on 2025-08-06T13:14:24.539Z>\nSuccessfully implemented BaseProvider abstract class:\n\nIMPLEMENTED FILES:\n✅ packages/tm-core/src/providers/base-provider.ts - Created new BaseProvider abstract class\n✅ packages/tm-core/src/providers/index.ts - Updated to export BaseProvider\n\nIMPLEMENTATION DETAILS:\n- Created BaseProviderConfig interface with required apiKey and optional model\n- BaseProvider abstract class implements IAIProvider interface\n- Protected properties implemented as specified:\n - apiKey: string \n - model: string\n - maxRetries: number = 3\n - retryDelay: number = 1000\n- Constructor accepts BaseProviderConfig and sets apiKey and model (using getDefaultModel() if not provided)\n- Implemented getModel() method that returns current model\n- All IAIProvider methods declared as abstract (to be implemented by concrete providers)\n- Uses .js extension for ESM import compatibility\n- TypeScript compilation verified successful\n\nThe BaseProvider provides the foundation for concrete provider implementations with shared retry logic properties and standardized configuration.\n</info added on 2025-08-06T13:14:24.539Z>\n<info added on 2025-08-20T17:16:14.037Z>\nREFACTORING REQUIRED: The BaseProvider implementation needs to be relocated from packages/tm-core/src/providers/base-provider.ts to packages/tm-core/src/providers/ai/base-provider.ts following the new directory structure. The class must implement the Template Method pattern with the following structure:\n\n1. Keep constructor concise (under 10 lines) - only initialize apiKey and model properties\n2. Remove maxRetries and retryDelay from constructor - these should be class-level constants or configurable separately\n3. Implement all abstract methods from IAIProvider: generateCompletion, calculateTokens, getName, getModel, getDefaultModel\n4. Add protected template methods for extensibility:\n - validateInput(input: string): void - for input validation with early returns\n - prepareRequest(input: string, options?: any): any - for request preparation\n - handleResponse(response: any): string - for response processing\n - handleError(error: any): never - for consistent error handling\n5. Apply clean code principles: extract complex logic into small focused methods, use early returns to reduce nesting, ensure each method has single responsibility\n\nThe refactored BaseProvider will serve as a robust foundation using Template Method pattern, allowing concrete providers to override specific behaviors while maintaining consistent structure and error handling across all AI provider implementations.\n</info added on 2025-08-20T17:16:14.037Z>\n<info added on 2025-08-21T15:57:30.467Z>\nREFACTORING UPDATE: The BaseProvider implementation in packages/tm-core/src/providers/base-provider.ts is now affected by the core/ folder removal and needs its import paths updated. Since base-provider.ts imports from '../interfaces/provider.interface.js', this import remains valid as both providers/ and interfaces/ are at the same level. No changes needed to BaseProvider imports due to the flattening. The file structure reorganization maintains the relative path relationship between providers/ and interfaces/ directories.\n</info added on 2025-08-21T15:57:30.467Z>", "status": "done", "testStrategy": "Create a test file that attempts to instantiate BaseProvider directly (should fail) and verify that protected properties are accessible in child classes" @@ -7302,9 +6412,7 @@ "id": 3, "title": "Define abstract methods and implement Template Method pattern", "description": "Add all abstract methods to BaseProvider and implement the Template Method pattern for common provider operations", - "dependencies": [ - "118.2" - ], + "dependencies": ["118.2"], "details": "Add abstract methods: protected abstract generateCompletionInternal(prompt: string, options?: CompletionOptions): Promise<CompletionResult>, abstract calculateTokens(text: string): number, abstract getName(): string, abstract getDefaultModel(): string. Implement public generateCompletion() as template method that calls generateCompletionInternal() with error handling and retry logic.\n<info added on 2025-08-20T17:16:38.315Z>\nApply Template Method pattern following clean code principles:\n\nDefine abstract methods:\n- protected abstract generateCompletionInternal(prompt: string, options?: CompletionOptions): Promise<CompletionResult>\n- protected abstract calculateTokens(text: string): number\n- protected abstract getName(): string\n- protected abstract getDefaultModel(): string\n- protected abstract getMaxRetries(): number\n- protected abstract getRetryDelay(): number\n\nImplement template method generateCompletion():\n- Call validateInput() with early returns for invalid prompt/options\n- Call prepareRequest() to format the request\n- Execute generateCompletionInternal() with retry logic\n- Call handleResponse() to process the result\n- Call handleError() in catch blocks\n\nAdd protected helper methods:\n- validateInput(prompt: string, options?: CompletionOptions): ValidationResult - Check prompt length, validate options, return early on errors\n- prepareRequest(prompt: string, options?: CompletionOptions): PreparedRequest - Format prompt, merge with defaults, add metadata\n- handleResponse(result: CompletionResult): ProcessedResult - Validate response format, extract completion text, add usage metrics\n- handleError(error: unknown, attempt: number): void - Log error details, determine if retryable, throw TaskMasterError\n\nExtract retry logic helpers:\n- shouldRetry(error: unknown, attempt: number): boolean - Check error type and attempt count\n- calculateBackoffDelay(attempt: number): number - Use exponential backoff with jitter\n- isRateLimitError(error: unknown): boolean - Detect rate limit responses\n- isTimeoutError(error: unknown): boolean - Detect timeout errors\n\nUse named constants:\n- DEFAULT_MAX_RETRIES = 3\n- BASE_RETRY_DELAY_MS = 1000\n- MAX_RETRY_DELAY_MS = 32000\n- BACKOFF_MULTIPLIER = 2\n- JITTER_FACTOR = 0.1\n\nEnsure each method stays under 30 lines by extracting complex logic into focused helper methods.\n</info added on 2025-08-20T17:16:38.315Z>", "status": "done", "testStrategy": "Create MockProvider extending BaseProvider to verify all abstract methods must be implemented and template method properly delegates to internal methods" @@ -7313,9 +6421,7 @@ "id": 4, "title": "Implement error handling and retry logic with exponential backoff", "description": "Add comprehensive error handling and retry mechanism with exponential backoff for API calls in the template method", - "dependencies": [ - "118.3" - ], + "dependencies": ["118.3"], "details": "In generateCompletion() template method, wrap generateCompletionInternal() in try-catch with retry logic. Implement exponential backoff: delay * Math.pow(2, attempt). Add error types: ProviderError, RateLimitError, AuthenticationError extending Error. Log errors in development mode only. Handle specific error cases like rate limits (429), authentication errors (401), and network timeouts.", "status": "done", "testStrategy": "Test retry logic with MockProvider that fails N times then succeeds, verify exponential backoff timing, test different error scenarios and their handling" @@ -7324,9 +6430,7 @@ "id": 5, "title": "Add validation, logging, and completion options handling", "description": "Implement input validation, debug logging for development, and proper handling of completion options like temperature and max tokens", - "dependencies": [ - "118.4" - ], + "dependencies": ["118.4"], "details": "Add validatePrompt() method to check for empty/invalid prompts. Add validateOptions() to ensure temperature is between 0-2, maxTokens is positive. Implement debug logging using console.log only when NODE_ENV !== 'production'. Create CompletionOptions interface with optional temperature, maxTokens, topP, frequencyPenalty, presencePenalty. Ensure all validation errors throw descriptive ProviderError instances.", "status": "done", "testStrategy": "Test validation with invalid inputs (empty prompts, negative maxTokens, temperature > 2), verify logging only occurs in development, test option merging with defaults" @@ -7340,9 +6444,7 @@ "details": "Implement ProviderFactory class in ai/provider-factory.ts with static create method. Use switch statement for provider selection ('anthropic', 'openai', 'google'). Implement dynamic imports for each provider to enable tree-shaking. Return Promise<IAIProvider> from create method. Handle unknown providers with meaningful error messages. Ensure proper typing for configuration object.", "testStrategy": "Test factory with each provider type, verify dynamic imports work correctly, test error handling for unknown providers, mock dynamic imports for unit testing", "priority": "medium", - "dependencies": [ - 118 - ], + "dependencies": [118], "status": "pending", "subtasks": [ { @@ -7358,9 +6460,7 @@ "id": 2, "title": "Implement provider selection logic with switch statement", "description": "Add the core switch statement logic to handle different provider types", - "dependencies": [ - "119.1" - ], + "dependencies": ["119.1"], "details": "Inside the static create method, implement switch statement on provider type parameter. Add cases for 'anthropic', 'openai', and 'google'. Add default case that throws a descriptive error for unknown providers (e.g., throw new Error(`Unknown provider: ${providerType}`)). Structure each case to prepare for dynamic imports.", "status": "pending", "testStrategy": "Test switch statement with valid and invalid provider types, verify error messages" @@ -7369,9 +6469,7 @@ "id": 3, "title": "Add dynamic imports for each provider", "description": "Implement dynamic import() statements for lazy loading provider modules", - "dependencies": [ - "119.2" - ], + "dependencies": ["119.2"], "details": "In each switch case, use dynamic import() to load the provider module: for 'anthropic' case use await import('./providers/anthropic-provider'), similar for OpenAI and Google providers. Extract the default export or specific class from each dynamic import. This enables tree-shaking by only loading the selected provider.", "status": "pending", "testStrategy": "Mock dynamic imports in tests, verify only requested provider is loaded" @@ -7380,9 +6478,7 @@ "id": 4, "title": "Instantiate providers with configuration", "description": "Create provider instances with proper configuration passing", - "dependencies": [ - "119.3" - ], + "dependencies": ["119.3"], "details": "After each dynamic import, instantiate the provider class with the configuration object passed to create method. Ensure configuration object is properly typed (use IConfiguration or relevant subset). Return the instantiated provider instance. Handle any instantiation errors and wrap them with context about which provider failed.", "status": "pending", "testStrategy": "Test provider instantiation with various configuration objects, verify configuration is passed correctly" @@ -7391,9 +6487,7 @@ "id": 5, "title": "Add error handling and validation", "description": "Implement comprehensive error handling for all failure scenarios", - "dependencies": [ - "119.4" - ], + "dependencies": ["119.4"], "details": "Wrap dynamic imports in try-catch blocks to handle module loading failures. Add validation for configuration object before passing to providers. Create custom error messages that include the provider type and specific failure reason. Consider adding a ProviderFactoryError custom error class. Ensure all errors bubble up properly while maintaining async/await chain.", "status": "pending", "testStrategy": "Test various error scenarios: missing provider modules, invalid configurations, network failures during dynamic import" @@ -7407,9 +6501,7 @@ "details": "Create AnthropicProvider class in ai/providers/anthropic-provider.ts extending BaseProvider. Import and use @anthropic-ai/sdk. Initialize private client property in constructor. Implement all abstract methods: generateCompletion using Claude API, calculateTokens using appropriate tokenizer, getName returning 'anthropic', getModel returning current model, getDefaultModel returning 'claude-3-sonnet-20240229'. Wrap API errors with context.", "testStrategy": "Mock Anthropic SDK for unit tests, test API error handling, verify token calculation accuracy, test with different model configurations", "priority": "high", - "dependencies": [ - 118 - ], + "dependencies": [118], "status": "pending", "subtasks": [ { @@ -7425,9 +6517,7 @@ "id": 2, "title": "Implement constructor and client initialization", "description": "Create the constructor that accepts configuration and initializes the Anthropic SDK client", - "dependencies": [ - "120.1" - ], + "dependencies": ["120.1"], "details": "Implement constructor accepting IConfiguration parameter. Call super(config) to initialize BaseProvider. Initialize the private client property by creating new Anthropic instance with apiKey from config.apiKeys.anthropic. Add validation to ensure API key exists, throwing meaningful error if missing. Store the model configuration from config.model or use default.", "status": "pending", "testStrategy": "Test constructor with valid and invalid configurations, verify client initialization, test API key validation" @@ -7436,9 +6526,7 @@ "id": 3, "title": "Implement generateCompletion method with Claude API", "description": "Implement the main generateCompletion method that calls Anthropic's Claude API and handles responses", - "dependencies": [ - "120.2" - ], + "dependencies": ["120.2"], "details": "Implement async generateCompletion method accepting ChatMessage array. Map ChatMessage format to Anthropic's expected format (role and content). Use client.messages.create() with appropriate parameters including model, max_tokens, and messages. Transform Anthropic response format to ChatCompletion interface. Handle streaming vs non-streaming responses. Implement proper error handling wrapping API errors with context.", "status": "pending", "testStrategy": "Mock Anthropic SDK client.messages.create, test with various message formats, verify response transformation, test error scenarios" @@ -7447,9 +6535,7 @@ "id": 4, "title": "Implement token calculation and utility methods", "description": "Implement calculateTokens method and other required abstract methods from BaseProvider", - "dependencies": [ - "120.3" - ], + "dependencies": ["120.3"], "details": "Implement calculateTokens method using appropriate tokenizer (tiktoken or claude-tokenizer if available). Implement getName method returning 'anthropic' string constant. Implement getModel method returning current model from configuration. Implement getDefaultModel method returning 'claude-3-sonnet-20240229'. Add any additional helper methods for token counting or message formatting.", "status": "pending", "testStrategy": "Test token calculation accuracy with various input strings, verify utility methods return correct values" @@ -7458,9 +6544,7 @@ "id": 5, "title": "Add comprehensive error handling and type exports", "description": "Implement robust error handling throughout the class and ensure proper TypeScript exports", - "dependencies": [ - "120.4" - ], + "dependencies": ["120.4"], "details": "Wrap all Anthropic API calls in try-catch blocks. Create custom error messages that include context about the operation being performed. Handle rate limiting errors specifically. Ensure all methods have proper TypeScript return types. Export the AnthropicProvider class as default export. Add JSDoc comments for all public methods. Ensure proper error propagation maintaining stack traces.", "status": "pending", "testStrategy": "Test various API error scenarios, verify error messages include context, test rate limit handling, ensure TypeScript types are correctly exported" @@ -7474,10 +6558,7 @@ "details": "Create PromptBuilder class with buildParsePrompt and buildExpandPrompt methods using template literals. Include specific JSON format instructions. Create TaskParser class accepting IAIProvider and IConfiguration via constructor (Dependency Injection). Implement parsePRD method to read PRD file, use PromptBuilder to create prompt, call AI provider, extract tasks from response, and enrich with metadata. Handle parsing errors gracefully.", "testStrategy": "Unit test prompt building with various inputs, mock AI provider responses, test JSON extraction logic, verify error handling for malformed responses, integration test with real PRD files", "priority": "high", - "dependencies": [ - 119, - 120 - ], + "dependencies": [119, 120], "status": "pending", "subtasks": [ { @@ -7493,9 +6574,7 @@ "id": 2, "title": "Implement TaskParser Class with DI", "description": "Create TaskParser class accepting IAIProvider and IConfiguration through constructor injection", - "dependencies": [ - "121.1" - ], + "dependencies": ["121.1"], "details": "Create src/services/task-parser.ts. Define TaskParser class with constructor(private aiProvider: IAIProvider, private config: IConfiguration). Add private promptBuilder property initialized in constructor. Implement basic class structure with placeholder methods. Ensure proper TypeScript typing for all parameters and properties. Follow dependency injection pattern for testability.\n<info added on 2025-08-20T17:17:49.624Z>\nUpdate file location to src/services/tasks/task-parser.ts instead of src/services/task-parser.ts. Refactor parsePRD() method to stay under 40 lines by extracting logic into helper methods: readPRD(), validatePRD(), extractTasksFromResponse(), and enrichTasksWithMetadata(). Each helper method should be under 20 lines. Implement early returns in validation methods for cleaner code flow. Remove any file I/O operations from the parser class - delegate all storage operations to injected dependencies. Ensure clean separation of concerns with parser focused only on task parsing logic.\n</info added on 2025-08-20T17:17:49.624Z>", "status": "pending", "testStrategy": "Test constructor properly stores injected dependencies. Verify class instantiation with mock providers. Test TypeScript compilation with proper interface implementations." @@ -7504,9 +6583,7 @@ "id": 3, "title": "Implement parsePRD Method Core Logic", "description": "Create the main parsePRD method that orchestrates the PRD parsing workflow", - "dependencies": [ - "121.2" - ], + "dependencies": ["121.2"], "details": "Implement parsePRD(filePath: string): Promise<ParsedTask[]> method in TaskParser. Read PRD file using fs.promises.readFile. Use promptBuilder.buildParsePrompt() to create AI prompt. Call aiProvider.generateResponse() with constructed prompt. Extract JSON array from AI response using regex or JSON.parse. Handle potential parsing errors with try-catch blocks. Return empty array on errors after logging.", "status": "pending", "testStrategy": "Test with mock AI provider returning valid JSON. Test file reading with various file paths. Mock file system for controlled testing. Verify proper error logging without throwing." @@ -7515,9 +6592,7 @@ "id": 4, "title": "Add Task Enrichment and Metadata", "description": "Enhance parsed tasks with additional metadata and validation", - "dependencies": [ - "121.3" - ], + "dependencies": ["121.3"], "details": "After extracting tasks from AI response, enrich each task with metadata: add createdAt timestamp, set initial status to 'pending', validate required fields (id, title, description). Add priority field with default 'medium' if not provided. Ensure all tasks have valid structure before returning. Create private enrichTask(task: any): ParsedTask method for this logic. Handle missing or malformed task data gracefully.", "status": "pending", "testStrategy": "Test enrichment adds all required metadata. Verify validation catches malformed tasks. Test default values are applied correctly. Ensure timestamps are properly formatted." @@ -7526,9 +6601,7 @@ "id": 5, "title": "Implement Comprehensive Error Handling", "description": "Add robust error handling throughout the TaskParser implementation", - "dependencies": [ - "121.4" - ], + "dependencies": ["121.4"], "details": "Wrap file reading in try-catch to handle FILE_NOT_FOUND errors. Catch AI provider errors and wrap in appropriate TaskMasterError. Handle JSON parsing errors when extracting from AI response. Add specific error handling for network timeouts, rate limits, and malformed responses. Log errors with context in development mode only. Return meaningful error messages without exposing internals. Ensure all errors are properly typed as TaskMasterError instances.", "status": "pending", "testStrategy": "Test each error scenario separately: missing files, AI provider failures, malformed JSON, network errors. Verify proper error codes are used. Test that errors don't expose sensitive information." @@ -7542,9 +6615,7 @@ "details": "Implement ConfigManager in config/config-manager.ts accepting Partial<IConfiguration> in constructor. Use Zod to create validation schema matching IConfiguration interface. Implement get method with TypeScript generics for type-safe access, getAll returning full config, validate method for validation. Set defaults: projectPath = process.cwd(), aiProvider = 'anthropic', enableTags = true. Handle validation errors with clear messages.", "testStrategy": "Test with various configuration combinations, verify Zod validation catches invalid configs, test default values, ensure type safety of get method", "priority": "medium", - "dependencies": [ - 116 - ], + "dependencies": [116], "status": "in-progress", "subtasks": [ { @@ -7560,9 +6631,7 @@ "id": 2, "title": "Implement ConfigManager class constructor and storage", "description": "Create ConfigManager class with constructor that accepts Partial<IConfiguration> and initializes configuration with defaults", - "dependencies": [ - "122.1" - ], + "dependencies": ["122.1"], "details": "Define ConfigManager class with private config property. In constructor, merge provided partial config with defaults (projectPath = process.cwd(), aiProvider = 'anthropic', enableTags = true). Store the merged configuration internally. Ensure the class is properly typed with IConfiguration interface.", "status": "pending", "testStrategy": "Test constructor with various partial configs, verify defaults are applied correctly, test with empty config" @@ -7571,10 +6640,7 @@ "id": 3, "title": "Implement validate method with error handling", "description": "Create validate method that uses Zod schema to validate configuration and provides clear error messages", - "dependencies": [ - "122.1", - "122.2" - ], + "dependencies": ["122.1", "122.2"], "details": "Implement validate(): void method that runs configSchema.parse(this.config) within try-catch block. On ZodError, transform the error into user-friendly messages that clearly indicate which fields are invalid and why. Consider creating a custom error class for configuration validation errors. The method should throw if validation fails.", "status": "pending", "testStrategy": "Test with invalid configs to ensure proper error messages, verify all validation rules work correctly" @@ -7583,9 +6649,7 @@ "id": 4, "title": "Implement type-safe get method with generics", "description": "Create generic get method for retrieving individual configuration values with TypeScript type inference", - "dependencies": [ - "122.2" - ], + "dependencies": ["122.2"], "details": "Implement get<K extends keyof IConfiguration>(key: K): IConfiguration[K] method that returns the value for a specific configuration key. Use TypeScript generics and keyof operator to ensure type safety. The method should provide proper type inference so consumers get the correct type based on the key they request.", "status": "pending", "testStrategy": "Test type inference with different keys, verify TypeScript catches invalid keys at compile time" @@ -7594,11 +6658,7 @@ "id": 5, "title": "Implement getAll method and finalize class", "description": "Create getAll method to return full configuration and ensure proper exports", - "dependencies": [ - "122.2", - "122.3", - "122.4" - ], + "dependencies": ["122.2", "122.3", "122.4"], "details": "Implement getAll(): IConfiguration method that returns a deep copy of the entire configuration object to prevent external mutations. Add JSDoc comments to all public methods. Export the ConfigManager class and ensure it's properly integrated with the module structure. Consider adding a static factory method if needed.", "status": "pending", "testStrategy": "Test getAll returns complete config, verify returned object is immutable, test integration with other modules" @@ -7612,9 +6672,7 @@ "details": "Create id-generator.ts with generateTaskId and generateSubtaskId functions using specified formats with timestamp and random components. Create TaskMasterError class extending Error in errors/task-master-error.ts with error codes (FILE_NOT_FOUND, PARSE_ERROR, etc.). Ensure errors don't expose internal details. Add development-only logging.", "testStrategy": "Test ID generation for uniqueness and format compliance, verify error classes properly extend Error, test error message formatting, ensure no sensitive data in errors", "priority": "low", - "dependencies": [ - 116 - ], + "dependencies": [116], "status": "in-progress", "subtasks": [ { @@ -7639,9 +6697,7 @@ "id": 3, "title": "Implement error sanitization and security features", "description": "Add security features to prevent exposure of sensitive internal details in error messages", - "dependencies": [ - "123.2" - ], + "dependencies": ["123.2"], "details": "In TaskMasterError class, add private sanitizeDetails() method that removes sensitive data like API keys, file paths beyond project root, and internal state. Implement toJSON() method that returns sanitized error object for external consumption. Add static isSafeForProduction() method to validate error messages don't contain patterns like absolute paths, environment variables, or API credentials. Store original details in private property for debugging.", "status": "pending", "testStrategy": "Test sanitization removes absolute paths, API keys, and sensitive patterns, verify toJSON returns safe object, test original details are preserved internally" @@ -7650,10 +6706,7 @@ "id": 4, "title": "Add development-only logging functionality", "description": "Implement conditional logging that only operates in development environment", - "dependencies": [ - "123.2", - "123.3" - ], + "dependencies": ["123.2", "123.3"], "details": "In task-master-error.ts, add static enableDevLogging property defaulting to process.env.NODE_ENV !== 'production'. Add logError() method that console.error's full error details only when enableDevLogging is true. Include timestamp, error code, sanitized message, and full stack trace in dev logs. In production, log only error code and safe message. Create static setDevLogging(enabled: boolean) to control logging.", "status": "pending", "testStrategy": "Test logging output in dev vs production modes, verify sensitive data isn't logged in production, test log format includes all required fields" @@ -7662,11 +6715,7 @@ "id": 5, "title": "Create specialized error subclasses", "description": "Implement specific error classes for different error scenarios inheriting from TaskMasterError", - "dependencies": [ - "123.2", - "123.3", - "123.4" - ], + "dependencies": ["123.2", "123.3", "123.4"], "details": "Create FileNotFoundError extending TaskMasterError with code FILE_NOT_FOUND, accepting filePath parameter. Create ParseError with code PARSE_ERROR for parsing failures, accepting source and line number. Create ValidationError with code VALIDATION_ERROR for data validation, accepting field and value. Create APIError with code API_ERROR for external API failures, accepting statusCode and provider. Each should format appropriate user-friendly messages while storing technical details internally.", "status": "pending", "testStrategy": "Test each error class constructor and message formatting, verify inheritance chain, test that each error type has correct code, ensure specialized errors work with logging system" @@ -7680,11 +6729,7 @@ "details": "Create TaskMasterCore class in index.ts with private properties for config, storage, aiProvider, and parser. Implement initialize method for lazy loading of AI provider. Implement parsePRD method that coordinates parser, storage, and configuration. Implement getTasks for retrieving stored tasks. Apply Facade pattern to hide complexity. Export createTaskMaster factory function, all types and interfaces. Use proper import paths with .js extensions for ESM.", "testStrategy": "Integration test full parse flow, test lazy initialization, verify facade properly delegates to subsystems, test with different configurations, ensure exports are correct", "priority": "high", - "dependencies": [ - 117, - 121, - 122 - ], + "dependencies": [117, 121, 122], "status": "pending", "subtasks": [ { @@ -7700,9 +6745,7 @@ "id": 2, "title": "Implement initialize method for lazy loading", "description": "Create the initialize method that handles lazy loading of AI provider and parser instances based on configuration", - "dependencies": [ - "124.1" - ], + "dependencies": ["124.1"], "details": "Implement async initialize() method in TaskMasterCore. Check if _aiProvider is null, if so create appropriate provider based on config.aiProvider value using a factory pattern or switch statement. Similarly initialize _parser if null. Store instances in private properties for reuse. Handle provider initialization errors gracefully. Ensure method is idempotent - calling multiple times should not recreate instances. Use dynamic imports if needed for code splitting.", "status": "pending", "testStrategy": "Test lazy initialization occurs only once, verify correct provider is instantiated based on config, test error handling for invalid providers, ensure idempotency" @@ -7711,10 +6754,7 @@ "id": 3, "title": "Implement parsePRD method with coordination logic", "description": "Create parsePRD method that coordinates the parser, AI provider, and storage to parse PRD content and store results", - "dependencies": [ - "124.1", - "124.2" - ], + "dependencies": ["124.1", "124.2"], "details": "Implement async parsePRD(content: string) method. First call initialize() to ensure components are loaded. Use _parser.parse() to parse the PRD content, passing the AI provider for task generation. Take the parsed tasks and use _storage.saveTasks() to persist them. Handle errors from parser or storage operations. Return the parsed tasks array. Implement proper error context and logging for debugging.", "status": "pending", "testStrategy": "Integration test with mock parser and storage, verify coordination between components, test error propagation from subsystems, ensure tasks are properly stored" @@ -7723,9 +6763,7 @@ "id": 4, "title": "Implement getTasks method and other facade methods", "description": "Create getTasks method and any other necessary facade methods to retrieve and manage tasks", - "dependencies": [ - "124.1" - ], + "dependencies": ["124.1"], "details": "Implement async getTasks() method that calls _storage.loadTasks() and returns the tasks array. Add getTask(id: string) for retrieving single task. Consider adding updateTask, deleteTask methods if needed. All methods should follow facade pattern - simple interface hiding complex operations. Add proper TypeScript return types for all methods. Handle storage not initialized scenarios.", "status": "pending", "testStrategy": "Test task retrieval with various scenarios, verify proper delegation to storage, test edge cases like empty task lists or invalid IDs" @@ -7734,12 +6772,7 @@ "id": 5, "title": "Create factory function and module exports", "description": "Implement createTaskMaster factory function and set up all module exports including types and interfaces", - "dependencies": [ - "124.1", - "124.2", - "124.3", - "124.4" - ], + "dependencies": ["124.1", "124.2", "124.3", "124.4"], "details": "Create createTaskMaster(options?: Partial<IConfiguration>) factory function that returns a new TaskMasterCore instance. Export this as the primary entry point. Re-export all types and interfaces from submodules: ITask, IConfiguration, IAIProvider, ITaskStorage, IPRDParser, etc. Use 'export type' for type-only exports. Ensure all imports use .js extensions for ESM. Create index.d.ts if needed for better TypeScript support. Add JSDoc comments for public API.", "status": "pending", "testStrategy": "Test factory function creates proper instances, verify all exports are accessible, test TypeScript type inference works correctly, ensure ESM imports resolve properly" @@ -7753,10 +6786,7 @@ "details": "Create OpenAIProvider and GoogleProvider classes extending BaseProvider, throwing 'not yet implemented' errors. Create MockProvider in tests/mocks for testing without API calls. Write unit tests for TaskParser, integration tests for parse-prd flow, ensure 80% code coverage. Follow kebab-case naming for test files. Test error scenarios comprehensively.", "testStrategy": "Run full test suite with coverage report, verify all edge cases are tested, ensure mock provider behaves like real providers, test both success and failure paths", "priority": "medium", - "dependencies": [ - 120, - 124 - ], + "dependencies": [120, 124], "status": "pending", "subtasks": [ { @@ -7790,9 +6820,7 @@ "id": 4, "title": "Write unit tests for TaskParser", "description": "Create comprehensive unit tests for TaskParser class covering all methods and edge cases", - "dependencies": [ - "125.3" - ], + "dependencies": ["125.3"], "details": "Create tests/unit/task-parser.test.ts file. Test TaskParser constructor with different providers. Test parseFromText method with valid/invalid inputs. Test error handling for malformed responses. Use MockProvider to simulate API responses. Test task ID generation and structure validation. Ensure all public methods are covered.", "status": "pending", "testStrategy": "Achieve 100% code coverage for TaskParser class, test both success and failure paths, verify error messages are appropriate" @@ -7801,10 +6829,7 @@ "id": 5, "title": "Write integration tests for parse-prd flow", "description": "Create end-to-end integration tests for the complete PRD parsing workflow", - "dependencies": [ - "125.3", - "125.4" - ], + "dependencies": ["125.3", "125.4"], "details": "Create tests/integration/parse-prd-flow.test.ts file. Test full flow from PRD input to task output. Test with MockProvider simulating successful parsing. Test error scenarios (file not found, parse errors, network failures). Test task dependency resolution. Verify output format matches expected structure. Test with different PRD formats and sizes.", "status": "pending", "testStrategy": "Run coverage report to ensure 80% overall coverage, verify all critical paths are tested, ensure tests are deterministic and don't depend on external services" @@ -7838,9 +6863,7 @@ "details": "Create a function in the StartCommand class that builds the standardized prompt with the actual task data (title, description, details) instead of instructing Claude to run 'tm show'.\n\n```typescript\nprivate buildPrompt(task: Task): string {\n return `You are an AI coding assistant with access to this repository's codebase.\n\nHere are the task details to implement:\n\nTask ID: ${task.id}\nTitle: ${task.title}\nDescription: ${task.description}\nDetails: ${task.details}\n\nImplement the task with these requirements:\n- Make the SMALLEST number of code changes possible\n- Follow ALL existing patterns in the codebase (you have access to analyze the code)\n- Do NOT over-engineer the solution\n- Use existing files/functions/patterns wherever possible\n- When complete, print: COMPLETED: <brief summary of changes>\n\nPlease implement this task now.`;\n}\n```\n<info added on 2025-09-12T02:40:01.812Z>\nThe prompt builder function will handle task context retrieval by instructing Claude to use the task-master show command. This approach ensures Claude has access to all necessary task details before implementation begins. The command syntax \"tm show ${taskId}\" embedded in the prompt will direct Claude to first gather the complete task context, including description, requirements, and any existing implementation details, before proceeding with code changes.\n</info added on 2025-09-12T02:40:01.812Z>\n\n<info added>\nThe updated approach eliminates the need for Claude to run 'tm show' by directly providing the task details in the prompt. The function now takes a Task object instead of just the task ID, allowing it to include the complete task information directly in the prompt.\n</info added>", "testStrategy": "Verify the prompt is correctly formatted by calling the function with a sample task object and checking that the output includes all the task details (ID, title, description, details) properly inserted into the template.", "priority": "medium", - "dependencies": [ - 1 - ], + "dependencies": [1], "status": "done", "subtasks": [] }, @@ -7851,9 +6874,7 @@ "details": "Implement the functionality to execute the claude command with the built prompt. This should use Node.js child_process.exec() to run the command directly in the terminal.\n\n```typescript\nimport { exec } from 'child_process';\n\n// Inside execute method, after task validation\nprivate async executeClaude(prompt: string): Promise<void> {\n console.log('Starting claude-code to implement the task...');\n \n try {\n // Execute claude with the prompt\n const claudeCommand = `claude \"${prompt.replace(/\"/g, '\\\\\"')}\"`;\n \n // Use execSync to wait for the command to complete\n const { execSync } = require('child_process');\n execSync(claudeCommand, { stdio: 'inherit' });\n \n console.log('Claude session completed.');\n } catch (error) {\n console.error('Error executing claude-code:', error.message);\n process.exit(1);\n }\n}\n```\n\nThen call this method from the execute method after building the prompt.", "testStrategy": "Test by running the command with a valid task ID and verifying that the claude command is executed with the correct prompt. Check that the command handles errors appropriately if claude-code is not available.", "priority": "high", - "dependencies": [ - 3 - ], + "dependencies": [3], "status": "done", "subtasks": [] }, @@ -7864,10 +6885,7 @@ "details": "Update the execute method in the StartCommand class to integrate all the components and implement the complete execution flow with the updated approach:\n1. Get task details directly using TaskMasterCore\n2. Build standardized prompt with actual task data\n3. Execute claude-code\n4. Check git status for changes\n5. Auto-mark task as done if changes detected\n\n```typescript\npublic async execute(taskId: string): Promise<void> {\n // Get task details directly\n const core = await createTaskMasterCore();\n const task = await core.tasks.getById(parseInt(taskId, 10));\n \n if (!task) {\n console.error(`Task with ID ${taskId} not found`);\n process.exit(1);\n }\n \n // Build prompt with actual task data\n const prompt = this.buildPrompt(task);\n \n // Execute claude-code\n await this.executeClaude(prompt);\n \n // Check git status\n const changedFiles = await this.checkGitChanges();\n \n if (changedFiles.length > 0) {\n console.log('\\nChanges detected in the following files:');\n changedFiles.forEach(file => console.log(`- ${file}`));\n \n // Auto-mark task as done\n await this.markTaskAsDone(taskId);\n console.log(`\\nTask ${taskId} completed successfully and marked as done.`);\n } else {\n console.warn('\\nNo changes detected after claude-code execution. Task not marked as done.');\n }\n}\n```", "testStrategy": "Test the complete execution flow by running the start command with a valid task ID and verifying that all steps are executed correctly. Verify that the task details are correctly retrieved from TaskMasterCore and included in the prompt. Test with both scenarios: when changes are detected and when no changes are detected.", "priority": "high", - "dependencies": [ - 3, - 4 - ], + "dependencies": [3, 4], "status": "done", "subtasks": [] }, @@ -7878,9 +6896,7 @@ "details": "Update the CLI application to register the new start command. This involves importing the StartCommand class and adding it to the commands array in the CLI initialization.\n\nIn `apps/cli/src/index.ts` or the appropriate file where commands are registered:\n\n```typescript\nimport { StartCommand } from './commands/start.command';\n\n// Add StartCommand to the commands array\nconst commands = [\n // ... existing commands\n new StartCommand(),\n];\n\n// Register all commands\ncommands.forEach(command => command.register(program));\n```", "testStrategy": "Verify the command is correctly registered by running the CLI with --help and checking that the start command appears in the list of available commands.", "priority": "high", - "dependencies": [ - 7 - ], + "dependencies": [7], "status": "done", "subtasks": [] }, @@ -7901,5 +6917,2471 @@ "updated": "2025-09-12T04:02:07.346Z", "description": "Tasks for tm-start context" } + }, + "autonomous-tdd-git-workflow": { + "tasks": [ + { + "id": 31, + "title": "Create WorkflowOrchestrator service foundation", + "description": "Implement the core WorkflowOrchestrator class in tm-core to manage the autonomous TDD workflow state machine", + "details": "Create packages/tm-core/src/services/workflow-orchestrator.ts with phases enum (Preflight, Branch, SubtaskLoop, Finalization), event emitter for progress tracking, and basic state management. Include interfaces for WorkflowConfig, WorkflowState, and WorkflowEvent. Implement constructor, start/pause/resume methods, and phase transition logic. Use existing TaskService and ConfigManager dependencies.", + "testStrategy": "Unit tests for state transitions, event emission, phase management, error handling, and integration with existing services. Mock TaskService and ConfigManager dependencies.", + "priority": "high", + "dependencies": [], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Create phase management system with workflow phases enum", + "description": "Implement the core phase management system for the WorkflowOrchestrator including the phases enum and phase transition logic", + "dependencies": [], + "details": "Create WorkflowPhase enum with Preflight, Branch, SubtaskLoop, and Finalization phases. Implement phase transition validation logic to ensure proper workflow progression. Include getCurrentPhase(), transitionToPhase(), and canTransitionTo() methods with proper error handling for invalid transitions.", + "status": "pending", + "testStrategy": "Unit tests for phase transitions, invalid transition handling, and phase validation logic" + }, + { + "id": 2, + "title": "Implement event emitter system for workflow progress tracking", + "description": "Create the event emitter infrastructure to broadcast workflow state changes and progress updates", + "dependencies": [1], + "details": "Implement EventEmitter-based system for WorkflowOrchestrator with typed event interfaces (WorkflowEvent). Define event types for phase changes, progress updates, errors, and completion. Include emit(), on(), off(), and once() methods with proper type safety. Support event filtering and listener management.", + "status": "pending", + "testStrategy": "Unit tests for event emission, listener registration/removal, and event type validation" + }, + { + "id": 3, + "title": "Design and implement core state management interfaces", + "description": "Create the foundational interfaces and state management structures for workflow configuration and state", + "dependencies": [], + "details": "Define WorkflowConfig interface with workflow settings, timeout configurations, and behavior flags. Create WorkflowState interface tracking current phase, progress, errors, and timing information. Implement WorkflowEvent interface with event types and payload structures. Include state validation and serialization support.", + "status": "pending", + "testStrategy": "Unit tests for interface validation, state serialization/deserialization, and configuration parsing" + }, + { + "id": 4, + "title": "Integrate TaskService and ConfigManager dependencies", + "description": "Implement dependency injection and integration with existing TaskService and ConfigManager services", + "dependencies": [3], + "details": "Create constructor that accepts TaskService and ConfigManager dependencies. Implement methods to retrieve workflow configuration from ConfigManager and interact with TaskService for task operations. Include dependency validation and error handling for missing or invalid services. Add configuration loading and validation logic.", + "status": "pending", + "testStrategy": "Unit tests with mocked dependencies, integration tests verifying service interactions and configuration loading" + }, + { + "id": 5, + "title": "Implement workflow lifecycle methods and state machine", + "description": "Create the core workflow control methods including start, pause, resume, and stop functionality", + "dependencies": [1, 2, 4], + "details": "Implement start() method to initialize workflow and begin Preflight phase. Create pause()/resume() methods for workflow interruption handling. Add stop() method for graceful workflow termination. Include state machine logic to manage workflow progression, error recovery, and cleanup operations. Support workflow validation before start.", + "status": "pending", + "testStrategy": "Unit tests for lifecycle methods, state machine transitions, error handling, and workflow validation logic" + } + ] + }, + { + "id": 32, + "title": "Implement GitAdapter for repository operations", + "description": "Create git operations adapter that wraps existing git-utils.js functionality for WorkflowOrchestrator", + "details": "Create packages/tm-core/src/services/git-adapter.ts that provides TypeScript interface over scripts/modules/utils/git-utils.js. Include methods: isGitRepository, getCurrentBranch, createBranch, checkoutBranch, isWorkingTreeClean, commitChanges, pushBranch, getDefaultBranch. Implement branch naming pattern support using config.git.branchPattern with {tag}, {id}, {slug} tokens. Add confirmation gates for destructive operations.", + "testStrategy": "Unit tests with mocked git commands, integration tests with temporary git repositories. Test branch naming patterns, confirmation flows, and error handling for git failures.", + "priority": "high", + "dependencies": [31], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Create TypeScript wrapper interface for git-utils.js", + "description": "Create the base GitAdapter class structure and TypeScript interfaces that wrap the existing JavaScript git utilities", + "dependencies": [], + "details": "Create packages/tm-core/src/services/git-adapter.ts with GitAdapter class. Define TypeScript interfaces for all git operations. Import and wrap existing git-utils.js functions with proper error handling and type safety. Implement basic methods: isGitRepository, getCurrentBranch, getDefaultBranch with TypeScript return types.", + "status": "pending", + "testStrategy": "Unit tests for TypeScript interface compliance, proper wrapping of git-utils.js functions, and basic git repository detection" + }, + { + "id": 2, + "title": "Implement core git operations with error handling", + "description": "Implement the main git operation methods with proper error handling and validation", + "dependencies": [1], + "details": "Implement createBranch, checkoutBranch, isWorkingTreeClean, commitChanges, pushBranch methods in GitAdapter. Add comprehensive error handling for git command failures, invalid repository states, and network issues. Include proper validation for branch names, commit messages, and repository state before operations.", + "status": "pending", + "testStrategy": "Unit tests with mocked git commands for success and failure scenarios. Integration tests with temporary git repositories testing actual git operations" + }, + { + "id": 3, + "title": "Add branch naming pattern support with token replacement", + "description": "Implement configurable branch naming patterns using template tokens for automated branch creation", + "dependencies": [2], + "details": "Implement branch naming pattern support using config.git.branchPattern. Create token replacement system for {tag}, {id}, {slug} tokens. Add pattern validation and sanitization for git-safe branch names. Support multiple pattern templates and fallback naming strategies for edge cases.", + "status": "pending", + "testStrategy": "Unit tests for token replacement logic, pattern validation, and branch name sanitization. Test various pattern configurations and edge cases" + }, + { + "id": 4, + "title": "Integrate confirmation gates for destructive operations", + "description": "Add user confirmation prompts and safety checks for potentially destructive git operations", + "dependencies": [3], + "details": "Implement confirmation gates for destructive operations like branch deletion, force push, and uncommitted changes overwrite. Add configurable confirmation levels (always, auto, never) and interactive prompts. Include safety checks for working tree state, unpushed commits, and branch protection rules.", + "status": "pending", + "testStrategy": "Unit tests for confirmation flow logic and safety checks. Integration tests simulating user interactions and validating destructive operation prevention" + } + ] + }, + { + "id": 33, + "title": "Create TestRunnerAdapter for framework detection and execution", + "description": "Implement test runner adapter that detects project test framework and executes tests with coverage", + "details": "Create packages/tm-core/src/services/test-runner-adapter.ts that detects test commands from package.json scripts (npm test, pnpm test, etc.), executes targeted and full test runs, parses test results and coverage reports. Support Jest, Vitest, and generic npm scripts. Implement TestResult interface with pass/fail counts, coverage percentages, and detailed failure information. Include timeout handling and retry logic.", + "testStrategy": "Unit tests for framework detection logic, test execution with mocked child_process, coverage parsing. Integration tests with actual test frameworks in fixture projects.", + "priority": "high", + "dependencies": [31], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Implement framework detection logic", + "description": "Create the core framework detection system that analyzes package.json and project structure to identify Jest, Vitest, or generic npm test configurations", + "dependencies": [], + "details": "Implement methods to parse package.json scripts section, detect testing framework dependencies (jest, vitest, @jest/core, etc.), analyze configuration files (jest.config.js, vitest.config.ts), and return FrameworkType enum. Include fallback to generic npm scripts when no specific framework is detected.", + "status": "pending", + "testStrategy": "Unit tests with mock package.json files for different framework configurations, edge cases for missing or malformed configs" + }, + { + "id": 2, + "title": "Create test execution engine with process management", + "description": "Implement the core test execution system that spawns child processes and manages test command execution with proper timeout and error handling", + "dependencies": [1], + "details": "Create TestExecutor class that uses child_process.spawn to run test commands, implements configurable timeouts, handles process termination, captures stdout/stderr streams, and provides retry logic for failed executions. Support both targeted test runs (specific files) and full test suite execution.", + "status": "pending", + "testStrategy": "Unit tests with mocked child_process, integration tests with actual test commands, timeout and retry scenario testing" + }, + { + "id": 3, + "title": "Implement Jest-specific result parsing", + "description": "Create Jest result parser that extracts test outcomes, failure details, and coverage information from Jest's output formats", + "dependencies": [2], + "details": "Parse Jest JSON reporter output to extract pass/fail counts, test file results, error messages, stack traces, and execution times. Handle both verbose and summary output modes. Extract coverage data from Jest coverage reports including line, branch, function, and statement coverage percentages.", + "status": "pending", + "testStrategy": "Unit tests with sample Jest output JSON files, edge cases for malformed output, coverage parsing validation" + }, + { + "id": 4, + "title": "Implement Vitest-specific result parsing", + "description": "Create Vitest result parser that handles Vitest's unique output format and coverage reporting structure", + "dependencies": [2], + "details": "Parse Vitest reporter output to extract test results, support both default and JSON reporters, handle Vitest's specific error formatting and stack traces. Extract coverage information from Vitest's coverage providers (c8, istanbul) with proper percentage calculations and file-level coverage details.", + "status": "pending", + "testStrategy": "Unit tests with Vitest output samples, comparison with Jest parser for consistency, coverage provider compatibility testing" + }, + { + "id": 5, + "title": "Create unified TestResult interface and data structures", + "description": "Design and implement comprehensive TypeScript interfaces for test results, coverage data, and failure information that works across all supported frameworks", + "dependencies": [3, 4], + "details": "Define TestResult interface with pass/fail counts, execution time, coverage percentages (line, branch, function, statement), detailed failure information with file paths and line numbers. Create CoverageReport interface with file-level and overall coverage data. Implement result aggregation and normalization logic.", + "status": "pending", + "testStrategy": "Unit tests for interface compliance across frameworks, data transformation accuracy, edge cases for missing coverage data" + }, + { + "id": 6, + "title": "Integrate components into TestRunnerAdapter service", + "description": "Assemble all components into the main TestRunnerAdapter class with public API methods for framework detection, test execution, and result retrieval", + "dependencies": [1, 2, 5], + "details": "Create main TestRunnerAdapter class that orchestrates framework detection, test execution, and result parsing. Implement public methods: detectFramework(), runTests(), runTargetedTests(), and getLastResults(). Add configuration options for timeout, retry count, and coverage options. Include comprehensive error handling and logging.", + "status": "pending", + "testStrategy": "Integration tests with real test projects, end-to-end workflow testing, error handling validation, performance testing with large test suites" + } + ] + }, + { + "id": 34, + "title": "Implement autopilot CLI command structure", + "description": "Create the main autopilot command in CLI with dry-run, configuration, and basic orchestration", + "details": "Create apps/cli/src/commands/autopilot.command.ts using Commander.js. Implement flags: --dry-run, --no-push, --no-pr, --no-confirm, --force, --max-attempts, --resume. Add preflight checks (clean working tree, test command detection, tool availability). Integrate with WorkflowOrchestrator and existing UI components from apps/cli/src/ui/components/. Show detailed execution plan in dry-run mode.", + "testStrategy": "Unit tests for command parsing, flag handling, dry-run output formatting. Integration tests with mock WorkflowOrchestrator to verify command flow without actual git operations.", + "priority": "medium", + "dependencies": [31, 32, 33], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Create autopilot command structure with Commander.js", + "description": "Set up the basic autopilot command file and integrate it with the existing CLI structure using Commander.js patterns", + "dependencies": [], + "details": "Create apps/cli/src/commands/autopilot.command.ts following existing CLI command patterns. Set up Commander.js command registration with basic structure, help text, and integration into the main CLI entry point. Ensure proper TypeScript types and follow existing code conventions.", + "status": "pending", + "testStrategy": "Unit tests for command registration, help text display, and basic command structure validation" + }, + { + "id": 2, + "title": "Implement comprehensive flag handling system", + "description": "Add all required command-line flags with proper parsing, validation, and default values", + "dependencies": [1], + "details": "Implement flags: --dry-run, --no-push, --no-pr, --no-confirm, --force, --max-attempts, --resume. Add proper flag parsing with Commander.js, validation logic, type definitions, and default value handling. Ensure flags are properly documented and follow CLI conventions.", + "status": "pending", + "testStrategy": "Unit tests for flag parsing, validation logic, default values, and error handling for invalid flag combinations" + }, + { + "id": 3, + "title": "Build preflight check validation system", + "description": "Create comprehensive preflight checks for git status, tool availability, and environment validation", + "dependencies": [2], + "details": "Implement preflight checks including: clean working tree validation, test command detection, required tool availability (git, gh), project structure validation. Create modular check system with clear error reporting and suggestions for resolution. Integrate with existing UI components for user feedback.", + "status": "pending", + "testStrategy": "Unit tests for individual preflight checks, error reporting, and validation logic. Integration tests with mock git states and tool availability scenarios" + }, + { + "id": 4, + "title": "Integrate WorkflowOrchestrator and execution planning", + "description": "Connect the command to WorkflowOrchestrator and implement detailed dry-run execution plan display", + "dependencies": [3], + "details": "Integrate with WorkflowOrchestrator for actual workflow execution. Implement dry-run mode showing detailed execution plan with task breakdown, estimated timeline, and resource requirements. Use existing UI components from apps/cli/src/ui/components/ for formatted output and user interaction.", + "status": "pending", + "testStrategy": "Integration tests with mock WorkflowOrchestrator to verify command flow, dry-run output formatting, and execution plan accuracy without actual git operations" + } + ] + }, + { + "id": 35, + "title": "Integrate surgical test generator with WorkflowOrchestrator", + "description": "Connect existing test generation capabilities with the TDD red phase of the workflow", + "details": "Enhance packages/tm-core/src/services/task-execution-service.ts to support test generation mode. Create TestGenerationService that uses existing executor framework with surgical-test-generator prompts. Implement prompt composition system that loads rules from .cursor/rules/ and .claude/agents/, combines with task context, and generates focused failing tests. Support framework-specific test patterns (Jest, Vitest).", + "testStrategy": "Unit tests for prompt composition, test generation calls with mocked executors. Integration tests generating actual test files and verifying they fail appropriately.", + "priority": "medium", + "dependencies": [31, 33], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Enhance TaskExecutionService for test generation mode", + "description": "Extend the existing TaskExecutionService to support test generation workflow by adding a test generation mode that integrates with the surgical-test-generator.", + "dependencies": [], + "details": "Modify packages/tm-core/src/services/task-execution-service.ts to add a testGenerationMode parameter and corresponding logic. Create new method executeTestGeneration() that handles test generation-specific workflows. Add configuration options for test generation behavior and integrate with existing executor framework patterns. Ensure backward compatibility with current execution flows.", + "status": "pending", + "testStrategy": "Unit tests for new test generation mode methods, mocked executor calls, and integration tests verifying test generation workflow execution." + }, + { + "id": 2, + "title": "Create TestGenerationService with executor framework integration", + "description": "Implement a new TestGenerationService that leverages the existing executor framework to generate surgical tests using specialized prompts.", + "dependencies": [1], + "details": "Create packages/tm-core/src/services/test-generation-service.ts that extends or uses the executor framework. Implement methods for generating failing tests based on task context. Add support for surgical-test-generator specific prompts and parameters. Include error handling, logging, and result validation. Integrate with TaskExecutionService's new test generation mode.", + "status": "pending", + "testStrategy": "Unit tests for test generation methods with mocked surgical-test-generator calls. Integration tests generating actual test files and verifying they compile but fail appropriately." + }, + { + "id": 3, + "title": "Implement prompt composition system for rule and context integration", + "description": "Build a sophisticated prompt composition system that loads rules from .cursor/rules/ and .claude/agents/, combines them with task context, and generates focused test generation prompts.", + "dependencies": [2], + "details": "Create prompt composition logic within TestGenerationService or as a separate utility. Implement file system scanning for rule files in .cursor/rules/ and .claude/agents/ directories. Add template system for combining rules with task context, subtask details, and framework-specific requirements. Include validation for prompt completeness and token replacement functionality.", + "status": "pending", + "testStrategy": "Unit tests for rule loading, template processing, and context injection. Integration tests verifying complete prompt generation with various rule combinations and task contexts." + }, + { + "id": 4, + "title": "Add framework-specific test pattern support", + "description": "Implement support for generating tests in different testing frameworks (Jest, Vitest) with appropriate patterns and conventions.", + "dependencies": [3], + "details": "Extend TestGenerationService to detect project testing framework through package.json analysis or configuration. Create framework-specific templates and patterns for Jest and Vitest. Implement test file naming conventions, import statements, and assertion patterns specific to each framework. Add configuration options to override framework detection and customize test generation patterns.", + "status": "pending", + "testStrategy": "Unit tests for framework detection logic and template generation. Integration tests generating tests for both Jest and Vitest projects, verifying correct syntax and runnable test files." + } + ] + }, + { + "id": 36, + "title": "Implement subtask TDD loop execution", + "description": "Create the core Red-Green-Commit cycle execution logic for individual subtasks", + "details": "Extend WorkflowOrchestrator with SubtaskExecutor class that implements the TDD loop: RED phase (generate failing tests), GREEN phase (implement code to pass tests), COMMIT phase (git add, commit with conventional commit message). Include retry logic for GREEN phase with configurable max attempts. Integrate with existing TaskService for subtask status updates. Support timeout and backoff policies.", + "testStrategy": "Unit tests for each phase execution, retry logic, timeout handling. Integration tests with actual test files and git operations in isolated test repositories.", + "priority": "high", + "dependencies": [31, 32, 33, 35], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Create SubtaskExecutor class architecture", + "description": "Design and implement the core SubtaskExecutor class that will orchestrate the TDD loop execution for individual subtasks", + "dependencies": [], + "details": "Create src/core/workflow/SubtaskExecutor.ts with class structure including constructor, phase management methods, and integration points with WorkflowOrchestrator. Define interfaces for TDD phase execution, configuration options, and error handling. Establish event-driven architecture for phase transitions and status reporting.", + "status": "pending", + "testStrategy": "Unit tests for class instantiation, method signatures, and basic configuration handling" + }, + { + "id": 2, + "title": "Implement RED phase test generation", + "description": "Create the RED phase logic that generates failing tests based on subtask requirements and existing codebase context", + "dependencies": [1], + "details": "Implement RED phase in SubtaskExecutor that analyzes subtask details, generates appropriate test files using AI models, and validates tests fail as expected. Include test file placement logic, import resolution, and test framework integration. Support multiple test frameworks and file naming conventions.", + "status": "pending", + "testStrategy": "Unit tests for test generation logic, file placement, and validation. Integration tests with actual test frameworks" + }, + { + "id": 3, + "title": "Implement GREEN phase code generation", + "description": "Create the GREEN phase logic that generates implementation code to make the failing tests pass", + "dependencies": [1, 2], + "details": "Implement GREEN phase that analyzes failing tests, generates implementation code using AI models, and validates tests pass. Include code placement logic, import management, and incremental implementation approach. Support multiple programming languages and frameworks with proper error handling and validation.", + "status": "pending", + "testStrategy": "Unit tests for code generation, test execution validation, and error handling. Integration tests with various test scenarios" + }, + { + "id": 4, + "title": "Implement COMMIT phase with conventional commits", + "description": "Create the COMMIT phase that handles git operations with properly formatted conventional commit messages", + "dependencies": [1, 3], + "details": "Implement COMMIT phase that stages changes, generates conventional commit messages based on task context, and commits with proper formatting. Include git status validation, conflict detection, and commit message templates. Support task ID embedding and automatic scope detection from file changes.", + "status": "pending", + "testStrategy": "Unit tests for git operations, commit message generation, and validation. Integration tests with actual git repositories" + }, + { + "id": 5, + "title": "Implement retry mechanism for GREEN phase", + "description": "Add configurable retry logic for GREEN phase when initial implementation attempts fail", + "dependencies": [3], + "details": "Enhance GREEN phase with retry mechanism including configurable max attempts, exponential backoff, and failure analysis. Include retry context preservation, progressive refinement of implementation, and detailed error logging. Support different retry strategies based on failure types and provide clear feedback on retry attempts.", + "status": "pending", + "testStrategy": "Unit tests for retry logic, backoff calculations, and failure handling. Integration tests with simulated failures and recovery scenarios" + }, + { + "id": 6, + "title": "Implement timeout and backoff policies", + "description": "Add comprehensive timeout handling and backoff policies for all TDD phases to prevent infinite execution", + "dependencies": [1, 5], + "details": "Implement timeout management for each TDD phase with configurable limits, graceful degradation, and resource cleanup. Include exponential backoff for AI API calls, circuit breaker patterns for external services, and proper cancellation handling. Support per-phase timeout configuration and global execution limits.", + "status": "pending", + "testStrategy": "Unit tests for timeout handling, backoff algorithms, and cancellation logic. Integration tests with long-running operations and resource constraints" + }, + { + "id": 7, + "title": "Integrate with TaskService for status updates", + "description": "Connect SubtaskExecutor with existing TaskService to provide real-time status updates and workflow coordination", + "dependencies": [1, 4], + "details": "Integrate SubtaskExecutor with TaskService for subtask status updates, progress reporting, and workflow coordination. Include status change notifications, progress tracking, error reporting, and completion handling. Support batch updates and real-time synchronization with task management system.", + "status": "pending", + "testStrategy": "Unit tests for TaskService integration, status update logic, and event handling. Integration tests with complete workflow execution and status tracking" + } + ] + }, + { + "id": 37, + "title": "Add configuration schema for autopilot settings", + "description": "Extend .taskmaster/config.json schema to support autopilot configuration options", + "details": "Update packages/tm-core/src/interfaces/configuration.interface.ts to include autopilot section with: enabled, requireCleanWorkingTree, commitTemplate, defaultCommitType, maxGreenAttempts, testTimeout. Add test section with runner, coverageThresholds, targetedRunPattern. Include git section with branchPattern, pr settings. Update ConfigManager to validate and provide defaults for new settings.", + "testStrategy": "Unit tests for config validation, default value application, schema validation. Integration tests loading config from actual .taskmaster/config.json files.", + "priority": "medium", + "dependencies": [31], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Extend configuration interface with autopilot settings", + "description": "Update configuration.interface.ts to include autopilot, test, and git configuration sections with all required fields", + "dependencies": [], + "details": "Modify packages/tm-core/src/interfaces/configuration.interface.ts to add autopilot section (enabled, requireCleanWorkingTree, commitTemplate, defaultCommitType, maxGreenAttempts, testTimeout), test section (runner, coverageThresholds, targetedRunPattern), and git section (branchPattern, pr settings). Ensure proper TypeScript typing and JSDoc documentation for all new configuration options.", + "status": "pending", + "testStrategy": "Unit tests for interface type validation and TypeScript compilation checks" + }, + { + "id": 2, + "title": "Update ConfigManager validation logic", + "description": "Enhance ConfigManager to validate new autopilot configuration sections and handle schema validation", + "dependencies": [1], + "details": "Update ConfigManager class to include validation rules for autopilot, test, and git configuration sections. Implement schema validation using existing validation patterns, add error handling for invalid configurations, and ensure proper error messages for configuration issues. Maintain backward compatibility with existing configuration files.", + "status": "pending", + "testStrategy": "Unit tests for validation logic with valid/invalid config scenarios, error message verification" + }, + { + "id": 3, + "title": "Implement default configuration values", + "description": "Add default value implementation for all new autopilot configuration options in ConfigManager", + "dependencies": [2], + "details": "Implement default value provider in ConfigManager for autopilot settings (enabled: false, requireCleanWorkingTree: true, maxGreenAttempts: 3, etc.), test settings (runner: 'npm test', coverageThresholds: null), and git settings (branchPattern: 'feature/*', pr settings). Ensure defaults are applied when configuration sections are missing or incomplete while preserving existing user settings.", + "status": "pending", + "testStrategy": "Integration tests loading config from actual .taskmaster/config.json files, default value application verification" + } + ] + }, + { + "id": 38, + "title": "Implement run state persistence and logging", + "description": "Create run artifact storage system for traceability and resume functionality", + "details": "Create packages/tm-core/src/services/run-state-manager.ts that persists run state to .taskmaster/reports/runs/<timestamp>/. Include manifest.json (run metadata), log.jsonl (event stream), test-results/ (per-phase test outputs), commits.txt (commit SHAs). Implement JSONL event logging format and structured test result storage. Support state checkpointing for resume functionality.", + "testStrategy": "Unit tests for file operations, JSON serialization, log formatting. Integration tests creating actual run directories and verifying persistence across WorkflowOrchestrator restarts.", + "priority": "medium", + "dependencies": [31, 33, 36], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Create RunStateManager service class structure", + "description": "Set up the basic RunStateManager service class with core methods and configuration", + "dependencies": [], + "details": "Create packages/tm-core/src/services/run-state-manager.ts with RunStateManager class. Define constructor accepting configuration options, implement basic methods for initialization, state persistence, and cleanup. Set up TypeScript interfaces for run state data structures and configuration options.", + "status": "pending", + "testStrategy": "Unit tests for class instantiation, method signatures, and configuration validation" + }, + { + "id": 2, + "title": "Implement run directory structure and manifest creation", + "description": "Create the standardized directory structure and manifest.json generation for run artifacts", + "dependencies": [1], + "details": "Implement createRunDirectory() method that creates .taskmaster/reports/runs/<timestamp>/ structure. Generate manifest.json with run metadata including start time, task context, git state, and configuration. Create subdirectories for test-results/ and ensure proper file permissions and error handling.", + "status": "pending", + "testStrategy": "Unit tests for directory creation, manifest JSON structure validation, and file system error handling" + }, + { + "id": 3, + "title": "Implement JSONL event logging system", + "description": "Create structured event logging using JSONL format for run event streams", + "dependencies": [1], + "details": "Implement logEvent() method that appends structured events to log.jsonl file. Define event schema with timestamp, level, type, message, and context data. Support buffering and batch writes for performance. Include event types for task start/complete, errors, state changes, and system events.", + "status": "pending", + "testStrategy": "Unit tests for JSONL formatting, event schema validation, and log file writing with concurrent access scenarios" + }, + { + "id": 4, + "title": "Implement test result storage and commits tracking", + "description": "Create structured storage for test outputs and commit SHA tracking", + "dependencies": [2], + "details": "Implement storeTestResults() method that saves test outputs to test-results/<phase>/ directories with proper JSON formatting. Create updateCommitLog() method that appends commit SHAs to commits.txt with timestamps and context. Support different test result formats and ensure atomic file operations.", + "status": "pending", + "testStrategy": "Unit tests for test result serialization, commit tracking, and file integrity validation" + }, + { + "id": 5, + "title": "Implement state checkpointing and resume functionality", + "description": "Add state checkpointing capabilities and resume logic for interrupted runs", + "dependencies": [3, 4], + "details": "Implement saveCheckpoint() and loadCheckpoint() methods for persisting current run state. Create resume detection logic that identifies incomplete runs and restores state. Support partial completion tracking and validate state consistency on resume. Include cleanup of corrupted or incomplete run directories.", + "status": "pending", + "testStrategy": "Integration tests for state persistence across service restarts, resume functionality validation, and corruption recovery scenarios" + } + ] + }, + { + "id": 39, + "title": "Add GitHub PR creation with run reports", + "description": "Implement automatic PR creation using gh CLI with detailed run reports", + "details": "Create packages/tm-core/src/services/pr-adapter.ts that uses gh CLI to create pull requests. Generate PR body from run manifest and test results, include task/subtask completion summary, coverage metrics, commit links. Implement PR title format: 'Task #{id} [{tag}]: {title}'. Add fallback instructions when gh CLI unavailable. Support custom PR body templates.", + "testStrategy": "Unit tests for PR body generation, gh CLI command construction, error handling. Integration tests with mocked gh CLI to verify PR creation flow and body formatting.", + "priority": "medium", + "dependencies": [31, 32, 38], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Create PRAdapter service base structure and interfaces", + "description": "Set up the foundational PRAdapter service class with interfaces and core structure", + "dependencies": [], + "details": "Create packages/tm-core/src/services/pr-adapter.ts with PRAdapter class, IPRBody interface, PROptions interface, and PRResult interface. Define basic constructor accepting ConfigManager and RunManifest dependencies. Include error handling types and template interface definitions.", + "status": "pending", + "testStrategy": "Unit tests for class instantiation, interface validation, and basic error handling" + }, + { + "id": 2, + "title": "Implement gh CLI integration and command execution", + "description": "Add GitHub CLI detection, validation, and PR creation command execution functionality", + "dependencies": [1], + "details": "Implement gh CLI availability check, version validation, and authentication status verification. Add createPR method that constructs gh pr create commands with title, body, and branch parameters. Include error handling for gh CLI failures, network issues, and authentication problems. Add fallback messaging when gh CLI is unavailable.", + "status": "pending", + "testStrategy": "Unit tests with mocked child_process for gh CLI commands, error scenarios, and fallback behavior" + }, + { + "id": 3, + "title": "Build PR body generation from run manifests and test results", + "description": "Implement comprehensive PR body generation using run data, task completion, and test metrics", + "dependencies": [1], + "details": "Create generatePRBody method that processes run manifest data, task/subtask completion summaries, test results, coverage metrics, and commit information. Implement PR title formatting as 'Task #{id} [{tag}]: {title}'. Include sections for completed tasks, test results, coverage changes, and commit links. Add markdown formatting for readability.", + "status": "pending", + "testStrategy": "Unit tests for body generation with various run manifest scenarios, test result formatting, and markdown output validation" + }, + { + "id": 4, + "title": "Add custom PR template system and configuration support", + "description": "Implement customizable PR body templates with variable substitution and configuration integration", + "dependencies": [2, 3], + "details": "Create template system supporting custom PR body templates from .taskmaster/templates/pr-template.md. Implement variable substitution for {{taskId}}, {{taskTitle}}, {{testResults}}, {{coverage}}, {{commits}}. Add template validation, fallback to default template, and integration with ConfigManager for template paths and PR settings.", + "status": "pending", + "testStrategy": "Unit tests for template loading, variable substitution, validation, and integration with configuration system" + } + ] + }, + { + "id": 40, + "title": "Implement task dependency resolution for subtask ordering", + "description": "Add intelligent subtask ordering based on dependencies and readiness", + "details": "Extend packages/tm-core/src/services/task-service.ts with getNextEligibleSubtask method that considers subtask dependencies, status, and priority. Implement topological sorting for subtask execution order. Handle blocked subtasks and dependency validation. Integration with existing dependency management and task status systems.", + "testStrategy": "Unit tests for dependency resolution algorithms, edge cases with circular dependencies, priority handling. Integration tests with complex task hierarchies and dependency chains.", + "priority": "medium", + "dependencies": [31, 36], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Implement dependency resolution algorithm", + "description": "Create core algorithm to resolve task dependencies and detect circular references", + "dependencies": [], + "details": "Implement a dependency resolution algorithm that can traverse task dependency graphs, detect circular dependencies, and validate dependency chains. Create utility functions for graph traversal and cycle detection using depth-first search. Handle edge cases like self-references and missing dependencies.", + "status": "pending", + "testStrategy": "Unit tests for cycle detection, dependency validation, and graph traversal edge cases" + }, + { + "id": 2, + "title": "Implement topological sorting for subtask ordering", + "description": "Add topological sorting algorithm to determine optimal subtask execution order", + "dependencies": [1], + "details": "Implement Kahn's algorithm or DFS-based topological sorting to order subtasks based on their dependencies. Handle priority weighting within dependency constraints. Create sorting functions that respect both dependencies and priority levels for optimal execution order.", + "status": "pending", + "testStrategy": "Unit tests for topological sort correctness, priority handling, and various dependency graph structures" + }, + { + "id": 3, + "title": "Create task eligibility checking system", + "description": "Implement logic to determine which subtasks are eligible for execution based on status and dependencies", + "dependencies": [1, 2], + "details": "Create getNextEligibleSubtask method that checks subtask status, dependency completion, and availability. Implement filtering logic for blocked, pending, and ready-to-execute subtasks. Add priority-based selection when multiple subtasks are eligible.", + "status": "pending", + "testStrategy": "Unit tests for eligibility logic with various status combinations and dependency states" + }, + { + "id": 4, + "title": "Integrate dependency resolution with TaskService", + "description": "Integrate the dependency resolution system into the existing TaskService architecture", + "dependencies": [1, 2, 3], + "details": "Extend packages/tm-core/src/services/task-service.ts with new dependency resolution methods. Integrate with existing task status management and dependency validation systems. Ensure backward compatibility and proper error handling for the new functionality.", + "status": "pending", + "testStrategy": "Integration tests with existing TaskService methods, complex task hierarchies, and dependency chain validation" + } + ] + }, + { + "id": 41, + "title": "Create resume functionality for interrupted runs", + "description": "Implement checkpoint/resume system for autopilot workflow interruptions", + "details": "Enhance RunStateManager with checkpoint creation and restoration. Add --resume flag to autopilot command that reconstructs WorkflowOrchestrator state from persisted run data. Implement state validation to ensure safe resume (git state, file changes, test status). Support partial phase resume (e.g., retry GREEN phase after manual fixes).", + "testStrategy": "Unit tests for state serialization/deserialization, validation logic. Integration tests interrupting and resuming workflows at different phases, verifying state consistency.", + "priority": "medium", + "dependencies": [34, 36, 38], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Implement checkpoint creation in RunStateManager", + "description": "Add checkpoint creation functionality to RunStateManager for persisting workflow state during execution", + "dependencies": [], + "details": "Enhance RunStateManager with methods to create checkpoints containing current workflow state, active phase, completed subtasks, git branch info, and test status. Implement state serialization to JSON format and save to .taskmaster/checkpoints/ directory with timestamp-based naming. Include metadata like workflow start time, current task ID, and phase progress.", + "status": "pending", + "testStrategy": "Unit tests for checkpoint creation, state serialization, file writing operations, and metadata accuracy. Test checkpoint creation at different workflow phases." + }, + { + "id": 2, + "title": "Implement state restoration logic", + "description": "Create state restoration functionality to reconstruct WorkflowOrchestrator from persisted checkpoint data", + "dependencies": [1], + "details": "Add methods to RunStateManager for loading checkpoint files, deserializing workflow state, and reconstructing WorkflowOrchestrator instance. Implement state reconstruction including phase restoration, task progress, git branch context, and test results. Handle checkpoint file validation and corruption detection with appropriate error messaging.", + "status": "pending", + "testStrategy": "Unit tests for checkpoint loading, state deserialization, WorkflowOrchestrator reconstruction. Test error handling for corrupted or invalid checkpoint files." + }, + { + "id": 3, + "title": "Add state validation for safe resume", + "description": "Implement validation logic to ensure safe workflow resumption by checking git state, file changes, and test status", + "dependencies": [2], + "details": "Create validation methods to verify git working tree is clean, current branch matches checkpoint, no unexpected file changes occurred, and test environment is consistent. Implement safety checks for package.json changes, dependency updates, and configuration modifications. Provide detailed validation failure messages and recovery suggestions.", + "status": "pending", + "testStrategy": "Unit tests for validation logic, git state checking, file change detection. Integration tests with scenarios like dirty working tree, branch changes, and dependency modifications." + }, + { + "id": 4, + "title": "Add --resume flag to autopilot command", + "description": "Implement CLI flag for resuming interrupted autopilot workflows from checkpoints", + "dependencies": [3], + "details": "Add --resume flag to autopilot command that lists available checkpoints, allows selection of specific checkpoint, and initiates workflow restoration. Implement checkpoint discovery from .taskmaster/checkpoints/ directory, display checkpoint metadata (timestamp, phase, progress), and handle user selection. Include --resume-latest option for automatic latest checkpoint selection.", + "status": "pending", + "testStrategy": "Integration tests for CLI flag functionality, checkpoint listing, user selection handling, and automatic latest checkpoint selection. Test error scenarios like no checkpoints found." + }, + { + "id": 5, + "title": "Support partial phase resume functionality", + "description": "Implement ability to resume workflow from specific phases like retrying GREEN phase after manual fixes", + "dependencies": [4], + "details": "Add phase-specific resume options allowing users to restart from particular workflow phases (Preflight, Branch, SubtaskLoop, Finalization). Implement phase validation to ensure prerequisites are met, state consistency checks for partial resume, and option to skip completed phases. Support retry scenarios like re-running tests after manual bug fixes or resuming after dependency updates.", + "status": "pending", + "testStrategy": "Integration tests for partial phase resume scenarios, phase validation logic, and state consistency checks. Test retry workflows after manual interventions and dependency changes." + } + ] + }, + { + "id": 42, + "title": "Add coverage threshold enforcement", + "description": "Implement code coverage validation before allowing commits and finalization", + "details": "Enhance TestRunnerAdapter to parse coverage reports from Jest/Vitest and enforce configurable thresholds (lines, branches, functions, statements). Default to 80% across all metrics. Add coverage gates in GREEN phase before commit and final test suite before PR creation. Provide detailed coverage failure reporting with suggestions for improvement.", + "testStrategy": "Unit tests for coverage report parsing from different formats (lcov, json), threshold validation logic. Integration tests with actual test runs generating coverage data.", + "priority": "medium", + "dependencies": [33, 36], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Parse coverage reports from Jest/Vitest", + "description": "Implement coverage report parsing to extract metrics from different test runner formats", + "dependencies": [], + "details": "Create coverage report parser in TestRunnerAdapter that can read Jest coverage reports (json, lcov formats) and Vitest coverage reports. Extract line coverage, branch coverage, function coverage, and statement coverage metrics. Handle different coverage output formats and normalize data structure for threshold validation.", + "status": "pending", + "testStrategy": "Unit tests for parsing different coverage report formats (lcov, json). Mock coverage files with various metrics and verify correct extraction." + }, + { + "id": 2, + "title": "Implement configurable threshold validation logic", + "description": "Create threshold validation system with configurable coverage requirements", + "dependencies": [1], + "details": "Build threshold validation engine that accepts configurable thresholds for lines, branches, functions, and statements (default 80%). Compare parsed coverage metrics against thresholds and generate detailed validation results. Support per-metric configuration and overall coverage requirements.", + "status": "pending", + "testStrategy": "Unit tests for threshold validation with various coverage scenarios. Test edge cases like missing metrics and invalid threshold configurations." + }, + { + "id": 3, + "title": "Add coverage gates in workflow phases", + "description": "Integrate coverage validation as gates in GREEN phase and pre-finalization", + "dependencies": [2], + "details": "Add coverage validation gates in the GREEN phase before allowing commits and in the final test suite before PR creation. Integrate with existing workflow phases to halt execution when coverage thresholds are not met. Provide clear gate failure messaging and next steps.", + "status": "pending", + "testStrategy": "Integration tests with actual test runs generating coverage data. Verify gates properly block workflow progression when thresholds fail." + }, + { + "id": 4, + "title": "Create detailed coverage failure reporting system", + "description": "Implement comprehensive coverage reporting with improvement suggestions", + "dependencies": [2], + "details": "Build detailed coverage failure reporting that shows which metrics failed thresholds, specific uncovered lines/branches, and actionable suggestions for improvement. Include file-level coverage breakdown, highlight critical uncovered areas, and provide guidance on writing additional tests.", + "status": "pending", + "testStrategy": "Unit tests for report generation with various failure scenarios. Verify report formatting and suggestion quality with mock coverage data." + } + ] + }, + { + "id": 43, + "title": "Implement tmux-based TUI navigator", + "description": "Create terminal user interface for interactive task selection and workflow monitoring", + "details": "Create apps/cli/src/ui/tui/navigator.ts using blessed or ink for terminal UI. Left pane shows project info, active tag, task list with status indicators. Right pane coordinates with tmux to spawn executor terminal. Implement keybindings for navigation (↑/↓), task selection (Enter), workflow control (s/p/q). Real-time status updates via file watching or event streams.", + "testStrategy": "Unit tests for UI component rendering, key handling, state updates. Integration tests with tmux session management and terminal interaction simulation.", + "priority": "low", + "dependencies": [31, 34], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Select and configure TUI framework (blessed vs ink)", + "description": "Research and choose between blessed and ink frameworks for terminal UI implementation, then set up the basic project structure with dependencies", + "dependencies": [], + "details": "Evaluate blessed vs ink for terminal UI capabilities, performance, and maintenance status. Install chosen framework and create basic project structure in apps/cli/src/ui/tui/. Set up TypeScript configuration and basic component architecture. Consider blessed for full-featured terminal UI or ink for React-like component model.", + "status": "pending", + "testStrategy": "Unit tests for framework initialization and basic component rendering" + }, + { + "id": 2, + "title": "Design and implement left pane interface layout", + "description": "Create the left pane UI components showing project information, active tag, and task list with status indicators", + "dependencies": [1], + "details": "Implement left pane layout with three sections: project info header, active tag display, and scrollable task list. Add status indicators using colors/symbols (✓ done, ⏳ in-progress, ○ pending). Implement proper text wrapping, overflow handling, and responsive layout for different terminal sizes.", + "status": "pending", + "testStrategy": "Unit tests for layout rendering, status indicator display, and text formatting" + }, + { + "id": 3, + "title": "Implement tmux integration and right pane coordination", + "description": "Create tmux session management and coordinate right pane terminal spawning for task execution", + "dependencies": [1], + "details": "Implement tmux session creation, window management, and pane coordination. Create TmuxAdapter class to handle session lifecycle, window/pane creation for executor terminals. Ensure proper cleanup of tmux sessions and handle edge cases like existing sessions or tmux not available.", + "status": "pending", + "testStrategy": "Integration tests for tmux session management, pane creation, and cleanup procedures" + }, + { + "id": 4, + "title": "Build navigation system with keybindings", + "description": "Implement keyboard navigation system with arrow keys, Enter for selection, and workflow control keys", + "dependencies": [2], + "details": "Implement keyboard event handling for navigation (↑/↓ arrow keys), task selection (Enter), and workflow control (s for start, p for pause, q for quit). Add visual feedback for selected items, proper focus management, and keyboard shortcut help display. Handle edge cases like empty task lists.", + "status": "pending", + "testStrategy": "Unit tests for key handling logic, navigation state management, and selection behavior" + }, + { + "id": 5, + "title": "Implement real-time status updates system", + "description": "Create real-time monitoring system for task status changes using file watching or event streams", + "dependencies": [2, 3], + "details": "Implement file system watching for tasks.json changes or event-based updates for real-time status synchronization. Update UI components when task status changes, add smooth transitions, and handle concurrent updates. Consider using chokidar for file watching or implement event emitter pattern for status changes.", + "status": "pending", + "testStrategy": "Integration tests for file watching, event handling, and UI update responsiveness" + }, + { + "id": 6, + "title": "Implement event handling and user experience polish", + "description": "Add comprehensive event handling, error management, and user experience improvements for the TUI navigator", + "dependencies": [4, 5], + "details": "Implement comprehensive error handling for tmux failures, terminal resize events, and graceful degradation. Add loading states, confirmation dialogs for destructive actions, and help screens. Implement proper cleanup on exit, signal handling (SIGINT, SIGTERM), and recovery from unexpected states.", + "status": "pending", + "testStrategy": "Integration tests for error scenarios, terminal interaction simulation, and edge case handling" + } + ] + }, + { + "id": 44, + "title": "Add prompt composition system for context-aware test generation", + "description": "Create sophisticated prompt assembly system combining rules, task context, and phase instructions", + "details": "Create packages/tm-core/src/services/prompt-composer.ts that loads and combines prompt fragments from .cursor/rules/, task context, and phase-specific instructions. Implement template system with token replacement ({task}, {subtask}, {framework}). Support rule precedence and conditional inclusion based on project type. Generate targeted prompts for RED (test generation) and GREEN (implementation) phases.", + "testStrategy": "Unit tests for template processing, rule loading, context injection. Integration tests generating complete prompts and validating content relevance and accuracy.", + "priority": "medium", + "dependencies": [35], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Create PromptComposer service foundation", + "description": "Implement the core PromptComposer class structure with basic interfaces and constructor", + "dependencies": [], + "details": "Create packages/tm-core/src/services/prompt-composer.ts with PromptComposer class, define interfaces for PromptTemplate, RuleFragment, and CompositionContext. Implement constructor with configuration options and basic method stubs for compose(), loadRules(), and processTemplate(). Set up error handling and logging infrastructure.", + "status": "pending", + "testStrategy": "Unit tests for class instantiation, interface validation, and basic error handling scenarios" + }, + { + "id": 2, + "title": "Implement template system with token replacement", + "description": "Build template processing engine that supports dynamic token replacement and conditional logic", + "dependencies": [1], + "details": "Implement processTemplate() method supporting token replacement for {task}, {subtask}, {framework}, {phase} placeholders. Add conditional template sections with {{#if condition}} syntax. Support nested replacements and escape sequences. Include template validation and syntax error reporting with helpful error messages.", + "status": "pending", + "testStrategy": "Unit tests for token replacement accuracy, conditional logic evaluation, nested template processing, and error handling for malformed templates" + }, + { + "id": 3, + "title": "Implement rule loading and precedence system", + "description": "Create rule fragment loading system from .cursor/rules/ with proper precedence and conditional inclusion", + "dependencies": [1], + "details": "Implement loadRules() method that scans .cursor/rules/ directory for rule files, parses rule metadata including precedence levels and project type conditions. Support rule inheritance and override patterns. Implement conditional inclusion based on project framework detection (React, Vue, Node.js, etc.). Cache loaded rules for performance.", + "status": "pending", + "testStrategy": "Unit tests for file system scanning, rule parsing, precedence resolution, and conditional filtering. Integration tests with various project structures and rule configurations" + }, + { + "id": 4, + "title": "Implement context injection and phase-specific prompt generation", + "description": "Build context assembly system that combines task data, rules, and phase instructions into complete prompts", + "dependencies": [2, 3], + "details": "Implement compose() method that combines task context, loaded rules, and phase-specific instructions into targeted prompts for RED (test generation) and GREEN (implementation) phases. Support framework-specific adaptations and context prioritization. Include prompt validation and length optimization to ensure effective AI consumption.", + "status": "pending", + "testStrategy": "Unit tests for context injection accuracy, phase-specific prompt generation, and framework adaptation. Integration tests generating complete prompts and validating content relevance for different TDD phases" + } + ] + }, + { + "id": 45, + "title": "Implement tag-branch mapping and automatic tag switching", + "description": "Create automatic tag management that maps branches to tags and switches context", + "details": "Enhance GitAdapter to automatically set active tag based on branch name using existing tag-management.js functionality. Extract tag from branch name using configured pattern, validate tag exists, and switch to tag context before workflow execution. Implement branch-to-tag mapping persistence and validation. Support tag creation if branch tag doesn't exist.", + "testStrategy": "Unit tests for tag extraction from branch names, tag switching logic, mapping persistence. Integration tests with actual git branches and tag management operations.", + "priority": "medium", + "dependencies": [32, 40], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Enhance GitAdapter with branch-to-tag extraction logic", + "description": "Implement branch name parsing to extract tag names using configurable patterns and integrate with existing tag management functionality", + "dependencies": [], + "details": "Extend GitAdapter class to include tag extraction methods that parse branch names using configurable regex patterns (e.g., feature/tag-name -> tag-name). Integrate with existing tag-management.js functionality to validate extracted tags exist. Add configuration options for branch naming patterns and fallback behaviors when tag extraction fails.", + "status": "pending", + "testStrategy": "Unit tests for branch name parsing with various patterns, tag validation logic, and configuration handling. Mock git operations and tag management calls." + }, + { + "id": 2, + "title": "Implement automatic tag switching workflow", + "description": "Create workflow logic that automatically switches to the appropriate tag context before executing any operations based on current branch", + "dependencies": [1], + "details": "Implement pre-workflow hook in GitAdapter that detects current branch, extracts tag using patterns from subtask 1, validates tag exists, and switches active tag context. Handle cases where extracted tag doesn't exist by optionally creating new tag or falling back to default. Ensure tag switching occurs before any workflow execution begins.", + "status": "pending", + "testStrategy": "Integration tests with actual git branches and tag switching operations. Test tag creation workflows and fallback scenarios. Verify workflow execution uses correct tag context." + }, + { + "id": 3, + "title": "Implement branch-to-tag mapping persistence and validation", + "description": "Create persistent storage for branch-tag mappings with validation and management capabilities", + "dependencies": [1], + "details": "Implement mapping persistence using configuration files or database to store branch-to-tag relationships. Add validation logic to ensure mappings are consistent and up-to-date. Include management commands to view, update, and clean up mappings. Support mapping inheritance for branch hierarchies and pattern-based rules.", + "status": "pending", + "testStrategy": "Unit tests for mapping storage, retrieval, and validation logic. Integration tests for mapping consistency across git operations and tag management scenarios." + } + ] + }, + { + "id": 46, + "title": "Add comprehensive error handling and recovery", + "description": "Implement robust error handling with actionable recovery suggestions", + "details": "Add comprehensive error handling throughout WorkflowOrchestrator with specific error types: GitError, TestError, ConfigError, DependencyError. Implement recovery suggestions for common failures (merge conflicts, test timeouts, missing dependencies). Add --force flag to bypass certain validations. Include error context in run reports and logs.", + "testStrategy": "Unit tests for error classification, recovery suggestion generation, force flag behavior. Integration tests simulating various failure scenarios and verifying appropriate error handling.", + "priority": "medium", + "dependencies": [31, 32, 33, 38], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Implement error classification system with specific error types", + "description": "Create comprehensive error type hierarchy including GitError, TestError, ConfigError, and DependencyError with specific subclasses for different failure scenarios", + "dependencies": [], + "details": "Define base ErrorHandler class and specific error types (GitError for merge conflicts/branch issues, TestError for test failures/timeouts, ConfigError for invalid configurations, DependencyError for missing tools/packages). Each error type should include error codes, context data, and categorization for appropriate handling strategies.", + "status": "pending", + "testStrategy": "Unit tests for error type instantiation, classification logic, and error code mapping. Mock various failure scenarios to verify correct error type detection." + }, + { + "id": 2, + "title": "Build recovery suggestion engine with actionable recommendations", + "description": "Develop intelligent system that provides specific recovery steps based on error type and context, with automated resolution where possible", + "dependencies": [1], + "details": "Create RecoverySuggestionEngine that maps error types to actionable recovery steps. For GitError: suggest merge conflict resolution, branch switching. For TestError: recommend timeout adjustments, dependency installation. For ConfigError: provide configuration fixes. Include automated recovery options where safe and manual step-by-step instructions.", + "status": "pending", + "testStrategy": "Unit tests for suggestion generation logic, recovery step validation. Integration tests simulating error scenarios and verifying suggested recovery actions are accurate and actionable." + }, + { + "id": 3, + "title": "Implement error context management and preservation", + "description": "Create system to capture, store, and propagate error context throughout the workflow execution chain for detailed debugging", + "dependencies": [1], + "details": "Build ErrorContext class to capture execution state, command history, environment details, and task information when errors occur. Implement context serialization for persistence and context propagation through async operations. Include stack trace enhancement and correlation IDs for tracking errors across workflow phases.", + "status": "pending", + "testStrategy": "Unit tests for context capture, serialization, and propagation. Integration tests verifying context preservation across workflow phases and accurate debugging information availability." + }, + { + "id": 4, + "title": "Add --force flag implementation with selective validation bypass", + "description": "Implement force flag mechanism that allows bypassing specific validations while maintaining critical safety checks", + "dependencies": [2, 3], + "details": "Add --force flag support to WorkflowOrchestrator commands with granular control over which validations to bypass. Implement ForceContext to track bypassed checks and maintain audit trail. Define which validations are force-bypassable (dependency checks, test requirements) vs critical safety checks (git repo integrity, file permissions).", + "status": "pending", + "testStrategy": "Unit tests for force flag parsing, validation bypass logic, safety check preservation. Integration tests with various force scenarios ensuring critical protections remain active." + }, + { + "id": 5, + "title": "Integrate error handling with logging and reporting systems", + "description": "Connect error handling system with existing logging infrastructure and enhance run reports with error context and recovery information", + "dependencies": [2, 3, 4], + "details": "Integrate ErrorHandler with existing logging system to provide structured error logs with context. Enhance run reports to include error summaries, recovery actions taken, and performance impact. Implement error metrics collection and add error information to workflow execution summaries with actionable next steps.", + "status": "pending", + "testStrategy": "Unit tests for logging integration, report enhancement, metrics collection. Integration tests verifying complete error flow from detection through logging to reporting with proper context preservation." + } + ] + }, + { + "id": 47, + "title": "Implement conventional commit message generation", + "description": "Create intelligent commit message generation based on task context and changes", + "details": "Enhance GitAdapter with commit message generation using configurable templates. Support conventional commit format with task context: '{type}({scope}): {message} (task {id}.{subtask})'. Auto-detect commit type (feat, fix, chore) based on task content and file changes. Include commit message templates and validation against conventional commit standards.", + "testStrategy": "Unit tests for message template processing, type detection, validation logic. Integration tests generating commit messages for various task types and validating format compliance.", + "priority": "low", + "dependencies": [32, 36], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Create commit message template system", + "description": "Implement configurable template system for conventional commit message generation with variable substitution", + "dependencies": [], + "details": "Create template engine that supports conventional commit format '{type}({scope}): {message} (task {id}.{subtask})'. Implement variable substitution for task context including task ID, subtask ID, scope, and message. Support configurable templates through configuration files with validation for required placeholders.", + "status": "pending", + "testStrategy": "Unit tests for template parsing, variable substitution, and configuration validation. Test various template formats and edge cases." + }, + { + "id": 2, + "title": "Implement commit type auto-detection logic", + "description": "Create intelligent detection of conventional commit types based on task content and file changes", + "dependencies": [1], + "details": "Analyze task descriptions, titles, and file changes to automatically determine commit type (feat, fix, chore, docs, style, refactor, test). Use pattern matching on task content and file extension analysis. Include fallback logic and confidence scoring for type suggestions.", + "status": "pending", + "testStrategy": "Unit tests for type detection algorithms with various task scenarios. Test file change analysis and pattern matching accuracy." + }, + { + "id": 3, + "title": "Add commit message validation and GitAdapter integration", + "description": "Implement validation against conventional commit standards and integrate with GitAdapter", + "dependencies": [1, 2], + "details": "Validate generated commit messages against conventional commit specification including format, length limits, and required components. Integrate template system and type detection into GitAdapter class. Add configuration options for strict vs lenient validation modes.", + "status": "pending", + "testStrategy": "Unit tests for validation rules and GitAdapter integration. Integration tests generating complete commit messages and verifying conventional commit compliance." + } + ] + }, + { + "id": 48, + "title": "Add multi-framework test execution support", + "description": "Extend TestRunnerAdapter to support multiple testing frameworks beyond Jest/Vitest", + "details": "Enhance TestRunnerAdapter with framework-specific adapters for pytest (Python), go test (Go), cargo test (Rust). Implement common interface for test execution, result parsing, and coverage reporting across frameworks. Add framework detection based on project files (requirements.txt, go.mod, Cargo.toml). Maintain backward compatibility with existing JavaScript/TypeScript support.", + "testStrategy": "Unit tests for framework detection, adapter interface implementation. Integration tests with fixture projects for each supported framework, verifying test execution and result parsing.", + "priority": "low", + "dependencies": [33], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Implement framework detection system", + "description": "Create a framework detection mechanism that identifies testing frameworks based on project files and configuration", + "dependencies": [], + "details": "Implement FrameworkDetector class that scans for framework-specific files (package.json for Jest/Vitest, requirements.txt/pyproject.toml for pytest, go.mod for go test, Cargo.toml for cargo test). Parse configuration files to determine active testing frameworks. Return framework type and configuration details for adapter initialization.", + "status": "pending", + "testStrategy": "Unit tests for file detection logic, configuration parsing. Integration tests with sample project structures for each framework type." + }, + { + "id": 2, + "title": "Design common adapter interface", + "description": "Create a unified interface for test execution adapters across all supported frameworks", + "dependencies": [1], + "details": "Define TestFrameworkAdapter interface with methods: execute(), parseResults(), getCoverage(), getTestFiles(). Create base adapter class with common functionality like result normalization, error handling, and timeout management. Define standardized result format for test outcomes, coverage data, and execution metadata.", + "status": "pending", + "testStrategy": "Unit tests for interface implementation, base adapter functionality. Mock adapter tests for interface compliance verification." + }, + { + "id": 3, + "title": "Implement Python pytest adapter", + "description": "Create adapter for Python pytest framework with result parsing and coverage support", + "dependencies": [2], + "details": "Implement PytestAdapter extending TestFrameworkAdapter. Handle pytest command execution with proper arguments for JSON output and coverage reporting. Parse pytest JSON results into standardized format. Support pytest-cov for coverage data extraction. Handle Python virtual environment detection and activation.", + "status": "pending", + "testStrategy": "Unit tests for pytest command construction, result parsing. Integration tests with real Python projects using pytest and pytest-cov." + }, + { + "id": 4, + "title": "Implement Go and Rust adapters", + "description": "Create adapters for Go test and Rust cargo test frameworks with result parsing", + "dependencies": [2], + "details": "Implement GoTestAdapter for 'go test' command with JSON output parsing and coverage support via go tool cover. Implement CargoTestAdapter for 'cargo test' with JSON message parsing and coverage via cargo-tarpaulin. Both adapters should handle framework-specific output formats and convert to standardized result structure.", + "status": "pending", + "testStrategy": "Unit tests for command execution and result parsing for both Go and Rust. Integration tests with sample Go modules and Rust crates." + }, + { + "id": 5, + "title": "Integrate adapters with TestRunnerAdapter and ensure backward compatibility", + "description": "Extend existing TestRunnerAdapter to use new framework adapters while maintaining JavaScript/TypeScript support", + "dependencies": [3, 4], + "details": "Modify TestRunnerAdapter to use FrameworkDetector and route to appropriate adapter. Maintain existing Jest/Vitest functionality as default for JavaScript/TypeScript projects. Add adapter factory pattern for framework selection. Ensure all existing TestRunnerAdapter methods work unchanged for backward compatibility.", + "status": "pending", + "testStrategy": "Unit tests for adapter routing and factory pattern. Integration tests verifying existing Jest/Vitest functionality remains unchanged. Cross-framework integration tests." + } + ] + }, + { + "id": 49, + "title": "Implement workflow event streaming for real-time monitoring", + "description": "Create event streaming system for real-time workflow progress monitoring", + "details": "Enhance WorkflowOrchestrator with EventEmitter-based streaming of workflow events (phase changes, test results, commit creation). Implement structured event format with timestamps, phase info, and progress data. Support event persistence to run logs and optional WebSocket streaming for external monitoring. Include progress percentage calculation and time estimates.", + "testStrategy": "Unit tests for event emission, formatting, persistence. Integration tests monitoring complete workflow execution with event verification and progress tracking accuracy.", + "priority": "low", + "dependencies": [31, 38], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Enhance WorkflowOrchestrator with EventEmitter capabilities", + "description": "Add EventEmitter functionality to WorkflowOrchestrator for streaming workflow events in real-time", + "dependencies": [], + "details": "Extend WorkflowOrchestrator class to inherit from EventEmitter or add EventEmitter instance. Define event types for phase changes, test results, commit creation, and workflow state transitions. Implement event emission at key workflow points with proper error handling and event cleanup.", + "status": "pending", + "testStrategy": "Unit tests for EventEmitter integration, event emission timing, and cleanup. Mock workflow phases to verify correct event firing sequence." + }, + { + "id": 2, + "title": "Implement structured event format with timestamps and metadata", + "description": "Create standardized event format including timestamps, phase information, progress data, and metadata", + "dependencies": [1], + "details": "Define TypeScript interfaces for workflow events with required fields: eventType, timestamp, phaseInfo, progressPercentage, timeEstimates, metadata. Implement event formatting utilities and validation. Include progress calculation logic based on workflow phases and subtask completion.", + "status": "pending", + "testStrategy": "Unit tests for event format validation, timestamp accuracy, progress percentage calculations. Test event serialization and deserialization." + }, + { + "id": 3, + "title": "Add event persistence to run logs", + "description": "Implement event logging system to persist workflow events to run logs for historical tracking", + "dependencies": [2], + "details": "Create event persistence layer that writes structured events to run log files. Implement log rotation and retention policies. Add event querying capabilities for retrieving historical workflow data. Ensure thread-safe writing and proper error handling for file operations.", + "status": "pending", + "testStrategy": "Unit tests for event persistence, log file management, and querying. Integration tests verifying complete workflow event logging and retrieval accuracy." + }, + { + "id": 4, + "title": "Implement optional WebSocket streaming for external monitoring", + "description": "Create WebSocket server for real-time event streaming to external monitoring clients", + "dependencies": [2], + "details": "Implement optional WebSocket server using ws library for streaming events to external clients. Add connection management, authentication, and rate limiting. Support multiple client connections with event filtering. Include graceful connection handling and error recovery mechanisms.", + "status": "pending", + "testStrategy": "Unit tests for WebSocket server setup, client connection handling, event broadcasting. Integration tests with multiple clients receiving real-time workflow events." + } + ] + }, + { + "id": 50, + "title": "Add intelligent test targeting for faster feedback", + "description": "Implement smart test selection that runs only relevant tests during GREEN phase", + "details": "Enhance TestRunnerAdapter with test targeting based on file changes and test dependencies. Implement test impact analysis to identify which tests are affected by implementation changes. Support framework-specific targeting (Jest --findRelatedTests, Vitest changed files). Fall back to full test suite if targeting fails or for final validation.", + "testStrategy": "Unit tests for change detection, test dependency analysis, targeting logic. Integration tests with various project structures verifying targeted test selection accuracy and performance improvements.", + "priority": "low", + "dependencies": [33, 36], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Implement file change detection system", + "description": "Create a system to detect and track file changes between test runs to identify which files have been modified", + "dependencies": [], + "details": "Implement GitChangeDetector class that uses git diff to identify changed files since last test run. Track file timestamps and git commit hashes. Support both staged and unstaged changes. Handle edge cases like new files, deleted files, and renamed files. Store change metadata for impact analysis.", + "status": "pending", + "testStrategy": "Unit tests for git diff parsing, file change detection accuracy, edge cases with file operations. Mock git operations for consistent testing." + }, + { + "id": 2, + "title": "Build test dependency analysis engine", + "description": "Create dependency mapping system to understand which tests are affected by specific file changes", + "dependencies": [1], + "details": "Implement TestDependencyAnalyzer that maps source files to test files through import analysis, test file naming conventions, and explicit dependency declarations. Parse import statements, require calls, and dynamic imports. Build dependency graph with file-to-test relationships. Support both direct and transitive dependencies.", + "status": "pending", + "testStrategy": "Unit tests for import parsing, dependency graph construction, transitive dependency resolution. Integration tests with real project structures." + }, + { + "id": 3, + "title": "Create framework-specific targeting adapters", + "description": "Implement framework-specific test targeting using native tools like Jest --findRelatedTests and Vitest changed files", + "dependencies": [2], + "details": "Create FrameworkTargetingAdapter with implementations for Jest (--findRelatedTests), Vitest (--changed), and generic fallback. Abstract framework detection and command generation. Handle framework-specific configuration and edge cases. Provide unified interface for all test runners.", + "status": "pending", + "testStrategy": "Unit tests for each framework adapter, command generation, framework detection. Integration tests with actual Jest and Vitest projects." + }, + { + "id": 4, + "title": "Implement test impact calculation algorithm", + "description": "Create intelligent algorithm to calculate which tests should run based on change analysis and dependency mapping", + "dependencies": [2, 3], + "details": "Implement TestImpactCalculator that combines file changes with dependency analysis to determine minimal test set. Use scoring algorithm considering change severity, dependency depth, and historical test failures. Optimize for both coverage and performance. Include confidence metrics for targeting decisions.", + "status": "pending", + "testStrategy": "Unit tests for impact calculation logic, scoring algorithms, confidence metrics. Performance tests with large codebases and dependency graphs." + }, + { + "id": 5, + "title": "Add fallback logic and integration with TestRunnerAdapter", + "description": "Integrate targeting system with existing TestRunnerAdapter and implement robust fallback mechanisms", + "dependencies": [3, 4], + "details": "Enhance TestRunnerAdapter with intelligent targeting methods. Implement fallback to full test suite when targeting fails, confidence is low, or for final validation. Add configuration options for targeting aggressiveness and fallback thresholds. Include performance monitoring and targeting effectiveness metrics.", + "status": "pending", + "testStrategy": "Integration tests with TestRunnerAdapter, fallback scenario testing, performance benchmarks. End-to-end tests verifying targeting accuracy and fallback reliability." + } + ] + }, + { + "id": 51, + "title": "Implement dry-run visualization with execution timeline", + "description": "Create detailed dry-run output showing complete execution plan with time estimates", + "details": "Enhance autopilot command dry-run mode with detailed execution timeline showing all phases, subtasks, estimated durations, and dependencies. Include preflight check results, branch operations, test generation/execution plans, and finalization steps. Add ASCII art progress visualization and resource requirements (git, gh, test tools).", + "testStrategy": "Unit tests for timeline calculation, duration estimation, visualization formatting. Integration tests generating dry-run output for various task complexities and verifying accuracy of plans.", + "priority": "low", + "dependencies": [34, 40], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Implement timeline calculation engine", + "description": "Create core timeline calculation logic for dry-run execution planning with duration estimates", + "dependencies": [], + "details": "Build timeline calculation engine in apps/cli/src/commands/autopilot/timeline.ts. Calculate estimated durations for each execution phase (preflight, branch ops, test generation, execution, finalization). Account for task complexity, subtask counts, and historical execution data. Support parallel vs sequential execution modeling with dependency resolution.", + "status": "pending", + "testStrategy": "Unit tests for duration calculation algorithms, dependency resolution logic, and parallel execution modeling. Mock historical data for various task complexities." + }, + { + "id": 2, + "title": "Implement duration estimation algorithms", + "description": "Create intelligent duration estimation based on task complexity and historical data", + "dependencies": [1], + "details": "Develop estimation algorithms in apps/cli/src/commands/autopilot/estimator.ts. Analyze task complexity factors (subtask count, file changes, test requirements). Use historical execution data or baseline estimates. Account for tool dependencies (git, gh, test runners) and system resources. Include confidence intervals and estimation accuracy tracking.", + "status": "pending", + "testStrategy": "Unit tests for estimation algorithms with various complexity scenarios. Integration tests comparing estimates against actual execution times for accuracy validation." + }, + { + "id": 3, + "title": "Create ASCII art progress visualization", + "description": "Build comprehensive visualization formatter for execution timeline display", + "dependencies": [1, 2], + "details": "Create visualization formatter in apps/cli/src/commands/autopilot/visualizer.ts. Generate ASCII art timeline with phase bars, dependency arrows, and progress indicators. Include execution tree view showing task hierarchy and parallel operations. Add resource requirement badges (git, gh, npm) and time estimates. Support colored output with status indicators.", + "status": "pending", + "testStrategy": "Unit tests for ASCII art generation, timeline formatting, and colored output. Visual regression tests comparing output layouts for different terminal widths and task complexities." + }, + { + "id": 4, + "title": "Implement resource validation and preflight checks", + "description": "Create comprehensive resource validation for execution environment requirements", + "dependencies": [], + "details": "Build resource validator in apps/cli/src/commands/autopilot/validator.ts. Check availability of required tools (git, gh, npm, test runners). Validate git repository state, branch permissions, and authentication. Verify disk space, network connectivity, and system resources. Include detailed preflight check results in dry-run output with remediation suggestions.", + "status": "pending", + "testStrategy": "Unit tests for individual resource checks and validation logic. Integration tests with various system configurations and missing dependencies to verify error handling and suggestions." + } + ] + }, + { + "id": 52, + "title": "Add autopilot workflow integration tests", + "description": "Create comprehensive end-to-end integration tests for complete autopilot workflows", + "details": "Create tests/integration/autopilot/ with full workflow tests using temporary git repositories, mock task data, and isolated test environments. Test complete red-green-commit cycles, error recovery, resume functionality, and PR creation. Include performance benchmarks and resource usage validation. Support both Jest and Vitest test execution.", + "testStrategy": "Integration tests with isolated environments, git repository fixtures, mock GitHub API responses. Performance tests measuring workflow execution times and resource consumption across different project sizes.", + "priority": "medium", + "dependencies": [36, 39, 41], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Set up isolated test environment infrastructure", + "description": "Create the foundational test infrastructure for isolated autopilot workflow testing including temporary directories, git repository fixtures, and environment cleanup", + "dependencies": [], + "details": "Create tests/integration/autopilot/ directory structure with utilities for creating temporary git repositories, setting up isolated file systems, and managing test data cleanup. Implement TestEnvironment class with methods for repository creation, task data injection, and environment teardown. Include support for both Jest and Vitest test runners with shared configuration.", + "status": "pending", + "testStrategy": "Unit tests for environment setup/teardown, fixture creation, and isolation verification. Test that environments don't interfere with each other when run in parallel." + }, + { + "id": 2, + "title": "Implement mock integrations and external service stubs", + "description": "Create comprehensive mocks for GitHub API, git operations, and external dependencies to enable controlled testing of autopilot workflows", + "dependencies": [1], + "details": "Develop mock implementations for GitHub API responses (PR creation, status checks), git command execution, and external tool integrations. Create MockGitHubService and MockGitService classes that can simulate various scenarios including API failures, network timeouts, and authentication errors. Include configurable response patterns for different test scenarios.", + "status": "pending", + "testStrategy": "Test mock accuracy against real API responses, verify error scenario simulation, and ensure mocks can be configured for different test cases without conflicts." + }, + { + "id": 3, + "title": "Create end-to-end workflow test scenarios", + "description": "Implement comprehensive test cases covering complete autopilot workflows including success paths, error scenarios, and edge cases", + "dependencies": [1, 2], + "details": "Develop test suites for complete red-green-commit cycles, error recovery scenarios, resume functionality after interruption, and PR creation workflows. Include tests for various project types, different task complexities, and failure modes (test failures, build errors, git conflicts). Create scenario-based test data with realistic task structures and dependencies.", + "status": "pending", + "testStrategy": "Integration tests that verify complete workflow execution, error handling paths, and state persistence across resume operations. Performance validation for different project sizes and task counts." + }, + { + "id": 4, + "title": "Implement performance benchmarking and resource monitoring", + "description": "Create performance testing infrastructure to measure autopilot workflow execution times, memory usage, and resource consumption", + "dependencies": [3], + "details": "Develop performance testing harness that measures workflow execution times, memory consumption, CPU usage, and git repository size impacts. Create benchmarking utilities that can test across different project sizes (small, medium, large codebases) and task counts. Include regression testing to detect performance degradation and resource leak detection.", + "status": "pending", + "testStrategy": "Performance tests with baseline measurements, resource usage monitoring, and regression detection. Validate that workflows complete within acceptable time limits for different project scales." + }, + { + "id": 5, + "title": "Develop test isolation and parallelization strategies", + "description": "Implement robust test isolation mechanisms to ensure autopilot integration tests can run safely in parallel without interference", + "dependencies": [1, 2], + "details": "Create test isolation strategies using unique temporary directories, port management for local services, and process isolation for concurrent test execution. Implement test cleanup mechanisms that prevent state leakage between tests. Include parallel execution support for both Jest and Vitest with proper resource management and conflict prevention.", + "status": "pending", + "testStrategy": "Concurrency tests to verify isolation effectiveness, cleanup verification, and resource contention prevention. Test that parallel execution produces consistent results without race conditions." + }, + { + "id": 6, + "title": "Create comprehensive result validation and reporting", + "description": "Implement validation mechanisms to verify autopilot workflow results and create detailed test reporting for integration test outcomes", + "dependencies": [3, 4, 5], + "details": "Develop result validation utilities that verify git history correctness, PR content accuracy, task completion status, and workflow state consistency. Create comprehensive test reporting that includes performance metrics, resource usage statistics, and detailed failure analysis. Include visual diff tools for comparing expected vs actual workflow outcomes.", + "status": "pending", + "testStrategy": "Validation tests for result verification accuracy, reporting completeness, and failure diagnosis capabilities. Test that validation can detect subtle workflow inconsistencies and state corruption." + } + ] + }, + { + "id": 53, + "title": "Finalize autopilot documentation and examples", + "description": "Create comprehensive documentation for autopilot workflow with examples and troubleshooting", + "details": "Create detailed documentation covering autopilot setup, configuration options, workflow phases, error handling, and best practices. Include example PRD files that demonstrate autopilot-compatible task structure. Add troubleshooting guide for common issues (git conflicts, test failures, dependency problems). Create demo video or GIF showing complete workflow execution.", + "testStrategy": "Documentation validation through user testing, example verification by running actual autopilot workflows, link checking and format validation. Accessibility and clarity review for technical documentation.", + "priority": "low", + "dependencies": [52], + "status": "pending", + "subtasks": [ + { + "id": 1, + "title": "Create comprehensive autopilot documentation", + "description": "Write detailed documentation covering autopilot setup, configuration options, workflow phases, error handling, and best practices", + "dependencies": [], + "details": "Create main autopilot documentation file covering installation, setup process, configuration options (flags like --dry-run, --no-push, etc.), workflow phases explanation, error handling strategies, and best practices for autonomous TDD workflows. Include API documentation for WorkflowOrchestrator and integration points.", + "status": "pending", + "testStrategy": "Documentation validation through technical review, link checking, format validation, and clarity assessment for technical accuracy" + }, + { + "id": 2, + "title": "Create example PRD files and templates", + "description": "Develop example PRD files that demonstrate autopilot-compatible task structure and workflow patterns", + "dependencies": [1], + "details": "Create example PRD files in .taskmaster/docs/examples/ showing different project types (web app, API, CLI tool) with autopilot-compatible task structures. Include templates for common scenarios and best practices for task decomposition that works well with autonomous workflows.", + "status": "pending", + "testStrategy": "Example verification by running actual autopilot workflows against the example PRDs to ensure they execute successfully and produce expected results" + }, + { + "id": 3, + "title": "Write troubleshooting guide", + "description": "Create comprehensive troubleshooting guide for common autopilot workflow issues and their solutions", + "dependencies": [1], + "details": "Document common issues like git conflicts during autonomous commits, test failures in red-green-refactor cycles, dependency resolution problems, workflow resume failures, and GitHub API rate limiting. Include step-by-step resolution guides, preventive measures, and debugging techniques.", + "status": "pending", + "testStrategy": "Troubleshooting guide validation by testing resolution steps against actual error scenarios and user feedback collection" + }, + { + "id": 4, + "title": "Create demo materials and workflow visualization", + "description": "Produce demo video or GIF showing complete autopilot workflow execution from start to finish", + "dependencies": [2, 3], + "details": "Create visual demo materials showing complete autopilot workflow: PRD parsing, task expansion, autonomous TDD cycles, git operations, test execution, and PR creation. Include screen recordings or animated GIFs demonstrating successful workflow execution and key decision points.", + "status": "pending", + "testStrategy": "Demo material validation through user testing sessions, accessibility review for visual content, and verification that examples accurately represent actual workflow behavior" + } + ] + } + ], + "metadata": { + "created": "2025-09-30T13:32:28.649Z", + "updated": "2025-10-06T17:44:07.207Z", + "description": "Tasks for autonomous-tdd-git-workflow context" + } + }, + "tdd-workflow-phase-0": { + "tasks": [ + { + "id": 1, + "title": "Create autopilot command CLI skeleton", + "description": "Create the basic CLI command structure for tm autopilot with dry-run mode support and argument parsing", + "details": "Create apps/cli/src/commands/autopilot.command.ts extending Commander's Command class pattern like existing commands. Support tm autopilot <taskId> with --dry-run flag. Include basic help text, argument validation, and command registration. Follow the existing StartCommand pattern for consistency with the codebase.", + "testStrategy": "Unit tests for command parsing, argument validation, and help text display. Test both with and without task ID argument.", + "priority": "high", + "dependencies": [], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create autopilot command file structure", + "description": "Create the basic autopilot.command.ts file with Commander class extension and basic structure", + "dependencies": [], + "details": "Create apps/cli/src/commands/autopilot.command.ts extending Commander's Command class. Set up basic class structure with constructor, command name 'autopilot', description, and empty execute method. Follow the pattern used in existing commands like StartCommand for consistency.", + "status": "done", + "testStrategy": "Unit tests for command instantiation and basic structure validation" + }, + { + "id": 2, + "title": "Implement argument parsing and validation", + "description": "Add task ID argument parsing and --dry-run flag support with validation", + "dependencies": [1], + "details": "Add task ID as required positional argument with validation to ensure it exists in tasks.json. Implement --dry-run boolean flag with proper Commander.js syntax. Add argument validation logic to check task ID format and existence. Include error handling for invalid inputs.", + "status": "done", + "testStrategy": "Unit tests for argument parsing with valid/invalid task IDs and flag combinations" + }, + { + "id": 3, + "title": "Add help text and command documentation", + "description": "Implement comprehensive help text, usage examples, and command documentation", + "dependencies": [2], + "details": "Add detailed command description, usage examples showing 'tm autopilot <taskId>' and 'tm autopilot <taskId> --dry-run'. Include examples with real task IDs, explain dry-run mode behavior, and provide troubleshooting tips. Follow help text patterns from existing commands.", + "status": "done", + "testStrategy": "Unit tests for help text display and content validation" + }, + { + "id": 4, + "title": "Implement command registration system", + "description": "Register the autopilot command with the CLI application and ensure proper integration", + "dependencies": [3], + "details": "Add autopilot command registration to the main CLI application in apps/cli/src/index.ts or appropriate registration file. Ensure command is properly exported and available when running 'tm autopilot'. Follow existing command registration patterns used by other commands.", + "status": "done", + "testStrategy": "Integration tests for command registration and CLI availability" + }, + { + "id": 5, + "title": "Create basic execute method with dry-run logic", + "description": "Implement the main execute method with basic dry-run mode and task loading logic", + "dependencies": [4], + "details": "Implement the execute method that loads the specified task from tasks.json, validates task existence, and handles dry-run mode by displaying what would be executed without performing actions. Add basic task loading using existing task utilities and prepare structure for future autopilot logic.", + "status": "done", + "testStrategy": "Unit tests for execute method with valid tasks and dry-run mode behavior" + } + ] + }, + { + "id": 2, + "title": "Implement preflight detection system", + "description": "Build system to detect test runner, git state, and validate required tools for autopilot execution", + "details": "Create a PreflightChecker class that: 1) Detects test command from package.json scripts.test field, 2) Checks git working tree status using existing git-utils.js, 3) Validates availability of git, gh, node/npm tools, 4) Detects default branch. Return structured status for each check with success/failure indicators.", + "testStrategy": "Unit tests for each detection method. Integration tests with different package.json configurations and git states.", + "priority": "high", + "dependencies": [1], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create PreflightChecker class structure and package.json test detection", + "description": "Implement the core PreflightChecker class with method to detect test command from package.json scripts.test field", + "dependencies": [], + "details": "Create src/autopilot/preflight-checker.js with PreflightChecker class. Implement detectTestCommand() method that reads package.json and extracts scripts.test field. Handle cases where package.json doesn't exist or scripts.test is undefined. Return structured result with success/failure status and detected command. Follow existing patterns from other service classes in the codebase.", + "status": "done", + "testStrategy": "Unit tests for detectTestCommand() with various package.json configurations: missing file, missing scripts, missing test script, valid test script. Mock fs.readFileSync for different scenarios." + }, + { + "id": 2, + "title": "Implement git state validation using existing git-utils", + "description": "Add git working tree status checks using the existing git-utils.js module functions", + "dependencies": [1], + "details": "Add checkGitWorkingTree() method to PreflightChecker that uses existing functions from scripts/modules/utils/git-utils.js. Use isGitRepository() to verify git repo, then check for uncommitted changes using git status. Return structured status indicating if working tree is clean, has staged changes, or has unstaged changes. Include helpful messages about what needs to be committed.", + "status": "done", + "testStrategy": "Unit tests with mocked git-utils functions for different git states: clean working tree, staged changes, unstaged changes, not a git repository. Integration tests with actual git repository setup." + }, + { + "id": 3, + "title": "Add tool availability validation for required commands", + "description": "Implement validation for git, gh, node/npm tool availability on the system", + "dependencies": [2], + "details": "Add validateRequiredTools() method that checks availability of git, gh CLI, node, and npm commands using execSync with 'which' or 'where' depending on platform. Handle platform differences (Unix vs Windows). Return structured results for each tool with version information where available. Use existing isGhCliAvailable() function from git-utils for gh CLI checking.", + "status": "done", + "testStrategy": "Unit tests mocking execSync for different scenarios: all tools available, missing tools, platform differences. Test version detection and error handling for command execution failures." + }, + { + "id": 4, + "title": "Implement default branch detection", + "description": "Add default branch detection using existing git-utils functions", + "dependencies": [3], + "details": "Add detectDefaultBranch() method that uses getDefaultBranch() function from existing git-utils.js. Handle cases where default branch cannot be determined and provide fallback logic. Return structured result with detected branch name and confidence level. Include handling for repositories without remote tracking.", + "status": "done", + "testStrategy": "Unit tests mocking git-utils getDefaultBranch() for various scenarios: GitHub repo with default branch, local repo without remote, repositories with different default branches (main vs master)." + }, + { + "id": 5, + "title": "Create comprehensive preflight check orchestration and result formatting", + "description": "Implement main runAllChecks() method that orchestrates all preflight checks and formats results", + "dependencies": [4], + "details": "Add runAllChecks() method that executes all preflight checks in sequence: detectTestCommand(), checkGitWorkingTree(), validateRequiredTools(), detectDefaultBranch(). Collect all results into structured PreflightResult object with overall success status, individual check results, and actionable error messages. Include summary of what passed/failed and next steps for resolving issues.", + "status": "done", + "testStrategy": "Integration tests running full preflight checks in different project configurations. Test error aggregation and result formatting. Verify that partial failures are handled gracefully with appropriate user guidance." + } + ] + }, + { + "id": 3, + "title": "Implement task loading and validation", + "description": "Load task data from TaskMaster state and validate task structure for autopilot execution", + "details": "Use existing TaskService from @tm/core to load task by ID. Validate task exists, has subtasks, and subtasks have proper dependency order. If no subtasks exist, provide helpful message about needing to expand first. Reuse existing task loading patterns from other commands.", + "testStrategy": "Unit tests for task loading with various scenarios: valid task with subtasks, task without subtasks, non-existent task, tasks with dependencies.", + "priority": "medium", + "dependencies": [1], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create TaskLoadingService class with TaskService integration", + "description": "Create a service class that wraps TaskService from @tm/core to load task data and handle initialization properly", + "dependencies": [], + "details": "Create TaskLoadingService that instantiates TaskService with ConfigManager, handles initialization, and provides methods for loading tasks by ID. Include proper error handling for cases where TaskService fails to initialize or tasks cannot be loaded. Follow existing patterns from tm-core for service instantiation.", + "status": "done", + "testStrategy": "Unit tests for service initialization, task loading success cases, and error handling for initialization failures" + }, + { + "id": 2, + "title": "Implement task existence validation logic", + "description": "Build validation to check if a task exists and has the proper structure for autopilot execution", + "dependencies": [1], + "details": "Create validation functions that check: 1) Task exists in TaskMaster state, 2) Task has valid structure according to Task interface from @tm/core types, 3) Task is not in 'done' or 'cancelled' status. Return structured validation results with specific error messages for each validation failure.", + "status": "done", + "testStrategy": "Unit tests with various task scenarios: valid tasks, non-existent tasks, malformed tasks, completed tasks" + }, + { + "id": 3, + "title": "Create subtask validation and dependency analysis", + "description": "Implement validation for subtask structure and dependency ordering for autopilot execution readiness", + "dependencies": [2], + "details": "Build validation logic that: 1) Checks if task has subtasks defined, 2) Validates subtask structure matches Subtask interface, 3) Analyzes dependency order to ensure subtasks can be executed sequentially, 4) Identifies any circular dependencies or missing dependencies. Provide detailed feedback on dependency issues.", + "status": "done", + "testStrategy": "Unit tests for tasks with valid subtasks, tasks without subtasks, tasks with circular dependencies, and tasks with missing dependencies" + }, + { + "id": 4, + "title": "Implement helpful expansion messaging system", + "description": "Create user-friendly messages when tasks lack subtasks and guide users toward expansion commands", + "dependencies": [3], + "details": "When validation detects tasks without subtasks, provide helpful messages explaining: 1) Why subtasks are needed for autopilot, 2) How to use 'task-master expand' to create subtasks, 3) Link to existing task expansion patterns from other commands. Include suggestions for complexity analysis if the task appears complex.", + "status": "done", + "testStrategy": "Unit tests for message generation with different task scenarios and integration tests to verify messaging appears correctly" + }, + { + "id": 5, + "title": "Create comprehensive validation result interface and error handling", + "description": "Design and implement structured validation results with detailed error reporting for all validation scenarios", + "dependencies": [4], + "details": "Create ValidationResult interface that includes: 1) Success/failure status, 2) Specific error types (task not found, no subtasks, dependency issues), 3) Detailed error messages with actionable guidance, 4) Task data when validation succeeds. Implement error handling that provides clear feedback for each validation failure scenario.", + "status": "done", + "testStrategy": "Unit tests for all validation result scenarios and integration tests to verify error handling provides helpful user feedback" + } + ] + }, + { + "id": 4, + "title": "Create execution plan display logic", + "description": "Build comprehensive display system for showing planned autopilot execution steps in dry-run mode", + "details": "Create ExecutionPlanDisplay class that formats and displays: preflight check results, branch/tag information, subtask execution order with RED/GREEN/COMMIT phases, estimated duration, and finalization steps. Use boxen and chalk for consistent CLI styling matching existing command patterns.", + "testStrategy": "Unit tests for display formatting with different task configurations. Visual regression tests for output formatting.", + "priority": "medium", + "dependencies": [2, 3], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create ExecutionPlanDisplay class structure", + "description": "Create the base ExecutionPlanDisplay class with proper imports and basic structure following existing CLI patterns", + "dependencies": [], + "details": "Create src/display/ExecutionPlanDisplay.ts with class structure. Import boxen and chalk libraries. Set up constructor to accept execution plan data. Define private methods for each display section. Follow existing CLI styling patterns from other commands.", + "status": "done", + "testStrategy": "Unit tests for class instantiation and basic structure validation" + }, + { + "id": 2, + "title": "Implement preflight check results display", + "description": "Add method to format and display preflight check results with appropriate styling and status indicators", + "dependencies": [1], + "details": "Implement displayPreflightChecks() method that formats check results using chalk for colored status indicators (green checkmarks, red X's). Use boxen for section containers. Display task validation, dependency checks, and branch status results.", + "status": "done", + "testStrategy": "Unit tests for preflight display formatting with passing and failing checks" + }, + { + "id": 3, + "title": "Implement branch and tag information display", + "description": "Add method to display planned branch creation and tag information in a formatted section", + "dependencies": [1], + "details": "Implement displayBranchInfo() method that shows planned branch name, current tag, and branch creation strategy. Use consistent styling with other sections. Display branch existence warnings if applicable.", + "status": "done", + "testStrategy": "Unit tests for branch info display with different tag configurations" + }, + { + "id": 4, + "title": "Implement subtask execution order display with phases", + "description": "Create comprehensive display for subtask execution order showing RED/GREEN/COMMIT phases for each subtask", + "dependencies": [1], + "details": "Implement displayExecutionOrder() method that shows ordered subtasks with phase indicators (RED: tests fail, GREEN: tests pass, COMMIT: changes committed). Use color coding and clear phase separation. Include dependency information and estimated duration per subtask.", + "status": "done", + "testStrategy": "Unit tests for execution order display with various dependency patterns and phase transitions" + }, + { + "id": 5, + "title": "Implement main display method and finalization steps", + "description": "Create the main display() method that orchestrates all sections and shows finalization steps", + "dependencies": [2, 3, 4], + "details": "Implement main display() method that calls all section display methods in proper order. Add displayFinalizationSteps() for PR creation and cleanup steps. Include estimated total duration and final summary. Ensure consistent spacing and styling throughout.", + "status": "done", + "testStrategy": "Integration tests for complete display output and visual regression tests for CLI formatting" + } + ] + }, + { + "id": 5, + "title": "Implement branch and tag planning", + "description": "Generate branch names and tag settings that would be used during autopilot execution", + "details": "Create BranchPlanner class that generates branch names following pattern: <tag>/task-<id>-<slug> where slug is kebab-case task title. Determine active tag from TaskMaster config. Show which tag would be set during execution. Handle edge cases like existing branches.", + "testStrategy": "Unit tests for branch name generation with various task titles and tags. Test edge cases like special characters and long titles.", + "priority": "low", + "dependencies": [2], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create BranchPlanner class with basic structure", + "description": "Create the BranchPlanner class with constructor and main method signatures to establish the foundation for branch name generation", + "dependencies": [], + "details": "Create src/commands/autopilot/branch-planner.js with BranchPlanner class. Include constructor that accepts TaskMaster config and task data. Define main method planBranch() that will return branch name and tag information. Set up basic error handling structure.", + "status": "done", + "testStrategy": "Unit tests for class instantiation and basic method structure" + }, + { + "id": 2, + "title": "Implement task title to kebab-case slug conversion", + "description": "Create utility function to convert task titles into kebab-case slugs suitable for branch names", + "dependencies": [1], + "details": "Implement convertToKebabCase() method that handles special characters, spaces, and length limits. Remove non-alphanumeric characters except hyphens, convert to lowercase, and truncate to reasonable length (e.g., 50 chars). Handle edge cases like empty titles or titles with only special characters.", + "status": "done", + "testStrategy": "Unit tests with various task title formats including special characters, long titles, and edge cases" + }, + { + "id": 3, + "title": "Implement branch name generation logic", + "description": "Create the core logic to generate branch names following the pattern <tag>/task-<id>-<slug>", + "dependencies": [2], + "details": "Implement generateBranchName() method that combines active tag from TaskMaster config, task ID, and kebab-case slug. Format as 'tag/task-id-slug'. Handle cases where tag is undefined or empty by using 'feature' as default. Validate generated names against git branch naming rules.", + "status": "done", + "testStrategy": "Unit tests for branch name generation with different tags, task IDs, and slugs" + }, + { + "id": 4, + "title": "Add active tag detection from TaskMaster config", + "description": "Implement logic to determine the active tag from TaskMaster configuration state", + "dependencies": [1], + "details": "Create getActiveTag() method that reads from TaskMaster state.json to determine current active tag. Use existing TaskService patterns to access configuration. Handle cases where no tag is set or config is missing. Return default tag 'feature' when no active tag is configured.", + "status": "done", + "testStrategy": "Unit tests for tag detection with various config states including missing config and undefined tags" + }, + { + "id": 5, + "title": "Implement existing branch conflict detection", + "description": "Add logic to detect and handle cases where generated branch names already exist", + "dependencies": [3, 4], + "details": "Create checkBranchExists() method using git-utils.js to check if generated branch name already exists locally or remotely. Implement conflict resolution by appending incremental numbers (e.g., -2, -3) to branch name. Provide warnings about existing branches in planning output.", + "status": "done", + "testStrategy": "Unit tests for branch conflict detection and resolution with mocked git commands" + } + ] + }, + { + "id": 6, + "title": "Create subtask execution order calculation", + "description": "Calculate the correct execution order for subtasks based on their dependencies", + "details": "Implement dependency resolution algorithm that: 1) Reads subtask dependencies from task data, 2) Creates execution order respecting dependencies, 3) Detects circular dependencies, 4) Groups independent subtasks. Return ordered list with dependency information for display.", + "testStrategy": "Unit tests for dependency resolution with linear dependencies, parallel subtasks, and circular dependency detection.", + "priority": "medium", + "dependencies": [3], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create DependencyResolver class with core algorithm", + "description": "Implement the core dependency resolution algorithm that reads subtask dependencies and creates execution order", + "dependencies": [], + "details": "Create src/utils/dependency-resolver.ts with DependencyResolver class. Implement topological sort algorithm that takes subtasks array and returns ordered execution plan. Handle basic dependency chains and ensure tasks without dependencies can execute first. Include proper TypeScript interfaces for subtask data and execution order results.", + "status": "done", + "testStrategy": "Unit tests for basic dependency resolution with linear chains, parallel independent subtasks, and empty dependency arrays" + }, + { + "id": 2, + "title": "Implement circular dependency detection", + "description": "Add circular dependency detection to prevent infinite loops in dependency resolution", + "dependencies": [1], + "details": "Extend DependencyResolver to detect circular dependencies using depth-first search with visit tracking. Throw descriptive error when circular dependencies are found, including the cycle path. Add validation before attempting topological sort to fail fast on invalid dependency graphs.", + "status": "done", + "testStrategy": "Unit tests for various circular dependency scenarios: direct cycles (A→B→A), indirect cycles (A→B→C→A), and self-dependencies (A→A)" + }, + { + "id": 3, + "title": "Add parallel subtask grouping functionality", + "description": "Group independent subtasks that can be executed in parallel phases", + "dependencies": [1], + "details": "Implement parallelization logic that groups subtasks into execution phases. Subtasks with no dependencies or whose dependencies are satisfied in previous phases can execute in parallel. Return execution plan with phase information showing which subtasks can run simultaneously.", + "status": "done", + "testStrategy": "Unit tests for parallel grouping with mixed dependency patterns, ensuring correct phase separation and parallel group identification" + }, + { + "id": 4, + "title": "Create execution order display interfaces", + "description": "Define TypeScript interfaces for execution order results and dependency information", + "dependencies": [1], + "details": "Create interfaces in types/ directory for ExecutionPlan, ExecutionPhase, and SubtaskDependencyInfo. Include fields for subtask ID, dependencies status, execution phase, and parallel group information. Ensure interfaces support both display formatting and autopilot execution needs.", + "status": "done", + "testStrategy": "Type checking tests and interface validation with sample data structures matching real subtask scenarios" + }, + { + "id": 5, + "title": "Integrate with TaskService and add CLI utilities", + "description": "Integrate DependencyResolver with existing TaskService and create utility functions for CLI display", + "dependencies": [1, 2, 3, 4], + "details": "Add calculateSubtaskExecutionOrder method to TaskService that uses DependencyResolver. Create UI utilities in apps/cli/src/utils/ui.ts for formatting execution order display with chalk colors and dependency status indicators. Follow existing patterns for task loading and error handling.", + "status": "done", + "testStrategy": "Integration tests with TaskService loading real task data, and visual tests for CLI display formatting with various dependency scenarios" + } + ] + }, + { + "id": 7, + "title": "Implement TDD phase planning for subtasks", + "description": "Plan RED/GREEN/COMMIT phases for each subtask showing test files and implementation files", + "details": "Create TDDPhasePlanner that for each subtask: 1) Determines test file paths based on project structure, 2) Identifies implementation files from subtask details, 3) Generates commit messages following conventional commits format, 4) Estimates implementation complexity. Support common project structures (src/, tests/, __tests__).", + "testStrategy": "Unit tests for file path generation with different project structures. Test commit message generation following conventional commits.", + "priority": "medium", + "dependencies": [6], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create TDDPhasePlanner class structure", + "description": "Design and implement the core TDDPhasePlanner class that handles phase planning for TDD workflow execution", + "dependencies": [], + "details": "Create a new TDDPhasePlanner class in src/tdd/ directory with methods for: 1) Analyzing subtask data to extract implementation files, 2) Determining appropriate test file paths based on project structure, 3) Generating conventional commit messages, 4) Estimating complexity. The class should accept subtask data and project configuration as inputs.", + "status": "done", + "testStrategy": "Unit tests for class instantiation and method existence. Mock subtask data to verify the class can be initialized properly." + }, + { + "id": 2, + "title": "Implement test file path detection logic", + "description": "Build logic to detect and generate appropriate test file paths based on common project structures", + "dependencies": [1], + "details": "Implement method to analyze project structure and determine test file paths for implementation files. Support common patterns: src/ with tests/, src/ with __tests__/, src/ with .test.js files alongside source, and packages/*/src with packages/*/tests. Use filesystem operations to check existing patterns and generate consistent test file paths.", + "status": "done", + "testStrategy": "Unit tests with mock filesystem structures testing various project layouts (Jest, Vitest, Node.js patterns). Verify correct test file paths are generated for different source file locations." + }, + { + "id": 3, + "title": "Implement implementation file extraction from subtask details", + "description": "Parse subtask details and descriptions to identify implementation files that will be created or modified", + "dependencies": [1], + "details": "Create method to analyze subtask details text and extract file paths mentioned or implied. Use regex patterns and natural language processing to identify: file names mentioned directly, directory structures implied by descriptions, and component/class names that translate to file paths. Handle various file extensions (.js, .ts, .tsx, .vue, .py, etc.).", + "status": "done", + "testStrategy": "Unit tests with various subtask detail examples. Test extraction accuracy with different description formats and file types. Verify edge cases like missing file paths or ambiguous descriptions." + }, + { + "id": 4, + "title": "Implement conventional commit message generation", + "description": "Generate properly formatted conventional commit messages for each TDD phase (RED/GREEN/COMMIT)", + "dependencies": [1], + "details": "Implement method to generate conventional commit messages following the format: type(scope): description. Support commit types: test (for RED phase), feat/fix (for GREEN phase), and refactor (for COMMIT phase). Extract scope from subtask context and generate descriptive messages. Follow the pattern seen in existing hooks: feat(task-<id>): <description>.", + "status": "done", + "testStrategy": "Unit tests for commit message generation with various subtask types and scopes. Verify conventional commit format compliance and message clarity." + }, + { + "id": 5, + "title": "Implement complexity estimation and phase organization", + "description": "Estimate implementation complexity and organize the three TDD phases (RED/GREEN/COMMIT) with appropriate metadata", + "dependencies": [2, 3, 4], + "details": "Create method to estimate implementation complexity based on: number of files involved, description length and complexity keywords, dependencies between subtasks. Organize the output into three distinct phases: RED (test creation), GREEN (implementation), COMMIT (cleanup/refactor). Include estimated time, file lists, and commit messages for each phase.", + "status": "done", + "testStrategy": "Unit tests for complexity calculation with various subtask scenarios. Integration tests verifying complete phase planning output includes all required metadata and follows TDD workflow structure." + } + ] + }, + { + "id": 8, + "title": "Add finalization steps planning", + "description": "Plan the final steps that would be executed after all subtasks complete", + "details": "Create FinalizationPlanner that shows: 1) Full test suite execution with coverage threshold detection, 2) Branch push confirmation, 3) PR creation targeting detected default branch, 4) Duration estimation based on subtask count and complexity. Use existing git-utils.js for git operations planning.", + "testStrategy": "Unit tests for finalization step generation. Test coverage threshold detection from package.json or config files.", + "priority": "low", + "dependencies": [2, 6], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create FinalizationPlanner class structure", + "description": "Create the basic FinalizationPlanner class with interface definition and core methods for planning finalization steps", + "dependencies": [], + "details": "Create a new FinalizationPlanner class that will handle planning final steps after subtask completion. Include interface definitions for finalization steps (test execution, branch push, PR creation, duration estimation) and basic class structure with methods for each planning component. The class should accept subtask data and project context as input.", + "status": "done", + "testStrategy": "Unit tests for class instantiation and method structure. Test interface definitions and basic method signatures." + }, + { + "id": 2, + "title": "Implement test suite execution planning with coverage detection", + "description": "Add functionality to detect test commands and coverage thresholds from package.json and project configuration", + "dependencies": [1], + "details": "Implement test command detection by reading package.json scripts for 'test', 'test:coverage', 'test:ci' commands. Parse coverage configuration from package.json, jest.config.js, vitest.config.js, or other config files to detect coverage thresholds. Generate execution plan showing which test command would be run and expected coverage requirements. Handle projects without test commands gracefully.", + "status": "done", + "testStrategy": "Unit tests for package.json parsing, config file detection, and coverage threshold extraction. Test with various project structures (Jest, Vitest, no tests)." + }, + { + "id": 3, + "title": "Add git operations planning using existing git-utils", + "description": "Integrate with git-utils.js to plan branch push confirmation and default branch detection for PR targeting", + "dependencies": [1], + "details": "Use existing git-utils.js functions like getDefaultBranch(), getCurrentBranch(), and isGhCliAvailable() to plan git operations. Generate branch push confirmation plan showing current branch and target remote. Detect default branch for PR creation planning. Check if gh CLI is available for PR operations. Plan git status checks and dirty working tree warnings.", + "status": "done", + "testStrategy": "Unit tests for git-utils integration, branch detection, and gh CLI availability checking. Mock git-utils functions to test various git states." + }, + { + "id": 4, + "title": "Implement PR creation planning", + "description": "Plan PR creation steps including title generation, body template, and target branch detection", + "dependencies": [3], + "details": "Generate PR creation plan that includes conventional commit-style title generation based on subtask changes, PR body template with task/subtask references, target branch detection using git-utils, and gh CLI command planning. Include checks for existing PR detection and conflict resolution. Plan PR description formatting with task context and implementation notes.", + "status": "done", + "testStrategy": "Unit tests for PR title generation, body template creation, and gh CLI command planning. Test with various task types and subtask combinations." + }, + { + "id": 5, + "title": "Add duration estimation based on subtask complexity", + "description": "Implement duration estimation algorithm that calculates expected completion time based on subtask count and complexity", + "dependencies": [2, 3, 4], + "details": "Create duration estimation algorithm that factors in number of subtasks, complexity indicators (file count, test coverage requirements, git operations), and historical patterns. Provide time estimates for each finalization step (testing, git operations, PR creation) and total completion time. Include confidence intervals and factors that might affect duration. Format estimates in human-readable time units.", + "status": "done", + "testStrategy": "Unit tests for duration calculation algorithms, complexity analysis, and time formatting. Test with various subtask configurations and complexity scenarios." + } + ] + }, + { + "id": 9, + "title": "Integrate command with existing CLI infrastructure", + "description": "Register autopilot command with the main CLI and ensure proper integration with command registry", + "details": "Update apps/cli/src/command-registry.ts to include AutopilotCommand. Follow existing patterns for command registration. Ensure proper cleanup and error handling. Update CLI help system to include autopilot command documentation.", + "testStrategy": "Integration tests for command registration. Test CLI help includes autopilot command. Test error handling and cleanup.", + "priority": "medium", + "dependencies": [1, 4], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create AutopilotCommand class file", + "description": "Create the AutopilotCommand class file following the existing command pattern used by StartCommand", + "dependencies": [], + "details": "Create apps/cli/src/commands/autopilot.command.ts with basic class structure extending Commander's Command class. Include constructor, command configuration, and placeholder action method. Follow the exact pattern from StartCommand including imports, error handling, and class structure.", + "status": "done", + "testStrategy": "Unit tests for class instantiation and basic command configuration" + }, + { + "id": 2, + "title": "Add AutopilotCommand import to command-registry.ts", + "description": "Import the AutopilotCommand class in the command registry imports section", + "dependencies": [1], + "details": "Add import statement for AutopilotCommand in apps/cli/src/command-registry.ts following the existing import pattern. Place it in the appropriate position with other command imports maintaining alphabetical order.", + "status": "done", + "testStrategy": "Verify import resolves correctly and doesn't break existing imports" + }, + { + "id": 3, + "title": "Register autopilot command in CommandRegistry", + "description": "Add autopilot command metadata to the CommandRegistry.commands array", + "dependencies": [2], + "details": "Add autopilot command entry to the commands array in CommandRegistry class. Use 'development' category, provide appropriate description, and reference AutopilotCommand class. Follow the existing pattern with name, description, commandClass, and category fields.", + "status": "done", + "testStrategy": "Unit tests to verify command is registered and appears in registry listing" + }, + { + "id": 4, + "title": "Export AutopilotCommand from index.ts", + "description": "Add AutopilotCommand export to the main CLI package index file", + "dependencies": [1], + "details": "Add AutopilotCommand export to apps/cli/src/index.ts in the Commands section following the existing export pattern. Ensure it's properly exported for external usage and testing.", + "status": "done", + "testStrategy": "Verify export is available and can be imported from @tm/cli package" + }, + { + "id": 5, + "title": "Update CLI help system for autopilot command", + "description": "Ensure autopilot command appears in help output and command listing", + "dependencies": [3], + "details": "Verify that the autopilot command appears in the CLI help system through the CommandRegistry.getFormattedCommandList() method. The 'development' category should be included in help output with autopilot command listed. Test help display functionality.", + "status": "done", + "testStrategy": "Integration tests for help system displaying autopilot command. Test CLI help output includes autopilot in development category." + } + ] + }, + { + "id": 10, + "title": "Add comprehensive error handling and edge cases", + "description": "Implement robust error handling for all failure scenarios and edge cases in autopilot dry-run", + "details": "Handle scenarios: missing task, task without subtasks, dirty git working tree, missing tools (git, gh, node), invalid dependencies, circular dependencies, no test command detected. Provide helpful error messages and suggestions for resolution. Use existing error patterns from other commands.", + "testStrategy": "Unit tests for each error scenario. Integration tests with various invalid configurations. Test error message clarity and helpfulness.", + "priority": "high", + "dependencies": [1, 2, 3], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Implement git and working tree validation", + "description": "Add error handling for git repository validation, dirty working tree detection, and missing git tool", + "dependencies": [], + "details": "Create validation functions to check if current directory is a git repository, detect dirty working tree status using git-utils.js patterns, and verify git CLI tool availability. Return specific error messages for each failure case with helpful suggestions for resolution.", + "status": "done", + "testStrategy": "Unit tests for git validation with mocked git states. Test clean/dirty working tree detection and missing git tool scenarios." + }, + { + "id": 2, + "title": "Add task and dependency validation error handling", + "description": "Implement error detection for missing tasks, tasks without subtasks, invalid dependencies, and circular dependency cycles", + "dependencies": [1], + "details": "Create TaskValidator class that checks task existence in tasks.json, validates task has subtasks for autopilot execution, detects invalid dependency references, and identifies circular dependency chains. Use existing dependency validation patterns from other commands.", + "status": "done", + "testStrategy": "Unit tests for each validation scenario with mock task data. Test circular dependency detection with various dependency graphs." + }, + { + "id": 3, + "title": "Implement tool availability validation", + "description": "Add error handling for missing required tools like gh CLI, node, and npm with helpful installation guidance", + "dependencies": [1], + "details": "Create ToolValidator that checks availability of gh CLI, node, npm, and other required tools using spawn or which-like detection. Provide specific error messages with installation instructions for each missing tool based on the user's platform.", + "status": "done", + "testStrategy": "Unit tests for tool detection with mocked command availability. Test error messages for different missing tool combinations." + }, + { + "id": 4, + "title": "Add test command detection error handling", + "description": "Implement error handling when no test command is detected in package.json with suggestions for configuration", + "dependencies": [2], + "details": "Extend PreflightChecker to handle cases where package.json is missing, has no scripts section, or no test script defined. Provide helpful error messages suggesting common test script configurations and link to documentation for test setup.", + "status": "done", + "testStrategy": "Unit tests for various package.json configurations. Test error messages for missing test scripts and invalid package.json files." + }, + { + "id": 5, + "title": "Create comprehensive error reporting system", + "description": "Build unified error reporting that aggregates all validation failures and provides actionable resolution steps", + "dependencies": [1, 2, 3, 4], + "details": "Create ErrorReporter class that collects all validation errors, formats them with clear descriptions and resolution steps, and provides a summary of required actions before autopilot can run. Follow existing error formatting patterns from other TaskMaster commands.", + "status": "done", + "testStrategy": "Integration tests combining multiple error scenarios. Test error message clarity and formatting. Verify all error types are properly handled and reported." + } + ] + } + ], + "metadata": { + "created": "2025-10-07T14:08:52.047Z", + "updated": "2025-10-07T15:23:43.279Z", + "description": "Tasks for tdd-workflow-phase-0 context" + } + }, + "tdd-phase-1-core-rails": { + "tasks": [ + { + "id": 1, + "title": "Design and Implement Global Storage System", + "description": "Create a global storage system for runtime state, logs, and artifacts outside the project directory, following the specified directory structure and normalization logic.", + "details": "Implement a Node.js module that manages all runtime artifacts in `~/.taskmaster/projects/<project-path>/runs/<run-id>/`, including `manifest.json`, `activity.jsonl`, `state.json`, and `test-results/`. Use `path` and `os` modules for cross-platform path handling. Ensure project path normalization replaces slashes with hyphens and removes leading slashes. Generate unique run IDs using ISO timestamps. Store active run pointers in `tags/<tag-name>/current-run.json`. Use `fs-extra` (v11+) for robust file operations. Enforce append-only for `activity.jsonl` and mutable `state.json`. Validate that no runtime artifacts are left in the project directory.", + "testStrategy": "Unit tests for path normalization, run ID generation, and file operations. Integration tests with temporary directories to verify directory structure, file permissions, and isolation. Verify that project directory remains clean after operations. Test resume logic by manually interrupting and resuming a run.", + "priority": "high", + "dependencies": [], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Implement Path Normalization Utilities", + "description": "Create cross-platform path normalization utilities that convert project paths to storage-safe directory names by replacing slashes with hyphens and removing leading slashes.", + "dependencies": [], + "details": "Build a module using Node.js `path` and `os` modules to normalize project paths for storage. Handle Windows backslashes, Unix forward slashes, and edge cases like relative paths, symlinks, and special characters. Create functions to convert project paths to storage directory names and reverse the process. Ensure consistent behavior across Windows, macOS, and Linux platforms.\n<info added on 2025-10-10T08:16:33.584Z>\nSuccessfully completed path normalization utilities implementation with full TDD workflow adherence. Created production-ready module with comprehensive test coverage for cross-platform path handling. Implementation includes normalizeProjectPath(), denormalizeProjectPath() with documented limitations, and isValidNormalizedPath() functions. All 24 test cases pass, covering Unix/Windows paths, special characters, edge cases, and cross-platform consistency. Module properly handles drive letter colons and documents known limitation about preserving hyphens in original directory names. Implementation ready for next subtask in run ID generation system.\n</info added on 2025-10-10T08:16:33.584Z>", + "status": "done", + "testStrategy": "Unit tests covering various path formats, edge cases with special characters, and cross-platform consistency. Test path round-trip conversion and validation of storage-safe names.", + "parentId": "undefined" + }, + { + "id": 2, + "title": "Build Run ID Generation and Validation System", + "description": "Implement unique run ID generation using ISO timestamps with validation and collision detection mechanisms.", + "dependencies": [1], + "details": "Create a module that generates unique run IDs using ISO 8601 timestamps with millisecond precision. Add validation functions to ensure run ID format correctness and uniqueness within project storage. Implement collision detection and resolution strategies. Include helper functions to parse run IDs back to timestamps and compare run chronology.", + "status": "done", + "testStrategy": "Unit tests for ID generation uniqueness, format validation, and collision detection. Performance tests for rapid ID generation. Test timestamp parsing and chronological ordering.", + "parentId": "undefined" + }, + { + "id": 3, + "title": "Design Manifest.json Structure and Management", + "description": "Create manifest.json schema and management functions to track run metadata, configuration, and workflow state for each execution run.", + "dependencies": [2], + "details": "Define JSON schema for manifest.json containing run metadata (start time, end time, task ID, tag, branch), configuration snapshot, and workflow phase tracking. Implement functions to create, update, and validate manifest files. Use atomic write operations to prevent corruption. Include version field for future schema evolution and migration support.", + "status": "done", + "testStrategy": "Unit tests for schema validation, manifest creation and updates. Test atomic write operations and corruption recovery. Verify metadata accuracy and schema evolution compatibility.", + "parentId": "undefined" + }, + { + "id": 4, + "title": "Implement Activity.jsonl Append-Only Logging", + "description": "Build append-only logging system using JSONL format to track all workflow activities with timestamps and structured event data.", + "dependencies": [3], + "details": "Create logging functions that append structured events to activity.jsonl file using newline-delimited JSON format. Implement event types for workflow phases, git operations, test executions, and error conditions. Ensure atomic append operations with proper file locking. Add functions to read and filter activity logs by event type, timestamp, or other criteria.", + "status": "in-progress", + "testStrategy": "Unit tests for event logging, file append operations, and log reading/filtering. Concurrency tests for simultaneous writes. Test log file integrity and recovery from interrupted operations.", + "parentId": "undefined" + }, + { + "id": 5, + "title": "Create State.json Mutable Checkpoint Handling", + "description": "Implement mutable state management system for workflow checkpoints, current phase tracking, and resumable execution state.", + "dependencies": [4], + "details": "Build state management functions for mutable state.json file containing current workflow phase, progress indicators, and resumable execution context. Implement atomic read-modify-write operations with proper locking. Add checkpoint creation, state restoration, and validation functions. Support state migration between schema versions and cleanup of stale state.", + "status": "pending", + "testStrategy": "Unit tests for state read/write operations, atomic updates, and checkpoint restoration. Test state validation and migration. Verify proper cleanup of stale state and error recovery scenarios.", + "parentId": "undefined" + }, + { + "id": 6, + "title": "Build Directory Structure Creation and Cleanup", + "description": "Implement directory structure management with robust error handling, atomic operations, and isolation between different workflow runs.", + "dependencies": [5], + "details": "Create functions to establish the complete directory hierarchy (~/.taskmaster/projects/<project-path>/runs/<run-id>/). Implement atomic directory creation with proper permissions and cleanup of failed operations. Add isolation mechanisms to prevent cross-run interference. Include functions to archive completed runs and clean up old data based on retention policies. Ensure no runtime artifacts remain in project directory.", + "status": "pending", + "testStrategy": "Integration tests for complete directory structure creation. Test cleanup operations and isolation between runs. Verify proper permissions and cross-platform compatibility. Test retention policies and archive functionality.", + "parentId": "undefined" + } + ], + "complexity": 7, + "recommendedSubtasks": 6, + "expansionPrompt": "Break down the global storage system implementation into: 1) Path normalization utilities with cross-platform support, 2) Run ID generation and validation, 3) Manifest.json structure and management, 4) Activity.jsonl append-only logging, 5) State.json mutable checkpoint handling, and 6) Directory structure creation and cleanup. Focus on robust error handling, atomic operations, and isolation between different runs.", + "updatedAt": "2025-10-10T08:45:46.816Z" + }, + { + "id": 2, + "title": "Build GitAdapter with Safety Checks", + "description": "Develop a GitAdapter class that handles all git operations (branching, committing, pushing) with built-in safety checks and confirmation gates.", + "details": "Implement in TypeScript using `simple-git` (v3+) for git operations. Add methods for branch creation, checkout, commit, push, and status checks. Enforce safety: never commit on default branch, always check working tree cleanliness, confirm destructive operations unless `--no-confirm`. Generate branch names from configurable patterns. Include methods to get current/default branch. Use `chalk` for colored CLI output. Expose a clean, promise-based API. Store branch↔tag mapping in run state.", + "testStrategy": "Unit tests with mocked git commands. Integration tests with a temporary git repo to verify branch creation, commit gating, and safety checks. Negative tests: attempt commits on default branch, dirty working tree. Verify branch name generation and mapping.", + "priority": "high", + "dependencies": ["1"], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Implement Git Repository Detection and Validation", + "description": "Create foundational methods to detect and validate git repositories, ensuring the GitAdapter only operates in valid git environments.", + "dependencies": [], + "details": "Implement methods to check if current directory is a git repository, validate git installation, and verify repository state. Use simple-git to check .git directory existence and run basic git commands. Add error handling for non-git directories and corrupted repositories. Include validation for git binary availability and minimum version requirements.", + "status": "pending", + "testStrategy": "Unit tests with mocked filesystem and git commands. Test detection in git/non-git directories, handling of corrupted repos, and error cases for missing git binary.", + "parentId": "undefined" + }, + { + "id": 2, + "title": "Build Working Tree Status Checker with Detailed Reporting", + "description": "Develop comprehensive working tree status checking functionality that provides detailed information about repository state and cleanliness.", + "dependencies": [1], + "details": "Create methods to check working tree cleanliness, staged changes, untracked files, and merge conflicts. Use simple-git status methods to gather detailed repository state. Implement colored output using chalk for status visualization. Add methods to categorize changes (modified, added, deleted, untracked) and provide human-readable summaries.", + "status": "pending", + "testStrategy": "Unit tests with mocked git status responses. Integration tests with temporary repos in various states (clean, dirty, staged changes, conflicts). Verify colored output and detailed reporting accuracy.", + "parentId": "undefined" + }, + { + "id": 3, + "title": "Implement Branch Operations with Safety Guards", + "description": "Create secure branch management functionality including creation, checkout, and listing with built-in safety mechanisms.", + "dependencies": [1, 2], + "details": "Implement branch creation with validation checks, safe checkout operations that verify clean working tree, and branch listing with current branch detection. Add safety guards to prevent operations on dirty repositories. Include branch existence validation and conflict detection. Support force operations with explicit confirmation gates.", + "status": "pending", + "testStrategy": "Unit tests for branch operations with mocked git responses. Integration tests with temporary repos to verify branch creation, checkout safety, and validation logic. Test edge cases like existing branches and dirty working trees.", + "parentId": "undefined" + }, + { + "id": 4, + "title": "Develop Commit Operations with Metadata Embedding", + "description": "Build commit functionality that embeds workflow metadata and enforces safety rules around committing.", + "dependencies": [1, 2, 3], + "details": "Implement commit creation with metadata embedding (task ID, workflow phase, timestamp). Add strict safety checks to prevent commits on default branch. Include staged file validation and commit message generation. Support metadata embedding in commit messages and branch-tag mapping storage in run state.", + "status": "pending", + "testStrategy": "Unit tests for commit logic with mocked git operations. Integration tests to verify metadata embedding, default branch protection, and commit creation. Test safety violations and error handling.", + "parentId": "undefined" + }, + { + "id": 5, + "title": "Create Default Branch Detection and Protection Logic", + "description": "Implement robust default branch detection and protection mechanisms to prevent accidental commits to main branches.", + "dependencies": [1, 3], + "details": "Add methods to detect default branch (main, master, develop) from remote origin and local configuration. Implement protection logic that blocks commits and destructive operations on default branches. Support configurable branch protection patterns and override mechanisms with confirmation gates.", + "status": "pending", + "testStrategy": "Unit tests for default branch detection with various git configurations. Integration tests with repos using different default branch names. Verify protection logic and confirmation gate behavior.", + "parentId": "undefined" + }, + { + "id": 6, + "title": "Build Push Operations with Conflict Handling", + "description": "Develop secure push functionality that handles conflicts, validates remote state, and provides clear error reporting.", + "dependencies": [1, 2, 3, 5], + "details": "Implement push operations with pre-push validation, conflict detection, and resolution guidance. Add upstream tracking setup and force-push protection. Include detailed error reporting with suggestions for conflict resolution. Support dry-run mode and confirmation gates for destructive operations.", + "status": "pending", + "testStrategy": "Unit tests for push logic with mocked remote scenarios. Integration tests with temporary repos and simulated remotes. Test conflict detection, error handling, and safety mechanisms.", + "parentId": "undefined" + }, + { + "id": 7, + "title": "Implement Branch Name Generation from Patterns", + "description": "Create configurable branch name generation system that follows consistent patterns and integrates with workflow state.", + "dependencies": [1], + "details": "Implement branch name generation using configurable patterns (task ID, description, timestamp). Support template variables and sanitization rules for valid git branch names. Include branch-tag mapping storage and retrieval from run state. Add validation for generated names and conflict resolution.", + "status": "pending", + "testStrategy": "Unit tests for pattern parsing and name generation with various templates. Integration tests to verify generated names are valid git branch names. Test mapping storage and conflict resolution logic.", + "parentId": "undefined" + } + ], + "complexity": 8, + "recommendedSubtasks": 7, + "expansionPrompt": "Decompose GitAdapter into: 1) Git repository detection and validation, 2) Working tree status checking with detailed reporting, 3) Branch operations (create, checkout, list) with safety guards, 4) Commit operations with metadata embedding, 5) Default branch detection and protection logic, 6) Push operations with conflict handling, and 7) Branch name generation from patterns. Emphasize safety checks, confirmation gates, and comprehensive error messages.", + "updatedAt": "2025-10-10T13:16:43.147Z" + }, + { + "id": 3, + "title": "Implement Test Result Validator", + "description": "Create a service to validate test results reported by the AI agent, enforcing TDD phase semantics (RED must fail, GREEN must pass).", + "details": "Build a `TestResultValidator` class in TypeScript. Validate that RED phase results include at least one failure, GREEN phase results have zero failures. Optionally validate coverage thresholds if provided. Return structured validation results with messages and suggestions. Use `joi` (v17+) for input validation. Keep validation logic framework-agnostic—TaskMaster does not parse test output, only checks reported numbers.", + "testStrategy": "Unit tests for validation logic: RED with no failures fails validation, GREEN with failures fails validation. Test coverage threshold enforcement. Verify error messages and suggestions. Mock invalid inputs.", + "priority": "high", + "dependencies": ["1"], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Implement Input Validation and Schema Definition", + "description": "Create comprehensive input validation for test result data using joi schema validation to ensure all required fields are present and properly formatted.", + "dependencies": [], + "details": "Define joi schemas for test result objects including total tests, passed tests, failed tests, skipped tests, and optional coverage data. Validate that numeric fields are non-negative integers and that totals are consistent (passed + failed + skipped = total). Include validation for coverage thresholds as percentages between 0-100. Create clear error messages for validation failures.\n<info added on 2025-10-10T13:39:49.804Z>\nI'll analyze the codebase first to understand the implementation structure and then provide the appropriate update.Based on my analysis of the codebase, I can see that the user has successfully completed the input validation implementation. Here's the new information that should be appended to the subtask:\n\nImplementation completed using Zod v4 schema validation in `/packages/tm-core/src/services/test-result-validator.ts`. Created comprehensive test result schema with numeric field validation (non-negative integers), enum validation for TestPhase ('RED', 'GREEN', 'REFACTOR'), optional coverage data validation (0-100 percentages), and total consistency checks (passed + failed + skipped = total). Key discovery: Zod v4 changed error structure - validation errors are now accessed via `parseResult.error.issues` instead of `parseResult.error.errors`. The implementation correctly handles this by mapping over `zodIssues` to extract validation error messages with proper path handling for nested field errors.\n</info added on 2025-10-10T13:39:49.804Z>", + "status": "done", + "testStrategy": "Unit tests for schema validation with various input combinations: valid inputs, missing fields, negative numbers, invalid types, inconsistent totals. Test error message clarity and structure.", + "parentId": "undefined" + }, + { + "id": 2, + "title": "Implement RED Phase Validation Logic", + "description": "Create validation logic to ensure RED phase test results contain at least one failing test, enforcing TDD semantics that require initial test failures.", + "dependencies": [1], + "details": "Implement validation method that checks if failed test count is greater than zero for RED phase. Provide specific error messages when RED phase has zero failures, suggesting the developer write failing tests first. Include validation that total test count is greater than zero to prevent empty test suites. Return structured validation results with phase-specific suggestions.\n<info added on 2025-10-10T13:41:04.020Z>\nBased on my analysis of the codebase and the completed RED phase validation implementation in `packages/tm-core/src/services/test-result-validator.ts:74-100`, the subtask has been successfully completed. The implementation includes:\n\n- Proper validation that RED phase must have at least one failing test (line 84-87)\n- Empty test suite prevention with appropriate error message (line 90-93)\n- Actionable TDD workflow suggestions (lines 86, 92)\n- Integration with the base validation system for structured error handling\n\nThe test suite in `packages/tm-core/src/services/test-result-validator.test.ts:118-178` comprehensively covers all RED phase validation scenarios including success cases, zero failures, empty test suites, and error propagation. Implementation follows TypeScript best practices with proper type safety and Zod schema validation for robust input handling.\n</info added on 2025-10-10T13:41:04.020Z>", + "status": "done", + "testStrategy": "Unit tests covering RED phase with zero failures (should fail validation), RED phase with failures (should pass), empty test suite scenarios. Verify error messages provide actionable feedback for TDD workflow.", + "parentId": "undefined" + }, + { + "id": 3, + "title": "Implement GREEN Phase Validation Logic", + "description": "Create validation logic to ensure GREEN phase test results have zero failures, confirming that all tests pass after implementation.", + "dependencies": [1], + "details": "Implement validation method that verifies failed test count equals zero for GREEN phase. Ensure at least one test exists and passed to prevent false positives from empty test suites. Provide clear error messages when GREEN phase has failures, suggesting code fixes or test adjustments. Include validation that passed test count matches or exceeds the previous RED phase test count.\n<info added on 2025-10-10T13:42:16.926Z>\nBased on my analysis of the TestResultValidator implementation in `packages/tm-core/src/services/test-result-validator.ts` and test file, I can see that the GREEN phase validation logic has been successfully implemented. The `validateGreenPhase` method at lines 106-145 contains the complete implementation with:\n\n1. Zero failures validation (lines 120-123)\n2. At least one passing test validation (lines 125-129) \n3. Test count regression tracking with warnings (lines 132-137)\n4. Comprehensive error messages and actionable suggestions\n\nThe test file shows comprehensive test coverage for all GREEN phase scenarios including edge cases. The implementation follows the project's TypeScript patterns and uses the established validation result structure.\n\nIMPLEMENTATION COMPLETE. TestResultValidator.validateGreenPhase method fully implemented with zero failures validation, minimum passing test requirement, test count regression warnings, and comprehensive error messaging. Follows existing codebase patterns using zod validation, structured error/warning/suggestion results, and maintains backward compatibility. All test scenarios covered including edge cases for empty test suites and count decreases.\n</info added on 2025-10-10T13:42:16.926Z>", + "status": "done", + "testStrategy": "Unit tests for GREEN phase with failures (should fail validation), GREEN phase with all passing tests (should pass), regression scenarios where test count decreases. Test error messages guide developers toward fixing implementation.", + "parentId": "undefined" + }, + { + "id": 4, + "title": "Implement Coverage Threshold Validation", + "description": "Create configurable coverage validation that enforces minimum coverage thresholds when coverage data is provided with test results.", + "dependencies": [1], + "details": "Implement optional coverage validation that accepts configurable thresholds for line, branch, function, and statement coverage. Validate coverage percentages against thresholds only when coverage data is present. Provide detailed messages indicating which coverage types failed to meet thresholds and by how much. Support different threshold configurations per project and phase. Return structured results with coverage gaps and improvement suggestions.\n<info added on 2025-10-10T13:44:18.503Z>\nI'll analyze the codebase to understand the current implementation before updating the subtask details.Based on my analysis of the codebase, I can see the TestResultValidator implementation in `packages/tm-core/src/services/test-result-validator.ts` and its comprehensive test suite in `test-result-validator.test.ts`. The implementation includes:\n\n1. **Complete coverage threshold validation logic** (lines 147-197)\n2. **Configurable thresholds** for line, branch, function, and statement coverage\n3. **Skips validation when coverage data is absent** (line 162-165)\n4. **Detailed error messages** showing specific coverage gaps (lines 171-185)\n5. **Structured results** with gaps and improvement suggestions (lines 188-190)\n\nThe test file shows comprehensive test coverage including coverage threshold validation tests (lines 270-423) with various scenarios for meeting/failing thresholds and handling missing coverage data.\n\nImplementation complete. The validateCoverage method correctly handles configurable thresholds for all four coverage types (line, branch, function, statement), gracefully skips validation when coverage data is undefined, and provides specific error messages detailing which coverage types failed and by how much. The implementation supports different threshold configurations through the CoverageThresholds interface and returns structured ValidationResult objects with detailed feedback. Test suite includes 23 comprehensive test cases covering input validation, RED/GREEN phase validation, and coverage threshold scenarios.\n</info added on 2025-10-10T13:44:18.503Z>", + "status": "done", + "testStrategy": "Unit tests for coverage validation with various threshold configurations, missing coverage data (should skip validation), coverage below/above thresholds. Test different coverage types independently and in combination.", + "parentId": "undefined" + } + ], + "complexity": 5, + "recommendedSubtasks": 4, + "expansionPrompt": "Split test validation into: 1) Input validation and schema definition for test results, 2) RED phase validation logic (ensuring failures exist), 3) GREEN phase validation logic (ensuring all tests pass), and 4) Coverage threshold validation with configurable limits. Include comprehensive validation messages and suggestions for common failure scenarios." + }, + { + "id": 4, + "title": "Develop WorkflowOrchestrator State Machine", + "description": "Implement the core state machine that drives the TDD workflow: Preflight → BranchSetup → SubtaskLoop (RED→GREEN→COMMIT) → Finalize → Complete.", + "details": "Use `xstate` (v5+) or a custom lightweight state machine. Emit events for phase/subtask transitions, test runs, commits, and errors. Coordinate GitAdapter, TestResultValidator, and (future) Executor adapters. Persist state to `state.json` after each transition. Support resumption from checkpoint. Implement phase hooks for extensibility. Follow the event API specified in the PRD.", + "testStrategy": "Unit tests for state transitions and event emission. Integration tests with mocked adapters to verify full workflow lifecycle. Test resume from interrupted state. Verify event stream in `activity.jsonl`.", + "priority": "high", + "dependencies": ["1", "2", "3"], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Define State Machine Structure and Transitions", + "description": "Design and implement the core state machine with phases: Preflight, BranchSetup, SubtaskLoop (RED→GREEN→COMMIT), Finalize, and Complete.", + "dependencies": [], + "details": "Use XState v5 or a custom lightweight state machine to define states and transitions according to the TDD workflow. Ensure the initial state is Preflight and transitions follow the specified sequence. Implement hierarchical or nested states if needed for SubtaskLoop phases.\n<info added on 2025-10-10T13:49:21.585Z>\nI'll analyze the codebase to understand the current implementation before generating the subtask update.Based on my analysis of the codebase, I can now generate the subtask update. The user has completed the state machine structure implementation with a lightweight custom approach in `/packages/tm-core/src/workflow/workflow-orchestrator.ts`. The implementation includes:\n\n1. **State machine class** (`WorkflowOrchestrator`) with phases: PREFLIGHT → BRANCH_SETUP → SUBTASK_LOOP → FINALIZE → COMPLETE\n2. **TDD cycle support** within SUBTASK_LOOP: RED → GREEN → COMMIT \n3. **State transitions** with validation using `defineTransitions()` method\n4. **Comprehensive test suite** in `workflow-orchestrator.test.ts` covering all transitions\n\n**COMPLETION NOTES (2025-10-10):**\n\nState machine implementation successfully completed using lightweight custom approach rather than XState. Created WorkflowOrchestrator class in packages/tm-core/src/workflow/workflow-orchestrator.ts with complete workflow phases (PREFLIGHT → BRANCH_SETUP → SUBTASK_LOOP → FINALIZE → COMPLETE) and nested TDD cycle states (RED → GREEN → COMMIT). \n\nKey implementation features:\n- defineTransitions() method defining valid state transitions with guard conditions\n- handleTDDPhaseTransition() for nested RED→GREEN→COMMIT cycle within SUBTASK_LOOP\n- State serialization/restoration for persistence (getState/restoreState methods)\n- Context management with branch names, subtask tracking, and error handling\n- Full TypeScript types in workflow/types.ts\n\nAll 15 tests pass covering: initial state verification, main workflow transitions, TDD cycle transitions, state validation, and serialization/restoration. Implementation ready for integration with GitAdapter and TestResultValidator components.\n</info added on 2025-10-10T13:49:21.585Z>", + "status": "done", + "testStrategy": "Unit tests verifying correct state transitions and initial state setup.", + "parentId": "undefined" + }, + { + "id": 2, + "title": "Implement Event Emission System with Comprehensive Event Types", + "description": "Develop an event system that emits events for phase transitions, subtask transitions, test runs, commits, and errors.", + "dependencies": [1], + "details": "Create a robust event emitter integrated with the state machine that dispatches detailed events on every significant action or state change. Define a comprehensive event schema matching the PRD specifications for extensibility and observability.\n<info added on 2025-10-10T13:52:07.951Z>\nI'll analyze the codebase to understand the current implementation and then provide the update based on your completion report.Now let me look for the workflow orchestrator to understand the event emission system:Let me also check the types file to understand the event system better:Now let me check the test file to see what tests were created:Let me check how many tests are currently passing by running the test suite:Let me check the package.json to understand the test script:Based on my analysis of the codebase, I can see that the WorkflowOrchestrator has been successfully implemented with a comprehensive event emission system. The implementation includes:\n\n1. **Event system infrastructure**: `on()` and `off()` methods for listener management\n2. **Comprehensive event types**: 23 different event types defined in `types.ts`\n3. **Event emission throughout the state machine**: Events are emitted for phase transitions, TDD cycle progression, subtask lifecycle, git operations, and errors\n4. **Complete test coverage**: 23 tests covering all aspects including 8 specific event emission tests\n\nThe event system is fully integrated with the state machine and emits events for all significant workflow actions as specified in the PRD.\n\nIMPLEMENTATION COMPLETED: Event emission system fully implemented with comprehensive event types covering workflow phases, TDD cycles, subtask lifecycle, git operations, and error handling. System includes on/off listener management methods and emits detailed events with timestamps, phase context, and additional data. Full test coverage achieved with 23 passing tests including 8 dedicated event emission tests validating listener registration/removal, event data integrity, and proper emission during state transitions.\n</info added on 2025-10-10T13:52:07.951Z>", + "status": "done", + "testStrategy": "Unit tests to verify event emission on all relevant state transitions and actions.", + "parentId": "undefined" + }, + { + "id": 3, + "title": "Develop State Persistence and Recovery Mechanisms", + "description": "Implement saving the state machine's current state to `state.json` after each transition and support resuming from this checkpoint.", + "dependencies": [1], + "details": "After every state transition, serialize and persist the current state and context to `state.json`. On startup, load this file to restore the state machine to the last checkpoint, enabling workflow resumption.\n<info added on 2025-10-10T13:57:11.049Z>\nImplementation successfully completed. Added auto-persist mode functionality with enableAutoPersist() and disableAutoPersist() methods, allowing external state management integration. Implemented manual persistState() method for on-demand state persistence. Added 'state:persisted' event emission to workflow events system as defined in types.ts:85. Successfully integrated with existing state-manager module in packages/tm-core/src/storage/state-manager.ts for atomic state file operations. Comprehensive test coverage achieved with 28 passing tests covering state transitions, event emission, persistence callbacks, auto-persist enable/disable functionality, and integration scenarios.\n</info added on 2025-10-10T13:57:11.049Z>", + "status": "done", + "testStrategy": "Integration tests simulating interruptions and verifying correct state restoration from persisted data.", + "parentId": "undefined" + }, + { + "id": 4, + "title": "Coordinate Phases and Validate Transitions", + "description": "Implement coordination logic to validate phase transitions and ensure correct sequencing and conditions are met.", + "dependencies": [1], + "details": "Add guards and validation logic within the state machine to enforce correct phase progression. Integrate with TestResultValidator and GitAdapter to confirm readiness before moving to next phases.", + "status": "done", + "testStrategy": "Unit and integration tests verifying that invalid transitions are blocked and validations are enforced.", + "parentId": "undefined" + }, + { + "id": 5, + "title": "Implement Subtask Iteration and Progress Tracking", + "description": "Manage the SubtaskLoop phase by iterating through subtasks and tracking progress through RED, GREEN, and COMMIT steps.", + "dependencies": [1, 4], + "details": "Within the SubtaskLoop state, implement logic to iterate subtasks, handle RED→GREEN→COMMIT cycles, update subtask statuses, and emit progress events. Support max attempt limits and pause/resume functionality.", + "status": "done", + "testStrategy": "Integration tests with mocked subtasks verifying correct iteration, status updates, and event emission.", + "parentId": "undefined" + }, + { + "id": 6, + "title": "Develop Error Handling and Recovery Strategies", + "description": "Implement robust error detection, event emission, and recovery mechanisms throughout the workflow.", + "dependencies": [1, 2, 3], + "details": "Detect errors during phases and subtasks, emit error events, and define recovery strategies such as retries, fallback states, or aborting the workflow gracefully. Ensure errors are logged and persisted.", + "status": "done", + "testStrategy": "Tests simulating errors in various phases to verify error event emission and recovery behavior.", + "parentId": "undefined" + }, + { + "id": 7, + "title": "Implement Resume Functionality from Checkpoints", + "description": "Enable the orchestrator to resume workflow execution seamlessly from persisted checkpoints after interruptions.", + "dependencies": [3, 5], + "details": "On startup, detect existing `state.json` checkpoint, restore state machine context and state, and continue execution from the last known point, including subtask progress and phase.", + "status": "done", + "testStrategy": "Integration tests simulating process restarts and verifying correct resumption of workflow state and progress.", + "parentId": "undefined" + }, + { + "id": 8, + "title": "Integrate GitAdapter, TestResultValidator, and Executor Adapters", + "description": "Coordinate with GitAdapter, TestResultValidator, and future Executor adapters to perform git operations, validate test results, and execute tasks.", + "dependencies": [1, 4, 5], + "details": "Implement adapter interfaces and invoke them at appropriate phases: GitAdapter for branch and commit operations, TestResultValidator for test result validation in RED/GREEN phases, and prepare hooks for Executor adapter integration. Ensure adapters are coordinated via events and state transitions.", + "status": "done", + "testStrategy": "Integration tests with mocked adapters verifying correct calls, coordination, and error handling.", + "parentId": "undefined" + } + ], + "complexity": 9, + "recommendedSubtasks": 8, + "expansionPrompt": "Structure the orchestrator into: 1) State machine definition and transitions (Preflight → BranchSetup → SubtaskLoop → Finalize), 2) Event emission system with comprehensive event types, 3) State persistence and recovery mechanisms, 4) Phase coordination and validation, 5) Subtask iteration and progress tracking, 6) Error handling and recovery strategies, 7) Resume functionality from checkpoints, and 8) Integration points for Git, Test, and other adapters." + }, + { + "id": 5, + "title": "Create Enhanced Commit Message Generator", + "description": "Build a module that generates conventional commit messages with embedded task metadata (task ID, phase, tag, test counts, coverage).", + "details": "Implement a function that takes changed files, task context, and test results, then generates a commit message per the template. Determine scope from changed files (e.g., 'cli', 'core'). Use `conventional-commits` parser (v2+) for validation. Include all required metadata in the commit body. Support configurable templates via `config.json`. Optionally allow message override.", + "testStrategy": "Unit tests for message generation, scope detection, and template rendering. Verify all metadata is included. Test with various file change patterns. Validate against `conventional-commits` spec.", + "priority": "medium", + "dependencies": ["1"], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create template parsing and variable substitution system", + "description": "Implement a configurable template system that can parse commit message templates and substitute variables with dynamic content", + "dependencies": [], + "details": "Build a TemplateEngine class that loads templates from config.json, supports variable substitution using placeholders like {{type}}, {{scope}}, {{description}}, {{taskId}}, etc. Support default templates and custom overrides. Implement template validation to ensure required fields are present. Use a simple string replacement or template library for variable substitution.", + "status": "done", + "testStrategy": "Unit tests for template loading, variable substitution, and template validation. Test with various template formats and missing variables.", + "parentId": "undefined" + }, + { + "id": 2, + "title": "Implement intelligent scope detection from changed files", + "description": "Create a system that analyzes changed files to automatically determine the appropriate commit scope", + "dependencies": [1], + "details": "Build a ScopeDetector class that analyzes file paths and determines conventional commit scopes (e.g., 'cli', 'core', 'test', 'docs'). Create configurable mapping rules in config.json. Handle multiple scopes by choosing the most relevant or using a priority system. Support custom scope detection rules and fallback to generic scopes when unclear.", + "status": "done", + "testStrategy": "Unit tests for scope detection with various file path patterns. Test priority resolution for multiple scopes. Verify fallback behavior.", + "parentId": "undefined" + }, + { + "id": 3, + "title": "Implement metadata embedding and conventional commits compliance", + "description": "Create the core commit message generator that embeds task metadata and ensures conventional commits format compliance", + "dependencies": [1, 2], + "details": "Build a CommitMessageGenerator class that combines template engine and scope detector to generate complete commit messages. Embed task metadata (ID, phase, tag, test counts, coverage) in the commit body. Use conventional-commits parser v2+ for validation. Support message override option. Format metadata as structured text or YAML in commit body for easy parsing.", + "status": "done", + "testStrategy": "Unit tests for message generation with various metadata combinations. Integration tests with conventional-commits parser validation. Test message override functionality.", + "parentId": "undefined" + } + ], + "complexity": 4, + "recommendedSubtasks": 3, + "expansionPrompt": "Organize commit message generation into: 1) Template parsing and variable substitution with configurable templates, 2) Scope detection from changed files with intelligent categorization, and 3) Metadata embedding (task context, test results, coverage) with conventional commits compliance. Ensure messages are parseable and contain all required task metadata." + }, + { + "id": 6, + "title": "Implement Subtask TDD Loop", + "description": "Develop the RED→GREEN→COMMIT loop for subtasks, integrating with the state machine, test validation, and commit creation.", + "details": "For each subtask, orchestrate: (1) RED: AI generates and runs tests, reports results; TaskMaster validates failures. (2) GREEN: AI implements code, runs tests, reports results; TaskMaster validates all pass. (3) COMMIT: TaskMaster stages files, generates commit message, creates commit. Update subtask status. Handle max attempts for GREEN phase. Log all phase transitions and results to `activity.jsonl`. Support pause/resume.", + "testStrategy": "Integration tests with mocked AI agent calls. Verify RED requires failures, GREEN requires passes. Test commit creation with metadata. Test max attempt logic. Verify activity log entries.", + "priority": "high", + "dependencies": ["3", "4", "5"], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Implement RED Phase Orchestrator", + "description": "Build the RED phase controller that coordinates AI test generation and validates required test failures.", + "dependencies": [], + "details": "Create RedPhaseOrchestrator class that: (1) Calls AI agent to generate tests for current subtask, (2) Runs generated tests using TestResultValidator, (3) Validates that at least one test fails (RED requirement), (4) Returns structured results with test outcomes and validation status. Handle edge cases like no tests generated or compilation errors. Integrate with WorkflowOrchestrator state machine for phase transitions.", + "status": "done", + "testStrategy": "Unit tests with mocked AI agent and TestResultValidator. Verify RED phase fails validation when all tests pass. Test error handling for invalid test generation.", + "parentId": "undefined" + }, + { + "id": 2, + "title": "Implement GREEN Phase Orchestrator", + "description": "Build the GREEN phase controller that guides AI implementation and validates all tests pass.", + "dependencies": [1], + "details": "Create GreenPhaseOrchestrator class that: (1) Calls AI agent to implement code for current subtask, (2) Runs all tests using TestResultValidator, (3) Validates that zero tests fail (GREEN requirement), (4) Tracks attempt count for max retry logic, (5) Returns structured results with implementation status. Support iterative implementation with feedback from failed test runs. Coordinate with attempt tracking system.", + "status": "done", + "testStrategy": "Unit tests with mocked AI agent and retry scenarios. Integration tests verifying GREEN phase passes only when all tests succeed. Test max attempt handling and failure modes.", + "parentId": "undefined" + }, + { + "id": 3, + "title": "Implement COMMIT Phase Orchestrator", + "description": "Build the COMMIT phase controller that handles file staging, commit message generation, and git operations.", + "dependencies": [2], + "details": "Create CommitPhaseOrchestrator class that: (1) Uses GitAdapter to stage modified files, (2) Generates descriptive commit messages with subtask metadata and test results, (3) Creates git commit with structured metadata, (4) Updates subtask status to completed, (5) Handles git operation failures and rollback scenarios. Include commit message templates and ensure atomic operations.", + "status": "done", + "testStrategy": "Unit tests with mocked GitAdapter. Integration tests verifying commit creation with proper metadata. Test rollback scenarios and file staging edge cases.", + "parentId": "undefined" + }, + { + "id": 4, + "title": "Implement Attempt Tracking and Retry Logic", + "description": "Build the attempt counter system with configurable maximum retry limits and exponential backoff.", + "dependencies": [], + "details": "Create AttemptTracker class that: (1) Tracks current attempt count per subtask and phase, (2) Enforces configurable maximum attempts for GREEN phase, (3) Implements exponential backoff between retries, (4) Provides clear failure messaging when max attempts reached, (5) Persists attempt state for resume functionality. Support different retry strategies and timeout handling.", + "status": "done", + "testStrategy": "Unit tests for attempt counting logic and max retry enforcement. Test exponential backoff timing. Verify state persistence and resume behavior.", + "parentId": "undefined" + }, + { + "id": 5, + "title": "Implement Phase Transition Validator and State Manager", + "description": "Build the state transition validator that ensures proper TDD phase sequencing and state updates.", + "dependencies": [1, 2, 3, 4], + "details": "Create PhaseTransitionValidator class that: (1) Validates legal phase transitions (RED→GREEN→COMMIT), (2) Enforces phase prerequisites and exit conditions, (3) Updates workflow state with phase results, (4) Handles pause/resume functionality with state checkpoints, (5) Provides rollback capabilities for failed transitions. Integrate with WorkflowOrchestrator for state machine coordination.", + "status": "done", + "testStrategy": "Unit tests for transition validation logic and state persistence. Integration tests verifying full TDD cycle transitions. Test pause/resume scenarios and rollback handling.", + "parentId": "undefined" + }, + { + "id": 6, + "title": "Implement Activity Logger for Phase Tracking", + "description": "Build the comprehensive logging system that records all phase transitions and results to activity.jsonl.", + "dependencies": [5], + "details": "Create ActivityLogger class that: (1) Logs structured entries for all phase transitions with timestamps, (2) Records test results, attempt counts, and error details, (3) Writes to activity.jsonl in append-only JSONL format, (4) Provides log querying and filtering capabilities, (5) Ensures log integrity and handles write failures gracefully. Include log rotation and cleanup policies.", + "status": "done", + "testStrategy": "Unit tests for log entry formatting and file operations. Integration tests verifying complete workflow logging. Test log rotation and error recovery scenarios.", + "parentId": "undefined" + } + ], + "complexity": 8, + "recommendedSubtasks": 6, + "expansionPrompt": "Break down the TDD loop into: 1) RED phase orchestration with test generation coordination, 2) GREEN phase orchestration with implementation guidance, 3) COMMIT phase with file staging and commit creation, 4) Attempt tracking and maximum retry logic, 5) Phase transition validation and state updates, and 6) Activity logging for all phase transitions. Focus on robust state management and clear error recovery paths." + }, + { + "id": 7, + "title": "Build CLI Commands for AI Agent Orchestration", + "description": "Implement the `tm autopilot` CLI commands for starting, resuming, and advancing the TDD workflow, with JSON output for machine parsing.", + "details": "Use `commander` (v11+) for CLI construction. Commands: `start`, `next`, `complete`, `commit`, `status`, `resume`, `abort`. `next` returns JSON with action details. `complete` accepts `--results` and `--coverage`. `commit` uses enhanced message generator. `status` shows current run progress. Support `--json` flag for machine-readable output. Integrate with WorkflowOrchestrator and GitAdapter.", + "testStrategy": "Unit tests for command parsing and output formatting. Integration tests for full workflow via CLI. Test JSON output for machine consumption. Verify error handling and help text.", + "priority": "medium", + "dependencies": ["4", "6"], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Set up CLI command registration and argument parsing infrastructure", + "description": "Create the foundation for `tm autopilot` commands using commander.js, establishing the command structure and shared argument parsing logic.", + "dependencies": [], + "details": "Create a new CLI module that registers the `autopilot` subcommand with commander (v11+). Set up shared argument parsing for common flags like `--json`, `--verbose`, and `--project-root`. Establish error handling patterns and help text formatting. Create base command class or utilities for consistent behavior across all autopilot commands.", + "status": "done", + "testStrategy": "Unit tests for command registration, argument parsing, and help text generation. Test error handling for invalid arguments.", + "parentId": "undefined" + }, + { + "id": 2, + "title": "Implement start and resume commands with workflow initialization", + "description": "Build the `start` and `resume` commands that initialize or restore the TDD workflow state, integrating with WorkflowOrchestrator.", + "dependencies": [1], + "details": "Implement `tm autopilot start` command that validates project setup, initializes WorkflowOrchestrator, and begins the TDD workflow. Implement `tm autopilot resume` that restores workflow state from `state.json`. Both commands should validate dependencies (tasks 4,6) are available, handle project root detection, and provide clear feedback on initialization status.", + "status": "done", + "testStrategy": "Integration tests for workflow initialization. Test state restoration from various state.json configurations. Verify dependency checking and error messages.", + "parentId": "undefined" + }, + { + "id": 3, + "title": "Build next and status commands with JSON output formatting", + "description": "Create the `next` and `status` commands that provide workflow progression information with structured JSON output for machine parsing.", + "dependencies": [1], + "details": "Implement `tm autopilot next` that returns JSON with the next action details (test generation, implementation, or commit). Implement `tm autopilot status` showing current run progress, active subtask, and phase. Both commands must support `--json` flag for machine-readable output and human-readable default format. Ensure consistent JSON schema across all output.", + "status": "done", + "testStrategy": "Unit tests for JSON output formatting and schema validation. Integration tests with various workflow states. Test both human and machine-readable output modes.", + "parentId": "undefined" + }, + { + "id": 4, + "title": "Implement complete command with result validation integration", + "description": "Build the `complete` command that accepts phase completion results and validates them against TDD requirements.", + "dependencies": [1], + "details": "Implement `tm autopilot complete` command accepting `--results` (test output/implementation details) and `--coverage` (coverage metrics) flags. Integrate with result validation logic to ensure RED phase has failures and GREEN phase has all tests passing. Update workflow state and advance to next phase. Provide clear feedback on validation success/failure.", + "status": "done", + "testStrategy": "Unit tests for result parsing and validation logic. Integration tests with mocked test results and coverage data. Test error cases for invalid phase transitions.", + "parentId": "undefined" + }, + { + "id": 5, + "title": "Create commit and abort commands with git operation coordination", + "description": "Implement the `commit` and `abort` commands that coordinate git operations and workflow state management.", + "dependencies": [1], + "details": "Implement `tm autopilot commit` that uses the enhanced commit message generator (task 5), stages appropriate files via GitAdapter, and creates commits with embedded metadata. Implement `tm autopilot abort` that safely terminates the workflow, cleaning up temporary state. Both commands should handle git operation errors gracefully and maintain workflow state consistency.", + "status": "done", + "testStrategy": "Integration tests with real git operations. Test commit message generation and metadata embedding. Verify abort command cleanup and state management. Test error handling for git failures.", + "parentId": "undefined" + } + ], + "complexity": 6, + "recommendedSubtasks": 5, + "expansionPrompt": "Structure CLI commands into: 1) Command registration and argument parsing setup, 2) `start` and `resume` commands with initialization logic, 3) `next` and `status` commands with JSON output formatting, 4) `complete` command with result validation integration, and 5) `commit` and `abort` commands with git operation coordination. Ensure consistent JSON output for machine parsing and comprehensive error handling." + }, + { + "id": 8, + "title": "Develop MCP Tools for AI Agent Integration", + "description": "Create MCP tools (`autopilot_*`) enabling AI agents to interact with the workflow programmatically, matching CLI functionality.", + "details": "Implement tools: `autopilot_start`, `autopilot_next`, `autopilot_complete_phase`, `autopilot_commit`, `autopilot_status`, `autopilot_resume`. Each tool takes parameters and returns structured results. Use the same core logic as the CLI, exposed via MCP's tool interface. Document expected inputs/outputs. Ensure tools are framework-agnostic and stateless (rely on `state.json`).", + "testStrategy": "Unit tests for each tool's input/output. Integration tests with mocked MCP environment. Verify parity with CLI behavior. Test error cases and edge conditions.", + "priority": "medium", + "dependencies": ["4", "6"], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Define MCP tool schemas and input/output validation", + "description": "Create comprehensive MCP tool schema definitions for all autopilot tools with proper input validation and structured output specifications.", + "dependencies": [], + "details": "Define TypeScript interfaces and JSON schemas for autopilot_start, autopilot_next, autopilot_complete_phase, autopilot_commit, autopilot_status, and autopilot_resume tools. Use joi or zod for runtime parameter validation. Specify required/optional parameters, data types, and validation rules. Document expected input/output formats matching CLI functionality. Create shared validation utilities for consistent error handling across all tools.", + "status": "done", + "testStrategy": "Unit tests for schema validation with valid/invalid inputs. Test parameter type checking and error message quality. Verify schema completeness against CLI parameter sets.", + "parentId": "undefined" + }, + { + "id": 2, + "title": "Implement autopilot_start and autopilot_resume tools", + "description": "Build MCP tools for workflow initialization and resumption, enabling AI agents to start new TDD workflows or resume interrupted ones.", + "dependencies": [1], + "details": "Implement autopilot_start tool accepting tag, target directory, and configuration parameters. Create autopilot_resume tool for continuing interrupted workflows from state.json. Both tools should initialize WorkflowOrchestrator, validate preconditions, and return workflow status. Use the same core logic as CLI commands but expose via MCP interface. Handle error cases like existing workflows, missing dependencies, or invalid configurations.", + "status": "done", + "testStrategy": "Integration tests with mocked workflow state. Test start with various configurations. Test resume from different workflow states. Verify error handling for invalid scenarios.", + "parentId": "undefined" + }, + { + "id": 3, + "title": "Implement autopilot_next and autopilot_status tools", + "description": "Create MCP tools that provide workflow state information and determine next required actions for AI agents.", + "dependencies": [1], + "details": "Build autopilot_next tool returning the current phase, required action, subtask context, and any parameters needed for the next step. Implement autopilot_status tool providing comprehensive workflow state including current phase, completed subtasks, test results, and progress metrics. Both tools should read from state.json and return structured data matching the event API specification. Include error handling for missing or corrupted state.", + "status": "done", + "testStrategy": "Unit tests with various workflow states. Test next action determination across all phases. Verify status reporting accuracy. Test error handling for corrupted state files.", + "parentId": "undefined" + }, + { + "id": 4, + "title": "Implement autopilot_complete_phase tool with validation", + "description": "Build MCP tool for phase completion that validates test results and transitions workflow state according to TDD semantics.", + "dependencies": [1], + "details": "Create autopilot_complete_phase tool accepting phase type, test results, and file changes. Integrate with TestResultValidator to enforce RED phase failures and GREEN phase passes. Update workflow state and trigger state machine transitions. Handle validation failures with clear error messages and suggested actions. Support coverage threshold validation when provided. Log phase completion to activity.jsonl.", + "status": "done", + "testStrategy": "Integration tests with TestResultValidator. Test RED phase with passing/failing tests. Test GREEN phase with various result combinations. Verify state transitions and activity logging.", + "parentId": "undefined" + }, + { + "id": 5, + "title": "Implement autopilot_commit tool with git operations", + "description": "Create MCP tool for automated commit creation with git staging, message generation, and metadata embedding.", + "dependencies": [1], + "details": "Build autopilot_commit tool that stages specified files, generates appropriate commit messages based on subtask context and phase, and creates commits with embedded metadata. Integrate with GitAdapter for safety checks and branch validation. Include options for commit message templates and metadata formats. Handle git errors gracefully and provide clear feedback. Support dry-run mode for validation without committing.", + "status": "done", + "testStrategy": "Integration tests with GitAdapter and temporary git repositories. Test commit creation with various file sets. Verify metadata embedding and message generation. Test error handling for git failures.", + "parentId": "undefined" + } + ], + "complexity": 6, + "recommendedSubtasks": 5, + "expansionPrompt": "Organize MCP tools into: 1) Tool schema definition and parameter validation, 2) `autopilot_start` and `autopilot_resume` tool implementation, 3) `autopilot_next` and `autopilot_status` tools with context provision, 4) `autopilot_complete_phase` tool with validation integration, and 5) `autopilot_commit` tool with git operations. Ensure parity with CLI functionality and proper error handling." + }, + { + "id": 9, + "title": "Write AI Agent Integration Documentation and Templates", + "description": "Document the autonomous workflow for AI agents, including example prompts, expected behaviors, and integration instructions.", + "details": "Create `CLAUDE.md` (or equivalent) with step-by-step instructions, example CLI/MCP usage, and responsibility matrix (AI vs TaskMaster). Include template prompts for test generation. Document how to parse test output, report results, and handle errors. Provide example JSON inputs/outputs for MCP tools. Keep documentation in the project's docs directory, versioned alongside code.", + "testStrategy": "Manual review of documentation for clarity and completeness. Verify example commands work against a test project. Test AI agent onboarding with the provided instructions.", + "priority": "low", + "dependencies": ["6", "7", "8"], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Create comprehensive workflow documentation with step-by-step examples", + "description": "Write detailed documentation covering the complete AI agent workflow integration, including command usage patterns, integration examples, and responsibility matrix between AI agents and TaskMaster.", + "dependencies": [], + "details": "Create comprehensive workflow documentation in the docs directory that includes: step-by-step integration instructions for AI agents, example CLI and MCP command usage patterns, clear responsibility matrix defining what AI agents handle vs TaskMaster, example JSON inputs/outputs for MCP tools, error handling patterns, and troubleshooting guides. Document the autonomous workflow phases and expected behaviors at each step.", + "status": "done", + "testStrategy": "Manual review for clarity and completeness. Verify all example commands work against a test project setup.", + "parentId": "undefined" + }, + { + "id": 2, + "title": "Create CLAUDE.md template and example prompts for AI integration", + "description": "Develop template files and example prompts that enable seamless AI agent onboarding and integration with the TaskMaster workflow system.", + "dependencies": [1], + "details": "Create CLAUDE.md template with integration instructions, develop example prompts for test generation and workflow automation, create template JSON structures for MCP tool interactions, document test output parsing patterns, and provide example error handling scenarios. Include practical integration guidance and ready-to-use prompt templates that AI agents can adapt for their specific use cases.", + "status": "done", + "testStrategy": "Test AI agent onboarding using the provided templates and instructions. Verify example prompts produce expected results.", + "parentId": "undefined" + } + ], + "complexity": 2, + "recommendedSubtasks": 2, + "expansionPrompt": "Structure documentation into: 1) Comprehensive workflow documentation with step-by-step examples, command usage, and integration patterns, and 2) Template creation for CLAUDE.md integration, example prompts, and troubleshooting guides. Focus on clear examples and practical integration guidance." + }, + { + "id": 10, + "title": "Implement Configuration System and Project Hygiene", + "description": "Set up `config.json` for workflow customization and ensure project directory hygiene (gitignore rules, no runtime artifacts).", + "details": "Define schema for `config.json`: autopilot settings, commit templates, test thresholds, branch patterns, etc. Use `ajv` (v8+) for schema validation. Update `.gitignore` to exclude `state/` and `reports/` directories, but keep `tasks/`, `config.json`, and `docs/` versioned. Provide default configs. Validate config on workflow start.", + "testStrategy": "Unit tests for config schema and validation. Integration tests to verify ignored directories are not committed. Test config overrides and defaults.", + "priority": "medium", + "dependencies": ["1"], + "status": "done", + "subtasks": [ + { + "id": 1, + "title": "Define Configuration Schema with AJV Validation", + "description": "Create comprehensive JSON schema for config.json including autopilot settings, commit templates, test thresholds, and branch patterns with AJV validation.", + "dependencies": [], + "details": "Define TypeScript interfaces and JSON schema for configuration options including autopilot mode settings, commit message templates, test coverage thresholds, branch naming patterns, and workflow preferences. Implement schema validation using ajv v8+ with detailed error reporting. Create separate schema files for modularity and maintainability.", + "status": "done", + "testStrategy": "Unit tests for schema validation with valid/invalid config objects. Test error message clarity and schema constraint enforcement.", + "parentId": "undefined" + }, + { + "id": 2, + "title": "Implement Default Configuration Loading System", + "description": "Build configuration loading mechanism with defaults, environment overrides, and user customization support.", + "dependencies": [1], + "details": "Create ConfigManager class to load default configuration, merge with user-provided config.json, and handle environment variable overrides. Implement configuration hierarchy: defaults < config.json < environment variables. Provide methods to get, set, and validate configuration values at runtime. Include configuration file creation for new projects.", + "status": "done", + "testStrategy": "Unit tests for configuration loading order, merging logic, and environment variable handling. Integration tests with sample config files.", + "parentId": "undefined" + }, + { + "id": 3, + "title": "Setup Gitignore Management and Directory Hygiene", + "description": "Update .gitignore rules to exclude runtime artifacts while preserving essential project files and implement directory hygiene checks.", + "dependencies": [], + "details": "Update .gitignore to exclude state/ and reports/ directories while keeping tasks/, config.json, and docs/ versioned. Implement utility functions to check project directory hygiene, verify no runtime artifacts are committed, and provide cleanup suggestions. Create directory structure validation for TaskMaster projects.", + "status": "done", + "testStrategy": "Integration tests to verify gitignore rules prevent committing excluded directories. Test directory hygiene validation and cleanup suggestions.", + "parentId": "undefined" + }, + { + "id": 4, + "title": "Build Configuration Validation and Error Reporting", + "description": "Implement startup configuration validation with comprehensive error reporting and user-friendly error messages.", + "dependencies": [1, 2], + "details": "Create validation pipeline that runs on workflow startup to verify configuration integrity. Implement detailed error reporting with specific field validation failures, suggested fixes, and examples of valid configurations. Add configuration health check command and validation summary reporting. Ensure graceful handling of missing or corrupted config files.", + "status": "done", + "testStrategy": "Unit tests for validation error messages and suggested fixes. Integration tests for startup validation flow and error handling scenarios.", + "parentId": "undefined" + } + ], + "complexity": 5, + "recommendedSubtasks": 4, + "expansionPrompt": "Structure configuration into: 1) Configuration schema definition with comprehensive validation using ajv, 2) Default configuration setup and loading mechanisms, 3) Gitignore management and project directory hygiene rules, and 4) Configuration validation and error reporting. Ensure configurations are validated on startup and provide clear error messages for invalid settings." + } + ], + "metadata": { + "version": "1.0.0", + "lastModified": "2025-10-10T13:16:43.150Z", + "taskCount": 10, + "completedCount": 2, + "tags": ["tdd-phase-1-core-rails"], + "created": "2025-10-10T13:35:33.065Z", + "description": "Tasks for tdd-phase-1-core-rails context", + "updated": "2025-10-10T16:07:52.412Z" + } } -} \ No newline at end of file +} diff --git a/CHANGELOG.md b/CHANGELOG.md index f304d8ec..e16d0f60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,50 @@ # task-master-ai +## 0.30.0-rc.0 + +### Minor Changes + +- [#1181](https://github.com/eyaltoledano/claude-task-master/pull/1181) [`a69d8c9`](https://github.com/eyaltoledano/claude-task-master/commit/a69d8c91dc9205a3fdaf9d32276144fa3bcad55d) Thanks [@karol-f](https://github.com/karol-f)! - Add configurable MCP tool loading to optimize LLM context usage + + You can now control which Task Master MCP tools are loaded by setting the `TASK_MASTER_TOOLS` environment variable in your MCP configuration. This helps reduce context usage for LLMs by only loading the tools you need. + + **Configuration Options:** + - `all` (default): Load all 36 tools + - `core` or `lean`: Load only 7 essential tools for daily development + - Includes: `get_tasks`, `next_task`, `get_task`, `set_task_status`, `update_subtask`, `parse_prd`, `expand_task` + - `standard`: Load 15 commonly used tools (all core tools plus 8 more) + - Additional tools: `initialize_project`, `analyze_project_complexity`, `expand_all`, `add_subtask`, `remove_task`, `generate`, `add_task`, `complexity_report` + - Custom list: Comma-separated tool names (e.g., `get_tasks,next_task,set_task_status`) + + **Example .mcp.json configuration:** + + ```json + { + "mcpServers": { + "task-master-ai": { + "command": "npx", + "args": ["-y", "task-master-ai"], + "env": { + "TASK_MASTER_TOOLS": "standard", + "ANTHROPIC_API_KEY": "your_key_here" + } + } + } + } + ``` + + For complete details on all available tools, configuration examples, and usage guidelines, see the [MCP Tools documentation](https://docs.task-master.dev/capabilities/mcp#configurable-tool-loading). + +- [#1312](https://github.com/eyaltoledano/claude-task-master/pull/1312) [`d7fca18`](https://github.com/eyaltoledano/claude-task-master/commit/d7fca1844f24ad8ce079c21d9799a3c4b4413381) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Improve next command to work with remote + +### Patch Changes + +- [#1314](https://github.com/eyaltoledano/claude-task-master/pull/1314) [`6bc75c0`](https://github.com/eyaltoledano/claude-task-master/commit/6bc75c0ac68b59cb10cee70574a689f83e4de768) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Improve auth token refresh flow + +- [#1302](https://github.com/eyaltoledano/claude-task-master/pull/1302) [`3283506`](https://github.com/eyaltoledano/claude-task-master/commit/3283506444d59896ecb97721ef2e96e290eb84d3) Thanks [@bjcoombs](https://github.com/bjcoombs)! - Enable Task Master commands to traverse parent directories to find project root from nested paths + + Fixes #1301 + ## 0.29.0 ### Minor Changes diff --git a/CLAUDE.md b/CLAUDE.md index a7dd5e1a..026d127f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,13 +6,20 @@ ## Test Guidelines +### Test File Placement + +- **Package & tests**: Place in `packages/<package-name>/src/<module>/<file>.spec.ts` or `apps/<app-name>/src/<module>/<file.spec.ts>` alongside source +- **Package integration tests**: Place in `packages/<package-name>/tests/integration/<module>/<file>.test.ts` or `apps/<app-name>/tests/integration/<module>/<file>.test.ts` alongside source +- **Isolated unit tests**: Use `tests/unit/packages/<package-name>/` only when parallel placement isn't possible +- **Test extension**: Always use `.ts` for TypeScript tests, never `.js` + ### Synchronous Tests - **NEVER use async/await in test functions** unless testing actual asynchronous operations - Use synchronous top-level imports instead of dynamic `await import()` - Test bodies should be synchronous whenever possible - Example: - ```javascript - // ✅ CORRECT - Synchronous imports + ```typescript + // ✅ CORRECT - Synchronous imports with .ts extension import { MyClass } from '../src/my-class.js'; it('should verify behavior', () => { @@ -26,6 +33,11 @@ }); ``` +## Documentation Guidelines + +- **Documentation location**: Write docs in `apps/docs/` (Mintlify site source), not `docs/` +- **Documentation URL**: Reference docs at https://docs.task-master.dev, not local file paths + ## Changeset Guidelines - When creating changesets, remember that it's user-facing, meaning we don't have to get into the specifics of the code, but rather mention what the end-user is getting or fixing from this changeset. \ No newline at end of file diff --git a/apps/cli/src/command-registry.ts b/apps/cli/src/command-registry.ts index 20721242..ef6c35d2 100644 --- a/apps/cli/src/command-registry.ts +++ b/apps/cli/src/command-registry.ts @@ -14,6 +14,7 @@ import { ContextCommand } from './commands/context.command.js'; import { StartCommand } from './commands/start.command.js'; import { SetStatusCommand } from './commands/set-status.command.js'; import { ExportCommand } from './commands/export.command.js'; +import { AutopilotCommand } from './commands/autopilot/index.js'; /** * Command metadata for registration @@ -70,6 +71,13 @@ export class CommandRegistry { commandClass: ExportCommand as any, category: 'task' }, + { + name: 'autopilot', + description: + 'AI agent orchestration for TDD workflow (start, resume, next, complete, commit, status, abort)', + commandClass: AutopilotCommand as any, + category: 'development' + }, // Authentication & Context Commands { diff --git a/apps/cli/src/commands/autopilot.command.ts b/apps/cli/src/commands/autopilot.command.ts new file mode 100644 index 00000000..75608631 --- /dev/null +++ b/apps/cli/src/commands/autopilot.command.ts @@ -0,0 +1,515 @@ +/** + * @fileoverview AutopilotCommand using Commander's native class pattern + * Extends Commander.Command for better integration with the framework + * This is a thin presentation layer over @tm/core's autopilot functionality + */ + +import { Command } from 'commander'; +import chalk from 'chalk'; +import boxen from 'boxen'; +import ora, { type Ora } from 'ora'; +import { + createTaskMasterCore, + type TaskMasterCore, + type Task, + type Subtask +} from '@tm/core'; +import * as ui from '../utils/ui.js'; + +/** + * CLI-specific options interface for the autopilot command + */ +export interface AutopilotCommandOptions { + format?: 'text' | 'json'; + project?: string; + dryRun?: boolean; +} + +/** + * Preflight check result for a single check + */ +export interface PreflightCheckResult { + success: boolean; + message?: string; +} + +/** + * Overall preflight check results + */ +export interface PreflightResult { + success: boolean; + testCommand: PreflightCheckResult; + gitWorkingTree: PreflightCheckResult; + requiredTools: PreflightCheckResult; + defaultBranch: PreflightCheckResult; +} + +/** + * CLI-specific result type from autopilot command + */ +export interface AutopilotCommandResult { + success: boolean; + taskId: string; + task?: Task; + error?: string; + message?: string; +} + +/** + * AutopilotCommand extending Commander's Command class + * This is a thin presentation layer over @tm/core's autopilot functionality + */ +export class AutopilotCommand extends Command { + private tmCore?: TaskMasterCore; + private lastResult?: AutopilotCommandResult; + + constructor(name?: string) { + super(name || 'autopilot'); + + // Configure the command + this.description( + 'Execute a task autonomously using TDD workflow with git integration' + ) + .argument('<taskId>', 'Task ID to execute autonomously') + .option('-f, --format <format>', 'Output format (text, json)', 'text') + .option('-p, --project <path>', 'Project root directory', process.cwd()) + .option( + '--dry-run', + 'Show what would be executed without performing actions' + ) + .action(async (taskId: string, options: AutopilotCommandOptions) => { + await this.executeCommand(taskId, options); + }); + } + + /** + * Execute the autopilot command + */ + private async executeCommand( + taskId: string, + options: AutopilotCommandOptions + ): Promise<void> { + let spinner: Ora | null = null; + + try { + // Validate options + if (!this.validateOptions(options)) { + process.exit(1); + } + + // Validate task ID format + if (!this.validateTaskId(taskId)) { + ui.displayError(`Invalid task ID format: ${taskId}`); + process.exit(1); + } + + // Initialize tm-core with spinner + spinner = ora('Initializing Task Master...').start(); + await this.initializeCore(options.project || process.cwd()); + spinner.succeed('Task Master initialized'); + + // Load and validate task existence + spinner = ora(`Loading task ${taskId}...`).start(); + const task = await this.loadTask(taskId); + + if (!task) { + spinner.fail(`Task ${taskId} not found`); + ui.displayError(`Task with ID ${taskId} does not exist`); + process.exit(1); + } + + spinner.succeed(`Task ${taskId} loaded`); + + // Display task information + this.displayTaskInfo(task, options.dryRun || false); + + // Execute autopilot logic (placeholder for now) + const result = await this.performAutopilot(taskId, task, options); + + // Store result for programmatic access + this.setLastResult(result); + + // Display results + this.displayResults(result, options); + } catch (error: unknown) { + if (spinner) { + spinner.fail('Operation failed'); + } + this.handleError(error); + process.exit(1); + } + } + + /** + * Validate command options + */ + private validateOptions(options: AutopilotCommandOptions): boolean { + // Validate format + if (options.format && !['text', 'json'].includes(options.format)) { + console.error(chalk.red(`Invalid format: ${options.format}`)); + console.error(chalk.gray(`Valid formats: text, json`)); + return false; + } + + return true; + } + + /** + * Validate task ID format + */ + private validateTaskId(taskId: string): boolean { + // Task ID should be a number or number.number format (e.g., "1" or "1.2") + const taskIdPattern = /^\d+(\.\d+)*$/; + return taskIdPattern.test(taskId); + } + + /** + * Initialize TaskMasterCore + */ + private async initializeCore(projectRoot: string): Promise<void> { + if (!this.tmCore) { + this.tmCore = await createTaskMasterCore({ projectPath: projectRoot }); + } + } + + /** + * Load task from tm-core + */ + private async loadTask(taskId: string): Promise<Task | null> { + if (!this.tmCore) { + throw new Error('TaskMasterCore not initialized'); + } + + try { + const { task } = await this.tmCore.getTaskWithSubtask(taskId); + return task; + } catch (error) { + return null; + } + } + + /** + * Display task information before execution + */ + private displayTaskInfo(task: Task, isDryRun: boolean): void { + const prefix = isDryRun ? '[DRY RUN] ' : ''; + console.log(); + console.log( + boxen( + chalk.cyan.bold(`${prefix}Autopilot Task Execution`) + + '\n\n' + + chalk.white(`Task ID: ${task.id}`) + + '\n' + + chalk.white(`Title: ${task.title}`) + + '\n' + + chalk.white(`Status: ${task.status}`) + + (task.description ? '\n\n' + chalk.gray(task.description) : ''), + { + padding: 1, + borderStyle: 'round', + borderColor: 'cyan', + width: process.stdout.columns ? process.stdout.columns * 0.95 : 100 + } + ) + ); + console.log(); + } + + /** + * Perform autopilot execution using PreflightChecker and TaskLoader + */ + private async performAutopilot( + taskId: string, + task: Task, + options: AutopilotCommandOptions + ): Promise<AutopilotCommandResult> { + // Run preflight checks + const preflightResult = await this.runPreflightChecks(options); + if (!preflightResult.success) { + return { + success: false, + taskId, + task, + error: 'Preflight checks failed', + message: 'Please resolve the issues above before running autopilot' + }; + } + + // Validate task structure and get execution order + const validationResult = await this.validateTaskStructure( + taskId, + task, + options + ); + if (!validationResult.success) { + return validationResult; + } + + // Display execution plan + this.displayExecutionPlan( + validationResult.task!, + validationResult.orderedSubtasks!, + options + ); + + return { + success: true, + taskId, + task: validationResult.task, + message: options.dryRun + ? 'Dry run completed successfully' + : 'Autopilot execution ready (actual execution not yet implemented)' + }; + } + + /** + * Run preflight checks and display results + */ + private async runPreflightChecks( + options: AutopilotCommandOptions + ): Promise<PreflightResult> { + const { PreflightChecker } = await import('@tm/core'); + + console.log(); + console.log(chalk.cyan.bold('Running preflight checks...')); + + const preflightChecker = new PreflightChecker( + options.project || process.cwd() + ); + const result = await preflightChecker.runAllChecks(); + + this.displayPreflightResults(result); + + return result; + } + + /** + * Validate task structure and get execution order + */ + private async validateTaskStructure( + taskId: string, + task: Task, + options: AutopilotCommandOptions + ): Promise<AutopilotCommandResult & { orderedSubtasks?: Subtask[] }> { + const { TaskLoaderService } = await import('@tm/core'); + + console.log(); + console.log(chalk.cyan.bold('Validating task structure...')); + + const taskLoader = new TaskLoaderService(options.project || process.cwd()); + const validationResult = await taskLoader.loadAndValidateTask(taskId); + + if (!validationResult.success) { + await taskLoader.cleanup(); + return { + success: false, + taskId, + task, + error: validationResult.errorMessage, + message: validationResult.suggestion + }; + } + + const orderedSubtasks = taskLoader.getExecutionOrder( + validationResult.task! + ); + + await taskLoader.cleanup(); + + return { + success: true, + taskId, + task: validationResult.task, + orderedSubtasks + }; + } + + /** + * Display execution plan with subtasks and TDD workflow + */ + private displayExecutionPlan( + task: Task, + orderedSubtasks: Subtask[], + options: AutopilotCommandOptions + ): void { + console.log(); + console.log(chalk.green.bold('✓ All checks passed!')); + console.log(); + console.log(chalk.cyan.bold('Execution Plan:')); + console.log(chalk.white(`Task: ${task.title}`)); + console.log( + chalk.gray( + `${orderedSubtasks.length} subtasks will be executed in dependency order` + ) + ); + console.log(); + + // Display subtasks + orderedSubtasks.forEach((subtask: Subtask, index: number) => { + console.log( + chalk.yellow(`${index + 1}. ${task.id}.${subtask.id}: ${subtask.title}`) + ); + if (subtask.dependencies && subtask.dependencies.length > 0) { + console.log( + chalk.gray(` Dependencies: ${subtask.dependencies.join(', ')}`) + ); + } + }); + + console.log(); + console.log( + chalk.cyan('Autopilot would execute each subtask using TDD workflow:') + ); + console.log(chalk.gray(' 1. RED phase: Write failing test')); + console.log(chalk.gray(' 2. GREEN phase: Implement code to pass test')); + console.log(chalk.gray(' 3. COMMIT phase: Commit changes')); + console.log(); + + if (options.dryRun) { + console.log( + chalk.yellow('This was a dry run. Use without --dry-run to execute.') + ); + } + } + + /** + * Display preflight check results + */ + private displayPreflightResults(result: PreflightResult): void { + const checks = [ + { name: 'Test command', result: result.testCommand }, + { name: 'Git working tree', result: result.gitWorkingTree }, + { name: 'Required tools', result: result.requiredTools }, + { name: 'Default branch', result: result.defaultBranch } + ]; + + checks.forEach((check) => { + const icon = check.result.success ? chalk.green('✓') : chalk.red('✗'); + const status = check.result.success + ? chalk.green('PASS') + : chalk.red('FAIL'); + console.log(`${icon} ${chalk.white(check.name)}: ${status}`); + if (check.result.message) { + console.log(chalk.gray(` ${check.result.message}`)); + } + }); + } + + /** + * Display results based on format + */ + private displayResults( + result: AutopilotCommandResult, + options: AutopilotCommandOptions + ): void { + const format = options.format || 'text'; + + switch (format) { + case 'json': + this.displayJson(result); + break; + + case 'text': + default: + this.displayTextResult(result); + break; + } + } + + /** + * Display in JSON format + */ + private displayJson(result: AutopilotCommandResult): void { + console.log(JSON.stringify(result, null, 2)); + } + + /** + * Display result in text format + */ + private displayTextResult(result: AutopilotCommandResult): void { + if (result.success) { + console.log( + boxen( + chalk.green.bold('✓ Autopilot Command Completed') + + '\n\n' + + chalk.white(result.message || 'Execution complete'), + { + padding: 1, + borderStyle: 'round', + borderColor: 'green', + margin: { top: 1 } + } + ) + ); + } else { + console.log( + boxen( + chalk.red.bold('✗ Autopilot Command Failed') + + '\n\n' + + chalk.white(result.error || 'Unknown error'), + { + padding: 1, + borderStyle: 'round', + borderColor: 'red', + margin: { top: 1 } + } + ) + ); + } + } + + /** + * Handle general errors + */ + private handleError(error: unknown): void { + const errorObj = error as { + getSanitizedDetails?: () => { message: string }; + message?: string; + stack?: string; + }; + + const msg = errorObj?.getSanitizedDetails?.() ?? { + message: errorObj?.message ?? String(error) + }; + console.error(chalk.red(`Error: ${msg.message || 'Unexpected error'}`)); + + // Show stack trace in development mode or when DEBUG is set + const isDevelopment = process.env.NODE_ENV !== 'production'; + if ((isDevelopment || process.env.DEBUG) && errorObj.stack) { + console.error(chalk.gray(errorObj.stack)); + } + } + + /** + * Set the last result for programmatic access + */ + private setLastResult(result: AutopilotCommandResult): void { + this.lastResult = result; + } + + /** + * Get the last result (for programmatic usage) + */ + getLastResult(): AutopilotCommandResult | undefined { + return this.lastResult; + } + + /** + * Clean up resources + */ + async cleanup(): Promise<void> { + if (this.tmCore) { + await this.tmCore.close(); + this.tmCore = undefined; + } + } + + /** + * Register this command on an existing program + */ + static register(program: Command, name?: string): AutopilotCommand { + const autopilotCommand = new AutopilotCommand(name); + program.addCommand(autopilotCommand); + return autopilotCommand; + } +} diff --git a/apps/cli/src/commands/autopilot/abort.command.ts b/apps/cli/src/commands/autopilot/abort.command.ts new file mode 100644 index 00000000..936ef1fa --- /dev/null +++ b/apps/cli/src/commands/autopilot/abort.command.ts @@ -0,0 +1,119 @@ +/** + * @fileoverview Abort Command - Safely terminate workflow + */ + +import { Command } from 'commander'; +import { WorkflowOrchestrator } from '@tm/core'; +import { + AutopilotBaseOptions, + hasWorkflowState, + loadWorkflowState, + deleteWorkflowState, + OutputFormatter +} from './shared.js'; +import inquirer from 'inquirer'; + +interface AbortOptions extends AutopilotBaseOptions { + force?: boolean; +} + +/** + * Abort Command - Safely terminate workflow and clean up state + */ +export class AbortCommand extends Command { + constructor() { + super('abort'); + + this.description('Abort the current TDD workflow and clean up state') + .option('-f, --force', 'Force abort without confirmation') + .action(async (options: AbortOptions) => { + await this.execute(options); + }); + } + + private async execute(options: AbortOptions): Promise<void> { + // Inherit parent options + const parentOpts = this.parent?.opts() as AutopilotBaseOptions; + const mergedOptions: AbortOptions = { + ...parentOpts, + ...options, + projectRoot: + options.projectRoot || parentOpts?.projectRoot || process.cwd() + }; + + const formatter = new OutputFormatter(mergedOptions.json || false); + + try { + // Check for workflow state + const hasState = await hasWorkflowState(mergedOptions.projectRoot!); + if (!hasState) { + formatter.warning('No active workflow to abort'); + return; + } + + // Load state + const state = await loadWorkflowState(mergedOptions.projectRoot!); + if (!state) { + formatter.error('Failed to load workflow state'); + process.exit(1); + } + + // Restore orchestrator + const orchestrator = new WorkflowOrchestrator(state.context); + orchestrator.restoreState(state); + + // Get progress before abort + const progress = orchestrator.getProgress(); + const currentSubtask = orchestrator.getCurrentSubtask(); + + // Confirm abort if not forced or in JSON mode + if (!mergedOptions.force && !mergedOptions.json) { + const { confirmed } = await inquirer.prompt([ + { + type: 'confirm', + name: 'confirmed', + message: + `This will abort the workflow for task ${state.context.taskId}. ` + + `Progress: ${progress.completed}/${progress.total} subtasks completed. ` + + `Continue?`, + default: false + } + ]); + + if (!confirmed) { + formatter.info('Abort cancelled'); + return; + } + } + + // Trigger abort in orchestrator + orchestrator.transition({ type: 'ABORT' }); + + // Delete workflow state + await deleteWorkflowState(mergedOptions.projectRoot!); + + // Output result + formatter.success('Workflow aborted', { + taskId: state.context.taskId, + branchName: state.context.branchName, + progress: { + completed: progress.completed, + total: progress.total + }, + lastSubtask: currentSubtask + ? { + id: currentSubtask.id, + title: currentSubtask.title + } + : null, + note: 'Branch and commits remain. Clean up manually if needed.' + }); + } catch (error) { + formatter.error((error as Error).message); + if (mergedOptions.verbose) { + console.error((error as Error).stack); + } + process.exit(1); + } + } +} diff --git a/apps/cli/src/commands/autopilot/commit.command.ts b/apps/cli/src/commands/autopilot/commit.command.ts new file mode 100644 index 00000000..3a56f66b --- /dev/null +++ b/apps/cli/src/commands/autopilot/commit.command.ts @@ -0,0 +1,169 @@ +/** + * @fileoverview Commit Command - Create commit with enhanced message generation + */ + +import { Command } from 'commander'; +import { WorkflowOrchestrator } from '@tm/core'; +import { + AutopilotBaseOptions, + hasWorkflowState, + loadWorkflowState, + createGitAdapter, + createCommitMessageGenerator, + OutputFormatter, + saveWorkflowState +} from './shared.js'; + +type CommitOptions = AutopilotBaseOptions; + +/** + * Commit Command - Create commit using enhanced message generator + */ +export class CommitCommand extends Command { + constructor() { + super('commit'); + + this.description('Create a commit for the completed GREEN phase').action( + async (options: CommitOptions) => { + await this.execute(options); + } + ); + } + + private async execute(options: CommitOptions): Promise<void> { + // Inherit parent options + const parentOpts = this.parent?.opts() as AutopilotBaseOptions; + const mergedOptions: CommitOptions = { + ...parentOpts, + ...options, + projectRoot: + options.projectRoot || parentOpts?.projectRoot || process.cwd() + }; + + const formatter = new OutputFormatter(mergedOptions.json || false); + + try { + // Check for workflow state + const hasState = await hasWorkflowState(mergedOptions.projectRoot!); + if (!hasState) { + formatter.error('No active workflow', { + suggestion: 'Start a workflow with: autopilot start <taskId>' + }); + process.exit(1); + } + + // Load state + const state = await loadWorkflowState(mergedOptions.projectRoot!); + if (!state) { + formatter.error('Failed to load workflow state'); + process.exit(1); + } + + const orchestrator = new WorkflowOrchestrator(state.context); + orchestrator.restoreState(state); + orchestrator.enableAutoPersist(async (newState) => { + await saveWorkflowState(mergedOptions.projectRoot!, newState); + }); + + // Verify in COMMIT phase + const tddPhase = orchestrator.getCurrentTDDPhase(); + if (tddPhase !== 'COMMIT') { + formatter.error('Not in COMMIT phase', { + currentPhase: tddPhase || orchestrator.getCurrentPhase(), + suggestion: 'Complete RED and GREEN phases first' + }); + process.exit(1); + } + + // Get current subtask + const currentSubtask = orchestrator.getCurrentSubtask(); + if (!currentSubtask) { + formatter.error('No current subtask'); + process.exit(1); + } + + // Initialize git adapter + const gitAdapter = createGitAdapter(mergedOptions.projectRoot!); + await gitAdapter.ensureGitRepository(); + + // Check for staged changes + const hasStagedChanges = await gitAdapter.hasStagedChanges(); + if (!hasStagedChanges) { + // Stage all changes + formatter.info('No staged changes, staging all changes...'); + await gitAdapter.stageFiles(['.']); + } + + // Get changed files for scope detection + const status = await gitAdapter.getStatus(); + const changedFiles = [...status.staged, ...status.modified]; + + // Generate commit message + const messageGenerator = createCommitMessageGenerator(); + const testResults = state.context.lastTestResults; + + const commitMessage = messageGenerator.generateMessage({ + type: 'feat', + description: currentSubtask.title, + changedFiles, + taskId: state.context.taskId, + phase: 'TDD', + tag: (state.context.metadata.tag as string) || undefined, + testsPassing: testResults?.passed, + testsFailing: testResults?.failed, + coveragePercent: undefined // Could be added if available + }); + + // Create commit with metadata + await gitAdapter.createCommit(commitMessage, { + metadata: { + taskId: state.context.taskId, + subtaskId: currentSubtask.id, + phase: 'COMMIT', + tddCycle: 'complete' + } + }); + + // Get commit info + const lastCommit = await gitAdapter.getLastCommit(); + + // Complete COMMIT phase (this marks subtask as completed) + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + + // Check if should advance to next subtask + const progress = orchestrator.getProgress(); + if (progress.current < progress.total) { + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + } else { + // All subtasks complete + orchestrator.transition({ type: 'ALL_SUBTASKS_COMPLETE' }); + } + + // Output success + formatter.success('Commit created', { + commitHash: lastCommit.hash.substring(0, 7), + message: commitMessage.split('\n')[0], // First line only + subtask: { + id: currentSubtask.id, + title: currentSubtask.title, + status: currentSubtask.status + }, + progress: { + completed: progress.completed, + total: progress.total, + percentage: progress.percentage + }, + nextAction: + progress.completed < progress.total + ? 'Start next subtask with RED phase' + : 'All subtasks complete. Run: autopilot status' + }); + } catch (error) { + formatter.error((error as Error).message); + if (mergedOptions.verbose) { + console.error((error as Error).stack); + } + process.exit(1); + } + } +} diff --git a/apps/cli/src/commands/autopilot/complete.command.ts b/apps/cli/src/commands/autopilot/complete.command.ts new file mode 100644 index 00000000..77ef4cd3 --- /dev/null +++ b/apps/cli/src/commands/autopilot/complete.command.ts @@ -0,0 +1,172 @@ +/** + * @fileoverview Complete Command - Complete current TDD phase with validation + */ + +import { Command } from 'commander'; +import { WorkflowOrchestrator, TestResult } from '@tm/core'; +import { + AutopilotBaseOptions, + hasWorkflowState, + loadWorkflowState, + OutputFormatter +} from './shared.js'; + +interface CompleteOptions extends AutopilotBaseOptions { + results?: string; + coverage?: string; +} + +/** + * Complete Command - Mark current phase as complete with validation + */ +export class CompleteCommand extends Command { + constructor() { + super('complete'); + + this.description('Complete the current TDD phase with result validation') + .option( + '-r, --results <json>', + 'Test results JSON (with total, passed, failed, skipped)' + ) + .option('-c, --coverage <percent>', 'Coverage percentage') + .action(async (options: CompleteOptions) => { + await this.execute(options); + }); + } + + private async execute(options: CompleteOptions): Promise<void> { + // Inherit parent options + const parentOpts = this.parent?.opts() as AutopilotBaseOptions; + const mergedOptions: CompleteOptions = { + ...parentOpts, + ...options, + projectRoot: + options.projectRoot || parentOpts?.projectRoot || process.cwd() + }; + + const formatter = new OutputFormatter(mergedOptions.json || false); + + try { + // Check for workflow state + const hasState = await hasWorkflowState(mergedOptions.projectRoot!); + if (!hasState) { + formatter.error('No active workflow', { + suggestion: 'Start a workflow with: autopilot start <taskId>' + }); + process.exit(1); + } + + // Load state + const state = await loadWorkflowState(mergedOptions.projectRoot!); + if (!state) { + formatter.error('Failed to load workflow state'); + process.exit(1); + } + + // Restore orchestrator with persistence + const { saveWorkflowState } = await import('./shared.js'); + const orchestrator = new WorkflowOrchestrator(state.context); + orchestrator.restoreState(state); + orchestrator.enableAutoPersist(async (newState) => { + await saveWorkflowState(mergedOptions.projectRoot!, newState); + }); + + // Get current phase + const tddPhase = orchestrator.getCurrentTDDPhase(); + const currentSubtask = orchestrator.getCurrentSubtask(); + + if (!tddPhase) { + formatter.error('Not in a TDD phase', { + phase: orchestrator.getCurrentPhase() + }); + process.exit(1); + } + + // Validate based on phase + if (tddPhase === 'RED' || tddPhase === 'GREEN') { + if (!mergedOptions.results) { + formatter.error('Test results required for RED/GREEN phase', { + usage: + '--results \'{"total":10,"passed":9,"failed":1,"skipped":0}\'' + }); + process.exit(1); + } + + // Parse test results + let testResults: TestResult; + try { + const parsed = JSON.parse(mergedOptions.results); + testResults = { + total: parsed.total || 0, + passed: parsed.passed || 0, + failed: parsed.failed || 0, + skipped: parsed.skipped || 0, + phase: tddPhase + }; + } catch (error) { + formatter.error('Invalid test results JSON', { + error: (error as Error).message + }); + process.exit(1); + } + + // Validate RED phase requirements + if (tddPhase === 'RED' && testResults.failed === 0) { + formatter.error('RED phase validation failed', { + reason: 'At least one test must be failing', + actual: { + passed: testResults.passed, + failed: testResults.failed + } + }); + process.exit(1); + } + + // Validate GREEN phase requirements + if (tddPhase === 'GREEN' && testResults.failed !== 0) { + formatter.error('GREEN phase validation failed', { + reason: 'All tests must pass', + actual: { + passed: testResults.passed, + failed: testResults.failed + } + }); + process.exit(1); + } + + // Complete phase with test results + if (tddPhase === 'RED') { + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults + }); + formatter.success('RED phase completed', { + nextPhase: 'GREEN', + testResults, + subtask: currentSubtask?.title + }); + } else { + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults + }); + formatter.success('GREEN phase completed', { + nextPhase: 'COMMIT', + testResults, + subtask: currentSubtask?.title, + suggestion: 'Run: autopilot commit' + }); + } + } else if (tddPhase === 'COMMIT') { + formatter.error('Use "autopilot commit" to complete COMMIT phase'); + process.exit(1); + } + } catch (error) { + formatter.error((error as Error).message); + if (mergedOptions.verbose) { + console.error((error as Error).stack); + } + process.exit(1); + } + } +} diff --git a/apps/cli/src/commands/autopilot/index.ts b/apps/cli/src/commands/autopilot/index.ts new file mode 100644 index 00000000..9eb54609 --- /dev/null +++ b/apps/cli/src/commands/autopilot/index.ts @@ -0,0 +1,82 @@ +/** + * @fileoverview Autopilot CLI Commands for AI Agent Orchestration + * Provides subcommands for starting, resuming, and advancing the TDD workflow + * with JSON output for machine parsing. + */ + +import { Command } from 'commander'; +import { StartCommand } from './start.command.js'; +import { ResumeCommand } from './resume.command.js'; +import { NextCommand } from './next.command.js'; +import { CompleteCommand } from './complete.command.js'; +import { CommitCommand } from './commit.command.js'; +import { StatusCommand } from './status.command.js'; +import { AbortCommand } from './abort.command.js'; + +/** + * Shared command options for all autopilot commands + */ +export interface AutopilotBaseOptions { + json?: boolean; + verbose?: boolean; + projectRoot?: string; +} + +/** + * AutopilotCommand with subcommands for TDD workflow orchestration + */ +export class AutopilotCommand extends Command { + constructor() { + super('autopilot'); + + // Configure main command + this.description('AI agent orchestration for TDD workflow execution') + .alias('ap') + // Global options for all subcommands + .option('--json', 'Output in JSON format for machine parsing') + .option('-v, --verbose', 'Enable verbose output') + .option( + '-p, --project-root <path>', + 'Project root directory', + process.cwd() + ); + + // Register subcommands + this.registerSubcommands(); + } + + /** + * Register all autopilot subcommands + */ + private registerSubcommands(): void { + // Start new TDD workflow + this.addCommand(new StartCommand()); + + // Resume existing workflow + this.addCommand(new ResumeCommand()); + + // Get next action + this.addCommand(new NextCommand()); + + // Complete current phase + this.addCommand(new CompleteCommand()); + + // Create commit + this.addCommand(new CommitCommand()); + + // Show status + this.addCommand(new StatusCommand()); + + // Abort workflow + this.addCommand(new AbortCommand()); + } + + /** + * Register this command on an existing program + */ + static register(program: Command): AutopilotCommand { + const autopilotCommand = new AutopilotCommand(); + program.addCommand(autopilotCommand); + return autopilotCommand; + } +} diff --git a/apps/cli/src/commands/autopilot/next.command.ts b/apps/cli/src/commands/autopilot/next.command.ts new file mode 100644 index 00000000..71cdef4b --- /dev/null +++ b/apps/cli/src/commands/autopilot/next.command.ts @@ -0,0 +1,164 @@ +/** + * @fileoverview Next Command - Get next action in TDD workflow + */ + +import { Command } from 'commander'; +import { WorkflowOrchestrator } from '@tm/core'; +import { + AutopilotBaseOptions, + hasWorkflowState, + loadWorkflowState, + OutputFormatter +} from './shared.js'; + +type NextOptions = AutopilotBaseOptions; + +/** + * Next Command - Get next action details + */ +export class NextCommand extends Command { + constructor() { + super('next'); + + this.description( + 'Get the next action to perform in the TDD workflow' + ).action(async (options: NextOptions) => { + await this.execute(options); + }); + } + + private async execute(options: NextOptions): Promise<void> { + // Inherit parent options + const parentOpts = this.parent?.opts() as AutopilotBaseOptions; + const mergedOptions: NextOptions = { + ...parentOpts, + ...options, + projectRoot: + options.projectRoot || parentOpts?.projectRoot || process.cwd() + }; + + const formatter = new OutputFormatter(mergedOptions.json || false); + + try { + // Check for workflow state + const hasState = await hasWorkflowState(mergedOptions.projectRoot!); + if (!hasState) { + formatter.error('No active workflow', { + suggestion: 'Start a workflow with: autopilot start <taskId>' + }); + process.exit(1); + } + + // Load state + const state = await loadWorkflowState(mergedOptions.projectRoot!); + if (!state) { + formatter.error('Failed to load workflow state'); + process.exit(1); + } + + // Restore orchestrator + const orchestrator = new WorkflowOrchestrator(state.context); + orchestrator.restoreState(state); + + // Get current phase and subtask + const phase = orchestrator.getCurrentPhase(); + const tddPhase = orchestrator.getCurrentTDDPhase(); + const currentSubtask = orchestrator.getCurrentSubtask(); + + // Determine next action based on phase + let actionType: string; + let actionDescription: string; + let actionDetails: Record<string, unknown> = {}; + + if (phase === 'COMPLETE') { + formatter.success('Workflow complete', { + message: 'All subtasks have been completed', + taskId: state.context.taskId + }); + return; + } + + if (phase === 'SUBTASK_LOOP' && tddPhase) { + switch (tddPhase) { + case 'RED': + actionType = 'generate_test'; + actionDescription = 'Write failing test for current subtask'; + actionDetails = { + subtask: currentSubtask + ? { + id: currentSubtask.id, + title: currentSubtask.title, + attempts: currentSubtask.attempts + } + : null, + testCommand: 'npm test', // Could be customized based on config + expectedOutcome: 'Test should fail' + }; + break; + + case 'GREEN': + actionType = 'implement_code'; + actionDescription = 'Implement code to pass the failing test'; + actionDetails = { + subtask: currentSubtask + ? { + id: currentSubtask.id, + title: currentSubtask.title, + attempts: currentSubtask.attempts + } + : null, + testCommand: 'npm test', + expectedOutcome: 'All tests should pass', + lastTestResults: state.context.lastTestResults + }; + break; + + case 'COMMIT': + actionType = 'commit_changes'; + actionDescription = 'Commit the changes'; + actionDetails = { + subtask: currentSubtask + ? { + id: currentSubtask.id, + title: currentSubtask.title, + attempts: currentSubtask.attempts + } + : null, + suggestion: 'Use: autopilot commit' + }; + break; + + default: + actionType = 'unknown'; + actionDescription = 'Unknown TDD phase'; + } + } else { + actionType = 'workflow_phase'; + actionDescription = `Currently in ${phase} phase`; + } + + // Output next action + const output = { + action: actionType, + description: actionDescription, + phase, + tddPhase, + taskId: state.context.taskId, + branchName: state.context.branchName, + ...actionDetails + }; + + if (mergedOptions.json) { + formatter.output(output); + } else { + formatter.success('Next action', output); + } + } catch (error) { + formatter.error((error as Error).message); + if (mergedOptions.verbose) { + console.error((error as Error).stack); + } + process.exit(1); + } + } +} diff --git a/apps/cli/src/commands/autopilot/resume.command.ts b/apps/cli/src/commands/autopilot/resume.command.ts new file mode 100644 index 00000000..69ac6306 --- /dev/null +++ b/apps/cli/src/commands/autopilot/resume.command.ts @@ -0,0 +1,111 @@ +/** + * @fileoverview Resume Command - Restore and resume TDD workflow + */ + +import { Command } from 'commander'; +import { WorkflowOrchestrator } from '@tm/core'; +import { + AutopilotBaseOptions, + hasWorkflowState, + loadWorkflowState, + OutputFormatter +} from './shared.js'; + +type ResumeOptions = AutopilotBaseOptions; + +/** + * Resume Command - Restore workflow from saved state + */ +export class ResumeCommand extends Command { + constructor() { + super('resume'); + + this.description('Resume a previously started TDD workflow').action( + async (options: ResumeOptions) => { + await this.execute(options); + } + ); + } + + private async execute(options: ResumeOptions): Promise<void> { + // Inherit parent options (autopilot command) + const parentOpts = this.parent?.opts() as AutopilotBaseOptions; + const mergedOptions: ResumeOptions = { + ...parentOpts, + ...options, + projectRoot: + options.projectRoot || parentOpts?.projectRoot || process.cwd() + }; + + const formatter = new OutputFormatter(mergedOptions.json || false); + + try { + // Check for workflow state + const hasState = await hasWorkflowState(mergedOptions.projectRoot!); + if (!hasState) { + formatter.error('No workflow state found', { + suggestion: 'Start a new workflow with: autopilot start <taskId>' + }); + process.exit(1); + } + + // Load state + formatter.info('Loading workflow state...'); + const state = await loadWorkflowState(mergedOptions.projectRoot!); + + if (!state) { + formatter.error('Failed to load workflow state'); + process.exit(1); + } + + // Validate state can be resumed + const orchestrator = new WorkflowOrchestrator(state.context); + if (!orchestrator.canResumeFromState(state)) { + formatter.error('Invalid workflow state', { + suggestion: + 'State file may be corrupted. Consider starting a new workflow.' + }); + process.exit(1); + } + + // Restore state + orchestrator.restoreState(state); + + // Re-enable auto-persistence + const { saveWorkflowState } = await import('./shared.js'); + orchestrator.enableAutoPersist(async (newState) => { + await saveWorkflowState(mergedOptions.projectRoot!, newState); + }); + + // Get progress + const progress = orchestrator.getProgress(); + const currentSubtask = orchestrator.getCurrentSubtask(); + + // Output success + formatter.success('Workflow resumed', { + taskId: state.context.taskId, + phase: orchestrator.getCurrentPhase(), + tddPhase: orchestrator.getCurrentTDDPhase(), + branchName: state.context.branchName, + progress: { + completed: progress.completed, + total: progress.total, + percentage: progress.percentage + }, + currentSubtask: currentSubtask + ? { + id: currentSubtask.id, + title: currentSubtask.title, + attempts: currentSubtask.attempts + } + : null + }); + } catch (error) { + formatter.error((error as Error).message); + if (mergedOptions.verbose) { + console.error((error as Error).stack); + } + process.exit(1); + } + } +} diff --git a/apps/cli/src/commands/autopilot/shared.ts b/apps/cli/src/commands/autopilot/shared.ts new file mode 100644 index 00000000..f949df0b --- /dev/null +++ b/apps/cli/src/commands/autopilot/shared.ts @@ -0,0 +1,262 @@ +/** + * @fileoverview Shared utilities for autopilot commands + */ + +import { + WorkflowOrchestrator, + WorkflowStateManager, + GitAdapter, + CommitMessageGenerator +} from '@tm/core'; +import type { WorkflowState, WorkflowContext, SubtaskInfo } from '@tm/core'; +import chalk from 'chalk'; + +/** + * Base options interface for all autopilot commands + */ +export interface AutopilotBaseOptions { + projectRoot?: string; + json?: boolean; + verbose?: boolean; +} + +/** + * Load workflow state from disk using WorkflowStateManager + */ +export async function loadWorkflowState( + projectRoot: string +): Promise<WorkflowState | null> { + const stateManager = new WorkflowStateManager(projectRoot); + + if (!(await stateManager.exists())) { + return null; + } + + try { + return await stateManager.load(); + } catch (error) { + throw new Error( + `Failed to load workflow state: ${(error as Error).message}` + ); + } +} + +/** + * Save workflow state to disk using WorkflowStateManager + */ +export async function saveWorkflowState( + projectRoot: string, + state: WorkflowState +): Promise<void> { + const stateManager = new WorkflowStateManager(projectRoot); + + try { + await stateManager.save(state); + } catch (error) { + throw new Error( + `Failed to save workflow state: ${(error as Error).message}` + ); + } +} + +/** + * Delete workflow state from disk using WorkflowStateManager + */ +export async function deleteWorkflowState(projectRoot: string): Promise<void> { + const stateManager = new WorkflowStateManager(projectRoot); + await stateManager.delete(); +} + +/** + * Check if workflow state exists using WorkflowStateManager + */ +export async function hasWorkflowState(projectRoot: string): Promise<boolean> { + const stateManager = new WorkflowStateManager(projectRoot); + return await stateManager.exists(); +} + +/** + * Initialize WorkflowOrchestrator with persistence + */ +export function createOrchestrator( + context: WorkflowContext, + projectRoot: string +): WorkflowOrchestrator { + const orchestrator = new WorkflowOrchestrator(context); + const stateManager = new WorkflowStateManager(projectRoot); + + // Enable auto-persistence + orchestrator.enableAutoPersist(async (state: WorkflowState) => { + await stateManager.save(state); + }); + + return orchestrator; +} + +/** + * Initialize GitAdapter for project + */ +export function createGitAdapter(projectRoot: string): GitAdapter { + return new GitAdapter(projectRoot); +} + +/** + * Initialize CommitMessageGenerator + */ +export function createCommitMessageGenerator(): CommitMessageGenerator { + return new CommitMessageGenerator(); +} + +/** + * Output formatter for JSON and text modes + */ +export class OutputFormatter { + constructor(private useJson: boolean) {} + + /** + * Output data in appropriate format + */ + output(data: Record<string, unknown>): void { + if (this.useJson) { + console.log(JSON.stringify(data, null, 2)); + } else { + this.outputText(data); + } + } + + /** + * Output data in human-readable text format + */ + private outputText(data: Record<string, unknown>): void { + for (const [key, value] of Object.entries(data)) { + if (typeof value === 'object' && value !== null) { + console.log(chalk.cyan(`${key}:`)); + this.outputObject(value as Record<string, unknown>, ' '); + } else { + console.log(chalk.white(`${key}: ${value}`)); + } + } + } + + /** + * Output nested object with indentation + */ + private outputObject(obj: Record<string, unknown>, indent: string): void { + for (const [key, value] of Object.entries(obj)) { + if (typeof value === 'object' && value !== null) { + console.log(chalk.cyan(`${indent}${key}:`)); + this.outputObject(value as Record<string, unknown>, indent + ' '); + } else { + console.log(chalk.gray(`${indent}${key}: ${value}`)); + } + } + } + + /** + * Output error message + */ + error(message: string, details?: Record<string, unknown>): void { + if (this.useJson) { + console.error( + JSON.stringify( + { + error: message, + ...details + }, + null, + 2 + ) + ); + } else { + console.error(chalk.red(`Error: ${message}`)); + if (details) { + for (const [key, value] of Object.entries(details)) { + console.error(chalk.gray(` ${key}: ${value}`)); + } + } + } + } + + /** + * Output success message + */ + success(message: string, data?: Record<string, unknown>): void { + if (this.useJson) { + console.log( + JSON.stringify( + { + success: true, + message, + ...data + }, + null, + 2 + ) + ); + } else { + console.log(chalk.green(`✓ ${message}`)); + if (data) { + this.output(data); + } + } + } + + /** + * Output warning message + */ + warning(message: string): void { + if (this.useJson) { + console.warn( + JSON.stringify( + { + warning: message + }, + null, + 2 + ) + ); + } else { + console.warn(chalk.yellow(`⚠ ${message}`)); + } + } + + /** + * Output info message + */ + info(message: string): void { + if (this.useJson) { + // Don't output info messages in JSON mode + return; + } + console.log(chalk.blue(`ℹ ${message}`)); + } +} + +/** + * Validate task ID format + */ +export function validateTaskId(taskId: string): boolean { + // Task ID should be in format: number or number.number (e.g., "1" or "1.2") + const pattern = /^\d+(\.\d+)*$/; + return pattern.test(taskId); +} + +/** + * Parse subtasks from task data + */ +export function parseSubtasks( + task: any, + maxAttempts: number = 3 +): SubtaskInfo[] { + if (!task.subtasks || !Array.isArray(task.subtasks)) { + return []; + } + + return task.subtasks.map((subtask: any) => ({ + id: subtask.id, + title: subtask.title, + status: subtask.status === 'done' ? 'completed' : 'pending', + attempts: 0, + maxAttempts + })); +} diff --git a/apps/cli/src/commands/autopilot/start.command.ts b/apps/cli/src/commands/autopilot/start.command.ts new file mode 100644 index 00000000..6bcae44c --- /dev/null +++ b/apps/cli/src/commands/autopilot/start.command.ts @@ -0,0 +1,168 @@ +/** + * @fileoverview Start Command - Initialize and start TDD workflow + */ + +import { Command } from 'commander'; +import { createTaskMasterCore, type WorkflowContext } from '@tm/core'; +import { + AutopilotBaseOptions, + hasWorkflowState, + createOrchestrator, + createGitAdapter, + OutputFormatter, + validateTaskId, + parseSubtasks +} from './shared.js'; + +interface StartOptions extends AutopilotBaseOptions { + force?: boolean; + maxAttempts?: string; +} + +/** + * Start Command - Initialize new TDD workflow + */ +export class StartCommand extends Command { + constructor() { + super('start'); + + this.description('Initialize and start a new TDD workflow for a task') + .argument('<taskId>', 'Task ID to start workflow for') + .option('-f, --force', 'Force start even if workflow state exists') + .option('--max-attempts <number>', 'Maximum attempts per subtask', '3') + .action(async (taskId: string, options: StartOptions) => { + await this.execute(taskId, options); + }); + } + + private async execute(taskId: string, options: StartOptions): Promise<void> { + // Inherit parent options + const parentOpts = this.parent?.opts() as AutopilotBaseOptions; + const mergedOptions: StartOptions = { + ...parentOpts, + ...options, + projectRoot: + options.projectRoot || parentOpts?.projectRoot || process.cwd() + }; + + const formatter = new OutputFormatter(mergedOptions.json || false); + + try { + // Validate task ID + if (!validateTaskId(taskId)) { + formatter.error('Invalid task ID format', { + taskId, + expected: 'Format: number or number.number (e.g., "1" or "1.2")' + }); + process.exit(1); + } + + // Check for existing workflow state + const hasState = await hasWorkflowState(mergedOptions.projectRoot!); + if (hasState && !mergedOptions.force) { + formatter.error( + 'Workflow state already exists. Use --force to overwrite or resume with "autopilot resume"' + ); + process.exit(1); + } + + // Initialize Task Master Core + const tmCore = await createTaskMasterCore({ + projectPath: mergedOptions.projectRoot! + }); + + // Get current tag from ConfigManager + const currentTag = tmCore.getActiveTag(); + + // Load task + formatter.info(`Loading task ${taskId}...`); + const { task } = await tmCore.getTaskWithSubtask(taskId); + + if (!task) { + formatter.error('Task not found', { taskId }); + await tmCore.close(); + process.exit(1); + } + + // Validate task has subtasks + if (!task.subtasks || task.subtasks.length === 0) { + formatter.error('Task has no subtasks. Expand task first.', { + taskId, + suggestion: `Run: task-master expand --id=${taskId}` + }); + await tmCore.close(); + process.exit(1); + } + + // Initialize Git adapter + const gitAdapter = createGitAdapter(mergedOptions.projectRoot!); + await gitAdapter.ensureGitRepository(); + await gitAdapter.ensureCleanWorkingTree(); + + // Parse subtasks + const maxAttempts = parseInt(mergedOptions.maxAttempts || '3', 10); + const subtasks = parseSubtasks(task, maxAttempts); + + // Create workflow context + const context: WorkflowContext = { + taskId: task.id, + subtasks, + currentSubtaskIndex: 0, + errors: [], + metadata: { + startedAt: new Date().toISOString(), + tags: task.tags || [] + } + }; + + // Create orchestrator with persistence + const orchestrator = createOrchestrator( + context, + mergedOptions.projectRoot! + ); + + // Complete PREFLIGHT phase + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + // Generate descriptive branch name + const sanitizedTitle = task.title + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .substring(0, 50); + const formattedTaskId = taskId.replace(/\./g, '-'); + const tagPrefix = currentTag ? `${currentTag}/` : ''; + const branchName = `${tagPrefix}task-${formattedTaskId}-${sanitizedTitle}`; + + // Create and checkout branch + formatter.info(`Creating branch: ${branchName}`); + await gitAdapter.createAndCheckoutBranch(branchName); + + // Transition to SUBTASK_LOOP + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName + }); + + // Output success + formatter.success('TDD workflow started', { + taskId: task.id, + title: task.title, + phase: orchestrator.getCurrentPhase(), + tddPhase: orchestrator.getCurrentTDDPhase(), + branchName, + subtasks: subtasks.length, + currentSubtask: subtasks[0]?.title + }); + + // Clean up + await tmCore.close(); + } catch (error) { + formatter.error((error as Error).message); + if (mergedOptions.verbose) { + console.error((error as Error).stack); + } + process.exit(1); + } + } +} diff --git a/apps/cli/src/commands/autopilot/status.command.ts b/apps/cli/src/commands/autopilot/status.command.ts new file mode 100644 index 00000000..4d0e5420 --- /dev/null +++ b/apps/cli/src/commands/autopilot/status.command.ts @@ -0,0 +1,114 @@ +/** + * @fileoverview Status Command - Show workflow progress + */ + +import { Command } from 'commander'; +import { WorkflowOrchestrator } from '@tm/core'; +import { + AutopilotBaseOptions, + hasWorkflowState, + loadWorkflowState, + OutputFormatter +} from './shared.js'; + +type StatusOptions = AutopilotBaseOptions; + +/** + * Status Command - Show current workflow status + */ +export class StatusCommand extends Command { + constructor() { + super('status'); + + this.description('Show current TDD workflow status and progress').action( + async (options: StatusOptions) => { + await this.execute(options); + } + ); + } + + private async execute(options: StatusOptions): Promise<void> { + // Inherit parent options + const parentOpts = this.parent?.opts() as AutopilotBaseOptions; + const mergedOptions: StatusOptions = { + ...parentOpts, + ...options, + projectRoot: + options.projectRoot || parentOpts?.projectRoot || process.cwd() + }; + + const formatter = new OutputFormatter(mergedOptions.json || false); + + try { + // Check for workflow state + const hasState = await hasWorkflowState(mergedOptions.projectRoot!); + if (!hasState) { + formatter.error('No active workflow', { + suggestion: 'Start a workflow with: autopilot start <taskId>' + }); + process.exit(1); + } + + // Load state + const state = await loadWorkflowState(mergedOptions.projectRoot!); + if (!state) { + formatter.error('Failed to load workflow state'); + process.exit(1); + } + + // Restore orchestrator + const orchestrator = new WorkflowOrchestrator(state.context); + orchestrator.restoreState(state); + + // Get status information + const phase = orchestrator.getCurrentPhase(); + const tddPhase = orchestrator.getCurrentTDDPhase(); + const progress = orchestrator.getProgress(); + const currentSubtask = orchestrator.getCurrentSubtask(); + const errors = state.context.errors ?? []; + + // Build status output + const status = { + taskId: state.context.taskId, + phase, + tddPhase, + branchName: state.context.branchName, + progress: { + completed: progress.completed, + total: progress.total, + current: progress.current, + percentage: progress.percentage + }, + currentSubtask: currentSubtask + ? { + id: currentSubtask.id, + title: currentSubtask.title, + status: currentSubtask.status, + attempts: currentSubtask.attempts, + maxAttempts: currentSubtask.maxAttempts + } + : null, + subtasks: state.context.subtasks.map((st) => ({ + id: st.id, + title: st.title, + status: st.status, + attempts: st.attempts + })), + errors: errors.length > 0 ? errors : undefined, + metadata: state.context.metadata + }; + + if (mergedOptions.json) { + formatter.output(status); + } else { + formatter.success('Workflow status', status); + } + } catch (error) { + formatter.error((error as Error).message); + if (mergedOptions.verbose) { + console.error((error as Error).stack); + } + process.exit(1); + } + } +} diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index 42f16dc9..55dd4dc9 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -12,6 +12,7 @@ export { ContextCommand } from './commands/context.command.js'; export { StartCommand } from './commands/start.command.js'; export { SetStatusCommand } from './commands/set-status.command.js'; export { ExportCommand } from './commands/export.command.js'; +export { AutopilotCommand } from './commands/autopilot.command.js'; // Command Registry export { diff --git a/apps/cli/tests/integration/commands/autopilot/workflow.test.ts b/apps/cli/tests/integration/commands/autopilot/workflow.test.ts new file mode 100644 index 00000000..3070b6ce --- /dev/null +++ b/apps/cli/tests/integration/commands/autopilot/workflow.test.ts @@ -0,0 +1,540 @@ +/** + * @fileoverview Integration tests for autopilot workflow commands + */ + +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import type { WorkflowState } from '@tm/core'; + +// Track file system state in memory - must be in vi.hoisted() for mock access +const { + mockFileSystem, + pathExistsFn, + readJSONFn, + writeJSONFn, + ensureDirFn, + removeFn +} = vi.hoisted(() => { + const mockFileSystem = new Map<string, string>(); + + return { + mockFileSystem, + pathExistsFn: vi.fn((path: string) => + Promise.resolve(mockFileSystem.has(path)) + ), + readJSONFn: vi.fn((path: string) => { + const data = mockFileSystem.get(path); + return data + ? Promise.resolve(JSON.parse(data)) + : Promise.reject(new Error('File not found')); + }), + writeJSONFn: vi.fn((path: string, data: any) => { + mockFileSystem.set(path, JSON.stringify(data)); + return Promise.resolve(); + }), + ensureDirFn: vi.fn(() => Promise.resolve()), + removeFn: vi.fn((path: string) => { + mockFileSystem.delete(path); + return Promise.resolve(); + }) + }; +}); + +// Mock fs-extra before any imports +vi.mock('fs-extra', () => ({ + default: { + pathExists: pathExistsFn, + readJSON: readJSONFn, + writeJSON: writeJSONFn, + ensureDir: ensureDirFn, + remove: removeFn + } +})); + +vi.mock('@tm/core', () => ({ + WorkflowOrchestrator: vi.fn().mockImplementation((context) => ({ + getCurrentPhase: vi.fn().mockReturnValue('SUBTASK_LOOP'), + getCurrentTDDPhase: vi.fn().mockReturnValue('RED'), + getContext: vi.fn().mockReturnValue(context), + transition: vi.fn(), + restoreState: vi.fn(), + getState: vi.fn().mockReturnValue({ phase: 'SUBTASK_LOOP', context }), + enableAutoPersist: vi.fn(), + canResumeFromState: vi.fn().mockReturnValue(true), + getCurrentSubtask: vi.fn().mockReturnValue({ + id: '1', + title: 'Test Subtask', + status: 'pending', + attempts: 0 + }), + getProgress: vi.fn().mockReturnValue({ + completed: 0, + total: 3, + current: 1, + percentage: 0 + }), + canProceed: vi.fn().mockReturnValue(false) + })), + GitAdapter: vi.fn().mockImplementation(() => ({ + ensureGitRepository: vi.fn().mockResolvedValue(undefined), + ensureCleanWorkingTree: vi.fn().mockResolvedValue(undefined), + createAndCheckoutBranch: vi.fn().mockResolvedValue(undefined), + hasStagedChanges: vi.fn().mockResolvedValue(true), + getStatus: vi.fn().mockResolvedValue({ + staged: ['file1.ts'], + modified: ['file2.ts'] + }), + createCommit: vi.fn().mockResolvedValue(undefined), + getLastCommit: vi.fn().mockResolvedValue({ + hash: 'abc123def456', + message: 'test commit' + }), + stageFiles: vi.fn().mockResolvedValue(undefined) + })), + CommitMessageGenerator: vi.fn().mockImplementation(() => ({ + generateMessage: vi.fn().mockReturnValue('feat: test commit message') + })), + createTaskMasterCore: vi.fn().mockResolvedValue({ + getTaskWithSubtask: vi.fn().mockResolvedValue({ + task: { + id: '1', + title: 'Test Task', + subtasks: [ + { id: '1', title: 'Subtask 1', status: 'pending' }, + { id: '2', title: 'Subtask 2', status: 'pending' }, + { id: '3', title: 'Subtask 3', status: 'pending' } + ], + tag: 'test' + } + }), + close: vi.fn().mockResolvedValue(undefined) + }) +})); + +// Import after mocks are set up +import { Command } from 'commander'; +import { AutopilotCommand } from '../../../../src/commands/autopilot/index.js'; + +describe('Autopilot Workflow Integration Tests', () => { + const projectRoot = '/test/project'; + let program: Command; + + beforeEach(() => { + mockFileSystem.clear(); + + // Clear mock call history + pathExistsFn.mockClear(); + readJSONFn.mockClear(); + writeJSONFn.mockClear(); + ensureDirFn.mockClear(); + removeFn.mockClear(); + + program = new Command(); + AutopilotCommand.register(program); + + // Use exitOverride to handle Commander exits in tests + program.exitOverride(); + }); + + afterEach(() => { + mockFileSystem.clear(); + vi.restoreAllMocks(); + }); + + describe('start command', () => { + it('should initialize workflow and create branch', async () => { + const consoleLogSpy = vi + .spyOn(console, 'log') + .mockImplementation(() => {}); + + await program.parseAsync([ + 'node', + 'test', + 'autopilot', + 'start', + '1', + '--project-root', + projectRoot, + '--json' + ]); + + // Verify writeJSON was called with state + expect(writeJSONFn).toHaveBeenCalledWith( + expect.stringContaining('workflow-state.json'), + expect.objectContaining({ + phase: expect.any(String), + context: expect.any(Object) + }), + expect.any(Object) + ); + + consoleLogSpy.mockRestore(); + }); + + it('should reject invalid task ID', async () => { + const consoleErrorSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await expect( + program.parseAsync([ + 'node', + 'test', + 'autopilot', + 'start', + 'invalid', + '--project-root', + projectRoot, + '--json' + ]) + ).rejects.toMatchObject({ exitCode: 1 }); + + consoleErrorSpy.mockRestore(); + }); + + it('should reject starting when workflow exists without force', async () => { + // Create existing state + const mockState: WorkflowState = { + phase: 'SUBTASK_LOOP', + context: { + taskId: '1', + subtasks: [], + currentSubtaskIndex: 0, + errors: [], + metadata: {} + } + }; + + mockFileSystem.set( + `${projectRoot}/.taskmaster/workflow-state.json`, + JSON.stringify(mockState) + ); + + const consoleErrorSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await expect( + program.parseAsync([ + 'node', + 'test', + 'autopilot', + 'start', + '1', + '--project-root', + projectRoot, + '--json' + ]) + ).rejects.toMatchObject({ exitCode: 1 }); + + consoleErrorSpy.mockRestore(); + }); + }); + + describe('resume command', () => { + beforeEach(() => { + // Create saved state + const mockState: WorkflowState = { + phase: 'SUBTASK_LOOP', + context: { + taskId: '1', + subtasks: [ + { + id: '1', + title: 'Test Subtask', + status: 'pending', + attempts: 0 + } + ], + currentSubtaskIndex: 0, + currentTDDPhase: 'RED', + branchName: 'task-1', + errors: [], + metadata: {} + } + }; + + mockFileSystem.set( + `${projectRoot}/.taskmaster/workflow-state.json`, + JSON.stringify(mockState) + ); + }); + + it('should restore workflow from saved state', async () => { + const consoleLogSpy = vi + .spyOn(console, 'log') + .mockImplementation(() => {}); + + await program.parseAsync([ + 'node', + 'test', + 'autopilot', + 'resume', + '--project-root', + projectRoot, + '--json' + ]); + + expect(consoleLogSpy).toHaveBeenCalled(); + const output = JSON.parse(consoleLogSpy.mock.calls[0][0]); + expect(output.success).toBe(true); + expect(output.taskId).toBe('1'); + + consoleLogSpy.mockRestore(); + }); + + it('should error when no state exists', async () => { + mockFileSystem.clear(); + + const consoleErrorSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await expect( + program.parseAsync([ + 'node', + 'test', + 'autopilot', + 'resume', + '--project-root', + projectRoot, + '--json' + ]) + ).rejects.toMatchObject({ exitCode: 1 }); + + consoleErrorSpy.mockRestore(); + }); + }); + + describe('next command', () => { + beforeEach(() => { + const mockState: WorkflowState = { + phase: 'SUBTASK_LOOP', + context: { + taskId: '1', + subtasks: [ + { + id: '1', + title: 'Test Subtask', + status: 'pending', + attempts: 0 + } + ], + currentSubtaskIndex: 0, + currentTDDPhase: 'RED', + branchName: 'task-1', + errors: [], + metadata: {} + } + }; + + mockFileSystem.set( + `${projectRoot}/.taskmaster/workflow-state.json`, + JSON.stringify(mockState) + ); + }); + + it('should return next action in JSON format', async () => { + const consoleLogSpy = vi + .spyOn(console, 'log') + .mockImplementation(() => {}); + + await program.parseAsync([ + 'node', + 'test', + 'autopilot', + 'next', + '--project-root', + projectRoot, + '--json' + ]); + + expect(consoleLogSpy).toHaveBeenCalled(); + const output = JSON.parse(consoleLogSpy.mock.calls[0][0]); + expect(output.action).toBe('generate_test'); + expect(output.phase).toBe('SUBTASK_LOOP'); + expect(output.tddPhase).toBe('RED'); + + consoleLogSpy.mockRestore(); + }); + }); + + describe('status command', () => { + beforeEach(() => { + const mockState: WorkflowState = { + phase: 'SUBTASK_LOOP', + context: { + taskId: '1', + subtasks: [ + { id: '1', title: 'Subtask 1', status: 'completed', attempts: 1 }, + { id: '2', title: 'Subtask 2', status: 'pending', attempts: 0 }, + { id: '3', title: 'Subtask 3', status: 'pending', attempts: 0 } + ], + currentSubtaskIndex: 1, + currentTDDPhase: 'RED', + branchName: 'task-1', + errors: [], + metadata: {} + } + }; + + mockFileSystem.set( + `${projectRoot}/.taskmaster/workflow-state.json`, + JSON.stringify(mockState) + ); + }); + + it('should display workflow progress', async () => { + const consoleLogSpy = vi + .spyOn(console, 'log') + .mockImplementation(() => {}); + + await program.parseAsync([ + 'node', + 'test', + 'autopilot', + 'status', + '--project-root', + projectRoot, + '--json' + ]); + + expect(consoleLogSpy).toHaveBeenCalled(); + const output = JSON.parse(consoleLogSpy.mock.calls[0][0]); + expect(output.taskId).toBe('1'); + expect(output.phase).toBe('SUBTASK_LOOP'); + expect(output.progress).toBeDefined(); + expect(output.subtasks).toHaveLength(3); + + consoleLogSpy.mockRestore(); + }); + }); + + describe('complete command', () => { + beforeEach(() => { + const mockState: WorkflowState = { + phase: 'SUBTASK_LOOP', + context: { + taskId: '1', + subtasks: [ + { + id: '1', + title: 'Test Subtask', + status: 'in-progress', + attempts: 0 + } + ], + currentSubtaskIndex: 0, + currentTDDPhase: 'RED', + branchName: 'task-1', + errors: [], + metadata: {} + } + }; + + mockFileSystem.set( + `${projectRoot}/.taskmaster/workflow-state.json`, + JSON.stringify(mockState) + ); + }); + + it('should validate RED phase has failures', async () => { + const consoleErrorSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await expect( + program.parseAsync([ + 'node', + 'test', + 'autopilot', + 'complete', + '--project-root', + projectRoot, + '--results', + '{"total":10,"passed":10,"failed":0,"skipped":0}', + '--json' + ]) + ).rejects.toMatchObject({ exitCode: 1 }); + + consoleErrorSpy.mockRestore(); + }); + + it('should complete RED phase with failures', async () => { + const consoleLogSpy = vi + .spyOn(console, 'log') + .mockImplementation(() => {}); + + await program.parseAsync([ + 'node', + 'test', + 'autopilot', + 'complete', + '--project-root', + projectRoot, + '--results', + '{"total":10,"passed":9,"failed":1,"skipped":0}', + '--json' + ]); + + expect(consoleLogSpy).toHaveBeenCalled(); + const output = JSON.parse(consoleLogSpy.mock.calls[0][0]); + expect(output.success).toBe(true); + expect(output.nextPhase).toBe('GREEN'); + + consoleLogSpy.mockRestore(); + }); + }); + + describe('abort command', () => { + beforeEach(() => { + const mockState: WorkflowState = { + phase: 'SUBTASK_LOOP', + context: { + taskId: '1', + subtasks: [ + { + id: '1', + title: 'Test Subtask', + status: 'pending', + attempts: 0 + } + ], + currentSubtaskIndex: 0, + currentTDDPhase: 'RED', + branchName: 'task-1', + errors: [], + metadata: {} + } + }; + + mockFileSystem.set( + `${projectRoot}/.taskmaster/workflow-state.json`, + JSON.stringify(mockState) + ); + }); + + it('should abort workflow and delete state', async () => { + const consoleLogSpy = vi + .spyOn(console, 'log') + .mockImplementation(() => {}); + + await program.parseAsync([ + 'node', + 'test', + 'autopilot', + 'abort', + '--project-root', + projectRoot, + '--force', + '--json' + ]); + + // Verify remove was called + expect(removeFn).toHaveBeenCalledWith( + expect.stringContaining('workflow-state.json') + ); + + consoleLogSpy.mockRestore(); + }); + }); +}); diff --git a/apps/cli/tests/unit/commands/autopilot/shared.test.ts b/apps/cli/tests/unit/commands/autopilot/shared.test.ts new file mode 100644 index 00000000..7e7f200a --- /dev/null +++ b/apps/cli/tests/unit/commands/autopilot/shared.test.ts @@ -0,0 +1,202 @@ +/** + * @fileoverview Unit tests for autopilot shared utilities + */ + +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { + validateTaskId, + parseSubtasks, + OutputFormatter +} from '../../../../src/commands/autopilot/shared.js'; + +// Mock fs-extra +vi.mock('fs-extra', () => ({ + default: { + pathExists: vi.fn(), + readJSON: vi.fn(), + writeJSON: vi.fn(), + ensureDir: vi.fn(), + remove: vi.fn() + }, + pathExists: vi.fn(), + readJSON: vi.fn(), + writeJSON: vi.fn(), + ensureDir: vi.fn(), + remove: vi.fn() +})); + +describe('Autopilot Shared Utilities', () => { + const projectRoot = '/test/project'; + const statePath = `${projectRoot}/.taskmaster/workflow-state.json`; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('validateTaskId', () => { + it('should validate simple task IDs', () => { + expect(validateTaskId('1')).toBe(true); + expect(validateTaskId('10')).toBe(true); + expect(validateTaskId('999')).toBe(true); + }); + + it('should validate subtask IDs', () => { + expect(validateTaskId('1.1')).toBe(true); + expect(validateTaskId('1.2')).toBe(true); + expect(validateTaskId('10.5')).toBe(true); + }); + + it('should validate nested subtask IDs', () => { + expect(validateTaskId('1.1.1')).toBe(true); + expect(validateTaskId('1.2.3')).toBe(true); + }); + + it('should reject invalid formats', () => { + expect(validateTaskId('')).toBe(false); + expect(validateTaskId('abc')).toBe(false); + expect(validateTaskId('1.')).toBe(false); + expect(validateTaskId('.1')).toBe(false); + expect(validateTaskId('1..2')).toBe(false); + expect(validateTaskId('1.2.3.')).toBe(false); + }); + }); + + describe('parseSubtasks', () => { + it('should parse subtasks from task data', () => { + const task = { + id: '1', + title: 'Test Task', + subtasks: [ + { id: '1', title: 'Subtask 1', status: 'pending' }, + { id: '2', title: 'Subtask 2', status: 'done' }, + { id: '3', title: 'Subtask 3', status: 'in-progress' } + ] + }; + + const result = parseSubtasks(task, 5); + + expect(result).toHaveLength(3); + expect(result[0]).toEqual({ + id: '1', + title: 'Subtask 1', + status: 'pending', + attempts: 0, + maxAttempts: 5 + }); + expect(result[1]).toEqual({ + id: '2', + title: 'Subtask 2', + status: 'completed', + attempts: 0, + maxAttempts: 5 + }); + }); + + it('should return empty array for missing subtasks', () => { + const task = { id: '1', title: 'Test Task' }; + expect(parseSubtasks(task)).toEqual([]); + }); + + it('should use default maxAttempts', () => { + const task = { + subtasks: [{ id: '1', title: 'Subtask 1', status: 'pending' }] + }; + + const result = parseSubtasks(task); + expect(result[0].maxAttempts).toBe(3); + }); + }); + + // State persistence tests omitted - covered in integration tests + + describe('OutputFormatter', () => { + let consoleLogSpy: any; + let consoleErrorSpy: any; + + beforeEach(() => { + consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + }); + + afterEach(() => { + consoleLogSpy.mockRestore(); + consoleErrorSpy.mockRestore(); + }); + + describe('JSON mode', () => { + it('should output JSON for success', () => { + const formatter = new OutputFormatter(true); + formatter.success('Test message', { key: 'value' }); + + expect(consoleLogSpy).toHaveBeenCalled(); + const output = JSON.parse(consoleLogSpy.mock.calls[0][0]); + expect(output.success).toBe(true); + expect(output.message).toBe('Test message'); + expect(output.key).toBe('value'); + }); + + it('should output JSON for error', () => { + const formatter = new OutputFormatter(true); + formatter.error('Error message', { code: 'ERR001' }); + + expect(consoleErrorSpy).toHaveBeenCalled(); + const output = JSON.parse(consoleErrorSpy.mock.calls[0][0]); + expect(output.error).toBe('Error message'); + expect(output.code).toBe('ERR001'); + }); + + it('should output JSON for data', () => { + const formatter = new OutputFormatter(true); + formatter.output({ test: 'data' }); + + expect(consoleLogSpy).toHaveBeenCalled(); + const output = JSON.parse(consoleLogSpy.mock.calls[0][0]); + expect(output.test).toBe('data'); + }); + }); + + describe('Text mode', () => { + it('should output formatted text for success', () => { + const formatter = new OutputFormatter(false); + formatter.success('Test message'); + + expect(consoleLogSpy).toHaveBeenCalledWith( + expect.stringContaining('✓ Test message') + ); + }); + + it('should output formatted text for error', () => { + const formatter = new OutputFormatter(false); + formatter.error('Error message'); + + expect(consoleErrorSpy).toHaveBeenCalledWith( + expect.stringContaining('Error: Error message') + ); + }); + + it('should output formatted text for warning', () => { + const consoleWarnSpy = vi + .spyOn(console, 'warn') + .mockImplementation(() => {}); + const formatter = new OutputFormatter(false); + formatter.warning('Warning message'); + + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('⚠ Warning message') + ); + consoleWarnSpy.mockRestore(); + }); + + it('should not output info in JSON mode', () => { + const formatter = new OutputFormatter(true); + formatter.info('Info message'); + + expect(consoleLogSpy).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/apps/cli/vitest.config.ts b/apps/cli/vitest.config.ts new file mode 100644 index 00000000..cfb90c2b --- /dev/null +++ b/apps/cli/vitest.config.ts @@ -0,0 +1,25 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['tests/**/*.test.ts', 'tests/**/*.spec.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + include: ['src/**/*.ts'], + exclude: [ + 'node_modules/', + 'dist/', + 'tests/', + '**/*.test.ts', + '**/*.spec.ts', + '**/*.d.ts', + '**/mocks/**', + '**/fixtures/**', + 'vitest.config.ts' + ] + } + } +}); diff --git a/apps/docs/command-reference.mdx b/apps/docs/command-reference.mdx new file mode 100644 index 00000000..5c8b1ff9 --- /dev/null +++ b/apps/docs/command-reference.mdx @@ -0,0 +1,235 @@ +--- +title: "Task Master Commands" +description: "A comprehensive reference of all available Task Master commands" +--- + +<AccordionGroup> + <Accordion title="Parse PRD"> + ```bash + # Parse a PRD file and generate tasks + task-master parse-prd <prd-file.txt> + + # Limit the number of tasks generated + task-master parse-prd <prd-file.txt> --num-tasks=10 + ``` + </Accordion> + + <Accordion title="List Tasks"> + ```bash + # List all tasks + task-master list + + # List tasks with a specific status + task-master list --status=<status> + + # List tasks with subtasks + task-master list --with-subtasks + + # List tasks with a specific status and include subtasks + task-master list --status=<status> --with-subtasks + ``` + </Accordion> + + <Accordion title="Show Next Task"> + ```bash + # Show the next task to work on based on dependencies and status + task-master next + ``` + </Accordion> + + <Accordion title="Show Specific Task"> + ```bash + # Show details of a specific task + task-master show <id> + # or + task-master show --id=<id> + + # View a specific subtask (e.g., subtask 2 of task 1) + task-master show 1.2 + ``` + </Accordion> + + <Accordion title="Update Tasks"> + ```bash + # Update tasks from a specific ID and provide context + task-master update --from=<id> --prompt="<prompt>" + ``` + </Accordion> + + <Accordion title="Update a Specific Task"> + ```bash + # Update a single task by ID with new information + task-master update-task --id=<id> --prompt="<prompt>" + + # Use research-backed updates with Perplexity AI + task-master update-task --id=<id> --prompt="<prompt>" --research + ``` + </Accordion> + + <Accordion title="Update a Subtask"> + ```bash + # Append additional information to a specific subtask + task-master update-subtask --id=<parentId.subtaskId> --prompt="<prompt>" + + # Example: Add details about API rate limiting to subtask 2 of task 5 + task-master update-subtask --id=5.2 --prompt="Add rate limiting of 100 requests per minute" + + # Use research-backed updates with Perplexity AI + task-master update-subtask --id=<parentId.subtaskId> --prompt="<prompt>" --research + ``` + + Unlike the `update-task` command which replaces task information, the `update-subtask` command _appends_ new information to the existing subtask details, marking it with a timestamp. This is useful for iteratively enhancing subtasks while preserving the original content. + </Accordion> + + <Accordion title="Generate Task Files"> + ```bash + # Generate individual task files from tasks.json + task-master generate + ``` + </Accordion> + + <Accordion title="Set Task Status"> + ```bash + # Set status of a single task + task-master set-status --id=<id> --status=<status> + + # Set status for multiple tasks + task-master set-status --id=1,2,3 --status=<status> + + # Set status for subtasks + task-master set-status --id=1.1,1.2 --status=<status> + ``` + + When marking a task as "done", all of its subtasks will automatically be marked as "done" as well. + </Accordion> + + <Accordion title="Expand Tasks"> + ```bash + # Expand a specific task with subtasks + task-master expand --id=<id> --num=<number> + + # Expand with additional context + task-master expand --id=<id> --prompt="<context>" + + # Expand all pending tasks + task-master expand --all + + # Force regeneration of subtasks for tasks that already have them + task-master expand --all --force + + # Research-backed subtask generation for a specific task + task-master expand --id=<id> --research + + # Research-backed generation for all tasks + task-master expand --all --research + ``` + </Accordion> + + <Accordion title="Clear Subtasks"> + ```bash + # Clear subtasks from a specific task + task-master clear-subtasks --id=<id> + + # Clear subtasks from multiple tasks + task-master clear-subtasks --id=1,2,3 + + # Clear subtasks from all tasks + task-master clear-subtasks --all + ``` + </Accordion> + + <Accordion title="Analyze Task Complexity"> + ```bash + # Analyze complexity of all tasks + task-master analyze-complexity + + # Save report to a custom location + task-master analyze-complexity --output=my-report.json + + # Use a specific LLM model + task-master analyze-complexity --model=claude-3-opus-20240229 + + # Set a custom complexity threshold (1-10) + task-master analyze-complexity --threshold=6 + + # Use an alternative tasks file + task-master analyze-complexity --file=custom-tasks.json + + # Use Perplexity AI for research-backed complexity analysis + task-master analyze-complexity --research + ``` + </Accordion> + + <Accordion title="View Complexity Report"> + ```bash + # Display the task complexity analysis report + task-master complexity-report + + # View a report at a custom location + task-master complexity-report --file=my-report.json + ``` + </Accordion> + + <Accordion title="Managing Task Dependencies"> + ```bash + # Add a dependency to a task + task-master add-dependency --id=<id> --depends-on=<id> + + # Remove a dependency from a task + task-master remove-dependency --id=<id> --depends-on=<id> + + # Validate dependencies without fixing them + task-master validate-dependencies + + # Find and fix invalid dependencies automatically + task-master fix-dependencies + ``` + </Accordion> + + <Accordion title="Add a New Task"> + ```bash + # Add a new task using AI + task-master add-task --prompt="Description of the new task" + + # Add a task with dependencies + task-master add-task --prompt="Description" --dependencies=1,2,3 + + # Add a task with priority + task-master add-task --prompt="Description" --priority=high + ``` + </Accordion> + + <Accordion title="Initialize a Project"> + ```bash + # Initialize a new project with Task Master structure + task-master init + ``` + </Accordion> + + <Accordion title="TDD Workflow (Autopilot)"> + ```bash + # Start autonomous TDD workflow for a task + task-master autopilot start <taskId> + + # Get next action with context + task-master autopilot next + + # Complete phase with test results + task-master autopilot complete --results '{"total":N,"passed":N,"failed":N}' + + # Commit changes + task-master autopilot commit + + # Check workflow status + task-master autopilot status + + # Resume interrupted workflow + task-master autopilot resume + + # Abort workflow + task-master autopilot abort + ``` + + The TDD workflow enforces RED → GREEN → COMMIT cycles for each subtask. See [AI Agent Integration](/tdd-workflow/ai-agent-integration) for details. + </Accordion> +</AccordionGroup> diff --git a/apps/docs/configuration.mdx b/apps/docs/configuration.mdx new file mode 100644 index 00000000..caa51082 --- /dev/null +++ b/apps/docs/configuration.mdx @@ -0,0 +1,94 @@ +--- +title: "Configuration" +description: "Configure Task Master through environment variables in a .env file" +--- + +## Required Configuration + +<Note> + Task Master requires an Anthropic API key to function. Add this to your `.env` file: + + ```bash + ANTHROPIC_API_KEY=sk-ant-api03-your-api-key + ``` + + You can obtain an API key from the [Anthropic Console](https://console.anthropic.com/). +</Note> + +## Optional Configuration + +| Variable | Default Value | Description | Example | +| --- | --- | --- | --- | +| `MODEL` | `"claude-3-7-sonnet-20250219"` | Claude model to use | `MODEL=claude-3-opus-20240229` | +| `MAX_TOKENS` | `"4000"` | Maximum tokens for responses | `MAX_TOKENS=8000` | +| `TEMPERATURE` | `"0.7"` | Temperature for model responses | `TEMPERATURE=0.5` | +| `DEBUG` | `"false"` | Enable debug logging | `DEBUG=true` | +| `LOG_LEVEL` | `"info"` | Console output level | `LOG_LEVEL=debug` | +| `DEFAULT_SUBTASKS` | `"3"` | Default subtask count | `DEFAULT_SUBTASKS=5` | +| `DEFAULT_PRIORITY` | `"medium"` | Default priority | `DEFAULT_PRIORITY=high` | +| `PROJECT_NAME` | `"MCP SaaS MVP"` | Project name in metadata | `PROJECT_NAME=My Awesome Project` | +| `PROJECT_VERSION` | `"1.0.0"` | Version in metadata | `PROJECT_VERSION=2.1.0` | +| `PERPLEXITY_API_KEY` | - | For research-backed features | `PERPLEXITY_API_KEY=pplx-...` | +| `PERPLEXITY_MODEL` | `"sonar-medium-online"` | Perplexity model | `PERPLEXITY_MODEL=sonar-large-online` | + +## TDD Workflow Configuration + +Additional options for autonomous TDD workflow: + +| Variable | Default | Description | +| --- | --- | --- | +| `TM_MAX_ATTEMPTS` | `3` | Max attempts per subtask before marking blocked | +| `TM_AUTO_COMMIT` | `true` | Auto-commit after GREEN phase | +| `TM_PROJECT_ROOT` | Current dir | Default project root | + +## Example .env File + +``` +# Required +ANTHROPIC_API_KEY=sk-ant-api03-your-api-key + +# Optional - Claude Configuration +MODEL=claude-3-7-sonnet-20250219 +MAX_TOKENS=4000 +TEMPERATURE=0.7 + +# Optional - Perplexity API for Research +PERPLEXITY_API_KEY=pplx-your-api-key +PERPLEXITY_MODEL=sonar-medium-online + +# Optional - Project Info +PROJECT_NAME=My Project +PROJECT_VERSION=1.0.0 + +# Optional - Application Configuration +DEFAULT_SUBTASKS=3 +DEFAULT_PRIORITY=medium +DEBUG=false +LOG_LEVEL=info + +# TDD Workflow +TM_MAX_ATTEMPTS=3 +TM_AUTO_COMMIT=true +``` + +## Troubleshooting + +### If `task-master init` doesn't respond: + +Try running it with Node directly: + +```bash +node node_modules/claude-task-master/scripts/init.js +``` + +Or clone the repository and run: + +```bash +git clone https://github.com/eyaltoledano/claude-task-master.git +cd claude-task-master +node scripts/init.js +``` + +<Note> +For advanced configuration options and detailed customization, see our [Advanced Configuration Guide] page. +</Note> diff --git a/apps/docs/docs.json b/apps/docs/docs.json index 787a85e0..25fefec1 100644 --- a/apps/docs/docs.json +++ b/apps/docs/docs.json @@ -52,6 +52,13 @@ "capabilities/cli-root-commands", "capabilities/task-structure" ] + }, + { + "group": "TDD Workflow (Autopilot)", + "pages": [ + "tdd-workflow/quickstart", + "tdd-workflow/ai-agent-integration" + ] } ] } diff --git a/apps/docs/tdd-workflow/ai-agent-integration.mdx b/apps/docs/tdd-workflow/ai-agent-integration.mdx new file mode 100644 index 00000000..faebb7f9 --- /dev/null +++ b/apps/docs/tdd-workflow/ai-agent-integration.mdx @@ -0,0 +1,1013 @@ +--- +title: "AI Agent Integration Guide" +description: "Complete guide for integrating AI agents with TaskMaster's autonomous TDD workflow system" +--- + +Complete guide for integrating AI agents with TaskMaster's autonomous TDD workflow system. + +## Overview + +TaskMaster provides a complete TDD workflow orchestration system that enables AI agents to autonomously implement features following strict Test-Driven Development practices. The system manages workflow state, git operations, test validation, and progress tracking. + +### Key Features + +- **TDD State Machine**: Enforces RED → GREEN → COMMIT cycle +- **Git Integration**: Automated branch creation, commits with metadata +- **Test Validation**: Ensures RED phase has failures, GREEN phase passes +- **Progress Tracking**: Subtask completion, attempt counting, error logging +- **State Persistence**: Automatic workflow state management +- **Dual Interface**: CLI commands and MCP tools for flexibility + +## Architecture + +``` +┌─────────────────────────────────────────────────────┐ +│ AI Agent │ +│ (Claude Code, Custom Agent, etc.) │ +└─────────────┬───────────────────────────────────────┘ + │ + │ Uses CLI or MCP + │ +┌─────────────▼───────────────────────────────────────┐ +│ TaskMaster Interface │ +│ ┌──────────────┐ ┌──────────────┐ │ +│ │ CLI Commands │ │ MCP Tools │ │ +│ └──────┬───────┘ └──────┬───────┘ │ +└─────────┼────────────────────────┼─────────────────┘ + │ │ +┌─────────▼────────────────────────▼─────────────────┐ +│ WorkflowOrchestrator (Core) │ +│ ┌─────────────────────────────────────────────┐ │ +│ │ State Machine: RED → GREEN → COMMIT │ │ +│ └─────────────────────────────────────────────┘ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ +│ │GitAdapter│ │TestResult│ │CommitMessage │ │ +│ │ │ │Validator │ │Generator │ │ +│ └──────────┘ └──────────┘ └──────────────┘ │ +└────────────────────────────────────────────────────┘ + │ + │ Persists to + │ +┌─────────▼───────────────────────────────────────────┐ +│ .taskmaster/workflow-state.json │ +└──────────────────────────────────────────────────────┘ +``` + +### Component Responsibilities + +**WorkflowOrchestrator** +- Manages TDD state machine transitions +- Tracks current subtask and progress +- Enforces workflow rules and validations +- Emits events for state changes + +**GitAdapter** +- Creates and manages workflow branches +- Stages files and creates commits +- Validates git repository state +- Provides safety checks (clean working tree, etc.) + +**TestResultValidator** +- Validates RED phase has test failures +- Validates GREEN phase has all tests passing +- Parses test results from various formats + +**CommitMessageGenerator** +- Generates conventional commit messages +- Embeds workflow metadata (subtask ID, phase, etc.) +- Follows project commit conventions + +## Getting Started + +### Prerequisites + +1. TaskMaster initialized project with subtasks +2. Git repository with clean working tree +3. Test framework configured (vitest, jest, etc.) + +### Quick Start + +```bash +# 1. Initialize workflow for a task +tm autopilot start 7 + +# 2. Check what to do next +tm autopilot next + +# 3. Write failing test (RED phase) +# ... create test file ... + +# 4. Run tests and complete RED phase +tm autopilot complete --results '{"total":1,"passed":0,"failed":1,"skipped":0}' + +# 5. Implement code to pass tests (GREEN phase) +# ... write implementation ... + +# 6. Run tests and complete GREEN phase +tm autopilot complete --results '{"total":1,"passed":1,"failed":0,"skipped":0}' + +# 7. Commit changes +tm autopilot commit + +# 8. Repeat for next subtask (automatically advanced) +tm autopilot next +``` + +## CLI Commands + +All commands support `--json` flag for machine-readable output. + +### `tm autopilot start <taskId>` + +Initialize a new TDD workflow for a task. + +**Options:** +- `--max-attempts <number>`: Maximum attempts per subtask (default: 3) +- `--force`: Force start even if workflow exists +- `--project-root <path>`: Project root directory +- `--json`: Output JSON + +**Example:** +```bash +tm autopilot start 7 --max-attempts 5 --json +``` + +**JSON Output:** +```json +{ + "success": true, + "message": "Workflow started for task 7", + "taskId": "7", + "branchName": "task-7", + "phase": "SUBTASK_LOOP", + "tddPhase": "RED", + "progress": { + "completed": 0, + "total": 5, + "percentage": 0 + }, + "currentSubtask": { + "id": "1", + "title": "Implement start command", + "status": "in-progress", + "attempts": 0 + }, + "nextAction": "generate_test" +} +``` + +### `tm autopilot resume` + +Resume a previously started workflow from saved state. + +**Example:** +```bash +tm autopilot resume --json +``` + +### `tm autopilot next` + +Get the next action to perform with detailed context. + +**JSON Output:** +```json +{ + "action": "generate_test", + "actionDescription": "Write a failing test for the current subtask", + "phase": "SUBTASK_LOOP", + "tddPhase": "RED", + "taskId": "7", + "branchName": "task-7", + "progress": { + "completed": 0, + "total": 5, + "current": 1, + "percentage": 0 + }, + "currentSubtask": { + "id": "1", + "title": "Implement start command", + "status": "in-progress", + "attempts": 0, + "maxAttempts": 3 + }, + "expectedFiles": ["test file"], + "context": { + "canProceed": false, + "errors": [] + } +} +``` + +### `tm autopilot status` + +Get comprehensive workflow progress and state information. + +**Example:** +```bash +tm autopilot status --json +``` + +### `tm autopilot complete` + +Complete the current TDD phase with test result validation. + +**Options:** +- `--results <json>`: Test results JSON string + +**Example:** +```bash +tm autopilot complete --results '{"total":10,"passed":9,"failed":1,"skipped":0}' --json +``` + +**Validation Rules:** +- **RED Phase**: Must have at least one failing test +- **GREEN Phase**: All tests must pass (failed === 0) + +### `tm autopilot commit` + +Create a git commit with enhanced message generation. + +**Options:** +- `--message <text>`: Custom commit message (optional) +- `--files <paths...>`: Specific files to stage (optional) + +**Example:** +```bash +tm autopilot commit --json +``` + +### `tm autopilot abort` + +Abort the workflow and clean up state (preserves git branch and code). + +**Example:** +```bash +tm autopilot abort --force --json +``` + +## MCP Tools + +MCP tools provide the same functionality as CLI commands for programmatic integration. + +### `autopilot_start` + +**Parameters:** +```typescript +{ + taskId: string; // Required: Task ID (e.g., "7", "2.3") + projectRoot: string; // Required: Absolute path to project + tag?: string; // Optional: Tag context + maxAttempts?: number; // Optional: Default 3 + force?: boolean; // Optional: Default false +} +``` + +**Returns:** +```typescript +{ + success: boolean; + message: string; + taskId: string; + branchName: string; + phase: WorkflowPhase; + tddPhase: TDDPhase; + progress: { + completed: number; + total: number; + percentage: number; + }; + currentSubtask: SubtaskInfo | null; + nextAction: string; +} +``` + +### `autopilot_resume` + +**Parameters:** +```typescript +{ + projectRoot: string; // Required: Absolute path to project +} +``` + +### `autopilot_next` + +**Parameters:** +```typescript +{ + projectRoot: string; // Required: Absolute path to project +} +``` + +**Returns:** +```typescript +{ + action: string; // 'generate_test' | 'implement_code' | 'commit_changes' + actionDescription: string; + phase: WorkflowPhase; + tddPhase: TDDPhase; + taskId: string; + branchName: string; + progress: ProgressInfo; + currentSubtask: SubtaskInfo | null; + expectedFiles: string[]; + context: { + canProceed: boolean; + errors: string[]; + }; +} +``` + +### `autopilot_status` + +**Parameters:** +```typescript +{ + projectRoot: string; // Required: Absolute path to project +} +``` + +**Returns:** +```typescript +{ + taskId: string; + branchName: string; + phase: WorkflowPhase; + tddPhase: TDDPhase; + progress: ProgressInfo; + currentSubtask: SubtaskInfo | null; + subtasks: SubtaskInfo[]; + errors: string[]; + metadata: Record<string, any>; + canProceed: boolean; +} +``` + +### `autopilot_complete_phase` + +**Parameters:** +```typescript +{ + projectRoot: string; // Required: Absolute path to project + testResults: { + total: number; // Required: Total tests + passed: number; // Required: Passing tests + failed: number; // Required: Failing tests + skipped?: number; // Optional: Skipped tests + }; +} +``` + +### `autopilot_commit` + +**Parameters:** +```typescript +{ + projectRoot: string; // Required: Absolute path to project + files?: string[]; // Optional: Files to stage + customMessage?: string; // Optional: Custom commit message +} +``` + +### `autopilot_abort` + +**Parameters:** +```typescript +{ + projectRoot: string; // Required: Absolute path to project +} +``` + +## Workflow Phases + +### Phase Diagram + +``` +PREFLIGHT → BRANCH_SETUP → SUBTASK_LOOP → FINALIZE → COMPLETE + ↓ + RED → GREEN → COMMIT + ↑ ↓ + └──────────────┘ + (Next Subtask) +``` + +### Phase Descriptions + +**PREFLIGHT** +- Validate task has subtasks +- Check git repository state +- Verify preconditions + +**BRANCH_SETUP** +- Create workflow branch: `task-{taskId}` +- Checkout new branch +- Initialize workflow context + +**SUBTASK_LOOP** +- **RED Phase**: Write failing tests + - Action: `generate_test` + - Validation: At least one test must fail + - Files: Test files + +- **GREEN Phase**: Implement code + - Action: `implement_code` + - Validation: All tests must pass + - Files: Implementation files + +- **COMMIT Phase**: Create commit + - Action: `commit_changes` + - Auto-generates commit message + - Advances to next subtask + +**FINALIZE** +- All subtasks complete +- Workflow ready for review/merge + +**COMPLETE** +- Workflow finished +- State can be cleaned up + +## Responsibility Matrix + +Clear division of responsibilities between AI Agent and TaskMaster. + +| Responsibility | AI Agent | TaskMaster | +|---------------|----------|------------| +| **Workflow Orchestration** | | ✓ | +| Start/resume workflow | Call CLI/MCP | Execute & validate | +| Track workflow state | Read state | Persist state | +| Manage TDD phases | Request transitions | Enforce transitions | +| Validate phase completion | | ✓ (RED must fail, GREEN must pass) | +| **Test Management** | | | +| Write test code | ✓ | | +| Run tests | ✓ | | +| Parse test output | ✓ | | +| Report test results | Provide JSON | Validate results | +| **Implementation** | | | +| Write implementation code | ✓ | | +| Ensure tests pass | ✓ | | +| Follow TDD cycle | ✓ (guided by TaskMaster) | Enforce rules | +| **Git Operations** | | | +| Create workflow branch | Request | ✓ Execute | +| Stage files | Request (optional) | ✓ Execute | +| Generate commit messages | | ✓ | +| Create commits | Request | ✓ Execute | +| **Progress Tracking** | | | +| Query progress | Call status | ✓ Provide data | +| Advance subtasks | | ✓ (automatic on commit) | +| Count attempts | | ✓ | +| Log activity | | ✓ | + +### AI Agent Responsibilities + +1. **Read and understand subtask requirements** +2. **Write test code** that validates the requirement +3. **Run test suite** using project's test command +4. **Parse test output** into JSON format +5. **Report results** to TaskMaster for validation +6. **Write implementation** to satisfy tests +7. **Request commits** when GREEN phase complete +8. **Handle errors** and retry within attempt limits + +### TaskMaster Responsibilities + +1. **Manage workflow state machine** +2. **Enforce TDD rules** (RED must fail, GREEN must pass) +3. **Track progress** (completed, current, attempts) +4. **Create git commits** with enhanced messages +5. **Manage git branches** and repository safety +6. **Validate transitions** between phases +7. **Persist state** for resumability +8. **Generate reports** and activity logs + +## Examples + +### Complete TDD Cycle Example + +#### 1. Start Workflow + +```bash +$ tm autopilot start 7 --json +``` + +```json +{ + "success": true, + "taskId": "7", + "branchName": "task-7", + "tddPhase": "RED", + "currentSubtask": { + "id": "1", + "title": "Implement start command", + "status": "in-progress" + }, + "nextAction": "generate_test" +} +``` + +#### 2. Get Next Action + +```bash +$ tm autopilot next --json +``` + +```json +{ + "action": "generate_test", + "actionDescription": "Write a failing test for the current subtask", + "tddPhase": "RED", + "currentSubtask": { + "id": "1", + "title": "Implement start command" + } +} +``` + +#### 3. Write Failing Test + +AI Agent creates `tests/start.test.ts`: + +```typescript +import { describe, it, expect } from 'vitest'; +import { StartCommand } from '../src/commands/start.js'; + +describe('StartCommand', () => { + it('should initialize workflow and create branch', async () => { + const command = new StartCommand(); + const result = await command.execute({ taskId: '7' }); + expect(result.success).toBe(true); + expect(result.branchName).toBe('task-7'); + }); +}); +``` + +#### 4. Run Tests (Should Fail) + +```bash +$ npm test +# Output: 1 test failed (expected) +``` + +Parse output to JSON: +```json +{ + "total": 1, + "passed": 0, + "failed": 1, + "skipped": 0 +} +``` + +#### 5. Complete RED Phase + +```bash +$ tm autopilot complete --results '{"total":1,"passed":0,"failed":1,"skipped":0}' --json +``` + +```json +{ + "success": true, + "message": "Completed RED phase", + "previousPhase": "RED", + "currentPhase": "GREEN", + "nextAction": "implement_code" +} +``` + +#### 6. Implement Code + +AI Agent creates `src/commands/start.ts`: + +```typescript +export class StartCommand { + async execute(options: { taskId: string }) { + // Implementation that makes test pass + return { + success: true, + branchName: `task-${options.taskId}` + }; + } +} +``` + +#### 7. Run Tests (Should Pass) + +```bash +$ npm test +# Output: 1 test passed +``` + +Parse output: +```json +{ + "total": 1, + "passed": 1, + "failed": 0, + "skipped": 0 +} +``` + +#### 8. Complete GREEN Phase + +```bash +$ tm autopilot complete --results '{"total":1,"passed":1,"failed":0,"skipped":0}' --json +``` + +```json +{ + "success": true, + "previousPhase": "GREEN", + "currentPhase": "COMMIT", + "nextAction": "commit_changes" +} +``` + +#### 9. Commit Changes + +```bash +$ tm autopilot commit --json +``` + +```json +{ + "success": true, + "commit": { + "hash": "abc123", + "message": "feat(autopilot): implement start command (Task 7.1)\n\n..." + }, + "subtaskCompleted": "1", + "currentSubtask": { + "id": "2", + "title": "Implement resume command" + }, + "nextAction": "generate_test" +} +``` + +### MCP Integration Example + +```typescript +// AI Agent using MCP tools + +async function implementTask(taskId: string) { + // Start workflow + const start = await mcp.call('autopilot_start', { + taskId, + projectRoot: '/path/to/project' + }); + + console.log(`Started task ${start.taskId} on branch ${start.branchName}`); + + while (true) { + // Get next action + const next = await mcp.call('autopilot_next', { + projectRoot: '/path/to/project' + }); + + console.log(`Next action: ${next.action}`); + + if (next.action === 'generate_test') { + // AI generates test + const testCode = await generateTest(next.currentSubtask); + await writeFile(testCode); + + // Run tests + const results = await runTests(); + + // Complete RED phase + await mcp.call('autopilot_complete_phase', { + projectRoot: '/path/to/project', + testResults: results + }); + + } else if (next.action === 'implement_code') { + // AI generates implementation + const implCode = await generateImplementation(next.currentSubtask); + await writeFile(implCode); + + // Run tests + const results = await runTests(); + + // Complete GREEN phase + await mcp.call('autopilot_complete_phase', { + projectRoot: '/path/to/project', + testResults: results + }); + + } else if (next.action === 'commit_changes') { + // Commit + const commit = await mcp.call('autopilot_commit', { + projectRoot: '/path/to/project' + }); + + console.log(`Committed: ${commit.commit.hash}`); + + if (commit.isComplete) { + console.log('Task complete!'); + break; + } + } + } +} +``` + +## Error Handling + +### Common Errors and Solutions + +#### Workflow Already Exists + +**Error:** +```json +{ + "error": "Workflow already in progress", + "suggestion": "Use autopilot_resume to continue the existing workflow" +} +``` + +**Solution:** +```bash +# Resume existing workflow +tm autopilot resume + +# OR force start new workflow +tm autopilot start 7 --force +``` + +#### RED Phase Validation Failed + +**Error:** +```json +{ + "error": "RED phase validation failed", + "reason": "At least one test must be failing in RED phase", + "actual": { "passed": 10, "failed": 0 }, + "suggestion": "Ensure you have written a failing test before proceeding" +} +``` + +**Solution:** The test isn't actually testing the new feature. Write a test that validates the new behavior that doesn't exist yet. + +#### GREEN Phase Validation Failed + +**Error:** +```json +{ + "error": "GREEN phase validation failed", + "reason": "All tests must pass in GREEN phase", + "actual": { "passed": 9, "failed": 1 }, + "suggestion": "Fix the implementation to make all tests pass" +} +``` + +**Solution:** Implementation isn't complete. Debug failing test and fix implementation. + +#### No Staged Changes + +**Error:** +```json +{ + "error": "No staged changes to commit", + "suggestion": "Make code changes before committing" +} +``` + +**Solution:** Ensure you've actually created/modified files before committing. + +#### Git Working Tree Not Clean + +**Error:** +```json +{ + "error": "Git validation failed: working tree not clean", + "suggestion": "Commit or stash changes before starting workflow" +} +``` + +**Solution:** +```bash +git status +git add . && git commit -m "chore: save work" +# Then start workflow +``` + +### Error Recovery Patterns + +#### Retry Pattern + +```typescript +async function withRetry<T>( + fn: () => Promise<T>, + maxAttempts: number = 3 +): Promise<T> { + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + return await fn(); + } catch (error) { + if (attempt === maxAttempts) throw error; + console.log(`Attempt ${attempt} failed, retrying...`); + await sleep(1000 * attempt); // Exponential backoff + } + } + throw new Error('Should not reach here'); +} + +// Usage +const results = await withRetry(async () => { + const output = await runTests(); + return parseTestResults(output); +}); +``` + +#### Graceful Degradation + +```typescript +async function completePhase(projectRoot: string, results: TestResults) { + try { + return await mcp.call('autopilot_complete_phase', { + projectRoot, + testResults: results + }); + } catch (error) { + console.error('Phase completion failed:', error); + + // Log error for debugging + await logError(error); + + // Attempt manual recovery + console.log('Attempting manual state recovery...'); + const status = await mcp.call('autopilot_status', { projectRoot }); + console.log('Current state:', status); + + // Provide user guidance + console.log('Manual intervention required:'); + console.log('1. Check test results are correct'); + console.log('2. Verify current phase allows transition'); + console.log('3. Run: tm autopilot status'); + + throw error; + } +} +``` + +## Troubleshooting + +### Workflow State Issues + +**Problem:** State file corrupted or inconsistent + +**Solution:** +```bash +# Check state file +cat .taskmaster/workflow-state.json + +# If corrupted, abort and restart +tm autopilot abort --force +tm autopilot start 7 +``` + +### Test Results Parsing + +**Problem:** Test output format not recognized + +**Solution:** +Ensure test results JSON has required fields: +```json +{ + "total": 10, // Required + "passed": 8, // Required + "failed": 2, // Required + "skipped": 0 // Optional +} +``` + +### Branch Conflicts + +**Problem:** Workflow branch already exists + +**Solution:** +```bash +# Check branches +git branch + +# Delete old workflow branch if safe +git branch -D task-7 + +# Start workflow again +tm autopilot start 7 +``` + +### Permission Issues + +**Problem:** Cannot write to .taskmaster directory + +**Solution:** +```bash +# Check directory permissions +ls -la .taskmaster/ + +# Fix permissions +chmod -R u+w .taskmaster/ +``` + +### State Persistence Failures + +**Problem:** State not saving between commands + +**Solution:** +```bash +# Check file system permissions +ls -la .taskmaster/workflow-state.json + +# Verify state is being written +tm autopilot status --json | jq . + +# If all else fails, reinstall +rm -rf .taskmaster/ +tm init +``` + +--- + +## Working with AI Agents + +Example prompts for AI agents (Claude Code, Cursor, etc.) to use the TDD workflow. + +### Starting a Task + +``` +I want to implement Task 7 using TDD workflow. Please: +1. Start the autopilot workflow +2. Show me the first subtask to implement +3. Begin the RED-GREEN-COMMIT cycle +``` + +### RED Phase - Writing Failing Tests + +``` +We're in RED phase for subtask "{SUBTASK_TITLE}". Please: +1. Read the subtask requirements +2. Write a test that validates the behavior +3. The test MUST fail because the feature doesn't exist yet +4. Run the tests and report results to complete the RED phase +``` + +### GREEN Phase - Implementing + +``` +We're in GREEN phase. The test is failing with: {ERROR_MESSAGE} + +Please: +1. Implement the minimal code to make this test pass +2. Don't over-engineer or add untested features +3. Run tests and report results to complete the GREEN phase +``` + +### Handling Errors + +``` +The RED phase validation failed - no test failures detected. + +Please: +1. Review the test I just wrote +2. Identify why it's not actually testing new behavior +3. Rewrite the test to properly fail until the feature is implemented +``` + +``` +GREEN phase validation failed - {N} tests still failing. + +Please: +1. Review the failing test output +2. Fix the implementation to pass all tests +3. Try completing the GREEN phase again +``` + +### Checking Progress + +``` +What's the current state of the workflow? Please show: +- Which subtask we're on +- Current TDD phase (RED/GREEN/COMMIT) +- Progress percentage +- Next action required +``` + +### Resuming Work + +``` +I have an in-progress workflow. Please: +1. Resume the autopilot workflow +2. Show current status +3. Continue from where we left off +``` + +--- + +## Additional Resources + +- [Command Reference](/command-reference) - Complete CLI command documentation +- [MCP Provider Guide](/capabilities/mcp) - MCP integration details +- [Task Structure](/capabilities/task-structure) - Understanding TaskMaster's task system +- [Configuration](/configuration) - Project configuration options diff --git a/apps/docs/tdd-workflow/quickstart.mdx b/apps/docs/tdd-workflow/quickstart.mdx new file mode 100644 index 00000000..75fc210c --- /dev/null +++ b/apps/docs/tdd-workflow/quickstart.mdx @@ -0,0 +1,313 @@ +--- +title: "TDD Workflow Quick Start" +description: "Get started with TaskMaster's autonomous TDD workflow in 5 minutes" +--- + +Get started with TaskMaster's autonomous TDD workflow in 5 minutes. + +## Prerequisites + +- TaskMaster initialized project (`tm init`) +- Tasks with subtasks created (`tm parse-prd` or `tm expand`) +- Git repository with clean working tree +- Test framework installed (vitest, jest, mocha, etc.) + +## 1. Start a Workflow + +```bash +tm autopilot start <taskId> +``` + +Example: +```bash +$ tm autopilot start 7 + +✓ Workflow started for task 7 +✓ Created branch: task-7 +✓ Current phase: RED +✓ Subtask 1/5: Implement start command +→ Next action: Write a failing test +``` + +## 2. The TDD Cycle + +### RED Phase: Write Failing Test + +```bash +# Check what to do next +$ tm autopilot next --json +{ + "action": "generate_test", + "currentSubtask": { + "id": "1", + "title": "Implement start command" + } +} +``` + +Write a test that fails: +```typescript +// tests/start.test.ts +import { describe, it, expect } from 'vitest'; +import { StartCommand } from '../src/commands/start'; + +describe('StartCommand', () => { + it('should initialize workflow', async () => { + const command = new StartCommand(); + const result = await command.execute({ taskId: '7' }); + expect(result.success).toBe(true); + }); +}); +``` + +Run tests: +```bash +$ npm test +# ✗ 1 test failed +``` + +Complete RED phase: +```bash +$ tm autopilot complete --results '{"total":1,"passed":0,"failed":1,"skipped":0}' + +✓ RED phase complete +✓ Current phase: GREEN +→ Next action: Implement code to pass tests +``` + +### GREEN Phase: Implement Feature + +Write minimal code to pass: +```typescript +// src/commands/start.ts +export class StartCommand { + async execute(options: { taskId: string }) { + return { success: true }; + } +} +``` + +Run tests: +```bash +$ npm test +# ✓ 1 test passed +``` + +Complete GREEN phase: +```bash +$ tm autopilot complete --results '{"total":1,"passed":1,"failed":0,"skipped":0}' + +✓ GREEN phase complete +✓ Current phase: COMMIT +→ Next action: Commit changes +``` + +### COMMIT Phase: Save Progress + +```bash +$ tm autopilot commit + +✓ Created commit: abc123 +✓ Message: feat(autopilot): implement start command (Task 7.1) +✓ Advanced to subtask 2/5 +✓ Current phase: RED +→ Next action: Write a failing test +``` + +## 3. Continue for All Subtasks + +Repeat the RED-GREEN-COMMIT cycle for each subtask until complete. + +```bash +# Check progress anytime +$ tm autopilot status --json +{ + "taskId": "7", + "progress": { + "completed": 1, + "total": 5, + "percentage": 20 + }, + "currentSubtask": { + "id": "2", + "title": "Implement resume command" + } +} +``` + +## 4. Complete the Workflow + +When all subtasks are done: + +```bash +$ tm autopilot status --json +{ + "phase": "COMPLETE", + "progress": { + "completed": 5, + "total": 5, + "percentage": 100 + } +} +``` + +Your branch `task-7` is ready for review/merge! + +## Common Patterns + +### Parse Test Output + +Your test runner outputs human-readable format - convert to JSON: + +**Vitest:** +``` +Tests 2 failed | 8 passed | 10 total +``` +→ `{"total":10,"passed":8,"failed":2,"skipped":0}` + +**Jest:** +``` +Tests: 2 failed, 8 passed, 10 total +``` +→ `{"total":10,"passed":8,"failed":2,"skipped":0}` + +### Handle Errors + +**Problem:** RED phase won't complete - "no test failures" + +**Solution:** Your test isn't testing new behavior. Make sure it fails: +```typescript +// Bad - test passes immediately +it('should exist', () => { + expect(StartCommand).toBeDefined(); // Always passes +}); + +// Good - test fails until feature exists +it('should initialize workflow', async () => { + const result = await new StartCommand().execute({ taskId: '7' }); + expect(result.success).toBe(true); // Fails until execute() is implemented +}); +``` + +**Problem:** GREEN phase won't complete - "tests still failing" + +**Solution:** Fix your implementation until all tests pass: +```bash +# Run tests to see what's failing +$ npm test + +# Fix the issue +$ vim src/commands/start.ts + +# Verify tests pass +$ npm test + +# Try again +$ tm autopilot complete --results '{"total":1,"passed":1,"failed":0,"skipped":0}' +``` + +### Resume Interrupted Work + +```bash +# If you interrupted the workflow +$ tm autopilot resume + +✓ Workflow resumed +✓ Task 7 - subtask 3/5 +✓ Current phase: GREEN +→ Continue from where you left off +``` + +## JSON Output Mode + +All commands support `--json` for programmatic use: + +```bash +$ tm autopilot start 7 --json | jq . +{ + "success": true, + "taskId": "7", + "branchName": "task-7", + "phase": "SUBTASK_LOOP", + "tddPhase": "RED", + "progress": { ... }, + "currentSubtask": { ... }, + "nextAction": "generate_test" +} +``` + +Perfect for: +- CI/CD integration +- Custom tooling +- Automated workflows +- Progress monitoring + +## MCP Integration + +For AI agents (Claude Code, etc.), use MCP tools: + +```typescript +// Start workflow +await mcp.call('autopilot_start', { + taskId: '7', + projectRoot: '/path/to/project' +}); + +// Get next action +const next = await mcp.call('autopilot_next', { + projectRoot: '/path/to/project' +}); + +// Complete phase +await mcp.call('autopilot_complete_phase', { + projectRoot: '/path/to/project', + testResults: { total: 1, passed: 0, failed: 1, skipped: 0 } +}); + +// Commit +await mcp.call('autopilot_commit', { + projectRoot: '/path/to/project' +}); +``` + +See [AI Agent Integration Guide](./ai-agent-integration.mdx) for details. + +## Cheat Sheet + +```bash +# Start +tm autopilot start <taskId> # Initialize workflow + +# Workflow Control +tm autopilot next # What's next? +tm autopilot status # Current state +tm autopilot resume # Continue interrupted work +tm autopilot abort # Cancel and cleanup + +# TDD Cycle +tm autopilot complete --results '{...}' # Advance phase +tm autopilot commit # Save progress + +# Options +--json # Machine-readable output +--project-root <path> # Specify project location +--force # Override safety checks +``` + +## Next Steps + +- Read [AI Agent Integration Guide](./ai-agent-integration.mdx) for complete documentation +- Check [Command Reference](/command-reference) for all options + +## Tips + +1. **Always let tests fail first** - That's the RED phase +2. **Write minimal code** - Just enough to pass +3. **Commit frequently** - After each subtask +4. **Use --json** - Better for programmatic use +5. **Check status often** - Know where you are +6. **Trust the workflow** - It enforces TDD rules + +--- + +**Ready to start?** Run `tm autopilot start <taskId>` and begin your TDD journey! diff --git a/apps/extension/package.json b/apps/extension/package.json index dcb85d8a..a90c3086 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -275,7 +275,7 @@ "tailwindcss": "4.1.11", "typescript": "^5.9.2", "@tm/core": "*", - "task-master-ai": "*" + "task-master-ai": "0.30.0-rc.0" }, "overrides": { "glob@<8": "^10.4.5", diff --git a/apps/mcp/package.json b/apps/mcp/package.json new file mode 100644 index 00000000..4fbf83a0 --- /dev/null +++ b/apps/mcp/package.json @@ -0,0 +1,54 @@ +{ + "name": "@tm/mcp", + "description": "Task Master MCP Tools - TypeScript MCP server tools for AI agent integration", + "type": "module", + "private": true, + "version": "", + "main": "./dist/index.js", + "types": "./src/index.ts", + "exports": { + ".": "./src/index.ts", + "./tools/autopilot": "./src/tools/autopilot/index.ts" + }, + "files": ["dist", "README.md"], + "scripts": { + "typecheck": "tsc --noEmit", + "lint": "biome check src", + "format": "biome format --write src", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "test:unit": "vitest run -t unit", + "test:integration": "vitest run -t integration", + "test:ci": "vitest run --coverage --reporter=dot" + }, + "dependencies": { + "@tm/core": "*", + "zod": "^4.1.11", + "fastmcp": "^3.19.2" + }, + "devDependencies": { + "@biomejs/biome": "^1.9.4", + "@types/node": "^22.10.5", + "typescript": "^5.9.2", + "vitest": "^3.2.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "keywords": [ + "task-master", + "mcp", + "mcp-server", + "ai-agent", + "workflow", + "tdd" + ], + "author": "", + "license": "MIT", + "typesVersions": { + "*": { + "*": ["src/*"] + } + } +} diff --git a/apps/mcp/src/index.ts b/apps/mcp/src/index.ts new file mode 100644 index 00000000..93c31ab1 --- /dev/null +++ b/apps/mcp/src/index.ts @@ -0,0 +1,8 @@ +/** + * @fileoverview Main entry point for @tm/mcp package + * Exports all MCP tool registration functions + */ + +export * from './tools/autopilot/index.js'; +export * from './shared/utils.js'; +export * from './shared/types.js'; diff --git a/apps/mcp/src/shared/types.ts b/apps/mcp/src/shared/types.ts new file mode 100644 index 00000000..0e15fef5 --- /dev/null +++ b/apps/mcp/src/shared/types.ts @@ -0,0 +1,36 @@ +/** + * Shared types for MCP tools + */ + +export interface MCPResponse<T = any> { + success: boolean; + data?: T; + error?: { + code: string; + message: string; + suggestion?: string; + details?: any; + }; + version?: { + version: string; + name: string; + }; + tag?: { + currentTag: string; + availableTags: string[]; + }; +} + +export interface MCPContext { + log: { + info: (message: string) => void; + warn: (message: string) => void; + error: (message: string) => void; + debug: (message: string) => void; + }; + session: any; +} + +export interface WithProjectRoot { + projectRoot: string; +} diff --git a/apps/mcp/src/shared/utils.ts b/apps/mcp/src/shared/utils.ts new file mode 100644 index 00000000..8d226f33 --- /dev/null +++ b/apps/mcp/src/shared/utils.ts @@ -0,0 +1,257 @@ +/** + * Shared utilities for MCP tools + */ + +import type { ContentResult } from 'fastmcp'; +import path from 'node:path'; +import fs from 'node:fs'; +import packageJson from '../../../../package.json' with { type: 'json' }; + +/** + * Get version information + */ +export function getVersionInfo() { + return { + version: packageJson.version || 'unknown', + name: packageJson.name || 'task-master-ai' + }; +} + +/** + * Get current tag for a project root + */ +export function getCurrentTag(projectRoot: string): string | null { + try { + // Try to read current tag from state.json + const stateJsonPath = path.join(projectRoot, '.taskmaster', 'state.json'); + + if (fs.existsSync(stateJsonPath)) { + const stateData = JSON.parse(fs.readFileSync(stateJsonPath, 'utf-8')); + return stateData.currentTag || 'master'; + } + + return 'master'; + } catch { + return null; + } +} + +/** + * Handle API result with standardized error handling and response formatting + * This provides a consistent response structure for all MCP tools + */ +export async function handleApiResult<T>(options: { + result: { success: boolean; data?: T; error?: { message: string } }; + log?: any; + errorPrefix?: string; + projectRoot?: string; +}): Promise<ContentResult> { + const { result, log, errorPrefix = 'API error', projectRoot } = options; + + // Get version info for every response + const versionInfo = getVersionInfo(); + + // Get current tag if project root is provided + const currentTag = projectRoot ? getCurrentTag(projectRoot) : null; + + if (!result.success) { + const errorMsg = result.error?.message || `Unknown ${errorPrefix}`; + log?.error?.(`${errorPrefix}: ${errorMsg}`); + + let errorText = `Error: ${errorMsg}\nVersion: ${versionInfo.version}\nName: ${versionInfo.name}`; + + if (currentTag) { + errorText += `\nCurrent Tag: ${currentTag}`; + } + + return { + content: [ + { + type: 'text', + text: errorText + } + ], + isError: true + }; + } + + log?.info?.('Successfully completed operation'); + + // Create the response payload including version info and tag + const responsePayload: any = { + data: result.data, + version: versionInfo + }; + + // Add current tag if available + if (currentTag) { + responsePayload.tag = currentTag; + } + + return { + content: [ + { + type: 'text', + text: JSON.stringify(responsePayload, null, 2) + } + ] + }; +} + +/** + * Normalize project root path (handles URI encoding, file:// protocol, Windows paths) + */ +export function normalizeProjectRoot(rawPath: string): string { + if (!rawPath) return process.cwd(); + + try { + let pathString = rawPath; + + // Decode URI encoding + try { + pathString = decodeURIComponent(pathString); + } catch { + // If decoding fails, use as-is + } + + // Strip file:// prefix + if (pathString.startsWith('file:///')) { + pathString = pathString.slice(7); + } else if (pathString.startsWith('file://')) { + pathString = pathString.slice(7); + } + + // Handle Windows drive letter after stripping prefix (e.g., /C:/...) + if ( + pathString.startsWith('/') && + /[A-Za-z]:/.test(pathString.substring(1, 3)) + ) { + pathString = pathString.substring(1); + } + + // Normalize backslashes to forward slashes + pathString = pathString.replace(/\\/g, '/'); + + // Resolve to absolute path + return path.resolve(pathString); + } catch { + return path.resolve(rawPath); + } +} + +/** + * Get project root from session object + */ +function getProjectRootFromSession(session: any): string | null { + try { + // Check primary location + if (session?.roots?.[0]?.uri) { + return normalizeProjectRoot(session.roots[0].uri); + } + // Check alternate location + else if (session?.roots?.roots?.[0]?.uri) { + return normalizeProjectRoot(session.roots.roots[0].uri); + } + return null; + } catch { + return null; + } +} + +/** + * Wrapper to normalize project root in args with proper precedence order + * + * PRECEDENCE ORDER: + * 1. TASK_MASTER_PROJECT_ROOT environment variable (from process.env or session) + * 2. args.projectRoot (explicitly provided) + * 3. Session-based project root resolution + * 4. Current directory fallback + */ +export function withNormalizedProjectRoot<T extends { projectRoot?: string }>( + fn: ( + args: T & { projectRoot: string }, + context: any + ) => Promise<ContentResult> +): (args: T, context: any) => Promise<ContentResult> { + return async (args: T, context: any): Promise<ContentResult> => { + const { log, session } = context; + let normalizedRoot: string | null = null; + let rootSource = 'unknown'; + + try { + // 1. Check for TASK_MASTER_PROJECT_ROOT environment variable first + if (process.env.TASK_MASTER_PROJECT_ROOT) { + const envRoot = process.env.TASK_MASTER_PROJECT_ROOT; + normalizedRoot = path.isAbsolute(envRoot) + ? envRoot + : path.resolve(process.cwd(), envRoot); + rootSource = 'TASK_MASTER_PROJECT_ROOT environment variable'; + log?.info?.(`Using project root from ${rootSource}: ${normalizedRoot}`); + } + // Also check session environment variables for TASK_MASTER_PROJECT_ROOT + else if (session?.env?.TASK_MASTER_PROJECT_ROOT) { + const envRoot = session.env.TASK_MASTER_PROJECT_ROOT; + normalizedRoot = path.isAbsolute(envRoot) + ? envRoot + : path.resolve(process.cwd(), envRoot); + rootSource = 'TASK_MASTER_PROJECT_ROOT session environment variable'; + log?.info?.(`Using project root from ${rootSource}: ${normalizedRoot}`); + } + // 2. If no environment variable, try args.projectRoot + else if (args.projectRoot) { + normalizedRoot = normalizeProjectRoot(args.projectRoot); + rootSource = 'args.projectRoot'; + log?.info?.(`Using project root from ${rootSource}: ${normalizedRoot}`); + } + // 3. If no args.projectRoot, try session-based resolution + else { + const sessionRoot = getProjectRootFromSession(session); + if (sessionRoot) { + normalizedRoot = sessionRoot; + rootSource = 'session'; + log?.info?.( + `Using project root from ${rootSource}: ${normalizedRoot}` + ); + } + } + + if (!normalizedRoot) { + log?.error?.( + 'Could not determine project root from environment, args, or session.' + ); + return handleApiResult({ + result: { + success: false, + error: { + message: + 'Could not determine project root. Please provide projectRoot argument or ensure TASK_MASTER_PROJECT_ROOT environment variable is set.' + } + } + }); + } + + // Inject the normalized root back into args + const updatedArgs = { ...args, projectRoot: normalizedRoot } as T & { + projectRoot: string; + }; + + // Execute the original function with normalized root in args + return await fn(updatedArgs, context); + } catch (error: any) { + log?.error?.( + `Error within withNormalizedProjectRoot HOF (Normalized Root: ${normalizedRoot}): ${error.message}` + ); + if (error.stack && log?.debug) { + log.debug(error.stack); + } + return handleApiResult({ + result: { + success: false, + error: { + message: `Operation failed: ${error.message}` + } + } + }); + } + }; +} diff --git a/apps/mcp/src/tools/autopilot/abort.tool.ts b/apps/mcp/src/tools/autopilot/abort.tool.ts new file mode 100644 index 00000000..1da290d8 --- /dev/null +++ b/apps/mcp/src/tools/autopilot/abort.tool.ts @@ -0,0 +1,99 @@ +/** + * @fileoverview autopilot-abort MCP tool + * Abort a running TDD workflow and clean up state + */ + +import { z } from 'zod'; +import { + handleApiResult, + withNormalizedProjectRoot +} from '../../shared/utils.js'; +import type { MCPContext } from '../../shared/types.js'; +import { WorkflowService } from '@tm/core'; +import type { FastMCP } from 'fastmcp'; + +const AbortSchema = z.object({ + projectRoot: z + .string() + .describe('Absolute path to the project root directory') +}); + +type AbortArgs = z.infer<typeof AbortSchema>; + +/** + * Register the autopilot_abort tool with the MCP server + */ +export function registerAutopilotAbortTool(server: FastMCP) { + server.addTool({ + name: 'autopilot_abort', + description: + 'Abort the current TDD workflow and clean up workflow state. This will remove the workflow state file but will NOT delete the git branch or any code changes.', + parameters: AbortSchema, + execute: withNormalizedProjectRoot( + async (args: AbortArgs, context: MCPContext) => { + const { projectRoot } = args; + + try { + context.log.info(`Aborting autopilot workflow in ${projectRoot}`); + + const workflowService = new WorkflowService(projectRoot); + + // Check if workflow exists + const hasWorkflow = await workflowService.hasWorkflow(); + + if (!hasWorkflow) { + context.log.warn('No active workflow to abort'); + return handleApiResult({ + result: { + success: true, + data: { + message: 'No active workflow to abort', + hadWorkflow: false + } + }, + log: context.log, + projectRoot + }); + } + + // Get info before aborting + await workflowService.resumeWorkflow(); + const status = workflowService.getStatus(); + + // Abort workflow + await workflowService.abortWorkflow(); + + context.log.info('Workflow state deleted'); + + return handleApiResult({ + result: { + success: true, + data: { + message: 'Workflow aborted', + hadWorkflow: true, + taskId: status.taskId, + branchName: status.branchName, + note: 'Git branch and code changes were preserved. You can manually clean them up if needed.' + } + }, + log: context.log, + projectRoot + }); + } catch (error: any) { + context.log.error(`Error in autopilot-abort: ${error.message}`); + if (error.stack) { + context.log.debug(error.stack); + } + return handleApiResult({ + result: { + success: false, + error: { message: `Failed to abort workflow: ${error.message}` } + }, + log: context.log, + projectRoot + }); + } + } + ) + }); +} diff --git a/apps/mcp/src/tools/autopilot/commit.tool.ts b/apps/mcp/src/tools/autopilot/commit.tool.ts new file mode 100644 index 00000000..babd94a2 --- /dev/null +++ b/apps/mcp/src/tools/autopilot/commit.tool.ts @@ -0,0 +1,240 @@ +/** + * @fileoverview autopilot-commit MCP tool + * Create a git commit with automatic staging and message generation + */ + +import { z } from 'zod'; +import { + handleApiResult, + withNormalizedProjectRoot +} from '../../shared/utils.js'; +import type { MCPContext } from '../../shared/types.js'; +import { WorkflowService, GitAdapter, CommitMessageGenerator } from '@tm/core'; +import type { FastMCP } from 'fastmcp'; + +const CommitSchema = z.object({ + projectRoot: z + .string() + .describe('Absolute path to the project root directory'), + files: z + .array(z.string()) + .optional() + .describe( + 'Specific files to stage (relative to project root). If not provided, stages all changes.' + ), + customMessage: z + .string() + .optional() + .describe('Custom commit message to use instead of auto-generated message') +}); + +type CommitArgs = z.infer<typeof CommitSchema>; + +/** + * Register the autopilot_commit tool with the MCP server + */ +export function registerAutopilotCommitTool(server: FastMCP) { + server.addTool({ + name: 'autopilot_commit', + description: + 'Create a git commit with automatic staging, message generation, and metadata embedding. Generates appropriate commit messages based on subtask context and TDD phase.', + parameters: CommitSchema, + execute: withNormalizedProjectRoot( + async (args: CommitArgs, context: MCPContext) => { + const { projectRoot, files, customMessage } = args; + + try { + context.log.info(`Creating commit for workflow in ${projectRoot}`); + + const workflowService = new WorkflowService(projectRoot); + + // Check if workflow exists + if (!(await workflowService.hasWorkflow())) { + return handleApiResult({ + result: { + success: false, + error: { + message: + 'No active workflow found. Start a workflow with autopilot_start' + } + }, + log: context.log, + projectRoot + }); + } + + // Resume workflow + await workflowService.resumeWorkflow(); + const status = workflowService.getStatus(); + const workflowContext = workflowService.getContext(); + + // Verify we're in COMMIT phase + if (status.tddPhase !== 'COMMIT') { + context.log.warn( + `Not in COMMIT phase (currently in ${status.tddPhase})` + ); + return handleApiResult({ + result: { + success: false, + error: { + message: `Cannot commit: currently in ${status.tddPhase} phase. Complete the ${status.tddPhase} phase first using autopilot_complete_phase` + } + }, + log: context.log, + projectRoot + }); + } + + // Verify there's an active subtask + if (!status.currentSubtask) { + return handleApiResult({ + result: { + success: false, + error: { message: 'No active subtask to commit' } + }, + log: context.log, + projectRoot + }); + } + + // Initialize git adapter + const gitAdapter = new GitAdapter(projectRoot); + + // Stage files + try { + if (files && files.length > 0) { + await gitAdapter.stageFiles(files); + context.log.info(`Staged ${files.length} files`); + } else { + await gitAdapter.stageFiles(['.']); + context.log.info('Staged all changes'); + } + } catch (error: any) { + context.log.error(`Failed to stage files: ${error.message}`); + return handleApiResult({ + result: { + success: false, + error: { message: `Failed to stage files: ${error.message}` } + }, + log: context.log, + projectRoot + }); + } + + // Check if there are staged changes + const hasStagedChanges = await gitAdapter.hasStagedChanges(); + if (!hasStagedChanges) { + context.log.warn('No staged changes to commit'); + return handleApiResult({ + result: { + success: false, + error: { + message: + 'No staged changes to commit. Make code changes before committing' + } + }, + log: context.log, + projectRoot + }); + } + + // Get git status for message generation + const gitStatus = await gitAdapter.getStatus(); + + // Generate commit message + let commitMessage: string; + if (customMessage) { + commitMessage = customMessage; + context.log.info('Using custom commit message'); + } else { + const messageGenerator = new CommitMessageGenerator(); + + // Determine commit type based on phase and subtask + // RED phase = test files, GREEN phase = implementation + const type = status.tddPhase === 'COMMIT' ? 'feat' : 'test'; + + // Use subtask title as description + const description = status.currentSubtask.title; + + // Construct proper CommitMessageOptions + const options = { + type, + description, + changedFiles: gitStatus.staged, + taskId: status.taskId, + phase: status.tddPhase, + testsPassing: workflowContext.lastTestResults?.passed, + testsFailing: workflowContext.lastTestResults?.failed + }; + + commitMessage = messageGenerator.generateMessage(options); + context.log.info('Generated commit message automatically'); + } + + // Create commit + try { + await gitAdapter.createCommit(commitMessage); + context.log.info('Commit created successfully'); + } catch (error: any) { + context.log.error(`Failed to create commit: ${error.message}`); + return handleApiResult({ + result: { + success: false, + error: { message: `Failed to create commit: ${error.message}` } + }, + log: context.log, + projectRoot + }); + } + + // Get last commit info + const lastCommit = await gitAdapter.getLastCommit(); + + // Complete COMMIT phase and advance workflow + const newStatus = await workflowService.commit(); + + context.log.info( + `Commit completed. Current phase: ${newStatus.tddPhase || newStatus.phase}` + ); + + const isComplete = newStatus.phase === 'COMPLETE'; + + // Get next action with guidance + const nextAction = workflowService.getNextAction(); + + return handleApiResult({ + result: { + success: true, + data: { + message: isComplete + ? 'Workflow completed successfully' + : 'Commit created and workflow advanced', + commitSha: lastCommit.sha, + commitMessage, + ...newStatus, + isComplete, + nextAction: nextAction.action, + nextSteps: nextAction.nextSteps + } + }, + log: context.log, + projectRoot + }); + } catch (error: any) { + context.log.error(`Error in autopilot-commit: ${error.message}`); + if (error.stack) { + context.log.debug(error.stack); + } + return handleApiResult({ + result: { + success: false, + error: { message: `Failed to commit: ${error.message}` } + }, + log: context.log, + projectRoot + }); + } + } + ) + }); +} diff --git a/apps/mcp/src/tools/autopilot/complete.tool.ts b/apps/mcp/src/tools/autopilot/complete.tool.ts new file mode 100644 index 00000000..5b4d1023 --- /dev/null +++ b/apps/mcp/src/tools/autopilot/complete.tool.ts @@ -0,0 +1,152 @@ +/** + * @fileoverview autopilot-complete MCP tool + * Complete the current TDD phase with test result validation + */ + +import { z } from 'zod'; +import { + handleApiResult, + withNormalizedProjectRoot +} from '../../shared/utils.js'; +import type { MCPContext } from '../../shared/types.js'; +import { WorkflowService } from '@tm/core'; +import type { FastMCP } from 'fastmcp'; + +const CompletePhaseSchema = z.object({ + projectRoot: z + .string() + .describe('Absolute path to the project root directory'), + testResults: z + .object({ + total: z.number().describe('Total number of tests'), + passed: z.number().describe('Number of passing tests'), + failed: z.number().describe('Number of failing tests'), + skipped: z.number().optional().describe('Number of skipped tests') + }) + .describe('Test results from running the test suite') +}); + +type CompletePhaseArgs = z.infer<typeof CompletePhaseSchema>; + +/** + * Register the autopilot_complete_phase tool with the MCP server + */ +export function registerAutopilotCompleteTool(server: FastMCP) { + server.addTool({ + name: 'autopilot_complete_phase', + description: + 'Complete the current TDD phase (RED, GREEN, or COMMIT) with test result validation. RED phase: expects failures (if 0 failures, feature is already implemented and subtask auto-completes). GREEN phase: expects all tests passing.', + parameters: CompletePhaseSchema, + execute: withNormalizedProjectRoot( + async (args: CompletePhaseArgs, context: MCPContext) => { + const { projectRoot, testResults } = args; + + try { + context.log.info( + `Completing current phase in workflow for ${projectRoot}` + ); + + const workflowService = new WorkflowService(projectRoot); + + // Check if workflow exists + if (!(await workflowService.hasWorkflow())) { + return handleApiResult({ + result: { + success: false, + error: { + message: + 'No active workflow found. Start a workflow with autopilot_start' + } + }, + log: context.log, + projectRoot + }); + } + + // Resume workflow to get current state + await workflowService.resumeWorkflow(); + const currentStatus = workflowService.getStatus(); + + // Validate that we're in a TDD phase (RED or GREEN) + if (!currentStatus.tddPhase) { + return handleApiResult({ + result: { + success: false, + error: { + message: `Cannot complete phase: not in a TDD phase (current phase: ${currentStatus.phase})` + } + }, + log: context.log, + projectRoot + }); + } + + // COMMIT phase completion is handled by autopilot_commit tool + if (currentStatus.tddPhase === 'COMMIT') { + return handleApiResult({ + result: { + success: false, + error: { + message: + 'Cannot complete COMMIT phase with this tool. Use autopilot_commit instead' + } + }, + log: context.log, + projectRoot + }); + } + + // Map TDD phase to TestResult phase (only RED or GREEN allowed) + const phase = currentStatus.tddPhase as 'RED' | 'GREEN'; + + // Construct full TestResult with phase + const fullTestResults = { + total: testResults.total, + passed: testResults.passed, + failed: testResults.failed, + skipped: testResults.skipped ?? 0, + phase + }; + + // Complete phase with test results + const status = await workflowService.completePhase(fullTestResults); + const nextAction = workflowService.getNextAction(); + + context.log.info( + `Phase completed. New phase: ${status.tddPhase || status.phase}` + ); + + return handleApiResult({ + result: { + success: true, + data: { + message: `Phase completed. Transitioned to ${status.tddPhase || status.phase}`, + ...status, + nextAction: nextAction.action, + actionDescription: nextAction.description, + nextSteps: nextAction.nextSteps + } + }, + log: context.log, + projectRoot + }); + } catch (error: any) { + context.log.error(`Error in autopilot-complete: ${error.message}`); + if (error.stack) { + context.log.debug(error.stack); + } + return handleApiResult({ + result: { + success: false, + error: { + message: `Failed to complete phase: ${error.message}` + } + }, + log: context.log, + projectRoot + }); + } + } + ) + }); +} diff --git a/apps/mcp/src/tools/autopilot/finalize.tool.ts b/apps/mcp/src/tools/autopilot/finalize.tool.ts new file mode 100644 index 00000000..4fb94f41 --- /dev/null +++ b/apps/mcp/src/tools/autopilot/finalize.tool.ts @@ -0,0 +1,114 @@ +/** + * @fileoverview autopilot-finalize MCP tool + * Finalize and complete the workflow with working tree validation + */ + +import { z } from 'zod'; +import { + handleApiResult, + withNormalizedProjectRoot +} from '../../shared/utils.js'; +import type { MCPContext } from '../../shared/types.js'; +import { WorkflowService } from '@tm/core'; +import type { FastMCP } from 'fastmcp'; + +const FinalizeSchema = z.object({ + projectRoot: z + .string() + .describe('Absolute path to the project root directory') +}); + +type FinalizeArgs = z.infer<typeof FinalizeSchema>; + +/** + * Register the autopilot_finalize tool with the MCP server + */ +export function registerAutopilotFinalizeTool(server: FastMCP) { + server.addTool({ + name: 'autopilot_finalize', + description: + 'Finalize and complete the workflow. Validates that all changes are committed and working tree is clean before marking workflow as complete.', + parameters: FinalizeSchema, + execute: withNormalizedProjectRoot( + async (args: FinalizeArgs, context: MCPContext) => { + const { projectRoot } = args; + + try { + context.log.info(`Finalizing workflow in ${projectRoot}`); + + const workflowService = new WorkflowService(projectRoot); + + // Check if workflow exists + if (!(await workflowService.hasWorkflow())) { + return handleApiResult({ + result: { + success: false, + error: { + message: + 'No active workflow found. Start a workflow with autopilot_start' + } + }, + log: context.log, + projectRoot + }); + } + + // Resume workflow + await workflowService.resumeWorkflow(); + const currentStatus = workflowService.getStatus(); + + // Verify we're in FINALIZE phase + if (currentStatus.phase !== 'FINALIZE') { + return handleApiResult({ + result: { + success: false, + error: { + message: `Cannot finalize: workflow is in ${currentStatus.phase} phase. Complete all subtasks first.` + } + }, + log: context.log, + projectRoot + }); + } + + // Finalize workflow (validates clean working tree) + const newStatus = await workflowService.finalizeWorkflow(); + + context.log.info('Workflow finalized successfully'); + + // Get next action + const nextAction = workflowService.getNextAction(); + + return handleApiResult({ + result: { + success: true, + data: { + message: 'Workflow completed successfully', + ...newStatus, + nextAction: nextAction.action, + nextSteps: nextAction.nextSteps + } + }, + log: context.log, + projectRoot + }); + } catch (error: any) { + context.log.error(`Error in autopilot-finalize: ${error.message}`); + if (error.stack) { + context.log.debug(error.stack); + } + return handleApiResult({ + result: { + success: false, + error: { + message: `Failed to finalize workflow: ${error.message}` + } + }, + log: context.log, + projectRoot + }); + } + } + ) + }); +} diff --git a/apps/mcp/src/tools/autopilot/index.ts b/apps/mcp/src/tools/autopilot/index.ts new file mode 100644 index 00000000..8d385370 --- /dev/null +++ b/apps/mcp/src/tools/autopilot/index.ts @@ -0,0 +1,13 @@ +/** + * @fileoverview Autopilot MCP tools index + * Exports all autopilot tool registration functions + */ + +export { registerAutopilotStartTool } from './start.tool.js'; +export { registerAutopilotResumeTool } from './resume.tool.js'; +export { registerAutopilotNextTool } from './next.tool.js'; +export { registerAutopilotStatusTool } from './status.tool.js'; +export { registerAutopilotCompleteTool } from './complete.tool.js'; +export { registerAutopilotCommitTool } from './commit.tool.js'; +export { registerAutopilotFinalizeTool } from './finalize.tool.js'; +export { registerAutopilotAbortTool } from './abort.tool.js'; diff --git a/apps/mcp/src/tools/autopilot/next.tool.ts b/apps/mcp/src/tools/autopilot/next.tool.ts new file mode 100644 index 00000000..821c95e2 --- /dev/null +++ b/apps/mcp/src/tools/autopilot/next.tool.ts @@ -0,0 +1,99 @@ +/** + * @fileoverview autopilot-next MCP tool + * Get the next action to perform in the TDD workflow + */ + +import { z } from 'zod'; +import { + handleApiResult, + withNormalizedProjectRoot +} from '../../shared/utils.js'; +import type { MCPContext } from '../../shared/types.js'; +import { WorkflowService } from '@tm/core'; +import type { FastMCP } from 'fastmcp'; + +const NextActionSchema = z.object({ + projectRoot: z + .string() + .describe('Absolute path to the project root directory') +}); + +type NextActionArgs = z.infer<typeof NextActionSchema>; + +/** + * Register the autopilot_next tool with the MCP server + */ +export function registerAutopilotNextTool(server: FastMCP) { + server.addTool({ + name: 'autopilot_next', + description: + 'Get the next action to perform in the TDD workflow. Returns detailed context about what needs to be done next, including the current phase, subtask, and expected actions.', + parameters: NextActionSchema, + execute: withNormalizedProjectRoot( + async (args: NextActionArgs, context: MCPContext) => { + const { projectRoot } = args; + + try { + context.log.info( + `Getting next action for workflow in ${projectRoot}` + ); + + const workflowService = new WorkflowService(projectRoot); + + // Check if workflow exists + if (!(await workflowService.hasWorkflow())) { + return handleApiResult({ + result: { + success: false, + error: { + message: + 'No active workflow found. Start a workflow with autopilot_start' + } + }, + log: context.log, + projectRoot + }); + } + + // Resume to load state + await workflowService.resumeWorkflow(); + + // Get next action + const nextAction = workflowService.getNextAction(); + const status = workflowService.getStatus(); + + context.log.info(`Next action determined: ${nextAction.action}`); + + return handleApiResult({ + result: { + success: true, + data: { + action: nextAction.action, + actionDescription: nextAction.description, + ...status, + nextSteps: nextAction.nextSteps + } + }, + log: context.log, + projectRoot + }); + } catch (error: any) { + context.log.error(`Error in autopilot-next: ${error.message}`); + if (error.stack) { + context.log.debug(error.stack); + } + return handleApiResult({ + result: { + success: false, + error: { + message: `Failed to get next action: ${error.message}` + } + }, + log: context.log, + projectRoot + }); + } + } + ) + }); +} diff --git a/apps/mcp/src/tools/autopilot/resume.tool.ts b/apps/mcp/src/tools/autopilot/resume.tool.ts new file mode 100644 index 00000000..1a65ad3a --- /dev/null +++ b/apps/mcp/src/tools/autopilot/resume.tool.ts @@ -0,0 +1,95 @@ +/** + * @fileoverview autopilot-resume MCP tool + * Resume a previously started TDD workflow from saved state + */ + +import { z } from 'zod'; +import { + handleApiResult, + withNormalizedProjectRoot +} from '../../shared/utils.js'; +import type { MCPContext } from '../../shared/types.js'; +import { WorkflowService } from '@tm/core'; +import type { FastMCP } from 'fastmcp'; + +const ResumeWorkflowSchema = z.object({ + projectRoot: z + .string() + .describe('Absolute path to the project root directory') +}); + +type ResumeWorkflowArgs = z.infer<typeof ResumeWorkflowSchema>; + +/** + * Register the autopilot_resume tool with the MCP server + */ +export function registerAutopilotResumeTool(server: FastMCP) { + server.addTool({ + name: 'autopilot_resume', + description: + 'Resume a previously started TDD workflow from saved state. Restores the workflow state machine and continues from where it left off.', + parameters: ResumeWorkflowSchema, + execute: withNormalizedProjectRoot( + async (args: ResumeWorkflowArgs, context: MCPContext) => { + const { projectRoot } = args; + + try { + context.log.info(`Resuming autopilot workflow in ${projectRoot}`); + + const workflowService = new WorkflowService(projectRoot); + + // Check if workflow exists + if (!(await workflowService.hasWorkflow())) { + return handleApiResult({ + result: { + success: false, + error: { + message: + 'No workflow state found. Start a new workflow with autopilot_start' + } + }, + log: context.log, + projectRoot + }); + } + + // Resume workflow + const status = await workflowService.resumeWorkflow(); + const nextAction = workflowService.getNextAction(); + + context.log.info( + `Workflow resumed successfully for task ${status.taskId}` + ); + + return handleApiResult({ + result: { + success: true, + data: { + message: 'Workflow resumed', + ...status, + nextAction: nextAction.action, + actionDescription: nextAction.description, + nextSteps: nextAction.nextSteps + } + }, + log: context.log, + projectRoot + }); + } catch (error: any) { + context.log.error(`Error in autopilot-resume: ${error.message}`); + if (error.stack) { + context.log.debug(error.stack); + } + return handleApiResult({ + result: { + success: false, + error: { message: `Failed to resume workflow: ${error.message}` } + }, + log: context.log, + projectRoot + }); + } + } + ) + }); +} diff --git a/apps/mcp/src/tools/autopilot/start.tool.ts b/apps/mcp/src/tools/autopilot/start.tool.ts new file mode 100644 index 00000000..8f4ea54b --- /dev/null +++ b/apps/mcp/src/tools/autopilot/start.tool.ts @@ -0,0 +1,197 @@ +/** + * @fileoverview autopilot-start MCP tool + * Initialize and start a new TDD workflow for a task + */ + +import { z } from 'zod'; +import { + handleApiResult, + withNormalizedProjectRoot +} from '../../shared/utils.js'; +import type { MCPContext } from '../../shared/types.js'; +import { createTaskMasterCore } from '@tm/core'; +import { WorkflowService } from '@tm/core'; +import type { FastMCP } from 'fastmcp'; + +const StartWorkflowSchema = z.object({ + taskId: z + .string() + .describe( + 'Main task ID to start workflow for (e.g., "1", "2", "HAM-123"). Subtask IDs (e.g., "2.3", "1.1") are not allowed.' + ), + projectRoot: z + .string() + .describe('Absolute path to the project root directory'), + maxAttempts: z + .number() + .optional() + .default(3) + .describe('Maximum attempts per subtask (default: 3)'), + force: z + .boolean() + .optional() + .default(false) + .describe('Force start even if workflow state exists') +}); + +type StartWorkflowArgs = z.infer<typeof StartWorkflowSchema>; + +/** + * Check if a task ID is a main task (not a subtask) + * Main tasks: "1", "2", "HAM-123", "PROJ-456" + * Subtasks: "1.1", "2.3", "HAM-123.1" + */ +function isMainTaskId(taskId: string): boolean { + // A main task has no dots in the ID after the optional prefix + // Examples: "1" ✓, "HAM-123" ✓, "1.1" ✗, "HAM-123.1" ✗ + const parts = taskId.split('.'); + return parts.length === 1; +} + +/** + * Register the autopilot_start tool with the MCP server + */ +export function registerAutopilotStartTool(server: FastMCP) { + server.addTool({ + name: 'autopilot_start', + description: + 'Initialize and start a new TDD workflow for a task. Creates a git branch and sets up the workflow state machine.', + parameters: StartWorkflowSchema, + execute: withNormalizedProjectRoot( + async (args: StartWorkflowArgs, context: MCPContext) => { + const { taskId, projectRoot, maxAttempts, force } = args; + + try { + context.log.info( + `Starting autopilot workflow for task ${taskId} in ${projectRoot}` + ); + + // Validate that taskId is a main task (not a subtask) + if (!isMainTaskId(taskId)) { + return handleApiResult({ + result: { + success: false, + error: { + message: `Task ID "${taskId}" is a subtask. Autopilot workflows can only be started for main tasks (e.g., "1", "2", "HAM-123"). Please provide the parent task ID instead.` + } + }, + log: context.log, + projectRoot + }); + } + + // Load task data and get current tag + const core = await createTaskMasterCore({ + projectPath: projectRoot + }); + + // Get current tag from ConfigManager + const currentTag = core.getActiveTag(); + + const taskResult = await core.getTaskWithSubtask(taskId); + + if (!taskResult || !taskResult.task) { + await core.close(); + return handleApiResult({ + result: { + success: false, + error: { message: `Task ${taskId} not found` } + }, + log: context.log, + projectRoot + }); + } + + const task = taskResult.task; + + // Validate task has subtasks + if (!task.subtasks || task.subtasks.length === 0) { + await core.close(); + return handleApiResult({ + result: { + success: false, + error: { + message: `Task ${taskId} has no subtasks. Please use expand_task (with id="${taskId}") to create subtasks first. For improved results, consider running analyze_complexity before expanding the task.` + } + }, + log: context.log, + projectRoot + }); + } + + // Initialize workflow service + const workflowService = new WorkflowService(projectRoot); + + // Check for existing workflow + const hasWorkflow = await workflowService.hasWorkflow(); + if (hasWorkflow && !force) { + context.log.warn('Workflow state already exists'); + return handleApiResult({ + result: { + success: false, + error: { + message: + 'Workflow already in progress. Use force=true to override or resume the existing workflow. Suggestion: Use autopilot_resume to continue the existing workflow' + } + }, + log: context.log, + projectRoot + }); + } + + // Start workflow + const status = await workflowService.startWorkflow({ + taskId, + taskTitle: task.title, + subtasks: task.subtasks.map((st: any) => ({ + id: st.id, + title: st.title, + status: st.status, + maxAttempts + })), + maxAttempts, + force, + tag: currentTag // Pass current tag for branch naming + }); + + context.log.info(`Workflow started successfully for task ${taskId}`); + + // Get next action with guidance from WorkflowService + const nextAction = workflowService.getNextAction(); + + return handleApiResult({ + result: { + success: true, + data: { + message: `Workflow started for task ${taskId}`, + taskId, + branchName: status.branchName, + phase: status.phase, + tddPhase: status.tddPhase, + progress: status.progress, + currentSubtask: status.currentSubtask, + nextAction: nextAction.action, + nextSteps: nextAction.nextSteps + } + }, + log: context.log, + projectRoot + }); + } catch (error: any) { + context.log.error(`Error in autopilot-start: ${error.message}`); + if (error.stack) { + context.log.debug(error.stack); + } + return handleApiResult({ + result: { + success: false, + error: { message: `Failed to start workflow: ${error.message}` } + }, + log: context.log, + projectRoot + }); + } + } + ) + }); +} diff --git a/apps/mcp/src/tools/autopilot/status.tool.ts b/apps/mcp/src/tools/autopilot/status.tool.ts new file mode 100644 index 00000000..f12d6e10 --- /dev/null +++ b/apps/mcp/src/tools/autopilot/status.tool.ts @@ -0,0 +1,93 @@ +/** + * @fileoverview autopilot-status MCP tool + * Get comprehensive workflow status and progress information + */ + +import { z } from 'zod'; +import { + handleApiResult, + withNormalizedProjectRoot +} from '../../shared/utils.js'; +import type { MCPContext } from '../../shared/types.js'; +import { WorkflowService } from '@tm/core'; +import type { FastMCP } from 'fastmcp'; + +const StatusSchema = z.object({ + projectRoot: z + .string() + .describe('Absolute path to the project root directory') +}); + +type StatusArgs = z.infer<typeof StatusSchema>; + +/** + * Register the autopilot_status tool with the MCP server + */ +export function registerAutopilotStatusTool(server: FastMCP) { + server.addTool({ + name: 'autopilot_status', + description: + 'Get comprehensive workflow status including current phase, progress, subtask details, and activity history.', + parameters: StatusSchema, + execute: withNormalizedProjectRoot( + async (args: StatusArgs, context: MCPContext) => { + const { projectRoot } = args; + + try { + context.log.info(`Getting workflow status for ${projectRoot}`); + + const workflowService = new WorkflowService(projectRoot); + + // Check if workflow exists + if (!(await workflowService.hasWorkflow())) { + return handleApiResult({ + result: { + success: false, + error: { + message: + 'No active workflow found. Start a workflow with autopilot_start' + } + }, + log: context.log, + projectRoot + }); + } + + // Resume to load state + await workflowService.resumeWorkflow(); + + // Get status + const status = workflowService.getStatus(); + + context.log.info( + `Workflow status retrieved for task ${status.taskId}` + ); + + return handleApiResult({ + result: { + success: true, + data: status + }, + log: context.log, + projectRoot + }); + } catch (error: any) { + context.log.error(`Error in autopilot-status: ${error.message}`); + if (error.stack) { + context.log.debug(error.stack); + } + return handleApiResult({ + result: { + success: false, + error: { + message: `Failed to get workflow status: ${error.message}` + } + }, + log: context.log, + projectRoot + }); + } + } + ) + }); +} diff --git a/apps/mcp/tsconfig.json b/apps/mcp/tsconfig.json new file mode 100644 index 00000000..1bca2f32 --- /dev/null +++ b/apps/mcp/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "lib": ["ES2022"], + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "baseUrl": ".", + "rootDir": "./src", + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "NodeNext", + "moduleDetection": "force", + "types": ["node"], + "resolveJsonModule": true, + "isolatedModules": true, + "allowImportingTsExtensions": false + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"] +} diff --git a/apps/mcp/vitest.config.ts b/apps/mcp/vitest.config.ts new file mode 100644 index 00000000..362f5604 --- /dev/null +++ b/apps/mcp/vitest.config.ts @@ -0,0 +1,23 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + exclude: [ + 'node_modules/', + 'dist/', + 'tests/', + '**/*.test.ts', + '**/*.spec.ts', + '**/*.d.ts', + '**/mocks/**', + '**/fixtures/**', + 'vitest.config.ts' + ] + } + } +}); diff --git a/docs/contributor-docs/worktree-setup.md b/docs/contributor-docs/worktree-setup.md new file mode 100644 index 00000000..33d254fd --- /dev/null +++ b/docs/contributor-docs/worktree-setup.md @@ -0,0 +1,420 @@ +# Git Worktree Setup for Parallel Development + +Simple git worktree setup for running multiple AI coding assistants in parallel. + +## Why Worktrees? + +Instead of Docker complexity, use git worktrees to create isolated working directories: + +✅ **Editor Agnostic** - Works with Cursor, Windsurf, VS Code, Claude Code, etc. +✅ **Simple** - No Docker, no containers, just git +✅ **Fast** - Instant setup, shared git history +✅ **Flexible** - Each worktree can be on a different branch +✅ **Task Master Works** - Full access to `.taskmaster/` in each worktree + +## Quick Start + +### 1. Create a Worktree + +```bash +# Using current branch as base +./scripts/create-worktree.sh + +# Or specify a branch name +./scripts/create-worktree.sh feature/my-feature +``` + +This creates a worktree in `../claude-task-master-worktrees/<branch-name>/` + +### 2. Open in Your Editor + +```bash +# Navigate to the worktree +cd ../claude-task-master-worktrees/auto-main/ # (or whatever branch) + +# Open with your preferred AI editor +cursor . # Cursor +code . # VS Code +windsurf . # Windsurf +claude # Claude Code CLI +``` + +### 3. Work in Parallel + +**Main directory** (where you are now): +```bash +# Keep working normally +git checkout main +cursor . +``` + +**Worktree directory**: +```bash +cd ../claude-task-master-worktrees/auto-main/ +# Different files, different branch, same git repo +claude +``` + +## Usage Examples + +### Example 1: Let Claude Work Autonomously + +```bash +# Create worktree +./scripts/create-worktree.sh auto/taskmaster-work + +# Navigate there +cd ../claude-task-master-worktrees/auto-taskmaster-work/ + +# Start Claude +claude + +# In Claude session +> Use task-master to get the next task and complete it +``` + +**Meanwhile in your main directory:** +```bash +# You keep working normally +cursor . +# No conflicts! +``` + +### Example 2: Multiple AI Assistants in Parallel + +```bash +# Create multiple worktrees +./scripts/create-worktree.sh cursor/feature-a +./scripts/create-worktree.sh claude/feature-b +./scripts/create-worktree.sh windsurf/feature-c + +# Terminal 1 +cd ../claude-task-master-worktrees/cursor-feature-a/ +cursor . + +# Terminal 2 +cd ../claude-task-master-worktrees/claude-feature-b/ +claude + +# Terminal 3 +cd ../claude-task-master-worktrees/windsurf-feature-c/ +windsurf . +``` + +### Example 3: Test vs Implementation + +```bash +# Main directory: Write implementation +cursor . + +# Worktree: Have Claude write tests +cd ../claude-task-master-worktrees/auto-main/ +claude -p "Write tests for the recent changes in the main branch" +``` + +## How It Works + +### Directory Structure + +``` +/Volumes/Workspace/workspace/contrib/task-master/ +├── claude-task-master/ # Main directory (this one) +│ ├── .git/ # Shared git repo +│ ├── .taskmaster/ # Synced via git +│ └── your code... +│ +└── claude-task-master-worktrees/ # Worktrees directory + ├── auto-main/ # Worktree 1 + │ ├── .git -> (points to main .git) + │ ├── .taskmaster/ # Same tasks, synced + │ └── your code... (on branch auto/main) + │ + └── feature-x/ # Worktree 2 + ├── .git -> (points to main .git) + ├── .taskmaster/ + └── your code... (on branch feature/x) +``` + +### Shared Git Repository + +All worktrees share the same `.git`: +- Commits in one worktree are immediately visible in others +- Branches are shared +- Git history is shared +- Only the working files differ + +## Task Master in Worktrees + +Task Master works perfectly in worktrees: + +```bash +# In any worktree +task-master list # Same tasks +task-master next # Same task queue +task-master show 1.2 # Same task data + +# Changes are shared (if committed/pushed) +``` + +### Recommended Workflow + +Use **tags** to separate task contexts: + +```bash +# Main directory - use default tag +task-master list + +# Worktree 1 - use separate tag +cd ../claude-task-master-worktrees/auto-main/ +task-master add-tag --name=claude-auto +task-master use-tag --name=claude-auto +task-master list # Shows claude-auto tasks only +``` + +## Managing Worktrees + +### List All Worktrees + +```bash +./scripts/list-worktrees.sh + +# Or directly with git +git worktree list +``` + +### Remove a Worktree + +```bash +# Remove specific worktree +git worktree remove ../claude-task-master-worktrees/auto-main/ + +# Or if there are uncommitted changes, force it +git worktree remove --force ../claude-task-master-worktrees/auto-main/ +``` + +### Sync Changes Between Worktrees + +Changes are automatically synced through git: + +```bash +# In worktree +git add . +git commit -m "feat: implement feature" +git push + +# In main directory +git pull +# Changes are now available +``` + +## Common Workflows + +### 1. Autonomous Claude with Task Master + +**Setup:** +```bash +./scripts/create-worktree.sh auto/claude-work +cd ../claude-task-master-worktrees/auto-claude-work/ +``` + +**Run:** +```bash +# Copy the autonomous script +cp ../claude-task-master/run-autonomous-tasks.sh . + +# Run Claude autonomously +./run-autonomous-tasks.sh +``` + +**Monitor from main directory:** +```bash +# In another terminal, in main directory +watch -n 5 "task-master list" +``` + +### 2. Code Review Workflow + +**Main directory:** +```bash +# You write code +cursor . +git add . +git commit -m "feat: new feature" +``` + +**Worktree:** +```bash +cd ../claude-task-master-worktrees/auto-main/ +git pull + +# Have Claude review +claude -p "Review the latest commit and suggest improvements" +``` + +### 3. Parallel Feature Development + +**Worktree 1 (Backend):** +```bash +./scripts/create-worktree.sh backend/api +cd ../claude-task-master-worktrees/backend-api/ +cursor . +# Work on API +``` + +**Worktree 2 (Frontend):** +```bash +./scripts/create-worktree.sh frontend/ui +cd ../claude-task-master-worktrees/frontend-ui/ +windsurf . +# Work on UI +``` + +**Main directory:** +```bash +# Monitor and merge +git log --all --graph --oneline +``` + +## Tips + +### 1. Branch Naming Convention + +Use prefixes to organize: +- `auto/*` - For autonomous AI work +- `cursor/*` - For Cursor-specific features +- `claude/*` - For Claude-specific features +- `review/*` - For code review worktrees + +### 2. Commit Often in Worktrees + +Worktrees make it easy to try things: +```bash +# In worktree +git commit -m "experiment: trying approach X" +# If it doesn't work, just delete the worktree +git worktree remove . +``` + +### 3. Use Different npm Dependencies + +Each worktree can have different `node_modules`: +```bash +# Main directory +npm install + +# Worktree (different dependencies) +cd ../claude-task-master-worktrees/auto-main/ +npm install +# Installs independently +``` + +### 4. .env Files + +Each worktree can have its own `.env`: +```bash +# Main directory +echo "API_URL=http://localhost:3000" > .env + +# Worktree +cd ../claude-task-master-worktrees/auto-main/ +echo "API_URL=http://localhost:4000" > .env +# Different config! +``` + +## Cleanup + +### Remove All Worktrees + +```bash +# List and manually remove +./scripts/list-worktrees.sh + +# Remove each one +git worktree remove ../claude-task-master-worktrees/auto-main/ +git worktree remove ../claude-task-master-worktrees/feature-x/ + +# Or remove all at once (careful!) +rm -rf ../claude-task-master-worktrees/ +git worktree prune # Clean up git's worktree metadata +``` + +### Delete Remote Branches + +```bash +# After merging/done with branches +git branch -d auto/claude-work +git push origin --delete auto/claude-work +``` + +## Troubleshooting + +### "Cannot create worktree: already exists" + +```bash +# Remove the existing worktree first +git worktree remove ../claude-task-master-worktrees/auto-main/ +``` + +### "Branch already checked out" + +Git won't let you check out the same branch in multiple worktrees: +```bash +# Use a different branch name +./scripts/create-worktree.sh auto/main-2 +``` + +### Changes Not Syncing + +Worktrees don't auto-sync files. Use git: +```bash +# In worktree with changes +git add . +git commit -m "changes" +git push + +# In other worktree +git pull +``` + +### npm install Fails + +Each worktree needs its own `node_modules`: +```bash +cd ../claude-task-master-worktrees/auto-main/ +npm install +``` + +## Comparison to Docker + +| Feature | Git Worktrees | Docker | +|---------|---------------|--------| +| Setup time | Instant | Minutes (build) | +| Disk usage | Minimal (shared .git) | GBs per container | +| Editor support | Native (any editor) | Limited (need special setup) | +| File sync | Via git | Via volumes (can be slow) | +| Resource usage | None (native) | RAM/CPU overhead | +| Complexity | Simple (just git) | Complex (Dockerfile, compose, etc.) | +| npm install | Per worktree | Per container | +| AI editor support | ✅ All editors work | ⚠️ Need web-based or special config | + +**TL;DR: Worktrees are simpler, faster, and more flexible for this use case.** + +--- + +## Summary + +```bash +# 1. Create worktree +./scripts/create-worktree.sh auto/claude-work + +# 2. Open in AI editor +cd ../claude-task-master-worktrees/auto-claude-work/ +cursor . # or claude, windsurf, code, etc. + +# 3. Work in parallel +# Main directory: You work +# Worktree: AI works +# No conflicts! +``` + +**Simple, fast, editor-agnostic.** 🚀 diff --git a/mcp-server/server.js b/mcp-server/server.js index 025cfc6f..47e4b343 100755 --- a/mcp-server/server.js +++ b/mcp-server/server.js @@ -7,6 +7,9 @@ import logger from './src/logger.js'; // Load environment variables dotenv.config(); +// Set MCP mode to silence tm-core console output +process.env.TASK_MASTER_MCP = 'true'; + /** * Start the MCP server */ diff --git a/mcp-server/src/tools/tool-registry.js b/mcp-server/src/tools/tool-registry.js index bf8456eb..845de603 100644 --- a/mcp-server/src/tools/tool-registry.js +++ b/mcp-server/src/tools/tool-registry.js @@ -40,8 +40,20 @@ import { registerRulesTool } from './rules.js'; import { registerScopeUpTool } from './scope-up.js'; import { registerScopeDownTool } from './scope-down.js'; +// Import TypeScript autopilot tools from apps/mcp +import { + registerAutopilotStartTool, + registerAutopilotResumeTool, + registerAutopilotNextTool, + registerAutopilotStatusTool, + registerAutopilotCompleteTool, + registerAutopilotCommitTool, + registerAutopilotFinalizeTool, + registerAutopilotAbortTool +} from '@tm/mcp'; + /** - * Comprehensive tool registry mapping all 36 tool names to their registration functions + * Comprehensive tool registry mapping all 44 tool names to their registration functions * Used for dynamic tool registration and validation */ export const toolRegistry = { @@ -80,7 +92,15 @@ export const toolRegistry = { use_tag: registerUseTagTool, rename_tag: registerRenameTagTool, copy_tag: registerCopyTagTool, - research: registerResearchTool + research: registerResearchTool, + autopilot_start: registerAutopilotStartTool, + autopilot_resume: registerAutopilotResumeTool, + autopilot_next: registerAutopilotNextTool, + autopilot_status: registerAutopilotStatusTool, + autopilot_complete: registerAutopilotCompleteTool, + autopilot_commit: registerAutopilotCommitTool, + autopilot_finalize: registerAutopilotFinalizeTool, + autopilot_abort: registerAutopilotAbortTool }; /** diff --git a/package-lock.json b/package-lock.json index 00e71c65..4cef418e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "express": "^4.21.2", "fastmcp": "^3.5.0", "figlet": "^1.8.0", + "fs-extra": "^11.3.0", "fuse.js": "^7.1.0", "gpt-tokens": "^1.3.14", "gradient-string": "^3.0.0", @@ -60,7 +61,9 @@ "marked": "^15.0.12", "marked-terminal": "^7.3.0", "ollama-ai-provider-v2": "^1.3.1", + "open": "^10.2.0", "ora": "^8.2.0", + "simple-git": "^3.28.0", "uuid": "^11.1.0", "zod": "^4.1.11" }, @@ -593,19 +596,6 @@ "zod": "^3.23.8" } }, - "apps/extension/node_modules/fs-extra": { - "version": "11.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "apps/extension/node_modules/ollama-ai-provider": { "version": "1.2.0", "dev": true, @@ -757,6 +747,851 @@ "zod": "^3.24.1" } }, + "apps/mcp": { + "name": "@tm/mcp", + "version": "0.28.0-rc.2", + "license": "MIT", + "dependencies": { + "@tm/core": "*", + "fastmcp": "^3.19.2", + "zod": "^4.1.11" + }, + "devDependencies": { + "@biomejs/biome": "^1.9.4", + "@types/node": "^22.10.5", + "typescript": "^5.9.2", + "vitest": "^3.2.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "apps/mcp/node_modules/@modelcontextprotocol/sdk": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.20.0.tgz", + "integrity": "sha512-kOQ4+fHuT4KbR2iq2IjeV32HiihueuOf1vJkq18z08CLZ1UQrTc8BXJpVfxZkq45+inLLD+D4xx4nBjUelJa4Q==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.6", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + } + }, + "apps/mcp/node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "apps/mcp/node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "apps/mcp/node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "apps/mcp/node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "apps/mcp/node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "apps/mcp/node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "apps/mcp/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "apps/mcp/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "apps/mcp/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "apps/mcp/node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "apps/mcp/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "apps/mcp/node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "apps/mcp/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "apps/mcp/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "apps/mcp/node_modules/execa": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "apps/mcp/node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "apps/mcp/node_modules/fastmcp": { + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/fastmcp/-/fastmcp-3.19.2.tgz", + "integrity": "sha512-6cMccOjY9+DFakHU6BUzu9u61neXCPTKnUY+4HAmk/ZLUsaNpMpduUTQZ2Ge+NqZLM7DJxEiiddEjVzleGso6Q==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.17.2", + "@standard-schema/spec": "^1.0.0", + "execa": "^9.6.0", + "file-type": "^21.0.0", + "fuse.js": "^7.1.0", + "mcp-proxy": "^5.8.0", + "strict-event-emitter-types": "^2.0.0", + "undici": "^7.13.0", + "uri-templates": "^0.2.0", + "xsschema": "0.3.5", + "yargs": "^18.0.0", + "zod": "^3.25.76", + "zod-to-json-schema": "^3.24.6" + }, + "bin": { + "fastmcp": "dist/bin/fastmcp.js" + } + }, + "apps/mcp/node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "apps/mcp/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "apps/mcp/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/mcp/node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "apps/mcp/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "apps/mcp/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/mcp/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/mcp/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "apps/mcp/node_modules/mcp-proxy": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/mcp-proxy/-/mcp-proxy-5.9.0.tgz", + "integrity": "sha512-xonJSkuy4wmwXeykBFl0mjRMt4D0pAGCtFIfBFUz4O80VrO92ruJseIdASJO3wN6MdYHi3vbZLOQol3NsUpg4g==", + "license": "MIT", + "bin": { + "mcp-proxy": "dist/bin/mcp-proxy.js" + } + }, + "apps/mcp/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "apps/mcp/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/mcp/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "apps/mcp/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "apps/mcp/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "apps/mcp/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "apps/mcp/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/mcp/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/mcp/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "apps/mcp/node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "apps/mcp/node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "apps/mcp/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/mcp/node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "apps/mcp/node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "apps/mcp/node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "apps/mcp/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "apps/mcp/node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "apps/mcp/node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "apps/mcp/node_modules/vitest/node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "apps/mcp/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "apps/mcp/node_modules/xsschema": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/xsschema/-/xsschema-0.3.5.tgz", + "integrity": "sha512-f+dcy11dTv7aBEEfTbXWnrm/1b/Ds2k4taN0TpN6KCtPAD58kyE/R1znUdrHdBnhIFexj9k+pS1fm6FgtSISrQ==", + "license": "MIT", + "peerDependencies": { + "@valibot/to-json-schema": "^1.0.0", + "arktype": "^2.1.20", + "effect": "^3.16.0", + "sury": "^10.0.0", + "zod": "^3.25.0 || ^4.0.0", + "zod-to-json-schema": "^3.24.5" + }, + "peerDependenciesMeta": { + "@valibot/to-json-schema": { + "optional": true + }, + "arktype": { + "optional": true + }, + "effect": { + "optional": true + }, + "sury": { + "optional": true + }, + "zod": { + "optional": true + }, + "zod-to-json-schema": { + "optional": true + } + } + }, + "apps/mcp/node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "apps/mcp/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "apps/mcp/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "apps/mcp/node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/@ai-sdk/amazon-bedrock": { "version": "3.0.25", "license": "Apache-2.0", @@ -2508,6 +3343,31 @@ "semver": "^7.5.3" } }, + "node_modules/@changesets/apply-release-plan/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/apply-release-plan/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/@changesets/apply-release-plan/node_modules/prettier": { "version": "2.8.8", "dev": true, @@ -2522,6 +3382,16 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/@changesets/apply-release-plan/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@changesets/assemble-release-plan": { "version": "6.0.9", "dev": true, @@ -2599,6 +3469,41 @@ "changeset": "bin.js" } }, + "node_modules/@changesets/cli/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/cli/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@changesets/cli/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@changesets/config": { "version": "3.1.1", "dev": true, @@ -2613,6 +3518,41 @@ "micromatch": "^4.0.8" } }, + "node_modules/@changesets/config/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/config/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@changesets/config/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@changesets/errors": { "version": "0.2.0", "dev": true, @@ -2699,6 +3639,41 @@ "fs-extra": "^7.0.1" } }, + "node_modules/@changesets/pre/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/pre/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@changesets/pre/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@changesets/read": { "version": "0.6.5", "dev": true, @@ -2713,6 +3688,41 @@ "picocolors": "^1.1.0" } }, + "node_modules/@changesets/read/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/read/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@changesets/read/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@changesets/should-skip-package": { "version": "0.1.2", "dev": true, @@ -2738,6 +3748,31 @@ "prettier": "^2.7.1" } }, + "node_modules/@changesets/write/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@changesets/write/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/@changesets/write/node_modules/prettier": { "version": "2.8.8", "dev": true, @@ -2752,6 +3787,16 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/@changesets/write/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "license": "MIT", @@ -4465,19 +5510,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/@mintlify/cli/node_modules/fs-extra": { - "version": "11.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/@mintlify/cli/node_modules/js-yaml": { "version": "4.1.0", "dev": true, @@ -4767,19 +5799,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@mintlify/link-rot/node_modules/fs-extra": { - "version": "11.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/@mintlify/link-rot/node_modules/unist-util-is": { "version": "5.2.1", "dev": true, @@ -4898,19 +5917,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/@mintlify/prebuild/node_modules/fs-extra": { - "version": "11.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/@mintlify/prebuild/node_modules/js-yaml": { "version": "4.1.0", "dev": true, @@ -5010,19 +6016,6 @@ "node": ">=10" } }, - "node_modules/@mintlify/previewing/node_modules/fs-extra": { - "version": "11.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/@mintlify/previewing/node_modules/js-yaml": { "version": "4.1.0", "dev": true, @@ -5160,19 +6153,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/@mintlify/scraping/node_modules/fs-extra": { - "version": "11.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/@mintlify/scraping/node_modules/js-yaml": { "version": "4.1.0", "dev": true, @@ -8396,6 +9376,10 @@ "resolved": "packages/tm-core", "link": true }, + "node_modules/@tm/mcp": { + "resolved": "apps/mcp", + "link": true + }, "node_modules/@tokenizer/inflate": { "version": "0.2.7", "license": "MIT", @@ -8545,6 +9529,17 @@ "@types/estree": "*" } }, + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, "node_modules/@types/glob": { "version": "8.1.0", "license": "MIT", @@ -8622,6 +9617,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/katex": { "version": "0.16.7", "dev": true, @@ -14258,32 +15263,17 @@ "optional": true }, "node_modules/fs-extra": { - "version": "7.0.1", - "dev": true, + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", + "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs-extra/node_modules/jsonfile": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/fs-extra/node_modules/universalify": { - "version": "0.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" + "node": ">=14.14" } }, "node_modules/fs-minipass": { @@ -14746,7 +15736,6 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "dev": true, "license": "ISC" }, "node_modules/gradient-string": { @@ -18238,7 +19227,6 @@ }, "node_modules/jsonfile": { "version": "6.2.0", - "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -23650,6 +24638,8 @@ }, "node_modules/simple-git": { "version": "3.28.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz", + "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==", "license": "MIT", "dependencies": { "@kwsites/file-exists": "^1.1.1", @@ -25708,7 +26698,6 @@ }, "node_modules/universalify": { "version": "2.0.1", - "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -27230,9 +28219,12 @@ "license": "MIT", "dependencies": { "@supabase/supabase-js": "^2.57.4", + "fs-extra": "^11.3.2", + "simple-git": "^3.28.0", "zod": "^4.1.11" }, "devDependencies": { + "@types/fs-extra": "^11.0.4", "@types/node": "^22.10.5", "@vitest/coverage-v8": "^3.2.4", "strip-literal": "3.1.0", diff --git a/package.json b/package.json index 149ee550..76c79359 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "task-master-ai", - "version": "0.29.0", + "version": "0.30.0-rc.0", "description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.", "main": "index.js", "type": "module", @@ -86,6 +86,7 @@ "express": "^4.21.2", "fastmcp": "^3.5.0", "figlet": "^1.8.0", + "fs-extra": "^11.3.0", "fuse.js": "^7.1.0", "gpt-tokens": "^1.3.14", "gradient-string": "^3.0.0", @@ -98,7 +99,9 @@ "marked": "^15.0.12", "marked-terminal": "^7.3.0", "ollama-ai-provider-v2": "^1.3.1", + "open": "^10.2.0", "ora": "^8.2.0", + "simple-git": "^3.28.0", "uuid": "^11.1.0", "zod": "^4.1.11" }, diff --git a/packages/tm-core/package.json b/packages/tm-core/package.json index 64dc815c..7f9c1a26 100644 --- a/packages/tm-core/package.json +++ b/packages/tm-core/package.json @@ -31,11 +31,15 @@ }, "dependencies": { "@supabase/supabase-js": "^2.57.4", + "fs-extra": "^11.3.2", + "simple-git": "^3.28.0", "zod": "^4.1.11" }, "devDependencies": { + "@types/fs-extra": "^11.0.4", "@types/node": "^22.10.5", "@vitest/coverage-v8": "^3.2.4", + "strip-literal": "^3.1.0", "typescript": "^5.9.2", "vitest": "^3.2.4", "strip-literal": "3.1.0" diff --git a/packages/tm-core/src/auth/auth-manager.ts b/packages/tm-core/src/auth/auth-manager.ts index b5842160..f525b36f 100644 --- a/packages/tm-core/src/auth/auth-manager.ts +++ b/packages/tm-core/src/auth/auth-manager.ts @@ -25,10 +25,12 @@ import { getLogger } from '../logger/index.js'; */ export class AuthManager { private static instance: AuthManager | null = null; + private static readonly staticLogger = getLogger('AuthManager'); private credentialStore: CredentialStore; private oauthService: OAuthService; private supabaseClient: SupabaseAuthClient; private organizationService?: OrganizationService; + private readonly logger = getLogger('AuthManager'); private constructor(config?: Partial<AuthConfig>) { this.credentialStore = CredentialStore.getInstance(config); @@ -50,8 +52,7 @@ export class AuthManager { await this.supabaseClient.initialize(); } catch (error) { // Log but don't throw - session might not exist yet - const logger = getLogger('AuthManager'); - logger.debug('No existing session to restore'); + this.logger.debug('No existing session to restore'); } } @@ -63,8 +64,7 @@ export class AuthManager { AuthManager.instance = new AuthManager(config); } else if (config) { // Warn if config is provided after initialization - const logger = getLogger('AuthManager'); - logger.warn( + AuthManager.staticLogger.warn( 'getInstance called with config after initialization; config is ignored.' ); } @@ -159,7 +159,7 @@ export class AuthManager { await this.supabaseClient.signOut(); } catch (error) { // Log but don't throw - we still want to clear local credentials - getLogger('AuthManager').warn('Failed to sign out from Supabase:', error); + this.logger.warn('Failed to sign out from Supabase:', error); } // Always clear local credentials (removes auth.json file) diff --git a/packages/tm-core/src/config/index.ts b/packages/tm-core/src/config/index.ts index 47f5fd9a..2f759695 100644 --- a/packages/tm-core/src/config/index.ts +++ b/packages/tm-core/src/config/index.ts @@ -27,6 +27,7 @@ export type { ProviderConfig, TaskSettings, TagSettings, + WorkflowSettings, StorageSettings, RetrySettings, LoggingSettings, diff --git a/packages/tm-core/src/config/services/config-loader.service.ts b/packages/tm-core/src/config/services/config-loader.service.ts index f7223e57..3988d1c0 100644 --- a/packages/tm-core/src/config/services/config-loader.service.ts +++ b/packages/tm-core/src/config/services/config-loader.service.ts @@ -38,6 +38,35 @@ export class ConfigLoader { main: DEFAULT_CONFIG_VALUES.MODELS.MAIN, fallback: DEFAULT_CONFIG_VALUES.MODELS.FALLBACK }, + workflow: { + enableAutopilot: DEFAULT_CONFIG_VALUES.WORKFLOW.ENABLE_AUTOPILOT, + maxPhaseAttempts: DEFAULT_CONFIG_VALUES.WORKFLOW.MAX_PHASE_ATTEMPTS, + branchPattern: DEFAULT_CONFIG_VALUES.WORKFLOW.BRANCH_PATTERN, + requireCleanWorkingTree: + DEFAULT_CONFIG_VALUES.WORKFLOW.REQUIRE_CLEAN_WORKING_TREE, + autoStageChanges: DEFAULT_CONFIG_VALUES.WORKFLOW.AUTO_STAGE_CHANGES, + includeCoAuthor: DEFAULT_CONFIG_VALUES.WORKFLOW.INCLUDE_CO_AUTHOR, + coAuthorName: DEFAULT_CONFIG_VALUES.WORKFLOW.CO_AUTHOR_NAME, + coAuthorEmail: DEFAULT_CONFIG_VALUES.WORKFLOW.CO_AUTHOR_EMAIL, + testThresholds: { + minTests: DEFAULT_CONFIG_VALUES.WORKFLOW.MIN_TESTS, + maxFailuresInGreen: + DEFAULT_CONFIG_VALUES.WORKFLOW.MAX_FAILURES_IN_GREEN + }, + commitMessageTemplate: + DEFAULT_CONFIG_VALUES.WORKFLOW.COMMIT_MESSAGE_TEMPLATE, + allowedCommitTypes: [ + ...DEFAULT_CONFIG_VALUES.WORKFLOW.ALLOWED_COMMIT_TYPES + ], + defaultCommitType: DEFAULT_CONFIG_VALUES.WORKFLOW.DEFAULT_COMMIT_TYPE, + operationTimeout: DEFAULT_CONFIG_VALUES.WORKFLOW.OPERATION_TIMEOUT, + enableActivityLogging: + DEFAULT_CONFIG_VALUES.WORKFLOW.ENABLE_ACTIVITY_LOGGING, + activityLogPath: DEFAULT_CONFIG_VALUES.WORKFLOW.ACTIVITY_LOG_PATH, + enableStateBackup: DEFAULT_CONFIG_VALUES.WORKFLOW.ENABLE_STATE_BACKUP, + maxStateBackups: DEFAULT_CONFIG_VALUES.WORKFLOW.MAX_STATE_BACKUPS, + abortOnMaxAttempts: DEFAULT_CONFIG_VALUES.WORKFLOW.ABORT_ON_MAX_ATTEMPTS + }, storage: { type: DEFAULT_CONFIG_VALUES.STORAGE.TYPE, encoding: DEFAULT_CONFIG_VALUES.STORAGE.ENCODING, diff --git a/packages/tm-core/src/config/services/config-persistence.service.ts b/packages/tm-core/src/config/services/config-persistence.service.ts index 7a3ae38a..393c4f9e 100644 --- a/packages/tm-core/src/config/services/config-persistence.service.ts +++ b/packages/tm-core/src/config/services/config-persistence.service.ts @@ -10,6 +10,7 @@ import { ERROR_CODES, TaskMasterError } from '../../errors/task-master-error.js'; +import { getLogger } from '../../logger/index.js'; /** * Persistence options @@ -30,6 +31,7 @@ export interface PersistenceOptions { export class ConfigPersistence { private localConfigPath: string; private backupDir: string; + private readonly logger = getLogger('ConfigPersistence'); constructor(projectRoot: string) { this.localConfigPath = path.join(projectRoot, '.taskmaster', 'config.json'); @@ -94,7 +96,7 @@ export class ConfigPersistence { return backupPath; } catch (error) { - console.warn('Failed to create backup:', error); + this.logger.warn('Failed to create backup:', error); throw error; } } @@ -116,7 +118,7 @@ export class ConfigPersistence { await fs.unlink(path.join(this.backupDir, file)); } } catch (error) { - console.warn('Failed to clean old backups:', error); + this.logger.warn('Failed to clean old backups:', error); } } diff --git a/packages/tm-core/src/config/services/environment-config-provider.service.ts b/packages/tm-core/src/config/services/environment-config-provider.service.ts index ec06d79e..067a9210 100644 --- a/packages/tm-core/src/config/services/environment-config-provider.service.ts +++ b/packages/tm-core/src/config/services/environment-config-provider.service.ts @@ -4,6 +4,7 @@ */ import type { PartialConfiguration } from '../../interfaces/configuration.interface.js'; +import { getLogger } from '../../logger/index.js'; /** * Environment variable mapping definition @@ -24,6 +25,8 @@ interface EnvMapping { * Single responsibility: Environment variable configuration extraction */ export class EnvironmentConfigProvider { + private readonly logger = getLogger('EnvironmentConfigProvider'); + /** * Default environment variable mappings */ @@ -75,7 +78,7 @@ export class EnvironmentConfigProvider { // Validate value if validator is provided if (mapping.validate && !mapping.validate(value)) { - console.warn(`Invalid value for ${mapping.env}: ${value}`); + this.logger.warn(`Invalid value for ${mapping.env}: ${value}`); continue; } diff --git a/packages/tm-core/src/config/services/runtime-state-manager.service.ts b/packages/tm-core/src/config/services/runtime-state-manager.service.ts index 6139289d..ef1e14ed 100644 --- a/packages/tm-core/src/config/services/runtime-state-manager.service.ts +++ b/packages/tm-core/src/config/services/runtime-state-manager.service.ts @@ -10,6 +10,7 @@ import { TaskMasterError } from '../../errors/task-master-error.js'; import { DEFAULT_CONFIG_VALUES } from '../../interfaces/configuration.interface.js'; +import { getLogger } from '../../logger/index.js'; /** * Runtime state data structure @@ -30,6 +31,7 @@ export interface RuntimeState { export class RuntimeStateManager { private stateFilePath: string; private currentState: RuntimeState; + private readonly logger = getLogger('RuntimeStateManager'); constructor(projectRoot: string) { this.stateFilePath = path.join(projectRoot, '.taskmaster', 'state.json'); @@ -66,7 +68,7 @@ export class RuntimeStateManager { } catch (error: any) { if (error.code === 'ENOENT') { // State file doesn't exist, use defaults - console.debug('No state.json found, using default state'); + this.logger.debug('No state.json found, using default state'); // Check environment variable if (process.env.TASKMASTER_TAG) { @@ -76,7 +78,8 @@ export class RuntimeStateManager { return this.currentState; } - console.warn('Failed to load state file:', error.message); + // Failed to load, use defaults + this.logger.warn('Failed to load state file:', error.message); return this.currentState; } } diff --git a/packages/tm-core/src/git/branch-name-generator.spec.ts b/packages/tm-core/src/git/branch-name-generator.spec.ts new file mode 100644 index 00000000..81134189 --- /dev/null +++ b/packages/tm-core/src/git/branch-name-generator.spec.ts @@ -0,0 +1,67 @@ +import { describe, it, expect } from 'vitest'; +import { + generateBranchName, + sanitizeBranchName +} from './branch-name-generator.js'; + +describe('Branch Name Generator', () => { + describe('sanitizeBranchName', () => { + it('should remove invalid characters', () => { + const result = sanitizeBranchName('feature/my feature!'); + expect(result).toBe('feature-my-feature'); + }); + + it('should replace spaces with hyphens', () => { + const result = sanitizeBranchName('my feature branch'); + expect(result).toBe('my-feature-branch'); + }); + + it('should convert to lowercase', () => { + const result = sanitizeBranchName('MyFeature'); + expect(result).toBe('myfeature'); + }); + + it('should remove consecutive hyphens', () => { + const result = sanitizeBranchName('my---feature'); + expect(result).toBe('my-feature'); + }); + + it('should handle empty string', () => { + const result = sanitizeBranchName(''); + expect(result).toBe('branch'); + }); + }); + + describe('generateBranchName', () => { + it('should generate branch name from task ID', () => { + const result = generateBranchName({ taskId: '2.7' }); + expect(result).toMatch(/^task-2-7-/); + }); + + it('should include description in branch name', () => { + const result = generateBranchName({ + taskId: '2.7', + description: 'Add Feature' + }); + expect(result).toContain('task-2-7'); + expect(result).toContain('add-feature'); + }); + + it('should handle custom pattern', () => { + const result = generateBranchName({ + taskId: '2.7', + pattern: 'feature/{taskId}' + }); + expect(result).toBe('feature-2-7'); + }); + + it('should truncate long descriptions', () => { + const longDesc = 'a'.repeat(100); + const result = generateBranchName({ + taskId: '2.7', + description: longDesc + }); + expect(result.length).toBeLessThan(80); + }); + }); +}); diff --git a/packages/tm-core/src/git/branch-name-generator.ts b/packages/tm-core/src/git/branch-name-generator.ts new file mode 100644 index 00000000..79a13311 --- /dev/null +++ b/packages/tm-core/src/git/branch-name-generator.ts @@ -0,0 +1,69 @@ +/** + * Branch Name Generator - Generates valid git branch names from patterns + * @module branch-name-generator + */ + +/** + * Sanitizes a string to be a valid git branch name. + * Removes invalid characters, converts to lowercase, replaces spaces with hyphens. + * + * @param {string} name - Name to sanitize + * @returns {string} Sanitized branch name + */ +export function sanitizeBranchName(name: string): string { + if (!name || name.trim() === '') { + return 'branch'; + } + + return name + .toLowerCase() + .replace(/[^a-z0-9-_.\/]/g, '-') // Replace invalid chars with hyphens + .replace(/\//g, '-') // Replace slashes with hyphens + .replace(/-+/g, '-') // Remove consecutive hyphens + .replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens +} + +/** + * Generates a branch name from a pattern and variables. + * + * @param {Object} options - Generation options + * @param {string} options.taskId - Task ID to include + * @param {string} [options.description] - Description to include + * @param {string} [options.pattern] - Custom pattern (default: 'task-{taskId}-{description}') + * @param {number} [options.maxLength=50] - Maximum branch name length + * @returns {string} Generated branch name + */ +export function generateBranchName(options: { + taskId: string; + description?: string; + pattern?: string; + maxLength?: number; +}): string { + const maxLength = options.maxLength || 50; + const pattern = options.pattern || 'task-{taskId}-{description}'; + + // Sanitize task ID (replace dots with hyphens) + const sanitizedTaskId = sanitizeBranchName( + options.taskId.replace(/\./g, '-') + ); + + // Sanitize description if provided + const sanitizedDescription = options.description + ? sanitizeBranchName(options.description) + : sanitizeBranchName(Date.now().toString()); + + // Replace pattern variables + let branchName = pattern + .replace(/{taskId}/g, sanitizedTaskId) + .replace(/{description}/g, sanitizedDescription); + + // Sanitize the final result + branchName = sanitizeBranchName(branchName); + + // Truncate if too long + if (branchName.length > maxLength) { + branchName = branchName.substring(0, maxLength).replace(/-+$/, ''); + } + + return branchName; +} diff --git a/packages/tm-core/src/git/commit-message-generator.test.ts b/packages/tm-core/src/git/commit-message-generator.test.ts new file mode 100644 index 00000000..9de37a5d --- /dev/null +++ b/packages/tm-core/src/git/commit-message-generator.test.ts @@ -0,0 +1,319 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { CommitMessageGenerator } from './commit-message-generator.js'; + +describe('CommitMessageGenerator', () => { + let generator: CommitMessageGenerator; + + beforeEach(() => { + generator = new CommitMessageGenerator(); + }); + + describe('generateMessage', () => { + it('should generate basic conventional commit message', () => { + const message = generator.generateMessage({ + type: 'feat', + description: 'add user authentication', + changedFiles: ['packages/tm-core/src/auth/auth-manager.ts'] + }); + + expect(message).toContain('feat(core): add user authentication'); + }); + + it('should include scope from changed files', () => { + const message = generator.generateMessage({ + type: 'fix', + description: 'resolve CLI argument parsing', + changedFiles: ['packages/cli/src/commands/start.ts'] + }); + + expect(message).toContain('fix(cli): resolve CLI argument parsing'); + }); + + it('should include task metadata in commit body', () => { + const message = generator.generateMessage({ + type: 'feat', + description: 'implement feature', + changedFiles: ['packages/tm-core/src/index.ts'], + taskId: '5.3', + phase: 'GREEN' + }); + + expect(message).toContain('Task: 5.3'); + expect(message).toContain('Phase: GREEN'); + }); + + it('should include test results metadata', () => { + const message = generator.generateMessage({ + type: 'test', + description: 'add unit tests', + changedFiles: ['packages/tm-core/src/auth/auth.test.ts'], + testsPassing: 42, + testsFailing: 0 + }); + + expect(message).toContain('Tests: 42 passing'); + }); + + it('should include failing test count when present', () => { + const message = generator.generateMessage({ + type: 'fix', + description: 'fix test failures', + changedFiles: ['packages/tm-core/src/index.ts'], + testsPassing: 40, + testsFailing: 2 + }); + + expect(message).toContain('Tests: 40 passing, 2 failing'); + }); + + it('should include custom body text', () => { + const message = generator.generateMessage({ + type: 'feat', + description: 'add new feature', + changedFiles: ['packages/tm-core/src/index.ts'], + body: 'This is a detailed explanation\nof the changes made.' + }); + + expect(message).toContain('This is a detailed explanation'); + expect(message).toContain('of the changes made.'); + }); + + it('should handle multiple changed files with different scopes', () => { + const message = generator.generateMessage({ + type: 'refactor', + description: 'reorganize code structure', + changedFiles: [ + 'packages/cli/src/index.ts', + 'packages/tm-core/src/index.ts' + ] + }); + + // Should use CLI scope (higher priority due to count or priority) + expect(message).toMatch(/refactor\((cli|core)\):/); + }); + + it('should handle test files and detect test scope', () => { + const message = generator.generateMessage({ + type: 'test', + description: 'add integration tests', + changedFiles: ['packages/tm-core/src/workflow/workflow.test.ts'] + }); + + expect(message).toContain('test(test):'); + }); + + it('should handle docs changes', () => { + const message = generator.generateMessage({ + type: 'docs', + description: 'update README', + changedFiles: ['README.md', 'docs/guide.md'] + }); + + expect(message).toContain('docs(docs):'); + }); + + it('should omit scope if not detected', () => { + const message = generator.generateMessage({ + type: 'chore', + description: 'update dependencies', + changedFiles: [] + }); + + expect(message).toContain('chore(repo): update dependencies'); + }); + + it('should support manual scope override', () => { + const message = generator.generateMessage({ + type: 'feat', + description: 'add feature', + changedFiles: ['packages/tm-core/src/index.ts'], + scope: 'api' + }); + + expect(message).toContain('feat(api): add feature'); + }); + + it('should handle breaking changes indicator', () => { + const message = generator.generateMessage({ + type: 'feat', + description: 'change API structure', + changedFiles: ['packages/tm-core/src/index.ts'], + breaking: true + }); + + expect(message).toContain('feat(core)!: change API structure'); + }); + + it('should format complete message with all metadata', () => { + const message = generator.generateMessage({ + type: 'feat', + description: 'implement TDD workflow', + changedFiles: ['packages/tm-core/src/workflow/orchestrator.ts'], + body: 'Implemented complete RED-GREEN-COMMIT cycle with state persistence.', + taskId: '4.1', + phase: 'GREEN', + testsPassing: 74, + testsFailing: 0 + }); + + expect(message).toContain('feat(core): implement TDD workflow'); + expect(message).toContain('Implemented complete RED-GREEN-COMMIT cycle'); + expect(message).toContain('Task: 4.1'); + expect(message).toContain('Phase: GREEN'); + expect(message).toContain('Tests: 74 passing'); + }); + }); + + describe('validateConventionalCommit', () => { + it('should validate correct conventional commit format', () => { + const message = 'feat(core): add feature\n\nDetails here.'; + const result = generator.validateConventionalCommit(message); + + expect(result.isValid).toBe(true); + expect(result.errors).toEqual([]); + }); + + it('should detect missing type', () => { + const message = 'add feature'; + const result = generator.validateConventionalCommit(message); + + expect(result.isValid).toBe(false); + expect(result.errors.length).toBeGreaterThan(0); + expect(result.errors[0]).toContain('Invalid conventional commit format'); + }); + + it('should detect invalid type', () => { + const message = 'invalid(core): add feature'; + const result = generator.validateConventionalCommit(message); + + expect(result.isValid).toBe(false); + expect(result.errors.length).toBeGreaterThan(0); + }); + + it('should detect missing description', () => { + const message = 'feat(core):'; + const result = generator.validateConventionalCommit(message); + + expect(result.isValid).toBe(false); + expect(result.errors.length).toBeGreaterThan(0); + expect(result.errors[0]).toContain('Invalid conventional commit format'); + }); + + it('should accept valid types', () => { + const validTypes = [ + 'feat', + 'fix', + 'docs', + 'style', + 'refactor', + 'test', + 'chore' + ]; + + for (const type of validTypes) { + const message = `${type}(core): do something`; + const result = generator.validateConventionalCommit(message); + + expect(result.isValid).toBe(true); + } + }); + + it('should accept breaking change indicator', () => { + const message = 'feat(core)!: breaking change'; + const result = generator.validateConventionalCommit(message); + + expect(result.isValid).toBe(true); + }); + + it('should accept message without scope', () => { + const message = 'fix: resolve issue'; + const result = generator.validateConventionalCommit(message); + + expect(result.isValid).toBe(true); + }); + }); + + describe('parseCommitMessage', () => { + it('should parse conventional commit message', () => { + const message = 'feat(core): add feature\n\nDetailed explanation.'; + const parsed = generator.parseCommitMessage(message); + + expect(parsed.type).toBe('feat'); + expect(parsed.scope).toBe('core'); + expect(parsed.description).toBe('add feature'); + expect(parsed.body).toContain('Detailed explanation.'); + expect(parsed.breaking).toBe(false); + }); + + it('should parse breaking change indicator', () => { + const message = 'feat(core)!: breaking change'; + const parsed = generator.parseCommitMessage(message); + + expect(parsed.type).toBe('feat'); + expect(parsed.breaking).toBe(true); + }); + + it('should parse message without scope', () => { + const message = 'fix: resolve issue'; + const parsed = generator.parseCommitMessage(message); + + expect(parsed.type).toBe('fix'); + expect(parsed.scope).toBeUndefined(); + expect(parsed.description).toBe('resolve issue'); + }); + + it('should handle multiline body', () => { + const message = 'feat: add feature\n\nLine 1\nLine 2\nLine 3'; + const parsed = generator.parseCommitMessage(message); + + expect(parsed.body).toContain('Line 1'); + expect(parsed.body).toContain('Line 2'); + expect(parsed.body).toContain('Line 3'); + }); + }); + + describe('edge cases', () => { + it('should handle empty changed files list', () => { + const message = generator.generateMessage({ + type: 'chore', + description: 'general maintenance', + changedFiles: [] + }); + + expect(message).toContain('chore(repo):'); + }); + + it('should handle very long description', () => { + const longDesc = 'a'.repeat(200); + const message = generator.generateMessage({ + type: 'feat', + description: longDesc, + changedFiles: ['packages/tm-core/src/index.ts'] + }); + + expect(message).toContain(longDesc); + }); + + it('should handle special characters in description', () => { + const message = generator.generateMessage({ + type: 'fix', + description: 'resolve issue with $special @characters #123', + changedFiles: ['packages/tm-core/src/index.ts'] + }); + + expect(message).toContain('$special @characters #123'); + }); + + it('should handle zero passing tests', () => { + const message = generator.generateMessage({ + type: 'test', + description: 'add failing test', + changedFiles: ['test.ts'], + testsPassing: 0, + testsFailing: 1 + }); + + expect(message).toContain('Tests: 0 passing, 1 failing'); + }); + }); +}); diff --git a/packages/tm-core/src/git/commit-message-generator.ts b/packages/tm-core/src/git/commit-message-generator.ts new file mode 100644 index 00000000..a5de41e9 --- /dev/null +++ b/packages/tm-core/src/git/commit-message-generator.ts @@ -0,0 +1,205 @@ +/** + * CommitMessageGenerator - Generate conventional commit messages with metadata + * + * Combines TemplateEngine and ScopeDetector to create structured commit messages + * that follow conventional commits specification and include task metadata. + */ + +import { TemplateEngine } from './template-engine.js'; +import { ScopeDetector } from './scope-detector.js'; + +export interface CommitMessageOptions { + type: string; + description: string; + changedFiles: string[]; + scope?: string; + body?: string; + breaking?: boolean; + taskId?: string; + phase?: string; + tag?: string; + testsPassing?: number; + testsFailing?: number; + coveragePercent?: number; +} + +export interface ValidationResult { + isValid: boolean; + errors: string[]; +} + +export interface ParsedCommitMessage { + type: string; + scope?: string; + breaking: boolean; + description: string; + body?: string; +} + +const CONVENTIONAL_COMMIT_TYPES = [ + 'feat', + 'fix', + 'docs', + 'style', + 'refactor', + 'perf', + 'test', + 'build', + 'ci', + 'chore', + 'revert' +]; + +export class CommitMessageGenerator { + private templateEngine: TemplateEngine; + private scopeDetector: ScopeDetector; + + constructor( + customTemplates?: Record<string, string>, + customScopeMappings?: Record<string, string>, + customScopePriorities?: Record<string, number> + ) { + this.templateEngine = new TemplateEngine(customTemplates); + this.scopeDetector = new ScopeDetector( + customScopeMappings, + customScopePriorities + ); + } + + /** + * Generate a conventional commit message with metadata + */ + generateMessage(options: CommitMessageOptions): string { + const { + type, + description, + changedFiles, + scope: manualScope, + body, + breaking = false, + taskId, + phase, + tag, + testsPassing, + testsFailing, + coveragePercent + } = options; + + // Determine scope (manual override or auto-detect) + const scope = manualScope ?? this.scopeDetector.detectScope(changedFiles); + + // Build template variables + const variables = { + type, + scope, + breaking: breaking ? '!' : '', + description, + body, + taskId, + phase, + tag, + testsPassing, + testsFailing, + coveragePercent + }; + + // Generate message from template + return this.templateEngine.render('commitMessage', variables); + } + + /** + * Validate that a commit message follows conventional commits format + */ + validateConventionalCommit(message: string): ValidationResult { + const errors: string[] = []; + + // Parse first line (header) + const lines = message.split('\n'); + const header = lines[0]; + + if (!header) { + errors.push('Missing commit message'); + return { isValid: false, errors }; + } + + // Check format: type(scope)?: description + const headerRegex = /^(\w+)(?:\(([^)]+)\))?(!)?:\s*(.+)$/; + const match = header.match(headerRegex); + + if (!match) { + errors.push( + 'Invalid conventional commit format. Expected: type(scope): description' + ); + return { isValid: false, errors }; + } + + const [, type, , , description] = match; + + // Validate type + if (!CONVENTIONAL_COMMIT_TYPES.includes(type)) { + errors.push( + `Invalid commit type "${type}". Must be one of: ${CONVENTIONAL_COMMIT_TYPES.join(', ')}` + ); + } + + // Validate description + if (!description || description.trim().length === 0) { + errors.push('Missing description'); + } + + return { + isValid: errors.length === 0, + errors + }; + } + + /** + * Parse a conventional commit message into its components + */ + parseCommitMessage(message: string): ParsedCommitMessage { + const lines = message.split('\n'); + const header = lines[0]; + + // Parse header: type(scope)!: description + const headerRegex = /^(\w+)(?:\(([^)]+)\))?(!)?:\s*(.+)$/; + const match = header.match(headerRegex); + + if (!match) { + throw new Error('Invalid conventional commit format'); + } + + const [, type, scope, breaking, description] = match; + + // Body is everything after the first blank line + const bodyStartIndex = lines.findIndex((line, i) => i > 0 && line === ''); + const body = + bodyStartIndex !== -1 + ? lines + .slice(bodyStartIndex + 1) + .join('\n') + .trim() + : undefined; + + return { + type, + scope, + breaking: breaking === '!', + description, + body + }; + } + + /** + * Get the scope detector instance (for testing/customization) + */ + getScopeDetector(): ScopeDetector { + return this.scopeDetector; + } + + /** + * Get the template engine instance (for testing/customization) + */ + getTemplateEngine(): TemplateEngine { + return this.templateEngine; + } +} diff --git a/packages/tm-core/src/git/git-adapter.test.ts b/packages/tm-core/src/git/git-adapter.test.ts new file mode 100644 index 00000000..baab5e26 --- /dev/null +++ b/packages/tm-core/src/git/git-adapter.test.ts @@ -0,0 +1,1211 @@ +import { + describe, + it, + expect, + beforeEach, + afterEach, + jest +} from '@jest/globals'; +import fs from 'fs-extra'; +import path from 'path'; +import os from 'os'; +import { GitAdapter } from '../../../../../packages/tm-core/src/git/git-adapter.js'; + +describe('GitAdapter - Repository Detection and Validation', () => { + let testDir; + let gitAdapter; + + beforeEach(async () => { + // Create temporary test directory + testDir = path.join(os.tmpdir(), `git-test-${Date.now()}`); + await fs.ensureDir(testDir); + }); + + afterEach(async () => { + // Clean up test directory + await fs.remove(testDir); + }); + + describe('isGitRepository', () => { + it('should return false for non-git directory', async () => { + gitAdapter = new GitAdapter(testDir); + + const isRepo = await gitAdapter.isGitRepository(); + + expect(isRepo).toBe(false); + }); + + it('should return true for git repository', async () => { + // Initialize real git repo + await fs.ensureDir(path.join(testDir, '.git')); + await fs.ensureDir(path.join(testDir, '.git', 'objects')); + await fs.ensureDir(path.join(testDir, '.git', 'refs')); + await fs.writeFile( + path.join(testDir, '.git', 'HEAD'), + 'ref: refs/heads/main\n' + ); + + gitAdapter = new GitAdapter(testDir); + + const isRepo = await gitAdapter.isGitRepository(); + + expect(isRepo).toBe(true); + }); + + it('should detect git repository in subdirectory', async () => { + // Initialize real git repo in parent + await fs.ensureDir(path.join(testDir, '.git')); + await fs.ensureDir(path.join(testDir, '.git', 'objects')); + await fs.ensureDir(path.join(testDir, '.git', 'refs')); + await fs.writeFile( + path.join(testDir, '.git', 'HEAD'), + 'ref: refs/heads/main\n' + ); + + // Create subdirectory + const subDir = path.join(testDir, 'src', 'components'); + await fs.ensureDir(subDir); + + gitAdapter = new GitAdapter(subDir); + + const isRepo = await gitAdapter.isGitRepository(); + + expect(isRepo).toBe(true); + }); + + it('should handle directory with .git file (submodule)', async () => { + // Create .git file (used in submodules/worktrees) + await fs.writeFile(path.join(testDir, '.git'), 'gitdir: /path/to/git'); + + gitAdapter = new GitAdapter(testDir); + + const isRepo = await gitAdapter.isGitRepository(); + + expect(isRepo).toBe(true); + }); + + it('should return false if .git is neither file nor directory', async () => { + gitAdapter = new GitAdapter(testDir); + + const isRepo = await gitAdapter.isGitRepository(); + + expect(isRepo).toBe(false); + }); + }); + + describe('validateGitInstallation', () => { + it('should validate git is installed', async () => { + gitAdapter = new GitAdapter(testDir); + + await expect(gitAdapter.validateGitInstallation()).resolves.not.toThrow(); + }); + + it('should throw error if git version check fails', async () => { + gitAdapter = new GitAdapter(testDir); + + // Mock simple-git to throw error + const mockGit = { + version: jest.fn().mockRejectedValue(new Error('git not found')) + }; + gitAdapter.git = mockGit; + + await expect(gitAdapter.validateGitInstallation()).rejects.toThrow( + 'git not found' + ); + }); + + it('should return git version info', async () => { + gitAdapter = new GitAdapter(testDir); + + const versionInfo = await gitAdapter.getGitVersion(); + + expect(versionInfo).toBeDefined(); + expect(versionInfo.major).toBeGreaterThan(0); + }); + }); + + describe('getRepositoryRoot', () => { + it('should return repository root path', async () => { + // Initialize real git repo + await fs.ensureDir(path.join(testDir, '.git')); + await fs.ensureDir(path.join(testDir, '.git', 'objects')); + await fs.ensureDir(path.join(testDir, '.git', 'refs')); + await fs.writeFile( + path.join(testDir, '.git', 'HEAD'), + 'ref: refs/heads/main\n' + ); + + gitAdapter = new GitAdapter(testDir); + + const root = await gitAdapter.getRepositoryRoot(); + + // Resolve both paths to handle symlinks (e.g., /var vs /private/var on macOS) + expect(await fs.realpath(root)).toBe(await fs.realpath(testDir)); + }); + + it('should find repository root from subdirectory', async () => { + // Initialize real git repo in parent + await fs.ensureDir(path.join(testDir, '.git')); + await fs.ensureDir(path.join(testDir, '.git', 'objects')); + await fs.ensureDir(path.join(testDir, '.git', 'refs')); + await fs.writeFile( + path.join(testDir, '.git', 'HEAD'), + 'ref: refs/heads/main\n' + ); + + // Create subdirectory + const subDir = path.join(testDir, 'src', 'components'); + await fs.ensureDir(subDir); + + gitAdapter = new GitAdapter(subDir); + + const root = await gitAdapter.getRepositoryRoot(); + + // Resolve both paths to handle symlinks (e.g., /var vs /private/var on macOS) + expect(await fs.realpath(root)).toBe(await fs.realpath(testDir)); + }); + + it('should throw error if not in git repository', async () => { + gitAdapter = new GitAdapter(testDir); + + await expect(gitAdapter.getRepositoryRoot()).rejects.toThrow( + 'not a git repository' + ); + }); + }); + + describe('validateRepository', () => { + it('should validate repository is in good state', async () => { + // Initialize git repo + await fs.ensureDir(path.join(testDir, '.git')); + await fs.ensureDir(path.join(testDir, '.git', 'refs')); + await fs.ensureDir(path.join(testDir, '.git', 'objects')); + await fs.writeFile( + path.join(testDir, '.git', 'HEAD'), + 'ref: refs/heads/main\n' + ); + + gitAdapter = new GitAdapter(testDir); + + await expect(gitAdapter.validateRepository()).resolves.not.toThrow(); + }); + + it('should throw error for non-git directory', async () => { + gitAdapter = new GitAdapter(testDir); + + await expect(gitAdapter.validateRepository()).rejects.toThrow( + 'not a git repository' + ); + }); + + it('should detect corrupted repository', async () => { + // Create .git directory but make it empty (corrupted) + await fs.ensureDir(path.join(testDir, '.git')); + + gitAdapter = new GitAdapter(testDir); + + // This should either succeed or throw a specific error + // depending on simple-git's behavior + try { + await gitAdapter.validateRepository(); + } catch (error) { + expect(error.message).toMatch(/repository|git/i); + } + }); + }); + + describe('ensureGitRepository', () => { + it('should not throw if in valid git repository', async () => { + // Initialize git repo + await fs.ensureDir(path.join(testDir, '.git')); + + gitAdapter = new GitAdapter(testDir); + + await expect(gitAdapter.ensureGitRepository()).resolves.not.toThrow(); + }); + + it('should throw error if not in git repository', async () => { + gitAdapter = new GitAdapter(testDir); + + await expect(gitAdapter.ensureGitRepository()).rejects.toThrow( + 'not a git repository' + ); + }); + + it('should provide helpful error message', async () => { + gitAdapter = new GitAdapter(testDir); + + try { + await gitAdapter.ensureGitRepository(); + fail('Should have thrown error'); + } catch (error) { + expect(error.message).toContain('not a git repository'); + expect(error.message).toContain(testDir); + } + }); + }); + + describe('constructor', () => { + it('should create GitAdapter with project path', () => { + gitAdapter = new GitAdapter(testDir); + + expect(gitAdapter).toBeDefined(); + expect(gitAdapter.projectPath).toBe(testDir); + }); + + it('should normalize project path', () => { + const unnormalizedPath = path.join(testDir, '..', path.basename(testDir)); + gitAdapter = new GitAdapter(unnormalizedPath); + + expect(gitAdapter.projectPath).toBe(testDir); + }); + + it('should initialize simple-git instance', () => { + gitAdapter = new GitAdapter(testDir); + + expect(gitAdapter.git).toBeDefined(); + }); + + it('should throw error for invalid path', () => { + expect(() => new GitAdapter('')).toThrow('Project path is required'); + }); + + it('should throw error for non-absolute path', () => { + expect(() => new GitAdapter('./relative/path')).toThrow('absolute'); + }); + }); + + describe('error handling', () => { + it('should provide clear error for permission denied', async () => { + // Create .git but make it inaccessible + await fs.ensureDir(path.join(testDir, '.git')); + + gitAdapter = new GitAdapter(testDir); + + try { + await fs.chmod(path.join(testDir, '.git'), 0o000); + + await gitAdapter.isGitRepository(); + } catch (error) { + // Error handling + } finally { + // Restore permissions + await fs.chmod(path.join(testDir, '.git'), 0o755); + } + }); + + it('should handle symbolic links correctly', async () => { + // Create actual git repo + const realRepo = path.join(testDir, 'real-repo'); + await fs.ensureDir(path.join(realRepo, '.git')); + + // Create symlink + const symlinkPath = path.join(testDir, 'symlink-repo'); + try { + await fs.symlink(realRepo, symlinkPath); + + gitAdapter = new GitAdapter(symlinkPath); + + const isRepo = await gitAdapter.isGitRepository(); + + expect(isRepo).toBe(true); + } catch (error) { + // Skip test on platforms without symlink support + if (error.code !== 'EPERM') { + throw error; + } + } + }); + }); + + describe('integration with simple-git', () => { + it('should use simple-git for git operations', () => { + gitAdapter = new GitAdapter(testDir); + + // Check that git instance is from simple-git + expect(typeof gitAdapter.git.status).toBe('function'); + expect(typeof gitAdapter.git.branch).toBe('function'); + }); + + it('should pass correct working directory to simple-git', () => { + gitAdapter = new GitAdapter(testDir); + + // simple-git should be initialized with testDir + expect(gitAdapter.git._executor).toBeDefined(); + }); + }); +}); + +describe('GitAdapter - Working Tree Status', () => { + let testDir; + let gitAdapter; + let simpleGit; + + beforeEach(async () => { + testDir = path.join(os.tmpdir(), `git-status-test-${Date.now()}`); + await fs.ensureDir(testDir); + + // Initialize actual git repo + simpleGit = (await import('simple-git')).default; + const git = simpleGit(testDir); + await git.init(); + await git.addConfig('user.name', 'Test User'); + await git.addConfig('user.email', 'test@example.com'); + + gitAdapter = new GitAdapter(testDir); + }); + + afterEach(async () => { + await fs.remove(testDir); + }); + + describe('isWorkingTreeClean', () => { + it('should return true for clean working tree', async () => { + const isClean = await gitAdapter.isWorkingTreeClean(); + expect(isClean).toBe(true); + }); + + it('should return false when files are modified', async () => { + // Create and commit a file + await fs.writeFile(path.join(testDir, 'test.txt'), 'initial'); + const git = simpleGit(testDir); + await git.add('test.txt'); + await git.commit('initial commit', undefined, { '--no-gpg-sign': null }); + + // Modify the file + await fs.writeFile(path.join(testDir, 'test.txt'), 'modified'); + + const isClean = await gitAdapter.isWorkingTreeClean(); + expect(isClean).toBe(false); + }); + + it('should return false when untracked files exist', async () => { + await fs.writeFile(path.join(testDir, 'untracked.txt'), 'content'); + + const isClean = await gitAdapter.isWorkingTreeClean(); + expect(isClean).toBe(false); + }); + + it('should return false when files are staged', async () => { + await fs.writeFile(path.join(testDir, 'staged.txt'), 'content'); + const git = simpleGit(testDir); + await git.add('staged.txt'); + + const isClean = await gitAdapter.isWorkingTreeClean(); + expect(isClean).toBe(false); + }); + }); + + describe('getStatus', () => { + it('should return status for clean repo', async () => { + const status = await gitAdapter.getStatus(); + + expect(status).toBeDefined(); + expect(status.modified).toEqual([]); + expect(status.not_added).toEqual([]); + expect(status.deleted).toEqual([]); + expect(status.created).toEqual([]); + }); + + it('should detect modified files', async () => { + // Create and commit + await fs.writeFile(path.join(testDir, 'test.txt'), 'initial'); + const git = simpleGit(testDir); + await git.add('test.txt'); + await git.commit('initial', undefined, { '--no-gpg-sign': null }); + + // Modify + await fs.writeFile(path.join(testDir, 'test.txt'), 'modified'); + + const status = await gitAdapter.getStatus(); + expect(status.modified).toContain('test.txt'); + }); + + it('should detect untracked files', async () => { + await fs.writeFile(path.join(testDir, 'untracked.txt'), 'content'); + + const status = await gitAdapter.getStatus(); + expect(status.not_added).toContain('untracked.txt'); + }); + + it('should detect staged files', async () => { + await fs.writeFile(path.join(testDir, 'staged.txt'), 'content'); + const git = simpleGit(testDir); + await git.add('staged.txt'); + + const status = await gitAdapter.getStatus(); + expect(status.created).toContain('staged.txt'); + }); + + it('should detect deleted files', async () => { + // Create and commit + await fs.writeFile(path.join(testDir, 'deleted.txt'), 'content'); + const git = simpleGit(testDir); + await git.add('deleted.txt'); + await git.commit('add file', undefined, { '--no-gpg-sign': null }); + + // Delete + await fs.remove(path.join(testDir, 'deleted.txt')); + + const status = await gitAdapter.getStatus(); + expect(status.deleted).toContain('deleted.txt'); + }); + }); + + describe('hasUncommittedChanges', () => { + it('should return false for clean repo', async () => { + const hasChanges = await gitAdapter.hasUncommittedChanges(); + expect(hasChanges).toBe(false); + }); + + it('should return true for modified files', async () => { + await fs.writeFile(path.join(testDir, 'test.txt'), 'initial'); + const git = simpleGit(testDir); + await git.add('test.txt'); + await git.commit('initial', undefined, { '--no-gpg-sign': null }); + + await fs.writeFile(path.join(testDir, 'test.txt'), 'modified'); + + const hasChanges = await gitAdapter.hasUncommittedChanges(); + expect(hasChanges).toBe(true); + }); + + it('should return true for staged changes', async () => { + await fs.writeFile(path.join(testDir, 'staged.txt'), 'content'); + const git = simpleGit(testDir); + await git.add('staged.txt'); + + const hasChanges = await gitAdapter.hasUncommittedChanges(); + expect(hasChanges).toBe(true); + }); + }); + + describe('hasStagedChanges', () => { + it('should return false when no staged changes', async () => { + const hasStaged = await gitAdapter.hasStagedChanges(); + expect(hasStaged).toBe(false); + }); + + it('should return true when files are staged', async () => { + await fs.writeFile(path.join(testDir, 'staged.txt'), 'content'); + const git = simpleGit(testDir); + await git.add('staged.txt'); + + const hasStaged = await gitAdapter.hasStagedChanges(); + expect(hasStaged).toBe(true); + }); + + it('should return false for unstaged changes only', async () => { + await fs.writeFile(path.join(testDir, 'test.txt'), 'initial'); + const git = simpleGit(testDir); + await git.add('test.txt'); + await git.commit('initial', undefined, { '--no-gpg-sign': null }); + + await fs.writeFile(path.join(testDir, 'test.txt'), 'modified'); + + const hasStaged = await gitAdapter.hasStagedChanges(); + expect(hasStaged).toBe(false); + }); + }); + + describe('hasUntrackedFiles', () => { + it('should return false when no untracked files', async () => { + const hasUntracked = await gitAdapter.hasUntrackedFiles(); + expect(hasUntracked).toBe(false); + }); + + it('should return true when untracked files exist', async () => { + await fs.writeFile(path.join(testDir, 'untracked.txt'), 'content'); + + const hasUntracked = await gitAdapter.hasUntrackedFiles(); + expect(hasUntracked).toBe(true); + }); + + it('should not count staged files as untracked', async () => { + await fs.writeFile(path.join(testDir, 'staged.txt'), 'content'); + const git = simpleGit(testDir); + await git.add('staged.txt'); + + const hasUntracked = await gitAdapter.hasUntrackedFiles(); + expect(hasUntracked).toBe(false); + }); + }); + + describe('getStatusSummary', () => { + it('should provide summary for clean repo', async () => { + const summary = await gitAdapter.getStatusSummary(); + + expect(summary).toBeDefined(); + expect(summary.isClean).toBe(true); + expect(summary.totalChanges).toBe(0); + }); + + it('should count all types of changes', async () => { + // Create committed file + await fs.writeFile(path.join(testDir, 'committed.txt'), 'content'); + const git = simpleGit(testDir); + await git.add('committed.txt'); + await git.commit('initial', undefined, { '--no-gpg-sign': null }); + + // Modify it + await fs.writeFile(path.join(testDir, 'committed.txt'), 'modified'); + + // Add untracked + await fs.writeFile(path.join(testDir, 'untracked.txt'), 'content'); + + // Add staged + await fs.writeFile(path.join(testDir, 'staged.txt'), 'content'); + await git.add('staged.txt'); + + const summary = await gitAdapter.getStatusSummary(); + + expect(summary.isClean).toBe(false); + expect(summary.totalChanges).toBeGreaterThan(0); + expect(summary.modified).toBeGreaterThan(0); + expect(summary.untracked).toBeGreaterThan(0); + expect(summary.staged).toBeGreaterThan(0); + }); + }); + + describe('ensureCleanWorkingTree', () => { + it('should not throw for clean repo', async () => { + await expect(gitAdapter.ensureCleanWorkingTree()).resolves.not.toThrow(); + }); + + it('should throw for dirty repo', async () => { + await fs.writeFile(path.join(testDir, 'dirty.txt'), 'content'); + + await expect(gitAdapter.ensureCleanWorkingTree()).rejects.toThrow( + 'working tree is not clean' + ); + }); + + it('should provide details about changes in error', async () => { + await fs.writeFile(path.join(testDir, 'modified.txt'), 'content'); + + try { + await gitAdapter.ensureCleanWorkingTree(); + fail('Should have thrown'); + } catch (error) { + expect(error.message).toContain('working tree is not clean'); + } + }); + }); + + describe('GitAdapter - Branch Operations', () => { + let testDir; + let gitAdapter; + let simpleGit; + + beforeEach(async () => { + testDir = path.join(os.tmpdir(), `git-branch-test-${Date.now()}`); + await fs.ensureDir(testDir); + + // Initialize actual git repo with initial commit + simpleGit = (await import('simple-git')).default; + const git = simpleGit(testDir); + await git.init(); + await git.addConfig('user.name', 'Test User'); + await git.addConfig('user.email', 'test@example.com'); + + // Create initial commit + await fs.writeFile(path.join(testDir, 'README.md'), '# Test Repo'); + await git.add('README.md'); + await git.commit('Initial commit', undefined, { '--no-gpg-sign': null }); + + // Rename master to main for consistency + try { + await git.branch(['-m', 'master', 'main']); + } catch (error) { + // Branch might already be main, ignore error + } + + gitAdapter = new GitAdapter(testDir); + }); + + afterEach(async () => { + if (await fs.pathExists(testDir)) { + await fs.remove(testDir); + } + }); + + describe('getCurrentBranch', () => { + it('should return current branch name', async () => { + const branch = await gitAdapter.getCurrentBranch(); + expect(branch).toBe('main'); + }); + + it('should return updated branch after checkout', async () => { + const git = simpleGit(testDir); + await git.checkoutLocalBranch('feature'); + + const branch = await gitAdapter.getCurrentBranch(); + expect(branch).toBe('feature'); + }); + }); + + describe('listBranches', () => { + it('should list all branches', async () => { + const git = simpleGit(testDir); + await git.checkoutLocalBranch('feature-a'); + await git.checkout('main'); + await git.checkoutLocalBranch('feature-b'); + + const branches = await gitAdapter.listBranches(); + expect(branches).toContain('main'); + expect(branches).toContain('feature-a'); + expect(branches).toContain('feature-b'); + expect(branches.length).toBeGreaterThanOrEqual(3); + }); + + it('should return empty array if only on detached HEAD', async () => { + const git = simpleGit(testDir); + const log = await git.log(); + await git.checkout(log.latest.hash); + + const branches = await gitAdapter.listBranches(); + expect(Array.isArray(branches)).toBe(true); + }); + }); + + describe('branchExists', () => { + it('should return true for existing branch', async () => { + const exists = await gitAdapter.branchExists('main'); + expect(exists).toBe(true); + }); + + it('should return false for non-existing branch', async () => { + const exists = await gitAdapter.branchExists('nonexistent'); + expect(exists).toBe(false); + }); + + it('should detect newly created branches', async () => { + const git = simpleGit(testDir); + await git.checkoutLocalBranch('new-feature'); + + const exists = await gitAdapter.branchExists('new-feature'); + expect(exists).toBe(true); + }); + }); + + describe('createBranch', () => { + it('should create a new branch', async () => { + await gitAdapter.createBranch('new-branch'); + + const exists = await gitAdapter.branchExists('new-branch'); + expect(exists).toBe(true); + }); + + it('should throw error if branch already exists', async () => { + await gitAdapter.createBranch('existing-branch'); + + await expect( + gitAdapter.createBranch('existing-branch') + ).rejects.toThrow(); + }); + + it('should not switch to new branch by default', async () => { + await gitAdapter.createBranch('new-branch'); + + const current = await gitAdapter.getCurrentBranch(); + expect(current).toBe('main'); + }); + + it('should throw if working tree is dirty when checkout is requested', async () => { + await fs.writeFile(path.join(testDir, 'dirty.txt'), 'content'); + + await expect( + gitAdapter.createBranch('new-branch', { checkout: true }) + ).rejects.toThrow('working tree is not clean'); + }); + }); + + describe('checkoutBranch', () => { + it('should checkout existing branch', async () => { + const git = simpleGit(testDir); + await git.checkoutLocalBranch('feature'); + await git.checkout('main'); + + await gitAdapter.checkoutBranch('feature'); + + const current = await gitAdapter.getCurrentBranch(); + expect(current).toBe('feature'); + }); + + it('should throw error for non-existing branch', async () => { + await expect( + gitAdapter.checkoutBranch('nonexistent') + ).rejects.toThrow(); + }); + + it('should throw if working tree is dirty', async () => { + const git = simpleGit(testDir); + await git.checkoutLocalBranch('feature'); + await git.checkout('main'); + + await fs.writeFile(path.join(testDir, 'dirty.txt'), 'content'); + + await expect(gitAdapter.checkoutBranch('feature')).rejects.toThrow( + 'working tree is not clean' + ); + }); + + it('should allow force checkout with force flag', async () => { + const git = simpleGit(testDir); + await git.checkoutLocalBranch('feature'); + await git.checkout('main'); + + await fs.writeFile(path.join(testDir, 'dirty.txt'), 'content'); + + await gitAdapter.checkoutBranch('feature', { force: true }); + + const current = await gitAdapter.getCurrentBranch(); + expect(current).toBe('feature'); + }); + }); + + describe('createAndCheckoutBranch', () => { + it('should create and checkout new branch', async () => { + await gitAdapter.createAndCheckoutBranch('new-feature'); + + const current = await gitAdapter.getCurrentBranch(); + expect(current).toBe('new-feature'); + + const exists = await gitAdapter.branchExists('new-feature'); + expect(exists).toBe(true); + }); + + it('should throw if branch already exists', async () => { + const git = simpleGit(testDir); + await git.checkoutLocalBranch('existing'); + await git.checkout('main'); + + await expect( + gitAdapter.createAndCheckoutBranch('existing') + ).rejects.toThrow(); + }); + + it('should throw if working tree is dirty', async () => { + await fs.writeFile(path.join(testDir, 'dirty.txt'), 'content'); + + await expect( + gitAdapter.createAndCheckoutBranch('new-feature') + ).rejects.toThrow('working tree is not clean'); + }); + }); + + describe('deleteBranch', () => { + it('should delete existing branch', async () => { + const git = simpleGit(testDir); + await git.checkoutLocalBranch('to-delete'); + await git.checkout('main'); + + await gitAdapter.deleteBranch('to-delete'); + + const exists = await gitAdapter.branchExists('to-delete'); + expect(exists).toBe(false); + }); + + it('should throw error when deleting current branch', async () => { + await expect(gitAdapter.deleteBranch('main')).rejects.toThrow(); + }); + + it('should throw error for non-existing branch', async () => { + await expect(gitAdapter.deleteBranch('nonexistent')).rejects.toThrow(); + }); + + it('should force delete with force flag', async () => { + const git = simpleGit(testDir); + await git.checkoutLocalBranch('unmerged'); + await fs.writeFile(path.join(testDir, 'unmerged.txt'), 'content'); + await git.add('unmerged.txt'); + await git.commit('Unmerged commit', undefined, { + '--no-gpg-sign': null + }); + await git.checkout('main'); + + await gitAdapter.deleteBranch('unmerged', { force: true }); + + const exists = await gitAdapter.branchExists('unmerged'); + expect(exists).toBe(false); + }); + }); + }); + + describe('GitAdapter - Commit Operations', () => { + let testDir; + let gitAdapter; + let simpleGit; + + beforeEach(async () => { + testDir = path.join(os.tmpdir(), `git-commit-test-${Date.now()}`); + await fs.ensureDir(testDir); + + // Initialize actual git repo with initial commit + simpleGit = (await import('simple-git')).default; + const git = simpleGit(testDir); + await git.init(); + await git.addConfig('user.name', 'Test User'); + await git.addConfig('user.email', 'test@example.com'); + + // Create initial commit + await fs.writeFile(path.join(testDir, 'README.md'), '# Test Repo'); + await git.add('README.md'); + await git.commit('Initial commit', undefined, { '--no-gpg-sign': null }); + + // Rename master to main for consistency + try { + await git.branch(['-m', 'master', 'main']); + } catch (error) { + // Branch might already be main, ignore error + } + + gitAdapter = new GitAdapter(testDir); + }); + + afterEach(async () => { + if (await fs.pathExists(testDir)) { + await fs.remove(testDir); + } + }); + + describe('stageFiles', () => { + it('should stage single file', async () => { + await fs.writeFile(path.join(testDir, 'new.txt'), 'content'); + await gitAdapter.stageFiles(['new.txt']); + + const status = await gitAdapter.getStatus(); + expect(status.staged).toContain('new.txt'); + }); + + it('should stage multiple files', async () => { + await fs.writeFile(path.join(testDir, 'file1.txt'), 'content1'); + await fs.writeFile(path.join(testDir, 'file2.txt'), 'content2'); + await gitAdapter.stageFiles(['file1.txt', 'file2.txt']); + + const status = await gitAdapter.getStatus(); + expect(status.staged).toContain('file1.txt'); + expect(status.staged).toContain('file2.txt'); + }); + + it('should stage all files with dot', async () => { + await fs.writeFile(path.join(testDir, 'file1.txt'), 'content1'); + await fs.writeFile(path.join(testDir, 'file2.txt'), 'content2'); + await gitAdapter.stageFiles(['.']); + + const status = await gitAdapter.getStatus(); + expect(status.staged.length).toBeGreaterThanOrEqual(2); + }); + }); + + describe('unstageFiles', () => { + it('should unstage single file', async () => { + await fs.writeFile(path.join(testDir, 'staged.txt'), 'content'); + const git = simpleGit(testDir); + await git.add('staged.txt'); + + await gitAdapter.unstageFiles(['staged.txt']); + + const status = await gitAdapter.getStatus(); + expect(status.staged).not.toContain('staged.txt'); + }); + + it('should unstage multiple files', async () => { + await fs.writeFile(path.join(testDir, 'file1.txt'), 'content1'); + await fs.writeFile(path.join(testDir, 'file2.txt'), 'content2'); + const git = simpleGit(testDir); + await git.add(['file1.txt', 'file2.txt']); + + await gitAdapter.unstageFiles(['file1.txt', 'file2.txt']); + + const status = await gitAdapter.getStatus(); + expect(status.staged).not.toContain('file1.txt'); + expect(status.staged).not.toContain('file2.txt'); + }); + }); + + describe('createCommit', () => { + it('should create commit with simple message', async () => { + await fs.writeFile(path.join(testDir, 'new.txt'), 'content'); + await gitAdapter.stageFiles(['new.txt']); + + await gitAdapter.createCommit('Add new file'); + + const git = simpleGit(testDir); + const log = await git.log(); + expect(log.latest.message).toBe('Add new file'); + }); + + it('should create commit with metadata', async () => { + await fs.writeFile(path.join(testDir, 'new.txt'), 'content'); + await gitAdapter.stageFiles(['new.txt']); + + const metadata = { + taskId: '2.4', + phase: 'implementation', + timestamp: new Date().toISOString() + }; + await gitAdapter.createCommit('Add new file', { metadata }); + + const commit = await gitAdapter.getLastCommit(); + expect(commit.message).toContain('Add new file'); + expect(commit.message).toContain('[taskId:2.4]'); + expect(commit.message).toContain('[phase:implementation]'); + }); + + it('should throw if no staged changes', async () => { + await expect(gitAdapter.createCommit('Empty commit')).rejects.toThrow(); + }); + + it('should allow empty commits with allowEmpty flag', async () => { + await gitAdapter.createCommit('Empty commit', { allowEmpty: true }); + + const git = simpleGit(testDir); + const log = await git.log(); + expect(log.latest.message).toBe('Empty commit'); + }); + + it('should throw if on default branch without force', async () => { + await fs.writeFile(path.join(testDir, 'new.txt'), 'content'); + await gitAdapter.stageFiles(['new.txt']); + + await expect( + gitAdapter.createCommit('Add new file', { + enforceNonDefaultBranch: true + }) + ).rejects.toThrow('cannot commit to default branch'); + }); + + it('should allow commit on default branch with force', async () => { + await fs.writeFile(path.join(testDir, 'new.txt'), 'content'); + await gitAdapter.stageFiles(['new.txt']); + + await gitAdapter.createCommit('Add new file', { + enforceNonDefaultBranch: true, + force: true + }); + + const git = simpleGit(testDir); + const log = await git.log(); + expect(log.latest.message).toBe('Add new file'); + }); + + it('should allow commit on feature branch with enforcement', async () => { + // Create and checkout feature branch + await gitAdapter.createAndCheckoutBranch('feature-branch'); + + await fs.writeFile(path.join(testDir, 'new.txt'), 'content'); + await gitAdapter.stageFiles(['new.txt']); + + await gitAdapter.createCommit('Add new file', { + enforceNonDefaultBranch: true + }); + + const git = simpleGit(testDir); + const log = await git.log(); + expect(log.latest.message).toBe('Add new file'); + }); + }); + + describe('getCommitLog', () => { + it('should get recent commits', async () => { + const log = await gitAdapter.getCommitLog(); + expect(log.length).toBeGreaterThan(0); + expect(log[0].message.trim()).toBe('Initial commit'); + }); + + it('should limit number of commits', async () => { + // Create additional commits + for (let i = 1; i <= 5; i++) { + await fs.writeFile(path.join(testDir, `file${i}.txt`), `content${i}`); + await gitAdapter.stageFiles([`file${i}.txt`]); + await gitAdapter.createCommit(`Commit ${i}`); + } + + const log = await gitAdapter.getCommitLog({ maxCount: 3 }); + expect(log.length).toBe(3); + }); + + it('should return commits with hash and date', async () => { + const log = await gitAdapter.getCommitLog(); + expect(log[0]).toHaveProperty('hash'); + expect(log[0]).toHaveProperty('date'); + expect(log[0]).toHaveProperty('message'); + expect(log[0]).toHaveProperty('author_name'); + expect(log[0]).toHaveProperty('author_email'); + }); + }); + + describe('getLastCommit', () => { + it('should get the last commit', async () => { + await fs.writeFile(path.join(testDir, 'new.txt'), 'content'); + await gitAdapter.stageFiles(['new.txt']); + await gitAdapter.createCommit('Latest commit'); + + const commit = await gitAdapter.getLastCommit(); + expect(commit.message.trim()).toBe('Latest commit'); + }); + + it('should return commit with metadata if present', async () => { + await fs.writeFile(path.join(testDir, 'new.txt'), 'content'); + await gitAdapter.stageFiles(['new.txt']); + const metadata = { taskId: '2.4' }; + await gitAdapter.createCommit('With metadata', { metadata }); + + const commit = await gitAdapter.getLastCommit(); + expect(commit.message).toContain('[taskId:2.4]'); + }); + }); + }); + + describe('GitAdapter - Default Branch Detection and Protection', () => { + let testDir; + let gitAdapter; + let simpleGit; + + beforeEach(async () => { + testDir = path.join(os.tmpdir(), `git-default-branch-test-${Date.now()}`); + await fs.ensureDir(testDir); + + // Initialize actual git repo with initial commit + simpleGit = (await import('simple-git')).default; + const git = simpleGit(testDir); + await git.init(); + await git.addConfig('user.name', 'Test User'); + await git.addConfig('user.email', 'test@example.com'); + + // Create initial commit + await fs.writeFile(path.join(testDir, 'README.md'), '# Test Repo'); + await git.add('README.md'); + await git.commit('Initial commit', undefined, { '--no-gpg-sign': null }); + + // Rename master to main for consistency + try { + await git.branch(['-m', 'master', 'main']); + } catch (error) { + // Branch might already be main, ignore error + } + + gitAdapter = new GitAdapter(testDir); + }); + + afterEach(async () => { + if (await fs.pathExists(testDir)) { + await fs.remove(testDir); + } + }); + + describe('getDefaultBranch', () => { + it('should detect main as default branch', async () => { + const defaultBranch = await gitAdapter.getDefaultBranch(); + expect(defaultBranch).toBe('main'); + }); + + it('should detect master if renamed back', async () => { + const git = simpleGit(testDir); + await git.branch(['-m', 'main', 'master']); + + const defaultBranch = await gitAdapter.getDefaultBranch(); + expect(defaultBranch).toBe('master'); + }); + }); + + describe('isDefaultBranch', () => { + it('should return true for main branch', async () => { + const isDefault = await gitAdapter.isDefaultBranch('main'); + expect(isDefault).toBe(true); + }); + + it('should return true for master branch', async () => { + const isDefault = await gitAdapter.isDefaultBranch('master'); + expect(isDefault).toBe(true); + }); + + it('should return true for develop branch', async () => { + const isDefault = await gitAdapter.isDefaultBranch('develop'); + expect(isDefault).toBe(true); + }); + + it('should return false for feature branch', async () => { + const isDefault = await gitAdapter.isDefaultBranch('feature-branch'); + expect(isDefault).toBe(false); + }); + }); + + describe('isOnDefaultBranch', () => { + it('should return true when on main', async () => { + const onDefault = await gitAdapter.isOnDefaultBranch(); + expect(onDefault).toBe(true); + }); + + it('should return false when on feature branch', async () => { + await gitAdapter.createAndCheckoutBranch('feature-branch'); + + const onDefault = await gitAdapter.isOnDefaultBranch(); + expect(onDefault).toBe(false); + }); + }); + + describe('ensureNotOnDefaultBranch', () => { + it('should throw when on main branch', async () => { + await expect(gitAdapter.ensureNotOnDefaultBranch()).rejects.toThrow( + 'currently on default branch' + ); + }); + + it('should not throw when on feature branch', async () => { + await gitAdapter.createAndCheckoutBranch('feature-branch'); + + await expect( + gitAdapter.ensureNotOnDefaultBranch() + ).resolves.not.toThrow(); + }); + }); + }); + + describe('GitAdapter - Push Operations', () => { + let testDir; + let gitAdapter; + + beforeEach(async () => { + testDir = path.join(os.tmpdir(), `git-push-test-${Date.now()}`); + await fs.ensureDir(testDir); + + const simpleGit = (await import('simple-git')).default; + const git = simpleGit(testDir); + await git.init(); + await git.addConfig('user.name', 'Test User'); + await git.addConfig('user.email', 'test@example.com'); + + await fs.writeFile(path.join(testDir, 'README.md'), '# Test Repo'); + await git.add('README.md'); + await git.commit('Initial commit', undefined, { '--no-gpg-sign': null }); + + try { + await git.branch(['-m', 'master', 'main']); + } catch (error) {} + + gitAdapter = new GitAdapter(testDir); + }); + + afterEach(async () => { + if (await fs.pathExists(testDir)) { + await fs.remove(testDir); + } + }); + + describe('hasRemote', () => { + it('should return false when no remotes exist', async () => { + const hasRemote = await gitAdapter.hasRemote(); + expect(hasRemote).toBe(false); + }); + }); + + describe('getRemotes', () => { + it('should return empty array when no remotes', async () => { + const remotes = await gitAdapter.getRemotes(); + expect(remotes).toEqual([]); + }); + }); + }); +}); diff --git a/packages/tm-core/src/git/git-adapter.ts b/packages/tm-core/src/git/git-adapter.ts new file mode 100644 index 00000000..f331dde4 --- /dev/null +++ b/packages/tm-core/src/git/git-adapter.ts @@ -0,0 +1,780 @@ +/** + * GitAdapter - Safe git operations wrapper with validation and safety checks. + * Handles all git operations (branching, committing, pushing) with built-in safety gates. + * + * @module git-adapter + */ + +import { simpleGit, type SimpleGit } from 'simple-git'; +import fs from 'fs-extra'; +import path from 'path'; + +/** + * GitAdapter class for safe git operations + */ +export class GitAdapter { + public projectPath: string; + public git: SimpleGit; + + /** + * Creates a new GitAdapter instance. + * + * @param {string} projectPath - Absolute path to the project directory + * @throws {Error} If projectPath is invalid or not absolute + * + * @example + * const git = new GitAdapter('/path/to/project'); + * await git.ensureGitRepository(); + */ + constructor(projectPath: string) { + // Validate project path + if (!projectPath) { + throw new Error('Project path is required'); + } + + if (!path.isAbsolute(projectPath)) { + throw new Error('Project path must be an absolute path'); + } + + // Normalize path + this.projectPath = path.normalize(projectPath); + + // Initialize simple-git + this.git = simpleGit(this.projectPath); + } + + /** + * Checks if the current directory is a git repository. + * Looks for .git directory or file (worktree/submodule). + * + * @returns {Promise<boolean>} True if in a git repository + * + * @example + * const isRepo = await git.isGitRepository(); + * if (!isRepo) { + * console.log('Not a git repository'); + * } + */ + async isGitRepository(): Promise<boolean> { + try { + // Check if .git exists (directory or file for submodules/worktrees) + const gitPath = path.join(this.projectPath, '.git'); + + if (await fs.pathExists(gitPath)) { + return true; + } + + // Try to find git root from subdirectory + try { + await this.git.revparse(['--git-dir']); + return true; + } catch { + return false; + } + } catch (error) { + return false; + } + } + + /** + * Validates that git is installed and accessible. + * Checks git binary availability and version. + * + * @returns {Promise<void>} + * @throws {Error} If git is not installed or not accessible + * + * @example + * await git.validateGitInstallation(); + * console.log('Git is installed'); + */ + async validateGitInstallation(): Promise<void> { + try { + await this.git.version(); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + throw new Error( + `Git is not installed or not accessible: ${errorMessage}` + ); + } + } + + /** + * Gets the git version information. + * + * @returns {Promise<{major: number, minor: number, patch: number, agent: string}>} + * + * @example + * const version = await git.getGitVersion(); + * console.log(`Git version: ${version.major}.${version.minor}.${version.patch}`); + */ + async getGitVersion(): Promise<{ + major: number; + minor: number; + patch: number; + agent: string; + }> { + const versionResult = await this.git.version(); + return { + major: versionResult.major, + minor: versionResult.minor, + patch: + typeof versionResult.patch === 'string' + ? parseInt(versionResult.patch) + : versionResult.patch || 0, + agent: versionResult.agent + }; + } + + /** + * Gets the repository root path. + * Works even when called from a subdirectory. + * + * @returns {Promise<string>} Absolute path to repository root + * @throws {Error} If not in a git repository + * + * @example + * const root = await git.getRepositoryRoot(); + * console.log(`Repository root: ${root}`); + */ + async getRepositoryRoot(): Promise<string> { + try { + const result = await this.git.revparse(['--show-toplevel']); + return path.normalize(result.trim()); + } catch (error) { + throw new Error(`not a git repository: ${this.projectPath}`); + } + } + + /** + * Validates the repository state. + * Checks for corruption and basic integrity. + * + * @returns {Promise<void>} + * @throws {Error} If repository is corrupted or invalid + * + * @example + * await git.validateRepository(); + * console.log('Repository is valid'); + */ + async validateRepository(): Promise<void> { + // Check if it's a git repository + const isRepo = await this.isGitRepository(); + if (!isRepo) { + throw new Error(`not a git repository: ${this.projectPath}`); + } + + // Try to get repository status to verify it's not corrupted + try { + await this.git.status(); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + throw new Error(`Repository validation failed: ${errorMessage}`); + } + } + + /** + * Ensures we're in a valid git repository before performing operations. + * Convenience method that throws descriptive errors. + * + * @returns {Promise<void>} + * @throws {Error} If not in a valid git repository + * + * @example + * await git.ensureGitRepository(); + * // Safe to perform git operations after this + */ + async ensureGitRepository(): Promise<void> { + const isRepo = await this.isGitRepository(); + if (!isRepo) { + throw new Error( + `not a git repository: ${this.projectPath}\n` + + `Please run this command from within a git repository, or initialize one with 'git init'.` + ); + } + } + + /** + * Checks if the working tree is clean (no uncommitted changes). + * A clean working tree has no staged, unstaged, or untracked files. + * + * @returns {Promise<boolean>} True if working tree is clean + * + * @example + * const isClean = await git.isWorkingTreeClean(); + * if (!isClean) { + * console.log('Working tree has uncommitted changes'); + * } + */ + async isWorkingTreeClean(): Promise<boolean> { + const status = await this.git.status(); + return status.isClean(); + } + + /** + * Gets the detailed status of the working tree. + * Returns raw status from simple-git with all file changes. + * + * @returns {Promise<import('simple-git').StatusResult>} Detailed status object + * + * @example + * const status = await git.getStatus(); + * console.log('Modified files:', status.modified); + * console.log('Staged files:', status.staged); + */ + async getStatus(): Promise<import('simple-git').StatusResult> { + return await this.git.status(); + } + + /** + * Checks if there are any uncommitted changes in the working tree. + * Includes staged, unstaged, and untracked files. + * + * @returns {Promise<boolean>} True if there are uncommitted changes + * + * @example + * const hasChanges = await git.hasUncommittedChanges(); + * if (hasChanges) { + * console.log('Please commit your changes before proceeding'); + * } + */ + async hasUncommittedChanges(): Promise<boolean> { + const status = await this.git.status(); + return !status.isClean(); + } + + /** + * Checks if there are any staged changes ready to commit. + * + * @returns {Promise<boolean>} True if there are staged changes + * + * @example + * const hasStaged = await git.hasStagedChanges(); + * if (hasStaged) { + * console.log('Ready to commit'); + * } + */ + async hasStagedChanges(): Promise<boolean> { + const status = await this.git.status(); + return status.staged.length > 0; + } + + /** + * Checks if there are any untracked files in the working tree. + * + * @returns {Promise<boolean>} True if there are untracked files + * + * @example + * const hasUntracked = await git.hasUntrackedFiles(); + * if (hasUntracked) { + * console.log('You have untracked files'); + * } + */ + async hasUntrackedFiles(): Promise<boolean> { + const status = await this.git.status(); + return status.not_added.length > 0; + } + + /** + * Gets a summary of the working tree status with counts. + * + * @returns {Promise<{isClean: boolean, staged: number, modified: number, deleted: number, untracked: number, totalChanges: number}>} + * + * @example + * const summary = await git.getStatusSummary(); + * console.log(`${summary.totalChanges} total changes`); + */ + async getStatusSummary(): Promise<{ + isClean: boolean; + staged: number; + modified: number; + deleted: number; + untracked: number; + totalChanges: number; + }> { + const status = await this.git.status(); + const staged = status.staged.length; + const modified = status.modified.length; + const deleted = status.deleted.length; + const untracked = status.not_added.length; + const totalChanges = staged + modified + deleted + untracked; + + return { + isClean: status.isClean(), + staged, + modified, + deleted, + untracked, + totalChanges + }; + } + + /** + * Ensures the working tree is clean before performing operations. + * Throws an error with details if there are uncommitted changes. + * + * @returns {Promise<void>} + * @throws {Error} If working tree is not clean + * + * @example + * await git.ensureCleanWorkingTree(); + * // Safe to perform git operations that require clean state + */ + async ensureCleanWorkingTree(): Promise<void> { + const status = await this.git.status(); + if (!status.isClean()) { + const summary = await this.getStatusSummary(); + throw new Error( + `working tree is not clean: ${this.projectPath}\n` + + `Staged: ${summary.staged}, Modified: ${summary.modified}, ` + + `Deleted: ${summary.deleted}, Untracked: ${summary.untracked}\n` + + `Please commit or stash your changes before proceeding.` + ); + } + } + + /** + * Gets the name of the current branch. + * + * @returns {Promise<string>} Current branch name + * @throws {Error} If unable to determine current branch + * + * @example + * const branch = await git.getCurrentBranch(); + * console.log(`Currently on: ${branch}`); + */ + async getCurrentBranch(): Promise<string> { + const status = await this.git.status(); + return status.current || 'HEAD'; + } + + /** + * Lists all local branches in the repository. + * + * @returns {Promise<string[]>} Array of branch names + * + * @example + * const branches = await git.listBranches(); + * console.log('Available branches:', branches); + */ + async listBranches(): Promise<string[]> { + const branchSummary = await this.git.branchLocal(); + return Object.keys(branchSummary.branches); + } + + /** + * Checks if a branch exists in the repository. + * + * @param {string} branchName - Name of branch to check + * @returns {Promise<boolean>} True if branch exists + * + * @example + * const exists = await git.branchExists('feature-branch'); + * if (!exists) { + * console.log('Branch does not exist'); + * } + */ + async branchExists(branchName: string): Promise<boolean> { + const branches = await this.listBranches(); + return branches.includes(branchName); + } + + /** + * Creates a new branch without checking it out. + * + * @param {string} branchName - Name for the new branch + * @param {Object} options - Branch creation options + * @param {boolean} options.checkout - Whether to checkout after creation + * @returns {Promise<void>} + * @throws {Error} If branch already exists or working tree is dirty (when checkout=true) + * + * @example + * await git.createBranch('feature-branch'); + * await git.createBranch('feature-branch', { checkout: true }); + */ + async createBranch( + branchName: string, + options: { checkout?: boolean } = {} + ): Promise<void> { + // Check if branch already exists + const exists = await this.branchExists(branchName); + if (exists) { + throw new Error(`branch already exists: ${branchName}`); + } + + // If checkout is requested, ensure working tree is clean + if (options.checkout) { + await this.ensureCleanWorkingTree(); + } + + // Create the branch + await this.git.branch([branchName]); + + // Checkout if requested + if (options.checkout) { + await this.git.checkout(branchName); + } + } + + /** + * Checks out an existing branch. + * + * @param {string} branchName - Name of branch to checkout + * @param {Object} options - Checkout options + * @param {boolean} options.force - Force checkout even with uncommitted changes + * @returns {Promise<void>} + * @throws {Error} If branch doesn't exist or working tree is dirty (unless force=true) + * + * @example + * await git.checkoutBranch('feature-branch'); + * await git.checkoutBranch('feature-branch', { force: true }); + */ + async checkoutBranch( + branchName: string, + options: { force?: boolean } = {} + ): Promise<void> { + // Check if branch exists + const exists = await this.branchExists(branchName); + if (!exists) { + throw new Error(`branch does not exist: ${branchName}`); + } + + // Ensure clean working tree unless force is specified + if (!options.force) { + await this.ensureCleanWorkingTree(); + } + + // Checkout the branch + const checkoutOptions = options.force ? ['-f', branchName] : [branchName]; + await this.git.checkout(checkoutOptions); + } + + /** + * Creates a new branch and checks it out. + * Convenience method combining createBranch and checkoutBranch. + * + * @param {string} branchName - Name for the new branch + * @returns {Promise<void>} + * @throws {Error} If branch already exists or working tree is dirty + * + * @example + * await git.createAndCheckoutBranch('new-feature'); + */ + async createAndCheckoutBranch(branchName: string): Promise<void> { + // Ensure working tree is clean + await this.ensureCleanWorkingTree(); + + // Check if branch already exists + const exists = await this.branchExists(branchName); + if (exists) { + throw new Error(`branch already exists: ${branchName}`); + } + + // Create and checkout the branch + await this.git.checkoutLocalBranch(branchName); + } + + /** + * Deletes a branch. + * + * @param {string} branchName - Name of branch to delete + * @param {Object} options - Delete options + * @param {boolean} options.force - Force delete even if unmerged + * @returns {Promise<void>} + * @throws {Error} If branch doesn't exist or is currently checked out + * + * @example + * await git.deleteBranch('old-feature'); + * await git.deleteBranch('unmerged-feature', { force: true }); + */ + async deleteBranch( + branchName: string, + options: { force?: boolean } = {} + ): Promise<void> { + // Check if branch exists + const exists = await this.branchExists(branchName); + if (!exists) { + throw new Error(`branch does not exist: ${branchName}`); + } + + // Check if trying to delete current branch + const current = await this.getCurrentBranch(); + if (current === branchName) { + throw new Error(`cannot delete current branch: ${branchName}`); + } + + // Delete the branch + const deleteOptions = options.force + ? ['-D', branchName] + : ['-d', branchName]; + await this.git.branch(deleteOptions); + } + + /** + * Stages files for commit. + * + * @param {string[]} files - Array of file paths to stage + * @returns {Promise<void>} + * + * @example + * await git.stageFiles(['file1.txt', 'file2.txt']); + * await git.stageFiles(['.']); // Stage all changes + */ + async stageFiles(files: string[]): Promise<void> { + await this.git.add(files); + } + + /** + * Unstages files that were previously staged. + * + * @param {string[]} files - Array of file paths to unstage + * @returns {Promise<void>} + * + * @example + * await git.unstageFiles(['file1.txt']); + */ + async unstageFiles(files: string[]): Promise<void> { + await this.git.reset(['HEAD', '--', ...files]); + } + + /** + * Creates a commit with optional metadata embedding. + * + * @param {string} message - Commit message + * @param {Object} options - Commit options + * @param {Object} options.metadata - Metadata to embed in commit message + * @param {boolean} options.allowEmpty - Allow empty commits + * @param {boolean} options.enforceNonDefaultBranch - Prevent commits on default branch + * @param {boolean} options.force - Force commit even on default branch + * @returns {Promise<void>} + * @throws {Error} If no staged changes (unless allowEmpty), or on default branch (unless force) + * + * @example + * await git.createCommit('Add feature'); + * await git.createCommit('Add feature', { + * metadata: { taskId: '2.4', phase: 'implementation' } + * }); + * await git.createCommit('Add feature', { + * enforceNonDefaultBranch: true + * }); + */ + async createCommit( + message: string, + options: { + metadata?: Record<string, string>; + allowEmpty?: boolean; + enforceNonDefaultBranch?: boolean; + force?: boolean; + } = {} + ): Promise<void> { + // Check if on default branch and enforcement is requested + if (options.enforceNonDefaultBranch && !options.force) { + const currentBranch = await this.getCurrentBranch(); + const defaultBranches = ['main', 'master', 'develop']; + if (defaultBranches.includes(currentBranch)) { + throw new Error( + `cannot commit to default branch: ${currentBranch}\n` + + `Please create a feature branch or use force option.` + ); + } + } + + // Check for staged changes unless allowEmpty + if (!options.allowEmpty) { + const hasStaged = await this.hasStagedChanges(); + if (!hasStaged) { + throw new Error('no staged changes to commit'); + } + } + + // Build commit arguments + const commitArgs: string[] = ['commit']; + + // Add message + commitArgs.push('-m', message); + + // Add metadata as separate commit message lines + if (options.metadata) { + commitArgs.push('-m', ''); // Empty line separator + for (const [key, value] of Object.entries(options.metadata)) { + commitArgs.push('-m', `[${key}:${value}]`); + } + } + + // Add flags + commitArgs.push('--no-gpg-sign'); + if (options.allowEmpty) { + commitArgs.push('--allow-empty'); + } + + await this.git.raw(commitArgs); + } + + /** + * Gets the commit log history. + * + * @param {Object} options - Log options + * @param {number} options.maxCount - Maximum number of commits to return + * @returns {Promise<Array>} Array of commit objects + * + * @example + * const log = await git.getCommitLog(); + * const recentLog = await git.getCommitLog({ maxCount: 10 }); + */ + async getCommitLog(options: { maxCount?: number } = {}): Promise<any[]> { + const logOptions: any = { + format: { + hash: '%H', + date: '%ai', + message: '%B', // Full commit message including body + author_name: '%an', + author_email: '%ae' + } + }; + if (options.maxCount) { + logOptions.maxCount = options.maxCount; + } + + const log = await this.git.log(logOptions); + return [...log.all]; + } + + /** + * Gets the last commit. + * + * @returns {Promise<any>} Last commit object + * + * @example + * const lastCommit = await git.getLastCommit(); + * console.log(lastCommit.message); + */ + async getLastCommit(): Promise<any> { + const log = await this.git.log({ + maxCount: 1, + format: { + hash: '%H', + date: '%ai', + message: '%B', // Full commit message including body + author_name: '%an', + author_email: '%ae' + } + }); + return log.latest; + } + + /** + * Detects the default branch for the repository. + * Returns the current branch name, assuming it's the default if it's main/master/develop. + * + * @returns {Promise<string>} Default branch name + * + * @example + * const defaultBranch = await git.getDefaultBranch(); + * console.log(`Default branch: ${defaultBranch}`); + */ + async getDefaultBranch(): Promise<string> { + const currentBranch = await this.getCurrentBranch(); + const defaultBranches = ['main', 'master', 'develop']; + + if (defaultBranches.includes(currentBranch)) { + return currentBranch; + } + + // If not on a default branch, check which default branches exist + const branches = await this.listBranches(); + for (const defaultBranch of defaultBranches) { + if (branches.includes(defaultBranch)) { + return defaultBranch; + } + } + + // Fallback to main + return 'main'; + } + + /** + * Checks if a given branch name is considered a default branch. + * Default branches are: main, master, develop. + * + * @param {string} branchName - Branch name to check + * @returns {Promise<boolean>} True if branch is a default branch + * + * @example + * const isDefault = await git.isDefaultBranch('main'); + * if (isDefault) { + * console.log('This is a default branch'); + * } + */ + async isDefaultBranch(branchName: string): Promise<boolean> { + const defaultBranches = ['main', 'master', 'develop']; + return defaultBranches.includes(branchName); + } + + /** + * Checks if currently on a default branch. + * + * @returns {Promise<boolean>} True if on a default branch + * + * @example + * const onDefault = await git.isOnDefaultBranch(); + * if (onDefault) { + * console.log('Warning: You are on a default branch'); + * } + */ + async isOnDefaultBranch(): Promise<boolean> { + const currentBranch = await this.getCurrentBranch(); + return await this.isDefaultBranch(currentBranch); + } + + /** + * Ensures the current branch is not a default branch. + * Throws an error if on a default branch. + * + * @returns {Promise<void>} + * @throws {Error} If currently on a default branch + * + * @example + * await git.ensureNotOnDefaultBranch(); + * // Safe to perform operations that shouldn't happen on default branches + */ + async ensureNotOnDefaultBranch(): Promise<void> { + const onDefault = await this.isOnDefaultBranch(); + if (onDefault) { + const currentBranch = await this.getCurrentBranch(); + throw new Error( + `currently on default branch: ${currentBranch}\n` + + `Please create a feature branch before proceeding.` + ); + } + } + + /** + * Checks if the repository has any remotes configured. + * + * @returns {Promise<boolean>} True if remotes exist + * + * @example + * const hasRemote = await git.hasRemote(); + * if (!hasRemote) { + * console.log('No remotes configured'); + * } + */ + async hasRemote(): Promise<boolean> { + const remotes = await this.git.getRemotes(); + return remotes.length > 0; + } + + /** + * Gets all configured remotes. + * + * @returns {Promise<Array>} Array of remote objects + * + * @example + * const remotes = await git.getRemotes(); + * console.log('Remotes:', remotes); + */ + async getRemotes(): Promise<any[]> { + return await this.git.getRemotes(true); + } +} diff --git a/packages/tm-core/src/git/index.ts b/packages/tm-core/src/git/index.ts new file mode 100644 index 00000000..9987566a --- /dev/null +++ b/packages/tm-core/src/git/index.ts @@ -0,0 +1,13 @@ +/** + * @fileoverview Git operations layer for the tm-core package + * This file exports all git-related classes and interfaces + */ + +// Export GitAdapter +export { GitAdapter } from './git-adapter.js'; + +// Export branch name utilities +export { + generateBranchName, + sanitizeBranchName +} from './branch-name-generator.js'; diff --git a/packages/tm-core/src/git/scope-detector.test.ts b/packages/tm-core/src/git/scope-detector.test.ts new file mode 100644 index 00000000..3d225e37 --- /dev/null +++ b/packages/tm-core/src/git/scope-detector.test.ts @@ -0,0 +1,284 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { ScopeDetector } from './scope-detector.js'; + +describe('ScopeDetector', () => { + let scopeDetector: ScopeDetector; + + beforeEach(() => { + scopeDetector = new ScopeDetector(); + }); + + describe('detectScope', () => { + it('should detect cli scope from CLI file changes', () => { + const files = ['packages/cli/src/commands/start.ts']; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('cli'); + }); + + it('should detect core scope from core package changes', () => { + const files = ['packages/tm-core/src/workflow/orchestrator.ts']; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('core'); + }); + + it('should detect test scope from test file changes', () => { + const files = ['packages/tm-core/src/workflow/orchestrator.test.ts']; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('test'); + }); + + it('should detect docs scope from documentation changes', () => { + const files = ['README.md', 'docs/guide.md']; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('docs'); + }); + + it('should detect config scope from configuration changes', () => { + const files = ['tsconfig.json']; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('config'); + }); + + it('should detect workflow scope from workflow files', () => { + const files = ['packages/tm-core/src/workflow/types.ts']; + const scope = scopeDetector.detectScope(files); + + // Files within packages get the package scope (more specific than feature scope) + expect(scope).toBe('core'); + }); + + it('should detect git scope from git adapter files', () => { + const files = ['packages/tm-core/src/git/git-adapter.ts']; + const scope = scopeDetector.detectScope(files); + + // Files within packages get the package scope (more specific than feature scope) + expect(scope).toBe('core'); + }); + + it('should detect storage scope from storage files', () => { + const files = ['packages/tm-core/src/storage/state-manager.ts']; + const scope = scopeDetector.detectScope(files); + + // Files within packages get the package scope (more specific than feature scope) + expect(scope).toBe('core'); + }); + + it('should use most relevant scope when multiple files', () => { + const files = [ + 'packages/cli/src/commands/start.ts', + 'packages/cli/src/commands/stop.ts', + 'packages/tm-core/src/types.ts' + ]; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('cli'); + }); + + it('should handle mixed scopes by choosing highest priority', () => { + const files = [ + 'README.md', + 'packages/tm-core/src/workflow/orchestrator.ts' + ]; + const scope = scopeDetector.detectScope(files); + + // Core is higher priority than docs + expect(scope).toBe('core'); + }); + + it('should handle empty file list gracefully', () => { + const files: string[] = []; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('repo'); + }); + + it('should detect mcp scope from MCP server files', () => { + const files = ['packages/mcp-server/src/tools.ts']; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('mcp'); + }); + + it('should detect auth scope from authentication files', () => { + const files = ['packages/tm-core/src/auth/auth-manager.ts']; + const scope = scopeDetector.detectScope(files); + + // Files within packages get the package scope (more specific than feature scope) + expect(scope).toBe('core'); + }); + + it('should detect deps scope from dependency changes', () => { + const files = ['pnpm-lock.yaml']; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('deps'); + }); + }); + + describe('detectScopeWithCustomRules', () => { + it('should use custom scope mapping rules', () => { + const customRules: Record<string, number> = { + custom: 100 + }; + + const customDetector = new ScopeDetector( + { + 'custom/**': 'custom' + }, + customRules + ); + + const files = ['custom/file.ts']; + const scope = customDetector.detectScope(files); + + expect(scope).toBe('custom'); + }); + + it('should override default priorities with custom priorities', () => { + const customPriorities: Record<string, number> = { + docs: 100, // Make docs highest priority + core: 10 + }; + + const customDetector = new ScopeDetector(undefined, customPriorities); + + const files = [ + 'README.md', + 'packages/tm-core/src/workflow/orchestrator.ts' + ]; + const scope = customDetector.detectScope(files); + + expect(scope).toBe('docs'); + }); + }); + + describe('getAllMatchingScopes', () => { + it('should return all matching scopes for files', () => { + const files = [ + 'packages/cli/src/commands/start.ts', + 'packages/tm-core/src/workflow/orchestrator.ts', + 'README.md' + ]; + + const scopes = scopeDetector.getAllMatchingScopes(files); + + expect(scopes).toContain('cli'); + expect(scopes).toContain('core'); + expect(scopes).toContain('docs'); + expect(scopes).toHaveLength(3); + }); + + it('should return unique scopes only', () => { + const files = [ + 'packages/cli/src/commands/start.ts', + 'packages/cli/src/commands/stop.ts' + ]; + + const scopes = scopeDetector.getAllMatchingScopes(files); + + expect(scopes).toEqual(['cli']); + }); + + it('should return empty array for files with no matches', () => { + const files = ['unknown/path/file.ts']; + const scopes = scopeDetector.getAllMatchingScopes(files); + + expect(scopes).toEqual([]); + }); + }); + + describe('getScopePriority', () => { + it('should return priority for known scope', () => { + const priority = scopeDetector.getScopePriority('core'); + + expect(priority).toBeGreaterThan(0); + }); + + it('should return 0 for unknown scope', () => { + const priority = scopeDetector.getScopePriority('nonexistent'); + + expect(priority).toBe(0); + }); + + it('should prioritize core > cli > test > docs', () => { + const corePriority = scopeDetector.getScopePriority('core'); + const cliPriority = scopeDetector.getScopePriority('cli'); + const testPriority = scopeDetector.getScopePriority('test'); + const docsPriority = scopeDetector.getScopePriority('docs'); + + expect(corePriority).toBeGreaterThan(cliPriority); + expect(cliPriority).toBeGreaterThan(testPriority); + expect(testPriority).toBeGreaterThan(docsPriority); + }); + }); + + describe('edge cases', () => { + it('should handle Windows paths', () => { + const files = ['packages\\cli\\src\\commands\\start.ts']; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('cli'); + }); + + it('should handle absolute paths', () => { + const files = [ + '/home/user/project/packages/tm-core/src/workflow/orchestrator.ts' + ]; + const scope = scopeDetector.detectScope(files); + + // Absolute paths won't match package patterns + expect(scope).toBe('workflow'); + }); + + it('should handle paths with special characters', () => { + const files = ['packages/tm-core/src/workflow/orchestrator@v2.ts']; + const scope = scopeDetector.detectScope(files); + + // Files within packages get the package scope + expect(scope).toBe('core'); + }); + + it('should handle very long file paths', () => { + const files = [ + 'packages/tm-core/src/deeply/nested/directory/structure/with/many/levels/file.ts' + ]; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('core'); + }); + + it('should handle files in root directory', () => { + const files = ['file.ts']; + const scope = scopeDetector.detectScope(files); + + expect(scope).toBe('repo'); + }); + }); + + describe('getMatchingScope', () => { + it('should return matching scope for single file', () => { + const scope = scopeDetector.getMatchingScope('packages/cli/src/index.ts'); + + expect(scope).toBe('cli'); + }); + + it('should return null for non-matching file', () => { + const scope = scopeDetector.getMatchingScope('unknown/file.ts'); + + expect(scope).toBeNull(); + }); + + it('should match test files', () => { + const scope = scopeDetector.getMatchingScope( + 'src/components/button.test.tsx' + ); + + expect(scope).toBe('test'); + }); + }); +}); diff --git a/packages/tm-core/src/git/scope-detector.ts b/packages/tm-core/src/git/scope-detector.ts new file mode 100644 index 00000000..36fdc97b --- /dev/null +++ b/packages/tm-core/src/git/scope-detector.ts @@ -0,0 +1,204 @@ +/** + * ScopeDetector - Intelligent scope detection from changed files + * + * Automatically determines conventional commit scopes based on file paths + * using configurable pattern matching and priority-based resolution. + * // TODO: remove this + */ + +export interface ScopeMapping { + [pattern: string]: string; +} + +export interface ScopePriority { + [scope: string]: number; +} + +// Ordered from most specific to least specific +const DEFAULT_SCOPE_MAPPINGS: Array<[string, string]> = [ + // Special file types (check first - most specific) + ['**/*.test.*', 'test'], + ['**/*.spec.*', 'test'], + ['**/test/**', 'test'], + ['**/tests/**', 'test'], + ['**/__tests__/**', 'test'], + + // Dependencies (specific files) + ['**/package-lock.json', 'deps'], + ['package-lock.json', 'deps'], + ['**/pnpm-lock.yaml', 'deps'], + ['pnpm-lock.yaml', 'deps'], + ['**/yarn.lock', 'deps'], + ['yarn.lock', 'deps'], + + // Configuration files (before packages so root configs don't match package patterns) + ['**/package.json', 'config'], + ['package.json', 'config'], + ['**/tsconfig*.json', 'config'], + ['tsconfig*.json', 'config'], + ['**/.eslintrc*', 'config'], + ['.eslintrc*', 'config'], + ['**/vite.config.*', 'config'], + ['vite.config.*', 'config'], + ['**/vitest.config.*', 'config'], + ['vitest.config.*', 'config'], + + // Package-level scopes (more specific than feature-level) + ['packages/cli/**', 'cli'], + ['packages/tm-core/**', 'core'], + ['packages/mcp-server/**', 'mcp'], + + // Feature-level scopes (within any package) + ['**/workflow/**', 'workflow'], + ['**/git/**', 'git'], + ['**/storage/**', 'storage'], + ['**/auth/**', 'auth'], + ['**/config/**', 'config'], + + // Documentation (least specific) + ['**/*.md', 'docs'], + ['**/docs/**', 'docs'], + ['README*', 'docs'], + ['CHANGELOG*', 'docs'] +]; + +const DEFAULT_SCOPE_PRIORITIES: ScopePriority = { + core: 100, + cli: 90, + mcp: 85, + workflow: 80, + git: 75, + storage: 70, + auth: 65, + config: 60, + test: 50, + docs: 30, + deps: 20, + repo: 10 +}; + +export class ScopeDetector { + private scopeMappings: Array<[string, string]>; + private scopePriorities: ScopePriority; + + constructor(customMappings?: ScopeMapping, customPriorities?: ScopePriority) { + // Start with default mappings + this.scopeMappings = [...DEFAULT_SCOPE_MAPPINGS]; + + // Add custom mappings at the start (highest priority) + if (customMappings) { + const customEntries = Object.entries(customMappings); + this.scopeMappings = [...customEntries, ...this.scopeMappings]; + } + + this.scopePriorities = { + ...DEFAULT_SCOPE_PRIORITIES, + ...customPriorities + }; + } + + /** + * Detect the most relevant scope from a list of changed files + * Returns the scope with the highest priority + */ + detectScope(files: string[]): string { + if (files.length === 0) { + return 'repo'; + } + + const scopeCounts = new Map<string, number>(); + + // Count occurrences of each scope + for (const file of files) { + const scope = this.getMatchingScope(file); + if (scope) { + scopeCounts.set(scope, (scopeCounts.get(scope) || 0) + 1); + } + } + + // If no scopes matched, default to 'repo' + if (scopeCounts.size === 0) { + return 'repo'; + } + + // Find scope with highest priority (considering both priority and count) + let bestScope = 'repo'; + let bestScore = 0; + + for (const [scope, count] of scopeCounts) { + const priority = this.getScopePriority(scope); + // Score = priority * count (files in that scope) + const score = priority * count; + + if (score > bestScore) { + bestScore = score; + bestScope = scope; + } + } + + return bestScope; + } + + /** + * Get all matching scopes for the given files + */ + getAllMatchingScopes(files: string[]): string[] { + const scopes = new Set<string>(); + + for (const file of files) { + const scope = this.getMatchingScope(file); + if (scope) { + scopes.add(scope); + } + } + + return Array.from(scopes); + } + + /** + * Get the matching scope for a single file + * Returns the first matching scope (order matters!) + */ + getMatchingScope(file: string): string | null { + // Normalize path separators + const normalizedFile = file.replace(/\\/g, '/'); + + for (const [pattern, scope] of this.scopeMappings) { + if (this.matchesPattern(normalizedFile, pattern)) { + return scope; + } + } + + return null; + } + + /** + * Get the priority of a scope + */ + getScopePriority(scope: string): number { + return this.scopePriorities[scope] || 0; + } + + /** + * Match a file path against a glob-like pattern + * Supports: + * - ** for multi-level directory matching + * - * for single-level matching + */ + private matchesPattern(filePath: string, pattern: string): boolean { + // Replace ** first with a unique placeholder + let regexPattern = pattern.replace(/\*\*/g, '§GLOBSTAR§'); + + // Escape special regex characters (but not our placeholder or *) + regexPattern = regexPattern.replace(/[.+^${}()|[\]\\]/g, '\\$&'); + + // Replace single * with [^/]* (matches anything except /) + regexPattern = regexPattern.replace(/\*/g, '[^/]*'); + + // Replace placeholder with .* (matches anything including /) + regexPattern = regexPattern.replace(/§GLOBSTAR§/g, '.*'); + + const regex = new RegExp(`^${regexPattern}$`); + return regex.test(filePath); + } +} diff --git a/packages/tm-core/src/git/template-engine.test.ts b/packages/tm-core/src/git/template-engine.test.ts new file mode 100644 index 00000000..3c9e332d --- /dev/null +++ b/packages/tm-core/src/git/template-engine.test.ts @@ -0,0 +1,277 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { TemplateEngine } from './template-engine.js'; + +describe('TemplateEngine', () => { + let templateEngine: TemplateEngine; + + beforeEach(() => { + templateEngine = new TemplateEngine(); + }); + + describe('constructor and initialization', () => { + it('should initialize with default templates', () => { + expect(templateEngine).toBeDefined(); + }); + + it('should accept custom templates in constructor', () => { + const customTemplate = '{{type}}({{scope}}): {{description}}'; + const engine = new TemplateEngine({ commitMessage: customTemplate }); + + const result = engine.render('commitMessage', { + type: 'feat', + scope: 'core', + description: 'add feature' + }); + + expect(result).toBe('feat(core): add feature'); + }); + }); + + describe('render', () => { + it('should render simple template with single variable', () => { + const template = 'Hello {{name}}'; + const result = templateEngine.render('test', { name: 'World' }, template); + + expect(result).toBe('Hello World'); + }); + + it('should render template with multiple variables', () => { + const template = '{{type}}({{scope}}): {{description}}'; + const result = templateEngine.render( + 'test', + { + type: 'feat', + scope: 'api', + description: 'add endpoint' + }, + template + ); + + expect(result).toBe('feat(api): add endpoint'); + }); + + it('should handle missing variables by leaving placeholder', () => { + const template = 'Hello {{name}} from {{location}}'; + const result = templateEngine.render('test', { name: 'Alice' }, template); + + expect(result).toBe('Hello Alice from {{location}}'); + }); + + it('should handle empty variable values', () => { + const template = '{{prefix}}{{message}}'; + const result = templateEngine.render( + 'test', + { + prefix: '', + message: 'hello' + }, + template + ); + + expect(result).toBe('hello'); + }); + + it('should handle numeric values', () => { + const template = 'Count: {{count}}'; + const result = templateEngine.render('test', { count: 42 }, template); + + expect(result).toBe('Count: 42'); + }); + + it('should handle boolean values', () => { + const template = 'Active: {{active}}'; + const result = templateEngine.render('test', { active: true }, template); + + expect(result).toBe('Active: true'); + }); + }); + + describe('setTemplate', () => { + it('should set and use custom template', () => { + templateEngine.setTemplate('custom', 'Value: {{value}}'); + const result = templateEngine.render('custom', { value: '123' }); + + expect(result).toBe('Value: 123'); + }); + + it('should override existing template', () => { + templateEngine.setTemplate('commitMessage', 'Custom: {{msg}}'); + const result = templateEngine.render('commitMessage', { msg: 'hello' }); + + expect(result).toBe('Custom: hello'); + }); + }); + + describe('getTemplate', () => { + it('should return existing template', () => { + templateEngine.setTemplate('test', 'Template: {{value}}'); + const template = templateEngine.getTemplate('test'); + + expect(template).toBe('Template: {{value}}'); + }); + + it('should return undefined for non-existent template', () => { + const template = templateEngine.getTemplate('nonexistent'); + + expect(template).toBeUndefined(); + }); + }); + + describe('hasTemplate', () => { + it('should return true for existing template', () => { + templateEngine.setTemplate('test', 'Template'); + + expect(templateEngine.hasTemplate('test')).toBe(true); + }); + + it('should return false for non-existent template', () => { + expect(templateEngine.hasTemplate('nonexistent')).toBe(false); + }); + }); + + describe('validateTemplate', () => { + it('should validate template with all required variables', () => { + const template = '{{type}}({{scope}}): {{description}}'; + const requiredVars = ['type', 'scope', 'description']; + + const result = templateEngine.validateTemplate(template, requiredVars); + + expect(result.isValid).toBe(true); + expect(result.missingVars).toEqual([]); + }); + + it('should detect missing required variables', () => { + const template = '{{type}}: {{description}}'; + const requiredVars = ['type', 'scope', 'description']; + + const result = templateEngine.validateTemplate(template, requiredVars); + + expect(result.isValid).toBe(false); + expect(result.missingVars).toEqual(['scope']); + }); + + it('should detect multiple missing variables', () => { + const template = '{{type}}'; + const requiredVars = ['type', 'scope', 'description']; + + const result = templateEngine.validateTemplate(template, requiredVars); + + expect(result.isValid).toBe(false); + expect(result.missingVars).toEqual(['scope', 'description']); + }); + + it('should handle optional variables in template', () => { + const template = '{{type}}({{scope}}): {{description}} [{{taskId}}]'; + const requiredVars = ['type', 'scope', 'description']; + + const result = templateEngine.validateTemplate(template, requiredVars); + + expect(result.isValid).toBe(true); + expect(result.missingVars).toEqual([]); + }); + }); + + describe('extractVariables', () => { + it('should extract all variables from template', () => { + const template = '{{type}}({{scope}}): {{description}}'; + const variables = templateEngine.extractVariables(template); + + expect(variables).toEqual(['type', 'scope', 'description']); + }); + + it('should extract unique variables only', () => { + const template = '{{name}} and {{name}} with {{other}}'; + const variables = templateEngine.extractVariables(template); + + expect(variables).toEqual(['name', 'other']); + }); + + it('should return empty array for template without variables', () => { + const template = 'Static text with no variables'; + const variables = templateEngine.extractVariables(template); + + expect(variables).toEqual([]); + }); + + it('should handle template with whitespace in placeholders', () => { + const template = '{{ type }} and {{ scope }}'; + const variables = templateEngine.extractVariables(template); + + expect(variables).toEqual(['type', 'scope']); + }); + }); + + describe('edge cases', () => { + it('should handle empty template', () => { + const result = templateEngine.render('test', { name: 'value' }, ''); + + expect(result).toBe(''); + }); + + it('should handle template with no variables', () => { + const template = 'Static text'; + const result = templateEngine.render('test', {}, template); + + expect(result).toBe('Static text'); + }); + + it('should handle empty variables object', () => { + const template = 'Hello {{name}}'; + const result = templateEngine.render('test', {}, template); + + expect(result).toBe('Hello {{name}}'); + }); + + it('should handle special characters in values', () => { + const template = 'Value: {{value}}'; + const result = templateEngine.render( + 'test', + { + value: 'hello$world{test}' + }, + template + ); + + expect(result).toBe('Value: hello$world{test}'); + }); + + it('should handle multiline templates', () => { + const template = '{{type}}: {{description}}\n\n{{body}}'; + const result = templateEngine.render( + 'test', + { + type: 'feat', + description: 'add feature', + body: 'Details here' + }, + template + ); + + expect(result).toBe('feat: add feature\n\nDetails here'); + }); + }); + + describe('default commit message template', () => { + it('should have default commit message template', () => { + const template = templateEngine.getTemplate('commitMessage'); + + expect(template).toBeDefined(); + expect(template).toContain('{{type}}'); + expect(template).toContain('{{description}}'); + }); + + it('should render default commit message template', () => { + const result = templateEngine.render('commitMessage', { + type: 'feat', + scope: 'core', + description: 'implement feature', + body: 'Additional details', + taskId: '5.1' + }); + + expect(result).toContain('feat'); + expect(result).toContain('core'); + expect(result).toContain('implement feature'); + }); + }); +}); diff --git a/packages/tm-core/src/git/template-engine.ts b/packages/tm-core/src/git/template-engine.ts new file mode 100644 index 00000000..473e6e1c --- /dev/null +++ b/packages/tm-core/src/git/template-engine.ts @@ -0,0 +1,203 @@ +/** + * TemplateEngine - Configurable template system for generating text from templates + * + * Supports: + * - Variable substitution using {{variableName}} syntax + * - Custom templates via constructor or setTemplate + * - Template validation with required variables + * - Variable extraction from templates + * - Multiple template storage and retrieval + */ + +export interface TemplateValidationResult { + isValid: boolean; + missingVars: string[]; +} + +export interface TemplateVariables { + [key: string]: string | number | boolean | undefined; +} + +export interface TemplateCollection { + [templateName: string]: string; +} + +export interface TemplateEngineOptions { + customTemplates?: TemplateCollection; + preservePlaceholders?: boolean; +} + +const DEFAULT_TEMPLATES: TemplateCollection = { + commitMessage: `{{type}}{{#scope}}({{scope}}){{/scope}}{{#breaking}}!{{/breaking}}: {{description}} + +{{#body}}{{body}} + +{{/body}}{{#taskId}}Task: {{taskId}}{{/taskId}}{{#phase}} +Phase: {{phase}}{{/phase}}{{#testsPassing}} +Tests: {{testsPassing}} passing{{#testsFailing}}, {{testsFailing}} failing{{/testsFailing}}{{/testsPassing}}` +}; + +export class TemplateEngine { + private templates: TemplateCollection; + private preservePlaceholders: boolean; + + constructor( + optionsOrTemplates: TemplateEngineOptions | TemplateCollection = {} + ) { + // Backward compatibility: support old signature (TemplateCollection) and new signature (TemplateEngineOptions) + const isOptions = + 'customTemplates' in optionsOrTemplates || + 'preservePlaceholders' in optionsOrTemplates; + const options: TemplateEngineOptions = isOptions + ? (optionsOrTemplates as TemplateEngineOptions) + : { customTemplates: optionsOrTemplates as TemplateCollection }; + + this.templates = { + ...DEFAULT_TEMPLATES, + ...(options.customTemplates || {}) + }; + this.preservePlaceholders = options.preservePlaceholders ?? false; + } + + /** + * Render a template with provided variables + */ + render( + templateName: string, + variables: TemplateVariables, + inlineTemplate?: string + ): string { + const template = + inlineTemplate !== undefined + ? inlineTemplate + : this.templates[templateName]; + + if (template === undefined) { + throw new Error(`Template "${templateName}" not found`); + } + + return this.substituteVariables(template, variables); + } + + /** + * Set or update a template + */ + setTemplate(name: string, template: string): void { + this.templates[name] = template; + } + + /** + * Get a template by name + */ + getTemplate(name: string): string | undefined { + return this.templates[name]; + } + + /** + * Check if a template exists + */ + hasTemplate(name: string): boolean { + return name in this.templates; + } + + /** + * Validate that a template contains all required variables + */ + validateTemplate( + template: string, + requiredVars: string[] + ): TemplateValidationResult { + const templateVars = this.extractVariables(template); + const missingVars = requiredVars.filter( + (varName) => !templateVars.includes(varName) + ); + + return { + isValid: missingVars.length === 0, + missingVars + }; + } + + /** + * Extract all variable names from a template + */ + extractVariables(template: string): string[] { + const regex = /\{\{\s*([^}#/\s]+)\s*\}\}/g; + const matches = template.matchAll(regex); + const variables = new Set<string>(); + + for (const match of matches) { + variables.add(match[1]); + } + + return Array.from(variables); + } + + /** + * Substitute variables in template + * Supports both {{variable}} and {{#variable}}...{{/variable}} (conditional blocks) + */ + private substituteVariables( + template: string, + variables: TemplateVariables + ): string { + let result = template; + + // Handle conditional blocks first ({{#var}}...{{/var}}) + result = this.processConditionalBlocks(result, variables); + + // Handle simple variable substitution ({{var}}) + result = result.replace(/\{\{\s*([^}#/\s]+)\s*\}\}/g, (_, varName) => { + const value = variables[varName]; + return value !== undefined && value !== null + ? String(value) + : this.preservePlaceholders + ? `{{${varName}}}` + : ''; + }); + + return result; + } + + /** + * Process conditional blocks in template + * {{#variable}}content{{/variable}} - shows content only if variable is truthy + * Processes innermost blocks first to handle nesting + */ + private processConditionalBlocks( + template: string, + variables: TemplateVariables + ): string { + let result = template; + let hasChanges = true; + + // Keep processing until no more conditional blocks are found + while (hasChanges) { + const before = result; + + // Find and replace innermost conditional blocks (non-greedy match) + result = result.replace( + /\{\{#([^}]+)\}\}((?:(?!\{\{#).)*?)\{\{\/\1\}\}/gs, + (_, varName, content) => { + const value = variables[varName.trim()]; + + // Show content if variable is truthy (not undefined, null, false, or empty string) + if ( + value !== undefined && + value !== null && + value !== false && + value !== '' + ) { + return content; + } + + return ''; + } + ); + + hasChanges = result !== before; + } + + return result; + } +} diff --git a/packages/tm-core/src/index.ts b/packages/tm-core/src/index.ts index 0f96f694..6f81ef51 100644 --- a/packages/tm-core/src/index.ts +++ b/packages/tm-core/src/index.ts @@ -72,3 +72,46 @@ export { type ComplexityAnalysis, type TaskComplexityData } from './reports/index.js'; + +// Re-export services +export { + PreflightChecker, + TaskLoaderService, + type CheckResult, + type PreflightResult, + type TaskValidationResult, + type ValidationErrorType, + type DependencyIssue +} from './services/index.js'; + +// Re-export Git adapter +export { GitAdapter } from './git/git-adapter.js'; +export { + CommitMessageGenerator, + type CommitMessageOptions +} from './git/commit-message-generator.js'; + +// Re-export workflow orchestrator, state manager, activity logger, and types +export { WorkflowOrchestrator } from './workflow/workflow-orchestrator.js'; +export { WorkflowStateManager } from './workflow/workflow-state-manager.js'; +export { WorkflowActivityLogger } from './workflow/workflow-activity-logger.js'; +export type { + WorkflowPhase, + TDDPhase, + WorkflowContext, + WorkflowState, + WorkflowEvent, + WorkflowEventData, + WorkflowEventListener, + SubtaskInfo, + TestResult, + WorkflowError +} from './workflow/types.js'; + +// Re-export workflow service +export { WorkflowService } from './services/workflow.service.js'; +export type { + StartWorkflowOptions, + WorkflowStatus, + NextAction +} from './services/workflow.service.js'; diff --git a/packages/tm-core/src/interfaces/configuration.interface.ts b/packages/tm-core/src/interfaces/configuration.interface.ts index 6eaf709f..17dd8988 100644 --- a/packages/tm-core/src/interfaces/configuration.interface.ts +++ b/packages/tm-core/src/interfaces/configuration.interface.ts @@ -9,6 +9,17 @@ import type { StorageType } from '../types/index.js'; +/** + * Conventional Commit types allowed in workflow + */ +export type CommitType = + | 'feat' + | 'fix' + | 'refactor' + | 'test' + | 'docs' + | 'chore'; + /** * Model configuration for different AI roles */ @@ -45,9 +56,15 @@ export interface TaskSettings { defaultPriority: TaskPriority; /** Default complexity for analysis */ defaultComplexity: TaskComplexity; - /** Maximum number of subtasks per task */ + /** + * Maximum number of subtasks per task + * @minimum 1 + */ maxSubtasks: number; - /** Maximum number of concurrent tasks */ + /** + * Maximum number of concurrent tasks + * @minimum 1 + */ maxConcurrentTasks: number; /** Enable automatic task ID generation */ autoGenerateIds: boolean; @@ -69,7 +86,10 @@ export interface TagSettings { enableTags: boolean; /** Default tag for new tasks */ defaultTag: string; - /** Maximum number of tags per task */ + /** + * Maximum number of tags per task + * @minimum 1 + */ maxTagsPerTask: number; /** Enable automatic tag creation from Git branches */ autoCreateFromBranch: boolean; @@ -114,7 +134,10 @@ export interface StorageSettings readonly apiConfigured?: boolean; /** Enable automatic backups */ enableBackup: boolean; - /** Maximum number of backups to retain */ + /** + * Maximum number of backups to retain + * @minimum 0 + */ maxBackups: number; /** Enable compression for storage */ enableCompression: boolean; @@ -128,15 +151,30 @@ export interface StorageSettings * Retry and resilience settings */ export interface RetrySettings { - /** Number of retry attempts for failed operations */ + /** + * Number of retry attempts for failed operations + * @minimum 0 + */ retryAttempts: number; - /** Base delay between retries in milliseconds */ + /** + * Base delay between retries in milliseconds + * @minimum 0 + */ retryDelay: number; - /** Maximum delay between retries in milliseconds */ + /** + * Maximum delay between retries in milliseconds + * @minimum 0 + */ maxRetryDelay: number; - /** Exponential backoff multiplier */ + /** + * Exponential backoff multiplier + * @minimum 1 + */ backoffMultiplier: number; - /** Request timeout in milliseconds */ + /** + * Request timeout in milliseconds + * @minimum 0 + */ requestTimeout: number; /** Enable retry for network errors */ retryOnNetworkError: boolean; @@ -160,9 +198,15 @@ export interface LoggingSettings { logPerformance: boolean; /** Enable error stack traces */ logStackTraces: boolean; - /** Maximum log file size in MB */ + /** + * Maximum log file size in MB + * @minimum 1 + */ maxFileSize: number; - /** Maximum number of log files to retain */ + /** + * Maximum number of log files to retain + * @minimum 1 + */ maxFiles: number; } @@ -174,11 +218,17 @@ export interface SecuritySettings { validateApiKeys: boolean; /** Enable request rate limiting */ enableRateLimit: boolean; - /** Maximum requests per minute */ + /** + * Maximum requests per minute + * @minimum 1 + */ maxRequestsPerMinute: number; /** Enable input sanitization */ sanitizeInputs: boolean; - /** Maximum prompt length in characters */ + /** + * Maximum prompt length in characters + * @minimum 1 + */ maxPromptLength: number; /** Allowed file extensions for imports */ allowedFileExtensions: string[]; @@ -186,6 +236,72 @@ export interface SecuritySettings { enableCors: boolean; } +/** + * Workflow and autopilot TDD settings + */ +export interface WorkflowSettings { + /** Enable autopilot/TDD workflow features */ + enableAutopilot: boolean; + /** + * Maximum retry attempts for phase validation + * @minimum 1 + * @maximum 10 + */ + maxPhaseAttempts: number; + /** Branch naming pattern for workflow branches */ + branchPattern: string; + /** Require clean working tree before starting workflow */ + requireCleanWorkingTree: boolean; + /** Automatically stage all changes during commit phase */ + autoStageChanges: boolean; + /** Include co-author attribution in commits */ + includeCoAuthor: boolean; + /** Co-author name for commit messages */ + coAuthorName: string; + /** Co-author email for commit messages (defaults to taskmaster@tryhamster.com) */ + coAuthorEmail: string; + /** Test result thresholds for phase validation */ + testThresholds: { + /** + * Minimum test count for valid RED phase + * @minimum 0 + */ + minTests: number; + /** + * Maximum allowed failing tests in GREEN phase + * @minimum 0 + */ + maxFailuresInGreen: number; + }; + /** Commit message template pattern */ + commitMessageTemplate: string; + /** Conventional commit types allowed */ + allowedCommitTypes: readonly CommitType[]; + /** + * Default commit type for autopilot + * @validation Must be present in allowedCommitTypes array + */ + defaultCommitType: CommitType; + /** + * Timeout for workflow operations in milliseconds + * @minimum 0 + */ + operationTimeout: number; + /** Enable activity logging for workflow events */ + enableActivityLogging: boolean; + /** Path to store workflow activity logs */ + activityLogPath: string; + /** Enable automatic backup of workflow state */ + enableStateBackup: boolean; + /** + * Maximum workflow state backups to retain + * @minimum 0 + */ + maxStateBackups: number; + /** Abort workflow if validation fails after max attempts */ + abortOnMaxAttempts: boolean; +} + /** * Main configuration interface for Task Master core */ @@ -211,6 +327,9 @@ export interface IConfiguration { /** Tag and context settings */ tags: TagSettings; + /** Workflow and autopilot settings */ + workflow: WorkflowSettings; + /** Storage configuration */ storage: StorageSettings; @@ -414,6 +533,35 @@ export const DEFAULT_CONFIG_VALUES = { MAX_TAGS_PER_TASK: 10, NAMING_CONVENTION: 'kebab-case' as const }, + WORKFLOW: { + ENABLE_AUTOPILOT: true, + MAX_PHASE_ATTEMPTS: 3, + BRANCH_PATTERN: 'task-{taskId}', + REQUIRE_CLEAN_WORKING_TREE: true, + AUTO_STAGE_CHANGES: true, + INCLUDE_CO_AUTHOR: true, + CO_AUTHOR_NAME: 'TaskMaster AI', + CO_AUTHOR_EMAIL: 'taskmaster@tryhamster.com', + MIN_TESTS: 1, + MAX_FAILURES_IN_GREEN: 0, + COMMIT_MESSAGE_TEMPLATE: + '{type}({scope}): {description} (Task {taskId}.{subtaskIndex})', + ALLOWED_COMMIT_TYPES: [ + 'feat', + 'fix', + 'refactor', + 'test', + 'docs', + 'chore' + ] as const satisfies readonly CommitType[], + DEFAULT_COMMIT_TYPE: 'feat' as CommitType, + OPERATION_TIMEOUT: 60000, + ENABLE_ACTIVITY_LOGGING: true, + ACTIVITY_LOG_PATH: '.taskmaster/logs/workflow-activity.log', + ENABLE_STATE_BACKUP: true, + MAX_STATE_BACKUPS: 5, + ABORT_ON_MAX_ATTEMPTS: false + }, STORAGE: { TYPE: 'auto' as const, ENCODING: 'utf8' as BufferEncoding, diff --git a/packages/tm-core/src/services/index.ts b/packages/tm-core/src/services/index.ts index 004c472b..92ac4099 100644 --- a/packages/tm-core/src/services/index.ts +++ b/packages/tm-core/src/services/index.ts @@ -6,8 +6,28 @@ export { TaskService } from './task-service.js'; export { OrganizationService } from './organization.service.js'; export { ExportService } from './export.service.js'; +export { PreflightChecker } from './preflight-checker.service.js'; +export { TaskLoaderService } from './task-loader.service.js'; +export { TestResultValidator } from './test-result-validator.js'; export type { Organization, Brief } from './organization.service.js'; export type { ExportTasksOptions, ExportResult } from './export.service.js'; +export type { + CheckResult, + PreflightResult +} from './preflight-checker.service.js'; +export type { + TaskValidationResult, + ValidationErrorType, + DependencyIssue +} from './task-loader.service.js'; +export type { + TestResult, + TestPhase, + Coverage, + CoverageThresholds, + ValidationResult, + PhaseValidationOptions +} from './test-result-validator.types.js'; diff --git a/packages/tm-core/src/services/preflight-checker.service.ts b/packages/tm-core/src/services/preflight-checker.service.ts new file mode 100644 index 00000000..abb8870b --- /dev/null +++ b/packages/tm-core/src/services/preflight-checker.service.ts @@ -0,0 +1,395 @@ +/** + * @fileoverview Preflight Checker Service + * Validates environment and prerequisites for autopilot execution + */ + +import { readFileSync, existsSync, readdirSync } from 'fs'; +import { join } from 'path'; +import { execSync } from 'child_process'; +import { getLogger } from '../logger/factory.js'; +import { + isGitRepository, + isGhCliAvailable, + getDefaultBranch +} from '../utils/git-utils.js'; + +const logger = getLogger('PreflightChecker'); + +/** + * Result of a single preflight check + */ +export interface CheckResult { + /** Whether the check passed */ + success: boolean; + /** The value detected/validated */ + value?: any; + /** Error or warning message */ + message?: string; +} + +/** + * Complete preflight validation results + */ +export interface PreflightResult { + /** Overall success - all checks passed */ + success: boolean; + /** Test command detection result */ + testCommand: CheckResult; + /** Git working tree status */ + gitWorkingTree: CheckResult; + /** Required tools availability */ + requiredTools: CheckResult; + /** Default branch detection */ + defaultBranch: CheckResult; + /** Summary message */ + summary: string; +} + +/** + * Tool validation result + */ +interface ToolCheck { + name: string; + available: boolean; + version?: string; + message?: string; +} + +/** + * PreflightChecker validates environment for autopilot execution + */ +export class PreflightChecker { + private projectRoot: string; + + constructor(projectRoot: string) { + if (!projectRoot) { + throw new Error('projectRoot is required for PreflightChecker'); + } + this.projectRoot = projectRoot; + } + + /** + * Detect test command from package.json + */ + async detectTestCommand(): Promise<CheckResult> { + try { + const packageJsonPath = join(this.projectRoot, 'package.json'); + const packageJsonContent = readFileSync(packageJsonPath, 'utf-8'); + const packageJson = JSON.parse(packageJsonContent); + + if (!packageJson.scripts || !packageJson.scripts.test) { + return { + success: false, + message: + 'No test script found in package.json. Please add a "test" script.' + }; + } + + const testCommand = packageJson.scripts.test; + + return { + success: true, + value: testCommand, + message: `Test command: ${testCommand}` + }; + } catch (error: any) { + if (error.code === 'ENOENT') { + return { + success: false, + message: 'package.json not found in project root' + }; + } + + return { + success: false, + message: `Failed to read package.json: ${error.message}` + }; + } + } + + /** + * Check git working tree status + */ + async checkGitWorkingTree(): Promise<CheckResult> { + try { + // Check if it's a git repository + const isRepo = await isGitRepository(this.projectRoot); + if (!isRepo) { + return { + success: false, + message: 'Not a git repository. Initialize git first.' + }; + } + + // Check for changes (staged/unstaged/untracked) without requiring HEAD + const status = execSync('git status --porcelain', { + cwd: this.projectRoot, + encoding: 'utf-8', + timeout: 5000 + }); + if (status.trim().length > 0) { + return { + success: false, + value: 'dirty', + message: + 'Working tree has uncommitted or untracked changes. Please commit or stash them.' + }; + } + return { + success: true, + value: 'clean', + message: 'Working tree is clean' + }; + } catch (error: any) { + return { + success: false, + message: `Git check failed: ${error.message}` + }; + } + } + + /** + * Detect project types based on common configuration files + */ + private detectProjectTypes(): string[] { + const types: string[] = []; + + if (existsSync(join(this.projectRoot, 'package.json'))) types.push('node'); + if ( + existsSync(join(this.projectRoot, 'requirements.txt')) || + existsSync(join(this.projectRoot, 'setup.py')) || + existsSync(join(this.projectRoot, 'pyproject.toml')) + ) + types.push('python'); + if ( + existsSync(join(this.projectRoot, 'pom.xml')) || + existsSync(join(this.projectRoot, 'build.gradle')) + ) + types.push('java'); + if (existsSync(join(this.projectRoot, 'go.mod'))) types.push('go'); + if (existsSync(join(this.projectRoot, 'Cargo.toml'))) types.push('rust'); + if (existsSync(join(this.projectRoot, 'composer.json'))) types.push('php'); + if (existsSync(join(this.projectRoot, 'Gemfile'))) types.push('ruby'); + const files = readdirSync(this.projectRoot); + if (files.some((f) => f.endsWith('.csproj') || f.endsWith('.sln'))) + types.push('dotnet'); + + return types; + } + + /** + * Get required tools for a project type + */ + private getToolsForProjectType( + type: string + ): Array<{ command: string; args: string[] }> { + const toolMap: Record< + string, + Array<{ command: string; args: string[] }> + > = { + node: [ + { command: 'node', args: ['--version'] }, + { command: 'npm', args: ['--version'] } + ], + python: [ + { command: 'python3', args: ['--version'] }, + { command: 'pip3', args: ['--version'] } + ], + java: [{ command: 'java', args: ['--version'] }], + go: [{ command: 'go', args: ['version'] }], + rust: [{ command: 'cargo', args: ['--version'] }], + php: [ + { command: 'php', args: ['--version'] }, + { command: 'composer', args: ['--version'] } + ], + ruby: [ + { command: 'ruby', args: ['--version'] }, + { command: 'bundle', args: ['--version'] } + ], + dotnet: [{ command: 'dotnet', args: ['--version'] }] + }; + + return toolMap[type] || []; + } + + /** + * Validate required tools availability + */ + async validateRequiredTools(): Promise<CheckResult> { + const tools: ToolCheck[] = []; + + // Always check git and gh CLI + tools.push(this.checkTool('git', ['--version'])); + tools.push(await this.checkGhCli()); + + // Detect project types and check their tools + const projectTypes = this.detectProjectTypes(); + + if (projectTypes.length === 0) { + logger.warn('No recognized project type detected'); + } else { + logger.info(`Detected project types: ${projectTypes.join(', ')}`); + } + + for (const type of projectTypes) { + const typeTools = this.getToolsForProjectType(type); + for (const tool of typeTools) { + tools.push(this.checkTool(tool.command, tool.args)); + } + } + + // Determine overall success + const allAvailable = tools.every((tool) => tool.available); + const missingTools = tools + .filter((tool) => !tool.available) + .map((tool) => tool.name); + + if (!allAvailable) { + return { + success: false, + value: tools, + message: `Missing required tools: ${missingTools.join(', ')}` + }; + } + + return { + success: true, + value: tools, + message: 'All required tools are available' + }; + } + + /** + * Check if a command-line tool is available + */ + private checkTool(command: string, versionArgs: string[]): ToolCheck { + try { + const version = execSync(`${command} ${versionArgs.join(' ')}`, { + cwd: this.projectRoot, + encoding: 'utf-8', + stdio: 'pipe', + timeout: 5000 + }) + .trim() + .split('\n')[0]; + + return { + name: command, + available: true, + version, + message: `${command} ${version}` + }; + } catch (error) { + return { + name: command, + available: false, + message: `${command} not found` + }; + } + } + + /** + * Check GitHub CLI installation and authentication status + */ + private async checkGhCli(): Promise<ToolCheck> { + try { + const version = execSync('gh --version', { + cwd: this.projectRoot, + encoding: 'utf-8', + stdio: 'pipe', + timeout: 5000 + }) + .trim() + .split('\n')[0]; + const authed = await isGhCliAvailable(this.projectRoot); + return { + name: 'gh', + available: true, + version, + message: authed + ? 'GitHub CLI installed (authenticated)' + : 'GitHub CLI installed (not authenticated)' + }; + } catch { + return { name: 'gh', available: false, message: 'GitHub CLI not found' }; + } + } + + /** + * Detect default branch + */ + async detectDefaultBranch(): Promise<CheckResult> { + try { + const defaultBranch = await getDefaultBranch(this.projectRoot); + + if (!defaultBranch) { + return { + success: false, + message: + 'Could not determine default branch. Make sure remote is configured.' + }; + } + + return { + success: true, + value: defaultBranch, + message: `Default branch: ${defaultBranch}` + }; + } catch (error: any) { + return { + success: false, + message: `Failed to detect default branch: ${error.message}` + }; + } + } + + /** + * Run all preflight checks + */ + async runAllChecks(): Promise<PreflightResult> { + logger.info('Running preflight checks...'); + + const testCommand = await this.detectTestCommand(); + const gitWorkingTree = await this.checkGitWorkingTree(); + const requiredTools = await this.validateRequiredTools(); + const defaultBranch = await this.detectDefaultBranch(); + + const allSuccess = + testCommand.success && + gitWorkingTree.success && + requiredTools.success && + defaultBranch.success; + + // Build summary + const passed: string[] = []; + const failed: string[] = []; + + if (testCommand.success) passed.push('Test command'); + else failed.push('Test command'); + + if (gitWorkingTree.success) passed.push('Git working tree'); + else failed.push('Git working tree'); + + if (requiredTools.success) passed.push('Required tools'); + else failed.push('Required tools'); + + if (defaultBranch.success) passed.push('Default branch'); + else failed.push('Default branch'); + + const total = passed.length + failed.length; + const summary = allSuccess + ? `All preflight checks passed (${passed.length}/${total})` + : `Preflight checks failed: ${failed.join(', ')} (${passed.length}/${total} passed)`; + + logger.info(summary); + + return { + success: allSuccess, + testCommand, + gitWorkingTree, + requiredTools, + defaultBranch, + summary + }; + } +} diff --git a/packages/tm-core/src/services/task-loader.service.ts b/packages/tm-core/src/services/task-loader.service.ts new file mode 100644 index 00000000..4c98a34b --- /dev/null +++ b/packages/tm-core/src/services/task-loader.service.ts @@ -0,0 +1,401 @@ +/** + * @fileoverview Task Loader Service + * Loads and validates tasks for autopilot execution + */ + +import type { Task, Subtask, TaskStatus } from '../types/index.js'; +import { TaskService } from './task-service.js'; +import { ConfigManager } from '../config/config-manager.js'; +import { getLogger } from '../logger/factory.js'; + +const logger = getLogger('TaskLoader'); + +/** + * Validation error types + */ +export type ValidationErrorType = + | 'task_not_found' + | 'task_completed' + | 'no_subtasks' + | 'circular_dependencies' + | 'missing_dependencies' + | 'invalid_structure'; + +/** + * Validation result for task loading + */ +export interface TaskValidationResult { + /** Whether validation passed */ + success: boolean; + /** Loaded task (only present if validation succeeded) */ + task?: Task; + /** Error type */ + errorType?: ValidationErrorType; + /** Human-readable error message */ + errorMessage?: string; + /** Actionable suggestion for fixing the error */ + suggestion?: string; + /** Dependency analysis (only for dependency errors) */ + dependencyIssues?: DependencyIssue[]; +} + +/** + * Dependency issue details + */ +export interface DependencyIssue { + /** Subtask ID with the issue */ + subtaskId: string; + /** Type of dependency issue */ + issueType: 'circular' | 'missing' | 'invalid'; + /** Description of the issue */ + message: string; + /** The problematic dependency reference */ + dependencyRef?: string; +} + +/** + * TaskLoaderService loads and validates tasks for autopilot execution + */ +export class TaskLoaderService { + private taskService: TaskService | null = null; + private projectRoot: string; + + constructor(projectRoot: string) { + if (!projectRoot) { + throw new Error('projectRoot is required for TaskLoaderService'); + } + this.projectRoot = projectRoot; + } + + /** + * Ensure TaskService is initialized + */ + private async ensureInitialized(): Promise<void> { + if (this.taskService) return; + + const configManager = await ConfigManager.create(this.projectRoot); + this.taskService = new TaskService(configManager); + await this.taskService.initialize(); + } + + /** + * Load and validate a task for autopilot execution + */ + async loadAndValidateTask(taskId: string): Promise<TaskValidationResult> { + logger.info(`Loading task ${taskId}...`); + + // Step 1: Load task + const task = await this.loadTask(taskId); + if (!task) { + return { + success: false, + errorType: 'task_not_found', + errorMessage: `Task with ID "${taskId}" not found`, + suggestion: + 'Use "task-master list" to see available tasks or verify the task ID is correct.' + }; + } + + // Step 2: Validate task status + const statusValidation = this.validateTaskStatus(task); + if (!statusValidation.success) { + return statusValidation; + } + + // Step 3: Check for subtasks + const subtaskValidation = this.validateSubtasksExist(task); + if (!subtaskValidation.success) { + return subtaskValidation; + } + + // Step 4: Validate subtask structure + const structureValidation = this.validateSubtaskStructure(task); + if (!structureValidation.success) { + return structureValidation; + } + + // Step 5: Analyze dependencies + const dependencyValidation = this.validateDependencies(task); + if (!dependencyValidation.success) { + return dependencyValidation; + } + + logger.info(`Task ${taskId} validated successfully`); + + return { + success: true, + task + }; + } + + /** + * Load task using TaskService + */ + private async loadTask(taskId: string): Promise<Task | null> { + try { + await this.ensureInitialized(); + if (!this.taskService) { + throw new Error('TaskService initialization failed'); + } + return await this.taskService.getTask(taskId); + } catch (error) { + logger.error(`Failed to load task ${taskId}:`, error); + return null; + } + } + + /** + * Validate task status is appropriate for autopilot + */ + private validateTaskStatus(task: Task): TaskValidationResult { + const completedStatuses: TaskStatus[] = ['done', 'completed', 'cancelled']; + + if (completedStatuses.includes(task.status)) { + return { + success: false, + errorType: 'task_completed', + errorMessage: `Task "${task.title}" is already ${task.status}`, + suggestion: + 'Autopilot can only execute tasks that are pending or in-progress. Use a different task.' + }; + } + + return { success: true }; + } + + /** + * Validate task has subtasks + */ + private validateSubtasksExist(task: Task): TaskValidationResult { + if (!task.subtasks || task.subtasks.length === 0) { + return { + success: false, + errorType: 'no_subtasks', + errorMessage: `Task "${task.title}" has no subtasks`, + suggestion: this.buildExpansionSuggestion(task) + }; + } + + return { success: true }; + } + + /** + * Build helpful suggestion for expanding tasks + */ + private buildExpansionSuggestion(task: Task): string { + const suggestions: string[] = [ + `Autopilot requires tasks to be broken down into subtasks for execution.` + ]; + + // Add expansion command suggestion + suggestions.push(`\nExpand this task using:`); + suggestions.push(` task-master expand --id=${task.id}`); + + // If task has complexity analysis, mention it + if (task.complexity || task.recommendedSubtasks) { + suggestions.push( + `\nThis task has complexity analysis available. Consider reviewing it first:` + ); + suggestions.push(` task-master show ${task.id}`); + } else { + suggestions.push( + `\nOr analyze task complexity first to determine optimal subtask count:` + ); + suggestions.push(` task-master analyze-complexity --from=${task.id}`); + } + + return suggestions.join('\n'); + } + + /** + * Validate subtask structure + */ + private validateSubtaskStructure(task: Task): TaskValidationResult { + for (const subtask of task.subtasks) { + // Check required fields + if (!subtask.title || !subtask.description) { + return { + success: false, + errorType: 'invalid_structure', + errorMessage: `Subtask ${task.id}.${subtask.id} is missing required fields`, + suggestion: + 'Subtasks must have title and description. Re-expand the task or manually fix the subtask structure.' + }; + } + + // Validate dependencies are arrays + if (subtask.dependencies && !Array.isArray(subtask.dependencies)) { + return { + success: false, + errorType: 'invalid_structure', + errorMessage: `Subtask ${task.id}.${subtask.id} has invalid dependencies format`, + suggestion: + 'Dependencies must be an array. Fix the task structure manually.' + }; + } + } + + return { success: true }; + } + + /** + * Validate subtask dependencies + */ + private validateDependencies(task: Task): TaskValidationResult { + const issues: DependencyIssue[] = []; + const subtaskIds = new Set(task.subtasks.map((st) => String(st.id))); + + for (const subtask of task.subtasks) { + const subtaskId = `${task.id}.${subtask.id}`; + + // Check for missing dependencies + if (subtask.dependencies && subtask.dependencies.length > 0) { + for (const depId of subtask.dependencies) { + const depIdStr = String(depId); + + if (!subtaskIds.has(depIdStr)) { + issues.push({ + subtaskId, + issueType: 'missing', + message: `References non-existent subtask ${depIdStr}`, + dependencyRef: depIdStr + }); + } + } + } + + // Check for circular dependencies + const circularCheck = this.detectCircularDependency( + subtask, + task.subtasks, + new Set() + ); + + if (circularCheck) { + issues.push({ + subtaskId, + issueType: 'circular', + message: `Circular dependency detected: ${circularCheck.join(' -> ')}` + }); + } + } + + if (issues.length > 0) { + const errorType = + issues[0].issueType === 'circular' + ? 'circular_dependencies' + : 'missing_dependencies'; + + return { + success: false, + errorType, + errorMessage: `Task "${task.title}" has dependency issues`, + suggestion: + 'Fix dependency issues manually or re-expand the task:\n' + + issues + .map((issue) => ` - ${issue.subtaskId}: ${issue.message}`) + .join('\n'), + dependencyIssues: issues + }; + } + + return { success: true }; + } + + /** + * Detect circular dependencies using depth-first search + */ + private detectCircularDependency( + subtask: Subtask, + allSubtasks: Subtask[], + visited: Set<string> + ): string[] | null { + const subtaskId = String(subtask.id); + + if (visited.has(subtaskId)) { + return [subtaskId]; + } + + visited.add(subtaskId); + + if (subtask.dependencies && subtask.dependencies.length > 0) { + for (const depId of subtask.dependencies) { + const depIdStr = String(depId); + const dependency = allSubtasks.find((st) => String(st.id) === depIdStr); + + if (dependency) { + const circular = this.detectCircularDependency( + dependency, + allSubtasks, + new Set(visited) + ); + + if (circular) { + return [subtaskId, ...circular]; + } + } + } + } + + return null; + } + + /** + * Get ordered subtask execution sequence + * Returns subtasks in dependency order (tasks with no deps first) + */ + getExecutionOrder(task: Task): Subtask[] { + const ordered: Subtask[] = []; + const completed = new Set<string>(); + + // Keep adding subtasks whose dependencies are all completed + while (ordered.length < task.subtasks.length) { + let added = false; + + for (const subtask of task.subtasks) { + const subtaskId = String(subtask.id); + + if (completed.has(subtaskId)) { + continue; + } + + // Check if all dependencies are completed + const allDepsCompleted = + !subtask.dependencies || + subtask.dependencies.length === 0 || + subtask.dependencies.every((depId) => completed.has(String(depId))); + + if (allDepsCompleted) { + ordered.push(subtask); + completed.add(subtaskId); + added = true; + break; + } + } + + // Safety check to prevent infinite loop + if (!added && ordered.length < task.subtasks.length) { + logger.warn( + `Could not determine complete execution order for task ${task.id}` + ); + // Add remaining subtasks in original order + for (const subtask of task.subtasks) { + if (!completed.has(String(subtask.id))) { + ordered.push(subtask); + } + } + break; + } + } + + return ordered; + } + + /** + * Clean up resources + */ + async cleanup(): Promise<void> { + // TaskService doesn't require explicit cleanup + // Resources are automatically released when instance is garbage collected + } +} diff --git a/packages/tm-core/src/services/test-result-validator.test.ts b/packages/tm-core/src/services/test-result-validator.test.ts new file mode 100644 index 00000000..1667d3ac --- /dev/null +++ b/packages/tm-core/src/services/test-result-validator.test.ts @@ -0,0 +1,456 @@ +import { describe, it, expect } from 'vitest'; +import { TestResultValidator } from './test-result-validator.js'; +import type { + TestResult, + ValidationResult, + TestPhase +} from './test-result-validator.types.js'; + +describe('TestResultValidator - Input Validation', () => { + const validator = new TestResultValidator(); + + describe('Schema Validation', () => { + it('should validate a valid test result', () => { + const validResult: TestResult = { + total: 10, + passed: 5, + failed: 5, + skipped: 0, + phase: 'RED' + }; + + const result = validator.validate(validResult); + expect(result.valid).toBe(true); + expect(result.errors).toEqual([]); + }); + + it('should reject negative test counts', () => { + const invalidResult = { + total: -1, + passed: 0, + failed: 0, + skipped: 0, + phase: 'RED' + }; + + const result = validator.validate(invalidResult as TestResult); + expect(result.valid).toBe(false); + expect(result.errors.length).toBeGreaterThan(0); + }); + + it('should reject when totals do not match', () => { + const invalidResult: TestResult = { + total: 10, + passed: 3, + failed: 3, + skipped: 3, // 3 + 3 + 3 = 9, not 10 + phase: 'RED' + }; + + const result = validator.validate(invalidResult); + expect(result.valid).toBe(false); + expect(result.errors).toContain( + 'Total tests must equal passed + failed + skipped' + ); + }); + + it('should reject missing required fields', () => { + const invalidResult = { + total: 10, + passed: 5 + // missing failed, skipped, phase + }; + + const result = validator.validate(invalidResult as TestResult); + expect(result.valid).toBe(false); + expect(result.errors.length).toBeGreaterThan(0); + }); + + it('should accept optional coverage data', () => { + const resultWithCoverage: TestResult = { + total: 10, + passed: 10, + failed: 0, + skipped: 0, + phase: 'GREEN', + coverage: { + line: 85, + branch: 75, + function: 90, + statement: 85 + } + }; + + const result = validator.validate(resultWithCoverage); + expect(result.valid).toBe(true); + }); + + it('should reject invalid coverage percentages', () => { + const invalidResult: TestResult = { + total: 10, + passed: 10, + failed: 0, + skipped: 0, + phase: 'GREEN', + coverage: { + line: 150, // Invalid: > 100 + branch: -10, // Invalid: < 0 + function: 90, + statement: 85 + } + }; + + const result = validator.validate(invalidResult); + expect(result.valid).toBe(false); + expect(result.errors.length).toBeGreaterThan(0); + }); + + it('should reject invalid phase values', () => { + const invalidResult = { + total: 10, + passed: 5, + failed: 5, + skipped: 0, + phase: 'INVALID_PHASE' + }; + + const result = validator.validate(invalidResult as TestResult); + expect(result.valid).toBe(false); + expect(result.errors.length).toBeGreaterThan(0); + }); + }); +}); + +describe('TestResultValidator - RED Phase Validation', () => { + const validator = new TestResultValidator(); + + it('should pass validation when RED phase has failures', () => { + const redResult: TestResult = { + total: 10, + passed: 5, + failed: 5, + skipped: 0, + phase: 'RED' + }; + + const result = validator.validateRedPhase(redResult); + expect(result.valid).toBe(true); + expect(result.errors).toEqual([]); + }); + + it('should fail validation when RED phase has zero failures', () => { + const redResult: TestResult = { + total: 10, + passed: 10, + failed: 0, + skipped: 0, + phase: 'RED' + }; + + const result = validator.validateRedPhase(redResult); + expect(result.valid).toBe(false); + expect(result.errors).toContain( + 'RED phase must have at least one failing test' + ); + expect(result.suggestions).toContain( + 'Write failing tests first to follow TDD workflow' + ); + }); + + it('should fail validation when RED phase has empty test suite', () => { + const emptyResult: TestResult = { + total: 0, + passed: 0, + failed: 0, + skipped: 0, + phase: 'RED' + }; + + const result = validator.validateRedPhase(emptyResult); + expect(result.valid).toBe(false); + expect(result.errors).toContain('Cannot validate empty test suite'); + expect(result.suggestions).toContain( + 'Add at least one test to begin TDD cycle' + ); + }); + + it('should propagate base validation errors', () => { + const invalidResult: TestResult = { + total: 10, + passed: 3, + failed: 3, + skipped: 3, // Total mismatch + phase: 'RED' + }; + + const result = validator.validateRedPhase(invalidResult); + expect(result.valid).toBe(false); + expect(result.errors).toContain( + 'Total tests must equal passed + failed + skipped' + ); + }); +}); + +describe('TestResultValidator - GREEN Phase Validation', () => { + const validator = new TestResultValidator(); + + it('should pass validation when GREEN phase has all tests passing', () => { + const greenResult: TestResult = { + total: 10, + passed: 10, + failed: 0, + skipped: 0, + phase: 'GREEN' + }; + + const result = validator.validateGreenPhase(greenResult); + expect(result.valid).toBe(true); + expect(result.errors).toEqual([]); + }); + + it('should fail validation when GREEN phase has failures', () => { + const greenResult: TestResult = { + total: 10, + passed: 5, + failed: 5, + skipped: 0, + phase: 'GREEN' + }; + + const result = validator.validateGreenPhase(greenResult); + expect(result.valid).toBe(false); + expect(result.errors).toContain('GREEN phase must have zero failures'); + expect(result.suggestions).toContain( + 'Fix implementation to make all tests pass' + ); + }); + + it('should fail validation when GREEN phase has no passing tests', () => { + const greenResult: TestResult = { + total: 5, + passed: 0, + failed: 0, + skipped: 5, + phase: 'GREEN' + }; + + const result = validator.validateGreenPhase(greenResult); + expect(result.valid).toBe(false); + expect(result.errors).toContain( + 'GREEN phase must have at least one passing test' + ); + }); + + it('should warn when test count decreases', () => { + const greenResult: TestResult = { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + }; + + const result = validator.validateGreenPhase(greenResult, 10); + expect(result.valid).toBe(true); + expect(result.warnings).toContain('Test count decreased from 10 to 5'); + expect(result.suggestions).toContain( + 'Verify that no tests were accidentally removed' + ); + }); + + it('should not warn when test count increases', () => { + const greenResult: TestResult = { + total: 15, + passed: 15, + failed: 0, + skipped: 0, + phase: 'GREEN' + }; + + const result = validator.validateGreenPhase(greenResult, 10); + expect(result.valid).toBe(true); + expect(result.warnings || []).toEqual([]); + }); + + it('should propagate base validation errors', () => { + const invalidResult: TestResult = { + total: 10, + passed: 3, + failed: 3, + skipped: 3, // Total mismatch + phase: 'GREEN' + }; + + const result = validator.validateGreenPhase(invalidResult); + expect(result.valid).toBe(false); + expect(result.errors).toContain( + 'Total tests must equal passed + failed + skipped' + ); + }); +}); + +describe('TestResultValidator - Coverage Threshold Validation', () => { + const validator = new TestResultValidator(); + + it('should pass validation when coverage meets thresholds', () => { + const result: TestResult = { + total: 10, + passed: 10, + failed: 0, + skipped: 0, + phase: 'GREEN', + coverage: { + line: 85, + branch: 80, + function: 90, + statement: 85 + } + }; + + const thresholds = { + line: 80, + branch: 75, + function: 85, + statement: 80 + }; + + const validationResult = validator.validateCoverage(result, thresholds); + expect(validationResult.valid).toBe(true); + expect(validationResult.errors).toEqual([]); + }); + + it('should fail validation when line coverage is below threshold', () => { + const result: TestResult = { + total: 10, + passed: 10, + failed: 0, + skipped: 0, + phase: 'GREEN', + coverage: { + line: 70, + branch: 80, + function: 90, + statement: 85 + } + }; + + const thresholds = { + line: 80 + }; + + const validationResult = validator.validateCoverage(result, thresholds); + expect(validationResult.valid).toBe(false); + expect(validationResult.errors[0]).toContain('line coverage (70% < 80%)'); + expect(validationResult.suggestions).toContain( + 'Add more tests to improve code coverage' + ); + }); + + it('should fail validation when multiple coverage types are below threshold', () => { + const result: TestResult = { + total: 10, + passed: 10, + failed: 0, + skipped: 0, + phase: 'GREEN', + coverage: { + line: 70, + branch: 60, + function: 75, + statement: 65 + } + }; + + const thresholds = { + line: 80, + branch: 75, + function: 85, + statement: 80 + }; + + const validationResult = validator.validateCoverage(result, thresholds); + expect(validationResult.valid).toBe(false); + expect(validationResult.errors[0]).toContain('line coverage (70% < 80%)'); + expect(validationResult.errors[0]).toContain('branch coverage (60% < 75%)'); + expect(validationResult.errors[0]).toContain( + 'function coverage (75% < 85%)' + ); + expect(validationResult.errors[0]).toContain( + 'statement coverage (65% < 80%)' + ); + }); + + it('should skip validation when no coverage data is provided', () => { + const result: TestResult = { + total: 10, + passed: 10, + failed: 0, + skipped: 0, + phase: 'GREEN' + }; + + const thresholds = { + line: 80, + branch: 75 + }; + + const validationResult = validator.validateCoverage(result, thresholds); + expect(validationResult.valid).toBe(true); + expect(validationResult.errors).toEqual([]); + }); + + it('should only validate specified threshold types', () => { + const result: TestResult = { + total: 10, + passed: 10, + failed: 0, + skipped: 0, + phase: 'GREEN', + coverage: { + line: 70, + branch: 60, + function: 90, + statement: 85 + } + }; + + const thresholds = { + line: 80 + // Only checking line coverage + }; + + const validationResult = validator.validateCoverage(result, thresholds); + expect(validationResult.valid).toBe(false); + expect(validationResult.errors[0]).toContain('line coverage'); + expect(validationResult.errors[0]).not.toContain('branch coverage'); + }); + + it('should propagate base validation errors', () => { + const invalidResult: TestResult = { + total: 10, + passed: 3, + failed: 3, + skipped: 3, // Total mismatch + phase: 'GREEN', + coverage: { + line: 90, + branch: 90, + function: 90, + statement: 90 + } + }; + + const thresholds = { + line: 80 + }; + + const validationResult = validator.validateCoverage( + invalidResult, + thresholds + ); + expect(validationResult.valid).toBe(false); + expect(validationResult.errors).toContain( + 'Total tests must equal passed + failed + skipped' + ); + }); +}); diff --git a/packages/tm-core/src/services/test-result-validator.ts b/packages/tm-core/src/services/test-result-validator.ts new file mode 100644 index 00000000..0248626e --- /dev/null +++ b/packages/tm-core/src/services/test-result-validator.ts @@ -0,0 +1,268 @@ +import { z } from 'zod'; +import type { + TestResult, + ValidationResult, + CoverageThresholds, + PhaseValidationOptions +} from './test-result-validator.types.js'; + +/** + * Schema for coverage metrics validation + */ +const coverageSchema = z.object({ + line: z.number().min(0).max(100), + branch: z.number().min(0).max(100), + function: z.number().min(0).max(100), + statement: z.number().min(0).max(100) +}); + +/** + * Schema for test result validation + */ +const testResultSchema = z.object({ + total: z.number().int().nonnegative(), + passed: z.number().int().nonnegative(), + failed: z.number().int().nonnegative(), + skipped: z.number().int().nonnegative(), + phase: z.enum(['RED', 'GREEN', 'REFACTOR']), + coverage: coverageSchema.optional() +}); + +/** + * Validates test results according to TDD phase semantics + */ +export class TestResultValidator { + /** + * Validates a test result object + */ + validate(testResult: TestResult): ValidationResult { + const errors: string[] = []; + const warnings: string[] = []; + const suggestions: string[] = []; + + // Schema validation + const parseResult = testResultSchema.safeParse(testResult); + if (!parseResult.success) { + const zodIssues = parseResult.error.issues || []; + errors.push( + ...zodIssues.map((e) => { + const path = e.path.length > 0 ? `${e.path.join('.')}: ` : ''; + return `${path}${e.message}`; + }) + ); + return { valid: false, errors, warnings, suggestions }; + } + + // Total validation + const sum = testResult.passed + testResult.failed + testResult.skipped; + if (sum !== testResult.total) { + errors.push('Total tests must equal passed + failed + skipped'); + } + + // If there are validation errors, return early + if (errors.length > 0) { + return { valid: false, errors, warnings, suggestions }; + } + + return { valid: true, errors, warnings, suggestions }; + } + + /** + * Validates RED phase test results + * RED phase must have at least one failing test + */ + validateRedPhase(testResult: TestResult): ValidationResult { + const baseValidation = this.validate(testResult); + if (!baseValidation.valid) { + return baseValidation; + } + + const errors: string[] = []; + const suggestions: string[] = []; + + // RED phase must have failures + if (testResult.failed === 0) { + errors.push('RED phase must have at least one failing test'); + suggestions.push('Write failing tests first to follow TDD workflow'); + } + + // Must have at least one test + if (testResult.total === 0) { + errors.push('Cannot validate empty test suite'); + suggestions.push('Add at least one test to begin TDD cycle'); + } + + return { + valid: errors.length === 0, + errors, + suggestions + }; + } + + /** + * Validates GREEN phase test results + * GREEN phase must have zero failures + */ + validateGreenPhase( + testResult: TestResult, + previousTestCount?: number + ): ValidationResult { + const baseValidation = this.validate(testResult); + if (!baseValidation.valid) { + return baseValidation; + } + + const errors: string[] = []; + const warnings: string[] = []; + const suggestions: string[] = []; + + // GREEN phase must have zero failures + if (testResult.failed > 0) { + errors.push('GREEN phase must have zero failures'); + suggestions.push('Fix implementation to make all tests pass'); + } + + // Must have at least one passing test + if (testResult.passed === 0) { + errors.push('GREEN phase must have at least one passing test'); + suggestions.push('Ensure tests exist and implementation makes them pass'); + } + + // Check for test count regression + if ( + previousTestCount !== undefined && + testResult.total < previousTestCount + ) { + warnings.push( + `Test count decreased from ${previousTestCount} to ${testResult.total}` + ); + suggestions.push('Verify that no tests were accidentally removed'); + } + + return { + valid: errors.length === 0, + errors, + warnings, + suggestions + }; + } + + /** + * Validates coverage thresholds if provided + */ + validateCoverage( + testResult: TestResult, + thresholds: CoverageThresholds + ): ValidationResult { + const baseValidation = this.validate(testResult); + if (!baseValidation.valid) { + return baseValidation; + } + + const errors: string[] = []; + const suggestions: string[] = []; + + // Skip validation if no coverage data + if (!testResult.coverage) { + return { valid: true, errors: [], suggestions: [] }; + } + + const coverage = testResult.coverage; + const gaps: string[] = []; + + // Check each coverage type against threshold + if (thresholds.line !== undefined && coverage.line < thresholds.line) { + gaps.push(`line coverage (${coverage.line}% < ${thresholds.line}%)`); + } + + if ( + thresholds.branch !== undefined && + coverage.branch < thresholds.branch + ) { + gaps.push( + `branch coverage (${coverage.branch}% < ${thresholds.branch}%)` + ); + } + + if ( + thresholds.function !== undefined && + coverage.function < thresholds.function + ) { + gaps.push( + `function coverage (${coverage.function}% < ${thresholds.function}%)` + ); + } + + if ( + thresholds.statement !== undefined && + coverage.statement < thresholds.statement + ) { + gaps.push( + `statement coverage (${coverage.statement}% < ${thresholds.statement}%)` + ); + } + + if (gaps.length > 0) { + errors.push(`Coverage thresholds not met: ${gaps.join(', ')}`); + suggestions.push('Add more tests to improve code coverage'); + } + + return { + valid: errors.length === 0, + errors, + suggestions + }; + } + + /** + * Validates test results based on TDD phase + */ + validatePhase( + testResult: TestResult, + options?: PhaseValidationOptions + ): ValidationResult { + const phase = options?.phase ?? testResult.phase; + + // Phase-specific validation + let phaseResult: ValidationResult; + if (phase === 'RED') { + phaseResult = this.validateRedPhase(testResult); + } else if (phase === 'GREEN') { + phaseResult = this.validateGreenPhase( + testResult, + options?.previousTestCount + ); + } else { + // REFACTOR phase uses same rules as GREEN + phaseResult = this.validateGreenPhase( + testResult, + options?.previousTestCount + ); + } + + if (!phaseResult.valid) { + return phaseResult; + } + + // Coverage validation if thresholds provided + if (options?.coverageThresholds) { + const coverageResult = this.validateCoverage( + testResult, + options.coverageThresholds + ); + + // Merge results + return { + valid: coverageResult.valid, + errors: [...(phaseResult.errors || []), ...coverageResult.errors], + warnings: phaseResult.warnings, + suggestions: [ + ...(phaseResult.suggestions || []), + ...(coverageResult.suggestions || []) + ] + }; + } + + return phaseResult; + } +} diff --git a/packages/tm-core/src/services/test-result-validator.types.ts b/packages/tm-core/src/services/test-result-validator.types.ts new file mode 100644 index 00000000..b34695db --- /dev/null +++ b/packages/tm-core/src/services/test-result-validator.types.ts @@ -0,0 +1,55 @@ +/** + * Test phase in TDD workflow + */ +export type TestPhase = 'RED' | 'GREEN' | 'REFACTOR'; + +/** + * Code coverage metrics + */ +export interface Coverage { + line: number; + branch: number; + function: number; + statement: number; +} + +/** + * Test result data structure + */ +export interface TestResult { + total: number; + passed: number; + failed: number; + skipped: number; + phase: TestPhase; + coverage?: Coverage; +} + +/** + * Coverage threshold configuration + */ +export interface CoverageThresholds { + line?: number; + branch?: number; + function?: number; + statement?: number; +} + +/** + * Validation result structure + */ +export interface ValidationResult { + valid: boolean; + errors: string[]; + warnings?: string[]; + suggestions?: string[]; +} + +/** + * Phase-specific validation options + */ +export interface PhaseValidationOptions { + phase: TestPhase; + coverageThresholds?: CoverageThresholds; + previousTestCount?: number; +} diff --git a/packages/tm-core/src/services/workflow.service.ts b/packages/tm-core/src/services/workflow.service.ts new file mode 100644 index 00000000..6148273b --- /dev/null +++ b/packages/tm-core/src/services/workflow.service.ts @@ -0,0 +1,494 @@ +/** + * @fileoverview WorkflowService - High-level facade for TDD workflow operations + * Provides a simplified API for MCP tools while delegating to WorkflowOrchestrator + */ + +import { WorkflowOrchestrator } from '../workflow/workflow-orchestrator.js'; +import { WorkflowStateManager } from '../workflow/workflow-state-manager.js'; +import { WorkflowActivityLogger } from '../workflow/workflow-activity-logger.js'; +import type { + WorkflowContext, + SubtaskInfo, + TestResult, + WorkflowPhase, + TDDPhase, + WorkflowState +} from '../workflow/types.js'; +import { GitAdapter } from '../git/git-adapter.js'; + +/** + * Options for starting a new workflow + */ +export interface StartWorkflowOptions { + taskId: string; + taskTitle: string; + subtasks: Array<{ + id: string; + title: string; + status: string; + maxAttempts?: number; + }>; + maxAttempts?: number; + force?: boolean; + tag?: string; // Optional tag for branch naming +} + +/** + * Simplified workflow status for MCP responses + */ +export interface WorkflowStatus { + taskId: string; + phase: WorkflowPhase; + tddPhase?: TDDPhase; + branchName?: string; + currentSubtask?: { + id: string; + title: string; + attempts: number; + maxAttempts: number; + }; + progress: { + completed: number; + total: number; + current: number; + percentage: number; + }; +} + +/** + * Next action recommendation for AI agent + */ +export interface NextAction { + action: string; + description: string; + nextSteps: string; + phase: WorkflowPhase; + tddPhase?: TDDPhase; + subtask?: { + id: string; + title: string; + }; +} + +/** + * WorkflowService - Facade for workflow operations + * Manages WorkflowOrchestrator lifecycle and state persistence + */ +export class WorkflowService { + private readonly projectRoot: string; + private readonly stateManager: WorkflowStateManager; + private orchestrator?: WorkflowOrchestrator; + private activityLogger?: WorkflowActivityLogger; + + constructor(projectRoot: string) { + this.projectRoot = projectRoot; + this.stateManager = new WorkflowStateManager(projectRoot); + } + + /** + * Check if workflow state exists + */ + async hasWorkflow(): Promise<boolean> { + return await this.stateManager.exists(); + } + + /** + * Start a new TDD workflow + */ + async startWorkflow(options: StartWorkflowOptions): Promise<WorkflowStatus> { + const { + taskId, + taskTitle, + subtasks, + maxAttempts = 3, + force, + tag + } = options; + + // Check for existing workflow + if ((await this.hasWorkflow()) && !force) { + throw new Error( + 'Workflow already exists. Use force=true to override or resume existing workflow.' + ); + } + + // Initialize git adapter and ensure clean state + const gitAdapter = new GitAdapter(this.projectRoot); + await gitAdapter.ensureGitRepository(); + await gitAdapter.ensureCleanWorkingTree(); + + // Parse subtasks to WorkflowContext format + const workflowSubtasks: SubtaskInfo[] = subtasks.map((st) => ({ + id: st.id, + title: st.title, + status: st.status === 'done' ? 'completed' : 'pending', + attempts: 0, + maxAttempts: st.maxAttempts || maxAttempts + })); + + // Find the first incomplete subtask to resume from + const firstIncompleteIndex = workflowSubtasks.findIndex( + (st) => st.status !== 'completed' + ); + + // If all subtasks are already completed, throw an error + if (firstIncompleteIndex === -1) { + throw new Error( + `All subtasks for task ${taskId} are already completed. Nothing to do.` + ); + } + + // Create workflow context, starting from first incomplete subtask + const context: WorkflowContext = { + taskId, + subtasks: workflowSubtasks, + currentSubtaskIndex: firstIncompleteIndex, + errors: [], + metadata: { + startedAt: new Date().toISOString(), + taskTitle, + resumedFromSubtask: + firstIncompleteIndex > 0 + ? workflowSubtasks[firstIncompleteIndex].id + : undefined + } + }; + + // Create orchestrator with auto-persistence + this.orchestrator = new WorkflowOrchestrator(context); + this.orchestrator.enableAutoPersist(async (state: WorkflowState) => { + await this.stateManager.save(state); + }); + + // Initialize activity logger to track all workflow events + this.activityLogger = new WorkflowActivityLogger( + this.orchestrator, + this.stateManager.getActivityLogPath() + ); + this.activityLogger.start(); + + // Transition through PREFLIGHT and BRANCH_SETUP phases + this.orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + // Create git branch with descriptive name + const branchName = this.generateBranchName(taskId, taskTitle, tag); + + // Check if we're already on the target branch + const currentBranch = await gitAdapter.getCurrentBranch(); + if (currentBranch !== branchName) { + // Only create branch if we're not already on it + await gitAdapter.createAndCheckoutBranch(branchName); + } + + // Transition to SUBTASK_LOOP with RED phase + this.orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName + }); + + return this.getStatus(); + } + + /** + * Resume an existing workflow + */ + async resumeWorkflow(): Promise<WorkflowStatus> { + // Load state + const state = await this.stateManager.load(); + + // Create new orchestrator with loaded context + this.orchestrator = new WorkflowOrchestrator(state.context); + + // Validate and restore state + if (!this.orchestrator.canResumeFromState(state)) { + throw new Error( + 'Invalid workflow state. State may be corrupted. Consider starting a new workflow.' + ); + } + + this.orchestrator.restoreState(state); + + // Re-enable auto-persistence + this.orchestrator.enableAutoPersist(async (newState: WorkflowState) => { + await this.stateManager.save(newState); + }); + + // Initialize activity logger to continue tracking events + this.activityLogger = new WorkflowActivityLogger( + this.orchestrator, + this.stateManager.getActivityLogPath() + ); + this.activityLogger.start(); + + return this.getStatus(); + } + + /** + * Get current workflow status + */ + getStatus(): WorkflowStatus { + if (!this.orchestrator) { + throw new Error('No active workflow. Start or resume a workflow first.'); + } + + const context = this.orchestrator.getContext(); + const progress = this.orchestrator.getProgress(); + const currentSubtask = this.orchestrator.getCurrentSubtask(); + + return { + taskId: context.taskId, + phase: this.orchestrator.getCurrentPhase(), + tddPhase: this.orchestrator.getCurrentTDDPhase(), + branchName: context.branchName, + currentSubtask: currentSubtask + ? { + id: currentSubtask.id, + title: currentSubtask.title, + attempts: currentSubtask.attempts, + maxAttempts: currentSubtask.maxAttempts || 3 + } + : undefined, + progress + }; + } + + /** + * Get workflow context (for accessing full state details) + */ + getContext(): WorkflowContext { + if (!this.orchestrator) { + throw new Error('No active workflow. Start or resume a workflow first.'); + } + + return this.orchestrator.getContext(); + } + + /** + * Get next recommended action for AI agent + */ + getNextAction(): NextAction { + if (!this.orchestrator) { + throw new Error('No active workflow. Start or resume a workflow first.'); + } + + const phase = this.orchestrator.getCurrentPhase(); + const tddPhase = this.orchestrator.getCurrentTDDPhase(); + const currentSubtask = this.orchestrator.getCurrentSubtask(); + + // Determine action based on current phase + if (phase === 'COMPLETE') { + return { + action: 'workflow_complete', + description: 'All subtasks completed', + nextSteps: + 'All subtasks completed! Review the entire implementation and merge your branch when ready.', + phase + }; + } + + if (phase === 'FINALIZE') { + return { + action: 'finalize_workflow', + description: 'Finalize and complete the workflow', + nextSteps: + 'All subtasks are complete! Use autopilot_finalize to verify no uncommitted changes remain and mark the workflow as complete.', + phase + }; + } + + if (phase !== 'SUBTASK_LOOP' || !tddPhase || !currentSubtask) { + return { + action: 'unknown', + description: 'Workflow is not in active state', + nextSteps: 'Use autopilot_status to check workflow state.', + phase + }; + } + + const baseAction = { + phase, + tddPhase, + subtask: { + id: currentSubtask.id, + title: currentSubtask.title + } + }; + + switch (tddPhase) { + case 'RED': + return { + ...baseAction, + action: 'generate_test', + description: 'Generate failing test for current subtask', + nextSteps: `Write failing tests for subtask ${currentSubtask.id}: "${currentSubtask.title}". Create test file(s) that validate the expected behavior. Run tests and use autopilot_complete_phase with results. Note: If all tests pass (0 failures), the feature is already implemented and the subtask will be auto-completed.` + }; + case 'GREEN': + return { + ...baseAction, + action: 'implement_code', + description: 'Implement feature to make tests pass', + nextSteps: `Implement code to make tests pass for subtask ${currentSubtask.id}: "${currentSubtask.title}". Write the minimal code needed to pass all tests (GREEN phase), then use autopilot_complete_phase with test results.` + }; + case 'COMMIT': + return { + ...baseAction, + action: 'commit_changes', + description: 'Commit RED-GREEN cycle changes', + nextSteps: `Review and commit your changes for subtask ${currentSubtask.id}: "${currentSubtask.title}". Use autopilot_commit to create the commit and advance to the next subtask.` + }; + default: + return { + ...baseAction, + action: 'unknown', + description: 'Unknown TDD phase', + nextSteps: 'Use autopilot_status to check workflow state.' + }; + } + } + + /** + * Complete current TDD phase with test results + */ + async completePhase(testResults: TestResult): Promise<WorkflowStatus> { + if (!this.orchestrator) { + throw new Error('No active workflow. Start or resume a workflow first.'); + } + + const tddPhase = this.orchestrator.getCurrentTDDPhase(); + + if (!tddPhase) { + throw new Error('Not in active TDD phase'); + } + + // Transition based on current phase + switch (tddPhase) { + case 'RED': + this.orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults + }); + break; + case 'GREEN': + this.orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults + }); + break; + case 'COMMIT': + throw new Error( + 'Cannot complete COMMIT phase with test results. Use commit() instead.' + ); + default: + throw new Error(`Unknown TDD phase: ${tddPhase}`); + } + + return this.getStatus(); + } + + /** + * Commit current changes and advance workflow + */ + async commit(): Promise<WorkflowStatus> { + if (!this.orchestrator) { + throw new Error('No active workflow. Start or resume a workflow first.'); + } + + const tddPhase = this.orchestrator.getCurrentTDDPhase(); + + if (tddPhase !== 'COMMIT') { + throw new Error( + `Cannot commit in ${tddPhase} phase. Complete RED and GREEN phases first.` + ); + } + + // Transition COMMIT phase complete + this.orchestrator.transition({ + type: 'COMMIT_COMPLETE' + }); + + // Check if should advance to next subtask + const progress = this.orchestrator.getProgress(); + if (progress.current < progress.total) { + this.orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + } else { + // All subtasks complete + this.orchestrator.transition({ type: 'ALL_SUBTASKS_COMPLETE' }); + } + + return this.getStatus(); + } + + /** + * Finalize and complete the workflow + * Validates working tree is clean before marking complete + */ + async finalizeWorkflow(): Promise<WorkflowStatus> { + if (!this.orchestrator) { + throw new Error('No active workflow. Start or resume a workflow first.'); + } + + const phase = this.orchestrator.getCurrentPhase(); + if (phase !== 'FINALIZE') { + throw new Error( + `Cannot finalize workflow in ${phase} phase. Complete all subtasks first.` + ); + } + + // Check working tree is clean + const gitAdapter = new GitAdapter(this.projectRoot); + const statusSummary = await gitAdapter.getStatusSummary(); + + if (!statusSummary.isClean) { + throw new Error( + `Cannot finalize workflow: working tree has uncommitted changes.\n` + + `Staged: ${statusSummary.staged}, Modified: ${statusSummary.modified}, ` + + `Deleted: ${statusSummary.deleted}, Untracked: ${statusSummary.untracked}\n` + + `Please commit all changes before finalizing the workflow.` + ); + } + + // Transition to COMPLETE + this.orchestrator.transition({ type: 'FINALIZE_COMPLETE' }); + + return this.getStatus(); + } + + /** + * Abort current workflow + */ + async abortWorkflow(): Promise<void> { + if (this.orchestrator) { + this.orchestrator.transition({ type: 'ABORT' }); + } + + // Delete state file + await this.stateManager.delete(); + + this.orchestrator = undefined; + } + + /** + * Generate a descriptive git branch name + * Format: tag-name/task-id-task-title or task-id-task-title + */ + private generateBranchName( + taskId: string, + taskTitle: string, + tag?: string + ): string { + // Sanitize task title for branch name + const sanitizedTitle = taskTitle + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with dash + .replace(/^-+|-+$/g, '') // Remove leading/trailing dashes + .substring(0, 50); // Limit length + + // Format task ID for branch name + const formattedTaskId = taskId.replace(/\./g, '-'); + + // Add tag prefix if tag is provided + const tagPrefix = tag ? `${tag}/` : ''; + + return `${tagPrefix}task-${formattedTaskId}-${sanitizedTitle}`; + } +} diff --git a/packages/tm-core/src/storage/activity-logger.ts b/packages/tm-core/src/storage/activity-logger.ts new file mode 100644 index 00000000..cb8b5359 --- /dev/null +++ b/packages/tm-core/src/storage/activity-logger.ts @@ -0,0 +1,182 @@ +/** + * Activity.jsonl append-only logging system for workflow tracking. + * Uses newline-delimited JSON (JSONL) format for structured event logging. + * + * @module activity-logger + */ + +import fs from 'fs-extra'; +import path from 'path'; + +/** + * Activity log entry structure + */ +export interface ActivityEvent { + timestamp: string; + type: string; + [key: string]: any; +} + +/** + * Filter criteria for activity log queries + */ +export interface ActivityFilter { + type?: string; + timestampFrom?: string; + timestampTo?: string; + predicate?: (event: ActivityEvent) => boolean; +} + +/** + * Appends an activity event to the log file. + * Uses atomic append operations to ensure data integrity. + * + * @param {string} activityPath - Path to the activity.jsonl file + * @param {Omit<ActivityEvent, 'timestamp'>} event - Event data to log (timestamp added automatically) + * @returns {Promise<void>} + * + * @example + * await logActivity('/path/to/activity.jsonl', { + * type: 'phase-start', + * phase: 'red' + * }); + */ +export async function logActivity( + activityPath: string, + event: Omit<ActivityEvent, 'timestamp'> +): Promise<void> { + // Add timestamp to event + const logEntry = { + ...event, + timestamp: new Date().toISOString() + } as ActivityEvent; + + // Ensure directory exists + await fs.ensureDir(path.dirname(activityPath)); + + // Convert to JSONL format (single line with newline) + const line = JSON.stringify(logEntry) + '\n'; + + // Append to file atomically + // Using 'a' flag ensures atomic append on most systems + await fs.appendFile(activityPath, line, 'utf-8'); +} + +/** + * Reads and parses all events from an activity log file. + * Returns events in chronological order. + * + * @param {string} activityPath - Path to the activity.jsonl file + * @returns {Promise<ActivityEvent[]>} Array of activity events + * @throws {Error} If file contains invalid JSON + * + * @example + * const events = await readActivityLog('/path/to/activity.jsonl'); + * console.log(`Found ${events.length} events`); + */ +export async function readActivityLog( + activityPath: string +): Promise<ActivityEvent[]> { + // Return empty array if file doesn't exist + if (!(await fs.pathExists(activityPath))) { + return []; + } + + // Read file content + const content = await fs.readFile(activityPath, 'utf-8'); + + // Parse JSONL (newline-delimited JSON) + const lines = content.trim().split('\n'); + const events: ActivityEvent[] = []; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + + // Skip empty lines + if (!line) { + continue; + } + + // Parse JSON + try { + const event = JSON.parse(line); + events.push(event); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + throw new Error(`Invalid JSON at line ${i + 1}: ${errorMessage}`); + } + } + + return events; +} + +/** + * Filters activity log events based on criteria. + * Supports filtering by event type, timestamp range, and custom predicates. + * + * @param {string} activityPath - Path to the activity.jsonl file + * @param {ActivityFilter} filter - Filter criteria + * @returns {Promise<ActivityEvent[]>} Filtered array of events + * + * @example + * // Filter by event type + * const phaseEvents = await filterActivityLog('/path/to/activity.jsonl', { + * type: 'phase-start' + * }); + * + * // Filter by timestamp range + * const recentEvents = await filterActivityLog('/path/to/activity.jsonl', { + * timestampFrom: '2024-01-15T10:00:00.000Z' + * }); + * + * // Filter with custom predicate + * const failedTests = await filterActivityLog('/path/to/activity.jsonl', { + * predicate: (event) => event.type === 'test-run' && event.result === 'fail' + * }); + */ +export async function filterActivityLog( + activityPath: string, + filter: ActivityFilter & Record<string, any> +): Promise<ActivityEvent[]> { + const events = await readActivityLog(activityPath); + + return events.filter((event) => { + // Filter by type + if (filter.type && event.type !== filter.type) { + return false; + } + + // Filter by timestamp range + if (filter.timestampFrom && event.timestamp < filter.timestampFrom) { + return false; + } + + if (filter.timestampTo && event.timestamp > filter.timestampTo) { + return false; + } + + // Filter by custom predicate + if (filter.predicate && !filter.predicate(event)) { + return false; + } + + // Filter by other fields (exact match) + for (const [key, value] of Object.entries(filter)) { + if ( + key === 'type' || + key === 'timestampFrom' || + key === 'timestampTo' || + key === 'predicate' + ) { + continue; + } + + if (event[key] !== value) { + return false; + } + } + + return true; + }); +} diff --git a/packages/tm-core/src/storage/index.ts b/packages/tm-core/src/storage/index.ts index d7c0f36d..2ee9a1d3 100644 --- a/packages/tm-core/src/storage/index.ts +++ b/packages/tm-core/src/storage/index.ts @@ -8,6 +8,15 @@ export { FileStorage } from './file-storage/index.js'; export { ApiStorage, type ApiStorageConfig } from './api-storage.js'; export { StorageFactory } from './storage-factory.js'; +// Export activity logger +export { + logActivity, + readActivityLog, + filterActivityLog, + type ActivityEvent, + type ActivityFilter +} from './activity-logger.js'; + // Export storage interface and types export type { IStorage, diff --git a/packages/tm-core/src/utils/git-utils.ts b/packages/tm-core/src/utils/git-utils.ts new file mode 100644 index 00000000..f08e31d5 --- /dev/null +++ b/packages/tm-core/src/utils/git-utils.ts @@ -0,0 +1,421 @@ +/** + * @fileoverview Git utilities for Task Master + * Git integration utilities using raw git commands and gh CLI + */ + +import { exec, execSync } from 'child_process'; +import { promisify } from 'util'; + +const execAsync = promisify(exec); + +/** + * GitHub repository information + */ +export interface GitHubRepoInfo { + name: string; + owner: { login: string }; + defaultBranchRef: { name: string }; +} + +/** + * Check if the specified directory is inside a git repository + */ +export async function isGitRepository(projectRoot: string): Promise<boolean> { + if (!projectRoot) { + throw new Error('projectRoot is required for isGitRepository'); + } + + try { + await execAsync('git rev-parse --git-dir', { cwd: projectRoot }); + return true; + } catch (error) { + return false; + } +} + +/** + * Synchronous check if directory is in a git repository + */ +export function isGitRepositorySync(projectRoot: string): boolean { + if (!projectRoot) { + return false; + } + + try { + execSync('git rev-parse --git-dir', { + cwd: projectRoot, + stdio: 'ignore' + }); + return true; + } catch (error) { + return false; + } +} + +/** + * Get the current git branch name + */ +export async function getCurrentBranch( + projectRoot: string +): Promise<string | null> { + if (!projectRoot) { + throw new Error('projectRoot is required for getCurrentBranch'); + } + + try { + const { stdout } = await execAsync('git rev-parse --abbrev-ref HEAD', { + cwd: projectRoot + }); + return stdout.trim(); + } catch (error) { + return null; + } +} + +/** + * Synchronous get current git branch name + */ +export function getCurrentBranchSync(projectRoot: string): string | null { + if (!projectRoot) { + return null; + } + + try { + const stdout = execSync('git rev-parse --abbrev-ref HEAD', { + cwd: projectRoot, + encoding: 'utf8' + }); + return stdout.trim(); + } catch (error) { + return null; + } +} + +/** + * Get list of all local git branches + */ +export async function getLocalBranches(projectRoot: string): Promise<string[]> { + if (!projectRoot) { + throw new Error('projectRoot is required for getLocalBranches'); + } + + try { + const { stdout } = await execAsync( + 'git branch --format="%(refname:short)"', + { cwd: projectRoot, maxBuffer: 10 * 1024 * 1024 } + ); + return stdout + .trim() + .split('\n') + .filter((branch) => branch.length > 0) + .map((branch) => branch.trim()); + } catch (error) { + return []; + } +} + +/** + * Get list of all remote branches + */ +export async function getRemoteBranches( + projectRoot: string +): Promise<string[]> { + if (!projectRoot) { + throw new Error('projectRoot is required for getRemoteBranches'); + } + + try { + const { stdout } = await execAsync( + 'git branch -r --format="%(refname:short)"', + { cwd: projectRoot, maxBuffer: 10 * 1024 * 1024 } + ); + const names = stdout + .trim() + .split('\n') + .filter((branch) => branch.length > 0 && !branch.includes('HEAD')) + .map((branch) => branch.replace(/^[^/]+\//, '').trim()); + return Array.from(new Set(names)); + } catch (error) { + return []; + } +} + +/** + * Check if gh CLI is available and authenticated + */ +export async function isGhCliAvailable(projectRoot?: string): Promise<boolean> { + try { + const options = projectRoot ? { cwd: projectRoot } : {}; + await execAsync('gh auth status', options); + return true; + } catch (error) { + return false; + } +} + +/** + * Get GitHub repository information using gh CLI + */ +export async function getGitHubRepoInfo( + projectRoot: string +): Promise<GitHubRepoInfo | null> { + if (!projectRoot) { + throw new Error('projectRoot is required for getGitHubRepoInfo'); + } + + try { + const { stdout } = await execAsync( + 'gh repo view --json name,owner,defaultBranchRef', + { cwd: projectRoot } + ); + return JSON.parse(stdout) as GitHubRepoInfo; + } catch (error) { + return null; + } +} + +/** + * Get git repository root directory + */ +export async function getGitRepositoryRoot( + projectRoot: string +): Promise<string | null> { + if (!projectRoot) { + throw new Error('projectRoot is required for getGitRepositoryRoot'); + } + + try { + const { stdout } = await execAsync('git rev-parse --show-toplevel', { + cwd: projectRoot + }); + return stdout.trim(); + } catch (error) { + return null; + } +} + +/** + * Get the default branch name for the repository + */ +export async function getDefaultBranch( + projectRoot: string +): Promise<string | null> { + if (!projectRoot) { + throw new Error('projectRoot is required for getDefaultBranch'); + } + + try { + // Try to get from GitHub first (if gh CLI is available) + if (await isGhCliAvailable(projectRoot)) { + const repoInfo = await getGitHubRepoInfo(projectRoot); + if (repoInfo && repoInfo.defaultBranchRef) { + return repoInfo.defaultBranchRef.name; + } + } + + // Fallback to git remote info (support non-origin remotes) + const remotesRaw = await execAsync('git remote', { cwd: projectRoot }); + const remotes = remotesRaw.stdout.trim().split('\n').filter(Boolean); + if (remotes.length > 0) { + const primary = remotes.includes('origin') ? 'origin' : remotes[0]; + // Parse `git remote show` (preferred) + try { + const { stdout } = await execAsync(`git remote show ${primary}`, { + cwd: projectRoot, + maxBuffer: 10 * 1024 * 1024 + }); + const m = stdout.match(/HEAD branch:\s+([^\s]+)/); + if (m) return m[1].trim(); + } catch {} + // Fallback to symbolic-ref of remote HEAD + try { + const { stdout } = await execAsync( + `git symbolic-ref refs/remotes/${primary}/HEAD`, + { cwd: projectRoot } + ); + return stdout.replace(`refs/remotes/${primary}/`, '').trim(); + } catch {} + } + // If we couldn't determine, throw to trigger final fallbacks + throw new Error('default-branch-not-found'); + } catch (error) { + // Final fallback - common default branch names + const commonDefaults = ['main', 'master']; + const branches = await getLocalBranches(projectRoot); + const remoteBranches = await getRemoteBranches(projectRoot); + + for (const defaultName of commonDefaults) { + if ( + branches.includes(defaultName) || + remoteBranches.includes(defaultName) + ) { + return defaultName; + } + } + + return null; + } +} + +/** + * Check if we're currently on the default branch + */ +export async function isOnDefaultBranch(projectRoot: string): Promise<boolean> { + if (!projectRoot) { + throw new Error('projectRoot is required for isOnDefaultBranch'); + } + + try { + const [currentBranch, defaultBranch] = await Promise.all([ + getCurrentBranch(projectRoot), + getDefaultBranch(projectRoot) + ]); + return ( + currentBranch !== null && + defaultBranch !== null && + currentBranch === defaultBranch + ); + } catch (error) { + return false; + } +} + +/** + * Check if the current working directory is inside a Git work-tree + */ +export function insideGitWorkTree(): boolean { + try { + execSync('git rev-parse --is-inside-work-tree', { + stdio: 'ignore', + cwd: process.cwd() + }); + return true; + } catch { + return false; + } +} + +/** + * Sanitize branch name to be a valid tag name + */ +export function sanitizeBranchNameForTag(branchName: string): string { + if (!branchName || typeof branchName !== 'string') { + return 'unknown-branch'; + } + + // Replace invalid characters with hyphens and clean up + return branchName + .replace(/[^a-zA-Z0-9_.-]/g, '-') // Replace invalid chars with hyphens (allow dots) + .replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens + .replace(/-+/g, '-') // Collapse multiple hyphens + .toLowerCase() // Convert to lowercase + .substring(0, 50); // Limit length +} + +/** + * Check if a branch name would create a valid tag name + */ +export function isValidBranchForTag(branchName: string): boolean { + if (!branchName || typeof branchName !== 'string') { + return false; + } + + // Check if it's a reserved branch name that shouldn't become tags + const reservedBranches = ['main', 'master', 'develop', 'dev', 'head']; + if (reservedBranches.includes(branchName.toLowerCase())) { + return false; + } + + // Check if sanitized name would be meaningful + const sanitized = sanitizeBranchNameForTag(branchName); + return sanitized.length > 0 && sanitized !== 'unknown-branch'; +} + +/** + * Git worktree information + */ +export interface GitWorktree { + path: string; + branch: string | null; + head: string; +} + +/** + * Get list of all git worktrees + */ +export async function getWorktrees( + projectRoot: string +): Promise<GitWorktree[]> { + if (!projectRoot) { + throw new Error('projectRoot is required for getWorktrees'); + } + + try { + const { stdout } = await execAsync('git worktree list --porcelain', { + cwd: projectRoot + }); + + const worktrees: GitWorktree[] = []; + const lines = stdout.trim().split('\n'); + let current: Partial<GitWorktree> = {}; + + for (const line of lines) { + if (line.startsWith('worktree ')) { + // flush previous entry if present + if (current.path) { + worktrees.push({ + path: current.path, + branch: current.branch || null, + head: current.head || '' + }); + current = {}; + } + current.path = line.substring(9); + } else if (line.startsWith('HEAD ')) { + current.head = line.substring(5); + } else if (line.startsWith('branch ')) { + current.branch = line.substring(7).replace('refs/heads/', ''); + } else if (line === '' && current.path) { + worktrees.push({ + path: current.path, + branch: current.branch || null, + head: current.head || '' + }); + current = {}; + } + } + + // Handle last entry if no trailing newline + if (current.path) { + worktrees.push({ + path: current.path, + branch: current.branch || null, + head: current.head || '' + }); + } + + return worktrees; + } catch (error) { + return []; + } +} + +/** + * Check if a branch is checked out in any worktree + * Returns the worktree path if found, null otherwise + */ +export async function isBranchCheckedOut( + projectRoot: string, + branchName: string +): Promise<string | null> { + if (!projectRoot) { + throw new Error('projectRoot is required for isBranchCheckedOut'); + } + if (!branchName) { + throw new Error('branchName is required for isBranchCheckedOut'); + } + + const worktrees = await getWorktrees(projectRoot); + const worktree = worktrees.find((wt) => wt.branch === branchName); + return worktree ? worktree.path : null; +} diff --git a/packages/tm-core/src/utils/index.ts b/packages/tm-core/src/utils/index.ts index 61969f78..0e88e30c 100644 --- a/packages/tm-core/src/utils/index.ts +++ b/packages/tm-core/src/utils/index.ts @@ -13,6 +13,40 @@ export { getParentTaskId } from './id-generator.js'; +// Export git utilities +export { + isGitRepository, + isGitRepositorySync, + getCurrentBranch, + getCurrentBranchSync, + getLocalBranches, + getRemoteBranches, + isGhCliAvailable, + getGitHubRepoInfo, + getGitRepositoryRoot, + getDefaultBranch, + isOnDefaultBranch, + insideGitWorkTree, + sanitizeBranchNameForTag, + isValidBranchForTag, + type GitHubRepoInfo +} from './git-utils.js'; + +// Export path normalization utilities +export { + normalizeProjectPath, + denormalizeProjectPath, + isValidNormalizedPath +} from './path-normalizer.js'; + +// Export run ID generation utilities +export { + generateRunId, + isValidRunId, + parseRunId, + compareRunIds +} from './run-id-generator.js'; + // Additional utility exports /** diff --git a/packages/tm-core/src/utils/path-normalizer.spec.ts b/packages/tm-core/src/utils/path-normalizer.spec.ts new file mode 100644 index 00000000..5ba815d6 --- /dev/null +++ b/packages/tm-core/src/utils/path-normalizer.spec.ts @@ -0,0 +1,282 @@ +import { describe, it, expect } from 'vitest'; +import { + normalizeProjectPath, + denormalizeProjectPath, + isValidNormalizedPath +} from './path-normalizer.js'; + +describe('Path Normalizer (base64url encoding)', () => { + describe('normalizeProjectPath', () => { + it('should encode Unix paths to base64url', () => { + const input = '/Users/test/projects/myapp'; + const normalized = normalizeProjectPath(input); + + // Should be valid base64url (only A-Z, a-z, 0-9, -, _) + expect(/^[A-Za-z0-9_-]+$/.test(normalized)).toBe(true); + // Should not contain slashes + expect(normalized).not.toContain('/'); + expect(normalized).not.toContain('\\'); + }); + + it('should encode Windows paths to base64url', () => { + const input = 'C:\\Users\\test\\projects\\myapp'; + const normalized = normalizeProjectPath(input); + + // Should be valid base64url + expect(/^[A-Za-z0-9_-]+$/.test(normalized)).toBe(true); + expect(normalized).not.toContain('/'); + expect(normalized).not.toContain('\\'); + }); + + it('should encode paths with hyphens (preserving them for round-trip)', () => { + const input = '/projects/my-app'; + const normalized = normalizeProjectPath(input); + + // Should be valid base64url + expect(/^[A-Za-z0-9_-]+$/.test(normalized)).toBe(true); + // Hyphens in base64url are from encoding, not original path + expect(isValidNormalizedPath(normalized)).toBe(true); + }); + + it('should encode paths with special characters', () => { + const input = '/projects/myapp (v2)'; + const normalized = normalizeProjectPath(input); + + // Should be valid base64url + expect(/^[A-Za-z0-9_-]+$/.test(normalized)).toBe(true); + }); + + it('should encode relative paths', () => { + const input = './projects/app'; + const normalized = normalizeProjectPath(input); + + // Should be valid base64url + expect(/^[A-Za-z0-9_-]+$/.test(normalized)).toBe(true); + }); + + it('should handle empty string', () => { + const input = ''; + const expected = ''; + expect(normalizeProjectPath(input)).toBe(expected); + }); + + it('should encode single directory', () => { + const input = 'project'; + const normalized = normalizeProjectPath(input); + + // Should be valid base64url + expect(/^[A-Za-z0-9_-]+$/.test(normalized)).toBe(true); + }); + + it('should encode paths with multiple consecutive slashes', () => { + const input = '/Users//test///project'; + const normalized = normalizeProjectPath(input); + + // Should be valid base64url + expect(/^[A-Za-z0-9_-]+$/.test(normalized)).toBe(true); + }); + }); + + describe('denormalizeProjectPath', () => { + it('should decode base64url back to original path', () => { + const original = '/Users/test/projects/myapp'; + const normalized = normalizeProjectPath(original); + const denormalized = denormalizeProjectPath(normalized); + + expect(denormalized).toBe(original); + }); + + it('should decode base64url for Windows paths', () => { + const original = 'C:\\Users\\test\\project'; + const normalized = normalizeProjectPath(original); + const denormalized = denormalizeProjectPath(normalized); + + expect(denormalized).toBe(original); + }); + + it('should handle empty string', () => { + const input = ''; + const expected = ''; + expect(denormalizeProjectPath(input)).toBe(expected); + }); + + it('should preserve hyphens in directory names (no longer a limitation!)', () => { + const original = '/projects/my-app'; + const normalized = normalizeProjectPath(original); + const denormalized = denormalizeProjectPath(normalized); + + // With base64url, hyphens are preserved correctly + expect(denormalized).toBe(original); + }); + + it('should handle invalid base64url gracefully', () => { + // Invalid base64url - should return the input as fallback + const invalid = 'not@valid#base64url'; + const result = denormalizeProjectPath(invalid); + + // Should return input unchanged for backward compatibility + expect(result).toBe(invalid); + }); + }); + + describe('isValidNormalizedPath', () => { + it('should return true for valid base64url strings', () => { + // Valid base64url characters: A-Z, a-z, 0-9, -, _ + expect(isValidNormalizedPath('VXNlcnMtdGVzdC1wcm9qZWN0')).toBe(true); + expect(isValidNormalizedPath('abc123_-ABC')).toBe(true); + }); + + it('should return true for base64url with hyphens and underscores', () => { + expect(isValidNormalizedPath('test-path_encoded')).toBe(true); + }); + + it('should return false for paths with slashes', () => { + expect(isValidNormalizedPath('Users/test/project')).toBe(false); + }); + + it('should return false for paths with backslashes', () => { + expect(isValidNormalizedPath('Users\\test\\project')).toBe(false); + }); + + it('should return true for empty string', () => { + expect(isValidNormalizedPath('')).toBe(true); + }); + + it('should return false for strings with special characters not in base64url', () => { + // Base64url only allows: A-Z, a-z, 0-9, -, _ + expect(isValidNormalizedPath('my-app (v2)')).toBe(false); // parentheses and spaces not allowed + expect(isValidNormalizedPath('test@example')).toBe(false); // @ not allowed + expect(isValidNormalizedPath('test+value')).toBe(false); // + not allowed + }); + + it('should validate normalized paths correctly', () => { + const path = '/Users/test/my-app'; + const normalized = normalizeProjectPath(path); + expect(isValidNormalizedPath(normalized)).toBe(true); + }); + }); + + describe('Round-trip conversion', () => { + it('should perfectly preserve ALL Unix paths (including those with hyphens)', () => { + const originalPaths = [ + '/Users/test/projects/myapp', + '/root/deep/nested/path', + './relative/path', + '/projects/my-app', // Now works correctly! + '/path/with-multiple-hyphens/in-names' + ]; + + for (const original of originalPaths) { + const normalized = normalizeProjectPath(original); + const denormalized = denormalizeProjectPath(normalized); + + // Perfect round-trip with base64url encoding + expect(denormalized).toBe(original); + } + }); + + it('should perfectly preserve Windows paths (including drive letters)', () => { + const originalPaths = [ + 'C:\\Users\\test\\project', + 'D:\\Projects\\my-app', + 'E:\\path\\with-hyphens\\test' + ]; + + for (const original of originalPaths) { + const normalized = normalizeProjectPath(original); + const denormalized = denormalizeProjectPath(normalized); + + // Perfect round-trip - drive letters and colons preserved + expect(denormalized).toBe(original); + } + }); + + it('should preserve paths with special characters', () => { + const originalPaths = [ + '/projects/my app (v2)', + '/path/with spaces/test', + '/path/with-dashes-and_underscores', + '/path/with.dots.and-dashes' + ]; + + for (const original of originalPaths) { + const normalized = normalizeProjectPath(original); + const denormalized = denormalizeProjectPath(normalized); + + // Perfect round-trip for all special characters + expect(denormalized).toBe(original); + } + }); + + it('should handle mixed slashes and preserve exact path structure', () => { + const original = '/Users/test\\mixed/path'; + const normalized = normalizeProjectPath(original); + const denormalized = denormalizeProjectPath(normalized); + + // Exact preservation of mixed slashes + expect(denormalized).toBe(original); + }); + + it('should preserve multiple consecutive slashes', () => { + const original = '/Users//test///project'; + const normalized = normalizeProjectPath(original); + const denormalized = denormalizeProjectPath(normalized); + + // Exact preservation of all slashes + expect(denormalized).toBe(original); + }); + }); + + describe('Cross-platform consistency', () => { + it('should produce filesystem-safe normalized output for all platforms', () => { + const unixPath = '/Users/test/project'; + const windowsPath = 'C:\\Users\\test\\project'; + + const normalizedUnix = normalizeProjectPath(unixPath); + const normalizedWindows = normalizeProjectPath(windowsPath); + + // Both should be valid base64url (no slashes or backslashes) + expect(normalizedUnix).not.toContain('/'); + expect(normalizedUnix).not.toContain('\\'); + expect(normalizedWindows).not.toContain('/'); + expect(normalizedWindows).not.toContain('\\'); + + // Both should be valid base64url format + expect(isValidNormalizedPath(normalizedUnix)).toBe(true); + expect(isValidNormalizedPath(normalizedWindows)).toBe(true); + }); + + it('should produce different normalized outputs for different paths', () => { + // Unix and Windows paths are different, so should produce different encoded values + const unixPath = '/Users/test/project'; + const windowsPath = 'C:\\Users\\test\\project'; + + const normalizedUnix = normalizeProjectPath(unixPath); + const normalizedWindows = normalizeProjectPath(windowsPath); + + // Different inputs should produce different outputs + expect(normalizedUnix).not.toBe(normalizedWindows); + + // But both should denormalize back to their originals + expect(denormalizeProjectPath(normalizedUnix)).toBe(unixPath); + expect(denormalizeProjectPath(normalizedWindows)).toBe(windowsPath); + }); + + it('should handle Unicode characters in paths', () => { + const unicodePaths = [ + '/Users/测试/project', + '/Users/test/プロジェクト', + '/Users/тест/project' + ]; + + for (const original of unicodePaths) { + const normalized = normalizeProjectPath(original); + const denormalized = denormalizeProjectPath(normalized); + + // Perfect round-trip for Unicode + expect(denormalized).toBe(original); + expect(isValidNormalizedPath(normalized)).toBe(true); + } + }); + }); +}); diff --git a/packages/tm-core/src/utils/path-normalizer.ts b/packages/tm-core/src/utils/path-normalizer.ts new file mode 100644 index 00000000..ca0c8dd0 --- /dev/null +++ b/packages/tm-core/src/utils/path-normalizer.ts @@ -0,0 +1,76 @@ +/** + * Path normalization utilities for global storage system. + * Converts project paths to storage-safe directory names using base64url encoding. + * + * This provides a bijective (one-to-one) mapping that preserves all characters + * and supports perfect round-trip conversion between paths and storage names. + * + * @module path-normalizer + */ + +/** + * Normalizes a project path to a storage-safe directory name using base64url encoding. + * This encoding is filesystem-safe (no slashes, backslashes, or special characters) + * and fully reversible, preserving hyphens and all other characters in paths. + * + * @param {string} projectPath - The project path to normalize + * @returns {string} The base64url-encoded path safe for use as a directory name + * + * @example + * normalizeProjectPath('/Users/test/project') // returns base64url encoded string + * normalizeProjectPath('C:\\Users\\test') // returns base64url encoded string + * normalizeProjectPath('/projects/my-app') // returns base64url encoded string (hyphens preserved) + */ +export function normalizeProjectPath(projectPath: string): string { + if (!projectPath) { + return ''; + } + + // Use base64url encoding: filesystem-safe and fully reversible + return Buffer.from(projectPath, 'utf-8').toString('base64url'); +} + +/** + * Denormalizes a storage directory name back to the original path. + * Decodes base64url-encoded paths with perfect fidelity. + * + * @param {string} normalizedPath - The base64url-encoded path to decode + * @returns {string} The original path with all characters preserved + * + * @example + * denormalizeProjectPath(normalizeProjectPath('/Users/test/project')) // returns '/Users/test/project' + * denormalizeProjectPath(normalizeProjectPath('/projects/my-app')) // returns '/projects/my-app' + */ +export function denormalizeProjectPath(normalizedPath: string): string { + if (!normalizedPath) { + return ''; + } + + // Validate that input is valid base64url before attempting to decode + if (!isValidNormalizedPath(normalizedPath)) { + // Return original string for backward compatibility with non-base64url inputs + return normalizedPath; + } + + return Buffer.from(normalizedPath, 'base64url').toString('utf-8'); +} + +/** + * Validates whether a path is in normalized (base64url) format. + * Valid base64url strings contain only: A-Z, a-z, 0-9, -, _ + * + * @param {string} path - The path to validate + * @returns {boolean} True if the path is in normalized base64url format + * + * @example + * isValidNormalizedPath('VXNlcnMvdGVzdC9wcm9qZWN0') // returns true (valid base64url) + * isValidNormalizedPath('Users/test/project') // returns false (contains slashes) + */ +export function isValidNormalizedPath(path: string): boolean { + if (path === '') { + return true; + } + + // Check if path is valid base64url: only A-Z, a-z, 0-9, -, _ + return /^[A-Za-z0-9_-]*$/.test(path); +} diff --git a/packages/tm-core/src/utils/run-id-generator.spec.ts b/packages/tm-core/src/utils/run-id-generator.spec.ts new file mode 100644 index 00000000..9dd6ca32 --- /dev/null +++ b/packages/tm-core/src/utils/run-id-generator.spec.ts @@ -0,0 +1,266 @@ +import { describe, it, expect } from 'vitest'; +import { + generateRunId, + isValidRunId, + parseRunId, + compareRunIds +} from './run-id-generator.js'; + +describe('Run ID Generator', () => { + describe('generateRunId', () => { + it('should generate a valid ISO 8601 timestamp-based ID', () => { + const runId = generateRunId(); + + // Should be in ISO 8601 format with milliseconds + expect(runId).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/); + }); + + it('should generate unique IDs when called multiple times', () => { + const id1 = generateRunId(); + const id2 = generateRunId(); + const id3 = generateRunId(); + + expect(id1).not.toBe(id2); + expect(id2).not.toBe(id3); + expect(id1).not.toBe(id3); + }); + + it('should generate chronologically ordered IDs', () => { + const id1 = generateRunId(); + // Small delay to ensure different timestamp + const id2 = generateRunId(); + + expect(id2 > id1).toBe(true); + }); + + it('should use current time by default', () => { + const before = new Date().toISOString(); + const runId = generateRunId(); + const after = new Date().toISOString(); + + expect(runId >= before).toBe(true); + expect(runId <= after).toBe(true); + }); + + it('should accept custom Date object', () => { + const customDate = new Date('2024-01-15T10:30:45.123Z'); + const runId = generateRunId(customDate); + + expect(runId).toBe('2024-01-15T10:30:45.123Z'); + }); + + it('should handle date at year boundary', () => { + const newYear = new Date('2025-01-01T00:00:00.000Z'); + const runId = generateRunId(newYear); + + expect(runId).toBe('2025-01-01T00:00:00.000Z'); + }); + + it('should handle millisecond precision correctly', () => { + const dateWithMs = new Date('2024-03-15T14:22:33.999Z'); + const runId = generateRunId(dateWithMs); + + expect(runId).toBe('2024-03-15T14:22:33.999Z'); + }); + }); + + describe('isValidRunId', () => { + it('should return true for valid ISO 8601 timestamp', () => { + expect(isValidRunId('2024-01-15T10:30:45.123Z')).toBe(true); + }); + + it('should return true for generated run IDs', () => { + const runId = generateRunId(); + expect(isValidRunId(runId)).toBe(true); + }); + + it('should return false for invalid format', () => { + expect(isValidRunId('not-a-timestamp')).toBe(false); + expect(isValidRunId('2024-01-15')).toBe(false); + expect(isValidRunId('2024-01-15T10:30:45')).toBe(false); // missing Z + expect(isValidRunId('2024-01-15 10:30:45.123Z')).toBe(false); // space instead of T + }); + + it('should return false for empty string', () => { + expect(isValidRunId('')).toBe(false); + }); + + it('should return false for null or undefined', () => { + expect(isValidRunId(null)).toBe(false); + expect(isValidRunId(undefined)).toBe(false); + }); + + it('should return false for invalid dates', () => { + expect(isValidRunId('2024-13-01T10:30:45.123Z')).toBe(false); // invalid month + expect(isValidRunId('2024-01-32T10:30:45.123Z')).toBe(false); // invalid day + expect(isValidRunId('2024-01-15T25:30:45.123Z')).toBe(false); // invalid hour + }); + + it('should return true for edge case valid dates', () => { + expect(isValidRunId('2024-02-29T23:59:59.999Z')).toBe(true); // leap year + expect(isValidRunId('2025-01-01T00:00:00.000Z')).toBe(true); // year boundary + }); + + it('should return false for missing milliseconds', () => { + expect(isValidRunId('2024-01-15T10:30:45Z')).toBe(false); + }); + + it('should return false for non-UTC timezone', () => { + expect(isValidRunId('2024-01-15T10:30:45.123+01:00')).toBe(false); + }); + }); + + describe('parseRunId', () => { + it('should parse valid run ID to Date object', () => { + const runId = '2024-01-15T10:30:45.123Z'; + const date = parseRunId(runId); + + expect(date).toBeInstanceOf(Date); + expect(date?.toISOString()).toBe(runId); + }); + + it('should parse generated run ID', () => { + const originalDate = new Date('2024-03-20T15:45:30.500Z'); + const runId = generateRunId(originalDate); + const parsedDate = parseRunId(runId); + + expect(parsedDate?.getTime()).toBe(originalDate.getTime()); + }); + + it('should return null for invalid run ID', () => { + expect(parseRunId('invalid')).toBe(null); + expect(parseRunId('')).toBe(null); + expect(parseRunId(null)).toBe(null); + expect(parseRunId(undefined)).toBe(null); + }); + + it('should handle edge case dates correctly', () => { + const leapYear = '2024-02-29T12:00:00.000Z'; + const parsed = parseRunId(leapYear); + + expect(parsed?.toISOString()).toBe(leapYear); + }); + }); + + describe('compareRunIds', () => { + it('should return negative when first ID is earlier', () => { + const earlier = '2024-01-15T10:00:00.000Z'; + const later = '2024-01-15T11:00:00.000Z'; + + expect(compareRunIds(earlier, later)).toBeLessThan(0); + }); + + it('should return positive when first ID is later', () => { + const earlier = '2024-01-15T10:00:00.000Z'; + const later = '2024-01-15T11:00:00.000Z'; + + expect(compareRunIds(later, earlier)).toBeGreaterThan(0); + }); + + it('should return zero when IDs are equal', () => { + const runId = '2024-01-15T10:00:00.000Z'; + + expect(compareRunIds(runId, runId)).toBe(0); + }); + + it('should handle millisecond differences', () => { + const id1 = '2024-01-15T10:00:00.100Z'; + const id2 = '2024-01-15T10:00:00.200Z'; + + expect(compareRunIds(id1, id2)).toBeLessThan(0); + expect(compareRunIds(id2, id1)).toBeGreaterThan(0); + }); + + it('should handle cross-day comparisons', () => { + const yesterday = '2024-01-14T23:59:59.999Z'; + const today = '2024-01-15T00:00:00.000Z'; + + expect(compareRunIds(yesterday, today)).toBeLessThan(0); + }); + + it('should handle cross-year comparisons', () => { + const lastYear = '2023-12-31T23:59:59.999Z'; + const thisYear = '2024-01-01T00:00:00.000Z'; + + expect(compareRunIds(lastYear, thisYear)).toBeLessThan(0); + }); + + it('should throw error for invalid run IDs', () => { + const valid = '2024-01-15T10:00:00.000Z'; + + expect(() => compareRunIds('invalid', valid)).toThrow(); + expect(() => compareRunIds(valid, 'invalid')).toThrow(); + expect(() => compareRunIds('invalid', 'invalid')).toThrow(); + }); + }); + + describe('Collision detection', () => { + it('should generate different IDs in rapid succession', () => { + const ids = new Set(); + const count = 100; + + for (let i = 0; i < count; i++) { + ids.add(generateRunId()); + } + + // All IDs should be unique + expect(ids.size).toBe(count); + }); + + it('should handle high-frequency generation', () => { + const ids = []; + const iterations = 1000; + + for (let i = 0; i < iterations; i++) { + ids.push(generateRunId()); + } + + // Check uniqueness + const uniqueIds = new Set(ids); + expect(uniqueIds.size).toBe(iterations); + + // Check chronological order + for (let i = 1; i < ids.length; i++) { + expect(compareRunIds(ids[i - 1], ids[i])).toBeLessThanOrEqual(0); + } + }); + }); + + describe('Chronological ordering', () => { + it('should allow sorting run IDs chronologically', () => { + const ids = [ + '2024-01-15T14:00:00.000Z', + '2024-01-15T10:00:00.000Z', + '2024-01-15T12:00:00.000Z', + '2024-01-14T23:00:00.000Z', + '2024-01-16T08:00:00.000Z' + ]; + + const sorted = [...ids].sort(compareRunIds); + + expect(sorted).toEqual([ + '2024-01-14T23:00:00.000Z', + '2024-01-15T10:00:00.000Z', + '2024-01-15T12:00:00.000Z', + '2024-01-15T14:00:00.000Z', + '2024-01-16T08:00:00.000Z' + ]); + }); + + it('should handle reverse chronological sorting', () => { + const ids = [ + '2024-01-15T10:00:00.000Z', + '2024-01-15T14:00:00.000Z', + '2024-01-15T12:00:00.000Z' + ]; + + const sorted = [...ids].sort((a, b) => compareRunIds(b, a)); + + expect(sorted).toEqual([ + '2024-01-15T14:00:00.000Z', + '2024-01-15T12:00:00.000Z', + '2024-01-15T10:00:00.000Z' + ]); + }); + }); +}); diff --git a/packages/tm-core/src/utils/run-id-generator.ts b/packages/tm-core/src/utils/run-id-generator.ts new file mode 100644 index 00000000..53ae5d5e --- /dev/null +++ b/packages/tm-core/src/utils/run-id-generator.ts @@ -0,0 +1,129 @@ +/** + * Run ID generation and validation utilities for the global storage system. + * Uses ISO 8601 timestamps with millisecond precision for unique, chronologically-ordered run IDs. + * + * @module run-id-generator + */ + +// Collision detection state +let lastTimestamp = 0; +let counter = 0; + +/** + * Generates a unique run ID using ISO 8601 timestamp format with millisecond precision. + * The ID is guaranteed to be chronologically sortable and URL-safe. + * Includes collision detection to ensure uniqueness even when called in rapid succession. + * + * @param {Date} [date=new Date()] - Optional date to use for the run ID. Defaults to current time. + * @returns {string} ISO 8601 formatted timestamp (e.g., '2024-01-15T10:30:45.123Z') + * + * @example + * generateRunId() // returns '2024-01-15T10:30:45.123Z' + * generateRunId(new Date('2024-01-15T10:00:00.000Z')) // returns '2024-01-15T10:00:00.000Z' + */ +export function generateRunId(date: Date = new Date()): string { + const timestamp = date.getTime(); + + // Collision detection: if same millisecond, wait for next millisecond + if (timestamp === lastTimestamp) { + counter++; + // Wait for next millisecond to ensure uniqueness + let newTimestamp = timestamp; + while (newTimestamp === timestamp) { + newTimestamp = Date.now(); + } + date = new Date(newTimestamp); + lastTimestamp = newTimestamp; + counter = 0; + } else { + lastTimestamp = timestamp; + counter = 0; + } + + return date.toISOString(); +} + +/** + * Validates whether a string is a valid run ID. + * A valid run ID must be: + * - In ISO 8601 format with milliseconds + * - In UTC timezone (ends with 'Z') + * - A valid date when parsed + * + * @param {any} runId - The value to validate + * @returns {boolean} True if the value is a valid run ID + * + * @example + * isValidRunId('2024-01-15T10:30:45.123Z') // returns true + * isValidRunId('invalid') // returns false + * isValidRunId('2024-01-15T10:30:45Z') // returns false (missing milliseconds) + */ +export function isValidRunId(runId: any): boolean { + if (!runId || typeof runId !== 'string') { + return false; + } + + // Check format: YYYY-MM-DDTHH:mm:ss.sssZ + const isoFormatRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; + if (!isoFormatRegex.test(runId)) { + return false; + } + + // Validate it's a real date + const date = new Date(runId); + if (isNaN(date.getTime())) { + return false; + } + + // Ensure the parsed date matches the input (catches invalid dates like 2024-13-01) + return date.toISOString() === runId; +} + +/** + * Parses a run ID string into a Date object. + * + * @param {any} runId - The run ID to parse + * @returns {Date | null} Date object if valid, null if invalid + * + * @example + * parseRunId('2024-01-15T10:30:45.123Z') // returns Date object + * parseRunId('invalid') // returns null + */ +export function parseRunId(runId: any): Date | null { + if (!isValidRunId(runId)) { + return null; + } + + return new Date(runId); +} + +/** + * Compares two run IDs chronologically. + * Returns a negative number if id1 is earlier, positive if id1 is later, or 0 if equal. + * Can be used as a comparator function for Array.sort(). + * + * @param {string} id1 - First run ID to compare + * @param {string} id2 - Second run ID to compare + * @returns {number} Negative if id1 < id2, positive if id1 > id2, zero if equal + * @throws {Error} If either run ID is invalid + * + * @example + * compareRunIds('2024-01-15T10:00:00.000Z', '2024-01-15T11:00:00.000Z') // returns negative number + * ['2024-01-15T14:00:00.000Z', '2024-01-15T10:00:00.000Z'].sort(compareRunIds) + * // returns ['2024-01-15T10:00:00.000Z', '2024-01-15T14:00:00.000Z'] + */ +export function compareRunIds(id1: string, id2: string): number { + if (!isValidRunId(id1)) { + throw new Error(`Invalid run ID: ${id1}`); + } + + if (!isValidRunId(id2)) { + throw new Error(`Invalid run ID: ${id2}`); + } + + // String comparison works for ISO 8601 timestamps + // because they are lexicographically sortable + if (id1 < id2) return -1; + if (id1 > id2) return 1; + return 0; +} diff --git a/packages/tm-core/src/workflow/types.ts b/packages/tm-core/src/workflow/types.ts new file mode 100644 index 00000000..8bb35d9c --- /dev/null +++ b/packages/tm-core/src/workflow/types.ts @@ -0,0 +1,150 @@ +/** + * Workflow phase definitions + */ +export type WorkflowPhase = + | 'PREFLIGHT' + | 'BRANCH_SETUP' + | 'SUBTASK_LOOP' + | 'FINALIZE' + | 'COMPLETE'; + +/** + * TDD cycle phases within subtask loop + */ +export type TDDPhase = 'RED' | 'GREEN' | 'COMMIT'; + +/** + * Workflow state context + */ +export interface WorkflowContext { + taskId: string; + subtasks: SubtaskInfo[]; + currentSubtaskIndex: number; + currentTDDPhase?: TDDPhase; + branchName?: string; + errors: WorkflowError[]; + metadata: Record<string, unknown>; + lastTestResults?: TestResult; +} + +/** + * Test result from test execution + */ +export interface TestResult { + total: number; + passed: number; + failed: number; + skipped: number; + phase: 'RED' | 'GREEN'; +} + +/** + * Subtask information + */ +export interface SubtaskInfo { + id: string; + title: string; + status: 'pending' | 'in-progress' | 'completed' | 'failed'; + attempts: number; + maxAttempts?: number; +} + +/** + * Workflow error information + */ +export interface WorkflowError { + phase: WorkflowPhase; + message: string; + timestamp: Date; + recoverable: boolean; +} + +/** + * State machine state + */ +export interface WorkflowState { + phase: WorkflowPhase; + context: WorkflowContext; +} + +/** + * State transition event types + */ +export type WorkflowEvent = + | { type: 'PREFLIGHT_COMPLETE' } + | { type: 'BRANCH_CREATED'; branchName: string } + | { type: 'SUBTASK_START'; subtaskId: string } + | { type: 'RED_PHASE_COMPLETE'; testResults?: TestResult } + | { type: 'GREEN_PHASE_COMPLETE'; testResults?: TestResult } + | { type: 'COMMIT_COMPLETE' } + | { type: 'SUBTASK_COMPLETE' } + | { type: 'ALL_SUBTASKS_COMPLETE' } + | { type: 'FINALIZE_COMPLETE' } + | { type: 'ERROR'; error: WorkflowError } + | { type: 'RETRY' } + | { type: 'ABORT' }; + +/** + * State transition definition + */ +export interface StateTransition { + from: WorkflowPhase; + to: WorkflowPhase; + event: WorkflowEvent['type']; + guard?: (context: WorkflowContext) => boolean; +} + +/** + * State machine configuration + */ +export interface StateMachineConfig { + initialPhase: WorkflowPhase; + transitions: StateTransition[]; +} + +/** + * Workflow event listener + */ +export type WorkflowEventListener = (event: WorkflowEventData) => void; + +/** + * Comprehensive event data for workflow events + */ +export interface WorkflowEventData { + type: WorkflowEventType; + timestamp: Date; + phase: WorkflowPhase; + tddPhase?: TDDPhase; + subtaskId?: string; + data?: Record<string, unknown>; +} + +/** + * All possible workflow event types + */ +export type WorkflowEventType = + | 'workflow:started' + | 'workflow:completed' + | 'workflow:error' + | 'workflow:resumed' + | 'phase:entered' + | 'phase:exited' + | 'tdd:feature-already-implemented' + | 'tdd:red:started' + | 'tdd:red:completed' + | 'tdd:green:started' + | 'tdd:green:completed' + | 'tdd:commit:started' + | 'tdd:commit:completed' + | 'subtask:started' + | 'subtask:completed' + | 'subtask:failed' + | 'test:run' + | 'test:passed' + | 'test:failed' + | 'git:branch:created' + | 'git:commit:created' + | 'error:occurred' + | 'state:persisted' + | 'progress:updated' + | 'adapter:configured'; diff --git a/packages/tm-core/src/workflow/workflow-activity-logger.ts b/packages/tm-core/src/workflow/workflow-activity-logger.ts new file mode 100644 index 00000000..43a8bdf3 --- /dev/null +++ b/packages/tm-core/src/workflow/workflow-activity-logger.ts @@ -0,0 +1,152 @@ +/** + * @fileoverview WorkflowActivityLogger - Logs all workflow events to activity.jsonl + * + * Subscribes to all WorkflowOrchestrator events and persists them to a JSONL file + * for debugging, auditing, and workflow analysis. + */ + +import type { WorkflowOrchestrator } from './workflow-orchestrator.js'; +import type { WorkflowEventData, WorkflowEventType } from './types.js'; +import { logActivity, type ActivityEvent } from '../storage/activity-logger.js'; +import { getLogger } from '../logger/index.js'; + +/** + * All workflow event types that should be logged + */ +const WORKFLOW_EVENT_TYPES: WorkflowEventType[] = [ + 'workflow:started', + 'workflow:completed', + 'workflow:error', + 'workflow:resumed', + 'phase:entered', + 'phase:exited', + 'tdd:feature-already-implemented', + 'tdd:red:started', + 'tdd:red:completed', + 'tdd:green:started', + 'tdd:green:completed', + 'tdd:commit:started', + 'tdd:commit:completed', + 'subtask:started', + 'subtask:completed', + 'subtask:failed', + 'test:run', + 'test:passed', + 'test:failed', + 'git:branch:created', + 'git:commit:created', + 'error:occurred', + 'state:persisted', + 'progress:updated', + 'adapter:configured' +]; + +/** + * Logs all workflow events to an activity.jsonl file + */ +export class WorkflowActivityLogger { + private readonly activityLogPath: string; + private readonly orchestrator: WorkflowOrchestrator; + private readonly logger = getLogger('WorkflowActivityLogger'); + private readonly listenerMap: Map< + WorkflowEventType, + (event: WorkflowEventData) => void + > = new Map(); + private isActive = false; + + constructor(orchestrator: WorkflowOrchestrator, activityLogPath: string) { + this.orchestrator = orchestrator; + this.activityLogPath = activityLogPath; + } + + /** + * Start logging workflow events + */ + start(): void { + if (this.isActive) { + this.logger.warn('Activity logger is already active'); + return; + } + + // Subscribe to all workflow events, storing listener references for cleanup + WORKFLOW_EVENT_TYPES.forEach((eventType) => { + const listener = (event: WorkflowEventData) => this.logEvent(event); + this.listenerMap.set(eventType, listener); + this.orchestrator.on(eventType, listener); + }); + + this.isActive = true; + this.logger.debug( + `Activity logger started, logging to: ${this.activityLogPath}` + ); + } + + /** + * Stop logging workflow events and remove all listeners + */ + stop(): void { + if (!this.isActive) { + return; + } + + // Remove all registered listeners + this.listenerMap.forEach((listener, eventType) => { + this.orchestrator.off(eventType, listener); + }); + + // Clear the listener map + this.listenerMap.clear(); + + this.isActive = false; + this.logger.debug('Activity logger stopped and listeners removed'); + } + + /** + * Log a workflow event to the activity log + */ + private async logEvent(event: WorkflowEventData): Promise<void> { + if (!this.isActive) { + return; + } + + try { + // Convert timestamp to ISO string, handling both Date objects and string/number timestamps + const ts = + (event.timestamp as any) instanceof Date + ? (event.timestamp as Date).toISOString() + : new Date(event.timestamp as any).toISOString(); + + // Convert WorkflowEventData to ActivityEvent format + const activityEvent: Omit<ActivityEvent, 'timestamp'> = { + type: event.type, + phase: event.phase, + tddPhase: event.tddPhase, + subtaskId: event.subtaskId, + // Event timestamp kept as ISO for readability; storage layer adds its own "timestamp" + eventTimestamp: ts, + ...(event.data || {}) + }; + + await logActivity(this.activityLogPath, activityEvent); + } catch (error: any) { + // Log errors but don't throw - we don't want activity logging to break the workflow + this.logger.error( + `Failed to log activity event ${event.type}: ${error.message}` + ); + } + } + + /** + * Get the path to the activity log file + */ + getActivityLogPath(): string { + return this.activityLogPath; + } + + /** + * Check if the logger is currently active + */ + isLogging(): boolean { + return this.isActive; + } +} diff --git a/packages/tm-core/src/workflow/workflow-orchestrator.test.ts b/packages/tm-core/src/workflow/workflow-orchestrator.test.ts new file mode 100644 index 00000000..921d43b0 --- /dev/null +++ b/packages/tm-core/src/workflow/workflow-orchestrator.test.ts @@ -0,0 +1,1535 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { WorkflowOrchestrator } from './workflow-orchestrator.js'; +import type { + WorkflowContext, + WorkflowPhase, + WorkflowEventData, + WorkflowError +} from './types.js'; +import { TestResultValidator } from '../services/test-result-validator.js'; +import type { TestResult } from '../services/test-result-validator.types.js'; + +describe('WorkflowOrchestrator - State Machine Structure', () => { + let orchestrator: WorkflowOrchestrator; + let initialContext: WorkflowContext; + + beforeEach(() => { + initialContext = { + taskId: 'task-1', + subtasks: [ + { id: '1.1', title: 'Subtask 1', status: 'pending', attempts: 0 }, + { id: '1.2', title: 'Subtask 2', status: 'pending', attempts: 0 } + ], + currentSubtaskIndex: 0, + errors: [], + metadata: {} + }; + orchestrator = new WorkflowOrchestrator(initialContext); + }); + + describe('Initial State', () => { + it('should start in PREFLIGHT phase', () => { + expect(orchestrator.getCurrentPhase()).toBe('PREFLIGHT'); + }); + + it('should have the provided context', () => { + const context = orchestrator.getContext(); + expect(context.taskId).toBe('task-1'); + expect(context.subtasks).toHaveLength(2); + }); + }); + + describe('State Transitions', () => { + it('should transition from PREFLIGHT to BRANCH_SETUP', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + expect(orchestrator.getCurrentPhase()).toBe('BRANCH_SETUP'); + }); + + it('should transition from BRANCH_SETUP to SUBTASK_LOOP', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + expect(orchestrator.getCurrentPhase()).toBe('SUBTASK_LOOP'); + }); + + it('should store branch name in context', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + expect(orchestrator.getContext().branchName).toBe('feature/test'); + }); + + it('should transition from SUBTASK_LOOP to FINALIZE when all subtasks complete', () => { + // Navigate to SUBTASK_LOOP + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + // Complete all subtasks + orchestrator.transition({ type: 'ALL_SUBTASKS_COMPLETE' }); + expect(orchestrator.getCurrentPhase()).toBe('FINALIZE'); + }); + + it('should transition from FINALIZE to COMPLETE', () => { + // Navigate to FINALIZE + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + orchestrator.transition({ type: 'ALL_SUBTASKS_COMPLETE' }); + + // Complete finalization + orchestrator.transition({ type: 'FINALIZE_COMPLETE' }); + expect(orchestrator.getCurrentPhase()).toBe('COMPLETE'); + }); + + it('should reject invalid transitions', () => { + expect(() => { + orchestrator.transition({ type: 'FINALIZE_COMPLETE' }); + }).toThrow('Invalid transition'); + }); + }); + + describe('TDD Cycle in SUBTASK_LOOP', () => { + beforeEach(() => { + // Navigate to SUBTASK_LOOP + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + }); + + it('should start with RED phase when entering SUBTASK_LOOP', () => { + expect(orchestrator.getCurrentTDDPhase()).toBe('RED'); + }); + + it('should transition from RED to GREEN', () => { + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + expect(orchestrator.getCurrentTDDPhase()).toBe('GREEN'); + }); + + it('should transition from GREEN to COMMIT', () => { + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + expect(orchestrator.getCurrentTDDPhase()).toBe('COMMIT'); + }); + + it('should complete subtask after COMMIT', () => { + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + + const context = orchestrator.getContext(); + expect(context.subtasks[0].status).toBe('completed'); + }); + + it('should move to next subtask after completion', () => { + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + + expect(orchestrator.getContext().currentSubtaskIndex).toBe(1); + expect(orchestrator.getCurrentTDDPhase()).toBe('RED'); + }); + }); + + describe('State Serialization', () => { + it('should serialize current state', () => { + const state = orchestrator.getState(); + expect(state).toHaveProperty('phase'); + expect(state).toHaveProperty('context'); + expect(state.phase).toBe('PREFLIGHT'); + }); + + it('should restore from serialized state', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + const state = orchestrator.getState(); + const restored = new WorkflowOrchestrator(state.context); + restored.restoreState(state); + + expect(restored.getCurrentPhase()).toBe('SUBTASK_LOOP'); + expect(restored.getContext().branchName).toBe('feature/test'); + }); + }); + + describe('Event Emission', () => { + it('should emit phase:entered event on state transition', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('phase:entered', (event) => events.push(event)); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + expect(events).toHaveLength(1); + expect(events[0].type).toBe('phase:entered'); + expect(events[0].phase).toBe('BRANCH_SETUP'); + }); + + it('should emit phase:exited event on state transition', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('phase:exited', (event) => events.push(event)); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + expect(events).toHaveLength(1); + expect(events[0].type).toBe('phase:exited'); + expect(events[0].phase).toBe('PREFLIGHT'); + }); + + it('should emit tdd phase events', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('tdd:red:started', (event) => events.push(event)); + orchestrator.on('tdd:green:started', (event) => events.push(event)); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + expect(events).toHaveLength(1); + expect(events[0].type).toBe('tdd:red:started'); + + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + expect(events).toHaveLength(2); + expect(events[1].type).toBe('tdd:green:started'); + }); + + it('should emit subtask events', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('subtask:started', (event) => events.push(event)); + orchestrator.on('subtask:completed', (event) => events.push(event)); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + expect(events).toHaveLength(1); + expect(events[0].type).toBe('subtask:started'); + expect(events[0].subtaskId).toBe('1.1'); + + // Complete TDD cycle + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + + expect(events).toHaveLength(3); + expect(events[1].type).toBe('subtask:completed'); + expect(events[2].type).toBe('subtask:started'); + expect(events[2].subtaskId).toBe('1.2'); + }); + + it('should support multiple listeners for same event', () => { + const listener1 = vi.fn(); + const listener2 = vi.fn(); + + orchestrator.on('phase:entered', listener1); + orchestrator.on('phase:entered', listener2); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + expect(listener1).toHaveBeenCalledOnce(); + expect(listener2).toHaveBeenCalledOnce(); + }); + + it('should allow removing event listeners', () => { + const listener = vi.fn(); + orchestrator.on('phase:entered', listener); + orchestrator.off('phase:entered', listener); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + expect(listener).not.toHaveBeenCalled(); + }); + + it('should include timestamp in all events', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('phase:entered', (event) => events.push(event)); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + expect(events[0].timestamp).toBeInstanceOf(Date); + }); + + it('should include additional data in events', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('git:branch:created', (event) => events.push(event)); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + const branchEvent = events.find((e) => e.type === 'git:branch:created'); + expect(branchEvent).toBeDefined(); + expect(branchEvent?.data?.branchName).toBe('feature/test'); + }); + }); + + describe('State Persistence', () => { + it('should persist state after transitions when auto-persist enabled', async () => { + const persistMock = vi.fn(); + orchestrator.enableAutoPersist(persistMock); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + expect(persistMock).toHaveBeenCalledOnce(); + const state = persistMock.mock.calls[0][0]; + expect(state.phase).toBe('BRANCH_SETUP'); + }); + + it('should emit state:persisted event', async () => { + const events: WorkflowEventData[] = []; + orchestrator.on('state:persisted', (event) => events.push(event)); + + await orchestrator.persistState(); + + expect(events).toHaveLength(1); + expect(events[0].type).toBe('state:persisted'); + }); + + it('should auto-persist after each transition when enabled', () => { + const persistMock = vi.fn(); + orchestrator.enableAutoPersist(persistMock); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + expect(persistMock).toHaveBeenCalledTimes(1); + + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + expect(persistMock).toHaveBeenCalledTimes(2); + }); + + it('should not auto-persist when disabled', () => { + const persistMock = vi.fn(); + orchestrator.enableAutoPersist(persistMock); + orchestrator.disableAutoPersist(); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + expect(persistMock).not.toHaveBeenCalled(); + }); + + it('should serialize state with all context data', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + const state = orchestrator.getState(); + + expect(state.phase).toBe('SUBTASK_LOOP'); + expect(state.context.branchName).toBe('feature/test'); + expect(state.context.currentTDDPhase).toBe('RED'); + expect(state.context.taskId).toBe('task-1'); + }); + }); + + describe('Phase Transition Guards and Validation', () => { + it('should enforce guard conditions on transitions', () => { + // Create orchestrator with guard condition that should fail + const guardedContext: WorkflowContext = { + taskId: 'task-1', + subtasks: [], + currentSubtaskIndex: 0, + errors: [], + metadata: { guardTest: true } + }; + + const guardedOrchestrator = new WorkflowOrchestrator(guardedContext); + + // Add guard that checks for subtasks (should fail since we have no subtasks) + guardedOrchestrator.addGuard('SUBTASK_LOOP', (context) => { + return context.subtasks.length > 0; + }); + + guardedOrchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + expect(() => { + guardedOrchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + }).toThrow('Guard condition failed'); + }); + + it('should allow transition when guard condition passes', () => { + const guardedContext: WorkflowContext = { + taskId: 'task-1', + subtasks: [ + { id: '1.1', title: 'Test', status: 'pending', attempts: 0 } + ], + currentSubtaskIndex: 0, + errors: [], + metadata: {} + }; + + const guardedOrchestrator = new WorkflowOrchestrator(guardedContext); + + guardedOrchestrator.addGuard('SUBTASK_LOOP', (context) => { + return context.subtasks.length > 0; + }); + + guardedOrchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + guardedOrchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + expect(guardedOrchestrator.getCurrentPhase()).toBe('SUBTASK_LOOP'); + }); + + it('should validate test results before GREEN phase transition', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + // Attempt to transition to GREEN without test results + expect(() => { + orchestrator.transition({ type: 'RED_PHASE_COMPLETE' }); + }).toThrow('Test results required'); + }); + + it('should validate RED phase test results have failures', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + // Provide passing test results (should fail RED phase validation) + expect(() => { + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'RED' + } + }); + }).toThrow('RED phase must have at least one failing test'); + }); + + it('should allow RED to GREEN transition with valid failing tests', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + + expect(orchestrator.getCurrentTDDPhase()).toBe('GREEN'); + }); + + it('should validate GREEN phase test results have no failures', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + + // Provide test results with failures (should fail GREEN phase validation) + expect(() => { + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 3, + failed: 2, + skipped: 0, + phase: 'GREEN' + } + }); + }).toThrow('GREEN phase must have zero failures'); + }); + + it('should allow GREEN to COMMIT transition with all tests passing', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + + expect(orchestrator.getCurrentTDDPhase()).toBe('COMMIT'); + }); + + it('should store test results in context', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + const redResults = { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' as const + }; + + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: redResults + }); + + const context = orchestrator.getContext(); + expect(context.lastTestResults).toEqual(redResults); + }); + + it('should validate git repository state before BRANCH_SETUP', () => { + // Set up orchestrator with git validation enabled + const gitContext: WorkflowContext = { + taskId: 'task-1', + subtasks: [ + { id: '1.1', title: 'Test', status: 'pending', attempts: 0 } + ], + currentSubtaskIndex: 0, + errors: [], + metadata: { requireGit: false } + }; + + const gitOrchestrator = new WorkflowOrchestrator(gitContext); + + // Guard that requires git to be true (but it's false) + gitOrchestrator.addGuard('BRANCH_SETUP', (context) => { + return context.metadata.requireGit === true; + }); + + expect(() => { + gitOrchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + }).toThrow('Guard condition failed'); + }); + }); + + describe('Subtask Iteration and Progress Tracking', () => { + beforeEach(() => { + // Navigate to SUBTASK_LOOP for all tests + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + }); + + it('should return current subtask', () => { + const currentSubtask = orchestrator.getCurrentSubtask(); + expect(currentSubtask).toBeDefined(); + expect(currentSubtask?.id).toBe('1.1'); + expect(currentSubtask?.title).toBe('Subtask 1'); + }); + + it('should return undefined when no current subtask', () => { + // Complete all subtasks + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + + const currentSubtask = orchestrator.getCurrentSubtask(); + expect(currentSubtask).toBeUndefined(); + }); + + it('should calculate workflow progress', () => { + const progress = orchestrator.getProgress(); + expect(progress.completed).toBe(0); + expect(progress.total).toBe(2); + expect(progress.current).toBe(1); + expect(progress.percentage).toBe(0); + }); + + it('should update progress as subtasks complete', () => { + // Complete first subtask + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + + const progress = orchestrator.getProgress(); + expect(progress.completed).toBe(1); + expect(progress.total).toBe(2); + expect(progress.current).toBe(2); + expect(progress.percentage).toBe(50); + }); + + it('should show 100% progress when all subtasks complete', () => { + // Complete all subtasks + for (let i = 0; i < 2; i++) { + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + } + + const progress = orchestrator.getProgress(); + expect(progress.completed).toBe(2); + expect(progress.total).toBe(2); + expect(progress.percentage).toBe(100); + }); + + it('should validate if can proceed to next phase', () => { + // In RED phase - cannot proceed without completing TDD cycle + expect(orchestrator.canProceed()).toBe(false); + + // Complete RED phase + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + + // In GREEN phase - still cannot proceed + expect(orchestrator.canProceed()).toBe(false); + + // Complete GREEN phase + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + + // In COMMIT phase - still cannot proceed + expect(orchestrator.canProceed()).toBe(false); + + // Complete COMMIT phase + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + + // Subtask complete - can proceed + expect(orchestrator.canProceed()).toBe(true); + }); + + it('should track subtask attempts', () => { + const context = orchestrator.getContext(); + expect(context.subtasks[0].attempts).toBe(0); + + // Increment attempt on starting RED phase + orchestrator.incrementAttempts(); + expect(orchestrator.getContext().subtasks[0].attempts).toBe(1); + }); + + it('should enforce max attempts limit', () => { + // Set max attempts to 3 + const limitedContext: WorkflowContext = { + taskId: 'task-1', + subtasks: [ + { + id: '1.1', + title: 'Subtask 1', + status: 'pending', + attempts: 0, + maxAttempts: 3 + } + ], + currentSubtaskIndex: 0, + errors: [], + metadata: {} + }; + + const limitedOrchestrator = new WorkflowOrchestrator(limitedContext); + limitedOrchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + limitedOrchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + // Increment attempts to max + for (let i = 0; i < 3; i++) { + limitedOrchestrator.incrementAttempts(); + } + + expect(limitedOrchestrator.hasExceededMaxAttempts()).toBe(false); + + // One more attempt should exceed + limitedOrchestrator.incrementAttempts(); + expect(limitedOrchestrator.hasExceededMaxAttempts()).toBe(true); + }); + + it('should allow unlimited attempts when maxAttempts is undefined', () => { + for (let i = 0; i < 100; i++) { + orchestrator.incrementAttempts(); + } + + expect(orchestrator.hasExceededMaxAttempts()).toBe(false); + }); + + it('should emit progress events on subtask completion', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('progress:updated', (event) => events.push(event)); + + // Complete first subtask + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + + expect(events).toHaveLength(1); + expect(events[0].type).toBe('progress:updated'); + expect(events[0].data?.completed).toBe(1); + expect(events[0].data?.total).toBe(2); + }); + }); + + describe('Error Handling and Recovery', () => { + beforeEach(() => { + // Navigate to SUBTASK_LOOP for all tests + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + }); + + it('should handle errors with ERROR event', () => { + const error: WorkflowError = { + phase: 'SUBTASK_LOOP', + message: 'Test execution failed', + timestamp: new Date(), + recoverable: true + }; + + orchestrator.transition({ type: 'ERROR', error }); + + const context = orchestrator.getContext(); + expect(context.errors).toHaveLength(1); + expect(context.errors[0].message).toBe('Test execution failed'); + }); + + it('should emit error:occurred event', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('error:occurred', (event) => events.push(event)); + + const error: WorkflowError = { + phase: 'SUBTASK_LOOP', + message: 'Test execution failed', + timestamp: new Date(), + recoverable: true + }; + + orchestrator.transition({ type: 'ERROR', error }); + + expect(events).toHaveLength(1); + expect(events[0].type).toBe('error:occurred'); + expect(events[0].data?.error).toEqual(error); + }); + + it('should support retry attempts', () => { + const currentSubtask = orchestrator.getCurrentSubtask(); + expect(currentSubtask?.attempts).toBe(0); + + // Simulate failed attempt + orchestrator.incrementAttempts(); + orchestrator.retryCurrentSubtask(); + + const context = orchestrator.getContext(); + expect(context.currentTDDPhase).toBe('RED'); + expect(context.subtasks[0].attempts).toBe(1); + }); + + it('should mark subtask as failed when max attempts exceeded', () => { + const limitedContext: WorkflowContext = { + taskId: 'task-1', + subtasks: [ + { + id: '1.1', + title: 'Subtask 1', + status: 'pending', + attempts: 0, + maxAttempts: 2 + } + ], + currentSubtaskIndex: 0, + errors: [], + metadata: {} + }; + + const limitedOrchestrator = new WorkflowOrchestrator(limitedContext); + limitedOrchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + limitedOrchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + // Exceed max attempts + for (let i = 0; i < 3; i++) { + limitedOrchestrator.incrementAttempts(); + } + + limitedOrchestrator.handleMaxAttemptsExceeded(); + + const context = limitedOrchestrator.getContext(); + expect(context.subtasks[0].status).toBe('failed'); + }); + + it('should emit subtask:failed event when max attempts exceeded', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('subtask:failed', (event) => events.push(event)); + + const limitedContext: WorkflowContext = { + taskId: 'task-1', + subtasks: [ + { + id: '1.1', + title: 'Subtask 1', + status: 'pending', + attempts: 0, + maxAttempts: 2 + } + ], + currentSubtaskIndex: 0, + errors: [], + metadata: {} + }; + + const limitedOrchestrator = new WorkflowOrchestrator(limitedContext); + limitedOrchestrator.on('subtask:failed', (event) => events.push(event)); + + limitedOrchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + limitedOrchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + // Exceed max attempts + for (let i = 0; i < 3; i++) { + limitedOrchestrator.incrementAttempts(); + } + + limitedOrchestrator.handleMaxAttemptsExceeded(); + + expect(events).toHaveLength(1); + expect(events[0].type).toBe('subtask:failed'); + }); + + it('should support abort workflow', () => { + orchestrator.transition({ type: 'ABORT' }); + + // Should still be in SUBTASK_LOOP but workflow should be aborted + expect(orchestrator.getCurrentPhase()).toBe('SUBTASK_LOOP'); + expect(orchestrator.isAborted()).toBe(true); + }); + + it('should prevent transitions after abort', () => { + orchestrator.transition({ type: 'ABORT' }); + + expect(() => { + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + }).toThrow('Workflow has been aborted'); + }); + + it('should allow retry after recoverable error', () => { + const error: WorkflowError = { + phase: 'SUBTASK_LOOP', + message: 'Temporary failure', + timestamp: new Date(), + recoverable: true + }; + + orchestrator.transition({ type: 'ERROR', error }); + + // Should be able to retry + expect(() => { + orchestrator.transition({ type: 'RETRY' }); + }).not.toThrow(); + + expect(orchestrator.getCurrentTDDPhase()).toBe('RED'); + }); + + it('should track error history in context', () => { + const error1: WorkflowError = { + phase: 'SUBTASK_LOOP', + message: 'Error 1', + timestamp: new Date(), + recoverable: true + }; + + const error2: WorkflowError = { + phase: 'SUBTASK_LOOP', + message: 'Error 2', + timestamp: new Date(), + recoverable: false + }; + + orchestrator.transition({ type: 'ERROR', error: error1 }); + orchestrator.transition({ type: 'RETRY' }); + orchestrator.transition({ type: 'ERROR', error: error2 }); + + const context = orchestrator.getContext(); + expect(context.errors).toHaveLength(2); + expect(context.errors[0].message).toBe('Error 1'); + expect(context.errors[1].message).toBe('Error 2'); + }); + }); + + describe('Resume Functionality from Checkpoints', () => { + it('should restore state from checkpoint', () => { + // Advance to SUBTASK_LOOP and complete first subtask + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + + // Save state + const state = orchestrator.getState(); + + // Create new orchestrator and restore + const restored = new WorkflowOrchestrator(state.context); + restored.restoreState(state); + + expect(restored.getCurrentPhase()).toBe('SUBTASK_LOOP'); + expect(restored.getContext().currentSubtaskIndex).toBe(1); + expect(restored.getContext().branchName).toBe('feature/test'); + }); + + it('should resume from mid-TDD cycle', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + + // Save state in GREEN phase + const state = orchestrator.getState(); + + // Restore and verify in GREEN phase + const restored = new WorkflowOrchestrator(state.context); + restored.restoreState(state); + + expect(restored.getCurrentPhase()).toBe('SUBTASK_LOOP'); + expect(restored.getCurrentTDDPhase()).toBe('GREEN'); + }); + + it('should validate restored state integrity', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + const state = orchestrator.getState(); + + // Validate state structure + expect(orchestrator.canResumeFromState(state)).toBe(true); + }); + + it('should reject invalid checkpoint state', () => { + const invalidState = { + phase: 'INVALID_PHASE' as WorkflowPhase, + context: initialContext + }; + + expect(orchestrator.canResumeFromState(invalidState)).toBe(false); + }); + + it('should preserve subtask attempts on resume', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + // Increment attempts + orchestrator.incrementAttempts(); + orchestrator.incrementAttempts(); + + const state = orchestrator.getState(); + + // Restore + const restored = new WorkflowOrchestrator(state.context); + restored.restoreState(state); + + const currentSubtask = restored.getCurrentSubtask(); + expect(currentSubtask?.attempts).toBe(2); + }); + + it('should preserve errors on resume', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + const error: WorkflowError = { + phase: 'SUBTASK_LOOP', + message: 'Test error', + timestamp: new Date(), + recoverable: true + }; + + orchestrator.transition({ type: 'ERROR', error }); + + const state = orchestrator.getState(); + + // Restore + const restored = new WorkflowOrchestrator(state.context); + restored.restoreState(state); + + expect(restored.getContext().errors).toHaveLength(1); + expect(restored.getContext().errors[0].message).toBe('Test error'); + }); + + it('should preserve completed subtask statuses on resume', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + // Complete first subtask + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + + const state = orchestrator.getState(); + + // Restore + const restored = new WorkflowOrchestrator(state.context); + restored.restoreState(state); + + const progress = restored.getProgress(); + expect(progress.completed).toBe(1); + expect(progress.current).toBe(2); + }); + + it('should emit workflow:resumed event on restore', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + const state = orchestrator.getState(); + + // Create new orchestrator with event listener + const events: WorkflowEventData[] = []; + const restored = new WorkflowOrchestrator(state.context); + restored.on('workflow:resumed', (event) => events.push(event)); + + restored.restoreState(state); + + expect(events).toHaveLength(1); + expect(events[0].type).toBe('workflow:resumed'); + expect(events[0].phase).toBe('SUBTASK_LOOP'); + }); + + it('should calculate correct progress after resume', () => { + // Complete first subtask + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'GREEN' + } + }); + orchestrator.transition({ type: 'COMMIT_COMPLETE' }); + orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); + + const state = orchestrator.getState(); + + // Restore and check progress + const restored = new WorkflowOrchestrator(state.context); + restored.restoreState(state); + + const progress = restored.getProgress(); + expect(progress.completed).toBe(1); + expect(progress.total).toBe(2); + expect(progress.percentage).toBe(50); + }); + }); + + describe('Adapter Integration', () => { + let testValidator: TestResultValidator; + + beforeEach(() => { + testValidator = new TestResultValidator(); + }); + + it('should integrate with TestResultValidator', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + // Set validator + orchestrator.setTestResultValidator(testValidator); + + // Validator should be used internally + expect(orchestrator.hasTestResultValidator()).toBe(true); + }); + + it('should use TestResultValidator to validate RED phase', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + orchestrator.setTestResultValidator(testValidator); + + // Should reject passing tests in RED phase + expect(() => { + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 5, + failed: 0, + skipped: 0, + phase: 'RED' + } + }); + }).toThrow('RED phase must have at least one failing test'); + }); + + it('should use TestResultValidator to validate GREEN phase', () => { + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + orchestrator.setTestResultValidator(testValidator); + + orchestrator.transition({ + type: 'RED_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 0, + failed: 5, + skipped: 0, + phase: 'RED' + } + }); + + // Should reject failing tests in GREEN phase + expect(() => { + orchestrator.transition({ + type: 'GREEN_PHASE_COMPLETE', + testResults: { + total: 5, + passed: 3, + failed: 2, + skipped: 0, + phase: 'GREEN' + } + }); + }).toThrow('GREEN phase must have zero failures'); + }); + + it('should support git adapter hooks', () => { + const gitOperations: string[] = []; + + orchestrator.onGitOperation((operation, data) => { + gitOperations.push(operation); + }); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + // Verify git operation hook was called + expect(gitOperations).toContain('branch:created'); + }); + + it('should support executor adapter hooks', () => { + const executions: string[] = []; + + orchestrator.onExecute((command, context) => { + executions.push(command); + }); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + orchestrator.executeCommand('run-tests'); + + expect(executions).toContain('run-tests'); + }); + + it('should provide adapter context in events', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('phase:entered', (event) => events.push(event)); + + orchestrator.setTestResultValidator(testValidator); + + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + + // Event should include adapter availability + expect(events[0].data?.adapters).toBeDefined(); + }); + + it('should allow adapter reconfiguration', () => { + orchestrator.setTestResultValidator(testValidator); + expect(orchestrator.hasTestResultValidator()).toBe(true); + + orchestrator.removeTestResultValidator(); + expect(orchestrator.hasTestResultValidator()).toBe(false); + }); + + it('should work without adapters (optional integration)', () => { + // Should work fine without adapters + orchestrator.transition({ type: 'PREFLIGHT_COMPLETE' }); + orchestrator.transition({ + type: 'BRANCH_CREATED', + branchName: 'feature/test' + }); + + expect(orchestrator.getCurrentPhase()).toBe('SUBTASK_LOOP'); + }); + + it('should emit adapter-related events', () => { + const events: WorkflowEventData[] = []; + orchestrator.on('adapter:configured', (event) => events.push(event)); + + orchestrator.setTestResultValidator(testValidator); + + expect(events).toHaveLength(1); + expect(events[0].type).toBe('adapter:configured'); + expect(events[0].data?.adapterType).toBe('test-validator'); + }); + }); +}); diff --git a/packages/tm-core/src/workflow/workflow-orchestrator.ts b/packages/tm-core/src/workflow/workflow-orchestrator.ts new file mode 100644 index 00000000..e4ac3e1f --- /dev/null +++ b/packages/tm-core/src/workflow/workflow-orchestrator.ts @@ -0,0 +1,698 @@ +import type { + WorkflowPhase, + TDDPhase, + WorkflowContext, + WorkflowEvent, + WorkflowState, + StateTransition, + WorkflowEventType, + WorkflowEventData, + WorkflowEventListener, + SubtaskInfo +} from './types.js'; +import type { TestResultValidator } from '../services/test-result-validator.js'; + +/** + * Lightweight state machine for TDD workflow orchestration + */ +export class WorkflowOrchestrator { + private currentPhase: WorkflowPhase; + private context: WorkflowContext; + private readonly transitions: StateTransition[]; + private readonly eventListeners: Map< + WorkflowEventType, + Set<WorkflowEventListener> + >; + private persistCallback?: (state: WorkflowState) => void | Promise<void>; + private autoPersistEnabled: boolean = false; + private readonly phaseGuards: Map< + WorkflowPhase, + (context: WorkflowContext) => boolean + >; + private aborted: boolean = false; + private testResultValidator?: TestResultValidator; + private gitOperationHook?: (operation: string, data?: unknown) => void; + private executeHook?: (command: string, context: WorkflowContext) => void; + + constructor(initialContext: WorkflowContext) { + this.currentPhase = 'PREFLIGHT'; + this.context = { ...initialContext }; + this.transitions = this.defineTransitions(); + this.eventListeners = new Map(); + this.phaseGuards = new Map(); + } + + /** + * Define valid state transitions + */ + private defineTransitions(): StateTransition[] { + return [ + { + from: 'PREFLIGHT', + to: 'BRANCH_SETUP', + event: 'PREFLIGHT_COMPLETE' + }, + { + from: 'BRANCH_SETUP', + to: 'SUBTASK_LOOP', + event: 'BRANCH_CREATED' + }, + { + from: 'SUBTASK_LOOP', + to: 'FINALIZE', + event: 'ALL_SUBTASKS_COMPLETE' + }, + { + from: 'FINALIZE', + to: 'COMPLETE', + event: 'FINALIZE_COMPLETE' + } + ]; + } + + /** + * Get current workflow phase + */ + getCurrentPhase(): WorkflowPhase { + return this.currentPhase; + } + + /** + * Get current TDD phase (only valid in SUBTASK_LOOP) + */ + getCurrentTDDPhase(): TDDPhase | undefined { + if (this.currentPhase === 'SUBTASK_LOOP') { + return this.context.currentTDDPhase || 'RED'; + } + return undefined; + } + + /** + * Get workflow context + */ + getContext(): WorkflowContext { + return { ...this.context }; + } + + /** + * Transition to next state based on event + */ + transition(event: WorkflowEvent): void { + // Check if workflow is aborted + if (this.aborted && event.type !== 'ABORT') { + throw new Error('Workflow has been aborted'); + } + + // Handle special events that work across all phases + if (event.type === 'ERROR') { + this.handleError(event.error); + void this.triggerAutoPersist(); + return; + } + + if (event.type === 'ABORT') { + this.aborted = true; + void this.triggerAutoPersist(); + return; + } + + if (event.type === 'RETRY') { + this.handleRetry(); + void this.triggerAutoPersist(); + return; + } + + // Handle TDD phase transitions within SUBTASK_LOOP + if (this.currentPhase === 'SUBTASK_LOOP') { + this.handleTDDPhaseTransition(event); + void this.triggerAutoPersist(); + return; + } + + // Handle main workflow phase transitions + const validTransition = this.transitions.find( + (t) => t.from === this.currentPhase && t.event === event.type + ); + + if (!validTransition) { + throw new Error( + `Invalid transition: ${event.type} from ${this.currentPhase}` + ); + } + + // Execute transition + this.executeTransition(validTransition, event); + void this.triggerAutoPersist(); + } + + /** + * Handle TDD phase transitions (RED -> GREEN -> COMMIT) + */ + private handleTDDPhaseTransition(event: WorkflowEvent): void { + const currentTDD = this.context.currentTDDPhase || 'RED'; + + switch (event.type) { + case 'RED_PHASE_COMPLETE': + if (currentTDD !== 'RED') { + throw new Error( + 'Invalid transition: RED_PHASE_COMPLETE from non-RED phase' + ); + } + + // Validate test results are provided + if (!event.testResults) { + throw new Error('Test results required for RED phase transition'); + } + + // Store test results in context + this.context.lastTestResults = event.testResults; + + // Special case: All tests passing in RED phase means feature already implemented + if (event.testResults.failed === 0) { + this.emit('tdd:red:completed'); + this.emit('tdd:feature-already-implemented', { + subtaskId: this.getCurrentSubtaskId(), + testResults: event.testResults + }); + + // Mark subtask as complete and move to next one + const subtask = + this.context.subtasks[this.context.currentSubtaskIndex]; + if (subtask) { + subtask.status = 'completed'; + } + + this.emit('subtask:completed'); + this.context.currentSubtaskIndex++; + + // Emit progress update + const progress = this.getProgress(); + this.emit('progress:updated', { + completed: progress.completed, + total: progress.total, + percentage: progress.percentage + }); + + // Start next subtask or complete workflow + if (this.context.currentSubtaskIndex < this.context.subtasks.length) { + this.context.currentTDDPhase = 'RED'; + this.emit('tdd:red:started'); + this.emit('subtask:started'); + } else { + // All subtasks complete, transition to FINALIZE + this.transition({ type: 'ALL_SUBTASKS_COMPLETE' }); + } + break; + } + + // Normal RED phase: has failing tests, proceed to GREEN + this.emit('tdd:red:completed'); + this.context.currentTDDPhase = 'GREEN'; + this.emit('tdd:green:started'); + break; + + case 'GREEN_PHASE_COMPLETE': + if (currentTDD !== 'GREEN') { + throw new Error( + 'Invalid transition: GREEN_PHASE_COMPLETE from non-GREEN phase' + ); + } + + // Validate test results are provided + if (!event.testResults) { + throw new Error('Test results required for GREEN phase transition'); + } + + // Validate GREEN phase has no failures + if (event.testResults.failed !== 0) { + throw new Error('GREEN phase must have zero failures'); + } + + // Store test results in context + this.context.lastTestResults = event.testResults; + + this.emit('tdd:green:completed'); + this.context.currentTDDPhase = 'COMMIT'; + this.emit('tdd:commit:started'); + break; + + case 'COMMIT_COMPLETE': + if (currentTDD !== 'COMMIT') { + throw new Error( + 'Invalid transition: COMMIT_COMPLETE from non-COMMIT phase' + ); + } + this.emit('tdd:commit:completed'); + // Mark current subtask as completed + const currentSubtask = + this.context.subtasks[this.context.currentSubtaskIndex]; + if (currentSubtask) { + currentSubtask.status = 'completed'; + } + break; + + case 'SUBTASK_COMPLETE': + this.emit('subtask:completed'); + // Move to next subtask + this.context.currentSubtaskIndex++; + + // Emit progress update + const progress = this.getProgress(); + this.emit('progress:updated', { + completed: progress.completed, + total: progress.total, + percentage: progress.percentage + }); + + if (this.context.currentSubtaskIndex < this.context.subtasks.length) { + // Start next subtask with RED phase + this.context.currentTDDPhase = 'RED'; + this.emit('tdd:red:started'); + this.emit('subtask:started'); + } else { + // All subtasks complete, transition to FINALIZE + this.transition({ type: 'ALL_SUBTASKS_COMPLETE' }); + } + break; + + case 'ALL_SUBTASKS_COMPLETE': + // Transition to FINALIZE phase + this.emit('phase:exited'); + this.currentPhase = 'FINALIZE'; + this.context.currentTDDPhase = undefined; + this.emit('phase:entered'); + // Note: Don't auto-transition to COMPLETE - requires explicit finalize call + break; + + default: + throw new Error(`Invalid transition: ${event.type} in SUBTASK_LOOP`); + } + } + + /** + * Execute a state transition + */ + private executeTransition( + transition: StateTransition, + event: WorkflowEvent + ): void { + // Check guard condition if present + if (transition.guard && !transition.guard(this.context)) { + throw new Error( + `Guard condition failed for transition to ${transition.to}` + ); + } + + // Check phase-specific guard if present + const phaseGuard = this.phaseGuards.get(transition.to); + if (phaseGuard && !phaseGuard(this.context)) { + throw new Error('Guard condition failed'); + } + + // Emit phase exit event + this.emit('phase:exited'); + + // Update context based on event + this.updateContext(event); + + // Transition to new phase + this.currentPhase = transition.to; + + // Emit phase entry event + this.emit('phase:entered'); + + // Initialize TDD phase if entering SUBTASK_LOOP + if (this.currentPhase === 'SUBTASK_LOOP') { + this.context.currentTDDPhase = 'RED'; + this.emit('tdd:red:started'); + this.emit('subtask:started'); + } + } + + /** + * Update context based on event + */ + private updateContext(event: WorkflowEvent): void { + switch (event.type) { + case 'BRANCH_CREATED': + this.context.branchName = event.branchName; + this.emit('git:branch:created', { branchName: event.branchName }); + + // Trigger git operation hook + if (this.gitOperationHook) { + this.gitOperationHook('branch:created', { + branchName: event.branchName + }); + } + break; + + case 'ERROR': + this.context.errors.push(event.error); + this.emit('error:occurred', { error: event.error }); + break; + + // Add more context updates as needed + } + } + + /** + * Get current state for serialization + */ + getState(): WorkflowState { + return { + phase: this.currentPhase, + context: { ...this.context } + }; + } + + /** + * Restore state from checkpoint + */ + restoreState(state: WorkflowState): void { + this.currentPhase = state.phase; + this.context = { ...state.context }; + + // Emit workflow:resumed event + this.emit('workflow:resumed', { + phase: this.currentPhase, + progress: this.getProgress() + }); + } + + /** + * Add event listener + */ + on(eventType: WorkflowEventType, listener: WorkflowEventListener): void { + if (!this.eventListeners.has(eventType)) { + this.eventListeners.set(eventType, new Set()); + } + this.eventListeners.get(eventType)!.add(listener); + } + + /** + * Remove event listener + */ + off(eventType: WorkflowEventType, listener: WorkflowEventListener): void { + const listeners = this.eventListeners.get(eventType); + if (listeners) { + listeners.delete(listener); + } + } + + /** + * Emit workflow event + */ + private emit( + eventType: WorkflowEventType, + data?: Record<string, unknown> + ): void { + const eventData: WorkflowEventData = { + type: eventType, + timestamp: new Date(), + phase: this.currentPhase, + tddPhase: this.context.currentTDDPhase, + subtaskId: this.getCurrentSubtaskId(), + data: { + ...data, + adapters: { + testValidator: !!this.testResultValidator, + gitHook: !!this.gitOperationHook, + executeHook: !!this.executeHook + } + } + }; + + const listeners = this.eventListeners.get(eventType); + if (listeners) { + listeners.forEach((listener) => listener(eventData)); + } + } + + /** + * Get current subtask ID + */ + private getCurrentSubtaskId(): string | undefined { + const currentSubtask = + this.context.subtasks[this.context.currentSubtaskIndex]; + return currentSubtask?.id; + } + + /** + * Register callback for state persistence + */ + onStatePersist( + callback: (state: WorkflowState) => void | Promise<void> + ): void { + this.persistCallback = callback; + } + + /** + * Enable auto-persistence after each transition + */ + enableAutoPersist( + callback: (state: WorkflowState) => void | Promise<void> + ): void { + this.persistCallback = callback; + this.autoPersistEnabled = true; + } + + /** + * Disable auto-persistence + */ + disableAutoPersist(): void { + this.autoPersistEnabled = false; + } + + /** + * Manually persist current state + */ + async persistState(): Promise<void> { + if (this.persistCallback) { + await this.persistCallback(this.getState()); + } + this.emit('state:persisted'); + } + + /** + * Trigger auto-persistence if enabled + */ + private async triggerAutoPersist(): Promise<void> { + if (this.autoPersistEnabled && this.persistCallback) { + await this.persistCallback(this.getState()); + } + } + + /** + * Add a guard condition for a specific phase + */ + addGuard( + phase: WorkflowPhase, + guard: (context: WorkflowContext) => boolean + ): void { + this.phaseGuards.set(phase, guard); + } + + /** + * Remove a guard condition for a specific phase + */ + removeGuard(phase: WorkflowPhase): void { + this.phaseGuards.delete(phase); + } + + /** + * Get current subtask being worked on + */ + getCurrentSubtask(): SubtaskInfo | undefined { + return this.context.subtasks[this.context.currentSubtaskIndex]; + } + + /** + * Get workflow progress information + */ + getProgress(): { + completed: number; + total: number; + current: number; + percentage: number; + } { + const completed = this.context.subtasks.filter( + (st) => st.status === 'completed' + ).length; + const total = this.context.subtasks.length; + const current = this.context.currentSubtaskIndex + 1; + const percentage = total > 0 ? Math.round((completed / total) * 100) : 0; + + return { completed, total, current, percentage }; + } + + /** + * Check if can proceed to next subtask or phase + */ + canProceed(): boolean { + if (this.currentPhase !== 'SUBTASK_LOOP') { + return false; + } + + const currentSubtask = this.getCurrentSubtask(); + + // Can proceed if current subtask is completed (after COMMIT phase) + return currentSubtask?.status === 'completed'; + } + + /** + * Increment attempts for current subtask + */ + incrementAttempts(): void { + const currentSubtask = this.getCurrentSubtask(); + if (currentSubtask) { + currentSubtask.attempts++; + } + } + + /** + * Check if current subtask has exceeded max attempts + */ + hasExceededMaxAttempts(): boolean { + const currentSubtask = this.getCurrentSubtask(); + if (!currentSubtask || !currentSubtask.maxAttempts) { + return false; + } + + return currentSubtask.attempts > currentSubtask.maxAttempts; + } + + /** + * Handle error event + */ + private handleError(error: import('./types.js').WorkflowError): void { + this.context.errors.push(error); + this.emit('error:occurred', { error }); + } + + /** + * Handle retry event + */ + private handleRetry(): void { + if (this.currentPhase === 'SUBTASK_LOOP') { + // Reset to RED phase to retry current subtask + this.context.currentTDDPhase = 'RED'; + this.emit('tdd:red:started'); + } + } + + /** + * Retry current subtask (resets to RED phase) + */ + retryCurrentSubtask(): void { + if (this.currentPhase === 'SUBTASK_LOOP') { + this.context.currentTDDPhase = 'RED'; + this.emit('tdd:red:started'); + } + } + + /** + * Handle max attempts exceeded for current subtask + */ + handleMaxAttemptsExceeded(): void { + const currentSubtask = this.getCurrentSubtask(); + if (currentSubtask) { + currentSubtask.status = 'failed'; + this.emit('subtask:failed', { + subtaskId: currentSubtask.id, + attempts: currentSubtask.attempts, + maxAttempts: currentSubtask.maxAttempts + }); + } + } + + /** + * Check if workflow has been aborted + */ + isAborted(): boolean { + return this.aborted; + } + + /** + * Validate if a state can be resumed from + */ + canResumeFromState(state: WorkflowState): boolean { + // Validate phase is valid + const validPhases: WorkflowPhase[] = [ + 'PREFLIGHT', + 'BRANCH_SETUP', + 'SUBTASK_LOOP', + 'FINALIZE', + 'COMPLETE' + ]; + + if (!validPhases.includes(state.phase)) { + return false; + } + + // Validate context structure + if (!state.context || typeof state.context !== 'object') { + return false; + } + + // Validate required context fields + if (!state.context.taskId || !Array.isArray(state.context.subtasks)) { + return false; + } + + if (typeof state.context.currentSubtaskIndex !== 'number') { + return false; + } + + if (!Array.isArray(state.context.errors)) { + return false; + } + + // All validations passed + return true; + } + + /** + * Set TestResultValidator adapter + */ + setTestResultValidator(validator: TestResultValidator): void { + this.testResultValidator = validator; + this.emit('adapter:configured', { adapterType: 'test-validator' }); + } + + /** + * Check if TestResultValidator is configured + */ + hasTestResultValidator(): boolean { + return !!this.testResultValidator; + } + + /** + * Remove TestResultValidator adapter + */ + removeTestResultValidator(): void { + this.testResultValidator = undefined; + } + + /** + * Register git operation hook + */ + onGitOperation(hook: (operation: string, data?: unknown) => void): void { + this.gitOperationHook = hook; + } + + /** + * Register execute command hook + */ + onExecute(hook: (command: string, context: WorkflowContext) => void): void { + this.executeHook = hook; + } + + /** + * Execute a command (triggers execute hook) + */ + executeCommand(command: string): void { + if (this.executeHook) { + this.executeHook(command, this.context); + } + } +} diff --git a/packages/tm-core/src/workflow/workflow-state-manager.spec.ts b/packages/tm-core/src/workflow/workflow-state-manager.spec.ts new file mode 100644 index 00000000..6c7045b1 --- /dev/null +++ b/packages/tm-core/src/workflow/workflow-state-manager.spec.ts @@ -0,0 +1,146 @@ +/** + * @fileoverview Tests for WorkflowStateManager path sanitization + */ + +import { describe, it, expect } from 'vitest'; +import { WorkflowStateManager } from './workflow-state-manager.js'; +import os from 'node:os'; +import path from 'node:path'; + +describe('WorkflowStateManager', () => { + describe('getProjectIdentifier', () => { + it('should sanitize paths like Claude Code', () => { + const projectRoot = + '/Volumes/Workspace/workspace/contrib/task-master/demos/nextjs-todo-tdd'; + const manager = new WorkflowStateManager(projectRoot); + + const sessionDir = manager.getSessionDir(); + const homeDir = os.homedir(); + + // Expected structure: ~/.taskmaster/{project-id}/sessions/ + const expectedPath = path.join( + homeDir, + '.taskmaster', + '-Volumes-Workspace-workspace-contrib-task-master-demos-nextjs-todo-tdd', + 'sessions' + ); + + expect(sessionDir).toBe(expectedPath); + }); + + it('should preserve case in paths', () => { + const projectRoot = '/Users/Alice/Projects/MyApp'; + const manager = new WorkflowStateManager(projectRoot); + + const sessionDir = manager.getSessionDir(); + // Extract project ID from: ~/.taskmaster/{project-id}/sessions/ + const projectId = sessionDir.split(path.sep).slice(-2, -1)[0]; + + // Case should be preserved + expect(projectId).toContain('Users'); + expect(projectId).toContain('Alice'); + expect(projectId).toContain('Projects'); + expect(projectId).toContain('MyApp'); + }); + + it('should handle paths with special characters', () => { + const projectRoot = '/tmp/my-project_v2.0/test'; + const manager = new WorkflowStateManager(projectRoot); + + const sessionDir = manager.getSessionDir(); + // Extract project ID from: ~/.taskmaster/{project-id}/sessions/ + const projectId = sessionDir.split(path.sep).slice(-2, -1)[0]; + + // Special chars should be replaced with dashes + expect(projectId).toBe('-tmp-my-project-v2-0-test'); + }); + + it('should create unique identifiers for different paths', () => { + const project1 = '/Users/alice/task-master'; + const project2 = '/Users/bob/task-master'; + + const manager1 = new WorkflowStateManager(project1); + const manager2 = new WorkflowStateManager(project2); + + // Extract project IDs from: ~/.taskmaster/{project-id}/sessions/ + const id1 = manager1.getSessionDir().split(path.sep).slice(-2, -1)[0]; + const id2 = manager2.getSessionDir().split(path.sep).slice(-2, -1)[0]; + + // Same basename but different full paths should be unique + expect(id1).not.toBe(id2); + expect(id1).toContain('alice'); + expect(id2).toContain('bob'); + }); + + it('should collapse multiple dashes', () => { + const projectRoot = '/path//with///multiple////slashes'; + const manager = new WorkflowStateManager(projectRoot); + + const sessionDir = manager.getSessionDir(); + // Extract project ID from: ~/.taskmaster/{project-id}/sessions/ + const projectId = sessionDir.split(path.sep).slice(-2, -1)[0]; + + // Multiple dashes should be collapsed to single dash + expect(projectId).not.toContain('--'); + expect(projectId).toBe('-path-with-multiple-slashes'); + }); + + it('should not have trailing dashes', () => { + const projectRoot = '/path/to/project'; + const manager = new WorkflowStateManager(projectRoot); + + const sessionDir = manager.getSessionDir(); + // Extract project ID from: ~/.taskmaster/{project-id}/sessions/ + const projectId = sessionDir.split(path.sep).slice(-2, -1)[0]; + + // Should not end with dash + expect(projectId).not.toMatch(/-$/); + }); + + it('should start with a dash like Claude Code', () => { + const projectRoot = '/any/path'; + const manager = new WorkflowStateManager(projectRoot); + + const sessionDir = manager.getSessionDir(); + // Extract project ID from: ~/.taskmaster/{project-id}/sessions/ + const projectId = sessionDir.split(path.sep).slice(-2, -1)[0]; + + // Should start with dash like Claude Code's pattern + expect(projectId).toMatch(/^-/); + }); + }); + + describe('session paths', () => { + it('should place sessions in global ~/.taskmaster/{project-id}/sessions/', () => { + const projectRoot = '/some/project'; + const manager = new WorkflowStateManager(projectRoot); + + const sessionDir = manager.getSessionDir(); + const homeDir = os.homedir(); + + // Should be: ~/.taskmaster/{project-id}/sessions/ + expect(sessionDir).toContain(path.join(homeDir, '.taskmaster')); + expect(sessionDir).toMatch(/\.taskmaster\/.*\/sessions$/); + }); + + it('should include workflow-state.json in session dir', () => { + const projectRoot = '/some/project'; + const manager = new WorkflowStateManager(projectRoot); + + const statePath = manager.getStatePath(); + const sessionDir = manager.getSessionDir(); + + expect(statePath).toBe(path.join(sessionDir, 'workflow-state.json')); + }); + + it('should include backups dir in session dir', () => { + const projectRoot = '/some/project'; + const manager = new WorkflowStateManager(projectRoot); + + const backupDir = manager.getBackupDir(); + const sessionDir = manager.getSessionDir(); + + expect(backupDir).toBe(path.join(sessionDir, 'backups')); + }); + }); +}); diff --git a/packages/tm-core/src/workflow/workflow-state-manager.ts b/packages/tm-core/src/workflow/workflow-state-manager.ts new file mode 100644 index 00000000..02e13409 --- /dev/null +++ b/packages/tm-core/src/workflow/workflow-state-manager.ts @@ -0,0 +1,263 @@ +/** + * @fileoverview WorkflowStateManager - Manages persistence of TDD workflow state + * + * Stores workflow state in global user directory (~/.taskmaster/{project-id}/sessions/) + * to avoid git conflicts and support multiple worktrees. + * Each project gets its own directory for organizing workflow-related data. + */ + +import { promises as fs } from 'node:fs'; +import path from 'node:path'; +import os from 'node:os'; +import type { WorkflowState } from './types.js'; +import { getLogger } from '../logger/index.js'; + +export interface WorkflowStateBackup { + timestamp: string; + state: WorkflowState; +} + +/** + * Manages workflow state persistence with backup support + * Stores state in global user directory to avoid git noise + */ +export class WorkflowStateManager { + private readonly projectRoot: string; + private readonly statePath: string; + private readonly backupDir: string; + private readonly sessionDir: string; + private maxBackups: number; + private readonly logger = getLogger('WorkflowStateManager'); + + constructor(projectRoot: string, maxBackups = 5) { + this.projectRoot = path.resolve(projectRoot); + this.maxBackups = maxBackups; + + // Create project-specific directory in global .taskmaster + // Structure: ~/.taskmaster/{project-id}/sessions/ + const projectId = this.getProjectIdentifier(this.projectRoot); + const homeDir = os.homedir(); + const projectDir = path.join(homeDir, '.taskmaster', projectId); + this.sessionDir = path.join(projectDir, 'sessions'); + + this.statePath = path.join(this.sessionDir, 'workflow-state.json'); + this.backupDir = path.join(this.sessionDir, 'backups'); + } + + /** + * Generate a unique identifier for the project using full sanitized path + * Uses Claude Code's pattern: leading dash + full path with case preserved + * Example: /Volumes/Workspace/... -> -Volumes-Workspace-... + */ + private getProjectIdentifier(projectRoot: string): string { + // Resolve to absolute path + const absolutePath = path.resolve(projectRoot); + + // Sanitize path like Claude Code does: + // - Add leading dash + // - Replace path separators and non-alphanumeric chars with dashes + // - Preserve case for readability + // - Collapse multiple dashes + const sanitized = + '-' + + absolutePath + .replace(/^\//, '') // Remove leading slash before adding dash + .replace(/[^a-zA-Z0-9]+/g, '-') // Replace sequences of non-alphanumeric with single dash + .replace(/-+/g, '-') // Collapse multiple dashes + .replace(/-+$/, ''); // Remove trailing dashes + + return sanitized; + } + + /** + * Check if workflow state exists + */ + async exists(): Promise<boolean> { + try { + await fs.access(this.statePath); + return true; + } catch { + return false; + } + } + + /** + * Load workflow state from disk + */ + async load(): Promise<WorkflowState> { + try { + const content = await fs.readFile(this.statePath, 'utf-8'); + return JSON.parse(content) as WorkflowState; + } catch (error: any) { + if (error.code === 'ENOENT') { + throw new Error(`Workflow state file not found at ${this.statePath}`); + } + throw new Error(`Failed to load workflow state: ${error.message}`); + } + } + + /** + * Save workflow state to disk + */ + async save(state: WorkflowState): Promise<void> { + try { + // Ensure session directory exists + await fs.mkdir(this.sessionDir, { recursive: true }); + + // Serialize and validate JSON + const jsonContent = JSON.stringify(state, null, 2); + + // Validate that the JSON is well-formed by parsing it back + try { + JSON.parse(jsonContent); + } catch (parseError) { + this.logger.error('Generated invalid JSON:', jsonContent); + throw new Error('Failed to generate valid JSON from workflow state'); + } + + // Write state atomically with newline at end + const tempPath = `${this.statePath}.tmp`; + await fs.writeFile(tempPath, jsonContent + '\n', 'utf-8'); + await fs.rename(tempPath, this.statePath); + + this.logger.debug(`Saved workflow state (${jsonContent.length} bytes)`); + } catch (error: any) { + throw new Error(`Failed to save workflow state: ${error.message}`); + } + } + + /** + * Create a backup of current state + */ + async createBackup(): Promise<void> { + try { + const exists = await this.exists(); + if (!exists) { + return; + } + + const state = await this.load(); + await fs.mkdir(this.backupDir, { recursive: true }); + + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const backupPath = path.join( + this.backupDir, + `workflow-state-${timestamp}.json` + ); + + const backup: WorkflowStateBackup = { + timestamp: new Date().toISOString(), + state + }; + + await fs.writeFile(backupPath, JSON.stringify(backup, null, 2), 'utf-8'); + + // Clean up old backups + await this.pruneBackups(); + } catch (error: any) { + throw new Error(`Failed to create backup: ${error.message}`); + } + } + + /** + * Delete workflow state file + */ + async delete(): Promise<void> { + try { + await fs.unlink(this.statePath); + } catch (error: any) { + if (error.code !== 'ENOENT') { + throw new Error(`Failed to delete workflow state: ${error.message}`); + } + } + } + + /** + * List available backups + */ + async listBackups(): Promise<string[]> { + try { + const files = await fs.readdir(this.backupDir); + return files + .filter((f) => f.startsWith('workflow-state-') && f.endsWith('.json')) + .sort() + .reverse(); + } catch (error: any) { + if (error.code === 'ENOENT') { + return []; + } + throw new Error(`Failed to list backups: ${error.message}`); + } + } + + /** + * Restore from a backup + */ + async restoreBackup(backupFileName: string): Promise<void> { + try { + const backupPath = path.join(this.backupDir, backupFileName); + const content = await fs.readFile(backupPath, 'utf-8'); + const backup: WorkflowStateBackup = JSON.parse(content); + + await this.save(backup.state); + } catch (error: any) { + throw new Error(`Failed to restore backup: ${error.message}`); + } + } + + /** + * Prune old backups to maintain max backup count + */ + private async pruneBackups(): Promise<void> { + try { + const backups = await this.listBackups(); + + if (backups.length > this.maxBackups) { + const toDelete = backups.slice(this.maxBackups); + + for (const backup of toDelete) { + await fs.unlink(path.join(this.backupDir, backup)); + } + } + } catch (error: any) { + // Non-critical error, log but don't throw + this.logger.warn(`Failed to prune backups: ${error.message}`); + } + } + + /** + * Get the path to the state file (for debugging/testing) + */ + getStatePath(): string { + return this.statePath; + } + + /** + * Get the path to the backup directory (for debugging/testing) + */ + getBackupDir(): string { + return this.backupDir; + } + + /** + * Get the session directory path (for debugging/testing) + */ + getSessionDir(): string { + return this.sessionDir; + } + + /** + * Get the project root this manager is for + */ + getProjectRoot(): string { + return this.projectRoot; + } + + /** + * Get the path to the activity log file + * Activity log is stored next to workflow-state.json for correlation + */ + getActivityLogPath(): string { + return path.join(this.sessionDir, 'activity.jsonl'); + } +} diff --git a/packages/tm-core/tests/integration/storage/activity-logger.test.ts b/packages/tm-core/tests/integration/storage/activity-logger.test.ts new file mode 100644 index 00000000..8a0b0f2d --- /dev/null +++ b/packages/tm-core/tests/integration/storage/activity-logger.test.ts @@ -0,0 +1,401 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import fs from 'fs-extra'; +import path from 'path'; +import os from 'os'; +import { + logActivity, + readActivityLog, + filterActivityLog +} from '../../../src/storage/activity-logger.js'; + +describe('Activity Logger', () => { + let testDir: string; + let activityPath: string; + + beforeEach(async () => { + // Create a unique temporary test directory + const prefix = path.join(os.tmpdir(), 'activity-test-'); + testDir = await fs.mkdtemp(prefix); + activityPath = path.join(testDir, 'activity.jsonl'); + }); + + afterEach(async () => { + // Clean up test directory + await fs.remove(testDir); + }); + + describe('logActivity', () => { + it('should create activity log file on first write', async () => { + await logActivity(activityPath, { + type: 'phase-start', + phase: 'red', + data: {} + }); + + const exists = await fs.pathExists(activityPath); + expect(exists).toBe(true); + }); + + it('should append event to log file', async () => { + await logActivity(activityPath, { + type: 'phase-start', + phase: 'red' + }); + + const content = await fs.readFile(activityPath, 'utf-8'); + const lines = content.trim().split(/\r?\n/); + + expect(lines.length).toBe(1); + }); + + it('should write valid JSONL format', async () => { + await logActivity(activityPath, { + type: 'test-run', + result: 'pass' + }); + + const content = await fs.readFile(activityPath, 'utf-8'); + const line = content.trim(); + const parsed = JSON.parse(line); + + expect(parsed).toBeDefined(); + expect(parsed.type).toBe('test-run'); + }); + + it('should include timestamp in log entry', async () => { + const before = new Date().toISOString(); + await logActivity(activityPath, { + type: 'phase-start', + phase: 'red' + }); + const after = new Date().toISOString(); + + const logs = await readActivityLog(activityPath); + expect(logs[0].timestamp).toBeDefined(); + expect(logs[0].timestamp >= before).toBe(true); + expect(logs[0].timestamp <= after).toBe(true); + }); + + it('should append multiple events', async () => { + await logActivity(activityPath, { type: 'event1' }); + await logActivity(activityPath, { type: 'event2' }); + await logActivity(activityPath, { type: 'event3' }); + + const logs = await readActivityLog(activityPath); + expect(logs.length).toBe(3); + expect(logs[0].type).toBe('event1'); + expect(logs[1].type).toBe('event2'); + expect(logs[2].type).toBe('event3'); + }); + + it('should preserve event data', async () => { + const eventData = { + type: 'git-commit', + hash: 'abc123', + message: 'test commit', + files: ['file1.ts', 'file2.ts'] + }; + + await logActivity(activityPath, eventData); + + const logs = await readActivityLog(activityPath); + expect(logs[0].type).toBe('git-commit'); + expect(logs[0].hash).toBe('abc123'); + expect(logs[0].message).toBe('test commit'); + expect(logs[0].files).toEqual(['file1.ts', 'file2.ts']); + }); + + it('should handle nested objects in event data', async () => { + await logActivity(activityPath, { + type: 'test-results', + results: { + passed: 10, + failed: 2, + details: { coverage: 85 } + } + }); + + const logs = await readActivityLog(activityPath); + expect(logs[0].results.details.coverage).toBe(85); + }); + + it('should handle special characters in event data', async () => { + await logActivity(activityPath, { + type: 'error', + message: 'Error: "Something went wrong"\nLine 2' + }); + + const logs = await readActivityLog(activityPath); + expect(logs[0].message).toBe('Error: "Something went wrong"\nLine 2'); + }); + + it('should create parent directory if it does not exist', async () => { + const nestedPath = path.join(testDir, 'nested', 'dir', 'activity.jsonl'); + + await logActivity(nestedPath, { type: 'test' }); + + const exists = await fs.pathExists(nestedPath); + expect(exists).toBe(true); + }); + }); + + describe('readActivityLog', () => { + it('should read all events from log', async () => { + await logActivity(activityPath, { type: 'event1' }); + await logActivity(activityPath, { type: 'event2' }); + + const logs = await readActivityLog(activityPath); + + expect(logs.length).toBe(2); + expect(logs[0].type).toBe('event1'); + expect(logs[1].type).toBe('event2'); + }); + + it('should return empty array for non-existent file', async () => { + const logs = await readActivityLog(activityPath); + expect(logs).toEqual([]); + }); + + it('should parse JSONL correctly', async () => { + await logActivity(activityPath, { type: 'event1', data: 'test1' }); + await logActivity(activityPath, { type: 'event2', data: 'test2' }); + + const logs = await readActivityLog(activityPath); + + expect(logs[0].data).toBe('test1'); + expect(logs[1].data).toBe('test2'); + }); + + it('should handle empty lines', async () => { + await fs.writeFile( + activityPath, + '{"type":"event1"}\n\n{"type":"event2"}\n' + ); + + const logs = await readActivityLog(activityPath); + + expect(logs.length).toBe(2); + expect(logs[0].type).toBe('event1'); + expect(logs[1].type).toBe('event2'); + }); + + it('should throw error for invalid JSON line', async () => { + await fs.writeFile(activityPath, '{"type":"event1"}\ninvalid json\n'); + + await expect(readActivityLog(activityPath)).rejects.toThrow( + /Invalid JSON/i + ); + }); + + it('should preserve chronological order', async () => { + for (let i = 0; i < 10; i++) { + await logActivity(activityPath, { type: 'event', index: i }); + } + + const logs = await readActivityLog(activityPath); + + for (let i = 0; i < 10; i++) { + expect(logs[i].index).toBe(i); + } + }); + }); + + describe('filterActivityLog', () => { + beforeEach(async () => { + // Create sample log entries + await logActivity(activityPath, { type: 'phase-start', phase: 'red' }); + await logActivity(activityPath, { type: 'test-run', result: 'fail' }); + await logActivity(activityPath, { type: 'phase-start', phase: 'green' }); + await logActivity(activityPath, { type: 'test-run', result: 'pass' }); + await logActivity(activityPath, { type: 'git-commit', hash: 'abc123' }); + }); + + it('should filter by event type', async () => { + const filtered = await filterActivityLog(activityPath, { + type: 'phase-start' + }); + + expect(filtered.length).toBe(2); + expect(filtered[0].type).toBe('phase-start'); + expect(filtered[1].type).toBe('phase-start'); + }); + + it('should filter by multiple criteria', async () => { + const filtered = await filterActivityLog(activityPath, { + type: 'test-run', + result: 'pass' + }); + + expect(filtered.length).toBe(1); + expect(filtered[0].result).toBe('pass'); + }); + + it('should return all events when no filter provided', async () => { + const filtered = await filterActivityLog(activityPath, {}); + + expect(filtered.length).toBe(5); + }); + + it('should filter by timestamp range', async () => { + const logs = await readActivityLog(activityPath); + const midpoint = logs[2].timestamp; + + const filtered = await filterActivityLog(activityPath, { + timestampFrom: midpoint + }); + + // Should get events from midpoint onwards (inclusive) + // Expect at least 3 events, may be more due to timestamp collisions + expect(filtered.length).toBeGreaterThanOrEqual(3); + expect(filtered.length).toBeLessThanOrEqual(5); + }); + + it('should filter by custom predicate', async () => { + const filtered = await filterActivityLog(activityPath, { + predicate: (event: any) => event.phase === 'red' + }); + + expect(filtered.length).toBe(1); + expect(filtered[0].phase).toBe('red'); + }); + + it('should return empty array for non-matching filter', async () => { + const filtered = await filterActivityLog(activityPath, { + type: 'non-existent' + }); + + expect(filtered).toEqual([]); + }); + + it('should handle nested property filters', async () => { + await logActivity(activityPath, { + type: 'test-results', + results: { coverage: 85 } + }); + + const filtered = await filterActivityLog(activityPath, { + predicate: (event: any) => event.results?.coverage > 80 + }); + + expect(filtered.length).toBe(1); + expect(filtered[0].results.coverage).toBe(85); + }); + }); + + describe('Event types', () => { + it('should support phase-transition events', async () => { + await logActivity(activityPath, { + type: 'phase-transition', + from: 'red', + to: 'green' + }); + + const logs = await readActivityLog(activityPath); + expect(logs[0].type).toBe('phase-transition'); + expect(logs[0].from).toBe('red'); + expect(logs[0].to).toBe('green'); + }); + + it('should support test-run events', async () => { + await logActivity(activityPath, { + type: 'test-run', + result: 'pass', + testsRun: 50, + testsPassed: 50, + testsFailed: 0, + coverage: 85.5 + }); + + const logs = await readActivityLog(activityPath); + expect(logs[0].testsRun).toBe(50); + expect(logs[0].coverage).toBe(85.5); + }); + + it('should support git-operation events', async () => { + await logActivity(activityPath, { + type: 'git-commit', + hash: 'abc123def456', + message: 'feat: add new feature', + files: ['file1.ts', 'file2.ts'] + }); + + const logs = await readActivityLog(activityPath); + expect(logs[0].hash).toBe('abc123def456'); + expect(logs[0].files.length).toBe(2); + }); + + it('should support error events', async () => { + await logActivity(activityPath, { + type: 'error', + phase: 'red', + error: 'Test failed', + stack: 'Error stack trace...' + }); + + const logs = await readActivityLog(activityPath); + expect(logs[0].type).toBe('error'); + expect(logs[0].error).toBe('Test failed'); + }); + }); + + describe('Concurrency handling', () => { + it('should handle rapid concurrent writes', async () => { + const writes: Promise<void>[] = []; + for (let i = 0; i < 50; i++) { + writes.push(logActivity(activityPath, { type: 'event', index: i })); + } + + await Promise.all(writes); + + const logs = await readActivityLog(activityPath); + expect(logs.length).toBe(50); + }); + + it('should maintain data integrity with concurrent writes', async () => { + const writes: Promise<void>[] = []; + for (let i = 0; i < 20; i++) { + writes.push( + logActivity(activityPath, { + type: 'concurrent-test', + id: i, + data: `data-${i}` + }) + ); + } + + await Promise.all(writes); + + const logs = await readActivityLog(activityPath); + + // All events should be present + expect(logs.length).toBe(20); + // Validate ids set + const ids = new Set(logs.map((l) => l.id)); + expect([...ids].sort((a, b) => a - b)).toEqual([...Array(20).keys()]); + // Validate shape + for (const log of logs) { + expect(log.type).toBe('concurrent-test'); + expect(typeof log.id).toBe('number'); + expect(log.data).toMatch(/^data-\d+$/); + } + }); + }); + + describe('File integrity', () => { + it('should maintain valid JSONL after many operations', async () => { + for (let i = 0; i < 100; i++) { + await logActivity(activityPath, { type: 'test', iteration: i }); + } + + const content = await fs.readFile(activityPath, 'utf-8'); + const lines = content.trim().split(/\r?\n/); + + expect(lines.length).toBe(100); + + // All lines should be valid JSON + for (const line of lines) { + expect(() => JSON.parse(line)).not.toThrow(); + } + }); + }); +}); diff --git a/packages/tm-core/vitest.config.ts b/packages/tm-core/vitest.config.ts index 999b4daa..b99dcf7d 100644 --- a/packages/tm-core/vitest.config.ts +++ b/packages/tm-core/vitest.config.ts @@ -22,12 +22,15 @@ export default defineConfig({ provider: 'v8', reporter: ['text', 'json', 'html', 'lcov'], exclude: [ - 'node_modules', - 'dist', - 'tests', + 'node_modules/', + 'dist/', + 'tests/', '**/*.test.ts', '**/*.spec.ts', '**/*.d.ts', + '**/mocks/**', + '**/fixtures/**', + 'vitest.config.ts', 'src/index.ts' ], thresholds: { diff --git a/scripts/create-worktree.sh b/scripts/create-worktree.sh new file mode 100755 index 00000000..f144df93 --- /dev/null +++ b/scripts/create-worktree.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# Create a git worktree for parallel Claude Code development +# Usage: ./scripts/create-worktree.sh [branch-name] + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +WORKTREES_DIR="$(cd "$PROJECT_ROOT/.." && pwd)/claude-task-master-worktrees" +cd "$PROJECT_ROOT" + +# Get branch name (default to current branch with auto/ prefix) +if [ -z "$1" ]; then + CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + if [ "$CURRENT_BRANCH" = "HEAD" ]; then + echo "Detached HEAD detected. Please specify a branch: ./scripts/create-worktree.sh <branch-name>" + exit 1 + fi + BRANCH_NAME="auto/$CURRENT_BRANCH" + echo "No branch specified, using: $BRANCH_NAME" +else + BRANCH_NAME="$1" +fi + +# Create worktrees directory if it doesn't exist +mkdir -p "$WORKTREES_DIR" + +# Sanitize branch name for directory (replace / with -) +DIR_NAME=$(echo "$BRANCH_NAME" | sed 's/\//-/g') +WORKTREE_PATH="$WORKTREES_DIR/$DIR_NAME" + +echo "Creating git worktree..." +echo " Branch: $BRANCH_NAME" +echo " Path: $WORKTREE_PATH" + +# Check if worktree already exists +if [ -d "$WORKTREE_PATH" ]; then + echo "❌ Worktree already exists at: $WORKTREE_PATH" + echo " Remove it first with: git worktree remove $WORKTREE_PATH" + exit 1 +fi + +# Create worktree (new or existing branch) +if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then + git worktree add "$WORKTREE_PATH" "$BRANCH_NAME" +elif git remote get-url origin >/dev/null 2>&1 && git ls-remote --exit-code --heads origin "$BRANCH_NAME" >/dev/null 2>&1; then + # Create a local branch from the remote and attach worktree + git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" "origin/$BRANCH_NAME" + # Ensure the new branch tracks the remote + git -C "$WORKTREE_PATH" branch --set-upstream-to="origin/$BRANCH_NAME" "$BRANCH_NAME" +else + git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" +fi + +echo "" +echo "✅ Worktree created successfully!" +echo "" +echo "📂 Location: $WORKTREE_PATH" +echo "🌿 Branch: $BRANCH_NAME" +echo "" +echo "Next steps:" +echo " 1. cd $WORKTREE_PATH" +echo " 2. Open with your AI editor:" +echo " - Cursor: cursor ." +echo " - VS Code: code ." +echo " - Windsurf: windsurf ." +echo " - Claude Code: claude" +echo "" +echo "To remove this worktree later:" +echo " git worktree remove $WORKTREE_PATH" diff --git a/scripts/list-worktrees.sh b/scripts/list-worktrees.sh new file mode 100755 index 00000000..1814de36 --- /dev/null +++ b/scripts/list-worktrees.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# List all git worktrees with helpful information + +set -e + +echo "📋 Git Worktrees:" +echo "" +git worktree list +echo "" +echo "To remove a worktree:" +echo " git worktree remove <path>" +echo "" +echo "To remove all worktrees:" +echo " git worktree list | grep -v '(bare)' | tail -n +2 | awk '{print \$1}' | xargs -I {} git worktree remove {}" diff --git a/tests/helpers/tool-counts.js b/tests/helpers/tool-counts.js index 95cbdc6f..5435923c 100644 --- a/tests/helpers/tool-counts.js +++ b/tests/helpers/tool-counts.js @@ -15,7 +15,7 @@ import { export const EXPECTED_TOOL_COUNTS = { core: 7, standard: 15, - total: 36 + total: 44 }; /** diff --git a/tsconfig.json b/tsconfig.json index 9145ac12..0883c824 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,8 @@ "@tm/core/*": ["./packages/tm-core/src/*"], "@tm/cli": ["./apps/cli/src/index.ts"], "@tm/cli/*": ["./apps/cli/src/*"], + "@tm/mcp": ["./apps/mcp/src/index.ts"], + "@tm/mcp/*": ["./apps/mcp/src/*"], "@tm/build-config": ["./packages/build-config/src/index.ts"], "@tm/build-config/*": ["./packages/build-config/src/*"], "@tm/ai-sdk-provider-grok-cli": [