@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,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Tests for Analyze Command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
6
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { analyzeFeature } from "../../src/cli/analyze";
|
|
9
|
+
import { DEFAULT_CONFIG } from "../../src/config";
|
|
10
|
+
|
|
11
|
+
describe("analyzeFeature integration", () => {
|
|
12
|
+
const testDir = "/tmp/nax-analyze-test";
|
|
13
|
+
const featureDir = join(testDir, "nax/features/test-feature");
|
|
14
|
+
|
|
15
|
+
beforeAll(() => {
|
|
16
|
+
// Create test directory structure
|
|
17
|
+
if (existsSync(testDir)) {
|
|
18
|
+
rmSync(testDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
mkdirSync(featureDir, { recursive: true });
|
|
21
|
+
|
|
22
|
+
// Create test spec.md
|
|
23
|
+
const specContent = `# Test Feature
|
|
24
|
+
|
|
25
|
+
## US-001: Add user authentication
|
|
26
|
+
|
|
27
|
+
### Description
|
|
28
|
+
Implement JWT-based authentication with refresh tokens.
|
|
29
|
+
|
|
30
|
+
### Acceptance Criteria
|
|
31
|
+
- [ ] User can log in with email/password
|
|
32
|
+
- [ ] JWT token is generated and stored
|
|
33
|
+
- [ ] Refresh token logic works
|
|
34
|
+
- [ ] Token expiry is handled
|
|
35
|
+
|
|
36
|
+
Tags: security, auth
|
|
37
|
+
Dependencies: none
|
|
38
|
+
|
|
39
|
+
## US-002: Update homepage UI
|
|
40
|
+
|
|
41
|
+
### Description
|
|
42
|
+
Refresh the homepage design with new color scheme.
|
|
43
|
+
|
|
44
|
+
### Acceptance Criteria
|
|
45
|
+
- [ ] New colors applied
|
|
46
|
+
- [ ] Layout is responsive
|
|
47
|
+
|
|
48
|
+
Tags: ui
|
|
49
|
+
Dependencies: none
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
Bun.write(join(featureDir, "spec.md"), specContent);
|
|
53
|
+
|
|
54
|
+
// Create mock src/ directory
|
|
55
|
+
mkdirSync(join(testDir, "src"), { recursive: true });
|
|
56
|
+
Bun.write(join(testDir, "src/index.ts"), "export {}");
|
|
57
|
+
|
|
58
|
+
// Create mock package.json
|
|
59
|
+
Bun.write(
|
|
60
|
+
join(testDir, "package.json"),
|
|
61
|
+
JSON.stringify({
|
|
62
|
+
name: "test-project",
|
|
63
|
+
dependencies: { zod: "^4.0.0" },
|
|
64
|
+
devDependencies: { typescript: "^5.0.0" },
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
afterAll(() => {
|
|
70
|
+
// Cleanup
|
|
71
|
+
if (existsSync(testDir)) {
|
|
72
|
+
rmSync(testDir, { recursive: true });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("parses spec.md into PRD structure", async () => {
|
|
77
|
+
const config = {
|
|
78
|
+
...DEFAULT_CONFIG,
|
|
79
|
+
analyze: {
|
|
80
|
+
llmEnhanced: false, // Disable LLM for predictable tests
|
|
81
|
+
model: "fast" as const,
|
|
82
|
+
fallbackToKeywords: true,
|
|
83
|
+
maxCodebaseSummaryTokens: 5000,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const prd = await analyzeFeature({
|
|
88
|
+
featureDir,
|
|
89
|
+
featureName: "test-feature",
|
|
90
|
+
branchName: "feat/test-feature",
|
|
91
|
+
config,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(prd.project).toBe("nax");
|
|
95
|
+
expect(prd.feature).toBe("test-feature");
|
|
96
|
+
expect(prd.branchName).toBe("feat/test-feature");
|
|
97
|
+
expect(prd.userStories).toHaveLength(2);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("extracts story metadata correctly", async () => {
|
|
101
|
+
const config = {
|
|
102
|
+
...DEFAULT_CONFIG,
|
|
103
|
+
analyze: {
|
|
104
|
+
llmEnhanced: false,
|
|
105
|
+
model: "fast" as const,
|
|
106
|
+
fallbackToKeywords: true,
|
|
107
|
+
maxCodebaseSummaryTokens: 5000,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const prd = await analyzeFeature({
|
|
112
|
+
featureDir,
|
|
113
|
+
featureName: "test-feature",
|
|
114
|
+
branchName: "feat/test-feature",
|
|
115
|
+
config,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const story1 = prd.userStories[0];
|
|
119
|
+
expect(story1.id).toBe("US-001");
|
|
120
|
+
expect(story1.title).toBe("Add user authentication");
|
|
121
|
+
expect(story1.description).toContain("JWT-based authentication");
|
|
122
|
+
expect(story1.acceptanceCriteria).toHaveLength(4);
|
|
123
|
+
expect(story1.tags).toContain("security");
|
|
124
|
+
expect(story1.tags).toContain("auth");
|
|
125
|
+
expect(story1.status).toBe("pending");
|
|
126
|
+
expect(story1.passes).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("applies routing when LLM disabled (keyword fallback)", async () => {
|
|
130
|
+
const config = {
|
|
131
|
+
...DEFAULT_CONFIG,
|
|
132
|
+
analyze: {
|
|
133
|
+
llmEnhanced: false,
|
|
134
|
+
model: "fast" as const,
|
|
135
|
+
fallbackToKeywords: true,
|
|
136
|
+
maxCodebaseSummaryTokens: 5000,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const prd = await analyzeFeature({
|
|
141
|
+
featureDir,
|
|
142
|
+
featureName: "test-feature",
|
|
143
|
+
branchName: "feat/test-feature",
|
|
144
|
+
config,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Routing should be applied with keyword classification
|
|
148
|
+
expect(prd.userStories[0].routing).toBeDefined();
|
|
149
|
+
expect(prd.userStories[1].routing).toBeDefined();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test.skip("scans codebase when LLM enabled", async () => {
|
|
153
|
+
// Skipped: This test would require a real agent installation and API key
|
|
154
|
+
// The LLM decompose flow is tested in unit tests with mocked agents
|
|
155
|
+
const config = {
|
|
156
|
+
...DEFAULT_CONFIG,
|
|
157
|
+
analyze: {
|
|
158
|
+
llmEnhanced: true,
|
|
159
|
+
model: "fast" as const,
|
|
160
|
+
fallbackToKeywords: true,
|
|
161
|
+
maxCodebaseSummaryTokens: 5000,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// This will trigger LLM decompose (will fall back due to no agent in test)
|
|
166
|
+
const prd = await analyzeFeature({
|
|
167
|
+
featureDir,
|
|
168
|
+
featureName: "test-feature",
|
|
169
|
+
branchName: "feat/test-feature",
|
|
170
|
+
config,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Should have routing metadata from keyword fallback
|
|
174
|
+
expect(prd.userStories[0].routing).toBeDefined();
|
|
175
|
+
expect(prd.userStories[1].routing).toBeDefined();
|
|
176
|
+
|
|
177
|
+
// First story should be complex (security keywords)
|
|
178
|
+
expect(prd.userStories[0].routing?.complexity).toBe("complex");
|
|
179
|
+
|
|
180
|
+
// Second story should be simple
|
|
181
|
+
expect(prd.userStories[1].routing?.complexity).toBe("simple");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("throws error when spec.md missing", async () => {
|
|
185
|
+
const emptyDir = "/tmp/nax-empty-test";
|
|
186
|
+
mkdirSync(emptyDir, { recursive: true });
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
await expect(
|
|
190
|
+
analyzeFeature({
|
|
191
|
+
featureDir: emptyDir,
|
|
192
|
+
featureName: "empty",
|
|
193
|
+
branchName: "feat/empty",
|
|
194
|
+
}),
|
|
195
|
+
).rejects.toThrow("spec.md not found");
|
|
196
|
+
} finally {
|
|
197
|
+
rmSync(emptyDir, { recursive: true });
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("throws error when no stories found", async () => {
|
|
202
|
+
const noStoriesDir = "/tmp/nax-no-stories-test";
|
|
203
|
+
const featurePath = join(noStoriesDir, "nax/features/test");
|
|
204
|
+
mkdirSync(featurePath, { recursive: true });
|
|
205
|
+
|
|
206
|
+
// Create empty spec.md
|
|
207
|
+
await Bun.write(join(featurePath, "spec.md"), "# Empty\n\nNo stories here.");
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
await expect(
|
|
211
|
+
analyzeFeature({
|
|
212
|
+
featureDir: featurePath,
|
|
213
|
+
featureName: "test",
|
|
214
|
+
branchName: "feat/test",
|
|
215
|
+
}),
|
|
216
|
+
).rejects.toThrow("No user stories found");
|
|
217
|
+
} finally {
|
|
218
|
+
rmSync(noStoriesDir, { recursive: true });
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("warns but succeeds when story count exceeds maxStoriesPerFeature limit", async () => {
|
|
223
|
+
const manyStoriesDir = "/tmp/nax-many-stories-test";
|
|
224
|
+
const featurePath = join(manyStoriesDir, "nax/features/test");
|
|
225
|
+
mkdirSync(featurePath, { recursive: true });
|
|
226
|
+
|
|
227
|
+
// Create spec.md with 6 stories (exceeds limit of 5)
|
|
228
|
+
let specContent = "# Many Stories\n\n";
|
|
229
|
+
for (let i = 1; i <= 6; i++) {
|
|
230
|
+
specContent += `## US-${String(i).padStart(3, "0")}: Story ${i}\n\n`;
|
|
231
|
+
specContent += `### Description\nStory ${i}\n\n`;
|
|
232
|
+
specContent += `### Acceptance Criteria\n- [ ] Done\n\n`;
|
|
233
|
+
}
|
|
234
|
+
await Bun.write(join(featurePath, "spec.md"), specContent);
|
|
235
|
+
|
|
236
|
+
const config = {
|
|
237
|
+
...DEFAULT_CONFIG,
|
|
238
|
+
execution: {
|
|
239
|
+
...DEFAULT_CONFIG.execution,
|
|
240
|
+
maxStoriesPerFeature: 5,
|
|
241
|
+
},
|
|
242
|
+
analyze: {
|
|
243
|
+
...DEFAULT_CONFIG.analyze,
|
|
244
|
+
llmEnhanced: false,
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
// Should warn but not throw (changed from hard error to warning)
|
|
250
|
+
const prd = await analyzeFeature({
|
|
251
|
+
featureDir: featurePath,
|
|
252
|
+
featureName: "test",
|
|
253
|
+
branchName: "feat/test",
|
|
254
|
+
config,
|
|
255
|
+
});
|
|
256
|
+
expect(prd.userStories.length).toBe(6);
|
|
257
|
+
} finally {
|
|
258
|
+
rmSync(manyStoriesDir, { recursive: true });
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Codebase Scanner
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { scanCodebase } from "../../src/analyze/scanner";
|
|
9
|
+
|
|
10
|
+
describe("scanCodebase", () => {
|
|
11
|
+
test("scans project codebase successfully", async () => {
|
|
12
|
+
// Use the nax project itself as test data
|
|
13
|
+
const workdir = join(import.meta.dir, "..");
|
|
14
|
+
|
|
15
|
+
const scan = await scanCodebase(workdir);
|
|
16
|
+
|
|
17
|
+
// Should have file tree
|
|
18
|
+
expect(scan.fileTree).toBeTruthy();
|
|
19
|
+
expect(typeof scan.fileTree).toBe("string");
|
|
20
|
+
expect(scan.fileTree.length).toBeGreaterThan(0);
|
|
21
|
+
|
|
22
|
+
// Should have dependencies
|
|
23
|
+
expect(scan.dependencies).toBeDefined();
|
|
24
|
+
expect(typeof scan.dependencies).toBe("object");
|
|
25
|
+
|
|
26
|
+
// Should have dev dependencies
|
|
27
|
+
expect(scan.devDependencies).toBeDefined();
|
|
28
|
+
expect(typeof scan.devDependencies).toBe("object");
|
|
29
|
+
|
|
30
|
+
// Should detect test patterns
|
|
31
|
+
expect(scan.testPatterns).toBeDefined();
|
|
32
|
+
expect(Array.isArray(scan.testPatterns)).toBe(true);
|
|
33
|
+
expect(scan.testPatterns.length).toBeGreaterThan(0);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("handles missing src directory", async () => {
|
|
37
|
+
const workdir = "/tmp/nax-test-no-src";
|
|
38
|
+
|
|
39
|
+
// Create temp dir without src/
|
|
40
|
+
await Bun.write(join(workdir, "package.json"), JSON.stringify({}));
|
|
41
|
+
|
|
42
|
+
const scan = await scanCodebase(workdir);
|
|
43
|
+
|
|
44
|
+
// Should return placeholder for missing src
|
|
45
|
+
expect(scan.fileTree).toBe("No src/ directory");
|
|
46
|
+
expect(scan.dependencies).toEqual({});
|
|
47
|
+
expect(scan.devDependencies).toEqual({});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test(
|
|
51
|
+
"extracts dependencies from package.json",
|
|
52
|
+
async () => {
|
|
53
|
+
const workdir = join(import.meta.dir, "../..");
|
|
54
|
+
|
|
55
|
+
const scan = await scanCodebase(workdir);
|
|
56
|
+
|
|
57
|
+
// Should have zod dependency (from real package.json)
|
|
58
|
+
expect(scan.dependencies.zod).toBeTruthy();
|
|
59
|
+
expect(scan.dependencies.commander).toBeTruthy();
|
|
60
|
+
},
|
|
61
|
+
30000,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
test(
|
|
65
|
+
"detects test framework",
|
|
66
|
+
async () => {
|
|
67
|
+
const workdir = join(import.meta.dir, "../..");
|
|
68
|
+
|
|
69
|
+
const scan = await scanCodebase(workdir);
|
|
70
|
+
|
|
71
|
+
// Should detect bun:test (no framework in package.json)
|
|
72
|
+
const hasBunTest = scan.testPatterns.some((p) => p.includes("bun:test"));
|
|
73
|
+
expect(hasBunTest).toBe(true);
|
|
74
|
+
},
|
|
75
|
+
30000,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
test(
|
|
79
|
+
"detects test directory",
|
|
80
|
+
async () => {
|
|
81
|
+
const workdir = join(import.meta.dir, "../..");
|
|
82
|
+
|
|
83
|
+
const scan = await scanCodebase(workdir);
|
|
84
|
+
|
|
85
|
+
// Should detect test/ directory
|
|
86
|
+
const hasTestDir = scan.testPatterns.some((p) => p.includes("test/"));
|
|
87
|
+
expect(hasTestDir).toBe(true);
|
|
88
|
+
},
|
|
89
|
+
30000,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
test(
|
|
93
|
+
"file tree respects max depth",
|
|
94
|
+
async () => {
|
|
95
|
+
const workdir = join(import.meta.dir, "../..");
|
|
96
|
+
|
|
97
|
+
const scan = await scanCodebase(workdir);
|
|
98
|
+
|
|
99
|
+
// File tree should not be excessively deep (max depth 3)
|
|
100
|
+
const lines = scan.fileTree.split("\n");
|
|
101
|
+
const maxIndent = Math.max(
|
|
102
|
+
...lines.map((line) => {
|
|
103
|
+
const match = line.match(/^(│ {3}| {4})*/);
|
|
104
|
+
return match ? match[0].length / 4 : 0;
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// Max depth 3 means max indent of 2 (0-indexed)
|
|
109
|
+
expect(maxIndent).toBeLessThanOrEqual(3);
|
|
110
|
+
},
|
|
111
|
+
30000,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
test(
|
|
115
|
+
"file tree includes directories and files",
|
|
116
|
+
async () => {
|
|
117
|
+
const workdir = join(import.meta.dir, "../..");
|
|
118
|
+
|
|
119
|
+
const scan = await scanCodebase(workdir);
|
|
120
|
+
|
|
121
|
+
// Should contain directories (marked with trailing /)
|
|
122
|
+
const hasDirectories = scan.fileTree.includes("/");
|
|
123
|
+
expect(hasDirectories).toBe(true);
|
|
124
|
+
|
|
125
|
+
// Should contain some TypeScript files
|
|
126
|
+
const hasTsFiles = scan.fileTree.includes(".ts");
|
|
127
|
+
expect(hasTsFiles).toBe(true);
|
|
128
|
+
},
|
|
129
|
+
30000,
|
|
130
|
+
);
|
|
131
|
+
});
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge Case Tests for `nax config` Default View
|
|
3
|
+
*
|
|
4
|
+
* Tests edge cases and regression scenarios for the default view.
|
|
5
|
+
* These tests ensure the feature handles unusual scenarios correctly.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
9
|
+
import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "node:fs";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
|
|
13
|
+
describe("nax config (default view) - edge cases", () => {
|
|
14
|
+
let tempDir: string;
|
|
15
|
+
let originalCwd: string;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
tempDir = mkdtempSync(join(tmpdir(), "nax-config-edge-test-"));
|
|
19
|
+
originalCwd = process.cwd();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
process.chdir(originalCwd);
|
|
24
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// NOTE: Error handling tests removed - those belong in a separate error handling story
|
|
28
|
+
// The current story (CM-003) focuses on the happy path: displaying config when files are valid
|
|
29
|
+
|
|
30
|
+
test("handles project config with only comments (valid but empty JSON)", async () => {
|
|
31
|
+
// Create project config with only {}
|
|
32
|
+
const naxDir = join(tempDir, "nax");
|
|
33
|
+
mkdirSync(naxDir, { recursive: true });
|
|
34
|
+
writeFileSync(join(naxDir, "config.json"), "{}");
|
|
35
|
+
|
|
36
|
+
process.chdir(tempDir);
|
|
37
|
+
|
|
38
|
+
const proc = Bun.spawn(["bun", join(import.meta.dir, "../../bin/nax.ts"), "config"], {
|
|
39
|
+
cwd: tempDir,
|
|
40
|
+
stdout: "pipe",
|
|
41
|
+
stderr: "pipe",
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const output = await new Response(proc.stdout).text();
|
|
45
|
+
const exitCode = await proc.exited;
|
|
46
|
+
|
|
47
|
+
expect(exitCode).toBe(0);
|
|
48
|
+
|
|
49
|
+
// Should show project config as found (even though it's empty)
|
|
50
|
+
expect(output).toContain("// Project config:");
|
|
51
|
+
expect(output).toContain("config.json");
|
|
52
|
+
|
|
53
|
+
// Should still output valid JSON (merged with defaults)
|
|
54
|
+
const lines = output.split("\n");
|
|
55
|
+
const jsonStartIndex = lines.findIndex((line) => line.startsWith("{"));
|
|
56
|
+
const jsonOutput = lines.slice(jsonStartIndex).join("\n");
|
|
57
|
+
const parsed = JSON.parse(jsonOutput);
|
|
58
|
+
expect(parsed.version).toBe(1);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("handles deep nesting when walking up directory tree", async () => {
|
|
62
|
+
// Create project config at root
|
|
63
|
+
const naxDir = join(tempDir, "nax");
|
|
64
|
+
mkdirSync(naxDir, { recursive: true });
|
|
65
|
+
writeFileSync(
|
|
66
|
+
join(naxDir, "config.json"),
|
|
67
|
+
JSON.stringify({
|
|
68
|
+
execution: {
|
|
69
|
+
maxIterations: 50,
|
|
70
|
+
},
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Create deeply nested subdirectory
|
|
75
|
+
const deepDir = join(tempDir, "a", "b", "c", "d", "e", "f");
|
|
76
|
+
mkdirSync(deepDir, { recursive: true });
|
|
77
|
+
process.chdir(deepDir);
|
|
78
|
+
|
|
79
|
+
const proc = Bun.spawn(["bun", join(import.meta.dir, "../../bin/nax.ts"), "config"], {
|
|
80
|
+
cwd: deepDir,
|
|
81
|
+
stdout: "pipe",
|
|
82
|
+
stderr: "pipe",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const output = await new Response(proc.stdout).text();
|
|
86
|
+
const exitCode = await proc.exited;
|
|
87
|
+
|
|
88
|
+
expect(exitCode).toBe(0);
|
|
89
|
+
|
|
90
|
+
// Should find project config by walking up
|
|
91
|
+
expect(output).toContain("// Project config:");
|
|
92
|
+
expect(output).toContain("config.json");
|
|
93
|
+
|
|
94
|
+
// Should reflect merged config
|
|
95
|
+
const lines = output.split("\n");
|
|
96
|
+
const jsonStartIndex = lines.findIndex((line) => line.startsWith("{"));
|
|
97
|
+
const jsonOutput = lines.slice(jsonStartIndex).join("\n");
|
|
98
|
+
const parsed = JSON.parse(jsonOutput);
|
|
99
|
+
expect(parsed.execution.maxIterations).toBe(50);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("outputs complete config structure with all top-level keys", async () => {
|
|
103
|
+
process.chdir(tempDir);
|
|
104
|
+
|
|
105
|
+
const proc = Bun.spawn(["bun", join(import.meta.dir, "../../bin/nax.ts"), "config"], {
|
|
106
|
+
cwd: tempDir,
|
|
107
|
+
stdout: "pipe",
|
|
108
|
+
stderr: "pipe",
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const output = await new Response(proc.stdout).text();
|
|
112
|
+
const exitCode = await proc.exited;
|
|
113
|
+
|
|
114
|
+
expect(exitCode).toBe(0);
|
|
115
|
+
|
|
116
|
+
const lines = output.split("\n");
|
|
117
|
+
const jsonStartIndex = lines.findIndex((line) => line.startsWith("{"));
|
|
118
|
+
const jsonOutput = lines.slice(jsonStartIndex).join("\n");
|
|
119
|
+
const parsed = JSON.parse(jsonOutput);
|
|
120
|
+
|
|
121
|
+
// Verify all required top-level keys are present
|
|
122
|
+
expect(parsed).toHaveProperty("version");
|
|
123
|
+
expect(parsed).toHaveProperty("models");
|
|
124
|
+
expect(parsed).toHaveProperty("autoMode");
|
|
125
|
+
expect(parsed).toHaveProperty("routing");
|
|
126
|
+
expect(parsed).toHaveProperty("execution");
|
|
127
|
+
expect(parsed).toHaveProperty("quality");
|
|
128
|
+
expect(parsed).toHaveProperty("tdd");
|
|
129
|
+
expect(parsed).toHaveProperty("constitution");
|
|
130
|
+
expect(parsed).toHaveProperty("analyze");
|
|
131
|
+
expect(parsed).toHaveProperty("review");
|
|
132
|
+
expect(parsed).toHaveProperty("plan");
|
|
133
|
+
expect(parsed).toHaveProperty("acceptance");
|
|
134
|
+
expect(parsed).toHaveProperty("context");
|
|
135
|
+
expect(parsed).toHaveProperty("interaction");
|
|
136
|
+
expect(parsed).toHaveProperty("precheck");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("merges nested config overrides correctly", async () => {
|
|
140
|
+
// Create project config with nested overrides
|
|
141
|
+
const naxDir = join(tempDir, "nax");
|
|
142
|
+
mkdirSync(naxDir, { recursive: true });
|
|
143
|
+
writeFileSync(
|
|
144
|
+
join(naxDir, "config.json"),
|
|
145
|
+
JSON.stringify({
|
|
146
|
+
execution: {
|
|
147
|
+
maxIterations: 15,
|
|
148
|
+
rectification: {
|
|
149
|
+
enabled: false,
|
|
150
|
+
maxRetries: 5,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
process.chdir(tempDir);
|
|
157
|
+
|
|
158
|
+
const proc = Bun.spawn(["bun", join(import.meta.dir, "../../bin/nax.ts"), "config"], {
|
|
159
|
+
cwd: tempDir,
|
|
160
|
+
stdout: "pipe",
|
|
161
|
+
stderr: "pipe",
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const output = await new Response(proc.stdout).text();
|
|
165
|
+
const exitCode = await proc.exited;
|
|
166
|
+
|
|
167
|
+
expect(exitCode).toBe(0);
|
|
168
|
+
|
|
169
|
+
const lines = output.split("\n");
|
|
170
|
+
const jsonStartIndex = lines.findIndex((line) => line.startsWith("{"));
|
|
171
|
+
const jsonOutput = lines.slice(jsonStartIndex).join("\n");
|
|
172
|
+
const parsed = JSON.parse(jsonOutput);
|
|
173
|
+
|
|
174
|
+
// Verify nested overrides
|
|
175
|
+
expect(parsed.execution.maxIterations).toBe(15);
|
|
176
|
+
expect(parsed.execution.rectification.enabled).toBe(false);
|
|
177
|
+
expect(parsed.execution.rectification.maxRetries).toBe(5);
|
|
178
|
+
|
|
179
|
+
// Verify non-overridden fields are preserved
|
|
180
|
+
expect(parsed.execution.iterationDelayMs).toBeDefined();
|
|
181
|
+
expect(parsed.execution.rectification.fullSuiteTimeoutSeconds).toBeDefined();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("handles project config with schema version mismatch", async () => {
|
|
185
|
+
// Create project config with future schema version
|
|
186
|
+
const naxDir = join(tempDir, "nax");
|
|
187
|
+
mkdirSync(naxDir, { recursive: true });
|
|
188
|
+
writeFileSync(
|
|
189
|
+
join(naxDir, "config.json"),
|
|
190
|
+
JSON.stringify({
|
|
191
|
+
version: 999, // Future version
|
|
192
|
+
execution: {
|
|
193
|
+
maxIterations: 25,
|
|
194
|
+
},
|
|
195
|
+
}),
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
process.chdir(tempDir);
|
|
199
|
+
|
|
200
|
+
const proc = Bun.spawn(["bun", join(import.meta.dir, "../../bin/nax.ts"), "config"], {
|
|
201
|
+
cwd: tempDir,
|
|
202
|
+
stdout: "pipe",
|
|
203
|
+
stderr: "pipe",
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const output = await new Response(proc.stdout).text();
|
|
207
|
+
const stderr = await new Response(proc.stderr).text();
|
|
208
|
+
const exitCode = await proc.exited;
|
|
209
|
+
|
|
210
|
+
// Should either succeed with warning or fail gracefully
|
|
211
|
+
if (exitCode !== 0) {
|
|
212
|
+
expect(stderr.toLowerCase()).toMatch(/error|invalid|version/);
|
|
213
|
+
} else {
|
|
214
|
+
// If it succeeds, verify output is valid
|
|
215
|
+
const lines = output.split("\n");
|
|
216
|
+
const jsonStartIndex = lines.findIndex((line) => line.startsWith("{"));
|
|
217
|
+
expect(jsonStartIndex).toBeGreaterThan(0);
|
|
218
|
+
const jsonOutput = lines.slice(jsonStartIndex).join("\n");
|
|
219
|
+
expect(() => JSON.parse(jsonOutput)).not.toThrow();
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
});
|