@ag-eco/agentplate-cli 0.13.0

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 (455) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +462 -0
  3. package/agents/ap-co-creation.md +90 -0
  4. package/agents/builder.md +144 -0
  5. package/agents/coordinator.md +377 -0
  6. package/agents/lead.md +435 -0
  7. package/agents/merger.md +164 -0
  8. package/agents/monitor.md +214 -0
  9. package/agents/orchestrator.md +239 -0
  10. package/agents/reviewer.md +140 -0
  11. package/agents/scout.md +125 -0
  12. package/agents/supervisor.md +427 -0
  13. package/package.json +66 -0
  14. package/src/agents/capabilities.test.ts +85 -0
  15. package/src/agents/capabilities.ts +125 -0
  16. package/src/agents/checkpoint.test.ts +88 -0
  17. package/src/agents/checkpoint.ts +101 -0
  18. package/src/agents/copilot-hooks-deployer.test.ts +162 -0
  19. package/src/agents/copilot-hooks-deployer.ts +93 -0
  20. package/src/agents/guard-rules.test.ts +372 -0
  21. package/src/agents/guard-rules.ts +97 -0
  22. package/src/agents/headless-mail-injector.test.ts +709 -0
  23. package/src/agents/headless-mail-injector.ts +377 -0
  24. package/src/agents/headless-prompt.test.ts +102 -0
  25. package/src/agents/headless-prompt.ts +68 -0
  26. package/src/agents/hooks-deployer.test.ts +3119 -0
  27. package/src/agents/hooks-deployer.ts +804 -0
  28. package/src/agents/identity.test.ts +604 -0
  29. package/src/agents/identity.ts +384 -0
  30. package/src/agents/lifecycle.test.ts +196 -0
  31. package/src/agents/lifecycle.ts +183 -0
  32. package/src/agents/mail-poll-detect.test.ts +153 -0
  33. package/src/agents/mail-poll-detect.ts +73 -0
  34. package/src/agents/manifest.test.ts +1026 -0
  35. package/src/agents/manifest.ts +376 -0
  36. package/src/agents/overlay.test.ts +1058 -0
  37. package/src/agents/overlay.ts +490 -0
  38. package/src/agents/scope-detect.test.ts +190 -0
  39. package/src/agents/scope-detect.ts +146 -0
  40. package/src/agents/turn-lock.test.ts +181 -0
  41. package/src/agents/turn-lock.ts +235 -0
  42. package/src/agents/turn-runner-dispatch.test.ts +182 -0
  43. package/src/agents/turn-runner-dispatch.ts +105 -0
  44. package/src/agents/turn-runner.test.ts +2312 -0
  45. package/src/agents/turn-runner.ts +1383 -0
  46. package/src/beads/client.test.ts +217 -0
  47. package/src/beads/client.ts +230 -0
  48. package/src/beads/molecules.test.ts +338 -0
  49. package/src/beads/molecules.ts +198 -0
  50. package/src/commands/agents.test.ts +328 -0
  51. package/src/commands/agents.ts +299 -0
  52. package/src/commands/clean.test.ts +797 -0
  53. package/src/commands/clean.ts +791 -0
  54. package/src/commands/completions.test.ts +348 -0
  55. package/src/commands/completions.ts +981 -0
  56. package/src/commands/coordinator.test.ts +2975 -0
  57. package/src/commands/coordinator.ts +1841 -0
  58. package/src/commands/costs.test.ts +1183 -0
  59. package/src/commands/costs.ts +599 -0
  60. package/src/commands/dashboard.test.ts +954 -0
  61. package/src/commands/dashboard.ts +1212 -0
  62. package/src/commands/discover.test.ts +288 -0
  63. package/src/commands/discover.ts +202 -0
  64. package/src/commands/doctor.test.ts +303 -0
  65. package/src/commands/doctor.ts +311 -0
  66. package/src/commands/ecosystem.test.ts +226 -0
  67. package/src/commands/ecosystem.ts +248 -0
  68. package/src/commands/errors.test.ts +654 -0
  69. package/src/commands/errors.ts +197 -0
  70. package/src/commands/feed.test.ts +709 -0
  71. package/src/commands/feed.ts +260 -0
  72. package/src/commands/group.test.ts +475 -0
  73. package/src/commands/group.ts +546 -0
  74. package/src/commands/hooks.test.ts +458 -0
  75. package/src/commands/hooks.ts +263 -0
  76. package/src/commands/init.test.ts +1011 -0
  77. package/src/commands/init.ts +967 -0
  78. package/src/commands/inspect.test.ts +1239 -0
  79. package/src/commands/inspect.ts +648 -0
  80. package/src/commands/log.test.ts +1913 -0
  81. package/src/commands/log.ts +958 -0
  82. package/src/commands/logs.test.ts +801 -0
  83. package/src/commands/logs.ts +483 -0
  84. package/src/commands/mail.test.ts +1501 -0
  85. package/src/commands/mail.ts +848 -0
  86. package/src/commands/merge.test.ts +864 -0
  87. package/src/commands/merge.ts +381 -0
  88. package/src/commands/metrics.test.ts +458 -0
  89. package/src/commands/metrics.ts +129 -0
  90. package/src/commands/monitor.test.ts +191 -0
  91. package/src/commands/monitor.ts +409 -0
  92. package/src/commands/nudge.test.ts +579 -0
  93. package/src/commands/nudge.ts +646 -0
  94. package/src/commands/orchestrator.ts +42 -0
  95. package/src/commands/prime.test.ts +612 -0
  96. package/src/commands/prime.ts +359 -0
  97. package/src/commands/replay.test.ts +757 -0
  98. package/src/commands/replay.ts +231 -0
  99. package/src/commands/run.test.ts +469 -0
  100. package/src/commands/run.ts +353 -0
  101. package/src/commands/serve/agent-actions.test.ts +210 -0
  102. package/src/commands/serve/agent-actions.ts +192 -0
  103. package/src/commands/serve/build.test.ts +202 -0
  104. package/src/commands/serve/build.ts +206 -0
  105. package/src/commands/serve/coordinator-actions.test.ts +339 -0
  106. package/src/commands/serve/coordinator-actions.ts +410 -0
  107. package/src/commands/serve/dev.test.ts +168 -0
  108. package/src/commands/serve/dev.ts +117 -0
  109. package/src/commands/serve/mail-actions.test.ts +312 -0
  110. package/src/commands/serve/mail-actions.ts +167 -0
  111. package/src/commands/serve/rest.test.ts +1680 -0
  112. package/src/commands/serve/rest.ts +1130 -0
  113. package/src/commands/serve/static.ts +51 -0
  114. package/src/commands/serve/ws.test.ts +361 -0
  115. package/src/commands/serve/ws.ts +332 -0
  116. package/src/commands/serve.test.ts +459 -0
  117. package/src/commands/serve.ts +654 -0
  118. package/src/commands/sling.test.ts +1583 -0
  119. package/src/commands/sling.ts +1351 -0
  120. package/src/commands/spec.test.ts +179 -0
  121. package/src/commands/spec.ts +105 -0
  122. package/src/commands/status.test.ts +614 -0
  123. package/src/commands/status.ts +403 -0
  124. package/src/commands/stop.test.ts +964 -0
  125. package/src/commands/stop.ts +319 -0
  126. package/src/commands/supervisor.test.ts +185 -0
  127. package/src/commands/supervisor.ts +537 -0
  128. package/src/commands/trace.test.ts +762 -0
  129. package/src/commands/trace.ts +205 -0
  130. package/src/commands/update.test.ts +466 -0
  131. package/src/commands/update.ts +263 -0
  132. package/src/commands/upgrade.test.ts +48 -0
  133. package/src/commands/upgrade.ts +240 -0
  134. package/src/commands/watch.test.ts +257 -0
  135. package/src/commands/watch.ts +308 -0
  136. package/src/commands/worktree.test.ts +1297 -0
  137. package/src/commands/worktree.ts +451 -0
  138. package/src/config.test.ts +1535 -0
  139. package/src/config.ts +1064 -0
  140. package/src/doctor/agents.test.ts +523 -0
  141. package/src/doctor/agents.ts +399 -0
  142. package/src/doctor/config-check.test.ts +191 -0
  143. package/src/doctor/config-check.ts +183 -0
  144. package/src/doctor/consistency.test.ts +807 -0
  145. package/src/doctor/consistency.ts +347 -0
  146. package/src/doctor/databases.test.ts +350 -0
  147. package/src/doctor/databases.ts +243 -0
  148. package/src/doctor/dependencies.test.ts +296 -0
  149. package/src/doctor/dependencies.ts +272 -0
  150. package/src/doctor/ecosystem.test.ts +308 -0
  151. package/src/doctor/ecosystem.ts +156 -0
  152. package/src/doctor/logs.test.ts +253 -0
  153. package/src/doctor/logs.ts +295 -0
  154. package/src/doctor/merge-queue.test.ts +315 -0
  155. package/src/doctor/merge-queue.ts +167 -0
  156. package/src/doctor/providers.test.ts +409 -0
  157. package/src/doctor/providers.ts +250 -0
  158. package/src/doctor/serve.test.ts +95 -0
  159. package/src/doctor/serve.ts +86 -0
  160. package/src/doctor/structure.test.ts +423 -0
  161. package/src/doctor/structure.ts +285 -0
  162. package/src/doctor/types.ts +43 -0
  163. package/src/doctor/version.test.ts +241 -0
  164. package/src/doctor/version.ts +132 -0
  165. package/src/doctor/watchdog.test.ts +167 -0
  166. package/src/doctor/watchdog.ts +214 -0
  167. package/src/e2e/init-sling-lifecycle.test.ts +283 -0
  168. package/src/errors.test.ts +350 -0
  169. package/src/errors.ts +217 -0
  170. package/src/events/store.test.ts +660 -0
  171. package/src/events/store.ts +369 -0
  172. package/src/events/tailer.test.ts +719 -0
  173. package/src/events/tailer.ts +332 -0
  174. package/src/events/tool-filter.test.ts +330 -0
  175. package/src/events/tool-filter.ts +126 -0
  176. package/src/index.ts +533 -0
  177. package/src/insights/analyzer.test.ts +466 -0
  178. package/src/insights/analyzer.ts +203 -0
  179. package/src/insights/quality-gates.test.ts +141 -0
  180. package/src/insights/quality-gates.ts +156 -0
  181. package/src/json.test.ts +72 -0
  182. package/src/json.ts +53 -0
  183. package/src/loam/client.test.ts +752 -0
  184. package/src/loam/client.ts +664 -0
  185. package/src/logging/color.test.ts +252 -0
  186. package/src/logging/color.ts +105 -0
  187. package/src/logging/format.test.ts +110 -0
  188. package/src/logging/format.ts +255 -0
  189. package/src/logging/logger.test.ts +814 -0
  190. package/src/logging/logger.ts +266 -0
  191. package/src/logging/reporter.test.ts +259 -0
  192. package/src/logging/reporter.ts +110 -0
  193. package/src/logging/sanitizer.test.ts +190 -0
  194. package/src/logging/sanitizer.ts +57 -0
  195. package/src/logging/theme.ts +140 -0
  196. package/src/mail/broadcast.test.ts +204 -0
  197. package/src/mail/broadcast.ts +92 -0
  198. package/src/mail/client.test.ts +774 -0
  199. package/src/mail/client.ts +236 -0
  200. package/src/mail/store.test.ts +898 -0
  201. package/src/mail/store.ts +425 -0
  202. package/src/merge/lock.test.ts +149 -0
  203. package/src/merge/lock.ts +140 -0
  204. package/src/merge/predict.test.ts +387 -0
  205. package/src/merge/predict.ts +249 -0
  206. package/src/merge/queue.test.ts +426 -0
  207. package/src/merge/queue.ts +246 -0
  208. package/src/merge/resolver.test.ts +1993 -0
  209. package/src/merge/resolver.ts +926 -0
  210. package/src/metrics/pricing.test.ts +258 -0
  211. package/src/metrics/pricing.ts +135 -0
  212. package/src/metrics/store.test.ts +978 -0
  213. package/src/metrics/store.ts +501 -0
  214. package/src/metrics/summary.test.ts +398 -0
  215. package/src/metrics/summary.ts +178 -0
  216. package/src/metrics/transcript.test.ts +483 -0
  217. package/src/metrics/transcript.ts +114 -0
  218. package/src/runtimes/__fixtures__/claude-stream-fixture.ts +22 -0
  219. package/src/runtimes/aider.test.ts +124 -0
  220. package/src/runtimes/aider.ts +147 -0
  221. package/src/runtimes/amp.test.ts +164 -0
  222. package/src/runtimes/amp.ts +154 -0
  223. package/src/runtimes/claude.test.ts +1474 -0
  224. package/src/runtimes/claude.ts +579 -0
  225. package/src/runtimes/codex.test.ts +805 -0
  226. package/src/runtimes/codex.ts +273 -0
  227. package/src/runtimes/connections.test.ts +214 -0
  228. package/src/runtimes/connections.ts +103 -0
  229. package/src/runtimes/copilot.test.ts +707 -0
  230. package/src/runtimes/copilot.ts +316 -0
  231. package/src/runtimes/cursor.test.ts +497 -0
  232. package/src/runtimes/cursor.ts +205 -0
  233. package/src/runtimes/gemini.test.ts +537 -0
  234. package/src/runtimes/gemini.ts +243 -0
  235. package/src/runtimes/goose.test.ts +133 -0
  236. package/src/runtimes/goose.ts +157 -0
  237. package/src/runtimes/headless-connection.test.ts +264 -0
  238. package/src/runtimes/headless-connection.ts +158 -0
  239. package/src/runtimes/opencode.test.ts +325 -0
  240. package/src/runtimes/opencode.ts +188 -0
  241. package/src/runtimes/pi-guards.test.ts +486 -0
  242. package/src/runtimes/pi-guards.ts +367 -0
  243. package/src/runtimes/pi.test.ts +789 -0
  244. package/src/runtimes/pi.ts +305 -0
  245. package/src/runtimes/registry.test.ts +196 -0
  246. package/src/runtimes/registry.ts +99 -0
  247. package/src/runtimes/sapling.test.ts +1267 -0
  248. package/src/runtimes/sapling.ts +710 -0
  249. package/src/runtimes/types.ts +266 -0
  250. package/src/schema-consistency.test.ts +246 -0
  251. package/src/sessions/compat.test.ts +281 -0
  252. package/src/sessions/compat.ts +105 -0
  253. package/src/sessions/store.test.ts +1748 -0
  254. package/src/sessions/store.ts +858 -0
  255. package/src/test-helpers.test.ts +124 -0
  256. package/src/test-helpers.ts +145 -0
  257. package/src/test-setup.test.ts +31 -0
  258. package/src/test-setup.ts +28 -0
  259. package/src/tools/loam/api.ts +368 -0
  260. package/src/tools/loam/cli.ts +278 -0
  261. package/src/tools/loam/commands/add.ts +52 -0
  262. package/src/tools/loam/commands/archive.ts +214 -0
  263. package/src/tools/loam/commands/audit.ts +276 -0
  264. package/src/tools/loam/commands/compact.ts +1062 -0
  265. package/src/tools/loam/commands/completions.ts +79 -0
  266. package/src/tools/loam/commands/config.ts +381 -0
  267. package/src/tools/loam/commands/delete-domain.ts +121 -0
  268. package/src/tools/loam/commands/delete.ts +316 -0
  269. package/src/tools/loam/commands/diff.ts +200 -0
  270. package/src/tools/loam/commands/doctor.ts +1113 -0
  271. package/src/tools/loam/commands/edit.ts +226 -0
  272. package/src/tools/loam/commands/init.ts +31 -0
  273. package/src/tools/loam/commands/learn.ts +179 -0
  274. package/src/tools/loam/commands/move.ts +323 -0
  275. package/src/tools/loam/commands/onboard.ts +374 -0
  276. package/src/tools/loam/commands/outcome.ts +185 -0
  277. package/src/tools/loam/commands/prime.ts +688 -0
  278. package/src/tools/loam/commands/prune.ts +614 -0
  279. package/src/tools/loam/commands/query.ts +218 -0
  280. package/src/tools/loam/commands/rank.ts +180 -0
  281. package/src/tools/loam/commands/ready.ts +189 -0
  282. package/src/tools/loam/commands/record.ts +1210 -0
  283. package/src/tools/loam/commands/restore.ts +166 -0
  284. package/src/tools/loam/commands/search.ts +327 -0
  285. package/src/tools/loam/commands/setup.ts +887 -0
  286. package/src/tools/loam/commands/status.ts +103 -0
  287. package/src/tools/loam/commands/sync.ts +298 -0
  288. package/src/tools/loam/commands/update.ts +19 -0
  289. package/src/tools/loam/commands/upgrade.ts +93 -0
  290. package/src/tools/loam/commands/validate.ts +190 -0
  291. package/src/tools/loam/index.ts +62 -0
  292. package/src/tools/loam/log.ts +127 -0
  293. package/src/tools/loam/registry/builtins.ts +409 -0
  294. package/src/tools/loam/registry/custom.ts +431 -0
  295. package/src/tools/loam/registry/init.ts +55 -0
  296. package/src/tools/loam/registry/template.ts +40 -0
  297. package/src/tools/loam/registry/type-registry.ts +113 -0
  298. package/src/tools/loam/schemas/config-schema.ts +489 -0
  299. package/src/tools/loam/schemas/config.ts +245 -0
  300. package/src/tools/loam/schemas/index.ts +18 -0
  301. package/src/tools/loam/schemas/record-schema.ts +191 -0
  302. package/src/tools/loam/schemas/record.ts +115 -0
  303. package/src/tools/loam/utils/active-work.ts +205 -0
  304. package/src/tools/loam/utils/anchor-validity.ts +80 -0
  305. package/src/tools/loam/utils/archive.ts +146 -0
  306. package/src/tools/loam/utils/audit.ts +667 -0
  307. package/src/tools/loam/utils/bm25.ts +238 -0
  308. package/src/tools/loam/utils/budget.ts +142 -0
  309. package/src/tools/loam/utils/config.ts +344 -0
  310. package/src/tools/loam/utils/dir-anchors.ts +62 -0
  311. package/src/tools/loam/utils/domain-rules.ts +114 -0
  312. package/src/tools/loam/utils/expertise.ts +393 -0
  313. package/src/tools/loam/utils/format-helpers.ts +96 -0
  314. package/src/tools/loam/utils/format.ts +1234 -0
  315. package/src/tools/loam/utils/git-context.ts +50 -0
  316. package/src/tools/loam/utils/git.ts +183 -0
  317. package/src/tools/loam/utils/hooks.ts +299 -0
  318. package/src/tools/loam/utils/index.ts +52 -0
  319. package/src/tools/loam/utils/json-output.ts +13 -0
  320. package/src/tools/loam/utils/lock.ts +76 -0
  321. package/src/tools/loam/utils/markers.ts +48 -0
  322. package/src/tools/loam/utils/numeric-flags.ts +20 -0
  323. package/src/tools/loam/utils/palette.ts +44 -0
  324. package/src/tools/loam/utils/prime-ranking.ts +135 -0
  325. package/src/tools/loam/utils/recipe-discovery.ts +195 -0
  326. package/src/tools/loam/utils/runtime-flags.ts +28 -0
  327. package/src/tools/loam/utils/scoring.ts +94 -0
  328. package/src/tools/loam/utils/version.ts +116 -0
  329. package/src/tools/sprout/commands/block.ts +64 -0
  330. package/src/tools/sprout/commands/blocked.ts +86 -0
  331. package/src/tools/sprout/commands/close.ts +129 -0
  332. package/src/tools/sprout/commands/completions.ts +198 -0
  333. package/src/tools/sprout/commands/config.ts +238 -0
  334. package/src/tools/sprout/commands/create.ts +164 -0
  335. package/src/tools/sprout/commands/dep.ts +148 -0
  336. package/src/tools/sprout/commands/doctor.ts +979 -0
  337. package/src/tools/sprout/commands/init.ts +83 -0
  338. package/src/tools/sprout/commands/label.ts +178 -0
  339. package/src/tools/sprout/commands/list.ts +210 -0
  340. package/src/tools/sprout/commands/migrate.ts +133 -0
  341. package/src/tools/sprout/commands/onboard.ts +207 -0
  342. package/src/tools/sprout/commands/plan-show.ts +278 -0
  343. package/src/tools/sprout/commands/plan.ts +2526 -0
  344. package/src/tools/sprout/commands/prime.ts +399 -0
  345. package/src/tools/sprout/commands/ready.ts +245 -0
  346. package/src/tools/sprout/commands/search.ts +221 -0
  347. package/src/tools/sprout/commands/show.ts +277 -0
  348. package/src/tools/sprout/commands/stats.ts +146 -0
  349. package/src/tools/sprout/commands/sync.ts +134 -0
  350. package/src/tools/sprout/commands/tpl.ts +364 -0
  351. package/src/tools/sprout/commands/unblock.ts +115 -0
  352. package/src/tools/sprout/commands/update.ts +257 -0
  353. package/src/tools/sprout/commands/upgrade.ts +91 -0
  354. package/src/tools/sprout/config-schema.ts +152 -0
  355. package/src/tools/sprout/config.ts +355 -0
  356. package/src/tools/sprout/filter.ts +107 -0
  357. package/src/tools/sprout/format.ts +43 -0
  358. package/src/tools/sprout/id.ts +22 -0
  359. package/src/tools/sprout/index.ts +204 -0
  360. package/src/tools/sprout/log.ts +76 -0
  361. package/src/tools/sprout/markers.ts +22 -0
  362. package/src/tools/sprout/output.ts +121 -0
  363. package/src/tools/sprout/plan-backref.ts +93 -0
  364. package/src/tools/sprout/plan-context.ts +81 -0
  365. package/src/tools/sprout/plan-domain.ts +139 -0
  366. package/src/tools/sprout/plan-lifecycle.ts +65 -0
  367. package/src/tools/sprout/plan-loam.ts +207 -0
  368. package/src/tools/sprout/plan-schema.ts +209 -0
  369. package/src/tools/sprout/sort.ts +31 -0
  370. package/src/tools/sprout/store.ts +172 -0
  371. package/src/tools/sprout/types.ts +118 -0
  372. package/src/tools/sprout/validation.ts +119 -0
  373. package/src/tools/sprout/version.ts +1 -0
  374. package/src/tools/sprout/yaml.ts +387 -0
  375. package/src/tools/trellis/commands/archive.ts +87 -0
  376. package/src/tools/trellis/commands/completions.ts +610 -0
  377. package/src/tools/trellis/commands/config.ts +382 -0
  378. package/src/tools/trellis/commands/create.ts +252 -0
  379. package/src/tools/trellis/commands/diff.ts +150 -0
  380. package/src/tools/trellis/commands/doctor.ts +771 -0
  381. package/src/tools/trellis/commands/emit.ts +365 -0
  382. package/src/tools/trellis/commands/history.ts +83 -0
  383. package/src/tools/trellis/commands/import.ts +198 -0
  384. package/src/tools/trellis/commands/init.ts +81 -0
  385. package/src/tools/trellis/commands/list.ts +103 -0
  386. package/src/tools/trellis/commands/onboard.ts +156 -0
  387. package/src/tools/trellis/commands/pin.ts +172 -0
  388. package/src/tools/trellis/commands/prime.ts +193 -0
  389. package/src/tools/trellis/commands/render.ts +122 -0
  390. package/src/tools/trellis/commands/schema.ts +353 -0
  391. package/src/tools/trellis/commands/show.ts +115 -0
  392. package/src/tools/trellis/commands/stats.ts +65 -0
  393. package/src/tools/trellis/commands/sync.ts +112 -0
  394. package/src/tools/trellis/commands/tree.ts +123 -0
  395. package/src/tools/trellis/commands/update.ts +330 -0
  396. package/src/tools/trellis/commands/upgrade.ts +95 -0
  397. package/src/tools/trellis/commands/validate.ts +166 -0
  398. package/src/tools/trellis/config-schema.ts +81 -0
  399. package/src/tools/trellis/config.ts +108 -0
  400. package/src/tools/trellis/frontmatter.ts +348 -0
  401. package/src/tools/trellis/id.ts +24 -0
  402. package/src/tools/trellis/index.ts +209 -0
  403. package/src/tools/trellis/markers.ts +28 -0
  404. package/src/tools/trellis/output.ts +84 -0
  405. package/src/tools/trellis/render.ts +212 -0
  406. package/src/tools/trellis/store.ts +144 -0
  407. package/src/tools/trellis/types.ts +82 -0
  408. package/src/tools/trellis/validate.ts +199 -0
  409. package/src/tools/trellis/yaml.ts +309 -0
  410. package/src/tracker/beads.test.ts +454 -0
  411. package/src/tracker/beads.ts +56 -0
  412. package/src/tracker/factory.test.ts +90 -0
  413. package/src/tracker/factory.ts +65 -0
  414. package/src/tracker/sprout.test.ts +461 -0
  415. package/src/tracker/sprout.ts +182 -0
  416. package/src/tracker/types.ts +52 -0
  417. package/src/trellis/client.test.ts +107 -0
  418. package/src/trellis/client.ts +179 -0
  419. package/src/types.ts +970 -0
  420. package/src/utils/bin.test.ts +10 -0
  421. package/src/utils/bin.ts +37 -0
  422. package/src/utils/browser.test.ts +49 -0
  423. package/src/utils/browser.ts +48 -0
  424. package/src/utils/fs.test.ts +119 -0
  425. package/src/utils/fs.ts +62 -0
  426. package/src/utils/pid.test.ts +152 -0
  427. package/src/utils/pid.ts +130 -0
  428. package/src/utils/process-scan.test.ts +53 -0
  429. package/src/utils/process-scan.ts +76 -0
  430. package/src/utils/time.test.ts +43 -0
  431. package/src/utils/time.ts +37 -0
  432. package/src/utils/version.test.ts +33 -0
  433. package/src/utils/version.ts +70 -0
  434. package/src/version.ts +5 -0
  435. package/src/watchdog/daemon.test.ts +3721 -0
  436. package/src/watchdog/daemon.ts +1257 -0
  437. package/src/watchdog/health.test.ts +830 -0
  438. package/src/watchdog/health.ts +434 -0
  439. package/src/watchdog/triage.test.ts +205 -0
  440. package/src/watchdog/triage.ts +205 -0
  441. package/src/worktree/manager.test.ts +720 -0
  442. package/src/worktree/manager.ts +405 -0
  443. package/src/worktree/process.test.ts +172 -0
  444. package/src/worktree/process.ts +131 -0
  445. package/src/worktree/tmux.test.ts +1616 -0
  446. package/src/worktree/tmux.ts +721 -0
  447. package/templates/CLAUDE.md.tmpl +100 -0
  448. package/templates/copilot-hooks.json.tmpl +13 -0
  449. package/templates/hooks.json.tmpl +109 -0
  450. package/templates/overlay.md.tmpl +88 -0
  451. package/ui/dist/apple-touch-icon-bdy6teep.png +0 -0
  452. package/ui/dist/chunk-8s31f05k.css +1 -0
  453. package/ui/dist/chunk-vm5rz679.js +300 -0
  454. package/ui/dist/favicon-nzb39vza.svg +4 -0
  455. package/ui/dist/index.html +17 -0
@@ -0,0 +1,483 @@
1
+ /**
2
+ * Tests for Claude Code transcript JSONL parser and pricing.ts module.
3
+ *
4
+ * Uses temp files with real-format JSONL data. No mocks.
5
+ * Philosophy: "never mock what you can use for real" (mx-252b16).
6
+ *
7
+ * Coverage:
8
+ * - parseTranscriptUsage (transcript.ts)
9
+ * - estimateCost (pricing.ts, imported directly)
10
+ * - getPricingForModel (pricing.ts)
11
+ */
12
+
13
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
14
+ import { mkdtemp } from "node:fs/promises";
15
+ import { tmpdir } from "node:os";
16
+ import { join } from "node:path";
17
+ import { cleanupTempDir } from "../test-helpers.ts";
18
+ import { estimateCost, getPricingForModel } from "./pricing.ts";
19
+ import { parseTranscriptUsage } from "./transcript.ts";
20
+
21
+ let tempDir: string;
22
+
23
+ beforeEach(async () => {
24
+ tempDir = await mkdtemp(join(tmpdir(), "agentplate-transcript-test-"));
25
+ });
26
+
27
+ afterEach(async () => {
28
+ await cleanupTempDir(tempDir);
29
+ });
30
+
31
+ /** Write a JSONL file with the given lines. */
32
+ async function writeJsonl(filename: string, lines: unknown[]): Promise<string> {
33
+ const path = join(tempDir, filename);
34
+ const content = `${lines.map((l) => JSON.stringify(l)).join("\n")}\n`;
35
+ await Bun.write(path, content);
36
+ return path;
37
+ }
38
+
39
+ // === parseTranscriptUsage ===
40
+
41
+ describe("parseTranscriptUsage", () => {
42
+ test("parses a single assistant entry with all usage fields", async () => {
43
+ const path = await writeJsonl("single.jsonl", [
44
+ {
45
+ type: "assistant",
46
+ message: {
47
+ model: "claude-opus-4-6",
48
+ usage: {
49
+ input_tokens: 100,
50
+ output_tokens: 50,
51
+ cache_read_input_tokens: 1000,
52
+ cache_creation_input_tokens: 500,
53
+ },
54
+ },
55
+ },
56
+ ]);
57
+
58
+ const usage = await parseTranscriptUsage(path);
59
+
60
+ expect(usage.inputTokens).toBe(100);
61
+ expect(usage.outputTokens).toBe(50);
62
+ expect(usage.cacheReadTokens).toBe(1000);
63
+ expect(usage.cacheCreationTokens).toBe(500);
64
+ expect(usage.modelUsed).toBe("claude-opus-4-6");
65
+ });
66
+
67
+ test("aggregates usage across multiple assistant turns", async () => {
68
+ const path = await writeJsonl("multi.jsonl", [
69
+ {
70
+ type: "assistant",
71
+ message: {
72
+ model: "claude-sonnet-4-20250514",
73
+ usage: {
74
+ input_tokens: 100,
75
+ output_tokens: 50,
76
+ cache_read_input_tokens: 1000,
77
+ cache_creation_input_tokens: 500,
78
+ },
79
+ },
80
+ },
81
+ {
82
+ type: "human",
83
+ message: { content: "follow-up question" },
84
+ },
85
+ {
86
+ type: "assistant",
87
+ message: {
88
+ model: "claude-sonnet-4-20250514",
89
+ usage: {
90
+ input_tokens: 200,
91
+ output_tokens: 75,
92
+ cache_read_input_tokens: 2000,
93
+ cache_creation_input_tokens: 0,
94
+ },
95
+ },
96
+ },
97
+ ]);
98
+
99
+ const usage = await parseTranscriptUsage(path);
100
+
101
+ expect(usage.inputTokens).toBe(300);
102
+ expect(usage.outputTokens).toBe(125);
103
+ expect(usage.cacheReadTokens).toBe(3000);
104
+ expect(usage.cacheCreationTokens).toBe(500);
105
+ expect(usage.modelUsed).toBe("claude-sonnet-4-20250514");
106
+ });
107
+
108
+ test("skips non-assistant entries (human, system, tool_use, etc.)", async () => {
109
+ const path = await writeJsonl("mixed.jsonl", [
110
+ { type: "system", content: "system prompt" },
111
+ {
112
+ type: "assistant",
113
+ message: {
114
+ model: "claude-opus-4-6",
115
+ usage: {
116
+ input_tokens: 100,
117
+ output_tokens: 50,
118
+ cache_read_input_tokens: 0,
119
+ cache_creation_input_tokens: 0,
120
+ },
121
+ },
122
+ },
123
+ { type: "human", message: { content: "hello" } },
124
+ { type: "tool_result", content: "result" },
125
+ ]);
126
+
127
+ const usage = await parseTranscriptUsage(path);
128
+
129
+ expect(usage.inputTokens).toBe(100);
130
+ expect(usage.outputTokens).toBe(50);
131
+ });
132
+
133
+ test("returns zeros for empty file", async () => {
134
+ const path = join(tempDir, "empty.jsonl");
135
+ await Bun.write(path, "");
136
+
137
+ const usage = await parseTranscriptUsage(path);
138
+
139
+ expect(usage.inputTokens).toBe(0);
140
+ expect(usage.outputTokens).toBe(0);
141
+ expect(usage.cacheReadTokens).toBe(0);
142
+ expect(usage.cacheCreationTokens).toBe(0);
143
+ expect(usage.modelUsed).toBeNull();
144
+ });
145
+
146
+ test("returns zeros for file with no assistant entries", async () => {
147
+ const path = await writeJsonl("no-assistant.jsonl", [
148
+ { type: "human", message: { content: "hello" } },
149
+ { type: "system", content: "system prompt" },
150
+ ]);
151
+
152
+ const usage = await parseTranscriptUsage(path);
153
+
154
+ expect(usage.inputTokens).toBe(0);
155
+ expect(usage.outputTokens).toBe(0);
156
+ expect(usage.modelUsed).toBeNull();
157
+ });
158
+
159
+ test("gracefully handles malformed JSON lines", async () => {
160
+ const path = join(tempDir, "malformed.jsonl");
161
+ const content = [
162
+ '{"type":"assistant","message":{"model":"claude-opus-4-6","usage":{"input_tokens":100,"output_tokens":50,"cache_read_input_tokens":0,"cache_creation_input_tokens":0}}}',
163
+ "this is not valid json",
164
+ "",
165
+ '{"type":"assistant","message":{"model":"claude-opus-4-6","usage":{"input_tokens":200,"output_tokens":75,"cache_read_input_tokens":0,"cache_creation_input_tokens":0}}}',
166
+ ].join("\n");
167
+ await Bun.write(path, content);
168
+
169
+ const usage = await parseTranscriptUsage(path);
170
+
171
+ // Should parse the two valid assistant entries, skip the malformed line
172
+ expect(usage.inputTokens).toBe(300);
173
+ expect(usage.outputTokens).toBe(125);
174
+ });
175
+
176
+ test("handles assistant entries with missing usage fields (defaults to 0)", async () => {
177
+ const path = await writeJsonl("partial.jsonl", [
178
+ {
179
+ type: "assistant",
180
+ message: {
181
+ model: "claude-haiku-3-5-20241022",
182
+ usage: {
183
+ input_tokens: 100,
184
+ output_tokens: 50,
185
+ // No cache fields
186
+ },
187
+ },
188
+ },
189
+ ]);
190
+
191
+ const usage = await parseTranscriptUsage(path);
192
+
193
+ expect(usage.inputTokens).toBe(100);
194
+ expect(usage.outputTokens).toBe(50);
195
+ expect(usage.cacheReadTokens).toBe(0);
196
+ expect(usage.cacheCreationTokens).toBe(0);
197
+ });
198
+
199
+ test("handles assistant entries with no usage object", async () => {
200
+ const path = await writeJsonl("no-usage.jsonl", [
201
+ {
202
+ type: "assistant",
203
+ message: {
204
+ model: "claude-opus-4-6",
205
+ content: "response without usage",
206
+ },
207
+ },
208
+ ]);
209
+
210
+ const usage = await parseTranscriptUsage(path);
211
+
212
+ expect(usage.inputTokens).toBe(0);
213
+ expect(usage.outputTokens).toBe(0);
214
+ expect(usage.modelUsed).toBeNull();
215
+ });
216
+
217
+ test("captures model from first assistant turn only", async () => {
218
+ const path = await writeJsonl("model-change.jsonl", [
219
+ {
220
+ type: "assistant",
221
+ message: {
222
+ model: "claude-sonnet-4-20250514",
223
+ usage: {
224
+ input_tokens: 10,
225
+ output_tokens: 5,
226
+ cache_read_input_tokens: 0,
227
+ cache_creation_input_tokens: 0,
228
+ },
229
+ },
230
+ },
231
+ {
232
+ type: "assistant",
233
+ message: {
234
+ model: "claude-opus-4-6",
235
+ usage: {
236
+ input_tokens: 20,
237
+ output_tokens: 10,
238
+ cache_read_input_tokens: 0,
239
+ cache_creation_input_tokens: 0,
240
+ },
241
+ },
242
+ },
243
+ ]);
244
+
245
+ const usage = await parseTranscriptUsage(path);
246
+
247
+ expect(usage.modelUsed).toBe("claude-sonnet-4-20250514");
248
+ expect(usage.inputTokens).toBe(30);
249
+ });
250
+
251
+ test("handles real-world transcript format with trailing newlines", async () => {
252
+ const path = join(tempDir, "trailing.jsonl");
253
+ const content =
254
+ '{"type":"assistant","message":{"model":"claude-opus-4-6","usage":{"input_tokens":3,"output_tokens":9,"cache_read_input_tokens":19401,"cache_creation_input_tokens":9918}}}\n\n\n';
255
+ await Bun.write(path, content);
256
+
257
+ const usage = await parseTranscriptUsage(path);
258
+
259
+ expect(usage.inputTokens).toBe(3);
260
+ expect(usage.outputTokens).toBe(9);
261
+ expect(usage.cacheReadTokens).toBe(19401);
262
+ expect(usage.cacheCreationTokens).toBe(9918);
263
+ });
264
+ });
265
+
266
+ // === estimateCost ===
267
+
268
+ describe("estimateCost", () => {
269
+ test("calculates cost for opus model", () => {
270
+ const cost = estimateCost({
271
+ inputTokens: 1_000_000,
272
+ outputTokens: 1_000_000,
273
+ cacheReadTokens: 1_000_000,
274
+ cacheCreationTokens: 1_000_000,
275
+ modelUsed: "claude-opus-4-6",
276
+ });
277
+
278
+ // opus: input=$15, output=$75, cacheRead=$1.50, cacheCreation=$3.75
279
+ expect(cost).toBeCloseTo(95.25, 2);
280
+ });
281
+
282
+ test("calculates cost for sonnet model", () => {
283
+ const cost = estimateCost({
284
+ inputTokens: 1_000_000,
285
+ outputTokens: 1_000_000,
286
+ cacheReadTokens: 1_000_000,
287
+ cacheCreationTokens: 1_000_000,
288
+ modelUsed: "claude-sonnet-4-20250514",
289
+ });
290
+
291
+ // sonnet: input=$3, output=$15, cacheRead=$0.30, cacheCreation=$0.75
292
+ expect(cost).toBeCloseTo(19.05, 2);
293
+ });
294
+
295
+ test("calculates cost for haiku model", () => {
296
+ const cost = estimateCost({
297
+ inputTokens: 1_000_000,
298
+ outputTokens: 1_000_000,
299
+ cacheReadTokens: 1_000_000,
300
+ cacheCreationTokens: 1_000_000,
301
+ modelUsed: "claude-haiku-3-5-20241022",
302
+ });
303
+
304
+ // haiku: input=$0.80, output=$4, cacheRead=$0.08, cacheCreation=$0.20
305
+ expect(cost).toBeCloseTo(5.08, 2);
306
+ });
307
+
308
+ test("returns null for unknown model", () => {
309
+ const cost = estimateCost({
310
+ inputTokens: 1_000_000,
311
+ outputTokens: 1_000_000,
312
+ cacheReadTokens: 0,
313
+ cacheCreationTokens: 0,
314
+ modelUsed: "unknown-model-xyz",
315
+ });
316
+
317
+ expect(cost).toBeNull();
318
+ });
319
+
320
+ test("calculates cost for gpt-4o", () => {
321
+ const cost = estimateCost({
322
+ inputTokens: 1_000_000,
323
+ outputTokens: 1_000_000,
324
+ cacheReadTokens: 1_000_000,
325
+ cacheCreationTokens: 1_000_000,
326
+ modelUsed: "gpt-4o",
327
+ });
328
+
329
+ // gpt-4o: input=2.5, output=10, cacheRead=1.25, cacheCreation=2.5 => total=16.25
330
+ expect(cost).toBeCloseTo(16.25, 2);
331
+ });
332
+
333
+ test("calculates cost for gemini flash", () => {
334
+ const cost = estimateCost({
335
+ inputTokens: 1_000_000,
336
+ outputTokens: 1_000_000,
337
+ cacheReadTokens: 1_000_000,
338
+ cacheCreationTokens: 1_000_000,
339
+ modelUsed: "gemini-2.5-flash",
340
+ });
341
+
342
+ // gemini-flash: input=0.1, output=0.4, cacheRead=0.025, cacheCreation=0.1 => total=0.625
343
+ expect(cost).toBeCloseTo(0.625, 3);
344
+ });
345
+
346
+ test("returns null when modelUsed is null", () => {
347
+ const cost = estimateCost({
348
+ inputTokens: 1_000_000,
349
+ outputTokens: 1_000_000,
350
+ cacheReadTokens: 0,
351
+ cacheCreationTokens: 0,
352
+ modelUsed: null,
353
+ });
354
+
355
+ expect(cost).toBeNull();
356
+ });
357
+
358
+ test("zero tokens yields zero cost", () => {
359
+ const cost = estimateCost({
360
+ inputTokens: 0,
361
+ outputTokens: 0,
362
+ cacheReadTokens: 0,
363
+ cacheCreationTokens: 0,
364
+ modelUsed: "claude-opus-4-6",
365
+ });
366
+
367
+ expect(cost).toBe(0);
368
+ });
369
+
370
+ test("realistic session cost calculation", () => {
371
+ // A typical agent session: ~20K input, ~5K output, heavy cache reads
372
+ const cost = estimateCost({
373
+ inputTokens: 20_000,
374
+ outputTokens: 5_000,
375
+ cacheReadTokens: 100_000,
376
+ cacheCreationTokens: 15_000,
377
+ modelUsed: "claude-sonnet-4-20250514",
378
+ });
379
+
380
+ // sonnet: (20K/1M)*3 + (5K/1M)*15 + (100K/1M)*0.30 + (15K/1M)*0.75
381
+ // = 0.06 + 0.075 + 0.03 + 0.01125 = $0.17625
382
+ expect(cost).not.toBeNull();
383
+ if (cost !== null) {
384
+ expect(cost).toBeGreaterThan(0.1);
385
+ expect(cost).toBeLessThan(1.0);
386
+ }
387
+ });
388
+ });
389
+
390
+ // === getPricingForModel (pricing.ts) ===
391
+
392
+ describe("getPricingForModel", () => {
393
+ test("matches opus substring", () => {
394
+ const pricing = getPricingForModel("claude-opus-4-6");
395
+ expect(pricing).not.toBeNull();
396
+ if (pricing !== null) {
397
+ expect(pricing.inputPerMTok).toBe(15);
398
+ expect(pricing.outputPerMTok).toBe(75);
399
+ }
400
+ });
401
+
402
+ test("matches sonnet substring", () => {
403
+ const pricing = getPricingForModel("claude-sonnet-4-20250514");
404
+ expect(pricing).not.toBeNull();
405
+ if (pricing !== null) {
406
+ expect(pricing.inputPerMTok).toBe(3);
407
+ expect(pricing.outputPerMTok).toBe(15);
408
+ }
409
+ });
410
+
411
+ test("matches haiku substring", () => {
412
+ const pricing = getPricingForModel("claude-haiku-3-5-20241022");
413
+ expect(pricing).not.toBeNull();
414
+ if (pricing !== null) {
415
+ expect(pricing.inputPerMTok).toBe(0.8);
416
+ expect(pricing.outputPerMTok).toBe(4);
417
+ }
418
+ });
419
+
420
+ test("returns null for unknown model", () => {
421
+ const pricing = getPricingForModel("unknown-model-xyz");
422
+ expect(pricing).toBeNull();
423
+ });
424
+
425
+ test("matches gpt-4o", () => {
426
+ const pricing = getPricingForModel("gpt-4o");
427
+ expect(pricing).not.toBeNull();
428
+ if (pricing !== null) {
429
+ expect(pricing.inputPerMTok).toBe(2.5);
430
+ }
431
+ });
432
+
433
+ test("matches gpt-4o-mini", () => {
434
+ const pricing = getPricingForModel("gpt-4o-mini");
435
+ expect(pricing).not.toBeNull();
436
+ if (pricing !== null) {
437
+ expect(pricing.inputPerMTok).toBe(0.15);
438
+ }
439
+ });
440
+
441
+ test("matches gpt-5", () => {
442
+ const pricing = getPricingForModel("gpt-5");
443
+ expect(pricing).not.toBeNull();
444
+ if (pricing !== null) {
445
+ expect(pricing.inputPerMTok).toBe(10);
446
+ }
447
+ });
448
+
449
+ test("matches o1", () => {
450
+ const pricing = getPricingForModel("o1");
451
+ expect(pricing).not.toBeNull();
452
+ if (pricing !== null) {
453
+ expect(pricing.inputPerMTok).toBe(15);
454
+ }
455
+ });
456
+
457
+ test("matches o3", () => {
458
+ const pricing = getPricingForModel("o3");
459
+ expect(pricing).not.toBeNull();
460
+ if (pricing !== null) {
461
+ expect(pricing.inputPerMTok).toBe(10);
462
+ }
463
+ });
464
+
465
+ test("matches gemini flash", () => {
466
+ const pricing = getPricingForModel("gemini-2.5-flash");
467
+ expect(pricing).not.toBeNull();
468
+ if (pricing !== null) {
469
+ expect(pricing.inputPerMTok).toBe(0.1);
470
+ }
471
+ });
472
+
473
+ test("matches gemini pro", () => {
474
+ const pricing = getPricingForModel("gemini-2.5-pro");
475
+ expect(pricing).not.toBeNull();
476
+ if (pricing !== null) {
477
+ expect(pricing.inputPerMTok).toBe(1.25);
478
+ }
479
+ });
480
+ });
481
+
482
+ // estimateCost re-export removed from transcript.ts (agentplate-aa00).
483
+ // estimateCost is now imported directly from pricing.ts everywhere.
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Parser for Claude Code transcript JSONL files.
3
+ *
4
+ * This is a Claude Code-specific JSONL parser that extracts token usage data
5
+ * from assistant-type entries in transcript files at
6
+ * the runtime-specific transcript directory (e.g. ~/.claude/projects/ for Claude Code).
7
+ *
8
+ * Runtime-agnostic pricing logic lives in ./pricing.ts. Other runtimes
9
+ * implement their own transcript parsing via AgentRuntime.parseTranscript().
10
+ *
11
+ * Each assistant entry contains per-turn usage:
12
+ * {
13
+ * "type": "assistant",
14
+ * "message": {
15
+ * "model": "claude-opus-4-6",
16
+ * "usage": {
17
+ * "input_tokens": 3,
18
+ * "output_tokens": 9,
19
+ * "cache_read_input_tokens": 19401,
20
+ * "cache_creation_input_tokens": 9918
21
+ * }
22
+ * }
23
+ * }
24
+ */
25
+
26
+ import type { TokenUsage } from "./pricing.ts";
27
+
28
+ export type TranscriptUsage = TokenUsage;
29
+
30
+ /**
31
+ * Narrow an unknown value to determine if it looks like a transcript assistant entry.
32
+ * Returns the usage fields if valid, or null otherwise.
33
+ */
34
+ function extractUsageFromEntry(entry: unknown): {
35
+ inputTokens: number;
36
+ outputTokens: number;
37
+ cacheReadTokens: number;
38
+ cacheCreationTokens: number;
39
+ model: string | undefined;
40
+ } | null {
41
+ if (typeof entry !== "object" || entry === null) return null;
42
+
43
+ const obj = entry as Record<string, unknown>;
44
+ if (obj.type !== "assistant") return null;
45
+
46
+ const message = obj.message;
47
+ if (typeof message !== "object" || message === null) return null;
48
+
49
+ const msg = message as Record<string, unknown>;
50
+ const usage = msg.usage;
51
+ if (typeof usage !== "object" || usage === null) return null;
52
+
53
+ const u = usage as Record<string, unknown>;
54
+
55
+ return {
56
+ inputTokens: typeof u.input_tokens === "number" ? u.input_tokens : 0,
57
+ outputTokens: typeof u.output_tokens === "number" ? u.output_tokens : 0,
58
+ cacheReadTokens: typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : 0,
59
+ cacheCreationTokens:
60
+ typeof u.cache_creation_input_tokens === "number" ? u.cache_creation_input_tokens : 0,
61
+ model: typeof msg.model === "string" ? msg.model : undefined,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Parse a Claude Code transcript JSONL file and aggregate token usage.
67
+ *
68
+ * Reads the file line by line, extracting usage data from each assistant
69
+ * entry. Returns aggregated totals and the model from the first assistant turn.
70
+ *
71
+ * @param transcriptPath - Absolute path to the transcript JSONL file
72
+ * @returns Aggregated usage data across all assistant turns
73
+ */
74
+ export async function parseTranscriptUsage(transcriptPath: string): Promise<TranscriptUsage> {
75
+ const file = Bun.file(transcriptPath);
76
+ const text = await file.text();
77
+ const lines = text.split("\n");
78
+
79
+ const result: TranscriptUsage = {
80
+ inputTokens: 0,
81
+ outputTokens: 0,
82
+ cacheReadTokens: 0,
83
+ cacheCreationTokens: 0,
84
+ modelUsed: null,
85
+ };
86
+
87
+ for (const line of lines) {
88
+ const trimmed = line.trim();
89
+ if (trimmed.length === 0) continue;
90
+
91
+ let parsed: unknown;
92
+ try {
93
+ parsed = JSON.parse(trimmed);
94
+ } catch {
95
+ // Skip malformed lines
96
+ continue;
97
+ }
98
+
99
+ const usage = extractUsageFromEntry(parsed);
100
+ if (usage === null) continue;
101
+
102
+ result.inputTokens += usage.inputTokens;
103
+ result.outputTokens += usage.outputTokens;
104
+ result.cacheReadTokens += usage.cacheReadTokens;
105
+ result.cacheCreationTokens += usage.cacheCreationTokens;
106
+
107
+ // Capture model from first assistant turn
108
+ if (result.modelUsed === null && usage.model !== undefined) {
109
+ result.modelUsed = usage.model;
110
+ }
111
+ }
112
+
113
+ return result;
114
+ }
@@ -0,0 +1,22 @@
1
+ // Fixture: emits a known sequence of Claude stream-json lines to stdout then exits.
2
+ // Used by the ClaudeRuntime.parseEvents() integration test.
3
+ const lines = [
4
+ JSON.stringify({ type: "system", subtype: "init", session_id: "sess-123" }),
5
+ JSON.stringify({
6
+ type: "assistant",
7
+ message: {
8
+ model: "claude-sonnet-4-6",
9
+ content: [{ type: "text", text: "hello" }],
10
+ usage: { input_tokens: 10, output_tokens: 5 },
11
+ },
12
+ }),
13
+ JSON.stringify({
14
+ type: "result",
15
+ session_id: "sess-123",
16
+ result: "done",
17
+ is_error: false,
18
+ duration_ms: 1234,
19
+ num_turns: 1,
20
+ }),
21
+ ];
22
+ for (const l of lines) process.stdout.write(`${l}\n`);