@shadowob/cloud 1.1.6-dev.311
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/README.md +509 -0
- package/dist/agent-browser-CERTMCDL.js +117 -0
- package/dist/agent-browser-CIRZRIY4.js +118 -0
- package/dist/agent-pack-LF3O5TR4.js +1236 -0
- package/dist/agent-pack-RQT27V7R.js +1235 -0
- package/dist/airtable-BG2Q75G2.js +82 -0
- package/dist/airtable-JCQXFM5D.js +83 -0
- package/dist/alipay-MZX2XCDB.js +52 -0
- package/dist/alipay-TZQI34RB.js +51 -0
- package/dist/amap-5RQB3VGC.js +45 -0
- package/dist/amap-KPCLZYYL.js +44 -0
- package/dist/atlassian-LGOEWYC7.js +54 -0
- package/dist/atlassian-TVS2A4IU.js +55 -0
- package/dist/baidu-appbuilder-6UMESXHW.js +41 -0
- package/dist/baidu-appbuilder-QRRL3ETM.js +42 -0
- package/dist/baidu-maps-HEPMVP5D.js +44 -0
- package/dist/baidu-maps-HXC4FBVP.js +45 -0
- package/dist/baidu-netdisk-G5Q6B5NH.js +45 -0
- package/dist/baidu-netdisk-RS2K5W2M.js +44 -0
- package/dist/baidu-smartprogram-EWTK5WKK.js +41 -0
- package/dist/baidu-smartprogram-JHD3XWF6.js +40 -0
- package/dist/browserbase-IUIYVYI7.js +67 -0
- package/dist/browserbase-JFO2PCIA.js +68 -0
- package/dist/canva-3YOFL7JS.js +62 -0
- package/dist/canva-FMYN65SM.js +61 -0
- package/dist/chunk-6P2K6QZR.js +529 -0
- package/dist/chunk-7VMRQ7MG.js +90 -0
- package/dist/chunk-AD3JTIU3.js +17 -0
- package/dist/chunk-BF6CV2Y4.js +64 -0
- package/dist/chunk-CTNUKOQE.js +439 -0
- package/dist/chunk-EEFMJYKB.js +97 -0
- package/dist/chunk-EJKFQ35I.js +739 -0
- package/dist/chunk-HUICDC56.js +62 -0
- package/dist/chunk-JUPAE5IA.js +527 -0
- package/dist/chunk-JY2HTT7Q.js +437 -0
- package/dist/chunk-KEPTCLUO.js +121 -0
- package/dist/chunk-KKK5H7YX.js +3622 -0
- package/dist/chunk-POSVEKIY.js +210 -0
- package/dist/chunk-QET4LT4J.js +5769 -0
- package/dist/chunk-QV4XWO3P.js +30 -0
- package/dist/chunk-R52J3PH2.js +120 -0
- package/dist/chunk-R5U7XKVJ.js +16 -0
- package/dist/chunk-RECNVWMT.js +212 -0
- package/dist/chunk-RTPBU5HF.js +92 -0
- package/dist/chunk-SUZ2ATT6.js +5774 -0
- package/dist/chunk-SVMXSIMG.js +98 -0
- package/dist/chunk-TV3CBM7R.js +28 -0
- package/dist/chunk-V2LU736V.js +3495 -0
- package/dist/chunk-ZUYL3W53.js +741 -0
- package/dist/claude-plugin-577TAQVS.js +1463 -0
- package/dist/claude-plugin-L3MXJJ6J.js +1464 -0
- package/dist/cli.js +7021 -0
- package/dist/cloudflare-HBBABPK6.js +114 -0
- package/dist/cloudflare-RDFPKMM5.js +115 -0
- package/dist/cnb-FLP3QX46.js +44 -0
- package/dist/cnb-YAVVEYFB.js +45 -0
- package/dist/console/index.html +12 -0
- package/dist/console/logo.png +0 -0
- package/dist/console/static/css/5079.f9e0918d.css +1 -0
- package/dist/console/static/css/index.7f91f806.css +1 -0
- package/dist/console/static/font/codicon.5b7d6fac.ttf +0 -0
- package/dist/console/static/js/5079.72a51ca2.js +699 -0
- package/dist/console/static/js/5079.72a51ca2.js.LICENSE.txt +35 -0
- package/dist/console/static/js/7426.f8d483ea.js +1 -0
- package/dist/console/static/js/async/1008.4df521b7.js +1 -0
- package/dist/console/static/js/async/102.1d473ec7.js +1 -0
- package/dist/console/static/js/async/1134.3f9fd9e7.js +1 -0
- package/dist/console/static/js/async/1318.4b8e48e7.js +1 -0
- package/dist/console/static/js/async/1360.5606da88.js +7 -0
- package/dist/console/static/js/async/1546.045f484f.js +1 -0
- package/dist/console/static/js/async/1562.187de2a8.js +1 -0
- package/dist/console/static/js/async/168.456d4813.js +1 -0
- package/dist/console/static/js/async/1750.e6dc2664.js +1 -0
- package/dist/console/static/js/async/1994.3fc86066.js +1 -0
- package/dist/console/static/js/async/2348.613ae3d9.js +1 -0
- package/dist/console/static/js/async/2390.1b890b9d.js +1 -0
- package/dist/console/static/js/async/2414.9d040212.js +1 -0
- package/dist/console/static/js/async/2454.4c1784ab.js +1 -0
- package/dist/console/static/js/async/2498.f5f92030.js +1 -0
- package/dist/console/static/js/async/2924.b823cd1a.js +1 -0
- package/dist/console/static/js/async/3062.63fddea6.js +1 -0
- package/dist/console/static/js/async/3078.dd712008.js +1 -0
- package/dist/console/static/js/async/3198.1f307065.js +1 -0
- package/dist/console/static/js/async/3246.3d5a899f.js +1 -0
- package/dist/console/static/js/async/3286.871676eb.js +1 -0
- package/dist/console/static/js/async/342.10bf3b90.js +1 -0
- package/dist/console/static/js/async/3446.9681a4d7.js +1 -0
- package/dist/console/static/js/async/3698.ccfaabec.js +1 -0
- package/dist/console/static/js/async/3790.2a1106a6.js +1 -0
- package/dist/console/static/js/async/4231.b29784d4.js +1 -0
- package/dist/console/static/js/async/4551.515bd41d.js +1 -0
- package/dist/console/static/js/async/4596.40f6e71b.js +1 -0
- package/dist/console/static/js/async/4600.4aaebe6d.js +1 -0
- package/dist/console/static/js/async/4718.1aae022f.js +1 -0
- package/dist/console/static/js/async/4846.a347c020.js +1 -0
- package/dist/console/static/js/async/4860.83dadf89.js +1 -0
- package/dist/console/static/js/async/500.fcfa37cb.js +1 -0
- package/dist/console/static/js/async/5096.b360203d.js +1 -0
- package/dist/console/static/js/async/5222.043274fe.js +1 -0
- package/dist/console/static/js/async/5362.f498001c.js +1 -0
- package/dist/console/static/js/async/54.c94f0755.js +1 -0
- package/dist/console/static/js/async/5478.50dd9ef0.js +2 -0
- package/dist/console/static/js/async/5478.50dd9ef0.js.LICENSE.txt +3 -0
- package/dist/console/static/js/async/5507.a6a1f793.js +1 -0
- package/dist/console/static/js/async/5638.bc6b102d.js +1 -0
- package/dist/console/static/js/async/5722.e0029049.js +1 -0
- package/dist/console/static/js/async/5942.74635c6b.js +1 -0
- package/dist/console/static/js/async/5994.1c5629c1.js +1 -0
- package/dist/console/static/js/async/6054.6bddf720.js +1 -0
- package/dist/console/static/js/async/6118.45e754e5.js +1 -0
- package/dist/console/static/js/async/6127.adcbcbb6.js +1 -0
- package/dist/console/static/js/async/614.3f434c20.js +1 -0
- package/dist/console/static/js/async/6234.ba3b002d.js +1 -0
- package/dist/console/static/js/async/6310.6546a9ba.js +1 -0
- package/dist/console/static/js/async/6378.9f805419.js +1 -0
- package/dist/console/static/js/async/6380.e4433c49.js +1 -0
- package/dist/console/static/js/async/6418.f23bcfda.js +1 -0
- package/dist/console/static/js/async/6428.77c86114.js +1 -0
- package/dist/console/static/js/async/6443.83318a6c.js +1 -0
- package/dist/console/static/js/async/6508.2b445d62.js +3 -0
- package/dist/console/static/js/async/6542.e82a26c8.js +1 -0
- package/dist/console/static/js/async/6544.62111ecb.js +1 -0
- package/dist/console/static/js/async/6612.a0c9fcf4.js +1 -0
- package/dist/console/static/js/async/6740.695aebf0.js +1 -0
- package/dist/console/static/js/async/6822.dbbb32bc.js +1 -0
- package/dist/console/static/js/async/6824.ad3540ab.js +1 -0
- package/dist/console/static/js/async/6930.585dab94.js +1 -0
- package/dist/console/static/js/async/6982.c81b95e6.js +1 -0
- package/dist/console/static/js/async/7046.ab2378c1.js +1 -0
- package/dist/console/static/js/async/7110.a603277f.js +1 -0
- package/dist/console/static/js/async/7142.4a21366f.js +1 -0
- package/dist/console/static/js/async/7348.15cc6148.js +1373 -0
- package/dist/console/static/js/async/7348.15cc6148.js.LICENSE.txt +14 -0
- package/dist/console/static/js/async/7374.b1ac5c44.js +1 -0
- package/dist/console/static/js/async/742.847f17ca.js +1 -0
- package/dist/console/static/js/async/7446.743954d8.js +1 -0
- package/dist/console/static/js/async/7673.59bbbaac.js +1 -0
- package/dist/console/static/js/async/7684.c5760c8c.js +1 -0
- package/dist/console/static/js/async/7714.c30d0f94.js +1 -0
- package/dist/console/static/js/async/8118.36d5a3bf.js +298 -0
- package/dist/console/static/js/async/8145.4bcf043a.js +1 -0
- package/dist/console/static/js/async/8246.408de938.js +1 -0
- package/dist/console/static/js/async/8390.bdae1f7d.js +1 -0
- package/dist/console/static/js/async/8422.fd94dbe1.js +1 -0
- package/dist/console/static/js/async/8434.94a0e2ae.js +1 -0
- package/dist/console/static/js/async/8518.3158de13.js +1 -0
- package/dist/console/static/js/async/8564.fc2eb841.js +1 -0
- package/dist/console/static/js/async/8678.73af4c9b.js +1 -0
- package/dist/console/static/js/async/8694.79747168.js +1 -0
- package/dist/console/static/js/async/8756.1de37b83.js +1 -0
- package/dist/console/static/js/async/8804.7fe6bdf9.js +3 -0
- package/dist/console/static/js/async/8883.e717ee94.js +1 -0
- package/dist/console/static/js/async/8886.fe6e876c.js +1 -0
- package/dist/console/static/js/async/9030.fc1ae402.js +1 -0
- package/dist/console/static/js/async/9094.5598d084.js +1 -0
- package/dist/console/static/js/async/9218.ee7b84b7.js +1 -0
- package/dist/console/static/js/async/94.9b80bc35.js +1 -0
- package/dist/console/static/js/async/9526.92aba34c.js +1 -0
- package/dist/console/static/js/async/9762.f83bc4f3.js +1 -0
- package/dist/console/static/js/async/984.e11c113a.js +1 -0
- package/dist/console/static/js/async/9846.246653cd.js +1 -0
- package/dist/console/static/js/index.4487e1ff.js +1 -0
- package/dist/console/static/js/lib-react.15d7ca9a.js +2 -0
- package/dist/console/static/js/lib-react.15d7ca9a.js.LICENSE.txt +49 -0
- package/dist/coze-C6PMDPBI.js +49 -0
- package/dist/coze-E6VGRNLV.js +48 -0
- package/dist/dashboard.command-J7XOZNXU.js +8 -0
- package/dist/dashboard.command-RV2NHDKW.js +7 -0
- package/dist/dingtalk-JNRNRN7E.js +77 -0
- package/dist/dingtalk-WZGGIAHJ.js +76 -0
- package/dist/douyin-miniprogram-AIJPPIZH.js +41 -0
- package/dist/douyin-miniprogram-HCYZ5NBW.js +42 -0
- package/dist/figma-2YYNSCDX.js +103 -0
- package/dist/figma-RYOBMENP.js +102 -0
- package/dist/firebase-2IJDDBXX.js +112 -0
- package/dist/firebase-OYSY6HPT.js +111 -0
- package/dist/firecrawl-2T3SBUW7.js +66 -0
- package/dist/firecrawl-IYYXLAZM.js +65 -0
- package/dist/flyai-7FJ4TRAG.js +81 -0
- package/dist/flyai-QS5Q6FJR.js +82 -0
- package/dist/gitagent-MWI75OIX.js +725 -0
- package/dist/gitagent-YBMWY7NZ.js +726 -0
- package/dist/gitee-3N7OFOM7.js +53 -0
- package/dist/gitee-KVNK6KLZ.js +54 -0
- package/dist/github-LUEC2LID.js +143 -0
- package/dist/github-XRO5Z3GC.js +142 -0
- package/dist/google-ads-A3QAJI4D.js +88 -0
- package/dist/google-ads-VPKWTX67.js +89 -0
- package/dist/google-analytics-C4UR5ZR2.js +50 -0
- package/dist/google-analytics-XDYZA2B7.js +49 -0
- package/dist/google-workspace-LL3EWVHH.js +320 -0
- package/dist/google-workspace-YX35SHHX.js +321 -0
- package/dist/huawei-xiaoyi-6BSMGJHR.js +40 -0
- package/dist/huawei-xiaoyi-KPWLTSHB.js +41 -0
- package/dist/hubspot-DIUHGEDI.js +45 -0
- package/dist/hubspot-FTIEMNZO.js +44 -0
- package/dist/huggingface-MJCOXA7E.js +116 -0
- package/dist/huggingface-UUXK2RHK.js +117 -0
- package/dist/index.d.ts +3013 -0
- package/dist/index.js +15649 -0
- package/dist/inference-ai-image-generation-CMI6R5T3.js +106 -0
- package/dist/inference-ai-image-generation-PXV6IG4U.js +107 -0
- package/dist/inference-sh-7AZOLEFI.js +94 -0
- package/dist/inference-sh-ABQOD3YF.js +95 -0
- package/dist/init.command-6E24K4H3.js +9 -0
- package/dist/init.command-O4HG4HKR.js +10 -0
- package/dist/klaviyo-6K5YEFNH.js +45 -0
- package/dist/klaviyo-LDPBWBSS.js +44 -0
- package/dist/kuaidi100-HGFM5VK2.js +42 -0
- package/dist/kuaidi100-UHPFCVXP.js +41 -0
- package/dist/lark-6LNA3LUQ.js +103 -0
- package/dist/lark-URVBZNS4.js +102 -0
- package/dist/linear-7QFSFPOD.js +57 -0
- package/dist/linear-T4ORUP7N.js +56 -0
- package/dist/lovart-PDUXRUHJ.js +99 -0
- package/dist/lovart-QO3SK55T.js +100 -0
- package/dist/meta-ads-SCNFI45S.js +42 -0
- package/dist/meta-ads-V6XPZWX3.js +41 -0
- package/dist/miclaw-5CNTW7VV.js +36 -0
- package/dist/miclaw-TPPPS2WN.js +35 -0
- package/dist/model-provider-AVSFJSZP.js +393 -0
- package/dist/model-provider-KFB76XV5.js +392 -0
- package/dist/notion-FZK76MN2.js +69 -0
- package/dist/notion-WFA7KGZZ.js +70 -0
- package/dist/oceanengine-3JZUS3PP.js +43 -0
- package/dist/oceanengine-5BRIJVJE.js +42 -0
- package/dist/opencli-PFXHGCS2.js +81 -0
- package/dist/opencli-VIGRJTGH.js +80 -0
- package/dist/paypal-33UADIPR.js +54 -0
- package/dist/paypal-Z5JYHIWD.js +55 -0
- package/dist/playwright-MG5WHK47.js +58 -0
- package/dist/playwright-SQAQ3DZG.js +59 -0
- package/dist/plugins-HZBWK3WQ.js +120 -0
- package/dist/plugins-I4GD5SZX.js +121 -0
- package/dist/posthog-MU5MAJOQ.js +79 -0
- package/dist/posthog-RJRRKDWB.js +80 -0
- package/dist/salesforce-34FVIJTG.js +82 -0
- package/dist/salesforce-3QZ6OFVO.js +83 -0
- package/dist/sentry-MCIRMACU.js +111 -0
- package/dist/sentry-PIWW46VA.js +110 -0
- package/dist/seo-suite-4SQ3I67Q.js +54 -0
- package/dist/seo-suite-WJXMA3S4.js +55 -0
- package/dist/serve.command-5FMIPQRY.js +10 -0
- package/dist/serve.command-DNE6GPMK.js +9 -0
- package/dist/shadowob-JELOWHWX.js +1068 -0
- package/dist/shadowob-PRSMI5MW.js +1069 -0
- package/dist/sherlock-2PKY2E2Y.js +66 -0
- package/dist/sherlock-C5ZWPPVT.js +67 -0
- package/dist/shopify-GL3NFVGE.js +94 -0
- package/dist/shopify-R4G3UXM6.js +93 -0
- package/dist/skill-discovery-7INAUP4D.js +77 -0
- package/dist/skill-discovery-YPXXV622.js +78 -0
- package/dist/state-7MCZBTR5.js +17 -0
- package/dist/state-FGOFLFBE.js +18 -0
- package/dist/stripe-C22RR4ZS.js +83 -0
- package/dist/stripe-LJNPQ3CQ.js +82 -0
- package/dist/supabase-IRNQ54FJ.js +98 -0
- package/dist/supabase-N4ONFJNQ.js +97 -0
- package/dist/taobao-aipaas-LRR4GMO3.js +45 -0
- package/dist/taobao-aipaas-RVKORSF4.js +46 -0
- package/dist/tapd-3JPVJ7XH.js +46 -0
- package/dist/tapd-TMQRSMFG.js +47 -0
- package/dist/tencent-ads-IGD33LO7.js +42 -0
- package/dist/tencent-ads-UHC6OPBV.js +43 -0
- package/dist/tencent-docs-C3A4J3CJ.js +47 -0
- package/dist/tencent-docs-O2SC4FHL.js +48 -0
- package/dist/tencent-maps-HMMWMNF4.js +37 -0
- package/dist/tencent-maps-OQOKHVW2.js +36 -0
- package/dist/vercel-KOXDDTHX.js +73 -0
- package/dist/vercel-OLNVDWMG.js +74 -0
- package/dist/webflow-FULU5Q2I.js +114 -0
- package/dist/webflow-OMJKZM54.js +115 -0
- package/dist/wechat-miniprogram-skyline-KYCDMQNW.js +74 -0
- package/dist/wechat-miniprogram-skyline-VR4FVIQL.js +75 -0
- package/dist/wechat-pay-BCMAJ6UW.js +50 -0
- package/dist/wechat-pay-YQQKXVUI.js +51 -0
- package/dist/wonda-NGWIORYN.js +81 -0
- package/dist/wonda-RBABXFNM.js +82 -0
- package/dist/wordpress-woocommerce-RDIUTHYT.js +57 -0
- package/dist/wordpress-woocommerce-RNA5HB3N.js +58 -0
- package/dist/wps-DAEFQHDE.js +47 -0
- package/dist/wps-LUWHMZQQ.js +48 -0
- package/dist/yuque-HCHTJWNI.js +72 -0
- package/dist/yuque-KRH5O74J.js +71 -0
- package/images/RUNNERS.md +270 -0
- package/images/cc-connect-runner/entrypoint.mjs +311 -0
- package/images/claude-runner/Dockerfile +88 -0
- package/images/claude-runner/RUNNER.md +222 -0
- package/images/claude-runner/entrypoint.mjs +2 -0
- package/images/codex-runner/Dockerfile +87 -0
- package/images/codex-runner/RUNNER.md +226 -0
- package/images/codex-runner/entrypoint.mjs +2 -0
- package/images/gemini-runner/Dockerfile +87 -0
- package/images/gemini-runner/RUNNER.md +218 -0
- package/images/gemini-runner/entrypoint.mjs +2 -0
- package/images/hermes-runner/Dockerfile +74 -0
- package/images/hermes-runner/RUNNER.md +243 -0
- package/images/hermes-runner/entrypoint.mjs +283 -0
- package/images/openclaw-runner/Dockerfile +212 -0
- package/images/openclaw-runner/RUNNER.md +170 -0
- package/images/openclaw-runner/entrypoint.mjs +1113 -0
- package/images/openclaw-runner/warm-runtime-deps.mjs +95 -0
- package/images/opencode-runner/Dockerfile +87 -0
- package/images/opencode-runner/RUNNER.md +202 -0
- package/images/opencode-runner/entrypoint.mjs +2 -0
- package/package.json +121 -0
- package/templates/agent-marketplace-buddy.template.json +131 -0
- package/templates/ai-werewolf.template.json +92 -0
- package/templates/bmad-method-buddy.template.json +123 -0
- package/templates/brain-fix.template.json +92 -0
- package/templates/claude-ads-buddy.template.json +123 -0
- package/templates/claude-financial-services-buddy.template.json +111 -0
- package/templates/claude-seo-buddy.template.json +123 -0
- package/templates/code-arena.template.json +92 -0
- package/templates/daily-brief.template.json +92 -0
- package/templates/e-wife.template.json +92 -0
- package/templates/everything-claude-code-buddy.template.json +125 -0
- package/templates/financial-freedom.template.json +92 -0
- package/templates/gitstory.template.json +92 -0
- package/templates/google-workspace-buddy.template.json +88 -0
- package/templates/gsd-buddy.template.json +119 -0
- package/templates/gstack-buddy.template.json +143 -0
- package/templates/gstack.template.json +92 -0
- package/templates/little-match-girl.template.json +114 -0
- package/templates/lovart-buddy.template.json +110 -0
- package/templates/marketingskills-buddy.template.json +102 -0
- package/templates/retire-buddy.template.json +92 -0
- package/templates/scientific-skills-buddy.template.json +119 -0
- package/templates/seomachine-buddy.template.json +113 -0
- package/templates/shadow-server-app-demo.template.json +105 -0
- package/templates/slavingia-skills-buddy.template.json +102 -0
- package/templates/superclaude-buddy.template.json +146 -0
- package/templates/superpowers-buddy.template.json +108 -0
- package/templates/world-pulse.template.json +92 -0
|
@@ -0,0 +1,1464 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
AGENT_PACK_SLASH_INDEXER_SCRIPT
|
|
4
|
+
} from "./chunk-ZUYL3W53.js";
|
|
5
|
+
import {
|
|
6
|
+
definePlugin
|
|
7
|
+
} from "./chunk-CTNUKOQE.js";
|
|
8
|
+
import "./chunk-AD3JTIU3.js";
|
|
9
|
+
|
|
10
|
+
// src/plugins/claude-plugin/importer-script.ts
|
|
11
|
+
var CLAUDE_PLUGIN_IMPORTER_SCRIPT = String.raw`
|
|
12
|
+
import { execFileSync } from 'node:child_process'
|
|
13
|
+
import {
|
|
14
|
+
chmodSync,
|
|
15
|
+
cpSync,
|
|
16
|
+
existsSync,
|
|
17
|
+
lstatSync,
|
|
18
|
+
mkdirSync,
|
|
19
|
+
mkdtempSync,
|
|
20
|
+
readdirSync,
|
|
21
|
+
readFileSync,
|
|
22
|
+
rmSync,
|
|
23
|
+
writeFileSync,
|
|
24
|
+
} from 'node:fs'
|
|
25
|
+
import { basename, dirname, extname, join, normalize, relative } from 'node:path'
|
|
26
|
+
import { tmpdir } from 'node:os'
|
|
27
|
+
|
|
28
|
+
const DEFAULT_MARKETPLACE_PATH = '.claude-plugin/marketplace.json'
|
|
29
|
+
const DEFAULT_INSTRUCTION_FILES = [
|
|
30
|
+
'README.md',
|
|
31
|
+
'CLAUDE.md',
|
|
32
|
+
'AGENTS.md',
|
|
33
|
+
'LICENSE',
|
|
34
|
+
'LICENSE.md',
|
|
35
|
+
'CHANGELOG.md',
|
|
36
|
+
]
|
|
37
|
+
const EXCLUDED_DIRS = new Set([
|
|
38
|
+
'.git',
|
|
39
|
+
'node_modules',
|
|
40
|
+
'dist',
|
|
41
|
+
'build',
|
|
42
|
+
'vendor',
|
|
43
|
+
'coverage',
|
|
44
|
+
'__pycache__',
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
function isPlainObject(value) {
|
|
48
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function asStringArray(value) {
|
|
52
|
+
if (typeof value === 'string' && value.trim()) return [value.trim()]
|
|
53
|
+
if (!Array.isArray(value)) return []
|
|
54
|
+
return value.filter((item) => typeof item === 'string' && item.trim()).map((item) => item.trim())
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function asInlineJsonComponents(value) {
|
|
58
|
+
if (isPlainObject(value)) return [value]
|
|
59
|
+
if (!Array.isArray(value)) return []
|
|
60
|
+
return value.some((item) => isPlainObject(item) || Array.isArray(item)) ? [value] : []
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function sanitizeId(value, fallback = 'plugin') {
|
|
64
|
+
const raw = String(value || fallback)
|
|
65
|
+
.replace(/\.git$/i, '')
|
|
66
|
+
.replace(/[^A-Za-z0-9._-]+/g, '-')
|
|
67
|
+
.replace(/^-+|-+$/g, '')
|
|
68
|
+
.slice(0, 63)
|
|
69
|
+
return raw || fallback
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readJson(path) {
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(readFileSync(path, 'utf-8'))
|
|
75
|
+
} catch {
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function writeJson(path, value) {
|
|
81
|
+
mkdirSync(dirname(path), { recursive: true })
|
|
82
|
+
writeFileSync(path, JSON.stringify(value, null, 2), 'utf-8')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function safeRelativePath(root, input) {
|
|
86
|
+
if (typeof input !== 'string' || !input.trim()) return null
|
|
87
|
+
let rel = input.trim().replace(/\\/g, '/')
|
|
88
|
+
if (rel.startsWith('./')) rel = rel.slice(2)
|
|
89
|
+
if (rel === '.' || rel === '') return root
|
|
90
|
+
if (rel.startsWith('/') || rel.split('/').includes('..')) return null
|
|
91
|
+
const resolved = normalize(join(root, rel))
|
|
92
|
+
const rootPrefix = normalize(root)
|
|
93
|
+
return resolved === rootPrefix || resolved.startsWith(rootPrefix + '/') ? resolved : null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function statMaybe(path) {
|
|
97
|
+
try {
|
|
98
|
+
return lstatSync(path)
|
|
99
|
+
} catch {
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function listChildDirs(dir) {
|
|
105
|
+
try {
|
|
106
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
107
|
+
.filter((entry) => entry.isDirectory() && !EXCLUDED_DIRS.has(entry.name))
|
|
108
|
+
.map((entry) => join(dir, entry.name))
|
|
109
|
+
} catch {
|
|
110
|
+
return []
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function listFilesRecursive(dir, maxDepth, depth = 0) {
|
|
115
|
+
if (depth > maxDepth) return []
|
|
116
|
+
let entries = []
|
|
117
|
+
try {
|
|
118
|
+
entries = readdirSync(dir, { withFileTypes: true })
|
|
119
|
+
} catch {
|
|
120
|
+
return []
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const out = []
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
if (entry.name.startsWith('.') && entry.name !== '.mcp.json' && entry.name !== '.lsp.json') {
|
|
126
|
+
continue
|
|
127
|
+
}
|
|
128
|
+
const path = join(dir, entry.name)
|
|
129
|
+
if (entry.isDirectory()) {
|
|
130
|
+
if (EXCLUDED_DIRS.has(entry.name)) continue
|
|
131
|
+
out.push(...listFilesRecursive(path, maxDepth, depth + 1))
|
|
132
|
+
} else if (entry.isFile()) {
|
|
133
|
+
out.push(path)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return out
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function copyTree(src, dest) {
|
|
140
|
+
mkdirSync(dirname(dest), { recursive: true })
|
|
141
|
+
rmSync(dest, { recursive: true, force: true })
|
|
142
|
+
cpSync(src, dest, { recursive: true, force: true, dereference: false })
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function copyFile(src, dest) {
|
|
146
|
+
mkdirSync(dirname(dest), { recursive: true })
|
|
147
|
+
cpSync(src, dest, { force: true, dereference: false })
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function copyDirectoryContents(src, dest) {
|
|
151
|
+
const stat = statMaybe(src)
|
|
152
|
+
if (!stat) return 0
|
|
153
|
+
mkdirSync(dest, { recursive: true })
|
|
154
|
+
if (stat.isFile()) {
|
|
155
|
+
copyFile(src, join(dest, basename(src)))
|
|
156
|
+
return 1
|
|
157
|
+
}
|
|
158
|
+
if (!stat.isDirectory()) return 0
|
|
159
|
+
copyTree(src, dest)
|
|
160
|
+
return 1
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function normalizeGitUrl(value) {
|
|
164
|
+
if (typeof value !== 'string' || !value.trim()) return ''
|
|
165
|
+
const input = value.trim()
|
|
166
|
+
if (/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(input)) {
|
|
167
|
+
return 'https://github.com/' + input + '.git'
|
|
168
|
+
}
|
|
169
|
+
return input
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function writeGithubNetrc() {
|
|
173
|
+
const token = process.env.GITHUB_TOKEN
|
|
174
|
+
if (!token) return
|
|
175
|
+
|
|
176
|
+
const home = join(tmpdir(), 'shadow-claude-plugin-home')
|
|
177
|
+
mkdirSync(home, { recursive: true })
|
|
178
|
+
writeFileSync(
|
|
179
|
+
join(home, '.netrc'),
|
|
180
|
+
'machine github.com\nlogin x-access-token\npassword ' + token + '\n',
|
|
181
|
+
{ encoding: 'utf-8', mode: 0o600 },
|
|
182
|
+
)
|
|
183
|
+
process.env.HOME = home
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function cloneGit(source, scratchRoot, cache) {
|
|
187
|
+
const url = normalizeGitUrl(source.url)
|
|
188
|
+
if (!url) throw new Error('Missing git URL for Claude plugin source ' + source.id)
|
|
189
|
+
const ref = source.ref || ''
|
|
190
|
+
const sha = source.sha || ''
|
|
191
|
+
const cacheKey = [url, ref, sha].join('|')
|
|
192
|
+
if (cache.has(cacheKey)) return cache.get(cacheKey)
|
|
193
|
+
|
|
194
|
+
const dest = join(scratchRoot, sanitizeId(source.id || basename(url)) + '-' + cache.size)
|
|
195
|
+
const args = ['clone', '--depth', String(Math.max(1, Number(source.depth || 1)))]
|
|
196
|
+
if (ref) args.push('--branch', ref)
|
|
197
|
+
args.push(url, dest)
|
|
198
|
+
execFileSync('git', args, { stdio: 'inherit' })
|
|
199
|
+
if (sha) execFileSync('git', ['-C', dest, 'checkout', '--detach', sha], { stdio: 'inherit' })
|
|
200
|
+
cache.set(cacheKey, dest)
|
|
201
|
+
return dest
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function pluginManifest(pluginRoot) {
|
|
205
|
+
const manifest = readJson(join(pluginRoot, '.claude-plugin', 'plugin.json'))
|
|
206
|
+
return isPlainObject(manifest) ? manifest : {}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function looksLikeClaudePluginRoot(dir) {
|
|
210
|
+
return (
|
|
211
|
+
existsSync(join(dir, '.claude-plugin', 'plugin.json')) ||
|
|
212
|
+
existsSync(join(dir, 'skills')) ||
|
|
213
|
+
existsSync(join(dir, 'commands')) ||
|
|
214
|
+
existsSync(join(dir, 'agents')) ||
|
|
215
|
+
existsSync(join(dir, 'hooks')) ||
|
|
216
|
+
existsSync(join(dir, 'monitors')) ||
|
|
217
|
+
existsSync(join(dir, 'bin')) ||
|
|
218
|
+
existsSync(join(dir, 'output-styles')) ||
|
|
219
|
+
existsSync(join(dir, 'themes')) ||
|
|
220
|
+
existsSync(join(dir, '.mcp.json')) ||
|
|
221
|
+
existsSync(join(dir, '.lsp.json')) ||
|
|
222
|
+
existsSync(join(dir, 'settings.json'))
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function selectedPlugin(root, include) {
|
|
227
|
+
if (!include || include.length === 0) return true
|
|
228
|
+
const manifest = pluginManifest(root)
|
|
229
|
+
const names = new Set([basename(root), manifest.name, manifest.id].filter(Boolean))
|
|
230
|
+
return include.some((item) => names.has(item))
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function discoverPluginRoots(base, include) {
|
|
234
|
+
const direct = statMaybe(base)
|
|
235
|
+
if (!direct || !direct.isDirectory()) return []
|
|
236
|
+
if (looksLikeClaudePluginRoot(base) && selectedPlugin(base, include)) {
|
|
237
|
+
return [{ root: base, entry: {} }]
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return listChildDirs(base)
|
|
241
|
+
.filter((child) => looksLikeClaudePluginRoot(child) && selectedPlugin(child, include))
|
|
242
|
+
.map((child) => ({ root: child, entry: {} }))
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function componentPathValues(manifest, entry, field) {
|
|
246
|
+
const values = []
|
|
247
|
+
values.push(...asStringArray(manifest[field]))
|
|
248
|
+
values.push(...asStringArray(entry[field]))
|
|
249
|
+
return [...new Set(values)]
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function componentValues(manifest, entry, field) {
|
|
253
|
+
return [manifest[field], entry[field]].filter((value) => value !== undefined)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function experimentalValues(manifest, entry, field) {
|
|
257
|
+
return [manifest.experimental?.[field], entry.experimental?.[field]].filter(
|
|
258
|
+
(value) => value !== undefined,
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function componentAndExperimentalValues(manifest, entry, field) {
|
|
263
|
+
return [...componentValues(manifest, entry, field), ...experimentalValues(manifest, entry, field)]
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function componentStringValues(values) {
|
|
267
|
+
const out = []
|
|
268
|
+
for (const value of values) out.push(...asStringArray(value))
|
|
269
|
+
return [...new Set(out)]
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function parseFrontmatterName(text) {
|
|
273
|
+
if (!text.startsWith('---')) return null
|
|
274
|
+
const end = text.indexOf('\n---', 3)
|
|
275
|
+
if (end === -1) return null
|
|
276
|
+
const raw = text.slice(3, end).trim()
|
|
277
|
+
const match = raw.match(/^name:\s*(.+)$/m)
|
|
278
|
+
if (!match?.[1]) return null
|
|
279
|
+
return match[1].trim().replace(/^['"]|['"]$/g, '')
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function frontmatterNameFromFile(path) {
|
|
283
|
+
try {
|
|
284
|
+
return parseFrontmatterName(readFileSync(path, 'utf-8'))
|
|
285
|
+
} catch {
|
|
286
|
+
return null
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function skillSlugForDir(skillDir, fallback) {
|
|
291
|
+
return sanitizeId(frontmatterNameFromFile(join(skillDir, 'SKILL.md')) || fallback, fallback)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function markdownSlug(path, fallback) {
|
|
295
|
+
const ext = extname(path)
|
|
296
|
+
const base = ext ? basename(path, ext) : basename(path)
|
|
297
|
+
return sanitizeId(frontmatterNameFromFile(path) || base || fallback, fallback)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function copySkillDir(skillDir, slug, pluginId, perPluginSkills, globalSkills) {
|
|
301
|
+
const safeSlug = skillSlugForDir(skillDir, slug || pluginId)
|
|
302
|
+
copyTree(skillDir, join(perPluginSkills, safeSlug))
|
|
303
|
+
copyTree(skillDir, join(globalSkills, sanitizeId(pluginId + '-' + safeSlug)))
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function copySkillFile(skillFile, slug, pluginId, perPluginSkills, globalSkills) {
|
|
307
|
+
const safeSlug = sanitizeId(frontmatterNameFromFile(skillFile) || slug, pluginId)
|
|
308
|
+
const text = readFileSync(skillFile, 'utf-8')
|
|
309
|
+
for (const root of [perPluginSkills, globalSkills]) {
|
|
310
|
+
const actualSlug = root === globalSkills ? sanitizeId(pluginId + '-' + safeSlug) : safeSlug
|
|
311
|
+
const skillPath = join(root, actualSlug, 'SKILL.md')
|
|
312
|
+
mkdirSync(dirname(skillPath), { recursive: true })
|
|
313
|
+
writeFileSync(skillPath, text, 'utf-8')
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function copySkillsFromPath(src, pluginId, perPluginSkills, globalSkills) {
|
|
318
|
+
const stat = statMaybe(src)
|
|
319
|
+
if (!stat) return 0
|
|
320
|
+
|
|
321
|
+
let count = 0
|
|
322
|
+
if (stat.isFile() && basename(src).endsWith('SKILL.md')) {
|
|
323
|
+
copySkillFile(src, markdownSlug(src, pluginId).replace(/-?SKILL$/i, '') || pluginId, pluginId, perPluginSkills, globalSkills)
|
|
324
|
+
return 1
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (!stat.isDirectory()) return 0
|
|
328
|
+
if (existsSync(join(src, 'SKILL.md'))) {
|
|
329
|
+
copySkillDir(src, basename(src) === '.' ? pluginId : basename(src), pluginId, perPluginSkills, globalSkills)
|
|
330
|
+
count++
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
for (const child of listChildDirs(src)) {
|
|
334
|
+
if (!existsSync(join(child, 'SKILL.md'))) continue
|
|
335
|
+
copySkillDir(child, basename(child), pluginId, perPluginSkills, globalSkills)
|
|
336
|
+
count++
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const copiedParents = new Set()
|
|
340
|
+
for (const file of listFilesRecursive(src, 6).filter((item) => basename(item) === 'SKILL.md')) {
|
|
341
|
+
const parent = dirname(file)
|
|
342
|
+
if (parent === src || copiedParents.has(parent)) continue
|
|
343
|
+
copiedParents.add(parent)
|
|
344
|
+
const rel = relative(src, parent)
|
|
345
|
+
const slug = sanitizeId(rel.split('/').join('-'), basename(parent))
|
|
346
|
+
copySkillDir(parent, slug, pluginId, perPluginSkills, globalSkills)
|
|
347
|
+
count++
|
|
348
|
+
}
|
|
349
|
+
return count
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function skillMarkdownFromCommandFile(path, pluginId, kind) {
|
|
353
|
+
const text = readFileSync(path, 'utf-8')
|
|
354
|
+
if (kind !== 'agents') return text
|
|
355
|
+
if (text.startsWith('---')) return text
|
|
356
|
+
return [
|
|
357
|
+
'---',
|
|
358
|
+
'name: ' + JSON.stringify(markdownSlug(path, pluginId)),
|
|
359
|
+
'description: Claude plugin agent imported from ' + pluginId + '.',
|
|
360
|
+
'---',
|
|
361
|
+
'',
|
|
362
|
+
text,
|
|
363
|
+
].join('\n')
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function copyMarkdownCapability(path, pluginId, perPluginDir, globalSkills, kind) {
|
|
367
|
+
const slug = markdownSlug(path, pluginId)
|
|
368
|
+
const text = skillMarkdownFromCommandFile(path, pluginId, kind)
|
|
369
|
+
const perDir = join(perPluginDir, slug)
|
|
370
|
+
mkdirSync(perDir, { recursive: true })
|
|
371
|
+
writeFileSync(join(perDir, 'SKILL.md'), text, 'utf-8')
|
|
372
|
+
if (kind === 'agents') writeFileSync(join(perDir, 'AGENT.md'), text, 'utf-8')
|
|
373
|
+
|
|
374
|
+
const globalDir = join(globalSkills, sanitizeId(pluginId + '-' + slug))
|
|
375
|
+
mkdirSync(globalDir, { recursive: true })
|
|
376
|
+
writeFileSync(join(globalDir, 'SKILL.md'), text, 'utf-8')
|
|
377
|
+
if (kind === 'agents') writeFileSync(join(globalDir, 'AGENT.md'), text, 'utf-8')
|
|
378
|
+
return 1
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function copyMarkdownCapabilitiesFromPath(src, pluginId, perPluginDir, globalSkills, kind) {
|
|
382
|
+
const stat = statMaybe(src)
|
|
383
|
+
if (!stat) return 0
|
|
384
|
+
if (stat.isFile()) {
|
|
385
|
+
return extname(src).toLowerCase() === '.md'
|
|
386
|
+
? copyMarkdownCapability(src, pluginId, perPluginDir, globalSkills, kind)
|
|
387
|
+
: 0
|
|
388
|
+
}
|
|
389
|
+
if (!stat.isDirectory()) return 0
|
|
390
|
+
|
|
391
|
+
let count = 0
|
|
392
|
+
for (const file of listFilesRecursive(src, 5).filter((item) => extname(item).toLowerCase() === '.md')) {
|
|
393
|
+
count += copyMarkdownCapability(file, pluginId, perPluginDir, globalSkills, kind)
|
|
394
|
+
}
|
|
395
|
+
return count
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function copyJsonLikePath(src, destDir, fallbackName) {
|
|
399
|
+
const stat = statMaybe(src)
|
|
400
|
+
if (!stat) return 0
|
|
401
|
+
if (stat.isFile()) {
|
|
402
|
+
copyFile(src, join(destDir, basename(src) || fallbackName))
|
|
403
|
+
return 1
|
|
404
|
+
}
|
|
405
|
+
if (!stat.isDirectory()) return 0
|
|
406
|
+
let count = 0
|
|
407
|
+
for (const file of listFilesRecursive(src, 2).filter((item) => extname(item).toLowerCase() === '.json')) {
|
|
408
|
+
copyFile(file, join(destDir, basename(file)))
|
|
409
|
+
count++
|
|
410
|
+
}
|
|
411
|
+
return count
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function copyMarkdownLikePath(src, destDir) {
|
|
415
|
+
const stat = statMaybe(src)
|
|
416
|
+
if (!stat) return 0
|
|
417
|
+
if (stat.isFile()) {
|
|
418
|
+
const ext = extname(src).toLowerCase()
|
|
419
|
+
if (ext !== '.md' && ext !== '.mdx' && ext !== '.txt') return 0
|
|
420
|
+
copyFile(src, join(destDir, basename(src)))
|
|
421
|
+
return 1
|
|
422
|
+
}
|
|
423
|
+
if (!stat.isDirectory()) return 0
|
|
424
|
+
|
|
425
|
+
let count = 0
|
|
426
|
+
for (const file of listFilesRecursive(src, 4).filter((item) =>
|
|
427
|
+
['.md', '.mdx', '.txt'].includes(extname(item).toLowerCase()),
|
|
428
|
+
)) {
|
|
429
|
+
copyFile(file, join(destDir, relative(src, file)))
|
|
430
|
+
count++
|
|
431
|
+
}
|
|
432
|
+
return count
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function copyInlineJsonComponents(values, destDir, fallbackName, transform = (value) => value) {
|
|
436
|
+
let count = 0
|
|
437
|
+
for (const value of values) {
|
|
438
|
+
for (const item of asInlineJsonComponents(value)) {
|
|
439
|
+
const ext = extname(fallbackName)
|
|
440
|
+
const base = ext ? fallbackName.slice(0, -ext.length) : fallbackName
|
|
441
|
+
const name = count === 0 ? fallbackName : base + '-' + (count + 1) + ext
|
|
442
|
+
writeJson(join(destDir, name), transform(item))
|
|
443
|
+
count++
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return count
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function copyManifestMetadata(pluginRoot, pluginDest, manifest, entry, pluginId) {
|
|
450
|
+
let count = 0
|
|
451
|
+
const manifestPath = join(pluginRoot, '.claude-plugin', 'plugin.json')
|
|
452
|
+
if (existsSync(manifestPath)) {
|
|
453
|
+
copyFile(manifestPath, join(pluginDest, '.claude-plugin', 'plugin.json'))
|
|
454
|
+
count++
|
|
455
|
+
} else {
|
|
456
|
+
writeJson(join(pluginDest, '.claude-plugin', 'plugin.json'), {
|
|
457
|
+
name: pluginId,
|
|
458
|
+
...(manifest.description || entry.description
|
|
459
|
+
? { description: manifest.description || entry.description }
|
|
460
|
+
: {}),
|
|
461
|
+
})
|
|
462
|
+
count++
|
|
463
|
+
}
|
|
464
|
+
writeJson(join(pluginDest, '.claude-plugin', 'marketplace-entry.json'), entry)
|
|
465
|
+
return count + 1
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function wrapMcpInlineConfig(value) {
|
|
469
|
+
return isPlainObject(value?.mcpServers) ? value : { mcpServers: value }
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function copyExecutablePath(src, pluginId, perPluginBin, globalBin) {
|
|
473
|
+
const stat = statMaybe(src)
|
|
474
|
+
if (!stat) return 0
|
|
475
|
+
const files = stat.isFile() ? [src] : listFilesRecursive(src, 4)
|
|
476
|
+
let count = 0
|
|
477
|
+
for (const file of files) {
|
|
478
|
+
const fileStat = statMaybe(file)
|
|
479
|
+
if (!fileStat?.isFile()) continue
|
|
480
|
+
const rel = stat.isFile() ? basename(file) : relative(src, file)
|
|
481
|
+
if (!rel || rel.split('/').some((part) => part.startsWith('.'))) continue
|
|
482
|
+
const name = basename(file)
|
|
483
|
+
if (
|
|
484
|
+
!name ||
|
|
485
|
+
['package.json', 'package-lock.json', 'pnpm-lock.yaml', 'yarn.lock'].includes(name)
|
|
486
|
+
) {
|
|
487
|
+
continue
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const perPluginDest = join(perPluginBin, rel)
|
|
491
|
+
copyFile(file, perPluginDest)
|
|
492
|
+
chmodSync(perPluginDest, 0o755)
|
|
493
|
+
|
|
494
|
+
const prefixedDest = join(globalBin, sanitizeId(pluginId + '-' + name))
|
|
495
|
+
copyFile(file, prefixedDest)
|
|
496
|
+
chmodSync(prefixedDest, 0o755)
|
|
497
|
+
|
|
498
|
+
const bareDest = join(globalBin, name)
|
|
499
|
+
if (!existsSync(bareDest)) {
|
|
500
|
+
copyFile(file, bareDest)
|
|
501
|
+
chmodSync(bareDest, 0o755)
|
|
502
|
+
}
|
|
503
|
+
count++
|
|
504
|
+
}
|
|
505
|
+
return count
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function copyInstructions(pluginRoot, pluginId, destDir, manifest, entry) {
|
|
509
|
+
mkdirSync(destDir, { recursive: true })
|
|
510
|
+
let count = 0
|
|
511
|
+
for (const file of DEFAULT_INSTRUCTION_FILES) {
|
|
512
|
+
const src = join(pluginRoot, file)
|
|
513
|
+
if (!existsSync(src)) continue
|
|
514
|
+
copyFile(src, join(destDir, file))
|
|
515
|
+
count++
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const summary = [
|
|
519
|
+
'# ' + pluginId,
|
|
520
|
+
'',
|
|
521
|
+
manifest.description || entry.description || '',
|
|
522
|
+
'',
|
|
523
|
+
'- Source plugin root: ' + pluginRoot,
|
|
524
|
+
].filter(Boolean)
|
|
525
|
+
writeFileSync(join(destDir, pluginId + '.md'), summary.join('\n'), 'utf-8')
|
|
526
|
+
return count + 1
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function importPluginRoot(ctx, pluginRoot, entry) {
|
|
530
|
+
const manifest = pluginManifest(pluginRoot)
|
|
531
|
+
const pluginId = sanitizeId(manifest.name || entry.name || basename(pluginRoot))
|
|
532
|
+
const pluginDest = join(ctx.mountPath, pluginId)
|
|
533
|
+
const globalSkills = join(ctx.mountPath, '.shadow', 'skills')
|
|
534
|
+
rmSync(pluginDest, { recursive: true, force: true })
|
|
535
|
+
mkdirSync(pluginDest, { recursive: true })
|
|
536
|
+
|
|
537
|
+
const perPluginSkills = join(pluginDest, 'skills')
|
|
538
|
+
const perPluginCommands = join(pluginDest, 'commands')
|
|
539
|
+
const perPluginAgents = join(pluginDest, 'agents')
|
|
540
|
+
const perPluginHooks = join(pluginDest, 'hooks')
|
|
541
|
+
const perPluginMcp = join(pluginDest, 'mcp')
|
|
542
|
+
const perPluginLsp = join(pluginDest, 'lsp')
|
|
543
|
+
const perPluginMonitors = join(pluginDest, 'monitors')
|
|
544
|
+
const perPluginOutputStyles = join(pluginDest, 'output-styles')
|
|
545
|
+
const perPluginThemes = join(pluginDest, 'themes')
|
|
546
|
+
const perPluginBin = join(pluginDest, 'bin')
|
|
547
|
+
const perPluginScripts = join(pluginDest, 'scripts')
|
|
548
|
+
const perPluginSettings = join(pluginDest, 'settings')
|
|
549
|
+
const perPluginInstructions = join(pluginDest, 'instructions')
|
|
550
|
+
const globalBin = join(ctx.mountPath, '.shadow', 'bin')
|
|
551
|
+
|
|
552
|
+
const counts = {
|
|
553
|
+
skills: 0,
|
|
554
|
+
commands: 0,
|
|
555
|
+
agents: 0,
|
|
556
|
+
hooks: 0,
|
|
557
|
+
mcp: 0,
|
|
558
|
+
lsp: 0,
|
|
559
|
+
monitors: 0,
|
|
560
|
+
outputStyles: 0,
|
|
561
|
+
themes: 0,
|
|
562
|
+
bin: 0,
|
|
563
|
+
scripts: 0,
|
|
564
|
+
settings: 0,
|
|
565
|
+
instructions: 0,
|
|
566
|
+
metadata: 0,
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
counts.metadata += copyManifestMetadata(pluginRoot, pluginDest, manifest, entry, pluginId)
|
|
570
|
+
|
|
571
|
+
const defaultSkills = join(pluginRoot, 'skills')
|
|
572
|
+
const skillPaths = [
|
|
573
|
+
...(existsSync(defaultSkills) ? ['./skills'] : []),
|
|
574
|
+
...componentPathValues(manifest, entry, 'skills'),
|
|
575
|
+
]
|
|
576
|
+
for (const rel of [...new Set(skillPaths)]) {
|
|
577
|
+
const src = safeRelativePath(pluginRoot, rel)
|
|
578
|
+
if (src) counts.skills += copySkillsFromPath(src, pluginId, perPluginSkills, globalSkills)
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const commandPaths = componentPathValues(manifest, entry, 'commands')
|
|
582
|
+
if (commandPaths.length === 0 && existsSync(join(pluginRoot, 'commands'))) commandPaths.push('./commands')
|
|
583
|
+
for (const rel of [...new Set(commandPaths)]) {
|
|
584
|
+
const src = safeRelativePath(pluginRoot, rel)
|
|
585
|
+
if (src) counts.commands += copyMarkdownCapabilitiesFromPath(src, pluginId, perPluginCommands, globalSkills, 'commands')
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const agentPaths = componentPathValues(manifest, entry, 'agents')
|
|
589
|
+
if (agentPaths.length === 0 && existsSync(join(pluginRoot, 'agents'))) agentPaths.push('./agents')
|
|
590
|
+
for (const rel of [...new Set(agentPaths)]) {
|
|
591
|
+
const src = safeRelativePath(pluginRoot, rel)
|
|
592
|
+
if (src) counts.agents += copyMarkdownCapabilitiesFromPath(src, pluginId, perPluginAgents, globalSkills, 'agents')
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const hookPaths = componentPathValues(manifest, entry, 'hooks')
|
|
596
|
+
if (existsSync(join(pluginRoot, 'hooks'))) hookPaths.unshift('./hooks')
|
|
597
|
+
for (const rel of [...new Set(hookPaths)]) {
|
|
598
|
+
const src = safeRelativePath(pluginRoot, rel)
|
|
599
|
+
if (src) counts.hooks += copyJsonLikePath(src, perPluginHooks, 'hooks.json')
|
|
600
|
+
}
|
|
601
|
+
counts.hooks += copyInlineJsonComponents(
|
|
602
|
+
componentValues(manifest, entry, 'hooks'),
|
|
603
|
+
perPluginHooks,
|
|
604
|
+
'hooks-inline.json',
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
const mcpPaths = componentPathValues(manifest, entry, 'mcpServers')
|
|
608
|
+
if (existsSync(join(pluginRoot, '.mcp.json'))) mcpPaths.unshift('./.mcp.json')
|
|
609
|
+
for (const rel of [...new Set(mcpPaths)]) {
|
|
610
|
+
const src = safeRelativePath(pluginRoot, rel)
|
|
611
|
+
if (src) counts.mcp += copyJsonLikePath(src, perPluginMcp, 'mcp.json')
|
|
612
|
+
}
|
|
613
|
+
counts.mcp += copyInlineJsonComponents(
|
|
614
|
+
componentValues(manifest, entry, 'mcpServers'),
|
|
615
|
+
perPluginMcp,
|
|
616
|
+
'mcp-inline.json',
|
|
617
|
+
wrapMcpInlineConfig,
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
const lspPaths = componentPathValues(manifest, entry, 'lspServers')
|
|
621
|
+
if (existsSync(join(pluginRoot, '.lsp.json'))) lspPaths.unshift('./.lsp.json')
|
|
622
|
+
for (const rel of [...new Set(lspPaths)]) {
|
|
623
|
+
const src = safeRelativePath(pluginRoot, rel)
|
|
624
|
+
if (src) counts.lsp += copyJsonLikePath(src, perPluginLsp, 'lsp.json')
|
|
625
|
+
}
|
|
626
|
+
counts.lsp += copyInlineJsonComponents(
|
|
627
|
+
componentValues(manifest, entry, 'lspServers'),
|
|
628
|
+
perPluginLsp,
|
|
629
|
+
'lsp-inline.json',
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
const monitorValues = componentAndExperimentalValues(manifest, entry, 'monitors')
|
|
633
|
+
const monitorPaths = componentStringValues(monitorValues)
|
|
634
|
+
if (monitorValues.length === 0 && existsSync(join(pluginRoot, 'monitors'))) {
|
|
635
|
+
monitorPaths.push('./monitors')
|
|
636
|
+
}
|
|
637
|
+
for (const rel of [...new Set(monitorPaths)]) {
|
|
638
|
+
const src = safeRelativePath(pluginRoot, rel)
|
|
639
|
+
if (src) counts.monitors += copyJsonLikePath(src, perPluginMonitors, 'monitors.json')
|
|
640
|
+
}
|
|
641
|
+
counts.monitors += copyInlineJsonComponents(
|
|
642
|
+
monitorValues,
|
|
643
|
+
perPluginMonitors,
|
|
644
|
+
'monitors-inline.json',
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
const outputStylePaths = componentPathValues(manifest, entry, 'outputStyles')
|
|
648
|
+
if (outputStylePaths.length === 0 && existsSync(join(pluginRoot, 'output-styles'))) {
|
|
649
|
+
outputStylePaths.push('./output-styles')
|
|
650
|
+
}
|
|
651
|
+
for (const rel of [...new Set(outputStylePaths)]) {
|
|
652
|
+
const src = safeRelativePath(pluginRoot, rel)
|
|
653
|
+
if (src) counts.outputStyles += copyMarkdownLikePath(src, perPluginOutputStyles)
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const themeValues = componentAndExperimentalValues(manifest, entry, 'themes')
|
|
657
|
+
const themePaths = componentStringValues(themeValues)
|
|
658
|
+
if (themeValues.length === 0 && existsSync(join(pluginRoot, 'themes'))) {
|
|
659
|
+
themePaths.push('./themes')
|
|
660
|
+
}
|
|
661
|
+
for (const rel of [...new Set(themePaths)]) {
|
|
662
|
+
const src = safeRelativePath(pluginRoot, rel)
|
|
663
|
+
if (src) counts.themes += copyJsonLikePath(src, perPluginThemes, 'theme.json')
|
|
664
|
+
}
|
|
665
|
+
counts.themes += copyInlineJsonComponents(themeValues, perPluginThemes, 'theme-inline.json')
|
|
666
|
+
|
|
667
|
+
const binRoot = join(pluginRoot, 'bin')
|
|
668
|
+
if (existsSync(binRoot)) counts.bin += copyExecutablePath(binRoot, pluginId, perPluginBin, globalBin)
|
|
669
|
+
|
|
670
|
+
const scriptsRoot = join(pluginRoot, 'scripts')
|
|
671
|
+
if (existsSync(scriptsRoot)) counts.scripts += copyDirectoryContents(scriptsRoot, perPluginScripts)
|
|
672
|
+
|
|
673
|
+
const settingsPath = join(pluginRoot, 'settings.json')
|
|
674
|
+
if (existsSync(settingsPath)) {
|
|
675
|
+
copyFile(settingsPath, join(perPluginSettings, 'settings.json'))
|
|
676
|
+
counts.settings++
|
|
677
|
+
}
|
|
678
|
+
counts.settings += copyInlineJsonComponents(
|
|
679
|
+
componentValues(manifest, entry, 'settings'),
|
|
680
|
+
perPluginSettings,
|
|
681
|
+
'settings-inline.json',
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
counts.instructions += copyInstructions(pluginRoot, pluginId, perPluginInstructions, manifest, entry)
|
|
685
|
+
writeJson(join(pluginDest, '.claude-plugin-import.json'), {
|
|
686
|
+
id: pluginId,
|
|
687
|
+
manifestName: manifest.name,
|
|
688
|
+
entryName: entry.name,
|
|
689
|
+
root: pluginRoot,
|
|
690
|
+
counts,
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
ctx.imported.push({ id: pluginId, root: pluginRoot, counts })
|
|
694
|
+
console.log('[claude-plugin] imported ' + pluginId + ' from ' + pluginRoot)
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function marketplaceEntryPluginRoots(entry, marketplaceRoot, scratchRoot, cloneCache, inheritedDepth) {
|
|
698
|
+
const source = entry.source
|
|
699
|
+
if (typeof source === 'string') {
|
|
700
|
+
const root = safeRelativePath(marketplaceRoot, source)
|
|
701
|
+
return root ? [{ root, entry }] : []
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (!isPlainObject(source)) return []
|
|
705
|
+
const kind = source.source
|
|
706
|
+
if (kind === 'github' && typeof source.repo === 'string') {
|
|
707
|
+
const cloneRoot = cloneGit(
|
|
708
|
+
{
|
|
709
|
+
id: entry.name || source.repo,
|
|
710
|
+
url: 'https://github.com/' + source.repo + '.git',
|
|
711
|
+
ref: source.ref,
|
|
712
|
+
sha: source.sha,
|
|
713
|
+
depth: inheritedDepth,
|
|
714
|
+
},
|
|
715
|
+
scratchRoot,
|
|
716
|
+
cloneCache,
|
|
717
|
+
)
|
|
718
|
+
return [{ root: cloneRoot, entry }]
|
|
719
|
+
}
|
|
720
|
+
if (kind === 'url' && typeof source.url === 'string') {
|
|
721
|
+
const cloneRoot = cloneGit(
|
|
722
|
+
{
|
|
723
|
+
id: entry.name || source.url,
|
|
724
|
+
url: source.url,
|
|
725
|
+
ref: source.ref,
|
|
726
|
+
sha: source.sha,
|
|
727
|
+
depth: inheritedDepth,
|
|
728
|
+
},
|
|
729
|
+
scratchRoot,
|
|
730
|
+
cloneCache,
|
|
731
|
+
)
|
|
732
|
+
return [{ root: cloneRoot, entry }]
|
|
733
|
+
}
|
|
734
|
+
if (kind === 'git-subdir' && typeof source.url === 'string' && typeof source.path === 'string') {
|
|
735
|
+
const cloneRoot = cloneGit(
|
|
736
|
+
{
|
|
737
|
+
id: entry.name || source.url,
|
|
738
|
+
url: source.url,
|
|
739
|
+
ref: source.ref,
|
|
740
|
+
sha: source.sha,
|
|
741
|
+
depth: inheritedDepth,
|
|
742
|
+
},
|
|
743
|
+
scratchRoot,
|
|
744
|
+
cloneCache,
|
|
745
|
+
)
|
|
746
|
+
const root = safeRelativePath(cloneRoot, source.path)
|
|
747
|
+
return root ? [{ root, entry }] : []
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
console.warn('[claude-plugin] skipping unsupported marketplace source for ' + (entry.name || 'unknown'))
|
|
751
|
+
return []
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function importMarketplaceSource(ctx, source, scratchRoot, cloneCache) {
|
|
755
|
+
const cloneRoot = cloneGit(source, scratchRoot, cloneCache)
|
|
756
|
+
const marketplaceRoot = safeRelativePath(cloneRoot, source.path || '.') || cloneRoot
|
|
757
|
+
const marketplacePath =
|
|
758
|
+
safeRelativePath(marketplaceRoot, source.marketplacePath || DEFAULT_MARKETPLACE_PATH) ||
|
|
759
|
+
join(marketplaceRoot, DEFAULT_MARKETPLACE_PATH)
|
|
760
|
+
const marketplace = readJson(marketplacePath)
|
|
761
|
+
const plugins = Array.isArray(marketplace?.plugins) ? marketplace.plugins : []
|
|
762
|
+
const include = new Set(source.include || [])
|
|
763
|
+
|
|
764
|
+
for (const entry of plugins) {
|
|
765
|
+
if (!isPlainObject(entry) || typeof entry.name !== 'string') continue
|
|
766
|
+
if (include.size > 0 && !include.has(entry.name)) continue
|
|
767
|
+
for (const item of marketplaceEntryPluginRoots(
|
|
768
|
+
entry,
|
|
769
|
+
marketplaceRoot,
|
|
770
|
+
scratchRoot,
|
|
771
|
+
cloneCache,
|
|
772
|
+
source.depth,
|
|
773
|
+
)) {
|
|
774
|
+
if (existsSync(item.root)) importPluginRoot(ctx, item.root, item.entry)
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function importDirectSource(ctx, source, scratchRoot, cloneCache) {
|
|
780
|
+
const cloneRoot = cloneGit(source, scratchRoot, cloneCache)
|
|
781
|
+
const base = safeRelativePath(cloneRoot, source.path || '.') || cloneRoot
|
|
782
|
+
const roots = discoverPluginRoots(base, source.include || [])
|
|
783
|
+
for (const item of roots) importPluginRoot(ctx, item.root, item.entry)
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function main() {
|
|
787
|
+
const planPath = process.argv[2]
|
|
788
|
+
if (!planPath) throw new Error('Usage: claude-plugin-importer.mjs <plan.json>')
|
|
789
|
+
const plan = readJson(planPath)
|
|
790
|
+
if (!isPlainObject(plan)) throw new Error('Invalid Claude plugin import plan')
|
|
791
|
+
const mountPath = plan.mountPath || '/claude-plugins'
|
|
792
|
+
const sources = Array.isArray(plan.sources) ? plan.sources : []
|
|
793
|
+
writeGithubNetrc()
|
|
794
|
+
|
|
795
|
+
rmSync(mountPath, { recursive: true, force: true })
|
|
796
|
+
mkdirSync(join(mountPath, '.shadow', 'skills'), { recursive: true })
|
|
797
|
+
mkdirSync(join(mountPath, '.shadow', 'bin'), { recursive: true })
|
|
798
|
+
const scratchRoot = mkdtempSync(join(tmpdir(), 'shadow-claude-plugin-'))
|
|
799
|
+
const cloneCache = new Map()
|
|
800
|
+
const ctx = { mountPath, imported: [] }
|
|
801
|
+
|
|
802
|
+
for (const source of sources) {
|
|
803
|
+
if (!isPlainObject(source)) continue
|
|
804
|
+
if (source.kind === 'marketplace') {
|
|
805
|
+
importMarketplaceSource(ctx, source, scratchRoot, cloneCache)
|
|
806
|
+
} else if (source.kind === 'plugins') {
|
|
807
|
+
importDirectSource(ctx, source, scratchRoot, cloneCache)
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
writeJson(join(mountPath, '.shadow', 'plugins.json'), ctx.imported)
|
|
812
|
+
console.log('[claude-plugin] imported ' + ctx.imported.length + ' plugin(s)')
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
main()
|
|
816
|
+
`;
|
|
817
|
+
|
|
818
|
+
// src/plugins/claude-plugin/k8s.ts
|
|
819
|
+
var CLAUDE_PLUGIN_IMAGE = "node:22-bookworm";
|
|
820
|
+
var IMPORTER_PATH = "/tmp/claude-plugin-importer.mjs";
|
|
821
|
+
var SLASH_INDEXER_PATH = "/tmp/claude-plugin-slash-indexer.mjs";
|
|
822
|
+
var CLAUDE_PLUGIN_SCRIPT_MOUNT_PATH = "/opt/shadow-claude-plugin";
|
|
823
|
+
var CLAUDE_PLUGIN_HELPER_SECURITY_CONTEXT = {
|
|
824
|
+
runAsNonRoot: true,
|
|
825
|
+
runAsUser: 1e3,
|
|
826
|
+
runAsGroup: 1e3,
|
|
827
|
+
allowPrivilegeEscalation: false,
|
|
828
|
+
capabilities: { drop: ["ALL"] }
|
|
829
|
+
};
|
|
830
|
+
var CLAUDE_PLUGIN_TOOL_CHECKS = [
|
|
831
|
+
'command -v git >/dev/null 2>&1 || { echo "[claude-plugin] git is missing from helper image" >&2; exit 127; }',
|
|
832
|
+
'command -v node >/dev/null 2>&1 || { echo "[claude-plugin] node is missing from helper image" >&2; exit 127; }'
|
|
833
|
+
];
|
|
834
|
+
function parsePollInterval(input) {
|
|
835
|
+
if (input == null) return 0;
|
|
836
|
+
if (typeof input === "number") return Math.max(0, Math.floor(input));
|
|
837
|
+
const m = /^(\d+)\s*(s|m|h)?$/.exec(input.trim());
|
|
838
|
+
if (!m) return 0;
|
|
839
|
+
const n = Number(m[1]);
|
|
840
|
+
const unit = m[2] ?? "s";
|
|
841
|
+
switch (unit) {
|
|
842
|
+
case "h":
|
|
843
|
+
return n * 3600;
|
|
844
|
+
case "m":
|
|
845
|
+
return n * 60;
|
|
846
|
+
default:
|
|
847
|
+
return n;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
function sanitizeId(value) {
|
|
851
|
+
const sanitized = value.replace(/\.git$/i, "").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63);
|
|
852
|
+
return sanitized || "claude-plugin";
|
|
853
|
+
}
|
|
854
|
+
function claudePluginSlashCommandsIndexPath(mountPath) {
|
|
855
|
+
return `${mountPath}/.shadow/slash-commands.json`;
|
|
856
|
+
}
|
|
857
|
+
function shQuote(value) {
|
|
858
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
859
|
+
}
|
|
860
|
+
function parseGithubTreeUrl(input) {
|
|
861
|
+
const match = input.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)(?:\/(.+))?$/);
|
|
862
|
+
if (!match) return void 0;
|
|
863
|
+
const owner = match[1];
|
|
864
|
+
const repoRaw = match[2];
|
|
865
|
+
const ref = match[3];
|
|
866
|
+
const path = match[4];
|
|
867
|
+
if (!owner || !repoRaw || !ref) return void 0;
|
|
868
|
+
const repo = repoRaw.replace(/\.git$/i, "");
|
|
869
|
+
return {
|
|
870
|
+
url: `https://github.com/${owner}/${repo}.git`,
|
|
871
|
+
ref,
|
|
872
|
+
path,
|
|
873
|
+
idHint: sanitizeId(`${owner}-${repo}${path ? `-${path}` : ""}`)
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function normalizeGitUrl(input) {
|
|
877
|
+
if (/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(input)) {
|
|
878
|
+
return `https://github.com/${input}.git`;
|
|
879
|
+
}
|
|
880
|
+
return input;
|
|
881
|
+
}
|
|
882
|
+
function deriveSourceId(input) {
|
|
883
|
+
if (input.id) return sanitizeId(input.id);
|
|
884
|
+
if (input.parsedTreeId) return input.parsedTreeId;
|
|
885
|
+
const raw = input.repo ?? input.url ?? "claude-plugin";
|
|
886
|
+
return sanitizeId(`${raw}${input.path ? `-${input.path}` : ""}`);
|
|
887
|
+
}
|
|
888
|
+
function normalizeClaudePluginGitSource(input) {
|
|
889
|
+
const sourceText = input.repo ?? input.url;
|
|
890
|
+
if (!sourceText) return null;
|
|
891
|
+
const parsedTree = input.url ? parseGithubTreeUrl(input.url) : void 0;
|
|
892
|
+
const url = parsedTree?.url ?? normalizeGitUrl(sourceText);
|
|
893
|
+
const path = input.path ?? parsedTree?.path;
|
|
894
|
+
const ref = input.ref ?? parsedTree?.ref;
|
|
895
|
+
return {
|
|
896
|
+
id: deriveSourceId({
|
|
897
|
+
id: input.id,
|
|
898
|
+
repo: input.repo,
|
|
899
|
+
url,
|
|
900
|
+
path,
|
|
901
|
+
parsedTreeId: parsedTree?.idHint
|
|
902
|
+
}),
|
|
903
|
+
url,
|
|
904
|
+
...ref ? { ref } : {},
|
|
905
|
+
...input.sha ? { sha: input.sha } : {},
|
|
906
|
+
depth: Math.max(1, Math.floor(input.depth ?? 1)),
|
|
907
|
+
...path ? { path } : {}
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function buildImporterSnippet(plan) {
|
|
911
|
+
return [
|
|
912
|
+
`cat > ${IMPORTER_PATH} <<'SHADOW_CLAUDE_PLUGIN_IMPORTER'`,
|
|
913
|
+
CLAUDE_PLUGIN_IMPORTER_SCRIPT,
|
|
914
|
+
"SHADOW_CLAUDE_PLUGIN_IMPORTER",
|
|
915
|
+
`cat > /tmp/claude-plugin-plan.json <<'SHADOW_CLAUDE_PLUGIN_PLAN'`,
|
|
916
|
+
JSON.stringify(plan, null, 2),
|
|
917
|
+
"SHADOW_CLAUDE_PLUGIN_PLAN",
|
|
918
|
+
`node ${IMPORTER_PATH} /tmp/claude-plugin-plan.json`
|
|
919
|
+
].join("\n");
|
|
920
|
+
}
|
|
921
|
+
function buildSlashCommandIndexSnippet(mountPath, options) {
|
|
922
|
+
if (!options?.enabled) return "";
|
|
923
|
+
const rulesJson = JSON.stringify(options.rules ?? []);
|
|
924
|
+
return [
|
|
925
|
+
`cat > ${SLASH_INDEXER_PATH} <<'SHADOW_CLAUDE_PLUGIN_SLASH_INDEXER'`,
|
|
926
|
+
AGENT_PACK_SLASH_INDEXER_SCRIPT,
|
|
927
|
+
"SHADOW_CLAUDE_PLUGIN_SLASH_INDEXER",
|
|
928
|
+
[
|
|
929
|
+
`node ${SLASH_INDEXER_PATH}`,
|
|
930
|
+
`--mount-path ${shQuote(mountPath)}`,
|
|
931
|
+
`--output ${shQuote(options.outputPath)}`,
|
|
932
|
+
`--infer-interactions ${options.inferInteractions === false ? "false" : "true"}`,
|
|
933
|
+
`--include-scripts ${options.includeScripts === false ? "false" : "true"}`,
|
|
934
|
+
`--generate-script-skills ${options.generateScriptSkills === false ? "false" : "true"}`,
|
|
935
|
+
`--max-script-commands-per-pack ${Math.max(0, Math.floor(options.maxScriptCommandsPerPack ?? 80))}`,
|
|
936
|
+
`--rules-json ${shQuote(rulesJson)}`
|
|
937
|
+
].join(" ")
|
|
938
|
+
].join("\n");
|
|
939
|
+
}
|
|
940
|
+
function buildImportScript(sources, mountPath, slashCommandIndex) {
|
|
941
|
+
return [
|
|
942
|
+
"set -e",
|
|
943
|
+
...CLAUDE_PLUGIN_TOOL_CHECKS,
|
|
944
|
+
`mkdir -p ${shQuote(mountPath)}`,
|
|
945
|
+
buildImporterSnippet({ mountPath, sources }),
|
|
946
|
+
buildSlashCommandIndexSnippet(mountPath, slashCommandIndex)
|
|
947
|
+
].filter(Boolean).join("\n");
|
|
948
|
+
}
|
|
949
|
+
function buildClaudePluginInitContainer(sources, mountPath, volumeName, scriptVolumeName) {
|
|
950
|
+
return {
|
|
951
|
+
name: "claude-plugin-import",
|
|
952
|
+
image: CLAUDE_PLUGIN_IMAGE,
|
|
953
|
+
imagePullPolicy: "IfNotPresent",
|
|
954
|
+
command: ["/bin/sh", `${CLAUDE_PLUGIN_SCRIPT_MOUNT_PATH}/init.sh`],
|
|
955
|
+
volumeMounts: [
|
|
956
|
+
{ name: volumeName, mountPath },
|
|
957
|
+
{ name: scriptVolumeName, mountPath: CLAUDE_PLUGIN_SCRIPT_MOUNT_PATH, readOnly: true }
|
|
958
|
+
],
|
|
959
|
+
resources: {
|
|
960
|
+
requests: { cpu: "50m", memory: "96Mi" },
|
|
961
|
+
limits: { cpu: "500m", memory: "512Mi" }
|
|
962
|
+
},
|
|
963
|
+
securityContext: CLAUDE_PLUGIN_HELPER_SECURITY_CONTEXT
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
function buildClaudePluginSyncSidecar(opts) {
|
|
967
|
+
if (!opts.intervalSec || opts.intervalSec <= 0) return void 0;
|
|
968
|
+
return {
|
|
969
|
+
name: "claude-plugin-sync",
|
|
970
|
+
image: CLAUDE_PLUGIN_IMAGE,
|
|
971
|
+
imagePullPolicy: "IfNotPresent",
|
|
972
|
+
command: ["/bin/sh", `${CLAUDE_PLUGIN_SCRIPT_MOUNT_PATH}/sync.sh`],
|
|
973
|
+
volumeMounts: [
|
|
974
|
+
{ name: opts.volumeName, mountPath: opts.mountPath },
|
|
975
|
+
{ name: opts.scriptVolumeName, mountPath: CLAUDE_PLUGIN_SCRIPT_MOUNT_PATH, readOnly: true }
|
|
976
|
+
],
|
|
977
|
+
resources: {
|
|
978
|
+
requests: { cpu: "50m", memory: "96Mi" },
|
|
979
|
+
limits: { cpu: "500m", memory: "512Mi" }
|
|
980
|
+
},
|
|
981
|
+
securityContext: CLAUDE_PLUGIN_HELPER_SECURITY_CONTEXT
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
function buildClaudePluginInitScript(sources, mountPath, slashCommandIndex) {
|
|
985
|
+
return buildImportScript(sources, mountPath, slashCommandIndex);
|
|
986
|
+
}
|
|
987
|
+
function buildClaudePluginSyncScript(opts) {
|
|
988
|
+
const runScript = buildImportScript(opts.sources, opts.mountPath, opts.slashCommandIndex);
|
|
989
|
+
return `
|
|
990
|
+
set -e
|
|
991
|
+
RUN_SCRIPT() {
|
|
992
|
+
${runScript}
|
|
993
|
+
}
|
|
994
|
+
while true; do
|
|
995
|
+
RUN_SCRIPT || echo "[claude-plugin-sync] iteration failed, will retry"
|
|
996
|
+
date -u +%FT%TZ > "${opts.mountPath}/.claude-plugin-synced-at" || true
|
|
997
|
+
sleep ${opts.intervalSec}
|
|
998
|
+
done
|
|
999
|
+
`.trim();
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// src/plugins/claude-plugin/manifest.json
|
|
1003
|
+
var manifest_default = {
|
|
1004
|
+
id: "claude-plugin",
|
|
1005
|
+
name: "Claude Plugin Importer",
|
|
1006
|
+
description: "Imports Claude Code plugins and marketplaces from GitHub or Git repositories at deploy time, then normalizes their skills, commands, agents, hooks, MCP/LSP descriptors, monitors, themes, settings, and bin tools for Shadow Cloud agents.",
|
|
1007
|
+
version: "1.0.0",
|
|
1008
|
+
category: "code",
|
|
1009
|
+
icon: "package-plus",
|
|
1010
|
+
auth: {
|
|
1011
|
+
type: "token",
|
|
1012
|
+
fields: [
|
|
1013
|
+
{
|
|
1014
|
+
key: "GITHUB_TOKEN",
|
|
1015
|
+
label: "GitHub Token",
|
|
1016
|
+
description: "Optional token for private GitHub plugin repositories",
|
|
1017
|
+
required: false,
|
|
1018
|
+
sensitive: true,
|
|
1019
|
+
placeholder: "ghp_..."
|
|
1020
|
+
}
|
|
1021
|
+
]
|
|
1022
|
+
},
|
|
1023
|
+
config: {
|
|
1024
|
+
type: "object",
|
|
1025
|
+
properties: {
|
|
1026
|
+
marketplaces: {
|
|
1027
|
+
type: "array",
|
|
1028
|
+
description: "Claude Code plugin marketplaces to import from GitHub or Git repositories.",
|
|
1029
|
+
items: {
|
|
1030
|
+
type: "object",
|
|
1031
|
+
properties: {
|
|
1032
|
+
id: { type: "string", description: "Stable source id for labels and logs." },
|
|
1033
|
+
repo: {
|
|
1034
|
+
type: "string",
|
|
1035
|
+
description: "GitHub owner/repo shorthand, e.g. anthropics/financial-services."
|
|
1036
|
+
},
|
|
1037
|
+
url: {
|
|
1038
|
+
type: "string",
|
|
1039
|
+
description: "Full git URL, owner/repo shorthand, or GitHub tree URL."
|
|
1040
|
+
},
|
|
1041
|
+
ref: { type: "string", description: "Branch, tag, or commit-ish." },
|
|
1042
|
+
sha: { type: "string", description: "Exact commit SHA to checkout." },
|
|
1043
|
+
depth: { type: "number", description: "Shallow clone depth. Defaults to 1." },
|
|
1044
|
+
path: {
|
|
1045
|
+
type: "string",
|
|
1046
|
+
description: "Marketplace root path inside the repository."
|
|
1047
|
+
},
|
|
1048
|
+
marketplacePath: {
|
|
1049
|
+
type: "string",
|
|
1050
|
+
description: "Path to .claude-plugin/marketplace.json relative to path or repo root."
|
|
1051
|
+
},
|
|
1052
|
+
plugins: {
|
|
1053
|
+
type: "array",
|
|
1054
|
+
items: { type: "string" },
|
|
1055
|
+
description: "Marketplace plugin names to import. Omit to import all entries."
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
plugins: {
|
|
1061
|
+
type: "array",
|
|
1062
|
+
description: "Direct Claude plugin directories or plugin-collection directories to import.",
|
|
1063
|
+
items: {
|
|
1064
|
+
type: "object",
|
|
1065
|
+
properties: {
|
|
1066
|
+
id: { type: "string", description: "Stable source id for labels and logs." },
|
|
1067
|
+
repo: {
|
|
1068
|
+
type: "string",
|
|
1069
|
+
description: "GitHub owner/repo shorthand."
|
|
1070
|
+
},
|
|
1071
|
+
url: {
|
|
1072
|
+
type: "string",
|
|
1073
|
+
description: "Full git URL, owner/repo shorthand, or GitHub tree URL."
|
|
1074
|
+
},
|
|
1075
|
+
ref: { type: "string", description: "Branch, tag, or commit-ish." },
|
|
1076
|
+
sha: { type: "string", description: "Exact commit SHA to checkout." },
|
|
1077
|
+
depth: { type: "number", description: "Shallow clone depth. Defaults to 1." },
|
|
1078
|
+
path: {
|
|
1079
|
+
type: "string",
|
|
1080
|
+
description: "Plugin directory or collection directory inside the repository."
|
|
1081
|
+
},
|
|
1082
|
+
plugins: {
|
|
1083
|
+
type: "array",
|
|
1084
|
+
items: { type: "string" },
|
|
1085
|
+
description: "Plugin directory names or manifest names to import when path points at a collection."
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
},
|
|
1090
|
+
mountPath: {
|
|
1091
|
+
type: "string",
|
|
1092
|
+
default: "/claude-plugins",
|
|
1093
|
+
description: "Container path where imported Claude plugin assets are mounted."
|
|
1094
|
+
},
|
|
1095
|
+
poll: {
|
|
1096
|
+
type: "string",
|
|
1097
|
+
description: "Optional poll interval (e.g. 5m, 30s, 1h) for live refresh sidecar."
|
|
1098
|
+
},
|
|
1099
|
+
slashCommands: {
|
|
1100
|
+
type: "object",
|
|
1101
|
+
description: "Controls automatic Shadow slash command registration for imported Claude commands and skills.",
|
|
1102
|
+
properties: {
|
|
1103
|
+
autoRegister: {
|
|
1104
|
+
type: "boolean",
|
|
1105
|
+
default: true,
|
|
1106
|
+
description: "Register discovered upstream commands with Shadow."
|
|
1107
|
+
},
|
|
1108
|
+
inferInteractions: {
|
|
1109
|
+
type: "boolean",
|
|
1110
|
+
default: true,
|
|
1111
|
+
description: "Derive form interactions from upstream AskUserQuestion-style markdown."
|
|
1112
|
+
},
|
|
1113
|
+
includeScripts: {
|
|
1114
|
+
type: "boolean",
|
|
1115
|
+
default: false,
|
|
1116
|
+
description: "Register executable helper scripts from imported script directories as slash commands."
|
|
1117
|
+
},
|
|
1118
|
+
generateScriptSkills: {
|
|
1119
|
+
type: "boolean",
|
|
1120
|
+
default: false,
|
|
1121
|
+
description: "Generate lightweight SKILL.md wrappers for discovered helper scripts."
|
|
1122
|
+
},
|
|
1123
|
+
maxScriptCommandsPerPack: {
|
|
1124
|
+
type: "number",
|
|
1125
|
+
default: 80,
|
|
1126
|
+
description: "Maximum number of script-backed commands to expose per imported plugin."
|
|
1127
|
+
},
|
|
1128
|
+
rules: {
|
|
1129
|
+
type: "array",
|
|
1130
|
+
description: "Optional command match rules for aliases or explicit interaction metadata.",
|
|
1131
|
+
items: { type: "object" }
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
capabilities: ["config-builder", "skill"],
|
|
1138
|
+
tags: ["claude", "claude-code", "plugin", "marketplace", "github", "skills", "agents"],
|
|
1139
|
+
popularity: 88
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
// src/plugins/claude-plugin/index.ts
|
|
1143
|
+
var VOLUME_NAME = "claude-plugins";
|
|
1144
|
+
var SCRIPT_VOLUME_NAME = "claude-plugin-scripts";
|
|
1145
|
+
var DEFAULT_MOUNT = "/claude-plugins";
|
|
1146
|
+
var GLOBAL_SKILLS_DIR = ".shadow/skills";
|
|
1147
|
+
var GLOBAL_BIN_DIR = ".shadow/bin";
|
|
1148
|
+
var DEFAULT_CONTAINER_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
1149
|
+
function isRecord(value) {
|
|
1150
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1151
|
+
}
|
|
1152
|
+
function validateGitSource(value, path, errors) {
|
|
1153
|
+
if (!isRecord(value)) {
|
|
1154
|
+
errors.push({ path, expected: "ClaudePluginGitSourceOption" });
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
for (const key of ["id", "repo", "url", "ref", "sha", "path", "marketplacePath"]) {
|
|
1158
|
+
if (value[key] !== void 0 && typeof value[key] !== "string") {
|
|
1159
|
+
errors.push({ path: `${path}.${key}`, expected: "string | undefined" });
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
if (value.depth !== void 0 && typeof value.depth !== "number") {
|
|
1163
|
+
errors.push({ path: `${path}.depth`, expected: "number | undefined" });
|
|
1164
|
+
}
|
|
1165
|
+
if (value.plugins !== void 0 && (!Array.isArray(value.plugins) || !value.plugins.every((item) => typeof item === "string"))) {
|
|
1166
|
+
errors.push({ path: `${path}.plugins`, expected: "string[] | undefined" });
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
function validateClaudePluginOptions(input) {
|
|
1170
|
+
const errors = [];
|
|
1171
|
+
if (!isRecord(input)) return { success: false, errors: [{ path: "", expected: "object" }] };
|
|
1172
|
+
for (const field of ["marketplaces", "plugins"]) {
|
|
1173
|
+
const value = input[field];
|
|
1174
|
+
if (value === void 0) continue;
|
|
1175
|
+
if (!Array.isArray(value)) {
|
|
1176
|
+
errors.push({ path: `.${field}`, expected: "array | undefined" });
|
|
1177
|
+
continue;
|
|
1178
|
+
}
|
|
1179
|
+
value.forEach((item, index) => validateGitSource(item, `.${field}[${index}]`, errors));
|
|
1180
|
+
}
|
|
1181
|
+
if (input.mountPath !== void 0 && typeof input.mountPath !== "string") {
|
|
1182
|
+
errors.push({ path: ".mountPath", expected: "string | undefined" });
|
|
1183
|
+
}
|
|
1184
|
+
if (input.poll !== void 0 && typeof input.poll !== "string" && typeof input.poll !== "number") {
|
|
1185
|
+
errors.push({ path: ".poll", expected: "string | number | undefined" });
|
|
1186
|
+
}
|
|
1187
|
+
if (input.slashCommands !== void 0) {
|
|
1188
|
+
if (!isRecord(input.slashCommands)) {
|
|
1189
|
+
errors.push({ path: ".slashCommands", expected: "object | undefined" });
|
|
1190
|
+
} else {
|
|
1191
|
+
for (const key of [
|
|
1192
|
+
"autoRegister",
|
|
1193
|
+
"inferInteractions",
|
|
1194
|
+
"includeScripts",
|
|
1195
|
+
"generateScriptSkills"
|
|
1196
|
+
]) {
|
|
1197
|
+
if (input.slashCommands[key] !== void 0 && typeof input.slashCommands[key] !== "boolean") {
|
|
1198
|
+
errors.push({ path: `.slashCommands.${key}`, expected: "boolean | undefined" });
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
if (input.slashCommands.maxScriptCommandsPerPack !== void 0 && typeof input.slashCommands.maxScriptCommandsPerPack !== "number") {
|
|
1202
|
+
errors.push({
|
|
1203
|
+
path: ".slashCommands.maxScriptCommandsPerPack",
|
|
1204
|
+
expected: "number | undefined"
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
if (input.slashCommands.rules !== void 0 && !Array.isArray(input.slashCommands.rules)) {
|
|
1208
|
+
errors.push({ path: ".slashCommands.rules", expected: "array | undefined" });
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
return { success: errors.length === 0, errors };
|
|
1213
|
+
}
|
|
1214
|
+
function mergeClaudePluginOptions(entries) {
|
|
1215
|
+
if (entries.length === 0) return null;
|
|
1216
|
+
const merged = {};
|
|
1217
|
+
for (const entry of entries) {
|
|
1218
|
+
if (entry.marketplaces?.length) {
|
|
1219
|
+
merged.marketplaces = [...merged.marketplaces ?? [], ...entry.marketplaces];
|
|
1220
|
+
}
|
|
1221
|
+
if (entry.plugins?.length) {
|
|
1222
|
+
merged.plugins = [...merged.plugins ?? [], ...entry.plugins];
|
|
1223
|
+
}
|
|
1224
|
+
if (entry.mountPath !== void 0) merged.mountPath = entry.mountPath;
|
|
1225
|
+
if (entry.poll !== void 0) merged.poll = entry.poll;
|
|
1226
|
+
if (entry.slashCommands) {
|
|
1227
|
+
merged.slashCommands = {
|
|
1228
|
+
...merged.slashCommands ?? {},
|
|
1229
|
+
...entry.slashCommands,
|
|
1230
|
+
...merged.slashCommands?.rules || entry.slashCommands.rules ? {
|
|
1231
|
+
rules: [...merged.slashCommands?.rules ?? [], ...entry.slashCommands.rules ?? []]
|
|
1232
|
+
} : {}
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return merged;
|
|
1237
|
+
}
|
|
1238
|
+
function readOptions(agent, config) {
|
|
1239
|
+
const entries = [
|
|
1240
|
+
...(config?.use ?? []).filter((u) => u.plugin === "claude-plugin").map((entry) => entry.options ?? {}),
|
|
1241
|
+
...(agent.use ?? []).filter((u) => u.plugin === "claude-plugin").map((entry) => entry.options ?? {})
|
|
1242
|
+
];
|
|
1243
|
+
return mergeClaudePluginOptions(entries);
|
|
1244
|
+
}
|
|
1245
|
+
function resolveClaudePluginSources(opts) {
|
|
1246
|
+
const sources = [];
|
|
1247
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1248
|
+
for (const source of opts.marketplaces ?? []) {
|
|
1249
|
+
const normalized = normalizeClaudePluginGitSource(source);
|
|
1250
|
+
if (!normalized) continue;
|
|
1251
|
+
const item = {
|
|
1252
|
+
...normalized,
|
|
1253
|
+
kind: "marketplace",
|
|
1254
|
+
...source.marketplacePath ? { marketplacePath: source.marketplacePath } : {},
|
|
1255
|
+
...source.plugins?.length ? { include: [...new Set(source.plugins)] } : {}
|
|
1256
|
+
};
|
|
1257
|
+
const key = JSON.stringify(item);
|
|
1258
|
+
if (seen.has(key)) continue;
|
|
1259
|
+
seen.add(key);
|
|
1260
|
+
sources.push(item);
|
|
1261
|
+
}
|
|
1262
|
+
for (const source of opts.plugins ?? []) {
|
|
1263
|
+
const normalized = normalizeClaudePluginGitSource(source);
|
|
1264
|
+
if (!normalized) continue;
|
|
1265
|
+
const item = {
|
|
1266
|
+
...normalized,
|
|
1267
|
+
kind: "plugins",
|
|
1268
|
+
...source.plugins?.length ? { include: [...new Set(source.plugins)] } : {}
|
|
1269
|
+
};
|
|
1270
|
+
const key = JSON.stringify(item);
|
|
1271
|
+
if (seen.has(key)) continue;
|
|
1272
|
+
seen.add(key);
|
|
1273
|
+
sources.push(item);
|
|
1274
|
+
}
|
|
1275
|
+
return sources;
|
|
1276
|
+
}
|
|
1277
|
+
function buildClaudePluginPrompt(sources, mountPath) {
|
|
1278
|
+
const lines = [
|
|
1279
|
+
"## Imported Claude Plugins",
|
|
1280
|
+
"",
|
|
1281
|
+
`Claude Code plugin assets are mounted under \`${mountPath}\` and normalized into OpenClaw skills at \`${mountPath}/${GLOBAL_SKILLS_DIR}\`.`,
|
|
1282
|
+
"",
|
|
1283
|
+
"Imported sources:"
|
|
1284
|
+
];
|
|
1285
|
+
for (const source of sources) {
|
|
1286
|
+
const selected = source.include?.length ? source.include.join(", ") : "all plugins";
|
|
1287
|
+
const path = source.path ? ` path ${source.path}` : "";
|
|
1288
|
+
lines.push(`- **${source.id}** \u2014 ${source.kind}${path}, ${selected}`);
|
|
1289
|
+
}
|
|
1290
|
+
lines.push(
|
|
1291
|
+
"",
|
|
1292
|
+
"When an imported Claude plugin is relevant, prefer its mounted skills, command definitions, agent files, MCP descriptors, LSP/monitor/hook descriptors, settings, bin tools, and instructions over generic memory.",
|
|
1293
|
+
"If a plugin has an agent file under `agents/`, treat it as workflow guidance and follow its guardrails."
|
|
1294
|
+
);
|
|
1295
|
+
return lines.join("\n");
|
|
1296
|
+
}
|
|
1297
|
+
function slashCommandIndexOptions(opts, mountPath) {
|
|
1298
|
+
const slashCommands = opts.slashCommands ?? {};
|
|
1299
|
+
if (slashCommands.autoRegister === false) return void 0;
|
|
1300
|
+
return {
|
|
1301
|
+
enabled: true,
|
|
1302
|
+
outputPath: claudePluginSlashCommandsIndexPath(mountPath),
|
|
1303
|
+
inferInteractions: slashCommands.inferInteractions ?? true,
|
|
1304
|
+
includeScripts: slashCommands.includeScripts ?? false,
|
|
1305
|
+
generateScriptSkills: slashCommands.generateScriptSkills ?? false,
|
|
1306
|
+
maxScriptCommandsPerPack: slashCommands.maxScriptCommandsPerPack,
|
|
1307
|
+
rules: slashCommands.rules
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
var claudePluginK8sProvider = {
|
|
1311
|
+
buildK8s(agent, ctx) {
|
|
1312
|
+
const opts = readOptions(agent, ctx.config);
|
|
1313
|
+
if (!opts) return void 0;
|
|
1314
|
+
const sources = resolveClaudePluginSources(opts);
|
|
1315
|
+
if (sources.length === 0) return void 0;
|
|
1316
|
+
const mountPath = opts.mountPath ?? DEFAULT_MOUNT;
|
|
1317
|
+
const slashCommandIndex = slashCommandIndexOptions(opts, mountPath);
|
|
1318
|
+
const scriptConfigMapName = `${agent.id}-claude-plugin-scripts`;
|
|
1319
|
+
const envVars = [
|
|
1320
|
+
{ name: "PATH", value: `${mountPath}/${GLOBAL_BIN_DIR}:${DEFAULT_CONTAINER_PATH}` },
|
|
1321
|
+
{ name: "SHADOW_CLAUDE_PLUGIN_MOUNT_ROOT", value: mountPath },
|
|
1322
|
+
{ name: "SHADOW_CLAUDE_PLUGIN_SKILLS_DIR", value: `${mountPath}/${GLOBAL_SKILLS_DIR}` },
|
|
1323
|
+
{ name: "SHADOW_CLAUDE_PLUGIN_BIN_DIR", value: `${mountPath}/${GLOBAL_BIN_DIR}` },
|
|
1324
|
+
{
|
|
1325
|
+
name: "SHADOW_CLAUDE_PLUGIN_COMMANDS_PATH",
|
|
1326
|
+
value: `${mountPath}/.shadow/slash-commands.json`
|
|
1327
|
+
},
|
|
1328
|
+
{ name: "SHADOW_CLAUDE_PLUGIN_COMPONENT_ROOT", value: mountPath }
|
|
1329
|
+
];
|
|
1330
|
+
const result = {
|
|
1331
|
+
initContainers: [
|
|
1332
|
+
buildClaudePluginInitContainer(sources, mountPath, VOLUME_NAME, SCRIPT_VOLUME_NAME)
|
|
1333
|
+
],
|
|
1334
|
+
configMaps: [
|
|
1335
|
+
{
|
|
1336
|
+
name: scriptConfigMapName,
|
|
1337
|
+
data: {
|
|
1338
|
+
"init.sh": buildClaudePluginInitScript(sources, mountPath, slashCommandIndex),
|
|
1339
|
+
"sync.sh": buildClaudePluginSyncScript({
|
|
1340
|
+
sources,
|
|
1341
|
+
mountPath,
|
|
1342
|
+
intervalSec: parsePollInterval(opts.poll),
|
|
1343
|
+
slashCommandIndex
|
|
1344
|
+
})
|
|
1345
|
+
},
|
|
1346
|
+
labels: {
|
|
1347
|
+
app: "shadowob-cloud",
|
|
1348
|
+
agent: agent.id
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
],
|
|
1352
|
+
volumes: [
|
|
1353
|
+
{ name: VOLUME_NAME, spec: { emptyDir: {} } },
|
|
1354
|
+
{
|
|
1355
|
+
name: SCRIPT_VOLUME_NAME,
|
|
1356
|
+
spec: {
|
|
1357
|
+
configMap: {
|
|
1358
|
+
name: scriptConfigMapName,
|
|
1359
|
+
defaultMode: 493
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
],
|
|
1364
|
+
volumeMounts: [{ name: VOLUME_NAME, mountPath, readOnly: false }],
|
|
1365
|
+
envVars,
|
|
1366
|
+
labels: {
|
|
1367
|
+
"claude-plugin.sources": sources.map((source) => source.id).join("_").slice(0, 63)
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
const sidecar = buildClaudePluginSyncSidecar({
|
|
1371
|
+
sources,
|
|
1372
|
+
mountPath,
|
|
1373
|
+
volumeName: VOLUME_NAME,
|
|
1374
|
+
scriptVolumeName: SCRIPT_VOLUME_NAME,
|
|
1375
|
+
intervalSec: parsePollInterval(opts.poll)
|
|
1376
|
+
});
|
|
1377
|
+
if (sidecar) result.sidecars = [sidecar];
|
|
1378
|
+
return result;
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
var plugin = definePlugin(manifest_default, (api) => {
|
|
1382
|
+
api.onBuildConfig((context) => {
|
|
1383
|
+
const opts = readOptions(context.agent, context.config);
|
|
1384
|
+
if (!opts) return {};
|
|
1385
|
+
const sources = resolveClaudePluginSources(opts);
|
|
1386
|
+
if (sources.length === 0) return {};
|
|
1387
|
+
const mountPath = opts.mountPath ?? DEFAULT_MOUNT;
|
|
1388
|
+
return {
|
|
1389
|
+
skills: {
|
|
1390
|
+
load: { extraDirs: [`${mountPath}/${GLOBAL_SKILLS_DIR}`] }
|
|
1391
|
+
}
|
|
1392
|
+
};
|
|
1393
|
+
});
|
|
1394
|
+
api.onBuildPrompt((context) => {
|
|
1395
|
+
const opts = readOptions(context.agent, context.config);
|
|
1396
|
+
if (!opts) return;
|
|
1397
|
+
const sources = resolveClaudePluginSources(opts);
|
|
1398
|
+
if (sources.length === 0) return;
|
|
1399
|
+
return buildClaudePluginPrompt(sources, opts.mountPath ?? DEFAULT_MOUNT);
|
|
1400
|
+
});
|
|
1401
|
+
api.onBuildRuntime((context) => {
|
|
1402
|
+
const opts = readOptions(context.agent, context.config);
|
|
1403
|
+
if (!opts) return;
|
|
1404
|
+
const sources = resolveClaudePluginSources(opts);
|
|
1405
|
+
if (sources.length === 0) return;
|
|
1406
|
+
const slashCommands = opts.slashCommands ?? {};
|
|
1407
|
+
if (slashCommands.autoRegister === false) return;
|
|
1408
|
+
return {
|
|
1409
|
+
artifacts: [
|
|
1410
|
+
{
|
|
1411
|
+
kind: "shadow.slashCommands",
|
|
1412
|
+
path: claudePluginSlashCommandsIndexPath(opts.mountPath ?? DEFAULT_MOUNT),
|
|
1413
|
+
mediaType: "application/json"
|
|
1414
|
+
}
|
|
1415
|
+
]
|
|
1416
|
+
};
|
|
1417
|
+
});
|
|
1418
|
+
api.onBuildEnv((context) => {
|
|
1419
|
+
const token = context.secrets.GITHUB_TOKEN;
|
|
1420
|
+
if (!token) return;
|
|
1421
|
+
return { GITHUB_TOKEN: token };
|
|
1422
|
+
});
|
|
1423
|
+
api.onValidate((context) => {
|
|
1424
|
+
const opts = readOptions(context.agent, context.config);
|
|
1425
|
+
if (!opts) return;
|
|
1426
|
+
const shape = validateClaudePluginOptions(opts);
|
|
1427
|
+
const errors = [];
|
|
1428
|
+
if (!shape.success) {
|
|
1429
|
+
errors.push(
|
|
1430
|
+
...shape.errors.map((error) => ({
|
|
1431
|
+
path: `use.claude-plugin.options${error.path}`,
|
|
1432
|
+
message: error.expected,
|
|
1433
|
+
severity: "error"
|
|
1434
|
+
}))
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
const candidates = [...opts.marketplaces ?? [], ...opts.plugins ?? []];
|
|
1438
|
+
if (candidates.length === 0) {
|
|
1439
|
+
errors.push({
|
|
1440
|
+
path: "use.claude-plugin.options",
|
|
1441
|
+
message: "Configure at least one marketplace or direct plugin source.",
|
|
1442
|
+
severity: "error"
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
candidates.forEach((source, index) => {
|
|
1446
|
+
if (!source.repo && !source.url) {
|
|
1447
|
+
errors.push({
|
|
1448
|
+
path: `use.claude-plugin.options.sources[${index}]`,
|
|
1449
|
+
message: 'Each Claude plugin source must specify "repo" or "url".',
|
|
1450
|
+
severity: "error"
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
if (errors.length > 0) return { valid: false, errors };
|
|
1455
|
+
});
|
|
1456
|
+
});
|
|
1457
|
+
plugin.k8s = claudePluginK8sProvider;
|
|
1458
|
+
var claude_plugin_default = plugin;
|
|
1459
|
+
export {
|
|
1460
|
+
buildClaudePluginPrompt,
|
|
1461
|
+
claude_plugin_default as default,
|
|
1462
|
+
resolveClaudePluginSources,
|
|
1463
|
+
validateClaudePluginOptions
|
|
1464
|
+
};
|