@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,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugins Command
|
|
3
|
+
*
|
|
4
|
+
* Lists loaded plugins with their metadata.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as os from "node:os";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import type { NaxConfig } from "../config/schema";
|
|
10
|
+
import { loadPlugins } from "../plugins/loader";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* List all loaded plugins with their metadata.
|
|
14
|
+
*
|
|
15
|
+
* @param config - nax configuration
|
|
16
|
+
* @param workdir - Working directory for resolving plugin paths
|
|
17
|
+
* @param overrideGlobalPluginsDir - Override global plugins directory (used in tests)
|
|
18
|
+
*/
|
|
19
|
+
export async function pluginsListCommand(
|
|
20
|
+
config: NaxConfig,
|
|
21
|
+
workdir: string,
|
|
22
|
+
overrideGlobalPluginsDir?: string,
|
|
23
|
+
): Promise<void> {
|
|
24
|
+
// Load plugins from all sources
|
|
25
|
+
const globalPluginsDir = overrideGlobalPluginsDir ?? path.join(os.homedir(), ".nax", "plugins");
|
|
26
|
+
const projectPluginsDir = path.join(workdir, "nax", "plugins");
|
|
27
|
+
const configPlugins = config.plugins || [];
|
|
28
|
+
const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, workdir);
|
|
29
|
+
const plugins = registry.plugins;
|
|
30
|
+
|
|
31
|
+
if (plugins.length === 0) {
|
|
32
|
+
console.log("No plugins installed.");
|
|
33
|
+
console.log("\nTo install plugins:");
|
|
34
|
+
console.log(" • Add to global directory: ~/.nax/plugins/");
|
|
35
|
+
console.log(" • Add to project directory: ./nax/plugins/");
|
|
36
|
+
console.log(" • Configure in nax/config.json");
|
|
37
|
+
console.log("\nSee https://github.com/nax/nax#plugins for more details.");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Build table data
|
|
42
|
+
const rows: Array<{
|
|
43
|
+
name: string;
|
|
44
|
+
version: string;
|
|
45
|
+
provides: string;
|
|
46
|
+
source: string;
|
|
47
|
+
}> = plugins.map((plugin) => {
|
|
48
|
+
const source = registry.getSource(plugin.name);
|
|
49
|
+
const sourceStr = source ? formatSource(source.type, source.path) : "unknown";
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
name: plugin.name,
|
|
53
|
+
version: plugin.version,
|
|
54
|
+
provides: plugin.provides.join(", "),
|
|
55
|
+
source: sourceStr,
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Calculate column widths
|
|
60
|
+
const widths = {
|
|
61
|
+
name: Math.max(4, ...rows.map((r) => r.name.length)),
|
|
62
|
+
version: Math.max(7, ...rows.map((r) => r.version.length)),
|
|
63
|
+
provides: Math.max(8, ...rows.map((r) => r.provides.length)),
|
|
64
|
+
source: Math.max(6, ...rows.map((r) => r.source.length)),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Display table
|
|
68
|
+
console.log("\nInstalled Plugins:\n");
|
|
69
|
+
console.log(
|
|
70
|
+
`${pad("Name", widths.name)} ${pad("Version", widths.version)} ${pad("Provides", widths.provides)} ${pad("Source", widths.source)}`,
|
|
71
|
+
);
|
|
72
|
+
console.log(
|
|
73
|
+
`${"-".repeat(widths.name)} ${"-".repeat(widths.version)} ${"-".repeat(widths.provides)} ${"-".repeat(widths.source)}`,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
for (const row of rows) {
|
|
77
|
+
console.log(
|
|
78
|
+
`${pad(row.name, widths.name)} ${pad(row.version, widths.version)} ${pad(row.provides, widths.provides)} ${pad(row.source, widths.source)}`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Format source type and path for display.
|
|
87
|
+
*
|
|
88
|
+
* @param type - Source type (global/project/config)
|
|
89
|
+
* @param sourcePath - Full path to plugin
|
|
90
|
+
* @returns Formatted source string
|
|
91
|
+
*/
|
|
92
|
+
function formatSource(type: "global" | "project" | "config", sourcePath: string): string {
|
|
93
|
+
if (type === "global") {
|
|
94
|
+
return `global (${path.basename(sourcePath)})`;
|
|
95
|
+
}
|
|
96
|
+
if (type === "project") {
|
|
97
|
+
return `project (${path.basename(sourcePath)})`;
|
|
98
|
+
}
|
|
99
|
+
return `config (${sourcePath})`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Pad string to width.
|
|
104
|
+
*
|
|
105
|
+
* @param str - String to pad
|
|
106
|
+
* @param width - Target width
|
|
107
|
+
* @returns Padded string
|
|
108
|
+
*/
|
|
109
|
+
function pad(str: string, width: number): string {
|
|
110
|
+
return str.padEnd(width);
|
|
111
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts CLI Command
|
|
3
|
+
*
|
|
4
|
+
* Assembles prompts for all stories in a feature without executing agents.
|
|
5
|
+
* Used for debugging prompt isolation and context leakage.
|
|
6
|
+
*
|
|
7
|
+
* Executes: routing → constitution → context → prompt stages only.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import type { NaxConfig } from "../config";
|
|
13
|
+
import type { BuiltContext } from "../context/types";
|
|
14
|
+
import { getLogger } from "../logger";
|
|
15
|
+
import { runPipeline } from "../pipeline";
|
|
16
|
+
import type { PipelineContext } from "../pipeline";
|
|
17
|
+
import { constitutionStage, contextStage, promptStage, routingStage } from "../pipeline/stages";
|
|
18
|
+
import type { UserStory } from "../prd";
|
|
19
|
+
import { loadPRD } from "../prd";
|
|
20
|
+
|
|
21
|
+
export interface PromptsCommandOptions {
|
|
22
|
+
/** Feature name */
|
|
23
|
+
feature: string;
|
|
24
|
+
/** Working directory (project root) */
|
|
25
|
+
workdir: string;
|
|
26
|
+
/** Ngent configuration */
|
|
27
|
+
config: NaxConfig;
|
|
28
|
+
/** Optional: filter to single story ID */
|
|
29
|
+
storyId?: string;
|
|
30
|
+
/** Optional: output directory (stdout if not provided) */
|
|
31
|
+
outputDir?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Execute the `nax prompts` command.
|
|
36
|
+
*
|
|
37
|
+
* Runs the pipeline through routing → constitution → context → prompt stages
|
|
38
|
+
* for each story, then outputs the assembled prompts to stdout or files.
|
|
39
|
+
*
|
|
40
|
+
* @param options - Command options
|
|
41
|
+
* @returns Array of story IDs that were processed
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```bash
|
|
45
|
+
* # Dump all story prompts to stdout
|
|
46
|
+
* nax prompts -f core
|
|
47
|
+
*
|
|
48
|
+
* # Write to directory
|
|
49
|
+
* nax prompts -f core --out ./prompt-dump/
|
|
50
|
+
*
|
|
51
|
+
* # Single story
|
|
52
|
+
* nax prompts -f core --story US-003
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export async function promptsCommand(options: PromptsCommandOptions): Promise<string[]> {
|
|
56
|
+
const logger = getLogger();
|
|
57
|
+
const { feature, workdir, config, storyId, outputDir } = options;
|
|
58
|
+
|
|
59
|
+
// Find nax directory
|
|
60
|
+
const naxDir = join(workdir, "nax");
|
|
61
|
+
if (!existsSync(naxDir)) {
|
|
62
|
+
throw new Error(`nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Load PRD
|
|
66
|
+
const featureDir = join(naxDir, "features", feature);
|
|
67
|
+
const prdPath = join(featureDir, "prd.json");
|
|
68
|
+
|
|
69
|
+
if (!existsSync(prdPath)) {
|
|
70
|
+
throw new Error(`Feature "${feature}" not found or missing prd.json`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const prd = await loadPRD(prdPath);
|
|
74
|
+
|
|
75
|
+
// Filter stories
|
|
76
|
+
const stories = storyId ? prd.userStories.filter((s) => s.id === storyId) : prd.userStories;
|
|
77
|
+
|
|
78
|
+
if (stories.length === 0) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
storyId ? `Story "${storyId}" not found in feature "${feature}"` : `No stories found in feature "${feature}"`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Create output directory if specified
|
|
85
|
+
if (outputDir) {
|
|
86
|
+
mkdirSync(outputDir, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
logger.info("cli", "Assembling prompts", {
|
|
90
|
+
feature,
|
|
91
|
+
storyCount: stories.length,
|
|
92
|
+
outputMode: outputDir ? "files" : "stdout",
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Process each story through the pipeline (routing → constitution → context → prompt)
|
|
96
|
+
const processedStories: string[] = [];
|
|
97
|
+
const promptPipeline = [routingStage, constitutionStage, contextStage, promptStage];
|
|
98
|
+
|
|
99
|
+
for (const story of stories) {
|
|
100
|
+
// Build initial pipeline context
|
|
101
|
+
const ctx: PipelineContext = {
|
|
102
|
+
config,
|
|
103
|
+
prd,
|
|
104
|
+
story,
|
|
105
|
+
stories: [story], // Single story, not batch
|
|
106
|
+
routing: {
|
|
107
|
+
complexity: "simple",
|
|
108
|
+
modelTier: "fast",
|
|
109
|
+
testStrategy: "test-after",
|
|
110
|
+
reasoning: "Placeholder routing",
|
|
111
|
+
}, // Will be set by routingStage
|
|
112
|
+
workdir,
|
|
113
|
+
featureDir,
|
|
114
|
+
hooks: { hooks: {} }, // Empty hooks config
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Run the prompt assembly pipeline
|
|
118
|
+
const result = await runPipeline(promptPipeline, ctx);
|
|
119
|
+
|
|
120
|
+
if (!result.success) {
|
|
121
|
+
logger.warn("cli", "Failed to assemble prompt for story", {
|
|
122
|
+
storyId: story.id,
|
|
123
|
+
reason: result.reason,
|
|
124
|
+
});
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Handle three-session TDD stories separately
|
|
129
|
+
if (ctx.routing.testStrategy === "three-session-tdd") {
|
|
130
|
+
await handleThreeSessionTddPrompts(story, ctx, outputDir, logger);
|
|
131
|
+
processedStories.push(story.id);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// For non-TDD stories, ensure prompt was built
|
|
136
|
+
if (!ctx.prompt) {
|
|
137
|
+
logger.warn("cli", "No prompt generated for story", {
|
|
138
|
+
storyId: story.id,
|
|
139
|
+
});
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Build YAML frontmatter
|
|
144
|
+
const frontmatter = buildFrontmatter(story, ctx);
|
|
145
|
+
|
|
146
|
+
// Full output: frontmatter + prompt
|
|
147
|
+
const fullOutput = `---\n${frontmatter}---\n\n${ctx.prompt}`;
|
|
148
|
+
|
|
149
|
+
// Write to file or stdout
|
|
150
|
+
if (outputDir) {
|
|
151
|
+
const promptFile = join(outputDir, `${story.id}.prompt.md`);
|
|
152
|
+
await Bun.write(promptFile, fullOutput);
|
|
153
|
+
|
|
154
|
+
// Also write context-only file for isolation audit
|
|
155
|
+
if (ctx.contextMarkdown) {
|
|
156
|
+
const contextFile = join(outputDir, `${story.id}.context.md`);
|
|
157
|
+
const contextOutput = `---\n${frontmatter}---\n\n${ctx.contextMarkdown}`;
|
|
158
|
+
await Bun.write(contextFile, contextOutput);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
logger.info("cli", "Written prompt files", {
|
|
162
|
+
storyId: story.id,
|
|
163
|
+
promptFile,
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
// Stdout mode: print separator + story ID + prompt
|
|
167
|
+
console.log(`\n${"=".repeat(80)}`);
|
|
168
|
+
console.log(`Story: ${story.id} — ${story.title}`);
|
|
169
|
+
console.log("=".repeat(80));
|
|
170
|
+
console.log(fullOutput);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
processedStories.push(story.id);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
logger.info("cli", "Prompt assembly complete", {
|
|
177
|
+
processedCount: processedStories.length,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return processedStories;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Build YAML frontmatter for a story prompt.
|
|
185
|
+
*
|
|
186
|
+
* Uses actual token counts from BuiltContext elements (computed by context builder
|
|
187
|
+
* using CHARS_PER_TOKEN=3) rather than re-estimating independently.
|
|
188
|
+
*
|
|
189
|
+
* @param story - User story
|
|
190
|
+
* @param ctx - Pipeline context after running prompt assembly
|
|
191
|
+
* @param role - Optional role for three-session TDD (test-writer, implementer, verifier)
|
|
192
|
+
* @returns YAML frontmatter string (without delimiters)
|
|
193
|
+
*/
|
|
194
|
+
function buildFrontmatter(story: UserStory, ctx: PipelineContext, role?: string): string {
|
|
195
|
+
const lines: string[] = [];
|
|
196
|
+
|
|
197
|
+
lines.push(`storyId: ${story.id}`);
|
|
198
|
+
lines.push(`title: "${story.title}"`);
|
|
199
|
+
lines.push(`testStrategy: ${ctx.routing.testStrategy}`);
|
|
200
|
+
lines.push(`modelTier: ${ctx.routing.modelTier}`);
|
|
201
|
+
|
|
202
|
+
if (role) {
|
|
203
|
+
lines.push(`role: ${role}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Use actual token counts from BuiltContext if available
|
|
207
|
+
const builtContext = ctx.builtContext;
|
|
208
|
+
const contextTokens = builtContext?.totalTokens ?? 0;
|
|
209
|
+
const promptTokens = ctx.prompt ? Math.ceil(ctx.prompt.length / 3) : 0;
|
|
210
|
+
|
|
211
|
+
lines.push(`contextTokens: ${contextTokens}`);
|
|
212
|
+
lines.push(`promptTokens: ${promptTokens}`);
|
|
213
|
+
|
|
214
|
+
// Dependencies
|
|
215
|
+
if (story.dependencies && story.dependencies.length > 0) {
|
|
216
|
+
lines.push(`dependencies: [${story.dependencies.join(", ")}]`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Context elements breakdown from actual BuiltContext
|
|
220
|
+
lines.push("contextElements:");
|
|
221
|
+
|
|
222
|
+
if (builtContext) {
|
|
223
|
+
for (const element of builtContext.elements) {
|
|
224
|
+
lines.push(` - type: ${element.type}`);
|
|
225
|
+
if (element.storyId) {
|
|
226
|
+
lines.push(` storyId: ${element.storyId}`);
|
|
227
|
+
}
|
|
228
|
+
if (element.filePath) {
|
|
229
|
+
lines.push(` filePath: ${element.filePath}`);
|
|
230
|
+
}
|
|
231
|
+
lines.push(` tokens: ${element.tokens}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (builtContext?.truncated) {
|
|
236
|
+
lines.push("truncated: true");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return `${lines.join("\n")}\n`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Handle three-session TDD prompts by building separate prompts for each role.
|
|
244
|
+
*
|
|
245
|
+
* @param story - User story
|
|
246
|
+
* @param ctx - Pipeline context
|
|
247
|
+
* @param outputDir - Output directory (undefined for stdout)
|
|
248
|
+
* @param logger - Logger instance
|
|
249
|
+
*/
|
|
250
|
+
async function handleThreeSessionTddPrompts(
|
|
251
|
+
story: UserStory,
|
|
252
|
+
ctx: PipelineContext,
|
|
253
|
+
outputDir: string | undefined,
|
|
254
|
+
logger: ReturnType<typeof getLogger>,
|
|
255
|
+
): Promise<void> {
|
|
256
|
+
// Import TDD prompt builders
|
|
257
|
+
const { buildTestWriterPrompt, buildImplementerPrompt, buildVerifierPrompt } = await import("../tdd/prompts");
|
|
258
|
+
|
|
259
|
+
// Build prompts for each session
|
|
260
|
+
const sessions = [
|
|
261
|
+
{ role: "test-writer", prompt: buildTestWriterPrompt(story, ctx.contextMarkdown) },
|
|
262
|
+
{ role: "implementer", prompt: buildImplementerPrompt(story, ctx.contextMarkdown) },
|
|
263
|
+
{ role: "verifier", prompt: buildVerifierPrompt(story) },
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
for (const session of sessions) {
|
|
267
|
+
const frontmatter = buildFrontmatter(story, ctx, session.role);
|
|
268
|
+
const fullOutput = `---\n${frontmatter}---\n\n${session.prompt}`;
|
|
269
|
+
|
|
270
|
+
if (outputDir) {
|
|
271
|
+
const promptFile = join(outputDir, `${story.id}.${session.role}.md`);
|
|
272
|
+
await Bun.write(promptFile, fullOutput);
|
|
273
|
+
|
|
274
|
+
logger.info("cli", "Written TDD prompt file", {
|
|
275
|
+
storyId: story.id,
|
|
276
|
+
role: session.role,
|
|
277
|
+
promptFile,
|
|
278
|
+
});
|
|
279
|
+
} else {
|
|
280
|
+
// Stdout mode: print separator + story ID + role + prompt
|
|
281
|
+
console.log(`\n${"=".repeat(80)}`);
|
|
282
|
+
console.log(`Story: ${story.id} — ${story.title} [${session.role}]`);
|
|
283
|
+
console.log("=".repeat(80));
|
|
284
|
+
console.log(fullOutput);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Also write context-only file for isolation audit
|
|
289
|
+
if (outputDir && ctx.contextMarkdown) {
|
|
290
|
+
const contextFile = join(outputDir, `${story.id}.context.md`);
|
|
291
|
+
const frontmatter = buildFrontmatter(story, ctx);
|
|
292
|
+
const contextOutput = `---\n${frontmatter}---\n\n${ctx.contextMarkdown}`;
|
|
293
|
+
await Bun.write(contextFile, contextOutput);
|
|
294
|
+
}
|
|
295
|
+
}
|
package/src/cli/runs.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs CLI Commands
|
|
3
|
+
*
|
|
4
|
+
* Display run history from JSONL log files.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { NaxError } from "../errors";
|
|
10
|
+
import { getLogger } from "../logger";
|
|
11
|
+
import type { LogEntry } from "../logger/types";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Options for runs list command.
|
|
15
|
+
*/
|
|
16
|
+
export interface RunsListOptions {
|
|
17
|
+
/** Feature name */
|
|
18
|
+
feature: string;
|
|
19
|
+
/** Project directory */
|
|
20
|
+
workdir: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Options for runs show command.
|
|
25
|
+
*/
|
|
26
|
+
export interface RunsShowOptions {
|
|
27
|
+
/** Run ID to display */
|
|
28
|
+
runId: string;
|
|
29
|
+
/** Feature name */
|
|
30
|
+
feature: string;
|
|
31
|
+
/** Project directory */
|
|
32
|
+
workdir: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parse JSONL log file and extract run events.
|
|
37
|
+
*
|
|
38
|
+
* @param logPath - Path to .jsonl log file
|
|
39
|
+
* @returns Array of log entries
|
|
40
|
+
*/
|
|
41
|
+
async function parseRunLog(logPath: string): Promise<LogEntry[]> {
|
|
42
|
+
const logger = getLogger();
|
|
43
|
+
try {
|
|
44
|
+
const content = await Bun.file(logPath).text();
|
|
45
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
46
|
+
return lines.map((line) => JSON.parse(line) as LogEntry);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
logger.warn("cli", "Failed to parse run log", { logPath, error: (err as Error).message });
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* List all runs for a feature.
|
|
55
|
+
*
|
|
56
|
+
* @param options - Command options
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```bash
|
|
60
|
+
* nax runs list -f auth-system
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export async function runsListCommand(options: RunsListOptions): Promise<void> {
|
|
64
|
+
const logger = getLogger();
|
|
65
|
+
const { feature, workdir } = options;
|
|
66
|
+
|
|
67
|
+
const runsDir = join(workdir, "nax", "features", feature, "runs");
|
|
68
|
+
|
|
69
|
+
if (!existsSync(runsDir)) {
|
|
70
|
+
logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// List all .jsonl files in runs directory
|
|
75
|
+
const files = readdirSync(runsDir).filter((f) => f.endsWith(".jsonl"));
|
|
76
|
+
|
|
77
|
+
if (files.length === 0) {
|
|
78
|
+
logger.info("cli", "No runs found for feature", { feature });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
logger.info("cli", `Runs for ${feature}`, { count: files.length });
|
|
83
|
+
|
|
84
|
+
// Parse each run file and extract run.start event
|
|
85
|
+
for (const file of files.sort().reverse()) {
|
|
86
|
+
const logPath = join(runsDir, file);
|
|
87
|
+
const entries = await parseRunLog(logPath);
|
|
88
|
+
|
|
89
|
+
// Find run.start and run.complete events
|
|
90
|
+
const startEvent = entries.find((e) => e.message === "run.start");
|
|
91
|
+
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
92
|
+
|
|
93
|
+
if (!startEvent) {
|
|
94
|
+
logger.warn("cli", "Run log missing run.start event", { file });
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const runId = startEvent.data?.runId || file.replace(".jsonl", "");
|
|
99
|
+
const startedAt = startEvent.timestamp;
|
|
100
|
+
const status = completeEvent ? "completed" : "in-progress";
|
|
101
|
+
const totalCost = completeEvent?.data?.totalCost || 0;
|
|
102
|
+
const storiesCompleted = completeEvent?.data?.storiesCompleted || 0;
|
|
103
|
+
const totalStories = completeEvent?.data?.totalStories || 0;
|
|
104
|
+
|
|
105
|
+
logger.info("cli", ` ${runId}`, {
|
|
106
|
+
status,
|
|
107
|
+
startedAt,
|
|
108
|
+
totalCost,
|
|
109
|
+
storiesCompleted,
|
|
110
|
+
totalStories,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Show detailed information for a specific run.
|
|
117
|
+
*
|
|
118
|
+
* @param options - Command options
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```bash
|
|
122
|
+
* nax runs show run-20260220-103045 -f auth-system
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export async function runsShowCommand(options: RunsShowOptions): Promise<void> {
|
|
126
|
+
const logger = getLogger();
|
|
127
|
+
const { runId, feature, workdir } = options;
|
|
128
|
+
|
|
129
|
+
const logPath = join(workdir, "nax", "features", feature, "runs", `${runId}.jsonl`);
|
|
130
|
+
|
|
131
|
+
if (!existsSync(logPath)) {
|
|
132
|
+
logger.error("cli", "Run not found", { runId, feature, logPath });
|
|
133
|
+
throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const entries = await parseRunLog(logPath);
|
|
137
|
+
|
|
138
|
+
// Find key events
|
|
139
|
+
const startEvent = entries.find((e) => e.message === "run.start");
|
|
140
|
+
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
141
|
+
const storyEvents = entries.filter((e) => e.stage === "execution" && e.data?.storyId);
|
|
142
|
+
|
|
143
|
+
if (!startEvent) {
|
|
144
|
+
logger.error("cli", "Run log missing run.start event", { runId });
|
|
145
|
+
throw new NaxError("Run log missing run.start event", "INVALID_RUN_LOG", { runId });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Display run summary
|
|
149
|
+
logger.info("cli", `Run: ${runId}`, {
|
|
150
|
+
feature,
|
|
151
|
+
startedAt: startEvent.timestamp,
|
|
152
|
+
completedAt: completeEvent?.timestamp,
|
|
153
|
+
status: completeEvent ? "completed" : "in-progress",
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (completeEvent) {
|
|
157
|
+
logger.info("cli", "Run Summary", {
|
|
158
|
+
totalStories: completeEvent.data?.totalStories,
|
|
159
|
+
storiesCompleted: completeEvent.data?.storiesCompleted,
|
|
160
|
+
storiesFailed: completeEvent.data?.storiesFailed,
|
|
161
|
+
totalCost: completeEvent.data?.totalCost,
|
|
162
|
+
totalDurationMs: completeEvent.data?.totalDurationMs,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Display per-story events
|
|
167
|
+
logger.info("cli", "Story Events", { count: storyEvents.length });
|
|
168
|
+
for (const event of storyEvents) {
|
|
169
|
+
logger.info("cli", ` ${event.data?.storyId}: ${event.message}`, {
|
|
170
|
+
timestamp: event.timestamp,
|
|
171
|
+
data: event.data,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|