@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,295 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { mkdirSync, rmSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { spawn } from "bun";
5
+ import { acquireLock, formatProgress, releaseLock } from "../../src/execution/helpers";
6
+ import type { StoryCounts } from "../../src/execution/helpers";
7
+
8
+ describe("formatProgress", () => {
9
+ test("formats progress with all stories pending", () => {
10
+ const counts: StoryCounts = {
11
+ total: 12,
12
+ passed: 0,
13
+ failed: 0,
14
+ pending: 12,
15
+ };
16
+
17
+ const progress = formatProgress(counts, 0, 5.0, 0, 12);
18
+
19
+ expect(progress).toContain("0/12 stories");
20
+ expect(progress).toContain("0 passed");
21
+ expect(progress).toContain("0 failed");
22
+ expect(progress).toContain("$0.00/$5.00");
23
+ expect(progress).toContain("calculating...");
24
+ });
25
+
26
+ test("formats progress with some stories completed", () => {
27
+ const counts: StoryCounts = {
28
+ total: 12,
29
+ passed: 5,
30
+ failed: 1,
31
+ pending: 6,
32
+ };
33
+
34
+ // 10 minutes elapsed (600000 ms), 6 stories completed
35
+ // avg = 600000 / 6 = 100000 ms per story
36
+ // remaining = 6 stories * 100000 = 600000 ms = 10 minutes
37
+ const progress = formatProgress(counts, 0.45, 5.0, 600000, 12);
38
+
39
+ expect(progress).toContain("6/12 stories");
40
+ expect(progress).toContain("5 passed");
41
+ expect(progress).toContain("1 failed");
42
+ expect(progress).toContain("$0.45/$5.00");
43
+ expect(progress).toContain("~10 min remaining");
44
+ });
45
+
46
+ test("formats progress when all stories are complete", () => {
47
+ const counts: StoryCounts = {
48
+ total: 12,
49
+ passed: 10,
50
+ failed: 2,
51
+ pending: 0,
52
+ };
53
+
54
+ const progress = formatProgress(counts, 1.23, 5.0, 1200000, 12);
55
+
56
+ expect(progress).toContain("12/12 stories");
57
+ expect(progress).toContain("10 passed");
58
+ expect(progress).toContain("2 failed");
59
+ expect(progress).toContain("$1.23/$5.00");
60
+ expect(progress).toContain("complete");
61
+ });
62
+
63
+ test("calculates ETA correctly for fast stories", () => {
64
+ const counts: StoryCounts = {
65
+ total: 20,
66
+ passed: 10,
67
+ failed: 0,
68
+ pending: 10,
69
+ };
70
+
71
+ // 2 minutes elapsed (120000 ms) for 10 stories
72
+ // avg = 120000 / 10 = 12000 ms per story
73
+ // remaining = 10 stories * 12000 = 120000 ms = 2 minutes
74
+ const progress = formatProgress(counts, 0.5, 10.0, 120000, 20);
75
+
76
+ expect(progress).toContain("~2 min remaining");
77
+ });
78
+
79
+ test("rounds ETA to nearest minute", () => {
80
+ const counts: StoryCounts = {
81
+ total: 10,
82
+ passed: 3,
83
+ failed: 0,
84
+ pending: 7,
85
+ };
86
+
87
+ // 8.5 minutes elapsed (510000 ms) for 3 stories
88
+ // avg = 510000 / 3 = 170000 ms per story
89
+ // remaining = 7 stories * 170000 = 1190000 ms ≈ 19.8 minutes → rounds to 20
90
+ const progress = formatProgress(counts, 0.3, 5.0, 510000, 10);
91
+
92
+ expect(progress).toContain("~20 min remaining");
93
+ });
94
+
95
+ test("includes cost information with proper formatting", () => {
96
+ const counts: StoryCounts = {
97
+ total: 5,
98
+ passed: 2,
99
+ failed: 0,
100
+ pending: 3,
101
+ };
102
+
103
+ const progress = formatProgress(counts, 1.2345, 10.0, 300000, 5);
104
+
105
+ // Should round cost to 2 decimal places
106
+ expect(progress).toContain("$1.23/$10.00");
107
+ });
108
+
109
+ test("handles zero elapsed time gracefully", () => {
110
+ const counts: StoryCounts = {
111
+ total: 10,
112
+ passed: 0,
113
+ failed: 0,
114
+ pending: 10,
115
+ };
116
+
117
+ const progress = formatProgress(counts, 0, 5.0, 0, 10);
118
+
119
+ expect(progress).toContain("calculating...");
120
+ expect(progress).not.toContain("NaN");
121
+ expect(progress).not.toContain("Infinity");
122
+ });
123
+
124
+ test("includes all required progress indicators", () => {
125
+ const counts: StoryCounts = {
126
+ total: 10,
127
+ passed: 3,
128
+ failed: 1,
129
+ pending: 6,
130
+ };
131
+
132
+ const progress = formatProgress(counts, 0.5, 5.0, 300000, 10);
133
+
134
+ expect(progress).toContain("Progress:");
135
+ expect(progress).toContain("passed");
136
+ expect(progress).toContain("failed");
137
+ expect(progress).toContain("$");
138
+ expect(progress).toContain("min remaining");
139
+ });
140
+ });
141
+
142
+ describe("acquireLock and releaseLock", () => {
143
+ const testDir = path.join(import.meta.dir, ".test-locks");
144
+ const lockPath = path.join(testDir, "nax.lock");
145
+
146
+ beforeEach(() => {
147
+ // Create clean test directory
148
+ rmSync(testDir, { recursive: true, force: true });
149
+ mkdirSync(testDir, { recursive: true });
150
+ });
151
+
152
+ afterEach(() => {
153
+ // Clean up test directory
154
+ rmSync(testDir, { recursive: true, force: true });
155
+ });
156
+
157
+ test("acquires lock when no lock file exists", async () => {
158
+ const acquired = await acquireLock(testDir);
159
+ expect(acquired).toBe(true);
160
+
161
+ // Verify lock file was created
162
+ const lockFile = Bun.file(lockPath);
163
+ expect(await lockFile.exists()).toBe(true);
164
+
165
+ // Verify lock file contains current PID
166
+ const lockContent = await lockFile.text();
167
+ const lockData = JSON.parse(lockContent);
168
+ expect(lockData.pid).toBe(process.pid);
169
+ expect(typeof lockData.timestamp).toBe("number");
170
+
171
+ await releaseLock(testDir);
172
+ });
173
+
174
+ test("fails to acquire lock when another process holds it", async () => {
175
+ // First process acquires lock
176
+ const acquired1 = await acquireLock(testDir);
177
+ expect(acquired1).toBe(true);
178
+
179
+ // Second process tries to acquire lock
180
+ const acquired2 = await acquireLock(testDir);
181
+ expect(acquired2).toBe(false);
182
+
183
+ await releaseLock(testDir);
184
+ });
185
+
186
+ test("releases lock successfully", async () => {
187
+ await acquireLock(testDir);
188
+ await releaseLock(testDir);
189
+
190
+ // Verify lock file was deleted
191
+ const lockFile = Bun.file(lockPath);
192
+ expect(await lockFile.exists()).toBe(false);
193
+ });
194
+
195
+ test("can re-acquire lock after release", async () => {
196
+ const acquired1 = await acquireLock(testDir);
197
+ expect(acquired1).toBe(true);
198
+
199
+ await releaseLock(testDir);
200
+
201
+ const acquired2 = await acquireLock(testDir);
202
+ expect(acquired2).toBe(true);
203
+
204
+ await releaseLock(testDir);
205
+ });
206
+
207
+ test("removes stale lock when process is dead", async () => {
208
+ // Create a lock file with a fake PID that doesn't exist
209
+ const stalePid = 999999; // Very unlikely to be a real process
210
+ const staleLock = {
211
+ pid: stalePid,
212
+ timestamp: Date.now() - 60000, // 1 minute ago
213
+ };
214
+ await Bun.write(lockPath, JSON.stringify(staleLock));
215
+
216
+ // Try to acquire lock - should detect stale lock and remove it
217
+ const acquired = await acquireLock(testDir);
218
+ expect(acquired).toBe(true);
219
+
220
+ // Verify new lock file has current PID
221
+ const lockFile = Bun.file(lockPath);
222
+ const lockContent = await lockFile.text();
223
+ const lockData = JSON.parse(lockContent);
224
+ expect(lockData.pid).toBe(process.pid);
225
+
226
+ await releaseLock(testDir);
227
+ });
228
+
229
+ test("detects stale lock from OOM-killed process", async () => {
230
+ // Spawn a short-lived child process
231
+ const proc = spawn({
232
+ cmd: ["sleep", "0.1"],
233
+ stdout: "pipe",
234
+ stderr: "pipe",
235
+ });
236
+
237
+ // Get the child PID
238
+ const childPid = proc.pid;
239
+
240
+ // Wait for it to exit
241
+ await proc.exited;
242
+
243
+ // Create a lock file with the dead child's PID
244
+ const staleLock = {
245
+ pid: childPid,
246
+ timestamp: Date.now() - 60000, // 1 minute ago
247
+ };
248
+ await Bun.write(lockPath, JSON.stringify(staleLock));
249
+
250
+ // Now try to acquire lock - should detect child process is dead
251
+ const acquired = await acquireLock(testDir);
252
+ expect(acquired).toBe(true);
253
+
254
+ // Verify new lock has current PID
255
+ const lockFile = Bun.file(lockPath);
256
+ const lockContent = await lockFile.text();
257
+ const lockData = JSON.parse(lockContent);
258
+ expect(lockData.pid).toBe(process.pid);
259
+
260
+ await releaseLock(testDir);
261
+ });
262
+
263
+ test("does not remove lock when process is still alive", async () => {
264
+ // Create lock with current process PID
265
+ const validLock = {
266
+ pid: process.pid,
267
+ timestamp: Date.now() - 60000, // 1 minute ago
268
+ };
269
+ await Bun.write(lockPath, JSON.stringify(validLock));
270
+
271
+ // Try to acquire lock - should NOT remove it since process is alive
272
+ const acquired = await acquireLock(testDir);
273
+ expect(acquired).toBe(false);
274
+
275
+ // Verify lock still exists with same PID
276
+ const lockFile = Bun.file(lockPath);
277
+ const lockContent = await lockFile.text();
278
+ const lockData = JSON.parse(lockContent);
279
+ expect(lockData.pid).toBe(process.pid);
280
+ });
281
+
282
+ test("handles corrupted lock file gracefully", async () => {
283
+ // Create invalid JSON lock file
284
+ await Bun.write(lockPath, "not valid json");
285
+
286
+ // Should fail to acquire but not crash
287
+ const acquired = await acquireLock(testDir);
288
+ expect(acquired).toBe(false);
289
+ });
290
+
291
+ test("handles release when lock file doesn't exist", async () => {
292
+ // Should not throw when releasing non-existent lock
293
+ await expect(releaseLock(testDir)).resolves.toBeUndefined();
294
+ });
295
+ });
@@ -0,0 +1,361 @@
1
+ /**
2
+ * Hook Runner Tests
3
+ *
4
+ * Tests for hook execution, security, and lifecycle
5
+ */
6
+
7
+ import { describe, expect, test } from "bun:test";
8
+ import { fireHook, loadHooksConfig } from "../../src/hooks/runner";
9
+ import type { HookContext, HooksConfig } from "../../src/hooks/types";
10
+
11
+ describe("Hook Security", () => {
12
+ test("executes safe commands without shell", async () => {
13
+ const config: HooksConfig = {
14
+ hooks: {
15
+ "on-start": {
16
+ command: "echo hello",
17
+ enabled: true,
18
+ },
19
+ },
20
+ };
21
+
22
+ const ctx: HookContext = {
23
+ event: "on-start",
24
+ feature: "test-feature",
25
+ };
26
+
27
+ // Should not throw
28
+ await fireHook(config, "on-start", ctx, process.cwd());
29
+ });
30
+
31
+ test("rejects command substitution with $()", async () => {
32
+ const config: HooksConfig = {
33
+ hooks: {
34
+ "on-start": {
35
+ command: "echo $(whoami)",
36
+ enabled: true,
37
+ },
38
+ },
39
+ };
40
+
41
+ const ctx: HookContext = {
42
+ event: "on-start",
43
+ feature: "test-feature",
44
+ };
45
+
46
+ // Should execute but validation should fail
47
+ await fireHook(config, "on-start", ctx, process.cwd());
48
+ // The hook should log a validation failure (tested via console.warn spy in integration)
49
+ });
50
+
51
+ test("rejects backtick command substitution", async () => {
52
+ const config: HooksConfig = {
53
+ hooks: {
54
+ "on-start": {
55
+ command: "echo `whoami`",
56
+ enabled: true,
57
+ },
58
+ },
59
+ };
60
+
61
+ const ctx: HookContext = {
62
+ event: "on-start",
63
+ feature: "test-feature",
64
+ };
65
+
66
+ await fireHook(config, "on-start", ctx, process.cwd());
67
+ // Should fail validation
68
+ });
69
+
70
+ test("rejects piping to bash", async () => {
71
+ const config: HooksConfig = {
72
+ hooks: {
73
+ "on-start": {
74
+ command: "echo malicious | bash",
75
+ enabled: true,
76
+ },
77
+ },
78
+ };
79
+
80
+ const ctx: HookContext = {
81
+ event: "on-start",
82
+ feature: "test-feature",
83
+ };
84
+
85
+ await fireHook(config, "on-start", ctx, process.cwd());
86
+ // Should fail validation
87
+ });
88
+
89
+ test("rejects dangerous rm -rf patterns", async () => {
90
+ const config: HooksConfig = {
91
+ hooks: {
92
+ "on-start": {
93
+ command: "echo test; rm -rf /",
94
+ enabled: true,
95
+ },
96
+ },
97
+ };
98
+
99
+ const ctx: HookContext = {
100
+ event: "on-start",
101
+ feature: "test-feature",
102
+ };
103
+
104
+ await fireHook(config, "on-start", ctx, process.cwd());
105
+ // Should fail validation
106
+ });
107
+
108
+ test("warns about shell operators", async () => {
109
+ const config: HooksConfig = {
110
+ hooks: {
111
+ "on-start": {
112
+ command: "echo hello && echo world",
113
+ enabled: true,
114
+ },
115
+ },
116
+ };
117
+
118
+ const ctx: HookContext = {
119
+ event: "on-start",
120
+ feature: "test-feature",
121
+ };
122
+
123
+ // Should execute but log warning about shell operators
124
+ await fireHook(config, "on-start", ctx, process.cwd());
125
+ });
126
+
127
+ test("escapes environment variables", async () => {
128
+ const config: HooksConfig = {
129
+ hooks: {
130
+ "on-story-start": {
131
+ command: "printenv",
132
+ enabled: true,
133
+ },
134
+ },
135
+ };
136
+
137
+ const ctx: HookContext = {
138
+ event: "on-story-start",
139
+ feature: "test-feature",
140
+ storyId: "story-with-\0null-byte",
141
+ reason: "reason\nwith\nnewlines",
142
+ };
143
+
144
+ // Should not throw, environment variables should be escaped
145
+ await fireHook(config, "on-story-start", ctx, process.cwd());
146
+ });
147
+
148
+ test("handles timeout correctly", async () => {
149
+ const config: HooksConfig = {
150
+ hooks: {
151
+ "on-start": {
152
+ command: "sleep 10",
153
+ enabled: true,
154
+ timeout: 100, // 100ms timeout
155
+ },
156
+ },
157
+ };
158
+
159
+ const ctx: HookContext = {
160
+ event: "on-start",
161
+ feature: "test-feature",
162
+ };
163
+
164
+ // Should timeout and not throw
165
+ await fireHook(config, "on-start", ctx, process.cwd());
166
+ // Should log timeout reason
167
+ });
168
+
169
+ test("skips disabled hooks", async () => {
170
+ const config: HooksConfig = {
171
+ hooks: {
172
+ "on-start": {
173
+ command: "echo should-not-run",
174
+ enabled: false,
175
+ },
176
+ },
177
+ };
178
+
179
+ const ctx: HookContext = {
180
+ event: "on-start",
181
+ feature: "test-feature",
182
+ };
183
+
184
+ // Should not execute
185
+ await fireHook(config, "on-start", ctx, process.cwd());
186
+ });
187
+
188
+ test("handles missing hooks gracefully", async () => {
189
+ const config: HooksConfig = {
190
+ hooks: {},
191
+ };
192
+
193
+ const ctx: HookContext = {
194
+ event: "on-start",
195
+ feature: "test-feature",
196
+ };
197
+
198
+ // Should not throw
199
+ await fireHook(config, "on-start", ctx, process.cwd());
200
+ });
201
+
202
+ test("passes context as JSON via stdin", async () => {
203
+ const config: HooksConfig = {
204
+ hooks: {
205
+ "on-complete": {
206
+ command: "cat",
207
+ enabled: true,
208
+ },
209
+ },
210
+ };
211
+
212
+ const ctx: HookContext = {
213
+ event: "on-complete",
214
+ feature: "test-feature",
215
+ storyId: "story-123",
216
+ status: "completed",
217
+ cost: 1.5,
218
+ model: "claude-sonnet-4",
219
+ };
220
+
221
+ // Should pass full context as JSON
222
+ await fireHook(config, "on-complete", ctx, process.cwd());
223
+ });
224
+ });
225
+
226
+ describe("Hook Configuration Loading", () => {
227
+ test("loads empty config when no hooks.json exists", async () => {
228
+ const config = await loadHooksConfig("/tmp/nonexistent-hooks-dir");
229
+
230
+ expect(config).toEqual({
231
+ hooks: {},
232
+ _global: { hooks: {} },
233
+ _skipGlobal: false,
234
+ });
235
+ });
236
+
237
+ test("merges global and project hooks", async () => {
238
+ // This test would require creating temporary hooks.json files
239
+ // Skipping for now, as it requires file system setup
240
+ });
241
+ });
242
+
243
+ describe("Hook Environment Variables", () => {
244
+ test("sets NAX_EVENT", async () => {
245
+ const config: HooksConfig = {
246
+ hooks: {
247
+ "on-start": {
248
+ command: "printenv",
249
+ enabled: true,
250
+ },
251
+ },
252
+ };
253
+
254
+ const ctx: HookContext = {
255
+ event: "on-start",
256
+ feature: "auth-feature",
257
+ };
258
+
259
+ await fireHook(config, "on-start", ctx, process.cwd());
260
+ // Environment variables should be set
261
+ });
262
+
263
+ test("sets NAX_FEATURE", async () => {
264
+ const config: HooksConfig = {
265
+ hooks: {
266
+ "on-start": {
267
+ command: "printenv",
268
+ enabled: true,
269
+ },
270
+ },
271
+ };
272
+
273
+ const ctx: HookContext = {
274
+ event: "on-start",
275
+ feature: "payment-system",
276
+ };
277
+
278
+ await fireHook(config, "on-start", ctx, process.cwd());
279
+ // NAX_FEATURE should be set to "payment-system"
280
+ });
281
+
282
+ test("sets NAX_STORY_ID when provided", async () => {
283
+ const config: HooksConfig = {
284
+ hooks: {
285
+ "on-story-start": {
286
+ command: "printenv",
287
+ enabled: true,
288
+ },
289
+ },
290
+ };
291
+
292
+ const ctx: HookContext = {
293
+ event: "on-story-start",
294
+ feature: "test-feature",
295
+ storyId: "user-login-001",
296
+ };
297
+
298
+ await fireHook(config, "on-story-start", ctx, process.cwd());
299
+ // NAX_STORY_ID should be set
300
+ });
301
+
302
+ test("sets NAX_COST when provided", async () => {
303
+ const config: HooksConfig = {
304
+ hooks: {
305
+ "on-complete": {
306
+ command: "printenv",
307
+ enabled: true,
308
+ },
309
+ },
310
+ };
311
+
312
+ const ctx: HookContext = {
313
+ event: "on-complete",
314
+ feature: "test-feature",
315
+ cost: 2.5678,
316
+ };
317
+
318
+ await fireHook(config, "on-complete", ctx, process.cwd());
319
+ // NAX_COST should be set to "2.5678"
320
+ });
321
+
322
+ test("sets NAX_MODEL when provided", async () => {
323
+ const config: HooksConfig = {
324
+ hooks: {
325
+ "on-complete": {
326
+ command: "printenv",
327
+ enabled: true,
328
+ },
329
+ },
330
+ };
331
+
332
+ const ctx: HookContext = {
333
+ event: "on-complete",
334
+ feature: "test-feature",
335
+ model: "claude-opus-4",
336
+ };
337
+
338
+ await fireHook(config, "on-complete", ctx, process.cwd());
339
+ // NAX_MODEL should be set
340
+ });
341
+
342
+ test("sets NAX_ITERATION when provided", async () => {
343
+ const config: HooksConfig = {
344
+ hooks: {
345
+ "on-error": {
346
+ command: "printenv",
347
+ enabled: true,
348
+ },
349
+ },
350
+ };
351
+
352
+ const ctx: HookContext = {
353
+ event: "on-error",
354
+ feature: "test-feature",
355
+ iteration: 3,
356
+ };
357
+
358
+ await fireHook(config, "on-error", ctx, process.cwd());
359
+ // NAX_ITERATION should be set to "3"
360
+ });
361
+ });