@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,341 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { execSync } from "node:child_process";
3
+ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { WorktreeManager } from "../../../src/worktree/manager";
7
+ import { MergeEngine } from "../../../src/worktree/merge";
8
+
9
+ describe("MergeEngine", () => {
10
+ let testDir: string;
11
+ let projectRoot: string;
12
+ let manager: WorktreeManager;
13
+ let engine: MergeEngine;
14
+
15
+ beforeEach(() => {
16
+ // Create a temporary directory for each test
17
+ testDir = mkdtempSync(join(tmpdir(), "merge-test-"));
18
+ projectRoot = join(testDir, "test-project");
19
+ mkdirSync(projectRoot, { recursive: true });
20
+
21
+ // Initialize a git repository
22
+ execSync("git init", { cwd: projectRoot, stdio: "pipe" });
23
+ execSync('git config user.email "test@example.com"', {
24
+ cwd: projectRoot,
25
+ stdio: "pipe",
26
+ });
27
+ execSync('git config user.name "Test User"', {
28
+ cwd: projectRoot,
29
+ stdio: "pipe",
30
+ });
31
+
32
+ // Create an initial commit (required for worktree creation)
33
+ writeFileSync(join(projectRoot, "README.md"), "# Test Project");
34
+ execSync("git add README.md", { cwd: projectRoot, stdio: "pipe" });
35
+ execSync('git commit -m "Initial commit"', {
36
+ cwd: projectRoot,
37
+ stdio: "pipe",
38
+ });
39
+
40
+ manager = new WorktreeManager();
41
+ engine = new MergeEngine(manager);
42
+ });
43
+
44
+ afterEach(() => {
45
+ // Clean up test directory
46
+ if (existsSync(testDir)) {
47
+ rmSync(testDir, { recursive: true, force: true });
48
+ }
49
+ });
50
+
51
+ describe("merge", () => {
52
+ test("performs git merge --no-ff of story branch", async () => {
53
+ const storyId = "story-merge-1";
54
+
55
+ // Create worktree and make a change
56
+ await manager.create(projectRoot, storyId);
57
+ const worktreePath = join(projectRoot, ".nax-wt", storyId);
58
+ writeFileSync(join(worktreePath, "feature.txt"), "feature content");
59
+ execSync("git add feature.txt", {
60
+ cwd: worktreePath,
61
+ stdio: "pipe",
62
+ });
63
+ execSync('git commit -m "Add feature"', {
64
+ cwd: worktreePath,
65
+ stdio: "pipe",
66
+ });
67
+
68
+ // Merge the branch
69
+ const result = await engine.merge(projectRoot, storyId);
70
+
71
+ expect(result.success).toBe(true);
72
+ expect(result.conflictFiles).toBeUndefined();
73
+
74
+ // Verify merge commit exists
75
+ const log = execSync("git log --oneline --graph", {
76
+ cwd: projectRoot,
77
+ encoding: "utf-8",
78
+ });
79
+ expect(log).toContain(`Merge branch 'nax/${storyId}'`);
80
+
81
+ // Verify feature file exists in main branch
82
+ expect(existsSync(join(projectRoot, "feature.txt"))).toBe(true);
83
+ });
84
+
85
+ test("returns { success: true } on clean merge", async () => {
86
+ const storyId = "story-clean";
87
+
88
+ // Create worktree and make a non-conflicting change
89
+ await manager.create(projectRoot, storyId);
90
+ const worktreePath = join(projectRoot, ".nax-wt", storyId);
91
+ writeFileSync(join(worktreePath, "new-file.txt"), "new content");
92
+ execSync("git add new-file.txt", {
93
+ cwd: worktreePath,
94
+ stdio: "pipe",
95
+ });
96
+ execSync('git commit -m "Add new file"', {
97
+ cwd: worktreePath,
98
+ stdio: "pipe",
99
+ });
100
+
101
+ const result = await engine.merge(projectRoot, storyId);
102
+
103
+ expect(result.success).toBe(true);
104
+ expect(result.conflictFiles).toBeUndefined();
105
+ });
106
+
107
+ test("returns { success: false, conflictFiles: [...] } on conflict", async () => {
108
+ const storyId = "story-conflict";
109
+
110
+ // Create worktree
111
+ await manager.create(projectRoot, storyId);
112
+ const worktreePath = join(projectRoot, ".nax-wt", storyId);
113
+
114
+ // Make conflicting changes in both branches
115
+ // Change in main branch
116
+ writeFileSync(join(projectRoot, "conflict.txt"), "main content");
117
+ execSync("git add conflict.txt", {
118
+ cwd: projectRoot,
119
+ stdio: "pipe",
120
+ });
121
+ execSync('git commit -m "Add conflict file in main"', {
122
+ cwd: projectRoot,
123
+ stdio: "pipe",
124
+ });
125
+
126
+ // Change in story branch (same file, different content)
127
+ writeFileSync(join(worktreePath, "conflict.txt"), "story content");
128
+ execSync("git add conflict.txt", {
129
+ cwd: worktreePath,
130
+ stdio: "pipe",
131
+ });
132
+ execSync('git commit -m "Add conflict file in story"', {
133
+ cwd: worktreePath,
134
+ stdio: "pipe",
135
+ });
136
+
137
+ const result = await engine.merge(projectRoot, storyId);
138
+
139
+ expect(result.success).toBe(false);
140
+ expect(result.conflictFiles).toBeDefined();
141
+ expect(result.conflictFiles?.length).toBeGreaterThan(0);
142
+ expect(result.conflictFiles).toContain("conflict.txt");
143
+
144
+ // Verify merge was aborted (working tree should be clean except for .nax-wt)
145
+ const status = execSync("git status --short", {
146
+ cwd: projectRoot,
147
+ encoding: "utf-8",
148
+ });
149
+ const nonWorktreeStatus = status
150
+ .split("\n")
151
+ .filter((line) => !line.includes(".nax-wt"))
152
+ .join("\n")
153
+ .trim();
154
+ expect(nonWorktreeStatus).toBe(""); // Clean working tree after abort
155
+ });
156
+
157
+ test("cleans up worktree after successful merge", async () => {
158
+ const storyId = "story-cleanup";
159
+
160
+ // Create worktree and make a change
161
+ await manager.create(projectRoot, storyId);
162
+ const worktreePath = join(projectRoot, ".nax-wt", storyId);
163
+ writeFileSync(join(worktreePath, "cleanup.txt"), "cleanup test");
164
+ execSync("git add cleanup.txt", {
165
+ cwd: worktreePath,
166
+ stdio: "pipe",
167
+ });
168
+ execSync('git commit -m "Add cleanup test"', {
169
+ cwd: worktreePath,
170
+ stdio: "pipe",
171
+ });
172
+
173
+ // Merge and verify cleanup
174
+ await engine.merge(projectRoot, storyId);
175
+
176
+ // Worktree should be removed
177
+ expect(existsSync(worktreePath)).toBe(false);
178
+
179
+ // Branch should be deleted
180
+ const branches = execSync("git branch --list", {
181
+ cwd: projectRoot,
182
+ encoding: "utf-8",
183
+ });
184
+ expect(branches).not.toContain(`nax/${storyId}`);
185
+ });
186
+ });
187
+
188
+ describe("mergeAll", () => {
189
+ test("processes stories in topological order", async () => {
190
+ // Create three stories with dependencies: story-1 <- story-2 <- story-3
191
+ const storyIds = ["story-1", "story-2", "story-3"];
192
+ const dependencies = {
193
+ "story-1": [],
194
+ "story-2": ["story-1"],
195
+ "story-3": ["story-2"],
196
+ };
197
+
198
+ // Create worktrees and commits for each story
199
+ for (const storyId of storyIds) {
200
+ await manager.create(projectRoot, storyId);
201
+ const worktreePath = join(projectRoot, ".nax-wt", storyId);
202
+ writeFileSync(join(worktreePath, `${storyId}.txt`), `${storyId} content`);
203
+ execSync(`git add ${storyId}.txt`, {
204
+ cwd: worktreePath,
205
+ stdio: "pipe",
206
+ });
207
+ execSync(`git commit -m "Add ${storyId}"`, {
208
+ cwd: worktreePath,
209
+ stdio: "pipe",
210
+ });
211
+ }
212
+
213
+ const results = await engine.mergeAll(projectRoot, storyIds, dependencies);
214
+
215
+ // All should succeed
216
+ expect(results.every((r) => r.success)).toBe(true);
217
+ expect(results.length).toBe(3);
218
+
219
+ // Verify all files exist
220
+ for (const storyId of storyIds) {
221
+ expect(existsSync(join(projectRoot, `${storyId}.txt`))).toBe(true);
222
+ }
223
+ });
224
+
225
+ test("retries once on conflict after rebasing worktree", async () => {
226
+ const storyIds = ["story-base", "story-conflict"];
227
+ const dependencies = {
228
+ "story-base": [],
229
+ "story-conflict": [],
230
+ };
231
+
232
+ // Create base story
233
+ await manager.create(projectRoot, "story-base");
234
+ const basePath = join(projectRoot, ".nax-wt", "story-base");
235
+ writeFileSync(join(basePath, "shared.txt"), "base content");
236
+ execSync("git add shared.txt", { cwd: basePath, stdio: "pipe" });
237
+ execSync('git commit -m "Add shared file"', {
238
+ cwd: basePath,
239
+ stdio: "pipe",
240
+ });
241
+
242
+ // Create conflicting story
243
+ await manager.create(projectRoot, "story-conflict");
244
+ const conflictPath = join(projectRoot, ".nax-wt", "story-conflict");
245
+ writeFileSync(join(conflictPath, "shared.txt"), "conflict content");
246
+ execSync("git add shared.txt", { cwd: conflictPath, stdio: "pipe" });
247
+ execSync('git commit -m "Add conflicting shared file"', {
248
+ cwd: conflictPath,
249
+ stdio: "pipe",
250
+ });
251
+
252
+ // This should handle the conflict scenario
253
+ const results = await engine.mergeAll(projectRoot, storyIds, dependencies);
254
+
255
+ expect(results.length).toBe(2);
256
+ // First story should succeed
257
+ expect(results[0].success).toBe(true);
258
+ expect(results[0].storyId).toBe("story-base");
259
+ });
260
+
261
+ test("marks story as failed on second conflict", async () => {
262
+ const storyIds = ["story-1", "story-2"];
263
+ const dependencies = {
264
+ "story-1": [],
265
+ "story-2": [],
266
+ };
267
+
268
+ // Create first story
269
+ await manager.create(projectRoot, "story-1");
270
+ const path1 = join(projectRoot, ".nax-wt", "story-1");
271
+ writeFileSync(join(path1, "conflict.txt"), "content 1");
272
+ execSync("git add conflict.txt", { cwd: path1, stdio: "pipe" });
273
+ execSync('git commit -m "Story 1"', { cwd: path1, stdio: "pipe" });
274
+
275
+ // Create second story with conflict
276
+ await manager.create(projectRoot, "story-2");
277
+ const path2 = join(projectRoot, ".nax-wt", "story-2");
278
+ writeFileSync(join(path2, "conflict.txt"), "content 2");
279
+ execSync("git add conflict.txt", { cwd: path2, stdio: "pipe" });
280
+ execSync('git commit -m "Story 2"', { cwd: path2, stdio: "pipe" });
281
+
282
+ const results = await engine.mergeAll(projectRoot, storyIds, dependencies);
283
+
284
+ // First should succeed, second should fail
285
+ expect(results.length).toBe(2);
286
+ expect(results[0].success).toBe(true);
287
+ expect(results[1].success).toBe(false);
288
+ expect(results[1].conflictFiles).toBeDefined();
289
+ });
290
+
291
+ test("continues with remaining stories after failure", async () => {
292
+ const storyIds = ["story-base", "story-conflict", "story-3"];
293
+ const dependencies = {
294
+ "story-base": [],
295
+ "story-conflict": [],
296
+ "story-3": [],
297
+ };
298
+
299
+ // Create base story with a file
300
+ await manager.create(projectRoot, "story-base");
301
+ const pathBase = join(projectRoot, ".nax-wt", "story-base");
302
+ writeFileSync(join(pathBase, "shared.txt"), "base content");
303
+ execSync("git add shared.txt", { cwd: pathBase, stdio: "pipe" });
304
+ execSync('git commit -m "Add shared file"', {
305
+ cwd: pathBase,
306
+ stdio: "pipe",
307
+ });
308
+
309
+ // Create conflicting story (modifies same file)
310
+ await manager.create(projectRoot, "story-conflict");
311
+ const pathConflict = join(projectRoot, ".nax-wt", "story-conflict");
312
+ writeFileSync(join(pathConflict, "shared.txt"), "conflict content");
313
+ execSync("git add shared.txt", {
314
+ cwd: pathConflict,
315
+ stdio: "pipe",
316
+ });
317
+ execSync('git commit -m "Modify shared file in story-conflict"', {
318
+ cwd: pathConflict,
319
+ stdio: "pipe",
320
+ });
321
+
322
+ // Create third story (no conflict)
323
+ await manager.create(projectRoot, "story-3");
324
+ const path3 = join(projectRoot, ".nax-wt", "story-3");
325
+ writeFileSync(join(path3, "file3.txt"), "content 3");
326
+ execSync("git add file3.txt", { cwd: path3, stdio: "pipe" });
327
+ execSync('git commit -m "Story 3"', { cwd: path3, stdio: "pipe" });
328
+
329
+ const results = await engine.mergeAll(projectRoot, storyIds, dependencies);
330
+
331
+ // Base should succeed, conflict should fail, story-3 should succeed
332
+ expect(results.length).toBe(3);
333
+ expect(results[0].success).toBe(true);
334
+ expect(results[0].storyId).toBe("story-base");
335
+ expect(results[1].success).toBe(false);
336
+ expect(results[1].storyId).toBe("story-conflict");
337
+ expect(results[2].success).toBe(true);
338
+ expect(results[2].storyId).toBe("story-3");
339
+ });
340
+ });
341
+ });
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Manual demo of the logging formatter
3
+ *
4
+ * Run with: bun run test/manual/logging-formatter-demo.ts
5
+ */
6
+
7
+ import type { LogEntry } from "../../src/logger/types.js";
8
+ import { formatLogEntry, formatRunSummary } from "../../src/logging/formatter.js";
9
+ import type { RunSummary } from "../../src/logging/types.js";
10
+
11
+ console.log("\n=== LOGGING FORMATTER DEMO ===\n");
12
+
13
+ // Run start
14
+ const runStart: LogEntry = {
15
+ timestamp: new Date().toISOString(),
16
+ level: "info",
17
+ stage: "run.start",
18
+ message: "Starting feature",
19
+ data: {
20
+ runId: "run-2026-02-27T14-00-00-000Z",
21
+ feature: "authentication",
22
+ workdir: "/Users/test/project",
23
+ totalStories: 5,
24
+ },
25
+ };
26
+
27
+ console.log(formatLogEntry(runStart, { mode: "normal", useColor: true }).output);
28
+
29
+ // Story start
30
+ const storyStart: LogEntry = {
31
+ timestamp: new Date().toISOString(),
32
+ level: "info",
33
+ stage: "story.start",
34
+ storyId: "US-001",
35
+ message: "Starting story",
36
+ data: {
37
+ storyId: "US-001",
38
+ storyTitle: "Add user authentication with JWT",
39
+ complexity: "medium",
40
+ modelTier: "balanced",
41
+ },
42
+ };
43
+
44
+ console.log(formatLogEntry(storyStart, { mode: "normal", useColor: true }).output);
45
+
46
+ // TDD session
47
+ const tddSession: LogEntry = {
48
+ timestamp: new Date().toISOString(),
49
+ level: "info",
50
+ stage: "tdd",
51
+ message: "→ Session: test-writer",
52
+ data: {
53
+ role: "test-writer",
54
+ storyId: "US-001",
55
+ },
56
+ };
57
+
58
+ console.log(formatLogEntry(tddSession, { mode: "normal", useColor: true }).output);
59
+
60
+ // Routing (verbose only)
61
+ const routing: LogEntry = {
62
+ timestamp: new Date().toISOString(),
63
+ level: "debug",
64
+ stage: "routing",
65
+ message: "Task classified",
66
+ data: {
67
+ complexity: "medium",
68
+ modelTier: "balanced",
69
+ tokens: 1500,
70
+ contextFiles: ["src/auth.ts", "src/middleware.ts"],
71
+ },
72
+ };
73
+
74
+ console.log("\n--- Normal mode (should hide debug) ---");
75
+ console.log(formatLogEntry(routing, { mode: "normal", useColor: true }).output);
76
+
77
+ console.log("\n--- Verbose mode (should show debug with data) ---");
78
+ console.log(formatLogEntry(routing, { mode: "verbose", useColor: true }).output);
79
+
80
+ // Story complete - success
81
+ const storySuccess: LogEntry = {
82
+ timestamp: new Date().toISOString(),
83
+ level: "info",
84
+ stage: "story.complete",
85
+ storyId: "US-001",
86
+ message: "Story completed",
87
+ data: {
88
+ storyId: "US-001",
89
+ success: true,
90
+ cost: 0.2567,
91
+ durationMs: 45000,
92
+ },
93
+ };
94
+
95
+ console.log("\n");
96
+ console.log(formatLogEntry(storySuccess, { mode: "normal", useColor: true }).output);
97
+
98
+ // Story complete - escalation
99
+ const storyEscalate: LogEntry = {
100
+ timestamp: new Date().toISOString(),
101
+ level: "warn",
102
+ stage: "agent.complete",
103
+ storyId: "US-002",
104
+ message: "Escalated",
105
+ data: {
106
+ storyId: "US-002",
107
+ success: false,
108
+ finalAction: "escalate",
109
+ cost: 0.12,
110
+ durationMs: 30000,
111
+ },
112
+ };
113
+
114
+ console.log(formatLogEntry(storyEscalate, { mode: "normal", useColor: true }).output);
115
+
116
+ // Story complete - failure
117
+ const storyFail: LogEntry = {
118
+ timestamp: new Date().toISOString(),
119
+ level: "error",
120
+ stage: "story.complete",
121
+ storyId: "US-003",
122
+ message: "Failed",
123
+ data: {
124
+ storyId: "US-003",
125
+ success: false,
126
+ finalAction: "fail",
127
+ reason: "Test suite failed after 3 attempts",
128
+ cost: 0.35,
129
+ durationMs: 120000,
130
+ },
131
+ };
132
+
133
+ console.log(formatLogEntry(storyFail, { mode: "verbose", useColor: true }).output);
134
+
135
+ // Run summary
136
+ const summary: RunSummary = {
137
+ total: 10,
138
+ passed: 8,
139
+ failed: 1,
140
+ skipped: 1,
141
+ durationMs: 300000,
142
+ totalCost: 1.2345,
143
+ startedAt: "2026-02-27T14:00:00Z",
144
+ completedAt: "2026-02-27T14:05:00Z",
145
+ };
146
+
147
+ console.log(formatRunSummary(summary, { mode: "normal", useColor: true }));
148
+
149
+ console.log("\n=== JSON MODE ===\n");
150
+ console.log(formatLogEntry(storyStart, { mode: "json" }).output);
151
+ console.log(formatRunSummary(summary, { mode: "json" }));
152
+
153
+ console.log("\n=== QUIET MODE (only shows critical events) ===\n");
154
+ console.log("Run start:", formatLogEntry(runStart, { mode: "quiet" }).shouldDisplay);
155
+ console.log("Story start:", formatLogEntry(storyStart, { mode: "quiet" }).shouldDisplay);
156
+ console.log("TDD session:", formatLogEntry(tddSession, { mode: "quiet" }).shouldDisplay);
157
+ console.log("Routing debug:", formatLogEntry(routing, { mode: "quiet" }).shouldDisplay);
158
+ console.log("Story complete:", formatLogEntry(storySuccess, { mode: "quiet" }).shouldDisplay);
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Tests for TUI Agent Panel and PTY integration (Phase 3).
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { render } from "ink-testing-library";
7
+ import { ClaudeCodeAdapter } from "../../src/agents/claude";
8
+ import type { PtyHandle } from "../../src/agents/types";
9
+ import { AgentPanel } from "../../src/tui/components/AgentPanel";
10
+
11
+ describe("AgentPanel", () => {
12
+ test("renders placeholder when no output", () => {
13
+ const { lastFrame } = render(<AgentPanel outputLines={[]} />);
14
+ expect(lastFrame()).toContain("Waiting for agent...");
15
+ });
16
+
17
+ test("renders output lines", () => {
18
+ const lines = ["Line 1", "Line 2", "Line 3"];
19
+ const { lastFrame } = render(<AgentPanel outputLines={lines} />);
20
+
21
+ for (const line of lines) {
22
+ expect(lastFrame()).toContain(line);
23
+ }
24
+ });
25
+
26
+ test("shows focus indicator when focused", () => {
27
+ const { lastFrame } = render(<AgentPanel focused outputLines={[]} />);
28
+ expect(lastFrame()).toContain("(focused)");
29
+ });
30
+
31
+ test("does not show focus indicator when not focused", () => {
32
+ const { lastFrame } = render(<AgentPanel focused={false} outputLines={[]} />);
33
+ expect(lastFrame()).not.toContain("(focused)");
34
+ });
35
+
36
+ test("buffers only last N lines", () => {
37
+ // Generate 600 lines (exceeds MAX_OUTPUT_LINES = 500)
38
+ const lines = Array.from({ length: 600 }, (_, i) => `Output ${i + 1}`);
39
+ const { lastFrame } = render(<AgentPanel outputLines={lines} />);
40
+ const frame = lastFrame();
41
+
42
+ // Should contain last 500 lines
43
+ expect(frame).toContain("Output 600");
44
+ expect(frame).toContain("Output 101");
45
+
46
+ // Should NOT contain first 100 lines (trimmed)
47
+ // Use exact line boundaries to avoid substring matches
48
+ expect(frame).not.toContain("Output 1\n");
49
+ expect(frame).not.toContain("Output 100\n");
50
+ });
51
+ });
52
+
53
+ describe("PtyHandle interface", () => {
54
+ test("ClaudeCodeAdapter has PtyHandle interface", () => {
55
+ const adapter = new ClaudeCodeAdapter();
56
+
57
+ // Check that runInteractive method exists
58
+ expect(adapter.runInteractive).toBeDefined();
59
+ expect(typeof adapter.runInteractive).toBe("function");
60
+ });
61
+
62
+ test("PtyHandle has required methods", () => {
63
+ // Mock PtyHandle to verify interface contract
64
+ const mockHandle: PtyHandle = {
65
+ write: (data: string) => {
66
+ expect(typeof data).toBe("string");
67
+ },
68
+ resize: (cols: number, rows: number) => {
69
+ expect(typeof cols).toBe("number");
70
+ expect(typeof rows).toBe("number");
71
+ },
72
+ kill: () => {
73
+ // noop
74
+ },
75
+ pid: 12345,
76
+ };
77
+
78
+ // Verify all methods exist
79
+ expect(mockHandle.write).toBeDefined();
80
+ expect(mockHandle.resize).toBeDefined();
81
+ expect(mockHandle.kill).toBeDefined();
82
+ expect(mockHandle.pid).toBe(12345);
83
+
84
+ // Test methods
85
+ mockHandle.write("test");
86
+ mockHandle.resize(80, 24);
87
+ mockHandle.kill();
88
+ });
89
+ });
90
+
91
+ describe("usePty hook", () => {
92
+ test("returns null handle when options is null", () => {
93
+ // This test would require rendering a component that uses usePty
94
+ // For now, we just document the expected behavior
95
+ // const { handle } = usePty(null);
96
+ // expect(handle).toBeNull();
97
+ expect(true).toBe(true); // Placeholder
98
+ });
99
+ });