shadow-claw 1.26.0 → 1.27.1
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.
- package/.agents/skills/main/skill-creator/SKILL.md +39 -0
- package/.agents/skills/main/skill-creator/references/declarative-pipelines.md +44 -0
- package/.agents/skills/main/skill-creator/references/declarative-tools.md +35 -0
- package/.agents/skills/main/skill-creator/references/frontmatter-spec.md +50 -0
- package/.well-known/agent-skills/index.json +14 -0
- package/README.md +5 -3
- package/bin/build/build.mjs +148 -15
- package/bin/build/build.test.mjs +128 -0
- package/bin/cli.mjs +35 -0
- package/bin/commands/mcp.mjs +277 -55
- package/bin/commands/mcp.test.mjs +275 -1
- package/bin/commands/skills-index.mjs +447 -0
- package/bin/commands/skills-index.test.mjs +243 -0
- package/bin/site-config/utils/custom-elements/get-approved-custom-element-scripts.test.mjs +2 -0
- package/bin/site-config/utils/custom-elements/sanitize-embedded-custom-element-scripts.test.mjs +10 -1
- package/dist/lib/components/index.js +25 -25
- package/dist/lib/components/settings/shadow-claw-accounts.js +1 -1
- package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
- package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
- package/dist/lib/components/settings/shadow-claw-git.js +1 -1
- package/dist/lib/components/settings/shadow-claw-integrations.js +1 -1
- package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
- package/dist/lib/components/settings/shadow-claw-mcp-remote.js +1 -1
- package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
- package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
- package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
- package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
- package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
- package/dist/lib/components/shadow-claw-a2ui.js +1 -1
- package/dist/lib/components/shadow-claw-accounts.js +1 -1
- package/dist/lib/components/shadow-claw-channel-config.js +1 -1
- package/dist/lib/components/shadow-claw-channels.js +1 -1
- package/dist/lib/components/shadow-claw-chat.js +1 -1
- package/dist/lib/components/shadow-claw-control-plane.js +1 -1
- package/dist/lib/components/shadow-claw-conversations.js +1 -1
- package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
- package/dist/lib/components/shadow-claw-files.js +1 -1
- package/dist/lib/components/shadow-claw-git.js +1 -1
- package/dist/lib/components/shadow-claw-integrations.js +1 -1
- package/dist/lib/components/shadow-claw-llm.js +1 -1
- package/dist/lib/components/shadow-claw-mcp-remote.js +1 -1
- package/dist/lib/components/shadow-claw-networking.js +1 -1
- package/dist/lib/components/shadow-claw-page-header.js +1 -1
- package/dist/lib/components/shadow-claw-pages.js +1 -1
- package/dist/lib/components/shadow-claw-peerjs.js +1 -1
- package/dist/lib/components/shadow-claw-settings.js +1 -1
- package/dist/lib/components/shadow-claw-storage.js +1 -1
- package/dist/lib/components/shadow-claw-task-server.js +1 -1
- package/dist/lib/components/shadow-claw-tasks.js +1 -1
- package/dist/lib/components/shadow-claw-terminal.js +1 -1
- package/dist/lib/components/shadow-claw-tools.js +1 -1
- package/dist/lib/components/shadow-claw-webvm.js +1 -1
- package/dist/lib/components/shadow-claw.js +1 -1
- package/dist/lib/{configurePeerJs-CrlGan0m.js → configurePeerJs-oODzltaC.js} +2 -2
- package/dist/lib/{configurePeerJs-CrlGan0m.js.map → configurePeerJs-oODzltaC.js.map} +1 -1
- package/dist/lib/core/control-plane-client.d.ts +1 -0
- package/dist/lib/{downloadGroupFile-DboUjmp2.js → downloadGroupFile-DmcBQU1n.js} +2 -2
- package/dist/lib/{downloadGroupFile-DboUjmp2.js.map → downloadGroupFile-DmcBQU1n.js.map} +1 -1
- package/dist/lib/{file-viewer-BrK2Wxgh.js → file-viewer-C3YKnq_-.js} +4 -3
- package/dist/lib/{file-viewer-BrK2Wxgh.js.map → file-viewer-C3YKnq_-.js.map} +1 -1
- package/dist/lib/index.js +26 -26
- package/dist/lib/{initControlPlane-CsO4UR6_.js → initControlPlane-C2zURVIS.js} +86 -13
- package/dist/lib/initControlPlane-C2zURVIS.js.map +1 -0
- package/dist/lib/{markdown-eeSTFnMA.js → markdown-C6EHl9zs.js} +3 -3
- package/dist/lib/{markdown-eeSTFnMA.js.map → markdown-C6EHl9zs.js.map} +1 -1
- package/dist/lib/{orchestrator-CJvoC2Uu.js → orchestrator-CACLa_Ro.js} +7 -6
- package/dist/lib/{orchestrator-CJvoC2Uu.js.map → orchestrator-CACLa_Ro.js.map} +1 -1
- package/dist/lib/{orchestrator-DCfomsn9.js → orchestrator-KRjx0eba.js} +6 -534
- package/dist/lib/{orchestrator-DCfomsn9.js.map → orchestrator-KRjx0eba.js.map} +1 -1
- package/dist/lib/parsePath-DwC_YZjw.js +16 -0
- package/dist/lib/parsePath-DwC_YZjw.js.map +1 -0
- package/dist/lib/{peerjs-Dp02bXcG.js → peerjs-DFWOBcXf.js} +4 -4
- package/dist/lib/{peerjs-Dp02bXcG.js.map → peerjs-DFWOBcXf.js.map} +1 -1
- package/dist/lib/{readGroupFileBytes-Bve-e5e_.js → readGroupFileBytes-C8oOe8Iu.js} +3 -16
- package/dist/lib/readGroupFileBytes-C8oOe8Iu.js.map +1 -0
- package/dist/lib/security/custom-element-security.d.ts +8 -0
- package/dist/lib/server/control-plane-types.d.ts +1 -1
- package/dist/lib/{shadow-claw-D8xINM3c.js → shadow-claw-6UHdiy4a.js} +5 -5
- package/dist/lib/{shadow-claw-D8xINM3c.js.map → shadow-claw-6UHdiy4a.js.map} +1 -1
- package/dist/lib/{shadow-claw-a2ui-7rJqG4Ke.js → shadow-claw-a2ui-CpZUTzlh.js} +5 -4
- package/dist/lib/{shadow-claw-a2ui-7rJqG4Ke.js.map → shadow-claw-a2ui-CpZUTzlh.js.map} +1 -1
- package/dist/lib/{shadow-claw-accounts-CdruwN8A.js → shadow-claw-accounts-03oJ__XC.js} +2 -2
- package/dist/lib/{shadow-claw-accounts-CdruwN8A.js.map → shadow-claw-accounts-03oJ__XC.js.map} +1 -1
- package/dist/lib/{shadow-claw-channel-config-Bj0-Nk3t.js → shadow-claw-channel-config-DmfwJc6G.js} +3 -3
- package/dist/lib/{shadow-claw-channel-config-Bj0-Nk3t.js.map → shadow-claw-channel-config-DmfwJc6G.js.map} +1 -1
- package/dist/lib/{shadow-claw-channels-C_jI4KhP.js → shadow-claw-channels-1N6Jl-By.js} +3 -3
- package/dist/lib/{shadow-claw-channels-C_jI4KhP.js.map → shadow-claw-channels-1N6Jl-By.js.map} +1 -1
- package/dist/lib/{shadow-claw-chat-B16MspoP.js → shadow-claw-chat-BMoqQJRK.js} +10 -10
- package/dist/lib/{shadow-claw-chat-B16MspoP.js.map → shadow-claw-chat-BMoqQJRK.js.map} +1 -1
- package/dist/lib/{shadow-claw-control-plane-x8Ldid_h.js → shadow-claw-control-plane-UG0o7pCz.js} +6 -6
- package/dist/lib/{shadow-claw-control-plane-x8Ldid_h.js.map → shadow-claw-control-plane-UG0o7pCz.js.map} +1 -1
- package/dist/lib/{shadow-claw-conversations-Ct1kAYdB.js → shadow-claw-conversations-BwXep3iu.js} +4 -3
- package/dist/lib/{shadow-claw-conversations-Ct1kAYdB.js.map → shadow-claw-conversations-BwXep3iu.js.map} +1 -1
- package/dist/lib/{shadow-claw-file-viewer-BfDHHgpA.js → shadow-claw-file-viewer-DUxzVxDT.js} +14 -10
- package/dist/lib/shadow-claw-file-viewer-DUxzVxDT.js.map +1 -0
- package/dist/lib/{shadow-claw-files-EYhOPYIH.js → shadow-claw-files-DIREEnT2.js} +7 -7
- package/dist/lib/{shadow-claw-files-EYhOPYIH.js.map → shadow-claw-files-DIREEnT2.js.map} +1 -1
- package/dist/lib/{shadow-claw-git-tcSpT-P0.js → shadow-claw-git-Bg1SWgsF.js} +4 -4
- package/dist/lib/{shadow-claw-git-tcSpT-P0.js.map → shadow-claw-git-Bg1SWgsF.js.map} +1 -1
- package/dist/lib/{shadow-claw-integrations-NbcCx_ZI.js → shadow-claw-integrations-Ds-CLPhj.js} +2 -2
- package/dist/lib/{shadow-claw-integrations-NbcCx_ZI.js.map → shadow-claw-integrations-Ds-CLPhj.js.map} +1 -1
- package/dist/lib/{shadow-claw-llm-BazJdD5f.js → shadow-claw-llm-CazBrVpM.js} +3 -3
- package/dist/lib/{shadow-claw-llm-BazJdD5f.js.map → shadow-claw-llm-CazBrVpM.js.map} +1 -1
- package/dist/lib/{shadow-claw-mcp-remote-CYmhpg7a.js → shadow-claw-mcp-remote-CT5Q-54E.js} +2 -2
- package/dist/lib/{shadow-claw-mcp-remote-CYmhpg7a.js.map → shadow-claw-mcp-remote-CT5Q-54E.js.map} +1 -1
- package/dist/lib/{shadow-claw-networking-DImPxe0H.js → shadow-claw-networking-Df85tCbO.js} +2 -2
- package/dist/lib/{shadow-claw-networking-DImPxe0H.js.map → shadow-claw-networking-Df85tCbO.js.map} +1 -1
- package/dist/lib/{shadow-claw-page-header-CcHUfFUx.js → shadow-claw-page-header-Cti2fzf-.js} +2 -2
- package/dist/lib/{shadow-claw-page-header-CcHUfFUx.js.map → shadow-claw-page-header-Cti2fzf-.js.map} +1 -1
- package/dist/lib/{shadow-claw-pages-D54jF1Qq.js → shadow-claw-pages-BgPyIEZO.js} +15 -11
- package/dist/lib/shadow-claw-pages-BgPyIEZO.js.map +1 -0
- package/dist/lib/{shadow-claw-peerjs-B9-T8Nlx.js → shadow-claw-peerjs-xkBXILed.js} +3 -3
- package/dist/lib/{shadow-claw-peerjs-B9-T8Nlx.js.map → shadow-claw-peerjs-xkBXILed.js.map} +1 -1
- package/dist/lib/{shadow-claw-settings-Bt1VAe-1.js → shadow-claw-settings-CN89xRVU.js} +4 -4
- package/dist/lib/{shadow-claw-settings-Bt1VAe-1.js.map → shadow-claw-settings-CN89xRVU.js.map} +1 -1
- package/dist/lib/{shadow-claw-storage-CFOjGf1d.js → shadow-claw-storage-VnhcObCe.js} +3 -3
- package/dist/lib/{shadow-claw-storage-CFOjGf1d.js.map → shadow-claw-storage-VnhcObCe.js.map} +1 -1
- package/dist/lib/{shadow-claw-task-server-BiKa2GOm.js → shadow-claw-task-server-jgEC2hHB.js} +2 -2
- package/dist/lib/{shadow-claw-task-server-BiKa2GOm.js.map → shadow-claw-task-server-jgEC2hHB.js.map} +1 -1
- package/dist/lib/{shadow-claw-tasks-DDHd8W4d.js → shadow-claw-tasks-i_evIufy.js} +6 -6
- package/dist/lib/{shadow-claw-tasks-DDHd8W4d.js.map → shadow-claw-tasks-i_evIufy.js.map} +1 -1
- package/dist/lib/{shadow-claw-terminal-kB6_tWKz.js → shadow-claw-terminal-CwScROlu.js} +2 -2
- package/dist/lib/{shadow-claw-terminal-kB6_tWKz.js.map → shadow-claw-terminal-CwScROlu.js.map} +1 -1
- package/dist/lib/{shadow-claw-tools-DxXIm0ku.js → shadow-claw-tools-DD3fyXP4.js} +5 -4
- package/dist/lib/{shadow-claw-tools-DxXIm0ku.js.map → shadow-claw-tools-DD3fyXP4.js.map} +1 -1
- package/dist/lib/{shadow-claw-webvm-Dc8oo7ao.js → shadow-claw-webvm-DbJq4I3-.js} +2 -2
- package/dist/lib/{shadow-claw-webvm-Dc8oo7ao.js.map → shadow-claw-webvm-DbJq4I3-.js.map} +1 -1
- package/dist/lib/{syncWebMcpRegistration-jyRGbC9w.js → syncWebMcpRegistration-DNLnQ-jz.js} +3 -2
- package/dist/lib/{syncWebMcpRegistration-jyRGbC9w.js.map → syncWebMcpRegistration-DNLnQ-jz.js.map} +1 -1
- package/dist/lib/tools-DwwB9RBa.js +546 -0
- package/dist/lib/tools-DwwB9RBa.js.map +1 -0
- package/dist/lib/{trusted-types-CkLDpLiZ.js → trusted-types-Dx0RZlrd.js} +19 -8
- package/dist/lib/trusted-types-Dx0RZlrd.js.map +1 -0
- package/dist/lib/utils/index.js +1 -1
- package/dist/lib/utils/utils.js +1 -1
- package/dist/public/.well-known/agent-skills/index.json +14 -0
- package/dist/public/AGENTS.md +5 -3
- package/dist/public/README.md +5 -3
- package/dist/public/agent.worker.js +1 -1
- package/dist/public/{configurePeerJs-COcwE0E7.js → configurePeerJs-BqgeBjP7.js} +1 -1
- package/dist/public/custom-element-security-FU04Cq05.js +1 -0
- package/dist/public/dist-B9m1QM9v.js +58 -0
- package/dist/public/docs/README.md +31 -31
- package/dist/public/docs/example/article/index.html +1 -1
- package/dist/public/docs/publishing/index.html +1 -1
- package/dist/public/docs/skill-creator/index.html +1 -1
- package/dist/public/docs/subsystems/a2ui.md +11 -1
- package/dist/public/docs/subsystems/cli.md +32 -0
- package/dist/public/docs/subsystems/control-plane.md +8 -2
- package/dist/public/docs/subsystems/custom-element-security.md +19 -9
- package/dist/public/docs/subsystems/mcp-server.md +39 -15
- package/dist/public/docs/subsystems/skills.md +30 -0
- package/dist/public/docs/subsystems/webmcp.md +9 -0
- package/dist/public/downloadGroupFile-D8KRJGGL.js +1 -0
- package/dist/public/e2e/README.md +7 -4
- package/dist/public/{e2e-bridge-CHARpbpW.js → e2e-bridge-Ceqr5kGc.js} +1 -1
- package/dist/public/effect-BJCrpFdp.js +1 -0
- package/dist/public/file-viewer-C-6Jc_tb.js +1 -0
- package/dist/public/index.html +1 -1
- package/dist/public/index.js +1 -1
- package/dist/public/initControlPlane-BN9cWyO0.js +1 -0
- package/dist/public/main/index.html +1 -1
- package/dist/public/main/memory/index.html +1 -1
- package/dist/public/{markdown-1I3O6Mmd.js → markdown-C3M4OW_A.js} +1 -1
- package/dist/public/{orchestrator-Cl95w2vb.js → orchestrator-CMbCTjBf.js} +7 -7
- package/dist/public/{orchestrator-D-xlXG92.js → orchestrator-DHjdLtqa.js} +663 -663
- package/dist/public/peerjs-DUCKH-m9.js +1 -0
- package/dist/public/service-worker.js +1 -1
- package/dist/public/{shadow-claw-CaoKmIIB.js → shadow-claw-EAt2ml5Y.js} +3 -3
- package/dist/public/{shadow-claw-accounts-CJvYC4li.js → shadow-claw-accounts-xUOLBsQZ.js} +1 -1
- package/dist/public/{shadow-claw-channels-3K62sU5f.js → shadow-claw-channels-XsfuWFF8.js} +4 -4
- package/dist/public/{shadow-claw-chat-Cu2keU2I.js → shadow-claw-chat-CbSaOZ4q.js} +7 -7
- package/dist/public/{shadow-claw-control-plane-D1OwlQI_.js → shadow-claw-control-plane-DHvN3Ukg.js} +2 -2
- package/dist/public/{shadow-claw-conversations-DDtg3BEZ.js → shadow-claw-conversations-BWs5VVfs.js} +2 -2
- package/dist/public/{shadow-claw-file-viewer-BR__1z9S.js → shadow-claw-file-viewer-BWsoW5QS.js} +5 -4
- package/dist/public/{shadow-claw-files-BJSyZ-Bl.js → shadow-claw-files-CG7MqWiV.js} +8 -8
- package/dist/public/{shadow-claw-git-MAjhXRLc.js → shadow-claw-git-DBRPZNav.js} +6 -6
- package/dist/public/{shadow-claw-integrations-82UoFuDR.js → shadow-claw-integrations-BBVnO0Fc.js} +8 -8
- package/dist/public/{shadow-claw-llm-R8j0lwPP.js → shadow-claw-llm-DMvLgKXo.js} +2 -2
- package/dist/public/{shadow-claw-mcp-remote-BzZoz4LX.js → shadow-claw-mcp-remote-Bkx0gsmF.js} +2 -2
- package/dist/public/{shadow-claw-networking-DLEd9cNm.js → shadow-claw-networking-Dd8Rw31D.js} +2 -2
- package/dist/public/{shadow-claw-notifications-1vwR7TbT.js → shadow-claw-notifications-Dbevj-lq.js} +2 -2
- package/dist/public/{shadow-claw-page-header-DMKbkv3s.js → shadow-claw-page-header-BccSHItx.js} +1 -1
- package/dist/public/{shadow-claw-pages-CjyeIV__.js → shadow-claw-pages-3_lQAv0t.js} +4 -3
- package/dist/public/{shadow-claw-settings-BfbT4dBU.js → shadow-claw-settings-t8TIlgrM.js} +5 -5
- package/dist/public/{shadow-claw-storage-C-mWQQML.js → shadow-claw-storage-CfEXoSIE.js} +2 -2
- package/dist/public/{shadow-claw-task-server-D3lHp0Nc.js → shadow-claw-task-server-DxQSsgRZ.js} +2 -2
- package/dist/public/{shadow-claw-tasks-DVVWEm-c.js → shadow-claw-tasks-DNpicQwz.js} +14 -14
- package/dist/public/{shadow-claw-terminal-BwuTRp-O.js → shadow-claw-terminal-DpY6zU_r.js} +1 -1
- package/dist/public/{shadow-claw-toast-ClCwgzFG.js → shadow-claw-toast-BkrQIP-3.js} +1 -1
- package/dist/public/{shadow-claw-tools-zFLk2nbV.js → shadow-claw-tools-WSwWohnk.js} +2 -2
- package/dist/public/{shadow-claw-webvm-qR9X7fg7.js → shadow-claw-webvm-CP6oFjg5.js} +2 -2
- package/dist/public/syncWebMcpRegistration-DOn9gLnD.js +1 -0
- package/dist/public/toast-BXgUfbyh.js +1 -0
- package/dist/public/tools-DvckJZG2.js +1 -0
- package/dist/server.js +188 -188
- package/package.json +3 -1
- package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.stories.ts +1380 -93
- package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.ts +11 -6
- package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +8 -1
- package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.ts +13 -5
- package/src/components/shadow-claw-pages/utils/buildHtmlPageSrcdoc.ts +13 -5
- package/src/core/control-plane-client.ts +15 -0
- package/src/core/utils/initControlPlane.test.ts +187 -0
- package/src/core/utils/initControlPlane.ts +194 -20
- package/src/security/custom-element-security.test.ts +51 -0
- package/src/security/custom-element-security.ts +33 -15
- package/src/server/control-plane-types.ts +1 -0
- package/src/server/control-plane.test.ts +69 -0
- package/src/server/control-plane.ts +45 -3
- package/src/server/mcp/tools/built-in-tools.ts +80 -5
- package/src/server/mcp/tools/client-tool-relay.ts +183 -26
- package/src/server/mcp/tools/mcp-tools.test.ts +140 -0
- package/src/server/middleware/static-files.test.ts +142 -0
- package/src/server/middleware/static-files.ts +66 -8
- package/src/testing/jest-setup.ts +14 -0
- package/src/worker/utils/handleMessage.ts +4 -1
- package/dist/lib/initControlPlane-CsO4UR6_.js.map +0 -1
- package/dist/lib/readGroupFileBytes-Bve-e5e_.js.map +0 -1
- package/dist/lib/shadow-claw-file-viewer-BfDHHgpA.js.map +0 -1
- package/dist/lib/shadow-claw-pages-D54jF1Qq.js.map +0 -1
- package/dist/lib/trusted-types-CkLDpLiZ.js.map +0 -1
- package/dist/public/custom-element-security-NCqQ5lmC.js +0 -1
- package/dist/public/downloadGroupFile-G0BZQgQg.js +0 -1
- package/dist/public/effect-rHYr4nfu.js +0 -1
- package/dist/public/file-viewer-8PdCURDD.js +0 -1
- package/dist/public/initControlPlane-CcGXax0A.js +0 -1
- package/dist/public/peerjs-BZdxWQFv.js +0 -1
- package/dist/public/syncWebMcpRegistration-DD42YsYV.js +0 -1
- 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`).
|
package/bin/build/build.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
151
|
-
|
|
200
|
+
log(` PAGES_ORIGIN : ${pagesOrigin || "(relative ./)"}`);
|
|
201
|
+
log(` PAGES_BASE_PATH: ${basePath}`);
|
|
152
202
|
|
|
153
203
|
if (pagesOrigin) {
|
|
154
204
|
try {
|
|
@@ -175,6 +225,7 @@ export async function runBuild(options = {}) {
|
|
|
175
225
|
try {
|
|
176
226
|
meta = execSync("npm run -s build:pkg:get:meta", {
|
|
177
227
|
encoding: "utf8",
|
|
228
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
178
229
|
}).trim();
|
|
179
230
|
} catch {}
|
|
180
231
|
if (meta) {
|
|
@@ -191,7 +242,7 @@ export async function runBuild(options = {}) {
|
|
|
191
242
|
await run("npm run -s build:service-worker");
|
|
192
243
|
await run("node bin/patch-service-worker-trusted-types.mjs");
|
|
193
244
|
|
|
194
|
-
|
|
245
|
+
log("Build completed successfully.");
|
|
195
246
|
return;
|
|
196
247
|
}
|
|
197
248
|
|
|
@@ -206,7 +257,7 @@ export async function runBuild(options = {}) {
|
|
|
206
257
|
const toolchainDist = join(toolchainRoot, "dist/public");
|
|
207
258
|
const toolchainBuilt = await pathExists(join(toolchainDist, "index.js"));
|
|
208
259
|
if (!toolchainBuilt) {
|
|
209
|
-
|
|
260
|
+
log("Building ShadowClaw core toolchain...");
|
|
210
261
|
await run("npm run -s build:clean", { cwd: toolchainRoot });
|
|
211
262
|
await run("npm run -s rolldown", { cwd: toolchainRoot });
|
|
212
263
|
}
|
|
@@ -289,20 +340,100 @@ export async function runBuild(options = {}) {
|
|
|
289
340
|
});
|
|
290
341
|
}
|
|
291
342
|
|
|
292
|
-
// 6. Copy .agents from contentRoot
|
|
343
|
+
// 6. Copy .agents from contentRoot (fallback to toolchainRoot)
|
|
293
344
|
const contentSkills = join(contentRoot, ".agents/skills");
|
|
294
345
|
if (await pathExists(contentSkills)) {
|
|
295
346
|
await cp(contentSkills, join(distPublicDir, ".agents/skills"), {
|
|
296
347
|
recursive: true,
|
|
297
348
|
force: true,
|
|
298
349
|
});
|
|
350
|
+
} else {
|
|
351
|
+
const toolchainSkills = join(toolchainRoot, ".agents/skills");
|
|
352
|
+
if (await pathExists(toolchainSkills)) {
|
|
353
|
+
await cp(toolchainSkills, join(distPublicDir, ".agents/skills"), {
|
|
354
|
+
recursive: true,
|
|
355
|
+
force: true,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
299
358
|
}
|
|
359
|
+
|
|
300
360
|
const contentTools = join(contentRoot, ".agents/tools");
|
|
301
361
|
if (await pathExists(contentTools)) {
|
|
302
362
|
await cp(contentTools, join(distPublicDir, ".agents/tools"), {
|
|
303
363
|
recursive: true,
|
|
304
364
|
force: true,
|
|
305
365
|
});
|
|
366
|
+
} else {
|
|
367
|
+
const toolchainTools = join(toolchainRoot, ".agents/tools");
|
|
368
|
+
if (await pathExists(toolchainTools)) {
|
|
369
|
+
await cp(toolchainTools, join(distPublicDir, ".agents/tools"), {
|
|
370
|
+
recursive: true,
|
|
371
|
+
force: true,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const contentScripts = join(contentRoot, ".agents/scripts");
|
|
377
|
+
if (await pathExists(contentScripts)) {
|
|
378
|
+
await cp(contentScripts, join(distPublicDir, ".agents/scripts"), {
|
|
379
|
+
recursive: true,
|
|
380
|
+
force: true,
|
|
381
|
+
});
|
|
382
|
+
} else {
|
|
383
|
+
const toolchainScripts = join(toolchainRoot, ".agents/scripts");
|
|
384
|
+
if (await pathExists(toolchainScripts)) {
|
|
385
|
+
await cp(toolchainScripts, join(distPublicDir, ".agents/scripts"), {
|
|
386
|
+
recursive: true,
|
|
387
|
+
force: true,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// 6b. Generate / sync Agent Skills Discovery index (.well-known/agent-skills/index.json)
|
|
393
|
+
const distSkillsDir = join(distPublicDir, ".agents/skills");
|
|
394
|
+
if (await pathExists(distSkillsDir)) {
|
|
395
|
+
try {
|
|
396
|
+
const { generateSkillsIndex } =
|
|
397
|
+
await import("../commands/skills-index.mjs");
|
|
398
|
+
await generateSkillsIndex(distPublicDir, {
|
|
399
|
+
metadataRoot: contentRoot,
|
|
400
|
+
outDir: ".well-known/agent-skills",
|
|
401
|
+
});
|
|
402
|
+
// Also keep contentRoot/.well-known/agent-skills/index.json in sync if contentSkills exists
|
|
403
|
+
if (await pathExists(contentSkills)) {
|
|
404
|
+
try {
|
|
405
|
+
await generateSkillsIndex(contentRoot, {
|
|
406
|
+
toolchainRoot,
|
|
407
|
+
includeBundled: true,
|
|
408
|
+
});
|
|
409
|
+
} catch {}
|
|
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
|
-
|
|
482
|
+
log("Running production post-build steps...");
|
|
352
483
|
|
|
353
484
|
const pagesOrigin = options.pagesOrigin || env.PAGES_ORIGIN;
|
|
354
|
-
|
|
355
|
-
|
|
485
|
+
log(` PAGES_ORIGIN : ${pagesOrigin || "(relative ./)"}`);
|
|
486
|
+
log(` PAGES_BASE_PATH: ${basePath}`);
|
|
356
487
|
|
|
357
488
|
if (pagesOrigin) {
|
|
358
489
|
try {
|
|
@@ -377,12 +508,14 @@ export async function runBuild(options = {}) {
|
|
|
377
508
|
meta = execSync("git rev-parse HEAD", {
|
|
378
509
|
cwd: contentRoot,
|
|
379
510
|
encoding: "utf8",
|
|
511
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
380
512
|
}).trim();
|
|
381
513
|
} catch {
|
|
382
514
|
try {
|
|
383
515
|
meta = execSync("git rev-parse HEAD", {
|
|
384
516
|
cwd: toolchainRoot,
|
|
385
517
|
encoding: "utf8",
|
|
518
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
386
519
|
}).trim();
|
|
387
520
|
} catch {}
|
|
388
521
|
}
|
|
@@ -425,7 +558,7 @@ export async function runBuild(options = {}) {
|
|
|
425
558
|
await run(`node "${patchSwScript}" "${swPath}"`);
|
|
426
559
|
}
|
|
427
560
|
|
|
428
|
-
|
|
561
|
+
log("Build completed successfully.");
|
|
429
562
|
}
|
|
430
563
|
|
|
431
564
|
const isMainModule =
|
package/bin/build/build.test.mjs
CHANGED
|
@@ -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,117 @@ 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
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("builds an external consumer with custom skills and retains bundled skill-creator in .well-known index", async () => {
|
|
209
|
+
const tempConsumerRoot = await mkdtemp(
|
|
210
|
+
path.join(os.tmpdir(), "shadow-claw-custom-consumer-"),
|
|
211
|
+
);
|
|
212
|
+
const logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
|
|
213
|
+
try {
|
|
214
|
+
await writeFile(
|
|
215
|
+
path.join(tempConsumerRoot, "shadow-claw.config.json"),
|
|
216
|
+
JSON.stringify({
|
|
217
|
+
site: {
|
|
218
|
+
title: "Custom Hub",
|
|
219
|
+
description: "Custom hub description.",
|
|
220
|
+
},
|
|
221
|
+
}),
|
|
222
|
+
"utf8",
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const skillDir = path.join(
|
|
226
|
+
tempConsumerRoot,
|
|
227
|
+
".agents",
|
|
228
|
+
"skills",
|
|
229
|
+
"main",
|
|
230
|
+
"custom-skill",
|
|
231
|
+
);
|
|
232
|
+
await mkdir(skillDir, { recursive: true });
|
|
233
|
+
await writeFile(
|
|
234
|
+
path.join(skillDir, "SKILL.md"),
|
|
235
|
+
"---\nname: custom-skill\ndescription: Custom consumer skill.\n---\n",
|
|
236
|
+
"utf8",
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const { runBuild: runConsumerBuild } = await import("./build.mjs");
|
|
240
|
+
await runConsumerBuild({
|
|
241
|
+
contentRoot: tempConsumerRoot,
|
|
242
|
+
toolchainRoot: tempProjectRoot,
|
|
243
|
+
isProduction: true,
|
|
244
|
+
quiet: true,
|
|
245
|
+
stdio: "pipe",
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const consumerDistPublic = path.join(tempConsumerRoot, "dist/public");
|
|
249
|
+
const wellKnownPath = path.join(
|
|
250
|
+
consumerDistPublic,
|
|
251
|
+
".well-known/agent-skills/index.json",
|
|
252
|
+
);
|
|
253
|
+
expect(fs.existsSync(wellKnownPath)).toBe(true);
|
|
254
|
+
|
|
255
|
+
const wellKnownIndex = JSON.parse(await readFile(wellKnownPath, "utf8"));
|
|
256
|
+
expect(wellKnownIndex.name).toBe("Custom Hub");
|
|
257
|
+
expect(wellKnownIndex.description).toBe("Custom hub description.");
|
|
258
|
+
expect(wellKnownIndex.skills.map((s) => s.name)).toEqual(
|
|
259
|
+
expect.arrayContaining(["custom-skill", "skill-creator"]),
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const contentWellKnownPath = path.join(
|
|
263
|
+
tempConsumerRoot,
|
|
264
|
+
".well-known/agent-skills/index.json",
|
|
265
|
+
);
|
|
266
|
+
expect(fs.existsSync(contentWellKnownPath)).toBe(true);
|
|
267
|
+
const contentIndex = JSON.parse(
|
|
268
|
+
await readFile(contentWellKnownPath, "utf8"),
|
|
269
|
+
);
|
|
270
|
+
expect(contentIndex.skills.map((s) => s.name)).toEqual(
|
|
271
|
+
expect.arrayContaining(["custom-skill", "skill-creator"]),
|
|
272
|
+
);
|
|
273
|
+
} finally {
|
|
274
|
+
logSpy.mockRestore();
|
|
275
|
+
await rm(tempConsumerRoot, { recursive: true, force: true });
|
|
276
|
+
}
|
|
149
277
|
});
|
|
150
278
|
});
|