@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,461 @@
1
+ /**
2
+ * Runner config plugins integration test (US-007)
3
+ *
4
+ * Verifies that plugins[] from nax/config.json are passed to loadPlugins()
5
+ * when the runner starts. This is the missing integration test that ensures
6
+ * the config loader and plugin loader work together correctly.
7
+ *
8
+ * Focus: Integration between loadConfig() and loadPlugins() in runner.ts
9
+ */
10
+
11
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
12
+ import * as fs from "node:fs/promises";
13
+ import * as os from "node:os";
14
+ import * as path from "node:path";
15
+ import { loadConfig } from "../../src/config/loader";
16
+ import { loadPlugins, _setPluginErrorSink, _resetPluginErrorSink } from "../../src/plugins/loader";
17
+
18
+ async function createTempDir(): Promise<string> {
19
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "nax-runner-config-plugins-"));
20
+ return tmpDir;
21
+ }
22
+
23
+ async function cleanupTempDir(dir: string): Promise<void> {
24
+ try {
25
+ await fs.rm(dir, { recursive: true, force: true });
26
+ } catch {
27
+ // Ignore cleanup errors
28
+ }
29
+ }
30
+
31
+ describe("Runner config plugins integration (US-007)", () => {
32
+ let projectRoot: string;
33
+ let naxDir: string;
34
+ let tempGlobalPluginsDir: string;
35
+
36
+ beforeEach(async () => {
37
+ projectRoot = await createTempDir();
38
+ naxDir = path.join(projectRoot, "nax");
39
+ await fs.mkdir(naxDir, { recursive: true });
40
+ tempGlobalPluginsDir = await fs.mkdtemp(path.join(os.tmpdir(), "nax-test-global-plugins-"));
41
+ });
42
+
43
+ afterEach(async () => {
44
+ await cleanupTempDir(projectRoot);
45
+ await cleanupTempDir(tempGlobalPluginsDir);
46
+ });
47
+
48
+ test("config.plugins[] entries are passed to loadPlugins() when runner initializes", async () => {
49
+ // Create a custom plugin directory
50
+ const customPluginsDir = path.join(projectRoot, "custom-plugins");
51
+ await fs.mkdir(customPluginsDir, { recursive: true });
52
+
53
+ // Track plugin initialization
54
+ const initTracker = path.join(projectRoot, "init-tracker.json");
55
+
56
+ // Create a test plugin
57
+ const testPluginCode = `
58
+ export default {
59
+ name: "config-test-plugin",
60
+ version: "1.0.0",
61
+ provides: ["optimizer"],
62
+ async setup(config) {
63
+ const fs = await import("node:fs/promises");
64
+ await fs.writeFile("${initTracker}", JSON.stringify({
65
+ initialized: true,
66
+ config: config
67
+ }), "utf-8");
68
+ },
69
+ extensions: {
70
+ optimizer: {
71
+ name: "test",
72
+ async optimize(input) {
73
+ return {
74
+ optimizedPrompt: input.prompt,
75
+ estimatedTokens: input.estimatedTokens,
76
+ tokensSaved: 0,
77
+ appliedStrategies: []
78
+ };
79
+ }
80
+ }
81
+ }
82
+ };
83
+ `;
84
+ await fs.writeFile(path.join(customPluginsDir, "test-plugin.ts"), testPluginCode, "utf-8");
85
+
86
+ // Create nax/config.json with plugins[] array
87
+ const configContent = {
88
+ plugins: [
89
+ {
90
+ module: "./custom-plugins/test-plugin.ts",
91
+ config: {
92
+ testKey: "test-value-123",
93
+ enabled: true,
94
+ },
95
+ },
96
+ ],
97
+ };
98
+ await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
99
+
100
+ // Step 1: Load config (as runner does)
101
+ const config = await loadConfig(naxDir);
102
+
103
+ // Step 2: Verify plugins[] is loaded into config
104
+ expect(config.plugins).toBeDefined();
105
+ expect(config.plugins).toHaveLength(1);
106
+ expect(config.plugins?.[0].module).toBe("./custom-plugins/test-plugin.ts");
107
+
108
+ // Step 3: Simulate what runner does - pass config.plugins to loadPlugins()
109
+ const globalPluginsDir = tempGlobalPluginsDir;
110
+ const projectPluginsDir = path.join(naxDir, "plugins");
111
+ const configPlugins = config.plugins || [];
112
+
113
+ const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
114
+
115
+ // Step 4: Verify plugin was loaded and initialized
116
+ expect(registry.plugins).toHaveLength(1);
117
+ expect(registry.plugins[0].name).toBe("config-test-plugin");
118
+
119
+ // Step 5: Verify plugin's setup() was called with the correct config
120
+ const tracker = JSON.parse(await fs.readFile(initTracker, "utf-8"));
121
+ expect(tracker.initialized).toBe(true);
122
+ expect(tracker.config).toEqual({
123
+ testKey: "test-value-123",
124
+ enabled: true,
125
+ });
126
+ });
127
+
128
+ test("relative plugin paths in config.plugins[] are resolved relative to project root", async () => {
129
+ // Create plugin in a subdirectory
130
+ const pluginSubdir = path.join(projectRoot, "lib", "plugins");
131
+ await fs.mkdir(pluginSubdir, { recursive: true });
132
+
133
+ const initTracker = path.join(projectRoot, "relative-path-tracker.json");
134
+
135
+ const pluginCode = `
136
+ export default {
137
+ name: "relative-path-plugin",
138
+ version: "1.0.0",
139
+ provides: ["optimizer"],
140
+ async setup(config) {
141
+ const fs = await import("node:fs/promises");
142
+ await fs.writeFile("${initTracker}", JSON.stringify({ loaded: true }), "utf-8");
143
+ },
144
+ extensions: {
145
+ optimizer: {
146
+ name: "test",
147
+ async optimize(input) {
148
+ return {
149
+ optimizedPrompt: input.prompt,
150
+ estimatedTokens: input.estimatedTokens,
151
+ tokensSaved: 0,
152
+ appliedStrategies: []
153
+ };
154
+ }
155
+ }
156
+ }
157
+ };
158
+ `;
159
+ await fs.writeFile(path.join(pluginSubdir, "plugin.ts"), pluginCode, "utf-8");
160
+
161
+ // Create config with relative path
162
+ const configContent = {
163
+ plugins: [
164
+ {
165
+ module: "./lib/plugins/plugin.ts",
166
+ config: {},
167
+ },
168
+ ],
169
+ };
170
+ await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
171
+
172
+ // Load config
173
+ const config = await loadConfig(naxDir);
174
+ expect(config.plugins?.[0].module).toBe("./lib/plugins/plugin.ts");
175
+
176
+ // Pass to loadPlugins with projectRoot (as runner does)
177
+ const globalPluginsDir = tempGlobalPluginsDir;
178
+ const projectPluginsDir = path.join(naxDir, "plugins");
179
+ const configPlugins = config.plugins || [];
180
+
181
+ const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
182
+
183
+ // Verify plugin was loaded
184
+ expect(registry.plugins).toHaveLength(1);
185
+ expect(registry.plugins[0].name).toBe("relative-path-plugin");
186
+
187
+ // Verify it was actually initialized (proves path resolution worked)
188
+ const tracker = JSON.parse(await fs.readFile(initTracker, "utf-8"));
189
+ expect(tracker.loaded).toBe(true);
190
+ });
191
+
192
+ test("absolute plugin paths in config.plugins[] work without project root resolution", async () => {
193
+ // Create plugin at an absolute path
194
+ const absolutePluginDir = path.join(projectRoot, "absolute-location");
195
+ await fs.mkdir(absolutePluginDir, { recursive: true });
196
+
197
+ const initTracker = path.join(projectRoot, "absolute-tracker.json");
198
+
199
+ const pluginCode = `
200
+ export default {
201
+ name: "absolute-plugin",
202
+ version: "1.0.0",
203
+ provides: ["optimizer"],
204
+ async setup(config) {
205
+ const fs = await import("node:fs/promises");
206
+ await fs.writeFile("${initTracker}", JSON.stringify({ absolutePath: true }), "utf-8");
207
+ },
208
+ extensions: {
209
+ optimizer: {
210
+ name: "test",
211
+ async optimize(input) {
212
+ return {
213
+ optimizedPrompt: input.prompt,
214
+ estimatedTokens: input.estimatedTokens,
215
+ tokensSaved: 0,
216
+ appliedStrategies: []
217
+ };
218
+ }
219
+ }
220
+ }
221
+ };
222
+ `;
223
+ const absolutePluginPath = path.join(absolutePluginDir, "plugin.ts");
224
+ await fs.writeFile(absolutePluginPath, pluginCode, "utf-8");
225
+
226
+ // Create config with absolute path
227
+ const configContent = {
228
+ plugins: [
229
+ {
230
+ module: absolutePluginPath,
231
+ config: {},
232
+ },
233
+ ],
234
+ };
235
+ await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
236
+
237
+ // Load config
238
+ const config = await loadConfig(naxDir);
239
+ expect(config.plugins?.[0].module).toBe(absolutePluginPath);
240
+
241
+ // Pass to loadPlugins
242
+ const globalPluginsDir = tempGlobalPluginsDir;
243
+ const projectPluginsDir = path.join(naxDir, "plugins");
244
+ const configPlugins = config.plugins || [];
245
+
246
+ const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
247
+
248
+ // Verify plugin was loaded
249
+ expect(registry.plugins).toHaveLength(1);
250
+ expect(registry.plugins[0].name).toBe("absolute-plugin");
251
+
252
+ // Verify it was initialized
253
+ const tracker = JSON.parse(await fs.readFile(initTracker, "utf-8"));
254
+ expect(tracker.absolutePath).toBe(true);
255
+ });
256
+
257
+ test("missing plugin module from config.plugins[] logs clear error (does not crash runner)", async () => {
258
+ // Capture plugin error output via the swappable sink (Bun ESM caches console at import time)
259
+ const errorLogs: string[] = [];
260
+ _setPluginErrorSink((...args: unknown[]) => {
261
+ errorLogs.push(args.map((arg) => String(arg)).join(" "));
262
+ });
263
+
264
+ try {
265
+ // Create config with non-existent plugin
266
+ const configContent = {
267
+ plugins: [
268
+ {
269
+ module: "./nonexistent/missing-plugin.ts",
270
+ config: {},
271
+ },
272
+ ],
273
+ };
274
+ await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
275
+
276
+ // Load config
277
+ const config = await loadConfig(naxDir);
278
+ expect(config.plugins).toBeDefined();
279
+ expect(config.plugins).toHaveLength(1);
280
+
281
+ // Pass to loadPlugins (should not throw)
282
+ const globalPluginsDir = tempGlobalPluginsDir;
283
+ const projectPluginsDir = path.join(naxDir, "plugins");
284
+ const configPlugins = config.plugins || [];
285
+
286
+ const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
287
+
288
+ // Should return empty registry (plugin failed to load)
289
+ expect(registry.plugins).toHaveLength(0);
290
+
291
+ // Verify helpful error was logged
292
+ const errorOutput = errorLogs.join("\n");
293
+ expect(errorOutput).toContain("Failed to load plugin module");
294
+ expect(errorOutput).toContain("./nonexistent/missing-plugin.ts");
295
+ expect(errorOutput).toContain("Attempted path:");
296
+ expect(errorOutput).toContain(path.join(projectRoot, "nonexistent/missing-plugin.ts"));
297
+ } finally {
298
+ _resetPluginErrorSink();
299
+ }
300
+ });
301
+
302
+ test("empty config.plugins[] array results in no config-based plugins loaded", async () => {
303
+ // Create config with empty plugins array
304
+ const configContent = {
305
+ plugins: [],
306
+ };
307
+ await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
308
+
309
+ // Load config
310
+ const config = await loadConfig(naxDir);
311
+ expect(config.plugins).toBeDefined();
312
+ expect(config.plugins).toHaveLength(0);
313
+
314
+ // Pass to loadPlugins
315
+ const globalPluginsDir = tempGlobalPluginsDir;
316
+ const projectPluginsDir = path.join(naxDir, "plugins");
317
+ const configPlugins = config.plugins || [];
318
+
319
+ const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
320
+
321
+ // Should have no plugins (no global, no project, no config)
322
+ expect(registry.plugins).toHaveLength(0);
323
+ });
324
+
325
+ test("undefined config.plugins in project config uses global config plugins if present", async () => {
326
+ // Create config without plugins field
327
+ const configContent = {
328
+ routing: {
329
+ strategy: "keyword",
330
+ },
331
+ };
332
+ await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
333
+
334
+ // Load config
335
+ const config = await loadConfig(naxDir);
336
+ // config.plugins may be defined from global config or undefined
337
+ // The key is that runner should handle both cases with: config.plugins || []
338
+
339
+ // Simulate runner's fallback: config.plugins || []
340
+ const configPlugins = config.plugins || [];
341
+ expect(Array.isArray(configPlugins)).toBe(true);
342
+
343
+ // Pass to loadPlugins
344
+ const globalPluginsDir = tempGlobalPluginsDir;
345
+ const projectPluginsDir = path.join(naxDir, "plugins");
346
+
347
+ // Should not throw
348
+ const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
349
+
350
+ // Registry should be valid (may have plugins from global config)
351
+ expect(registry).toBeDefined();
352
+ expect(Array.isArray(registry.plugins)).toBe(true);
353
+ });
354
+
355
+ test("config.plugins[] takes precedence over auto-discovered plugins (name collision)", async () => {
356
+ // Create auto-discovery plugin in project plugins directory
357
+ const projectPluginsDir = path.join(naxDir, "plugins");
358
+ await fs.mkdir(projectPluginsDir, { recursive: true });
359
+
360
+ const initTracker = path.join(projectRoot, "precedence-tracker.json");
361
+ const initOrder: string[] = [];
362
+
363
+ const autoDiscoveredPluginCode = `
364
+ export default {
365
+ name: "collision-plugin",
366
+ version: "1.0.0",
367
+ provides: ["optimizer"],
368
+ async setup(config) {
369
+ const fs = await import("node:fs/promises");
370
+ let tracker = [];
371
+ try {
372
+ tracker = JSON.parse(await fs.readFile("${initTracker}", "utf-8"));
373
+ } catch {}
374
+ tracker.push("auto-discovered");
375
+ await fs.writeFile("${initTracker}", JSON.stringify(tracker), "utf-8");
376
+ },
377
+ extensions: {
378
+ optimizer: {
379
+ name: "auto",
380
+ async optimize(input) {
381
+ return {
382
+ optimizedPrompt: input.prompt,
383
+ estimatedTokens: input.estimatedTokens,
384
+ tokensSaved: 0,
385
+ appliedStrategies: []
386
+ };
387
+ }
388
+ }
389
+ }
390
+ };
391
+ `;
392
+ await fs.writeFile(path.join(projectPluginsDir, "plugin.ts"), autoDiscoveredPluginCode, "utf-8");
393
+
394
+ // Create config-specified plugin with same name
395
+ const customPluginsDir = path.join(projectRoot, "custom");
396
+ await fs.mkdir(customPluginsDir, { recursive: true });
397
+
398
+ const configPluginCode = `
399
+ export default {
400
+ name: "collision-plugin",
401
+ version: "2.0.0",
402
+ provides: ["optimizer"],
403
+ async setup(config) {
404
+ const fs = await import("node:fs/promises");
405
+ let tracker = [];
406
+ try {
407
+ tracker = JSON.parse(await fs.readFile("${initTracker}", "utf-8"));
408
+ } catch {}
409
+ tracker.push("config-specified");
410
+ await fs.writeFile("${initTracker}", JSON.stringify(tracker), "utf-8");
411
+ },
412
+ extensions: {
413
+ optimizer: {
414
+ name: "config",
415
+ async optimize(input) {
416
+ return {
417
+ optimizedPrompt: input.prompt,
418
+ estimatedTokens: input.estimatedTokens,
419
+ tokensSaved: 0,
420
+ appliedStrategies: []
421
+ };
422
+ }
423
+ }
424
+ }
425
+ };
426
+ `;
427
+ await fs.writeFile(path.join(customPluginsDir, "plugin.ts"), configPluginCode, "utf-8");
428
+
429
+ // Create config with explicit plugin
430
+ const configContent = {
431
+ plugins: [
432
+ {
433
+ module: "./custom/plugin.ts",
434
+ config: {},
435
+ },
436
+ ],
437
+ };
438
+ await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
439
+
440
+ // Load config
441
+ const config = await loadConfig(naxDir);
442
+
443
+ // Pass to loadPlugins
444
+ const globalPluginsDir = tempGlobalPluginsDir;
445
+ const configPlugins = config.plugins || [];
446
+
447
+ const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
448
+
449
+ // Should have both plugins loaded (name collision allowed, last one wins)
450
+ expect(registry.plugins.length).toBeGreaterThanOrEqual(1);
451
+
452
+ // Verify init order shows auto-discovered loads first, then config
453
+ const tracker = JSON.parse(await fs.readFile(initTracker, "utf-8"));
454
+ expect(tracker).toContain("auto-discovered");
455
+ expect(tracker).toContain("config-specified");
456
+
457
+ // Config plugin should be loaded last (overrides auto-discovered)
458
+ const lastIndex = tracker.lastIndexOf("config-specified");
459
+ expect(lastIndex).toBeGreaterThan(tracker.indexOf("auto-discovered"));
460
+ });
461
+ });