@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,332 @@
1
+ /**
2
+ * TUI Stories Panel Tests
3
+ *
4
+ * Tests the StoriesPanel component rendering with different story states,
5
+ * cost display, elapsed time formatting, and layout breakpoint logic.
6
+ */
7
+
8
+ import { describe, expect, test } from "bun:test";
9
+ import { render } from "ink-testing-library";
10
+ import { createElement } from "react";
11
+ import type { UserStory } from "../../src/prd/types";
12
+ import { StatusBar } from "../../src/tui/components/StatusBar";
13
+ import { StoriesPanel } from "../../src/tui/components/StoriesPanel";
14
+ import type { StoryDisplayState } from "../../src/tui/types";
15
+
16
+ // Helper to create mock stories
17
+ function createMockStory(id: string, status: StoryDisplayState["status"]): StoryDisplayState {
18
+ const story: UserStory = {
19
+ id,
20
+ title: `Test story ${id}`,
21
+ description: "Test description",
22
+ acceptanceCriteria: [],
23
+ dependencies: [],
24
+ tags: [],
25
+ passes: status === "passed",
26
+ status: status === "passed" ? "passed" : "pending",
27
+ escalations: [],
28
+ attempts: 0,
29
+ };
30
+
31
+ return {
32
+ story,
33
+ status,
34
+ routing: {
35
+ complexity: "simple",
36
+ modelTier: "fast",
37
+ testStrategy: "test-after",
38
+ reasoning: "Test routing",
39
+ },
40
+ cost: 0.01,
41
+ };
42
+ }
43
+
44
+ describe("StoriesPanel", () => {
45
+ test("renders pending story with ⬚ icon", () => {
46
+ const stories = [createMockStory("US-001", "pending")];
47
+ const { lastFrame } = render(
48
+ createElement(StoriesPanel, {
49
+ stories,
50
+ totalCost: 0,
51
+ elapsedMs: 0,
52
+ width: 30,
53
+ }),
54
+ );
55
+
56
+ expect(lastFrame()).toContain("⬚ US-001");
57
+ });
58
+
59
+ test("renders running story with 🔄 icon", () => {
60
+ const stories = [createMockStory("US-001", "running")];
61
+ const { lastFrame } = render(
62
+ createElement(StoriesPanel, {
63
+ stories,
64
+ totalCost: 0,
65
+ elapsedMs: 0,
66
+ width: 30,
67
+ }),
68
+ );
69
+
70
+ expect(lastFrame()).toContain("🔄 US-001");
71
+ });
72
+
73
+ test("renders passed story with ✅ icon", () => {
74
+ const stories = [createMockStory("US-001", "passed")];
75
+ const { lastFrame } = render(
76
+ createElement(StoriesPanel, {
77
+ stories,
78
+ totalCost: 0,
79
+ elapsedMs: 0,
80
+ width: 30,
81
+ }),
82
+ );
83
+
84
+ expect(lastFrame()).toContain("✅ US-001");
85
+ });
86
+
87
+ test("renders failed story with ❌ icon", () => {
88
+ const stories = [createMockStory("US-001", "failed")];
89
+ const { lastFrame } = render(
90
+ createElement(StoriesPanel, {
91
+ stories,
92
+ totalCost: 0,
93
+ elapsedMs: 0,
94
+ width: 30,
95
+ }),
96
+ );
97
+
98
+ expect(lastFrame()).toContain("❌ US-001");
99
+ });
100
+
101
+ test("renders skipped story with ⏭️ icon", () => {
102
+ const stories = [createMockStory("US-001", "skipped")];
103
+ const { lastFrame } = render(
104
+ createElement(StoriesPanel, {
105
+ stories,
106
+ totalCost: 0,
107
+ elapsedMs: 0,
108
+ width: 30,
109
+ }),
110
+ );
111
+
112
+ expect(lastFrame()).toContain("⏭️ US-001");
113
+ });
114
+
115
+ test("renders retrying story with 🔁 icon", () => {
116
+ const stories = [createMockStory("US-001", "retrying")];
117
+ const { lastFrame } = render(
118
+ createElement(StoriesPanel, {
119
+ stories,
120
+ totalCost: 0,
121
+ elapsedMs: 0,
122
+ width: 30,
123
+ }),
124
+ );
125
+
126
+ expect(lastFrame()).toContain("🔁 US-001");
127
+ });
128
+
129
+ test("renders paused story with ⏸️ icon", () => {
130
+ const stories = [createMockStory("US-001", "paused")];
131
+ const { lastFrame } = render(
132
+ createElement(StoriesPanel, {
133
+ stories,
134
+ totalCost: 0,
135
+ elapsedMs: 0,
136
+ width: 30,
137
+ }),
138
+ );
139
+
140
+ expect(lastFrame()).toContain("⏸️ US-001");
141
+ });
142
+
143
+ test("displays routing info (complexity and model tier)", () => {
144
+ const stories = [createMockStory("US-001", "pending")];
145
+ const { lastFrame } = render(
146
+ createElement(StoriesPanel, {
147
+ stories,
148
+ totalCost: 0,
149
+ elapsedMs: 0,
150
+ width: 30,
151
+ }),
152
+ );
153
+
154
+ const output = lastFrame();
155
+ expect(output).toContain("sim"); // "simple".slice(0, 3) = "sim"
156
+ expect(output).toContain("fast");
157
+ });
158
+
159
+ test("displays total cost", () => {
160
+ const stories = [createMockStory("US-001", "passed")];
161
+ const { lastFrame } = render(
162
+ createElement(StoriesPanel, {
163
+ stories,
164
+ totalCost: 0.4235,
165
+ elapsedMs: 0,
166
+ width: 30,
167
+ }),
168
+ );
169
+
170
+ expect(lastFrame()).toContain("$0.4235");
171
+ });
172
+
173
+ test("displays elapsed time in mm:ss format", () => {
174
+ const stories = [createMockStory("US-001", "running")];
175
+ const { lastFrame } = render(
176
+ createElement(StoriesPanel, {
177
+ stories,
178
+ totalCost: 0,
179
+ elapsedMs: 263000, // 4 minutes 23 seconds
180
+ width: 30,
181
+ }),
182
+ );
183
+
184
+ expect(lastFrame()).toContain("4m 23s");
185
+ });
186
+
187
+ test("renders multiple stories", () => {
188
+ const stories = [
189
+ createMockStory("US-001", "passed"),
190
+ createMockStory("US-002", "running"),
191
+ createMockStory("US-003", "pending"),
192
+ ];
193
+
194
+ const { lastFrame } = render(
195
+ createElement(StoriesPanel, {
196
+ stories,
197
+ totalCost: 0.05,
198
+ elapsedMs: 120000,
199
+ width: 30,
200
+ }),
201
+ );
202
+
203
+ const output = lastFrame();
204
+ expect(output).toContain("✅ US-001");
205
+ expect(output).toContain("🔄 US-002");
206
+ expect(output).toContain("⬚ US-003");
207
+ });
208
+ });
209
+
210
+ describe("StatusBar", () => {
211
+ test("displays 'Idle' when no current story", () => {
212
+ const { lastFrame } = render(createElement(StatusBar, {}));
213
+ expect(lastFrame()).toContain("Idle");
214
+ });
215
+
216
+ test("displays current story ID", () => {
217
+ const story: UserStory = {
218
+ id: "US-042",
219
+ title: "Test story",
220
+ description: "Test",
221
+ acceptanceCriteria: [],
222
+ dependencies: [],
223
+ tags: [],
224
+ passes: false,
225
+ status: "pending",
226
+ escalations: [],
227
+ attempts: 0,
228
+ };
229
+
230
+ const { lastFrame } = render(
231
+ createElement(StatusBar, {
232
+ currentStory: story,
233
+ }),
234
+ );
235
+
236
+ expect(lastFrame()).toContain("Story US-042");
237
+ });
238
+
239
+ test("displays current stage", () => {
240
+ const story: UserStory = {
241
+ id: "US-001",
242
+ title: "Test",
243
+ description: "Test",
244
+ acceptanceCriteria: [],
245
+ dependencies: [],
246
+ tags: [],
247
+ passes: false,
248
+ status: "pending",
249
+ escalations: [],
250
+ attempts: 0,
251
+ };
252
+
253
+ const { lastFrame } = render(
254
+ createElement(StatusBar, {
255
+ currentStory: story,
256
+ currentStage: "execution",
257
+ }),
258
+ );
259
+
260
+ expect(lastFrame()).toContain("execution");
261
+ });
262
+
263
+ test("displays model tier", () => {
264
+ const story: UserStory = {
265
+ id: "US-001",
266
+ title: "Test",
267
+ description: "Test",
268
+ acceptanceCriteria: [],
269
+ dependencies: [],
270
+ tags: [],
271
+ passes: false,
272
+ status: "pending",
273
+ escalations: [],
274
+ attempts: 0,
275
+ };
276
+
277
+ const { lastFrame } = render(
278
+ createElement(StatusBar, {
279
+ currentStory: story,
280
+ modelTier: "balanced",
281
+ }),
282
+ );
283
+
284
+ expect(lastFrame()).toContain("balanced");
285
+ });
286
+
287
+ test("displays test strategy", () => {
288
+ const story: UserStory = {
289
+ id: "US-001",
290
+ title: "Test",
291
+ description: "Test",
292
+ acceptanceCriteria: [],
293
+ dependencies: [],
294
+ tags: [],
295
+ passes: false,
296
+ status: "pending",
297
+ escalations: [],
298
+ attempts: 0,
299
+ };
300
+
301
+ const { lastFrame } = render(
302
+ createElement(StatusBar, {
303
+ currentStory: story,
304
+ testStrategy: "three-session-tdd",
305
+ }),
306
+ );
307
+
308
+ expect(lastFrame()).toContain("three-session-tdd");
309
+ });
310
+ });
311
+
312
+ describe("Layout breakpoints", () => {
313
+ test("single column mode for width < 80", () => {
314
+ // This would be tested via useLayout hook, but since we can't mock process.stdout.columns
315
+ // in Bun tests easily, we verify the logic manually:
316
+ const width = 70;
317
+ const mode = width < 80 ? "single" : width < 140 ? "narrow" : "wide";
318
+ expect(mode).toBe("single");
319
+ });
320
+
321
+ test("narrow mode for width 80-140", () => {
322
+ const width = 100;
323
+ const mode = width < 80 ? "single" : width < 140 ? "narrow" : "wide";
324
+ expect(mode).toBe("narrow");
325
+ });
326
+
327
+ test("wide mode for width > 140", () => {
328
+ const width = 150;
329
+ const mode = width < 80 ? "single" : width < 140 ? "narrow" : "wide";
330
+ expect(mode).toBe("wide");
331
+ });
332
+ });
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Tests for acceptance test generation module
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import {
7
+ buildAcceptanceTestPrompt,
8
+ generateSkeletonTests,
9
+ parseAcceptanceCriteria,
10
+ } from "../../src/acceptance/generator";
11
+
12
+ describe("parseAcceptanceCriteria", () => {
13
+ test("parses AC-N: format", () => {
14
+ const spec = `
15
+ ## Acceptance Criteria
16
+ - AC-1: System should handle empty input
17
+ - AC-2: set(key, value, ttl) expires after ttl milliseconds
18
+ `;
19
+
20
+ const criteria = parseAcceptanceCriteria(spec);
21
+
22
+ expect(criteria).toHaveLength(2);
23
+ expect(criteria[0]).toEqual({
24
+ id: "AC-1",
25
+ text: "System should handle empty input",
26
+ lineNumber: 3,
27
+ });
28
+ expect(criteria[1]).toEqual({
29
+ id: "AC-2",
30
+ text: "set(key, value, ttl) expires after ttl milliseconds",
31
+ lineNumber: 4,
32
+ });
33
+ });
34
+
35
+ test("parses checklist format", () => {
36
+ const spec = `
37
+ ## Acceptance Criteria
38
+ - [ ] AC-1: Feature works correctly
39
+ - [x] AC-2: Tests pass
40
+ `;
41
+
42
+ const criteria = parseAcceptanceCriteria(spec);
43
+
44
+ expect(criteria).toHaveLength(2);
45
+ expect(criteria[0].id).toBe("AC-1");
46
+ expect(criteria[1].id).toBe("AC-2");
47
+ });
48
+
49
+ test("normalizes AC IDs to uppercase", () => {
50
+ const spec = "- ac-1: lowercase id";
51
+ const criteria = parseAcceptanceCriteria(spec);
52
+
53
+ expect(criteria[0].id).toBe("AC-1");
54
+ });
55
+
56
+ test("handles AC without list marker", () => {
57
+ const spec = `
58
+ AC-1: Standalone criterion
59
+ AC-2: Another criterion
60
+ `;
61
+
62
+ const criteria = parseAcceptanceCriteria(spec);
63
+
64
+ expect(criteria).toHaveLength(2);
65
+ expect(criteria[0].text).toBe("Standalone criterion");
66
+ });
67
+
68
+ test("returns empty array when no AC found", () => {
69
+ const spec = `
70
+ # Feature
71
+ This is a spec with no acceptance criteria.
72
+ `;
73
+
74
+ const criteria = parseAcceptanceCriteria(spec);
75
+
76
+ expect(criteria).toEqual([]);
77
+ });
78
+
79
+ test("tracks correct line numbers", () => {
80
+ const spec = `Line 1
81
+ Line 2
82
+ - AC-1: First criterion
83
+ Line 4
84
+ - AC-2: Second criterion`;
85
+
86
+ const criteria = parseAcceptanceCriteria(spec);
87
+
88
+ expect(criteria[0].lineNumber).toBe(3);
89
+ expect(criteria[1].lineNumber).toBe(5);
90
+ });
91
+ });
92
+
93
+ describe("buildAcceptanceTestPrompt", () => {
94
+ test("includes all criteria in prompt", () => {
95
+ const criteria = [
96
+ { id: "AC-1", text: "handles empty input", lineNumber: 5 },
97
+ { id: "AC-2", text: "validates email format", lineNumber: 6 },
98
+ ];
99
+
100
+ const prompt = buildAcceptanceTestPrompt(criteria, "auth", "File tree:\nsrc/\n auth.ts\n");
101
+
102
+ expect(prompt).toContain("AC-1: handles empty input");
103
+ expect(prompt).toContain("AC-2: validates email format");
104
+ expect(prompt).toContain('"auth"');
105
+ expect(prompt).toContain("File tree:");
106
+ });
107
+
108
+ test("formats prompt with correct structure", () => {
109
+ const criteria = [{ id: "AC-1", text: "test criterion", lineNumber: 1 }];
110
+
111
+ const prompt = buildAcceptanceTestPrompt(criteria, "feature", "context");
112
+
113
+ expect(prompt).toContain("CODEBASE CONTEXT:");
114
+ expect(prompt).toContain("ACCEPTANCE CRITERIA:");
115
+ expect(prompt).toContain("One test per AC");
116
+ expect(prompt).toContain("bun:test");
117
+ });
118
+ });
119
+
120
+ describe("generateSkeletonTests", () => {
121
+ test("generates skeleton with TODO placeholders", () => {
122
+ const criteria = [
123
+ { id: "AC-1", text: "handles empty input", lineNumber: 5 },
124
+ { id: "AC-2", text: "validates email", lineNumber: 6 },
125
+ ];
126
+
127
+ const skeleton = generateSkeletonTests("auth", criteria);
128
+
129
+ expect(skeleton).toContain('describe("auth - Acceptance Tests"');
130
+ expect(skeleton).toContain('test("AC-1: handles empty input"');
131
+ expect(skeleton).toContain('test("AC-2: validates email"');
132
+ expect(skeleton).toContain("// TODO: Implement acceptance test for AC-1");
133
+ expect(skeleton).toContain("// TODO: Implement acceptance test for AC-2");
134
+ expect(skeleton).toContain("expect(true).toBe(false)");
135
+ });
136
+
137
+ test("generates valid TypeScript structure", () => {
138
+ const criteria = [{ id: "AC-1", text: "test", lineNumber: 1 }];
139
+
140
+ const skeleton = generateSkeletonTests("feature", criteria);
141
+
142
+ expect(skeleton).toContain('import { describe, test, expect } from "bun:test"');
143
+ expect(skeleton).toContain("describe(");
144
+ expect(skeleton).toContain("test(");
145
+ expect(skeleton).toContain("async () => {");
146
+ });
147
+
148
+ test("handles empty criteria array", () => {
149
+ const skeleton = generateSkeletonTests("feature", []);
150
+
151
+ expect(skeleton).toContain('describe("feature - Acceptance Tests"');
152
+ expect(skeleton).toContain("// No acceptance criteria found");
153
+ });
154
+
155
+ test("escapes special characters in criteria text", () => {
156
+ const criteria = [{ id: "AC-1", text: 'handles "quotes" correctly', lineNumber: 1 }];
157
+
158
+ const skeleton = generateSkeletonTests("feature", criteria);
159
+
160
+ // Should still contain the escaped text
161
+ expect(skeleton).toContain('handles "quotes" correctly');
162
+ });
163
+ });
164
+
165
+ describe("integration: AC parsing and skeleton generation", () => {
166
+ test("full workflow from spec to skeleton", () => {
167
+ const spec = `
168
+ # Feature: URL Shortener
169
+
170
+ ## Acceptance Criteria
171
+ - AC-1: Shortened URLs redirect to original URL
172
+ - AC-2: Invalid URLs return 404
173
+ - AC-3: Analytics track click counts
174
+ `;
175
+
176
+ const criteria = parseAcceptanceCriteria(spec);
177
+ expect(criteria).toHaveLength(3);
178
+
179
+ const skeleton = generateSkeletonTests("url-shortener", criteria);
180
+
181
+ expect(skeleton).toContain("AC-1: Shortened URLs redirect to original URL");
182
+ expect(skeleton).toContain("AC-2: Invalid URLs return 404");
183
+ expect(skeleton).toContain("AC-3: Analytics track click counts");
184
+ expect(skeleton).toContain("url-shortener");
185
+ });
186
+ });
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Tests for stderr capture in agent result type
3
+ *
4
+ * Covers: AgentResult interface includes stderr field
5
+ */
6
+
7
+ import { describe, expect, it } from "bun:test";
8
+ import type { AgentResult } from "../../src/agents/types";
9
+
10
+ // ─────────────────────────────────────────────────────────────────────────────
11
+ // Test fixtures
12
+ // ─────────────────────────────────────────────────────────────────────────────
13
+
14
+ function createAgentResult(overrides: Partial<AgentResult> = {}): AgentResult {
15
+ return {
16
+ success: false,
17
+ exitCode: 1,
18
+ output: "",
19
+ stderr: "",
20
+ rateLimited: false,
21
+ durationMs: 1000,
22
+ estimatedCost: 0.01,
23
+ ...overrides,
24
+ };
25
+ }
26
+
27
+ // ─────────────────────────────────────────────────────────────────────────────
28
+ // Tests for AgentResult interface
29
+ // ─────────────────────────────────────────────────────────────────────────────
30
+
31
+ describe("AgentResult stderr field", () => {
32
+ it("supports stderr field in AgentResult", () => {
33
+ const result = createAgentResult({
34
+ exitCode: 1,
35
+ stderr: "Error: 401 Unauthorized",
36
+ });
37
+
38
+ expect(result.stderr).toBe("Error: 401 Unauthorized");
39
+ });
40
+
41
+ it("supports empty stderr string", () => {
42
+ const result = createAgentResult({
43
+ stderr: "",
44
+ });
45
+
46
+ expect(result.stderr).toBe("");
47
+ });
48
+
49
+ it("supports undefined stderr for backward compatibility", () => {
50
+ const result: AgentResult = {
51
+ success: false,
52
+ exitCode: 1,
53
+ output: "output",
54
+ // stderr not provided
55
+ rateLimited: false,
56
+ durationMs: 1000,
57
+ estimatedCost: 0.01,
58
+ };
59
+
60
+ expect(result.stderr).toBeUndefined();
61
+ });
62
+
63
+ it("can store long error messages in stderr", () => {
64
+ const longStderr = "Error: ".padEnd(1000, "x");
65
+ const result = createAgentResult({
66
+ stderr: longStderr,
67
+ });
68
+
69
+ expect(result.stderr).toHaveLength(1000);
70
+ expect(result.stderr).toContain("Error:");
71
+ });
72
+
73
+ it("includes stderr in all failure scenarios", () => {
74
+ const scenarios = [
75
+ { exitCode: 1, stderr: "Generic error" },
76
+ { exitCode: 401, stderr: "Unauthorized" },
77
+ { exitCode: 500, stderr: "Internal server error" },
78
+ { exitCode: 124, stderr: "Timeout" },
79
+ ];
80
+
81
+ for (const scenario of scenarios) {
82
+ const result = createAgentResult({
83
+ exitCode: scenario.exitCode,
84
+ stderr: scenario.stderr,
85
+ });
86
+
87
+ expect(result.exitCode).toBe(scenario.exitCode);
88
+ expect(result.stderr).toBe(scenario.stderr);
89
+ }
90
+ });
91
+
92
+ it("preserves newlines in stderr", () => {
93
+ const multilineStderr = "Error: Something failed\nDetails: xyz\nContext: abc";
94
+ const result = createAgentResult({
95
+ stderr: multilineStderr,
96
+ });
97
+
98
+ expect(result.stderr).toContain("\n");
99
+ expect(result.stderr).toContain("Details:");
100
+ });
101
+
102
+ it("allows stderr with special characters", () => {
103
+ const specialStderr = 'Error: "quoted" with \\backslash and \t tab';
104
+ const result = createAgentResult({
105
+ stderr: specialStderr,
106
+ });
107
+
108
+ expect(result.stderr).toBe(specialStderr);
109
+ });
110
+
111
+ it("can be serialized to JSON", () => {
112
+ const result = createAgentResult({
113
+ exitCode: 401,
114
+ stderr: "401 Unauthorized: Invalid API key",
115
+ });
116
+
117
+ const json = JSON.stringify(result);
118
+ expect(json).toContain("401");
119
+ expect(json).toContain("Unauthorized");
120
+
121
+ const parsed = JSON.parse(json) as AgentResult;
122
+ expect(parsed.stderr).toBe("401 Unauthorized: Invalid API key");
123
+ });
124
+
125
+ it("maintains other fields when stderr is set", () => {
126
+ const result = createAgentResult({
127
+ success: false,
128
+ exitCode: 1,
129
+ output: "stdout output",
130
+ stderr: "stderr output",
131
+ rateLimited: false,
132
+ durationMs: 5000,
133
+ estimatedCost: 0.05,
134
+ pid: 12345,
135
+ });
136
+
137
+ expect(result.success).toBe(false);
138
+ expect(result.exitCode).toBe(1);
139
+ expect(result.output).toBe("stdout output");
140
+ expect(result.stderr).toBe("stderr output");
141
+ expect(result.rateLimited).toBe(false);
142
+ expect(result.durationMs).toBe(5000);
143
+ expect(result.estimatedCost).toBe(0.05);
144
+ expect(result.pid).toBe(12345);
145
+ });
146
+ });