@edspencer/paddock 0.0.1 → 0.57.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 (296) hide show
  1. package/README.md +69 -0
  2. package/install-notice.mjs +29 -0
  3. package/package.json +58 -5
  4. package/packages/server/dist/adoptable.d.ts +127 -0
  5. package/packages/server/dist/adoptable.js +315 -0
  6. package/packages/server/dist/app.d.ts +67 -0
  7. package/packages/server/dist/app.js +325 -0
  8. package/packages/server/dist/archive.d.ts +42 -0
  9. package/packages/server/dist/archive.js +125 -0
  10. package/packages/server/dist/attachments-config.d.ts +114 -0
  11. package/packages/server/dist/attachments-config.js +165 -0
  12. package/packages/server/dist/attachments-hint.d.ts +53 -0
  13. package/packages/server/dist/attachments-hint.js +140 -0
  14. package/packages/server/dist/attachments.d.ts +33 -0
  15. package/packages/server/dist/attachments.js +117 -0
  16. package/packages/server/dist/auth.d.ts +84 -0
  17. package/packages/server/dist/auth.js +247 -0
  18. package/packages/server/dist/background.d.ts +36 -0
  19. package/packages/server/dist/background.js +137 -0
  20. package/packages/server/dist/bind-safety.d.ts +56 -0
  21. package/packages/server/dist/bind-safety.js +51 -0
  22. package/packages/server/dist/brand.d.ts +44 -0
  23. package/packages/server/dist/brand.js +88 -0
  24. package/packages/server/dist/chat-dto.d.ts +210 -0
  25. package/packages/server/dist/chat-dto.js +308 -0
  26. package/packages/server/dist/cli/import-chats.d.ts +21 -0
  27. package/packages/server/dist/cli/import-chats.js +197 -0
  28. package/packages/server/dist/cli/paddock.d.ts +2 -0
  29. package/packages/server/dist/cli/paddock.js +247 -0
  30. package/packages/server/dist/concurrency.d.ts +57 -0
  31. package/packages/server/dist/concurrency.js +71 -0
  32. package/packages/server/dist/config.d.ts +538 -0
  33. package/packages/server/dist/config.js +625 -0
  34. package/packages/server/dist/curation-config.d.ts +55 -0
  35. package/packages/server/dist/curation-config.js +64 -0
  36. package/packages/server/dist/environment-prompt.d.ts +52 -0
  37. package/packages/server/dist/environment-prompt.js +61 -0
  38. package/packages/server/dist/event-bus.d.ts +87 -0
  39. package/packages/server/dist/event-bus.js +48 -0
  40. package/packages/server/dist/git.d.ts +128 -0
  41. package/packages/server/dist/git.js +446 -0
  42. package/packages/server/dist/github-auth.d.ts +44 -0
  43. package/packages/server/dist/github-auth.js +169 -0
  44. package/packages/server/dist/herdctl-agent-config.d.ts +98 -0
  45. package/packages/server/dist/herdctl-agent-config.js +331 -0
  46. package/packages/server/dist/herdctl-agent-names.d.ts +172 -0
  47. package/packages/server/dist/herdctl-agent-names.js +228 -0
  48. package/packages/server/dist/herdctl-jobs-index.d.ts +65 -0
  49. package/packages/server/dist/herdctl-jobs-index.js +225 -0
  50. package/packages/server/dist/herdctl-jobs.d.ts +98 -0
  51. package/packages/server/dist/herdctl-jobs.js +225 -0
  52. package/packages/server/dist/herdctl.d.ts +623 -0
  53. package/packages/server/dist/herdctl.js +1279 -0
  54. package/packages/server/dist/hook-config.d.ts +217 -0
  55. package/packages/server/dist/hook-config.js +224 -0
  56. package/packages/server/dist/http-bytes.d.ts +24 -0
  57. package/packages/server/dist/http-bytes.js +52 -0
  58. package/packages/server/dist/index.d.ts +1 -0
  59. package/packages/server/dist/index.js +14 -0
  60. package/packages/server/dist/instance-config.d.ts +143 -0
  61. package/packages/server/dist/instance-config.js +385 -0
  62. package/packages/server/dist/localcommand.d.ts +23 -0
  63. package/packages/server/dist/localcommand.js +140 -0
  64. package/packages/server/dist/management-auth.d.ts +99 -0
  65. package/packages/server/dist/management-auth.js +165 -0
  66. package/packages/server/dist/management-config.d.ts +164 -0
  67. package/packages/server/dist/management-config.js +286 -0
  68. package/packages/server/dist/management-mcp-server.d.ts +20 -0
  69. package/packages/server/dist/management-mcp-server.js +95 -0
  70. package/packages/server/dist/management-metadata.d.ts +67 -0
  71. package/packages/server/dist/management-metadata.js +56 -0
  72. package/packages/server/dist/management-ops.d.ts +110 -0
  73. package/packages/server/dist/management-ops.js +449 -0
  74. package/packages/server/dist/management-policy.d.ts +172 -0
  75. package/packages/server/dist/management-policy.js +259 -0
  76. package/packages/server/dist/message-provenance.d.ts +112 -0
  77. package/packages/server/dist/message-provenance.js +260 -0
  78. package/packages/server/dist/models.d.ts +164 -0
  79. package/packages/server/dist/models.js +208 -0
  80. package/packages/server/dist/openapi.d.ts +66 -0
  81. package/packages/server/dist/openapi.js +183 -0
  82. package/packages/server/dist/parent-detach.d.ts +30 -0
  83. package/packages/server/dist/parent-detach.js +109 -0
  84. package/packages/server/dist/preload.d.ts +43 -0
  85. package/packages/server/dist/preload.js +55 -0
  86. package/packages/server/dist/project-files.d.ts +76 -0
  87. package/packages/server/dist/project-files.js +191 -0
  88. package/packages/server/dist/project-mime.d.ts +35 -0
  89. package/packages/server/dist/project-mime.js +55 -0
  90. package/packages/server/dist/project-paths.d.ts +88 -0
  91. package/packages/server/dist/project-paths.js +126 -0
  92. package/packages/server/dist/project-types.d.ts +316 -0
  93. package/packages/server/dist/project-types.js +48 -0
  94. package/packages/server/dist/projects.d.ts +202 -0
  95. package/packages/server/dist/projects.js +894 -0
  96. package/packages/server/dist/queued-message.d.ts +35 -0
  97. package/packages/server/dist/queued-message.js +116 -0
  98. package/packages/server/dist/read-state.d.ts +46 -0
  99. package/packages/server/dist/read-state.js +139 -0
  100. package/packages/server/dist/recovery-config.d.ts +111 -0
  101. package/packages/server/dist/recovery-config.js +105 -0
  102. package/packages/server/dist/recovery.d.ts +262 -0
  103. package/packages/server/dist/recovery.js +490 -0
  104. package/packages/server/dist/resume-drain.d.ts +122 -0
  105. package/packages/server/dist/resume-drain.js +158 -0
  106. package/packages/server/dist/route-context.d.ts +168 -0
  107. package/packages/server/dist/route-context.js +39 -0
  108. package/packages/server/dist/route-errors.d.ts +8 -0
  109. package/packages/server/dist/route-errors.js +13 -0
  110. package/packages/server/dist/routes/chats.d.ts +12 -0
  111. package/packages/server/dist/routes/chats.js +1406 -0
  112. package/packages/server/dist/routes/git.d.ts +13 -0
  113. package/packages/server/dist/routes/git.js +236 -0
  114. package/packages/server/dist/routes/mcp.d.ts +83 -0
  115. package/packages/server/dist/routes/mcp.js +250 -0
  116. package/packages/server/dist/routes/meta.d.ts +15 -0
  117. package/packages/server/dist/routes/meta.js +447 -0
  118. package/packages/server/dist/routes/projects.d.ts +25 -0
  119. package/packages/server/dist/routes/projects.js +736 -0
  120. package/packages/server/dist/routes/triggers.d.ts +21 -0
  121. package/packages/server/dist/routes/triggers.js +324 -0
  122. package/packages/server/dist/routes/workspace-mount.d.ts +48 -0
  123. package/packages/server/dist/routes/workspace-mount.js +32 -0
  124. package/packages/server/dist/routes.d.ts +26 -0
  125. package/packages/server/dist/routes.js +29 -0
  126. package/packages/server/dist/run-provenance.d.ts +109 -0
  127. package/packages/server/dist/run-provenance.js +206 -0
  128. package/packages/server/dist/runs.d.ts +92 -0
  129. package/packages/server/dist/runs.js +72 -0
  130. package/packages/server/dist/schedule-config.d.ts +68 -0
  131. package/packages/server/dist/schedule-config.js +75 -0
  132. package/packages/server/dist/schedule-session.d.ts +34 -0
  133. package/packages/server/dist/schedule-session.js +120 -0
  134. package/packages/server/dist/self-mcp-descriptions.d.ts +20 -0
  135. package/packages/server/dist/self-mcp-descriptions.js +145 -0
  136. package/packages/server/dist/self-mcp-read.d.ts +15 -0
  137. package/packages/server/dist/self-mcp-read.js +97 -0
  138. package/packages/server/dist/self-mcp-triggers.d.ts +30 -0
  139. package/packages/server/dist/self-mcp-triggers.js +144 -0
  140. package/packages/server/dist/self-mcp-types.d.ts +248 -0
  141. package/packages/server/dist/self-mcp-types.js +10 -0
  142. package/packages/server/dist/self-mcp-util.d.ts +73 -0
  143. package/packages/server/dist/self-mcp-util.js +157 -0
  144. package/packages/server/dist/self-mcp-write.d.ts +35 -0
  145. package/packages/server/dist/self-mcp-write.js +215 -0
  146. package/packages/server/dist/self-mcp.d.ts +95 -0
  147. package/packages/server/dist/self-mcp.js +448 -0
  148. package/packages/server/dist/send-file-mcp.d.ts +46 -0
  149. package/packages/server/dist/send-file-mcp.js +281 -0
  150. package/packages/server/dist/session-hub.d.ts +186 -0
  151. package/packages/server/dist/session-hub.js +291 -0
  152. package/packages/server/dist/spawn-capability.d.ts +99 -0
  153. package/packages/server/dist/spawn-capability.js +106 -0
  154. package/packages/server/dist/star.d.ts +26 -0
  155. package/packages/server/dist/star.js +94 -0
  156. package/packages/server/dist/start.d.ts +1 -0
  157. package/packages/server/dist/start.js +21 -0
  158. package/packages/server/dist/subagents.d.ts +337 -0
  159. package/packages/server/dist/subagents.js +626 -0
  160. package/packages/server/dist/sweep.d.ts +122 -0
  161. package/packages/server/dist/sweep.js +696 -0
  162. package/packages/server/dist/tooldetails.d.ts +33 -0
  163. package/packages/server/dist/tooldetails.js +466 -0
  164. package/packages/server/dist/transcribe.d.ts +69 -0
  165. package/packages/server/dist/transcribe.js +119 -0
  166. package/packages/server/dist/transcripts.d.ts +42 -0
  167. package/packages/server/dist/transcripts.js +164 -0
  168. package/packages/server/dist/trigger-config.d.ts +278 -0
  169. package/packages/server/dist/trigger-config.js +417 -0
  170. package/packages/server/dist/trigger-runtime.d.ts +94 -0
  171. package/packages/server/dist/trigger-runtime.js +82 -0
  172. package/packages/server/dist/trigger-session.d.ts +34 -0
  173. package/packages/server/dist/trigger-session.js +122 -0
  174. package/packages/server/dist/triggers.d.ts +83 -0
  175. package/packages/server/dist/triggers.js +124 -0
  176. package/packages/server/dist/trusted-proxy.d.ts +57 -0
  177. package/packages/server/dist/trusted-proxy.js +157 -0
  178. package/packages/server/dist/turn-notice.d.ts +154 -0
  179. package/packages/server/dist/turn-notice.js +271 -0
  180. package/packages/server/dist/unread.d.ts +36 -0
  181. package/packages/server/dist/unread.js +123 -0
  182. package/packages/server/dist/usage.d.ts +66 -0
  183. package/packages/server/dist/usage.js +243 -0
  184. package/packages/server/dist/wake-injection.d.ts +156 -0
  185. package/packages/server/dist/wake-injection.js +136 -0
  186. package/packages/server/dist/whisper-local.d.ts +9 -0
  187. package/packages/server/dist/whisper-local.js +122 -0
  188. package/packages/server/dist/ws-context.d.ts +158 -0
  189. package/packages/server/dist/ws-context.js +1 -0
  190. package/packages/server/dist/ws-protocol.d.ts +423 -0
  191. package/packages/server/dist/ws-protocol.js +251 -0
  192. package/packages/server/dist/ws-self-mcp.d.ts +52 -0
  193. package/packages/server/dist/ws-self-mcp.js +28 -0
  194. package/packages/server/dist/ws-triggers.d.ts +29 -0
  195. package/packages/server/dist/ws-triggers.js +261 -0
  196. package/packages/server/dist/ws-turn.d.ts +86 -0
  197. package/packages/server/dist/ws-turn.js +751 -0
  198. package/packages/server/dist/ws.d.ts +62 -0
  199. package/packages/server/dist/ws.js +1024 -0
  200. package/packages/server/package.json +6 -0
  201. package/packages/web/dist/assets/InstanceConfigPage-Bypd_quL.js +2 -0
  202. package/packages/web/dist/assets/MarkdownRenderer-Co8w9iuX.js +30 -0
  203. package/packages/web/dist/assets/ProjectRedirect-DXrA24Cy.js +2 -0
  204. package/packages/web/dist/assets/ProjectView-B9opP7Sv.js +13 -0
  205. package/packages/web/dist/assets/ProjectsGrid-D2cwb2jY.js +2 -0
  206. package/packages/web/dist/assets/arc-B--FIRB8.js +2 -0
  207. package/packages/web/dist/assets/architectureDiagram-3BPJPVTR-B6dRWZZ6.js +37 -0
  208. package/packages/web/dist/assets/bash-I8pq0VWm.js +2 -0
  209. package/packages/web/dist/assets/blockDiagram-GPEHLZMM-DLxFAvvf.js +133 -0
  210. package/packages/web/dist/assets/c-D2F1sQUL.js +2 -0
  211. package/packages/web/dist/assets/c4Diagram-AAUBKEIU-CNee5ks4.js +11 -0
  212. package/packages/web/dist/assets/channel-Bw0A52CB.js +2 -0
  213. package/packages/web/dist/assets/chunk-2J33WTMH-DObw03Xl.js +2 -0
  214. package/packages/web/dist/assets/chunk-4BX2VUAB-CH2SR6nN.js +2 -0
  215. package/packages/web/dist/assets/chunk-55IACEB6-WlVpegT3.js +2 -0
  216. package/packages/web/dist/assets/chunk-727SXJPM-g8GT9Rgd.js +207 -0
  217. package/packages/web/dist/assets/chunk-AQP2D5EJ-DbN4PsDF.js +232 -0
  218. package/packages/web/dist/assets/chunk-FMBD7UC4-DSN3BjFG.js +16 -0
  219. package/packages/web/dist/assets/chunk-ND2GUHAM-D9R4le0z.js +2 -0
  220. package/packages/web/dist/assets/chunk-QZHKN3VN-D4YgRaKH.js +2 -0
  221. package/packages/web/dist/assets/classDiagram-4FO5ZUOK-itf_kt-q.js +2 -0
  222. package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-itf_kt-q.js +2 -0
  223. package/packages/web/dist/assets/core-CysJ2gLj.js +4 -0
  224. package/packages/web/dist/assets/cose-bilkent-S5V4N54A-D1PB0KNu.js +2 -0
  225. package/packages/web/dist/assets/cpp-Bm3ii6kY.js +2 -0
  226. package/packages/web/dist/assets/csharp-BNpuYZMG.js +2 -0
  227. package/packages/web/dist/assets/css-DazXZka4.js +2 -0
  228. package/packages/web/dist/assets/cytoscape.esm-CUqq0XTU.js +332 -0
  229. package/packages/web/dist/assets/dagre-BM42HDAG-C6Uuq-dq.js +5 -0
  230. package/packages/web/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
  231. package/packages/web/dist/assets/diagram-2AECGRRQ-C9JvQ425.js +44 -0
  232. package/packages/web/dist/assets/diagram-5GNKFQAL-Cu6pdh2d.js +11 -0
  233. package/packages/web/dist/assets/diagram-KO2AKTUF-PmXcUQhY.js +4 -0
  234. package/packages/web/dist/assets/diagram-LMA3HP47-9ww6_9Iq.js +25 -0
  235. package/packages/web/dist/assets/diagram-OG6HWLK6-DWQVjv_n.js +25 -0
  236. package/packages/web/dist/assets/erDiagram-TEJ5UH35-6F7xTuff.js +86 -0
  237. package/packages/web/dist/assets/flowDiagram-I6XJVG4X-SCiGu7m6.js +163 -0
  238. package/packages/web/dist/assets/format-yf7Vf9kR.js +2 -0
  239. package/packages/web/dist/assets/ganttDiagram-6RSMTGT7-BNpPwxkL.js +293 -0
  240. package/packages/web/dist/assets/gitGraphDiagram-PVQCEYII-C0gyAJjW.js +107 -0
  241. package/packages/web/dist/assets/go-BPx_y7j8.js +2 -0
  242. package/packages/web/dist/assets/graph-BlwPajkw.js +2 -0
  243. package/packages/web/dist/assets/index-B-YuzkF6.js +71 -0
  244. package/packages/web/dist/assets/index-CvGiQddO.css +1 -0
  245. package/packages/web/dist/assets/infoDiagram-5YYISTIA-CJ2CFPfy.js +3 -0
  246. package/packages/web/dist/assets/ini-B3BVgEsG.js +2 -0
  247. package/packages/web/dist/assets/init-Gi6I4Gst.js +2 -0
  248. package/packages/web/dist/assets/ishikawaDiagram-YF4QCWOH-Cs0D32Ry.js +71 -0
  249. package/packages/web/dist/assets/java-B5VKqtH2.js +2 -0
  250. package/packages/web/dist/assets/javascript-BKRaQes9.js +2 -0
  251. package/packages/web/dist/assets/journeyDiagram-JHISSGLW-Bc9fuI2i.js +140 -0
  252. package/packages/web/dist/assets/json-DIYVocXf.js +2 -0
  253. package/packages/web/dist/assets/kanban-definition-UN3LZRKU-D_tC7QfC.js +90 -0
  254. package/packages/web/dist/assets/katex-C5jXJg4s.js +258 -0
  255. package/packages/web/dist/assets/kotlin-Cw-eF_bm.js +3 -0
  256. package/packages/web/dist/assets/lastTab-CU1zsF0j.js +2 -0
  257. package/packages/web/dist/assets/layout-Boy8vhSP.js +2 -0
  258. package/packages/web/dist/assets/linear-BzGHkF9K.js +2 -0
  259. package/packages/web/dist/assets/mermaid.core-DlbsYzE-.js +310 -0
  260. package/packages/web/dist/assets/mindmap-definition-RKZ34NQL-Bxeh-ToD.js +97 -0
  261. package/packages/web/dist/assets/ordinal-Cboi1Yqb.js +2 -0
  262. package/packages/web/dist/assets/php-CRdd4dfS.js +3 -0
  263. package/packages/web/dist/assets/pieDiagram-4H26LBE5-CDmilo8S.js +31 -0
  264. package/packages/web/dist/assets/python-kSlZHhJb.js +2 -0
  265. package/packages/web/dist/assets/quadrantDiagram-W4KKPZXB-D14-JyE9.js +8 -0
  266. package/packages/web/dist/assets/queued-LamEU0GH.js +2 -0
  267. package/packages/web/dist/assets/requirementDiagram-4Y6WPE33-CJ1PUGu-.js +85 -0
  268. package/packages/web/dist/assets/ruby-7T4ktxyv.js +2 -0
  269. package/packages/web/dist/assets/rust-HC3MBUhY.js +2 -0
  270. package/packages/web/dist/assets/sankeyDiagram-5OEKKPKP-BkUK73W0.js +41 -0
  271. package/packages/web/dist/assets/scss-DZLA7YXz.js +2 -0
  272. package/packages/web/dist/assets/sequenceDiagram-3UESZ5HK-sbcvsuLp.js +163 -0
  273. package/packages/web/dist/assets/sql-BC88IN-V.js +2 -0
  274. package/packages/web/dist/assets/stateDiagram-AJRCARHV-DV-7BdHK.js +2 -0
  275. package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-BlkGdXKW.js +2 -0
  276. package/packages/web/dist/assets/swift-Dn8kKO_1.js +2 -0
  277. package/packages/web/dist/assets/timeline-definition-PNZ67QCA-C5su21G_.js +121 -0
  278. package/packages/web/dist/assets/typescript-C2FFdlUC.js +2 -0
  279. package/packages/web/dist/assets/vennDiagram-CIIHVFJN-JNkIJAaQ.js +35 -0
  280. package/packages/web/dist/assets/wardley-L42UT6IY-qcz6t2vR.js +174 -0
  281. package/packages/web/dist/assets/wardleyDiagram-YWT4CUSO-CMrOIZ-c.js +79 -0
  282. package/packages/web/dist/assets/xml-BXBhIUeX.js +2 -0
  283. package/packages/web/dist/assets/xychartDiagram-2RQKCTM6-C9q6Oy9u.js +8 -0
  284. package/packages/web/dist/assets/yaml-drsDnIKT.js +2 -0
  285. package/packages/web/dist/favicon.ico +0 -0
  286. package/packages/web/dist/fonts/inter-latin.woff2 +0 -0
  287. package/packages/web/dist/fonts/jetbrains-mono-latin.woff2 +0 -0
  288. package/packages/web/dist/icons/apple-touch-icon.png +0 -0
  289. package/packages/web/dist/icons/favicon-16.png +0 -0
  290. package/packages/web/dist/icons/favicon-32.png +0 -0
  291. package/packages/web/dist/icons/icon-192.png +0 -0
  292. package/packages/web/dist/icons/icon-512.png +0 -0
  293. package/packages/web/dist/icons/maskable-512.png +0 -0
  294. package/packages/web/dist/index.html +56 -0
  295. package/packages/web/dist/manifest.webmanifest +32 -0
  296. package/packages/web/dist/sw.js +139 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Sweeper curation budgets — the per-file token limits the post-turn curator
3
+ * keeps OVERVIEW.md / CHANGELOG.md / CLAUDE.md under (issue #379), with a
4
+ * per-project override layer (issue #384).
5
+ *
6
+ * ── Config discipline (the `recovery`/`driveMode` pattern) ────────────────────
7
+ * Every budget is an instance default (`PADDOCK_CURATION_*` env, YAML instance
8
+ * file beneath it — resolved in config.ts's `loadCurationConfig`) with an
9
+ * optional PER-PROJECT override (`project.yaml` →
10
+ * {@link import("./projects.js").ProjectYaml.curation}). An absent/invalid
11
+ * override inherits the instance default, resolved at sweep time by
12
+ * {@link resolveCurationConfig} — never baked into the DTO (mirrors how
13
+ * `recovery` resolves via `resolveRecoveryConfig`). A malformed override is
14
+ * ignored rather than fatal so a hand-edited `project.yaml` can't wedge a sweep.
15
+ *
16
+ * #379 shipped these instance-only; #384 adds the override so a docs-heavy
17
+ * project can raise its CHANGELOG budget (or a lean project tighten it) without
18
+ * changing the fleet-wide default.
19
+ */
20
+ /**
21
+ * Resolved curation budgets — all fields concrete. Held on {@link
22
+ * import("./config.js").PaddockConfig.curation} (instance defaults) and produced
23
+ * per-sweep by {@link resolveCurationConfig} (project override else instance).
24
+ */
25
+ export interface CurationConfig {
26
+ /** Budget for OVERVIEW.md (regenerated wholesale each sweep). */
27
+ overviewMaxTokens: number;
28
+ /** Budget for CHANGELOG.md (injected into the preload; the biggest lever). */
29
+ changelogMaxTokens: number;
30
+ /** Budget for the CLAUDE.md curated-notes section (auto-loaded every turn). */
31
+ claudeMaxTokens: number;
32
+ }
33
+ /**
34
+ * A per-project curation override as stored in `project.yaml` — every field
35
+ * optional (an absent field inherits the instance default at sweep time).
36
+ */
37
+ export type CurationOverride = Partial<CurationConfig>;
38
+ /** Built-in defaults when neither env nor YAML nor a project override sets a budget. */
39
+ export declare const DEFAULT_CURATION: CurationConfig;
40
+ /**
41
+ * Validate + normalise an untrusted value (from `project.yaml` or a PATCH body)
42
+ * into a {@link CurationOverride}, dropping any field that is missing or invalid,
43
+ * and returning `undefined` when nothing valid remains (so an empty override is
44
+ * never persisted). Each budget must be a positive integer. Defensive by design —
45
+ * a malformed hand-edit degrades to "inherit the instance default", never a
46
+ * sweep crash.
47
+ */
48
+ export declare function sanitizeCurationOverride(value: unknown): CurationOverride | undefined;
49
+ /**
50
+ * Resolve the effective curation budgets for a sweep: a valid per-project
51
+ * override wins field-by-field; every absent field inherits the instance default
52
+ * (which is itself `env > paddock.config.yaml > built-in default`). The override
53
+ * is re-sanitised so a corrupt on-disk value can't leak through.
54
+ */
55
+ export declare function resolveCurationConfig(override: CurationOverride | undefined, instanceDefault: CurationConfig): CurationConfig;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Sweeper curation budgets — the per-file token limits the post-turn curator
3
+ * keeps OVERVIEW.md / CHANGELOG.md / CLAUDE.md under (issue #379), with a
4
+ * per-project override layer (issue #384).
5
+ *
6
+ * ── Config discipline (the `recovery`/`driveMode` pattern) ────────────────────
7
+ * Every budget is an instance default (`PADDOCK_CURATION_*` env, YAML instance
8
+ * file beneath it — resolved in config.ts's `loadCurationConfig`) with an
9
+ * optional PER-PROJECT override (`project.yaml` →
10
+ * {@link import("./projects.js").ProjectYaml.curation}). An absent/invalid
11
+ * override inherits the instance default, resolved at sweep time by
12
+ * {@link resolveCurationConfig} — never baked into the DTO (mirrors how
13
+ * `recovery` resolves via `resolveRecoveryConfig`). A malformed override is
14
+ * ignored rather than fatal so a hand-edited `project.yaml` can't wedge a sweep.
15
+ *
16
+ * #379 shipped these instance-only; #384 adds the override so a docs-heavy
17
+ * project can raise its CHANGELOG budget (or a lean project tighten it) without
18
+ * changing the fleet-wide default.
19
+ */
20
+ /** Built-in defaults when neither env nor YAML nor a project override sets a budget. */
21
+ export const DEFAULT_CURATION = Object.freeze({
22
+ overviewMaxTokens: 2000,
23
+ changelogMaxTokens: 8000,
24
+ claudeMaxTokens: 6000,
25
+ });
26
+ /** True when `n` is a usable budget: a positive, finite integer. */
27
+ function isPositiveInt(n) {
28
+ return typeof n === "number" && Number.isInteger(n) && n > 0 && Number.isFinite(n);
29
+ }
30
+ /**
31
+ * Validate + normalise an untrusted value (from `project.yaml` or a PATCH body)
32
+ * into a {@link CurationOverride}, dropping any field that is missing or invalid,
33
+ * and returning `undefined` when nothing valid remains (so an empty override is
34
+ * never persisted). Each budget must be a positive integer. Defensive by design —
35
+ * a malformed hand-edit degrades to "inherit the instance default", never a
36
+ * sweep crash.
37
+ */
38
+ export function sanitizeCurationOverride(value) {
39
+ if (!value || typeof value !== "object" || Array.isArray(value))
40
+ return undefined;
41
+ const o = value;
42
+ const out = {};
43
+ if (isPositiveInt(o.overviewMaxTokens))
44
+ out.overviewMaxTokens = o.overviewMaxTokens;
45
+ if (isPositiveInt(o.changelogMaxTokens))
46
+ out.changelogMaxTokens = o.changelogMaxTokens;
47
+ if (isPositiveInt(o.claudeMaxTokens))
48
+ out.claudeMaxTokens = o.claudeMaxTokens;
49
+ return Object.keys(out).length > 0 ? out : undefined;
50
+ }
51
+ /**
52
+ * Resolve the effective curation budgets for a sweep: a valid per-project
53
+ * override wins field-by-field; every absent field inherits the instance default
54
+ * (which is itself `env > paddock.config.yaml > built-in default`). The override
55
+ * is re-sanitised so a corrupt on-disk value can't leak through.
56
+ */
57
+ export function resolveCurationConfig(override, instanceDefault) {
58
+ const clean = sanitizeCurationOverride(override) ?? {};
59
+ return {
60
+ overviewMaxTokens: clean.overviewMaxTokens ?? instanceDefault.overviewMaxTokens,
61
+ changelogMaxTokens: clean.changelogMaxTokens ?? instanceDefault.changelogMaxTokens,
62
+ claudeMaxTokens: clean.claudeMaxTokens ?? instanceDefault.claudeMaxTokens,
63
+ };
64
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Paddock's built-in **environment** system prompt (issue #635).
3
+ *
4
+ * Paddock injects no system prompt of its own: on a default instance the keeper
5
+ * runs on Claude Code's stock `claude_code` preset, which is written for a
6
+ * terminal. Nothing anywhere tells the model that its replies are rendered as
7
+ * GitHub-Flavored Markdown in a browser (`MarkdownRenderer.tsx` = react-markdown
8
+ * + remark-gfm), or that `mcp__paddock__send_file` renders a file inline.
9
+ *
10
+ * This text is a small **append** to whatever system prompt the agent already
11
+ * has — it states environmental fact about the deployment, and deliberately does
12
+ * not instruct on role, tone or workflow (that is the `CLAUDE.md` hierarchy's
13
+ * layer, and it stays the user's).
14
+ *
15
+ * ## Why exactly two rules
16
+ *
17
+ * The text is not written from first principles — an earlier draft was, and the
18
+ * audit in issue #635 refuted most of it. It comes from auditing the 100 most
19
+ * recent chats on the dogfood instance (3,944 assistant text blocks, 2.1 MB of
20
+ * user-facing prose). Two findings carried ~90% of the measured value:
21
+ *
22
+ * - **`#123` is dead text.** 4,440 bare refs vs 155 markdown links. Bare URLs
23
+ * autolink under GFM and came out fine (only 4 inert, all backticked), so the
24
+ * defect is specifically issue/PR refs — and it is not inability: single
25
+ * messages link some refs and leave others bare.
26
+ * - **Visual work is described, never shown.** 194 image reads, 138
27
+ * screenshots, and **zero** images ever sent to a user. `send_file` was used
28
+ * 10 times, none of them an image. In one chat this cost a full re-work round
29
+ * — the agent read 17 QC frames, showed none, misread one, and shipped a
30
+ * regression the user then had to screenshot themselves.
31
+ *
32
+ * Things measured and deliberately **cut**, so they don't get re-added: "no ANSI
33
+ * colour" (0 escapes in 2.1 MB), "don't paste long content" (0 fences ≥40 lines;
34
+ * median fence is 3), "use markdown structure" (580 headings, 892 bullets, 101
35
+ * well-formed tables — already excellent), `file.ts:123` guidance (real but
36
+ * low-stakes; 72 of 75 sampled sit in backticks beside the symbol name), and
37
+ * mermaid (0 genuine missed opportunities; an instruction would over-trigger).
38
+ *
39
+ * ## Keep in sync
40
+ *
41
+ * The `send_file` tool description (`send-file-mcp.ts`) is the only other
42
+ * Paddock-aware signal in a normal turn. These two are the same message told
43
+ * twice; change one and re-read the other.
44
+ */
45
+ /**
46
+ * The default environment prompt appended to every keeper turn unless the
47
+ * instance overrides or opts out (see `environmentPrompt` in config.ts).
48
+ *
49
+ * Kept verbatim from issue #635's evidence pass. Resist adding a third rule
50
+ * without measurement — several plausible candidates were cut above.
51
+ */
52
+ export declare const DEFAULT_ENVIRONMENT_PROMPT = "You are running in Paddock, a web app \u2014 your replies render as GitHub-Flavored\nMarkdown in a browser, not as terminal output.\n\n- Show, don't describe. A screenshot, rendering, chart or file the user is meant\n to look at should be sent with `mcp__paddock__send_file`, which renders it\n inline. A filesystem path, or \"it's up on port 5026\", leaves them unable to see\n your evidence \u2014 or to catch you misreading it.\n- Make clickable things clickable. Bare URLs autolink; `#123` does not \u2014 write\n `[#123](https://github.com/owner/repo/issues/123)`. Prefer a full URL to a bare\n port number, and link a chat or project rather than quoting its id.";
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Paddock's built-in **environment** system prompt (issue #635).
3
+ *
4
+ * Paddock injects no system prompt of its own: on a default instance the keeper
5
+ * runs on Claude Code's stock `claude_code` preset, which is written for a
6
+ * terminal. Nothing anywhere tells the model that its replies are rendered as
7
+ * GitHub-Flavored Markdown in a browser (`MarkdownRenderer.tsx` = react-markdown
8
+ * + remark-gfm), or that `mcp__paddock__send_file` renders a file inline.
9
+ *
10
+ * This text is a small **append** to whatever system prompt the agent already
11
+ * has — it states environmental fact about the deployment, and deliberately does
12
+ * not instruct on role, tone or workflow (that is the `CLAUDE.md` hierarchy's
13
+ * layer, and it stays the user's).
14
+ *
15
+ * ## Why exactly two rules
16
+ *
17
+ * The text is not written from first principles — an earlier draft was, and the
18
+ * audit in issue #635 refuted most of it. It comes from auditing the 100 most
19
+ * recent chats on the dogfood instance (3,944 assistant text blocks, 2.1 MB of
20
+ * user-facing prose). Two findings carried ~90% of the measured value:
21
+ *
22
+ * - **`#123` is dead text.** 4,440 bare refs vs 155 markdown links. Bare URLs
23
+ * autolink under GFM and came out fine (only 4 inert, all backticked), so the
24
+ * defect is specifically issue/PR refs — and it is not inability: single
25
+ * messages link some refs and leave others bare.
26
+ * - **Visual work is described, never shown.** 194 image reads, 138
27
+ * screenshots, and **zero** images ever sent to a user. `send_file` was used
28
+ * 10 times, none of them an image. In one chat this cost a full re-work round
29
+ * — the agent read 17 QC frames, showed none, misread one, and shipped a
30
+ * regression the user then had to screenshot themselves.
31
+ *
32
+ * Things measured and deliberately **cut**, so they don't get re-added: "no ANSI
33
+ * colour" (0 escapes in 2.1 MB), "don't paste long content" (0 fences ≥40 lines;
34
+ * median fence is 3), "use markdown structure" (580 headings, 892 bullets, 101
35
+ * well-formed tables — already excellent), `file.ts:123` guidance (real but
36
+ * low-stakes; 72 of 75 sampled sit in backticks beside the symbol name), and
37
+ * mermaid (0 genuine missed opportunities; an instruction would over-trigger).
38
+ *
39
+ * ## Keep in sync
40
+ *
41
+ * The `send_file` tool description (`send-file-mcp.ts`) is the only other
42
+ * Paddock-aware signal in a normal turn. These two are the same message told
43
+ * twice; change one and re-read the other.
44
+ */
45
+ /**
46
+ * The default environment prompt appended to every keeper turn unless the
47
+ * instance overrides or opts out (see `environmentPrompt` in config.ts).
48
+ *
49
+ * Kept verbatim from issue #635's evidence pass. Resist adding a third rule
50
+ * without measurement — several plausible candidates were cut above.
51
+ */
52
+ export const DEFAULT_ENVIRONMENT_PROMPT = `You are running in Paddock, a web app — your replies render as GitHub-Flavored
53
+ Markdown in a browser, not as terminal output.
54
+
55
+ - Show, don't describe. A screenshot, rendering, chart or file the user is meant
56
+ to look at should be sent with \`mcp__paddock__send_file\`, which renders it
57
+ inline. A filesystem path, or "it's up on port 5026", leaves them unable to see
58
+ your evidence — or to catch you misreading it.
59
+ - Make clickable things clickable. Bare URLs autolink; \`#123\` does not — write
60
+ \`[#123](https://github.com/owner/repo/issues/123)\`. Prefer a full URL to a bare
61
+ port number, and link a chat or project rather than quoting its id.`;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * In-process lifecycle event bus (Epic G, ticket G1 — hook foundation).
3
+ *
4
+ * Paddock lifecycle events (a chat archived, later: created/forked/unarchived) fire
5
+ * INSIDE Paddock's own server — unlike schedules (Epic D), there is no herdctl seam
6
+ * to add. This tiny typed emitter is that seam: the place that COMMITS a lifecycle
7
+ * action ({@link ArchiveEvent}, from the archive route / the self-MCP archive tool)
8
+ * `emit`s the event AFTER it commits, and the hook dispatcher (wired in `ws.ts`)
9
+ * `on`s it to fire each of the project's enabled hooks via `startAgentTurn`.
10
+ *
11
+ * Two invariants make this safe as a foundation (GG-2, non-blocking after-commit):
12
+ *
13
+ * 1. **`emit` never throws into its caller and never blocks it.** Listeners run
14
+ * fire-and-forget; a listener that throws (sync or async) is swallowed so the
15
+ * triggering action (the archive) can NEVER fail or hang because of a hook. This
16
+ * is the whole point — a buggy hook must not break the thing that triggered it.
17
+ * 2. **The bus event name IS a hook/trigger `event` value.** So a dispatcher is a
18
+ * trivial `bus.on("onArchive", …)` that matches hooks/triggers with that `event`.
19
+ * v1 wired `onArchive` (hooks + event triggers); T5 adds `afterTurn`, which drives
20
+ * the folded-in sweeper (the default post-turn curator trigger).
21
+ *
22
+ * Kept deliberately minimal (no Node `EventEmitter`: its `error`-event footgun and
23
+ * default max-listener warnings are noise here) and dependency-free, matching the
24
+ * hand-rolled sidecar style of the rest of the server.
25
+ */
26
+ /**
27
+ * Payload for the `onArchive` event: which chat, in which project, was archived.
28
+ * The dispatcher passes `slug` to resolve the project's hooks and `sessionId` into
29
+ * the fired hook's prompt so the hook knows WHAT was archived (e.g. which pm server
30
+ * / clone to tear down).
31
+ */
32
+ export interface ArchiveEvent {
33
+ /** The project slug whose chat was archived. */
34
+ slug: string;
35
+ /** The archived chat's session id. */
36
+ sessionId: string;
37
+ }
38
+ /**
39
+ * Payload for the `afterTurn` event (Epic T / T5): a turn just completed in `slug`.
40
+ * Emitted at every post-turn commit site (a human chat turn, a session-mode wake, and
41
+ * every server-initiated `startAgentTurn`) so the folded-in sweeper — the default
42
+ * `curate-overview` `event`/`afterTurn` trigger — dispatches EXACTLY ONCE per turn.
43
+ * `sessionId` is the chat whose turn completed (`null` when none was resolved), carried
44
+ * for parity with `onArchive`; the curator only needs the `slug` to sweep.
45
+ */
46
+ export interface AfterTurnEvent {
47
+ /** The project slug whose chat turn just completed. */
48
+ slug: string;
49
+ /** The completed turn's session id, or `null` if none was resolved. */
50
+ sessionId: string | null;
51
+ }
52
+ /**
53
+ * The typed event map. The bus event name IS a trigger/hook `event` value, so
54
+ * `on`/`emit` line up with the dispatchers. `onArchive` fires the enabled onArchive
55
+ * hooks + event triggers (Epic G/T); `afterTurn` (T5) drives the post-turn curator
56
+ * (the sweeper). Adding a sibling event is: a new value here + its payload + an `emit`
57
+ * at the commit site.
58
+ */
59
+ export interface PaddockEventMap {
60
+ onArchive: ArchiveEvent;
61
+ afterTurn: AfterTurnEvent;
62
+ }
63
+ /** A lifecycle event name — a key of {@link PaddockEventMap}, matching a trigger/hook event. */
64
+ export type PaddockEventName = keyof PaddockEventMap;
65
+ /** A listener for event `E`; may be async — its result is awaited-and-swallowed. */
66
+ export type EventListener<E extends PaddockEventName> = (payload: PaddockEventMap[E]) => void | Promise<void>;
67
+ /**
68
+ * A minimal typed publish/subscribe hub. One instance is constructed in `app.ts` and
69
+ * shared by the commit sites (archive route / self-MCP archive tool → `emit`) and the
70
+ * hook dispatcher in `ws.ts` (→ `on`).
71
+ */
72
+ export declare class PaddockEventBus {
73
+ private readonly listeners;
74
+ /**
75
+ * Subscribe `listener` to `event`. Returns an unsubscribe function. Multiple
76
+ * listeners per event are supported (v1 registers one — the hook dispatcher).
77
+ */
78
+ on<E extends PaddockEventName>(event: E, listener: EventListener<E>): () => void;
79
+ /**
80
+ * Fire `event` to every subscribed listener, FIRE-AND-FORGET. Returns immediately
81
+ * (never awaits a listener) and never throws into the caller — a listener that
82
+ * throws synchronously or rejects asynchronously is swallowed. So the action that
83
+ * emitted the event (an archive commit) is fully decoupled from hook execution: it
84
+ * neither waits on hooks nor fails because of them (GG-2).
85
+ */
86
+ emit<E extends PaddockEventName>(event: E, payload: PaddockEventMap[E]): void;
87
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * A minimal typed publish/subscribe hub. One instance is constructed in `app.ts` and
3
+ * shared by the commit sites (archive route / self-MCP archive tool → `emit`) and the
4
+ * hook dispatcher in `ws.ts` (→ `on`).
5
+ */
6
+ export class PaddockEventBus {
7
+ listeners = new Map();
8
+ /**
9
+ * Subscribe `listener` to `event`. Returns an unsubscribe function. Multiple
10
+ * listeners per event are supported (v1 registers one — the hook dispatcher).
11
+ */
12
+ on(event, listener) {
13
+ let set = this.listeners.get(event);
14
+ if (!set) {
15
+ set = new Set();
16
+ this.listeners.set(event, set);
17
+ }
18
+ set.add(listener);
19
+ return () => {
20
+ this.listeners.get(event)?.delete(listener);
21
+ };
22
+ }
23
+ /**
24
+ * Fire `event` to every subscribed listener, FIRE-AND-FORGET. Returns immediately
25
+ * (never awaits a listener) and never throws into the caller — a listener that
26
+ * throws synchronously or rejects asynchronously is swallowed. So the action that
27
+ * emitted the event (an archive commit) is fully decoupled from hook execution: it
28
+ * neither waits on hooks nor fails because of them (GG-2).
29
+ */
30
+ emit(event, payload) {
31
+ const set = this.listeners.get(event);
32
+ if (!set || set.size === 0)
33
+ return;
34
+ for (const listener of set) {
35
+ try {
36
+ // A listener may be sync or async. Wrap in Promise.resolve so a synchronous
37
+ // throw and an async rejection are both funneled into the same swallow — the
38
+ // emitter's caller is never affected either way.
39
+ void Promise.resolve()
40
+ .then(() => listener(payload))
41
+ .catch(() => undefined);
42
+ }
43
+ catch {
44
+ /* a synchronous throw before the microtask — swallow too */
45
+ }
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Clone an external git repo into `dest` — the checkout that becomes a
3
+ * repo-backed project's working directory (issue #187). Shells out to the `git`
4
+ * binary via execFile (arg array, no shell → no injection surface), the same
5
+ * discipline as {@link GitService}.
6
+ *
7
+ * A FULL clone (not `--depth 1`): a repo-backed project is where you *do
8
+ * engineering*, so the keeper wants real history — `git log`, blame, bisect, and
9
+ * a non-shallow base for branches/PRs — from the start. Credentials are the
10
+ * ambient git environment's job (a public URL needs none; a private repo needs a
11
+ * box-level credential helper / token — per-project scoped credentials are a
12
+ * documented #187 follow-up). Throws with git's stderr on failure so the caller
13
+ * can surface a clean error and roll back the half-created project.
14
+ */
15
+ export declare function cloneRepo(url: string, dest: string): Promise<void>;
16
+ /** A single changed path within a project (porcelain v1 semantics). */
17
+ export interface GitFileChange {
18
+ /** Path relative to the project directory. */
19
+ path: string;
20
+ /** Two-letter porcelain code, trimmed (e.g. "M", "??", "A", "RM"). */
21
+ status: string;
22
+ /** Whether the change is staged (index differs from HEAD). */
23
+ staged: boolean;
24
+ /** True for an untracked ("??") path. */
25
+ untracked: boolean;
26
+ /** Lines added (undefined for a binary change). Untracked text files count as all-added. */
27
+ added?: number;
28
+ /** Lines removed (undefined for a binary change / an untracked file). */
29
+ removed?: number;
30
+ /** True when the change is binary (no line-level stat). */
31
+ binary?: boolean;
32
+ }
33
+ /** A project's git status (or `repo: false` when the store isn't a repo). */
34
+ export interface GitProjectStatus {
35
+ /** Whether `projectsRoot` is a git working tree at all. */
36
+ repo: boolean;
37
+ /** Current branch (omitted when not a repo / detached). */
38
+ branch?: string;
39
+ /** Changes confined to this project's subtree. */
40
+ files: GitFileChange[];
41
+ /** True when the project subtree has no pending changes. */
42
+ clean: boolean;
43
+ }
44
+ export declare class GitService {
45
+ private readonly projectsRoot;
46
+ private readonly author;
47
+ /** Cached repo-detection result for `projectsRoot` (null = not yet checked). */
48
+ private repoFlag;
49
+ /**
50
+ * @param projectsRoot Absolute path to the projects root the service operates on.
51
+ * @param author Commit identity used when Paddock commits on a project's behalf.
52
+ * Folded into PaddockConfig from `PADDOCK_GIT_AUTHOR_*` (issue #269); defaults
53
+ * preserve the pre-fold values so a bare `new GitService(root)` is unchanged.
54
+ */
55
+ constructor(projectsRoot: string, author?: {
56
+ name: string;
57
+ email: string;
58
+ });
59
+ /** Run git in a directory, returning stdout. Throws on non-zero exit. */
60
+ private git;
61
+ /**
62
+ * Whether the backing store is a git working tree. Cached after the first
63
+ * successful check (repo-ness doesn't change under a running server); a thrown
64
+ * git / missing-binary error resolves to `false`.
65
+ */
66
+ isRepo(): Promise<boolean>;
67
+ /** Force the next `isRepo()` to re-check (e.g. after `git init` at runtime). */
68
+ resetRepoCache(): void;
69
+ /**
70
+ * Status for one project's subtree. Returns `{ repo: false }` when the store
71
+ * isn't a repo (the UI then hides the git affordance). Paths are relative to
72
+ * `projectDir`.
73
+ */
74
+ projectStatus(projectDir: string): Promise<GitProjectStatus>;
75
+ /**
76
+ * `git diff --numstat` for a project's tracked working-tree changes, keyed by
77
+ * PROJECT-relative path (`--relative` makes git emit paths relative to the
78
+ * project dir, matching the porcelain paths). Binary changes report `-\t-` and
79
+ * are flagged `binary`. Returns an empty map on any failure.
80
+ */
81
+ private trackedNumstat;
82
+ /**
83
+ * Unified diff for a project's tracked changes (working tree vs HEAD), or for
84
+ * a single file when `file` is given. Untracked files don't appear in a diff
85
+ * (they're listed by `projectStatus`). Returns "" when not a repo / no diff.
86
+ */
87
+ projectDiff(projectDir: string, file?: string): Promise<string>;
88
+ /** Commit identity (from config, no global git config needed). */
89
+ private identity;
90
+ /** Origin remote info + ahead/behind vs upstream (best-effort). */
91
+ remote(): Promise<{
92
+ repo: boolean;
93
+ configured: boolean;
94
+ url?: string;
95
+ branch?: string;
96
+ ahead?: number;
97
+ behind?: number;
98
+ }>;
99
+ /**
100
+ * Stage + commit a single project's changes (tracked mods, deletions, and
101
+ * untracked files within its subtree). `committed: false` when nothing was
102
+ * pending. Explicit identity so the LXC needs no global git config.
103
+ *
104
+ * When `paths` (project-relative) is given, commit ONLY those files — the
105
+ * pathspec scopes both the staging and the commit, so unselected changes stay
106
+ * uncommitted (issue #258). Paths are validated to stay inside the subtree; an
107
+ * all-invalid selection is an error rather than a silent commit-everything.
108
+ * Omitting `paths` keeps the legacy commit-the-whole-subtree behavior.
109
+ */
110
+ commitProject(projectDir: string, message: string, paths?: string[]): Promise<{
111
+ committed: boolean;
112
+ hash?: string;
113
+ error?: string;
114
+ }>;
115
+ /**
116
+ * Uncommitted-file counts per top-level project subtree, in ONE `git status`
117
+ * over the whole store (cheap — no per-project fan-out) so the projects grid
118
+ * can flag "N uncommitted" without opening each project (issue #258). Keyed by
119
+ * the first path segment (= project slug); only dirty projects appear. `{}`
120
+ * when the store isn't a repo.
121
+ */
122
+ dirtyCounts(): Promise<Record<string, number>>;
123
+ /** Push the current branch to origin (sets upstream on first push). */
124
+ push(): Promise<{
125
+ pushed: boolean;
126
+ error?: string;
127
+ }>;
128
+ }