clawmini 0.0.7 → 0.0.9

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 (367) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +14 -0
  3. package/.github/workflows/release.yml +49 -0
  4. package/CHANGELOG.md +36 -0
  5. package/README.md +5 -4
  6. package/dist/adapter-discord/index.d.mts.map +1 -1
  7. package/dist/adapter-discord/index.mjs +465 -282
  8. package/dist/adapter-discord/index.mjs.map +1 -1
  9. package/dist/adapter-google-chat/index.mjs +367 -243
  10. package/dist/adapter-google-chat/index.mjs.map +1 -1
  11. package/dist/cli/index.mjs +684 -24
  12. package/dist/cli/index.mjs.map +1 -1
  13. package/dist/cli/lite.mjs +43 -13
  14. package/dist/cli/lite.mjs.map +1 -1
  15. package/dist/cli/{propose-policy.mjs → manage-policies.mjs} +270 -47
  16. package/dist/cli/manage-policies.mjs.map +1 -0
  17. package/dist/cli/run-host.d.mts +1 -0
  18. package/dist/cli/run-host.mjs +3090 -0
  19. package/dist/cli/run-host.mjs.map +1 -0
  20. package/dist/config-CPFQIGdG.mjs +57 -0
  21. package/dist/config-CPFQIGdG.mjs.map +1 -0
  22. package/dist/config-Dvl-Pov4.mjs +76 -0
  23. package/dist/config-Dvl-Pov4.mjs.map +1 -0
  24. package/dist/daemon/index.d.mts.map +1 -1
  25. package/dist/daemon/index.mjs +970 -332
  26. package/dist/daemon/index.mjs.map +1 -1
  27. package/dist/supervisor-actions-CiW56eLi.mjs +843 -0
  28. package/dist/supervisor-actions-CiW56eLi.mjs.map +1 -0
  29. package/dist/turn-log-buffer-DRgW53gl.mjs +767 -0
  30. package/dist/turn-log-buffer-DRgW53gl.mjs.map +1 -0
  31. package/dist/web/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
  32. package/dist/web/_app/immutable/chunks/BhRSsUCh.js +2 -0
  33. package/dist/web/_app/immutable/chunks/BiLeM2i1.js +1 -0
  34. package/{web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js → dist/web/_app/immutable/chunks/BmBj85Ll.js} +1 -1
  35. package/dist/web/_app/immutable/chunks/BrERcKAH.js +1 -0
  36. package/dist/web/_app/immutable/chunks/Bv9252RM.js +1 -0
  37. package/dist/web/_app/immutable/chunks/CIXNBPKi.js +1 -0
  38. package/dist/web/_app/immutable/chunks/DISKL3GN.js +2 -0
  39. package/dist/web/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
  40. package/dist/web/_app/immutable/chunks/DnQ3vS13.js +1 -0
  41. package/dist/web/_app/immutable/chunks/KsloHTKS.js +1 -0
  42. package/{web/.svelte-kit/output/client/_app/immutable/chunks/Ck-be5J2.js → dist/web/_app/immutable/chunks/RsHsUj-8.js} +2 -2
  43. package/dist/web/_app/immutable/chunks/{G_zz-Gou.js → wpfV79dV.js} +1 -1
  44. package/dist/web/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
  45. package/dist/web/_app/immutable/entry/start.Di0-Jhte.js +1 -0
  46. package/dist/web/_app/immutable/nodes/{0.CYS8iApT.js → 0.DYyUA1au.js} +1 -1
  47. package/dist/web/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
  48. package/dist/web/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
  49. package/{web/.svelte-kit/output/client/_app/immutable/nodes/3.Dr0ot9sV.js → dist/web/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
  50. package/dist/web/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
  51. package/dist/web/_app/immutable/nodes/{5.BBGQ_i84.js → 5.DLbPVJY2.js} +1 -1
  52. package/dist/web/_app/version.json +1 -1
  53. package/dist/web/index.html +12 -12
  54. package/dist/workspace-oWmVh5mi.mjs +1001 -0
  55. package/dist/workspace-oWmVh5mi.mjs.map +1 -0
  56. package/docs/23_adapter_slash_autocomplete/development_log.md +19 -0
  57. package/docs/23_adapter_slash_autocomplete/notes.md +18 -0
  58. package/docs/23_adapter_slash_autocomplete/prd.md +46 -0
  59. package/docs/23_adapter_slash_autocomplete/questions.md +6 -0
  60. package/docs/23_adapter_slash_autocomplete/tickets.md +21 -0
  61. package/docs/24_subagent_job_policy_fixes/development_log.md +22 -0
  62. package/docs/24_subagent_job_policy_fixes/notes.md +28 -0
  63. package/docs/24_subagent_job_policy_fixes/prd.md +59 -0
  64. package/docs/24_subagent_job_policy_fixes/questions.md +3 -0
  65. package/docs/24_subagent_job_policy_fixes/tickets.md +49 -0
  66. package/docs/25_e2e_test_improvements/development_log.md +30 -0
  67. package/docs/25_e2e_test_improvements/notes.md +29 -0
  68. package/docs/25_e2e_test_improvements/prd.md +43 -0
  69. package/docs/25_e2e_test_improvements/questions.md +12 -0
  70. package/docs/25_e2e_test_improvements/tickets-2.md +22 -0
  71. package/docs/25_e2e_test_improvements/tickets.md +22 -0
  72. package/docs/25_policy_cwd/development_log.md +30 -0
  73. package/docs/25_policy_cwd/notes.md +28 -0
  74. package/docs/25_policy_cwd/prd.md +77 -0
  75. package/docs/25_policy_cwd/questions.md +6 -0
  76. package/docs/25_policy_cwd/tickets.md +77 -0
  77. package/docs/CLI_REFERENCE.md +3 -1
  78. package/docs/PHILOSOPHY.md +35 -0
  79. package/docs/adapter-visibility/SPEC.md +461 -0
  80. package/docs/adapter-visibility/SPEC_v2.md +202 -0
  81. package/docs/auto-update/SPEC.md +344 -0
  82. package/docs/backups/SPEC.md +296 -0
  83. package/docs/backups/clawmini.gitignore +69 -0
  84. package/docs/guides/assets/clawmini-avatar.png +0 -0
  85. package/docs/guides/backups.md +332 -0
  86. package/docs/guides/discord_adapter_setup.md +1 -1
  87. package/docs/guides/google_chat_adapter_setup.md +81 -0
  88. package/docs/unified-startup/SPEC.md +203 -0
  89. package/e2e/_helpers/test-environment.test.ts +49 -0
  90. package/e2e/_helpers/test-environment.ts +548 -0
  91. package/e2e/adapters/_google-chat-fixtures.ts +340 -0
  92. package/{src/cli/e2e → e2e/adapters}/adapter-discord.test.ts +22 -23
  93. package/e2e/adapters/adapter-google-chat-downtime.test.ts +157 -0
  94. package/e2e/adapters/adapter-google-chat-inbound.test.ts +697 -0
  95. package/e2e/adapters/adapter-google-chat-outbound.test.ts +297 -0
  96. package/e2e/adapters/adapter-google-chat-roundtrip.test.ts +56 -0
  97. package/e2e/adapters/adapter-google-chat-threads.test.ts +1078 -0
  98. package/e2e/agents/custom-api-env.test.ts +80 -0
  99. package/e2e/agents/export-lite-func.test.ts +104 -0
  100. package/e2e/agents/fallbacks.test.ts +124 -0
  101. package/e2e/agents/interrupt.test.ts +50 -0
  102. package/e2e/agents/no-reply-necessary.test.ts +57 -0
  103. package/e2e/agents/session-timeout-subagents.test.ts +76 -0
  104. package/e2e/agents/subagent-authorization.test.ts +246 -0
  105. package/e2e/agents/subagent-env.test.ts +49 -0
  106. package/e2e/agents/subagent-lifecycle.test.ts +782 -0
  107. package/e2e/agents/subagents-depth.test.ts +47 -0
  108. package/e2e/cli/agents.test.ts +176 -0
  109. package/e2e/cli/auto-update.test.ts +741 -0
  110. package/e2e/cli/basic.test.ts +44 -0
  111. package/{src/cli/e2e → e2e/cli}/export-lite.test.ts +16 -12
  112. package/e2e/cli/init-gitignore.test.ts +86 -0
  113. package/e2e/cli/init.test.ts +76 -0
  114. package/e2e/cli/messages.test.ts +363 -0
  115. package/e2e/cli/serve.test.ts +76 -0
  116. package/{src/cli/e2e → e2e/cli}/skills.test.ts +11 -10
  117. package/{src/cli/e2e → e2e/daemon}/daemon.test.ts +57 -195
  118. package/e2e/jobs/agent-jobs.test.ts +216 -0
  119. package/e2e/jobs/cron.test.ts +64 -0
  120. package/e2e/jobs/restart.test.ts +108 -0
  121. package/e2e/policies/approval-session.test.ts +69 -0
  122. package/e2e/policies/auto-create-policies-file.test.ts +35 -0
  123. package/e2e/policies/builtin-manage-policies.test.ts +184 -0
  124. package/e2e/policies/builtin-run-host.test.ts +180 -0
  125. package/e2e/policies/environment-policies.test.ts +177 -0
  126. package/e2e/policies/manage-policies.test.ts +566 -0
  127. package/e2e/policies/output-size.test.ts +98 -0
  128. package/e2e/policies/policies-context-cwd.test.ts +160 -0
  129. package/e2e/policies/relative-script-path.test.ts +60 -0
  130. package/e2e/policies/requests-show.test.ts +135 -0
  131. package/e2e/policies/requests.test.ts +208 -0
  132. package/e2e/policies/slash-policies.test.ts +308 -0
  133. package/e2e/policies/startup-cleanup.test.ts +48 -0
  134. package/e2e/routers/session-timeout.test.ts +106 -0
  135. package/e2e/routers/slash-model.test.ts +152 -0
  136. package/e2e/routers/slash-new.test.ts +50 -0
  137. package/e2e/routers/slash-restart-adapter.test.ts +96 -0
  138. package/e2e/routers/slash-restart.test.ts +114 -0
  139. package/e2e/routers/slash-shutdown.test.ts +55 -0
  140. package/e2e/routers/slash-stop.test.ts +232 -0
  141. package/e2e/routers/slash-upgrade.test.ts +88 -0
  142. package/{src/cli/e2e → e2e/sandbox}/environments.test.ts +14 -13
  143. package/eslint.config.js +6 -0
  144. package/napkin.md +1 -1
  145. package/package.json +8 -3
  146. package/src/adapter-discord/commands.test.ts +42 -0
  147. package/src/adapter-discord/commands.ts +33 -0
  148. package/src/adapter-discord/config.ts +12 -0
  149. package/src/adapter-discord/forwarder.test.ts +499 -21
  150. package/src/adapter-discord/forwarder.ts +343 -124
  151. package/src/adapter-discord/inbound-cache.test.ts +47 -0
  152. package/src/adapter-discord/inbound-cache.ts +37 -0
  153. package/src/adapter-discord/index.test.ts +67 -2
  154. package/src/adapter-discord/index.ts +84 -216
  155. package/src/adapter-discord/interactions.test.ts +54 -3
  156. package/src/adapter-discord/interactions.ts +97 -53
  157. package/src/adapter-discord/processMessage.ts +239 -0
  158. package/src/adapter-discord/state.ts +1 -0
  159. package/src/adapter-google-chat/auth.test.ts +9 -5
  160. package/src/adapter-google-chat/auth.ts +29 -23
  161. package/src/adapter-google-chat/cards.ts +7 -2
  162. package/src/adapter-google-chat/client.test.ts +37 -2
  163. package/src/adapter-google-chat/client.ts +138 -38
  164. package/src/adapter-google-chat/config.ts +19 -0
  165. package/src/adapter-google-chat/forwarder.test.ts +81 -56
  166. package/src/adapter-google-chat/forwarder.ts +394 -185
  167. package/src/adapter-google-chat/inbound-cache.test.ts +61 -0
  168. package/src/adapter-google-chat/inbound-cache.ts +36 -0
  169. package/src/adapter-google-chat/state.test.ts +1 -0
  170. package/src/adapter-google-chat/state.ts +9 -1
  171. package/src/adapter-google-chat/subscriptions.ts +8 -6
  172. package/src/cli/builtin-policies.ts +44 -0
  173. package/src/cli/commands/agents.ts +59 -5
  174. package/src/cli/commands/down.ts +54 -2
  175. package/src/cli/commands/environments.ts +8 -2
  176. package/src/cli/commands/init.ts +31 -0
  177. package/src/cli/commands/logs.ts +116 -0
  178. package/src/cli/commands/policies.ts +6 -4
  179. package/src/cli/commands/serve.test.ts +67 -0
  180. package/src/cli/commands/serve.ts +284 -0
  181. package/src/cli/commands/up.ts +122 -2
  182. package/src/cli/commands/web-api/agents.ts +3 -2
  183. package/src/cli/index.ts +4 -0
  184. package/src/cli/install-detection.test.ts +72 -0
  185. package/src/cli/install-detection.ts +48 -0
  186. package/src/cli/lite.ts +54 -22
  187. package/src/cli/manage-policies-utils.ts +104 -0
  188. package/src/cli/manage-policies.ts +291 -0
  189. package/src/cli/run-host.ts +45 -0
  190. package/src/cli/supervisor-actions.ts +267 -0
  191. package/src/cli/supervisor-control.test.ts +129 -0
  192. package/src/cli/supervisor-control.ts +155 -0
  193. package/src/cli/supervisor-pid.ts +68 -0
  194. package/src/cli/supervisor.ts +277 -0
  195. package/src/daemon/agent/agent-context.ts +11 -11
  196. package/src/daemon/agent/agent-session.ts +8 -1
  197. package/src/daemon/agent/chat-logger.test.ts +78 -9
  198. package/src/daemon/agent/chat-logger.ts +25 -5
  199. package/src/daemon/agent/turn-registry.test.ts +89 -0
  200. package/src/daemon/agent/turn-registry.ts +94 -0
  201. package/src/daemon/agent/types.ts +2 -0
  202. package/src/daemon/api/agent-policy-endpoints.ts +263 -0
  203. package/src/daemon/api/agent-router.ts +47 -126
  204. package/src/daemon/api/index.test.ts +1 -0
  205. package/src/daemon/api/policy-request.test.ts +7 -5
  206. package/src/daemon/api/router-utils.ts +6 -5
  207. package/src/daemon/api/subagent-router.ts +110 -74
  208. package/src/daemon/api/subagent-utils.test.ts +60 -0
  209. package/src/daemon/api/subagent-utils.ts +113 -87
  210. package/src/daemon/api/user-router.ts +34 -8
  211. package/src/daemon/auth.ts +1 -0
  212. package/src/daemon/cron.test.ts +62 -4
  213. package/src/daemon/cron.ts +42 -16
  214. package/src/daemon/events.ts +65 -0
  215. package/src/daemon/index.ts +24 -1
  216. package/src/daemon/message-interruption.test.ts +1 -0
  217. package/src/daemon/message-jobs.test.ts +1 -0
  218. package/src/daemon/message.ts +78 -14
  219. package/src/daemon/observation.test.ts +26 -18
  220. package/src/daemon/pending-replies.test.ts +112 -0
  221. package/src/daemon/pending-replies.ts +162 -0
  222. package/src/daemon/policy-request-service.ts +3 -1
  223. package/src/daemon/policy-utils.test.ts +66 -1
  224. package/src/daemon/policy-utils.ts +126 -1
  225. package/src/daemon/request-store.ts +31 -0
  226. package/src/daemon/routers/session-timeout.ts +4 -0
  227. package/src/daemon/routers/slash-model.test.ts +344 -0
  228. package/src/daemon/routers/slash-model.ts +207 -0
  229. package/src/daemon/routers/slash-policies.test.ts +38 -32
  230. package/src/daemon/routers/slash-policies.ts +84 -33
  231. package/src/daemon/routers/slash-restart.test.ts +69 -0
  232. package/src/daemon/routers/slash-restart.ts +36 -0
  233. package/src/daemon/routers/slash-shutdown.test.ts +50 -0
  234. package/src/daemon/routers/slash-shutdown.ts +28 -0
  235. package/src/daemon/routers/slash-upgrade.test.ts +116 -0
  236. package/src/daemon/routers/slash-upgrade.ts +76 -0
  237. package/src/daemon/routers/types.ts +7 -0
  238. package/src/daemon/routers.ts +16 -0
  239. package/src/shared/adapters/blockquote.test.ts +28 -0
  240. package/src/shared/adapters/blockquote.ts +20 -0
  241. package/src/shared/adapters/filtering.test.ts +224 -10
  242. package/src/shared/adapters/filtering.ts +95 -7
  243. package/src/shared/adapters/inbound-cache.test.ts +48 -0
  244. package/src/shared/adapters/inbound-cache.ts +54 -0
  245. package/src/shared/adapters/turn-log-buffer.ts +266 -0
  246. package/src/shared/adapters/turn-log.test.ts +389 -0
  247. package/src/shared/adapters/turn-log.ts +357 -0
  248. package/src/shared/agent-utils.ts +12 -5
  249. package/src/shared/chats.test.ts +4 -0
  250. package/src/shared/chats.ts +9 -0
  251. package/src/shared/config.ts +16 -1
  252. package/src/shared/lite.ts +76 -2
  253. package/src/shared/policies.ts +26 -0
  254. package/src/shared/template-manifest.ts +267 -0
  255. package/src/shared/utils/shell.ts +61 -0
  256. package/src/shared/version.ts +34 -0
  257. package/src/shared/workspace.test.ts +217 -0
  258. package/src/shared/workspace.ts +626 -48
  259. package/templates/environments/cladding/allowlist-domain.mjs +125 -0
  260. package/templates/environments/cladding/env.json +21 -1
  261. package/templates/environments/cladding/run-with-network.mjs +54 -0
  262. package/templates/environments/macos-proxy/allowlist-domain.mjs +95 -0
  263. package/templates/environments/macos-proxy/env.json +8 -1
  264. package/templates/environments/macos-proxy/proxy.mjs +42 -13
  265. package/templates/gemini/template.json +5 -0
  266. package/templates/gemini-claw/template.json +13 -0
  267. package/templates/skills/clawmini-requests/SKILL.md +69 -10
  268. package/templates/skills/run-host/SKILL.md +51 -0
  269. package/templates/skills/skill-creator/SKILL.md +4 -3
  270. package/templates/skills/skill-creator/scripts/validate.sh +52 -0
  271. package/tsdown.config.ts +10 -1
  272. package/vitest.config.ts +2 -2
  273. package/web/.svelte-kit/ambient.d.ts +292 -176
  274. package/web/.svelte-kit/generated/server/internal.js +1 -1
  275. package/web/.svelte-kit/output/client/.vite/manifest.json +127 -137
  276. package/web/.svelte-kit/output/client/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
  277. package/web/.svelte-kit/output/client/_app/immutable/chunks/BhRSsUCh.js +2 -0
  278. package/web/.svelte-kit/output/client/_app/immutable/chunks/BiLeM2i1.js +1 -0
  279. package/{dist/web/_app/immutable/chunks/CME08kGM.js → web/.svelte-kit/output/client/_app/immutable/chunks/BmBj85Ll.js} +1 -1
  280. package/web/.svelte-kit/output/client/_app/immutable/chunks/BrERcKAH.js +1 -0
  281. package/web/.svelte-kit/output/client/_app/immutable/chunks/Bv9252RM.js +1 -0
  282. package/web/.svelte-kit/output/client/_app/immutable/chunks/CIXNBPKi.js +1 -0
  283. package/web/.svelte-kit/output/client/_app/immutable/chunks/DISKL3GN.js +2 -0
  284. package/web/.svelte-kit/output/client/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
  285. package/web/.svelte-kit/output/client/_app/immutable/chunks/DnQ3vS13.js +1 -0
  286. package/web/.svelte-kit/output/client/_app/immutable/chunks/KsloHTKS.js +1 -0
  287. package/{dist/web/_app/immutable/chunks/Ck-be5J2.js → web/.svelte-kit/output/client/_app/immutable/chunks/RsHsUj-8.js} +2 -2
  288. package/web/.svelte-kit/output/client/_app/immutable/chunks/{G_zz-Gou.js → wpfV79dV.js} +1 -1
  289. package/web/.svelte-kit/output/client/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
  290. package/web/.svelte-kit/output/client/_app/immutable/entry/start.Di0-Jhte.js +1 -0
  291. package/web/.svelte-kit/output/client/_app/immutable/nodes/{0.CYS8iApT.js → 0.DYyUA1au.js} +1 -1
  292. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
  293. package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
  294. package/{dist/web/_app/immutable/nodes/3.Dr0ot9sV.js → web/.svelte-kit/output/client/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
  295. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
  296. package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.BBGQ_i84.js → 5.DLbPVJY2.js} +1 -1
  297. package/web/.svelte-kit/output/client/_app/version.json +1 -1
  298. package/web/.svelte-kit/output/server/.vite/manifest.json +12 -10
  299. package/web/.svelte-kit/output/server/chunks/Icon.js +1 -1
  300. package/web/.svelte-kit/output/server/chunks/client.js +1 -1
  301. package/web/.svelte-kit/output/server/chunks/exports.js +1 -1
  302. package/web/.svelte-kit/output/server/chunks/index-server.js +2 -1
  303. package/web/.svelte-kit/output/server/chunks/internal.js +1 -1
  304. package/web/.svelte-kit/output/server/chunks/render-context.js +77 -0
  305. package/web/.svelte-kit/output/server/chunks/root.js +739 -788
  306. package/web/.svelte-kit/output/server/chunks/shared.js +234 -21
  307. package/web/.svelte-kit/output/server/index.js +126 -90
  308. package/web/.svelte-kit/output/server/manifest-full.js +1 -1
  309. package/web/.svelte-kit/output/server/manifest.js +1 -1
  310. package/web/.svelte-kit/output/server/nodes/0.js +1 -1
  311. package/web/.svelte-kit/output/server/nodes/1.js +1 -1
  312. package/web/.svelte-kit/output/server/nodes/2.js +1 -1
  313. package/web/.svelte-kit/output/server/nodes/3.js +1 -1
  314. package/web/.svelte-kit/output/server/nodes/4.js +1 -1
  315. package/web/.svelte-kit/output/server/nodes/5.js +1 -1
  316. package/web/.svelte-kit/output/server/remote-entry.js +245 -81
  317. package/web/.svelte-kit/tsconfig.json +4 -1
  318. package/dist/cli/propose-policy.mjs.map +0 -1
  319. package/dist/lite-CBxOT1y5.mjs +0 -241
  320. package/dist/lite-CBxOT1y5.mjs.map +0 -1
  321. package/dist/routing-D8rTxtaV.mjs +0 -245
  322. package/dist/routing-D8rTxtaV.mjs.map +0 -1
  323. package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +0 -1
  324. package/dist/web/_app/immutable/chunks/BmRlVmv6.js +0 -1
  325. package/dist/web/_app/immutable/chunks/CK9JZLaG.js +0 -2
  326. package/dist/web/_app/immutable/chunks/Ck3rYNON.js +0 -1
  327. package/dist/web/_app/immutable/chunks/DMtIqaiV.js +0 -2
  328. package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
  329. package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
  330. package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
  331. package/dist/web/_app/immutable/chunks/bBmtyQMj.js +0 -1
  332. package/dist/web/_app/immutable/entry/app.CJmSwntr.js +0 -2
  333. package/dist/web/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
  334. package/dist/web/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
  335. package/dist/web/_app/immutable/nodes/4.oBhvQhcA.js +0 -60
  336. package/dist/workspace-BJmJBfKi.mjs +0 -456
  337. package/dist/workspace-BJmJBfKi.mjs.map +0 -1
  338. package/src/cli/e2e/agents.test.ts +0 -140
  339. package/src/cli/e2e/basic.test.ts +0 -43
  340. package/src/cli/e2e/cron.test.ts +0 -132
  341. package/src/cli/e2e/export-lite-func.test.ts +0 -206
  342. package/src/cli/e2e/fallbacks.test.ts +0 -175
  343. package/src/cli/e2e/init.test.ts +0 -77
  344. package/src/cli/e2e/messages.test.ts +0 -332
  345. package/src/cli/e2e/propose-policy.test.ts +0 -203
  346. package/src/cli/e2e/requests.test.ts +0 -180
  347. package/src/cli/e2e/session-timeout.test.ts +0 -192
  348. package/src/cli/e2e/slash-new.test.ts +0 -93
  349. package/src/cli/e2e/subagents.test.ts +0 -106
  350. package/src/cli/e2e/utils.ts +0 -66
  351. package/src/cli/propose-policy.ts +0 -91
  352. package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +0 -1
  353. package/web/.svelte-kit/output/client/_app/immutable/chunks/BmRlVmv6.js +0 -1
  354. package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +0 -2
  355. package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +0 -1
  356. package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +0 -2
  357. package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
  358. package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
  359. package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
  360. package/web/.svelte-kit/output/client/_app/immutable/chunks/bBmtyQMj.js +0 -1
  361. package/web/.svelte-kit/output/client/_app/immutable/entry/app.CJmSwntr.js +0 -2
  362. package/web/.svelte-kit/output/client/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
  363. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
  364. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.oBhvQhcA.js +0 -60
  365. package/web/.svelte-kit/output/server/chunks/false.js +0 -4
  366. /package/dist/cli/{propose-policy.d.mts → manage-policies.d.mts} +0 -0
  367. /package/{src/cli/e2e → e2e/_helpers}/global-setup.ts +0 -0
@@ -0,0 +1,1001 @@
1
+ import fs from "node:fs";
2
+ import path, { sep } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { execSync } from "node:child_process";
5
+ import crypto, { createHash, randomBytes } from "node:crypto";
6
+ import fsPromises from "node:fs/promises";
7
+ import { z } from "zod";
8
+
9
+ //#region src/shared/policies.ts
10
+ const BUILTIN_POLICY_SCRIPTS_DIR = ".clawmini/policy-scripts";
11
+ const BUILTIN_POLICIES = {
12
+ "manage-policies": {
13
+ description: "Add, update, or remove clawmini policies (subcommands: add, update, remove). Reads are unrestricted via `requests show`.",
14
+ command: `./${BUILTIN_POLICY_SCRIPTS_DIR}/manage-policies.js`,
15
+ allowHelp: true,
16
+ autoApprove: false
17
+ },
18
+ "run-host": {
19
+ description: "Run an arbitrary shell command on the host via `sh -c`",
20
+ command: `./${BUILTIN_POLICY_SCRIPTS_DIR}/run-host.js`,
21
+ allowHelp: true,
22
+ autoApprove: false
23
+ }
24
+ };
25
+
26
+ //#endregion
27
+ //#region src/shared/config.ts
28
+ const PolicyDefinitionSchema = z.looseObject({
29
+ description: z.string().optional(),
30
+ command: z.string(),
31
+ args: z.array(z.string()).optional(),
32
+ allowHelp: z.boolean().optional(),
33
+ autoApprove: z.boolean().optional()
34
+ });
35
+ const FallbackSchema = z.looseObject({
36
+ commands: z.looseObject({
37
+ new: z.string().optional(),
38
+ append: z.string().optional(),
39
+ getSessionId: z.string().optional(),
40
+ getMessageContent: z.string().optional()
41
+ }).optional(),
42
+ env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
43
+ retries: z.number().int().min(0).default(1),
44
+ delayMs: z.number().int().min(0).default(1e3)
45
+ });
46
+ const AgentSchema = z.looseObject({
47
+ extends: z.string().optional(),
48
+ commands: z.looseObject({
49
+ new: z.string().optional(),
50
+ append: z.string().optional(),
51
+ getSessionId: z.string().optional(),
52
+ getMessageContent: z.string().optional()
53
+ }).optional(),
54
+ apiTokenEnvVar: z.string().optional(),
55
+ apiUrlEnvVar: z.string().optional(),
56
+ env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
57
+ subagentEnv: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
58
+ modelShorthands: z.record(z.string(), z.string()).optional(),
59
+ directory: z.string().optional(),
60
+ skillsDir: z.string().nullable().optional(),
61
+ fallbacks: z.array(FallbackSchema).optional(),
62
+ files: z.string().default("./attachments").optional()
63
+ });
64
+ const CronJobSchema = z.lazy(() => z.looseObject({
65
+ id: z.string().min(1),
66
+ createdAt: z.string().optional(),
67
+ message: z.string().default(""),
68
+ reply: z.string().optional(),
69
+ agentId: z.string().optional(),
70
+ env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
71
+ session: z.looseObject({ type: z.string() }).optional(),
72
+ schedule: z.union([
73
+ z.looseObject({ cron: z.string() }),
74
+ z.looseObject({ every: z.string() }),
75
+ z.looseObject({ at: z.string() })
76
+ ]),
77
+ nextSessionId: z.string().optional(),
78
+ action: z.enum([
79
+ "stop",
80
+ "interrupt",
81
+ "continue"
82
+ ]).optional(),
83
+ jobs: z.looseObject({
84
+ add: z.array(z.lazy(() => CronJobSchema)).optional(),
85
+ remove: z.array(z.string()).optional()
86
+ }).optional()
87
+ }));
88
+ const RouterConfigSchema = z.union([z.string(), z.looseObject({
89
+ use: z.string(),
90
+ with: z.record(z.string(), z.any()).optional()
91
+ })]);
92
+ const SubagentTrackerSchema = z.looseObject({
93
+ id: z.string(),
94
+ agentId: z.string().optional(),
95
+ sessionId: z.string().optional(),
96
+ createdAt: z.string(),
97
+ status: z.enum([
98
+ "active",
99
+ "completed",
100
+ "failed"
101
+ ]),
102
+ parentId: z.string().optional()
103
+ });
104
+ const ChatSettingsSchema = z.looseObject({
105
+ defaultAgent: z.string().optional(),
106
+ sessions: z.record(z.string(), z.string()).optional(),
107
+ routers: z.array(RouterConfigSchema).optional(),
108
+ jobs: z.array(CronJobSchema).optional(),
109
+ subagents: z.record(z.string(), SubagentTrackerSchema).optional()
110
+ });
111
+ const AgentSessionSettingsSchema = z.looseObject({ env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional() });
112
+ const EnvironmentSchema = z.looseObject({
113
+ extends: z.string().optional(),
114
+ init: z.string().optional(),
115
+ up: z.string().optional(),
116
+ down: z.string().optional(),
117
+ prefix: z.string().optional(),
118
+ envFormat: z.string().optional(),
119
+ exportLiteTo: z.string().optional(),
120
+ baseDir: z.string().optional(),
121
+ env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
122
+ policies: z.record(z.string(), PolicyDefinitionSchema).optional()
123
+ });
124
+ const SettingsSchema = z.looseObject({
125
+ chats: z.looseObject({ defaultId: z.string().optional() }).optional(),
126
+ defaultAgent: AgentSchema.optional(),
127
+ environments: z.record(z.string(), z.string()).optional(),
128
+ routers: z.array(RouterConfigSchema).optional(),
129
+ files: z.string().default("./attachments").optional(),
130
+ api: z.union([z.boolean(), z.looseObject({
131
+ host: z.string().optional(),
132
+ port: z.number().optional(),
133
+ proxy_host: z.string().optional()
134
+ })]).optional()
135
+ });
136
+
137
+ //#endregion
138
+ //#region src/shared/utils/fs.ts
139
+ function pathIsInsideDir(path, dir, { allowSameDir = false } = {}) {
140
+ const dirWithSep = dir.endsWith(sep) ? dir : dir + sep;
141
+ if (allowSameDir && path === dir) return true;
142
+ return path.startsWith(dirWithSep) && path !== dir;
143
+ }
144
+
145
+ //#endregion
146
+ //#region src/shared/template-manifest.ts
147
+ const TemplateManifestSchema = z.looseObject({ files: z.record(z.string(), z.enum(["track", "seed-once"])).optional() });
148
+ const InstalledFilesSchema = z.looseObject({ files: z.record(z.string(), z.looseObject({ sha: z.string() })).optional() });
149
+ function sha256(content) {
150
+ return createHash("sha256").update(content).digest("hex");
151
+ }
152
+ async function fileSha(filePath) {
153
+ try {
154
+ return sha256(await fsPromises.readFile(filePath));
155
+ } catch {
156
+ return null;
157
+ }
158
+ }
159
+ async function readTemplateManifest(templateDir) {
160
+ const manifestPath = path.join(templateDir, "template.json");
161
+ try {
162
+ const raw = await fsPromises.readFile(manifestPath, "utf-8");
163
+ const parsed = TemplateManifestSchema.safeParse(JSON.parse(raw));
164
+ return parsed.success ? parsed.data : null;
165
+ } catch {
166
+ return null;
167
+ }
168
+ }
169
+ function getFileMode(relPath, manifest, defaultMode) {
170
+ if (!manifest?.files) return defaultMode;
171
+ const normalized = relPath.split(path.sep).join("/");
172
+ if (manifest.files[normalized]) return manifest.files[normalized];
173
+ const dirEntries = Object.entries(manifest.files).filter(([k]) => k.endsWith("/"));
174
+ dirEntries.sort((a, b) => b[0].length - a[0].length);
175
+ for (const [entry, mode] of dirEntries) if (normalized.startsWith(entry)) return mode;
176
+ return defaultMode;
177
+ }
178
+ async function walkTemplateFiles(dir, opts = {}) {
179
+ const skipRoot = new Set(opts.skipRoot ?? ["template.json", "settings.json"]);
180
+ const out = [];
181
+ async function walk(current, prefix) {
182
+ let entries;
183
+ try {
184
+ entries = await fsPromises.readdir(current, { withFileTypes: true });
185
+ } catch {
186
+ return;
187
+ }
188
+ for (const entry of entries) {
189
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
190
+ if (entry.isDirectory()) await walk(path.join(current, entry.name), rel);
191
+ else if (entry.isFile()) {
192
+ if (!prefix && skipRoot.has(entry.name)) continue;
193
+ out.push(rel);
194
+ }
195
+ }
196
+ }
197
+ await walk(dir, "");
198
+ return out;
199
+ }
200
+ async function planRefresh(templateDir, targetDir, manifest, installed, options) {
201
+ const templateFiles = await walkTemplateFiles(templateDir);
202
+ const actions = [];
203
+ const nextFiles = { ...installed?.files ?? {} };
204
+ for (const rel of templateFiles) {
205
+ const mode = getFileMode(rel, manifest, options.defaultMode);
206
+ const templatePath = path.join(templateDir, rel);
207
+ const targetPath = path.join(targetDir, rel);
208
+ const templateHash = sha256(await fsPromises.readFile(templatePath));
209
+ if (options.firstInstall) {
210
+ actions.push({
211
+ action: "write",
212
+ relPath: rel,
213
+ reason: "new"
214
+ });
215
+ nextFiles[rel] = { sha: templateHash };
216
+ continue;
217
+ }
218
+ if (mode === "seed-once") {
219
+ const diskHash = await fileSha(targetPath);
220
+ const recorded = installed?.files?.[rel]?.sha;
221
+ if (diskHash === null && !recorded) {
222
+ actions.push({
223
+ action: "write",
224
+ relPath: rel,
225
+ reason: "new"
226
+ });
227
+ nextFiles[rel] = { sha: templateHash };
228
+ } else actions.push({
229
+ action: "skip-seed-once",
230
+ relPath: rel
231
+ });
232
+ continue;
233
+ }
234
+ const recorded = installed?.files?.[rel]?.sha;
235
+ const diskHash = await fileSha(targetPath);
236
+ if (diskHash === null) {
237
+ actions.push({
238
+ action: "write",
239
+ relPath: rel,
240
+ reason: "refresh"
241
+ });
242
+ nextFiles[rel] = { sha: templateHash };
243
+ continue;
244
+ }
245
+ if (!recorded) {
246
+ if (options.accept) {
247
+ actions.push({
248
+ action: "write",
249
+ relPath: rel,
250
+ reason: "refresh"
251
+ });
252
+ nextFiles[rel] = { sha: templateHash };
253
+ } else actions.push({
254
+ action: "skip-diverged",
255
+ relPath: rel,
256
+ reason: "no-recorded-sha"
257
+ });
258
+ continue;
259
+ }
260
+ if (diskHash === templateHash) {
261
+ actions.push({
262
+ action: "skip-unchanged",
263
+ relPath: rel
264
+ });
265
+ nextFiles[rel] = { sha: templateHash };
266
+ continue;
267
+ }
268
+ if (diskHash === recorded || options.accept) {
269
+ actions.push({
270
+ action: "write",
271
+ relPath: rel,
272
+ reason: "refresh"
273
+ });
274
+ nextFiles[rel] = { sha: templateHash };
275
+ } else actions.push({
276
+ action: "skip-diverged",
277
+ relPath: rel,
278
+ reason: "edited"
279
+ });
280
+ }
281
+ return {
282
+ actions,
283
+ nextInstalled: { files: nextFiles }
284
+ };
285
+ }
286
+ async function applyPlan(templateDir, targetDir, plan) {
287
+ for (const action of plan.actions) {
288
+ if (action.action !== "write") continue;
289
+ const src = path.join(templateDir, action.relPath);
290
+ const dst = path.join(targetDir, action.relPath);
291
+ await fsPromises.mkdir(path.dirname(dst), { recursive: true });
292
+ await fsPromises.copyFile(src, dst);
293
+ }
294
+ }
295
+ async function readInstalledFiles(filePath) {
296
+ try {
297
+ const raw = await fsPromises.readFile(filePath, "utf-8");
298
+ const parsed = InstalledFilesSchema.safeParse(JSON.parse(raw));
299
+ return parsed.success ? parsed.data : null;
300
+ } catch {
301
+ return null;
302
+ }
303
+ }
304
+ async function writeInstalledFiles(filePath, data) {
305
+ await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
306
+ const tmpPath = `${filePath}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
307
+ await fsPromises.writeFile(tmpPath, JSON.stringify(data, null, 2), "utf-8");
308
+ await fsPromises.rename(tmpPath, filePath);
309
+ }
310
+ function sliceInstalledUnder(installed, prefix) {
311
+ if (!installed?.files) return null;
312
+ const p = `${prefix}/`;
313
+ const filtered = {};
314
+ for (const [k, v] of Object.entries(installed.files)) if (k.startsWith(p)) filtered[k.slice(p.length)] = v;
315
+ return { files: filtered };
316
+ }
317
+ function prefixPlanKeys(plan, prefix) {
318
+ const p = prefix ? `${prefix}/` : "";
319
+ return {
320
+ actions: plan.actions.map((a) => ({
321
+ ...a,
322
+ relPath: p + a.relPath
323
+ })),
324
+ nextInstalled: { files: Object.fromEntries(Object.entries(plan.nextInstalled.files ?? {}).map(([k, v]) => [p + k, v])) }
325
+ };
326
+ }
327
+
328
+ //#endregion
329
+ //#region src/shared/workspace.ts
330
+ function getWorkspaceRoot(startDir = process.cwd()) {
331
+ let curr = startDir;
332
+ while (curr !== path.parse(curr).root) {
333
+ if (fs.existsSync(path.join(curr, ".clawmini"))) return curr;
334
+ if (fs.existsSync(path.join(curr, "package.json")) || fs.existsSync(path.join(curr, ".git"))) return curr;
335
+ curr = path.dirname(curr);
336
+ }
337
+ return startDir;
338
+ }
339
+ function resolveAgentWorkDir(agentId, customDir, startDir = process.cwd()) {
340
+ const workspaceRoot = getWorkspaceRoot(startDir);
341
+ let dirPath = workspaceRoot;
342
+ if (customDir) dirPath = path.resolve(workspaceRoot, customDir);
343
+ else if (agentId !== "default") dirPath = path.resolve(workspaceRoot, agentId);
344
+ if (!pathIsInsideDir(dirPath, workspaceRoot, { allowSameDir: true })) throw new Error("Invalid agent directory: resolves outside the workspace.");
345
+ return dirPath;
346
+ }
347
+ function resolveAgentSkillsDir(agentId, agentData, startDir = process.cwd()) {
348
+ if (agentData.skillsDir === null) return null;
349
+ const workDir = resolveAgentWorkDir(agentId, agentData.directory, startDir);
350
+ return path.resolve(workDir, agentData.skillsDir || ".agents/skills");
351
+ }
352
+ async function ensureAgentWorkDir(agentId, customDir, startDir = process.cwd()) {
353
+ const dirPath = resolveAgentWorkDir(agentId, customDir, startDir);
354
+ if (!fs.existsSync(dirPath)) {
355
+ await fsPromises.mkdir(dirPath, { recursive: true });
356
+ console.log(`Created agent working directory at ${dirPath}`);
357
+ }
358
+ return dirPath;
359
+ }
360
+ function getClawminiDir(startDir = process.cwd()) {
361
+ return path.join(getWorkspaceRoot(startDir), ".clawmini");
362
+ }
363
+ function getSocketPath(startDir = process.cwd()) {
364
+ return path.join(getClawminiDir(startDir), "daemon.sock");
365
+ }
366
+ function getSettingsPath(startDir = process.cwd()) {
367
+ return path.join(getClawminiDir(startDir), "settings.json");
368
+ }
369
+ function getPoliciesPath(startDir = process.cwd()) {
370
+ return path.join(getClawminiDir(startDir), "policies.json");
371
+ }
372
+ function getChatSettingsPath(chatId, startDir = process.cwd()) {
373
+ return path.join(getClawminiDir(startDir), "chats", chatId, "settings.json");
374
+ }
375
+ function isValidAgentId(agentId) {
376
+ if (!agentId || agentId.length === 0) return false;
377
+ return /^[a-zA-Z0-9_]+(?:-[a-zA-Z0-9_]+)*$/.test(agentId);
378
+ }
379
+ function getAgentDir(agentId, startDir = process.cwd()) {
380
+ if (!isValidAgentId(agentId)) throw new Error(`Invalid agent ID: ${agentId}`);
381
+ return path.join(getClawminiDir(startDir), "agents", agentId);
382
+ }
383
+ function getAgentSettingsPath(agentId, startDir = process.cwd()) {
384
+ return path.join(getAgentDir(agentId, startDir), "settings.json");
385
+ }
386
+ function getInstalledFilesPath(agentId, startDir = process.cwd()) {
387
+ return path.join(getAgentDir(agentId, startDir), "installed-files.json");
388
+ }
389
+ function getAgentSessionSettingsPath(agentId, sessionId, startDir = process.cwd()) {
390
+ if (!isValidAgentId(agentId)) throw new Error(`Invalid agent ID: ${agentId}`);
391
+ return path.join(getClawminiDir(startDir), "agents", agentId, "sessions", sessionId, "settings.json");
392
+ }
393
+ async function readJsonFile(filePath) {
394
+ try {
395
+ const data = await fsPromises.readFile(filePath, "utf-8");
396
+ return JSON.parse(data);
397
+ } catch {
398
+ return null;
399
+ }
400
+ }
401
+ async function writeJsonFile(filePath, data) {
402
+ const dir = path.dirname(filePath);
403
+ await fsPromises.mkdir(dir, { recursive: true });
404
+ const tmpPath = `${filePath}.${process.pid}.${crypto.randomBytes(4).toString("hex")}.tmp`;
405
+ await fsPromises.writeFile(tmpPath, JSON.stringify(data, null, 2), "utf-8");
406
+ await fsPromises.rename(tmpPath, filePath);
407
+ }
408
+ async function readChatSettings(chatId, startDir = process.cwd()) {
409
+ const data = await readJsonFile(getChatSettingsPath(chatId, startDir));
410
+ if (!data) return null;
411
+ const parsed = ChatSettingsSchema.safeParse(data);
412
+ return parsed.success ? parsed.data : null;
413
+ }
414
+ async function writeChatSettings(chatId, data, startDir = process.cwd()) {
415
+ await writeJsonFile(getChatSettingsPath(chatId, startDir), data);
416
+ }
417
+ const chatSettingsLocks = /* @__PURE__ */ new Map();
418
+ async function updateChatSettings(chatId, updater, startDir = process.cwd()) {
419
+ const prevLock = chatSettingsLocks.get(chatId) || Promise.resolve();
420
+ let release;
421
+ const nextLock = new Promise((resolve) => {
422
+ release = resolve;
423
+ });
424
+ const nextLockPromise = prevLock.catch(() => {}).then(() => nextLock);
425
+ chatSettingsLocks.set(chatId, nextLockPromise);
426
+ try {
427
+ await prevLock;
428
+ await writeChatSettings(chatId, await updater(await readChatSettings(chatId, startDir) || {}), startDir);
429
+ } finally {
430
+ release();
431
+ if (chatSettingsLocks.get(chatId) === nextLockPromise) chatSettingsLocks.delete(chatId);
432
+ }
433
+ }
434
+ async function readAgentSessionSettings(agentId, sessionId, startDir = process.cwd()) {
435
+ const data = await readJsonFile(getAgentSessionSettingsPath(agentId, sessionId, startDir));
436
+ if (!data) return null;
437
+ const parsed = AgentSessionSettingsSchema.safeParse(data);
438
+ return parsed.success ? parsed.data : null;
439
+ }
440
+ async function writeAgentSessionSettings(agentId, sessionId, data, startDir = process.cwd()) {
441
+ await writeJsonFile(getAgentSessionSettingsPath(agentId, sessionId, startDir), data);
442
+ }
443
+ async function getAgentOverlay(agentId, startDir = process.cwd()) {
444
+ const filePath = getAgentSettingsPath(agentId, startDir);
445
+ let dataStr;
446
+ try {
447
+ dataStr = await fsPromises.readFile(filePath, "utf-8");
448
+ } catch (err) {
449
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") return null;
450
+ throw err;
451
+ }
452
+ let data;
453
+ try {
454
+ data = JSON.parse(dataStr);
455
+ } catch (parseErr) {
456
+ const message = parseErr instanceof Error ? parseErr.message : String(parseErr);
457
+ throw new Error(`Invalid JSON in ${filePath}: ${message}`, { cause: parseErr });
458
+ }
459
+ const parsed = AgentSchema.safeParse(data);
460
+ if (!parsed.success) throw new Error(`Invalid schema in ${filePath}: ${parsed.error.message}`);
461
+ return parsed.data;
462
+ }
463
+ async function readAgentTemplateSettings(templateName, startDir) {
464
+ let templatePath;
465
+ try {
466
+ templatePath = await resolveTemplatePath(templateName, startDir);
467
+ } catch {
468
+ return null;
469
+ }
470
+ const data = await readJsonFile(path.join(templatePath, "settings.json"));
471
+ if (!data) return null;
472
+ const parsed = AgentSchema.safeParse(data);
473
+ if (!parsed.success) return null;
474
+ const result = { ...parsed.data };
475
+ delete result.directory;
476
+ return result;
477
+ }
478
+ async function getAgent(agentId, startDir = process.cwd()) {
479
+ const overlay = await getAgentOverlay(agentId, startDir);
480
+ if (!overlay) return null;
481
+ if (!overlay.extends) return overlay;
482
+ const template = await readAgentTemplateSettings(overlay.extends, startDir);
483
+ if (!template) return overlay;
484
+ const { env: overlayEnv, subagentEnv: overlaySub, modelShorthands: overlayShorthands, ...overlayRest } = overlay;
485
+ const { env: templateEnv, subagentEnv: templateSub, modelShorthands: templateShorthands, ...templateRest } = template;
486
+ const merged = {
487
+ ...templateRest,
488
+ ...overlayRest
489
+ };
490
+ const mergedEnv = mergeOneLevel(templateEnv, overlayEnv);
491
+ if (mergedEnv) merged.env = mergedEnv;
492
+ const mergedSub = mergeOneLevel(templateSub, overlaySub);
493
+ if (mergedSub) merged.subagentEnv = mergedSub;
494
+ const mergedShorthands = mergeOneLevel(templateShorthands, overlayShorthands);
495
+ if (mergedShorthands) merged.modelShorthands = mergedShorthands;
496
+ return merged;
497
+ }
498
+ async function writeAgentSettings(agentId, data, startDir = process.cwd()) {
499
+ await ensureAgentWorkDir(agentId, data.directory, startDir);
500
+ await writeJsonFile(getAgentSettingsPath(agentId, startDir), data);
501
+ }
502
+ const agentSettingsLocks = /* @__PURE__ */ new Map();
503
+ async function updateAgentOverlay(agentId, updater, startDir = process.cwd()) {
504
+ const prevLock = agentSettingsLocks.get(agentId) || Promise.resolve();
505
+ let release;
506
+ const nextLock = new Promise((resolve) => {
507
+ release = resolve;
508
+ });
509
+ const nextLockPromise = prevLock.catch(() => {}).then(() => nextLock);
510
+ agentSettingsLocks.set(agentId, nextLockPromise);
511
+ try {
512
+ await prevLock;
513
+ const overlay = await getAgentOverlay(agentId, startDir);
514
+ if (!overlay) throw new Error(`Agent '${agentId}' has no settings overlay.`);
515
+ const updated = await updater(overlay);
516
+ if (updated === null) return false;
517
+ await writeAgentSettings(agentId, updated, startDir);
518
+ return true;
519
+ } finally {
520
+ release();
521
+ if (agentSettingsLocks.get(agentId) === nextLockPromise) agentSettingsLocks.delete(agentId);
522
+ }
523
+ }
524
+ async function listAgents(startDir = process.cwd()) {
525
+ const agentsDir = path.join(getClawminiDir(startDir), "agents");
526
+ try {
527
+ const entries = await fsPromises.readdir(agentsDir, { withFileTypes: true });
528
+ const agentIds = [];
529
+ for (const entry of entries) if (entry.isDirectory()) {
530
+ const settingsPath = path.join(agentsDir, entry.name, "settings.json");
531
+ try {
532
+ await fsPromises.access(settingsPath);
533
+ agentIds.push(entry.name);
534
+ } catch {}
535
+ }
536
+ return agentIds;
537
+ } catch {
538
+ return [];
539
+ }
540
+ }
541
+ async function deleteAgent(agentId, startDir = process.cwd()) {
542
+ const dir = getAgentDir(agentId, startDir);
543
+ const agentsDir = path.join(getClawminiDir(startDir), "agents");
544
+ if (!pathIsInsideDir(dir, agentsDir)) throw new Error(`Security Error: Cannot delete agent directory outside of ${agentsDir}`);
545
+ try {
546
+ await fsPromises.rm(dir, {
547
+ recursive: true,
548
+ force: true
549
+ });
550
+ } catch {}
551
+ }
552
+ async function isDirectory(dirPath) {
553
+ try {
554
+ return (await fsPromises.stat(dirPath)).isDirectory();
555
+ } catch {
556
+ return false;
557
+ }
558
+ }
559
+ async function resolveTemplatePathBase(templateName, startDir = process.cwd()) {
560
+ const workspaceRoot = getWorkspaceRoot(startDir);
561
+ const localTemplatePath = path.join(workspaceRoot, ".clawmini", "templates", templateName);
562
+ if (await isDirectory(localTemplatePath)) return localTemplatePath;
563
+ let currentDir = path.dirname(fileURLToPath(import.meta.url));
564
+ while (currentDir !== path.parse(currentDir).root && !fs.existsSync(path.join(currentDir, "package.json"))) currentDir = path.dirname(currentDir);
565
+ const searchPath = path.join(currentDir, "templates", templateName);
566
+ if (await isDirectory(searchPath)) return searchPath;
567
+ throw new Error(`Template not found: ${templateName} (searched local: ${localTemplatePath}, built-in: ${searchPath})`);
568
+ }
569
+ async function resolveTemplatePath(templateName, startDir = process.cwd()) {
570
+ if (templateName === "environments" || templateName.startsWith("environments/")) throw new Error(`Template not found: ${templateName}`);
571
+ return resolveTemplatePathBase(templateName, startDir);
572
+ }
573
+ async function resolveEnvironmentTemplatePath(templateName, startDir = process.cwd()) {
574
+ return resolveTemplatePathBase(path.join("environments", templateName), startDir);
575
+ }
576
+ async function resolveSkillsTemplatePath(startDir = process.cwd()) {
577
+ return resolveTemplatePathBase("skills", startDir);
578
+ }
579
+ async function copyTemplateBase(templatePath, targetDir, allowMissingDir = false, overwrite = false) {
580
+ try {
581
+ if ((await fsPromises.readdir(targetDir)).length > 0 && !overwrite) throw new Error(`Target directory is not empty: ${targetDir}`);
582
+ } catch (err) {
583
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") if (allowMissingDir) await fsPromises.mkdir(targetDir, { recursive: true });
584
+ else throw new Error(`Target directory does not exist: ${targetDir}`, { cause: err });
585
+ else throw err;
586
+ }
587
+ const rootTemplateJson = path.resolve(templatePath, "template.json");
588
+ await fsPromises.cp(templatePath, targetDir, {
589
+ recursive: true,
590
+ force: true,
591
+ filter: (src) => path.resolve(src) !== rootTemplateJson
592
+ });
593
+ }
594
+ async function copyTemplate(templateName, targetDir, startDir = process.cwd(), opts = {}) {
595
+ await copyTemplateBase(await resolveTemplatePath(templateName, startDir), targetDir, false, opts.force ?? false);
596
+ }
597
+ async function resolveTargetAgentSkillsDir(agentId, startDir = process.cwd()) {
598
+ const agentDir = getAgentDir(agentId, startDir);
599
+ try {
600
+ if (!(await fsPromises.stat(agentDir)).isDirectory()) throw new Error(`Agent not found: ${agentId}`);
601
+ } catch (err) {
602
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") throw new Error(`Agent not found: ${agentId}`, { cause: err });
603
+ throw err;
604
+ }
605
+ let agentData = null;
606
+ try {
607
+ agentData = await getAgent(agentId, startDir);
608
+ } catch {}
609
+ if (agentData) return resolveAgentSkillsDir(agentId, agentData, startDir);
610
+ const workDir = resolveAgentWorkDir(agentId, void 0, startDir);
611
+ return path.resolve(workDir, ".agents/skills");
612
+ }
613
+ async function copyEnvironmentTemplate(templateName, targetDir, startDir = process.cwd()) {
614
+ await copyTemplateBase(await resolveEnvironmentTemplatePath(templateName, startDir), targetDir, true);
615
+ }
616
+ async function copyAgentSkills(agentId, startDir = process.cwd(), overwrite = false) {
617
+ const targetDir = await resolveTargetAgentSkillsDir(agentId, startDir);
618
+ if (targetDir === null) throw new Error(`Agent '${agentId}' has skills disabled (skillsDir is null).`);
619
+ await copyTemplateBase(await resolveSkillsTemplatePath(startDir), targetDir, true, overwrite);
620
+ }
621
+ async function copyAgentSkill(agentId, skillName, startDir = process.cwd(), overwrite = false) {
622
+ const targetDir = await resolveTargetAgentSkillsDir(agentId, startDir);
623
+ if (targetDir === null) throw new Error(`Agent '${agentId}' has skills disabled (skillsDir is null).`);
624
+ const templatePath = await resolveSkillsTemplatePath(startDir);
625
+ const specificSkillPath = path.join(templatePath, skillName);
626
+ try {
627
+ if (!(await fsPromises.stat(specificSkillPath)).isDirectory()) throw new Error(`Skill not found: ${skillName}`);
628
+ } catch (err) {
629
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") throw new Error(`Skill not found: ${skillName}`, { cause: err });
630
+ throw err;
631
+ }
632
+ await copyTemplateBase(specificSkillPath, path.join(targetDir, skillName), true, overwrite);
633
+ }
634
+ async function collectTemplateCollisions(templateDir, targetDir) {
635
+ const templateFiles = await walkTemplateFiles(templateDir);
636
+ const collisions = [];
637
+ for (const rel of templateFiles) try {
638
+ await fsPromises.access(path.join(targetDir, rel));
639
+ collisions.push(rel);
640
+ } catch {}
641
+ return collisions;
642
+ }
643
+ function formatCollisionError(collisions) {
644
+ return `Target directory has existing files that the template would overwrite:\n${collisions.slice(0, 5).map((p) => ` ${p}`).join("\n")}${collisions.length > 5 ? `\n ... and ${collisions.length - 5} more` : ""}\nRe-run with --force to overwrite.`;
645
+ }
646
+ async function applyTemplateToAgent(agentId, templateName, overrides, startDir = process.cwd(), opts = {}) {
647
+ const agentWorkDir = resolveAgentWorkDir(agentId, overrides.directory, startDir);
648
+ if (opts.fork) {
649
+ await copyTemplate(templateName, agentWorkDir, startDir, { force: opts.force ?? false });
650
+ const settingsPath = path.join(agentWorkDir, "settings.json");
651
+ const manifestPath = path.join(agentWorkDir, "template.json");
652
+ try {
653
+ const rawSettings = await fsPromises.readFile(settingsPath, "utf-8");
654
+ const parsedSettings = JSON.parse(rawSettings);
655
+ const validation = AgentSchema.safeParse(parsedSettings);
656
+ if (validation.success) {
657
+ const templateData = validation.data;
658
+ if (templateData.directory) {
659
+ console.warn(`Warning: Ignoring 'directory' field from template settings.json. Using default or provided directory.`);
660
+ delete templateData.directory;
661
+ }
662
+ const mergedEnv = {
663
+ ...templateData.env || {},
664
+ ...overrides.env || {}
665
+ };
666
+ const mergedData = {
667
+ ...templateData,
668
+ ...overrides
669
+ };
670
+ delete mergedData.extends;
671
+ if (Object.keys(mergedEnv).length > 0) mergedData.env = mergedEnv;
672
+ await writeAgentSettings(agentId, mergedData, startDir);
673
+ }
674
+ } catch {}
675
+ for (const tmp of [settingsPath, manifestPath]) try {
676
+ await fsPromises.rm(tmp);
677
+ } catch {}
678
+ return;
679
+ }
680
+ const templateDir = await resolveTemplatePath(templateName, startDir);
681
+ const manifest = await readTemplateManifest(templateDir);
682
+ await fsPromises.mkdir(agentWorkDir, { recursive: true });
683
+ if (!opts.force) {
684
+ const collisions = await collectTemplateCollisions(templateDir, agentWorkDir);
685
+ if (collisions.length > 0) throw new Error(formatCollisionError(collisions));
686
+ }
687
+ const plan = await planRefresh(templateDir, agentWorkDir, manifest, null, {
688
+ defaultMode: "seed-once",
689
+ firstInstall: true
690
+ });
691
+ await applyPlan(templateDir, agentWorkDir, plan);
692
+ await writeInstalledFiles(getInstalledFilesPath(agentId, startDir), plan.nextInstalled);
693
+ await writeAgentSettings(agentId, {
694
+ extends: templateName,
695
+ ...overrides
696
+ }, startDir);
697
+ }
698
+ async function refreshAgentTemplate(agentId, agent, startDir = process.cwd(), opts = {}) {
699
+ if (!agent.extends) return null;
700
+ const templateDir = await resolveTemplatePath(agent.extends, startDir);
701
+ const agentWorkDir = resolveAgentWorkDir(agentId, agent.directory, startDir);
702
+ const manifest = await readTemplateManifest(templateDir);
703
+ const installedPath = getInstalledFilesPath(agentId, startDir);
704
+ const plan = await planRefresh(templateDir, agentWorkDir, manifest, await readInstalledFiles(installedPath), {
705
+ defaultMode: "seed-once",
706
+ ...opts.accept === void 0 ? {} : { accept: opts.accept }
707
+ });
708
+ if (opts.dryRun) return plan;
709
+ await applyPlan(templateDir, agentWorkDir, plan);
710
+ await writeInstalledFiles(installedPath, plan.nextInstalled);
711
+ return plan;
712
+ }
713
+ async function refreshAgentSkills(agentId, agent, startDir = process.cwd(), opts = {}) {
714
+ const skillsTargetDir = resolveAgentSkillsDir(agentId, agent, startDir);
715
+ if (skillsTargetDir === null) return null;
716
+ let skillsTemplateRoot;
717
+ try {
718
+ skillsTemplateRoot = await resolveSkillsTemplatePath(startDir);
719
+ } catch {
720
+ return null;
721
+ }
722
+ const agentWorkDir = resolveAgentWorkDir(agentId, agent.directory, startDir);
723
+ const prefixRel = path.relative(agentWorkDir, skillsTargetDir).split(path.sep).join("/");
724
+ let skillDirs;
725
+ try {
726
+ skillDirs = await fsPromises.readdir(skillsTemplateRoot, { withFileTypes: true });
727
+ } catch {
728
+ return null;
729
+ }
730
+ const installedPath = getInstalledFilesPath(agentId, startDir);
731
+ let installed = await readInstalledFiles(installedPath);
732
+ const allActions = [];
733
+ for (const entry of skillDirs) {
734
+ if (!entry.isDirectory()) continue;
735
+ const skillName = entry.name;
736
+ const skillTemplateDir = path.join(skillsTemplateRoot, skillName);
737
+ const skillTargetDir = path.join(skillsTargetDir, skillName);
738
+ const keyPrefix = `${prefixRel}/${skillName}`;
739
+ const plan = await planRefresh(skillTemplateDir, skillTargetDir, await readTemplateManifest(skillTemplateDir), sliceInstalledUnder(installed, keyPrefix), {
740
+ defaultMode: "track",
741
+ ...opts.firstInstall ? { firstInstall: true } : {},
742
+ ...opts.accept === void 0 ? {} : { accept: opts.accept }
743
+ });
744
+ const prefixed = prefixPlanKeys(plan, keyPrefix);
745
+ allActions.push(...prefixed.actions);
746
+ if (!opts.dryRun) {
747
+ await applyPlan(skillTemplateDir, skillTargetDir, plan);
748
+ installed = { files: {
749
+ ...installed?.files ?? {},
750
+ ...prefixed.nextInstalled.files ?? {}
751
+ } };
752
+ }
753
+ }
754
+ if (!opts.dryRun && installed) await writeInstalledFiles(installedPath, installed);
755
+ return {
756
+ actions: allActions,
757
+ nextInstalled: installed ?? { files: {} }
758
+ };
759
+ }
760
+ function formatPlanActions(plan, opts = {}) {
761
+ const prefix = opts.prefix ?? (opts.agentId ? `[${opts.agentId}] ` : "");
762
+ return plan.actions.map((action) => {
763
+ switch (action.action) {
764
+ case "write": return `${prefix}${action.reason === "new" ? "install" : "refresh"} ${action.relPath}`;
765
+ case "skip-unchanged": return `${prefix}unchanged ${action.relPath}`;
766
+ case "skip-seed-once": return `${prefix}seed-once ${action.relPath}`;
767
+ case "skip-diverged": return `${prefix}diverged ${action.relPath} (${action.reason})`;
768
+ case "skip-absent-from-template": return `${prefix}absent ${action.relPath}`;
769
+ }
770
+ });
771
+ }
772
+ async function readSettings(startDir = process.cwd()) {
773
+ const data = await readJsonFile(getSettingsPath(startDir));
774
+ if (!data) return null;
775
+ const parsed = SettingsSchema.safeParse(data);
776
+ return parsed.success ? parsed.data : null;
777
+ }
778
+ async function writeSettings(data, startDir = process.cwd()) {
779
+ await writeJsonFile(getSettingsPath(startDir), data);
780
+ }
781
+ async function readPoliciesFile(startDir = process.cwd()) {
782
+ const data = await readJsonFile(getPoliciesPath(startDir));
783
+ if (!data) return null;
784
+ if (data.policies && typeof data.policies === "object") return data;
785
+ return null;
786
+ }
787
+ function resolvePolicies(file, clawminiDir) {
788
+ if (!file) return null;
789
+ const workspaceRoot = path.dirname(clawminiDir);
790
+ const resolveCommand = (definition) => {
791
+ if (!definition.command.startsWith("./") && !definition.command.startsWith("../")) return definition;
792
+ return {
793
+ ...definition,
794
+ command: path.resolve(workspaceRoot, definition.command)
795
+ };
796
+ };
797
+ const resolved = {};
798
+ for (const [name, value] of Object.entries(file.policies)) if (value !== false) resolved[name] = resolveCommand(value);
799
+ for (const [name, definition] of Object.entries(BUILTIN_POLICIES)) {
800
+ if (name in file.policies) continue;
801
+ const scriptPath = path.join(clawminiDir, "policy-scripts", `${name}.js`);
802
+ if (!fs.existsSync(scriptPath)) continue;
803
+ resolved[name] = resolveCommand(definition);
804
+ }
805
+ return { policies: resolved };
806
+ }
807
+ async function readBasePolicies(startDir = process.cwd()) {
808
+ return resolvePolicies(await readPoliciesFile(startDir), getClawminiDir(startDir));
809
+ }
810
+ async function readEnvironmentPoliciesForPath(targetPath, startDir = process.cwd()) {
811
+ const envInfo = await getActiveEnvironmentInfo(targetPath, startDir);
812
+ if (!envInfo) return {};
813
+ const envConfig = await readEnvironment(envInfo.name, startDir);
814
+ if (!envConfig?.policies) return {};
815
+ const searchDirs = await getEnvironmentSearchDirs(envInfo.name, startDir);
816
+ const resolved = {};
817
+ for (const [name, definition] of Object.entries(envConfig.policies)) {
818
+ const command = definition.command.startsWith("./") || definition.command.startsWith("../") ? resolveLayeredRelativePath(definition.command, searchDirs) : definition.command;
819
+ const entries = Object.entries({
820
+ ...definition,
821
+ command
822
+ }).filter(([, value]) => value !== void 0);
823
+ resolved[name] = Object.fromEntries(entries);
824
+ }
825
+ return resolved;
826
+ }
827
+ async function readPoliciesForPath(targetPath, startDir = process.cwd()) {
828
+ const base = await readBasePolicies(startDir);
829
+ const envPolicies = await readEnvironmentPoliciesForPath(targetPath, startDir);
830
+ if (Object.keys(envPolicies).length === 0) return base;
831
+ return { policies: {
832
+ ...base?.policies || {},
833
+ ...envPolicies
834
+ } };
835
+ }
836
+ function getEnvironmentPath(name, startDir = process.cwd()) {
837
+ return path.join(getClawminiDir(startDir), "environments", name);
838
+ }
839
+ function mergeOneLevel(base, overlay) {
840
+ if (!base && !overlay) return void 0;
841
+ return {
842
+ ...base || {},
843
+ ...overlay || {}
844
+ };
845
+ }
846
+ async function readEnvironmentRaw(name, startDir) {
847
+ const local = await readJsonFile(path.join(getEnvironmentPath(name, startDir), "env.json"));
848
+ if (local) {
849
+ const parsed = EnvironmentSchema.safeParse(local);
850
+ if (parsed.success) return parsed.data;
851
+ }
852
+ try {
853
+ const builtinDir = await resolveEnvironmentTemplatePath(name, startDir);
854
+ const builtinData = await readJsonFile(path.join(builtinDir, "env.json"));
855
+ if (builtinData) {
856
+ const parsed = EnvironmentSchema.safeParse(builtinData);
857
+ if (parsed.success) return parsed.data;
858
+ }
859
+ } catch {}
860
+ return null;
861
+ }
862
+ async function readBuiltinEnvironment(name, startDir) {
863
+ let builtinDir;
864
+ try {
865
+ builtinDir = await resolveEnvironmentTemplatePath(name, startDir);
866
+ } catch {
867
+ return null;
868
+ }
869
+ const data = await readJsonFile(path.join(builtinDir, "env.json"));
870
+ if (!data) return null;
871
+ const parsed = EnvironmentSchema.safeParse(data);
872
+ return parsed.success ? parsed.data : null;
873
+ }
874
+ async function resolveEnvironmentWithSeen(name, startDir, seen) {
875
+ if (seen.has(name)) throw new Error(`Environment extends cycle detected at '${name}'`);
876
+ seen.add(name);
877
+ const local = await readEnvironmentRaw(name, startDir);
878
+ if (!local || !local.extends) return local;
879
+ const parent = local.extends === name ? await readBuiltinEnvironment(name, startDir) : await resolveEnvironmentWithSeen(local.extends, startDir, seen);
880
+ if (!parent) return local;
881
+ const { env: localEnv, policies: localPolicies, ...localRestRaw } = local;
882
+ delete localRestRaw.extends;
883
+ const { env: parentEnv, policies: parentPolicies, ...parentRest } = parent;
884
+ const merged = {
885
+ ...parentRest,
886
+ ...localRestRaw
887
+ };
888
+ const mergedEnv = mergeOneLevel(parentEnv, localEnv);
889
+ if (mergedEnv) merged.env = mergedEnv;
890
+ const mergedPolicies = mergeOneLevel(parentPolicies, localPolicies);
891
+ if (mergedPolicies) merged.policies = mergedPolicies;
892
+ return merged;
893
+ }
894
+ async function readEnvironment(name, startDir = process.cwd()) {
895
+ return resolveEnvironmentWithSeen(name, startDir, /* @__PURE__ */ new Set());
896
+ }
897
+ async function getEnvironmentSearchDirs(name, startDir = process.cwd()) {
898
+ const dirs = [];
899
+ const seen = /* @__PURE__ */ new Set();
900
+ let currentName = name;
901
+ while (currentName && !seen.has(currentName)) {
902
+ seen.add(currentName);
903
+ const overlayDir = getEnvironmentPath(currentName, startDir);
904
+ if (fs.existsSync(overlayDir) && !dirs.includes(overlayDir)) dirs.push(overlayDir);
905
+ let builtinDir = null;
906
+ try {
907
+ builtinDir = await resolveEnvironmentTemplatePath(currentName, startDir);
908
+ } catch {}
909
+ if (builtinDir && !dirs.includes(builtinDir)) dirs.push(builtinDir);
910
+ const overlayData = await readJsonFile(path.join(overlayDir, "env.json"));
911
+ const overlayParsed = overlayData ? EnvironmentSchema.safeParse(overlayData) : null;
912
+ if (overlayParsed?.success && overlayParsed.data.extends) {
913
+ currentName = overlayParsed.data.extends;
914
+ continue;
915
+ }
916
+ if (builtinDir) {
917
+ const builtinData = await readJsonFile(path.join(builtinDir, "env.json"));
918
+ const builtinParsed = builtinData ? EnvironmentSchema.safeParse(builtinData) : null;
919
+ if (builtinParsed?.success && builtinParsed.data.extends) {
920
+ currentName = builtinParsed.data.extends;
921
+ continue;
922
+ }
923
+ }
924
+ currentName = void 0;
925
+ }
926
+ return dirs;
927
+ }
928
+ function substituteLayeredEnvDir(input, searchDirs) {
929
+ if (searchDirs.length === 0) return input;
930
+ return input.replace(/\{ENV_DIR\}(?:\/([^\s'"}]+))?/g, (_match, sub) => {
931
+ if (!sub) return searchDirs[0];
932
+ for (const dir of searchDirs) {
933
+ const candidate = path.resolve(dir, sub);
934
+ if (fs.existsSync(candidate)) return candidate;
935
+ }
936
+ return path.resolve(searchDirs[0], sub);
937
+ });
938
+ }
939
+ function resolveLayeredRelativePath(relPath, searchDirs) {
940
+ if (searchDirs.length === 0) return relPath;
941
+ for (const dir of searchDirs) {
942
+ const candidate = path.resolve(dir, relPath);
943
+ if (fs.existsSync(candidate)) return candidate;
944
+ }
945
+ return path.resolve(searchDirs[0], relPath);
946
+ }
947
+ async function getActiveEnvironmentInfo(targetPath, startDir = process.cwd()) {
948
+ const settings = await readSettings(startDir);
949
+ if (!settings?.environments) return null;
950
+ const workspaceRoot = getWorkspaceRoot(startDir);
951
+ const resolvedTarget = path.resolve(workspaceRoot, targetPath);
952
+ let bestMatch = null;
953
+ let maxDepth = -1;
954
+ for (const [envPath, envName] of Object.entries(settings.environments)) {
955
+ const resolvedEnvPath = path.resolve(workspaceRoot, envPath);
956
+ if (pathIsInsideDir(resolvedTarget, resolvedEnvPath, { allowSameDir: true })) {
957
+ const depth = resolvedEnvPath.split(path.sep).length;
958
+ if (depth > maxDepth) {
959
+ maxDepth = depth;
960
+ bestMatch = {
961
+ name: envName,
962
+ targetPath: resolvedEnvPath
963
+ };
964
+ }
965
+ }
966
+ }
967
+ return bestMatch;
968
+ }
969
+ async function enableEnvironment(name, targetPath = "./", startDir = process.cwd(), opts = {}) {
970
+ const targetDir = getEnvironmentPath(name, startDir);
971
+ if (!fs.existsSync(targetDir)) if (opts.fork) {
972
+ await copyEnvironmentTemplate(name, targetDir, startDir);
973
+ console.log(`Forked environment template '${name}'.`);
974
+ } else {
975
+ await resolveEnvironmentTemplatePath(name, startDir);
976
+ await fsPromises.mkdir(targetDir, { recursive: true });
977
+ await fsPromises.writeFile(path.join(targetDir, "env.json"), JSON.stringify({ extends: name }, null, 2), "utf-8");
978
+ console.log(`Enabled environment overlay '${name}' (extends built-in).`);
979
+ }
980
+ else console.log(`Environment '${name}' already exists in workspace.`);
981
+ const settings = await readSettings(startDir) || { chats: { defaultId: "" } };
982
+ const environments = settings.environments || {};
983
+ environments[targetPath] = name;
984
+ settings.environments = environments;
985
+ await writeSettings(settings, startDir);
986
+ console.log(`Enabled environment '${name}' for path '${targetPath}'.`);
987
+ const envConfig = await readEnvironment(name, startDir);
988
+ if (envConfig?.init) {
989
+ const workspaceRoot = getWorkspaceRoot(startDir);
990
+ const affectedDir = path.resolve(workspaceRoot, targetPath);
991
+ console.log(`Executing init command for environment '${name}': ${envConfig.init}`);
992
+ execSync(envConfig.init, {
993
+ cwd: affectedDir,
994
+ stdio: "inherit"
995
+ });
996
+ }
997
+ }
998
+
999
+ //#endregion
1000
+ export { updateChatSettings as A, readSettings as C, resolveSkillsTemplatePath as D, resolveAgentWorkDir as E, pathIsInsideDir as F, CronJobSchema as I, SettingsSchema as L, writeAgentSettings as M, writeChatSettings as N, substituteLayeredEnvDir as O, writeSettings as P, BUILTIN_POLICIES as R, readPoliciesForPath as S, refreshAgentTemplate as T, isValidAgentId as _, enableEnvironment as a, readChatSettings as b, getAgent as c, getEnvironmentPath as d, getEnvironmentSearchDirs as f, getWorkspaceRoot as g, getSocketPath as h, deleteAgent as i, writeAgentSessionSettings as j, updateAgentOverlay as k, getAgentOverlay as l, getSettingsPath as m, copyAgentSkill as n, formatPlanActions as o, getPoliciesPath as p, copyAgentSkills as r, getActiveEnvironmentInfo as s, applyTemplateToAgent as t, getClawminiDir as u, listAgents as v, refreshAgentSkills as w, readEnvironment as x, readAgentSessionSettings as y };
1001
+ //# sourceMappingURL=workspace-oWmVh5mi.mjs.map