@nathapp/nax 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitlab-ci.yml +96 -0
- package/BRIEF.md +140 -0
- package/CHANGELOG.md +60 -0
- package/CLAUDE.md +159 -0
- package/README.md +373 -0
- package/US-007-IMPLEMENTATION.md +139 -0
- package/bin/nax.ts +930 -0
- package/biome.json +14 -0
- package/bun.lock +168 -0
- package/bunfig.toml +11 -0
- package/docs/20260216-fix-plan-context-review.md +56 -0
- package/docs/20260216-relentless-vs-ngent-comparison.md +208 -0
- package/docs/20260216-v02-plan.md +136 -0
- package/docs/20260216-v02-review.md +685 -0
- package/docs/20260217-dogfood-findings.md +56 -0
- package/docs/20260217-p2-plus-plan.md +117 -0
- package/docs/20260217-partial-fixes-plan.md +62 -0
- package/docs/20260217-plan-analyze-spec.md +117 -0
- package/docs/20260217-post-impl-review.md +1137 -0
- package/docs/20260217-quick-wins-plan.md +66 -0
- package/docs/20260217-split-runner-plan.md +75 -0
- package/docs/20260217-v03-impl-plan.md +80 -0
- package/docs/20260217-v03-post-impl-review.md +589 -0
- package/docs/20260217-v04-impl-plan.md +86 -0
- package/docs/20260217-v05-post-impl-review.md +850 -0
- package/docs/20260217-v06-post-impl-review.md +817 -0
- package/docs/20260218-adr003-port-plan.md +151 -0
- package/docs/20260218-review-adr003-verification.md +175 -0
- package/docs/20260219-fix-plan-bug16-19.md +79 -0
- package/docs/20260219-fix-plan-bug20-22.md +114 -0
- package/docs/20260219-plan-llm-routing.md +116 -0
- package/docs/20260219-review-bug20-22-fixes.md +135 -0
- package/docs/20260219-routing-baseline-keyword.md +63 -0
- package/docs/20260220-plan-structured-logging-p1.md +80 -0
- package/docs/20260220-plan-structured-logging-p2.md +37 -0
- package/docs/20260220-review-llm-routing.md +180 -0
- package/docs/20260220-review-post-fix-llm-routing.md +70 -0
- package/docs/20260221-fix-plan-relevantfiles-split.md +101 -0
- package/docs/20260221-fix-plan-routing-mode.md +125 -0
- package/docs/20260221-review-v0.9-implementation.md +379 -0
- package/docs/20260222-fix-plan-v091-routing-isolation.md +197 -0
- package/docs/20260223-fix-plan-prompt-audit.md +62 -0
- package/docs/20260224-nax-roadmap-phases.md +189 -0
- package/docs/20260225-phase2-llm-service-layer.md +401 -0
- package/docs/20260225-review-v0.10.1.md +187 -0
- package/docs/20260303-v010-implementation-plan.md +165 -0
- package/docs/CLAUDE.md.bak +191 -0
- package/docs/ROADMAP.md +165 -0
- package/docs/SPEC-rectification.md +0 -0
- package/docs/SPEC.md +324 -0
- package/docs/US-001-plugin-loading-verification.md +152 -0
- package/docs/architecture-analysis.md +1076 -0
- package/docs/bugs/BUG-21-escalation-null-attempts.md +48 -0
- package/docs/bugs-from-dogfood-run-c.md +243 -0
- package/docs/code-review-20260228.md +612 -0
- package/docs/code-review-v0.15.0.md +629 -0
- package/docs/hook-lifecycle-test-plan.md +149 -0
- package/docs/releases/v0.11.0-and-earlier.md +20 -0
- package/docs/releases/v0.12.0.md +15 -0
- package/docs/releases/v0.13.0.md +14 -0
- package/docs/releases/v0.14.0.md +20 -0
- package/docs/releases/v0.14.1.md +36 -0
- package/docs/releases/v0.14.2.md +51 -0
- package/docs/releases/v0.14.3.md +174 -0
- package/docs/releases/v0.14.4.md +94 -0
- package/docs/releases/v0.15.0.md +502 -0
- package/docs/releases/v0.15.1.md +170 -0
- package/docs/releases/v0.15.3.md +193 -0
- package/docs/specs/status-file-v0.10.1.md +812 -0
- package/docs/v0.10-global-config.md +206 -0
- package/docs/v0.10-plugin-system.md +415 -0
- package/docs/v0.10-prompt-optimizer.md +234 -0
- package/docs/v0.3-spec.md +244 -0
- package/docs/v0.4-spec.md +140 -0
- package/docs/v0.5-spec.md +237 -0
- package/docs/v0.6-spec.md +371 -0
- package/docs/v0.7-spec.md +177 -0
- package/docs/v0.8-llm-routing.md +206 -0
- package/docs/v0.8-structured-logging.md +132 -0
- package/docs/v0.9.3-prompt-audit.md +112 -0
- package/examples/plugins/console-reporter/index.test.ts +207 -0
- package/examples/plugins/console-reporter/index.ts +110 -0
- package/nax/config.json +147 -0
- package/nax/features/bugfix-v0171/prd.json +52 -0
- package/nax/features/config-management/prd.json +108 -0
- package/nax/features/config-management/progress.txt +5 -0
- package/nax/features/diagnose/acceptance.test.ts +412 -0
- package/nax/features/diagnose/prd.json +41 -0
- package/nax/features/orchestration-fixes/prd.json +89 -0
- package/nax/features/orchestration-fixes/progress.txt +1 -0
- package/nax/features/plugin-integration/US-007-VERIFICATION.md +259 -0
- package/nax/features/plugin-integration/prd.json +208 -0
- package/nax/features/plugin-integration/progress.txt +5 -0
- package/nax/features/precheck/prd.json +205 -0
- package/nax/features/precheck/progress.txt +15 -0
- package/nax/features/structured-logging/prd.json +199 -0
- package/nax/features/unlock/prd.json +36 -0
- package/package.json +47 -0
- package/src/acceptance/fix-generator.ts +348 -0
- package/src/acceptance/generator.ts +282 -0
- package/src/acceptance/index.ts +30 -0
- package/src/acceptance/types.ts +79 -0
- package/src/agents/claude-decompose.ts +169 -0
- package/src/agents/claude-plan.ts +139 -0
- package/src/agents/claude.ts +324 -0
- package/src/agents/cost.ts +268 -0
- package/src/agents/index.ts +13 -0
- package/src/agents/registry.ts +48 -0
- package/src/agents/types-extended.ts +133 -0
- package/src/agents/types.ts +113 -0
- package/src/agents/validation.ts +69 -0
- package/src/analyze/classifier.ts +305 -0
- package/src/analyze/index.ts +16 -0
- package/src/analyze/scanner.ts +175 -0
- package/src/analyze/types.ts +51 -0
- package/src/cli/accept.ts +108 -0
- package/src/cli/analyze-parser.ts +284 -0
- package/src/cli/analyze.ts +207 -0
- package/src/cli/config.ts +561 -0
- package/src/cli/constitution.ts +109 -0
- package/src/cli/diagnose-analysis.ts +159 -0
- package/src/cli/diagnose-formatter.ts +87 -0
- package/src/cli/diagnose.ts +203 -0
- package/src/cli/generate.ts +127 -0
- package/src/cli/index.ts +37 -0
- package/src/cli/init.ts +188 -0
- package/src/cli/interact.ts +295 -0
- package/src/cli/plan.ts +198 -0
- package/src/cli/plugins.ts +111 -0
- package/src/cli/prompts.ts +295 -0
- package/src/cli/runs.ts +174 -0
- package/src/cli/status-cost.ts +151 -0
- package/src/cli/status-features.ts +338 -0
- package/src/cli/status.ts +13 -0
- package/src/commands/common.ts +171 -0
- package/src/commands/diagnose.ts +17 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/logs.ts +384 -0
- package/src/commands/precheck.ts +86 -0
- package/src/commands/unlock.ts +96 -0
- package/src/config/defaults.ts +160 -0
- package/src/config/index.ts +22 -0
- package/src/config/loader.ts +121 -0
- package/src/config/merger.ts +147 -0
- package/src/config/path-security.ts +121 -0
- package/src/config/paths.ts +27 -0
- package/src/config/schema.ts +56 -0
- package/src/config/schemas.ts +286 -0
- package/src/config/types.ts +423 -0
- package/src/config/validate.ts +103 -0
- package/src/constitution/generator.ts +191 -0
- package/src/constitution/generators/aider.ts +41 -0
- package/src/constitution/generators/claude.ts +35 -0
- package/src/constitution/generators/cursor.ts +36 -0
- package/src/constitution/generators/opencode.ts +38 -0
- package/src/constitution/generators/types.ts +33 -0
- package/src/constitution/generators/windsurf.ts +36 -0
- package/src/constitution/index.ts +10 -0
- package/src/constitution/loader.ts +133 -0
- package/src/constitution/types.ts +31 -0
- package/src/context/auto-detect.ts +227 -0
- package/src/context/builder.ts +246 -0
- package/src/context/elements.ts +83 -0
- package/src/context/formatter.ts +107 -0
- package/src/context/generator.ts +129 -0
- package/src/context/generators/aider.ts +34 -0
- package/src/context/generators/claude.ts +28 -0
- package/src/context/generators/cursor.ts +28 -0
- package/src/context/generators/opencode.ts +30 -0
- package/src/context/generators/windsurf.ts +28 -0
- package/src/context/greenfield.ts +114 -0
- package/src/context/index.ts +33 -0
- package/src/context/injector.ts +279 -0
- package/src/context/test-scanner.ts +370 -0
- package/src/context/types.ts +98 -0
- package/src/errors.ts +67 -0
- package/src/execution/batching.ts +157 -0
- package/src/execution/crash-recovery.ts +373 -0
- package/src/execution/escalation/escalation.ts +44 -0
- package/src/execution/escalation/index.ts +13 -0
- package/src/execution/escalation/tier-escalation.ts +295 -0
- package/src/execution/escalation/tier-outcome.ts +158 -0
- package/src/execution/helpers.ts +38 -0
- package/src/execution/index.ts +45 -0
- package/src/execution/lifecycle/acceptance-loop.ts +272 -0
- package/src/execution/lifecycle/headless-formatter.ts +85 -0
- package/src/execution/lifecycle/index.ts +12 -0
- package/src/execution/lifecycle/parallel-lifecycle.ts +101 -0
- package/src/execution/lifecycle/precheck-runner.ts +140 -0
- package/src/execution/lifecycle/run-cleanup.ts +81 -0
- package/src/execution/lifecycle/run-completion.ts +129 -0
- package/src/execution/lifecycle/run-initialization.ts +141 -0
- package/src/execution/lifecycle/run-lifecycle.ts +312 -0
- package/src/execution/lifecycle/run-setup.ts +204 -0
- package/src/execution/lifecycle/story-hooks.ts +38 -0
- package/src/execution/lifecycle/story-size-prompts.ts +123 -0
- package/src/execution/lock.ts +115 -0
- package/src/execution/parallel-executor.ts +216 -0
- package/src/execution/parallel.ts +400 -0
- package/src/execution/pid-registry.ts +280 -0
- package/src/execution/pipeline-result-handler.ts +388 -0
- package/src/execution/post-verify-rectification.ts +188 -0
- package/src/execution/post-verify.ts +274 -0
- package/src/execution/progress.ts +25 -0
- package/src/execution/prompts.ts +127 -0
- package/src/execution/queue-handler.ts +109 -0
- package/src/execution/rectification.ts +13 -0
- package/src/execution/runner.ts +377 -0
- package/src/execution/sequential-executor.ts +388 -0
- package/src/execution/status-file.ts +264 -0
- package/src/execution/status-writer.ts +139 -0
- package/src/execution/story-context.ts +229 -0
- package/src/execution/test-output-parser.ts +14 -0
- package/src/execution/verification.ts +72 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/runner.ts +286 -0
- package/src/hooks/types.ts +67 -0
- package/src/interaction/chain.ts +154 -0
- package/src/interaction/index.ts +60 -0
- package/src/interaction/init.ts +83 -0
- package/src/interaction/plugins/auto.ts +217 -0
- package/src/interaction/plugins/cli.ts +300 -0
- package/src/interaction/plugins/telegram.ts +384 -0
- package/src/interaction/plugins/webhook.ts +258 -0
- package/src/interaction/state.ts +171 -0
- package/src/interaction/triggers.ts +229 -0
- package/src/interaction/types.ts +163 -0
- package/src/logger/formatters.ts +84 -0
- package/src/logger/index.ts +16 -0
- package/src/logger/logger.ts +298 -0
- package/src/logger/types.ts +48 -0
- package/src/logging/formatter.ts +355 -0
- package/src/logging/index.ts +22 -0
- package/src/logging/types.ts +93 -0
- package/src/metrics/aggregator.ts +190 -0
- package/src/metrics/index.ts +14 -0
- package/src/metrics/tracker.ts +200 -0
- package/src/metrics/types.ts +109 -0
- package/src/optimizer/index.ts +62 -0
- package/src/optimizer/noop.optimizer.ts +24 -0
- package/src/optimizer/rule-based.optimizer.ts +248 -0
- package/src/optimizer/types.ts +53 -0
- package/src/pipeline/events.ts +130 -0
- package/src/pipeline/index.ts +19 -0
- package/src/pipeline/runner.ts +161 -0
- package/src/pipeline/stages/acceptance.ts +197 -0
- package/src/pipeline/stages/completion.ts +99 -0
- package/src/pipeline/stages/constitution.ts +63 -0
- package/src/pipeline/stages/context.ts +117 -0
- package/src/pipeline/stages/execution.ts +194 -0
- package/src/pipeline/stages/index.ts +62 -0
- package/src/pipeline/stages/optimizer.ts +74 -0
- package/src/pipeline/stages/prompt.ts +57 -0
- package/src/pipeline/stages/queue-check.ts +103 -0
- package/src/pipeline/stages/review.ts +181 -0
- package/src/pipeline/stages/routing.ts +81 -0
- package/src/pipeline/stages/verify.ts +100 -0
- package/src/pipeline/types.ts +167 -0
- package/src/plugins/index.ts +31 -0
- package/src/plugins/loader.ts +287 -0
- package/src/plugins/registry.ts +168 -0
- package/src/plugins/types.ts +327 -0
- package/src/plugins/validator.ts +352 -0
- package/src/prd/index.ts +172 -0
- package/src/prd/types.ts +202 -0
- package/src/precheck/checks-blockers.ts +391 -0
- package/src/precheck/checks-warnings.ts +142 -0
- package/src/precheck/checks.ts +30 -0
- package/src/precheck/index.ts +247 -0
- package/src/precheck/story-size-gate.ts +144 -0
- package/src/precheck/types.ts +31 -0
- package/src/queue/index.ts +2 -0
- package/src/queue/manager.ts +254 -0
- package/src/queue/types.ts +54 -0
- package/src/review/index.ts +8 -0
- package/src/review/runner.ts +172 -0
- package/src/review/types.ts +66 -0
- package/src/routing/builder.ts +81 -0
- package/src/routing/chain.ts +74 -0
- package/src/routing/index.ts +16 -0
- package/src/routing/loader.ts +58 -0
- package/src/routing/router.ts +303 -0
- package/src/routing/strategies/adaptive.ts +215 -0
- package/src/routing/strategies/index.ts +8 -0
- package/src/routing/strategies/keyword.ts +163 -0
- package/src/routing/strategies/llm-prompts.ts +209 -0
- package/src/routing/strategies/llm.ts +235 -0
- package/src/routing/strategies/manual.ts +50 -0
- package/src/routing/strategy.ts +99 -0
- package/src/tdd/cleanup.ts +111 -0
- package/src/tdd/index.ts +23 -0
- package/src/tdd/isolation.ts +123 -0
- package/src/tdd/orchestrator.ts +383 -0
- package/src/tdd/prompts.ts +270 -0
- package/src/tdd/rectification-gate.ts +183 -0
- package/src/tdd/session-runner.ts +179 -0
- package/src/tdd/types.ts +81 -0
- package/src/tdd/verdict.ts +271 -0
- package/src/tui/App.tsx +265 -0
- package/src/tui/components/AgentPanel.tsx +75 -0
- package/src/tui/components/CostOverlay.tsx +118 -0
- package/src/tui/components/HelpOverlay.tsx +107 -0
- package/src/tui/components/StatusBar.tsx +63 -0
- package/src/tui/components/StoriesPanel.tsx +177 -0
- package/src/tui/hooks/useKeyboard.ts +142 -0
- package/src/tui/hooks/useLayout.ts +137 -0
- package/src/tui/hooks/usePipelineEvents.ts +183 -0
- package/src/tui/hooks/usePty.ts +194 -0
- package/src/tui/index.tsx +38 -0
- package/src/tui/types.ts +76 -0
- package/src/utils/git.ts +83 -0
- package/src/utils/queue-writer.ts +54 -0
- package/src/verification/executor.ts +235 -0
- package/src/verification/gate.ts +207 -0
- package/src/verification/index.ts +12 -0
- package/src/verification/parser.ts +230 -0
- package/src/verification/rectification.ts +108 -0
- package/src/verification/types.ts +113 -0
- package/src/worktree/dispatcher.ts +65 -0
- package/src/worktree/index.ts +2 -0
- package/src/worktree/manager.ts +187 -0
- package/src/worktree/merge.ts +301 -0
- package/src/worktree/types.ts +4 -0
- package/test/TEST_COVERAGE_US001.md +217 -0
- package/test/TEST_COVERAGE_US003.md +84 -0
- package/test/TEST_COVERAGE_US005.md +86 -0
- package/test/US-002-orchestrator.test.ts +246 -0
- package/test/acceptance/cm-003-default-view.test.ts +194 -0
- package/test/execution/pid-registry.test.ts +240 -0
- package/test/execution/post-verify.test.ts +224 -0
- package/test/helpers/timeout.ts +42 -0
- package/test/integration/US-002-TEST-SUMMARY.md +107 -0
- package/test/integration/US-003-TEST-SUMMARY.md +149 -0
- package/test/integration/US-004-TEST-SUMMARY.md +106 -0
- package/test/integration/US-005-TEST-SUMMARY.md +138 -0
- package/test/integration/US-007-TEST-SUMMARY.md +100 -0
- package/test/integration/agent-validation.test.ts +439 -0
- package/test/integration/analyze-integration.test.ts +261 -0
- package/test/integration/analyze-scanner.test.ts +131 -0
- package/test/integration/cli-config-default-edge-cases.test.ts +222 -0
- package/test/integration/cli-config-default-view.test.ts +229 -0
- package/test/integration/cli-config-diff.test.ts +460 -0
- package/test/integration/cli-config.test.ts +736 -0
- package/test/integration/cli-diagnose.test.ts +592 -0
- package/test/integration/cli-logs.test.ts +314 -0
- package/test/integration/cli-plugins.test.ts +678 -0
- package/test/integration/cli-precheck.test.ts +371 -0
- package/test/integration/cli-run-headless.test.ts +173 -0
- package/test/integration/cli.test.ts +75 -0
- package/test/integration/config/merger.test.ts +465 -0
- package/test/integration/config/paths.test.ts +51 -0
- package/test/integration/config-loader.test.ts +265 -0
- package/test/integration/config.test.ts +444 -0
- package/test/integration/context-integration.test.ts +702 -0
- package/test/integration/context-provider-injection.test.ts +506 -0
- package/test/integration/context-verification-integration.test.ts +295 -0
- package/test/integration/e2e.test.ts +896 -0
- package/test/integration/execution.test.ts +625 -0
- package/test/integration/helpers.test.ts +295 -0
- package/test/integration/hooks.test.ts +361 -0
- package/test/integration/interaction-chain-pipeline.test.ts +464 -0
- package/test/integration/isolation.test.ts +143 -0
- package/test/integration/logger.test.ts +461 -0
- package/test/integration/parallel.test.ts +250 -0
- package/test/integration/path-security.test.ts +173 -0
- package/test/integration/pipeline-acceptance.test.ts +302 -0
- package/test/integration/pipeline-events.test.ts +475 -0
- package/test/integration/pipeline.test.ts +658 -0
- package/test/integration/plan.test.ts +157 -0
- package/test/integration/plugin-routing.test.ts +921 -0
- package/test/integration/plugins/config-integration.test.ts +172 -0
- package/test/integration/plugins/config-resolution.test.ts +522 -0
- package/test/integration/plugins/loader.test.ts +641 -0
- package/test/integration/plugins/registry.test.ts +746 -0
- package/test/integration/plugins/validator.test.ts +563 -0
- package/test/integration/prd-pause.test.ts +205 -0
- package/test/integration/prd-resolvers.test.ts +185 -0
- package/test/integration/precheck-integration.test.ts +468 -0
- package/test/integration/precheck.test.ts +805 -0
- package/test/integration/progress.test.ts +34 -0
- package/test/integration/rectification-flow.test.ts +512 -0
- package/test/integration/reporter-lifecycle.test.ts +860 -0
- package/test/integration/review-config-commands.test.ts +319 -0
- package/test/integration/review-config-schema.test.ts +116 -0
- package/test/integration/review-plugin-integration.test.ts +722 -0
- package/test/integration/review.test.ts +149 -0
- package/test/integration/routing-stage-bug-021.test.ts +274 -0
- package/test/integration/routing-stage-greenfield.test.ts +286 -0
- package/test/integration/runner-config-plugins.test.ts +461 -0
- package/test/integration/runner-fixes.test.ts +399 -0
- package/test/integration/runner-plugin-integration.test.ts +543 -0
- package/test/integration/runner.test.ts +1679 -0
- package/test/integration/s5-greenfield-fallback.test.ts +297 -0
- package/test/integration/status-file-integration.test.ts +325 -0
- package/test/integration/status-file.test.ts +379 -0
- package/test/integration/status-writer.test.ts +345 -0
- package/test/integration/story-id-in-events.test.ts +273 -0
- package/test/integration/tdd-cleanup.test.ts +246 -0
- package/test/integration/tdd-orchestrator.test.ts +1762 -0
- package/test/integration/test-scanner.test.ts +403 -0
- package/test/integration/verification-asset-check.test.ts +142 -0
- package/test/integration/verify-stage.test.ts +275 -0
- package/test/integration/worktree/manager.test.ts +218 -0
- package/test/integration/worktree/merge.test.ts +341 -0
- package/test/manual/logging-formatter-demo.ts +158 -0
- package/test/ui/tui-agent-panel.test.tsx +99 -0
- package/test/ui/tui-controls.test.ts +334 -0
- package/test/ui/tui-cost-and-pty.test.ts +189 -0
- package/test/ui/tui-layout.test.ts +378 -0
- package/test/ui/tui-pty-integration.test.tsx +159 -0
- package/test/ui/tui-stories.test.ts +332 -0
- package/test/unit/acceptance.test.ts +186 -0
- package/test/unit/agent-stderr-capture.test.ts +146 -0
- package/test/unit/analyze-classifier.test.ts +215 -0
- package/test/unit/analyze.test.ts +224 -0
- package/test/unit/auto-detect.test.ts +249 -0
- package/test/unit/cli-status.test.ts +417 -0
- package/test/unit/commands/common.test.ts +320 -0
- package/test/unit/commands/logs.test.ts +416 -0
- package/test/unit/commands/unlock.test.ts +319 -0
- package/test/unit/constitution-generators.test.ts +160 -0
- package/test/unit/constitution.test.ts +209 -0
- package/test/unit/context.test.ts +1722 -0
- package/test/unit/cost.test.ts +231 -0
- package/test/unit/crash-recovery.test.ts +308 -0
- package/test/unit/escalation.test.ts +126 -0
- package/test/unit/execution-logging-stderr.test.ts +156 -0
- package/test/unit/execution-stage.test.ts +122 -0
- package/test/unit/fix-generator.test.ts +275 -0
- package/test/unit/formatters.test.ts +469 -0
- package/test/unit/greenfield.test.ts +179 -0
- package/test/unit/helpers.test.ts +317 -0
- package/test/unit/interaction/human-review-trigger.test.ts +164 -0
- package/test/unit/interaction-network-failures.test.ts +389 -0
- package/test/unit/interaction-plugins.test.ts +164 -0
- package/test/unit/isolation.test.ts +134 -0
- package/test/unit/logging/formatter.test.ts +455 -0
- package/test/unit/merge.test.ts +268 -0
- package/test/unit/metrics.test.ts +276 -0
- package/test/unit/optimizer/noop.optimizer.test.ts +125 -0
- package/test/unit/optimizer/rule-based.optimizer.test.ts +358 -0
- package/test/unit/prd-auto-default.test.ts +290 -0
- package/test/unit/prd-failure-category.test.ts +176 -0
- package/test/unit/prd-get-next-story.test.ts +186 -0
- package/test/unit/precheck-checks.test.ts +840 -0
- package/test/unit/precheck-story-size-gate.test.ts +287 -0
- package/test/unit/precheck-types.test.ts +142 -0
- package/test/unit/prompts.test.ts +475 -0
- package/test/unit/queue.test.ts +237 -0
- package/test/unit/rectification.test.ts +284 -0
- package/test/unit/registry.test.ts +287 -0
- package/test/unit/routing.test.ts +937 -0
- package/test/unit/run-lifecycle.test.ts +140 -0
- package/test/unit/storyid-events.test.ts +224 -0
- package/test/unit/tdd-verdict.test.ts +492 -0
- package/test/unit/test-output-parser.test.ts +377 -0
- package/test/unit/verdict.test.ts +324 -0
- package/test/unit/worktree-manager.test.ts +158 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CostOverlay — modal-style overlay showing cost breakdown per story.
|
|
3
|
+
*
|
|
4
|
+
* Shown when c is pressed, dismissed with Esc.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Box, Text } from "ink";
|
|
8
|
+
import type { StoryDisplayState } from "../types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props for CostOverlay component.
|
|
12
|
+
*/
|
|
13
|
+
export interface CostOverlayProps {
|
|
14
|
+
/** Whether the overlay is visible */
|
|
15
|
+
visible?: boolean;
|
|
16
|
+
/** All stories with cost data */
|
|
17
|
+
stories?: StoryDisplayState[];
|
|
18
|
+
/** Total accumulated cost */
|
|
19
|
+
totalCost?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Format cost as USD with 4 decimal places.
|
|
24
|
+
*/
|
|
25
|
+
function formatCost(cost: number): string {
|
|
26
|
+
return `$${cost.toFixed(4)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* CostOverlay component.
|
|
31
|
+
*
|
|
32
|
+
* Displays a modal-style overlay with cost breakdown per story.
|
|
33
|
+
* Shows story ID, status, and cost incurred.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* const [showCost, setShowCost] = useState(false);
|
|
38
|
+
*
|
|
39
|
+
* <CostOverlay
|
|
40
|
+
* visible={showCost}
|
|
41
|
+
* stories={state.stories}
|
|
42
|
+
* totalCost={state.totalCost}
|
|
43
|
+
* />
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function CostOverlay({ visible = false, stories = [], totalCost = 0 }: CostOverlayProps) {
|
|
47
|
+
if (!visible) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Only show stories that have been executed (cost > 0 or status !== pending)
|
|
52
|
+
const executedStories = stories.filter((s) => (s.cost && s.cost > 0) || s.status !== "pending");
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Box flexDirection="column" borderStyle="double" borderColor="cyan" paddingX={2} paddingY={1} minWidth={60}>
|
|
56
|
+
<Box paddingBottom={1}>
|
|
57
|
+
<Text bold color="cyan">
|
|
58
|
+
Cost Breakdown
|
|
59
|
+
</Text>
|
|
60
|
+
</Box>
|
|
61
|
+
|
|
62
|
+
{/* Header row */}
|
|
63
|
+
<Box paddingBottom={1} borderBottom borderColor="gray">
|
|
64
|
+
<Box width={12}>
|
|
65
|
+
<Text bold>Story ID</Text>
|
|
66
|
+
</Box>
|
|
67
|
+
<Box width={12}>
|
|
68
|
+
<Text bold>Status</Text>
|
|
69
|
+
</Box>
|
|
70
|
+
<Box width={12}>
|
|
71
|
+
<Text bold>Cost</Text>
|
|
72
|
+
</Box>
|
|
73
|
+
</Box>
|
|
74
|
+
|
|
75
|
+
{/* Story rows */}
|
|
76
|
+
<Box flexDirection="column" paddingY={1}>
|
|
77
|
+
{executedStories.length > 0 ? (
|
|
78
|
+
executedStories.map((story) => (
|
|
79
|
+
<Box key={story.story.id}>
|
|
80
|
+
<Box width={12}>
|
|
81
|
+
<Text>{story.story.id}</Text>
|
|
82
|
+
</Box>
|
|
83
|
+
<Box width={12}>
|
|
84
|
+
<Text color={story.status === "passed" ? "green" : story.status === "failed" ? "red" : undefined}>
|
|
85
|
+
{story.status}
|
|
86
|
+
</Text>
|
|
87
|
+
</Box>
|
|
88
|
+
<Box width={12}>
|
|
89
|
+
<Text>{formatCost(story.cost || 0)}</Text>
|
|
90
|
+
</Box>
|
|
91
|
+
</Box>
|
|
92
|
+
))
|
|
93
|
+
) : (
|
|
94
|
+
<Text dimColor>No stories executed yet</Text>
|
|
95
|
+
)}
|
|
96
|
+
</Box>
|
|
97
|
+
|
|
98
|
+
{/* Total row */}
|
|
99
|
+
<Box paddingTop={1} borderTop borderColor="gray">
|
|
100
|
+
<Box width={24}>
|
|
101
|
+
<Text bold>Total Cost:</Text>
|
|
102
|
+
</Box>
|
|
103
|
+
<Box width={12}>
|
|
104
|
+
<Text bold color="cyan">
|
|
105
|
+
{formatCost(totalCost)}
|
|
106
|
+
</Text>
|
|
107
|
+
</Box>
|
|
108
|
+
</Box>
|
|
109
|
+
|
|
110
|
+
{/* Footer */}
|
|
111
|
+
<Box justifyContent="center" paddingTop={1} borderTop borderColor="gray">
|
|
112
|
+
<Text dimColor>
|
|
113
|
+
Press <Text color="yellow">Esc</Text> to close
|
|
114
|
+
</Text>
|
|
115
|
+
</Box>
|
|
116
|
+
</Box>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HelpOverlay — modal-style overlay listing all keybindings.
|
|
3
|
+
*
|
|
4
|
+
* Shown when ? is pressed, dismissed with Esc.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Box, Text } from "ink";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Props for HelpOverlay component.
|
|
11
|
+
*/
|
|
12
|
+
export interface HelpOverlayProps {
|
|
13
|
+
/** Whether the overlay is visible */
|
|
14
|
+
visible?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* HelpOverlay component.
|
|
19
|
+
*
|
|
20
|
+
* Displays a modal-style overlay with keybinding reference.
|
|
21
|
+
* Centered on screen with a border.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* const [showHelp, setShowHelp] = useState(false);
|
|
26
|
+
*
|
|
27
|
+
* <HelpOverlay visible={showHelp} />
|
|
28
|
+
*
|
|
29
|
+
* // Close with Esc or by toggling state
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function HelpOverlay({ visible = false }: HelpOverlayProps) {
|
|
33
|
+
if (!visible) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Box flexDirection="column" borderStyle="double" borderColor="cyan" paddingX={2} paddingY={1}>
|
|
39
|
+
<Box paddingBottom={1}>
|
|
40
|
+
<Text bold color="cyan">
|
|
41
|
+
Keyboard Shortcuts
|
|
42
|
+
</Text>
|
|
43
|
+
</Box>
|
|
44
|
+
|
|
45
|
+
{/* Stories panel shortcuts */}
|
|
46
|
+
<Box flexDirection="column" paddingBottom={1}>
|
|
47
|
+
<Text dimColor>Stories Panel (default):</Text>
|
|
48
|
+
<Text>
|
|
49
|
+
{" "}
|
|
50
|
+
<Text color="yellow">p</Text> — Pause after current story
|
|
51
|
+
</Text>
|
|
52
|
+
<Text>
|
|
53
|
+
{" "}
|
|
54
|
+
<Text color="yellow">a</Text> — Abort run
|
|
55
|
+
</Text>
|
|
56
|
+
<Text>
|
|
57
|
+
{" "}
|
|
58
|
+
<Text color="yellow">s</Text> — Skip current story
|
|
59
|
+
</Text>
|
|
60
|
+
<Text>
|
|
61
|
+
{" "}
|
|
62
|
+
<Text color="yellow">Tab</Text> — Toggle focus to Agent panel
|
|
63
|
+
</Text>
|
|
64
|
+
<Text>
|
|
65
|
+
{" "}
|
|
66
|
+
<Text color="yellow">q</Text> — Quit TUI
|
|
67
|
+
</Text>
|
|
68
|
+
<Text>
|
|
69
|
+
{" "}
|
|
70
|
+
<Text color="yellow">?</Text> — Show this help
|
|
71
|
+
</Text>
|
|
72
|
+
<Text>
|
|
73
|
+
{" "}
|
|
74
|
+
<Text color="yellow">c</Text> — Show cost breakdown
|
|
75
|
+
</Text>
|
|
76
|
+
<Text>
|
|
77
|
+
{" "}
|
|
78
|
+
<Text color="yellow">r</Text> — Retry last failed story
|
|
79
|
+
</Text>
|
|
80
|
+
<Text>
|
|
81
|
+
{" "}
|
|
82
|
+
<Text color="yellow">Esc</Text> — Close overlay
|
|
83
|
+
</Text>
|
|
84
|
+
</Box>
|
|
85
|
+
|
|
86
|
+
{/* Agent panel shortcuts */}
|
|
87
|
+
<Box flexDirection="column" paddingBottom={1}>
|
|
88
|
+
<Text dimColor>Agent Panel (when focused):</Text>
|
|
89
|
+
<Text>
|
|
90
|
+
{" "}
|
|
91
|
+
<Text color="yellow">Ctrl+]</Text> — Escape back to Stories panel
|
|
92
|
+
</Text>
|
|
93
|
+
<Text>
|
|
94
|
+
{" "}
|
|
95
|
+
<Text dimColor>All other keys</Text> — Forwarded to agent PTY
|
|
96
|
+
</Text>
|
|
97
|
+
</Box>
|
|
98
|
+
|
|
99
|
+
{/* Footer */}
|
|
100
|
+
<Box justifyContent="center" paddingTop={1} borderTop borderColor="gray">
|
|
101
|
+
<Text dimColor>
|
|
102
|
+
Press <Text color="yellow">Esc</Text> to close
|
|
103
|
+
</Text>
|
|
104
|
+
</Box>
|
|
105
|
+
</Box>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusBar — displays current story and pipeline stage information.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Box, Text } from "ink";
|
|
6
|
+
import type { UserStory } from "../../prd/types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Props for StatusBar component.
|
|
10
|
+
*/
|
|
11
|
+
export interface StatusBarProps {
|
|
12
|
+
/** Current story being executed */
|
|
13
|
+
currentStory?: UserStory;
|
|
14
|
+
/** Current pipeline stage */
|
|
15
|
+
currentStage?: string;
|
|
16
|
+
/** Model tier for current story */
|
|
17
|
+
modelTier?: string;
|
|
18
|
+
/** Test strategy for current story */
|
|
19
|
+
testStrategy?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* StatusBar component.
|
|
24
|
+
*
|
|
25
|
+
* Displays a single line showing the current story ID, stage, model tier, and test strategy.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <StatusBar
|
|
30
|
+
* currentStory={story}
|
|
31
|
+
* currentStage="execution"
|
|
32
|
+
* modelTier="balanced"
|
|
33
|
+
* testStrategy="single-session"
|
|
34
|
+
* />
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function StatusBar({ currentStory, currentStage, modelTier, testStrategy }: StatusBarProps) {
|
|
38
|
+
if (!currentStory) {
|
|
39
|
+
return (
|
|
40
|
+
<Box paddingX={1} borderStyle="single" borderColor="gray">
|
|
41
|
+
<Text dimColor>Idle</Text>
|
|
42
|
+
</Box>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const storyInfo = `Story ${currentStory.id}`;
|
|
47
|
+
const stageInfo = currentStage ? ` · ${currentStage}` : "";
|
|
48
|
+
const tierInfo = modelTier ? ` · ${modelTier}` : "";
|
|
49
|
+
const strategyInfo = testStrategy ? ` · ${testStrategy}` : "";
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Box paddingX={1} borderStyle="single" borderColor="gray">
|
|
53
|
+
<Text>
|
|
54
|
+
<Text bold>{storyInfo}</Text>
|
|
55
|
+
<Text dimColor>
|
|
56
|
+
{stageInfo}
|
|
57
|
+
{tierInfo}
|
|
58
|
+
{strategyInfo}
|
|
59
|
+
</Text>
|
|
60
|
+
</Text>
|
|
61
|
+
</Box>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StoriesPanel — displays story list with status icons, cost, and elapsed time.
|
|
3
|
+
*
|
|
4
|
+
* Supports scrolling for >15 stories and compact mode for single-column layout.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Box, Text } from "ink";
|
|
8
|
+
import { useEffect, useState } from "react";
|
|
9
|
+
import { COMPACT_MAX_VISIBLE_STORIES, MAX_VISIBLE_STORIES } from "../hooks/useLayout";
|
|
10
|
+
import type { StoryDisplayState } from "../types";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props for StoriesPanel component.
|
|
14
|
+
*/
|
|
15
|
+
export interface StoriesPanelProps {
|
|
16
|
+
/** Stories to display */
|
|
17
|
+
stories: StoryDisplayState[];
|
|
18
|
+
/** Total cost accumulated */
|
|
19
|
+
totalCost: number;
|
|
20
|
+
/** Elapsed time in milliseconds */
|
|
21
|
+
elapsedMs: number;
|
|
22
|
+
/** Panel width (columns) */
|
|
23
|
+
width?: number;
|
|
24
|
+
/** Compact mode (fewer details, for single-column layout) */
|
|
25
|
+
compact?: boolean;
|
|
26
|
+
/** Maximum height in rows (for single-column mode) */
|
|
27
|
+
maxHeight?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get status icon for a story.
|
|
32
|
+
*/
|
|
33
|
+
function getStatusIcon(status: StoryDisplayState["status"]): string {
|
|
34
|
+
switch (status) {
|
|
35
|
+
case "pending":
|
|
36
|
+
return "⬚";
|
|
37
|
+
case "running":
|
|
38
|
+
return "🔄";
|
|
39
|
+
case "passed":
|
|
40
|
+
return "✅";
|
|
41
|
+
case "failed":
|
|
42
|
+
return "❌";
|
|
43
|
+
case "skipped":
|
|
44
|
+
return "⏭️";
|
|
45
|
+
case "retrying":
|
|
46
|
+
return "🔁";
|
|
47
|
+
case "paused":
|
|
48
|
+
return "⏸️";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Format elapsed time as mm:ss.
|
|
54
|
+
*/
|
|
55
|
+
function formatElapsedTime(ms: number): string {
|
|
56
|
+
const totalSeconds = Math.floor(ms / 1000);
|
|
57
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
58
|
+
const seconds = totalSeconds % 60;
|
|
59
|
+
return `${minutes}m ${seconds}s`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* StoriesPanel component.
|
|
64
|
+
*
|
|
65
|
+
* Displays all stories with status icons, routing info, cost total, and elapsed time.
|
|
66
|
+
* Supports scrolling for >15 stories (or >8 in compact mode) and shows scroll indicators.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```tsx
|
|
70
|
+
* <StoriesPanel
|
|
71
|
+
* stories={storyStates}
|
|
72
|
+
* totalCost={0.42}
|
|
73
|
+
* elapsedMs={263000}
|
|
74
|
+
* width={30}
|
|
75
|
+
* compact={false}
|
|
76
|
+
* />
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export function StoriesPanel({ stories, totalCost, elapsedMs, width, compact = false, maxHeight }: StoriesPanelProps) {
|
|
80
|
+
// Determine max visible stories based on mode
|
|
81
|
+
const maxVisible = compact ? COMPACT_MAX_VISIBLE_STORIES : MAX_VISIBLE_STORIES;
|
|
82
|
+
const needsScrolling = stories.length > maxVisible;
|
|
83
|
+
|
|
84
|
+
// Scroll position (0-indexed offset)
|
|
85
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
86
|
+
|
|
87
|
+
// Auto-scroll to keep the current running story in view
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const runningIndex = stories.findIndex((s) => s.status === "running");
|
|
90
|
+
if (runningIndex !== -1 && needsScrolling) {
|
|
91
|
+
// If running story is outside the visible window, scroll to it
|
|
92
|
+
if (runningIndex < scrollOffset) {
|
|
93
|
+
setScrollOffset(runningIndex);
|
|
94
|
+
} else if (runningIndex >= scrollOffset + maxVisible) {
|
|
95
|
+
setScrollOffset(runningIndex - maxVisible + 1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}, [stories, scrollOffset, maxVisible, needsScrolling]);
|
|
99
|
+
|
|
100
|
+
// Get visible stories (either all or a scrolled window)
|
|
101
|
+
const visibleStories = needsScrolling ? stories.slice(scrollOffset, scrollOffset + maxVisible) : stories;
|
|
102
|
+
|
|
103
|
+
const canScrollUp = scrollOffset > 0;
|
|
104
|
+
const canScrollDown = scrollOffset + maxVisible < stories.length;
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<Box flexDirection="column" width={width} height={maxHeight} borderStyle="single" borderColor="gray">
|
|
108
|
+
{/* Header */}
|
|
109
|
+
<Box paddingX={1} borderStyle="single" borderBottom borderColor="gray">
|
|
110
|
+
<Text bold>Stories</Text>
|
|
111
|
+
{needsScrolling && <Text dimColor> ({stories.length} total)</Text>}
|
|
112
|
+
</Box>
|
|
113
|
+
|
|
114
|
+
{/* Scroll indicator (top) */}
|
|
115
|
+
{needsScrolling && canScrollUp && (
|
|
116
|
+
<Box paddingX={1}>
|
|
117
|
+
<Text dimColor>▲ {scrollOffset} more above</Text>
|
|
118
|
+
</Box>
|
|
119
|
+
)}
|
|
120
|
+
|
|
121
|
+
{/* Story list */}
|
|
122
|
+
<Box flexDirection="column" paddingX={1} paddingY={1} flexGrow={1}>
|
|
123
|
+
{visibleStories.map((s) => {
|
|
124
|
+
const icon = getStatusIcon(s.status);
|
|
125
|
+
|
|
126
|
+
if (compact) {
|
|
127
|
+
// Compact mode: just icon and ID
|
|
128
|
+
return (
|
|
129
|
+
<Box key={s.story.id}>
|
|
130
|
+
<Text>
|
|
131
|
+
{icon} {s.story.id}
|
|
132
|
+
</Text>
|
|
133
|
+
</Box>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Normal mode: icon, ID, and routing info
|
|
138
|
+
const routing = s.routing ? ` ${s.routing.complexity.slice(0, 3)} ${s.routing.modelTier}` : "";
|
|
139
|
+
return (
|
|
140
|
+
<Box key={s.story.id}>
|
|
141
|
+
<Text>
|
|
142
|
+
{icon} {s.story.id}
|
|
143
|
+
<Text dimColor>{routing}</Text>
|
|
144
|
+
</Text>
|
|
145
|
+
</Box>
|
|
146
|
+
);
|
|
147
|
+
})}
|
|
148
|
+
</Box>
|
|
149
|
+
|
|
150
|
+
{/* Scroll indicator (bottom) */}
|
|
151
|
+
{needsScrolling && canScrollDown && (
|
|
152
|
+
<Box paddingX={1}>
|
|
153
|
+
<Text dimColor>▼ {stories.length - scrollOffset - maxVisible} more below</Text>
|
|
154
|
+
</Box>
|
|
155
|
+
)}
|
|
156
|
+
|
|
157
|
+
{/* Footer — Cost and time */}
|
|
158
|
+
<Box flexDirection="column" paddingX={1} paddingY={1} borderStyle="single" borderTop borderColor="gray">
|
|
159
|
+
{!compact && (
|
|
160
|
+
<>
|
|
161
|
+
<Text>
|
|
162
|
+
Cost: <Text color="green">${totalCost.toFixed(4)}</Text>
|
|
163
|
+
</Text>
|
|
164
|
+
<Text>
|
|
165
|
+
Time: <Text color="cyan">{formatElapsedTime(elapsedMs)}</Text>
|
|
166
|
+
</Text>
|
|
167
|
+
</>
|
|
168
|
+
)}
|
|
169
|
+
{compact && (
|
|
170
|
+
<Text>
|
|
171
|
+
${totalCost.toFixed(2)} · {formatElapsedTime(elapsedMs)}
|
|
172
|
+
</Text>
|
|
173
|
+
)}
|
|
174
|
+
</Box>
|
|
175
|
+
</Box>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useKeyboard hook — handle keyboard shortcuts for TUI controls.
|
|
3
|
+
*
|
|
4
|
+
* Listens for keyboard input when agent panel is NOT focused and dispatches
|
|
5
|
+
* actions like PAUSE/ABORT/SKIP to the queue file.
|
|
6
|
+
*
|
|
7
|
+
* When agent panel IS focused, only Ctrl+] escapes back to TUI controls.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useInput } from "ink";
|
|
11
|
+
import type { UserStory } from "../../prd/types";
|
|
12
|
+
import { PanelFocus } from "../types";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Keyboard action types.
|
|
16
|
+
*/
|
|
17
|
+
export type KeyboardAction =
|
|
18
|
+
| { type: "PAUSE" }
|
|
19
|
+
| { type: "ABORT" }
|
|
20
|
+
| { type: "SKIP"; storyId: string }
|
|
21
|
+
| { type: "TOGGLE_FOCUS" }
|
|
22
|
+
| { type: "ESCAPE_AGENT" }
|
|
23
|
+
| { type: "QUIT" }
|
|
24
|
+
| { type: "SHOW_HELP" }
|
|
25
|
+
| { type: "SHOW_COST" }
|
|
26
|
+
| { type: "RETRY" }
|
|
27
|
+
| { type: "CLOSE_OVERLAY" };
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Props for useKeyboard hook.
|
|
31
|
+
*/
|
|
32
|
+
export interface UseKeyboardProps {
|
|
33
|
+
/** Current panel focus state */
|
|
34
|
+
focus: PanelFocus;
|
|
35
|
+
/** Current story being executed (for SKIP command) */
|
|
36
|
+
currentStory?: UserStory;
|
|
37
|
+
/** Callback when an action is triggered */
|
|
38
|
+
onAction: (action: KeyboardAction) => void;
|
|
39
|
+
/** Disable keyboard handling (e.g., during confirmation dialogs) */
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Hook for handling keyboard shortcuts.
|
|
45
|
+
*
|
|
46
|
+
* Keybindings (when Stories panel is focused):
|
|
47
|
+
* - p: PAUSE after current story
|
|
48
|
+
* - a: ABORT run
|
|
49
|
+
* - s: SKIP current story
|
|
50
|
+
* - Tab: Toggle focus between Stories and Agent panels
|
|
51
|
+
* - q: Quit TUI
|
|
52
|
+
* - ?: Show help overlay
|
|
53
|
+
* - c: Show cost breakdown overlay
|
|
54
|
+
* - r: Retry last failed story
|
|
55
|
+
* - Esc: Close overlay
|
|
56
|
+
*
|
|
57
|
+
* When Agent panel is focused:
|
|
58
|
+
* - Ctrl+]: Escape back to TUI controls
|
|
59
|
+
* - All other keys: Forwarded to PTY (handled elsewhere)
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```tsx
|
|
63
|
+
* const [focus, setFocus] = useState(PanelFocus.Stories);
|
|
64
|
+
* const [showHelp, setShowHelp] = useState(false);
|
|
65
|
+
*
|
|
66
|
+
* useKeyboard({
|
|
67
|
+
* focus,
|
|
68
|
+
* currentStory: state.currentStory,
|
|
69
|
+
* onAction: (action) => {
|
|
70
|
+
* if (action.type === "TOGGLE_FOCUS") {
|
|
71
|
+
* setFocus(prev => prev === PanelFocus.Stories ? PanelFocus.Agent : PanelFocus.Stories);
|
|
72
|
+
* } else if (action.type === "SHOW_HELP") {
|
|
73
|
+
* setShowHelp(true);
|
|
74
|
+
* } else if (action.type === "PAUSE") {
|
|
75
|
+
* writeQueueCommand({ type: "PAUSE" });
|
|
76
|
+
* }
|
|
77
|
+
* }
|
|
78
|
+
* });
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export function useKeyboard({ focus, currentStory, onAction, disabled = false }: UseKeyboardProps): void {
|
|
82
|
+
useInput((input, key) => {
|
|
83
|
+
// If disabled, don't process any input
|
|
84
|
+
if (disabled) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// When Agent panel is focused, only Ctrl+] escapes back to TUI
|
|
89
|
+
if (focus === PanelFocus.Agent) {
|
|
90
|
+
// Ctrl+] is key.ctrl === true and input === ']'
|
|
91
|
+
if (key.ctrl && input === "]") {
|
|
92
|
+
onAction({ type: "ESCAPE_AGENT" });
|
|
93
|
+
}
|
|
94
|
+
// All other input is routed to PTY (handled by caller)
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Stories panel is focused — handle TUI shortcuts
|
|
99
|
+
// Tab key toggles focus
|
|
100
|
+
if (key.tab) {
|
|
101
|
+
onAction({ type: "TOGGLE_FOCUS" });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Esc closes overlays
|
|
106
|
+
if (key.escape) {
|
|
107
|
+
onAction({ type: "CLOSE_OVERLAY" });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Character-based shortcuts
|
|
112
|
+
switch (input.toLowerCase()) {
|
|
113
|
+
case "p":
|
|
114
|
+
onAction({ type: "PAUSE" });
|
|
115
|
+
break;
|
|
116
|
+
case "a":
|
|
117
|
+
onAction({ type: "ABORT" });
|
|
118
|
+
break;
|
|
119
|
+
case "s":
|
|
120
|
+
// Skip requires a current story
|
|
121
|
+
if (currentStory) {
|
|
122
|
+
onAction({ type: "SKIP", storyId: currentStory.id });
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
case "q":
|
|
126
|
+
onAction({ type: "QUIT" });
|
|
127
|
+
break;
|
|
128
|
+
case "?":
|
|
129
|
+
onAction({ type: "SHOW_HELP" });
|
|
130
|
+
break;
|
|
131
|
+
case "c":
|
|
132
|
+
onAction({ type: "SHOW_COST" });
|
|
133
|
+
break;
|
|
134
|
+
case "r":
|
|
135
|
+
onAction({ type: "RETRY" });
|
|
136
|
+
break;
|
|
137
|
+
default:
|
|
138
|
+
// Ignore unrecognized keys
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|