@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,95 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import type { AgentplateConfig } from "../types.ts";
6
+ import { checkServe } from "./serve.ts";
7
+
8
+ describe("checkServe", () => {
9
+ let tempDir: string;
10
+ let mockConfig: AgentplateConfig;
11
+
12
+ beforeEach(() => {
13
+ tempDir = mkdtempSync(join(tmpdir(), "agentplate-serve-doctor-test-"));
14
+ mockConfig = {
15
+ project: { name: "test", root: tempDir, canonicalBranch: "main" },
16
+ agents: {
17
+ manifestPath: "",
18
+ baseDir: "",
19
+ maxConcurrent: 5,
20
+ staggerDelayMs: 100,
21
+ maxDepth: 2,
22
+ maxSessionsPerRun: 0,
23
+ maxAgentsPerLead: 5,
24
+ },
25
+ worktrees: { baseDir: "" },
26
+ taskTracker: { backend: "auto", enabled: true },
27
+ loam: { enabled: true, domains: [], primeFormat: "markdown" },
28
+ merge: { aiResolveEnabled: false, reimagineEnabled: false },
29
+ providers: {
30
+ anthropic: { type: "native" },
31
+ },
32
+ watchdog: {
33
+ tier0Enabled: false,
34
+ tier0IntervalMs: 30000,
35
+ tier1Enabled: false,
36
+ tier2Enabled: false,
37
+ staleThresholdMs: 300000,
38
+ zombieThresholdMs: 600000,
39
+ nudgeIntervalMs: 60000,
40
+ },
41
+ models: {},
42
+ logging: { verbose: false, redactSecrets: true },
43
+ };
44
+ });
45
+
46
+ afterEach(() => {
47
+ rmSync(tempDir, { recursive: true, force: true });
48
+ });
49
+
50
+ test("ui/dist missing — returns warn about missing build", async () => {
51
+ const checks = await checkServe(mockConfig, tempDir);
52
+ const distCheck = checks.find((c) => c.name === "serve ui/dist");
53
+
54
+ expect(distCheck).toBeDefined();
55
+ expect(distCheck?.status).toBe("warn");
56
+ expect(distCheck?.message).toContain("ui/dist not found");
57
+ expect(distCheck?.details?.some((d) => d.includes("ui/dist"))).toBe(true);
58
+ });
59
+
60
+ test("ui/dist exists but index.html missing — returns warn about incomplete build", async () => {
61
+ mkdirSync(join(tempDir, "ui", "dist"), { recursive: true });
62
+ const checks = await checkServe(mockConfig, tempDir);
63
+ const distCheck = checks.find((c) => c.name === "serve ui/dist");
64
+
65
+ expect(distCheck).toBeDefined();
66
+ expect(distCheck?.status).toBe("warn");
67
+ expect(distCheck?.message).toContain("index.html is missing");
68
+ });
69
+
70
+ test("ui/dist with index.html — returns pass", async () => {
71
+ mkdirSync(join(tempDir, "ui", "dist"), { recursive: true });
72
+ writeFileSync(join(tempDir, "ui", "dist", "index.html"), "<html></html>");
73
+ const checks = await checkServe(mockConfig, tempDir);
74
+ const distCheck = checks.find((c) => c.name === "serve ui/dist");
75
+
76
+ expect(distCheck).toBeDefined();
77
+ expect(distCheck?.status).toBe("pass");
78
+ expect(distCheck?.message).toContain("index.html");
79
+ });
80
+
81
+ test("port check included in results", async () => {
82
+ const checks = await checkServe(mockConfig, tempDir);
83
+ const portCheck = checks.find((c) => c.name === "serve port");
84
+
85
+ expect(portCheck).toBeDefined();
86
+ // Server not running — should warn (or pass if something happens to be on the default port)
87
+ expect(portCheck?.status === "warn" || portCheck?.status === "pass").toBe(true);
88
+ });
89
+
90
+ test("returns exactly 2 checks (ui/dist + port)", async () => {
91
+ const checks = await checkServe(mockConfig, tempDir);
92
+ expect(checks).toHaveLength(2);
93
+ expect(checks.map((c) => c.category).every((cat) => cat === "serve")).toBe(true);
94
+ });
95
+ });
@@ -0,0 +1,86 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { DEFAULT_SERVE_PORT } from "../commands/serve.ts";
4
+ import type { DoctorCheck, DoctorCheckFn } from "./types.ts";
5
+
6
+ /**
7
+ * ap serve subsystem health checks.
8
+ * Validates ui/dist build output and port reachability.
9
+ */
10
+ export const checkServe: DoctorCheckFn = async (config, _agentplateDir): Promise<DoctorCheck[]> => {
11
+ const checks: DoctorCheck[] = [];
12
+
13
+ // Check 1: ui/dist directory exists (only relevant if a UI has been built)
14
+ const uiDistPath = join(config.project.root, "ui", "dist");
15
+ const uiDistExists = existsSync(uiDistPath);
16
+ const indexHtmlExists = uiDistExists && existsSync(join(uiDistPath, "index.html"));
17
+
18
+ if (!uiDistExists) {
19
+ checks.push({
20
+ name: "serve ui/dist",
21
+ category: "serve",
22
+ status: "warn",
23
+ message: "ui/dist not found — run the UI build before starting ap serve",
24
+ details: [`Expected: ${uiDistPath}`],
25
+ });
26
+ } else if (!indexHtmlExists) {
27
+ checks.push({
28
+ name: "serve ui/dist",
29
+ category: "serve",
30
+ status: "warn",
31
+ message: "ui/dist exists but index.html is missing — UI build may be incomplete",
32
+ details: [`Expected: ${join(uiDistPath, "index.html")}`],
33
+ });
34
+ } else {
35
+ checks.push({
36
+ name: "serve ui/dist",
37
+ category: "serve",
38
+ status: "pass",
39
+ message: "ui/dist is present with index.html",
40
+ });
41
+ }
42
+
43
+ // Check 2: default port reachability (non-blocking probe)
44
+ const port = DEFAULT_SERVE_PORT;
45
+ const host = "127.0.0.1";
46
+ const reachable = await probePort(host, port);
47
+ if (reachable) {
48
+ checks.push({
49
+ name: "serve port",
50
+ category: "serve",
51
+ status: "pass",
52
+ message: `ap serve is reachable on ${host}:${port}`,
53
+ });
54
+ } else {
55
+ checks.push({
56
+ name: "serve port",
57
+ category: "serve",
58
+ status: "warn",
59
+ message: `ap serve is not running on ${host}:${port}`,
60
+ details: [`Start with: ap serve --port ${port}`],
61
+ });
62
+ }
63
+
64
+ return checks;
65
+ };
66
+
67
+ /**
68
+ * Probe whether a TCP port is open by attempting an HTTP connection.
69
+ * Returns true if the server responds, false on any error.
70
+ */
71
+ async function probePort(host: string, port: number): Promise<boolean> {
72
+ try {
73
+ const controller = new AbortController();
74
+ const timeout = setTimeout(() => controller.abort(), 1000);
75
+ try {
76
+ const res = await fetch(`http://${host}:${port}/healthz`, {
77
+ signal: controller.signal,
78
+ });
79
+ return res.ok || res.status < 500;
80
+ } finally {
81
+ clearTimeout(timeout);
82
+ }
83
+ } catch {
84
+ return false;
85
+ }
86
+ }
@@ -0,0 +1,423 @@
1
+ /**
2
+ * Tests for structure doctor checks.
3
+ *
4
+ * Uses temp directories with real filesystem operations.
5
+ * No mocks needed -- all operations are cheap and local.
6
+ */
7
+
8
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
9
+ import { mkdir, mkdtemp, utimes } from "node:fs/promises";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { cleanupTempDir } from "../test-helpers.ts";
13
+ import type { AgentplateConfig } from "../types.ts";
14
+ import { checkStructure } from "./structure.ts";
15
+
16
+ describe("checkStructure", () => {
17
+ let tempDir: string;
18
+ let agentplateDir: string;
19
+ let mockConfig: AgentplateConfig;
20
+
21
+ beforeEach(async () => {
22
+ tempDir = await mkdtemp(join(tmpdir(), "structure-test-"));
23
+ agentplateDir = join(tempDir, ".agentplate");
24
+
25
+ mockConfig = {
26
+ project: {
27
+ name: "test-project",
28
+ root: tempDir,
29
+ canonicalBranch: "main",
30
+ },
31
+ agents: {
32
+ manifestPath: ".agentplate/agent-manifest.json",
33
+ baseDir: ".agentplate/agent-defs",
34
+ maxConcurrent: 5,
35
+ staggerDelayMs: 1000,
36
+ maxDepth: 2,
37
+ maxSessionsPerRun: 0,
38
+ maxAgentsPerLead: 5,
39
+ },
40
+ worktrees: {
41
+ baseDir: ".agentplate/worktrees",
42
+ },
43
+ taskTracker: {
44
+ backend: "auto",
45
+ enabled: true,
46
+ },
47
+ loam: {
48
+ enabled: true,
49
+ domains: [],
50
+ primeFormat: "markdown",
51
+ },
52
+ merge: {
53
+ aiResolveEnabled: false,
54
+ reimagineEnabled: false,
55
+ },
56
+ providers: {
57
+ anthropic: { type: "native" },
58
+ },
59
+ watchdog: {
60
+ tier0Enabled: true,
61
+ tier0IntervalMs: 30000,
62
+ tier1Enabled: false,
63
+ tier2Enabled: false,
64
+ staleThresholdMs: 300000,
65
+ zombieThresholdMs: 600000,
66
+ nudgeIntervalMs: 60000,
67
+ },
68
+ models: {},
69
+ logging: {
70
+ verbose: false,
71
+ redactSecrets: true,
72
+ },
73
+ };
74
+ });
75
+
76
+ afterEach(async () => {
77
+ await cleanupTempDir(tempDir);
78
+ });
79
+
80
+ test("fails when .agentplate/ directory does not exist", async () => {
81
+ const checks = await checkStructure(mockConfig, agentplateDir);
82
+
83
+ expect(checks.length).toBeGreaterThan(0);
84
+ const dirCheck = checks.find((c) => c.name === ".agentplate/ directory");
85
+ expect(dirCheck).toBeDefined();
86
+ expect(dirCheck?.status).toBe("fail");
87
+ expect(dirCheck?.message).toContain("missing");
88
+ expect(dirCheck?.fixable).toBe(true);
89
+ });
90
+
91
+ test("passes when all required files and directories exist", async () => {
92
+ // Create .agentplate/ and all required structure
93
+ await mkdir(agentplateDir, { recursive: true });
94
+ await mkdir(join(agentplateDir, "agent-defs"), { recursive: true });
95
+ await mkdir(join(agentplateDir, "agents"), { recursive: true });
96
+ await mkdir(join(agentplateDir, "worktrees"), { recursive: true });
97
+ await mkdir(join(agentplateDir, "specs"), { recursive: true });
98
+ await mkdir(join(agentplateDir, "logs"), { recursive: true });
99
+
100
+ await Bun.write(join(agentplateDir, "config.yaml"), "project:\n name: test\n");
101
+ await Bun.write(
102
+ join(agentplateDir, "agent-manifest.json"),
103
+ JSON.stringify({ version: "1.0", agents: {}, capabilityIndex: {} }, null, 2),
104
+ );
105
+ await Bun.write(join(agentplateDir, "hooks.json"), "{}");
106
+ await Bun.write(
107
+ join(agentplateDir, ".gitignore"),
108
+ `# Wildcard+whitelist: ignore everything, whitelist tracked files
109
+ # Auto-healed by ap prime on each session start
110
+ *
111
+ !.gitignore
112
+ !config.yaml
113
+ !agent-manifest.json
114
+ !hooks.json
115
+ !groups.json
116
+ !agent-defs/
117
+ !agent-defs/**
118
+ `,
119
+ );
120
+
121
+ const checks = await checkStructure(mockConfig, agentplateDir);
122
+
123
+ // All checks should pass
124
+ const failedChecks = checks.filter((c) => c.status === "fail");
125
+ expect(failedChecks).toHaveLength(0);
126
+
127
+ const dirCheck = checks.find((c) => c.name === ".agentplate/ directory");
128
+ expect(dirCheck?.status).toBe("pass");
129
+
130
+ const filesCheck = checks.find((c) => c.name === "Required files");
131
+ expect(filesCheck?.status).toBe("pass");
132
+
133
+ const dirsCheck = checks.find((c) => c.name === "Required subdirectories");
134
+ expect(dirsCheck?.status).toBe("pass");
135
+
136
+ const gitignoreCheck = checks.find((c) => c.name === ".gitignore entries");
137
+ expect(gitignoreCheck?.status).toBe("pass");
138
+ });
139
+
140
+ test("reports missing required files", async () => {
141
+ await mkdir(agentplateDir, { recursive: true });
142
+ await Bun.write(join(agentplateDir, "config.yaml"), "project:\n name: test\n");
143
+ // Missing: agent-manifest.json, hooks.json, .gitignore
144
+
145
+ const checks = await checkStructure(mockConfig, agentplateDir);
146
+
147
+ const filesCheck = checks.find((c) => c.name === "Required files");
148
+ expect(filesCheck).toBeDefined();
149
+ expect(filesCheck?.status).toBe("fail");
150
+ expect(filesCheck?.details).toContain("agent-manifest.json");
151
+ expect(filesCheck?.details).toContain("hooks.json");
152
+ expect(filesCheck?.details).toContain(".gitignore");
153
+ expect(filesCheck?.fixable).toBe(true);
154
+ });
155
+
156
+ test("reports missing required subdirectories", async () => {
157
+ await mkdir(agentplateDir, { recursive: true });
158
+ await mkdir(join(agentplateDir, "agent-defs"), { recursive: true });
159
+ // Missing: agents/, worktrees/, specs/, logs/
160
+
161
+ const checks = await checkStructure(mockConfig, agentplateDir);
162
+
163
+ const dirsCheck = checks.find((c) => c.name === "Required subdirectories");
164
+ expect(dirsCheck).toBeDefined();
165
+ expect(dirsCheck?.status).toBe("fail");
166
+ expect(dirsCheck?.details).toContain("agents/");
167
+ expect(dirsCheck?.details).toContain("worktrees/");
168
+ expect(dirsCheck?.details).toContain("specs/");
169
+ expect(dirsCheck?.details).toContain("logs/");
170
+ expect(dirsCheck?.fixable).toBe(true);
171
+ });
172
+
173
+ test("warns when .gitignore is missing entries", async () => {
174
+ await mkdir(agentplateDir, { recursive: true });
175
+ await Bun.write(
176
+ join(agentplateDir, ".gitignore"),
177
+ `# Incomplete gitignore
178
+ *
179
+ !.gitignore
180
+ !config.yaml
181
+ `,
182
+ );
183
+
184
+ const checks = await checkStructure(mockConfig, agentplateDir);
185
+
186
+ const gitignoreCheck = checks.find((c) => c.name === ".gitignore entries");
187
+ expect(gitignoreCheck).toBeDefined();
188
+ expect(gitignoreCheck?.status).toBe("warn");
189
+ expect(gitignoreCheck?.details).toBeDefined();
190
+ expect(gitignoreCheck?.details?.length).toBeGreaterThan(0);
191
+ expect(gitignoreCheck?.fixable).toBe(true);
192
+ });
193
+
194
+ test("validates agent-defs files against manifest", async () => {
195
+ await mkdir(agentplateDir, { recursive: true });
196
+ await mkdir(join(agentplateDir, "agent-defs"), { recursive: true });
197
+
198
+ const manifest = {
199
+ version: "1.0",
200
+ agents: {
201
+ scout: { file: "scout.md", model: "haiku", tools: [], capabilities: [], canSpawn: false },
202
+ builder: {
203
+ file: "builder.md",
204
+ model: "sonnet",
205
+ tools: [],
206
+ capabilities: [],
207
+ canSpawn: false,
208
+ },
209
+ },
210
+ capabilityIndex: {},
211
+ };
212
+
213
+ await Bun.write(join(agentplateDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
214
+ await Bun.write(join(agentplateDir, "agent-defs", "scout.md"), "# Scout");
215
+ // Missing: builder.md
216
+
217
+ const checks = await checkStructure(mockConfig, agentplateDir);
218
+
219
+ const agentDefsCheck = checks.find((c) => c.name === "Agent definition files");
220
+ expect(agentDefsCheck).toBeDefined();
221
+ expect(agentDefsCheck?.status).toBe("fail");
222
+ expect(agentDefsCheck?.details).toContain("builder.md");
223
+ expect(agentDefsCheck?.fixable).toBe(true);
224
+ });
225
+
226
+ test("passes when all agent-defs files are present", async () => {
227
+ await mkdir(agentplateDir, { recursive: true });
228
+ await mkdir(join(agentplateDir, "agent-defs"), { recursive: true });
229
+
230
+ const manifest = {
231
+ version: "1.0",
232
+ agents: {
233
+ scout: { file: "scout.md", model: "haiku", tools: [], capabilities: [], canSpawn: false },
234
+ builder: {
235
+ file: "builder.md",
236
+ model: "sonnet",
237
+ tools: [],
238
+ capabilities: [],
239
+ canSpawn: false,
240
+ },
241
+ },
242
+ capabilityIndex: {},
243
+ };
244
+
245
+ await Bun.write(join(agentplateDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
246
+ await Bun.write(join(agentplateDir, "agent-defs", "scout.md"), "# Scout");
247
+ await Bun.write(join(agentplateDir, "agent-defs", "builder.md"), "# Builder");
248
+
249
+ const checks = await checkStructure(mockConfig, agentplateDir);
250
+
251
+ const agentDefsCheck = checks.find((c) => c.name === "Agent definition files");
252
+ expect(agentDefsCheck).toBeDefined();
253
+ expect(agentDefsCheck?.status).toBe("pass");
254
+ });
255
+
256
+ test("fails gracefully when manifest is malformed", async () => {
257
+ await mkdir(agentplateDir, { recursive: true });
258
+ await Bun.write(join(agentplateDir, "agent-manifest.json"), "invalid json{");
259
+
260
+ const checks = await checkStructure(mockConfig, agentplateDir);
261
+
262
+ const agentDefsCheck = checks.find((c) => c.name === "Agent definition files");
263
+ expect(agentDefsCheck).toBeDefined();
264
+ expect(agentDefsCheck?.status).toBe("fail");
265
+ expect(agentDefsCheck?.message).toContain("Cannot validate");
266
+ expect(agentDefsCheck?.fixable).toBe(false);
267
+ });
268
+
269
+ test("detects leftover temp files", async () => {
270
+ await mkdir(agentplateDir, { recursive: true });
271
+ await Bun.write(join(agentplateDir, "config.yaml.tmp"), "temp");
272
+ await Bun.write(join(agentplateDir, "old-file.bak"), "backup");
273
+
274
+ const checks = await checkStructure(mockConfig, agentplateDir);
275
+
276
+ const tempFilesCheck = checks.find((c) => c.name === "Leftover temp files");
277
+ expect(tempFilesCheck).toBeDefined();
278
+ expect(tempFilesCheck?.status).toBe("warn");
279
+ expect(tempFilesCheck?.details).toContain("config.yaml.tmp");
280
+ expect(tempFilesCheck?.details).toContain("old-file.bak");
281
+ expect(tempFilesCheck?.fixable).toBe(true);
282
+ });
283
+
284
+ test("passes when no temp files exist", async () => {
285
+ await mkdir(agentplateDir, { recursive: true });
286
+ await Bun.write(join(agentplateDir, "config.yaml"), "project:\n name: test\n");
287
+
288
+ const checks = await checkStructure(mockConfig, agentplateDir);
289
+
290
+ const tempFilesCheck = checks.find((c) => c.name === "Leftover temp files");
291
+ expect(tempFilesCheck).toBeDefined();
292
+ expect(tempFilesCheck?.status).toBe("pass");
293
+ });
294
+
295
+ test("fix() creates missing subdirectories", async () => {
296
+ await mkdir(agentplateDir, { recursive: true });
297
+
298
+ const checks = await checkStructure(mockConfig, agentplateDir);
299
+
300
+ const dirsCheck = checks.find((c) => c.name === "Required subdirectories");
301
+ expect(dirsCheck?.status).toBe("fail");
302
+ expect(dirsCheck?.fix).toBeDefined();
303
+
304
+ const actions = await dirsCheck?.fix?.();
305
+ expect(actions).toBeDefined();
306
+ expect(actions?.length).toBeGreaterThan(0);
307
+ expect(actions?.some((a) => a.includes("agents/"))).toBe(true);
308
+ expect(actions?.some((a) => a.includes("worktrees/"))).toBe(true);
309
+ expect(actions?.some((a) => a.includes("specs/"))).toBe(true);
310
+ expect(actions?.some((a) => a.includes("logs/"))).toBe(true);
311
+
312
+ // Verify directories were actually created
313
+ const { stat: fsStat } = await import("node:fs/promises");
314
+ const agentsStat = await fsStat(join(agentplateDir, "agents"));
315
+ expect(agentsStat.isDirectory()).toBe(true);
316
+ const worktreesStat = await fsStat(join(agentplateDir, "worktrees"));
317
+ expect(worktreesStat.isDirectory()).toBe(true);
318
+ });
319
+
320
+ test("fix() appends missing .gitignore entries", async () => {
321
+ await mkdir(agentplateDir, { recursive: true });
322
+ await Bun.write(join(agentplateDir, ".gitignore"), `*\n!.gitignore\n!config.yaml\n`);
323
+
324
+ const checks = await checkStructure(mockConfig, agentplateDir);
325
+
326
+ const gitignoreCheck = checks.find((c) => c.name === ".gitignore entries");
327
+ expect(gitignoreCheck?.status).toBe("warn");
328
+ expect(gitignoreCheck?.fix).toBeDefined();
329
+
330
+ const actions = await gitignoreCheck?.fix?.();
331
+ expect(actions).toBeDefined();
332
+ expect(actions?.length).toBeGreaterThan(0);
333
+ expect(actions?.some((a) => a.includes("!agent-manifest.json"))).toBe(true);
334
+
335
+ // Verify entries were appended
336
+ const content = await Bun.file(join(agentplateDir, ".gitignore")).text();
337
+ expect(content).toContain("!agent-manifest.json");
338
+ expect(content).toContain("!hooks.json");
339
+ });
340
+
341
+ test("fix() removes leftover temp files", async () => {
342
+ await mkdir(agentplateDir, { recursive: true });
343
+ const tmpFile = join(agentplateDir, "config.yaml.tmp");
344
+ const bakFile = join(agentplateDir, "old.bak");
345
+ await Bun.write(tmpFile, "temp content");
346
+ await Bun.write(bakFile, "backup content");
347
+
348
+ const checks = await checkStructure(mockConfig, agentplateDir);
349
+
350
+ const tempCheck = checks.find((c) => c.name === "Leftover temp files");
351
+ expect(tempCheck?.status).toBe("warn");
352
+ expect(tempCheck?.fix).toBeDefined();
353
+
354
+ const actions = await tempCheck?.fix?.();
355
+ expect(actions).toBeDefined();
356
+ expect(actions?.some((a) => a.includes("config.yaml.tmp"))).toBe(true);
357
+ expect(actions?.some((a) => a.includes("old.bak"))).toBe(true);
358
+
359
+ // Verify files were deleted
360
+ expect(await Bun.file(tmpFile).exists()).toBe(false);
361
+ expect(await Bun.file(bakFile).exists()).toBe(false);
362
+ });
363
+
364
+ test("passes when no stale lock files exist", async () => {
365
+ await mkdir(agentplateDir, { recursive: true });
366
+
367
+ const checks = await checkStructure(mockConfig, agentplateDir);
368
+
369
+ const lockCheck = checks.find((c) => c.name === "Stale lock files");
370
+ expect(lockCheck).toBeDefined();
371
+ expect(lockCheck?.status).toBe("pass");
372
+ expect(lockCheck?.fix).toBeUndefined();
373
+ });
374
+
375
+ test("warns when stale lock files exist", async () => {
376
+ await mkdir(agentplateDir, { recursive: true });
377
+ const lockFile = join(agentplateDir, "mail.lock");
378
+ await Bun.write(lockFile, "locked");
379
+ // Set mtime to 10 minutes ago
380
+ const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000);
381
+ await utimes(lockFile, tenMinutesAgo, tenMinutesAgo);
382
+
383
+ const checks = await checkStructure(mockConfig, agentplateDir);
384
+
385
+ const lockCheck = checks.find((c) => c.name === "Stale lock files");
386
+ expect(lockCheck).toBeDefined();
387
+ expect(lockCheck?.status).toBe("warn");
388
+ expect(lockCheck?.details).toContain("mail.lock");
389
+ expect(lockCheck?.fixable).toBe(true);
390
+ expect(lockCheck?.fix).toBeDefined();
391
+ });
392
+
393
+ test("does not warn about fresh lock files", async () => {
394
+ await mkdir(agentplateDir, { recursive: true });
395
+ // Write a fresh lock file (just created = now)
396
+ await Bun.write(join(agentplateDir, "sessions.lock"), "locked");
397
+
398
+ const checks = await checkStructure(mockConfig, agentplateDir);
399
+
400
+ const lockCheck = checks.find((c) => c.name === "Stale lock files");
401
+ expect(lockCheck?.status).toBe("pass");
402
+ });
403
+
404
+ test("fix() removes stale lock files", async () => {
405
+ await mkdir(agentplateDir, { recursive: true });
406
+ const lockFile = join(agentplateDir, "stale.lock");
407
+ await Bun.write(lockFile, "locked");
408
+ // Set mtime to 10 minutes ago
409
+ const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000);
410
+ await utimes(lockFile, tenMinutesAgo, tenMinutesAgo);
411
+
412
+ const checks = await checkStructure(mockConfig, agentplateDir);
413
+
414
+ const lockCheck = checks.find((c) => c.name === "Stale lock files");
415
+ expect(lockCheck?.fix).toBeDefined();
416
+
417
+ const actions = await lockCheck?.fix?.();
418
+ expect(actions?.some((a) => a.includes("stale.lock"))).toBe(true);
419
+
420
+ // Verify the lock file was removed
421
+ expect(await Bun.file(lockFile).exists()).toBe(false);
422
+ });
423
+ });