@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,391 @@
1
+ /**
2
+ * Precheck Tier 1 Blockers
3
+ *
4
+ * Extracted from checks.ts: individual check implementations for Tier 1 blockers.
5
+ */
6
+ import { existsSync, statSync } from "node:fs";
7
+ import type { NaxConfig } from "../config";
8
+ import type { PRD } from "../prd/types";
9
+ import type { Check } from "./types";
10
+
11
+ /** Check if directory is a git repository. Uses: git rev-parse --git-dir */
12
+ export async function checkGitRepoExists(workdir: string): Promise<Check> {
13
+ // First try git rev-parse command
14
+ const proc = Bun.spawn(["git", "rev-parse", "--git-dir"], {
15
+ cwd: workdir,
16
+ stdout: "pipe",
17
+ stderr: "pipe",
18
+ });
19
+
20
+ const exitCode = await proc.exited;
21
+ let passed = exitCode === 0;
22
+
23
+ // Fallback: if git command fails, check if .git directory exists
24
+ // This handles test scenarios where .git exists but isn't fully initialized
25
+ if (!passed) {
26
+ const gitDir = `${workdir}/.git`;
27
+ if (existsSync(gitDir)) {
28
+ const stats = statSync(gitDir);
29
+ passed = stats.isDirectory();
30
+ }
31
+ }
32
+
33
+ return {
34
+ name: "git-repo-exists",
35
+ tier: "blocker",
36
+ passed,
37
+ message: passed ? "git repository detected" : "not a git repository",
38
+ };
39
+ }
40
+
41
+ /** Check if working tree is clean. Uses: git status --porcelain */
42
+ export async function checkWorkingTreeClean(workdir: string): Promise<Check> {
43
+ const proc = Bun.spawn(["git", "status", "--porcelain"], {
44
+ cwd: workdir,
45
+ stdout: "pipe",
46
+ stderr: "pipe",
47
+ });
48
+
49
+ const output = await new Response(proc.stdout).text();
50
+ const exitCode = await proc.exited;
51
+ const passed = exitCode === 0 && output.trim() === "";
52
+
53
+ return {
54
+ name: "working-tree-clean",
55
+ tier: "blocker",
56
+ passed,
57
+ message: passed ? "Working tree is clean" : "Uncommitted changes detected",
58
+ };
59
+ }
60
+
61
+ /** Check if nax.lock is older than 2 hours. */
62
+ export async function checkStaleLock(workdir: string): Promise<Check> {
63
+ const lockPath = `${workdir}/nax.lock`;
64
+ const exists = existsSync(lockPath);
65
+
66
+ if (!exists) {
67
+ return {
68
+ name: "no-stale-lock",
69
+ tier: "blocker",
70
+ passed: true,
71
+ message: "No lock file present",
72
+ };
73
+ }
74
+
75
+ try {
76
+ const file = Bun.file(lockPath);
77
+ const content = await file.text();
78
+ const lockData = JSON.parse(content);
79
+
80
+ // Support both timestamp (ms) and startedAt (ISO string) formats
81
+ let lockTimeMs: number;
82
+ if (lockData.timestamp) {
83
+ lockTimeMs = lockData.timestamp;
84
+ } else if (lockData.startedAt) {
85
+ lockTimeMs = new Date(lockData.startedAt).getTime();
86
+ } else {
87
+ // Fallback to file mtime if no timestamp in JSON
88
+ const stat = statSync(lockPath);
89
+ lockTimeMs = stat.mtimeMs;
90
+ }
91
+
92
+ const ageMs = Date.now() - lockTimeMs;
93
+ const twoHoursMs = 2 * 60 * 60 * 1000;
94
+ const passed = ageMs < twoHoursMs;
95
+
96
+ const ageMinutes = Math.floor(ageMs / 60000);
97
+ const ageHours = Math.floor(ageMinutes / 60);
98
+
99
+ return {
100
+ name: "no-stale-lock",
101
+ tier: "blocker",
102
+ passed,
103
+ message: passed ? "Lock file is fresh" : "stale lock detected (over 2 hours old)",
104
+ };
105
+ } catch (error) {
106
+ return {
107
+ name: "no-stale-lock",
108
+ tier: "blocker",
109
+ passed: false,
110
+ message: "Failed to read lock file",
111
+ };
112
+ }
113
+ }
114
+
115
+ /** Validate PRD structure and required fields. Auto-defaults: tags=[], status=pending, storyPoints=1 */
116
+ export async function checkPRDValid(prd: PRD): Promise<Check> {
117
+ const errors: string[] = [];
118
+
119
+ // Validate required PRD fields
120
+ if (!prd.project || prd.project.trim() === "") {
121
+ errors.push("Missing project field");
122
+ }
123
+ if (!prd.feature || prd.feature.trim() === "") {
124
+ errors.push("Missing feature field");
125
+ }
126
+ if (!prd.branchName || prd.branchName.trim() === "") {
127
+ errors.push("Missing branchName field");
128
+ }
129
+ if (!Array.isArray(prd.userStories)) {
130
+ errors.push("userStories must be an array");
131
+ }
132
+
133
+ // Validate each story
134
+ if (Array.isArray(prd.userStories)) {
135
+ for (const story of prd.userStories) {
136
+ // Auto-default optional fields in-memory (don't modify the PRD)
137
+ story.tags = story.tags ?? [];
138
+ story.status = story.status ?? "pending";
139
+ story.storyPoints = story.storyPoints ?? 1;
140
+ story.acceptanceCriteria = story.acceptanceCriteria ?? [];
141
+
142
+ // Validate required fields
143
+ if (!story.id || story.id.trim() === "") {
144
+ errors.push(`Story missing id: ${JSON.stringify(story).slice(0, 50)}`);
145
+ }
146
+ if (!story.title || story.title.trim() === "") {
147
+ errors.push(`Story ${story.id} missing title`);
148
+ }
149
+ if (!story.description || story.description.trim() === "") {
150
+ errors.push(`Story ${story.id} missing description`);
151
+ }
152
+ }
153
+ }
154
+
155
+ const passed = errors.length === 0;
156
+
157
+ return {
158
+ name: "prd-valid",
159
+ tier: "blocker",
160
+ passed,
161
+ message: passed ? "PRD structure is valid" : errors.join("; "),
162
+ };
163
+ }
164
+
165
+ /** Check if Claude CLI is available. Uses: claude --version */
166
+ export async function checkClaudeCLI(): Promise<Check> {
167
+ try {
168
+ const proc = Bun.spawn(["claude", "--version"], {
169
+ stdout: "pipe",
170
+ stderr: "pipe",
171
+ });
172
+
173
+ const exitCode = await proc.exited;
174
+ const passed = exitCode === 0;
175
+
176
+ return {
177
+ name: "claude-cli-available",
178
+ tier: "blocker",
179
+ passed,
180
+ message: passed ? "Claude CLI is available" : "Claude CLI not found. Install from https://claude.ai/download",
181
+ };
182
+ } catch {
183
+ // Bun.spawn throws ENOENT when the binary is not found in PATH.
184
+ // Treat this as a failed check rather than an unhandled exception so the
185
+ // rest of the precheck pipeline can continue and report all issues at once.
186
+ return {
187
+ name: "claude-cli-available",
188
+ tier: "blocker",
189
+ passed: false,
190
+ message: "Claude CLI not found in PATH. Install from https://claude.ai/download",
191
+ };
192
+ }
193
+ }
194
+
195
+ /** Check if dependencies are installed (language-aware). Detects: node_modules, target, venv, vendor */
196
+ export async function checkDependenciesInstalled(workdir: string): Promise<Check> {
197
+ const depPaths = [
198
+ { path: "node_modules" },
199
+ { path: "target" },
200
+ { path: "venv" },
201
+ { path: ".venv" },
202
+ { path: "vendor" },
203
+ ];
204
+
205
+ const found: string[] = [];
206
+ for (const { path } of depPaths) {
207
+ const fullPath = `${workdir}/${path}`;
208
+ // Check if it exists and is a directory
209
+ if (existsSync(fullPath)) {
210
+ const stats = statSync(fullPath);
211
+ if (stats.isDirectory()) {
212
+ found.push(path);
213
+ }
214
+ }
215
+ }
216
+
217
+ const passed = found.length > 0;
218
+
219
+ return {
220
+ name: "dependencies-installed",
221
+ tier: "blocker",
222
+ passed,
223
+ message: passed ? `Dependencies found: ${found.join(", ")}` : "No dependency directories detected",
224
+ };
225
+ }
226
+
227
+ /** Check if test command works. Skips silently if command is null/false. */
228
+ export async function checkTestCommand(config: NaxConfig): Promise<Check> {
229
+ // Try multiple possible locations for testCommand
230
+ const testCommand = config.execution.testCommand || (config.quality?.commands?.test as string | undefined);
231
+
232
+ // Skip if explicitly disabled or not configured
233
+ if (!testCommand || testCommand === null || testCommand === null) {
234
+ return {
235
+ name: "test-command-works",
236
+ tier: "blocker",
237
+ passed: true,
238
+ message: "Test command not configured (skipped)",
239
+ };
240
+ }
241
+
242
+ // Parse command and args
243
+ const parts = testCommand.split(" ");
244
+ const [cmd, ...args] = parts;
245
+
246
+ try {
247
+ const proc = Bun.spawn([cmd, ...args, "--help"], {
248
+ stdout: "pipe",
249
+ stderr: "pipe",
250
+ });
251
+
252
+ const exitCode = await proc.exited;
253
+ const passed = exitCode === 0;
254
+
255
+ return {
256
+ name: "test-command-works",
257
+ tier: "blocker",
258
+ passed,
259
+ message: passed ? "Test command is available" : `Test command failed: ${testCommand}`,
260
+ };
261
+ } catch (error) {
262
+ return {
263
+ name: "test-command-works",
264
+ tier: "blocker",
265
+ passed: false,
266
+ message: `Test command failed: ${testCommand}`,
267
+ };
268
+ }
269
+ }
270
+
271
+ /** Check if lint command works. Skips silently if command is null/false. */
272
+ export async function checkLintCommand(config: NaxConfig): Promise<Check> {
273
+ const lintCommand = config.execution.lintCommand;
274
+
275
+ // Skip if explicitly disabled or not configured
276
+ if (!lintCommand || lintCommand === null || lintCommand === null) {
277
+ return {
278
+ name: "lint-command-works",
279
+ tier: "blocker",
280
+ passed: true,
281
+ message: "Lint command not configured (skipped)",
282
+ };
283
+ }
284
+
285
+ // Parse command and args
286
+ const parts = lintCommand.split(" ");
287
+ const [cmd, ...args] = parts;
288
+
289
+ try {
290
+ const proc = Bun.spawn([cmd, ...args, "--help"], {
291
+ stdout: "pipe",
292
+ stderr: "pipe",
293
+ });
294
+
295
+ const exitCode = await proc.exited;
296
+ const passed = exitCode === 0;
297
+
298
+ return {
299
+ name: "lint-command-works",
300
+ tier: "blocker",
301
+ passed,
302
+ message: passed ? "Lint command is available" : `Lint command failed: ${lintCommand}`,
303
+ };
304
+ } catch (error) {
305
+ return {
306
+ name: "lint-command-works",
307
+ tier: "blocker",
308
+ passed: false,
309
+ message: `Lint command failed: ${lintCommand}`,
310
+ };
311
+ }
312
+ }
313
+
314
+ /** Check if typecheck command works. Skips silently if command is null/false. */
315
+ export async function checkTypecheckCommand(config: NaxConfig): Promise<Check> {
316
+ const typecheckCommand = config.execution.typecheckCommand;
317
+
318
+ // Skip if explicitly disabled or not configured
319
+ if (!typecheckCommand || typecheckCommand === null || typecheckCommand === null) {
320
+ return {
321
+ name: "typecheck-command-works",
322
+ tier: "blocker",
323
+ passed: true,
324
+ message: "Typecheck command not configured (skipped)",
325
+ };
326
+ }
327
+
328
+ // Parse command and args
329
+ const parts = typecheckCommand.split(" ");
330
+ const [cmd, ...args] = parts;
331
+
332
+ try {
333
+ const proc = Bun.spawn([cmd, ...args, "--help"], {
334
+ stdout: "pipe",
335
+ stderr: "pipe",
336
+ });
337
+
338
+ const exitCode = await proc.exited;
339
+ const passed = exitCode === 0;
340
+
341
+ return {
342
+ name: "typecheck-command-works",
343
+ tier: "blocker",
344
+ passed,
345
+ message: passed
346
+ ? `Typecheck command is available: ${typecheckCommand}`
347
+ : `Typecheck command failed: ${typecheckCommand}`,
348
+ };
349
+ } catch (error) {
350
+ return {
351
+ name: "typecheck-command-works",
352
+ tier: "blocker",
353
+ passed: false,
354
+ message: `Typecheck command failed: ${typecheckCommand}`,
355
+ };
356
+ }
357
+ }
358
+
359
+ /** Check if git user is configured. */
360
+ export async function checkGitUserConfigured(workdir?: string): Promise<Check> {
361
+ const spawnOptions = {
362
+ stdout: "pipe" as const,
363
+ stderr: "pipe" as const,
364
+ ...(workdir && { cwd: workdir }),
365
+ };
366
+
367
+ const nameProc = Bun.spawn(["git", "config", "user.name"], spawnOptions);
368
+ const emailProc = Bun.spawn(["git", "config", "user.email"], spawnOptions);
369
+
370
+ const nameOutput = await new Response(nameProc.stdout).text();
371
+ const emailOutput = await new Response(emailProc.stdout).text();
372
+ const nameExitCode = await nameProc.exited;
373
+ const emailExitCode = await emailProc.exited;
374
+
375
+ const hasName = nameExitCode === 0 && nameOutput.trim() !== "";
376
+ const hasEmail = emailExitCode === 0 && emailOutput.trim() !== "";
377
+ const passed = hasName && hasEmail;
378
+
379
+ return {
380
+ name: "git-user-configured",
381
+ tier: "blocker",
382
+ passed,
383
+ message: passed
384
+ ? "Git user is configured"
385
+ : !hasName && !hasEmail
386
+ ? "Git user.name and user.email not configured"
387
+ : !hasName
388
+ ? "Git user.name not configured"
389
+ : "Git user.email not configured",
390
+ };
391
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Precheck Tier 2 Warnings
3
+ *
4
+ * Extracted from checks.ts: individual check implementations for Tier 2 warnings.
5
+ * These checks produce warnings but do not block execution.
6
+ */
7
+
8
+ import { existsSync } from "node:fs";
9
+ import type { NaxConfig } from "../config";
10
+ import type { PRD } from "../prd/types";
11
+ import type { Check } from "./types";
12
+
13
+ /**
14
+ * Check if CLAUDE.md exists.
15
+ */
16
+ export async function checkClaudeMdExists(workdir: string): Promise<Check> {
17
+ const claudeMdPath = `${workdir}/CLAUDE.md`;
18
+ const passed = existsSync(claudeMdPath);
19
+
20
+ return {
21
+ name: "claude-md-exists",
22
+ tier: "warning",
23
+ passed,
24
+ message: passed ? "CLAUDE.md found" : "CLAUDE.md not found (recommended for project context)",
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Check if disk space is above 1GB.
30
+ */
31
+ export async function checkDiskSpace(): Promise<Check> {
32
+ const proc = Bun.spawn(["df", "-k", "."], {
33
+ stdout: "pipe",
34
+ stderr: "pipe",
35
+ });
36
+
37
+ const output = await new Response(proc.stdout).text();
38
+ const exitCode = await proc.exited;
39
+
40
+ if (exitCode !== 0) {
41
+ return {
42
+ name: "disk-space-sufficient",
43
+ tier: "warning",
44
+ passed: false,
45
+ message: "Unable to check disk space",
46
+ };
47
+ }
48
+
49
+ // Parse df output (second line, fourth column is available space in KB)
50
+ const lines = output.trim().split("\n");
51
+ if (lines.length < 2) {
52
+ return {
53
+ name: "disk-space-sufficient",
54
+ tier: "warning",
55
+ passed: false,
56
+ message: "Unable to parse disk space output",
57
+ };
58
+ }
59
+
60
+ const parts = lines[1].split(/\s+/);
61
+ const availableKB = Number.parseInt(parts[3], 10);
62
+ const availableGB = availableKB / 1024 / 1024;
63
+ const passed = availableGB >= 1;
64
+
65
+ return {
66
+ name: "disk-space-sufficient",
67
+ tier: "warning",
68
+ passed,
69
+ message: passed
70
+ ? `Disk space: ${availableGB.toFixed(2)}GB available`
71
+ : `Low disk space: ${availableGB.toFixed(2)}GB available`,
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Check if PRD has pending stories.
77
+ */
78
+ export async function checkPendingStories(prd: PRD): Promise<Check> {
79
+ const pendingStories = prd.userStories.filter((s) => s.status === "pending");
80
+ const passed = pendingStories.length > 0;
81
+
82
+ return {
83
+ name: "has-pending-stories",
84
+ tier: "warning",
85
+ passed,
86
+ message: passed ? `${pendingStories.length} pending stories found` : "no pending stories to execute",
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Check if optional commands are configured.
92
+ */
93
+ export async function checkOptionalCommands(config: NaxConfig): Promise<Check> {
94
+ const missing: string[] = [];
95
+
96
+ if (!config.execution.lintCommand) {
97
+ missing.push("lint");
98
+ }
99
+ if (!config.execution.typecheckCommand) {
100
+ missing.push("typecheck");
101
+ }
102
+
103
+ const passed = missing.length === 0;
104
+
105
+ return {
106
+ name: "optional-commands-configured",
107
+ tier: "warning",
108
+ passed,
109
+ message: passed ? "All optional commands configured" : `Optional commands not configured: ${missing.join(", ")}`,
110
+ };
111
+ }
112
+
113
+ /**
114
+ * Check if .gitignore covers nax runtime files.
115
+ * Patterns: nax.lock, runs/, test/tmp/
116
+ */
117
+ export async function checkGitignoreCoversNax(workdir: string): Promise<Check> {
118
+ const gitignorePath = `${workdir}/.gitignore`;
119
+ const exists = existsSync(gitignorePath);
120
+
121
+ if (!exists) {
122
+ return {
123
+ name: "gitignore-covers-nax",
124
+ tier: "warning",
125
+ passed: false,
126
+ message: ".gitignore not found",
127
+ };
128
+ }
129
+
130
+ const file = Bun.file(gitignorePath);
131
+ const content = await file.text();
132
+ const patterns = ["nax.lock", "runs/", "test/tmp/"];
133
+ const missing = patterns.filter((pattern) => !content.includes(pattern));
134
+ const passed = missing.length === 0;
135
+
136
+ return {
137
+ name: "gitignore-covers-nax",
138
+ tier: "warning",
139
+ passed,
140
+ message: passed ? ".gitignore covers nax runtime files" : `.gitignore missing patterns: ${missing.join(", ")}`,
141
+ };
142
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Precheck implementation functions
3
+ *
4
+ * Re-export barrel for backward compatibility.
5
+ * Tier 1 blockers: ./checks-blockers
6
+ * Tier 2 warnings: ./checks-warnings
7
+ */
8
+
9
+ // Tier 1 Blockers
10
+ export {
11
+ checkGitRepoExists,
12
+ checkWorkingTreeClean,
13
+ checkStaleLock,
14
+ checkPRDValid,
15
+ checkClaudeCLI,
16
+ checkDependenciesInstalled,
17
+ checkTestCommand,
18
+ checkLintCommand,
19
+ checkTypecheckCommand,
20
+ checkGitUserConfigured,
21
+ } from "./checks-blockers";
22
+
23
+ // Tier 2 Warnings
24
+ export {
25
+ checkClaudeMdExists,
26
+ checkDiskSpace,
27
+ checkPendingStories,
28
+ checkOptionalCommands,
29
+ checkGitignoreCoversNax,
30
+ } from "./checks-warnings";