@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,501 @@
1
+ /**
2
+ * SQLite-backed metrics storage for agent session data.
3
+ *
4
+ * Uses bun:sqlite for zero-dependency, synchronous database access.
5
+ * All operations are sync — no async/await needed.
6
+ */
7
+
8
+ import { Database } from "bun:sqlite";
9
+ import type { SessionMetrics, TokenSnapshot } from "../types.ts";
10
+
11
+ export interface MetricsStore {
12
+ recordSession(metrics: SessionMetrics): void;
13
+ getRecentSessions(limit?: number): SessionMetrics[];
14
+ getSessionsByAgent(agentName: string): SessionMetrics[];
15
+ getSessionsByRun(runId: string): SessionMetrics[];
16
+ getSessionsByTask(taskId: string): SessionMetrics[];
17
+ getAverageDuration(capability?: string): number;
18
+ /** Count the total number of sessions in the database (no limit cap). */
19
+ countSessions(): number;
20
+ /** Delete metrics matching the given criteria. Returns the number of rows deleted. */
21
+ purge(options: { all?: boolean; agent?: string }): number;
22
+ /** Record a token usage snapshot for a running agent. */
23
+ recordSnapshot(snapshot: TokenSnapshot): void;
24
+ /** Get the most recent snapshot per active agent (one row per agent).
25
+ * When runId is provided, restricts to snapshots recorded for that run. */
26
+ getLatestSnapshots(runId?: string): TokenSnapshot[];
27
+ /** Get the timestamp of the most recent snapshot for an agent, or null. */
28
+ getLatestSnapshotTime(agentName: string): string | null;
29
+ /** Delete snapshots matching criteria. Returns number of rows deleted. */
30
+ purgeSnapshots(options: { all?: boolean; agent?: string; olderThanMs?: number }): number;
31
+ close(): void;
32
+ }
33
+
34
+ /** Row shape as stored in SQLite (snake_case columns). */
35
+ interface SessionRow {
36
+ agent_name: string;
37
+ task_id: string;
38
+ capability: string;
39
+ started_at: string;
40
+ completed_at: string | null;
41
+ duration_ms: number;
42
+ exit_code: number | null;
43
+ merge_result: string | null;
44
+ parent_agent: string | null;
45
+ input_tokens: number;
46
+ output_tokens: number;
47
+ cache_read_tokens: number;
48
+ cache_creation_tokens: number;
49
+ estimated_cost_usd: number | null;
50
+ model_used: string | null;
51
+ run_id: string | null;
52
+ }
53
+
54
+ /** Snapshot row shape as stored in SQLite (snake_case columns). */
55
+ interface SnapshotRow {
56
+ id: number;
57
+ agent_name: string;
58
+ input_tokens: number;
59
+ output_tokens: number;
60
+ cache_read_tokens: number;
61
+ cache_creation_tokens: number;
62
+ estimated_cost_usd: number | null;
63
+ model_used: string | null;
64
+ run_id: string | null;
65
+ created_at: string;
66
+ }
67
+
68
+ const CREATE_TABLE = `
69
+ CREATE TABLE IF NOT EXISTS sessions (
70
+ agent_name TEXT NOT NULL,
71
+ task_id TEXT NOT NULL,
72
+ capability TEXT NOT NULL,
73
+ started_at TEXT NOT NULL,
74
+ completed_at TEXT,
75
+ duration_ms INTEGER NOT NULL DEFAULT 0,
76
+ exit_code INTEGER,
77
+ merge_result TEXT,
78
+ parent_agent TEXT,
79
+ input_tokens INTEGER NOT NULL DEFAULT 0,
80
+ output_tokens INTEGER NOT NULL DEFAULT 0,
81
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
82
+ cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
83
+ estimated_cost_usd REAL,
84
+ model_used TEXT,
85
+ run_id TEXT,
86
+ PRIMARY KEY (agent_name, task_id)
87
+ )`;
88
+
89
+ const CREATE_SNAPSHOTS_TABLE = `
90
+ CREATE TABLE IF NOT EXISTS token_snapshots (
91
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
92
+ agent_name TEXT NOT NULL,
93
+ input_tokens INTEGER NOT NULL DEFAULT 0,
94
+ output_tokens INTEGER NOT NULL DEFAULT 0,
95
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
96
+ cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
97
+ estimated_cost_usd REAL,
98
+ model_used TEXT,
99
+ run_id TEXT,
100
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f','now'))
101
+ )`;
102
+
103
+ const CREATE_SNAPSHOTS_INDEX = `
104
+ CREATE INDEX IF NOT EXISTS idx_snapshots_agent_time
105
+ ON token_snapshots(agent_name, created_at)
106
+ `;
107
+
108
+ /** Token columns added in the token instrumentation migration. */
109
+ const TOKEN_COLUMNS = [
110
+ { name: "input_tokens", ddl: "INTEGER NOT NULL DEFAULT 0" },
111
+ { name: "output_tokens", ddl: "INTEGER NOT NULL DEFAULT 0" },
112
+ { name: "cache_read_tokens", ddl: "INTEGER NOT NULL DEFAULT 0" },
113
+ { name: "cache_creation_tokens", ddl: "INTEGER NOT NULL DEFAULT 0" },
114
+ { name: "estimated_cost_usd", ddl: "REAL" },
115
+ { name: "model_used", ddl: "TEXT" },
116
+ ] as const;
117
+
118
+ /**
119
+ * Migrate an existing sessions table from bead_id to task_id column.
120
+ * Safe to call multiple times — only renames if bead_id exists and task_id does not.
121
+ */
122
+ function migrateBeadIdToTaskId(db: Database): void {
123
+ const rows = db.prepare("PRAGMA table_info(sessions)").all() as Array<{ name: string }>;
124
+ const existingColumns = new Set(rows.map((r) => r.name));
125
+ if (existingColumns.has("bead_id") && !existingColumns.has("task_id")) {
126
+ db.exec("ALTER TABLE sessions RENAME COLUMN bead_id TO task_id");
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Migrate an existing sessions table to include the run_id column.
132
+ * Safe to call multiple times — only adds the column if missing.
133
+ */
134
+ function migrateRunIdColumn(db: Database): void {
135
+ const rows = db.prepare("PRAGMA table_info(sessions)").all() as Array<{ name: string }>;
136
+ const existingColumns = new Set(rows.map((r) => r.name));
137
+ if (!existingColumns.has("run_id")) {
138
+ db.exec("ALTER TABLE sessions ADD COLUMN run_id TEXT");
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Migrate an existing token_snapshots table to include the run_id column.
144
+ * Safe to call multiple times — only adds the column if missing.
145
+ */
146
+ function migrateSnapshotRunIdColumn(db: Database): void {
147
+ const rows = db.prepare("PRAGMA table_info(token_snapshots)").all() as Array<{ name: string }>;
148
+ const existingColumns = new Set(rows.map((r) => r.name));
149
+ if (!existingColumns.has("run_id")) {
150
+ db.exec("ALTER TABLE token_snapshots ADD COLUMN run_id TEXT");
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Migrate an existing sessions table to include token columns.
156
+ * Safe to call multiple times — only adds columns that are missing.
157
+ */
158
+ function migrateTokenColumns(db: Database): void {
159
+ const rows = db.prepare("PRAGMA table_info(sessions)").all() as Array<{ name: string }>;
160
+ const existingColumns = new Set(rows.map((r) => r.name));
161
+
162
+ for (const col of TOKEN_COLUMNS) {
163
+ if (!existingColumns.has(col.name)) {
164
+ db.exec(`ALTER TABLE sessions ADD COLUMN ${col.name} ${col.ddl}`);
165
+ }
166
+ }
167
+ }
168
+
169
+ /** Convert a database row (snake_case) to a SessionMetrics object (camelCase). */
170
+ function rowToMetrics(row: SessionRow): SessionMetrics {
171
+ return {
172
+ agentName: row.agent_name,
173
+ taskId: row.task_id,
174
+ capability: row.capability,
175
+ startedAt: row.started_at,
176
+ completedAt: row.completed_at,
177
+ durationMs: row.duration_ms,
178
+ exitCode: row.exit_code,
179
+ mergeResult: row.merge_result as SessionMetrics["mergeResult"],
180
+ parentAgent: row.parent_agent,
181
+ inputTokens: row.input_tokens,
182
+ outputTokens: row.output_tokens,
183
+ cacheReadTokens: row.cache_read_tokens,
184
+ cacheCreationTokens: row.cache_creation_tokens,
185
+ estimatedCostUsd: row.estimated_cost_usd,
186
+ modelUsed: row.model_used,
187
+ runId: row.run_id,
188
+ };
189
+ }
190
+
191
+ /** Convert a database snapshot row (snake_case) to a TokenSnapshot object (camelCase). */
192
+ function rowToSnapshot(row: SnapshotRow): TokenSnapshot {
193
+ return {
194
+ agentName: row.agent_name,
195
+ inputTokens: row.input_tokens,
196
+ outputTokens: row.output_tokens,
197
+ cacheReadTokens: row.cache_read_tokens,
198
+ cacheCreationTokens: row.cache_creation_tokens,
199
+ estimatedCostUsd: row.estimated_cost_usd,
200
+ modelUsed: row.model_used,
201
+ runId: row.run_id,
202
+ createdAt: row.created_at,
203
+ };
204
+ }
205
+
206
+ /**
207
+ * Create a new MetricsStore backed by a SQLite database at the given path.
208
+ *
209
+ * Initializes the database with WAL mode and a 5-second busy timeout.
210
+ * Creates the sessions table if it does not already exist.
211
+ * Migrates existing tables to add token columns if missing.
212
+ */
213
+ export function createMetricsStore(dbPath: string): MetricsStore {
214
+ const db = new Database(dbPath);
215
+
216
+ // Configure for concurrent access
217
+ db.exec("PRAGMA journal_mode = WAL");
218
+ db.exec("PRAGMA busy_timeout = 5000");
219
+
220
+ // Create schema
221
+ db.exec(CREATE_TABLE);
222
+ db.exec(CREATE_SNAPSHOTS_TABLE);
223
+ db.exec(CREATE_SNAPSHOTS_INDEX);
224
+
225
+ // Migrate: rename bead_id → task_id, add token columns and run_id column to existing tables
226
+ migrateBeadIdToTaskId(db);
227
+ migrateTokenColumns(db);
228
+ migrateRunIdColumn(db);
229
+ migrateSnapshotRunIdColumn(db);
230
+
231
+ // Prepare statements for all queries
232
+ const insertStmt = db.prepare<
233
+ void,
234
+ {
235
+ $agent_name: string;
236
+ $task_id: string;
237
+ $capability: string;
238
+ $started_at: string;
239
+ $completed_at: string | null;
240
+ $duration_ms: number;
241
+ $exit_code: number | null;
242
+ $merge_result: string | null;
243
+ $parent_agent: string | null;
244
+ $input_tokens: number;
245
+ $output_tokens: number;
246
+ $cache_read_tokens: number;
247
+ $cache_creation_tokens: number;
248
+ $estimated_cost_usd: number | null;
249
+ $model_used: string | null;
250
+ $run_id: string | null;
251
+ }
252
+ >(`
253
+ INSERT OR REPLACE INTO sessions
254
+ (agent_name, task_id, capability, started_at, completed_at, duration_ms, exit_code, merge_result, parent_agent, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, estimated_cost_usd, model_used, run_id)
255
+ VALUES
256
+ ($agent_name, $task_id, $capability, $started_at, $completed_at, $duration_ms, $exit_code, $merge_result, $parent_agent, $input_tokens, $output_tokens, $cache_read_tokens, $cache_creation_tokens, $estimated_cost_usd, $model_used, $run_id)
257
+ `);
258
+
259
+ const recentStmt = db.prepare<SessionRow, { $limit: number }>(`
260
+ SELECT * FROM sessions ORDER BY started_at DESC LIMIT $limit
261
+ `);
262
+
263
+ const byAgentStmt = db.prepare<SessionRow, { $agent_name: string }>(`
264
+ SELECT * FROM sessions WHERE agent_name = $agent_name ORDER BY started_at DESC
265
+ `);
266
+
267
+ const byRunStmt = db.prepare<SessionRow, { $run_id: string }>(`
268
+ SELECT * FROM sessions WHERE run_id = $run_id ORDER BY started_at DESC
269
+ `);
270
+
271
+ const byTaskStmt = db.prepare<SessionRow, { $task_id: string }>(`
272
+ SELECT * FROM sessions WHERE task_id = $task_id ORDER BY started_at DESC
273
+ `);
274
+
275
+ const avgDurationAllStmt = db.prepare<{ avg_duration: number | null }, Record<string, never>>(`
276
+ SELECT AVG(duration_ms) AS avg_duration FROM sessions WHERE completed_at IS NOT NULL
277
+ `);
278
+
279
+ const countSessionsStmt = db.prepare<{ cnt: number }, Record<string, never>>(`
280
+ SELECT COUNT(*) as cnt FROM sessions
281
+ `);
282
+
283
+ const avgDurationByCapStmt = db.prepare<
284
+ { avg_duration: number | null },
285
+ { $capability: string }
286
+ >(`
287
+ SELECT AVG(duration_ms) AS avg_duration FROM sessions
288
+ WHERE completed_at IS NOT NULL AND capability = $capability
289
+ `);
290
+
291
+ // Snapshot prepared statements
292
+ const insertSnapshotStmt = db.prepare<
293
+ void,
294
+ {
295
+ $agent_name: string;
296
+ $input_tokens: number;
297
+ $output_tokens: number;
298
+ $cache_read_tokens: number;
299
+ $cache_creation_tokens: number;
300
+ $estimated_cost_usd: number | null;
301
+ $model_used: string | null;
302
+ $run_id: string | null;
303
+ $created_at: string;
304
+ }
305
+ >(`
306
+ INSERT INTO token_snapshots
307
+ (agent_name, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, estimated_cost_usd, model_used, run_id, created_at)
308
+ VALUES
309
+ ($agent_name, $input_tokens, $output_tokens, $cache_read_tokens, $cache_creation_tokens, $estimated_cost_usd, $model_used, $run_id, $created_at)
310
+ `);
311
+
312
+ const latestSnapshotsStmt = db.prepare<SnapshotRow, Record<string, never>>(`
313
+ SELECT s.*
314
+ FROM token_snapshots s
315
+ INNER JOIN (
316
+ SELECT agent_name, MAX(created_at) as max_created_at
317
+ FROM token_snapshots
318
+ GROUP BY agent_name
319
+ ) latest ON s.agent_name = latest.agent_name AND s.created_at = latest.max_created_at
320
+ `);
321
+
322
+ const latestSnapshotsByRunStmt = db.prepare<SnapshotRow, { $run_id: string }>(`
323
+ SELECT s.*
324
+ FROM token_snapshots s
325
+ INNER JOIN (
326
+ SELECT agent_name, MAX(created_at) as max_created_at
327
+ FROM token_snapshots
328
+ WHERE run_id = $run_id
329
+ GROUP BY agent_name
330
+ ) latest ON s.agent_name = latest.agent_name AND s.created_at = latest.max_created_at
331
+ WHERE s.run_id = $run_id
332
+ `);
333
+
334
+ const latestSnapshotTimeStmt = db.prepare<
335
+ { created_at: string } | null,
336
+ { $agent_name: string }
337
+ >(`
338
+ SELECT MAX(created_at) as created_at
339
+ FROM token_snapshots
340
+ WHERE agent_name = $agent_name
341
+ `);
342
+
343
+ return {
344
+ recordSession(metrics: SessionMetrics): void {
345
+ insertStmt.run({
346
+ $agent_name: metrics.agentName,
347
+ $task_id: metrics.taskId,
348
+ $capability: metrics.capability,
349
+ $started_at: metrics.startedAt,
350
+ $completed_at: metrics.completedAt,
351
+ $duration_ms: metrics.durationMs,
352
+ $exit_code: metrics.exitCode,
353
+ $merge_result: metrics.mergeResult,
354
+ $parent_agent: metrics.parentAgent,
355
+ $input_tokens: metrics.inputTokens,
356
+ $output_tokens: metrics.outputTokens,
357
+ $cache_read_tokens: metrics.cacheReadTokens,
358
+ $cache_creation_tokens: metrics.cacheCreationTokens,
359
+ $estimated_cost_usd: metrics.estimatedCostUsd,
360
+ $model_used: metrics.modelUsed,
361
+ $run_id: metrics.runId,
362
+ });
363
+ },
364
+
365
+ getRecentSessions(limit = 20): SessionMetrics[] {
366
+ const rows = recentStmt.all({ $limit: limit });
367
+ return rows.map(rowToMetrics);
368
+ },
369
+
370
+ getSessionsByAgent(agentName: string): SessionMetrics[] {
371
+ const rows = byAgentStmt.all({ $agent_name: agentName });
372
+ return rows.map(rowToMetrics);
373
+ },
374
+
375
+ getSessionsByRun(runId: string): SessionMetrics[] {
376
+ const rows = byRunStmt.all({ $run_id: runId });
377
+ return rows.map(rowToMetrics);
378
+ },
379
+
380
+ getSessionsByTask(taskId: string): SessionMetrics[] {
381
+ const rows = byTaskStmt.all({ $task_id: taskId });
382
+ return rows.map(rowToMetrics);
383
+ },
384
+
385
+ getAverageDuration(capability?: string): number {
386
+ if (capability !== undefined) {
387
+ const row = avgDurationByCapStmt.get({ $capability: capability });
388
+ return row?.avg_duration ?? 0;
389
+ }
390
+ const row = avgDurationAllStmt.get({});
391
+ return row?.avg_duration ?? 0;
392
+ },
393
+
394
+ countSessions(): number {
395
+ const row = countSessionsStmt.get({});
396
+ return row?.cnt ?? 0;
397
+ },
398
+
399
+ purge(options: { all?: boolean; agent?: string }): number {
400
+ if (options.all) {
401
+ const countRow = db
402
+ .prepare<{ cnt: number }, []>("SELECT COUNT(*) as cnt FROM sessions")
403
+ .get();
404
+ const count = countRow?.cnt ?? 0;
405
+ db.prepare("DELETE FROM sessions").run();
406
+ return count;
407
+ }
408
+
409
+ if (options.agent !== undefined) {
410
+ const countRow = db
411
+ .prepare<{ cnt: number }, { $agent: string }>(
412
+ "SELECT COUNT(*) as cnt FROM sessions WHERE agent_name = $agent",
413
+ )
414
+ .get({ $agent: options.agent });
415
+ const count = countRow?.cnt ?? 0;
416
+ db.prepare<void, { $agent: string }>("DELETE FROM sessions WHERE agent_name = $agent").run({
417
+ $agent: options.agent,
418
+ });
419
+ return count;
420
+ }
421
+
422
+ return 0;
423
+ },
424
+
425
+ recordSnapshot(snapshot: TokenSnapshot): void {
426
+ insertSnapshotStmt.run({
427
+ $agent_name: snapshot.agentName,
428
+ $input_tokens: snapshot.inputTokens,
429
+ $output_tokens: snapshot.outputTokens,
430
+ $cache_read_tokens: snapshot.cacheReadTokens,
431
+ $cache_creation_tokens: snapshot.cacheCreationTokens,
432
+ $estimated_cost_usd: snapshot.estimatedCostUsd,
433
+ $model_used: snapshot.modelUsed,
434
+ $run_id: snapshot.runId,
435
+ $created_at: snapshot.createdAt,
436
+ });
437
+ },
438
+
439
+ getLatestSnapshots(runId?: string): TokenSnapshot[] {
440
+ if (runId !== undefined) {
441
+ const rows = latestSnapshotsByRunStmt.all({ $run_id: runId });
442
+ return rows.map(rowToSnapshot);
443
+ }
444
+ const rows = latestSnapshotsStmt.all({});
445
+ return rows.map(rowToSnapshot);
446
+ },
447
+
448
+ getLatestSnapshotTime(agentName: string): string | null {
449
+ const row = latestSnapshotTimeStmt.get({ $agent_name: agentName });
450
+ return row?.created_at ?? null;
451
+ },
452
+
453
+ purgeSnapshots(options: { all?: boolean; agent?: string; olderThanMs?: number }): number {
454
+ if (options.all) {
455
+ const countRow = db
456
+ .prepare<{ cnt: number }, []>("SELECT COUNT(*) as cnt FROM token_snapshots")
457
+ .get();
458
+ const count = countRow?.cnt ?? 0;
459
+ db.prepare("DELETE FROM token_snapshots").run();
460
+ return count;
461
+ }
462
+
463
+ if (options.agent !== undefined) {
464
+ const countRow = db
465
+ .prepare<{ cnt: number }, { $agent: string }>(
466
+ "SELECT COUNT(*) as cnt FROM token_snapshots WHERE agent_name = $agent",
467
+ )
468
+ .get({ $agent: options.agent });
469
+ const count = countRow?.cnt ?? 0;
470
+ db.prepare<void, { $agent: string }>(
471
+ "DELETE FROM token_snapshots WHERE agent_name = $agent",
472
+ ).run({
473
+ $agent: options.agent,
474
+ });
475
+ return count;
476
+ }
477
+
478
+ if (options.olderThanMs !== undefined) {
479
+ const cutoffTime = new Date(Date.now() - options.olderThanMs).toISOString();
480
+ const countRow = db
481
+ .prepare<{ cnt: number }, { $cutoff: string }>(
482
+ "SELECT COUNT(*) as cnt FROM token_snapshots WHERE created_at < $cutoff",
483
+ )
484
+ .get({ $cutoff: cutoffTime });
485
+ const count = countRow?.cnt ?? 0;
486
+ db.prepare<void, { $cutoff: string }>(
487
+ "DELETE FROM token_snapshots WHERE created_at < $cutoff",
488
+ ).run({
489
+ $cutoff: cutoffTime,
490
+ });
491
+ return count;
492
+ }
493
+
494
+ return 0;
495
+ },
496
+
497
+ close(): void {
498
+ db.close();
499
+ },
500
+ };
501
+ }