@edspencer/paddock 0.0.1 → 0.56.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 (294) 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 +512 -0
  33. package/packages/server/dist/config.js +593 -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/event-bus.d.ts +87 -0
  37. package/packages/server/dist/event-bus.js +48 -0
  38. package/packages/server/dist/git.d.ts +128 -0
  39. package/packages/server/dist/git.js +446 -0
  40. package/packages/server/dist/github-auth.d.ts +44 -0
  41. package/packages/server/dist/github-auth.js +169 -0
  42. package/packages/server/dist/herdctl-agent-config.d.ts +98 -0
  43. package/packages/server/dist/herdctl-agent-config.js +331 -0
  44. package/packages/server/dist/herdctl-agent-names.d.ts +172 -0
  45. package/packages/server/dist/herdctl-agent-names.js +228 -0
  46. package/packages/server/dist/herdctl-jobs-index.d.ts +65 -0
  47. package/packages/server/dist/herdctl-jobs-index.js +225 -0
  48. package/packages/server/dist/herdctl-jobs.d.ts +98 -0
  49. package/packages/server/dist/herdctl-jobs.js +225 -0
  50. package/packages/server/dist/herdctl.d.ts +572 -0
  51. package/packages/server/dist/herdctl.js +1218 -0
  52. package/packages/server/dist/hook-config.d.ts +217 -0
  53. package/packages/server/dist/hook-config.js +224 -0
  54. package/packages/server/dist/http-bytes.d.ts +24 -0
  55. package/packages/server/dist/http-bytes.js +52 -0
  56. package/packages/server/dist/index.d.ts +1 -0
  57. package/packages/server/dist/index.js +14 -0
  58. package/packages/server/dist/instance-config.d.ts +135 -0
  59. package/packages/server/dist/instance-config.js +345 -0
  60. package/packages/server/dist/localcommand.d.ts +23 -0
  61. package/packages/server/dist/localcommand.js +140 -0
  62. package/packages/server/dist/management-auth.d.ts +99 -0
  63. package/packages/server/dist/management-auth.js +165 -0
  64. package/packages/server/dist/management-config.d.ts +164 -0
  65. package/packages/server/dist/management-config.js +286 -0
  66. package/packages/server/dist/management-mcp-server.d.ts +20 -0
  67. package/packages/server/dist/management-mcp-server.js +95 -0
  68. package/packages/server/dist/management-metadata.d.ts +67 -0
  69. package/packages/server/dist/management-metadata.js +56 -0
  70. package/packages/server/dist/management-ops.d.ts +110 -0
  71. package/packages/server/dist/management-ops.js +449 -0
  72. package/packages/server/dist/management-policy.d.ts +172 -0
  73. package/packages/server/dist/management-policy.js +259 -0
  74. package/packages/server/dist/message-provenance.d.ts +112 -0
  75. package/packages/server/dist/message-provenance.js +260 -0
  76. package/packages/server/dist/models.d.ts +164 -0
  77. package/packages/server/dist/models.js +208 -0
  78. package/packages/server/dist/openapi.d.ts +66 -0
  79. package/packages/server/dist/openapi.js +183 -0
  80. package/packages/server/dist/parent-detach.d.ts +30 -0
  81. package/packages/server/dist/parent-detach.js +109 -0
  82. package/packages/server/dist/preload.d.ts +43 -0
  83. package/packages/server/dist/preload.js +55 -0
  84. package/packages/server/dist/project-files.d.ts +76 -0
  85. package/packages/server/dist/project-files.js +191 -0
  86. package/packages/server/dist/project-mime.d.ts +35 -0
  87. package/packages/server/dist/project-mime.js +55 -0
  88. package/packages/server/dist/project-paths.d.ts +88 -0
  89. package/packages/server/dist/project-paths.js +126 -0
  90. package/packages/server/dist/project-types.d.ts +316 -0
  91. package/packages/server/dist/project-types.js +48 -0
  92. package/packages/server/dist/projects.d.ts +202 -0
  93. package/packages/server/dist/projects.js +894 -0
  94. package/packages/server/dist/queued-message.d.ts +35 -0
  95. package/packages/server/dist/queued-message.js +116 -0
  96. package/packages/server/dist/read-state.d.ts +46 -0
  97. package/packages/server/dist/read-state.js +139 -0
  98. package/packages/server/dist/recovery-config.d.ts +111 -0
  99. package/packages/server/dist/recovery-config.js +105 -0
  100. package/packages/server/dist/recovery.d.ts +262 -0
  101. package/packages/server/dist/recovery.js +490 -0
  102. package/packages/server/dist/resume-drain.d.ts +122 -0
  103. package/packages/server/dist/resume-drain.js +158 -0
  104. package/packages/server/dist/route-context.d.ts +168 -0
  105. package/packages/server/dist/route-context.js +39 -0
  106. package/packages/server/dist/route-errors.d.ts +8 -0
  107. package/packages/server/dist/route-errors.js +13 -0
  108. package/packages/server/dist/routes/chats.d.ts +12 -0
  109. package/packages/server/dist/routes/chats.js +1406 -0
  110. package/packages/server/dist/routes/git.d.ts +13 -0
  111. package/packages/server/dist/routes/git.js +236 -0
  112. package/packages/server/dist/routes/mcp.d.ts +83 -0
  113. package/packages/server/dist/routes/mcp.js +250 -0
  114. package/packages/server/dist/routes/meta.d.ts +15 -0
  115. package/packages/server/dist/routes/meta.js +447 -0
  116. package/packages/server/dist/routes/projects.d.ts +25 -0
  117. package/packages/server/dist/routes/projects.js +736 -0
  118. package/packages/server/dist/routes/triggers.d.ts +21 -0
  119. package/packages/server/dist/routes/triggers.js +324 -0
  120. package/packages/server/dist/routes/workspace-mount.d.ts +48 -0
  121. package/packages/server/dist/routes/workspace-mount.js +32 -0
  122. package/packages/server/dist/routes.d.ts +26 -0
  123. package/packages/server/dist/routes.js +29 -0
  124. package/packages/server/dist/run-provenance.d.ts +109 -0
  125. package/packages/server/dist/run-provenance.js +206 -0
  126. package/packages/server/dist/runs.d.ts +92 -0
  127. package/packages/server/dist/runs.js +72 -0
  128. package/packages/server/dist/schedule-config.d.ts +68 -0
  129. package/packages/server/dist/schedule-config.js +75 -0
  130. package/packages/server/dist/schedule-session.d.ts +34 -0
  131. package/packages/server/dist/schedule-session.js +120 -0
  132. package/packages/server/dist/self-mcp-descriptions.d.ts +20 -0
  133. package/packages/server/dist/self-mcp-descriptions.js +145 -0
  134. package/packages/server/dist/self-mcp-read.d.ts +15 -0
  135. package/packages/server/dist/self-mcp-read.js +97 -0
  136. package/packages/server/dist/self-mcp-triggers.d.ts +30 -0
  137. package/packages/server/dist/self-mcp-triggers.js +144 -0
  138. package/packages/server/dist/self-mcp-types.d.ts +248 -0
  139. package/packages/server/dist/self-mcp-types.js +10 -0
  140. package/packages/server/dist/self-mcp-util.d.ts +73 -0
  141. package/packages/server/dist/self-mcp-util.js +157 -0
  142. package/packages/server/dist/self-mcp-write.d.ts +35 -0
  143. package/packages/server/dist/self-mcp-write.js +215 -0
  144. package/packages/server/dist/self-mcp.d.ts +95 -0
  145. package/packages/server/dist/self-mcp.js +448 -0
  146. package/packages/server/dist/send-file-mcp.d.ts +46 -0
  147. package/packages/server/dist/send-file-mcp.js +281 -0
  148. package/packages/server/dist/session-hub.d.ts +186 -0
  149. package/packages/server/dist/session-hub.js +291 -0
  150. package/packages/server/dist/spawn-capability.d.ts +99 -0
  151. package/packages/server/dist/spawn-capability.js +106 -0
  152. package/packages/server/dist/star.d.ts +26 -0
  153. package/packages/server/dist/star.js +94 -0
  154. package/packages/server/dist/start.d.ts +1 -0
  155. package/packages/server/dist/start.js +21 -0
  156. package/packages/server/dist/subagents.d.ts +337 -0
  157. package/packages/server/dist/subagents.js +626 -0
  158. package/packages/server/dist/sweep.d.ts +122 -0
  159. package/packages/server/dist/sweep.js +696 -0
  160. package/packages/server/dist/tooldetails.d.ts +33 -0
  161. package/packages/server/dist/tooldetails.js +466 -0
  162. package/packages/server/dist/transcribe.d.ts +69 -0
  163. package/packages/server/dist/transcribe.js +119 -0
  164. package/packages/server/dist/transcripts.d.ts +42 -0
  165. package/packages/server/dist/transcripts.js +164 -0
  166. package/packages/server/dist/trigger-config.d.ts +278 -0
  167. package/packages/server/dist/trigger-config.js +417 -0
  168. package/packages/server/dist/trigger-runtime.d.ts +94 -0
  169. package/packages/server/dist/trigger-runtime.js +82 -0
  170. package/packages/server/dist/trigger-session.d.ts +34 -0
  171. package/packages/server/dist/trigger-session.js +122 -0
  172. package/packages/server/dist/triggers.d.ts +83 -0
  173. package/packages/server/dist/triggers.js +124 -0
  174. package/packages/server/dist/trusted-proxy.d.ts +57 -0
  175. package/packages/server/dist/trusted-proxy.js +157 -0
  176. package/packages/server/dist/turn-notice.d.ts +154 -0
  177. package/packages/server/dist/turn-notice.js +271 -0
  178. package/packages/server/dist/unread.d.ts +36 -0
  179. package/packages/server/dist/unread.js +123 -0
  180. package/packages/server/dist/usage.d.ts +66 -0
  181. package/packages/server/dist/usage.js +243 -0
  182. package/packages/server/dist/wake-injection.d.ts +156 -0
  183. package/packages/server/dist/wake-injection.js +136 -0
  184. package/packages/server/dist/whisper-local.d.ts +9 -0
  185. package/packages/server/dist/whisper-local.js +122 -0
  186. package/packages/server/dist/ws-context.d.ts +158 -0
  187. package/packages/server/dist/ws-context.js +1 -0
  188. package/packages/server/dist/ws-protocol.d.ts +423 -0
  189. package/packages/server/dist/ws-protocol.js +251 -0
  190. package/packages/server/dist/ws-self-mcp.d.ts +52 -0
  191. package/packages/server/dist/ws-self-mcp.js +28 -0
  192. package/packages/server/dist/ws-triggers.d.ts +29 -0
  193. package/packages/server/dist/ws-triggers.js +261 -0
  194. package/packages/server/dist/ws-turn.d.ts +86 -0
  195. package/packages/server/dist/ws-turn.js +751 -0
  196. package/packages/server/dist/ws.d.ts +62 -0
  197. package/packages/server/dist/ws.js +1024 -0
  198. package/packages/server/package.json +6 -0
  199. package/packages/web/dist/assets/InstanceConfigPage-4cQGTj9Y.js +2 -0
  200. package/packages/web/dist/assets/MarkdownRenderer-CJZFLj_8.js +30 -0
  201. package/packages/web/dist/assets/ProjectRedirect-BHwVbbqB.js +2 -0
  202. package/packages/web/dist/assets/ProjectView-BOvuAITg.js +13 -0
  203. package/packages/web/dist/assets/ProjectsGrid-BCojZOGY.js +2 -0
  204. package/packages/web/dist/assets/arc-BVyxvnkl.js +2 -0
  205. package/packages/web/dist/assets/architectureDiagram-3BPJPVTR-Cd9Puqg1.js +37 -0
  206. package/packages/web/dist/assets/bash-I8pq0VWm.js +2 -0
  207. package/packages/web/dist/assets/blockDiagram-GPEHLZMM-B_AQt_Rx.js +133 -0
  208. package/packages/web/dist/assets/c-D2F1sQUL.js +2 -0
  209. package/packages/web/dist/assets/c4Diagram-AAUBKEIU-7e0TQtXB.js +11 -0
  210. package/packages/web/dist/assets/channel-C_1aISNn.js +2 -0
  211. package/packages/web/dist/assets/chunk-2J33WTMH-Dz4WZmUZ.js +2 -0
  212. package/packages/web/dist/assets/chunk-4BX2VUAB-DVWYldo6.js +2 -0
  213. package/packages/web/dist/assets/chunk-55IACEB6-BIMrizQd.js +2 -0
  214. package/packages/web/dist/assets/chunk-727SXJPM-CL2q9fF1.js +207 -0
  215. package/packages/web/dist/assets/chunk-AQP2D5EJ-BSq3HlQ9.js +232 -0
  216. package/packages/web/dist/assets/chunk-FMBD7UC4-Bgt64pLE.js +16 -0
  217. package/packages/web/dist/assets/chunk-ND2GUHAM-C8Pr2A38.js +2 -0
  218. package/packages/web/dist/assets/chunk-QZHKN3VN-BAOqh-MN.js +2 -0
  219. package/packages/web/dist/assets/classDiagram-4FO5ZUOK-B8JE7cHJ.js +2 -0
  220. package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-B8JE7cHJ.js +2 -0
  221. package/packages/web/dist/assets/core-lJeGACQg.js +4 -0
  222. package/packages/web/dist/assets/cose-bilkent-S5V4N54A-BF4tameA.js +2 -0
  223. package/packages/web/dist/assets/cpp-Bm3ii6kY.js +2 -0
  224. package/packages/web/dist/assets/csharp-BNpuYZMG.js +2 -0
  225. package/packages/web/dist/assets/css-DazXZka4.js +2 -0
  226. package/packages/web/dist/assets/cytoscape.esm-CUqq0XTU.js +332 -0
  227. package/packages/web/dist/assets/dagre-BM42HDAG-DdsLM6ql.js +5 -0
  228. package/packages/web/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
  229. package/packages/web/dist/assets/diagram-2AECGRRQ-BQ78S_RM.js +44 -0
  230. package/packages/web/dist/assets/diagram-5GNKFQAL-CAKE6nHE.js +11 -0
  231. package/packages/web/dist/assets/diagram-KO2AKTUF-fEvHRXgr.js +4 -0
  232. package/packages/web/dist/assets/diagram-LMA3HP47-CtWsgeSm.js +25 -0
  233. package/packages/web/dist/assets/diagram-OG6HWLK6-Buog8k7_.js +25 -0
  234. package/packages/web/dist/assets/erDiagram-TEJ5UH35-CWvVVPZS.js +86 -0
  235. package/packages/web/dist/assets/flowDiagram-I6XJVG4X-DsCGXtp4.js +163 -0
  236. package/packages/web/dist/assets/format-DamJQ9LO.js +2 -0
  237. package/packages/web/dist/assets/ganttDiagram-6RSMTGT7-CsdwIvvC.js +293 -0
  238. package/packages/web/dist/assets/gitGraphDiagram-PVQCEYII-hZvrX1sD.js +107 -0
  239. package/packages/web/dist/assets/go-BPx_y7j8.js +2 -0
  240. package/packages/web/dist/assets/graph-BlwPajkw.js +2 -0
  241. package/packages/web/dist/assets/index-CGbZdrm8.js +71 -0
  242. package/packages/web/dist/assets/index-DJDQ210-.css +1 -0
  243. package/packages/web/dist/assets/infoDiagram-5YYISTIA-nE_Vtz_l.js +3 -0
  244. package/packages/web/dist/assets/ini-B3BVgEsG.js +2 -0
  245. package/packages/web/dist/assets/init-Gi6I4Gst.js +2 -0
  246. package/packages/web/dist/assets/ishikawaDiagram-YF4QCWOH-2Nlqs0oH.js +71 -0
  247. package/packages/web/dist/assets/java-B5VKqtH2.js +2 -0
  248. package/packages/web/dist/assets/javascript-BKRaQes9.js +2 -0
  249. package/packages/web/dist/assets/journeyDiagram-JHISSGLW-mjU1fMc4.js +140 -0
  250. package/packages/web/dist/assets/json-DIYVocXf.js +2 -0
  251. package/packages/web/dist/assets/kanban-definition-UN3LZRKU-C_9xNaa3.js +90 -0
  252. package/packages/web/dist/assets/katex-C5jXJg4s.js +258 -0
  253. package/packages/web/dist/assets/kotlin-Cw-eF_bm.js +3 -0
  254. package/packages/web/dist/assets/lastTab-CU1zsF0j.js +2 -0
  255. package/packages/web/dist/assets/layout-Boy8vhSP.js +2 -0
  256. package/packages/web/dist/assets/linear-BxKd91ho.js +2 -0
  257. package/packages/web/dist/assets/mermaid.core-C-CuhD8I.js +310 -0
  258. package/packages/web/dist/assets/mindmap-definition-RKZ34NQL-Bs6yfcZv.js +97 -0
  259. package/packages/web/dist/assets/ordinal-Cboi1Yqb.js +2 -0
  260. package/packages/web/dist/assets/php-CRdd4dfS.js +3 -0
  261. package/packages/web/dist/assets/pieDiagram-4H26LBE5-DNdqX7O5.js +31 -0
  262. package/packages/web/dist/assets/python-kSlZHhJb.js +2 -0
  263. package/packages/web/dist/assets/quadrantDiagram-W4KKPZXB-ZMJl9OdZ.js +8 -0
  264. package/packages/web/dist/assets/queued-LamEU0GH.js +2 -0
  265. package/packages/web/dist/assets/requirementDiagram-4Y6WPE33-RmxMdm2q.js +85 -0
  266. package/packages/web/dist/assets/ruby-7T4ktxyv.js +2 -0
  267. package/packages/web/dist/assets/rust-HC3MBUhY.js +2 -0
  268. package/packages/web/dist/assets/sankeyDiagram-5OEKKPKP-OAAhWcfK.js +41 -0
  269. package/packages/web/dist/assets/scss-DZLA7YXz.js +2 -0
  270. package/packages/web/dist/assets/sequenceDiagram-3UESZ5HK-BWF7rCyN.js +163 -0
  271. package/packages/web/dist/assets/sql-BC88IN-V.js +2 -0
  272. package/packages/web/dist/assets/stateDiagram-AJRCARHV-M3b_zqqo.js +2 -0
  273. package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-CI7saFDa.js +2 -0
  274. package/packages/web/dist/assets/swift-Dn8kKO_1.js +2 -0
  275. package/packages/web/dist/assets/timeline-definition-PNZ67QCA-BpOCh63N.js +121 -0
  276. package/packages/web/dist/assets/typescript-C2FFdlUC.js +2 -0
  277. package/packages/web/dist/assets/vennDiagram-CIIHVFJN-D_iPH45Q.js +35 -0
  278. package/packages/web/dist/assets/wardley-L42UT6IY-DVD9I2Om.js +174 -0
  279. package/packages/web/dist/assets/wardleyDiagram-YWT4CUSO-Bn1IKwjn.js +79 -0
  280. package/packages/web/dist/assets/xml-BXBhIUeX.js +2 -0
  281. package/packages/web/dist/assets/xychartDiagram-2RQKCTM6-DwtI0bkR.js +8 -0
  282. package/packages/web/dist/assets/yaml-drsDnIKT.js +2 -0
  283. package/packages/web/dist/favicon.ico +0 -0
  284. package/packages/web/dist/fonts/inter-latin.woff2 +0 -0
  285. package/packages/web/dist/fonts/jetbrains-mono-latin.woff2 +0 -0
  286. package/packages/web/dist/icons/apple-touch-icon.png +0 -0
  287. package/packages/web/dist/icons/favicon-16.png +0 -0
  288. package/packages/web/dist/icons/favicon-32.png +0 -0
  289. package/packages/web/dist/icons/icon-192.png +0 -0
  290. package/packages/web/dist/icons/icon-512.png +0 -0
  291. package/packages/web/dist/icons/maskable-512.png +0 -0
  292. package/packages/web/dist/index.html +56 -0
  293. package/packages/web/dist/manifest.webmanifest +32 -0
  294. package/packages/web/dist/sw.js +139 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Shared schema description for the optional per-chat `model` override on the spawn
3
+ * tools (create_chat / fork_chat / fork_chat_batch, issue #336). Lists the valid
4
+ * ids from the SAME allow-list the web picker uses so the model stays in sync.
5
+ */
6
+ export declare const MODEL_ARG_DESC: string;
7
+ export declare const LIST_PROJECTS_DESC: string;
8
+ export declare const LIST_CHATS_DESC: string;
9
+ export declare const READ_CHAT_DESC: string;
10
+ export declare const CREATE_CHAT_DESC: string;
11
+ export declare const FORK_CHAT_DESC: string;
12
+ export declare const SEND_MESSAGE_DESC: string;
13
+ export declare const ARCHIVE_CHAT_DESC: string;
14
+ export declare const UNARCHIVE_CHAT_DESC: string;
15
+ export declare const FORK_CHAT_BATCH_DESC: string;
16
+ export declare const CREATE_PROJECT_DESC: string;
17
+ export declare const SET_TRIGGER_DESC: string;
18
+ export declare const REMOVE_TRIGGER_DESC: string;
19
+ export declare const RUN_TRIGGER_DESC: string;
20
+ export declare const LIST_TRIGGERS_DESC: string;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Agent-facing tool descriptions for the Paddock self-management MCP (issue #214).
3
+ *
4
+ * The long `*_DESC` prose the injected tools surface to the keeper agent, plus the
5
+ * shared `MODEL_ARG_DESC` for the per-chat `model` override. Pure strings, split
6
+ * out of `self-mcp.ts` so the logic file isn't dominated by prose. Some interpolate
7
+ * the numeric caps from `self-mcp-util.ts` (which stay the single source of truth).
8
+ */
9
+ import { MODELS } from "./models.js";
10
+ import { FORK_BATCH_MAX, READ_CHAT_DEFAULT_LIMIT, READ_CHAT_MAX_LIMIT, READ_CHAT_MAX_TEXT, } from "./self-mcp-util.js";
11
+ /**
12
+ * Shared schema description for the optional per-chat `model` override on the spawn
13
+ * tools (create_chat / fork_chat / fork_chat_batch, issue #336). Lists the valid
14
+ * ids from the SAME allow-list the web picker uses so the model stays in sync.
15
+ */
16
+ export const MODEL_ARG_DESC = "Optional model for the spawned chat ONLY (not the project default). One of: " +
17
+ `${MODELS.map((m) => m.id).join(", ")}. Omit to inherit the project/box default.`;
18
+ // ── Read tools ──────────────────────────────────────────────────────────────
19
+ export const LIST_PROJECTS_DESC = "List all Paddock projects (across every area). Returns each project's slug, " +
20
+ "display name, area, and status. Use the slug to target `list_chats`/`read_chat`. " +
21
+ "Also returns `root`: the ROOT WORKSPACE — the instance's own top-level " +
22
+ "directory, which has chats of its own but is NOT a project and so is not in " +
23
+ '`projects`/`count`. Its key is the EMPTY STRING: pass `project: ""` to ' +
24
+ "`list_chats`/`read_chat` to target it.";
25
+ export const LIST_CHATS_DESC = "List chats. Pass `project` (a workspace key) to list only that workspace's " +
26
+ "chats, or omit it to list chats across ALL workspaces — every project AND the " +
27
+ 'root workspace. The ROOT workspace\'s key is the EMPTY STRING (`project: ""`), ' +
28
+ "and its chats are reported with `project: \"\"`; pass that same value back to " +
29
+ "`read_chat`. Returns each chat's owning project, " +
30
+ "sessionId, display name, last-updated time, whether a turn is currently " +
31
+ "running, and whether it is archived. ARCHIVED chats are EXCLUDED by default " +
32
+ "(matching the web UI, which files them away in a collapsed section); the " +
33
+ "result's `omittedArchived` count says how many were hidden — pass " +
34
+ "`include_archived: true` to get them back. You MUST do that to obtain the " +
35
+ "session_id of an archived chat: this tool is the only source of session ids, " +
36
+ "so an archived chat is otherwise unreachable by `read_chat`/`unarchive_chat`. " +
37
+ "`name` falls back to an 8-character sessionId prefix when the chat has no " +
38
+ "stored title — read that as \"untitled\", NOT as a meaningful name, and do not " +
39
+ "pass it anywhere a full session_id is wanted. " +
40
+ "Cheap — does not read full transcripts; use `read_chat` for content.";
41
+ export const READ_CHAT_DESC = "Read a trimmed tail of a chat's transcript. Pass `project` (a workspace key: a " +
42
+ 'project slug, or the empty string `""` for the ROOT workspace) and ' +
43
+ "`session_id` (from `list_chats`) — use whichever `project` value `list_chats` " +
44
+ "reported for the chat, verbatim. Returns the last `limit` messages (default " +
45
+ `${READ_CHAT_DEFAULT_LIMIT}, max ${READ_CHAT_MAX_LIMIT}) as {role, text, timestamp}; each ` +
46
+ `message's text is capped at ${READ_CHAT_MAX_TEXT} chars. ` +
47
+ "Use it to see what another chat is about or what was decided there. " +
48
+ "LOSSY — know what you are NOT getting. Tool calls come back as `role: \"tool\"` " +
49
+ "entries with EMPTY text (no tool name, no input, no output) and they still " +
50
+ "count against `limit`, so on a tool-heavy chat most of the reply is blank " +
51
+ "padding; thinking blocks, attachments and sub-agent transcripts are dropped " +
52
+ "outright. So this tool CANNOT show you how a chat went — errors, tool " +
53
+ "failures, stalls, cost. For that, read the transcript yourself: it is JSONL " +
54
+ "(one JSON object per line) at " +
55
+ "`<paddock-data-dir>/projects/<slug>/.chats/<sessionId>.jsonl`, with any " +
56
+ "sub-agents alongside it under `<sessionId>/subagents/agent-*.jsonl`. " +
57
+ "Also note an unknown session_id returns `total: 0` and no error — that means " +
58
+ "NOT FOUND, not \"empty chat\"; re-check the id before concluding anything.";
59
+ // ── Write tools (Phase 2) ───────────────────────────────────────────────────
60
+ export const CREATE_CHAT_DESC = "Start a BRAND-NEW chat and kick it off with `prompt` (a full first turn to the " +
61
+ "new keeper). Defaults to the current project; pass `project` (a slug) to create " +
62
+ "it elsewhere. Set `name` to a concise 3–5 word title for the chat (STRONGLY " +
63
+ "recommended — without it the title falls back to a long auto-summary of the " +
64
+ "first turn). Optionally set `preload_context` to seed the new chat with the " +
65
+ "project's OVERVIEW.md + CHANGELOG.md. Optionally set `model` to run the spawned " +
66
+ "chat on a specific model (e.g. a cheaper/faster one) without changing the " +
67
+ "project default. Returns the new chat's sessionId.";
68
+ export const FORK_CHAT_DESC = "Fork an existing chat into a NEW child chat that inherits its history, then " +
69
+ "optionally kick the child off with `prompt`. Defaults to forking the CURRENT " +
70
+ "chat (the one you're in) — omit `session_id` to fork yourself, or pass a " +
71
+ "`session_id` (from list_chats) to fork another. Defaults to the current project; " +
72
+ "pass `project` to fork one elsewhere. Optionally set `name`, and `model` to run " +
73
+ "the child's kickoff turn on a specific model. Returns the new " +
74
+ "chat's sessionId. For fanning one source out into many children, use " +
75
+ "`fork_chat_batch` instead.";
76
+ export const SEND_MESSAGE_DESC = "Send `prompt` as a NEW turn to an existing chat (identified by `session_id` from " +
77
+ "list_chats). Defaults to the current project; pass `project` to target a chat in " +
78
+ "another project. Use this to hand work or context to a chat that already exists.";
79
+ export const ARCHIVE_CHAT_DESC = "Archive a chat — file it away into the collapsible \"Archived\" section without " +
80
+ "touching its transcript (it stays fully openable/resumable/forkable). " +
81
+ "Defaults to the CURRENT chat (the one you're in) — omit `session_id` to archive " +
82
+ "YOURSELF, or pass a `session_id` (from list_chats) to archive another. Defaults " +
83
+ "to the current project; pass `project` to target a chat elsewhere. This powers " +
84
+ "the self-reporting convention: do the work, then archive yourself on success so " +
85
+ "an un-archived chat is the signal that something needs a human's attention.";
86
+ export const UNARCHIVE_CHAT_DESC = "Unarchive a chat — bring it back out of the \"Archived\" section into the active " +
87
+ "list. Defaults to the CURRENT chat — omit `session_id` to unarchive yourself, or " +
88
+ "pass a `session_id` (from list_chats) to unarchive another. Defaults to the " +
89
+ "current project; pass `project` to target a chat elsewhere.";
90
+ export const FORK_CHAT_BATCH_DESC = "FAN-OUT primitive: fork ONE source chat into many child chats at once — one child " +
91
+ "per entry in `prompts`, each kicked off with its own prompt. Classic use: you " +
92
+ "found N items (e.g. 10 changes) and want to fork this chat N times, one worker per " +
93
+ "item. Pass `prompts` as one directive PER LINE. " +
94
+ "Defaults the source to the CURRENT chat; pass `session_id` to fork a different " +
95
+ "one, and `project` to target another project. With `name_prefix`, each fork is named " +
96
+ `"<name_prefix> <i>" (1-based). With \`model\`, every fork's kickoff turn runs on ` +
97
+ `that model. Up to ${FORK_BATCH_MAX} forks per call; they run ` +
98
+ "concurrently. Returns the source and every new child's sessionId.";
99
+ // ── Project tools (issue #467) ──────────────────────────────────────────────
100
+ export const CREATE_PROJECT_DESC = "Create a BRAND-NEW Paddock project — the workspace a keeper agent lives in — and " +
101
+ "register its keeper. `name` is the only required argument; the kebab-case `slug` " +
102
+ "is derived from it unless you pass one. Two kinds: pass a `repo` git URL for a " +
103
+ "REPO-BACKED project (the repo is cloned into a nested checkout that becomes the " +
104
+ "keeper's working directory — this is what you want for a project that IS a " +
105
+ "codebase), or omit `repo` for a NOTEBOOK project (a notes/docs workspace whose " +
106
+ "own directory is the working directory). Optionally set `summary` (a one-line " +
107
+ "description), `area` (the grouping shown in the sidebar) and `status`. Returns " +
108
+ "the new project's slug, its working directory, and whether it is repo-backed — " +
109
+ "pass that slug to `create_chat` to start working in it. A bad or unreachable " +
110
+ "repo URL fails cleanly and leaves NO half-created project behind, so it is safe " +
111
+ "to retry with a corrected URL.";
112
+ // ── Unified trigger tools (Epic T / T3) ─────────────────────────────────────
113
+ export const SET_TRIGGER_DESC = "Create or update a unified project TRIGGER — a standing rule that runs an agent " +
114
+ "turn when something happens, keyed by `name`. Enable/disable is just this call " +
115
+ "with `enabled` flipped (no separate verb); a NEW trigger defaults disabled. " +
116
+ "WHEN (`type`): \"schedule\" — a cron/interval fires it (give a 5-field `cron`, e.g. " +
117
+ "\"0 9 * * *\" = 9am daily host-local, OR an `interval` like \"30m\"/\"1h\"); \"event\" — a " +
118
+ "lifecycle `event` fires it (v1: \"onArchive\" — after a chat is archived; \"afterTurn\" " +
119
+ "reserved); \"webhook\" — reserved (give a `path`), not yet fired. WHAT (the run): the " +
120
+ "instruction as `prompt` (inline) OR `prompt_file` — a git-tracked `.md` under the " +
121
+ "project's `.paddock/triggers/` dir (e.g. \"daily.md\"), read at fire time; `session` " +
122
+ "\"new\" (a FRESH chat each fire, default) or \"resume\" (accrete the trigger's ONE owned " +
123
+ "session); `tools` = the deny-by-default capability allow-list (one per line or " +
124
+ "comma-separated, e.g. \"Bash, Read\"; omit / empty = a tool-less curator that only " +
125
+ "returns text; a JSON array is also accepted); `model`, `permission_mode` " +
126
+ "(\"default\"/\"acceptEdits\"/\"bypassPermissions\"/\"plan\"), `max_spawn_depth` (0 = may not " +
127
+ "spawn), `max_turns`. Editing preserves omitted fields (so an `enabled`-only call " +
128
+ "just flips the toggle); supplying `prompt` clears an inherited `prompt_file` and " +
129
+ "vice versa; supplying `type` re-specifies the whole trigger. Defaults to the " +
130
+ "current project; pass `project` (a slug) to target another.";
131
+ export const REMOVE_TRIGGER_DESC = "Delete a unified project trigger by `name` (removes it from `project.yaml` and " +
132
+ "disarms its agent/schedule). Safe when absent — returns `removed: false` if no " +
133
+ "such trigger. Defaults to the current project; pass `project` to target another.";
134
+ export const RUN_TRIGGER_DESC = "Fire a trigger NOW, on demand, by `name` — runs it through the SAME path a cron / " +
135
+ "event fire uses, so the resulting chat is a first-class, badged run. Works for any " +
136
+ "trigger type and regardless of its `enabled` flag (a manual run is deliberate). Use " +
137
+ "this to test a trigger you just wrote or to kick one off out of band. Returns the " +
138
+ "started chat's `sessionId`. Defaults to the current project; pass `project` to " +
139
+ "target another.";
140
+ export const LIST_TRIGGERS_DESC = "List a project's unified triggers: each trigger's name, agent, `type` " +
141
+ "(schedule/event/webhook), its WHEN fields (cron/interval, event, path), its run " +
142
+ "(prompt/promptFile, session, tools, model, permissionMode, maxSpawnDepth, " +
143
+ "maxTurns), and `enabled` — plus live runtime state (status, lastRunAt, nextRunAt, " +
144
+ "lastError) for an armed schedule trigger. Read-only. Defaults to the current " +
145
+ "project; pass `project` (a slug) to target another.";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Read-only handler factories for the Paddock self-management MCP (issue #214).
3
+ *
4
+ * The three always-on tools — list_projects / list_chats / read_chat — bound to a
5
+ * per-turn {@link SelfMcpContext}. Each factory returns an async handler that
6
+ * validates/normalizes args, delegates to the context callbacks, and shapes the
7
+ * plain-JSON tool result. read_chat applies the tail/limit + per-message
8
+ * truncation here (the context returns the FULL message list) so that logic stays
9
+ * unit-testable without the fleet.
10
+ */
11
+ import type { McpToolCallResult } from "@herdctl/core";
12
+ import type { SelfMcpContext } from "./self-mcp-types.js";
13
+ export declare function listProjectsHandler(context: SelfMcpContext): () => Promise<McpToolCallResult>;
14
+ export declare function listChatsHandler(context: SelfMcpContext): (args: Record<string, unknown>) => Promise<McpToolCallResult>;
15
+ export declare function readChatHandler(context: SelfMcpContext): (args: Record<string, unknown>) => Promise<McpToolCallResult>;
@@ -0,0 +1,97 @@
1
+ import { isRootKey } from "./project-paths.js";
2
+ import { ok, fail, errText, clampLimit, coerceBoolean, truncateText } from "./self-mcp-util.js";
3
+ /**
4
+ * The workspace key an arg names, or `undefined` when the arg is ABSENT (#560).
5
+ *
6
+ * Load-bearing distinction: the ROOT workspace's key is the empty string, so a
7
+ * truthiness test on this value silently collapses "the root" into "unspecified".
8
+ * Callers must therefore branch on `=== undefined`, never on falsiness. A
9
+ * whitespace-only value trims to `""` and so addresses the root, which is the
10
+ * same normalisation a slug already gets.
11
+ */
12
+ function workspaceArg(value) {
13
+ return typeof value === "string" ? value.trim() : undefined;
14
+ }
15
+ export function listProjectsHandler(context) {
16
+ return async () => {
17
+ try {
18
+ const all = await context.listProjects();
19
+ // #560: the root workspace is NOT a project and is deliberately absent from
20
+ // `ProjectStore.list()` (enumeration walks children only) — but a caller
21
+ // that can't learn it exists can't reach its chats either. So it rides
22
+ // along as its OWN field, exactly the shape `GET /api/projects` settled on
23
+ // (`{ projects, root }`): reachable without being enumerated. `count` stays
24
+ // the project count. `root` is null when the caller's scope excludes it.
25
+ const projects = all.filter((p) => !isRootKey(p.slug));
26
+ const root = all.find((p) => isRootKey(p.slug)) ?? null;
27
+ return ok({ count: projects.length, projects, root });
28
+ }
29
+ catch (error) {
30
+ return fail(`Error listing projects: ${errText(error)}`);
31
+ }
32
+ };
33
+ }
34
+ export function listChatsHandler(context) {
35
+ return async (args) => {
36
+ try {
37
+ const project = workspaceArg(args.project);
38
+ const includeArchived = coerceBoolean(args.include_archived, false);
39
+ // #560: `""` is the ROOT workspace's key, so it is passed THROUGH as an
40
+ // address. The old `project && project.length > 0` collapsed it into "no
41
+ // filter", which answered an explicitly-named target with a different
42
+ // target's chats — silently. Absent (`undefined`) still means every
43
+ // workspace, root included.
44
+ const all = await context.listChats(project);
45
+ // #489: archived chats are hidden by default so the tool agrees with the web
46
+ // UI (which files them into a collapsed section). Filtering HERE rather than
47
+ // in the op keeps the blast radius off `SelfMcpContext`, the policy wrapper
48
+ // and both test fakes. The omitted count is reported rather than dropped
49
+ // silently: `list_chats` is the ONLY source of session ids, so a silent
50
+ // filter would make an archived chat unaddressable — no read_chat, no
51
+ // unarchive_chat — with nothing in the response to hint at why.
52
+ const chats = includeArchived ? all : all.filter((c) => !c.archived);
53
+ return ok({
54
+ count: chats.length,
55
+ omittedArchived: all.length - chats.length,
56
+ project: project ?? null,
57
+ chats,
58
+ });
59
+ }
60
+ catch (error) {
61
+ return fail(`Error listing chats: ${errText(error)}`);
62
+ }
63
+ };
64
+ }
65
+ export function readChatHandler(context) {
66
+ return async (args) => {
67
+ try {
68
+ const project = workspaceArg(args.project);
69
+ const sessionId = typeof args.session_id === "string" ? args.session_id.trim() : "";
70
+ // ABSENT, not empty (#560): `""` is the root workspace's key and a perfectly
71
+ // valid target. The old truthiness check made the root unaddressable AND
72
+ // reported a supplied argument as missing.
73
+ if (project === undefined) {
74
+ return fail('Error: `project` (a workspace key — a project slug, or "" for the root workspace) is required.');
75
+ }
76
+ if (!sessionId)
77
+ return fail("Error: `session_id` is required (get it from list_chats).");
78
+ const limit = clampLimit(args.limit);
79
+ const all = await context.readChat(project, sessionId);
80
+ const tail = all.slice(-limit).map((m) => ({
81
+ role: m.role,
82
+ text: truncateText(m.text),
83
+ timestamp: m.timestamp,
84
+ }));
85
+ return ok({
86
+ project,
87
+ sessionId,
88
+ total: all.length,
89
+ returned: tail.length,
90
+ messages: tail,
91
+ });
92
+ }
93
+ catch (error) {
94
+ return fail(`Error reading chat: ${errText(error)}`);
95
+ }
96
+ };
97
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Unified trigger-management handler factories for the Paddock self-management MCP
3
+ * (Epic T "Unify Triggers" / T3 — issue #214). Collapse the paired schedule
4
+ * (issue #289) + hook (Epic G / G5) verbs onto ONE TriggerService over
5
+ * `project.yaml`'s single `triggers` block. A trigger is WHEN (`type`
6
+ * schedule|event|webhook) + WHAT (the shared run) + `enabled`. These tools are
7
+ * injected ONLY when the project opted into the trigger-management MCP
8
+ * (`triggersMcpEnabled`) — a coarse binary gate. Enable/disable is `set_trigger`
9
+ * with `enabled` flipped (GG-3), not a separate verb.
10
+ */
11
+ import type { McpToolCallResult } from "@herdctl/core";
12
+ import type { SelfMcpWriteContext } from "./self-mcp-types.js";
13
+ /**
14
+ * Assemble the PARTIAL structured `{ trigger?, run?, enabled? }` record the
15
+ * `set_trigger` handler passes to `write.setTrigger` (which merges it over the
16
+ * existing trigger via `mergeTriggerUpdate`, then validates). Only fields the
17
+ * caller actually supplied are set, so an edit that omits a field preserves it
18
+ * (create-or-update patch semantics) — the classic being an `enabled`-only flip.
19
+ * Flat MCP args are the portable transport: the CLI runtime was observed dropping
20
+ * nested/array args, so the discriminant is rebuilt here from scalar args. Chats now
21
+ * run on the SDK runtime, but the flat shape is kept — it also has to survive
22
+ * the CLI runtime that a `driveMode: batch` project (or a trigger) still uses.
23
+ * Returns a string error message instead of a partial when a supplied `type`'s
24
+ * required WHEN field is missing; `null` on none.
25
+ */
26
+ export declare function buildTriggerUpdate(args: Record<string, unknown>): Record<string, unknown> | string;
27
+ export declare function setTriggerHandler(write: SelfMcpWriteContext): (args: Record<string, unknown>) => Promise<McpToolCallResult>;
28
+ export declare function removeTriggerHandler(write: SelfMcpWriteContext): (args: Record<string, unknown>) => Promise<McpToolCallResult>;
29
+ export declare function listTriggersHandler(write: SelfMcpWriteContext): (args: Record<string, unknown>) => Promise<McpToolCallResult>;
30
+ export declare function runTriggerHandler(write: SelfMcpWriteContext): (args: Record<string, unknown>) => Promise<McpToolCallResult>;
@@ -0,0 +1,144 @@
1
+ import { ok, fail, errText, coerceToolList } from "./self-mcp-util.js";
2
+ /**
3
+ * Assemble the PARTIAL structured `{ trigger?, run?, enabled? }` record the
4
+ * `set_trigger` handler passes to `write.setTrigger` (which merges it over the
5
+ * existing trigger via `mergeTriggerUpdate`, then validates). Only fields the
6
+ * caller actually supplied are set, so an edit that omits a field preserves it
7
+ * (create-or-update patch semantics) — the classic being an `enabled`-only flip.
8
+ * Flat MCP args are the portable transport: the CLI runtime was observed dropping
9
+ * nested/array args, so the discriminant is rebuilt here from scalar args. Chats now
10
+ * run on the SDK runtime, but the flat shape is kept — it also has to survive
11
+ * the CLI runtime that a `driveMode: batch` project (or a trigger) still uses.
12
+ * Returns a string error message instead of a partial when a supplied `type`'s
13
+ * required WHEN field is missing; `null` on none.
14
+ */
15
+ export function buildTriggerUpdate(args) {
16
+ const incoming = {};
17
+ // WHEN: only when `type` is supplied does the caller (re)specify the discriminant;
18
+ // an omitted `type` inherits the existing trigger's WHEN unchanged (partial edit).
19
+ const type = typeof args.type === "string" ? args.type.trim() : "";
20
+ if (type) {
21
+ if (type === "schedule") {
22
+ const cron = typeof args.cron === "string" ? args.cron.trim() : "";
23
+ const interval = typeof args.interval === "string" ? args.interval.trim() : "";
24
+ if (!cron && !interval) {
25
+ return 'Error: a "schedule" trigger needs `cron` (a 5-field expression, e.g. "0 9 * * *") or `interval` (e.g. "30m").';
26
+ }
27
+ if (cron && interval) {
28
+ return 'Error: a "schedule" trigger takes exactly ONE of `cron` or `interval`, not both.';
29
+ }
30
+ incoming.trigger = cron ? { type, cron } : { type, interval };
31
+ }
32
+ else if (type === "event") {
33
+ const event = typeof args.event === "string" ? args.event.trim() : "";
34
+ if (!event)
35
+ return 'Error: an "event" trigger needs `event` (the lifecycle trigger, e.g. "onArchive").';
36
+ incoming.trigger = { type, on: event };
37
+ }
38
+ else if (type === "webhook") {
39
+ const p = typeof args.path === "string" ? args.path.trim() : "";
40
+ if (!p)
41
+ return 'Error: a "webhook" trigger needs `path` (the ingress path; reserved — not yet fired).';
42
+ incoming.trigger = { type, path: p };
43
+ }
44
+ else {
45
+ return 'Error: `type` must be "schedule", "event", or "webhook".';
46
+ }
47
+ }
48
+ // WHAT: assemble only the run fields the caller supplied.
49
+ const run = {};
50
+ const prompt = typeof args.prompt === "string" ? args.prompt.trim() : "";
51
+ const promptFile = typeof args.prompt_file === "string" ? args.prompt_file.trim() : "";
52
+ if (prompt)
53
+ run.prompt = prompt;
54
+ if (promptFile)
55
+ run.promptFile = promptFile;
56
+ if (typeof args.session === "string" && (args.session === "new" || args.session === "resume")) {
57
+ run.session = args.session;
58
+ }
59
+ if (typeof args.model === "string" && args.model.trim() !== "")
60
+ run.model = args.model.trim();
61
+ // `tools` is present (even as "") → set it (an empty list = a tool-less curator);
62
+ // absent → leave the existing grant untouched on an edit.
63
+ if (args.tools !== undefined)
64
+ run.tools = coerceToolList(args.tools);
65
+ if (typeof args.max_spawn_depth === "number")
66
+ run.maxSpawnDepth = args.max_spawn_depth;
67
+ if (typeof args.permission_mode === "string" && args.permission_mode.trim() !== "") {
68
+ run.permissionMode = args.permission_mode.trim();
69
+ }
70
+ if (typeof args.max_turns === "number")
71
+ run.maxTurns = args.max_turns;
72
+ if (Object.keys(run).length > 0)
73
+ incoming.run = run;
74
+ if (typeof args.enabled === "boolean")
75
+ incoming.enabled = args.enabled;
76
+ return incoming;
77
+ }
78
+ export function setTriggerHandler(write) {
79
+ return async (args) => {
80
+ try {
81
+ const name = typeof args.name === "string" ? args.name.trim() : "";
82
+ if (!name)
83
+ return fail("Error: `name` is required (the trigger's key).");
84
+ const incoming = buildTriggerUpdate(args);
85
+ if (typeof incoming === "string")
86
+ return fail(incoming);
87
+ const projectArg = typeof args.project === "string" ? args.project.trim() : "";
88
+ const project = projectArg.length > 0 ? projectArg : write.currentProjectSlug;
89
+ const saved = await write.setTrigger(project, name, incoming);
90
+ return ok({ set: true, project, trigger: saved });
91
+ }
92
+ catch (error) {
93
+ return fail(`Error setting trigger: ${errText(error)}`);
94
+ }
95
+ };
96
+ }
97
+ export function removeTriggerHandler(write) {
98
+ return async (args) => {
99
+ try {
100
+ const name = typeof args.name === "string" ? args.name.trim() : "";
101
+ if (!name)
102
+ return fail("Error: `name` is required (the trigger to remove).");
103
+ const projectArg = typeof args.project === "string" ? args.project.trim() : "";
104
+ const project = projectArg.length > 0 ? projectArg : write.currentProjectSlug;
105
+ const removed = await write.removeTrigger(project, name);
106
+ return ok({ removed, project, name });
107
+ }
108
+ catch (error) {
109
+ return fail(`Error removing trigger: ${errText(error)}`);
110
+ }
111
+ };
112
+ }
113
+ export function listTriggersHandler(write) {
114
+ return async (args) => {
115
+ try {
116
+ const projectArg = typeof args.project === "string" ? args.project.trim() : "";
117
+ const project = projectArg.length > 0 ? projectArg : write.currentProjectSlug;
118
+ const triggers = await write.listTriggers(project);
119
+ return ok({ project, count: triggers.length, triggers });
120
+ }
121
+ catch (error) {
122
+ return fail(`Error listing triggers: ${errText(error)}`);
123
+ }
124
+ };
125
+ }
126
+ export function runTriggerHandler(write) {
127
+ return async (args) => {
128
+ try {
129
+ const name = typeof args.name === "string" ? args.name.trim() : "";
130
+ if (!name)
131
+ return fail("Error: `name` is required (the trigger to run).");
132
+ const projectArg = typeof args.project === "string" ? args.project.trim() : "";
133
+ const project = projectArg.length > 0 ? projectArg : write.currentProjectSlug;
134
+ const sessionId = await write.runTrigger(project, name);
135
+ if (!sessionId) {
136
+ return fail(`Error running trigger “${name}”: no such trigger, or it did not start a chat.`);
137
+ }
138
+ return ok({ ran: true, project, name, sessionId });
139
+ }
140
+ catch (error) {
141
+ return fail(`Error running trigger: ${errText(error)}`);
142
+ }
143
+ };
144
+ }