shadow-claw 1.26.0 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/.agents/skills/main/skill-creator/SKILL.md +39 -0
  2. package/.agents/skills/main/skill-creator/references/declarative-pipelines.md +44 -0
  3. package/.agents/skills/main/skill-creator/references/declarative-tools.md +35 -0
  4. package/.agents/skills/main/skill-creator/references/frontmatter-spec.md +50 -0
  5. package/.well-known/agent-skills/index.json +14 -0
  6. package/README.md +5 -3
  7. package/bin/build/build.mjs +146 -15
  8. package/bin/build/build.test.mjs +57 -0
  9. package/bin/cli.mjs +26 -0
  10. package/bin/commands/mcp.mjs +277 -55
  11. package/bin/commands/mcp.test.mjs +275 -1
  12. package/bin/commands/skills-index.mjs +328 -0
  13. package/bin/commands/skills-index.test.mjs +161 -0
  14. package/bin/site-config/utils/custom-elements/get-approved-custom-element-scripts.test.mjs +2 -0
  15. package/bin/site-config/utils/custom-elements/sanitize-embedded-custom-element-scripts.test.mjs +10 -1
  16. package/dist/lib/components/index.js +25 -25
  17. package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
  18. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  19. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  20. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  21. package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
  22. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  23. package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
  24. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  25. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  26. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  27. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  28. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  29. package/dist/lib/components/shadow-claw-a2ui.js +1 -1
  30. package/dist/lib/components/shadow-claw-accounts.js +1 -1
  31. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  32. package/dist/lib/components/shadow-claw-channels.js +1 -1
  33. package/dist/lib/components/shadow-claw-chat.js +1 -1
  34. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  35. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  36. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  37. package/dist/lib/components/shadow-claw-files.js +1 -1
  38. package/dist/lib/components/shadow-claw-git.js +1 -1
  39. package/dist/lib/components/shadow-claw-integrations.js +1 -1
  40. package/dist/lib/components/shadow-claw-llm.js +1 -1
  41. package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
  42. package/dist/lib/components/shadow-claw-networking.js +1 -1
  43. package/dist/lib/components/shadow-claw-page-header.js +1 -1
  44. package/dist/lib/components/shadow-claw-pages.js +1 -1
  45. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  46. package/dist/lib/components/shadow-claw-settings.js +1 -1
  47. package/dist/lib/components/shadow-claw-storage.js +1 -1
  48. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  49. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  50. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  51. package/dist/lib/components/shadow-claw-tools.js +1 -1
  52. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  53. package/dist/lib/components/shadow-claw.js +1 -1
  54. package/dist/lib/{configurePeerJs-CrlGan0m.js → configurePeerJs-oODzltaC.js} +2 -2
  55. package/dist/lib/{configurePeerJs-CrlGan0m.js.map → configurePeerJs-oODzltaC.js.map} +1 -1
  56. package/dist/lib/core/control-plane-client.d.ts +1 -0
  57. package/dist/lib/{downloadGroupFile-DboUjmp2.js → downloadGroupFile-DmcBQU1n.js} +2 -2
  58. package/dist/lib/{downloadGroupFile-DboUjmp2.js.map → downloadGroupFile-DmcBQU1n.js.map} +1 -1
  59. package/dist/lib/{file-viewer-BrK2Wxgh.js → file-viewer-C3YKnq_-.js} +4 -3
  60. package/dist/lib/{file-viewer-BrK2Wxgh.js.map → file-viewer-C3YKnq_-.js.map} +1 -1
  61. package/dist/lib/index.js +26 -26
  62. package/dist/lib/{initControlPlane-CsO4UR6_.js → initControlPlane-C2zURVIS.js} +86 -13
  63. package/dist/lib/initControlPlane-C2zURVIS.js.map +1 -0
  64. package/dist/lib/{markdown-eeSTFnMA.js → markdown-C6EHl9zs.js} +3 -3
  65. package/dist/lib/{markdown-eeSTFnMA.js.map → markdown-C6EHl9zs.js.map} +1 -1
  66. package/dist/lib/{orchestrator-CJvoC2Uu.js → orchestrator-CACLa_Ro.js} +7 -6
  67. package/dist/lib/{orchestrator-CJvoC2Uu.js.map → orchestrator-CACLa_Ro.js.map} +1 -1
  68. package/dist/lib/{orchestrator-DCfomsn9.js → orchestrator-KRjx0eba.js} +6 -534
  69. package/dist/lib/{orchestrator-DCfomsn9.js.map → orchestrator-KRjx0eba.js.map} +1 -1
  70. package/dist/lib/parsePath-DwC_YZjw.js +16 -0
  71. package/dist/lib/parsePath-DwC_YZjw.js.map +1 -0
  72. package/dist/lib/{peerjs-Dp02bXcG.js → peerjs-DFWOBcXf.js} +4 -4
  73. package/dist/lib/{peerjs-Dp02bXcG.js.map → peerjs-DFWOBcXf.js.map} +1 -1
  74. package/dist/lib/{readGroupFileBytes-Bve-e5e_.js → readGroupFileBytes-C8oOe8Iu.js} +3 -16
  75. package/dist/lib/readGroupFileBytes-C8oOe8Iu.js.map +1 -0
  76. package/dist/lib/security/custom-element-security.d.ts +8 -0
  77. package/dist/lib/server/control-plane-types.d.ts +1 -1
  78. package/dist/lib/{shadow-claw-D8xINM3c.js → shadow-claw-6UHdiy4a.js} +5 -5
  79. package/dist/lib/{shadow-claw-D8xINM3c.js.map → shadow-claw-6UHdiy4a.js.map} +1 -1
  80. package/dist/lib/{shadow-claw-a2ui-7rJqG4Ke.js → shadow-claw-a2ui-CpZUTzlh.js} +5 -4
  81. package/dist/lib/{shadow-claw-a2ui-7rJqG4Ke.js.map → shadow-claw-a2ui-CpZUTzlh.js.map} +1 -1
  82. package/dist/lib/{shadow-claw-accounts-CdruwN8A.js → shadow-claw-accounts-03oJ__XC.js} +2 -2
  83. package/dist/lib/{shadow-claw-accounts-CdruwN8A.js.map → shadow-claw-accounts-03oJ__XC.js.map} +1 -1
  84. package/dist/lib/{shadow-claw-channel-config-Bj0-Nk3t.js → shadow-claw-channel-config-DmfwJc6G.js} +3 -3
  85. package/dist/lib/{shadow-claw-channel-config-Bj0-Nk3t.js.map → shadow-claw-channel-config-DmfwJc6G.js.map} +1 -1
  86. package/dist/lib/{shadow-claw-channels-C_jI4KhP.js → shadow-claw-channels-1N6Jl-By.js} +3 -3
  87. package/dist/lib/{shadow-claw-channels-C_jI4KhP.js.map → shadow-claw-channels-1N6Jl-By.js.map} +1 -1
  88. package/dist/lib/{shadow-claw-chat-B16MspoP.js → shadow-claw-chat-BMoqQJRK.js} +10 -10
  89. package/dist/lib/{shadow-claw-chat-B16MspoP.js.map → shadow-claw-chat-BMoqQJRK.js.map} +1 -1
  90. package/dist/lib/{shadow-claw-control-plane-x8Ldid_h.js → shadow-claw-control-plane-UG0o7pCz.js} +6 -6
  91. package/dist/lib/{shadow-claw-control-plane-x8Ldid_h.js.map → shadow-claw-control-plane-UG0o7pCz.js.map} +1 -1
  92. package/dist/lib/{shadow-claw-conversations-Ct1kAYdB.js → shadow-claw-conversations-BwXep3iu.js} +4 -3
  93. package/dist/lib/{shadow-claw-conversations-Ct1kAYdB.js.map → shadow-claw-conversations-BwXep3iu.js.map} +1 -1
  94. package/dist/lib/{shadow-claw-file-viewer-BfDHHgpA.js → shadow-claw-file-viewer-DUxzVxDT.js} +14 -10
  95. package/dist/lib/shadow-claw-file-viewer-DUxzVxDT.js.map +1 -0
  96. package/dist/lib/{shadow-claw-files-EYhOPYIH.js → shadow-claw-files-DIREEnT2.js} +7 -7
  97. package/dist/lib/{shadow-claw-files-EYhOPYIH.js.map → shadow-claw-files-DIREEnT2.js.map} +1 -1
  98. package/dist/lib/{shadow-claw-git-tcSpT-P0.js → shadow-claw-git-Bg1SWgsF.js} +4 -4
  99. package/dist/lib/{shadow-claw-git-tcSpT-P0.js.map → shadow-claw-git-Bg1SWgsF.js.map} +1 -1
  100. package/dist/lib/{shadow-claw-integrations-NbcCx_ZI.js → shadow-claw-integrations-Ds-CLPhj.js} +2 -2
  101. package/dist/lib/{shadow-claw-integrations-NbcCx_ZI.js.map → shadow-claw-integrations-Ds-CLPhj.js.map} +1 -1
  102. package/dist/lib/{shadow-claw-llm-BazJdD5f.js → shadow-claw-llm-CazBrVpM.js} +3 -3
  103. package/dist/lib/{shadow-claw-llm-BazJdD5f.js.map → shadow-claw-llm-CazBrVpM.js.map} +1 -1
  104. package/dist/lib/{shadow-claw-mcp-remote-CYmhpg7a.js → shadow-claw-mcp-remote-CT5Q-54E.js} +2 -2
  105. package/dist/lib/{shadow-claw-mcp-remote-CYmhpg7a.js.map → shadow-claw-mcp-remote-CT5Q-54E.js.map} +1 -1
  106. package/dist/lib/{shadow-claw-networking-DImPxe0H.js → shadow-claw-networking-Df85tCbO.js} +2 -2
  107. package/dist/lib/{shadow-claw-networking-DImPxe0H.js.map → shadow-claw-networking-Df85tCbO.js.map} +1 -1
  108. package/dist/lib/{shadow-claw-page-header-CcHUfFUx.js → shadow-claw-page-header-Cti2fzf-.js} +2 -2
  109. package/dist/lib/{shadow-claw-page-header-CcHUfFUx.js.map → shadow-claw-page-header-Cti2fzf-.js.map} +1 -1
  110. package/dist/lib/{shadow-claw-pages-D54jF1Qq.js → shadow-claw-pages-BgPyIEZO.js} +15 -11
  111. package/dist/lib/shadow-claw-pages-BgPyIEZO.js.map +1 -0
  112. package/dist/lib/{shadow-claw-peerjs-B9-T8Nlx.js → shadow-claw-peerjs-xkBXILed.js} +3 -3
  113. package/dist/lib/{shadow-claw-peerjs-B9-T8Nlx.js.map → shadow-claw-peerjs-xkBXILed.js.map} +1 -1
  114. package/dist/lib/{shadow-claw-settings-Bt1VAe-1.js → shadow-claw-settings-CN89xRVU.js} +4 -4
  115. package/dist/lib/{shadow-claw-settings-Bt1VAe-1.js.map → shadow-claw-settings-CN89xRVU.js.map} +1 -1
  116. package/dist/lib/{shadow-claw-storage-CFOjGf1d.js → shadow-claw-storage-VnhcObCe.js} +3 -3
  117. package/dist/lib/{shadow-claw-storage-CFOjGf1d.js.map → shadow-claw-storage-VnhcObCe.js.map} +1 -1
  118. package/dist/lib/{shadow-claw-task-server-BiKa2GOm.js → shadow-claw-task-server-jgEC2hHB.js} +2 -2
  119. package/dist/lib/{shadow-claw-task-server-BiKa2GOm.js.map → shadow-claw-task-server-jgEC2hHB.js.map} +1 -1
  120. package/dist/lib/{shadow-claw-tasks-DDHd8W4d.js → shadow-claw-tasks-i_evIufy.js} +6 -6
  121. package/dist/lib/{shadow-claw-tasks-DDHd8W4d.js.map → shadow-claw-tasks-i_evIufy.js.map} +1 -1
  122. package/dist/lib/{shadow-claw-terminal-kB6_tWKz.js → shadow-claw-terminal-CwScROlu.js} +2 -2
  123. package/dist/lib/{shadow-claw-terminal-kB6_tWKz.js.map → shadow-claw-terminal-CwScROlu.js.map} +1 -1
  124. package/dist/lib/{shadow-claw-tools-DxXIm0ku.js → shadow-claw-tools-DD3fyXP4.js} +5 -4
  125. package/dist/lib/{shadow-claw-tools-DxXIm0ku.js.map → shadow-claw-tools-DD3fyXP4.js.map} +1 -1
  126. package/dist/lib/{shadow-claw-webvm-Dc8oo7ao.js → shadow-claw-webvm-DbJq4I3-.js} +2 -2
  127. package/dist/lib/{shadow-claw-webvm-Dc8oo7ao.js.map → shadow-claw-webvm-DbJq4I3-.js.map} +1 -1
  128. package/dist/lib/{syncWebMcpRegistration-jyRGbC9w.js → syncWebMcpRegistration-DNLnQ-jz.js} +3 -2
  129. package/dist/lib/{syncWebMcpRegistration-jyRGbC9w.js.map → syncWebMcpRegistration-DNLnQ-jz.js.map} +1 -1
  130. package/dist/lib/tools-DwwB9RBa.js +546 -0
  131. package/dist/lib/tools-DwwB9RBa.js.map +1 -0
  132. package/dist/lib/{trusted-types-CkLDpLiZ.js → trusted-types-Dx0RZlrd.js} +19 -8
  133. package/dist/lib/trusted-types-Dx0RZlrd.js.map +1 -0
  134. package/dist/lib/utils/index.js +1 -1
  135. package/dist/lib/utils/utils.js +1 -1
  136. package/dist/public/.well-known/agent-skills/index.json +14 -0
  137. package/dist/public/AGENTS.md +5 -3
  138. package/dist/public/README.md +5 -3
  139. package/dist/public/agent.worker.js +1 -1
  140. package/dist/public/{configurePeerJs-COcwE0E7.js → configurePeerJs-BqgeBjP7.js} +1 -1
  141. package/dist/public/custom-element-security-FU04Cq05.js +1 -0
  142. package/dist/public/dist-B9m1QM9v.js +58 -0
  143. package/dist/public/docs/README.md +31 -31
  144. package/dist/public/docs/example/article/index.html +1 -1
  145. package/dist/public/docs/publishing/index.html +1 -1
  146. package/dist/public/docs/skill-creator/index.html +1 -1
  147. package/dist/public/docs/subsystems/a2ui.md +11 -1
  148. package/dist/public/docs/subsystems/cli.md +29 -0
  149. package/dist/public/docs/subsystems/control-plane.md +8 -2
  150. package/dist/public/docs/subsystems/custom-element-security.md +19 -9
  151. package/dist/public/docs/subsystems/mcp-server.md +39 -15
  152. package/dist/public/docs/subsystems/skills.md +30 -0
  153. package/dist/public/docs/subsystems/webmcp.md +9 -0
  154. package/dist/public/downloadGroupFile-D8KRJGGL.js +1 -0
  155. package/dist/public/e2e/README.md +7 -4
  156. package/dist/public/{e2e-bridge-CHARpbpW.js → e2e-bridge-Ceqr5kGc.js} +1 -1
  157. package/dist/public/effect-BJCrpFdp.js +1 -0
  158. package/dist/public/file-viewer-C-6Jc_tb.js +1 -0
  159. package/dist/public/index.html +1 -1
  160. package/dist/public/index.js +1 -1
  161. package/dist/public/initControlPlane-BN9cWyO0.js +1 -0
  162. package/dist/public/main/index.html +1 -1
  163. package/dist/public/main/memory/index.html +1 -1
  164. package/dist/public/{markdown-1I3O6Mmd.js → markdown-C3M4OW_A.js} +1 -1
  165. package/dist/public/{orchestrator-Cl95w2vb.js → orchestrator-CMbCTjBf.js} +7 -7
  166. package/dist/public/{orchestrator-D-xlXG92.js → orchestrator-DHjdLtqa.js} +663 -663
  167. package/dist/public/peerjs-DUCKH-m9.js +1 -0
  168. package/dist/public/service-worker.js +1 -1
  169. package/dist/public/{shadow-claw-CaoKmIIB.js → shadow-claw-EAt2ml5Y.js} +3 -3
  170. package/dist/public/{shadow-claw-accounts-CJvYC4li.js → shadow-claw-accounts-xUOLBsQZ.js} +1 -1
  171. package/dist/public/{shadow-claw-channels-3K62sU5f.js → shadow-claw-channels-XsfuWFF8.js} +4 -4
  172. package/dist/public/{shadow-claw-chat-Cu2keU2I.js → shadow-claw-chat-CbSaOZ4q.js} +7 -7
  173. package/dist/public/{shadow-claw-control-plane-D1OwlQI_.js → shadow-claw-control-plane-DHvN3Ukg.js} +2 -2
  174. package/dist/public/{shadow-claw-conversations-DDtg3BEZ.js → shadow-claw-conversations-BWs5VVfs.js} +2 -2
  175. package/dist/public/{shadow-claw-file-viewer-BR__1z9S.js → shadow-claw-file-viewer-BWsoW5QS.js} +5 -4
  176. package/dist/public/{shadow-claw-files-BJSyZ-Bl.js → shadow-claw-files-CG7MqWiV.js} +8 -8
  177. package/dist/public/{shadow-claw-git-MAjhXRLc.js → shadow-claw-git-DBRPZNav.js} +6 -6
  178. package/dist/public/{shadow-claw-integrations-82UoFuDR.js → shadow-claw-integrations-BBVnO0Fc.js} +8 -8
  179. package/dist/public/{shadow-claw-llm-R8j0lwPP.js → shadow-claw-llm-DMvLgKXo.js} +2 -2
  180. package/dist/public/{shadow-claw-mcp-remote-BzZoz4LX.js → shadow-claw-mcp-remote-Bkx0gsmF.js} +2 -2
  181. package/dist/public/{shadow-claw-networking-DLEd9cNm.js → shadow-claw-networking-Dd8Rw31D.js} +2 -2
  182. package/dist/public/{shadow-claw-notifications-1vwR7TbT.js → shadow-claw-notifications-Dbevj-lq.js} +2 -2
  183. package/dist/public/{shadow-claw-page-header-DMKbkv3s.js → shadow-claw-page-header-BccSHItx.js} +1 -1
  184. package/dist/public/{shadow-claw-pages-CjyeIV__.js → shadow-claw-pages-3_lQAv0t.js} +4 -3
  185. package/dist/public/{shadow-claw-settings-BfbT4dBU.js → shadow-claw-settings-t8TIlgrM.js} +5 -5
  186. package/dist/public/{shadow-claw-storage-C-mWQQML.js → shadow-claw-storage-CfEXoSIE.js} +2 -2
  187. package/dist/public/{shadow-claw-task-server-D3lHp0Nc.js → shadow-claw-task-server-DxQSsgRZ.js} +2 -2
  188. package/dist/public/{shadow-claw-tasks-DVVWEm-c.js → shadow-claw-tasks-DNpicQwz.js} +14 -14
  189. package/dist/public/{shadow-claw-terminal-BwuTRp-O.js → shadow-claw-terminal-DpY6zU_r.js} +1 -1
  190. package/dist/public/{shadow-claw-toast-ClCwgzFG.js → shadow-claw-toast-BkrQIP-3.js} +1 -1
  191. package/dist/public/{shadow-claw-tools-zFLk2nbV.js → shadow-claw-tools-WSwWohnk.js} +2 -2
  192. package/dist/public/{shadow-claw-webvm-qR9X7fg7.js → shadow-claw-webvm-CP6oFjg5.js} +2 -2
  193. package/dist/public/syncWebMcpRegistration-DOn9gLnD.js +1 -0
  194. package/dist/public/toast-BXgUfbyh.js +1 -0
  195. package/dist/public/tools-DvckJZG2.js +1 -0
  196. package/dist/server.js +188 -188
  197. package/package.json +3 -1
  198. package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.stories.ts +1380 -93
  199. package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.ts +11 -6
  200. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +8 -1
  201. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.ts +13 -5
  202. package/src/components/shadow-claw-pages/utils/buildHtmlPageSrcdoc.ts +13 -5
  203. package/src/core/control-plane-client.ts +15 -0
  204. package/src/core/utils/initControlPlane.test.ts +187 -0
  205. package/src/core/utils/initControlPlane.ts +194 -20
  206. package/src/security/custom-element-security.test.ts +51 -0
  207. package/src/security/custom-element-security.ts +33 -15
  208. package/src/server/control-plane-types.ts +1 -0
  209. package/src/server/control-plane.test.ts +69 -0
  210. package/src/server/control-plane.ts +45 -3
  211. package/src/server/mcp/tools/built-in-tools.ts +80 -5
  212. package/src/server/mcp/tools/client-tool-relay.ts +183 -26
  213. package/src/server/mcp/tools/mcp-tools.test.ts +140 -0
  214. package/src/server/middleware/static-files.test.ts +142 -0
  215. package/src/server/middleware/static-files.ts +66 -8
  216. package/src/testing/jest-setup.ts +1 -0
  217. package/src/worker/utils/handleMessage.ts +4 -1
  218. package/dist/lib/initControlPlane-CsO4UR6_.js.map +0 -1
  219. package/dist/lib/readGroupFileBytes-Bve-e5e_.js.map +0 -1
  220. package/dist/lib/shadow-claw-file-viewer-BfDHHgpA.js.map +0 -1
  221. package/dist/lib/shadow-claw-pages-D54jF1Qq.js.map +0 -1
  222. package/dist/lib/trusted-types-CkLDpLiZ.js.map +0 -1
  223. package/dist/public/custom-element-security-NCqQ5lmC.js +0 -1
  224. package/dist/public/downloadGroupFile-G0BZQgQg.js +0 -1
  225. package/dist/public/effect-rHYr4nfu.js +0 -1
  226. package/dist/public/file-viewer-8PdCURDD.js +0 -1
  227. package/dist/public/initControlPlane-CcGXax0A.js +0 -1
  228. package/dist/public/peerjs-BZdxWQFv.js +0 -1
  229. package/dist/public/syncWebMcpRegistration-DD42YsYV.js +0 -1
  230. package/dist/public/toast-HAq8XWFj.js +0 -58
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: skill-creator
3
+ description: Create, edit, review, and validate ShadowClaw declarative skills and their supporting resources.
4
+ user-invocable: true
5
+ metadata:
6
+ allowed-tools: read_file write_file javascript create_directory
7
+ ---
8
+
9
+ # ShadowClaw Skill Creator
10
+
11
+ Use this skill when creating, modifying, reviewing, or validating a ShadowClaw agent skill or declarative tool pipeline.
12
+
13
+ ## Workspace Layout Standards
14
+
15
+ - **Skills**: Create each skill in its dedicated directory under `.agents/skills/<group>/<skill-name>/SKILL.md` (default group: `main`).
16
+ - **Declarative Tools**: Define supporting declarative tools under `.agents/tools/<group>/<tool-name>.json` (default group: `main`).
17
+ - **Supporting Resources**: Co-locate skill-specific assets (reference documents, templates, helper scripts) inside the skill directory alongside `SKILL.md`.
18
+
19
+ ## Workflow
20
+
21
+ 1. **Establish Contract & Scope**
22
+ - For existing skills, read `SKILL.md` and related resources before editing.
23
+ - For new skills, define the trigger prompt, intended execution outcome, required tools, and output artifacts.
24
+ 2. **Configure Frontmatter**
25
+ - Provide a clear, concise trigger description in `description` (1024 characters max).
26
+ - Set `user-invocable: true` if the skill should be triggerable via slash command (e.g. `/skill-name`).
27
+ - Use `disable-model-invocation: true` for skills intended strictly for slash-command execution.
28
+ - Place non-standard configuration (such as `allowed-tools`) within the standard `metadata` block to maintain Agent Skills specification compliance.
29
+ 3. **Structure & Draft Content**
30
+ - Write instructions in `SKILL.md` using clear, modular procedures.
31
+ - Place deterministic operations in `javascript` tool calls; use `read_file` and `write_file` for workspace persistence.
32
+ - Create required target directories via `create_directory` prior to writing files.
33
+ 4. **Declarative Pipelines (`execution.type: tools`)**
34
+ - Define sequential tool pipelines in frontmatter via `execution: { type: "tools", tools: [...] }`.
35
+ - Pass outputs between steps using `{ "$pipe": "prev" }` or step/tool identifiers.
36
+ - Optionally apply `suppressToast: true` or `suppressOutput: true` at the execution root or per step to suppress notifications and output blocks.
37
+ 5. **Verify & Validate**
38
+ - Re-read the saved `SKILL.md` and verify valid YAML frontmatter syntax.
39
+ - Ensure the skill name uses 1–64 lowercase alphanumeric characters or hyphens and all referenced resource paths exist.
@@ -0,0 +1,44 @@
1
+ # Declarative Execution Pipelines
2
+
3
+ Declarative execution pipelines allow skills to execute deterministic sequential tool chains directly on the worker thread without scheduling a Task or making model LLM calls.
4
+
5
+ ## Configuration Syntax
6
+
7
+ Pipelines are declared in `SKILL.md` frontmatter under `metadata.execution`:
8
+
9
+ ```yaml
10
+ ---
11
+ name: sample-pipeline
12
+ description: Run a deterministic tool chain.
13
+ user-invocable: true
14
+ metadata:
15
+ allowed-tools: javascript show_toast
16
+ execution:
17
+ type: tools
18
+ suppressToast: true
19
+ suppressOutput: true
20
+ tools:
21
+ - name: javascript
22
+ input:
23
+ code: "Math.floor(Math.random() * 100) + 1"
24
+ - name: show_toast
25
+ input:
26
+ title: "Random Number"
27
+ message:
28
+ $pipe: prev
29
+ ---
30
+ ```
31
+
32
+ ## Data Piping (`$pipe`)
33
+
34
+ Inputs in tool step configurations can reference outputs from previous steps using the `$pipe` operator:
35
+
36
+ - `{ "$pipe": "prev" }` or `{ "$pipe": -1 }`: Resolves to the return output of the immediately preceding tool step.
37
+ - `{ "$pipe": 0 }`: Resolves to the return output of step index `0` (0-indexed).
38
+ - `{ "$pipe": "javascript" }`: Resolves to the return output of the last step that ran the `javascript` tool.
39
+
40
+ ## Execution Suppression
41
+
42
+ - `suppressToast: true`: Suppresses step progress toasts ("Running skill tool...") during pipeline execution.
43
+ - `suppressOutput: true`: Suppresses raw tool result blocks from being rendered in the chat thread.
44
+ - Setting suppression options at the `execution` root cascades down to all steps unless explicitly overridden per tool step.
@@ -0,0 +1,35 @@
1
+ # Declarative Tools
2
+
3
+ Declarative tools allow workspace authors to define reusable custom tools using JSON definitions placed under `.agents/tools/<group>/<tool-name>.json`.
4
+
5
+ ## Directory Structure
6
+
7
+ ```text
8
+ .agents/tools/
9
+ └── main/
10
+ └── generate_random_number.json
11
+ ```
12
+
13
+ ## Schema Format
14
+
15
+ ```json
16
+ {
17
+ "name": "generate_random_number",
18
+ "description": "Generate a random integer within a specified range.",
19
+ "executor": "javascript",
20
+ "inputSchema": {
21
+ "type": "object",
22
+ "properties": {
23
+ "min": { "type": "number", "default": 1 },
24
+ "max": { "type": "number", "default": 1000000 }
25
+ }
26
+ },
27
+ "code": "const min = Number(inputs.min ?? 1); const max = Number(inputs.max ?? 1000000); return Math.floor(Math.random() * (max - min + 1)) + min;"
28
+ }
29
+ ```
30
+
31
+ ## Features
32
+
33
+ - **Automatic Discovery**: Automatically loaded from `.agents/tools/` across conversation workspaces.
34
+ - **WebMCP Integration**: Registered with the browser ModelContext API for both manual pinning and automated agent execution.
35
+ - **Expression Evaluation**: Code expressions automatically evaluate and return results without needing explicit wrapper boilerplate.
@@ -0,0 +1,50 @@
1
+ # Frontmatter Specification
2
+
3
+ Agent skills in ShadowClaw are configured via YAML frontmatter at the top of `SKILL.md`. Frontmatter attributes adhere strictly to the standard Agent Skills specification.
4
+
5
+ ## Specification Schema
6
+
7
+ ```yaml
8
+ ---
9
+ name: skill-name
10
+ description: Clear, concise model-facing trigger description.
11
+ user-invocable: true
12
+ disable-model-invocation: false
13
+ argument-hint: "[arguments]"
14
+ metadata:
15
+ allowed-tools: read_file write_file javascript
16
+ compatibility: ShadowClaw
17
+ license: MIT
18
+ ---
19
+ ```
20
+
21
+ ## Attribute Reference
22
+
23
+ | Attribute | Type | Required | Description |
24
+ | :------------------------- | :-------- | :------- | :-------------------------------------------------------------------------------------------------------- |
25
+ | `name` | `string` | **Yes** | Skill identifier (1–64 lowercase alphanumeric characters and single hyphens). |
26
+ | `description` | `string` | **Yes** | Model-facing description used for skill discovery and activation (max 1024 characters). |
27
+ | `user-invocable` | `boolean` | No | When `true` (default), allows users to invoke the skill directly via slash commands (e.g. `/skill-name`). |
28
+ | `disable-model-invocation` | `boolean` | No | When `true`, hides the skill from model discovery so it is only triggered via slash command. |
29
+ | `argument-hint` | `string` | No | Display placeholder in the UI search/autocomplete menu when typing slash commands. |
30
+ | `metadata` | `object` | No | Key-value dictionary for custom metadata, tool permissions (`allowed-tools`), and `execution` pipelines. |
31
+ | `compatibility` | `string` | No | Platform compatibility requirements or version strings. |
32
+ | `license` | `string` | No | Open-source software license string (e.g. `MIT`, `AGPL-3.0`). |
33
+
34
+ ## Metadata Attributes (`metadata`)
35
+
36
+ Custom configuration properties and extension specifications (like declarative execution pipelines) that fall outside the standard Agent Skills frontmatter attributes should be placed inside `metadata`:
37
+
38
+ ```yaml
39
+ metadata:
40
+ allowed-tools: javascript show_toast generate_random_number
41
+ author: ShadowClaw Team
42
+ version: 1.0.0
43
+ execution:
44
+ type: tools
45
+ suppressToast: true
46
+ tools:
47
+ - name: javascript
48
+ input:
49
+ code: Math.floor(Math.random() * 100) + 1
50
+ ```
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
3
+ "name": "ShadowClaw",
4
+ "description": "ShadowClaw - Browser-native AI agent.",
5
+ "skills": [
6
+ {
7
+ "name": "skill-creator",
8
+ "type": "skill-md",
9
+ "description": "Create, edit, review, and validate ShadowClaw declarative skills and their supporting resources.",
10
+ "url": "../../.agents/skills/main/skill-creator/SKILL.md",
11
+ "digest": "sha256:e9876a79ab38c3a9acd0a990f2a26a578e1971abc2f3cd0eeda1988e6529f684"
12
+ }
13
+ ]
14
+ }
package/README.md CHANGED
@@ -62,7 +62,7 @@ A fully-functional agent runtime built on browser-native technology — the orch
62
62
  - **Model registry** — Dynamic metadata fetch (context window, modality support)
63
63
  - **Attachment capabilities** — Native multimodal delivery with automatic text fallback
64
64
  - **Remote MCP** — Discover and execute tools from external MCP servers
65
- - **Stateless MCP server** — Expose ShadowClaw CLI commands and live browser tools to external agent hosts (Claude Desktop, Cursor, Goose) via STDIO (`npx shadow-claw mcp`) or Streamable HTTP (`POST /mcp`)
65
+ - **Stateless MCP server** — Expose ShadowClaw CLI commands and live browser tools to external agent hosts (Claude Desktop, Cursor, Goose) via STDIO (`npx shadow-claw mcp`) or Streamable HTTP (`POST /mcp`), featuring multi-client tool targeting (`shadowclaw_set_active_client`), per-tool client capability validation, client-side execution guards, and interactive `ask_user` relaying
66
66
  - **A2UI interactive surfaces** — Render responsive UI components (Text, Button, TextField, Row/Column layouts) from agents via PeerJS WebRTC with two-way data binding
67
67
  - **Multi-Agent Shared State** — Synchronize agent knowledge across participants using `STATE_SNAPSHOT` and `STATE_DELTA` events
68
68
  - **Email integration** — IMAP/SMTP support with encrypted credentials
@@ -243,6 +243,7 @@ Pages complement the **main group MEMORY** (auto-created as `MEMORY.md` on first
243
243
  ## Agent Skills and Declarative Tools
244
244
 
245
245
  - **Workspace Agent Skills** — Discovers `.agents/skills/**/SKILL.md` instruction packages, presents model-invocable skill descriptions in the system prompt, and loads instructions plus bundled resources through `activate_skill`. Default bundled skills include `skill-creator`.
246
+ - **Agent Skills Discovery Index** — Automatically indexes skills, tools, and scripts into `/.well-known/agent-skills/index.json` complying with Agent Skills Discovery RFC v0.2.0, computing SHA-256 digests and RFC 3986 relative URLs. Integrated into static builds (`bin/build/build.mjs`) and runnable via `npx shadow-claw skills:index [dir]` (alias `agent-skills`).
246
247
  - **Slash Commands & Declarative Execution Pipelines** — User-invocable skills support `/skill-name` slash-command triggers. Skills with `execution: { type: "tools", tools: [...] }` execute tool pipelines directly on the worker thread via `executeToolChain` without scheduling Tasks or invoking LLM prompts. Supports `$pipe` output chaining and step-level or cascaded `suppressToast: true` and `suppressOutput: true` options.
247
248
  - **Declarative Tools** — Content repositories can define executable tools as JSON under `.agents/tools/main/` using sandboxed Bash or JavaScript, or delegate to existing tools without changing ShadowClaw source (e.g. `generate_random_number` in the starter template repository).
248
249
  - **Enhanced JS Tool Expression Evaluation** — The `javascript` tool automatically evaluates single expressions without explicit `return` statements by wrapping them in `return (<expression>);`.
@@ -252,7 +253,7 @@ Pages complement the **main group MEMORY** (auto-created as `MEMORY.md` on first
252
253
  ShadowClaw packages its UI components as standard Web Components built on TC39 Signals and native Custom Elements:
253
254
 
254
255
  - **Modular Package Exports** — Import pre-bundled components and utilities directly via `shadow-claw/components` (e.g. `ShadowClawToast`, `ShadowClawCard`, `ShadowClawDialog`, `ShadowClawEmptyState`, `ShadowClawPageHeader`) and `shadow-claw/utils` (e.g. `namespacedStorage`, `ulid`). TypeScript declaration files (`.d.ts`) are provided out of the box.
255
- - **Storybook Workbench** — Interactive visual development environment powered by Storybook (`@storybook/web-components-vite`) with dark mode default, live controls, and isolated component testing (`npm run storybook`).
256
+ - **Storybook Workbench** — Interactive visual development environment powered by Storybook (`@storybook/web-components-vite`) with dark mode default, live controls, and isolated component testing (`npm run storybook`), including all 18 A2UI catalog components and UI primitives.
256
257
  - **Reactive Element Lifecycle** — Custom elements extending `ShadowClawElement` observe attributes (`observedAttributes`) for declarative reactive DOM updates.
257
258
 
258
259
  ## WebVM (Optional Alpine Linux)
@@ -277,7 +278,7 @@ ShadowClaw uses **IndexedDB** for structured data (messages, config, tasks) and
277
278
  - **30-second key expiry** for plaintext operations
278
279
  - **No plaintext secrets on disk** — encrypted before storage
279
280
  - **Trusted Types enforcement** — idempotent `"default"` policy (`src/security/default-trusted-types-policy.ts`) registered at boot via `theme-init.ts`; `getPolicy()` fallback prevents duplicate-creation errors on module reload
280
- - **Custom element security guards** — `installCustomElementsRegistryGuard` and `installCustomElementDomGuard` prevent unauthorized custom element registration and dynamic DOM injection, strictly enforcing allowlists from `shadow-claw.config.json` (or `site-config.json`) or storage
281
+ - **Custom element security guards** — `installCustomElementsRegistryGuard` and `installCustomElementDomGuard` prevent unauthorized custom element registration and dynamic DOM injection, strictly enforcing allowlists from `shadow-claw.config.json` (or `site-config.json`) or storage. Script declarations support URLs and `{ src, hasInit }` descriptors (standardized on `src`), invoking `init()` exclusively when `hasInit: true` is configured.
281
282
  - **Iframe sandbox & CSP hardening** — sandboxed preview iframes omit `allow-same-origin` by default to enforce opaque-origin (`null`) isolation without Chrome sandbox escape warnings, while injecting a transparent `postMessage` storage proxy bridge (`iframe-storage-bridge.js`) to provide namespaced `IndexedDB` and `localStorage` persistence for custom elements, trapping `ServiceWorker`/`caches` `SecurityError` rejections, polyfilling `showOpenFilePicker`/`showSaveFilePicker`, and relaying programmatic/link navigation and declarative `BroadcastChannel` messages via `IframeBroadcastProxy`
282
283
  - **Iframe embed sanitization** — DOMPurify-based iframe allowlisting protects markdown and HTML previews, with Settings-backed host patterns and a safe default host list
283
284
  - **SSRF proxy hardening** — `/proxy` blocks non-HTTP/S schemes and private/loopback IP ranges by default; bypassed via `--allow-private-proxy` flag or the authenticated service-worker JSON format
@@ -430,6 +431,7 @@ npx shadow-claw server --tmp # Run services with tempora
430
431
  npx shadow-claw server --cache-dir <dir> # Run services with custom cache directory
431
432
  npx shadow-claw webrtc listen # Start headless WebRTC DataChannel daemon
432
433
  npx shadow-claw peer-id # Get or generate persistent CLI Peer ID
434
+ npx shadow-claw skills:index # Generate or update .well-known/agent-skills/index.json
433
435
  ```
434
436
 
435
437
  When launching `dev`, `run`, `serve`, or `server` without an existing cache, ShadowClaw prompts interactively to select between the local directory (`.cache`), system temporary storage (`tmpdir()`), or a custom path (skip prompting via `--tmp`, `-y`, `--cache-dir <dir>`, or `SHADOWCLAW_CACHE_DIR`).
@@ -18,11 +18,6 @@ const __filename = fileURLToPath(import.meta.url);
18
18
  const __dirname = dirname(__filename);
19
19
  const projectRoot = resolve(__dirname, "../..");
20
20
 
21
- async function run(command, options = {}) {
22
- console.log(`> ${command}`);
23
- execSync(command, { stdio: "inherit", ...options });
24
- }
25
-
26
21
  async function pathExists(p) {
27
22
  try {
28
23
  await stat(p);
@@ -33,6 +28,28 @@ async function pathExists(p) {
33
28
  }
34
29
 
35
30
  export async function runBuild(options = {}) {
31
+ const isQuiet =
32
+ options.quiet ?? (process.env.NODE_ENV === "test" && !process.env.VERBOSE);
33
+ const defaultStdio =
34
+ options.stdio ??
35
+ (process.env.NODE_ENV === "test" && !process.env.VERBOSE
36
+ ? "pipe"
37
+ : "inherit");
38
+
39
+ const log = (...args) => {
40
+ if (!isQuiet) {
41
+ console.log(...args);
42
+ }
43
+ };
44
+
45
+ async function run(command, runOptions = {}) {
46
+ if (!isQuiet && !runOptions.quiet) {
47
+ console.log(`> ${command}`);
48
+ }
49
+ const stdio = runOptions.stdio ?? defaultStdio;
50
+ execSync(command, { stdio, ...runOptions });
51
+ }
52
+
36
53
  const contentRoot = resolve(
37
54
  options.contentRoot || env.SHADOWCLAW_CONTENT_ROOT || process.cwd(),
38
55
  );
@@ -120,6 +137,39 @@ export async function runBuild(options = {}) {
120
137
  });
121
138
  } catch {}
122
139
 
140
+ try {
141
+ await cp(".agents/scripts", "dist/public/.agents/scripts", {
142
+ recursive: true,
143
+ });
144
+ } catch {}
145
+
146
+ // Generate / sync Agent Skills Discovery index (.well-known/agent-skills/index.json)
147
+ if (await pathExists(".agents/skills")) {
148
+ try {
149
+ const { generateSkillsIndex } =
150
+ await import("../commands/skills-index.mjs");
151
+ await generateSkillsIndex(projectRoot);
152
+ if (await pathExists(".well-known/agent-skills/index.json")) {
153
+ await cp(".well-known", "dist/public/.well-known", {
154
+ recursive: true,
155
+ force: true,
156
+ });
157
+ }
158
+ } catch (err) {
159
+ console.warn(
160
+ "Notice: Failed to auto-generate agent-skills index:",
161
+ err,
162
+ );
163
+ }
164
+ } else if (await pathExists(".well-known")) {
165
+ try {
166
+ await cp(".well-known", "dist/public/.well-known", {
167
+ recursive: true,
168
+ force: true,
169
+ });
170
+ } catch {}
171
+ }
172
+
123
173
  await run("npm run -s rolldown");
124
174
 
125
175
  if (prerenderMainMemory) {
@@ -141,14 +191,14 @@ export async function runBuild(options = {}) {
141
191
  } catch {}
142
192
 
143
193
  if (isProduction) {
144
- console.log("Running production post-build steps...");
194
+ log("Running production post-build steps...");
145
195
 
146
196
  const pagesOrigin = options.pagesOrigin || env.PAGES_ORIGIN;
147
197
  const basePath =
148
198
  options.basePath || (env.PAGES_BASE_PATH ?? "/shadow-claw/");
149
199
 
150
- console.log(` PAGES_ORIGIN : ${pagesOrigin || "(relative ./)"}`);
151
- console.log(` PAGES_BASE_PATH: ${basePath}`);
200
+ log(` PAGES_ORIGIN : ${pagesOrigin || "(relative ./)"}`);
201
+ log(` PAGES_BASE_PATH: ${basePath}`);
152
202
 
153
203
  if (pagesOrigin) {
154
204
  try {
@@ -191,7 +241,7 @@ export async function runBuild(options = {}) {
191
241
  await run("npm run -s build:service-worker");
192
242
  await run("node bin/patch-service-worker-trusted-types.mjs");
193
243
 
194
- console.log("Build completed successfully.");
244
+ log("Build completed successfully.");
195
245
  return;
196
246
  }
197
247
 
@@ -206,7 +256,7 @@ export async function runBuild(options = {}) {
206
256
  const toolchainDist = join(toolchainRoot, "dist/public");
207
257
  const toolchainBuilt = await pathExists(join(toolchainDist, "index.js"));
208
258
  if (!toolchainBuilt) {
209
- console.log("Building ShadowClaw core toolchain...");
259
+ log("Building ShadowClaw core toolchain...");
210
260
  await run("npm run -s build:clean", { cwd: toolchainRoot });
211
261
  await run("npm run -s rolldown", { cwd: toolchainRoot });
212
262
  }
@@ -289,20 +339,101 @@ export async function runBuild(options = {}) {
289
339
  });
290
340
  }
291
341
 
292
- // 6. Copy .agents from contentRoot
342
+ // 6. Copy .agents from contentRoot (fallback to toolchainRoot)
293
343
  const contentSkills = join(contentRoot, ".agents/skills");
294
344
  if (await pathExists(contentSkills)) {
295
345
  await cp(contentSkills, join(distPublicDir, ".agents/skills"), {
296
346
  recursive: true,
297
347
  force: true,
298
348
  });
349
+ } else {
350
+ const toolchainSkills = join(toolchainRoot, ".agents/skills");
351
+ if (await pathExists(toolchainSkills)) {
352
+ await cp(toolchainSkills, join(distPublicDir, ".agents/skills"), {
353
+ recursive: true,
354
+ force: true,
355
+ });
356
+ }
299
357
  }
358
+
300
359
  const contentTools = join(contentRoot, ".agents/tools");
301
360
  if (await pathExists(contentTools)) {
302
361
  await cp(contentTools, join(distPublicDir, ".agents/tools"), {
303
362
  recursive: true,
304
363
  force: true,
305
364
  });
365
+ } else {
366
+ const toolchainTools = join(toolchainRoot, ".agents/tools");
367
+ if (await pathExists(toolchainTools)) {
368
+ await cp(toolchainTools, join(distPublicDir, ".agents/tools"), {
369
+ recursive: true,
370
+ force: true,
371
+ });
372
+ }
373
+ }
374
+
375
+ const contentScripts = join(contentRoot, ".agents/scripts");
376
+ if (await pathExists(contentScripts)) {
377
+ await cp(contentScripts, join(distPublicDir, ".agents/scripts"), {
378
+ recursive: true,
379
+ force: true,
380
+ });
381
+ } else {
382
+ const toolchainScripts = join(toolchainRoot, ".agents/scripts");
383
+ if (await pathExists(toolchainScripts)) {
384
+ await cp(toolchainScripts, join(distPublicDir, ".agents/scripts"), {
385
+ recursive: true,
386
+ force: true,
387
+ });
388
+ }
389
+ }
390
+
391
+ // 6b. Generate / sync Agent Skills Discovery index (.well-known/agent-skills/index.json)
392
+ if (await pathExists(contentSkills)) {
393
+ try {
394
+ const { generateSkillsIndex } =
395
+ await import("../commands/skills-index.mjs");
396
+ await generateSkillsIndex(contentRoot);
397
+ const rootWellKnownIndex = join(
398
+ contentRoot,
399
+ ".well-known/agent-skills/index.json",
400
+ );
401
+ if (await pathExists(rootWellKnownIndex)) {
402
+ await cp(
403
+ join(contentRoot, ".well-known"),
404
+ join(distPublicDir, ".well-known"),
405
+ {
406
+ recursive: true,
407
+ force: true,
408
+ },
409
+ );
410
+ }
411
+ } catch (err) {
412
+ console.warn("Notice: Failed to auto-generate agent-skills index:", err);
413
+ const contentWellKnown = join(contentRoot, ".well-known");
414
+ if (await pathExists(contentWellKnown)) {
415
+ await cp(contentWellKnown, join(distPublicDir, ".well-known"), {
416
+ recursive: true,
417
+ force: true,
418
+ });
419
+ }
420
+ }
421
+ } else {
422
+ const contentWellKnown = join(contentRoot, ".well-known");
423
+ if (await pathExists(contentWellKnown)) {
424
+ await cp(contentWellKnown, join(distPublicDir, ".well-known"), {
425
+ recursive: true,
426
+ force: true,
427
+ });
428
+ } else {
429
+ const toolchainWellKnown = join(toolchainRoot, ".well-known");
430
+ if (await pathExists(toolchainWellKnown)) {
431
+ await cp(toolchainWellKnown, join(distPublicDir, ".well-known"), {
432
+ recursive: true,
433
+ force: true,
434
+ });
435
+ }
436
+ }
306
437
  }
307
438
 
308
439
  // 7. Prerender DSD Shell
@@ -348,11 +479,11 @@ export async function runBuild(options = {}) {
348
479
  } catch {}
349
480
 
350
481
  if (isProduction) {
351
- console.log("Running production post-build steps...");
482
+ log("Running production post-build steps...");
352
483
 
353
484
  const pagesOrigin = options.pagesOrigin || env.PAGES_ORIGIN;
354
- console.log(` PAGES_ORIGIN : ${pagesOrigin || "(relative ./)"}`);
355
- console.log(` PAGES_BASE_PATH: ${basePath}`);
485
+ log(` PAGES_ORIGIN : ${pagesOrigin || "(relative ./)"}`);
486
+ log(` PAGES_BASE_PATH: ${basePath}`);
356
487
 
357
488
  if (pagesOrigin) {
358
489
  try {
@@ -425,7 +556,7 @@ export async function runBuild(options = {}) {
425
556
  await run(`node "${patchSwScript}" "${swPath}"`);
426
557
  }
427
558
 
428
- console.log("Build completed successfully.");
559
+ log("Build completed successfully.");
429
560
  }
430
561
 
431
562
  const isMainModule =
@@ -121,6 +121,22 @@ describe("build without and with pages", () => {
121
121
  manifest.skills.filter((s) => s.displayPath.endsWith("SKILL.md")),
122
122
  ).toHaveLength(2);
123
123
  expect(manifest.skillsPurgeId).toBe("skills-build-001");
124
+
125
+ const wellKnownIndexPath = path.join(
126
+ tempProjectRoot,
127
+ "dist/public/.well-known/agent-skills/index.json",
128
+ );
129
+ expect(fs.existsSync(wellKnownIndexPath)).toBe(true);
130
+ const wellKnownIndex = JSON.parse(
131
+ await readFile(wellKnownIndexPath, "utf8"),
132
+ );
133
+ expect(wellKnownIndex.$schema).toBe(
134
+ "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
135
+ );
136
+ expect(wellKnownIndex.skills.map((s) => s.name)).toEqual(
137
+ expect.arrayContaining(["skill-creator", "example"]),
138
+ );
139
+ expect(wellKnownIndex.tools.map((t) => t.name)).toContain("echo");
124
140
  });
125
141
 
126
142
  it("builds with no pages directory and keeps default pages", async () => {
@@ -146,5 +162,46 @@ describe("build without and with pages", () => {
146
162
  path.join(tempProjectRoot, "site-config.json"),
147
163
  ].find((p) => fs.existsSync(p));
148
164
  expect(await readFile(configPath, "utf8")).toContain("ShadowClaw");
165
+
166
+ const wellKnownIndexPath = path.join(
167
+ tempProjectRoot,
168
+ "dist/public/.well-known/agent-skills/index.json",
169
+ );
170
+ expect(fs.existsSync(wellKnownIndexPath)).toBe(true);
171
+ });
172
+
173
+ it("builds an external consumer content root and falls back to toolchain .agents and .well-known", async () => {
174
+ const tempConsumerRoot = await mkdtemp(
175
+ path.join(os.tmpdir(), "shadow-claw-consumer-"),
176
+ );
177
+ const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
178
+ try {
179
+ const { runBuild: runConsumerBuild } = await import("./build.mjs");
180
+ await runConsumerBuild({
181
+ contentRoot: tempConsumerRoot,
182
+ toolchainRoot: tempProjectRoot,
183
+ isProduction: false,
184
+ quiet: true,
185
+ stdio: "pipe",
186
+ });
187
+
188
+ const consumerDistPublic = path.join(tempConsumerRoot, "dist/public");
189
+ expect(
190
+ fs.existsSync(
191
+ path.join(consumerDistPublic, ".well-known/agent-skills/index.json"),
192
+ ),
193
+ ).toBe(true);
194
+ expect(
195
+ fs.existsSync(
196
+ path.join(
197
+ consumerDistPublic,
198
+ ".agents/skills/main/skill-creator/SKILL.md",
199
+ ),
200
+ ),
201
+ ).toBe(true);
202
+ } finally {
203
+ logSpy.mockRestore();
204
+ await rm(tempConsumerRoot, { recursive: true, force: true });
205
+ }
149
206
  });
150
207
  });
package/bin/cli.mjs CHANGED
@@ -15,6 +15,7 @@ import { runTasksCommand } from "./commands/tasks.mjs";
15
15
  import { runPeerIdCommand } from "./commands/peer-id.mjs";
16
16
  import { runWebRtcListenCommand } from "./commands/webrtc-listen.mjs";
17
17
  import { runMcpCommand } from "./commands/mcp.mjs";
18
+ import { runSkillsIndexCommand } from "./commands/skills-index.mjs";
18
19
  import { resolveCacheDir } from "./utils/resolve-cache-dir.mjs";
19
20
 
20
21
  const __filename = fileURLToPath(import.meta.url);
@@ -962,4 +963,29 @@ program
962
963
  await runMcpCommand(options);
963
964
  });
964
965
 
966
+ // ---------------------------------------------------------------------------
967
+ // AGENT SKILLS INDEX COMMAND
968
+ // ---------------------------------------------------------------------------
969
+ program
970
+ .command("skills:index [dir]")
971
+ .alias("agent-skills")
972
+ .description(
973
+ "Generate or update .well-known/agent-skills/index.json per the Agent Skills Discovery RFC",
974
+ )
975
+ .option(
976
+ "--out-dir <dir>",
977
+ "Output directory relative to content root (default: .well-known/agent-skills)",
978
+ )
979
+ .option("--out-file <file>", "Explicit destination path for index.json")
980
+ .option("--stdout", "Print generated index.json to stdout", false)
981
+ .option("--no-write", "Skip writing file to disk")
982
+ .action(async (dir, options) => {
983
+ try {
984
+ await runSkillsIndexCommand(dir, options);
985
+ } catch (err) {
986
+ console.error("Failed to generate skills index:", err);
987
+ process.exit(1);
988
+ }
989
+ });
990
+
965
991
  program.parse(process.argv);