@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,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended Agent Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Types for plan mode, decompose mode, and interactive PTY sessions.
|
|
5
|
+
* Separated from core types to keep each file under 400 lines.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ModelDef, ModelTier } from "../config/schema";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Configuration options for running an agent in plan mode.
|
|
12
|
+
*
|
|
13
|
+
* Plan mode spawns the agent interactively (or non-interactively with input file)
|
|
14
|
+
* to gather requirements, ask clarifying questions, and produce a structured spec.
|
|
15
|
+
*/
|
|
16
|
+
export interface PlanOptions {
|
|
17
|
+
/** The initial planning prompt or task description */
|
|
18
|
+
prompt: string;
|
|
19
|
+
/** Working directory */
|
|
20
|
+
workdir: string;
|
|
21
|
+
/** Whether to run in interactive mode (agent takes over terminal) */
|
|
22
|
+
interactive: boolean;
|
|
23
|
+
/** Optional codebase context (file tree, dependencies, test patterns) */
|
|
24
|
+
codebaseContext?: string;
|
|
25
|
+
/** Optional input file path for non-interactive mode */
|
|
26
|
+
inputFile?: string;
|
|
27
|
+
/** Model tier to use for planning (default: "balanced") */
|
|
28
|
+
modelTier?: ModelTier;
|
|
29
|
+
/** Resolved model definition */
|
|
30
|
+
modelDef?: ModelDef;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Result from running an agent in plan mode.
|
|
35
|
+
*
|
|
36
|
+
* Contains the generated specification content and optional conversation log.
|
|
37
|
+
*/
|
|
38
|
+
export interface PlanResult {
|
|
39
|
+
/** The generated specification markdown content */
|
|
40
|
+
specContent: string;
|
|
41
|
+
/** Optional conversation log (for debugging/review) */
|
|
42
|
+
conversationLog?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Configuration options for running an agent in decompose mode.
|
|
47
|
+
*
|
|
48
|
+
* Decompose mode reads a spec document and breaks it down into classified user stories
|
|
49
|
+
* in a single LLM call (decompose + classify combined).
|
|
50
|
+
*/
|
|
51
|
+
export interface DecomposeOptions {
|
|
52
|
+
/** The spec document content to decompose */
|
|
53
|
+
specContent: string;
|
|
54
|
+
/** Working directory */
|
|
55
|
+
workdir: string;
|
|
56
|
+
/** Codebase context (file tree, dependencies, test patterns) */
|
|
57
|
+
codebaseContext: string;
|
|
58
|
+
/** Model tier to use for decomposition (default: "balanced") */
|
|
59
|
+
modelTier?: ModelTier;
|
|
60
|
+
/** Resolved model definition */
|
|
61
|
+
modelDef?: ModelDef;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A single classified user story from decompose result. */
|
|
65
|
+
export interface DecomposedStory {
|
|
66
|
+
/** Story ID (e.g., "US-001") */
|
|
67
|
+
id: string;
|
|
68
|
+
/** Story title */
|
|
69
|
+
title: string;
|
|
70
|
+
/** Story description */
|
|
71
|
+
description: string;
|
|
72
|
+
/** Acceptance criteria */
|
|
73
|
+
acceptanceCriteria: string[];
|
|
74
|
+
/** Tags for routing */
|
|
75
|
+
tags: string[];
|
|
76
|
+
/** Dependencies (story IDs) */
|
|
77
|
+
dependencies: string[];
|
|
78
|
+
/** Classified complexity */
|
|
79
|
+
complexity: "simple" | "medium" | "complex" | "expert";
|
|
80
|
+
/** Context files to inject into agent prompt before execution */
|
|
81
|
+
contextFiles: string[];
|
|
82
|
+
/** Classification reasoning */
|
|
83
|
+
reasoning: string;
|
|
84
|
+
/** Estimated lines of code */
|
|
85
|
+
estimatedLOC: number;
|
|
86
|
+
/** Implementation risks */
|
|
87
|
+
risks: string[];
|
|
88
|
+
/** Test strategy recommendation from LLM */
|
|
89
|
+
testStrategy?: "three-session-tdd" | "test-after";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Result from running an agent in decompose mode.
|
|
94
|
+
*
|
|
95
|
+
* Contains the decomposed and classified user stories.
|
|
96
|
+
*/
|
|
97
|
+
export interface DecomposeResult {
|
|
98
|
+
/** The decomposed and classified user stories */
|
|
99
|
+
stories: DecomposedStory[];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* PTY handle interface for managing spawned PTY process.
|
|
104
|
+
*
|
|
105
|
+
* Provides methods to write input, resize terminal, and kill process.
|
|
106
|
+
* Returned by runInteractive() for TUI integration.
|
|
107
|
+
*/
|
|
108
|
+
export interface PtyHandle {
|
|
109
|
+
/** Write input to PTY stdin */
|
|
110
|
+
write(data: string): void;
|
|
111
|
+
/** Resize PTY terminal */
|
|
112
|
+
resize(cols: number, rows: number): void;
|
|
113
|
+
/** Kill PTY process */
|
|
114
|
+
kill(): void;
|
|
115
|
+
/** Process ID */
|
|
116
|
+
pid: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Configuration options for running an agent in interactive PTY mode.
|
|
121
|
+
*
|
|
122
|
+
* Extends AgentRunOptions with PTY-specific callbacks for output streaming
|
|
123
|
+
* and exit handling. Used by TUI to embed agent sessions.
|
|
124
|
+
*/
|
|
125
|
+
export interface InteractiveRunOptions extends AgentRunOptions {
|
|
126
|
+
/** Callback fired when PTY outputs data */
|
|
127
|
+
onOutput: (data: Buffer) => void;
|
|
128
|
+
/** Callback fired when PTY process exits */
|
|
129
|
+
onExit: (code: number) => void;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Re-import for the extends clause
|
|
133
|
+
import type { AgentRunOptions } from "./types";
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Adapter Interface
|
|
3
|
+
*
|
|
4
|
+
* Every coding agent (Claude Code, Codex, OpenCode, etc.)
|
|
5
|
+
* implements this interface so nax can spawn, monitor, and
|
|
6
|
+
* collect results from them uniformly.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ModelDef, ModelTier } from "../config/schema";
|
|
10
|
+
|
|
11
|
+
// Re-export extended types for backward compatibility
|
|
12
|
+
export type {
|
|
13
|
+
PlanOptions,
|
|
14
|
+
PlanResult,
|
|
15
|
+
DecomposeOptions,
|
|
16
|
+
DecomposeResult,
|
|
17
|
+
DecomposedStory,
|
|
18
|
+
PtyHandle,
|
|
19
|
+
InteractiveRunOptions,
|
|
20
|
+
} from "./types-extended";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Agent execution result returned after running a coding agent.
|
|
24
|
+
*/
|
|
25
|
+
export interface AgentResult {
|
|
26
|
+
/** Whether the agent completed successfully */
|
|
27
|
+
success: boolean;
|
|
28
|
+
/** Exit code from the process */
|
|
29
|
+
exitCode: number;
|
|
30
|
+
/** stdout output (last N lines) */
|
|
31
|
+
output: string;
|
|
32
|
+
/** stderr output tail (last N lines) — useful for diagnosing failures */
|
|
33
|
+
stderr?: string;
|
|
34
|
+
/** Whether the agent hit a rate limit */
|
|
35
|
+
rateLimited: boolean;
|
|
36
|
+
/** Duration in milliseconds */
|
|
37
|
+
durationMs: number;
|
|
38
|
+
/** Estimated cost for this run (USD) */
|
|
39
|
+
estimatedCost: number;
|
|
40
|
+
/** Process ID of the spawned agent (for cleanup on failure) */
|
|
41
|
+
pid?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Configuration options for running a coding agent session.
|
|
46
|
+
*/
|
|
47
|
+
export interface AgentRunOptions {
|
|
48
|
+
/** The prompt to send to the agent */
|
|
49
|
+
prompt: string;
|
|
50
|
+
/** Working directory */
|
|
51
|
+
workdir: string;
|
|
52
|
+
/** Model tier (for cost estimation) */
|
|
53
|
+
modelTier: ModelTier;
|
|
54
|
+
/** Resolved model definition */
|
|
55
|
+
modelDef: ModelDef;
|
|
56
|
+
/** Maximum runtime in seconds */
|
|
57
|
+
timeoutSeconds: number;
|
|
58
|
+
/** Environment variables to pass */
|
|
59
|
+
env?: Record<string, string>;
|
|
60
|
+
/** Use --dangerously-skip-permissions flag (default: true) */
|
|
61
|
+
dangerouslySkipPermissions?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Agent capability metadata describing what features and tiers the agent supports.
|
|
66
|
+
*/
|
|
67
|
+
export interface AgentCapabilities {
|
|
68
|
+
/** Model tiers this agent supports (e.g., fast/balanced/powerful) */
|
|
69
|
+
readonly supportedTiers: readonly ModelTier[];
|
|
70
|
+
/** Maximum context window size in tokens */
|
|
71
|
+
readonly maxContextTokens: number;
|
|
72
|
+
/** Feature flags — what workflows this agent can handle */
|
|
73
|
+
readonly features: ReadonlySet<"tdd" | "review" | "refactor" | "batch">;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Agent adapter interface — one implementation per supported coding agent.
|
|
78
|
+
*
|
|
79
|
+
* Provides uniform interface for checking installation, running agents,
|
|
80
|
+
* and building CLI commands across different coding agent tools.
|
|
81
|
+
*/
|
|
82
|
+
export interface AgentAdapter {
|
|
83
|
+
/** Unique agent name (e.g., "claude", "codex", "opencode") */
|
|
84
|
+
readonly name: string;
|
|
85
|
+
/** Human-readable display name */
|
|
86
|
+
readonly displayName: string;
|
|
87
|
+
/** Binary command to check if agent is installed */
|
|
88
|
+
readonly binary: string;
|
|
89
|
+
/** Capability metadata describing supported tiers and features */
|
|
90
|
+
readonly capabilities: AgentCapabilities;
|
|
91
|
+
|
|
92
|
+
/** Check if the agent binary is available on this machine. */
|
|
93
|
+
isInstalled(): Promise<boolean>;
|
|
94
|
+
|
|
95
|
+
/** Run the agent with a prompt and return the result. */
|
|
96
|
+
run(options: AgentRunOptions): Promise<AgentResult>;
|
|
97
|
+
|
|
98
|
+
/** Build the CLI command for a given run (for dry-run display). */
|
|
99
|
+
buildCommand(options: AgentRunOptions): string[];
|
|
100
|
+
|
|
101
|
+
/** Run the agent in plan mode to generate a feature specification. */
|
|
102
|
+
plan(options: import("./types-extended").PlanOptions): Promise<import("./types-extended").PlanResult>;
|
|
103
|
+
|
|
104
|
+
/** Run the agent in decompose mode to break spec into classified stories. */
|
|
105
|
+
decompose(options: import("./types-extended").DecomposeOptions): Promise<import("./types-extended").DecomposeResult>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Run the agent in interactive PTY mode for TUI embedding.
|
|
109
|
+
* This method is optional — only implemented by agents that support
|
|
110
|
+
* interactive terminal sessions (e.g., Claude Code).
|
|
111
|
+
*/
|
|
112
|
+
runInteractive?(options: import("./types-extended").InteractiveRunOptions): import("./types-extended").PtyHandle;
|
|
113
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Validation Helpers
|
|
3
|
+
*
|
|
4
|
+
* Runtime validation for agent capabilities and tier compatibility.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ModelTier } from "../config/schema";
|
|
8
|
+
import type { AgentAdapter } from "./types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check if an agent supports a given model tier.
|
|
12
|
+
*
|
|
13
|
+
* Used to validate routing decisions at runtime — ensures the orchestrator
|
|
14
|
+
* doesn't try to use a tier the agent doesn't support.
|
|
15
|
+
*
|
|
16
|
+
* @param agent - The agent adapter to validate
|
|
17
|
+
* @param tier - The model tier to check (fast/balanced/powerful)
|
|
18
|
+
* @returns true if the agent declares support for this tier
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const agent = new ClaudeCodeAdapter();
|
|
23
|
+
* if (!validateAgentForTier(agent, "powerful")) {
|
|
24
|
+
* console.warn(`Agent ${agent.name} does not support powerful tier`);
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function validateAgentForTier(agent: AgentAdapter, tier: ModelTier): boolean {
|
|
29
|
+
return agent.capabilities.supportedTiers.includes(tier);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Check if an agent supports a specific feature.
|
|
34
|
+
*
|
|
35
|
+
* @param agent - The agent adapter to validate
|
|
36
|
+
* @param feature - The feature to check (tdd/review/refactor/batch)
|
|
37
|
+
* @returns true if the agent declares support for this feature
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const agent = new ClaudeCodeAdapter();
|
|
42
|
+
* if (!validateAgentFeature(agent, "tdd")) {
|
|
43
|
+
* throw new Error("Agent does not support TDD workflow");
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function validateAgentFeature(agent: AgentAdapter, feature: "tdd" | "review" | "refactor" | "batch"): boolean {
|
|
48
|
+
return agent.capabilities.features.has(feature);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get a human-readable description of agent capabilities.
|
|
53
|
+
*
|
|
54
|
+
* @param agent - The agent adapter to describe
|
|
55
|
+
* @returns Formatted capability summary
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* const agent = new ClaudeCodeAdapter();
|
|
60
|
+
* console.log(describeAgentCapabilities(agent));
|
|
61
|
+
* // "claude: tiers=[fast,balanced,powerful], maxTokens=200000, features=[tdd,review,refactor,batch]"
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export function describeAgentCapabilities(agent: AgentAdapter): string {
|
|
65
|
+
const tiers = agent.capabilities.supportedTiers.join(",");
|
|
66
|
+
const features = Array.from(agent.capabilities.features).join(",");
|
|
67
|
+
const maxTokens = agent.capabilities.maxContextTokens;
|
|
68
|
+
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
69
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-Enhanced Classifier
|
|
3
|
+
*
|
|
4
|
+
* Uses a single cheap LLM call (haiku) to classify all stories in one shot.
|
|
5
|
+
* Falls back to keyword matching if LLM call fails.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Anthropic } from "@anthropic-ai/sdk";
|
|
9
|
+
import type { NaxConfig } from "../config";
|
|
10
|
+
import { getLogger } from "../logger";
|
|
11
|
+
import type { UserStory } from "../prd";
|
|
12
|
+
import { classifyComplexity } from "../routing";
|
|
13
|
+
import type { ClassificationResult, CodebaseScan, StoryClassification } from "./types";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Raw LLM classification item (before validation)
|
|
17
|
+
*/
|
|
18
|
+
interface LLMClassificationItem {
|
|
19
|
+
storyId: unknown;
|
|
20
|
+
complexity: unknown;
|
|
21
|
+
relevantFiles: unknown;
|
|
22
|
+
reasoning: unknown;
|
|
23
|
+
estimatedLOC: unknown;
|
|
24
|
+
risks: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Classify stories using LLM-enhanced analysis with fallback to keyword matching.
|
|
29
|
+
*
|
|
30
|
+
* Makes a single Anthropic API call (haiku tier) to classify all stories.
|
|
31
|
+
* If the LLM call fails or returns invalid JSON, falls back to keyword matching.
|
|
32
|
+
*
|
|
33
|
+
* @param stories - User stories to classify
|
|
34
|
+
* @param scan - Codebase scan result
|
|
35
|
+
* @param config - Ngent configuration
|
|
36
|
+
* @returns Classification result with method used
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const result = await classifyStories(stories, scan, config);
|
|
41
|
+
* if (result.method === "keyword-fallback") {
|
|
42
|
+
* console.warn("LLM classification failed:", result.fallbackReason);
|
|
43
|
+
* }
|
|
44
|
+
* console.log(result.classifications);
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export async function classifyStories(
|
|
48
|
+
stories: UserStory[],
|
|
49
|
+
scan: CodebaseScan,
|
|
50
|
+
config: NaxConfig,
|
|
51
|
+
): Promise<ClassificationResult> {
|
|
52
|
+
// Check if LLM-enhanced analysis is enabled
|
|
53
|
+
if (!config.analyze?.llmEnhanced) {
|
|
54
|
+
return {
|
|
55
|
+
classifications: stories.map((story) => fallbackClassification(story)),
|
|
56
|
+
method: "keyword-fallback",
|
|
57
|
+
fallbackReason: "LLM-enhanced analysis disabled in config",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Try LLM classification
|
|
62
|
+
try {
|
|
63
|
+
const classifications = await classifyWithLLM(stories, scan, config);
|
|
64
|
+
return {
|
|
65
|
+
classifications,
|
|
66
|
+
method: "llm",
|
|
67
|
+
};
|
|
68
|
+
} catch (error) {
|
|
69
|
+
// Fall back to keyword matching
|
|
70
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
71
|
+
const logger = getLogger();
|
|
72
|
+
logger.warn("analyze", "LLM classification failed, falling back to keyword matching", { error: reason });
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
classifications: stories.map((story) => fallbackClassification(story)),
|
|
76
|
+
method: "keyword-fallback",
|
|
77
|
+
fallbackReason: reason,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Classify stories using LLM with structured JSON output.
|
|
84
|
+
*
|
|
85
|
+
* @param stories - User stories to classify
|
|
86
|
+
* @param scan - Codebase scan result
|
|
87
|
+
* @param config - Ngent configuration
|
|
88
|
+
* @returns Array of story classifications
|
|
89
|
+
*/
|
|
90
|
+
async function classifyWithLLM(
|
|
91
|
+
stories: UserStory[],
|
|
92
|
+
scan: CodebaseScan,
|
|
93
|
+
config: NaxConfig,
|
|
94
|
+
): Promise<StoryClassification[]> {
|
|
95
|
+
// Check API key
|
|
96
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
97
|
+
if (!apiKey) {
|
|
98
|
+
throw new Error("ANTHROPIC_API_KEY not set");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const client = new Anthropic({ apiKey });
|
|
102
|
+
|
|
103
|
+
// Build prompt
|
|
104
|
+
const prompt = buildClassificationPrompt(stories, scan, config);
|
|
105
|
+
|
|
106
|
+
// Make API call (use haiku for cheap classification)
|
|
107
|
+
const response = await client.messages.create({
|
|
108
|
+
model: "claude-haiku-4-20250514",
|
|
109
|
+
max_tokens: 4096,
|
|
110
|
+
messages: [
|
|
111
|
+
{
|
|
112
|
+
role: "user",
|
|
113
|
+
content: prompt,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Extract text from response
|
|
119
|
+
const textContent = response.content.find((c) => c.type === "text");
|
|
120
|
+
if (!textContent || textContent.type !== "text") {
|
|
121
|
+
throw new Error("No text response from LLM");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Parse JSON response
|
|
125
|
+
const jsonText = extractJSON(textContent.text);
|
|
126
|
+
const parsed: unknown = JSON.parse(jsonText);
|
|
127
|
+
|
|
128
|
+
// Validate structure
|
|
129
|
+
if (!Array.isArray(parsed)) {
|
|
130
|
+
throw new Error("LLM response is not an array");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Map to StoryClassification[]
|
|
134
|
+
const classifications: StoryClassification[] = parsed.map((item: unknown) => {
|
|
135
|
+
const rawItem = item as LLMClassificationItem;
|
|
136
|
+
return {
|
|
137
|
+
storyId: String(rawItem.storyId),
|
|
138
|
+
complexity: validateComplexity(rawItem.complexity),
|
|
139
|
+
contextFiles: Array.isArray(rawItem.relevantFiles) ? rawItem.relevantFiles.map(String) : [],
|
|
140
|
+
reasoning: String(rawItem.reasoning || "No reasoning provided"),
|
|
141
|
+
estimatedLOC: Number(rawItem.estimatedLOC) || 0,
|
|
142
|
+
risks: Array.isArray(rawItem.risks) ? rawItem.risks.map(String) : [],
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Ensure all stories are classified
|
|
147
|
+
const classifiedIds = new Set(classifications.map((c) => c.storyId));
|
|
148
|
+
for (const story of stories) {
|
|
149
|
+
if (!classifiedIds.has(story.id)) {
|
|
150
|
+
throw new Error(`Story ${story.id} not classified by LLM`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return classifications;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Build classification prompt for LLM.
|
|
159
|
+
*
|
|
160
|
+
* @param stories - User stories to classify
|
|
161
|
+
* @param scan - Codebase scan result
|
|
162
|
+
* @param config - Ngent configuration
|
|
163
|
+
* @returns Formatted prompt string
|
|
164
|
+
*/
|
|
165
|
+
function buildClassificationPrompt(stories: UserStory[], scan: CodebaseScan, config: NaxConfig): string {
|
|
166
|
+
// Format codebase summary
|
|
167
|
+
const codebaseSummary = `
|
|
168
|
+
FILE TREE:
|
|
169
|
+
${scan.fileTree}
|
|
170
|
+
|
|
171
|
+
DEPENDENCIES:
|
|
172
|
+
${Object.entries(scan.dependencies)
|
|
173
|
+
.map(([name, version]) => `- ${name}: ${version}`)
|
|
174
|
+
.join("\n")}
|
|
175
|
+
|
|
176
|
+
DEV DEPENDENCIES:
|
|
177
|
+
${Object.entries(scan.devDependencies)
|
|
178
|
+
.map(([name, version]) => `- ${name}: ${version}`)
|
|
179
|
+
.join("\n")}
|
|
180
|
+
|
|
181
|
+
TEST PATTERNS:
|
|
182
|
+
${scan.testPatterns.map((p) => `- ${p}`).join("\n")}
|
|
183
|
+
`.trim();
|
|
184
|
+
|
|
185
|
+
// Format stories as JSON for LLM
|
|
186
|
+
const storiesJson = JSON.stringify(
|
|
187
|
+
stories.map((s) => ({
|
|
188
|
+
id: s.id,
|
|
189
|
+
title: s.title,
|
|
190
|
+
description: s.description,
|
|
191
|
+
acceptanceCriteria: s.acceptanceCriteria,
|
|
192
|
+
tags: s.tags,
|
|
193
|
+
})),
|
|
194
|
+
null,
|
|
195
|
+
2,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return `You are a code complexity classifier. Given a codebase summary and user stories,
|
|
199
|
+
classify each story's implementation complexity.
|
|
200
|
+
|
|
201
|
+
CODEBASE:
|
|
202
|
+
${codebaseSummary}
|
|
203
|
+
|
|
204
|
+
STORIES:
|
|
205
|
+
${storiesJson}
|
|
206
|
+
|
|
207
|
+
For each story, respond with a JSON array (and ONLY the JSON array, no markdown code fences):
|
|
208
|
+
[{
|
|
209
|
+
"storyId": "US-001",
|
|
210
|
+
"complexity": "simple|medium|complex|expert",
|
|
211
|
+
"relevantFiles": ["src/path/to/file.ts"],
|
|
212
|
+
"reasoning": "Why this complexity level",
|
|
213
|
+
"estimatedLOC": 50,
|
|
214
|
+
"risks": ["No existing cache layer"]
|
|
215
|
+
}]
|
|
216
|
+
|
|
217
|
+
Classification rules:
|
|
218
|
+
- simple: 1-3 files, <100 LOC, straightforward implementation, existing patterns
|
|
219
|
+
- medium: 3-6 files, 100-300 LOC, moderate logic, some new patterns
|
|
220
|
+
- complex: 6+ files, 300-800 LOC, architectural changes, cross-cutting concerns
|
|
221
|
+
- expert: Security/crypto/real-time/distributed systems, >800 LOC, new infrastructure
|
|
222
|
+
|
|
223
|
+
Consider:
|
|
224
|
+
1. Does infrastructure exist? (e.g., "add caching" when no cache layer exists = complex)
|
|
225
|
+
2. How many files will be touched?
|
|
226
|
+
3. Are there cross-cutting concerns (auth, validation, error handling)?
|
|
227
|
+
4. Does it require new dependencies or architectural decisions?
|
|
228
|
+
|
|
229
|
+
Respond with ONLY the JSON array.`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Extract JSON from LLM response (handles markdown code fences).
|
|
234
|
+
*
|
|
235
|
+
* @param text - LLM response text
|
|
236
|
+
* @returns JSON string
|
|
237
|
+
*/
|
|
238
|
+
function extractJSON(text: string): string {
|
|
239
|
+
// Remove markdown code fences if present
|
|
240
|
+
const jsonMatch = text.match(/```(?:json)?\s*(\[[\s\S]*?\])\s*```/);
|
|
241
|
+
if (jsonMatch) {
|
|
242
|
+
return jsonMatch[1];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Try to find JSON array directly
|
|
246
|
+
const arrayMatch = text.match(/\[[\s\S]*\]/);
|
|
247
|
+
if (arrayMatch) {
|
|
248
|
+
return arrayMatch[0];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Return as-is if no special formatting detected
|
|
252
|
+
return text.trim();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Validate complexity value from LLM response.
|
|
257
|
+
*
|
|
258
|
+
* @param value - Complexity value from LLM
|
|
259
|
+
* @returns Valid Complexity type
|
|
260
|
+
*/
|
|
261
|
+
function validateComplexity(value: unknown): "simple" | "medium" | "complex" | "expert" {
|
|
262
|
+
if (value === "simple" || value === "medium" || value === "complex" || value === "expert") {
|
|
263
|
+
return value;
|
|
264
|
+
}
|
|
265
|
+
// Default to medium if invalid
|
|
266
|
+
return "medium";
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Fallback classification using keyword matching.
|
|
271
|
+
*
|
|
272
|
+
* @param story - User story to classify
|
|
273
|
+
* @returns Story classification using keyword-based complexity
|
|
274
|
+
*/
|
|
275
|
+
function fallbackClassification(story: UserStory): StoryClassification {
|
|
276
|
+
const complexity = classifyComplexity(story.title, story.description, story.acceptanceCriteria, story.tags);
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
storyId: story.id,
|
|
280
|
+
complexity,
|
|
281
|
+
contextFiles: [],
|
|
282
|
+
reasoning: `Keyword-based classification: ${complexity}`,
|
|
283
|
+
estimatedLOC: estimateLOCFromComplexity(complexity),
|
|
284
|
+
risks: [],
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Estimate LOC from complexity level (rough heuristic).
|
|
290
|
+
*
|
|
291
|
+
* @param complexity - Complexity level
|
|
292
|
+
* @returns Estimated lines of code
|
|
293
|
+
*/
|
|
294
|
+
function estimateLOCFromComplexity(complexity: "simple" | "medium" | "complex" | "expert"): number {
|
|
295
|
+
switch (complexity) {
|
|
296
|
+
case "simple":
|
|
297
|
+
return 50;
|
|
298
|
+
case "medium":
|
|
299
|
+
return 150;
|
|
300
|
+
case "complex":
|
|
301
|
+
return 400;
|
|
302
|
+
case "expert":
|
|
303
|
+
return 800;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyze Module
|
|
3
|
+
*
|
|
4
|
+
* LLM-enhanced story classification with codebase scanning.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type {
|
|
8
|
+
CodebaseScan,
|
|
9
|
+
StoryClassification,
|
|
10
|
+
ClassifierResponse,
|
|
11
|
+
ClassificationMethod,
|
|
12
|
+
ClassificationResult,
|
|
13
|
+
} from "./types";
|
|
14
|
+
|
|
15
|
+
export { scanCodebase } from "./scanner";
|
|
16
|
+
export { classifyStories } from "./classifier";
|