@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,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Precheck orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Runs all prechecks with formatted output. Stops on first Tier 1 blocker (fail-fast).
|
|
5
|
+
* Collects all Tier 2 warnings. Formats human-readable output with emoji indicators.
|
|
6
|
+
* Supports --json flag for machine-readable output.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { NaxConfig } from "../config";
|
|
10
|
+
import type { PRD } from "../prd/types";
|
|
11
|
+
import {
|
|
12
|
+
checkClaudeCLI,
|
|
13
|
+
checkClaudeMdExists,
|
|
14
|
+
checkDependenciesInstalled,
|
|
15
|
+
checkDiskSpace,
|
|
16
|
+
checkGitRepoExists,
|
|
17
|
+
checkGitUserConfigured,
|
|
18
|
+
checkGitignoreCoversNax,
|
|
19
|
+
checkLintCommand,
|
|
20
|
+
checkOptionalCommands,
|
|
21
|
+
checkPRDValid,
|
|
22
|
+
checkPendingStories,
|
|
23
|
+
checkStaleLock,
|
|
24
|
+
checkTestCommand,
|
|
25
|
+
checkTypecheckCommand,
|
|
26
|
+
checkWorkingTreeClean,
|
|
27
|
+
} from "./checks";
|
|
28
|
+
import type { Check, PrecheckResult } from "./types";
|
|
29
|
+
|
|
30
|
+
/** Formatted output with summary */
|
|
31
|
+
export interface PrecheckOutput {
|
|
32
|
+
/** Whether all checks passed (no blockers) */
|
|
33
|
+
passed: boolean;
|
|
34
|
+
/** Tier 1 blockers (if any) */
|
|
35
|
+
blockers: Check[];
|
|
36
|
+
/** Tier 2 warnings (if any) */
|
|
37
|
+
warnings: Check[];
|
|
38
|
+
/** Summary counts */
|
|
39
|
+
summary: {
|
|
40
|
+
total: number;
|
|
41
|
+
passed: number;
|
|
42
|
+
failed: number;
|
|
43
|
+
warnings: number;
|
|
44
|
+
};
|
|
45
|
+
/** PRD feature name (for context) */
|
|
46
|
+
feature: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Exit codes for precheck command */
|
|
50
|
+
export const EXIT_CODES = {
|
|
51
|
+
/** All checks passed (or only warnings) */
|
|
52
|
+
SUCCESS: 0,
|
|
53
|
+
/** Tier 1 blocker detected */
|
|
54
|
+
BLOCKER: 1,
|
|
55
|
+
/** Invalid PRD structure */
|
|
56
|
+
INVALID_PRD: 2,
|
|
57
|
+
} as const;
|
|
58
|
+
|
|
59
|
+
/** Options for precheck execution */
|
|
60
|
+
export interface PrecheckOptions {
|
|
61
|
+
/** Output format: "human" (default) or "json" */
|
|
62
|
+
format?: "human" | "json";
|
|
63
|
+
/** Working directory */
|
|
64
|
+
workdir: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Extended result with exit code for CLI usage */
|
|
68
|
+
export interface PrecheckResultWithCode {
|
|
69
|
+
/** Precheck result */
|
|
70
|
+
result: PrecheckResult;
|
|
71
|
+
/** Exit code (0=success, 1=blocker, 2=invalid PRD) */
|
|
72
|
+
exitCode: number;
|
|
73
|
+
/** Output for display */
|
|
74
|
+
output: PrecheckOutput;
|
|
75
|
+
/** Flagged stories from story size gate (v0.16.0) */
|
|
76
|
+
flaggedStories?: import("./story-size-gate").FlaggedStory[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Run all precheck validations.
|
|
81
|
+
* Returns result, exit code, and formatted output.
|
|
82
|
+
*/
|
|
83
|
+
export async function runPrecheck(
|
|
84
|
+
config: NaxConfig,
|
|
85
|
+
prd: PRD,
|
|
86
|
+
options?: PrecheckOptions,
|
|
87
|
+
): Promise<PrecheckResultWithCode> {
|
|
88
|
+
const workdir = options?.workdir || process.cwd();
|
|
89
|
+
const format = options?.format || "human";
|
|
90
|
+
|
|
91
|
+
const passed: Check[] = [];
|
|
92
|
+
const blockers: Check[] = [];
|
|
93
|
+
const warnings: Check[] = [];
|
|
94
|
+
|
|
95
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
96
|
+
// Tier 1 Blockers - fail-fast on first failure
|
|
97
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
const tier1Checks = [
|
|
100
|
+
() => checkGitRepoExists(workdir),
|
|
101
|
+
() => checkWorkingTreeClean(workdir),
|
|
102
|
+
() => checkStaleLock(workdir),
|
|
103
|
+
() => checkPRDValid(prd),
|
|
104
|
+
() => checkClaudeCLI(),
|
|
105
|
+
() => checkDependenciesInstalled(workdir),
|
|
106
|
+
() => checkTestCommand(config),
|
|
107
|
+
() => checkLintCommand(config),
|
|
108
|
+
() => checkTypecheckCommand(config),
|
|
109
|
+
() => checkGitUserConfigured(workdir),
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
for (const checkFn of tier1Checks) {
|
|
113
|
+
const result = await checkFn();
|
|
114
|
+
|
|
115
|
+
if (format === "human") {
|
|
116
|
+
printCheckResult(result);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (result.passed) {
|
|
120
|
+
passed.push(result);
|
|
121
|
+
} else {
|
|
122
|
+
blockers.push(result);
|
|
123
|
+
// Fail-fast: stop on first blocker
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
129
|
+
// Tier 2 Warnings - run all regardless of failures
|
|
130
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
let flaggedStories: import("./story-size-gate").FlaggedStory[] = [];
|
|
133
|
+
|
|
134
|
+
// Only run Tier 2 if no blockers
|
|
135
|
+
if (blockers.length === 0) {
|
|
136
|
+
const tier2Checks = [
|
|
137
|
+
() => checkClaudeMdExists(workdir),
|
|
138
|
+
() => checkDiskSpace(),
|
|
139
|
+
() => checkPendingStories(prd),
|
|
140
|
+
() => checkOptionalCommands(config),
|
|
141
|
+
() => checkGitignoreCoversNax(workdir),
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
for (const checkFn of tier2Checks) {
|
|
145
|
+
const result = await checkFn();
|
|
146
|
+
|
|
147
|
+
if (format === "human") {
|
|
148
|
+
printCheckResult(result);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (result.passed) {
|
|
152
|
+
passed.push(result);
|
|
153
|
+
} else {
|
|
154
|
+
warnings.push(result);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Story size gate (v0.16.0) — separate from standard checks, returns metadata
|
|
159
|
+
const { checkStorySizeGate } = await import("./story-size-gate");
|
|
160
|
+
const sizeGateResult = await checkStorySizeGate(config, prd);
|
|
161
|
+
|
|
162
|
+
if (format === "human") {
|
|
163
|
+
printCheckResult(sizeGateResult.check);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (sizeGateResult.check.passed) {
|
|
167
|
+
passed.push(sizeGateResult.check);
|
|
168
|
+
} else {
|
|
169
|
+
warnings.push(sizeGateResult.check);
|
|
170
|
+
flaggedStories = sizeGateResult.flaggedStories;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
175
|
+
// Output formatting
|
|
176
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
177
|
+
|
|
178
|
+
const output: PrecheckOutput = {
|
|
179
|
+
passed: blockers.length === 0,
|
|
180
|
+
blockers,
|
|
181
|
+
warnings,
|
|
182
|
+
summary: {
|
|
183
|
+
total: passed.length + blockers.length + warnings.length,
|
|
184
|
+
passed: passed.length,
|
|
185
|
+
failed: blockers.length,
|
|
186
|
+
warnings: warnings.length,
|
|
187
|
+
},
|
|
188
|
+
feature: prd.feature,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Determine exit code
|
|
192
|
+
let exitCode: number = EXIT_CODES.SUCCESS;
|
|
193
|
+
if (blockers.length > 0) {
|
|
194
|
+
// Check if PRD validation failed specifically
|
|
195
|
+
const hasPRDError = blockers.some((b) => b.name === "prd-valid");
|
|
196
|
+
exitCode = hasPRDError ? EXIT_CODES.INVALID_PRD : EXIT_CODES.BLOCKER;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (format === "json") {
|
|
200
|
+
console.log(JSON.stringify(output, null, 2));
|
|
201
|
+
} else {
|
|
202
|
+
printSummary(output);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
result: { blockers, warnings },
|
|
207
|
+
exitCode,
|
|
208
|
+
output,
|
|
209
|
+
flaggedStories,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Print a single check result with emoji indicator.
|
|
215
|
+
*/
|
|
216
|
+
function printCheckResult(check: Check): void {
|
|
217
|
+
let emoji = "";
|
|
218
|
+
if (check.passed) {
|
|
219
|
+
emoji = "✓";
|
|
220
|
+
} else if (check.tier === "blocker") {
|
|
221
|
+
emoji = "✗";
|
|
222
|
+
} else {
|
|
223
|
+
emoji = "⚠";
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
console.log(`${emoji} ${check.name}: ${check.message}`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Print summary line with counts.
|
|
231
|
+
*/
|
|
232
|
+
function printSummary(output: PrecheckOutput): void {
|
|
233
|
+
console.log("");
|
|
234
|
+
console.log("─────────────────────────────────────────────────────────────────────────────");
|
|
235
|
+
console.log(`Feature: ${output.feature}`);
|
|
236
|
+
console.log(
|
|
237
|
+
`Checks: ${output.summary.total} total | ${output.summary.passed} passed | ${output.summary.failed} failed | ${output.summary.warnings} warnings`,
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
if (output.blockers.length > 0) {
|
|
241
|
+
console.log("\n❌ BLOCKED: Cannot proceed due to failed prechecks");
|
|
242
|
+
} else if (output.warnings.length > 0) {
|
|
243
|
+
console.log("\n⚠️ WARNINGS: Prechecks passed with warnings");
|
|
244
|
+
} else {
|
|
245
|
+
console.log("\n✅ PASSED: All prechecks passed");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story Size Gate (v0.16.0)
|
|
3
|
+
*
|
|
4
|
+
* Detects user stories that are too large (waste tokens, yield poor output).
|
|
5
|
+
* Uses heuristic signals: AC count, description length, bullet point count.
|
|
6
|
+
* Returns Tier 2 warning with flaggedStory metadata for interaction prompts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { NaxConfig } from "../config";
|
|
10
|
+
import type { PRD, UserStory } from "../prd/types";
|
|
11
|
+
import type { Check } from "./types";
|
|
12
|
+
|
|
13
|
+
/** Flagged story metadata */
|
|
14
|
+
export interface FlaggedStory {
|
|
15
|
+
storyId: string;
|
|
16
|
+
signals: {
|
|
17
|
+
acCount: { value: number; threshold: number; flagged: boolean };
|
|
18
|
+
descriptionLength: { value: number; threshold: number; flagged: boolean };
|
|
19
|
+
bulletPoints: { value: number; threshold: number; flagged: boolean };
|
|
20
|
+
};
|
|
21
|
+
recommendation: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Story size gate result with metadata */
|
|
25
|
+
export interface StorySizeGateResult {
|
|
26
|
+
check: Check;
|
|
27
|
+
flaggedStories: FlaggedStory[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Count bullet points in text (lines starting with -, *, •, or digit.)
|
|
32
|
+
*/
|
|
33
|
+
function countBulletPoints(text: string): number {
|
|
34
|
+
const lines = text.split("\n");
|
|
35
|
+
const bulletPattern = /^\s*[-*•]|\d+\./;
|
|
36
|
+
return lines.filter((line) => bulletPattern.test(line)).length;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Analyze a single story for size signals
|
|
41
|
+
*/
|
|
42
|
+
function analyzeStory(story: UserStory, config: NaxConfig): FlaggedStory | null {
|
|
43
|
+
const thresholds = config.precheck?.storySizeGate ?? {
|
|
44
|
+
enabled: true,
|
|
45
|
+
maxAcCount: 6,
|
|
46
|
+
maxDescriptionLength: 2000,
|
|
47
|
+
maxBulletPoints: 8,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const acCount = story.acceptanceCriteria.length;
|
|
51
|
+
const descriptionLength = story.description.length;
|
|
52
|
+
const bulletPoints = countBulletPoints(story.description);
|
|
53
|
+
|
|
54
|
+
const acFlagged = acCount > thresholds.maxAcCount;
|
|
55
|
+
const descFlagged = descriptionLength > thresholds.maxDescriptionLength;
|
|
56
|
+
const bulletsFlagged = bulletPoints > thresholds.maxBulletPoints;
|
|
57
|
+
|
|
58
|
+
// Only flag if at least one signal exceeds threshold
|
|
59
|
+
if (!acFlagged && !descFlagged && !bulletsFlagged) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const signals = {
|
|
64
|
+
acCount: { value: acCount, threshold: thresholds.maxAcCount, flagged: acFlagged },
|
|
65
|
+
descriptionLength: {
|
|
66
|
+
value: descriptionLength,
|
|
67
|
+
threshold: thresholds.maxDescriptionLength,
|
|
68
|
+
flagged: descFlagged,
|
|
69
|
+
},
|
|
70
|
+
bulletPoints: { value: bulletPoints, threshold: thresholds.maxBulletPoints, flagged: bulletsFlagged },
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Build recommendation message
|
|
74
|
+
const flaggedSignals = [];
|
|
75
|
+
if (acFlagged) flaggedSignals.push(`${acCount} AC (max ${thresholds.maxAcCount})`);
|
|
76
|
+
if (descFlagged) flaggedSignals.push(`${descriptionLength} chars (max ${thresholds.maxDescriptionLength})`);
|
|
77
|
+
if (bulletsFlagged) flaggedSignals.push(`${bulletPoints} bullets (max ${thresholds.maxBulletPoints})`);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
storyId: story.id,
|
|
81
|
+
signals,
|
|
82
|
+
recommendation: `Story ${story.id} is too large: ${flaggedSignals.join(", ")}. Consider splitting into smaller stories.`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check story size gate for all pending stories
|
|
88
|
+
*/
|
|
89
|
+
export async function checkStorySizeGate(config: NaxConfig, prd: PRD): Promise<StorySizeGateResult> {
|
|
90
|
+
const gateConfig = config.precheck?.storySizeGate ?? {
|
|
91
|
+
enabled: true,
|
|
92
|
+
maxAcCount: 6,
|
|
93
|
+
maxDescriptionLength: 2000,
|
|
94
|
+
maxBulletPoints: 8,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Gate disabled - pass with no flags
|
|
98
|
+
if (!gateConfig.enabled) {
|
|
99
|
+
return {
|
|
100
|
+
check: {
|
|
101
|
+
name: "story-size-gate",
|
|
102
|
+
tier: "warning",
|
|
103
|
+
passed: true,
|
|
104
|
+
message: "Story size gate disabled",
|
|
105
|
+
},
|
|
106
|
+
flaggedStories: [],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const pendingStories = prd.userStories.filter((s) => s.status === "pending");
|
|
111
|
+
const flaggedStories: FlaggedStory[] = [];
|
|
112
|
+
|
|
113
|
+
for (const story of pendingStories) {
|
|
114
|
+
const flagged = analyzeStory(story, config);
|
|
115
|
+
if (flagged) {
|
|
116
|
+
flaggedStories.push(flagged);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// If stories are flagged, return warning (Tier 2, non-blocking)
|
|
121
|
+
if (flaggedStories.length > 0) {
|
|
122
|
+
const storyIds = flaggedStories.map((f) => f.storyId).join(", ");
|
|
123
|
+
return {
|
|
124
|
+
check: {
|
|
125
|
+
name: "story-size-gate",
|
|
126
|
+
tier: "warning",
|
|
127
|
+
passed: false,
|
|
128
|
+
message: `${flaggedStories.length} large stories detected: ${storyIds}`,
|
|
129
|
+
},
|
|
130
|
+
flaggedStories,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// All stories pass size gate
|
|
135
|
+
return {
|
|
136
|
+
check: {
|
|
137
|
+
name: "story-size-gate",
|
|
138
|
+
tier: "warning",
|
|
139
|
+
passed: true,
|
|
140
|
+
message: `All ${pendingStories.length} pending stories within size limits`,
|
|
141
|
+
},
|
|
142
|
+
flaggedStories: [],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Precheck type definitions
|
|
3
|
+
*
|
|
4
|
+
* Types for the precheck system including results, checks, statuses, and tiers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Check tier classification */
|
|
8
|
+
export type CheckTier = "blocker" | "warning";
|
|
9
|
+
|
|
10
|
+
/** Check execution status */
|
|
11
|
+
export type CheckStatus = "passed" | "failed" | "skipped";
|
|
12
|
+
|
|
13
|
+
/** Individual check result */
|
|
14
|
+
export interface Check {
|
|
15
|
+
/** Check identifier (kebab-case) */
|
|
16
|
+
name: string;
|
|
17
|
+
/** Severity tier */
|
|
18
|
+
tier: CheckTier;
|
|
19
|
+
/** Whether the check passed */
|
|
20
|
+
passed: boolean;
|
|
21
|
+
/** Human-readable message explaining the result */
|
|
22
|
+
message: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Complete precheck result */
|
|
26
|
+
export interface PrecheckResult {
|
|
27
|
+
/** Failed Tier 1 checks (execution blockers) */
|
|
28
|
+
blockers: Check[];
|
|
29
|
+
/** Failed Tier 2 checks (non-blocking warnings) */
|
|
30
|
+
warnings: Check[];
|
|
31
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages priority queue for user stories.
|
|
5
|
+
* Supports dependency-based ordering and multi-agent execution.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { QueueCommand, QueueFileResult, QueueItem, QueueItemStatus, QueueStats } from "./types";
|
|
9
|
+
|
|
10
|
+
export class QueueManager {
|
|
11
|
+
private items: QueueItem[] = [];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Add a story to the queue.
|
|
15
|
+
* Priority is used for ordering (higher = more urgent).
|
|
16
|
+
*/
|
|
17
|
+
enqueue(storyId: string, title: string, priority: number): void {
|
|
18
|
+
const existing = this.items.find((item) => item.storyId === storyId);
|
|
19
|
+
if (existing) {
|
|
20
|
+
throw new Error(`Story ${storyId} already in queue`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const item: QueueItem = {
|
|
24
|
+
storyId,
|
|
25
|
+
title,
|
|
26
|
+
priority,
|
|
27
|
+
status: "pending",
|
|
28
|
+
attempts: 0,
|
|
29
|
+
addedAt: new Date(),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
this.items.push(item);
|
|
33
|
+
// Sort by priority (descending) — higher priority first
|
|
34
|
+
this.items.sort((a, b) => b.priority - a.priority);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the next pending item (highest priority).
|
|
39
|
+
* Returns null if no pending items.
|
|
40
|
+
*/
|
|
41
|
+
dequeue(): QueueItem | null {
|
|
42
|
+
const pending = this.items.find((item) => item.status === "pending");
|
|
43
|
+
return pending ?? null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Peek at the next pending item without removing it.
|
|
48
|
+
*/
|
|
49
|
+
peek(): QueueItem | null {
|
|
50
|
+
return this.dequeue();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Mark a story as in-progress and assign it to an agent.
|
|
55
|
+
*/
|
|
56
|
+
markInProgress(storyId: string, assignedAgent: string): void {
|
|
57
|
+
const item = this.items.find((i) => i.storyId === storyId);
|
|
58
|
+
if (!item) {
|
|
59
|
+
throw new Error(`Story ${storyId} not found in queue`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
item.status = "in-progress";
|
|
63
|
+
item.assignedAgent = assignedAgent;
|
|
64
|
+
item.startedAt = new Date();
|
|
65
|
+
item.attempts += 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Mark a story as completed.
|
|
70
|
+
*/
|
|
71
|
+
markComplete(storyId: string): void {
|
|
72
|
+
const item = this.items.find((i) => i.storyId === storyId);
|
|
73
|
+
if (!item) {
|
|
74
|
+
throw new Error(`Story ${storyId} not found in queue`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
item.status = "completed";
|
|
78
|
+
item.completedAt = new Date();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Mark a story as failed with an error message.
|
|
83
|
+
*/
|
|
84
|
+
markFailed(storyId: string, error: string): void {
|
|
85
|
+
const item = this.items.find((i) => i.storyId === storyId);
|
|
86
|
+
if (!item) {
|
|
87
|
+
throw new Error(`Story ${storyId} not found in queue`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
item.status = "failed";
|
|
91
|
+
item.error = error;
|
|
92
|
+
item.completedAt = new Date();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Reset a story back to pending (e.g., for retry).
|
|
97
|
+
*/
|
|
98
|
+
resetToPending(storyId: string): void {
|
|
99
|
+
const item = this.items.find((i) => i.storyId === storyId);
|
|
100
|
+
if (!item) {
|
|
101
|
+
throw new Error(`Story ${storyId} not found in queue`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
item.status = "pending";
|
|
105
|
+
item.assignedAgent = undefined;
|
|
106
|
+
item.startedAt = undefined;
|
|
107
|
+
item.completedAt = undefined;
|
|
108
|
+
item.error = undefined;
|
|
109
|
+
// Re-sort after status change
|
|
110
|
+
this.items.sort((a, b) => b.priority - a.priority);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get all items with a specific status.
|
|
115
|
+
*/
|
|
116
|
+
getByStatus(status: QueueItemStatus): QueueItem[] {
|
|
117
|
+
return this.items.filter((item) => item.status === status);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get a specific item by story ID.
|
|
122
|
+
*/
|
|
123
|
+
getItem(storyId: string): QueueItem | null {
|
|
124
|
+
return this.items.find((i) => i.storyId === storyId) ?? null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get all items in the queue.
|
|
129
|
+
*/
|
|
130
|
+
getAllItems(): QueueItem[] {
|
|
131
|
+
return [...this.items];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get queue statistics.
|
|
136
|
+
*/
|
|
137
|
+
getStats(): QueueStats {
|
|
138
|
+
return {
|
|
139
|
+
total: this.items.length,
|
|
140
|
+
pending: this.items.filter((i) => i.status === "pending").length,
|
|
141
|
+
inProgress: this.items.filter((i) => i.status === "in-progress").length,
|
|
142
|
+
completed: this.items.filter((i) => i.status === "completed").length,
|
|
143
|
+
failed: this.items.filter((i) => i.status === "failed").length,
|
|
144
|
+
skipped: this.items.filter((i) => i.status === "skipped").length,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Remove a story from the queue entirely.
|
|
150
|
+
*/
|
|
151
|
+
remove(storyId: string): void {
|
|
152
|
+
const index = this.items.findIndex((i) => i.storyId === storyId);
|
|
153
|
+
if (index === -1) {
|
|
154
|
+
throw new Error(`Story ${storyId} not found in queue`);
|
|
155
|
+
}
|
|
156
|
+
this.items.splice(index, 1);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Clear all items from the queue.
|
|
161
|
+
*/
|
|
162
|
+
clear(): void {
|
|
163
|
+
this.items = [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if the queue is empty.
|
|
168
|
+
*/
|
|
169
|
+
isEmpty(): boolean {
|
|
170
|
+
return this.items.length === 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Check if there are any pending items.
|
|
175
|
+
*/
|
|
176
|
+
hasPending(): boolean {
|
|
177
|
+
return this.items.some((i) => i.status === "pending");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Mark a story as skipped.
|
|
182
|
+
*/
|
|
183
|
+
markSkipped(storyId: string): void {
|
|
184
|
+
const item = this.items.find((i) => i.storyId === storyId);
|
|
185
|
+
if (!item) {
|
|
186
|
+
throw new Error(`Story ${storyId} not found in queue`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
item.status = "skipped";
|
|
190
|
+
item.error = "Skipped by user command";
|
|
191
|
+
item.completedAt = new Date();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Parse queue file content into commands and guidance.
|
|
197
|
+
*
|
|
198
|
+
* Commands:
|
|
199
|
+
* - PAUSE: Pause execution after current story
|
|
200
|
+
* - ABORT: Mark all remaining stories as skipped and stop
|
|
201
|
+
* - SKIP US-XXX: Skip a specific story
|
|
202
|
+
*
|
|
203
|
+
* Everything else after "--- PENDING ---" is treated as guidance text.
|
|
204
|
+
*/
|
|
205
|
+
export function parseQueueFile(content: string): QueueFileResult {
|
|
206
|
+
const commands: QueueCommand[] = [];
|
|
207
|
+
const guidance: string[] = [];
|
|
208
|
+
|
|
209
|
+
const lines = content.split("\n");
|
|
210
|
+
let inPendingSection = false;
|
|
211
|
+
|
|
212
|
+
for (const line of lines) {
|
|
213
|
+
const trimmed = line.trim();
|
|
214
|
+
|
|
215
|
+
// Skip empty lines
|
|
216
|
+
if (!trimmed) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Check for pending section marker
|
|
221
|
+
if (trimmed === "--- PENDING ---") {
|
|
222
|
+
inPendingSection = true;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Parse commands (case-insensitive)
|
|
227
|
+
const upper = trimmed.toUpperCase();
|
|
228
|
+
|
|
229
|
+
if (upper === "PAUSE") {
|
|
230
|
+
commands.push({ type: "PAUSE" });
|
|
231
|
+
} else if (upper === "ABORT") {
|
|
232
|
+
commands.push({ type: "ABORT" });
|
|
233
|
+
} else if (upper.startsWith("SKIP ")) {
|
|
234
|
+
// Extract story ID after "SKIP"
|
|
235
|
+
const storyId = trimmed.substring(5).trim();
|
|
236
|
+
if (storyId) {
|
|
237
|
+
commands.push({ type: "SKIP", storyId });
|
|
238
|
+
} else {
|
|
239
|
+
// No story ID, treat as guidance
|
|
240
|
+
guidance.push(trimmed);
|
|
241
|
+
}
|
|
242
|
+
} else if (upper === "SKIP") {
|
|
243
|
+
// SKIP with no story ID, treat as guidance
|
|
244
|
+
guidance.push(trimmed);
|
|
245
|
+
} else {
|
|
246
|
+
// Not a command, treat as guidance if in pending section
|
|
247
|
+
if (inPendingSection) {
|
|
248
|
+
guidance.push(trimmed);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return { commands, guidance };
|
|
254
|
+
}
|