@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,334 @@
1
+ /**
2
+ * TUI Controls Tests
3
+ *
4
+ * Tests keyboard shortcuts, overlays, focus mode switching,
5
+ * and queue command writing.
6
+ */
7
+
8
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
9
+ import { unlink } from "node:fs/promises";
10
+ import { render } from "ink-testing-library";
11
+ import { createElement } from "react";
12
+ import type { UserStory } from "../../src/prd/types";
13
+ import { CostOverlay } from "../../src/tui/components/CostOverlay";
14
+ import { HelpOverlay } from "../../src/tui/components/HelpOverlay";
15
+ import type { KeyboardAction } from "../../src/tui/hooks/useKeyboard";
16
+ import { PanelFocus } from "../../src/tui/types";
17
+ import type { StoryDisplayState } from "../../src/tui/types";
18
+ import { writeQueueCommand } from "../../src/utils/queue-writer";
19
+
20
+ // Helper to create mock stories
21
+ function createMockStory(id: string, status: StoryDisplayState["status"], cost = 0.01): StoryDisplayState {
22
+ const story: UserStory = {
23
+ id,
24
+ title: `Test story ${id}`,
25
+ description: "Test description",
26
+ acceptanceCriteria: [],
27
+ dependencies: [],
28
+ tags: [],
29
+ passes: status === "passed",
30
+ status: status === "passed" ? "passed" : "pending",
31
+ escalations: [],
32
+ attempts: 0,
33
+ };
34
+
35
+ return {
36
+ story,
37
+ status,
38
+ routing: {
39
+ complexity: "simple",
40
+ modelTier: "fast",
41
+ testStrategy: "test-after",
42
+ reasoning: "Test routing",
43
+ },
44
+ cost,
45
+ };
46
+ }
47
+
48
+ describe("HelpOverlay", () => {
49
+ test("does not render when visible=false", () => {
50
+ const { lastFrame } = render(createElement(HelpOverlay, { visible: false }));
51
+ expect(lastFrame()).toBe("");
52
+ });
53
+
54
+ test("renders keybindings when visible=true", () => {
55
+ const { lastFrame } = render(createElement(HelpOverlay, { visible: true }));
56
+ const output = lastFrame();
57
+
58
+ expect(output).toContain("Keyboard Shortcuts");
59
+ expect(output).toContain("p"); // Pause
60
+ expect(output).toContain("a"); // Abort
61
+ expect(output).toContain("s"); // Skip
62
+ expect(output).toContain("Tab"); // Toggle focus
63
+ expect(output).toContain("q"); // Quit
64
+ expect(output).toContain("?"); // Show help
65
+ expect(output).toContain("c"); // Show cost
66
+ expect(output).toContain("r"); // Retry
67
+ expect(output).toContain("Esc"); // Close overlay
68
+ expect(output).toContain("Ctrl+]"); // Escape agent panel
69
+ });
70
+
71
+ test("displays Stories panel keybindings", () => {
72
+ const { lastFrame } = render(createElement(HelpOverlay, { visible: true }));
73
+ const output = lastFrame();
74
+
75
+ expect(output).toContain("Stories Panel");
76
+ expect(output).toContain("Pause after current story");
77
+ expect(output).toContain("Abort run");
78
+ expect(output).toContain("Skip current story");
79
+ });
80
+
81
+ test("displays Agent panel keybindings", () => {
82
+ const { lastFrame } = render(createElement(HelpOverlay, { visible: true }));
83
+ const output = lastFrame();
84
+
85
+ expect(output).toContain("Agent Panel");
86
+ expect(output).toContain("Escape back to Stories panel");
87
+ expect(output).toContain("Forwarded to agent PTY");
88
+ });
89
+ });
90
+
91
+ describe("CostOverlay", () => {
92
+ test("does not render when visible=false", () => {
93
+ const { lastFrame } = render(createElement(CostOverlay, { visible: false }));
94
+ expect(lastFrame()).toBe("");
95
+ });
96
+
97
+ test("renders cost breakdown when visible=true", () => {
98
+ const stories = [
99
+ createMockStory("US-001", "passed", 0.023),
100
+ createMockStory("US-002", "running", 0.015),
101
+ createMockStory("US-003", "pending", 0),
102
+ ];
103
+
104
+ const { lastFrame } = render(
105
+ createElement(CostOverlay, {
106
+ visible: true,
107
+ stories,
108
+ totalCost: 0.038,
109
+ }),
110
+ );
111
+
112
+ const output = lastFrame();
113
+ expect(output).toContain("Cost Breakdown");
114
+ expect(output).toContain("Story ID");
115
+ expect(output).toContain("Status");
116
+ expect(output).toContain("Cost");
117
+ });
118
+
119
+ test("displays executed stories with costs", () => {
120
+ const stories = [createMockStory("US-001", "passed", 0.023), createMockStory("US-002", "failed", 0.015)];
121
+
122
+ const { lastFrame } = render(
123
+ createElement(CostOverlay, {
124
+ visible: true,
125
+ stories,
126
+ totalCost: 0.038,
127
+ }),
128
+ );
129
+
130
+ const output = lastFrame();
131
+ expect(output).toContain("US-001");
132
+ expect(output).toContain("passed");
133
+ expect(output).toContain("$0.0230");
134
+ expect(output).toContain("US-002");
135
+ expect(output).toContain("failed");
136
+ expect(output).toContain("$0.0150");
137
+ });
138
+
139
+ test("displays total cost", () => {
140
+ const stories = [createMockStory("US-001", "passed", 0.023)];
141
+
142
+ const { lastFrame } = render(
143
+ createElement(CostOverlay, {
144
+ visible: true,
145
+ stories,
146
+ totalCost: 0.123456,
147
+ }),
148
+ );
149
+
150
+ const output = lastFrame();
151
+ expect(output).toContain("Total Cost:");
152
+ expect(output).toContain("$0.1235");
153
+ });
154
+
155
+ test("shows message when no stories executed", () => {
156
+ const stories = [createMockStory("US-001", "pending", 0)];
157
+
158
+ const { lastFrame } = render(
159
+ createElement(CostOverlay, {
160
+ visible: true,
161
+ stories,
162
+ totalCost: 0,
163
+ }),
164
+ );
165
+
166
+ const output = lastFrame();
167
+ expect(output).toContain("No stories executed yet");
168
+ });
169
+ });
170
+
171
+ describe("Keyboard action types", () => {
172
+ test("PAUSE action has correct type", () => {
173
+ const action: KeyboardAction = { type: "PAUSE" };
174
+ expect(action.type).toBe("PAUSE");
175
+ });
176
+
177
+ test("ABORT action has correct type", () => {
178
+ const action: KeyboardAction = { type: "ABORT" };
179
+ expect(action.type).toBe("ABORT");
180
+ });
181
+
182
+ test("SKIP action has story ID", () => {
183
+ const action: KeyboardAction = { type: "SKIP", storyId: "US-042" };
184
+ expect(action.type).toBe("SKIP");
185
+ expect(action.storyId).toBe("US-042");
186
+ });
187
+
188
+ test("TOGGLE_FOCUS action has correct type", () => {
189
+ const action: KeyboardAction = { type: "TOGGLE_FOCUS" };
190
+ expect(action.type).toBe("TOGGLE_FOCUS");
191
+ });
192
+
193
+ test("ESCAPE_AGENT action has correct type", () => {
194
+ const action: KeyboardAction = { type: "ESCAPE_AGENT" };
195
+ expect(action.type).toBe("ESCAPE_AGENT");
196
+ });
197
+
198
+ test("QUIT action has correct type", () => {
199
+ const action: KeyboardAction = { type: "QUIT" };
200
+ expect(action.type).toBe("QUIT");
201
+ });
202
+
203
+ test("SHOW_HELP action has correct type", () => {
204
+ const action: KeyboardAction = { type: "SHOW_HELP" };
205
+ expect(action.type).toBe("SHOW_HELP");
206
+ });
207
+
208
+ test("SHOW_COST action has correct type", () => {
209
+ const action: KeyboardAction = { type: "SHOW_COST" };
210
+ expect(action.type).toBe("SHOW_COST");
211
+ });
212
+
213
+ test("RETRY action has correct type", () => {
214
+ const action: KeyboardAction = { type: "RETRY" };
215
+ expect(action.type).toBe("RETRY");
216
+ });
217
+
218
+ test("CLOSE_OVERLAY action has correct type", () => {
219
+ const action: KeyboardAction = { type: "CLOSE_OVERLAY" };
220
+ expect(action.type).toBe("CLOSE_OVERLAY");
221
+ });
222
+ });
223
+
224
+ describe("Focus mode", () => {
225
+ test("PanelFocus enum has Stories value", () => {
226
+ expect(PanelFocus.Stories).toBe("stories");
227
+ });
228
+
229
+ test("PanelFocus enum has Agent value", () => {
230
+ expect(PanelFocus.Agent).toBe("agent");
231
+ });
232
+
233
+ test("focus mode toggles between Stories and Agent", () => {
234
+ let focus: PanelFocus = PanelFocus.Stories;
235
+ focus = focus === PanelFocus.Stories ? PanelFocus.Agent : PanelFocus.Stories;
236
+ expect(focus).toBe(PanelFocus.Agent);
237
+
238
+ focus = focus === PanelFocus.Stories ? PanelFocus.Agent : PanelFocus.Stories;
239
+ expect(focus).toBe(PanelFocus.Stories);
240
+ });
241
+ });
242
+
243
+ describe("Queue command writer", () => {
244
+ const tempQueueFile = "/tmp/nax-test-queue.txt";
245
+
246
+ beforeEach(async () => {
247
+ // Clean up any existing test file
248
+ try {
249
+ await unlink(tempQueueFile);
250
+ } catch {
251
+ // Ignore if doesn't exist
252
+ }
253
+ });
254
+
255
+ afterEach(async () => {
256
+ // Clean up after tests
257
+ try {
258
+ await unlink(tempQueueFile);
259
+ } catch {
260
+ // Ignore if doesn't exist
261
+ }
262
+ });
263
+
264
+ test("writes PAUSE command to queue file", async () => {
265
+ await writeQueueCommand(tempQueueFile, { type: "PAUSE" });
266
+
267
+ const content = await Bun.file(tempQueueFile).text();
268
+ expect(content.trim()).toBe("PAUSE");
269
+ });
270
+
271
+ test("writes ABORT command to queue file", async () => {
272
+ await writeQueueCommand(tempQueueFile, { type: "ABORT" });
273
+
274
+ const content = await Bun.file(tempQueueFile).text();
275
+ expect(content.trim()).toBe("ABORT");
276
+ });
277
+
278
+ test("writes SKIP command with story ID to queue file", async () => {
279
+ await writeQueueCommand(tempQueueFile, { type: "SKIP", storyId: "US-042" });
280
+
281
+ const content = await Bun.file(tempQueueFile).text();
282
+ expect(content.trim()).toBe("SKIP US-042");
283
+ });
284
+
285
+ test("appends multiple commands to queue file", async () => {
286
+ await writeQueueCommand(tempQueueFile, { type: "SKIP", storyId: "US-001" });
287
+ await writeQueueCommand(tempQueueFile, { type: "PAUSE" });
288
+ await writeQueueCommand(tempQueueFile, { type: "ABORT" });
289
+
290
+ const content = await Bun.file(tempQueueFile).text();
291
+ const lines = content.trim().split("\n");
292
+
293
+ expect(lines).toEqual(["SKIP US-001", "PAUSE", "ABORT"]);
294
+ });
295
+
296
+ test("creates queue file if it doesn't exist", async () => {
297
+ // Ensure file doesn't exist
298
+ try {
299
+ await unlink(tempQueueFile);
300
+ } catch {
301
+ // Ignore
302
+ }
303
+
304
+ await writeQueueCommand(tempQueueFile, { type: "PAUSE" });
305
+
306
+ const file = Bun.file(tempQueueFile);
307
+ expect(await file.exists()).toBe(true);
308
+
309
+ const content = await file.text();
310
+ expect(content.trim()).toBe("PAUSE");
311
+ });
312
+ });
313
+
314
+ describe("Ctrl+] escape sequence", () => {
315
+ test("Ctrl+] should escape from agent panel", () => {
316
+ // This would be tested in integration with the useKeyboard hook
317
+ // For now, we verify the concept:
318
+ const input = "]";
319
+ const keyCtrl = true;
320
+
321
+ if (keyCtrl && input === "]") {
322
+ const action: KeyboardAction = { type: "ESCAPE_AGENT" };
323
+ expect(action.type).toBe("ESCAPE_AGENT");
324
+ }
325
+ });
326
+
327
+ test("regular ] without Ctrl should not escape", () => {
328
+ const input = "]";
329
+ const keyCtrl = false;
330
+
331
+ // Should not trigger escape
332
+ expect(keyCtrl && input === "]").toBe(false);
333
+ });
334
+ });
@@ -0,0 +1,189 @@
1
+ /**
2
+ * TUI Cost Accumulation and PTY Line Length Tests
3
+ *
4
+ * Tests for BUG-1 (story cost accumulation) and MEM-1 (PTY line length limits).
5
+ */
6
+
7
+ import { describe, expect, test } from "bun:test";
8
+ import { PipelineEventEmitter } from "../../src/pipeline/events";
9
+ import type { StageResult } from "../../src/pipeline/types";
10
+ import type { UserStory } from "../../src/prd/types";
11
+
12
+ // ── Test Fixtures ────────────────────────────────────
13
+
14
+ const createMockStory = (id: string): UserStory => ({
15
+ id,
16
+ title: `Test story ${id}`,
17
+ description: "Test description",
18
+ acceptanceCriteria: [],
19
+ dependencies: [],
20
+ tags: [],
21
+ passes: false,
22
+ status: "pending",
23
+ escalations: [],
24
+ attempts: 0,
25
+ });
26
+
27
+ // ── Cost Accumulation Tests (BUG-1) ──────────────────
28
+
29
+ describe("StageResult - Cost field in types (BUG-1)", () => {
30
+ test("should support cost field in continue action", () => {
31
+ const result: StageResult = { action: "continue", cost: 0.05 };
32
+ expect(result.action).toBe("continue");
33
+ expect(result.cost).toBe(0.05);
34
+ });
35
+
36
+ test("should support cost field in fail action", () => {
37
+ const result: StageResult = { action: "fail", reason: "Build failed", cost: 0.02 };
38
+ expect(result.action).toBe("fail");
39
+ expect(result.cost).toBe(0.02);
40
+ });
41
+
42
+ test("should support cost field in skip action", () => {
43
+ const result: StageResult = { action: "skip", reason: "Dependency not met", cost: 0.01 };
44
+ expect(result.action).toBe("skip");
45
+ expect(result.cost).toBe(0.01);
46
+ });
47
+
48
+ test("should support cost field in escalate action", () => {
49
+ const result: StageResult = { action: "escalate", cost: 0.03 };
50
+ expect(result.action).toBe("escalate");
51
+ expect(result.cost).toBe(0.03);
52
+ });
53
+
54
+ test("should support cost field in pause action", () => {
55
+ const result: StageResult = { action: "pause", reason: "User requested", cost: 0.01 };
56
+ expect(result.action).toBe("pause");
57
+ expect(result.cost).toBe(0.01);
58
+ });
59
+
60
+ test("should allow omitting cost field (backward compatibility)", () => {
61
+ const result: StageResult = { action: "continue" };
62
+ expect(result.action).toBe("continue");
63
+ expect(result.cost).toBeUndefined();
64
+ });
65
+ });
66
+
67
+ describe("PipelineEventEmitter - Cost in story:complete (BUG-1)", () => {
68
+ test("should emit story:complete with cost field", () => {
69
+ const emitter = new PipelineEventEmitter();
70
+ const story = createMockStory("US-001");
71
+
72
+ const events: Array<{ story: UserStory; result: StageResult }> = [];
73
+ emitter.on("story:complete", (story, result) => {
74
+ events.push({ story, result });
75
+ });
76
+
77
+ const result: StageResult = { action: "continue", cost: 0.05 };
78
+ emitter.emit("story:complete", story, result);
79
+
80
+ expect(events).toHaveLength(1);
81
+ expect(events[0].story.id).toBe("US-001");
82
+ expect(events[0].result.cost).toBe(0.05);
83
+ });
84
+
85
+ test("should emit story:complete without cost field", () => {
86
+ const emitter = new PipelineEventEmitter();
87
+ const story = createMockStory("US-001");
88
+
89
+ const events: Array<{ story: UserStory; result: StageResult }> = [];
90
+ emitter.on("story:complete", (story, result) => {
91
+ events.push({ story, result });
92
+ });
93
+
94
+ const result: StageResult = { action: "continue" };
95
+ emitter.emit("story:complete", story, result);
96
+
97
+ expect(events).toHaveLength(1);
98
+ expect(events[0].result.cost).toBeUndefined();
99
+ });
100
+ });
101
+
102
+ // ── PTY Line Length Tests (MEM-1) ────────────────────
103
+
104
+ describe("usePty - Line Length Limits (MEM-1)", () => {
105
+ test("should truncate lines exceeding MAX_LINE_LENGTH", async () => {
106
+ // This test verifies the line truncation logic exists
107
+ // We'll check the source code directly since node-pty mocking is complex
108
+
109
+ const usePtySource = await Bun.file("src/tui/hooks/usePty.ts").text();
110
+
111
+ // Verify MAX_LINE_LENGTH constant exists
112
+ expect(usePtySource).toContain("const MAX_LINE_LENGTH = 10_000");
113
+
114
+ // Verify truncation logic for complete lines
115
+ expect(usePtySource).toContain("line.length > MAX_LINE_LENGTH");
116
+ expect(usePtySource).toContain("`${line.slice(0, MAX_LINE_LENGTH)}…` : line");
117
+
118
+ // Verify truncation logic for incomplete lines (currentLine)
119
+ expect(usePtySource).toContain("if (currentLine.length > MAX_LINE_LENGTH)");
120
+ expect(usePtySource).toContain("currentLine = currentLine.slice(-MAX_LINE_LENGTH)");
121
+ });
122
+
123
+ test("should have MAX_LINE_LENGTH constant set to 10000", async () => {
124
+ const usePtySource = await Bun.file("src/tui/hooks/usePty.ts").text();
125
+ const match = usePtySource.match(/const MAX_LINE_LENGTH = ([\d_]+)/);
126
+
127
+ expect(match).toBeTruthy();
128
+ expect(match?.[1]).toBe("10_000");
129
+ });
130
+
131
+ test("PTY truncation behavior - unit test for truncation logic", () => {
132
+ const MAX_LINE_LENGTH = 10_000;
133
+
134
+ // Simulate the truncation logic
135
+ const longLine = "x".repeat(15_000);
136
+ const truncatedLine = longLine.length > MAX_LINE_LENGTH ? longLine.slice(0, MAX_LINE_LENGTH) + "…" : longLine;
137
+
138
+ expect(truncatedLine.length).toBe(MAX_LINE_LENGTH + 1); // +1 for ellipsis
139
+ expect(truncatedLine.endsWith("…")).toBe(true);
140
+ expect(truncatedLine.startsWith("x".repeat(100))).toBe(true);
141
+ });
142
+
143
+ test("PTY incomplete line truncation - unit test for currentLine logic", () => {
144
+ const MAX_LINE_LENGTH = 10_000;
145
+
146
+ // Simulate incomplete line accumulation
147
+ let currentLine = "y".repeat(15_000);
148
+
149
+ // Apply truncation (keep last N chars)
150
+ if (currentLine.length > MAX_LINE_LENGTH) {
151
+ currentLine = currentLine.slice(-MAX_LINE_LENGTH);
152
+ }
153
+
154
+ expect(currentLine.length).toBe(MAX_LINE_LENGTH);
155
+ expect(currentLine).toBe("y".repeat(MAX_LINE_LENGTH));
156
+ });
157
+ });
158
+
159
+ // ── Integration Test: Cost Events ────────────────────
160
+
161
+ describe("Integration - Cost in multiple story:complete events", () => {
162
+ test("should emit multiple story:complete events with different costs", () => {
163
+ const emitter = new PipelineEventEmitter();
164
+ const stories = [createMockStory("US-001"), createMockStory("US-002"), createMockStory("US-003")];
165
+
166
+ const events: Array<{ story: UserStory; result: StageResult }> = [];
167
+ emitter.on("story:complete", (story, result) => {
168
+ events.push({ story, result });
169
+ });
170
+
171
+ // Story 1: passed ($0.05)
172
+ emitter.emit("story:complete", stories[0], { action: "continue", cost: 0.05 });
173
+
174
+ // Story 2: failed ($0.03)
175
+ emitter.emit("story:complete", stories[1], { action: "fail", reason: "test", cost: 0.03 });
176
+
177
+ // Story 3: skipped ($0.01)
178
+ emitter.emit("story:complete", stories[2], { action: "skip", reason: "test", cost: 0.01 });
179
+
180
+ expect(events).toHaveLength(3);
181
+ expect(events[0].result.cost).toBe(0.05);
182
+ expect(events[1].result.cost).toBe(0.03);
183
+ expect(events[2].result.cost).toBe(0.01);
184
+
185
+ // Verify total cost would be sum
186
+ const totalCost = events.reduce((sum, e) => sum + (e.result.cost || 0), 0);
187
+ expect(totalCost).toBe(0.09);
188
+ });
189
+ });