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