@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,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-Agent Verification (ADR-003)
|
|
3
|
+
*
|
|
4
|
+
* Runs verification after the agent completes, reverts story state on failure.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawn } from "bun";
|
|
8
|
+
import type { NaxConfig } from "../config";
|
|
9
|
+
import { getSafeLogger } from "../logger";
|
|
10
|
+
import type { StoryMetrics } from "../metrics";
|
|
11
|
+
import type { PRD, UserStory } from "../prd";
|
|
12
|
+
import { getExpectedFiles, savePRD } from "../prd";
|
|
13
|
+
import { getTierConfig } from "./escalation";
|
|
14
|
+
import { revertStoriesOnFailure, runRectificationLoop } from "./post-verify-rectification";
|
|
15
|
+
import { appendProgress } from "./progress";
|
|
16
|
+
import { getEnvironmentalEscalationThreshold, parseTestOutput, runVerification } from "./verification";
|
|
17
|
+
|
|
18
|
+
/** Get test files changed since a git ref. Returns empty array if detection fails. */
|
|
19
|
+
async function getChangedTestFiles(workdir: string, gitRef?: string): Promise<string[]> {
|
|
20
|
+
if (!gitRef) return [];
|
|
21
|
+
try {
|
|
22
|
+
const proc = spawn({
|
|
23
|
+
cmd: ["git", "diff", "--name-only", gitRef, "HEAD"],
|
|
24
|
+
cwd: workdir,
|
|
25
|
+
stdout: "pipe",
|
|
26
|
+
stderr: "pipe",
|
|
27
|
+
});
|
|
28
|
+
const exitCode = await proc.exited;
|
|
29
|
+
if (exitCode !== 0) return [];
|
|
30
|
+
const stdout = await new Response(proc.stdout).text();
|
|
31
|
+
return stdout
|
|
32
|
+
.trim()
|
|
33
|
+
.split("\n")
|
|
34
|
+
.filter(
|
|
35
|
+
(f) =>
|
|
36
|
+
f && (f.includes("test/") || f.includes("__tests__/") || f.endsWith(".test.ts") || f.endsWith(".spec.ts")),
|
|
37
|
+
);
|
|
38
|
+
} catch {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Scope a test command to only run specific test files. */
|
|
44
|
+
function scopeTestCommand(baseCommand: string, testFiles: string[]): string {
|
|
45
|
+
if (testFiles.length === 0) return baseCommand;
|
|
46
|
+
return `${baseCommand} ${testFiles.join(" ")}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface PostVerifyOptions {
|
|
50
|
+
config: NaxConfig;
|
|
51
|
+
prd: PRD;
|
|
52
|
+
prdPath: string;
|
|
53
|
+
workdir: string;
|
|
54
|
+
featureDir?: string;
|
|
55
|
+
story: UserStory;
|
|
56
|
+
storiesToExecute: UserStory[];
|
|
57
|
+
allStoryMetrics: StoryMetrics[];
|
|
58
|
+
timeoutRetryCountMap: Map<string, number>;
|
|
59
|
+
storyGitRef?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface PostVerifyResult {
|
|
63
|
+
passed: boolean;
|
|
64
|
+
prd: PRD;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Run post-agent verification and handle failure state.
|
|
69
|
+
*
|
|
70
|
+
* @design Shell command in config.quality.commands.test is operator-controlled,
|
|
71
|
+
* not user/PRD input. No shell injection risk from untrusted sources.
|
|
72
|
+
*/
|
|
73
|
+
export async function runPostAgentVerification(opts: PostVerifyOptions): Promise<PostVerifyResult> {
|
|
74
|
+
const {
|
|
75
|
+
config,
|
|
76
|
+
prd,
|
|
77
|
+
prdPath,
|
|
78
|
+
workdir,
|
|
79
|
+
featureDir,
|
|
80
|
+
story,
|
|
81
|
+
storiesToExecute,
|
|
82
|
+
allStoryMetrics,
|
|
83
|
+
timeoutRetryCountMap,
|
|
84
|
+
storyGitRef,
|
|
85
|
+
} = opts;
|
|
86
|
+
const logger = getSafeLogger();
|
|
87
|
+
|
|
88
|
+
if (!config.quality.commands.test) return { passed: true, prd };
|
|
89
|
+
|
|
90
|
+
// Scoped verification: only run test files changed by this story
|
|
91
|
+
const changedTestFiles = await getChangedTestFiles(workdir, storyGitRef);
|
|
92
|
+
const testCommand = scopeTestCommand(config.quality.commands.test, changedTestFiles);
|
|
93
|
+
const timeoutRetryCount = timeoutRetryCountMap.get(story.id) || 0;
|
|
94
|
+
|
|
95
|
+
const verificationResult = await runVerification({
|
|
96
|
+
workingDirectory: workdir,
|
|
97
|
+
expectedFiles: getExpectedFiles(story),
|
|
98
|
+
command: testCommand,
|
|
99
|
+
timeoutSeconds: config.execution.verificationTimeoutSeconds,
|
|
100
|
+
forceExit: config.quality.forceExit,
|
|
101
|
+
detectOpenHandles: config.quality.detectOpenHandles,
|
|
102
|
+
detectOpenHandlesRetries: config.quality.detectOpenHandlesRetries,
|
|
103
|
+
timeoutRetryCount,
|
|
104
|
+
gracePeriodMs: config.quality.gracePeriodMs,
|
|
105
|
+
drainTimeoutMs: config.quality.drainTimeoutMs,
|
|
106
|
+
shell: config.quality.shell,
|
|
107
|
+
stripEnvVars: config.quality.stripEnvVars,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const rectificationEnabled = config.execution.rectification?.enabled ?? false;
|
|
111
|
+
|
|
112
|
+
if (verificationResult.success) {
|
|
113
|
+
logger?.info("verification", "Scoped verification passed");
|
|
114
|
+
if (verificationResult.output) {
|
|
115
|
+
const analysis = parseTestOutput(verificationResult.output, 0);
|
|
116
|
+
if (analysis.passCount > 0) {
|
|
117
|
+
logger?.debug("verification", "Scoped test results", {
|
|
118
|
+
passCount: analysis.passCount,
|
|
119
|
+
failCount: analysis.failCount,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Regression Gate (BUG-009): run full suite after scoped tests pass
|
|
125
|
+
const regressionResult = await runRegressionGate(config, workdir, story, changedTestFiles, rectificationEnabled);
|
|
126
|
+
if (regressionResult === "passed" || regressionResult === "skipped") {
|
|
127
|
+
return { passed: true, prd };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Regression failed -- revert stories
|
|
131
|
+
const updatedPrd = await revertStoriesOnFailure({
|
|
132
|
+
prd,
|
|
133
|
+
prdPath,
|
|
134
|
+
story,
|
|
135
|
+
storiesToExecute,
|
|
136
|
+
allStoryMetrics,
|
|
137
|
+
featureDir,
|
|
138
|
+
diagnosticContext: "REGRESSION: full-suite regression detected",
|
|
139
|
+
countsTowardEscalation: true,
|
|
140
|
+
});
|
|
141
|
+
return { passed: false, prd: updatedPrd };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// --- Verification failed ---
|
|
145
|
+
// Attempt rectification if enabled and tests failed (not timeout/env)
|
|
146
|
+
const isTestFailure = verificationResult.status === "TEST_FAILURE" && verificationResult.output;
|
|
147
|
+
if (rectificationEnabled && isTestFailure && verificationResult.output) {
|
|
148
|
+
const fixed = await runRectificationLoop({
|
|
149
|
+
config,
|
|
150
|
+
workdir,
|
|
151
|
+
story,
|
|
152
|
+
testCommand,
|
|
153
|
+
timeoutSeconds: config.execution.verificationTimeoutSeconds,
|
|
154
|
+
testOutput: verificationResult.output,
|
|
155
|
+
});
|
|
156
|
+
if (fixed) return { passed: true, prd };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Track timeout retries for --detectOpenHandles escalation
|
|
160
|
+
if (verificationResult.status === "TIMEOUT") {
|
|
161
|
+
timeoutRetryCountMap.set(story.id, timeoutRetryCount + 1);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
logger?.warn("verification", `Verification ${verificationResult.status}`, {
|
|
165
|
+
status: verificationResult.status,
|
|
166
|
+
error: verificationResult.error?.split("\n")[0],
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Handle environmental failure escalation
|
|
170
|
+
if (verificationResult.countsTowardEscalation && verificationResult.status === "ENVIRONMENTAL_FAILURE") {
|
|
171
|
+
checkEnvironmentalEscalation(config, story, prd, logger);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Revert stories and save
|
|
175
|
+
const diagnosticContext = verificationResult.error || `Verification failed: ${verificationResult.status}`;
|
|
176
|
+
const updatedPrd = await revertStoriesOnFailure({
|
|
177
|
+
prd,
|
|
178
|
+
prdPath,
|
|
179
|
+
story,
|
|
180
|
+
storiesToExecute,
|
|
181
|
+
allStoryMetrics,
|
|
182
|
+
featureDir,
|
|
183
|
+
diagnosticContext,
|
|
184
|
+
countsTowardEscalation: verificationResult.countsTowardEscalation ?? false,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return { passed: false, prd: updatedPrd };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Run regression gate (full suite) after scoped tests pass. */
|
|
191
|
+
async function runRegressionGate(
|
|
192
|
+
config: NaxConfig,
|
|
193
|
+
workdir: string,
|
|
194
|
+
story: UserStory,
|
|
195
|
+
changedTestFiles: string[],
|
|
196
|
+
rectificationEnabled: boolean,
|
|
197
|
+
): Promise<"passed" | "skipped" | "failed"> {
|
|
198
|
+
const logger = getSafeLogger();
|
|
199
|
+
const regressionGateEnabled = config.execution.regressionGate?.enabled ?? true;
|
|
200
|
+
const scopedTestsWereRun = changedTestFiles.length > 0;
|
|
201
|
+
|
|
202
|
+
if (!regressionGateEnabled || !scopedTestsWereRun) {
|
|
203
|
+
if (regressionGateEnabled && !scopedTestsWereRun) {
|
|
204
|
+
logger?.debug("regression-gate", "Skipping regression gate (full suite already run in scoped verification)");
|
|
205
|
+
}
|
|
206
|
+
return "skipped";
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
logger?.info("regression-gate", "Running full-suite regression gate");
|
|
210
|
+
const fullSuiteCommand = config.quality.commands.test ?? "bun test";
|
|
211
|
+
const regressionResult = await runVerification({
|
|
212
|
+
workingDirectory: workdir,
|
|
213
|
+
expectedFiles: getExpectedFiles(story),
|
|
214
|
+
command: fullSuiteCommand,
|
|
215
|
+
timeoutSeconds: config.execution.regressionGate.timeoutSeconds,
|
|
216
|
+
forceExit: config.quality.forceExit,
|
|
217
|
+
detectOpenHandles: config.quality.detectOpenHandles,
|
|
218
|
+
detectOpenHandlesRetries: config.quality.detectOpenHandlesRetries,
|
|
219
|
+
timeoutRetryCount: 0,
|
|
220
|
+
gracePeriodMs: config.quality.gracePeriodMs,
|
|
221
|
+
drainTimeoutMs: config.quality.drainTimeoutMs,
|
|
222
|
+
shell: config.quality.shell,
|
|
223
|
+
stripEnvVars: config.quality.stripEnvVars,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
if (regressionResult.success) {
|
|
227
|
+
logger?.info("regression-gate", "Full-suite regression gate passed");
|
|
228
|
+
return "passed";
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
logger?.warn("regression-gate", "Full-suite regression detected", { status: regressionResult.status });
|
|
232
|
+
|
|
233
|
+
// Attempt rectification on regression failures
|
|
234
|
+
const isTestFailure = regressionResult.status === "TEST_FAILURE" && regressionResult.output;
|
|
235
|
+
if (rectificationEnabled && isTestFailure && regressionResult.output) {
|
|
236
|
+
const fixed = await runRectificationLoop({
|
|
237
|
+
config,
|
|
238
|
+
workdir,
|
|
239
|
+
story,
|
|
240
|
+
testCommand: fullSuiteCommand,
|
|
241
|
+
timeoutSeconds: config.execution.regressionGate.timeoutSeconds,
|
|
242
|
+
testOutput: regressionResult.output,
|
|
243
|
+
promptPrefix:
|
|
244
|
+
"# REGRESSION: Cross-Story Test Failures\n\nYour changes passed scoped tests but broke unrelated tests. Fix these regressions.",
|
|
245
|
+
});
|
|
246
|
+
if (fixed) return "passed";
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return "failed";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Check if environmental failure should trigger early escalation. */
|
|
253
|
+
function checkEnvironmentalEscalation(
|
|
254
|
+
config: NaxConfig,
|
|
255
|
+
story: UserStory,
|
|
256
|
+
prd: PRD,
|
|
257
|
+
logger: ReturnType<typeof getSafeLogger>,
|
|
258
|
+
): void {
|
|
259
|
+
const currentTier = story.routing?.modelTier || config.autoMode.escalation.tierOrder[0]?.tier;
|
|
260
|
+
const tierCfg = currentTier ? getTierConfig(currentTier, config.autoMode.escalation.tierOrder) : undefined;
|
|
261
|
+
if (!tierCfg) return;
|
|
262
|
+
|
|
263
|
+
const threshold = getEnvironmentalEscalationThreshold(
|
|
264
|
+
tierCfg.attempts,
|
|
265
|
+
config.quality.environmentalEscalationDivisor,
|
|
266
|
+
);
|
|
267
|
+
const currentAttempts = prd.userStories.find((s) => s.id === story.id)?.attempts ?? 0;
|
|
268
|
+
if (currentAttempts >= threshold) {
|
|
269
|
+
logger?.warn("verification", "Environmental failure hit early escalation threshold", {
|
|
270
|
+
currentAttempts,
|
|
271
|
+
threshold,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progress Logging
|
|
3
|
+
*
|
|
4
|
+
* Append timestamped entries to progress.txt after story completion.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import type { StoryStatus } from "../prd";
|
|
9
|
+
|
|
10
|
+
/** Append a progress entry to progress.txt */
|
|
11
|
+
export async function appendProgress(
|
|
12
|
+
featureDir: string,
|
|
13
|
+
storyId: string,
|
|
14
|
+
status: StoryStatus,
|
|
15
|
+
message: string,
|
|
16
|
+
): Promise<void> {
|
|
17
|
+
const progressPath = join(featureDir, "progress.txt");
|
|
18
|
+
const timestamp = new Date().toISOString();
|
|
19
|
+
const entry = `[${timestamp}] ${storyId} — ${status.toUpperCase()} — ${message}\n`;
|
|
20
|
+
|
|
21
|
+
// Append to file (creates if doesn't exist)
|
|
22
|
+
const file = Bun.file(progressPath);
|
|
23
|
+
const existing = (await file.exists()) ? await file.text() : "";
|
|
24
|
+
await Bun.write(progressPath, existing + entry);
|
|
25
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt builders for agent sessions
|
|
3
|
+
*
|
|
4
|
+
* Constructs prompts for single-session and batch execution modes.
|
|
5
|
+
* Supports constitution injection for project-level governance.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ConstitutionResult } from "../constitution";
|
|
9
|
+
import type { UserStory } from "../prd";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build prompt for single-session (test-after) execution
|
|
13
|
+
*
|
|
14
|
+
* Priority order: Constitution (95) → Story prompt (100) → Context (90)
|
|
15
|
+
*
|
|
16
|
+
* @param story - User story to execute
|
|
17
|
+
* @param contextMarkdown - Optional context markdown from context builder
|
|
18
|
+
* @param constitution - Optional constitution result
|
|
19
|
+
* @returns Formatted prompt string
|
|
20
|
+
*/
|
|
21
|
+
export function buildSingleSessionPrompt(
|
|
22
|
+
story: UserStory,
|
|
23
|
+
contextMarkdown?: string,
|
|
24
|
+
constitution?: ConstitutionResult,
|
|
25
|
+
): string {
|
|
26
|
+
const basePrompt = `# Task: ${story.title}
|
|
27
|
+
|
|
28
|
+
**Description:**
|
|
29
|
+
${story.description}
|
|
30
|
+
|
|
31
|
+
**Acceptance Criteria:**
|
|
32
|
+
${story.acceptanceCriteria.map((ac, i) => `${i + 1}. ${ac}`).join("\n")}
|
|
33
|
+
|
|
34
|
+
**Instructions:**
|
|
35
|
+
1. Implement the functionality described above
|
|
36
|
+
2. Write tests to verify all acceptance criteria are met
|
|
37
|
+
3. Ensure all tests pass
|
|
38
|
+
4. Follow existing code patterns and conventions
|
|
39
|
+
5. If existing test coverage is listed below, do NOT duplicate those tests — only test NEW behavior
|
|
40
|
+
6. Commit your changes when done
|
|
41
|
+
|
|
42
|
+
Use test-after approach: implement first, then add tests to verify.`;
|
|
43
|
+
|
|
44
|
+
// Build sections in priority order
|
|
45
|
+
const sections: string[] = [];
|
|
46
|
+
|
|
47
|
+
// Priority 95: Constitution
|
|
48
|
+
if (constitution) {
|
|
49
|
+
sections.push(`# CONSTITUTION (follow these rules strictly)
|
|
50
|
+
|
|
51
|
+
${constitution.content}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Priority 100: Story prompt (always first in display order)
|
|
55
|
+
sections.push(basePrompt);
|
|
56
|
+
|
|
57
|
+
// Priority 90: Context
|
|
58
|
+
if (contextMarkdown) {
|
|
59
|
+
sections.push(contextMarkdown);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return sections.join("\n\n---\n\n");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build prompt for batched stories (multiple simple stories in one session)
|
|
67
|
+
*
|
|
68
|
+
* Priority order: Constitution (95) → Story prompt (100) → Context (90)
|
|
69
|
+
*
|
|
70
|
+
* @param stories - Array of user stories to execute in batch
|
|
71
|
+
* @param contextMarkdown - Optional context markdown from context builder
|
|
72
|
+
* @param constitution - Optional constitution result
|
|
73
|
+
* @returns Formatted prompt string
|
|
74
|
+
*/
|
|
75
|
+
export function buildBatchPrompt(
|
|
76
|
+
stories: UserStory[],
|
|
77
|
+
contextMarkdown?: string,
|
|
78
|
+
constitution?: ConstitutionResult,
|
|
79
|
+
): string {
|
|
80
|
+
const storyPrompts = stories
|
|
81
|
+
.map((story, idx) => {
|
|
82
|
+
return `## Story ${idx + 1}: ${story.id} — ${story.title}
|
|
83
|
+
|
|
84
|
+
**Description:**
|
|
85
|
+
${story.description}
|
|
86
|
+
|
|
87
|
+
**Acceptance Criteria:**
|
|
88
|
+
${story.acceptanceCriteria.map((ac, i) => `${i + 1}. ${ac}`).join("\n")}`;
|
|
89
|
+
})
|
|
90
|
+
.join("\n\n");
|
|
91
|
+
|
|
92
|
+
const basePrompt = `# Batch Task: ${stories.length} Stories
|
|
93
|
+
|
|
94
|
+
You are assigned ${stories.length} related stories to implement in sequence. Each story should be implemented, tested, and committed separately.
|
|
95
|
+
|
|
96
|
+
${storyPrompts}
|
|
97
|
+
|
|
98
|
+
**Instructions:**
|
|
99
|
+
1. Implement each story in order
|
|
100
|
+
2. Write tests to verify all acceptance criteria are met for each story
|
|
101
|
+
3. Ensure all tests pass for each story
|
|
102
|
+
4. **Commit each story separately** with a clear commit message referencing the story ID
|
|
103
|
+
5. Follow existing code patterns and conventions
|
|
104
|
+
6. If existing test coverage is listed below, do NOT duplicate those tests — only test NEW behavior
|
|
105
|
+
|
|
106
|
+
Use test-after approach: implement first, then add tests to verify.`;
|
|
107
|
+
|
|
108
|
+
// Build sections in priority order
|
|
109
|
+
const sections: string[] = [];
|
|
110
|
+
|
|
111
|
+
// Priority 95: Constitution
|
|
112
|
+
if (constitution) {
|
|
113
|
+
sections.push(`# CONSTITUTION (follow these rules strictly)
|
|
114
|
+
|
|
115
|
+
${constitution.content}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Priority 100: Story prompt (always first in display order)
|
|
119
|
+
sections.push(basePrompt);
|
|
120
|
+
|
|
121
|
+
// Priority 90: Context
|
|
122
|
+
if (contextMarkdown) {
|
|
123
|
+
sections.push(contextMarkdown);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return sections.join("\n\n---\n\n");
|
|
127
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue File Handler
|
|
3
|
+
*
|
|
4
|
+
* Provides atomic read/write operations for .queue.txt command files.
|
|
5
|
+
* Uses rename-before-read pattern to prevent race conditions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { getLogger } from "../logger";
|
|
10
|
+
import { parseQueueFile } from "../queue";
|
|
11
|
+
import type { QueueCommand } from "../queue";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Safely get logger instance, returns null if not initialized
|
|
15
|
+
*/
|
|
16
|
+
function getSafeLogger() {
|
|
17
|
+
try {
|
|
18
|
+
return getLogger();
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Read and parse queue file atomically.
|
|
26
|
+
* Uses rename-before-read pattern to prevent race conditions:
|
|
27
|
+
* 1. Rename .queue.txt → .queue.txt.processing (atomic operation)
|
|
28
|
+
* 2. Read from .queue.txt.processing
|
|
29
|
+
* 3. Delete .queue.txt.processing after processing
|
|
30
|
+
*
|
|
31
|
+
* This ensures commands written during processing aren't lost.
|
|
32
|
+
*
|
|
33
|
+
* @param workdir - Working directory containing .queue.txt
|
|
34
|
+
* @returns Array of parsed queue commands, or empty array if no queue file
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const commands = await readQueueFile("/path/to/project");
|
|
39
|
+
* for (const cmd of commands) {
|
|
40
|
+
* if (cmd.type === "PAUSE") {
|
|
41
|
+
* // Handle pause
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
* await clearQueueFile("/path/to/project");
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export async function readQueueFile(workdir: string): Promise<QueueCommand[]> {
|
|
48
|
+
const queuePath = path.join(workdir, ".queue.txt");
|
|
49
|
+
const processingPath = path.join(workdir, ".queue.txt.processing");
|
|
50
|
+
const logger = getSafeLogger();
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Check if queue file exists
|
|
54
|
+
const file = Bun.file(queuePath);
|
|
55
|
+
const exists = await file.exists();
|
|
56
|
+
if (!exists) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Atomically rename to .processing (prevents concurrent reads)
|
|
61
|
+
try {
|
|
62
|
+
await Bun.spawn(["mv", queuePath, processingPath], { stdout: "pipe" }).exited;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
// File was already moved by another process, or doesn't exist anymore
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Read from processing file
|
|
69
|
+
const processingFile = Bun.file(processingPath);
|
|
70
|
+
const content = await processingFile.text();
|
|
71
|
+
const result = parseQueueFile(content);
|
|
72
|
+
|
|
73
|
+
return result.commands;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
logger?.warn("queue", "Failed to read queue file", {
|
|
76
|
+
error: (error as Error).message,
|
|
77
|
+
});
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Clear queue file after processing commands.
|
|
84
|
+
* Deletes .queue.txt.processing file.
|
|
85
|
+
*
|
|
86
|
+
* @param workdir - Working directory containing .queue.txt.processing
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* const commands = await readQueueFile("/path/to/project");
|
|
91
|
+
* // Process commands...
|
|
92
|
+
* await clearQueueFile("/path/to/project");
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export async function clearQueueFile(workdir: string): Promise<void> {
|
|
96
|
+
const processingPath = path.join(workdir, ".queue.txt.processing");
|
|
97
|
+
const logger = getSafeLogger();
|
|
98
|
+
try {
|
|
99
|
+
const file = Bun.file(processingPath);
|
|
100
|
+
const exists = await file.exists();
|
|
101
|
+
if (exists) {
|
|
102
|
+
await Bun.spawn(["rm", processingPath], { stdout: "pipe" }).exited;
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
logger?.warn("queue", "Failed to clear queue file", {
|
|
106
|
+
error: (error as Error).message,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rectification Core Logic (v0.11)
|
|
3
|
+
*
|
|
4
|
+
* DEPRECATED: Use src/verification/rectification.ts instead.
|
|
5
|
+
* This file is kept for backward compatibility only.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Re-export from unified verification layer
|
|
9
|
+
export {
|
|
10
|
+
type RectificationState,
|
|
11
|
+
shouldRetryRectification,
|
|
12
|
+
createRectificationPrompt,
|
|
13
|
+
} from "../verification/rectification";
|