@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,167 @@
1
+ /**
2
+ * Pipeline Framework Types
3
+ *
4
+ * Composable stage-based execution pipeline for refactoring the monolithic runner.
5
+ */
6
+
7
+ import type { AgentResult } from "../agents/types";
8
+ import type { NaxConfig } from "../config/schema";
9
+ import type { ConstitutionResult } from "../constitution/types";
10
+ import type { BuiltContext } from "../context/types";
11
+ import type { HooksConfig } from "../hooks/types";
12
+ import type { InteractionChain } from "../interaction/chain";
13
+ import type { StoryMetrics } from "../metrics/types";
14
+ import type { PluginRegistry } from "../plugins/registry";
15
+ import type { PRD, UserStory } from "../prd/types";
16
+ import type { ReviewResult } from "../review/types";
17
+ import type { FailureCategory } from "../tdd/types";
18
+
19
+ /**
20
+ * Routing result from complexity classification
21
+ */
22
+ export interface RoutingResult {
23
+ /** Classified complexity */
24
+ complexity: "simple" | "medium" | "complex" | "expert";
25
+ /** Selected model tier */
26
+ modelTier: "fast" | "balanced" | "powerful";
27
+ /** Test strategy */
28
+ testStrategy: "test-after" | "three-session-tdd" | "three-session-tdd-lite";
29
+ /** Reasoning for the classification */
30
+ reasoning: string;
31
+ /** Estimated cost for this story */
32
+ estimatedCost?: number;
33
+ }
34
+
35
+ /**
36
+ * Pipeline context — shared state passed through all stages.
37
+ *
38
+ * Stages read from and write to this context. It accumulates data
39
+ * as the pipeline progresses through each stage.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * const ctx: PipelineContext = {
44
+ * config: loadedConfig,
45
+ * prd: loadedPRD,
46
+ * story: currentStory,
47
+ * stories: [currentStory],
48
+ * routing: { complexity: "simple", modelTier: "fast", ... },
49
+ * workdir: "/home/user/project",
50
+ * hooks: loadedHooks,
51
+ * };
52
+ * ```
53
+ */
54
+ export interface PipelineContext {
55
+ /** Ngent configuration */
56
+ config: NaxConfig;
57
+ /** Full PRD document */
58
+ prd: PRD;
59
+ /** Current story (or batch leader) */
60
+ story: UserStory;
61
+ /** Batch of stories (length 1 for single-story execution) */
62
+ stories: UserStory[];
63
+ /** Routing result from complexity classification */
64
+ routing: RoutingResult;
65
+ /** Working directory (project root) */
66
+ workdir: string;
67
+ /** Feature directory (optional, e.g., nax/features/my-feature/) */
68
+ featureDir?: string;
69
+ /** Hooks configuration */
70
+ hooks: HooksConfig;
71
+ /** Plugin registry (optional, for plugin-provided extensions) */
72
+ plugins?: PluginRegistry;
73
+ /** Interaction chain (optional, for human-in-the-loop triggers) */
74
+ interaction?: InteractionChain;
75
+ /** Constitution result (set by constitutionStage) */
76
+ constitution?: ConstitutionResult;
77
+ /** Context markdown for the agent (set by contextStage) */
78
+ contextMarkdown?: string;
79
+ /** Built context with element-level token tracking (set by contextStage) */
80
+ builtContext?: BuiltContext;
81
+ /** Final prompt sent to agent (set by promptStage) */
82
+ prompt?: string;
83
+ /** Agent execution result (set by executionStage) */
84
+ agentResult?: AgentResult;
85
+ /** Review result (set by reviewStage) */
86
+ reviewResult?: ReviewResult;
87
+ /** Acceptance test failures (set by acceptanceStage) */
88
+ acceptanceFailures?: {
89
+ failedACs: string[];
90
+ testOutput: string;
91
+ };
92
+ /** Story start timestamp (ISO string, set by runner before pipeline) */
93
+ storyStartTime?: string;
94
+ /** Collected story metrics (set by completionStage) */
95
+ storyMetrics?: StoryMetrics[];
96
+ /** Whether to retry the story in lite mode after a failure */
97
+ retryAsLite?: boolean;
98
+ /** Failure category from TDD orchestrator (set by executionStage on TDD failure) */
99
+ tddFailureCategory?: FailureCategory;
100
+ }
101
+
102
+ /**
103
+ * Stage action — determines how the pipeline proceeds after a stage executes.
104
+ */
105
+ export type StageAction =
106
+ /** Continue to the next stage */
107
+ | { action: "continue"; cost?: number }
108
+ /** Skip this story (mark as skipped, don't run further stages) */
109
+ | { action: "skip"; reason: string; cost?: number }
110
+ /** Mark story as failed (don't run further stages) */
111
+ | { action: "fail"; reason: string; cost?: number }
112
+ /** Escalate to a higher tier and retry the pipeline */
113
+ | { action: "escalate"; reason?: string; cost?: number }
114
+ /** Pause execution (user intervention required via queue command) */
115
+ | { action: "pause"; reason: string; cost?: number };
116
+
117
+ /**
118
+ * Result returned by a pipeline stage after execution.
119
+ */
120
+ export type StageResult = StageAction;
121
+
122
+ /**
123
+ * A single pipeline stage.
124
+ *
125
+ * Stages are composable units of work that execute sequentially.
126
+ * Each stage can read from and modify the pipeline context, then
127
+ * return an action that determines whether to continue, skip, fail,
128
+ * escalate, or pause.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * const routingStage: PipelineStage = {
133
+ * name: "routing",
134
+ * enabled: (ctx) => true,
135
+ * execute: async (ctx) => {
136
+ * const result = await classifyComplexity(ctx.story);
137
+ * ctx.routing = result;
138
+ * return { action: "continue" };
139
+ * },
140
+ * };
141
+ * ```
142
+ */
143
+ export interface PipelineStage {
144
+ /** Unique stage identifier (e.g., "routing", "execution", "review") */
145
+ name: string;
146
+
147
+ /**
148
+ * Determines if this stage should run.
149
+ *
150
+ * If false, the stage is skipped and the pipeline continues to the next stage.
151
+ *
152
+ * @param ctx - Current pipeline context
153
+ * @returns true if the stage should execute, false to skip
154
+ */
155
+ enabled: (ctx: PipelineContext) => boolean;
156
+
157
+ /**
158
+ * Execute the stage logic.
159
+ *
160
+ * Can read from and modify the pipeline context, then returns a result
161
+ * that determines how the pipeline should proceed.
162
+ *
163
+ * @param ctx - Current pipeline context
164
+ * @returns Stage result indicating next action
165
+ */
166
+ execute: (ctx: PipelineContext) => Promise<StageResult>;
167
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Plugin System — Public API
3
+ *
4
+ * Exports all plugin types, interfaces, and loading utilities.
5
+ */
6
+
7
+ export type {
8
+ NaxPlugin,
9
+ PluginType,
10
+ PluginExtensions,
11
+ PluginConfigEntry,
12
+ IReviewPlugin,
13
+ ReviewCheckResult,
14
+ IContextProvider,
15
+ ContextProviderResult,
16
+ IReporter,
17
+ RunStartEvent,
18
+ StoryCompleteEvent,
19
+ RunEndEvent,
20
+ } from "./types";
21
+
22
+ // Re-export optimizer types from optimizer module (via types.ts)
23
+ export type {
24
+ IPromptOptimizer,
25
+ PromptOptimizerInput,
26
+ PromptOptimizerResult,
27
+ } from "./types";
28
+
29
+ export { validatePlugin } from "./validator";
30
+ export { loadPlugins } from "./loader";
31
+ export { PluginRegistry } from "./registry";
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Plugin Loader
3
+ *
4
+ * Discovers, imports, validates, and initializes plugins from:
5
+ * 1. Global directory (~/.nax/plugins/)
6
+ * 2. Project directory (<project>/nax/plugins/)
7
+ * 3. Config entries (explicit module paths)
8
+ */
9
+
10
+ import * as fs from "node:fs/promises";
11
+ import * as path from "node:path";
12
+ import { getSafeLogger as _getSafeLoggerFromModule } from "../logger";
13
+ import { PluginRegistry } from "./registry";
14
+ import type { NaxPlugin, PluginConfigEntry } from "./types";
15
+ import { validatePlugin } from "./validator";
16
+
17
+ /**
18
+ * Swappable error sink — defaults to console.error.
19
+ * Tests can replace this to capture plugin error output.
20
+ * @internal
21
+ */
22
+ export let _pluginErrorSink: (...args: unknown[]) => void = (...args) => console.error(...args);
23
+
24
+ /** @internal — for testing only */
25
+ export function _setPluginErrorSink(fn: (...args: unknown[]) => void): void {
26
+ _pluginErrorSink = fn;
27
+ }
28
+
29
+ /** @internal — reset to default */
30
+ export function _resetPluginErrorSink(): void {
31
+ _pluginErrorSink = (...args) => console.error(...args);
32
+ }
33
+
34
+ /**
35
+ * Safely get logger instance, returns null if not initialized.
36
+ * Delegates to the module's getSafeLogger which correctly returns null for noopLogger.
37
+ */
38
+ function getSafeLogger() {
39
+ return _getSafeLoggerFromModule();
40
+ }
41
+
42
+ /**
43
+ * Plugin source metadata.
44
+ */
45
+ export interface PluginSource {
46
+ type: "global" | "project" | "config";
47
+ path: string;
48
+ }
49
+
50
+ /**
51
+ * Plugin with source information.
52
+ */
53
+ export interface LoadedPlugin {
54
+ plugin: NaxPlugin;
55
+ source: PluginSource;
56
+ }
57
+
58
+ /**
59
+ * Load and validate all plugins from global + project + config sources.
60
+ *
61
+ * Load order:
62
+ * 1. Scan ~/.nax/plugins/ (if exists)
63
+ * 2. Scan <project>/nax/plugins/ (if exists)
64
+ * 3. Load explicit modules from config.plugins[]
65
+ *
66
+ * Each plugin is validated, then setup() is called with its config.
67
+ *
68
+ * @param globalDir - Global plugins directory (e.g., ~/.nax/plugins)
69
+ * @param projectDir - Project plugins directory (e.g., <project>/nax/plugins)
70
+ * @param configPlugins - Explicit plugin entries from config
71
+ * @param projectRoot - Project root directory for resolving relative paths in config
72
+ * @returns PluginRegistry with all loaded plugins and their sources
73
+ */
74
+ export async function loadPlugins(
75
+ globalDir: string,
76
+ projectDir: string,
77
+ configPlugins: PluginConfigEntry[],
78
+ projectRoot?: string,
79
+ ): Promise<PluginRegistry> {
80
+ const loadedPlugins: LoadedPlugin[] = [];
81
+ const pluginNames = new Set<string>();
82
+
83
+ // 1. Load plugins from global directory
84
+ const globalPlugins = await discoverPlugins(globalDir);
85
+ for (const plugin of globalPlugins) {
86
+ const validated = await loadAndValidatePlugin(plugin.path, {});
87
+ if (validated) {
88
+ if (pluginNames.has(validated.name)) {
89
+ const logger = getSafeLogger();
90
+ logger?.warn("plugins", `Plugin name collision: '${validated.name}' (global directory)`);
91
+ }
92
+ loadedPlugins.push({
93
+ plugin: validated,
94
+ source: { type: "global", path: plugin.path },
95
+ });
96
+ pluginNames.add(validated.name);
97
+ }
98
+ }
99
+
100
+ // 2. Load plugins from project directory
101
+ const projectPlugins = await discoverPlugins(projectDir);
102
+ for (const plugin of projectPlugins) {
103
+ const validated = await loadAndValidatePlugin(plugin.path, {});
104
+ if (validated) {
105
+ if (pluginNames.has(validated.name)) {
106
+ const logger = getSafeLogger();
107
+ logger?.warn("plugins", `Plugin name collision: '${validated.name}' (project directory overrides global)`);
108
+ }
109
+ loadedPlugins.push({
110
+ plugin: validated,
111
+ source: { type: "project", path: plugin.path },
112
+ });
113
+ pluginNames.add(validated.name);
114
+ }
115
+ }
116
+
117
+ // 3. Load plugins from config entries
118
+ for (const entry of configPlugins) {
119
+ // Resolve module path relative to project root for relative paths
120
+ const resolvedModule = resolveModulePath(entry.module, projectRoot);
121
+ const validated = await loadAndValidatePlugin(resolvedModule, entry.config ?? {}, entry.module);
122
+ if (validated) {
123
+ if (pluginNames.has(validated.name)) {
124
+ const logger = getSafeLogger();
125
+ logger?.warn("plugins", `Plugin name collision: '${validated.name}' (config entry overrides previous)`);
126
+ }
127
+ loadedPlugins.push({
128
+ plugin: validated,
129
+ source: { type: "config", path: entry.module },
130
+ });
131
+ pluginNames.add(validated.name);
132
+ }
133
+ }
134
+
135
+ return new PluginRegistry(loadedPlugins);
136
+ }
137
+
138
+ /**
139
+ * Discover plugin files in a directory.
140
+ *
141
+ * Scans for:
142
+ * - Single-file plugins (*.ts, *.js, *.mjs)
143
+ * - Directory plugins with index.ts/index.js/index.mjs
144
+ *
145
+ * @param dir - Directory to scan
146
+ * @returns Array of discovered plugin paths
147
+ */
148
+ async function discoverPlugins(dir: string): Promise<Array<{ path: string }>> {
149
+ const discovered: Array<{ path: string }> = [];
150
+
151
+ try {
152
+ const entries = await fs.readdir(dir, { withFileTypes: true });
153
+
154
+ for (const entry of entries) {
155
+ const fullPath = path.join(dir, entry.name);
156
+
157
+ if (entry.isFile()) {
158
+ // Single-file plugin
159
+ if (isPluginFile(entry.name)) {
160
+ discovered.push({ path: fullPath });
161
+ }
162
+ } else if (entry.isDirectory()) {
163
+ // Directory plugin — check for index file
164
+ const indexPaths = ["index.ts", "index.js", "index.mjs"];
165
+ for (const indexFile of indexPaths) {
166
+ const indexPath = path.join(fullPath, indexFile);
167
+ try {
168
+ await fs.access(indexPath);
169
+ discovered.push({ path: indexPath });
170
+ break;
171
+ } catch {
172
+ // Index file doesn't exist, try next
173
+ }
174
+ }
175
+ }
176
+ }
177
+ } catch (error) {
178
+ // ERR-1 fix: Only catch ENOENT, re-throw other errors
179
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
180
+ // Directory doesn't exist — not an error, just no plugins
181
+ return [];
182
+ }
183
+ // Re-throw permission errors, disk failures, etc.
184
+ throw error;
185
+ }
186
+
187
+ return discovered;
188
+ }
189
+
190
+ /**
191
+ * Check if a filename is a valid plugin file.
192
+ *
193
+ * @param filename - Filename to check
194
+ * @returns Whether the file could be a plugin
195
+ */
196
+ function isPluginFile(filename: string): boolean {
197
+ return /\.(ts|js|mjs)$/.test(filename) && !filename.endsWith(".test.ts") && !filename.endsWith(".spec.ts");
198
+ }
199
+
200
+ /**
201
+ * Resolve a module path, handling relative paths, absolute paths, and npm packages.
202
+ *
203
+ * @param modulePath - Module path from config (can be relative, absolute, or npm package)
204
+ * @param projectRoot - Project root directory for resolving relative paths
205
+ * @returns Resolved absolute path or npm package name
206
+ */
207
+ function resolveModulePath(modulePath: string, projectRoot?: string): string {
208
+ // Absolute paths and npm packages (no leading ./ or ../) work as-is
209
+ if (path.isAbsolute(modulePath) || (!modulePath.startsWith("./") && !modulePath.startsWith("../"))) {
210
+ return modulePath;
211
+ }
212
+
213
+ // Relative paths need to be resolved relative to project root
214
+ if (projectRoot) {
215
+ return path.resolve(projectRoot, modulePath);
216
+ }
217
+
218
+ // Fallback: resolve relative to cwd (shouldn't happen in normal usage)
219
+ return path.resolve(modulePath);
220
+ }
221
+
222
+ /**
223
+ * Load and validate a plugin from a module path.
224
+ *
225
+ * @param modulePath - Path to plugin module (should be resolved)
226
+ * @param config - Plugin-specific config
227
+ * @param originalPath - Original path from config (for error messages)
228
+ * @returns Validated plugin or null if invalid
229
+ */
230
+ async function loadAndValidatePlugin(
231
+ modulePath: string,
232
+ config: Record<string, unknown>,
233
+ originalPath?: string,
234
+ ): Promise<NaxPlugin | null> {
235
+ try {
236
+ // Import the module
237
+ const imported = await import(modulePath);
238
+
239
+ // Try default export first, then named exports
240
+ const module = imported.default || imported;
241
+
242
+ // Validate plugin shape
243
+ const validated = validatePlugin(module);
244
+ if (!validated) {
245
+ return null;
246
+ }
247
+
248
+ // Call setup() if defined
249
+ if (validated.setup) {
250
+ try {
251
+ await validated.setup(config);
252
+ } catch (error) {
253
+ const logger = getSafeLogger();
254
+ logger?.error("plugins", `Plugin '${validated.name}' setup failed`, { error });
255
+ return null;
256
+ }
257
+ }
258
+
259
+ return validated;
260
+ } catch (error) {
261
+ const displayPath = originalPath || modulePath;
262
+ const errorMsg = error instanceof Error ? error.message : String(error);
263
+ const logger = getSafeLogger();
264
+
265
+ // Provide helpful error message with attempted paths
266
+ if (errorMsg.includes("Cannot find module") || errorMsg.includes("ENOENT")) {
267
+ const msg = `Failed to load plugin module '${displayPath}'`;
268
+ logger?.error("plugins", msg);
269
+ logger?.error("plugins", `Attempted path: ${modulePath}`);
270
+ logger?.error(
271
+ "plugins",
272
+ "Ensure the module exists and the path is correct (relative paths are resolved from project root)",
273
+ );
274
+ // Always emit to sink so tests (and headless mode without logger) can capture output
275
+ _pluginErrorSink(`[plugins] ${msg}`);
276
+ _pluginErrorSink(`[plugins] Attempted path: ${modulePath}`);
277
+ _pluginErrorSink(
278
+ "[plugins] Ensure the module exists and the path is correct (relative paths are resolved from project root)",
279
+ );
280
+ } else {
281
+ logger?.warn("plugins", `Failed to load plugin from '${displayPath}'`, { error: errorMsg });
282
+ // Always emit to sink
283
+ _pluginErrorSink(`[plugins] Failed to load plugin from '${displayPath}': ${errorMsg}`);
284
+ }
285
+ return null;
286
+ }
287
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Plugin Registry
3
+ *
4
+ * Central registry for all loaded plugins with typed getters.
5
+ */
6
+
7
+ import type { AgentAdapter } from "../agents/types";
8
+ import { getSafeLogger } from "../logger";
9
+ import type { RoutingStrategy } from "../routing/strategy";
10
+ import type { LoadedPlugin, PluginSource } from "./loader";
11
+ import type { IContextProvider, IPromptOptimizer, IReporter, IReviewPlugin, NaxPlugin } from "./types";
12
+
13
+ /**
14
+ * Plugin registry with typed getters for each extension type.
15
+ *
16
+ * Created once at run start and passed through the pipeline context.
17
+ * Provides efficient access to plugins by extension type.
18
+ */
19
+ export class PluginRegistry {
20
+ /** All loaded plugins (readonly) */
21
+ readonly plugins: ReadonlyArray<NaxPlugin>;
22
+
23
+ /** Plugin source information (maps plugin name to source) */
24
+ private readonly sources: Map<string, PluginSource>;
25
+
26
+ constructor(loadedPlugins: LoadedPlugin[] | NaxPlugin[]) {
27
+ // Support both LoadedPlugin[] and NaxPlugin[] for backward compatibility
28
+ if (loadedPlugins.length > 0 && "plugin" in loadedPlugins[0]) {
29
+ // New format: LoadedPlugin[]
30
+ const typed = loadedPlugins as LoadedPlugin[];
31
+ this.plugins = typed.map((lp) => lp.plugin);
32
+ this.sources = new Map(typed.map((lp) => [lp.plugin.name, lp.source]));
33
+ } else {
34
+ // Legacy format: NaxPlugin[]
35
+ const typed = loadedPlugins as NaxPlugin[];
36
+ this.plugins = typed;
37
+ this.sources = new Map();
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Get the source information for a plugin.
43
+ *
44
+ * @param pluginName - Name of the plugin
45
+ * @returns Plugin source or undefined if not found
46
+ */
47
+ getSource(pluginName: string): PluginSource | undefined {
48
+ return this.sources.get(pluginName);
49
+ }
50
+
51
+ /**
52
+ * Get all prompt optimizers.
53
+ *
54
+ * @returns Array of optimizer implementations
55
+ */
56
+ getOptimizers(): IPromptOptimizer[] {
57
+ return this.plugins
58
+ .filter((p) => p.provides.includes("optimizer"))
59
+ .map((p) => p.extensions.optimizer)
60
+ .filter((opt): opt is IPromptOptimizer => opt !== undefined);
61
+ }
62
+
63
+ /**
64
+ * Get all routing strategies.
65
+ *
66
+ * Plugin routers are returned in load order and should be inserted
67
+ * before built-in strategies in the routing chain.
68
+ *
69
+ * @returns Array of routing strategy implementations
70
+ */
71
+ getRouters(): RoutingStrategy[] {
72
+ return this.plugins
73
+ .filter((p) => p.provides.includes("router"))
74
+ .map((p) => p.extensions.router)
75
+ .filter((router): router is RoutingStrategy => router !== undefined);
76
+ }
77
+
78
+ /**
79
+ * Get agent adapter by name.
80
+ *
81
+ * If multiple plugins provide the same agent name, the last loaded wins.
82
+ *
83
+ * @param name - Agent name to lookup
84
+ * @returns Agent adapter or undefined if not found
85
+ */
86
+ getAgent(name: string): AgentAdapter | undefined {
87
+ const agents = this.plugins
88
+ .filter((p) => p.provides.includes("agent"))
89
+ .map((p) => p.extensions.agent)
90
+ .filter((agent): agent is AgentAdapter => agent !== undefined);
91
+
92
+ // Last loaded wins on name collision
93
+ for (let i = agents.length - 1; i >= 0; i--) {
94
+ if (agents[i].name === name) {
95
+ return agents[i];
96
+ }
97
+ }
98
+
99
+ return undefined;
100
+ }
101
+
102
+ /**
103
+ * Get all review plugins.
104
+ *
105
+ * Review plugins run after built-in checks (typecheck, lint, test).
106
+ * All plugin checks are additive.
107
+ *
108
+ * @returns Array of review plugin implementations
109
+ */
110
+ getReviewers(): IReviewPlugin[] {
111
+ return this.plugins
112
+ .filter((p) => p.provides.includes("reviewer"))
113
+ .map((p) => p.extensions.reviewer)
114
+ .filter((reviewer): reviewer is IReviewPlugin => reviewer !== undefined);
115
+ }
116
+
117
+ /**
118
+ * Get all context providers.
119
+ *
120
+ * Context providers fetch external data (Jira, Linear, etc.) and
121
+ * inject it into agent prompts. All providers are additive, subject
122
+ * to token budget.
123
+ *
124
+ * @returns Array of context provider implementations
125
+ */
126
+ getContextProviders(): IContextProvider[] {
127
+ return this.plugins
128
+ .filter((p) => p.provides.includes("context-provider"))
129
+ .map((p) => p.extensions.contextProvider)
130
+ .filter((provider): provider is IContextProvider => provider !== undefined);
131
+ }
132
+
133
+ /**
134
+ * Get all reporters.
135
+ *
136
+ * Reporters receive run lifecycle events for dashboards, CI, etc.
137
+ * All reporters are additive and fire-and-forget.
138
+ *
139
+ * @returns Array of reporter implementations
140
+ */
141
+ getReporters(): IReporter[] {
142
+ return this.plugins
143
+ .filter((p) => p.provides.includes("reporter"))
144
+ .map((p) => p.extensions.reporter)
145
+ .filter((reporter): reporter is IReporter => reporter !== undefined);
146
+ }
147
+
148
+ /**
149
+ * Teardown all plugins.
150
+ *
151
+ * Calls teardown() on each plugin (if defined) in order.
152
+ * Logs errors but continues teardown for all plugins.
153
+ *
154
+ * Called when the nax run ends (success or failure).
155
+ */
156
+ async teardownAll(): Promise<void> {
157
+ const logger = getSafeLogger();
158
+ for (const plugin of this.plugins) {
159
+ if (plugin.teardown) {
160
+ try {
161
+ await plugin.teardown();
162
+ } catch (error) {
163
+ logger?.error("plugins", `Plugin '${plugin.name}' teardown failed`, { error });
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }