@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,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto AI Responder Plugin (v0.15.0 US-008)
|
|
3
|
+
*
|
|
4
|
+
* Automatically responds to interactions using LLM decision-making.
|
|
5
|
+
* Never auto-approves security-review triggers.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import type { NaxConfig } from "../../config";
|
|
10
|
+
import { resolveModel } from "../../config";
|
|
11
|
+
import type { InteractionPlugin, InteractionRequest, InteractionResponse } from "../types";
|
|
12
|
+
|
|
13
|
+
/** Auto plugin configuration */
|
|
14
|
+
interface AutoConfig {
|
|
15
|
+
/** Model tier to use for decisions (default: fast) */
|
|
16
|
+
model?: string;
|
|
17
|
+
/** Confidence threshold (0-1, default: 0.7) */
|
|
18
|
+
confidenceThreshold?: number;
|
|
19
|
+
/** Max cost per decision in USD (default: 0.01) */
|
|
20
|
+
maxCostPerDecision?: number;
|
|
21
|
+
/** Global nax config (injected by chain) */
|
|
22
|
+
naxConfig?: NaxConfig;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Zod schema for validating auto plugin config */
|
|
26
|
+
const AutoConfigSchema = z.object({
|
|
27
|
+
model: z.string().optional(),
|
|
28
|
+
confidenceThreshold: z.number().min(0).max(1).optional(),
|
|
29
|
+
maxCostPerDecision: z.number().positive().optional(),
|
|
30
|
+
naxConfig: z.any().optional(), // NaxConfig is complex, skip deep validation
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/** LLM decision response */
|
|
34
|
+
interface DecisionResponse {
|
|
35
|
+
action: "approve" | "reject" | "choose" | "input" | "skip" | "abort";
|
|
36
|
+
value?: string;
|
|
37
|
+
confidence: number;
|
|
38
|
+
reasoning: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Auto plugin for AI-powered interaction responses
|
|
43
|
+
*/
|
|
44
|
+
export class AutoInteractionPlugin implements InteractionPlugin {
|
|
45
|
+
name = "auto";
|
|
46
|
+
private config: AutoConfig = {};
|
|
47
|
+
|
|
48
|
+
async init(config: Record<string, unknown>): Promise<void> {
|
|
49
|
+
const cfg = AutoConfigSchema.parse(config);
|
|
50
|
+
this.config = {
|
|
51
|
+
model: cfg.model ?? "fast",
|
|
52
|
+
confidenceThreshold: cfg.confidenceThreshold ?? 0.7,
|
|
53
|
+
maxCostPerDecision: cfg.maxCostPerDecision ?? 0.01,
|
|
54
|
+
naxConfig: cfg.naxConfig,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async destroy(): Promise<void> {
|
|
59
|
+
// No-op
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async send(request: InteractionRequest): Promise<void> {
|
|
63
|
+
// No-op — in-process plugin
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async receive(requestId: string, timeout = 60000): Promise<InteractionResponse> {
|
|
67
|
+
// For auto plugin, we need to fetch the request from somewhere
|
|
68
|
+
// In practice, the chain should pass the request to us
|
|
69
|
+
// For now, throw an error since we need the full request
|
|
70
|
+
throw new Error("Auto plugin requires full request context (not just requestId)");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Decide on an interaction request using LLM
|
|
75
|
+
*/
|
|
76
|
+
async decide(request: InteractionRequest): Promise<InteractionResponse | undefined> {
|
|
77
|
+
// SAFETY: Never auto-approve security-review
|
|
78
|
+
if (request.metadata?.trigger === "security-review") {
|
|
79
|
+
return undefined; // Escalate to human
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const decision = await this.callLlm(request);
|
|
84
|
+
|
|
85
|
+
// Check confidence threshold
|
|
86
|
+
if (decision.confidence < (this.config.confidenceThreshold ?? 0.7)) {
|
|
87
|
+
return undefined; // Escalate to human
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
requestId: request.id,
|
|
92
|
+
action: decision.action,
|
|
93
|
+
value: decision.value,
|
|
94
|
+
respondedBy: "auto-ai",
|
|
95
|
+
respondedAt: Date.now(),
|
|
96
|
+
};
|
|
97
|
+
} catch (err) {
|
|
98
|
+
// On error, escalate to human
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Call LLM to make decision
|
|
105
|
+
*/
|
|
106
|
+
private async callLlm(request: InteractionRequest): Promise<DecisionResponse> {
|
|
107
|
+
const prompt = this.buildPrompt(request);
|
|
108
|
+
const modelTier = this.config.model ?? "fast";
|
|
109
|
+
|
|
110
|
+
if (!this.config.naxConfig) {
|
|
111
|
+
throw new Error("Auto plugin requires naxConfig in init()");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const modelEntry = this.config.naxConfig.models[modelTier];
|
|
115
|
+
if (!modelEntry) {
|
|
116
|
+
throw new Error(`Model tier "${modelTier}" not found in config.models`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const modelDef = resolveModel(modelEntry);
|
|
120
|
+
const modelArg = modelDef.model;
|
|
121
|
+
|
|
122
|
+
// Spawn claude CLI
|
|
123
|
+
const proc = Bun.spawn(["claude", "-p", prompt, "--model", modelArg], {
|
|
124
|
+
stdout: "pipe",
|
|
125
|
+
stderr: "pipe",
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const [stdout, stderr] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text()]);
|
|
129
|
+
|
|
130
|
+
const exitCode = await proc.exited;
|
|
131
|
+
if (exitCode !== 0) {
|
|
132
|
+
throw new Error(`claude CLI failed with exit code ${exitCode}: ${stderr}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const output = stdout.trim();
|
|
136
|
+
return this.parseResponse(output);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Build LLM prompt for decision
|
|
141
|
+
*/
|
|
142
|
+
private buildPrompt(request: InteractionRequest): string {
|
|
143
|
+
let prompt = `You are an AI decision assistant for a code orchestration system. Given an interaction request, decide the best action.
|
|
144
|
+
|
|
145
|
+
## Interaction Request
|
|
146
|
+
Type: ${request.type}
|
|
147
|
+
Stage: ${request.stage}
|
|
148
|
+
Feature: ${request.featureName}
|
|
149
|
+
${request.storyId ? `Story: ${request.storyId}` : ""}
|
|
150
|
+
|
|
151
|
+
Summary: ${request.summary}
|
|
152
|
+
${request.detail ? `\nDetail: ${request.detail}` : ""}
|
|
153
|
+
`;
|
|
154
|
+
|
|
155
|
+
if (request.options && request.options.length > 0) {
|
|
156
|
+
prompt += "\nOptions:\n";
|
|
157
|
+
for (const opt of request.options) {
|
|
158
|
+
const desc = opt.description ? ` — ${opt.description}` : "";
|
|
159
|
+
prompt += ` [${opt.key}] ${opt.label}${desc}\n`;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
prompt += `\nFallback behavior on timeout: ${request.fallback}
|
|
164
|
+
Safety tier: ${request.metadata?.safety ?? "unknown"}
|
|
165
|
+
|
|
166
|
+
## Available Actions
|
|
167
|
+
- approve: Proceed with the operation
|
|
168
|
+
- reject: Deny the operation
|
|
169
|
+
- choose: Select an option (requires value field)
|
|
170
|
+
- input: Provide text input (requires value field)
|
|
171
|
+
- skip: Skip this interaction
|
|
172
|
+
- abort: Abort execution
|
|
173
|
+
|
|
174
|
+
## Rules
|
|
175
|
+
1. For "red" safety tier (security-review, cost-exceeded, merge-conflict): ALWAYS return confidence 0 to escalate to human
|
|
176
|
+
2. For "yellow" safety tier (cost-warning, max-retries, pre-merge): High confidence (0.8+) ONLY if clearly safe
|
|
177
|
+
3. For "green" safety tier (story-ambiguity, review-gate): Can approve with moderate confidence (0.6+)
|
|
178
|
+
4. Default to the fallback behavior if unsure
|
|
179
|
+
5. Never auto-approve security issues
|
|
180
|
+
6. If the summary mentions "critical" or "security", confidence MUST be < 0.5
|
|
181
|
+
|
|
182
|
+
Respond with ONLY this JSON (no markdown, no explanation):
|
|
183
|
+
{"action":"approve|reject|choose|input|skip|abort","value":"<optional>","confidence":0.0-1.0,"reasoning":"<one line>"}`;
|
|
184
|
+
|
|
185
|
+
return prompt;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Parse LLM response
|
|
190
|
+
*/
|
|
191
|
+
private parseResponse(output: string): DecisionResponse {
|
|
192
|
+
let jsonText = output.trim();
|
|
193
|
+
|
|
194
|
+
// Strip markdown code fences
|
|
195
|
+
if (jsonText.startsWith("```")) {
|
|
196
|
+
const lines = jsonText.split("\n");
|
|
197
|
+
jsonText = lines.slice(1, -1).join("\n").trim();
|
|
198
|
+
}
|
|
199
|
+
if (jsonText.startsWith("json")) {
|
|
200
|
+
jsonText = jsonText.slice(4).trim();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const parsed = JSON.parse(jsonText) as DecisionResponse;
|
|
204
|
+
|
|
205
|
+
// Validate
|
|
206
|
+
if (!parsed.action || parsed.confidence === undefined || !parsed.reasoning) {
|
|
207
|
+
throw new Error(`Invalid LLM response: ${jsonText}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Validate confidence is 0-1
|
|
211
|
+
if (parsed.confidence < 0 || parsed.confidence > 1) {
|
|
212
|
+
throw new Error(`Invalid confidence: ${parsed.confidence} (must be 0-1)`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return parsed;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Interaction Plugin (v0.15.0 US-002)
|
|
3
|
+
*
|
|
4
|
+
* Default plugin for stdin/stdout interaction in non-headless mode.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as readline from "node:readline";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import type { InteractionPlugin, InteractionRequest, InteractionResponse } from "../types";
|
|
10
|
+
|
|
11
|
+
/** Zod schema for validating CLI plugin config */
|
|
12
|
+
const CLIConfigSchema = z.object({}).passthrough();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* CLI plugin for interactive prompts via stdin/stdout
|
|
16
|
+
*/
|
|
17
|
+
export class CLIInteractionPlugin implements InteractionPlugin {
|
|
18
|
+
name = "cli";
|
|
19
|
+
private pendingRequests = new Map<string, InteractionRequest>();
|
|
20
|
+
private rl: readline.Interface | null = null;
|
|
21
|
+
|
|
22
|
+
async init(config: Record<string, unknown> = {}): Promise<void> {
|
|
23
|
+
CLIConfigSchema.parse(config);
|
|
24
|
+
// Initialize readline interface
|
|
25
|
+
this.rl = readline.createInterface({
|
|
26
|
+
input: process.stdin,
|
|
27
|
+
output: process.stdout,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async destroy(): Promise<void> {
|
|
32
|
+
if (this.rl) {
|
|
33
|
+
this.rl.close();
|
|
34
|
+
this.rl = null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async send(request: InteractionRequest): Promise<void> {
|
|
39
|
+
this.pendingRequests.set(request.id, request);
|
|
40
|
+
|
|
41
|
+
// Format and print the request
|
|
42
|
+
console.log(`\n${"=".repeat(80)}`);
|
|
43
|
+
console.log(`[INTERACTION] ${request.stage.toUpperCase()} — ${request.type.toUpperCase()}`);
|
|
44
|
+
console.log("=".repeat(80));
|
|
45
|
+
console.log(`\n${request.summary}\n`);
|
|
46
|
+
|
|
47
|
+
if (request.detail) {
|
|
48
|
+
console.log(request.detail);
|
|
49
|
+
console.log("");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (request.options && request.options.length > 0) {
|
|
53
|
+
console.log("Options:");
|
|
54
|
+
for (const opt of request.options) {
|
|
55
|
+
const desc = opt.description ? ` — ${opt.description}` : "";
|
|
56
|
+
console.log(` [${opt.key}] ${opt.label}${desc}`);
|
|
57
|
+
}
|
|
58
|
+
console.log("");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (request.timeout) {
|
|
62
|
+
const timeoutSec = Math.floor(request.timeout / 1000);
|
|
63
|
+
console.log(`[Timeout: ${timeoutSec}s | Fallback: ${request.fallback}]`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(`${"=".repeat(80)}\n`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async receive(requestId: string, timeout = 60000): Promise<InteractionResponse> {
|
|
70
|
+
const request = this.pendingRequests.get(requestId);
|
|
71
|
+
if (!request) {
|
|
72
|
+
throw new Error(`No pending request with ID: ${requestId}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!this.rl) {
|
|
76
|
+
throw new Error("CLI plugin not initialized");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const response = await this.promptUser(request, timeout);
|
|
80
|
+
this.pendingRequests.delete(requestId);
|
|
81
|
+
return response;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async cancel(requestId: string): Promise<void> {
|
|
85
|
+
this.pendingRequests.delete(requestId);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Prompt user for response with timeout
|
|
90
|
+
*/
|
|
91
|
+
private async promptUser(request: InteractionRequest, timeout: number): Promise<InteractionResponse> {
|
|
92
|
+
if (!this.rl) {
|
|
93
|
+
throw new Error("CLI plugin not initialized");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const timeoutPromise = new Promise<InteractionResponse>((resolve) => {
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
resolve({
|
|
99
|
+
requestId: request.id,
|
|
100
|
+
action: "skip",
|
|
101
|
+
respondedBy: "timeout",
|
|
102
|
+
respondedAt: Date.now(),
|
|
103
|
+
});
|
|
104
|
+
}, timeout);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const userPromise = this.getUserInput(request);
|
|
108
|
+
|
|
109
|
+
const response = await Promise.race([userPromise, timeoutPromise]);
|
|
110
|
+
return response;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get user input based on interaction type
|
|
115
|
+
*/
|
|
116
|
+
private async getUserInput(request: InteractionRequest): Promise<InteractionResponse> {
|
|
117
|
+
if (!this.rl) {
|
|
118
|
+
throw new Error("CLI plugin not initialized");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
switch (request.type) {
|
|
122
|
+
case "confirm":
|
|
123
|
+
return this.promptConfirm(request);
|
|
124
|
+
case "choose":
|
|
125
|
+
return this.promptChoose(request);
|
|
126
|
+
case "input":
|
|
127
|
+
return this.promptInput(request);
|
|
128
|
+
case "review":
|
|
129
|
+
return this.promptReview(request);
|
|
130
|
+
case "notify":
|
|
131
|
+
// Notify doesn't require input
|
|
132
|
+
return {
|
|
133
|
+
requestId: request.id,
|
|
134
|
+
action: "approve",
|
|
135
|
+
respondedBy: "system",
|
|
136
|
+
respondedAt: Date.now(),
|
|
137
|
+
};
|
|
138
|
+
case "webhook":
|
|
139
|
+
// Webhook is handled externally
|
|
140
|
+
return {
|
|
141
|
+
requestId: request.id,
|
|
142
|
+
action: "approve",
|
|
143
|
+
respondedBy: "system",
|
|
144
|
+
respondedAt: Date.now(),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Prompt for confirmation (yes/no)
|
|
151
|
+
*/
|
|
152
|
+
private async promptConfirm(request: InteractionRequest): Promise<InteractionResponse> {
|
|
153
|
+
const answer = await this.question("Approve? [y/n/skip/abort]: ");
|
|
154
|
+
const normalized = answer.toLowerCase().trim();
|
|
155
|
+
|
|
156
|
+
let action: InteractionResponse["action"];
|
|
157
|
+
if (normalized === "y" || normalized === "yes") {
|
|
158
|
+
action = "approve";
|
|
159
|
+
} else if (normalized === "n" || normalized === "no") {
|
|
160
|
+
action = "reject";
|
|
161
|
+
} else if (normalized === "skip") {
|
|
162
|
+
action = "skip";
|
|
163
|
+
} else if (normalized === "abort") {
|
|
164
|
+
action = "abort";
|
|
165
|
+
} else {
|
|
166
|
+
// Invalid input, default to skip
|
|
167
|
+
action = "skip";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
requestId: request.id,
|
|
172
|
+
action,
|
|
173
|
+
respondedBy: "user",
|
|
174
|
+
respondedAt: Date.now(),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Prompt for choice from options
|
|
180
|
+
*/
|
|
181
|
+
private async promptChoose(request: InteractionRequest): Promise<InteractionResponse> {
|
|
182
|
+
const answer = await this.question("Choose [key or skip/abort]: ");
|
|
183
|
+
const normalized = answer.toLowerCase().trim();
|
|
184
|
+
|
|
185
|
+
if (normalized === "skip") {
|
|
186
|
+
return {
|
|
187
|
+
requestId: request.id,
|
|
188
|
+
action: "skip",
|
|
189
|
+
respondedBy: "user",
|
|
190
|
+
respondedAt: Date.now(),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (normalized === "abort") {
|
|
195
|
+
return {
|
|
196
|
+
requestId: request.id,
|
|
197
|
+
action: "abort",
|
|
198
|
+
respondedBy: "user",
|
|
199
|
+
respondedAt: Date.now(),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Check if answer matches an option key
|
|
204
|
+
const option = request.options?.find((opt) => opt.key === normalized);
|
|
205
|
+
if (option) {
|
|
206
|
+
return {
|
|
207
|
+
requestId: request.id,
|
|
208
|
+
action: "choose",
|
|
209
|
+
value: option.key,
|
|
210
|
+
respondedBy: "user",
|
|
211
|
+
respondedAt: Date.now(),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Invalid choice, default to skip
|
|
216
|
+
return {
|
|
217
|
+
requestId: request.id,
|
|
218
|
+
action: "skip",
|
|
219
|
+
respondedBy: "user",
|
|
220
|
+
respondedAt: Date.now(),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Prompt for text input
|
|
226
|
+
*/
|
|
227
|
+
private async promptInput(request: InteractionRequest): Promise<InteractionResponse> {
|
|
228
|
+
const answer = await this.question("Input [or skip/abort]: ");
|
|
229
|
+
const normalized = answer.trim();
|
|
230
|
+
|
|
231
|
+
if (normalized.toLowerCase() === "skip") {
|
|
232
|
+
return {
|
|
233
|
+
requestId: request.id,
|
|
234
|
+
action: "skip",
|
|
235
|
+
respondedBy: "user",
|
|
236
|
+
respondedAt: Date.now(),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (normalized.toLowerCase() === "abort") {
|
|
241
|
+
return {
|
|
242
|
+
requestId: request.id,
|
|
243
|
+
action: "abort",
|
|
244
|
+
respondedBy: "user",
|
|
245
|
+
respondedAt: Date.now(),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
requestId: request.id,
|
|
251
|
+
action: "input",
|
|
252
|
+
value: normalized,
|
|
253
|
+
respondedBy: "user",
|
|
254
|
+
respondedAt: Date.now(),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Prompt for review (detailed approval)
|
|
260
|
+
*/
|
|
261
|
+
private async promptReview(request: InteractionRequest): Promise<InteractionResponse> {
|
|
262
|
+
const answer = await this.question("Review complete? [approve/reject/skip/abort]: ");
|
|
263
|
+
const normalized = answer.toLowerCase().trim();
|
|
264
|
+
|
|
265
|
+
let action: InteractionResponse["action"];
|
|
266
|
+
if (normalized === "approve") {
|
|
267
|
+
action = "approve";
|
|
268
|
+
} else if (normalized === "reject") {
|
|
269
|
+
action = "reject";
|
|
270
|
+
} else if (normalized === "skip") {
|
|
271
|
+
action = "skip";
|
|
272
|
+
} else if (normalized === "abort") {
|
|
273
|
+
action = "abort";
|
|
274
|
+
} else {
|
|
275
|
+
action = "skip";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
requestId: request.id,
|
|
280
|
+
action,
|
|
281
|
+
respondedBy: "user",
|
|
282
|
+
respondedAt: Date.now(),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Async wrapper for readline.question
|
|
288
|
+
*/
|
|
289
|
+
private async question(prompt: string): Promise<string> {
|
|
290
|
+
if (!this.rl) {
|
|
291
|
+
throw new Error("CLI plugin not initialized");
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return new Promise((resolve) => {
|
|
295
|
+
this.rl?.question(prompt, (answer) => {
|
|
296
|
+
resolve(answer);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|