@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,111 @@
1
+ /**
2
+ * Plugins Command
3
+ *
4
+ * Lists loaded plugins with their metadata.
5
+ */
6
+
7
+ import * as os from "node:os";
8
+ import * as path from "node:path";
9
+ import type { NaxConfig } from "../config/schema";
10
+ import { loadPlugins } from "../plugins/loader";
11
+
12
+ /**
13
+ * List all loaded plugins with their metadata.
14
+ *
15
+ * @param config - nax configuration
16
+ * @param workdir - Working directory for resolving plugin paths
17
+ * @param overrideGlobalPluginsDir - Override global plugins directory (used in tests)
18
+ */
19
+ export async function pluginsListCommand(
20
+ config: NaxConfig,
21
+ workdir: string,
22
+ overrideGlobalPluginsDir?: string,
23
+ ): Promise<void> {
24
+ // Load plugins from all sources
25
+ const globalPluginsDir = overrideGlobalPluginsDir ?? path.join(os.homedir(), ".nax", "plugins");
26
+ const projectPluginsDir = path.join(workdir, "nax", "plugins");
27
+ const configPlugins = config.plugins || [];
28
+ const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, workdir);
29
+ const plugins = registry.plugins;
30
+
31
+ if (plugins.length === 0) {
32
+ console.log("No plugins installed.");
33
+ console.log("\nTo install plugins:");
34
+ console.log(" • Add to global directory: ~/.nax/plugins/");
35
+ console.log(" • Add to project directory: ./nax/plugins/");
36
+ console.log(" • Configure in nax/config.json");
37
+ console.log("\nSee https://github.com/nax/nax#plugins for more details.");
38
+ return;
39
+ }
40
+
41
+ // Build table data
42
+ const rows: Array<{
43
+ name: string;
44
+ version: string;
45
+ provides: string;
46
+ source: string;
47
+ }> = plugins.map((plugin) => {
48
+ const source = registry.getSource(plugin.name);
49
+ const sourceStr = source ? formatSource(source.type, source.path) : "unknown";
50
+
51
+ return {
52
+ name: plugin.name,
53
+ version: plugin.version,
54
+ provides: plugin.provides.join(", "),
55
+ source: sourceStr,
56
+ };
57
+ });
58
+
59
+ // Calculate column widths
60
+ const widths = {
61
+ name: Math.max(4, ...rows.map((r) => r.name.length)),
62
+ version: Math.max(7, ...rows.map((r) => r.version.length)),
63
+ provides: Math.max(8, ...rows.map((r) => r.provides.length)),
64
+ source: Math.max(6, ...rows.map((r) => r.source.length)),
65
+ };
66
+
67
+ // Display table
68
+ console.log("\nInstalled Plugins:\n");
69
+ console.log(
70
+ `${pad("Name", widths.name)} ${pad("Version", widths.version)} ${pad("Provides", widths.provides)} ${pad("Source", widths.source)}`,
71
+ );
72
+ console.log(
73
+ `${"-".repeat(widths.name)} ${"-".repeat(widths.version)} ${"-".repeat(widths.provides)} ${"-".repeat(widths.source)}`,
74
+ );
75
+
76
+ for (const row of rows) {
77
+ console.log(
78
+ `${pad(row.name, widths.name)} ${pad(row.version, widths.version)} ${pad(row.provides, widths.provides)} ${pad(row.source, widths.source)}`,
79
+ );
80
+ }
81
+
82
+ console.log();
83
+ }
84
+
85
+ /**
86
+ * Format source type and path for display.
87
+ *
88
+ * @param type - Source type (global/project/config)
89
+ * @param sourcePath - Full path to plugin
90
+ * @returns Formatted source string
91
+ */
92
+ function formatSource(type: "global" | "project" | "config", sourcePath: string): string {
93
+ if (type === "global") {
94
+ return `global (${path.basename(sourcePath)})`;
95
+ }
96
+ if (type === "project") {
97
+ return `project (${path.basename(sourcePath)})`;
98
+ }
99
+ return `config (${sourcePath})`;
100
+ }
101
+
102
+ /**
103
+ * Pad string to width.
104
+ *
105
+ * @param str - String to pad
106
+ * @param width - Target width
107
+ * @returns Padded string
108
+ */
109
+ function pad(str: string, width: number): string {
110
+ return str.padEnd(width);
111
+ }
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Prompts CLI Command
3
+ *
4
+ * Assembles prompts for all stories in a feature without executing agents.
5
+ * Used for debugging prompt isolation and context leakage.
6
+ *
7
+ * Executes: routing → constitution → context → prompt stages only.
8
+ */
9
+
10
+ import { existsSync, mkdirSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import type { NaxConfig } from "../config";
13
+ import type { BuiltContext } from "../context/types";
14
+ import { getLogger } from "../logger";
15
+ import { runPipeline } from "../pipeline";
16
+ import type { PipelineContext } from "../pipeline";
17
+ import { constitutionStage, contextStage, promptStage, routingStage } from "../pipeline/stages";
18
+ import type { UserStory } from "../prd";
19
+ import { loadPRD } from "../prd";
20
+
21
+ export interface PromptsCommandOptions {
22
+ /** Feature name */
23
+ feature: string;
24
+ /** Working directory (project root) */
25
+ workdir: string;
26
+ /** Ngent configuration */
27
+ config: NaxConfig;
28
+ /** Optional: filter to single story ID */
29
+ storyId?: string;
30
+ /** Optional: output directory (stdout if not provided) */
31
+ outputDir?: string;
32
+ }
33
+
34
+ /**
35
+ * Execute the `nax prompts` command.
36
+ *
37
+ * Runs the pipeline through routing → constitution → context → prompt stages
38
+ * for each story, then outputs the assembled prompts to stdout or files.
39
+ *
40
+ * @param options - Command options
41
+ * @returns Array of story IDs that were processed
42
+ *
43
+ * @example
44
+ * ```bash
45
+ * # Dump all story prompts to stdout
46
+ * nax prompts -f core
47
+ *
48
+ * # Write to directory
49
+ * nax prompts -f core --out ./prompt-dump/
50
+ *
51
+ * # Single story
52
+ * nax prompts -f core --story US-003
53
+ * ```
54
+ */
55
+ export async function promptsCommand(options: PromptsCommandOptions): Promise<string[]> {
56
+ const logger = getLogger();
57
+ const { feature, workdir, config, storyId, outputDir } = options;
58
+
59
+ // Find nax directory
60
+ const naxDir = join(workdir, "nax");
61
+ if (!existsSync(naxDir)) {
62
+ throw new Error(`nax directory not found. Run 'nax init' first in ${workdir}`);
63
+ }
64
+
65
+ // Load PRD
66
+ const featureDir = join(naxDir, "features", feature);
67
+ const prdPath = join(featureDir, "prd.json");
68
+
69
+ if (!existsSync(prdPath)) {
70
+ throw new Error(`Feature "${feature}" not found or missing prd.json`);
71
+ }
72
+
73
+ const prd = await loadPRD(prdPath);
74
+
75
+ // Filter stories
76
+ const stories = storyId ? prd.userStories.filter((s) => s.id === storyId) : prd.userStories;
77
+
78
+ if (stories.length === 0) {
79
+ throw new Error(
80
+ storyId ? `Story "${storyId}" not found in feature "${feature}"` : `No stories found in feature "${feature}"`,
81
+ );
82
+ }
83
+
84
+ // Create output directory if specified
85
+ if (outputDir) {
86
+ mkdirSync(outputDir, { recursive: true });
87
+ }
88
+
89
+ logger.info("cli", "Assembling prompts", {
90
+ feature,
91
+ storyCount: stories.length,
92
+ outputMode: outputDir ? "files" : "stdout",
93
+ });
94
+
95
+ // Process each story through the pipeline (routing → constitution → context → prompt)
96
+ const processedStories: string[] = [];
97
+ const promptPipeline = [routingStage, constitutionStage, contextStage, promptStage];
98
+
99
+ for (const story of stories) {
100
+ // Build initial pipeline context
101
+ const ctx: PipelineContext = {
102
+ config,
103
+ prd,
104
+ story,
105
+ stories: [story], // Single story, not batch
106
+ routing: {
107
+ complexity: "simple",
108
+ modelTier: "fast",
109
+ testStrategy: "test-after",
110
+ reasoning: "Placeholder routing",
111
+ }, // Will be set by routingStage
112
+ workdir,
113
+ featureDir,
114
+ hooks: { hooks: {} }, // Empty hooks config
115
+ };
116
+
117
+ // Run the prompt assembly pipeline
118
+ const result = await runPipeline(promptPipeline, ctx);
119
+
120
+ if (!result.success) {
121
+ logger.warn("cli", "Failed to assemble prompt for story", {
122
+ storyId: story.id,
123
+ reason: result.reason,
124
+ });
125
+ continue;
126
+ }
127
+
128
+ // Handle three-session TDD stories separately
129
+ if (ctx.routing.testStrategy === "three-session-tdd") {
130
+ await handleThreeSessionTddPrompts(story, ctx, outputDir, logger);
131
+ processedStories.push(story.id);
132
+ continue;
133
+ }
134
+
135
+ // For non-TDD stories, ensure prompt was built
136
+ if (!ctx.prompt) {
137
+ logger.warn("cli", "No prompt generated for story", {
138
+ storyId: story.id,
139
+ });
140
+ continue;
141
+ }
142
+
143
+ // Build YAML frontmatter
144
+ const frontmatter = buildFrontmatter(story, ctx);
145
+
146
+ // Full output: frontmatter + prompt
147
+ const fullOutput = `---\n${frontmatter}---\n\n${ctx.prompt}`;
148
+
149
+ // Write to file or stdout
150
+ if (outputDir) {
151
+ const promptFile = join(outputDir, `${story.id}.prompt.md`);
152
+ await Bun.write(promptFile, fullOutput);
153
+
154
+ // Also write context-only file for isolation audit
155
+ if (ctx.contextMarkdown) {
156
+ const contextFile = join(outputDir, `${story.id}.context.md`);
157
+ const contextOutput = `---\n${frontmatter}---\n\n${ctx.contextMarkdown}`;
158
+ await Bun.write(contextFile, contextOutput);
159
+ }
160
+
161
+ logger.info("cli", "Written prompt files", {
162
+ storyId: story.id,
163
+ promptFile,
164
+ });
165
+ } else {
166
+ // Stdout mode: print separator + story ID + prompt
167
+ console.log(`\n${"=".repeat(80)}`);
168
+ console.log(`Story: ${story.id} — ${story.title}`);
169
+ console.log("=".repeat(80));
170
+ console.log(fullOutput);
171
+ }
172
+
173
+ processedStories.push(story.id);
174
+ }
175
+
176
+ logger.info("cli", "Prompt assembly complete", {
177
+ processedCount: processedStories.length,
178
+ });
179
+
180
+ return processedStories;
181
+ }
182
+
183
+ /**
184
+ * Build YAML frontmatter for a story prompt.
185
+ *
186
+ * Uses actual token counts from BuiltContext elements (computed by context builder
187
+ * using CHARS_PER_TOKEN=3) rather than re-estimating independently.
188
+ *
189
+ * @param story - User story
190
+ * @param ctx - Pipeline context after running prompt assembly
191
+ * @param role - Optional role for three-session TDD (test-writer, implementer, verifier)
192
+ * @returns YAML frontmatter string (without delimiters)
193
+ */
194
+ function buildFrontmatter(story: UserStory, ctx: PipelineContext, role?: string): string {
195
+ const lines: string[] = [];
196
+
197
+ lines.push(`storyId: ${story.id}`);
198
+ lines.push(`title: "${story.title}"`);
199
+ lines.push(`testStrategy: ${ctx.routing.testStrategy}`);
200
+ lines.push(`modelTier: ${ctx.routing.modelTier}`);
201
+
202
+ if (role) {
203
+ lines.push(`role: ${role}`);
204
+ }
205
+
206
+ // Use actual token counts from BuiltContext if available
207
+ const builtContext = ctx.builtContext;
208
+ const contextTokens = builtContext?.totalTokens ?? 0;
209
+ const promptTokens = ctx.prompt ? Math.ceil(ctx.prompt.length / 3) : 0;
210
+
211
+ lines.push(`contextTokens: ${contextTokens}`);
212
+ lines.push(`promptTokens: ${promptTokens}`);
213
+
214
+ // Dependencies
215
+ if (story.dependencies && story.dependencies.length > 0) {
216
+ lines.push(`dependencies: [${story.dependencies.join(", ")}]`);
217
+ }
218
+
219
+ // Context elements breakdown from actual BuiltContext
220
+ lines.push("contextElements:");
221
+
222
+ if (builtContext) {
223
+ for (const element of builtContext.elements) {
224
+ lines.push(` - type: ${element.type}`);
225
+ if (element.storyId) {
226
+ lines.push(` storyId: ${element.storyId}`);
227
+ }
228
+ if (element.filePath) {
229
+ lines.push(` filePath: ${element.filePath}`);
230
+ }
231
+ lines.push(` tokens: ${element.tokens}`);
232
+ }
233
+ }
234
+
235
+ if (builtContext?.truncated) {
236
+ lines.push("truncated: true");
237
+ }
238
+
239
+ return `${lines.join("\n")}\n`;
240
+ }
241
+
242
+ /**
243
+ * Handle three-session TDD prompts by building separate prompts for each role.
244
+ *
245
+ * @param story - User story
246
+ * @param ctx - Pipeline context
247
+ * @param outputDir - Output directory (undefined for stdout)
248
+ * @param logger - Logger instance
249
+ */
250
+ async function handleThreeSessionTddPrompts(
251
+ story: UserStory,
252
+ ctx: PipelineContext,
253
+ outputDir: string | undefined,
254
+ logger: ReturnType<typeof getLogger>,
255
+ ): Promise<void> {
256
+ // Import TDD prompt builders
257
+ const { buildTestWriterPrompt, buildImplementerPrompt, buildVerifierPrompt } = await import("../tdd/prompts");
258
+
259
+ // Build prompts for each session
260
+ const sessions = [
261
+ { role: "test-writer", prompt: buildTestWriterPrompt(story, ctx.contextMarkdown) },
262
+ { role: "implementer", prompt: buildImplementerPrompt(story, ctx.contextMarkdown) },
263
+ { role: "verifier", prompt: buildVerifierPrompt(story) },
264
+ ];
265
+
266
+ for (const session of sessions) {
267
+ const frontmatter = buildFrontmatter(story, ctx, session.role);
268
+ const fullOutput = `---\n${frontmatter}---\n\n${session.prompt}`;
269
+
270
+ if (outputDir) {
271
+ const promptFile = join(outputDir, `${story.id}.${session.role}.md`);
272
+ await Bun.write(promptFile, fullOutput);
273
+
274
+ logger.info("cli", "Written TDD prompt file", {
275
+ storyId: story.id,
276
+ role: session.role,
277
+ promptFile,
278
+ });
279
+ } else {
280
+ // Stdout mode: print separator + story ID + role + prompt
281
+ console.log(`\n${"=".repeat(80)}`);
282
+ console.log(`Story: ${story.id} — ${story.title} [${session.role}]`);
283
+ console.log("=".repeat(80));
284
+ console.log(fullOutput);
285
+ }
286
+ }
287
+
288
+ // Also write context-only file for isolation audit
289
+ if (outputDir && ctx.contextMarkdown) {
290
+ const contextFile = join(outputDir, `${story.id}.context.md`);
291
+ const frontmatter = buildFrontmatter(story, ctx);
292
+ const contextOutput = `---\n${frontmatter}---\n\n${ctx.contextMarkdown}`;
293
+ await Bun.write(contextFile, contextOutput);
294
+ }
295
+ }
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Runs CLI Commands
3
+ *
4
+ * Display run history from JSONL log files.
5
+ */
6
+
7
+ import { existsSync, readdirSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { NaxError } from "../errors";
10
+ import { getLogger } from "../logger";
11
+ import type { LogEntry } from "../logger/types";
12
+
13
+ /**
14
+ * Options for runs list command.
15
+ */
16
+ export interface RunsListOptions {
17
+ /** Feature name */
18
+ feature: string;
19
+ /** Project directory */
20
+ workdir: string;
21
+ }
22
+
23
+ /**
24
+ * Options for runs show command.
25
+ */
26
+ export interface RunsShowOptions {
27
+ /** Run ID to display */
28
+ runId: string;
29
+ /** Feature name */
30
+ feature: string;
31
+ /** Project directory */
32
+ workdir: string;
33
+ }
34
+
35
+ /**
36
+ * Parse JSONL log file and extract run events.
37
+ *
38
+ * @param logPath - Path to .jsonl log file
39
+ * @returns Array of log entries
40
+ */
41
+ async function parseRunLog(logPath: string): Promise<LogEntry[]> {
42
+ const logger = getLogger();
43
+ try {
44
+ const content = await Bun.file(logPath).text();
45
+ const lines = content.trim().split("\n").filter(Boolean);
46
+ return lines.map((line) => JSON.parse(line) as LogEntry);
47
+ } catch (err) {
48
+ logger.warn("cli", "Failed to parse run log", { logPath, error: (err as Error).message });
49
+ return [];
50
+ }
51
+ }
52
+
53
+ /**
54
+ * List all runs for a feature.
55
+ *
56
+ * @param options - Command options
57
+ *
58
+ * @example
59
+ * ```bash
60
+ * nax runs list -f auth-system
61
+ * ```
62
+ */
63
+ export async function runsListCommand(options: RunsListOptions): Promise<void> {
64
+ const logger = getLogger();
65
+ const { feature, workdir } = options;
66
+
67
+ const runsDir = join(workdir, "nax", "features", feature, "runs");
68
+
69
+ if (!existsSync(runsDir)) {
70
+ logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
71
+ return;
72
+ }
73
+
74
+ // List all .jsonl files in runs directory
75
+ const files = readdirSync(runsDir).filter((f) => f.endsWith(".jsonl"));
76
+
77
+ if (files.length === 0) {
78
+ logger.info("cli", "No runs found for feature", { feature });
79
+ return;
80
+ }
81
+
82
+ logger.info("cli", `Runs for ${feature}`, { count: files.length });
83
+
84
+ // Parse each run file and extract run.start event
85
+ for (const file of files.sort().reverse()) {
86
+ const logPath = join(runsDir, file);
87
+ const entries = await parseRunLog(logPath);
88
+
89
+ // Find run.start and run.complete events
90
+ const startEvent = entries.find((e) => e.message === "run.start");
91
+ const completeEvent = entries.find((e) => e.message === "run.complete");
92
+
93
+ if (!startEvent) {
94
+ logger.warn("cli", "Run log missing run.start event", { file });
95
+ continue;
96
+ }
97
+
98
+ const runId = startEvent.data?.runId || file.replace(".jsonl", "");
99
+ const startedAt = startEvent.timestamp;
100
+ const status = completeEvent ? "completed" : "in-progress";
101
+ const totalCost = completeEvent?.data?.totalCost || 0;
102
+ const storiesCompleted = completeEvent?.data?.storiesCompleted || 0;
103
+ const totalStories = completeEvent?.data?.totalStories || 0;
104
+
105
+ logger.info("cli", ` ${runId}`, {
106
+ status,
107
+ startedAt,
108
+ totalCost,
109
+ storiesCompleted,
110
+ totalStories,
111
+ });
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Show detailed information for a specific run.
117
+ *
118
+ * @param options - Command options
119
+ *
120
+ * @example
121
+ * ```bash
122
+ * nax runs show run-20260220-103045 -f auth-system
123
+ * ```
124
+ */
125
+ export async function runsShowCommand(options: RunsShowOptions): Promise<void> {
126
+ const logger = getLogger();
127
+ const { runId, feature, workdir } = options;
128
+
129
+ const logPath = join(workdir, "nax", "features", feature, "runs", `${runId}.jsonl`);
130
+
131
+ if (!existsSync(logPath)) {
132
+ logger.error("cli", "Run not found", { runId, feature, logPath });
133
+ throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
134
+ }
135
+
136
+ const entries = await parseRunLog(logPath);
137
+
138
+ // Find key events
139
+ const startEvent = entries.find((e) => e.message === "run.start");
140
+ const completeEvent = entries.find((e) => e.message === "run.complete");
141
+ const storyEvents = entries.filter((e) => e.stage === "execution" && e.data?.storyId);
142
+
143
+ if (!startEvent) {
144
+ logger.error("cli", "Run log missing run.start event", { runId });
145
+ throw new NaxError("Run log missing run.start event", "INVALID_RUN_LOG", { runId });
146
+ }
147
+
148
+ // Display run summary
149
+ logger.info("cli", `Run: ${runId}`, {
150
+ feature,
151
+ startedAt: startEvent.timestamp,
152
+ completedAt: completeEvent?.timestamp,
153
+ status: completeEvent ? "completed" : "in-progress",
154
+ });
155
+
156
+ if (completeEvent) {
157
+ logger.info("cli", "Run Summary", {
158
+ totalStories: completeEvent.data?.totalStories,
159
+ storiesCompleted: completeEvent.data?.storiesCompleted,
160
+ storiesFailed: completeEvent.data?.storiesFailed,
161
+ totalCost: completeEvent.data?.totalCost,
162
+ totalDurationMs: completeEvent.data?.totalDurationMs,
163
+ });
164
+ }
165
+
166
+ // Display per-story events
167
+ logger.info("cli", "Story Events", { count: storyEvents.length });
168
+ for (const event of storyEvents) {
169
+ logger.info("cli", ` ${event.data?.storyId}: ${event.message}`, {
170
+ timestamp: event.timestamp,
171
+ data: event.data,
172
+ });
173
+ }
174
+ }