zidane 7.2.5 → 8.0.4

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 (376) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -6
  3. package/dist/{acp-D-tVyUjw.js → acp-DQDhi2dj.js} +230 -77
  4. package/dist/acp-DQDhi2dj.js.map +1 -0
  5. package/dist/acp-cli.d.ts +1 -1
  6. package/dist/acp-cli.js +9 -66
  7. package/dist/acp-cli.js.map +1 -1
  8. package/dist/acp.d.ts +10 -590
  9. package/dist/acp.d.ts.map +1 -1
  10. package/dist/acp.js +3 -3
  11. package/dist/{agent-D0zZnGbt.js → agent-DSLNmBp0.js} +14401 -13214
  12. package/dist/agent-DSLNmBp0.js.map +1 -0
  13. package/dist/agent.d.ts +1 -1
  14. package/dist/agent.js +3 -2
  15. package/dist/aliasing-lVFbm81n.js +217 -0
  16. package/dist/aliasing-lVFbm81n.js.map +1 -0
  17. package/dist/{anthropic-C2aole0G.js → anthropic-CD00LhUP.js} +1442 -1264
  18. package/dist/anthropic-CD00LhUP.js.map +1 -0
  19. package/dist/artifacts-CV2hUKnL.js.map +1 -1
  20. package/dist/{atomic-write-B7zr1ddF.js → atomic-write-Ijgf2Mhc.js} +13 -3
  21. package/dist/atomic-write-Ijgf2Mhc.js.map +1 -0
  22. package/dist/call-tool-dHlbGOIY.js +70 -0
  23. package/dist/call-tool-dHlbGOIY.js.map +1 -0
  24. package/dist/chat/pure.d.ts +372 -6
  25. package/dist/chat/pure.d.ts.map +1 -1
  26. package/dist/chat/pure.js +35 -244
  27. package/dist/chat/pure.js.map +1 -1
  28. package/dist/{approvals-DKaJZYlm.js → chat-JPJJ8uu5.js} +15879 -14631
  29. package/dist/chat-JPJJ8uu5.js.map +1 -0
  30. package/dist/chat.d.ts +4 -6
  31. package/dist/chat.js +23 -12
  32. package/dist/cli-shared-C-YCaglL.js +159 -0
  33. package/dist/cli-shared-C-YCaglL.js.map +1 -0
  34. package/dist/client-core-BQSyDALT.d.ts +2115 -0
  35. package/dist/client-core-BQSyDALT.d.ts.map +1 -0
  36. package/dist/codemode-worker.d.ts +1 -0
  37. package/dist/codemode-worker.js +155 -0
  38. package/dist/codemode-worker.js.map +1 -0
  39. package/dist/codemode.d.ts +352 -0
  40. package/dist/codemode.d.ts.map +1 -0
  41. package/dist/codemode.js +5 -0
  42. package/dist/connection-Dr-Js4yZ.d.ts +28 -0
  43. package/dist/connection-Dr-Js4yZ.d.ts.map +1 -0
  44. package/dist/connection-loop-gdh22LTI.js +112 -0
  45. package/dist/connection-loop-gdh22LTI.js.map +1 -0
  46. package/dist/context-segments-BHKlagL6.js +1016 -0
  47. package/dist/context-segments-BHKlagL6.js.map +1 -0
  48. package/dist/{context-transport-T_6AYIvy.js → context-transport-DSMTUsK1.js} +3 -3
  49. package/dist/{context-transport-T_6AYIvy.js.map → context-transport-DSMTUsK1.js.map} +1 -1
  50. package/dist/contexts/daytona.d.ts +19 -8
  51. package/dist/contexts/daytona.d.ts.map +1 -1
  52. package/dist/contexts/daytona.js +1 -440
  53. package/dist/contexts/docker.d.ts +36 -7
  54. package/dist/contexts/docker.d.ts.map +1 -1
  55. package/dist/contexts/docker.js +1 -456
  56. package/dist/contexts/e2b.d.ts +68 -13
  57. package/dist/contexts/e2b.d.ts.map +1 -1
  58. package/dist/contexts/e2b.js +1 -382
  59. package/dist/contexts/sandbox.d.ts +1 -1
  60. package/dist/contexts/sandbox.js +5 -0
  61. package/dist/contexts/sandbox.js.map +1 -1
  62. package/dist/{contexts-CRE9pw07.js → contexts-d7UolYot.js} +1059 -524
  63. package/dist/contexts-d7UolYot.js.map +1 -0
  64. package/dist/contexts.d.ts +3 -3
  65. package/dist/contexts.js +1 -1
  66. package/dist/cred-keys-GpBg71cP.js +105 -0
  67. package/dist/cred-keys-GpBg71cP.js.map +1 -0
  68. package/dist/{auth-BG2rVKSt.js → credentials-JttEuRON.js} +55 -206
  69. package/dist/credentials-JttEuRON.js.map +1 -0
  70. package/dist/{cursor-iojRXHF9.js → cursor-CdjaQTBW.js} +3 -3
  71. package/dist/{cursor-iojRXHF9.js.map → cursor-CdjaQTBW.js.map} +1 -1
  72. package/dist/daemon-cli.d.ts +1 -1
  73. package/dist/daemon-cli.js +3 -2
  74. package/dist/daemon-cli.js.map +1 -1
  75. package/dist/daytona-DG3omipZ.js +513 -0
  76. package/dist/daytona-DG3omipZ.js.map +1 -0
  77. package/dist/defaults-Cx3zvDOV.js +23 -0
  78. package/dist/defaults-Cx3zvDOV.js.map +1 -0
  79. package/dist/dispatcher-BMlu2BmZ.js +4941 -0
  80. package/dist/dispatcher-BMlu2BmZ.js.map +1 -0
  81. package/dist/docker-BZhMMJUp.js +727 -0
  82. package/dist/docker-BZhMMJUp.js.map +1 -0
  83. package/dist/e2b-CEVOdRib.js +480 -0
  84. package/dist/e2b-CEVOdRib.js.map +1 -0
  85. package/dist/edit-utils-EGosADZq.js +234 -0
  86. package/dist/edit-utils-EGosADZq.js.map +1 -0
  87. package/dist/environment-8wsKE2Ln.js +214 -0
  88. package/dist/environment-8wsKE2Ln.js.map +1 -0
  89. package/dist/eval-BNcvle_J.js +1595 -0
  90. package/dist/eval-BNcvle_J.js.map +1 -0
  91. package/dist/eval.d.ts +2 -2
  92. package/dist/eval.js +1 -1489
  93. package/dist/executor-Cx5-8yqi.js +621 -0
  94. package/dist/executor-Cx5-8yqi.js.map +1 -0
  95. package/dist/extension-authoring-DyGGmq9V.js +385 -0
  96. package/dist/extension-authoring-DyGGmq9V.js.map +1 -0
  97. package/dist/extensions.d.ts +2 -2
  98. package/dist/extensions.js +3 -3
  99. package/dist/{fetch-url-DX75gpL0.js → fetch-url-Ct2TjDWt.js} +4 -4
  100. package/dist/{fetch-url-DX75gpL0.js.map → fetch-url-Ct2TjDWt.js.map} +1 -1
  101. package/dist/{format-BF745pLL.js → format-C2NQEVUH.js} +39 -2
  102. package/dist/format-C2NQEVUH.js.map +1 -0
  103. package/dist/format-bytes-C7tfc3Zm.js +55 -0
  104. package/dist/format-bytes-C7tfc3Zm.js.map +1 -0
  105. package/dist/headless-C_rNUuYC.js +960 -0
  106. package/dist/headless-C_rNUuYC.js.map +1 -0
  107. package/dist/headless.d.ts +1 -1
  108. package/dist/headless.js +1 -413
  109. package/dist/{history-search-C4bk1Wz_.js → history-search-CcYzF8-0.js} +7 -3
  110. package/dist/history-search-CcYzF8-0.js.map +1 -0
  111. package/dist/history-search-worker.d.ts +1 -1
  112. package/dist/history-search-worker.js +1 -1
  113. package/dist/host-D7NrI03V.js +378 -0
  114. package/dist/host-D7NrI03V.js.map +1 -0
  115. package/dist/host-signals-BcP5QKUD.js +118 -0
  116. package/dist/host-signals-BcP5QKUD.js.map +1 -0
  117. package/dist/identity-CwfO8K-t.js +26 -0
  118. package/dist/identity-CwfO8K-t.js.map +1 -0
  119. package/dist/{index-BgN1T3lk.d.ts → index-BGe1fQT7.d.ts} +10987 -9320
  120. package/dist/index-BGe1fQT7.d.ts.map +1 -0
  121. package/dist/index-BVQsIKGn.d.ts +448 -0
  122. package/dist/index-BVQsIKGn.d.ts.map +1 -0
  123. package/dist/{index-C6NEd5y_.d.ts → index-CHABJd-L.d.ts} +3386 -502
  124. package/dist/index-CHABJd-L.d.ts.map +1 -0
  125. package/dist/{index-C37_ppWv.d.ts → index-D6a7qXpg.d.ts} +2 -3
  126. package/dist/index-D6a7qXpg.d.ts.map +1 -0
  127. package/dist/index.d.ts +6 -6
  128. package/dist/index.js +27 -23
  129. package/dist/{completion-core-BRo86R_L.js → inline-ghost-BVRQeGoL.js} +50 -2
  130. package/dist/inline-ghost-BVRQeGoL.js.map +1 -0
  131. package/dist/ipc/remote.d.ts +313 -0
  132. package/dist/ipc/remote.d.ts.map +1 -0
  133. package/dist/ipc/remote.js +4 -0
  134. package/dist/ipc.d.ts +6 -964
  135. package/dist/ipc.js +403 -4
  136. package/dist/ipc.js.map +1 -0
  137. package/dist/{isolation-Kfoof0MM.d.ts → isolation-D9L650W5.d.ts} +100 -15
  138. package/dist/isolation-D9L650W5.d.ts.map +1 -0
  139. package/dist/json-rpc--HXQti1E.js +350 -0
  140. package/dist/json-rpc--HXQti1E.js.map +1 -0
  141. package/dist/links-j8rED1sR.js +603 -0
  142. package/dist/links-j8rED1sR.js.map +1 -0
  143. package/dist/local-terminal-D8QThwo_.js +252 -0
  144. package/dist/local-terminal-D8QThwo_.js.map +1 -0
  145. package/dist/{logger--C1VQjwJ.js → logger-Gtqlayx8.js} +199 -173
  146. package/dist/logger-Gtqlayx8.js.map +1 -0
  147. package/dist/logs-view-CWG8aUAG.js.map +1 -1
  148. package/dist/{mcp-CgNd-f3F.js → mcp-_wecH9Qe.js} +608 -562
  149. package/dist/mcp-_wecH9Qe.js.map +1 -0
  150. package/dist/mcp.d.ts +2 -2
  151. package/dist/mcp.js +2 -2
  152. package/dist/media-sniff-UKijEaiQ.js.map +1 -1
  153. package/dist/{messages-2jcOA_5R.js → messages-BqiwcD2Q.js} +1017 -1014
  154. package/dist/messages-BqiwcD2Q.js.map +1 -0
  155. package/dist/{oauth-DxHO17JE.js → oauth-Px_pBB9W.js} +5 -5
  156. package/dist/{oauth-DxHO17JE.js.map → oauth-Px_pBB9W.js.map} +1 -1
  157. package/dist/{openai-compat-Dy56weK8.js → openai-compat-CFrpH7uq.js} +432 -589
  158. package/dist/openai-compat-CFrpH7uq.js.map +1 -0
  159. package/dist/orchestration-BCGmB8Ps.js +511 -0
  160. package/dist/orchestration-BCGmB8Ps.js.map +1 -0
  161. package/dist/output/stream-json.d.ts +1 -2
  162. package/dist/output/stream-json.d.ts.map +1 -1
  163. package/dist/output/stream-json.js +1 -1
  164. package/dist/output/terminal.d.ts +1 -2
  165. package/dist/output/terminal.d.ts.map +1 -1
  166. package/dist/output/terminal.js +13 -7
  167. package/dist/output/terminal.js.map +1 -1
  168. package/dist/{plugins-gbI7SBA6.js → plugins-DS7mahzZ.js} +20 -101
  169. package/dist/plugins-DS7mahzZ.js.map +1 -0
  170. package/dist/plugins.d.ts +1 -1
  171. package/dist/plugins.js +1 -2
  172. package/dist/prelude-Cxo4t74B.js +517 -0
  173. package/dist/prelude-Cxo4t74B.js.map +1 -0
  174. package/dist/presets.d.ts +1 -1
  175. package/dist/presets.js +1 -2
  176. package/dist/project-root-B8zADr4o.js +167 -0
  177. package/dist/project-root-B8zADr4o.js.map +1 -0
  178. package/dist/project-user-paths-BSgtrRxl.js +58 -0
  179. package/dist/project-user-paths-BSgtrRxl.js.map +1 -0
  180. package/dist/{prompts-Pqc3JdW_.d.ts → prompts-DYcvqShf.d.ts} +14 -11
  181. package/dist/prompts-DYcvqShf.d.ts.map +1 -0
  182. package/dist/protocol-DnhqqSmE.js +13 -0
  183. package/dist/protocol-DnhqqSmE.js.map +1 -0
  184. package/dist/providers/anthropic.d.ts +2 -2
  185. package/dist/providers/anthropic.js +1 -2
  186. package/dist/providers/arcee.d.ts +1 -1
  187. package/dist/providers/arcee.js +1 -1
  188. package/dist/providers/baseten.d.ts +1 -1
  189. package/dist/providers/baseten.js +1 -1
  190. package/dist/providers/cerebras.d.ts +1 -1
  191. package/dist/providers/cerebras.js +3 -2
  192. package/dist/providers/cerebras.js.map +1 -1
  193. package/dist/providers/local.d.ts +1 -1
  194. package/dist/providers/local.js +1 -1
  195. package/dist/providers/openai-compat.d.ts +1 -1
  196. package/dist/providers/openai-compat.js +1 -2
  197. package/dist/providers/openai.d.ts +1 -1
  198. package/dist/providers/openai.js +15 -12
  199. package/dist/providers/openai.js.map +1 -1
  200. package/dist/providers/openrouter.d.ts +1 -1
  201. package/dist/providers/openrouter.js +3 -2
  202. package/dist/providers/openrouter.js.map +1 -1
  203. package/dist/providers/xai.d.ts +1 -1
  204. package/dist/providers/xai.js +3 -3
  205. package/dist/{providers-BVhnFyiY.js → providers-XUR7F4Hi.js} +4 -4
  206. package/dist/{providers-BVhnFyiY.js.map → providers-XUR7F4Hi.js.map} +1 -1
  207. package/dist/providers.d.ts +1 -1
  208. package/dist/providers.js +4 -4
  209. package/dist/registry-D6ShBWJU.js +5234 -0
  210. package/dist/registry-D6ShBWJU.js.map +1 -0
  211. package/dist/remote-hosts-BtA_9ZGp.js +97 -0
  212. package/dist/remote-hosts-BtA_9ZGp.js.map +1 -0
  213. package/dist/restate.d.ts +2 -3
  214. package/dist/restate.d.ts.map +1 -1
  215. package/dist/restate.js +631 -572
  216. package/dist/restate.js.map +1 -1
  217. package/dist/run-summary-CklLiZ4y.js +288 -0
  218. package/dist/run-summary-CklLiZ4y.js.map +1 -0
  219. package/dist/{sandbox-BH0WQkXn.d.ts → sandbox-C5dR5DwQ.d.ts} +6 -3
  220. package/dist/sandbox-C5dR5DwQ.d.ts.map +1 -0
  221. package/dist/session/sqlite.d.ts +1 -2
  222. package/dist/session/sqlite.d.ts.map +1 -1
  223. package/dist/session/sqlite.js +1 -616
  224. package/dist/session-factory-BUGY5P40.js +1576 -0
  225. package/dist/session-factory-BUGY5P40.js.map +1 -0
  226. package/dist/{session-BqRP1hS7.js → session-xh9JXu6i.js} +94 -52
  227. package/dist/session-xh9JXu6i.js.map +1 -0
  228. package/dist/session.d.ts +1 -1
  229. package/dist/session.js +2 -2
  230. package/dist/settings-keys-Ckf29NeM.js +677 -0
  231. package/dist/settings-keys-Ckf29NeM.js.map +1 -0
  232. package/dist/shell-path-Dqaoow2J.js +412 -0
  233. package/dist/shell-path-Dqaoow2J.js.map +1 -0
  234. package/dist/{skills-BC3dodWp.js → skills-DKqZJm4X.js} +430 -470
  235. package/dist/skills-DKqZJm4X.js.map +1 -0
  236. package/dist/skills.d.ts +2 -2
  237. package/dist/skills.js +1 -2
  238. package/dist/source-mPExaUm8.js +381 -0
  239. package/dist/source-mPExaUm8.js.map +1 -0
  240. package/dist/sqlite-DV1DQzN2.js +698 -0
  241. package/dist/sqlite-DV1DQzN2.js.map +1 -0
  242. package/dist/{src-CkNQKFKr.js → src-BRMYRJib.js} +903 -1121
  243. package/dist/src-BRMYRJib.js.map +1 -0
  244. package/dist/{stdio-DPpBDPx5.js → stdio-C6YyoW3I.js} +4 -4
  245. package/dist/{stdio-DPpBDPx5.js.map → stdio-C6YyoW3I.js.map} +1 -1
  246. package/dist/{stdio-loader-Bbx19dMZ.js → stdio-loader-C830Vg-Z.js} +4 -4
  247. package/dist/{stdio-loader-Bbx19dMZ.js.map → stdio-loader-C830Vg-Z.js.map} +1 -1
  248. package/dist/system-prompt-MNVHatSD.d.ts.map +1 -1
  249. package/dist/timeout-DEaqslLO.d.ts.map +1 -1
  250. package/dist/todo-edits-Dn251YUP.js +1790 -0
  251. package/dist/todo-edits-Dn251YUP.js.map +1 -0
  252. package/dist/{turn-operations-DrbJlpq9.js → tool-formatters-IQFDrdrs.js} +1456 -2120
  253. package/dist/tool-formatters-IQFDrdrs.js.map +1 -0
  254. package/dist/tool-state-Dku6YqAB.js +989 -0
  255. package/dist/tool-state-Dku6YqAB.js.map +1 -0
  256. package/dist/tools/fetch-url.d.ts +1 -2
  257. package/dist/tools/fetch-url.d.ts.map +1 -1
  258. package/dist/tools/fetch-url.js +1 -2
  259. package/dist/tools/prompts.d.ts +1 -1
  260. package/dist/tools/prompts.js +7 -7
  261. package/dist/tools/prompts.js.map +1 -1
  262. package/dist/tools/web-search.d.ts +1 -2
  263. package/dist/tools/web-search.d.ts.map +1 -1
  264. package/dist/tools/web-search.js +2 -2
  265. package/dist/tools/web-search.js.map +1 -1
  266. package/dist/tools-_DnT3AGM.js +578 -0
  267. package/dist/tools-_DnT3AGM.js.map +1 -0
  268. package/dist/tools.d.ts +3 -3
  269. package/dist/tools.js +4 -2
  270. package/dist/tui.d.ts +534 -517
  271. package/dist/tui.d.ts.map +1 -1
  272. package/dist/tui.js +34933 -27243
  273. package/dist/tui.js.map +1 -1
  274. package/dist/turn-operations-B8sokwi5.js +333 -0
  275. package/dist/turn-operations-B8sokwi5.js.map +1 -0
  276. package/dist/turn-operations-DQX5ZZni.d.ts +3089 -0
  277. package/dist/turn-operations-DQX5ZZni.d.ts.map +1 -0
  278. package/dist/types-C7ZFOztE.d.ts +132 -0
  279. package/dist/types-C7ZFOztE.d.ts.map +1 -0
  280. package/dist/types-CH5XpIHw.js +114 -0
  281. package/dist/types-CH5XpIHw.js.map +1 -0
  282. package/dist/{types-CWgENVLY.js → types-Cu0ZGDu9.js} +10 -6
  283. package/dist/types-Cu0ZGDu9.js.map +1 -0
  284. package/dist/types-IqegbJEE.d.ts +624 -0
  285. package/dist/types-IqegbJEE.d.ts.map +1 -0
  286. package/dist/types.d.ts +4 -4
  287. package/dist/types.js +1 -1
  288. package/dist/url-D8HCl65b.js +1259 -0
  289. package/dist/url-D8HCl65b.js.map +1 -0
  290. package/dist/wasm-BVT-j5cl.js +33 -0
  291. package/dist/wasm-BVT-j5cl.js.map +1 -0
  292. package/dist/workspace-exec-stream-SV2R9d5S.js +355 -0
  293. package/dist/workspace-exec-stream-SV2R9d5S.js.map +1 -0
  294. package/dist/ws-CIHMvlSh.js +952 -0
  295. package/dist/ws-CIHMvlSh.js.map +1 -0
  296. package/dist/ws-client-N7NxzvQp.js +230 -0
  297. package/dist/ws-client-N7NxzvQp.js.map +1 -0
  298. package/dist/{xai-CazjStOK.js → xai-CvJTeLrI.js} +165 -5
  299. package/dist/xai-CvJTeLrI.js.map +1 -0
  300. package/dist/xdg-zweed0-t.js +117 -0
  301. package/dist/xdg-zweed0-t.js.map +1 -0
  302. package/docs/ACP.md +52 -0
  303. package/docs/ARCHITECTURE.md +103 -6
  304. package/docs/ARTIFACTS.md +8 -4
  305. package/docs/CHAT.md +165 -14
  306. package/docs/EXECUTION_CONTEXT.md +8 -0
  307. package/docs/EXTENSIONS.md +123 -2
  308. package/docs/HEADLESS.md +2 -0
  309. package/docs/IPC.md +775 -64
  310. package/docs/MACHINES.md +456 -0
  311. package/docs/PANELS.md +3 -0
  312. package/docs/README.md +4 -0
  313. package/docs/RESTATE.md +3 -2
  314. package/docs/RUN_IN_BACKGROUND.md +10 -2
  315. package/docs/SERVER.md +1085 -0
  316. package/docs/SKILL.md +6 -1
  317. package/docs/TERMINAL.md +75 -0
  318. package/docs/TUI.md +65 -14
  319. package/package.json +58 -26
  320. package/scripts/eval.ts +4 -0
  321. package/dist/acp-D-tVyUjw.js.map +0 -1
  322. package/dist/agent-D0zZnGbt.js.map +0 -1
  323. package/dist/anthropic-C2aole0G.js.map +0 -1
  324. package/dist/approvals-DKaJZYlm.js.map +0 -1
  325. package/dist/atomic-write-B7zr1ddF.js.map +0 -1
  326. package/dist/auth-BG2rVKSt.js.map +0 -1
  327. package/dist/completion-core-BRo86R_L.js.map +0 -1
  328. package/dist/contexts/daytona.js.map +0 -1
  329. package/dist/contexts/docker.js.map +0 -1
  330. package/dist/contexts/e2b.js.map +0 -1
  331. package/dist/contexts-CRE9pw07.js.map +0 -1
  332. package/dist/eval.js.map +0 -1
  333. package/dist/format-BF745pLL.js.map +0 -1
  334. package/dist/glob-DluQFSw1.js +0 -270
  335. package/dist/glob-DluQFSw1.js.map +0 -1
  336. package/dist/headless.js.map +0 -1
  337. package/dist/history-search-C4bk1Wz_.js.map +0 -1
  338. package/dist/index-BgN1T3lk.d.ts.map +0 -1
  339. package/dist/index-C37_ppWv.d.ts.map +0 -1
  340. package/dist/index-C6NEd5y_.d.ts.map +0 -1
  341. package/dist/ipc-BKVIIOqH.js +0 -331
  342. package/dist/ipc-BKVIIOqH.js.map +0 -1
  343. package/dist/ipc.d.ts.map +0 -1
  344. package/dist/isolation-Kfoof0MM.d.ts.map +0 -1
  345. package/dist/json-rpc-rSWMUh9b.js +0 -192
  346. package/dist/json-rpc-rSWMUh9b.js.map +0 -1
  347. package/dist/logger--C1VQjwJ.js.map +0 -1
  348. package/dist/mcp-CgNd-f3F.js.map +0 -1
  349. package/dist/messages-2jcOA_5R.js.map +0 -1
  350. package/dist/openai-compat-Dy56weK8.js.map +0 -1
  351. package/dist/output-cap-C9zukcCj.js +0 -100
  352. package/dist/output-cap-C9zukcCj.js.map +0 -1
  353. package/dist/panels-B6gRk4bf.js +0 -789
  354. package/dist/panels-B6gRk4bf.js.map +0 -1
  355. package/dist/plugins-gbI7SBA6.js.map +0 -1
  356. package/dist/prompts-Pqc3JdW_.d.ts.map +0 -1
  357. package/dist/run-summary-BU_NgwUT.js +0 -269
  358. package/dist/run-summary-BU_NgwUT.js.map +0 -1
  359. package/dist/sandbox-BH0WQkXn.d.ts.map +0 -1
  360. package/dist/session/sqlite.js.map +0 -1
  361. package/dist/session-BqRP1hS7.js.map +0 -1
  362. package/dist/shell-path-DfdXrGg9.js +0 -1549
  363. package/dist/shell-path-DfdXrGg9.js.map +0 -1
  364. package/dist/skills-BC3dodWp.js.map +0 -1
  365. package/dist/src-CkNQKFKr.js.map +0 -1
  366. package/dist/todo-edits-UOwgxGKu.js +0 -528
  367. package/dist/todo-edits-UOwgxGKu.js.map +0 -1
  368. package/dist/tool-formatters-DoRLnfuD.d.ts +0 -759
  369. package/dist/tool-formatters-DoRLnfuD.d.ts.map +0 -1
  370. package/dist/transcript-anchors-BeudMbXx.d.ts +0 -1762
  371. package/dist/transcript-anchors-BeudMbXx.d.ts.map +0 -1
  372. package/dist/turn-operations-C8bMBDl_.d.ts +0 -1258
  373. package/dist/turn-operations-C8bMBDl_.d.ts.map +0 -1
  374. package/dist/turn-operations-DrbJlpq9.js.map +0 -1
  375. package/dist/types-CWgENVLY.js.map +0 -1
  376. package/dist/xai-CazjStOK.js.map +0 -1
@@ -1,116 +1,55 @@
1
1
  import { o as AgentToolNotAllowedError, u as errorMessage } from "./errors-DKiMrOKq.js";
2
2
  import { t as escapeXml } from "./xml-D5HBhJle.js";
3
- import { basename, delimiter, dirname, join, resolve, sep } from "node:path";
3
+ import { t as projectUserPaths } from "./project-user-paths-BSgtrRxl.js";
4
4
  import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
5
+ import { basename, delimiter, dirname, join, resolve, sep } from "node:path";
5
6
  import { homedir, tmpdir } from "node:os";
6
7
  import { realpath } from "node:fs/promises";
7
8
  import { execFile } from "node:child_process";
8
- //#region src/chat/project-root.ts
9
- /**
10
- * Git root detection — walks parents from `cwd` looking for a `.git`
11
- * entry, returning the absolute path of the repo root or `null` when
12
- * the search reaches the filesystem root.
13
- *
14
- * Why parent-walk over `git rev-parse --show-toplevel`:
15
- *
16
- * - Zero dependencies (no shell-out, no git binary requirement,
17
- * works even when `git` is missing from `PATH`).
18
- * - Pure-sync, deterministic, easy to test against tmp dirs.
19
- * - Equally fast in practice — most lookups are a single `existsSync`.
20
- *
21
- * Recognizes `.git` as either:
22
- *
23
- * - A directory (standard repo layout).
24
- * - A file containing `gitdir: …` (worktrees + submodules).
25
- *
26
- * Bare repos (no working tree) are intentionally NOT detected here —
27
- * the TUI's data dir contract is "project working tree", and a bare
28
- * repo has no working files to scope sessions against.
29
- */
30
- /**
31
- * Walk parents of `cwd` looking for a `.git` entry. Returns the
32
- * absolute path of the directory containing `.git`, or `null` when no
33
- * git repo is found above `cwd`.
34
- *
35
- * Stops at the filesystem root (`/`) — no infinite loop on `dirname`'s
36
- * idempotent root case (`dirname('/')` returns `/`).
37
- */
38
- function findGitRoot(cwd = process.cwd()) {
39
- let dir = resolve(cwd);
40
- for (let depth = 0; depth < 64; depth++) {
41
- if (hasGitMarker(dir)) return dir;
42
- const parent = dirname(dir);
43
- if (parent === dir) return null;
44
- dir = parent;
45
- }
46
- return null;
47
- }
48
- /**
49
- * `.git` exists as a directory (standard repo) OR as a file containing
50
- * a `gitdir:` pointer (worktree / submodule). Tolerant on stat failures
51
- * — a permission error on a parent directory shouldn't crash the walk.
52
- */
53
- function hasGitMarker(dir) {
54
- const candidate = resolve(dir, ".git");
55
- try {
56
- if (!existsSync(candidate)) return false;
57
- const stat = statSync(candidate);
58
- return stat.isDirectory() || stat.isFile();
59
- } catch {
60
- return false;
61
- }
62
- }
63
- //#endregion
64
- //#region src/chat/project-user-paths.ts
65
- /**
66
- * Shared search-path builder for project + user config discovery.
67
- *
68
- * Both Skills (`skills/<name>/SKILL.md`) and MCP servers (`mcps.json`)
69
- * follow the same first-found-wins order:
70
- *
71
- * 1. `{project-root}/.agents/<sub>` — project, agnostic convention
72
- * 2. `{project-root}/.{prefix}/<sub>` — project, zidane (`.zidane` default)
73
- * 3. `~/.agents/<sub>` — user, agnostic
74
- * 4. `~/.{prefix}/<sub>` — user, zidane
75
- *
76
- * `{project-root}` is the git root walked up from `cwd` when present,
77
- * else `cwd` itself. This pairs with the project-scoped session
78
- * storage (`resolveStoragePaths`) so all three project-aware surfaces
79
- * — sessions, mcps, skills — anchor at the same `.{prefix}/`.
80
- *
81
- * Centralizing the pattern keeps the four paths in one place — a third
82
- * discovery surface (hooks, rules, …) gets the same convention for free
83
- * and stays in lock-step on a prefix bump.
84
- */
9
+ //#region src/skills/catalog.ts
85
10
  /**
86
- * Resolve the four search paths for a given subdirectory (`skills`,
87
- * `mcps.json`, …). Returns absolute paths regardless of whether they
88
- * exist — the caller chooses to `existsSync`-filter or feed straight
89
- * into a tolerant discovery routine.
11
+ * Build the skill catalog XML and behavioral instructions for the system prompt.
90
12
  */
91
- function projectUserPaths(opts) {
92
- const cwd = opts.cwd ?? process.cwd();
93
- const home = opts.home ?? homedir();
94
- const projectRoot = findGitRoot(cwd) ?? cwd;
95
- const prefix = (opts.prefix ?? ".zidane").replace(/^\./, "");
96
- return [
97
- {
98
- path: resolve(projectRoot, `.agents/${opts.subPath}`),
99
- source: "project"
100
- },
101
- {
102
- path: resolve(projectRoot, `.${prefix}/${opts.subPath}`),
103
- source: "project"
104
- },
105
- {
106
- path: resolve(home, `.agents/${opts.subPath}`),
107
- source: "user"
108
- },
109
- {
110
- path: resolve(home, `.${prefix}/${opts.subPath}`),
111
- source: "user"
112
- }
13
+ function buildCatalog(skills, options = {}) {
14
+ if (skills.length === 0) return "";
15
+ const skillsToolRegistered = options.skillsToolRegistered ?? true;
16
+ const readToolName = options.readToolName ?? "read_file";
17
+ const entries = skills.map((skill) => {
18
+ const locationLine = skill.location ? `\n <location>${escapeXml(skill.location)}</location>` : "";
19
+ return ` <skill>
20
+ <name>${escapeXml(skill.name)}</name>
21
+ <description>${escapeXml(skill.description)}</description>${locationLine}
22
+ </skill>`;
23
+ }).join("\n");
24
+ const hasFsSkills = skills.some((s) => s.location);
25
+ const hasInlineSkills = skills.some((s) => !s.location);
26
+ const behavioralParts = [];
27
+ behavioralParts.push("The following skills provide specialized instructions for specific tasks.", "When a task matches a skill's description, activate the skill to load its full instructions before proceeding.");
28
+ if (skillsToolRegistered) behavioralParts.push("To activate a skill, call the `skills_use` tool with the skill's name. The response contains the full instructions and any bundled resources you can then load via `skills_read` (reference files) or execute via `skills_run_script` (scripts/ directory).", "Relative paths referenced in a skill's instructions resolve against the skill directory noted in the `skills_use` response.");
29
+ else if (hasFsSkills) behavioralParts.push(`For skills with a <location>, use the ${readToolName} tool to read the SKILL.md file at that path.`, "When a skill references relative paths, resolve them against the skill's directory (the parent of SKILL.md) and use absolute paths in tool calls.");
30
+ if (hasInlineSkills && !skillsToolRegistered) behavioralParts.push("Skills without a <location> have their instructions included directly in <instructions> tags below.");
31
+ const parts = [
32
+ behavioralParts.join("\n"),
33
+ "",
34
+ "<available_skills>",
35
+ entries,
36
+ "</available_skills>"
113
37
  ];
38
+ if (hasInlineSkills && !skillsToolRegistered) {
39
+ parts.push("");
40
+ for (const skill of skills) if (!skill.location && skill.instructions) {
41
+ parts.push(`<skill_instructions name="${escapeXml(skill.name)}">`);
42
+ parts.push(skill.instructions);
43
+ if (skill.resources && skill.resources.length > 0) {
44
+ parts.push("");
45
+ parts.push("<skill_resources>");
46
+ for (const res of skill.resources) parts.push(` <file type="${res.type}">${escapeXml(res.path)}</file>`);
47
+ parts.push("</skill_resources>");
48
+ }
49
+ parts.push("</skill_instructions>");
50
+ }
51
+ }
52
+ return parts.join("\n");
114
53
  }
115
54
  //#endregion
116
55
  //#region src/skills/validate.ts
@@ -327,155 +266,9 @@ function isToolAllowedByUnion(displayName, input, union) {
327
266
  return union.some((pattern) => matchesAllowedTool(displayName, input, pattern));
328
267
  }
329
268
  //#endregion
330
- //#region src/skills/activation.ts
331
- function createSkillActivationState(options = {}) {
332
- const byName = /* @__PURE__ */ new Map();
333
- const maxActive = typeof options.maxActive === "number" && options.maxActive > 0 ? options.maxActive : void 0;
334
- return {
335
- active() {
336
- return [...byName.values()];
337
- },
338
- isActive(name) {
339
- return byName.has(name);
340
- },
341
- get(name) {
342
- return byName.get(name);
343
- },
344
- activate(skill, via) {
345
- if (byName.has(skill.name)) return "already-active";
346
- if (maxActive !== void 0 && byName.size >= maxActive) return "cap-reached";
347
- byName.set(skill.name, {
348
- skill,
349
- activatedAt: Date.now(),
350
- activatedVia: via
351
- });
352
- return "ok";
353
- },
354
- deactivate(name) {
355
- const existing = byName.get(name);
356
- if (!existing) return void 0;
357
- byName.delete(name);
358
- return existing;
359
- },
360
- clear() {
361
- const snapshot = [...byName.values()];
362
- byName.clear();
363
- return snapshot;
364
- }
365
- };
366
- }
367
- //#endregion
368
- //#region src/skills/allowed-tools.ts
369
- /** Tools that are always allowed regardless of the active skills' allow-list. */
370
- const IMPLICITLY_ALLOWED_SKILL_TOOLS = [
371
- "skills_use",
372
- "skills_read",
373
- "skills_run_script"
374
- ];
375
- /**
376
- * Register `tool:gate` / `mcp:tool:gate` handlers that enforce the union of
377
- * `allowedTools` across active skills.
378
- *
379
- * No-op when no active skill declares an allow-list (permissive default —
380
- * matches the spec's "experimental" note for `allowed-tools`).
381
- *
382
- * Returns an `uninstall` fn. The agent calls this at run end to detach the
383
- * handlers and prevent cross-run hook leaks.
384
- */
385
- function installAllowedToolsGate(hooks, state) {
386
- function effectiveUnion() {
387
- const active = state.active();
388
- const declared = [];
389
- for (const record of active) if (record.skill.allowedTools?.length) declared.push(...record.skill.allowedTools);
390
- return {
391
- union: declared.length > 0 ? [...declared, ...IMPLICITLY_ALLOWED_SKILL_TOOLS] : [],
392
- active: active.map((a) => a.skill.name)
393
- };
394
- }
395
- function gateHandler(ctx) {
396
- if (ctx.block) return;
397
- const { union, active } = effectiveUnion();
398
- if (union.length === 0) return;
399
- if (isToolAllowedByUnion(ctx.displayName, ctx.input, union)) return;
400
- const err = new AgentToolNotAllowedError({
401
- toolName: ctx.name,
402
- displayName: ctx.displayName,
403
- allowedUnion: union,
404
- activeSkills: active
405
- });
406
- ctx.block = true;
407
- ctx.reason = err.message;
408
- }
409
- function mcpGateHandler(ctx) {
410
- if (ctx.block) return;
411
- const { union, active } = effectiveUnion();
412
- if (union.length === 0) return;
413
- if (isToolAllowedByUnion(ctx.displayName, ctx.input, union)) return;
414
- const err = new AgentToolNotAllowedError({
415
- toolName: `mcp_${ctx.server}_${ctx.tool}`,
416
- displayName: ctx.displayName,
417
- allowedUnion: union,
418
- activeSkills: active
419
- });
420
- ctx.block = true;
421
- ctx.reason = err.message;
422
- }
423
- const unregisterTool = hooks.hook("tool:gate", gateHandler);
424
- const unregisterMcp = hooks.hook("mcp:tool:gate", mcpGateHandler);
425
- return function uninstall() {
426
- unregisterTool();
427
- unregisterMcp();
428
- };
429
- }
430
- //#endregion
431
- //#region src/skills/catalog.ts
432
- /**
433
- * Build the skill catalog XML and behavioral instructions for the system prompt.
434
- */
435
- function buildCatalog(skills, options = {}) {
436
- if (skills.length === 0) return "";
437
- const skillsToolRegistered = options.skillsToolRegistered ?? true;
438
- const readToolName = options.readToolName ?? "read_file";
439
- const entries = skills.map((skill) => {
440
- const locationLine = skill.location ? `\n <location>${escapeXml(skill.location)}</location>` : "";
441
- return ` <skill>
442
- <name>${escapeXml(skill.name)}</name>
443
- <description>${escapeXml(skill.description)}</description>${locationLine}
444
- </skill>`;
445
- }).join("\n");
446
- const hasFsSkills = skills.some((s) => s.location);
447
- const hasInlineSkills = skills.some((s) => !s.location);
448
- const behavioralParts = [];
449
- behavioralParts.push("The following skills provide specialized instructions for specific tasks.", "When a task matches a skill's description, activate the skill to load its full instructions before proceeding.");
450
- if (skillsToolRegistered) behavioralParts.push("To activate a skill, call the `skills_use` tool with the skill's name. The response contains the full instructions and any bundled resources you can then load via `skills_read` (reference files) or execute via `skills_run_script` (scripts/ directory).", "Relative paths referenced in a skill's instructions resolve against the skill directory noted in the `skills_use` response.");
451
- else if (hasFsSkills) behavioralParts.push(`For skills with a <location>, use the ${readToolName} tool to read the SKILL.md file at that path.`, "When a skill references relative paths, resolve them against the skill's directory (the parent of SKILL.md) and use absolute paths in tool calls.");
452
- if (hasInlineSkills && !skillsToolRegistered) behavioralParts.push("Skills without a <location> have their instructions included directly in <instructions> tags below.");
453
- const parts = [
454
- behavioralParts.join("\n"),
455
- "",
456
- "<available_skills>",
457
- entries,
458
- "</available_skills>"
459
- ];
460
- if (hasInlineSkills && !skillsToolRegistered) {
461
- parts.push("");
462
- for (const skill of skills) if (!skill.location && skill.instructions) {
463
- parts.push(`<skill_instructions name="${escapeXml(skill.name)}">`);
464
- parts.push(skill.instructions);
465
- if (skill.resources && skill.resources.length > 0) {
466
- parts.push("");
467
- parts.push("<skill_resources>");
468
- for (const res of skill.resources) parts.push(` <file type="${res.type}">${escapeXml(res.path)}</file>`);
469
- parts.push("</skill_resources>");
470
- }
471
- parts.push("</skill_instructions>");
472
- }
473
- }
474
- return parts.join("\n");
475
- }
476
- //#endregion
477
269
  //#region src/skills/discovery.ts
478
- const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
270
+ const FRONTMATTER_RE = /^---[ \t]*\r?\n(?:([\s\S]*?)\r?\n)?---\r?\n?([\s\S]*)$/;
271
+ const LEADING_BOM_AND_BLANK_RE = /^\uFEFF?(?:[ \t]*\r?\n)*/;
479
272
  const INDENT_RE = /^[ \t]{2,}/;
480
273
  const KV_RE = /^([^:]+):(.*)$/;
481
274
  const DOUBLE_QUOTED_RE = /^"((?:\\.|[^"\\])*)"$/;
@@ -543,7 +336,7 @@ function normalizeKeyAliases(frontmatter, diagnostics) {
543
336
  diagnostics.push({
544
337
  severity: "warning",
545
338
  code: "frontmatter-key-alt-spelling",
546
- message: `Frontmatter field "${alias}" is not a spec key — the spec uses "${canonical}" (kebab-case). Reading the value from "${alias}" as a courtesy; rename it to "${canonical}" to silence this warning.`,
339
+ message: `Frontmatter field "${alias}" is not a spec key; the spec uses "${canonical}" (kebab-case). Reading the value from "${alias}" as a courtesy; rename it to "${canonical}" to silence this warning.`,
547
340
  field: alias
548
341
  });
549
342
  break;
@@ -568,15 +361,18 @@ function normalizeKeyAliases(frontmatter, diagnostics) {
568
361
  */
569
362
  function parseFrontmatter(content) {
570
363
  const diagnostics = [];
571
- const match = content.match(FRONTMATTER_RE);
364
+ const blankKeys = [];
365
+ const match = content.replace(LEADING_BOM_AND_BLANK_RE, "").match(FRONTMATTER_RE);
572
366
  if (!match) return {
573
367
  frontmatter: {},
574
- body: content.trim(),
575
- diagnostics
368
+ body: content.replace(/^\uFEFF/, "").trim(),
369
+ diagnostics,
370
+ blankKeys
576
371
  };
577
- const yamlBlock = match[1];
578
- const body = match[2].trim();
372
+ const yamlBlock = match[1] ?? "";
373
+ const body = (match[2] ?? "").trim();
579
374
  const frontmatter = {};
375
+ const seenKeys = /* @__PURE__ */ new Set();
580
376
  let currentKey = null;
581
377
  let currentMap = null;
582
378
  let currentList = null;
@@ -584,20 +380,24 @@ function parseFrontmatter(content) {
584
380
  if (currentKey === null) return;
585
381
  if (currentList !== null) frontmatter[currentKey] = currentList;
586
382
  else if (currentMap !== null) frontmatter[currentKey] = currentMap;
383
+ else {
384
+ frontmatter[currentKey] = "";
385
+ blankKeys.push(currentKey);
386
+ }
587
387
  currentKey = null;
588
388
  currentMap = null;
589
389
  currentList = null;
590
390
  };
591
391
  for (const line of yamlBlock.split("\n")) {
592
392
  if (!line.trim() || line.trim().startsWith("#")) continue;
593
- if (currentKey !== null && INDENT_RE.test(line)) {
393
+ if (currentKey !== null && (INDENT_RE.test(line) || LIST_ITEM_RE.test(line.trimStart()))) {
594
394
  const stripped = line.trim();
595
395
  if (LIST_ITEM_RE.test(stripped)) {
596
396
  if (currentMap !== null && Object.keys(currentMap).length > 0) {
597
397
  diagnostics.push({
598
398
  severity: "warning",
599
399
  code: "mixed-list-and-map",
600
- message: `Frontmatter field "${currentKey}" mixes list items and map entries — keeping the map and dropping the list items.`,
400
+ message: `Frontmatter field "${currentKey}" mixes list items and map entries, keeping the map.`,
601
401
  field: currentKey
602
402
  });
603
403
  continue;
@@ -612,7 +412,7 @@ function parseFrontmatter(content) {
612
412
  diagnostics.push({
613
413
  severity: "warning",
614
414
  code: "mixed-list-and-map",
615
- message: `Frontmatter field "${currentKey}" mixes list items and map entries — keeping the list and dropping the map entries.`,
415
+ message: `Frontmatter field "${currentKey}" mixes list items and map entries, keeping the list.`,
616
416
  field: currentKey
617
417
  });
618
418
  continue;
@@ -628,6 +428,17 @@ function parseFrontmatter(content) {
628
428
  if (!kvMatch) continue;
629
429
  const key = kvMatch.key.trim();
630
430
  const rawValue = kvMatch.value.trim();
431
+ if (seenKeys.has(key)) {
432
+ diagnostics.push({
433
+ severity: "warning",
434
+ code: "duplicate-key",
435
+ message: `Frontmatter field "${key}" appears more than once; the last value wins.`,
436
+ field: key
437
+ });
438
+ const blankIdx = blankKeys.indexOf(key);
439
+ if (blankIdx >= 0) blankKeys.splice(blankIdx, 1);
440
+ }
441
+ seenKeys.add(key);
631
442
  if (!rawValue) currentKey = key;
632
443
  else frontmatter[key] = unquoteYaml(rawValue);
633
444
  }
@@ -635,7 +446,8 @@ function parseFrontmatter(content) {
635
446
  return {
636
447
  frontmatter,
637
448
  body,
638
- diagnostics
449
+ diagnostics,
450
+ blankKeys
639
451
  };
640
452
  }
641
453
  function matchFirstColon(line) {
@@ -751,7 +563,8 @@ function enumerateResources(baseDir) {
751
563
  const files = readdirSync(dirPath, { recursive: true });
752
564
  for (const file of files) {
753
565
  const rel = typeof file === "string" ? file : file.toString("utf-8");
754
- if (statSync(join(dirPath, rel)).isFile()) resources.push({
566
+ const fullPath = join(dirPath, rel);
567
+ if (statSync(fullPath).isFile()) resources.push({
755
568
  path: join(dir, rel),
756
569
  type
757
570
  });
@@ -761,7 +574,8 @@ function enumerateResources(baseDir) {
761
574
  try {
762
575
  for (const entry of readdirSync(baseDir)) {
763
576
  if (entry === "SKILL.md") continue;
764
- if (statSync(join(baseDir, entry)).isFile()) resources.push({
577
+ const entryPath = join(baseDir, entry);
578
+ if (statSync(entryPath).isFile()) resources.push({
765
579
  path: entry,
766
580
  type: "other"
767
581
  });
@@ -783,7 +597,19 @@ function enumerateResources(baseDir) {
783
597
  async function parseSkillFile(filePath, options = {}) {
784
598
  const absPath = resolve(filePath);
785
599
  if (!existsSync(absPath)) return null;
786
- const { frontmatter, body, diagnostics } = parseFrontmatter(readFileSync(absPath, "utf-8"));
600
+ return parseSkillContent(readFileSync(absPath, "utf-8"), absPath, {
601
+ ...options,
602
+ resources: enumerateResources(dirname(absPath))
603
+ });
604
+ }
605
+ /**
606
+ * {@link parseSkillFile} over content already read: a SKILL.md that is not
607
+ * on this filesystem (a machine's, `docs/MACHINES.md`). `absPath` is where it
608
+ * lives there; `resources` what its folder holds (none when unknown).
609
+ */
610
+ function parseSkillContent(content, absPath, options = {}) {
611
+ const { frontmatter, body, diagnostics, blankKeys } = parseFrontmatter(content);
612
+ for (const key of blankKeys) if (frontmatter[key] === "") delete frontmatter[key];
787
613
  normalizeKeyAliases(frontmatter, diagnostics);
788
614
  let description = takeString(frontmatter, "description", diagnostics);
789
615
  if (!description && body) {
@@ -826,7 +652,7 @@ async function parseSkillFile(filePath, options = {}) {
826
652
  source: options.source ?? "project",
827
653
  location: absPath,
828
654
  baseDir,
829
- resources: enumerateResources(baseDir)
655
+ resources: options.resources ?? []
830
656
  };
831
657
  const license = takeString(frontmatter, "license", diagnostics);
832
658
  if (license) config.license = license;
@@ -867,7 +693,7 @@ async function parseSkillFile(filePath, options = {}) {
867
693
  diagnostics.push({
868
694
  severity: "warning",
869
695
  code: "deprecated-top-level-field",
870
- message: "`paths` is not a spec field and is deprecated — moved to `metadata[\"zidane.paths\"]`.",
696
+ message: "`paths` is not a spec field and is deprecated, moved to `metadata[\"zidane.paths\"]`.",
871
697
  field: "paths"
872
698
  });
873
699
  }
@@ -877,7 +703,7 @@ async function parseSkillFile(filePath, options = {}) {
877
703
  diagnostics.push({
878
704
  severity: "warning",
879
705
  code: "deprecated-top-level-field",
880
- message: "`model` is not a spec field and is deprecated — moved to `metadata[\"zidane.model\"]`.",
706
+ message: "`model` is not a spec field and is deprecated, moved to `metadata[\"zidane.model\"]`.",
881
707
  field: "model"
882
708
  });
883
709
  }
@@ -889,7 +715,7 @@ async function parseSkillFile(filePath, options = {}) {
889
715
  diagnostics.push({
890
716
  severity: "warning",
891
717
  code: "deprecated-top-level-field",
892
- message: `\`${thinkingField ? "thinking" : "effort"}\` is not a spec field and is deprecated — moved to \`metadata["zidane.thinking"]\`.`,
718
+ message: `\`${thinkingField ? "thinking" : "effort"}\` is not a spec field and is deprecated, moved to \`metadata["zidane.thinking"]\`.`,
893
719
  field: thinkingField ? "thinking" : "effort"
894
720
  });
895
721
  }
@@ -943,52 +769,296 @@ function getDefaultScanPaths() {
943
769
  path: join(home, ".agents", "skills"),
944
770
  source: "user"
945
771
  },
946
- {
947
- path: join(home, ".zidane", "skills"),
948
- source: "user"
772
+ {
773
+ path: join(home, ".zidane", "skills"),
774
+ source: "user"
775
+ }
776
+ ];
777
+ }
778
+ /**
779
+ * Infer a source tag for a user-provided scan path.
780
+ * Paths under `$HOME` are treated as 'user'; everything else as 'project'.
781
+ */
782
+ function inferSource(path) {
783
+ return path.startsWith(homedir()) ? "user" : "project";
784
+ }
785
+ /**
786
+ * Discover skills from sourced filesystem paths.
787
+ * Each path is scanned for subdirectories containing SKILL.md.
788
+ * Earlier paths have higher priority (first-found wins on name collision).
789
+ *
790
+ * `signal` is checked between each `parseSkillFile` so a rapid `cwd` switch
791
+ * in the host (TUI / GUI) can stop a long scan without leaking I/O. Aborts
792
+ * reject with the signal's `reason` (or a default `AbortError`) — same shape
793
+ * `AbortSignal.throwIfAborted()` produces in modern runtimes.
794
+ */
795
+ async function discoverSkills(paths, signal) {
796
+ const skillsByName = /* @__PURE__ */ new Map();
797
+ for (const { path: scanPath, source } of paths) {
798
+ signal?.throwIfAborted?.();
799
+ const skillFiles = findSkillDirs(resolve(scanPath));
800
+ for (const file of skillFiles) {
801
+ signal?.throwIfAborted?.();
802
+ const skill = await parseSkillFile(file, { source });
803
+ if (!skill) continue;
804
+ if (skillsByName.has(skill.name)) {
805
+ const existing = skillsByName.get(skill.name);
806
+ const diag = {
807
+ severity: "warning",
808
+ code: "name-collision-shadowed",
809
+ message: `A skill with name "${skill.name}" was also found at ${file} (source: ${source}); shadowed by ${existing.location} (source: ${existing.source}).`
810
+ };
811
+ existing.diagnostics = [...existing.diagnostics ?? [], diag];
812
+ continue;
813
+ }
814
+ skillsByName.set(skill.name, skill);
815
+ }
816
+ }
817
+ return [...skillsByName.values()];
818
+ }
819
+ //#endregion
820
+ //#region src/skills/writer.ts
821
+ const YAML_RESERVED_RE = /[:#&*!|>%@`]/;
822
+ const YAML_EDGE_OR_QUOTE_RE = /^\s|\s$|["']/;
823
+ const DQUOTE_RE = /"/g;
824
+ const LEADING_NEWLINES_RE = /^\n+/;
825
+ function yamlEscape(value) {
826
+ if (YAML_RESERVED_RE.test(value) || YAML_EDGE_OR_QUOTE_RE.test(value) || value === "") return `"${value.replace(DQUOTE_RE, "\\\"")}"`;
827
+ return value;
828
+ }
829
+ function serializeFrontmatter(skill) {
830
+ const lines = ["---"];
831
+ lines.push(`name: ${yamlEscape(skill.name)}`);
832
+ lines.push(`description: ${yamlEscape(skill.description)}`);
833
+ if (skill.license) lines.push(`license: ${yamlEscape(skill.license)}`);
834
+ if (skill.compatibility) lines.push(`compatibility: ${yamlEscape(skill.compatibility)}`);
835
+ if (skill.allowedTools?.length) lines.push(`allowed-tools: ${skill.allowedTools.join(" ")}`);
836
+ if (skill.metadata && Object.keys(skill.metadata).length > 0) {
837
+ lines.push("metadata:");
838
+ for (const [key, value] of Object.entries(skill.metadata)) lines.push(` ${key}: "${value.replace(DQUOTE_RE, "\\\"")}"`);
839
+ }
840
+ lines.push("---");
841
+ return lines.join("\n");
842
+ }
843
+ /**
844
+ * Write a `SkillConfig` to disk as a proper skill directory with SKILL.md.
845
+ * Returns the path to the written SKILL.md file.
846
+ *
847
+ * Throws if the skill fails `validateSkillForWrite` — the authoring path is
848
+ * strict on purpose. For loading third-party skills, use `parseSkillFile`
849
+ * directly (lenient).
850
+ */
851
+ function writeSkillToDisk(skill, targetDir) {
852
+ const result = validateSkillForWrite(skill);
853
+ if (!result.valid) {
854
+ const summary = result.errors.map((e) => ` - [${e.code}] ${e.message}`).join("\n");
855
+ throw new Error(`Cannot write invalid skill "${skill.name}":\n${summary}`);
856
+ }
857
+ const skillDir = join(targetDir, skill.name);
858
+ mkdirSync(skillDir, { recursive: true });
859
+ const frontmatter = serializeFrontmatter(skill);
860
+ const bodyTrimmed = skill.instructions ? skill.instructions.replace(LEADING_NEWLINES_RE, "") : "";
861
+ const content = bodyTrimmed ? `${frontmatter}\n\n${bodyTrimmed}\n` : `${frontmatter}\n`;
862
+ const skillPath = join(skillDir, "SKILL.md");
863
+ writeFileSync(skillPath, content);
864
+ return skillPath;
865
+ }
866
+ /**
867
+ * Write multiple `SkillConfig` objects to a target directory.
868
+ * Each skill gets its own subdirectory with a SKILL.md file.
869
+ * Returns the target directory path (for use as a scan path).
870
+ */
871
+ function writeSkillsToDisk(skills, targetDir) {
872
+ mkdirSync(targetDir, { recursive: true });
873
+ for (const skill of skills) writeSkillToDisk(skill, targetDir);
874
+ return targetDir;
875
+ }
876
+ //#endregion
877
+ //#region src/skills/resolve.ts
878
+ /**
879
+ * Resolve all skills from a SkillsConfig:
880
+ *
881
+ * 1. Materialize `config.write` entries to a temp directory, tagged as `inline`
882
+ * 2. Combine with default + user-provided scan paths (project-first, user-next)
883
+ * 3. Run lenient disk discovery
884
+ * 4. Merge with any host-provided `config.catalog`. Catalog entries seed the
885
+ * resolution map FIRST, so they shadow disk-discovered skills with the
886
+ * same name (shadow diagnostic attached to the winner — same shape as
887
+ * the disk-vs-disk collision case).
888
+ * 5. Apply filters: optional project-skill trust gate, `exclude`,
889
+ * `enabled` allowlist
890
+ *
891
+ * Returns `{ skills, cleanup }` — call `cleanup()` on agent destroy to remove
892
+ * the temp directory created for inline `config.write` skills. The agent
893
+ * factory wires this automatically; standalone callers must invoke it
894
+ * themselves to avoid leaking OS temp.
895
+ */
896
+ async function resolveSkills(config) {
897
+ const sourcedPaths = [];
898
+ let writeDir;
899
+ if (!config.skipDefaultPaths) sourcedPaths.push(...getDefaultScanPaths());
900
+ for (const p of config.scan ?? []) sourcedPaths.push(typeof p === "string" ? {
901
+ path: p,
902
+ source: inferSource(p)
903
+ } : p);
904
+ if (config.write?.length) {
905
+ writeDir = mkdtempSync(join(tmpdir(), "zidane-skills-"));
906
+ writeSkillsToDisk(config.write, writeDir);
907
+ sourcedPaths.push({
908
+ path: writeDir,
909
+ source: "inline"
910
+ });
911
+ }
912
+ const diskSkills = await discoverSkills(sourcedPaths);
913
+ const skillsByName = /* @__PURE__ */ new Map();
914
+ for (const entry of config.catalog ?? []) {
915
+ const winner = { ...entry };
916
+ const shadowed = skillsByName.get(entry.name);
917
+ if (shadowed) {
918
+ const diag = {
919
+ severity: "warning",
920
+ code: "name-collision-shadowed",
921
+ message: `A skill with name "${entry.name}" was also supplied in the catalog by ${shadowed.location ?? "(catalog)"} (source: ${shadowed.source ?? "inline"}); shadowed by ${winner.location ?? "(catalog)"} (source: ${winner.source ?? "inline"}).`
922
+ };
923
+ winner.diagnostics = [...winner.diagnostics ?? [], diag];
924
+ }
925
+ skillsByName.set(entry.name, winner);
926
+ }
927
+ for (const skill of diskSkills) {
928
+ const existing = skillsByName.get(skill.name);
929
+ if (existing) {
930
+ const loserLoc = skill.location ?? "(unknown)";
931
+ const winnerLoc = existing.location ?? "(catalog)";
932
+ const diag = {
933
+ severity: "warning",
934
+ code: "name-collision-shadowed",
935
+ message: `A skill with name "${skill.name}" was also found at ${loserLoc} (source: ${skill.source ?? "project"}); shadowed by ${winnerLoc} (source: ${existing.source ?? "inline"}).`
936
+ };
937
+ existing.diagnostics = [...existing.diagnostics ?? [], diag];
938
+ continue;
939
+ }
940
+ skillsByName.set(skill.name, skill);
941
+ }
942
+ let skills = [...skillsByName.values()];
943
+ if (config.trustProjectSkills === false) skills = skills.filter((s) => s.source !== void 0 && s.source !== "project");
944
+ const exclude = new Set(config.exclude ?? []);
945
+ let filtered = skills.filter((s) => !exclude.has(s.name));
946
+ if (Array.isArray(config.enabled)) {
947
+ const allowlist = new Set(config.enabled);
948
+ filtered = filtered.filter((s) => allowlist.has(s.name));
949
+ }
950
+ return {
951
+ skills: filtered,
952
+ cleanup: writeDir ? () => {
953
+ try {
954
+ rmSync(writeDir, {
955
+ recursive: true,
956
+ force: true
957
+ });
958
+ } catch {}
959
+ } : () => {}
960
+ };
961
+ }
962
+ //#endregion
963
+ //#region src/skills/activation.ts
964
+ function createSkillActivationState(options = {}) {
965
+ const byName = /* @__PURE__ */ new Map();
966
+ const maxActive = typeof options.maxActive === "number" && options.maxActive > 0 ? options.maxActive : void 0;
967
+ return {
968
+ active() {
969
+ return [...byName.values()];
970
+ },
971
+ isActive(name) {
972
+ return byName.has(name);
973
+ },
974
+ get(name) {
975
+ return byName.get(name);
976
+ },
977
+ activate(skill, via) {
978
+ if (byName.has(skill.name)) return "already-active";
979
+ if (maxActive !== void 0 && byName.size >= maxActive) return "cap-reached";
980
+ byName.set(skill.name, {
981
+ skill,
982
+ activatedAt: Date.now(),
983
+ activatedVia: via
984
+ });
985
+ return "ok";
986
+ },
987
+ deactivate(name) {
988
+ const existing = byName.get(name);
989
+ if (!existing) return void 0;
990
+ byName.delete(name);
991
+ return existing;
992
+ },
993
+ clear() {
994
+ const snapshot = [...byName.values()];
995
+ byName.clear();
996
+ return snapshot;
949
997
  }
950
- ];
951
- }
952
- /**
953
- * Infer a source tag for a user-provided scan path.
954
- * Paths under `$HOME` are treated as 'user'; everything else as 'project'.
955
- */
956
- function inferSource(path) {
957
- return path.startsWith(homedir()) ? "user" : "project";
998
+ };
958
999
  }
1000
+ //#endregion
1001
+ //#region src/skills/allowed-tools.ts
1002
+ /** Tools that are always allowed regardless of the active skills' allow-list. */
1003
+ const IMPLICITLY_ALLOWED_SKILL_TOOLS = [
1004
+ "skills_use",
1005
+ "skills_read",
1006
+ "skills_run_script"
1007
+ ];
959
1008
  /**
960
- * Discover skills from sourced filesystem paths.
961
- * Each path is scanned for subdirectories containing SKILL.md.
962
- * Earlier paths have higher priority (first-found wins on name collision).
1009
+ * Register `tool:gate` / `mcp:tool:gate` handlers that enforce the union of
1010
+ * `allowedTools` across active skills.
963
1011
  *
964
- * `signal` is checked between each `parseSkillFile` so a rapid `cwd` switch
965
- * in the host (TUI / GUI) can stop a long scan without leaking I/O. Aborts
966
- * reject with the signal's `reason` (or a default `AbortError`) — same shape
967
- * `AbortSignal.throwIfAborted()` produces in modern runtimes.
1012
+ * No-op when no active skill declares an allow-list (permissive default —
1013
+ * matches the spec's "experimental" note for `allowed-tools`).
1014
+ *
1015
+ * Returns an `uninstall` fn. The agent calls this at run end to detach the
1016
+ * handlers and prevent cross-run hook leaks.
968
1017
  */
969
- async function discoverSkills(paths, signal) {
970
- const skillsByName = /* @__PURE__ */ new Map();
971
- for (const { path: scanPath, source } of paths) {
972
- signal?.throwIfAborted?.();
973
- const skillFiles = findSkillDirs(resolve(scanPath));
974
- for (const file of skillFiles) {
975
- signal?.throwIfAborted?.();
976
- const skill = await parseSkillFile(file, { source });
977
- if (!skill) continue;
978
- if (skillsByName.has(skill.name)) {
979
- const existing = skillsByName.get(skill.name);
980
- const diag = {
981
- severity: "warning",
982
- code: "name-collision-shadowed",
983
- message: `A skill with name "${skill.name}" was also found at ${file} (source: ${source}); shadowed by ${existing.location} (source: ${existing.source}).`
984
- };
985
- existing.diagnostics = [...existing.diagnostics ?? [], diag];
986
- continue;
987
- }
988
- skillsByName.set(skill.name, skill);
989
- }
1018
+ function installAllowedToolsGate(hooks, state) {
1019
+ function effectiveUnion() {
1020
+ const active = state.active();
1021
+ const declared = [];
1022
+ for (const record of active) if (record.skill.allowedTools?.length) declared.push(...record.skill.allowedTools);
1023
+ return {
1024
+ union: declared.length > 0 ? [...declared, ...IMPLICITLY_ALLOWED_SKILL_TOOLS] : [],
1025
+ active: active.map((a) => a.skill.name)
1026
+ };
990
1027
  }
991
- return [...skillsByName.values()];
1028
+ function gateHandler(ctx) {
1029
+ if (ctx.block) return;
1030
+ const { union, active } = effectiveUnion();
1031
+ if (union.length === 0) return;
1032
+ if (isToolAllowedByUnion(ctx.displayName, ctx.input, union)) return;
1033
+ const err = new AgentToolNotAllowedError({
1034
+ toolName: ctx.name,
1035
+ displayName: ctx.displayName,
1036
+ allowedUnion: union,
1037
+ activeSkills: active
1038
+ });
1039
+ ctx.block = true;
1040
+ ctx.reason = err.message;
1041
+ }
1042
+ function mcpGateHandler(ctx) {
1043
+ if (ctx.block) return;
1044
+ const { union, active } = effectiveUnion();
1045
+ if (union.length === 0) return;
1046
+ if (isToolAllowedByUnion(ctx.displayName, ctx.input, union)) return;
1047
+ const err = new AgentToolNotAllowedError({
1048
+ toolName: `mcp_${ctx.server}_${ctx.tool}`,
1049
+ displayName: ctx.displayName,
1050
+ allowedUnion: union,
1051
+ activeSkills: active
1052
+ });
1053
+ ctx.block = true;
1054
+ ctx.reason = err.message;
1055
+ }
1056
+ const unregisterTool = hooks.hook("tool:gate", gateHandler);
1057
+ const unregisterMcp = hooks.hook("mcp:tool:gate", mcpGateHandler);
1058
+ return function uninstall() {
1059
+ unregisterTool();
1060
+ unregisterMcp();
1061
+ };
992
1062
  }
993
1063
  //#endregion
994
1064
  //#region src/install/source.ts
@@ -1032,6 +1102,38 @@ var InstallSourceError = class extends Error {
1032
1102
  if (candidates) this.candidates = candidates;
1033
1103
  }
1034
1104
  };
1105
+ /**
1106
+ * `io` stopped by `signal` (a bounded install, `catalog.install` on a
1107
+ * server): every exec gets it (the child is killed), every fetch too, and
1108
+ * once it fired no further step starts and the current one throws, so the
1109
+ * installer's own rollback (staging dir, transactional target) runs.
1110
+ */
1111
+ function abortableInstallIo(io, signal) {
1112
+ const exec = io?.exec ?? defaultExec;
1113
+ const fetchImpl = io?.fetch ?? fetch;
1114
+ const stopped = () => {
1115
+ if (signal.aborted) throw new InstallSourceError("fetch-failed", "The install was stopped.");
1116
+ };
1117
+ return {
1118
+ ...io,
1119
+ exec: async (cmd, args, opts) => {
1120
+ stopped();
1121
+ const result = await exec(cmd, args, {
1122
+ ...opts,
1123
+ signal
1124
+ });
1125
+ stopped();
1126
+ return result;
1127
+ },
1128
+ fetch: (async (input, init) => {
1129
+ stopped();
1130
+ return fetchImpl(input, {
1131
+ ...init,
1132
+ signal
1133
+ });
1134
+ })
1135
+ };
1136
+ }
1035
1137
  const GITHUB_TREE_URL_RE = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([^/\s]+)\/([^/\s#@:]+?)(?:\.git)?\/tree\/([^/\s:]+)(?:\/([^\s:]+?))?\/?$/;
1036
1138
  const GITHUB_URL_RE = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([^/\s]+)\/([^/\s#@:]+?)(?:\.git)?\/?$/;
1037
1139
  const OWNER_REPO_RE = /^([\w.-]+)\/([\w.-]+)$/;
@@ -1041,7 +1143,7 @@ const NPM_NAME_RE = /^(@[\w.-]+\/[\w.-]+|[\w.-]+)(?:@([\w.^~><=*-]+))?$/;
1041
1143
  function parseSubdir(rawSubdir, raw) {
1042
1144
  const subdir = rawSubdir.replace(/^\.\//, "").replace(/\/+$/, "");
1043
1145
  const segments = subdir.split("/");
1044
- if (subdir.length === 0 || subdir.startsWith("/") || subdir.includes("\\") || segments.some((s) => s.length === 0 || s === "." || s === "..")) throw new InstallSourceError("invalid-source", `"${raw}" has an invalid subdirectory — expected a repo-relative path (no leading /, no ..).`);
1146
+ if (subdir.length === 0 || subdir.startsWith("/") || subdir.includes("\\") || segments.some((s) => s.length === 0 || s === "." || s === "..")) throw new InstallSourceError("invalid-source", `"${raw}" has an invalid subdirectory. Use a repo-relative path (no leading /, no ..).`);
1045
1147
  return subdir;
1046
1148
  }
1047
1149
  /**
@@ -1148,7 +1250,7 @@ async function fetchInstallSource(source, destDir, io = {}, onProgress) {
1148
1250
  force: true
1149
1251
  });
1150
1252
  mkdirSync(destDir, { recursive: true });
1151
- onProgress?.(`anonymous clone rejected — retrying via SSH (git@github.com:${source.owner}/${source.repo})…`);
1253
+ onProgress?.(`anonymous clone rejected, retrying over SSH (git@github.com:${source.owner}/${source.repo})…`);
1152
1254
  const ssh = await exec("git", cloneArgs(`git@github.com:${source.owner}/${source.repo}.git`), { env: {
1153
1255
  GIT_TERMINAL_PROMPT: "0",
1154
1256
  GIT_SSH_COMMAND: sshCommandWithBatchMode(process.env.GIT_SSH_COMMAND)
@@ -1205,7 +1307,7 @@ async function resolveNpmVersion(source, fetchImpl) {
1205
1307
  }
1206
1308
  const version = source.version && meta.versions?.[source.version] ? source.version : meta["dist-tags"]?.[source.version ?? "latest"];
1207
1309
  const tarball = version ? meta.versions?.[version]?.dist?.tarball : void 0;
1208
- if (!version || !tarball) throw new InstallSourceError("fetch-failed", `npm package "${source.pkg}" has no ${source.version ? `version/tag "${source.version}"` : "latest version"} (exact versions and dist-tags only — ranges are not resolved).`);
1310
+ if (!version || !tarball) throw new InstallSourceError("fetch-failed", `npm package "${source.pkg}" has no ${source.version ? `version/tag "${source.version}"` : "latest version"} (only exact versions and dist-tags, not ranges).`);
1209
1311
  return {
1210
1312
  version,
1211
1313
  tarball
@@ -1216,16 +1318,16 @@ async function resolveNpmVersion(source, fetchImpl) {
1216
1318
  * unexpectedly huge (or hostile) URL must fail fast instead of ballooning
1217
1319
  * RSS. Install sources are source trees — 64MB is already generous.
1218
1320
  */
1219
- const MAX_DOWNLOAD_BYTES = 64 * 1024 * 1024;
1321
+ const MAX_DOWNLOAD_BYTES = 67108864;
1220
1322
  async function downloadAndExtract(url, destDir, opts) {
1221
1323
  let bytes;
1222
1324
  try {
1223
1325
  const res = await opts.fetchImpl(url);
1224
1326
  if (!res.ok) throw new Error(`download returned ${res.status}`);
1225
1327
  const declared = Number(res.headers.get("content-length") ?? "0");
1226
- if (declared > 67108864) throw new Error(`archive is ${Math.round(declared / 1024 / 1024)}MB — larger than the ${MAX_DOWNLOAD_BYTES / 1024 / 1024}MB limit`);
1328
+ if (declared > 67108864) throw new Error(`archive is ${Math.round(declared / 1024 / 1024)}MB, over the ${MAX_DOWNLOAD_BYTES / 1024 / 1024}MB limit`);
1227
1329
  bytes = await res.arrayBuffer();
1228
- if (bytes.byteLength > 67108864) throw new Error(`archive is ${Math.round(bytes.byteLength / 1024 / 1024)}MB — larger than the ${MAX_DOWNLOAD_BYTES / 1024 / 1024}MB limit`);
1330
+ if (bytes.byteLength > 67108864) throw new Error(`archive is ${Math.round(bytes.byteLength / 1024 / 1024)}MB, over the ${MAX_DOWNLOAD_BYTES / 1024 / 1024}MB limit`);
1229
1331
  } catch (err) {
1230
1332
  throw new InstallSourceError("fetch-failed", `Download failed for ${opts.what}: ${errorText(err)}`);
1231
1333
  }
@@ -1291,7 +1393,8 @@ function defaultExec(cmd, args, opts) {
1291
1393
  execFile(cmd, [...args], {
1292
1394
  cwd: opts.cwd,
1293
1395
  env,
1294
- maxBuffer: 16 * 1024 * 1024
1396
+ maxBuffer: 16777216,
1397
+ ...opts.signal ? { signal: opts.signal } : {}
1295
1398
  }, (err, stdout, stderr) => {
1296
1399
  resolvePromise({
1297
1400
  ok: !err,
@@ -1438,7 +1541,7 @@ function parseCliAddCommand(rest) {
1438
1541
  if (token.startsWith("-")) continue;
1439
1542
  if (sourceToken === void 0) sourceToken = token;
1440
1543
  }
1441
- if (sourceToken === void 0) throw new SkillInstallError("invalid-source", "Pasted skills command has no source — expected “skills add <owner/repo>”.");
1544
+ if (sourceToken === void 0) throw new SkillInstallError("invalid-source", "The pasted command has no source. Expected “skills add <owner/repo>”.");
1442
1545
  const parsed = parseSkillInstallSpec(sourceToken);
1443
1546
  return {
1444
1547
  ...parsed,
@@ -1598,14 +1701,14 @@ async function installNatively(spec, skillSelector, opts) {
1598
1701
  fallbackName = basename(resolve(fetchedInto, source.subdir));
1599
1702
  }
1600
1703
  const candidates = listSkillCandidates(candidateRoot, fallbackName);
1601
- if (candidates.length === 0) throw new SkillInstallError("no-skill", `No skill found in ${opts.source} — no SKILL.md anywhere in the tree (searched ${CANDIDATE_WALK_MAX_DEPTH} levels deep).`);
1704
+ if (candidates.length === 0) throw new SkillInstallError("no-skill", `No skill found in ${opts.source}: no SKILL.md in the tree (searched ${CANDIDATE_WALK_MAX_DEPTH} levels deep).`);
1602
1705
  let picked;
1603
1706
  if (!skillSelector && opts.skills && opts.skills.length > 0) {
1604
1707
  const byRel = /* @__PURE__ */ new Map();
1605
1708
  for (const sel of opts.skills) {
1606
1709
  const matches = candidates.filter((c) => c.name === sel || c.rel === sel);
1607
1710
  if (matches.length === 0) throw new SkillInstallError("no-skill", `No skill named "${sel}" in ${opts.source}. Available: ${describeCandidates(candidates)}`, candidates.map((c) => c.rel));
1608
- if (matches.length > 1) throw new SkillInstallError("multiple-candidates", `"${sel}" is ambiguous in ${opts.source} — pick a path: ${matches.map((c) => c.rel).join(", ")}`, matches.map((c) => c.rel));
1711
+ if (matches.length > 1) throw new SkillInstallError("multiple-candidates", `"${sel}" matches several skills in ${opts.source}. Pick a path: ${matches.map((c) => c.rel).join(", ")}`, matches.map((c) => c.rel));
1609
1712
  const match = matches[0];
1610
1713
  byRel.set(match.rel, match);
1611
1714
  }
@@ -1613,9 +1716,9 @@ async function installNatively(spec, skillSelector, opts) {
1613
1716
  } else if (skillSelector) {
1614
1717
  const matches = candidates.filter((c) => c.name === skillSelector || c.rel === skillSelector);
1615
1718
  if (matches.length === 0) throw new SkillInstallError("no-skill", `No skill named "${skillSelector}" in ${opts.source}. Available: ${describeCandidates(candidates)}`, candidates.map((c) => c.rel));
1616
- if (matches.length > 1) throw new SkillInstallError("multiple-candidates", `"${skillSelector}" is ambiguous in ${opts.source} — pick a path: ${matches.map((c) => c.rel).join(", ")}`, matches.map((c) => c.rel));
1719
+ if (matches.length > 1) throw new SkillInstallError("multiple-candidates", `"${skillSelector}" matches several skills in ${opts.source}. Pick a path: ${matches.map((c) => c.rel).join(", ")}`, matches.map((c) => c.rel));
1617
1720
  picked = [matches[0]];
1618
- } else if (candidates.length > 1 && !opts.all) throw new SkillInstallError("multiple-candidates", `${opts.source} contains ${candidates.length} skills — pick one: ${describeCandidates(candidates)}`, candidates.map((c) => c.rel), await candidateDetails(candidates));
1721
+ } else if (candidates.length > 1 && !opts.all) throw new SkillInstallError("multiple-candidates", `${opts.source} contains ${candidates.length} skills. Pick one: ${describeCandidates(candidates)}`, candidates.map((c) => c.rel), await candidateDetails(candidates));
1619
1722
  else picked = [...candidates];
1620
1723
  const root = scopeRoot(opts);
1621
1724
  const installed = [];
@@ -1630,7 +1733,7 @@ async function installNatively(spec, skillSelector, opts) {
1630
1733
  const conflict = nameToRel.get(name);
1631
1734
  if (conflict !== void 0) {
1632
1735
  failed.push(candidate.rel);
1633
- opts.onProgress?.(`skipping "${candidate.rel}" — name "${name}" conflicts with "${conflict}"`);
1736
+ opts.onProgress?.(`skipped "${candidate.rel}": the name "${name}" conflicts with "${conflict}"`);
1634
1737
  continue;
1635
1738
  }
1636
1739
  nameToRel.set(name, candidate.rel);
@@ -1641,7 +1744,7 @@ async function installNatively(spec, skillSelector, opts) {
1641
1744
  } catch (err) {
1642
1745
  if (!batch) throw err;
1643
1746
  failed.push(candidate.rel);
1644
- opts.onProgress?.(`skipping "${candidate.rel}" — ${errorMessage(err)}`);
1747
+ opts.onProgress?.(`skipped "${candidate.rel}": ${errorMessage(err)}`);
1645
1748
  }
1646
1749
  for (const { candidate, name } of resolved) {
1647
1750
  const targetDir = join(root, name);
@@ -1649,7 +1752,7 @@ async function installNatively(spec, skillSelector, opts) {
1649
1752
  if (existsSync(targetDir) && !opts.overwrite) {
1650
1753
  if (batch) {
1651
1754
  skipped.push(name);
1652
- opts.onProgress?.(`skipping "${name}" — already installed`);
1755
+ opts.onProgress?.(`skipped "${name}": already installed`);
1653
1756
  continue;
1654
1757
  }
1655
1758
  throw new SkillInstallError("exists", `Skill "${name}" is already installed at ${targetDir}. Overwrite to replace it.`);
@@ -1692,7 +1795,7 @@ async function installNatively(spec, skillSelector, opts) {
1692
1795
  if (batch) opts.onProgress?.(`installed "${name}" (${installed.length}/${resolved.length})`);
1693
1796
  }
1694
1797
  if (installed.length === 0 && skipped.length > 0) throw new SkillInstallError("exists", `All ${skipped.length} installable skills from ${opts.source} are already installed: ${skipped.join(", ")}. Overwrite to replace them.`);
1695
- if (installed.length === 0 && failed.length > 0) throw new SkillInstallError("validate-failed", `No skill from ${opts.source} was installable — invalid: ${failed.join(", ")}.`);
1798
+ if (installed.length === 0 && failed.length > 0) throw new SkillInstallError("validate-failed", `No skill from ${opts.source} could be installed. Invalid: ${failed.join(", ")}.`);
1696
1799
  return {
1697
1800
  installed,
1698
1801
  ...skipped.length > 0 ? { skipped } : {},
@@ -1829,149 +1932,6 @@ async function interpolateShellCommands(instructions, execution, handle, options
1829
1932
  return result;
1830
1933
  }
1831
1934
  //#endregion
1832
- //#region src/skills/writer.ts
1833
- const YAML_RESERVED_RE = /[:#&*!|>%@`]/;
1834
- const YAML_EDGE_OR_QUOTE_RE = /^\s|\s$|["']/;
1835
- const DQUOTE_RE = /"/g;
1836
- const LEADING_NEWLINES_RE = /^\n+/;
1837
- function yamlEscape(value) {
1838
- if (YAML_RESERVED_RE.test(value) || YAML_EDGE_OR_QUOTE_RE.test(value) || value === "") return `"${value.replace(DQUOTE_RE, "\\\"")}"`;
1839
- return value;
1840
- }
1841
- function serializeFrontmatter(skill) {
1842
- const lines = ["---"];
1843
- lines.push(`name: ${yamlEscape(skill.name)}`);
1844
- lines.push(`description: ${yamlEscape(skill.description)}`);
1845
- if (skill.license) lines.push(`license: ${yamlEscape(skill.license)}`);
1846
- if (skill.compatibility) lines.push(`compatibility: ${yamlEscape(skill.compatibility)}`);
1847
- if (skill.allowedTools?.length) lines.push(`allowed-tools: ${skill.allowedTools.join(" ")}`);
1848
- if (skill.metadata && Object.keys(skill.metadata).length > 0) {
1849
- lines.push("metadata:");
1850
- for (const [key, value] of Object.entries(skill.metadata)) lines.push(` ${key}: "${value.replace(DQUOTE_RE, "\\\"")}"`);
1851
- }
1852
- lines.push("---");
1853
- return lines.join("\n");
1854
- }
1855
- /**
1856
- * Write a `SkillConfig` to disk as a proper skill directory with SKILL.md.
1857
- * Returns the path to the written SKILL.md file.
1858
- *
1859
- * Throws if the skill fails `validateSkillForWrite` — the authoring path is
1860
- * strict on purpose. For loading third-party skills, use `parseSkillFile`
1861
- * directly (lenient).
1862
- */
1863
- function writeSkillToDisk(skill, targetDir) {
1864
- const result = validateSkillForWrite(skill);
1865
- if (!result.valid) {
1866
- const summary = result.errors.map((e) => ` - [${e.code}] ${e.message}`).join("\n");
1867
- throw new Error(`Cannot write invalid skill "${skill.name}":\n${summary}`);
1868
- }
1869
- const skillDir = join(targetDir, skill.name);
1870
- mkdirSync(skillDir, { recursive: true });
1871
- const frontmatter = serializeFrontmatter(skill);
1872
- const bodyTrimmed = skill.instructions ? skill.instructions.replace(LEADING_NEWLINES_RE, "") : "";
1873
- const content = bodyTrimmed ? `${frontmatter}\n\n${bodyTrimmed}\n` : `${frontmatter}\n`;
1874
- const skillPath = join(skillDir, "SKILL.md");
1875
- writeFileSync(skillPath, content);
1876
- return skillPath;
1877
- }
1878
- /**
1879
- * Write multiple `SkillConfig` objects to a target directory.
1880
- * Each skill gets its own subdirectory with a SKILL.md file.
1881
- * Returns the target directory path (for use as a scan path).
1882
- */
1883
- function writeSkillsToDisk(skills, targetDir) {
1884
- mkdirSync(targetDir, { recursive: true });
1885
- for (const skill of skills) writeSkillToDisk(skill, targetDir);
1886
- return targetDir;
1887
- }
1888
- //#endregion
1889
- //#region src/skills/resolve.ts
1890
- /**
1891
- * Resolve all skills from a SkillsConfig:
1892
- *
1893
- * 1. Materialize `config.write` entries to a temp directory, tagged as `inline`
1894
- * 2. Combine with default + user-provided scan paths (project-first, user-next)
1895
- * 3. Run lenient disk discovery
1896
- * 4. Merge with any host-provided `config.catalog`. Catalog entries seed the
1897
- * resolution map FIRST, so they shadow disk-discovered skills with the
1898
- * same name (shadow diagnostic attached to the winner — same shape as
1899
- * the disk-vs-disk collision case).
1900
- * 5. Apply filters: optional project-skill trust gate, `exclude`,
1901
- * `enabled` allowlist
1902
- *
1903
- * Returns `{ skills, cleanup }` — call `cleanup()` on agent destroy to remove
1904
- * the temp directory created for inline `config.write` skills. The agent
1905
- * factory wires this automatically; standalone callers must invoke it
1906
- * themselves to avoid leaking OS temp.
1907
- */
1908
- async function resolveSkills(config) {
1909
- const sourcedPaths = [];
1910
- let writeDir;
1911
- if (!config.skipDefaultPaths) sourcedPaths.push(...getDefaultScanPaths());
1912
- for (const p of config.scan ?? []) sourcedPaths.push({
1913
- path: p,
1914
- source: inferSource(p)
1915
- });
1916
- if (config.write?.length) {
1917
- writeDir = mkdtempSync(join(tmpdir(), "zidane-skills-"));
1918
- writeSkillsToDisk(config.write, writeDir);
1919
- sourcedPaths.push({
1920
- path: writeDir,
1921
- source: "inline"
1922
- });
1923
- }
1924
- const diskSkills = await discoverSkills(sourcedPaths);
1925
- const skillsByName = /* @__PURE__ */ new Map();
1926
- for (const entry of config.catalog ?? []) {
1927
- const winner = { ...entry };
1928
- const shadowed = skillsByName.get(entry.name);
1929
- if (shadowed) {
1930
- const diag = {
1931
- severity: "warning",
1932
- code: "name-collision-shadowed",
1933
- message: `A skill with name "${entry.name}" was also supplied in the catalog by ${shadowed.location ?? "(catalog)"} (source: ${shadowed.source ?? "inline"}); shadowed by ${winner.location ?? "(catalog)"} (source: ${winner.source ?? "inline"}).`
1934
- };
1935
- winner.diagnostics = [...winner.diagnostics ?? [], diag];
1936
- }
1937
- skillsByName.set(entry.name, winner);
1938
- }
1939
- for (const skill of diskSkills) {
1940
- const existing = skillsByName.get(skill.name);
1941
- if (existing) {
1942
- const loserLoc = skill.location ?? "(unknown)";
1943
- const winnerLoc = existing.location ?? "(catalog)";
1944
- const diag = {
1945
- severity: "warning",
1946
- code: "name-collision-shadowed",
1947
- message: `A skill with name "${skill.name}" was also found at ${loserLoc} (source: ${skill.source ?? "project"}); shadowed by ${winnerLoc} (source: ${existing.source ?? "inline"}).`
1948
- };
1949
- existing.diagnostics = [...existing.diagnostics ?? [], diag];
1950
- continue;
1951
- }
1952
- skillsByName.set(skill.name, skill);
1953
- }
1954
- let skills = [...skillsByName.values()];
1955
- if (config.trustProjectSkills === false) skills = skills.filter((s) => s.source !== void 0 && s.source !== "project");
1956
- const exclude = new Set(config.exclude ?? []);
1957
- let filtered = skills.filter((s) => !exclude.has(s.name));
1958
- if (Array.isArray(config.enabled)) {
1959
- const allowlist = new Set(config.enabled);
1960
- filtered = filtered.filter((s) => allowlist.has(s.name));
1961
- }
1962
- return {
1963
- skills: filtered,
1964
- cleanup: writeDir ? () => {
1965
- try {
1966
- rmSync(writeDir, {
1967
- recursive: true,
1968
- force: true
1969
- });
1970
- } catch {}
1971
- } : () => {}
1972
- };
1973
- }
1974
- //#endregion
1975
1935
  //#region src/skills/index.ts
1976
1936
  /**
1977
1937
  * Define an inline skill configuration.
@@ -1992,6 +1952,6 @@ function defineSkill(config) {
1992
1952
  return normalized;
1993
1953
  }
1994
1954
  //#endregion
1995
- export { isToolAllowedByUnion as A, inferSource as C, IMPLICITLY_ALLOWED_SKILL_TOOLS as D, buildCatalog as E, validateSkillForWrite as F, validateSkillName as I, projectUserPaths as L, parseAllowedToolPattern as M, validateResourcePath as N, installAllowedToolsGate as O, validateResourcePathReal as P, findGitRoot as R, getDefaultScanPaths as S, parseSkillFile as T, lastLine as _, interpolateShellCommands as a, resolveSubdirRoot as b, SkillInstallError as c, parseSkillInstallSpec as d, readSkillProvenance as f, fetchInstallSource as g, defaultWhich as h, writeSkillsToDisk as i, matchesAllowedTool as j, createSkillActivationState as k, installSkillFromSpec as l, defaultExec as m, resolveSkills as n, stripShellInterpolations as o, InstallSourceError as p, writeSkillToDisk as r, SKILL_PROVENANCE_FILE as s, defineSkill as t, listSkillCandidates as u, parseInstallSource as v, parseFrontmatter as w, discoverSkills as x, resolveNpmVersion as y };
1955
+ export { parseSkillFile as A, writeSkillToDisk as C, inferSource as D, getDefaultScanPaths as E, validateResourcePathReal as F, validateSkillForWrite as I, validateSkillName as L, matchesAllowedTool as M, parseAllowedToolPattern as N, parseFrontmatter as O, validateResourcePath as P, buildCatalog as R, resolveSkills as S, discoverSkills as T, resolveNpmVersion as _, SkillInstallError as a, installAllowedToolsGate as b, parseSkillInstallSpec as c, abortableInstallIo as d, defaultExec as f, parseInstallSource as g, lastLine as h, SKILL_PROVENANCE_FILE as i, isToolAllowedByUnion as j, parseSkillContent as k, readSkillProvenance as l, fetchInstallSource as m, interpolateShellCommands as n, installSkillFromSpec as o, defaultWhich as p, stripShellInterpolations as r, listSkillCandidates as s, defineSkill as t, InstallSourceError as u, resolveSubdirRoot as v, writeSkillsToDisk as w, createSkillActivationState as x, IMPLICITLY_ALLOWED_SKILL_TOOLS as y };
1996
1956
 
1997
- //# sourceMappingURL=skills-BC3dodWp.js.map
1957
+ //# sourceMappingURL=skills-DKqZJm4X.js.map