@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.
Files changed (459) hide show
  1. package/.gitlab-ci.yml +96 -0
  2. package/BRIEF.md +140 -0
  3. package/CHANGELOG.md +60 -0
  4. package/CLAUDE.md +159 -0
  5. package/README.md +373 -0
  6. package/US-007-IMPLEMENTATION.md +139 -0
  7. package/bin/nax.ts +930 -0
  8. package/biome.json +14 -0
  9. package/bun.lock +168 -0
  10. package/bunfig.toml +11 -0
  11. package/docs/20260216-fix-plan-context-review.md +56 -0
  12. package/docs/20260216-relentless-vs-ngent-comparison.md +208 -0
  13. package/docs/20260216-v02-plan.md +136 -0
  14. package/docs/20260216-v02-review.md +685 -0
  15. package/docs/20260217-dogfood-findings.md +56 -0
  16. package/docs/20260217-p2-plus-plan.md +117 -0
  17. package/docs/20260217-partial-fixes-plan.md +62 -0
  18. package/docs/20260217-plan-analyze-spec.md +117 -0
  19. package/docs/20260217-post-impl-review.md +1137 -0
  20. package/docs/20260217-quick-wins-plan.md +66 -0
  21. package/docs/20260217-split-runner-plan.md +75 -0
  22. package/docs/20260217-v03-impl-plan.md +80 -0
  23. package/docs/20260217-v03-post-impl-review.md +589 -0
  24. package/docs/20260217-v04-impl-plan.md +86 -0
  25. package/docs/20260217-v05-post-impl-review.md +850 -0
  26. package/docs/20260217-v06-post-impl-review.md +817 -0
  27. package/docs/20260218-adr003-port-plan.md +151 -0
  28. package/docs/20260218-review-adr003-verification.md +175 -0
  29. package/docs/20260219-fix-plan-bug16-19.md +79 -0
  30. package/docs/20260219-fix-plan-bug20-22.md +114 -0
  31. package/docs/20260219-plan-llm-routing.md +116 -0
  32. package/docs/20260219-review-bug20-22-fixes.md +135 -0
  33. package/docs/20260219-routing-baseline-keyword.md +63 -0
  34. package/docs/20260220-plan-structured-logging-p1.md +80 -0
  35. package/docs/20260220-plan-structured-logging-p2.md +37 -0
  36. package/docs/20260220-review-llm-routing.md +180 -0
  37. package/docs/20260220-review-post-fix-llm-routing.md +70 -0
  38. package/docs/20260221-fix-plan-relevantfiles-split.md +101 -0
  39. package/docs/20260221-fix-plan-routing-mode.md +125 -0
  40. package/docs/20260221-review-v0.9-implementation.md +379 -0
  41. package/docs/20260222-fix-plan-v091-routing-isolation.md +197 -0
  42. package/docs/20260223-fix-plan-prompt-audit.md +62 -0
  43. package/docs/20260224-nax-roadmap-phases.md +189 -0
  44. package/docs/20260225-phase2-llm-service-layer.md +401 -0
  45. package/docs/20260225-review-v0.10.1.md +187 -0
  46. package/docs/20260303-v010-implementation-plan.md +165 -0
  47. package/docs/CLAUDE.md.bak +191 -0
  48. package/docs/ROADMAP.md +165 -0
  49. package/docs/SPEC-rectification.md +0 -0
  50. package/docs/SPEC.md +324 -0
  51. package/docs/US-001-plugin-loading-verification.md +152 -0
  52. package/docs/architecture-analysis.md +1076 -0
  53. package/docs/bugs/BUG-21-escalation-null-attempts.md +48 -0
  54. package/docs/bugs-from-dogfood-run-c.md +243 -0
  55. package/docs/code-review-20260228.md +612 -0
  56. package/docs/code-review-v0.15.0.md +629 -0
  57. package/docs/hook-lifecycle-test-plan.md +149 -0
  58. package/docs/releases/v0.11.0-and-earlier.md +20 -0
  59. package/docs/releases/v0.12.0.md +15 -0
  60. package/docs/releases/v0.13.0.md +14 -0
  61. package/docs/releases/v0.14.0.md +20 -0
  62. package/docs/releases/v0.14.1.md +36 -0
  63. package/docs/releases/v0.14.2.md +51 -0
  64. package/docs/releases/v0.14.3.md +174 -0
  65. package/docs/releases/v0.14.4.md +94 -0
  66. package/docs/releases/v0.15.0.md +502 -0
  67. package/docs/releases/v0.15.1.md +170 -0
  68. package/docs/releases/v0.15.3.md +193 -0
  69. package/docs/specs/status-file-v0.10.1.md +812 -0
  70. package/docs/v0.10-global-config.md +206 -0
  71. package/docs/v0.10-plugin-system.md +415 -0
  72. package/docs/v0.10-prompt-optimizer.md +234 -0
  73. package/docs/v0.3-spec.md +244 -0
  74. package/docs/v0.4-spec.md +140 -0
  75. package/docs/v0.5-spec.md +237 -0
  76. package/docs/v0.6-spec.md +371 -0
  77. package/docs/v0.7-spec.md +177 -0
  78. package/docs/v0.8-llm-routing.md +206 -0
  79. package/docs/v0.8-structured-logging.md +132 -0
  80. package/docs/v0.9.3-prompt-audit.md +112 -0
  81. package/examples/plugins/console-reporter/index.test.ts +207 -0
  82. package/examples/plugins/console-reporter/index.ts +110 -0
  83. package/nax/config.json +147 -0
  84. package/nax/features/bugfix-v0171/prd.json +52 -0
  85. package/nax/features/config-management/prd.json +108 -0
  86. package/nax/features/config-management/progress.txt +5 -0
  87. package/nax/features/diagnose/acceptance.test.ts +412 -0
  88. package/nax/features/diagnose/prd.json +41 -0
  89. package/nax/features/orchestration-fixes/prd.json +89 -0
  90. package/nax/features/orchestration-fixes/progress.txt +1 -0
  91. package/nax/features/plugin-integration/US-007-VERIFICATION.md +259 -0
  92. package/nax/features/plugin-integration/prd.json +208 -0
  93. package/nax/features/plugin-integration/progress.txt +5 -0
  94. package/nax/features/precheck/prd.json +205 -0
  95. package/nax/features/precheck/progress.txt +15 -0
  96. package/nax/features/structured-logging/prd.json +199 -0
  97. package/nax/features/unlock/prd.json +36 -0
  98. package/package.json +47 -0
  99. package/src/acceptance/fix-generator.ts +348 -0
  100. package/src/acceptance/generator.ts +282 -0
  101. package/src/acceptance/index.ts +30 -0
  102. package/src/acceptance/types.ts +79 -0
  103. package/src/agents/claude-decompose.ts +169 -0
  104. package/src/agents/claude-plan.ts +139 -0
  105. package/src/agents/claude.ts +324 -0
  106. package/src/agents/cost.ts +268 -0
  107. package/src/agents/index.ts +13 -0
  108. package/src/agents/registry.ts +48 -0
  109. package/src/agents/types-extended.ts +133 -0
  110. package/src/agents/types.ts +113 -0
  111. package/src/agents/validation.ts +69 -0
  112. package/src/analyze/classifier.ts +305 -0
  113. package/src/analyze/index.ts +16 -0
  114. package/src/analyze/scanner.ts +175 -0
  115. package/src/analyze/types.ts +51 -0
  116. package/src/cli/accept.ts +108 -0
  117. package/src/cli/analyze-parser.ts +284 -0
  118. package/src/cli/analyze.ts +207 -0
  119. package/src/cli/config.ts +561 -0
  120. package/src/cli/constitution.ts +109 -0
  121. package/src/cli/diagnose-analysis.ts +159 -0
  122. package/src/cli/diagnose-formatter.ts +87 -0
  123. package/src/cli/diagnose.ts +203 -0
  124. package/src/cli/generate.ts +127 -0
  125. package/src/cli/index.ts +37 -0
  126. package/src/cli/init.ts +188 -0
  127. package/src/cli/interact.ts +295 -0
  128. package/src/cli/plan.ts +198 -0
  129. package/src/cli/plugins.ts +111 -0
  130. package/src/cli/prompts.ts +295 -0
  131. package/src/cli/runs.ts +174 -0
  132. package/src/cli/status-cost.ts +151 -0
  133. package/src/cli/status-features.ts +338 -0
  134. package/src/cli/status.ts +13 -0
  135. package/src/commands/common.ts +171 -0
  136. package/src/commands/diagnose.ts +17 -0
  137. package/src/commands/index.ts +8 -0
  138. package/src/commands/logs.ts +384 -0
  139. package/src/commands/precheck.ts +86 -0
  140. package/src/commands/unlock.ts +96 -0
  141. package/src/config/defaults.ts +160 -0
  142. package/src/config/index.ts +22 -0
  143. package/src/config/loader.ts +121 -0
  144. package/src/config/merger.ts +147 -0
  145. package/src/config/path-security.ts +121 -0
  146. package/src/config/paths.ts +27 -0
  147. package/src/config/schema.ts +56 -0
  148. package/src/config/schemas.ts +286 -0
  149. package/src/config/types.ts +423 -0
  150. package/src/config/validate.ts +103 -0
  151. package/src/constitution/generator.ts +191 -0
  152. package/src/constitution/generators/aider.ts +41 -0
  153. package/src/constitution/generators/claude.ts +35 -0
  154. package/src/constitution/generators/cursor.ts +36 -0
  155. package/src/constitution/generators/opencode.ts +38 -0
  156. package/src/constitution/generators/types.ts +33 -0
  157. package/src/constitution/generators/windsurf.ts +36 -0
  158. package/src/constitution/index.ts +10 -0
  159. package/src/constitution/loader.ts +133 -0
  160. package/src/constitution/types.ts +31 -0
  161. package/src/context/auto-detect.ts +227 -0
  162. package/src/context/builder.ts +246 -0
  163. package/src/context/elements.ts +83 -0
  164. package/src/context/formatter.ts +107 -0
  165. package/src/context/generator.ts +129 -0
  166. package/src/context/generators/aider.ts +34 -0
  167. package/src/context/generators/claude.ts +28 -0
  168. package/src/context/generators/cursor.ts +28 -0
  169. package/src/context/generators/opencode.ts +30 -0
  170. package/src/context/generators/windsurf.ts +28 -0
  171. package/src/context/greenfield.ts +114 -0
  172. package/src/context/index.ts +33 -0
  173. package/src/context/injector.ts +279 -0
  174. package/src/context/test-scanner.ts +370 -0
  175. package/src/context/types.ts +98 -0
  176. package/src/errors.ts +67 -0
  177. package/src/execution/batching.ts +157 -0
  178. package/src/execution/crash-recovery.ts +373 -0
  179. package/src/execution/escalation/escalation.ts +44 -0
  180. package/src/execution/escalation/index.ts +13 -0
  181. package/src/execution/escalation/tier-escalation.ts +295 -0
  182. package/src/execution/escalation/tier-outcome.ts +158 -0
  183. package/src/execution/helpers.ts +38 -0
  184. package/src/execution/index.ts +45 -0
  185. package/src/execution/lifecycle/acceptance-loop.ts +272 -0
  186. package/src/execution/lifecycle/headless-formatter.ts +85 -0
  187. package/src/execution/lifecycle/index.ts +12 -0
  188. package/src/execution/lifecycle/parallel-lifecycle.ts +101 -0
  189. package/src/execution/lifecycle/precheck-runner.ts +140 -0
  190. package/src/execution/lifecycle/run-cleanup.ts +81 -0
  191. package/src/execution/lifecycle/run-completion.ts +129 -0
  192. package/src/execution/lifecycle/run-initialization.ts +141 -0
  193. package/src/execution/lifecycle/run-lifecycle.ts +312 -0
  194. package/src/execution/lifecycle/run-setup.ts +204 -0
  195. package/src/execution/lifecycle/story-hooks.ts +38 -0
  196. package/src/execution/lifecycle/story-size-prompts.ts +123 -0
  197. package/src/execution/lock.ts +115 -0
  198. package/src/execution/parallel-executor.ts +216 -0
  199. package/src/execution/parallel.ts +400 -0
  200. package/src/execution/pid-registry.ts +280 -0
  201. package/src/execution/pipeline-result-handler.ts +388 -0
  202. package/src/execution/post-verify-rectification.ts +188 -0
  203. package/src/execution/post-verify.ts +274 -0
  204. package/src/execution/progress.ts +25 -0
  205. package/src/execution/prompts.ts +127 -0
  206. package/src/execution/queue-handler.ts +109 -0
  207. package/src/execution/rectification.ts +13 -0
  208. package/src/execution/runner.ts +377 -0
  209. package/src/execution/sequential-executor.ts +388 -0
  210. package/src/execution/status-file.ts +264 -0
  211. package/src/execution/status-writer.ts +139 -0
  212. package/src/execution/story-context.ts +229 -0
  213. package/src/execution/test-output-parser.ts +14 -0
  214. package/src/execution/verification.ts +72 -0
  215. package/src/hooks/index.ts +2 -0
  216. package/src/hooks/runner.ts +286 -0
  217. package/src/hooks/types.ts +67 -0
  218. package/src/interaction/chain.ts +154 -0
  219. package/src/interaction/index.ts +60 -0
  220. package/src/interaction/init.ts +83 -0
  221. package/src/interaction/plugins/auto.ts +217 -0
  222. package/src/interaction/plugins/cli.ts +300 -0
  223. package/src/interaction/plugins/telegram.ts +384 -0
  224. package/src/interaction/plugins/webhook.ts +258 -0
  225. package/src/interaction/state.ts +171 -0
  226. package/src/interaction/triggers.ts +229 -0
  227. package/src/interaction/types.ts +163 -0
  228. package/src/logger/formatters.ts +84 -0
  229. package/src/logger/index.ts +16 -0
  230. package/src/logger/logger.ts +298 -0
  231. package/src/logger/types.ts +48 -0
  232. package/src/logging/formatter.ts +355 -0
  233. package/src/logging/index.ts +22 -0
  234. package/src/logging/types.ts +93 -0
  235. package/src/metrics/aggregator.ts +190 -0
  236. package/src/metrics/index.ts +14 -0
  237. package/src/metrics/tracker.ts +200 -0
  238. package/src/metrics/types.ts +109 -0
  239. package/src/optimizer/index.ts +62 -0
  240. package/src/optimizer/noop.optimizer.ts +24 -0
  241. package/src/optimizer/rule-based.optimizer.ts +248 -0
  242. package/src/optimizer/types.ts +53 -0
  243. package/src/pipeline/events.ts +130 -0
  244. package/src/pipeline/index.ts +19 -0
  245. package/src/pipeline/runner.ts +161 -0
  246. package/src/pipeline/stages/acceptance.ts +197 -0
  247. package/src/pipeline/stages/completion.ts +99 -0
  248. package/src/pipeline/stages/constitution.ts +63 -0
  249. package/src/pipeline/stages/context.ts +117 -0
  250. package/src/pipeline/stages/execution.ts +194 -0
  251. package/src/pipeline/stages/index.ts +62 -0
  252. package/src/pipeline/stages/optimizer.ts +74 -0
  253. package/src/pipeline/stages/prompt.ts +57 -0
  254. package/src/pipeline/stages/queue-check.ts +103 -0
  255. package/src/pipeline/stages/review.ts +181 -0
  256. package/src/pipeline/stages/routing.ts +81 -0
  257. package/src/pipeline/stages/verify.ts +100 -0
  258. package/src/pipeline/types.ts +167 -0
  259. package/src/plugins/index.ts +31 -0
  260. package/src/plugins/loader.ts +287 -0
  261. package/src/plugins/registry.ts +168 -0
  262. package/src/plugins/types.ts +327 -0
  263. package/src/plugins/validator.ts +352 -0
  264. package/src/prd/index.ts +172 -0
  265. package/src/prd/types.ts +202 -0
  266. package/src/precheck/checks-blockers.ts +391 -0
  267. package/src/precheck/checks-warnings.ts +142 -0
  268. package/src/precheck/checks.ts +30 -0
  269. package/src/precheck/index.ts +247 -0
  270. package/src/precheck/story-size-gate.ts +144 -0
  271. package/src/precheck/types.ts +31 -0
  272. package/src/queue/index.ts +2 -0
  273. package/src/queue/manager.ts +254 -0
  274. package/src/queue/types.ts +54 -0
  275. package/src/review/index.ts +8 -0
  276. package/src/review/runner.ts +172 -0
  277. package/src/review/types.ts +66 -0
  278. package/src/routing/builder.ts +81 -0
  279. package/src/routing/chain.ts +74 -0
  280. package/src/routing/index.ts +16 -0
  281. package/src/routing/loader.ts +58 -0
  282. package/src/routing/router.ts +303 -0
  283. package/src/routing/strategies/adaptive.ts +215 -0
  284. package/src/routing/strategies/index.ts +8 -0
  285. package/src/routing/strategies/keyword.ts +163 -0
  286. package/src/routing/strategies/llm-prompts.ts +209 -0
  287. package/src/routing/strategies/llm.ts +235 -0
  288. package/src/routing/strategies/manual.ts +50 -0
  289. package/src/routing/strategy.ts +99 -0
  290. package/src/tdd/cleanup.ts +111 -0
  291. package/src/tdd/index.ts +23 -0
  292. package/src/tdd/isolation.ts +123 -0
  293. package/src/tdd/orchestrator.ts +383 -0
  294. package/src/tdd/prompts.ts +270 -0
  295. package/src/tdd/rectification-gate.ts +183 -0
  296. package/src/tdd/session-runner.ts +179 -0
  297. package/src/tdd/types.ts +81 -0
  298. package/src/tdd/verdict.ts +271 -0
  299. package/src/tui/App.tsx +265 -0
  300. package/src/tui/components/AgentPanel.tsx +75 -0
  301. package/src/tui/components/CostOverlay.tsx +118 -0
  302. package/src/tui/components/HelpOverlay.tsx +107 -0
  303. package/src/tui/components/StatusBar.tsx +63 -0
  304. package/src/tui/components/StoriesPanel.tsx +177 -0
  305. package/src/tui/hooks/useKeyboard.ts +142 -0
  306. package/src/tui/hooks/useLayout.ts +137 -0
  307. package/src/tui/hooks/usePipelineEvents.ts +183 -0
  308. package/src/tui/hooks/usePty.ts +194 -0
  309. package/src/tui/index.tsx +38 -0
  310. package/src/tui/types.ts +76 -0
  311. package/src/utils/git.ts +83 -0
  312. package/src/utils/queue-writer.ts +54 -0
  313. package/src/verification/executor.ts +235 -0
  314. package/src/verification/gate.ts +207 -0
  315. package/src/verification/index.ts +12 -0
  316. package/src/verification/parser.ts +230 -0
  317. package/src/verification/rectification.ts +108 -0
  318. package/src/verification/types.ts +113 -0
  319. package/src/worktree/dispatcher.ts +65 -0
  320. package/src/worktree/index.ts +2 -0
  321. package/src/worktree/manager.ts +187 -0
  322. package/src/worktree/merge.ts +301 -0
  323. package/src/worktree/types.ts +4 -0
  324. package/test/TEST_COVERAGE_US001.md +217 -0
  325. package/test/TEST_COVERAGE_US003.md +84 -0
  326. package/test/TEST_COVERAGE_US005.md +86 -0
  327. package/test/US-002-orchestrator.test.ts +246 -0
  328. package/test/acceptance/cm-003-default-view.test.ts +194 -0
  329. package/test/execution/pid-registry.test.ts +240 -0
  330. package/test/execution/post-verify.test.ts +224 -0
  331. package/test/helpers/timeout.ts +42 -0
  332. package/test/integration/US-002-TEST-SUMMARY.md +107 -0
  333. package/test/integration/US-003-TEST-SUMMARY.md +149 -0
  334. package/test/integration/US-004-TEST-SUMMARY.md +106 -0
  335. package/test/integration/US-005-TEST-SUMMARY.md +138 -0
  336. package/test/integration/US-007-TEST-SUMMARY.md +100 -0
  337. package/test/integration/agent-validation.test.ts +439 -0
  338. package/test/integration/analyze-integration.test.ts +261 -0
  339. package/test/integration/analyze-scanner.test.ts +131 -0
  340. package/test/integration/cli-config-default-edge-cases.test.ts +222 -0
  341. package/test/integration/cli-config-default-view.test.ts +229 -0
  342. package/test/integration/cli-config-diff.test.ts +460 -0
  343. package/test/integration/cli-config.test.ts +736 -0
  344. package/test/integration/cli-diagnose.test.ts +592 -0
  345. package/test/integration/cli-logs.test.ts +314 -0
  346. package/test/integration/cli-plugins.test.ts +678 -0
  347. package/test/integration/cli-precheck.test.ts +371 -0
  348. package/test/integration/cli-run-headless.test.ts +173 -0
  349. package/test/integration/cli.test.ts +75 -0
  350. package/test/integration/config/merger.test.ts +465 -0
  351. package/test/integration/config/paths.test.ts +51 -0
  352. package/test/integration/config-loader.test.ts +265 -0
  353. package/test/integration/config.test.ts +444 -0
  354. package/test/integration/context-integration.test.ts +702 -0
  355. package/test/integration/context-provider-injection.test.ts +506 -0
  356. package/test/integration/context-verification-integration.test.ts +295 -0
  357. package/test/integration/e2e.test.ts +896 -0
  358. package/test/integration/execution.test.ts +625 -0
  359. package/test/integration/helpers.test.ts +295 -0
  360. package/test/integration/hooks.test.ts +361 -0
  361. package/test/integration/interaction-chain-pipeline.test.ts +464 -0
  362. package/test/integration/isolation.test.ts +143 -0
  363. package/test/integration/logger.test.ts +461 -0
  364. package/test/integration/parallel.test.ts +250 -0
  365. package/test/integration/path-security.test.ts +173 -0
  366. package/test/integration/pipeline-acceptance.test.ts +302 -0
  367. package/test/integration/pipeline-events.test.ts +475 -0
  368. package/test/integration/pipeline.test.ts +658 -0
  369. package/test/integration/plan.test.ts +157 -0
  370. package/test/integration/plugin-routing.test.ts +921 -0
  371. package/test/integration/plugins/config-integration.test.ts +172 -0
  372. package/test/integration/plugins/config-resolution.test.ts +522 -0
  373. package/test/integration/plugins/loader.test.ts +641 -0
  374. package/test/integration/plugins/registry.test.ts +746 -0
  375. package/test/integration/plugins/validator.test.ts +563 -0
  376. package/test/integration/prd-pause.test.ts +205 -0
  377. package/test/integration/prd-resolvers.test.ts +185 -0
  378. package/test/integration/precheck-integration.test.ts +468 -0
  379. package/test/integration/precheck.test.ts +805 -0
  380. package/test/integration/progress.test.ts +34 -0
  381. package/test/integration/rectification-flow.test.ts +512 -0
  382. package/test/integration/reporter-lifecycle.test.ts +860 -0
  383. package/test/integration/review-config-commands.test.ts +319 -0
  384. package/test/integration/review-config-schema.test.ts +116 -0
  385. package/test/integration/review-plugin-integration.test.ts +722 -0
  386. package/test/integration/review.test.ts +149 -0
  387. package/test/integration/routing-stage-bug-021.test.ts +274 -0
  388. package/test/integration/routing-stage-greenfield.test.ts +286 -0
  389. package/test/integration/runner-config-plugins.test.ts +461 -0
  390. package/test/integration/runner-fixes.test.ts +399 -0
  391. package/test/integration/runner-plugin-integration.test.ts +543 -0
  392. package/test/integration/runner.test.ts +1679 -0
  393. package/test/integration/s5-greenfield-fallback.test.ts +297 -0
  394. package/test/integration/status-file-integration.test.ts +325 -0
  395. package/test/integration/status-file.test.ts +379 -0
  396. package/test/integration/status-writer.test.ts +345 -0
  397. package/test/integration/story-id-in-events.test.ts +273 -0
  398. package/test/integration/tdd-cleanup.test.ts +246 -0
  399. package/test/integration/tdd-orchestrator.test.ts +1762 -0
  400. package/test/integration/test-scanner.test.ts +403 -0
  401. package/test/integration/verification-asset-check.test.ts +142 -0
  402. package/test/integration/verify-stage.test.ts +275 -0
  403. package/test/integration/worktree/manager.test.ts +218 -0
  404. package/test/integration/worktree/merge.test.ts +341 -0
  405. package/test/manual/logging-formatter-demo.ts +158 -0
  406. package/test/ui/tui-agent-panel.test.tsx +99 -0
  407. package/test/ui/tui-controls.test.ts +334 -0
  408. package/test/ui/tui-cost-and-pty.test.ts +189 -0
  409. package/test/ui/tui-layout.test.ts +378 -0
  410. package/test/ui/tui-pty-integration.test.tsx +159 -0
  411. package/test/ui/tui-stories.test.ts +332 -0
  412. package/test/unit/acceptance.test.ts +186 -0
  413. package/test/unit/agent-stderr-capture.test.ts +146 -0
  414. package/test/unit/analyze-classifier.test.ts +215 -0
  415. package/test/unit/analyze.test.ts +224 -0
  416. package/test/unit/auto-detect.test.ts +249 -0
  417. package/test/unit/cli-status.test.ts +417 -0
  418. package/test/unit/commands/common.test.ts +320 -0
  419. package/test/unit/commands/logs.test.ts +416 -0
  420. package/test/unit/commands/unlock.test.ts +319 -0
  421. package/test/unit/constitution-generators.test.ts +160 -0
  422. package/test/unit/constitution.test.ts +209 -0
  423. package/test/unit/context.test.ts +1722 -0
  424. package/test/unit/cost.test.ts +231 -0
  425. package/test/unit/crash-recovery.test.ts +308 -0
  426. package/test/unit/escalation.test.ts +126 -0
  427. package/test/unit/execution-logging-stderr.test.ts +156 -0
  428. package/test/unit/execution-stage.test.ts +122 -0
  429. package/test/unit/fix-generator.test.ts +275 -0
  430. package/test/unit/formatters.test.ts +469 -0
  431. package/test/unit/greenfield.test.ts +179 -0
  432. package/test/unit/helpers.test.ts +317 -0
  433. package/test/unit/interaction/human-review-trigger.test.ts +164 -0
  434. package/test/unit/interaction-network-failures.test.ts +389 -0
  435. package/test/unit/interaction-plugins.test.ts +164 -0
  436. package/test/unit/isolation.test.ts +134 -0
  437. package/test/unit/logging/formatter.test.ts +455 -0
  438. package/test/unit/merge.test.ts +268 -0
  439. package/test/unit/metrics.test.ts +276 -0
  440. package/test/unit/optimizer/noop.optimizer.test.ts +125 -0
  441. package/test/unit/optimizer/rule-based.optimizer.test.ts +358 -0
  442. package/test/unit/prd-auto-default.test.ts +290 -0
  443. package/test/unit/prd-failure-category.test.ts +176 -0
  444. package/test/unit/prd-get-next-story.test.ts +186 -0
  445. package/test/unit/precheck-checks.test.ts +840 -0
  446. package/test/unit/precheck-story-size-gate.test.ts +287 -0
  447. package/test/unit/precheck-types.test.ts +142 -0
  448. package/test/unit/prompts.test.ts +475 -0
  449. package/test/unit/queue.test.ts +237 -0
  450. package/test/unit/rectification.test.ts +284 -0
  451. package/test/unit/registry.test.ts +287 -0
  452. package/test/unit/routing.test.ts +937 -0
  453. package/test/unit/run-lifecycle.test.ts +140 -0
  454. package/test/unit/storyid-events.test.ts +224 -0
  455. package/test/unit/tdd-verdict.test.ts +492 -0
  456. package/test/unit/test-output-parser.test.ts +377 -0
  457. package/test/unit/verdict.test.ts +324 -0
  458. package/test/unit/worktree-manager.test.ts +158 -0
  459. package/tsconfig.json +27 -0
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Codebase Scanner
3
+ *
4
+ * Scans the project directory to generate a summary for LLM classification.
5
+ */
6
+
7
+ import { existsSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import type { CodebaseScan } from "./types";
10
+
11
+ /**
12
+ * Scan codebase to generate summary for LLM classification.
13
+ *
14
+ * Generates:
15
+ * - File tree (src/ directory, max depth 3)
16
+ * - Package.json dependencies
17
+ * - Test pattern detection
18
+ *
19
+ * @param workdir - Project root directory
20
+ * @returns Codebase scan result
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const scan = await scanCodebase("/path/to/project");
25
+ * console.log(scan.fileTree);
26
+ * console.log(scan.dependencies);
27
+ * ```
28
+ */
29
+ export async function scanCodebase(workdir: string): Promise<CodebaseScan> {
30
+ const srcPath = join(workdir, "src");
31
+ const packageJsonPath = join(workdir, "package.json");
32
+
33
+ // Generate file tree (src/ only, max depth 3)
34
+ const fileTree = existsSync(srcPath) ? await generateFileTree(srcPath, 3) : "No src/ directory";
35
+
36
+ // Extract dependencies from package.json
37
+ let dependencies: Record<string, string> = {};
38
+ let devDependencies: Record<string, string> = {};
39
+
40
+ if (existsSync(packageJsonPath)) {
41
+ try {
42
+ const pkg = await Bun.file(packageJsonPath).json();
43
+ dependencies = pkg.dependencies || {};
44
+ devDependencies = pkg.devDependencies || {};
45
+ } catch {
46
+ // Invalid package.json, use empty deps
47
+ }
48
+ }
49
+
50
+ // Detect test patterns
51
+ const testPatterns = detectTestPatterns(workdir, dependencies, devDependencies);
52
+
53
+ return {
54
+ fileTree,
55
+ dependencies,
56
+ devDependencies,
57
+ testPatterns,
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Generate file tree for a directory with depth limit.
63
+ *
64
+ * @param dir - Directory path
65
+ * @param maxDepth - Maximum depth to traverse
66
+ * @param currentDepth - Current depth (internal)
67
+ * @param prefix - Line prefix for formatting (internal)
68
+ * @returns Formatted file tree string
69
+ */
70
+ async function generateFileTree(dir: string, maxDepth: number, currentDepth = 0, prefix = ""): Promise<string> {
71
+ if (currentDepth >= maxDepth) {
72
+ return "";
73
+ }
74
+
75
+ const entries: string[] = [];
76
+
77
+ try {
78
+ const dirEntries = Array.from(
79
+ new Bun.Glob("*").scanSync({
80
+ cwd: dir,
81
+ onlyFiles: false,
82
+ }),
83
+ );
84
+
85
+ // Sort: directories first, then files
86
+ dirEntries.sort((a, b) => {
87
+ const aIsDir = !a.includes(".");
88
+ const bIsDir = !b.includes(".");
89
+ if (aIsDir && !bIsDir) return -1;
90
+ if (!aIsDir && bIsDir) return 1;
91
+ return a.localeCompare(b);
92
+ });
93
+
94
+ for (let i = 0; i < dirEntries.length; i++) {
95
+ const entry = dirEntries[i];
96
+ const fullPath = join(dir, entry);
97
+ const isLast = i === dirEntries.length - 1;
98
+ const connector = isLast ? "└── " : "├── ";
99
+ const childPrefix = isLast ? " " : "│ ";
100
+
101
+ // Check if directory
102
+ const stat = await Bun.file(fullPath).stat();
103
+ const isDir = stat.isDirectory();
104
+
105
+ entries.push(`${prefix}${connector}${entry}${isDir ? "/" : ""}`);
106
+
107
+ // Recurse into directories
108
+ if (isDir) {
109
+ const subtree = await generateFileTree(fullPath, maxDepth, currentDepth + 1, prefix + childPrefix);
110
+ if (subtree) {
111
+ entries.push(subtree);
112
+ }
113
+ }
114
+ }
115
+ } catch {
116
+ // Directory not accessible, skip
117
+ }
118
+
119
+ return entries.join("\n");
120
+ }
121
+
122
+ /**
123
+ * Detect test patterns from directory structure and dependencies.
124
+ *
125
+ * Checks for:
126
+ * - Test framework (vitest, jest, bun:test, mocha, etc.)
127
+ * - Test directory structure (test/, __tests__/)
128
+ * - Test file patterns (*.test.ts, *.spec.ts)
129
+ *
130
+ * @param workdir - Project root directory
131
+ * @param dependencies - Production dependencies
132
+ * @param devDependencies - Dev dependencies
133
+ * @returns Array of detected patterns
134
+ */
135
+ function detectTestPatterns(
136
+ workdir: string,
137
+ dependencies: Record<string, string>,
138
+ devDependencies: Record<string, string>,
139
+ ): string[] {
140
+ const patterns: string[] = [];
141
+ const allDeps = { ...dependencies, ...devDependencies };
142
+
143
+ // Detect test framework
144
+ if (allDeps.vitest) {
145
+ patterns.push("Test framework: vitest");
146
+ } else if (allDeps.jest || allDeps["@jest/globals"]) {
147
+ patterns.push("Test framework: jest");
148
+ } else if (allDeps.mocha) {
149
+ patterns.push("Test framework: mocha");
150
+ } else if (allDeps.ava) {
151
+ patterns.push("Test framework: ava");
152
+ } else {
153
+ // Check for bun:test (no package.json entry)
154
+ patterns.push("Test framework: likely bun:test (no framework dependency)");
155
+ }
156
+
157
+ // Detect test directory
158
+ if (existsSync(join(workdir, "test"))) {
159
+ patterns.push("Test directory: test/");
160
+ }
161
+ if (existsSync(join(workdir, "__tests__"))) {
162
+ patterns.push("Test directory: __tests__/");
163
+ }
164
+ if (existsSync(join(workdir, "tests"))) {
165
+ patterns.push("Test directory: tests/");
166
+ }
167
+
168
+ // Detect test file patterns
169
+ const hasTestFiles = existsSync(join(workdir, "test")) || existsSync(join(workdir, "src"));
170
+ if (hasTestFiles) {
171
+ patterns.push("Test files: *.test.ts, *.spec.ts");
172
+ }
173
+
174
+ return patterns;
175
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Analyze Module Types
3
+ *
4
+ * Types for codebase scanning and LLM-enhanced classification.
5
+ */
6
+
7
+ import type { Complexity } from "../config";
8
+
9
+ /** Codebase scan result */
10
+ export interface CodebaseScan {
11
+ /** File tree (src/ directory, max depth 3) */
12
+ fileTree: string;
13
+ /** Package dependencies */
14
+ dependencies: Record<string, string>;
15
+ /** Dev dependencies */
16
+ devDependencies: Record<string, string>;
17
+ /** Detected test patterns */
18
+ testPatterns: string[];
19
+ }
20
+
21
+ /** LLM classification result for a single story */
22
+ export interface StoryClassification {
23
+ /** Story ID (e.g., "US-001") */
24
+ storyId: string;
25
+ /** Classified complexity */
26
+ complexity: Complexity;
27
+ /** Context files to inject into agent prompt before execution */
28
+ contextFiles: string[];
29
+ /** Reasoning for the classification */
30
+ reasoning: string;
31
+ /** Estimated lines of code to change */
32
+ estimatedLOC: number;
33
+ /** Potential implementation risks */
34
+ risks: string[];
35
+ }
36
+
37
+ /** LLM classifier response (array of classifications) */
38
+ export type ClassifierResponse = StoryClassification[];
39
+
40
+ /** Classification method used */
41
+ export type ClassificationMethod = "llm" | "keyword-fallback";
42
+
43
+ /** Classification result with metadata */
44
+ export interface ClassificationResult {
45
+ /** Classification data */
46
+ classifications: StoryClassification[];
47
+ /** Method used (llm or keyword-fallback) */
48
+ method: ClassificationMethod;
49
+ /** Error message if LLM failed and fallback was used */
50
+ fallbackReason?: string;
51
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Accept Command
3
+ *
4
+ * Allows manual override of failed acceptance criteria.
5
+ * Stores override in prd.json with reason.
6
+ *
7
+ * Usage:
8
+ * nax accept --override AC-2 "intentional: using lazy expiry instead of exact timing"
9
+ */
10
+
11
+ import path from "node:path";
12
+ import { findProjectDir, validateDirectory } from "../config";
13
+ import { NaxError } from "../errors";
14
+ import { getLogger } from "../logger";
15
+ import { loadPRD, savePRD } from "../prd";
16
+
17
+ /**
18
+ * Accept command options.
19
+ */
20
+ export interface AcceptOptions {
21
+ /** Feature name */
22
+ feature: string;
23
+ /** AC ID to override (e.g., "AC-2") */
24
+ override: string;
25
+ /** Reason for accepting despite test failure */
26
+ reason: string;
27
+ }
28
+
29
+ /**
30
+ * Execute the accept command.
31
+ *
32
+ * Loads the PRD, adds the AC override with reason, and saves.
33
+ *
34
+ * @param options - Command options
35
+ *
36
+ * @example
37
+ * ```bash
38
+ * nax accept --feature auth-system --override AC-2 "intentional: lazy expiry"
39
+ * ```
40
+ */
41
+ export async function acceptCommand(options: AcceptOptions): Promise<void> {
42
+ const logger = getLogger();
43
+ const { feature, override, reason } = options;
44
+
45
+ // Validate AC ID format
46
+ if (!override.match(/^AC-\d+$/i)) {
47
+ logger.error("cli", "Invalid AC ID format", { override, expected: "AC-1, AC-2, etc." });
48
+ throw new NaxError("Invalid AC ID format", "INVALID_AC_ID", { override, expected: "AC-1, AC-2, etc." });
49
+ }
50
+
51
+ // Normalize AC ID to uppercase
52
+ const acId = override.toUpperCase();
53
+
54
+ // Find project directory
55
+ const projectDirResult = findProjectDir(process.cwd());
56
+ if (!projectDirResult) {
57
+ logger.error("cli", "Not in a nax project directory", { hint: "Run 'nax init' first" });
58
+ throw new NaxError("Not in a nax project directory", "PROJECT_NOT_FOUND", { hint: "Run 'nax init' first" });
59
+ }
60
+ const projectDir = projectDirResult;
61
+
62
+ // Validate directory
63
+ try {
64
+ validateDirectory(projectDir);
65
+ } catch (err) {
66
+ logger.error("cli", "Invalid project directory", { error: (err as Error).message });
67
+ throw new NaxError("Invalid project directory", "INVALID_DIRECTORY", { error: (err as Error).message });
68
+ }
69
+
70
+ // Build path to feature PRD
71
+ const featureDir = path.join(projectDir, "nax", "features", feature);
72
+ const prdPath = path.join(featureDir, "prd.json");
73
+
74
+ // Check if feature exists
75
+ const prdFile = Bun.file(prdPath);
76
+ if (!(await prdFile.exists())) {
77
+ logger.error("cli", "Feature not found", { feature, prdPath });
78
+ throw new NaxError("Feature not found", "FEATURE_NOT_FOUND", { feature, prdPath });
79
+ }
80
+
81
+ // Load PRD
82
+ const prd = await loadPRD(prdPath);
83
+
84
+ // Add override
85
+ if (!prd.acceptanceOverrides) {
86
+ prd.acceptanceOverrides = {};
87
+ }
88
+
89
+ if (prd.acceptanceOverrides[acId]) {
90
+ logger.warn("cli", "Override already exists", {
91
+ acId,
92
+ previous: prd.acceptanceOverrides[acId],
93
+ new: reason,
94
+ });
95
+ }
96
+
97
+ prd.acceptanceOverrides[acId] = reason;
98
+
99
+ // Save PRD
100
+ await savePRD(prd, prdPath);
101
+
102
+ logger.info("cli", "✓ Override added", {
103
+ acId,
104
+ reason,
105
+ prdPath,
106
+ hint: `Re-run acceptance tests: bun test ${path.join(featureDir, "acceptance.test.ts")}`,
107
+ });
108
+ }
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Analyze Parser & Classification
3
+ *
4
+ * Extracted from analyze.ts: spec parsing, keyword classification,
5
+ * codebase context building, and reclassification logic.
6
+ */
7
+
8
+ import { existsSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { getAgent } from "../agents/registry";
11
+ import { scanCodebase } from "../analyze/scanner";
12
+ import type { CodebaseScan } from "../analyze/types";
13
+ import type { NaxConfig } from "../config";
14
+ import { resolveModel } from "../config/schema";
15
+ import { getLogger } from "../logger";
16
+ import type { PRD, UserStory } from "../prd";
17
+ import { loadPRD } from "../prd";
18
+ import { classifyComplexity, routeTask } from "../routing";
19
+
20
+ /** Parse user stories from spec.md markdown */
21
+ export function parseUserStoriesFromSpec(markdown: string): UserStory[] {
22
+ const stories: UserStory[] = [];
23
+ const lines = markdown.split("\n");
24
+
25
+ let currentStory: Partial<UserStory> | null = null;
26
+ let currentSection: "title" | "description" | "criteria" | null = null;
27
+ let descriptionLines: string[] = [];
28
+ let criteriaLines: string[] = [];
29
+
30
+ for (let i = 0; i < lines.length; i++) {
31
+ const line = lines[i];
32
+
33
+ const storyMatch = line.match(/^##\s+(US-\d+|Story)(?::\s*(.+))?/);
34
+ if (storyMatch) {
35
+ if (currentStory) {
36
+ stories.push(finalizeStory(currentStory, descriptionLines, criteriaLines));
37
+ }
38
+
39
+ const id = storyMatch[1] === "Story" ? `US-${String(stories.length + 1).padStart(3, "0")}` : storyMatch[1];
40
+ currentStory = {
41
+ id,
42
+ title: storyMatch[2]?.trim() || "[Untitled]",
43
+ description: "",
44
+ acceptanceCriteria: [],
45
+ tags: [],
46
+ dependencies: [],
47
+ status: "pending",
48
+ passes: false,
49
+ escalations: [],
50
+ attempts: 0,
51
+ };
52
+ descriptionLines = [];
53
+ criteriaLines = [];
54
+ currentSection = "title";
55
+ continue;
56
+ }
57
+
58
+ if (!currentStory) continue;
59
+
60
+ if (line.match(/^###\s+Description/i)) {
61
+ currentSection = "description";
62
+ continue;
63
+ }
64
+ if (line.match(/^###\s+Acceptance\s+Criteria/i)) {
65
+ currentSection = "criteria";
66
+ continue;
67
+ }
68
+
69
+ if (currentSection === "description" && line.trim()) {
70
+ descriptionLines.push(line.trim());
71
+ }
72
+ if (currentSection === "criteria" && line.trim()) {
73
+ const criterionMatch = line.match(/^-\s+\[.\]\s+(.+)/);
74
+ if (criterionMatch) criteriaLines.push(criterionMatch[1].trim());
75
+ }
76
+
77
+ const tagsMatch = line.match(/^Tags:\s*(.+)/i);
78
+ if (tagsMatch && currentStory) {
79
+ currentStory.tags = tagsMatch[1]
80
+ .split(",")
81
+ .map((t) => t.trim())
82
+ .filter(Boolean);
83
+ }
84
+
85
+ const depsMatch = line.match(/^Dependencies:\s*(.+)/i);
86
+ if (depsMatch && currentStory) {
87
+ currentStory.dependencies = depsMatch[1]
88
+ .split(",")
89
+ .map((d) => d.trim())
90
+ .filter(Boolean);
91
+ }
92
+ }
93
+
94
+ if (currentStory) {
95
+ stories.push(finalizeStory(currentStory, descriptionLines, criteriaLines));
96
+ }
97
+
98
+ return stories;
99
+ }
100
+
101
+ /** Finalize a story by combining collected data */
102
+ function finalizeStory(story: Partial<UserStory>, descriptionLines: string[], criteriaLines: string[]): UserStory {
103
+ return {
104
+ id: story.id || "US-000",
105
+ title: story.title || "[Untitled]",
106
+ description: descriptionLines.join(" ").trim() || story.title || "",
107
+ acceptanceCriteria: criteriaLines.length > 0 ? criteriaLines : ["Implementation complete"],
108
+ tags: story.tags || [],
109
+ dependencies: story.dependencies || [],
110
+ status: "pending",
111
+ passes: false,
112
+ escalations: [],
113
+ attempts: 0,
114
+ };
115
+ }
116
+
117
+ /** Build codebase context string from scan result. */
118
+ export function buildCodebaseContext(scan: CodebaseScan): string {
119
+ return `FILE TREE:
120
+ ${scan.fileTree}
121
+
122
+ DEPENDENCIES:
123
+ ${Object.entries(scan.dependencies)
124
+ .map(([name, version]) => `- ${name}: ${version}`)
125
+ .join("\n")}
126
+
127
+ DEV DEPENDENCIES:
128
+ ${Object.entries(scan.devDependencies)
129
+ .map(([name, version]) => `- ${name}: ${version}`)
130
+ .join("\n")}
131
+
132
+ TEST PATTERNS:
133
+ ${scan.testPatterns.map((p) => `- ${p}`).join("\n")}`.trim();
134
+ }
135
+
136
+ /** Apply keyword-based classification to user stories. */
137
+ export function applyKeywordClassification(stories: UserStory[], config?: NaxConfig): UserStory[] {
138
+ return stories.map((story) => {
139
+ const complexity = classifyComplexity(story.title, story.description, story.acceptanceCriteria, story.tags);
140
+ const routing = config
141
+ ? routeTask(story.title, story.description, story.acceptanceCriteria, story.tags, config)
142
+ : { complexity, testStrategy: "test-after" as const, reasoning: "No config provided" };
143
+
144
+ return {
145
+ ...story,
146
+ routing: {
147
+ complexity,
148
+ testStrategy: routing.testStrategy,
149
+ reasoning: `Keyword-based classification: ${complexity}`,
150
+ estimatedLOC: estimateLOCFromComplexity(complexity),
151
+ risks: [],
152
+ strategy: "keyword" as const,
153
+ },
154
+ };
155
+ });
156
+ }
157
+
158
+ /** Estimate LOC from complexity level (rough heuristic). */
159
+ export function estimateLOCFromComplexity(complexity: "simple" | "medium" | "complex" | "expert"): number {
160
+ switch (complexity) {
161
+ case "simple":
162
+ return 50;
163
+ case "medium":
164
+ return 150;
165
+ case "complex":
166
+ return 400;
167
+ case "expert":
168
+ return 800;
169
+ }
170
+ }
171
+
172
+ /** Re-classify existing prd.json without decomposing. */
173
+ export async function reclassifyExistingPRD(
174
+ featureDir: string,
175
+ featureName: string,
176
+ branchName: string,
177
+ workdir: string,
178
+ config?: NaxConfig,
179
+ ): Promise<PRD> {
180
+ const prdPath = join(featureDir, "prd.json");
181
+ if (!existsSync(prdPath)) {
182
+ throw new Error(`prd.json not found at ${prdPath}. Run analyze without --reclassify first.`);
183
+ }
184
+
185
+ const prd = await loadPRD(prdPath);
186
+ const logger = getLogger();
187
+ logger.info("cli", "Re-classifying existing stories");
188
+
189
+ const scan = await scanCodebase(workdir);
190
+ const codebaseContext = buildCodebaseContext(scan);
191
+ const updatedStories: UserStory[] = [];
192
+
193
+ for (const story of prd.userStories) {
194
+ const storySpec = `## ${story.id}: ${story.title}\n\n${story.description}\n\n### Acceptance Criteria\n${story.acceptanceCriteria.map((c) => `- ${c}`).join("\n")}`;
195
+
196
+ try {
197
+ if (config?.analyze.llmEnhanced) {
198
+ const classified = await reclassifyWithLLM(story, storySpec, workdir, codebaseContext, config);
199
+ updatedStories.push(classified);
200
+ logger.info("cli", `[OK] ${story.id} reclassified`, {
201
+ storyId: story.id,
202
+ complexity: classified.routing?.complexity,
203
+ });
204
+ } else {
205
+ const classified = reclassifyWithKeywords(story, config);
206
+ updatedStories.push(classified);
207
+ logger.info("cli", `[OK] ${story.id} reclassified`, {
208
+ storyId: story.id,
209
+ complexity: classified.routing?.complexity,
210
+ });
211
+ }
212
+ } catch (error) {
213
+ logger.warn("cli", `[WARN] ${story.id} kept original`, { storyId: story.id, error: (error as Error).message });
214
+ updatedStories.push(story);
215
+ }
216
+ }
217
+
218
+ return { ...prd, updatedAt: new Date().toISOString(), userStories: updatedStories };
219
+ }
220
+
221
+ /** Reclassify a single story via LLM agent. */
222
+ async function reclassifyWithLLM(
223
+ story: UserStory,
224
+ storySpec: string,
225
+ workdir: string,
226
+ codebaseContext: string,
227
+ config: NaxConfig,
228
+ ): Promise<UserStory> {
229
+ const agentName = config.autoMode.defaultAgent;
230
+ const adapter = getAgent(agentName);
231
+ if (!adapter) throw new Error(`Agent "${agentName}" not found`);
232
+
233
+ const modelTier = config.analyze.model;
234
+ const modelDef = resolveModel(config.models[modelTier]);
235
+
236
+ const result = await adapter.decompose({ specContent: storySpec, workdir, codebaseContext, modelTier, modelDef });
237
+
238
+ if (result.stories.length === 0) return story;
239
+ const ds = result.stories[0];
240
+
241
+ let testStrategy: import("../config").TestStrategy;
242
+ let routingStrategy: "llm" | "keyword";
243
+ if (ds.testStrategy) {
244
+ testStrategy = ds.testStrategy;
245
+ routingStrategy = "llm";
246
+ } else {
247
+ const routing = routeTask(story.title, story.description, story.acceptanceCriteria, story.tags, config);
248
+ testStrategy = routing.testStrategy;
249
+ routingStrategy = "keyword";
250
+ }
251
+
252
+ return {
253
+ ...story,
254
+ routing: {
255
+ complexity: ds.complexity,
256
+ testStrategy,
257
+ reasoning: ds.reasoning,
258
+ estimatedLOC: ds.estimatedLOC,
259
+ risks: ds.risks,
260
+ strategy: routingStrategy,
261
+ llmModel: routingStrategy === "llm" ? modelDef.model : undefined,
262
+ },
263
+ contextFiles: ds.contextFiles,
264
+ };
265
+ }
266
+
267
+ /** Reclassify a single story via keyword heuristics. */
268
+ function reclassifyWithKeywords(story: UserStory, config?: NaxConfig): UserStory {
269
+ const complexity = classifyComplexity(story.title, story.description, story.acceptanceCriteria, story.tags);
270
+ const routing = config
271
+ ? routeTask(story.title, story.description, story.acceptanceCriteria, story.tags, config)
272
+ : { complexity, testStrategy: "test-after" as const, reasoning: "No config provided" };
273
+
274
+ return {
275
+ ...story,
276
+ routing: {
277
+ complexity,
278
+ testStrategy: routing.testStrategy,
279
+ reasoning: `Keyword-based classification: ${complexity}`,
280
+ estimatedLOC: estimateLOCFromComplexity(complexity),
281
+ risks: [],
282
+ },
283
+ };
284
+ }