blade-code 0.10.2 → 0.10.3
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/dist/blade.js +2 -2
- package/dist/{chunk-8nm1hqr1.js → chunk-0n97186m.js} +2 -2
- package/dist/{chunk-t8q9ks7e.js → chunk-15d49qty.js} +1 -1
- package/dist/chunk-1cx96gx1.js +1 -0
- package/dist/{chunk-8mepk4tc.js → chunk-1g5qc1qx.js} +2 -2
- package/dist/{chunk-d9ddpapp.js → chunk-35yktzm1.js} +1 -1
- package/dist/chunk-3wp3h373.js +180 -0
- package/dist/chunk-4ncbd4k0.js +1 -0
- package/dist/chunk-5fxc90rb.js +1 -0
- package/dist/{chunk-zkbf8n6y.js → chunk-5twefby5.js} +1 -1
- package/dist/{chunk-sndbq5xt.js → chunk-63ysxybd.js} +1 -1
- package/dist/{chunk-nah0cbnp.js → chunk-6tx8k249.js} +2 -2
- package/dist/{chunk-k9a1vb8a.js → chunk-7089z4e1.js} +2 -2
- package/dist/chunk-7443b95r.js +1 -0
- package/dist/chunk-79s052sw.js +2 -0
- package/dist/{chunk-m9zpxzg2.js → chunk-7hqbwpt5.js} +2 -2
- package/dist/chunk-8th744ef.js +1 -0
- package/dist/chunk-8thp1nep.js +1 -0
- package/dist/{chunk-6n5xdf2r.js → chunk-948qe2pp.js} +1 -1
- package/dist/{chunk-mjp61rqd.js → chunk-9ffr01c3.js} +1 -1
- package/dist/{chunk-gafbzeyk.js → chunk-9p5f3ryf.js} +1 -1
- package/dist/{chunk-e2ccyhgz.js → chunk-9yytfg19.js} +1 -1
- package/dist/chunk-aa0ffw17.js +1 -0
- package/dist/{chunk-44wkjs2j.js → chunk-an96c54z.js} +2 -2
- package/dist/{chunk-2beq0syt.js → chunk-bexgcc2d.js} +1 -1
- package/dist/{chunk-5tfzehcg.js → chunk-bfw2p0x8.js} +2 -2
- package/dist/{chunk-bpzv054r.js → chunk-bn148dr5.js} +2 -2
- package/dist/{chunk-ct32ngah.js → chunk-c05t8207.js} +8 -8
- package/dist/chunk-c45y8tx1.js +1484 -0
- package/dist/chunk-ccwskwb3.js +1 -0
- package/dist/chunk-da1jvzbz.js +10 -0
- package/dist/{chunk-af49jmns.js → chunk-dqvcgwhe.js} +2 -2
- package/dist/{chunk-kq5my0yy.js → chunk-drrdzmaa.js} +2 -2
- package/dist/{chunk-px5ck9pf.js → chunk-ea7wsges.js} +2 -2
- package/dist/{chunk-2km4w0ky.js → chunk-eezg6pee.js} +2 -2
- package/dist/{chunk-vjpd3nee.js → chunk-epj56s0n.js} +1 -1
- package/dist/{chunk-0h81hxkw.js → chunk-fvhc1yj8.js} +1 -1
- package/dist/{chunk-d3236505.js → chunk-gb54hc78.js} +1 -1
- package/dist/{chunk-m5tjrr5c.js → chunk-grp43mdb.js} +2 -2
- package/dist/{chunk-fjw32ex5.js → chunk-gxmrakrk.js} +1 -1
- package/dist/{chunk-a7nbn5kf.js → chunk-gxzbv67r.js} +2 -2
- package/dist/{chunk-k89phc5j.js → chunk-jqwntnp1.js} +2 -2
- package/dist/{chunk-88vy9hh0.js → chunk-jza1rsan.js} +1 -1
- package/dist/{chunk-0h9m2fqd.js → chunk-mgwny24b.js} +1 -1
- package/dist/chunk-n3y9ytvg.js +3 -0
- package/dist/chunk-nxj1638n.js +6 -0
- package/dist/chunk-p5tpzh6f.js +1 -0
- package/dist/{chunk-gr5mzxdf.js → chunk-pnrx3spk.js} +1 -1
- package/dist/{chunk-jxv6r0c7.js → chunk-qda309d9.js} +2 -2
- package/dist/{chunk-beqaxytd.js → chunk-qm11hjd3.js} +1 -1
- package/dist/chunk-r4j5ca1h.js +1 -0
- package/dist/{chunk-tm2ckreq.js → chunk-r4pz99sy.js} +20 -20
- package/dist/{chunk-rkzds6k8.js → chunk-rcdynprn.js} +2 -2
- package/dist/{chunk-y0hwzmdn.js → chunk-s95rgkd8.js} +1 -1
- package/dist/chunk-t8x2qpe2.js +1 -0
- package/dist/{chunk-4vaywb2r.js → chunk-tq52pbde.js} +1 -1
- package/dist/{chunk-vvy506b3.js → chunk-v93hcxe7.js} +1 -1
- package/dist/chunk-vmg5n0zv.js +1 -0
- package/dist/{chunk-6sc78810.js → chunk-vwq9s5nj.js} +3 -3
- package/dist/{chunk-ws3gc024.js → chunk-wje6bck2.js} +1 -1
- package/dist/{chunk-qhf0knnx.js → chunk-xdbg7cn3.js} +1 -1
- package/dist/{chunk-th7sxjtk.js → chunk-ydtcvxsm.js} +1 -1
- package/dist/{chunk-wvj9h7mb.js → chunk-ygx03mdh.js} +1 -1
- package/dist/{chunk-6hj2dk75.js → chunk-z906d5tb.js} +2 -2
- package/dist/chunk-zq9r0jx3.js +1 -0
- package/dist/chunk-zyz82mcd.js +1 -0
- package/dist/web/assets/{ChatView-CnwNosSG.js → ChatView-Pqa25Vua.js} +5 -5
- package/dist/web/assets/{CodeBlockHighlighter-CBo_AxjZ.js → CodeBlockHighlighter-Dxt-FWJk.js} +1 -1
- package/dist/web/assets/{FilePreview-hNo6RCNY.js → FilePreview-Dj2yppPn.js} +2 -2
- package/dist/web/assets/{MarkdownRenderer-BON4fqSr.js → MarkdownRenderer-BI2xpTV2.js} +12 -12
- package/dist/web/assets/McpModal-COw0BcHJ.js +5 -0
- package/dist/web/assets/SettingsModal-h4AxN730.js +1 -0
- package/dist/web/assets/SkillsModal-CTZTu9xg.js +1 -0
- package/dist/web/assets/{TaskSwitcher-Ds8CBVtp.js → TaskSwitcher-DPXkV-qb.js} +1 -1
- package/dist/web/assets/{TerminalPanel-Dm4bu6ib.js → TerminalPanel-tpUxwaCh.js} +1 -1
- package/dist/web/assets/index-Dep3eEsS.css +1 -0
- package/dist/web/assets/index-bjyn3NHs.js +32 -0
- package/dist/web/assets/useRequest-BvqVDnKl.js +1 -0
- package/dist/web/assets/{vendor-icons-CsBKlDXT.js → vendor-icons-D_AkWmWB.js} +70 -60
- package/dist/web/assets/{vendor-radix-DXRuAJHH.js → vendor-radix-DBy9B2DN.js} +1 -1
- package/dist/web/index.html +4 -4
- package/package.json +1 -1
- package/dist/chunk-1wyfb6s7.js +0 -1
- package/dist/chunk-3vyjshqp.js +0 -1
- package/dist/chunk-6g21dh3n.js +0 -1
- package/dist/chunk-asbp4cv4.js +0 -1
- package/dist/chunk-bqk3wqts.js +0 -1
- package/dist/chunk-cjdnfyy0.js +0 -1485
- package/dist/chunk-d8zj7c0t.js +0 -6
- package/dist/chunk-jv4nkkb5.js +0 -1
- package/dist/chunk-m3tx1k0s.js +0 -1
- package/dist/chunk-m78ceafk.js +0 -1
- package/dist/chunk-nqq4sbmv.js +0 -1
- package/dist/chunk-p7ppnewt.js +0 -3
- package/dist/chunk-qev36qrb.js +0 -2
- package/dist/chunk-tckfyfsx.js +0 -1
- package/dist/chunk-xhpezpjq.js +0 -10
- package/dist/chunk-y5vq7gek.js +0 -174
- package/dist/chunk-ydqeg4p8.js +0 -1
- package/dist/chunk-yknrffcs.js +0 -1
- package/dist/chunk-ymmtbq2b.js +0 -1
- package/dist/chunk-z2yzyr9h.js +0 -1
- package/dist/web/assets/McpModal-Go_8v1Vu.js +0 -5
- package/dist/web/assets/SettingsModal-CRu1zGQf.js +0 -1
- package/dist/web/assets/index-BNsnHxlt.js +0 -32
- package/dist/web/assets/index-DVTiq-7u.css +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{bd as t1,cd as GO}from"./chunk-35yktzm1.js";import{fd as K1}from"./chunk-63ysxybd.js";import{gd as M0,id as B0}from"./chunk-vwq9s5nj.js";import{nd as h,od as M}from"./chunk-qda309d9.js";import{$d as S0,Rd as V0,Sd as u1}from"./chunk-bn148dr5.js";import{ce as u,ee as Y,he as T1}from"./chunk-tq52pbde.js";import{re as x}from"./chunk-epj56s0n.js";import{Ee as P0}from"./chunk-jqwntnp1.js";import{Je as T}from"./chunk-z906d5tb.js";import Z1 from"node:path";import Q$ from"node:path";import w0 from"node:fs";import Z0 from"node:os";import R from"node:path";import LO from"yaml";var J1={name:"verification",description:"Independent verification agent that validates implementation by running builds, tests, linters, and adversarial"+" probes. Strictly read-only — cannot modify code. Use"+" after completing implementation to get an independent quality assessment.",tools:["Read","Glob","Grep","Bash"],systemPrompt:`# Verification Agent
|
|
2
2
|
|
|
3
3
|
You are an **independent verification engineer**. Your sole purpose is to find problems — not to praise or reassure. You are the last line of defense before code ships.
|
|
4
4
|
|
|
@@ -39,7 +39,7 @@ Run all applicable checks. Capture full output.
|
|
|
39
39
|
|
|
40
40
|
### Phase 3: Code Review of Changed Files
|
|
41
41
|
|
|
42
|
-
1.
|
|
42
|
+
1. Treat the changed-file list supplied by the parent as authoritative. Use \`git status --short\` and \`git diff --name-only\` only to discover additional uncommitted changes. Do not use \`HEAD~1\` as a substitute for the supplied scope.
|
|
43
43
|
2. Read each changed file and review for:
|
|
44
44
|
- **Logic errors**: off-by-one, null/undefined handling, race conditions
|
|
45
45
|
- **Type safety**: any casts, type assertions, missing null checks
|
|
@@ -60,7 +60,7 @@ Think like an attacker or a hostile user:
|
|
|
60
60
|
|
|
61
61
|
## Output Format
|
|
62
62
|
|
|
63
|
-
You MUST end your response with a structured verification report:
|
|
63
|
+
You MUST end your response with a structured verification report. Emit exactly one \`## Verification Result:\` heading in the entire response:
|
|
64
64
|
|
|
65
65
|
\`\`\`
|
|
66
66
|
## Verification Result: PASS | FAIL | PARTIAL
|
|
@@ -89,7 +89,7 @@ You MUST end your response with a structured verification report:
|
|
|
89
89
|
- **FAIL**: Any automated check fails OR any HIGH severity issue found.
|
|
90
90
|
- **PARTIAL**: All automated checks pass BUT MEDIUM severity issues exist.
|
|
91
91
|
|
|
92
|
-
Be thorough. Be skeptical. Find the bugs.`,source:"builtin"};var
|
|
92
|
+
Be thorough. Be skeptical. Find the bugs.`,source:"builtin"};var T0=[{name:"general-purpose",description:"General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you.",tools:[]},{name:"Explore",description:'Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. "src/components/**/*.tsx"), search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "very thorough" for comprehensive analysis across multiple locations and naming conventions.',tools:["Glob","Grep","Read","WebFetch","WebSearch"],systemPrompt:`# Explore Subagent
|
|
93
93
|
|
|
94
94
|
You are a specialized code exploration agent. Your job is to **directly execute searches** using the tools available to you.
|
|
95
95
|
|
|
@@ -156,9 +156,9 @@ You are a software architect specializing in implementation planning.
|
|
|
156
156
|
**Risks**:
|
|
157
157
|
- [Potential issue and mitigation]
|
|
158
158
|
|
|
159
|
-
Be thorough but concise. Focus on actionable steps.`},{name:"statusline-setup",description:"Use this agent to configure the user's Claude Code status line setting.",tools:["Read","Edit"]},
|
|
159
|
+
Be thorough but concise. Focus on actionable steps.`},{name:"statusline-setup",description:"Use this agent to configure the user's Claude Code status line setting.",tools:["Read","Edit"]},J1];var J0=h("Agent"),C0=new Set(["verification"]);class E{workspaceRoot;static instances=new Map;subagents=new Map;constructor(O=x()){this.workspaceRoot=O}static getInstance(O=x()){let $=R.resolve(O),Q=E.instances.get($);if(!Q)Q=new E($),E.instances.set($,Q);return Q}static resetInstances(){E.instances.clear()}register(O){if(C0.has(O.name))throw Error(`Subagent '${O.name}' is reserved by Blade`);if(this.subagents.has(O.name))throw Error(`Subagent '${O.name}' already registered`);this.subagents.set(O.name,O)}applyOverrides(O){for(let $ of O){if(C0.has($.name))throw Error(`Subagent '${$.name}' is reserved by Blade`);this.subagents.set($.name,$)}}getSubagent(O){return this.subagents.get(O)}getAllNames(){return Array.from(this.subagents.keys())}getAllSubagents(){return Array.from(this.subagents.values())}getDescriptionsForPrompt(){let O=this.getAllSubagents();if(O.length===0)return"No subagents available.";return`Available agent types and the tools they have access to:
|
|
160
160
|
${O.map((Q)=>{let q=!Q.tools||Q.tools.length===0?"All tools":Q.tools.join(", ");return`- ${Q.name}: ${Q.description} (Tools: ${q})`}).join(`
|
|
161
|
-
`)}`}loadFromDirectory(O,$){if(!
|
|
161
|
+
`)}`}loadFromDirectory(O,$){if(!w0.existsSync(O))return;let Q=w0.readdirSync(O);for(let q of Q){if(!q.endsWith(".md"))continue;let Z=R.join(O,q);try{let J=this.parseConfigFile(Z,$);if(C0.has(J.name)){J0.warn(`Skipping reserved subagent configuration: ${J.name}`);continue}this.subagents.set(J.name,J)}catch(J){J0.warn(`Failed to load subagent config from ${Z}:`,J)}}}parseConfigFile(O,$){let q=w0.readFileSync(O,"utf-8").match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);if(!q)throw Error(`No YAML frontmatter found in ${O}`);let[,Z,J]=q,K=LO.parse(Z);if(!K.name||!K.description)throw Error(`Missing required fields (name, description) in ${O}`);if(K.isolation!==void 0&&K.isolation!=="none"&&K.isolation!=="worktree")throw Error(`Invalid isolation mode "${String(K.isolation)}" in ${O}`);let L=J.trim(),G=this.parseStringOrArray(K.tools),W=this.parseStringOrArray(K.skills),X=K1(K.permissionMode);return{name:K.name,description:K.description,systemPrompt:L,tools:G,color:K.color,configPath:O,model:K.model||"inherit",permissionMode:X,skills:W,isolation:K.isolation,source:$}}parseStringOrArray(O){if(!O)return;if(Array.isArray(O))return O.map(($)=>$.trim()).filter(Boolean);return O.split(",").map(($)=>$.trim()).filter(Boolean)}loadFromStandardLocations(O){let $=O??T.getInstance().isTrustedCached(this.workspaceRoot);this.loadBuiltinAgents();let Q=R.join(Z0.homedir(),".claude","agents");if(this.loadFromDirectory(Q,"claude-code-user"),$){let J=R.join(this.workspaceRoot,".claude","agents");this.loadFromDirectory(J,"claude-code-project")}let q=R.join(Z0.homedir(),".blade","agents");if(this.loadFromDirectory(q,"blade-user"),$){let J=R.join(this.workspaceRoot,".blade","agents");this.loadFromDirectory(J,"blade-project")}let Z=this.getAllNames().length;return J0.debug(`Loaded ${Z} subagents from standard locations`),Z}loadBuiltinAgents(){for(let O of T0)this.subagents.set(O.name,{...O,model:O.model||"inherit",source:"builtin"});J0.debug(`Loaded ${T0.length} builtin subagents`)}clear(){this.subagents.clear()}snapshot(){let O=new E(this.workspaceRoot);for(let $ of this.subagents.values())O.subagents.set($.name,{...$,...$.tools?{tools:[...$.tools]}:{},...$.disallowedTools?{disallowedTools:[...$.disallowedTools]}:{},...$.skills?{skills:[...$.skills]}:{}});return O}getSubagentsBySource(){let O={builtin:[],"claude-code-user":[],"claude-code-project":[],"blade-user":[],"blade-project":[],flag:[],plugin:[]};for(let $ of this.subagents.values()){let Q=$.source||"builtin",q=Q.startsWith("plugin:")?"plugin":Q;O[q].push($)}return O}clearPluginAgents(){let O=[];for(let[$,Q]of this.subagents.entries())if(Q.source?.startsWith("plugin:"))O.push($);for(let $ of O)this.subagents.delete($)}static getClaudeCodeAgentsDir(O){if(O==="user")return R.join(Z0.homedir(),".claude","agents");return R.join(x(),".claude","agents")}static getBladeAgentsDir(O){if(O==="user")return R.join(Z0.homedir(),".blade","agents");return R.join(x(),".blade","agents")}}function i(O=x()){return E.getInstance(O)}var c$=new Proxy({},{get(O,$){let Q=i(),q=Reflect.get(Q,$,Q);return typeof q==="function"?q.bind(Q):q}});import*as X0 from"node:fs/promises";import{homedir as S1}from"node:os";import*as _ from"node:path";var K0={name:"skill-creator",description:"Create new Skills interactively. Use when the user wants to create a new Skill, define a custom workflow, or add a specialized capability to Blade.",allowedTools:["Read","Write","Glob","Bash","AskUserQuestion"],version:"1.0.0",argumentHint:void 0,userInvocable:!0,disableModelInvocation:!1,model:void 0,whenToUse:"User wants to create a new skill, define a custom workflow, or add a specialized capability.",path:"builtin://skill-creator",basePath:"",source:"builtin"};function G1(){return{metadata:K0,instructions:`# Skill Creator
|
|
162
162
|
|
|
163
163
|
帮助用户创建新的 Blade Skills。
|
|
164
164
|
|
|
@@ -341,7 +341,7 @@ user-invocable: true
|
|
|
341
341
|
<footer>
|
|
342
342
|
\`\`\`
|
|
343
343
|
\`\`\`
|
|
344
|
-
`}}var
|
|
344
|
+
`}}var G0={name:"update-config",description:"配置 Blade harness(settings/hooks/permissions)。"+"用户请求自动化行为时使用此 Skill。",allowedTools:["ConfigTool","Read","Bash","AskUserQuestion"],version:"1.0.0",userInvocable:!0,disableModelInvocation:!1,whenToUse:'用户说"从现在起..."、"每次..."、"当...时..."、'+"需要改配置、安装 hooks、修改权限",path:"builtin://update-config",basePath:"",source:"builtin"};function Y1(){return{metadata:G0,instructions:`# Update Config Skill
|
|
345
345
|
|
|
346
346
|
配置 Blade 运行环境。管理 settings、hooks、permissions、环境变量等。
|
|
347
347
|
|
|
@@ -614,23 +614,23 @@ ConfigTool({
|
|
|
614
614
|
2. **permissions 字段使用 replace 策略**:设置 permissions 会完全替换现有值。如果只想添加规则,先 GET 当前值,合并后再 SET
|
|
615
615
|
3. **env 字段使用 deep-merge 策略**:可以逐个添加环境变量
|
|
616
616
|
4. **scope 默认值**:不指定 scope 时,根据字段路由表决定(hooks/permissions/env 默认 local,temperature/theme 等默认 global)
|
|
617
|
-
`}}import{exec as
|
|
618
|
-
`)}get size(){return this.skills.size}async refresh(){return this.skills.clear(),this.pluginSkills.clear(),this.initialized=!1,this.initialize()}snapshot(){let O=new k({...this.config});return O.skills=new Map(Array.from(this.skills,([$,Q])=>[$,{...Q,...Q.allowedTools?{allowedTools:[...Q.allowedTools]}:{}}])),O.pluginSkills=new Map(Array.from(this.pluginSkills,([$,Q])=>[$,{...Q,metadata:{...Q.metadata,...Q.metadata.allowedTools?{allowedTools:[...Q.metadata.allowedTools]}:{}}}])),O.initialized=this.initialized,O}registerPluginSkill(O){this.pluginSkills.set(O.namespacedName,O),this.skills.set(O.namespacedName,O.metadata)}findPluginSkill(O){let $=this.pluginSkills.get(O);if($)return $;let Q=[];for(let q of this.pluginSkills.values())if(q.originalName===O)Q.push(q);if(Q.length===1)return Q[0];return}getAllPluginSkills(){return Array.from(this.pluginSkills.values())}clearPluginSkills(){for(let O of this.pluginSkills.values())this.skills.delete(O.namespacedName);this.pluginSkills.clear()}getPluginSkillCount(){return this.pluginSkills.size}}function P(O){return k.getInstance(O)}async function IO(O){return P(O).initialize()}import
|
|
617
|
+
`}}import{exec as WO}from"node:child_process";import*as U from"node:fs/promises";import{homedir as XO}from"node:os";import*as A from"node:path";import{promisify as FO}from"node:util";var Y0=FO(WO),z=h("General"),N0={url:"https://github.com/anthropics/skills.git",branch:"main",defaultSkills:["skill-creator"]};class L1{skillsDir;constructor(O){this.skillsDir=O||A.join(XO(),".blade","skills")}async isInstalled(O){let $=A.join(this.skillsDir,O,"SKILL.md");try{return await U.access($),!0}catch{return!1}}async isGitAvailable(){try{return await Y0("git --version"),!0}catch{return!1}}async installOfficialSkill(O){let{url:$,branch:Q}=N0,q=A.join(this.skillsDir,O),Z=A.join(this.skillsDir,`.tmp-${O}-${Date.now()}`);try{if(!await this.isGitAvailable())return z.warn("Git not available, skipping skill installation"),!1;z.info(`Installing official skill: ${O}...`),await U.mkdir(this.skillsDir,{recursive:!0,mode:493}),await Y0(`git clone --depth 1 --branch ${Q} --single-branch ${$} "${Z}"`,{timeout:30000});let J=A.join(Z,"skills",O);try{await U.access(J)}catch{return z.warn(`Skill ${O} not found in official repository`),await U.rm(Z,{recursive:!0,force:!0}),!1}try{await U.rm(q,{recursive:!0,force:!0})}catch{}return await U.cp(J,q,{recursive:!0}),await U.rm(Z,{recursive:!0,force:!0}),z.info(`Successfully installed: ${O}`),!0}catch(J){try{await U.rm(Z,{recursive:!0,force:!0})}catch{}return z.warn(`Failed to install ${O}: ${J instanceof Error?J.message:"Unknown error"}`),!1}}async ensureDefaultSkillsInstalled(){await U.mkdir(this.skillsDir,{recursive:!0,mode:493});for(let O of N0.defaultSkills)if(!await this.isInstalled(O))await this.installOfficialSkill(O)}async installFromRepo(O,$){let Q=$||this.extractRepoName(O),q=A.join(this.skillsDir,Q),Z=A.join(this.skillsDir,`.tmp-repo-${Q}-${Date.now()}`);try{if(!await this.isGitAvailable())return z.warn("Git not available, cannot install from repo"),!1;z.info(`Installing skill from repo: ${O}...`),await U.mkdir(this.skillsDir,{recursive:!0,mode:493}),await Y0(`git clone --depth 1 "${O}" "${Z}"`,{timeout:60000});let J=A.join(Z,"SKILL.md");try{await U.access(J)}catch{return z.warn(`No SKILL.md found in repository ${O}`),await U.rm(Z,{recursive:!0,force:!0}),!1}await U.rm(q,{recursive:!0,force:!0}),await U.rename(Z,q);try{await U.rm(A.join(q,".git"),{recursive:!0,force:!0})}catch{}return z.info(`Successfully installed skill from repo: ${Q}`),!0}catch(J){try{await U.rm(Z,{recursive:!0,force:!0})}catch{}return z.warn(`Failed to install from repo ${O}: ${J instanceof Error?J.message:"Unknown error"}`),!1}}async installFromLocal(O,$,Q=!0){let q=$||A.basename(O),Z=A.join(this.skillsDir,q);try{let J=A.resolve(O);try{await U.access(J)}catch{return z.warn(`Local path does not exist: ${J}`),!1}let K=A.join(J,"SKILL.md");try{await U.access(K)}catch{return z.warn(`No SKILL.md found in local path: ${J}`),!1}if(z.info(`Installing skill from local path: ${J}...`),await U.mkdir(this.skillsDir,{recursive:!0,mode:493}),await U.rm(Z,{recursive:!0,force:!0}),Q)await U.symlink(J,Z,"dir"),z.info(`Created symlink for skill: ${q}`);else await U.cp(J,Z,{recursive:!0}),z.info(`Copied skill to: ${q}`);return!0}catch(J){return z.warn(`Failed to install from local path ${O}: ${J instanceof Error?J.message:"Unknown error"}`),!1}}extractRepoName(O){return O.match(/\/([^/]+?)(\.git)?$/)?.[1]||"unknown-skill"}async installAllOfficialSkills(){let{url:O,branch:$}=N0,Q=A.join(this.skillsDir,`.tmp-all-${Date.now()}`),q=[],Z=[];try{if(!await this.isGitAvailable())return z.warn("Git not available, skipping skill installation"),{installed:q,failed:Z};await U.mkdir(this.skillsDir,{recursive:!0,mode:493}),z.info("Cloning official skills repository..."),await Y0(`git clone --depth 1 --branch ${$} --single-branch ${O} "${Q}"`,{timeout:60000});let J=A.join(Q,"skills"),K=await U.readdir(J,{withFileTypes:!0});for(let L of K){if(!L.isDirectory())continue;let G=L.name,W=A.join(J,G),X=A.join(this.skillsDir,G);try{await U.access(A.join(W,"SKILL.md")),await U.rm(X,{recursive:!0,force:!0}),await U.cp(W,X,{recursive:!0}),z.info(`Installed: ${G}`),q.push(G)}catch(H){z.warn(`Failed to install ${G}`),Z.push(G)}}await U.rm(Q,{recursive:!0,force:!0})}catch(J){try{await U.rm(Q,{recursive:!0,force:!0})}catch{}z.warn(`Failed to install skills: ${J instanceof Error?J.message:"Unknown error"}`)}return{installed:q,failed:Z}}}var R0=null;function W1(O){if(!R0)R0=new L1(O);return R0}import*as t from"node:fs/promises";import*as L0 from"node:path";import{parse as VO}from"yaml";var HO=/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/,UO=/^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]?$/,X1=1024;function SO(O){if(!O)return;if(typeof O==="string")return O.split(",").map(($)=>$.trim()).filter(Boolean);if(Array.isArray(O))return O.map(($)=>String($).trim()).filter(Boolean);return}function F1(O){if(O===void 0)return;if(typeof O==="boolean")return O;if(typeof O==="string"){let $=O.toLowerCase().trim();if($==="true"||$==="yes"||$==="1")return!0;if($==="false"||$==="no"||$==="0")return!1}return}function jO(O,$){if(!O.name)return{valid:!1,error:"Missing required field: name"};if(!UO.test(O.name))return{valid:!1,error:`Invalid name "${O.name}": must be lowercase letters, numbers, and hyphens only, 1-64 characters`};if(!O.description)return{valid:!1,error:"Missing required field: description"};if(O.description.length>X1)return{valid:!1,error:`Description too long: ${O.description.length} characters (max ${X1})`};let Q;if(O.model)Q=O.model==="inherit"?"inherit":O.model;return{valid:!0,metadata:{name:O.name,description:O.description.trim(),allowedTools:SO(O["allowed-tools"]),version:O.version,argumentHint:O["argument-hint"]?.trim(),userInvocable:F1(O["user-invocable"]),disableModelInvocation:F1(O["disable-model-invocation"]),model:Q,whenToUse:O.when_to_use?.trim()}}}function V1(O,$,Q){let q=O.match(HO);if(!q)return{success:!1,error:"Invalid SKILL.md format: missing YAML frontmatter (must start with ---)"};let[,Z,J]=q,K;try{K=VO(Z)}catch(W){return{success:!1,error:`Failed to parse YAML frontmatter: ${W instanceof Error?W.message:String(W)}`}}let L=jO(K,$);if(!L.valid)return{success:!1,error:L.error};let G=L0.dirname($);return{success:!0,content:{metadata:{...L.metadata,path:$,basePath:G,source:Q},instructions:J.trim()}}}async function W0(O,$){try{let Q=await t.readFile(O,"utf-8");return V1(Q,O,$)}catch(Q){if(Q.code==="ENOENT")return{success:!1,error:`File not found: ${O}`};return{success:!1,error:`Failed to read file: ${Q instanceof Error?Q.message:String(Q)}`}}}async function H1(O){try{let $=await t.readFile(O.path,"utf-8"),Q=V1($,O.path,O.source);return Q.success?Q.content:null}catch{return null}}async function U1(O){try{return await t.access(L0.join(O,"SKILL.md")),!0}catch{return!1}}var MO={userSkillsDir:_.join(S1(),".blade","skills"),projectSkillsDir:".blade/skills",claudeUserSkillsDir:_.join(S1(),".claude","skills"),claudeProjectSkillsDir:".claude/skills"},v0=new Map;function BO(O){return _.resolve(O?.cwd??x())}class k{skills=new Map;pluginSkills=new Map;config;initialized=!1;constructor(O){this.config={...MO,cwd:x(),...O}}static getInstance(O){let $=BO(O),Q=v0.get($);if(!Q)Q=new k({...O,cwd:_.resolve(O?.cwd??x())}),v0.set($,Q);return Q}static resetInstance(){v0.clear()}async initialize(){if(this.initialized)return{skills:Array.from(this.skills.values()),errors:[]};let O=[],$=[];try{await W1(this.config.userSkillsDir).ensureDefaultSkillsInstalled()}catch(J){O.push({path:"SkillInstaller",error:`Failed to install default skills: ${J instanceof Error?J.message:"Unknown error"}`})}this.loadBuiltinSkills();let Q=await this.scanDirectory(this.config.claudeUserSkillsDir,"user");$.push(...Q.skills),O.push(...Q.errors);let q=await this.scanDirectory(this.config.userSkillsDir,"user");if($.push(...q.skills),O.push(...q.errors),(await T.getInstance().getStatus(this.config.cwd)).state==="trusted"){let J=_.isAbsolute(this.config.claudeProjectSkillsDir)?this.config.claudeProjectSkillsDir:_.join(this.config.cwd,this.config.claudeProjectSkillsDir),K=await this.scanDirectory(J,"project");$.push(...K.skills),O.push(...K.errors);let L=_.isAbsolute(this.config.projectSkillsDir)?this.config.projectSkillsDir:_.join(this.config.cwd,this.config.projectSkillsDir),G=await this.scanDirectory(L,"project");$.push(...G.skills),O.push(...G.errors)}for(let J of $)this.skills.set(J.name,J);return this.initialized=!0,{skills:Array.from(this.skills.values()),errors:O}}loadBuiltinSkills(){this.skills.set(K0.name,K0),this.skills.set(G0.name,G0)}async scanDirectory(O,$){let Q=[],q=[];try{await X0.access(O)}catch{return{skills:Q,errors:q}}try{let Z=await X0.readdir(O,{withFileTypes:!0});for(let J of Z){if(!J.isDirectory())continue;let K=_.join(O,J.name),L=_.join(K,"SKILL.md");if(!await U1(K))continue;let G=await W0(L,$);if(G.success&&G.content)Q.push(G.content.metadata);else q.push({path:L,error:G.error||"Unknown error"})}}catch(Z){q.push({path:O,error:`Failed to scan directory: ${Z instanceof Error?Z.message:String(Z)}`})}return{skills:Q,errors:q}}getAll(){return Array.from(this.skills.values())}get(O){return this.skills.get(O)}has(O){return this.skills.has(O)}async loadContent(O){let $=this.skills.get(O);if(!$)return null;if($.source==="builtin")return this.loadBuiltinContent(O);return H1($)}loadBuiltinContent(O){switch(O){case"skill-creator":return G1();case"update-config":return Y1();default:return null}}getModelInvocableSkills(){return Array.from(this.skills.values()).filter((O)=>!O.disableModelInvocation)}getUserInvocableSkills(){return Array.from(this.skills.values()).filter((O)=>O.userInvocable===!0)}generateAvailableSkillsList(){let O=this.getModelInvocableSkills();if(O.length===0)return"";let $=[];for(let Q of O){let q=Q.description.length>100?`${Q.description.substring(0,97)}...`:Q.description,Z=Q.argumentHint?`${Q.name} ${Q.argumentHint}`:Q.name;$.push(`- ${Z}: ${q}`)}return $.join(`
|
|
618
|
+
`)}get size(){return this.skills.size}async refresh(){return this.skills.clear(),this.pluginSkills.clear(),this.initialized=!1,this.initialize()}snapshot(){let O=new k({...this.config});return O.skills=new Map(Array.from(this.skills,([$,Q])=>[$,{...Q,...Q.allowedTools?{allowedTools:[...Q.allowedTools]}:{}}])),O.pluginSkills=new Map(Array.from(this.pluginSkills,([$,Q])=>[$,{...Q,metadata:{...Q.metadata,...Q.metadata.allowedTools?{allowedTools:[...Q.metadata.allowedTools]}:{}}}])),O.initialized=this.initialized,O}registerPluginSkill(O){this.pluginSkills.set(O.namespacedName,O),this.skills.set(O.namespacedName,O.metadata)}findPluginSkill(O){let $=this.pluginSkills.get(O);if($)return $;let Q=[];for(let q of this.pluginSkills.values())if(q.originalName===O)Q.push(q);if(Q.length===1)return Q[0];return}getAllPluginSkills(){return Array.from(this.pluginSkills.values())}clearPluginSkills(){for(let O of this.pluginSkills.values())this.skills.delete(O.namespacedName);this.pluginSkills.clear()}getPluginSkillCount(){return this.pluginSkills.size}}function P(O){return k.getInstance(O)}async function IO(O){return P(O).initialize()}import bO from"node:path";import{execSync as zO}from"node:child_process";import j1 from"node:fs";import M1 from"node:path";class E0{async execute(O,$){let Q=O.content;if($.signal?.aborted)throw Error("Command execution aborted");return Q=this.interpolateArgs(Q,$.args),Q=await this.executeBashEmbeds(Q,$),Q=await this.resolveFileReferences(Q,$.workspaceRoot),Q}interpolateArgs(O,$){O=O.replace(/\$ARGUMENTS/g,$.join(" "));for(let Q=9;Q>=1;Q--){let q=`$${Q}`,Z=$[Q-1]??"";O=O.split(q).join(Z)}return O}async executeBashEmbeds(O,$){let Q=/!`([^`]+)`/g,q=[];for(let J of O.matchAll(Q))q.push({match:J[0],command:J[1]});let Z=O;for(let{match:J,command:K}of q){if($.signal?.aborted){Z=Z.replace(J,"[Execution aborted]");continue}try{let L=zO(K,{cwd:$.workspaceRoot,encoding:"utf-8",timeout:30000,maxBuffer:1048576,stdio:["pipe","pipe","pipe"]}).trim();Z=Z.replace(J,L)}catch(L){let G=L instanceof Error?L.message:String(L);Z=Z.replace(J,`[Error executing '${K}': ${G}]`)}}return Z}async resolveFileReferences(O,$){let Q=/@([\w./-]+(?:\/[\w./-]+|\.[\w]+))/g,q=[];for(let J of O.matchAll(Q))q.push({match:J[0],relativePath:J[1]});let Z=O;for(let{match:J,relativePath:K}of q){let L=M1.resolve($,K);try{if(j1.statSync(L).isFile()){let W=j1.readFileSync(L,"utf-8"),H=`\`\`\`${M1.extname(K).slice(1)||"text"}
|
|
619
619
|
${W}
|
|
620
|
-
\`\`\``;Z=Z.replace(J,H)}}catch{}}return Z}hasDynamicContent(O){return{hasArgs:/\$ARGUMENTS|\$\d/.test(O),hasBashEmbeds:/!`[^`]+`/.test(O),hasFileRefs:/@[\w./-]+(?:\/[\w./-]+|\.[\w]+)/.test(O)}}}import
|
|
620
|
+
\`\`\``;Z=Z.replace(J,H)}}catch{}}return Z}hasDynamicContent(O){return{hasArgs:/\$ARGUMENTS|\$\d/.test(O),hasBashEmbeds:/!`[^`]+`/.test(O),hasFileRefs:/@[\w./-]+(?:\/[\w./-]+|\.[\w]+)/.test(O)}}}import f0 from"node:fs";import I1 from"node:os";import y from"node:path";import AO from"node:fs";import B1 from"node:path";import xO from"gray-matter";class y0{parse(O,$,Q,q){try{let Z=AO.readFileSync(O,"utf-8"),{data:J,content:K}=xO(Z),{name:L,namespace:G}=this.extractNameAndNamespace(O,$);if(!L)return null;return{name:L,namespace:G,config:this.normalizeConfig(J),content:K.trim(),path:O,source:Q,sourceDir:q}}catch(Z){return console.error(`Failed to parse command file: ${O}`,Z),null}}normalizeConfig(O){return{description:this.asString(O.description),allowedTools:this.parseAllowedTools(O["allowed-tools"]),argumentHint:this.asString(O["argument-hint"]),model:this.asString(O.model),disableModelInvocation:O["disable-model-invocation"]===!0}}asString(O){if(typeof O==="string"&&O.trim())return O.trim();return}parseAllowedTools(O){if(!O)return;if(Array.isArray(O))return O.map(($)=>String($).trim()).filter(Boolean);if(typeof O==="string")return O.split(",").map(($)=>$.trim()).filter(Boolean);return}extractNameAndNamespace(O,$){let q=B1.relative($,O).split(B1.sep),Z=q.pop();if(!Z)return{name:""};let J=Z.replace(/\.md$/i,""),K=q.length>0?q.join("/"):void 0;return{name:J,namespace:K}}validateConfig(O){let $=[];if(O.model&&!this.isValidModelId(O.model))$.push(`Invalid model ID: ${O.model}`);return $}isValidModelId(O){return O.length>0&&O.length<200}}class h0{parser=new y0;async discover(O){let $=[],Q=[],q=[],Z=this.getSearchDirs(O);for(let J of Z){if(!f0.existsSync(J.path))continue;Q.push(J.path);try{let K=await this.scanDirectory(J.path);for(let L of K)try{let G=this.parser.parse(L,J.path,J.source,J.sourceDir);if(G)$.push(G)}catch(G){q.push({path:L,error:G instanceof Error?G.message:String(G)})}}catch(K){q.push({path:J.path,error:K instanceof Error?K.message:String(K)})}}return{commands:$,scannedDirs:Q,errors:q}}getSearchDirs(O){let $=I1.homedir();return[{path:y.join($,".blade","commands"),source:"user",sourceDir:"blade"},{path:y.join($,".claude","commands"),source:"user",sourceDir:"claude"},{path:y.join(O,".blade","commands"),source:"project",sourceDir:"blade"},{path:y.join(O,".claude","commands"),source:"project",sourceDir:"claude"}]}async scanDirectory(O){let $=[],Q=async(q)=>{let Z=await f0.promises.readdir(q,{withFileTypes:!0});for(let J of Z){let K=y.join(q,J.name);if(J.isDirectory())await Q(K);else if(J.isFile()&&J.name.endsWith(".md"))$.push(K)}};return await Q(O),$}async hasCommands(O){let $=this.getSearchDirs(O);for(let Q of $){if(!f0.existsSync(Q.path))continue;if((await this.scanDirectory(Q.path)).length>0)return!0}return!1}getCommandDirs(O){let $=I1.homedir();return{projectBlade:y.join(O,".blade","commands"),projectClaude:y.join(O,".claude","commands"),userBlade:y.join($,".blade","commands"),userClaude:y.join($,".claude","commands")}}}class w{static instances=new Map;commands=new Map;pluginCommands=new Map;loader=new h0;executor=new E0;initialized=!1;workspaceRoot="";lastDiscoveryResult=null;static getInstance(O=x()){let $=bO.resolve(O),Q=w.instances.get($);if(!Q)Q=new w,w.instances.set($,Q);return Q}static resetInstance(){w.instances.clear()}constructor(){}async initialize(O){this.workspaceRoot=O;let $=await this.loader.discover(O),Q=(await T.getInstance().getStatus(O)).state==="trusted",q={...$,commands:$.commands.filter((Z)=>Z.source!=="project"||Q)};this.lastDiscoveryResult=q,this.commands.clear();for(let Z of q.commands)this.commands.set(Z.name,Z);return this.initialized=!0,q}async refresh(){if(!this.workspaceRoot)throw Error("Registry not initialized. Call initialize() first.");return this.initialize(this.workspaceRoot)}isInitialized(){return this.initialized}getWorkspaceRoot(){return this.workspaceRoot}getCommand(O){return this.commands.get(O)}hasCommand(O){return this.commands.has(O)}getAllCommands(){return Array.from(this.commands.values())}getCommandCount(){return this.commands.size}getModelInvocableCommands(){return this.getAllCommands().filter((O)=>O.config.description&&!O.config.disableModelInvocation)}getModelInvocablePluginCommands(){return this.getAllPluginCommands().filter((O)=>O.config.description&&!O.config.disableModelInvocation)}async executeCommand(O,$){let Q=this.getCommand(O);if(!Q)return null;return this.executor.execute(Q,$)}getCommandLabel(O){let $=O.source==="project"?"project":"user";if(O.namespace)return`(${$}:${O.namespace})`;return`(${$})`}getCommandDisplayName(O){let $=[`/${O.name}`];if(O.config.argumentHint)$.push(O.config.argumentHint);if(O.config.description)$.push("-",O.config.description);return $.push(this.getCommandLabel(O)),$.join(" ")}getCommandsBySource(){let O=[],$=[];for(let Q of this.commands.values())if(Q.source==="project")O.push(Q);else $.push(Q);return{project:O,user:$}}getLastDiscoveryResult(){return this.lastDiscoveryResult}getCommandDirs(){if(!this.workspaceRoot)return null;return this.loader.getCommandDirs(this.workspaceRoot)}generateCommandListDescription(O=15000){let $=[...this.getModelInvocableCommands().map((K)=>({name:K.name,description:K.config.description,argumentHint:K.config.argumentHint,label:this.getCommandLabel(K)})),...this.getModelInvocablePluginCommands().map((K)=>({name:K.namespacedName,description:K.config.description,argumentHint:K.config.argumentHint,label:`(plugin:${K.pluginName})`}))],Q=$.length;if(Q===0)return{text:"No custom commands available.",includedCount:0,totalCount:0};let q=`Available custom commands:
|
|
621
621
|
|
|
622
|
-
`,Z=q.length,J=0;for(let K of $){let
|
|
622
|
+
`,Z=q.length,J=0;for(let K of $){let L=K.argumentHint?` ${K.argumentHint}`:"",G=`- /${K.name}${L}: ${K.description} ${K.label}
|
|
623
623
|
`;if(Z+G.length>O)break;q+=G,Z+=G.length,J++}if(J<Q)q+=`
|
|
624
|
-
(${J} of ${Q} commands shown due to character budget)`;return{text:q,includedCount:J,totalCount:Q}}snapshot(){let O=new w;return O.workspaceRoot=this.workspaceRoot,O.commands=new Map(Array.from(this.commands,([$,Q])=>[$,{...Q,config:{...Q.config,...Q.config.allowedTools?{allowedTools:[...Q.config.allowedTools]}:{}}}])),O.pluginCommands=new Map(Array.from(this.pluginCommands,([$,Q])=>[$,{...Q,config:{...Q.config,...Q.config.allowedTools?{allowedTools:[...Q.config.allowedTools]}:{}}}])),O.initialized=this.initialized,O.lastDiscoveryResult=this.lastDiscoveryResult?{commands:Array.from(O.commands.values()),scannedDirs:[...this.lastDiscoveryResult.scannedDirs],errors:this.lastDiscoveryResult.errors.map(($)=>({...$}))}:null,O}registerPluginCommand(O){this.pluginCommands.set(O.namespacedName,O)}findPluginCommand(O){let $=this.pluginCommands.get(O);if($)return $;let Q=[];for(let q of this.pluginCommands.values())if(q.originalName===O)Q.push(q);if(Q.length===1)return Q[0];return}hasPluginCommand(O){return this.findPluginCommand(O)!==void 0}getAllPluginCommands(){return Array.from(this.pluginCommands.values())}async executePluginCommand(O,$){let Q=this.findPluginCommand(O);if(!Q)return null;let q={name:Q.namespacedName,config:Q.config,content:Q.content,path:Q.path,source:"project",sourceDir:"blade"};return this.executor.execute(q,$)}clearPluginCommands(){this.pluginCommands.clear()}getPluginCommandCount(){return this.pluginCommands.size}hasPluginCommandConflict(O){let $=0;for(let Q of this.pluginCommands.values())if(Q.originalName===O){if($++,$>1)return!0}return!1}getPluginCommandProviders(O){let $=[];for(let Q of this.pluginCommands.values())if(Q.originalName===O)$.push(Q.pluginName);return $}}import O$ from"node:path";import{satisfies as h0,validRange as M1}from"semver";var bO=/^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$|^[a-z0-9]$/;function P0(O){if(O.bladeVersion!==void 0&&(!O.bladeVersion.trim()||!M1(O.bladeVersion)))throw Error(`Invalid Blade version range in plugin "${O.name}": ${O.bladeVersion}`);for(let[$,Q]of Object.entries(O.dependencies??{})){if(!bO.test($))throw Error(`Invalid dependency name in plugin "${O.name}": ${$}`);if($===O.name)throw Error(`Plugin "${O.name}" cannot depend on itself`);if(!Q.trim()||!M1(Q))throw Error(`Invalid dependency range for ${$} in plugin "${O.name}": ${Q}`)}}function A1(O,$=k0()){if(P0(O),O.bladeVersion&&!h0($,O.bladeVersion))throw Error(`Plugin "${O.name}" requires Blade ${O.bladeVersion}, current ${$}`)}function z1(O,$){O.status="error",O.compatibilityIssues=[...O.compatibilityIssues??[],$],O.error=O.compatibilityIssues.map((Q)=>Q.message).join("; ")}function x1(O,$=k0()){let Q=[],q=new Map(O.map((J)=>[J.manifest.name,J]));for(let J of O){if(J.status!=="active")continue;if(J.manifest.bladeVersion&&!h0($,J.manifest.bladeVersion)){let K={code:"blade-version",expected:J.manifest.bladeVersion,actual:$,message:`Requires Blade ${J.manifest.bladeVersion}, current ${$}`};z1(J,K),Q.push(K)}}let Z=!0;while(Z){Z=!1;for(let J of O){if(J.status!=="active")continue;for(let[K,Y]of Object.entries(J.manifest.dependencies??{})){let G=q.get(K),W;if(!G)W={code:"dependency-missing",dependency:K,expected:Y,message:`Missing dependency ${K}@${Y}`};else if(!h0(G.manifest.version,Y))W={code:"dependency-version",dependency:K,expected:Y,actual:G.manifest.version,message:`Dependency ${K} requires ${Y}, found ${G.manifest.version}`};else if(G.status!=="active")W={code:"dependency-inactive",dependency:K,expected:Y,actual:G.status,message:`Dependency ${K} is ${G.status}`};if(!W)continue;z1(J,W),Q.push(W),Z=!0;break}}}return Q}import{execFile as pO}from"node:child_process";import{createHash as mO,randomUUID as y1}from"node:crypto";import{constants as e,promises as F}from"node:fs";import{homedir as m0}from"node:os";import S from"node:path";import{promisify as gO}from"node:util";import{Mutex as uO}from"async-mutex";import{satisfies as g0}from"semver";import cO from"write-file-atomic";import*as X0 from"node:fs/promises";import*as R1 from"node:path";var c=L.String({minLength:2,maxLength:64,pattern:"^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]{1,2}$"}),d0=L.String({pattern:"^\\d+\\.\\d+\\.\\d+(-[\\w.]+)?(\\+[\\w.]+)?$"}),_O=L.Object({name:L.String({minLength:1}),email:L.Optional(L.String({format:"email"})),url:L.Optional(L.String({format:"uri"}))}),T1=L.Object({name:c,description:L.String({minLength:1,maxLength:500}),version:d0,author:L.Optional(_O),license:L.Optional(L.String()),repository:L.Optional(L.String({format:"uri"})),homepage:L.Optional(L.String({format:"uri"})),keywords:L.Optional(L.Array(L.String())),dependencies:L.Optional(L.Record(L.String(),L.String())),bladeVersion:L.Optional(L.String())}),TO=L.Union([L.Object({type:L.Literal("git"),url:L.String({minLength:1}),ref:L.Optional(L.String({minLength:1}))},{additionalProperties:!1}),L.Object({type:L.Literal("local"),path:L.String({minLength:1})},{additionalProperties:!1}),L.Object({type:L.Literal("marketplace"),marketplace:c},{additionalProperties:!1})]),wO=L.Object({name:c,source:TO,installPath:L.String({minLength:1}),version:d0,revision:L.String({minLength:1,maxLength:128}),contentDigest:L.String({pattern:"^[a-f0-9]{64}$"}),installedAt:L.String({format:"date-time"}),updatedAt:L.String({format:"date-time"})},{additionalProperties:!1}),CO=L.Union([L.Object({type:L.Literal("git"),url:L.String({minLength:1}),ref:L.Optional(L.String({minLength:1}))},{additionalProperties:!1}),L.Object({type:L.Literal("local"),path:L.String({minLength:1})},{additionalProperties:!1})]),DO=L.Object({name:c,source:CO,installPath:L.String({minLength:1}),revision:L.String({minLength:1,maxLength:128}),contentDigest:L.String({pattern:"^[a-f0-9]{64}$"}),addedAt:L.String({format:"date-time"}),updatedAt:L.String({format:"date-time"})},{additionalProperties:!1}),NO=L.Object({version:L.Literal(1),installed:L.Record(L.String(),wO),marketplaces:L.Record(L.String(),DO)},{additionalProperties:!1}),w1=L.Object({name:L.String({minLength:1}),email:L.Optional(L.String({format:"email"})),url:L.Optional(L.String({format:"uri"}))}),RO=L.Object({name:c,description:L.Optional(L.String({maxLength:500})),version:L.Optional(d0),author:L.Optional(L.Union([L.String({minLength:1}),w1])),source:L.Union([L.String({minLength:1}),L.Object({source:L.Literal("url"),url:L.String({minLength:1}),ref:L.Optional(L.String({minLength:1})),sha:L.Optional(L.String({pattern:"^[a-fA-F0-9]{40}$"}))})]),category:L.Optional(L.String()),homepage:L.Optional(L.String({format:"uri"})),tags:L.Optional(L.Array(L.String()))}),EO=L.Object({name:c,description:L.Optional(L.String({maxLength:500})),owner:L.Optional(w1),metadata:L.Optional(L.Object({description:L.Optional(L.String({maxLength:500})),version:L.Optional(L.String())})),plugins:L.Array(RO,{maxItems:5000})}),b1=L.Object({type:_1(["stdio","sse","http"]),command:L.Optional(L.String()),args:L.Optional(L.Array(L.String())),env:L.Optional(L.Record(L.String(),L.String())),cwd:L.Optional(L.String()),url:L.Optional(L.String({format:"uri"})),headers:L.Optional(L.Record(L.String(),L.String())),timeout:L.Optional(L.Integer({minimum:1000,maximum:1800000})),idleTimeout:L.Optional(L.Integer({minimum:1000,maximum:1800000})),sampling:L.Optional(L.Object({enabled:L.Boolean(),maxTokens:L.Optional(L.Integer({minimum:1,maximum:4096})),maxRequestsPerToolCall:L.Optional(L.Integer({minimum:1,maximum:8})),maxInputBytes:L.Optional(L.Integer({minimum:1,maximum:1048576}))})),oauth:L.Optional(L.Object({enabled:L.Optional(L.Boolean()),clientId:L.Optional(L.String({minLength:1,maxLength:4096})),scopes:L.Optional(L.Array(L.String({minLength:1,maxLength:128}),{maxItems:32})),callbackPort:L.Optional(L.Integer({minimum:1024,maximum:65535}))},{additionalProperties:!1})),healthCheck:L.Optional(L.Object({enabled:L.Optional(L.Boolean()),interval:L.Optional(L.Number({exclusiveMinimum:0})),timeout:L.Optional(L.Number({exclusiveMinimum:0})),failureThreshold:L.Optional(L.Number({exclusiveMinimum:0}))}))}),vO=L.Union([L.Object({mcpServers:L.Record(L.String(),b1)}),L.Record(L.String(),b1)]);function C1(O){return u(vO,O)}function D1(O){return u(NO,O)}function N1(O){return u(EO,O)}var yO=[{dir:".blade-plugin",source:"blade"},{dir:".claude-plugin",source:"claude"}];async function a(O){for(let{dir:$,source:Q}of yO){let q=R1.join(O,$,"plugin.json");try{await X0.access(q)}catch{continue}try{let Z=await X0.readFile(q,"utf-8"),J=JSON.parse(Z),K=u(T1,J);if(!K.success){let Y=K.error.issues.map((G)=>`${G.path.join(".")}: ${G.message}`).join("; ");throw Error(`Invalid plugin.json: ${Y}`)}return P0(K.data),j.debug(`Parsed plugin manifest from ${q}`),{manifest:K.data,source:Q,manifestPath:q}}catch(Z){if(Z instanceof SyntaxError)throw Error(`Invalid JSON in ${q}: ${Z.message}`);throw Z}}return null}import{realpathSync as fO}from"node:fs";import p0 from"node:path";var hO=/^[a-fA-F0-9]{40}$/;class E extends Error{code;constructor(O,$){super($);this.code=O;this.name="PluginSourcePolicyError"}}function kO(O,$){let Q=(Z)=>{try{return fO.native(Z)}catch{return p0.resolve(Z)}},q=p0.relative(Q(O),Q($));return q===""||!q.startsWith("..")&&!p0.isAbsolute(q)}function PO(O){let $=O.match(/^[^@]+@([^:]+):/);if($?.[1])return $[1].toLowerCase();try{return new URL(O).hostname.toLowerCase()}catch{return}}function dO(O,$){if(!$.startsWith("*."))return O===$;let Q=$.slice(1);return O.endsWith(Q)&&O.length>Q.length}function E1(O,$,Q){if(O.requireGitCommitSha&&(!$.ref||!hO.test($.ref)))throw new E("PLUGIN_GIT_SHA_REQUIRED",`${Q} must pin a full 40-character Git commit SHA`);if(!O.restrictToAllowedSources)return;let q=PO($.url);if(!q||!O.allowedGitHosts.some((Z)=>dO(q,Z)))throw new E("PLUGIN_SOURCE_BLOCKED",`${Q} Git host is not allowed by plugin source policy`)}function v1(O,$,Q){if(!O.restrictToAllowedSources)return;if(!O.allowedLocalRoots.some((q)=>kO(q,$)))throw new E("PLUGIN_SOURCE_BLOCKED",`${Q} local path is not allowed by plugin source policy`)}function l(O,$,Q="Marketplace"){if($.type==="git")E1(O,$,Q);else v1(O,$.path,Q)}function r(O,$,Q,q="Plugin"){if($.type==="git"){E1(O,$,q);return}if($.type==="local"){v1(O,$.path,q);return}if(O.restrictToAllowedSources&&!O.allowedMarketplaces.includes($.marketplace))throw new E("PLUGIN_SOURCE_BLOCKED",`Marketplace "${$.marketplace}" is not allowed by plugin source policy`);let Z=Q[$.marketplace];if(!Z)throw new E("MARKETPLACE_NOT_FOUND",`Marketplace "${$.marketplace}" is not configured`);l(O,Z.source,`Marketplace "${$.marketplace}"`)}var lO=gO(pO),sO=1,f1=2097152,h1=1e4,k1=104857600,nO=120000,oO=15000,iO=600000,l0=/^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$|^[a-z0-9]$/,P1=/^[a-fA-F0-9]{40}$/,tO=[S.join(".blade-plugin","marketplace.json"),S.join(".claude-plugin","marketplace.json"),"marketplace.json"];class V extends Error{code;constructor(O,$){super($);this.code=O;this.name="PluginPackageError"}}function aO(){return{version:sO,installed:{},marketplaces:{}}}function s(O){return O.code==="ENOENT"}function n(O){if(O instanceof V||O instanceof E)return{code:O.code,message:O.message};return{code:"PLUGIN_PACKAGE_ERROR",message:O instanceof Error?O.message:String(O)}}function u0(O){return S.isAbsolute(O)||O.startsWith("./")||O.startsWith("../")||O==="~"||O.startsWith("~/")}function O0(O,$){let Q=S.relative(O,$);return Q===""||!Q.startsWith("..")&&!S.isAbsolute(Q)}function o(O,$="plugin"){if(!l0.test(O))throw new V("INVALID_PLUGIN_NAME",`Invalid ${$} name: ${O}`);return O}function rO(O){if(O.startsWith("git@"))return;let $=O.lastIndexOf("@");if($<=0||$===O.length-1)return;let Q=O.slice(0,$),q=O.slice($+1);if(!l0.test(Q)||!l0.test(q))return;return{pluginName:Q,marketplace:q}}class d1{userPluginsDir;stateRoot;statePath;mutex=new uO;constructor(O,$){this.userPluginsDir=O??S.join(m0(),".blade","plugins"),this.stateRoot=$??S.join(S.dirname(this.userPluginsDir),"plugin-state"),this.statePath=S.join(this.stateRoot,"state.json")}async install(O,$={}){return this.runMutation(async()=>{let Q=[];try{this.requireSourceTrust($.trusted);let q=await this.readState(),Z=await this.resolvePluginSource(O,q,$),J=this.effectivePolicy($.policy);if(r(J,Z.recordSource,q.marketplaces),Z.expectedName&&q.installed[Z.expectedName])throw new V("PLUGIN_ALREADY_INSTALLED",`Plugin "${Z.expectedName}" is already installed`);let K=await this.preparePluginClosure({state:q,source:Z.source,recordSource:Z.recordSource,expectedName:Z.expectedName,policy:J,updateRoot:!1});Q=K.prepared;let Y=await this.commitPreparedPlugins(q,Q),G=Y.records.get(K.root.name);if(!G)throw new V("PLUGIN_PACKAGE_ERROR","Root plugin was not committed");let W=Q.map((X)=>X.name).filter((X)=>X!==K.root.name);return j.info(`Installed managed plugin ${K.root.name} at revision ${G.revision}`),{success:!0,pluginName:K.root.name,pluginPath:G.installPath,manifest:K.root.manifest,installation:G,installedDependencies:W,changed:Y.changed.has(K.root.name)}}catch(q){await this.cleanupPreparedPlugins(Q);let Z=n(q);return{success:!1,code:Z.code,error:Z.message}}})}async update(O,$={}){return this.runMutation(async()=>{let Q=[];try{this.requireSourceTrust($.trusted),o(O);let q=await this.readState(),Z=q.installed[O];if(!Z)throw new V("PLUGIN_NOT_MANAGED",`Plugin "${O}" is not managed by Blade's package store`);let J=this.effectivePolicy($.policy);r(J,Z.source,q.marketplaces);let K=await this.preparePluginClosure({state:q,source:Z.source,recordSource:Z.source,expectedName:O,policy:J,updateRoot:!0});Q=K.prepared;let Y=await this.commitPreparedPlugins(q,Q),G=Y.records.get(O);if(!G)throw new V("PLUGIN_PACKAGE_ERROR","Updated root plugin was not committed");let W=Q.map((X)=>X.name).filter((X)=>X!==O&&Y.changed.has(X));return j.info(`Updated managed plugin ${O} to revision ${G.revision}`),{success:!0,pluginName:O,pluginPath:G.installPath,manifest:K.root.manifest,installation:G,updatedDependencies:W,changed:Y.changed.has(O)}}catch(q){await this.cleanupPreparedPlugins(Q);let Z=n(q);return{success:!1,pluginName:O,code:Z.code,error:Z.message}}})}async uninstall(O,$=!1){return this.runMutation(async()=>{try{if(o(O),!$)throw new V("CONFIRMATION_REQUIRED",`Uninstalling "${O}" requires explicit confirmation`);let Q=await this.readState(),q=Q.installed[O];if(!q)throw new V("PLUGIN_NOT_MANAGED",`Plugin "${O}" is not managed by Blade's package store`);return delete Q.installed[O],await this.writeState(Q),j.info(`Uninstalled managed plugin ${O}`),{success:!0,pluginName:O,installation:q}}catch(Q){let q=n(Q);return{success:!1,pluginName:O,code:q.code,error:q.message}}})}async listInstalled(){return Object.keys((await this.readState()).installed).sort()}async listInstallationRecords(){return Object.values((await this.readState()).installed).sort((O,$)=>O.name.localeCompare($.name))}async getInstallation(O){return(await this.readState()).installed[O]}async verifyInstallation(O){let $=await F.lstat(O.installPath);if(!$.isDirectory()||$.isSymbolicLink())throw new V("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" has an invalid package path`);let Q=await F.realpath(this.stateRoot),q=await F.realpath(O.installPath);if(!O0(Q,q))throw new V("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" has an invalid package path`);if(await this.digestTree(q)!==O.contentDigest)throw new V("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" failed content verification`);let J=await a(q);if(!J||J.manifest.name!==O.name||J.manifest.version!==O.version)throw new V("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" failed manifest verification`)}async addMarketplace(O,$={}){return this.runMutation(async()=>{let Q;try{let q=await this.normalizeMarketplaceSource(O,$);l(this.effectivePolicy($.policy),q);let Z=await this.readState();Q=await this.materializeSource(q);let J=await this.readMarketplaceManifest(Q.stagingPath),K=o(J.name,"marketplace");if(Z.marketplaces[K])throw new V("MARKETPLACE_ALREADY_EXISTS",`Marketplace "${K}" is already configured`);let{revision:Y,contentDigest:G}=Q,W=await this.publishPackage("marketplaces",K,Q);Q=void 0;let X=new Date().toISOString(),H={name:K,source:q,installPath:W,revision:Y,contentDigest:G,addedAt:X,updatedAt:X};return Z.marketplaces[K]=H,await this.writeState(Z),{success:!0,marketplace:H,manifest:J,changed:!0}}catch(q){if(Q)await F.rm(Q.stagingPath,{recursive:!0,force:!0});let Z=n(q);return{success:!1,code:Z.code,error:Z.message}}})}async refreshMarketplace(O,$){return this.runMutation(async()=>{let Q;try{o(O,"marketplace");let q=await this.readState(),Z=q.marketplaces[O];if(!Z)throw new V("MARKETPLACE_NOT_FOUND",`Marketplace "${O}" is not configured`);l(this.effectivePolicy($),Z.source,`Marketplace "${O}"`),Q=await this.materializeSource(Z.source);let J=await this.readMarketplaceManifest(Q.stagingPath);if(J.name!==O)throw new V("MARKETPLACE_IDENTITY_MISMATCH",`Marketplace "${O}" now declares "${J.name}"`);if(Z.revision===Q.revision&&Z.contentDigest===Q.contentDigest)return await F.rm(Q.stagingPath,{recursive:!0,force:!0}),Q=void 0,{success:!0,marketplace:Z,manifest:J,changed:!1};let{revision:K,contentDigest:Y}=Q,G=await this.publishPackage("marketplaces",O,Q);Q=void 0;let W={...Z,installPath:G,revision:K,contentDigest:Y,updatedAt:new Date().toISOString()};return q.marketplaces[O]=W,await this.writeState(q),{success:!0,marketplace:W,manifest:J,changed:!0}}catch(q){if(Q)await F.rm(Q.stagingPath,{recursive:!0,force:!0});let Z=n(q);return{success:!1,code:Z.code,error:Z.message}}})}async removeMarketplace(O,$=!1){return this.runMutation(async()=>{try{if(o(O,"marketplace"),!$)throw new V("CONFIRMATION_REQUIRED",`Removing marketplace "${O}" requires explicit confirmation`);let Q=await this.readState(),q=Q.marketplaces[O];if(!q)throw new V("MARKETPLACE_NOT_FOUND",`Marketplace "${O}" is not configured`);let Z=Object.values(Q.installed).filter((J)=>J.source.type==="marketplace"&&J.source.marketplace===O).map((J)=>J.name).sort();if(Z.length>0)throw new V("MARKETPLACE_IN_USE",`Marketplace "${O}" still owns installed plugins: ${Z.join(", ")}`);return delete Q.marketplaces[O],await this.writeState(Q),{success:!0,marketplace:q,changed:!0}}catch(Q){let q=n(Q);return{success:!1,code:q.code,error:q.message}}})}async listMarketplaces(){return Object.values((await this.readState()).marketplaces).sort((O,$)=>O.name.localeCompare($.name))}async listCatalogs(){let O=await this.listMarketplaces();return Promise.all(O.map(async($)=>({marketplace:$,manifest:await this.readMarketplaceManifest($.installPath)})))}async runMutation(O){return this.mutex.runExclusive(async()=>{let $=await this.acquireStoreLock();try{return await O()}finally{await $()}})}async acquireStoreLock(){await this.ensureStoreDirectory();let O=S.join(this.stateRoot,".operation.lock"),$=y1(),Q=Date.now()+oO;while(Date.now()<Q)try{let q=await F.open(O,e.O_CREAT|e.O_EXCL|e.O_WRONLY,384);return await q.writeFile(`${JSON.stringify({version:1,pid:process.pid,token:$,createdAt:new Date().toISOString()})}
|
|
625
|
-
`,"utf8"),await q.close(),async()=>{try{if(JSON.parse(await F.readFile(O,"utf8")).token===$)await F.unlink(O)}catch(Z){if(!s(Z))j.warn(`Failed to release plugin package lock: ${Z}`)}}}catch(q){if(q.code!=="EEXIST")throw q;await this.removeStaleStoreLock(O),await new Promise((Z)=>setTimeout(Z,50))}throw new V("PLUGIN_STORE_BUSY","Plugin package store is busy; retry the operation")}async removeStaleStoreLock(O){try{let $=await F.lstat(O);if(!$.isFile()||$.isSymbolicLink())throw new V("PLUGIN_STORE_INVALID","Plugin package lock must be a regular file");if(process.getuid&&$.uid!==process.getuid()||($.mode&511)!==384)throw new V("PLUGIN_STORE_INVALID","Plugin package lock ownership or permissions are invalid");if(Date.now()-$.mtimeMs<iO)return;await F.unlink(O)}catch($){if(!s($)&&$ instanceof V)throw $}}requireSourceTrust(O){if(O!==!0)throw new V("SOURCE_TRUST_REQUIRED","Plugin installation or update requires explicit source trust")}effectivePolicy(O){return O??V0.pluginSourcePolicy}async preparePluginClosure(O){let $=[],Q=new Map,q=[],Z=(K,Y,G,W)=>{if(!g0(Y,G))throw new V("DEPENDENCY_VERSION_MISMATCH",`Plugin "${W}" requires ${K}@${G}, found ${Y}`)},J=async(K)=>{let Y=K.expectedName?O.state.installed[K.expectedName]:void 0;if(Y&&!K.root&&K.requiredRange){if(g0(Y.version,K.requiredRange))return Y;if(K.recordSource.type!=="marketplace"||Y.source.type!=="marketplace"||Y.source.marketplace!==K.recordSource.marketplace)Z(Y.name,Y.version,K.requiredRange,K.requiredBy??"dependency");K={...K,source:Y.source,recordSource:Y.source}}if(Y&&K.root&&!O.updateRoot)throw new V("PLUGIN_ALREADY_INSTALLED",`Plugin "${Y.name}" is already installed`);if(K.expectedName){let B=Q.get(K.expectedName);if(B){if(K.requiredRange)Z(B.name,B.manifest.version,K.requiredRange,K.requiredBy??"dependency");return B}if(q.includes(K.expectedName))throw new V("DEPENDENCY_CYCLE",`Plugin dependency cycle: ${[...q,K.expectedName].join(" -> ")}`)}let G=await this.materializePluginSource(K.source,O.state,K.expectedName,O.policy),W=await a(G.stagingPath);if(!W)throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),new V("INVALID_PLUGIN","Plugin manifest was not found");let X=o(W.manifest.name);if(K.expectedName&&K.expectedName!==X)throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),new V("PLUGIN_IDENTITY_MISMATCH",`Requested "${K.expectedName}" but source declares "${X}"`);if(q.includes(X))throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),new V("DEPENDENCY_CYCLE",`Plugin dependency cycle: ${[...q,X].join(" -> ")}`);if(K.root&&O.state.installed[X]&&!O.updateRoot)throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),new V("PLUGIN_ALREADY_INSTALLED",`Plugin "${X}" is already installed`);try{A1(W.manifest)}catch(B){throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),new V("VERSION_INCOMPATIBLE",B instanceof Error?B.message:String(B))}let H={name:X,source:K.recordSource,manifest:W.manifest,materialized:G,existing:O.state.installed[X]};q.push(X);try{for(let[B,g]of Object.entries(W.manifest.dependencies??{}).sort(([q0],[N])=>q0.localeCompare(N))){let q0=Q.get(B);if(q0){Z(B,q0.manifest.version,g,X);continue}let N=O.state.installed[B];if(N&&g0(N.version,g))continue;if(K.recordSource.type!=="marketplace")throw new V(N?"DEPENDENCY_VERSION_MISMATCH":"DEPENDENCY_MISSING",N?`Plugin "${X}" requires ${B}@${g}, found ${N.version}`:`Plugin "${X}" requires missing dependency ${B}@${g}; direct sources cannot auto-install dependencies`);let b0=await J({source:N?.source.type==="marketplace"&&N.source.marketplace===K.recordSource.marketplace?N.source:{type:"marketplace",marketplace:K.recordSource.marketplace},recordSource:{type:"marketplace",marketplace:K.recordSource.marketplace},expectedName:B,requiredRange:g,requiredBy:X,root:!1});Z(B,"manifest"in b0?b0.manifest.version:b0.version,g,X)}}catch(B){throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),B}finally{q.pop()}return Q.set(X,H),$.push(H),H};try{let K=await J({source:O.source,recordSource:O.recordSource,expectedName:O.expectedName,root:!0});if(!("manifest"in K))throw new V("PLUGIN_PACKAGE_ERROR","Root plugin unexpectedly resolved to an existing dependency");return{root:K,prepared:$}}catch(K){throw await this.cleanupPreparedPlugins($),K}}async commitPreparedPlugins(O,$){let Q=new Map,q=new Set,Z=new Date().toISOString();for(let J of $){let K=J.existing;if(K?.revision===J.materialized.revision&&K.contentDigest===J.materialized.contentDigest){await F.rm(J.materialized.stagingPath,{recursive:!0,force:!0}),Q.set(J.name,K);continue}let Y=await this.publishPackage("packages",J.name,J.materialized),G={name:J.name,source:J.source,installPath:Y,version:J.manifest.version,revision:J.materialized.revision,contentDigest:J.materialized.contentDigest,installedAt:K?.installedAt??Z,updatedAt:Z};O.installed[J.name]=G,Q.set(J.name,G),q.add(J.name)}if(q.size>0)await this.writeState(O);return{records:Q,changed:q}}async cleanupPreparedPlugins(O){await Promise.all(O.map(($)=>F.rm($.materialized.stagingPath,{recursive:!0,force:!0})))}async resolvePluginSource(O,$,Q){let q=O.trim();if(!q)throw new V("INVALID_SOURCE","Plugin source is required");let Z=rO(q);if(Z){if(!$.marketplaces[Z.marketplace])throw new V("MARKETPLACE_NOT_FOUND",`Marketplace "${Z.marketplace}" is not configured`);return{source:{type:"marketplace",marketplace:Z.marketplace},recordSource:{type:"marketplace",marketplace:Z.marketplace},expectedName:Z.pluginName}}if(!u0(q)&&!this.looksLikeGitSource(q)){let K=[];for(let Y of await this.catalogsFromState($))if(Y.manifest.plugins.some((G)=>G.name===q))K.push({pluginName:q,marketplace:Y.marketplace.name});if(K.length===1)return{source:{type:"marketplace",marketplace:K[0].marketplace},recordSource:{type:"marketplace",marketplace:K[0].marketplace},expectedName:q};if(K.length>1)throw new V("AMBIGUOUS_PLUGIN_SOURCE",`Plugin "${q}" exists in multiple marketplaces; use name@marketplace`)}if(u0(q)){let Y={type:"local",path:await this.resolveLocalSource(q,Q.workspaceRoot)};return{source:Y,recordSource:Y}}let J=this.normalizeGitSource(q,Q.ref);return{source:J,recordSource:J}}async materializePluginSource(O,$,Q,q){if(O.type!=="marketplace")return this.materializeSource(O);let Z=$.marketplaces[O.marketplace];if(!Z)throw new V("MARKETPLACE_NOT_FOUND",`Marketplace "${O.marketplace}" is not configured`);let J=await this.readMarketplaceManifest(Z.installPath),K=Q?J.plugins.filter((Y)=>Y.name===Q):J.plugins;if(K.length!==1)throw new V("MARKETPLACE_PLUGIN_NOT_FOUND",Q?`Plugin "${Q}" was not found in marketplace "${O.marketplace}"`:`Marketplace "${O.marketplace}" requires an explicit plugin name`);return this.materializeMarketplaceEntry(Z,K[0],q)}async materializeMarketplaceEntry(O,$,Q){if(typeof $.source!=="string"){let K=this.normalizeGitSource($.source.url,$.source.sha??$.source.ref);return l(Q,K,`Plugin "${$.name}"`),this.materializeSource(K)}if($.source.startsWith("https://")||$.source.startsWith("ssh://")||$.source.startsWith("git@")){let K=this.normalizeGitSource($.source);return l(Q,K,`Plugin "${$.name}"`),this.materializeSource(K)}let q=await F.realpath(O.installPath),Z=S.resolve(q,$.source);if(!O0(q,Z)||Z===q)throw new V("MARKETPLACE_PATH_ESCAPE",`Plugin "${$.name}" source escapes its marketplace`);let J=await F.realpath(Z);if(!O0(q,J))throw new V("MARKETPLACE_PATH_ESCAPE",`Plugin "${$.name}" source escapes its marketplace`);return this.materializeSource({type:"local",path:J})}async normalizeMarketplaceSource(O,$){let Q=O.trim();if(!Q)throw new V("INVALID_SOURCE","Marketplace source is required");if(u0(Q))return{type:"local",path:await this.resolveLocalSource(Q,$.workspaceRoot)};return this.normalizeGitSource(Q,$.ref)}normalizeGitSource(O,$){let Q=O;if(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(O))Q=`https://github.com/${O}.git`;else if(O.startsWith("git@")){if(!/^git@[A-Za-z0-9.-]+:[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\.git)?$/.test(O))throw new V("INVALID_SOURCE","Invalid SSH Git source")}else{let q;try{q=new URL(O)}catch{throw new V("INVALID_SOURCE","Invalid Git source")}if(q.protocol!=="https:"&&q.protocol!=="ssh:")throw new V("UNSAFE_GIT_PROTOCOL","Git sources must use HTTPS or SSH");if(q.password||q.protocol==="https:"&&q.username)throw new V("GIT_CREDENTIALS_IN_URL","Git credentials must not be embedded in plugin source URLs");if(q.hash||q.search)throw new V("INVALID_SOURCE","Use the ref option instead of URL query or fragment parameters");Q=q.toString()}if($&&(!$.trim()||$.startsWith("-")||$.includes("\x00")))throw new V("INVALID_GIT_REF","Invalid Git ref");return{type:"git",url:Q,...$?{ref:$}:{}}}looksLikeGitSource(O){return O.startsWith("https://")||O.startsWith("ssh://")||O.startsWith("git@")||/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(O)}async resolveLocalSource(O,$){let Q=O==="~"?m0():O.startsWith("~/")?S.join(m0(),O.slice(2)):O,q=S.resolve($??process.cwd(),Q),Z;try{Z=await F.realpath(q)}catch{throw new V("LOCAL_SOURCE_NOT_FOUND",`Local source does not exist: ${O}`)}let J=await F.lstat(Z);if(!J.isDirectory()||J.isSymbolicLink())throw new V("INVALID_LOCAL_SOURCE","Local plugin sources must be regular directories");return Z}async materializeSource(O){await this.ensureStoreDirectory();let $=S.join(this.stateRoot,"staging",y1());if(await F.mkdir(S.dirname($),{recursive:!0,mode:448}),O.type==="git"){await this.cloneGitSource(O,$);let q=await this.runGit(["rev-parse","HEAD"],$);if(O.ref&&P1.test(O.ref)&&q.toLowerCase()!==O.ref.toLowerCase())throw await F.rm($,{recursive:!0,force:!0}),new V("GIT_PIN_MISMATCH","Plugin checkout does not match the requested Git commit SHA");await F.rm(S.join($,".git"),{recursive:!0,force:!0});let Z=await this.digestTree($);return{stagingPath:$,revision:q,contentDigest:Z}}await this.copyLocalSource(O.path,$);let Q=await this.digestTree($);return{stagingPath:$,revision:`local-${Q.slice(0,40)}`,contentDigest:Q}}async cloneGitSource(O,$){try{if(O.ref&&P1.test(O.ref))await F.mkdir($,{recursive:!0,mode:448}),await this.runGit(["init"],$),await this.runGit(["remote","add","origin",O.url],$),await this.runGit(["fetch","--depth","1","origin",O.ref],$),await this.runGit(["checkout","--detach","FETCH_HEAD"],$);else{let Q=["clone","--depth","1","--no-tags"];if(O.ref)Q.push("--branch",O.ref);Q.push("--",O.url,$),await this.runGit(Q)}}catch(Q){throw await F.rm($,{recursive:!0,force:!0}),new V("GIT_CLONE_FAILED",`Failed to clone plugin source: ${this.sanitizeError(Q)}`)}}async runGit(O,$){let Q=await lO("git",O,{cwd:$,encoding:"utf8",timeout:nO,maxBuffer:4194304,env:{...process.env,GIT_TERMINAL_PROMPT:"0"}}),q=typeof Q==="string"?Q:Q.stdout;if(q===void 0)throw Error("Git process returned an invalid result");return String(q).trim()}sanitizeError(O){return(O instanceof Error?O.message:String(O)).replaceAll(this.stateRoot,"<plugin-store>").slice(0,500)}async copyLocalSource(O,$){await this.digestTree(O),await F.cp(O,$,{recursive:!0,force:!1,errorOnExist:!0,filter:(Q)=>{return!S.relative(O,Q).split(S.sep).includes(".git")}})}async digestTree(O){let $=[],Q=0,q=async(J)=>{let K=await F.readdir(J,{withFileTypes:!0});K.sort((Y,G)=>Y.name.localeCompare(G.name));for(let Y of K){if(Y.name===".git")continue;let G=S.join(J,Y.name),W=S.relative(O,G),X=await F.lstat(G);if(X.isSymbolicLink())throw new V("PACKAGE_SYMLINK_FORBIDDEN",`Plugin packages may not contain symbolic links: ${W}`);if(X.isDirectory()){await q(G);continue}if(!X.isFile())throw new V("PACKAGE_ENTRY_FORBIDDEN",`Plugin packages may contain only files and directories: ${W}`);if(Q+=X.size,$.push({relative:W,absolute:G,executable:(X.mode&73)!==0}),$.length>h1||Q>k1)throw new V("PACKAGE_TOO_LARGE",`Plugin package exceeds ${h1} files or ${k1} bytes`)}};await q(O);let Z=mO("sha256");for(let J of $)Z.update(J.relative),Z.update("\x00"),Z.update(J.executable?"x":"-"),Z.update("\x00"),Z.update(await F.readFile(J.absolute)),Z.update("\x00");return Z.digest("hex")}async publishPackage(O,$,Q){let q=S.join(this.stateRoot,O,$,Q.contentDigest);await F.mkdir(S.dirname(q),{recursive:!0,mode:448});try{let Z=await F.lstat(q);if(!Z.isDirectory()||Z.isSymbolicLink())throw new V("PACKAGE_STORE_CONFLICT","Plugin package destination is not a regular directory");return await F.rm(Q.stagingPath,{recursive:!0,force:!0}),q}catch(Z){if(!s(Z))throw Z}return await F.rename(Q.stagingPath,q),q}async readMarketplaceManifest(O){for(let $ of tO){let Q=S.join(O,$);try{let q=await F.lstat(Q);if(!q.isFile()||q.isSymbolicLink()||q.size>f1)throw new V("INVALID_MARKETPLACE","Marketplace manifest must be a regular bounded file");let Z=JSON.parse(await F.readFile(Q,"utf8")),J=N1(Z);if(!J.success)throw new V("INVALID_MARKETPLACE",`Invalid marketplace manifest: ${J.error.issues.map((Y)=>`${Y.path.join(".")}: ${Y.message}`).join("; ")}`);let K=new Set;for(let Y of J.data.plugins){if(K.has(Y.name))throw new V("INVALID_MARKETPLACE",`Marketplace contains duplicate plugin: ${Y.name}`);K.add(Y.name)}return J.data}catch(q){if(s(q))continue;if(q instanceof SyntaxError)throw new V("INVALID_MARKETPLACE","Marketplace manifest contains invalid JSON");throw q}}throw new V("INVALID_MARKETPLACE","Marketplace manifest was not found")}async catalogsFromState(O){let $=Object.values(O.marketplaces).sort((Q,q)=>Q.name.localeCompare(q.name));return Promise.all($.map(async(Q)=>({marketplace:Q,manifest:await this.readMarketplaceManifest(Q.installPath)})))}async readState(){let O;try{O=await F.open(this.statePath,e.O_RDONLY|e.O_NOFOLLOW)}catch($){if(s($))return aO();throw new V("PLUGIN_STATE_INVALID","Plugin package state must be a regular file")}try{let $=await O.stat();if(!$.isFile()||$.size>f1)throw new V("PLUGIN_STATE_INVALID","Plugin package state is invalid");if(process.getuid&&$.uid!==process.getuid())throw new V("PLUGIN_STATE_INVALID","Plugin package state must be owned by the current user");if(($.mode&511)!==384)throw new V("PLUGIN_STATE_INVALID","Plugin package state permissions must be 0600");let Q=JSON.parse(await O.readFile("utf8")),q=D1(Q);if(!q.success)throw new V("PLUGIN_STATE_INVALID","Plugin package state schema is invalid");let Z=q.data;for(let[J,K]of Object.entries(Z.installed)){let Y=S.join(this.stateRoot,"packages",J);if(J!==K.name||!S.isAbsolute(K.installPath)||!O0(Y,K.installPath)||S.basename(K.installPath)!==K.contentDigest||K.source.type==="local"&&!S.isAbsolute(K.source.path))throw new V("PLUGIN_STATE_INVALID","Plugin package state contains an invalid installation");if(K.source.type==="git"){let G=this.normalizeGitSource(K.source.url,K.source.ref);if(G.url!==K.source.url||G.ref!==K.source.ref)throw new V("PLUGIN_STATE_INVALID","Plugin package state contains an invalid Git source")}}for(let[J,K]of Object.entries(Z.marketplaces)){let Y=S.join(this.stateRoot,"marketplaces",J);if(J!==K.name||!S.isAbsolute(K.installPath)||!O0(Y,K.installPath)||S.basename(K.installPath)!==K.contentDigest||K.source.type==="local"&&!S.isAbsolute(K.source.path))throw new V("PLUGIN_STATE_INVALID","Plugin package state contains an invalid marketplace");if(K.source.type==="git"){let G=this.normalizeGitSource(K.source.url,K.source.ref);if(G.url!==K.source.url||G.ref!==K.source.ref)throw new V("PLUGIN_STATE_INVALID","Plugin package state contains an invalid Marketplace Git source")}}for(let J of Object.values(Z.installed))if(J.source.type==="marketplace"&&!Z.marketplaces[J.source.marketplace])throw new V("PLUGIN_STATE_INVALID","Plugin package state references a missing marketplace");return Z}catch($){if($ instanceof SyntaxError)throw new V("PLUGIN_STATE_INVALID","Plugin package state contains invalid JSON");throw $}finally{await O.close()}}async writeState(O){await this.ensureStoreDirectory(),await cO(this.statePath,`${JSON.stringify(O,null,2)}
|
|
626
|
-
`,{mode:384,encoding:"utf8"}),await F.chmod(this.statePath,384)}async ensureStoreDirectory(){try{let O=await F.lstat(this.stateRoot);if(!O.isDirectory()||O.isSymbolicLink())throw new V("PLUGIN_STORE_INVALID","Plugin package store must be a regular directory");if(process.getuid&&O.uid!==process.getuid())throw new V("PLUGIN_STORE_INVALID","Plugin package store must be owned by the current user");if((O.mode&511)!==448)throw new V("PLUGIN_STORE_INVALID","Plugin package store permissions must be 0700")}catch(O){if(!s(O))throw O;await F.mkdir(this.stateRoot,{recursive:!0,mode:448}),await F.chmod(this.stateRoot,448)}}}var c0=null;function s0(O,$){if(!c0)c0=new d1(O,$);return c0}import*as b from"node:fs/promises";import{homedir as eO}from"node:os";import*as M from"node:path";import m1 from"gray-matter";function F0(O,$){return`${O}:${$}`}function p1(O,$){return`${O}__${$}`}class H0{async loadPlugin(O,$,Q={}){let q=await a(O);if(!q)throw Error(`Not a valid plugin directory: ${O}`);let{manifest:Z,source:J}=q,K=Z.name;j.debug(`Loading plugin "${K}" from ${O}`);let Y=Q.skipCommands?[]:await this.loadCommands(O,K),G=Q.skipAgents?[]:await this.loadAgents(O,K),W=Q.skipSkills?[]:await this.loadSkills(O,K),X=Q.skipHooks?void 0:await this.loadHooks(O),H=Q.skipMcp?void 0:await this.loadMcpConfig(O,K),B=Q.skipLsp?void 0:await this.loadLspConfig(O,K);return j.debug(`Plugin "${K}" loaded: ${Y.length} commands, ${G.length} agents, ${W.length} skills`),{manifest:Z,basePath:O,source:$,manifestSource:J,commands:Y,agents:G,skills:W,hooks:X,mcpServers:H,lspServers:B,status:"active",loadedAt:new Date}}async loadCommands(O,$){let Q=M.join(O,"commands"),q=[];if(!await this.dirExists(Q))return q;let Z=await this.scanMarkdownFiles(Q);for(let J of Z)try{let K=await this.parseCommandFile(J,Q,$);if(K)q.push(K)}catch(K){j.warn(`Failed to load command from ${J}: ${K instanceof Error?K.message:String(K)}`)}return q}async parseCommandFile(O,$,Q){let q=await b.readFile(O,"utf-8"),{data:Z,content:J}=m1(q),Y=M.relative($,O).split(M.sep),G=Y.pop();if(!G)return null;let W=G.replace(/\.md$/i,""),X=W;if(Y.length>0)X=[...Y,W].join("/");let H=F0(Q,X),B=this.normalizeCommandConfig(Z);return{originalName:X,namespacedName:H,pluginName:Q,config:B,content:J.trim(),path:O}}normalizeCommandConfig(O){return{description:this.asString(O.description),allowedTools:this.parseStringArray(O["allowed-tools"]),argumentHint:this.asString(O["argument-hint"]),model:this.asString(O.model),disableModelInvocation:O["disable-model-invocation"]===!0}}async loadAgents(O,$){let Q=M.join(O,"agents"),q=[];if(!await this.dirExists(Q))return q;let Z=await this.scanMarkdownFiles(Q);for(let J of Z)try{let K=await this.parseAgentFile(J,Q,$);if(K)q.push(K)}catch(K){j.warn(`Failed to load agent from ${J}: ${K instanceof Error?K.message:String(K)}`)}return q}async parseAgentFile(O,$,Q){let q=await b.readFile(O,"utf-8"),{data:Z,content:J}=m1(q),K=M.relative($,O),Y=M.basename(K,".md"),G=Z.name||Y,W=F0(Q,G),X={name:W,description:this.asString(Z.description)||"",tools:this.parseStringArray(Z.tools),color:this.asString(Z.color),model:this.asString(Z.model),permissionMode:this.asString(Z.permissionMode),skills:this.parseStringArray(Z.skills),systemPrompt:J.trim(),source:`plugin:${Q}`};return{originalName:G,namespacedName:W,pluginName:Q,config:X,path:O}}async loadSkills(O,$){let Q=M.join(O,"skills"),q=[];if(!await this.dirExists(Q))return q;let Z=await b.readdir(Q,{withFileTypes:!0});for(let J of Z){if(!J.isDirectory())continue;let K=M.join(Q,J.name),Y=M.join(K,"SKILL.md");try{let G=await Y0(Y,"project");if(G.success&&G.content){let W=G.content.metadata.name,X=F0($,W);q.push({originalName:W,namespacedName:X,pluginName:$,metadata:{...G.content.metadata,name:X},path:K})}}catch(G){j.warn(`Failed to load skill from ${Y}: ${G instanceof Error?G.message:String(G)}`)}}return q}async loadHooks(O){let $=M.join(O,"hooks","hooks.json");try{let Q=await b.readFile($,"utf-8"),q=JSON.parse(Q);return q.hooks||q}catch{return}}async loadMcpConfig(O,$){let Q=M.join(O,".mcp.json");try{let q=await b.readFile(Q,"utf-8"),Z=JSON.parse(q),J=C1(Z);if(!J.success){j.warn(`Invalid .mcp.json in ${O}: ${J.error}`);return}let K="mcpServers"in Z?Z.mcpServers:Z,Y={};for(let[G,W]of Object.entries(K)){let X=p1($,G);Y[X]=W}return Y}catch{return}}async loadLspConfig(O,$){let Q=M.join(O,".lsp.json");try{let q=JSON.parse(await b.readFile(Q,"utf8")),Z=q&&typeof q==="object"&&!Array.isArray(q)&&"lspServers"in q?q.lspServers:q,J=g1(Z),K={};for(let[Y,G]of Object.entries(J)){let W=(X)=>X.replaceAll("${CLAUDE_PLUGIN_ROOT}",O).replaceAll("${BLADE_PLUGIN_ROOT}",O);K[`plugin:${$}:${Y}`]={...G,command:W(G.command),args:G.args?.map(W),env:G.env?Object.fromEntries(Object.entries(G.env).map(([X,H])=>[X,W(H)])):void 0}}return K}catch(q){if(q.code!=="ENOENT")j.warn(`Invalid .lsp.json in ${O}: ${q instanceof Error?q.message:String(q)}`);return}}async scanMarkdownFiles(O){let $=[],Q=async(q)=>{let Z=await b.readdir(q,{withFileTypes:!0});for(let J of Z){let K=M.join(q,J.name);if(J.isDirectory())await Q(K);else if(J.name.endsWith(".md"))$.push(K)}};return await Q(O),$}async dirExists(O){try{return(await b.stat(O)).isDirectory()}catch{return!1}}asString(O){if(typeof O==="string"&&O.trim())return O.trim();return}parseStringArray(O){if(!O)return;if(Array.isArray(O))return O.map(($)=>String($).trim()).filter(Boolean);if(typeof O==="string")return O.split(",").map(($)=>$.trim()).filter(Boolean);return}static getPluginDirs(O){let $=eO();return[{path:M.join($,".claude","plugins"),source:"user",type:"claude"},{path:M.join($,".blade","plugins"),source:"user",type:"blade"},{path:M.join(O,".claude","plugins"),source:"project",type:"claude"},{path:M.join(O,".blade","plugins"),source:"project",type:"blade"}]}async discoverPluginsInDir(O,$){let Q=[],q=[];try{await b.access(O)}catch{return{plugins:Q,errors:q}}let Z=await b.readdir(O,{withFileTypes:!0});for(let J of Z){if(!J.isDirectory())continue;let K=M.join(O,J.name);try{let Y=await this.loadPlugin(K,$);Q.push(Y)}catch(Y){q.push({path:K,error:Y instanceof Error?Y.message:String(Y)})}}return{plugins:Q,errors:q}}}class f{static instances=new Map;plugins=new Map;loader=new H0;initialized=!1;workspaceRoot="";cliPluginDirs=[];enabledSettings={};sourcePolicy={...V0.pluginSourcePolicy};marketplaces={};constructor(O){this.workspaceRoot=O}static getInstance(O=x()){let $=O$.resolve(O),Q=f.instances.get($);if(!Q)Q=new f($),f.instances.set($,Q);return Q}static resetInstance(){f.instances.clear()}static getInitializedInstances(){return Array.from(f.instances.values()).filter((O)=>O.isInitialized())}async initialize(O,$=[]){this.workspaceRoot=O,this.cliPluginDirs=$,this.enabledSettings=await U0.getInstance().loadWorkspacePluginSettings(O),await this.refreshPolicyContext();let Q=[],q=[];for(let G of $)try{let W=await this.loader.loadPlugin(G,"cli");this.plugins.set(W.manifest.name,W),Q.push(W),j.info(`Loaded CLI plugin: ${W.manifest.name} from ${G}`)}catch(W){q.push({path:G,error:W instanceof Error?W.message:String(W)}),j.warn(`Failed to load CLI plugin from ${G}: ${W}`)}let Z=(await T.getInstance().getStatus(O)).state==="trusted",J=H0.getPluginDirs(O).filter((G)=>G.source!=="project"||Z),K=!1,Y=async()=>{if(K)return;K=!0;let G=s0(),W=await G.listInstallationRecords();for(let X of W)try{await G.verifyInstallation(X);let H=await this.loader.loadPlugin(X.installPath,"user");if(H.manifest.name!==X.name)throw Error(`Managed plugin identity mismatch: ${X.name}`);H.installation=Object.freeze({...X});let B=this.plugins.get(H.manifest.name);if(!B||B.source==="user")this.plugins.set(H.manifest.name,H),Q.push(H)}catch(H){q.push({path:X.installPath,code:"IO_ERROR",error:H instanceof Error?H.message:String(H)})}};for(let{path:G,source:W}of J){if(W==="project")await Y();let X=await this.loader.discoverPluginsInDir(G,W);for(let H of X.plugins){let B=this.plugins.get(H.manifest.name);if(!B||B.source!=="cli")this.plugins.set(H.manifest.name,H),Q.push(H)}q.push(...X.errors)}await Y(),this.applyEnabledSettings();for(let G of this.plugins.values()){if(G.status!=="error")continue;q.push({path:G.basePath,code:G.compatibilityIssues?.some((W)=>W.code==="source-policy")?"SOURCE_POLICY_BLOCKED":G.compatibilityIssues?.some((W)=>W.code==="blade-version")?"VERSION_INCOMPATIBLE":"DEPENDENCY_MISSING",error:G.error??"Plugin compatibility check failed"})}return this.initialized=!0,j.info(`Plugin system initialized: ${this.plugins.size} plugins loaded`+(q.length>0?`, ${q.length} errors`:"")),{plugins:Array.from(this.plugins.values()),errors:q}}isInitialized(){return this.initialized}getWorkspaceRoot(){return this.workspaceRoot}getSourcePolicy(){return{...this.sourcePolicy,allowedGitHosts:[...this.sourcePolicy.allowedGitHosts],allowedMarketplaces:[...this.sourcePolicy.allowedMarketplaces],allowedLocalRoots:[...this.sourcePolicy.allowedLocalRoots]}}getAll(){return Array.from(this.plugins.values())}getActive(){return Array.from(this.plugins.values()).filter((O)=>O.status==="active")}get(O){return this.plugins.get(O)}has(O){return this.plugins.has(O)}async reapplyEnabledSettings(){this.enabledSettings=await U0.getInstance().loadWorkspacePluginSettings(this.workspaceRoot),await this.refreshPolicyContext(),this.applyEnabledSettings()}applyEnabledSettings(){for(let O of this.plugins.values()){if(O.compatibilityIssues=[],O.error=void 0,O.status=O.source==="cli"||this.enabledSettings[O.manifest.name]!==!1?"active":"inactive",O.status!=="active")continue;try{r(this.sourcePolicy,O.installation?.source??{type:"local",path:O.basePath},this.marketplaces,`Plugin "${O.manifest.name}"`)}catch($){if(!($ instanceof E))throw $;O.status="error",O.error=$.message,O.compatibilityIssues=[{code:"source-policy",message:$.message}]}}x1(Array.from(this.plugins.values()))}async refreshPolicyContext(){this.sourcePolicy=await U0.getInstance().loadWorkspacePluginSourcePolicy(this.workspaceRoot),this.marketplaces=Object.fromEntries((await s0().listMarketplaces()).map((O)=>[O.name,O]))}getBySource(){let O={cli:[],project:[],user:[]};for(let $ of this.plugins.values())O[$.source].push($);return O}getAllCommands(){let O=[];for(let $ of this.plugins.values())if($.status==="active")O.push(...$.commands);return O}getAllSkills(){let O=[];for(let $ of this.plugins.values())if($.status==="active")O.push(...$.skills);return O}getAllAgents(){let O=[];for(let $ of this.plugins.values())if($.status==="active")O.push(...$.agents);return O}findCommand(O){for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.commands)if(q.namespacedName===O)return q}let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.commands)if(q.originalName===O)$.push(q)}if($.length===1)return $[0];return}findSkill(O){for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.skills)if(q.namespacedName===O)return q}let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.skills)if(q.originalName===O)$.push(q)}if($.length===1)return $[0];return}findAgent(O){for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.agents)if(q.namespacedName===O)return q}let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.agents)if(q.originalName===O)$.push(q)}if($.length===1)return $[0];return}hasCommandConflict(O){let $=0;for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.commands)if(q.originalName===O){if($++,$>1)return!0}}return!1}getCommandProviders(O){let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.commands)if(q.originalName===O){$.push(Q.manifest.name);break}}return $}disable(O){let $=this.plugins.get(O);if($&&$.status==="active")return $.status="inactive",j.info(`Plugin "${O}" disabled`),!0;return!1}enable(O){let $=this.plugins.get(O);if($&&$.status==="inactive")return $.status="active",j.info(`Plugin "${O}" enabled`),!0;return!1}async refresh(){return j.info("Refreshing plugin list..."),this.plugins.clear(),this.initialized=!1,this.initialize(this.workspaceRoot,this.cliPluginDirs)}getStats(){let O=0,$=0,Q=0,q=0,Z=0;for(let J of this.plugins.values())if(J.status==="active")O++,Q+=J.commands.length,q+=J.skills.length,Z+=J.agents.length;else $++;return{total:this.plugins.size,active:O,inactive:$,commands:Q,skills:q,agents:Z}}formatPluginList(){if(this.getAll().length===0)return"没有已加载的插件。";let $=[],Q=this.getBySource();if(Q.cli.length>0){$.push("## CLI 指定的插件");for(let q of Q.cli){let Z=q.status==="inactive"?" (禁用)":"";$.push(`- **${q.manifest.name}** v${q.manifest.version}${Z}`),$.push(` ${q.manifest.description}`)}$.push("")}if(Q.project.length>0){$.push("## 项目级插件");for(let q of Q.project){let Z=q.status==="inactive"?" (禁用)":"";$.push(`- **${q.manifest.name}** v${q.manifest.version}${Z}`),$.push(` ${q.manifest.description}`)}$.push("")}if(Q.user.length>0){$.push("## 用户级插件");for(let q of Q.user){let Z=q.status==="inactive"?" (禁用)":"";$.push(`- **${q.manifest.name}** v${q.manifest.version}${Z}`),$.push(` ${q.manifest.description}`)}}return $.join(`
|
|
627
|
-
`)}}function S0(O=x()){return f.getInstance(O)}var d=new Map,u1=Object.values(B0);function n0(O){let $={...O};for(let Q of u1)$[Q]=(O[Q]??[]).map((q)=>({...q,matcher:q.matcher?{...q.matcher}:void 0,hooks:q.hooks.map((Z)=>({...Z}))}));return $}class o0{commandRegistry;hookManager;skillRegistry;subagentRegistry;workspaceRoot;constructor(O=x()){this.workspaceRoot=$$.resolve(O),this.commandRegistry=w.getInstance(this.workspaceRoot),this.hookManager=j0.getInstance(),this.skillRegistry=P({cwd:this.workspaceRoot}),this.subagentRegistry=i(this.workspaceRoot)}clearAllPluginResources(){this.commandRegistry.clearPluginCommands(),this.skillRegistry.clearPluginSkills(),this.subagentRegistry.clearPluginAgents();let O=d.get(this.workspaceRoot);if(O)this.hookManager.loadConfig(O,this.workspaceRoot),d.delete(this.workspaceRoot);j.debug("Cleared all plugin resources from subsystems")}async integrateAll(){let $=S0(this.workspaceRoot).getActive(),Q=[],q=[],Z=0,J=0,K=0,Y=0,G=0;for(let W of $){let X=this.integratePlugin(W);Q.push(X),Z+=X.commandsRegistered,J+=X.skillsRegistered,K+=X.agentsRegistered,Y+=X.mcpServersRegistered,G+=X.lspServersRegistered,q.push(...X.errors)}if(this.integrateHooks($),Z+J+K>0)j.info(`Plugin integration complete: ${Z} commands, ${J} skills, ${K} agents, ${Y} MCP servers, ${G} LSP servers`);return{plugins:Q,totalCommands:Z,totalSkills:J,totalAgents:K,totalMcpServers:Y,errors:q}}integratePlugin(O){let $={pluginName:O.manifest.name,commandsRegistered:0,skillsRegistered:0,agentsRegistered:0,hooksRegistered:!1,mcpServersRegistered:0,lspServersRegistered:0,errors:[]};try{$.commandsRegistered=this.integrateCommands(O)}catch(Q){$.errors.push(`Failed to integrate commands: ${Q instanceof Error?Q.message:String(Q)}`)}try{$.skillsRegistered=this.integrateSkills(O)}catch(Q){$.errors.push(`Failed to integrate skills: ${Q instanceof Error?Q.message:String(Q)}`)}try{$.agentsRegistered=this.integrateAgents(O)}catch(Q){$.errors.push(`Failed to integrate agents: ${Q instanceof Error?Q.message:String(Q)}`)}$.hooksRegistered=O.hooks!==void 0;try{$.mcpServersRegistered=this.integrateMcp(O)}catch(Q){$.errors.push(`Failed to integrate MCP servers: ${Q instanceof Error?Q.message:String(Q)}`)}return $.lspServersRegistered=Object.keys(O.lspServers??{}).length,j.debug(`Integrated plugin "${O.manifest.name}": ${$.commandsRegistered} commands, ${$.skillsRegistered} skills, ${$.agentsRegistered} agents`),$}integrateCommands(O){let $=0;for(let Q of O.commands)this.commandRegistry.registerPluginCommand(Q),$++;return $}integrateSkills(O){let $=0;for(let Q of O.skills)this.skillRegistry.registerPluginSkill(Q),$++;return $}integrateAgents(O){let $=0;for(let Q of O.agents)this.subagentRegistry.register({...Q.config,name:Q.namespacedName}),$++;return $}integrateHooks(O){let $=O.filter((J)=>J.hooks);if($.length===0){let J=d.get(this.workspaceRoot);if(J)this.hookManager.loadConfig(J,this.workspaceRoot),d.delete(this.workspaceRoot);return}let Q=n0(this.hookManager.getConfig(this.workspaceRoot));if(!d.has(this.workspaceRoot))d.set(this.workspaceRoot,n0(Q));let q=d.get(this.workspaceRoot)??Q,Z=n0(q);Z.enabled=!0;for(let J of u1){let K=q.enabled?[...q[J]??[]]:[],Y=[];for(let G of $)for(let W of G.hooks?.[J]??[])Y.push({...W,name:W.name?`plugin:${G.manifest.name}:${W.name}`:`plugin:${G.manifest.name}:${J}`,hooks:W.hooks.map((X)=>({...X,source:{kind:"plugin",pluginName:G.manifest.name,pluginSource:G.source,pluginRoot:G.basePath}}))});Z[J]=[...K,...Y]}this.hookManager.loadConfig(Z,this.workspaceRoot)}integrateMcp(O){return Object.keys(O.mcpServers??{}).length}}async function i0(O=x()){return new o0(O).integrateAll()}function I0(O=x()){new o0(O).clearAllPluginResources()}import{createHash as Q$}from"node:crypto";import*as D from"node:fs/promises";import q$ from"node:os";import C from"node:path";import Z$ from"gray-matter";var M0=32,c1=24576,l1=16384,s1=131072,z0=4,n1=80,o1=256,t1=/^[a-z0-9][a-z0-9._-]{0,63}$/,J$=h("Agent");function t0(O){for(let $ of O){let Q=$.codePointAt(0)??0;if(Q<=8||Q===11||Q===12||Q>=14&&Q<=31||Q===127||Q>=8203&&Q<=8207||Q>=8234&&Q<=8238||Q>=8288&&Q<=8303||Q===65279)return!0}return!1}function K$(O,$,Q){let q=typeof O==="string"?O.trim():"",Z=$.split(/\r?\n/).map((K)=>K.trim()).find(Boolean)?.replace(/^#+\s*/,"")??"",J=q||Z||Q;if(J.length>o1)throw Error(`Description exceeds ${o1} characters`);if(t0(J))throw Error("Description contains hidden control characters");return J}function G$(O,$){let Q=typeof O==="string"&&O.trim()?O.trim():$;if(Q.length>n1)throw Error(`Name exceeds ${n1} characters`);if(t0(Q))throw Error("Name contains hidden control characters");return Q}function L$(O){let Q=O.replace(/\.md$/i,"").split(C.sep);if(Q.length===0||Q.length>z0||Q.some((q)=>!t1.test(q)))throw Error(`Style path must use 1-${z0} lowercase safe segments`);return Q.join(":")}async function Y$(O){let $;try{$=await D.lstat(O)}catch(J){if(J.code==="ENOENT")return[];throw J}if($.isSymbolicLink())throw Error(`Output style root cannot be a symlink: ${O}`);if(!$.isDirectory())return[];let Q=await D.realpath(O),q=[],Z=async(J,K)=>{if(K>z0)throw Error(`Output style directory exceeds depth ${z0}`);let Y=await D.readdir(J,{withFileTypes:!0});Y.sort((G,W)=>G.name.localeCompare(W.name));for(let G of Y){let W=C.join(J,G.name);if(G.isSymbolicLink())throw Error(`Output style path cannot be a symlink: ${W}`);if(G.isDirectory()){await Z(W,K+1);continue}if(!G.isFile()||!G.name.toLowerCase().endsWith(".md"))continue;let X=await D.realpath(W),H=C.relative(Q,X);if(!H||H.startsWith("..")||C.isAbsolute(H))throw Error(`Output style path escapes its source root: ${W}`);if(q.push(X),q.length>M0)throw Error(`Output style catalog exceeds ${M0} files`)}};return await Z(Q,1),q}async function W$(O,$,Q){let q=await D.lstat(Q);if(!q.isFile()||q.isSymbolicLink())throw Error("Output style must be a regular file");if(q.size>c1)throw Error(`Output style exceeds ${c1} bytes`);let Z=await D.readFile(Q,"utf8"),J=Z$(Z),K=J.content.trim(),Y=Buffer.byteLength(K);if(Y===0)throw Error("Output style prompt must not be empty");if(Y>l1)throw Error(`Output style prompt exceeds ${l1} bytes`);if(t0(K))throw Error("Output style prompt contains hidden control characters");let G=C.relative($,Q),W=L$(G);return{id:`${O.idPrefix}:${W}`,name:G$(J.data.name,W),description:K$(J.data.description,K,`Custom ${W} communication style`),source:O.source,contentSha256:Q$("sha256").update(K,"utf8").digest("hex"),prompt:K}}async function X$(O,$){let Q=await Y$(O.directory);if(Q.length===0)return;let q=await D.realpath(O.directory);for(let Z of Q)try{let J=await W$(O,q,Z);$.set(J.id,J)}catch(J){J$.warn(`[CommunicationStyles] Ignoring ${O.source} style: ${J instanceof Error?J.message:String(J)}`)}}function V$(O,$,Q,q){let Z=[{directory:C.join(q,".claude","output-styles"),source:"user",idPrefix:"user"},{directory:C.join(q,".blade","output-styles"),source:"user",idPrefix:"user"}];if($)Z.push({directory:C.join(O,".claude","output-styles"),source:"project",idPrefix:"project"},{directory:C.join(O,".blade","output-styles"),source:"project",idPrefix:"project"});for(let J of Q){if(J.status!=="active"||!t1.test(J.manifest.name))continue;Z.push({directory:C.join(J.basePath,"output-styles"),source:"plugin",idPrefix:`plugin:${J.manifest.name}`})}return Z}async function a0(O,$){let Q=new Map;for(let Z of V$(C.resolve(O),$.projectTrusted,$.plugins??[],$.homeDirectory??q$.homedir()))await X$(Z,Q);let q=0;for(let Z of Q.values())q+=Buffer.byteLength(Z.prompt??"");if(Q.size>M0)throw Error(`Output style catalog exceeds ${M0} styles`);if(q>s1)throw Error(`Output style catalog exceeds ${s1} bytes`);return new i1([...Q.values()])}import{createHash as F$}from"node:crypto";import{existsSync as A0,realpathSync as H$}from"node:fs";import*as m from"node:fs/promises";import I from"node:path";import U$ from"fast-glob";import S$ from"gray-matter";import qO from"picomatch";var a1=128,r1=32768,e1=262144,B$=49152,j$=32768,I$=24,OO=16,M$=256,z$=h("Agent"),A$=["**/CLAUDE.md","**/.claude/CLAUDE.md","**/CLAUDE.local.md","**/AGENTS.md","**/AGENTS.override.md","**/BLADE.md","**/.claude/rules/**/*.md","**/.blade/rules/**/*.md"],x$=["**/.git/**","**/node_modules/**","**/.venv/**","**/dist/**","**/build/**","**/coverage/**","**/target/**"];function x0(O){return F$("sha256").update(O,"utf8").digest("hex")}function ZO(O){for(let $ of O){let Q=$.codePointAt(0)??0;if(Q<=8||Q===11||Q===12||Q>=14&&Q<=31||Q===127||Q>=8203&&Q<=8207||Q>=8234&&Q<=8238||Q>=8288&&Q<=8303||Q===65279)return!0}return!1}function $1(O){return O.split(I.sep).join("/")}function r0(O){let $=I.resolve(O),Q=[];while(!0)try{return I.join(H$($),...Q.reverse())}catch(q){if(q.code!=="ENOENT")return I.resolve(O);let Z=I.dirname($);if(Z===$)return I.resolve(O);Q.push(I.basename($)),$=Z}}function b$(O){let $=I.resolve(O),Q;while(!0){if(A0(I.join($,".git")))return $;if(!Q&&(A0(I.join($,".blade"))||A0(I.join($,".claude"))||A0(I.join($,"package.json"))))Q=$;let q=I.dirname($);if(q===$)return Q??I.resolve(O);$=q}}function Q1(O){return Boolean(O)&&O!==".."&&!O.startsWith("../")}function e0(O,$){return O===""||$===O||$.startsWith(`${O}/`)}function $O(O){return O.replaceAll("&","&").replaceAll('"',""").replaceAll("<","<").replaceAll(">",">")}function _$(O){let $=typeof O==="string"?O.split(/[,\n]/u):Array.isArray(O)?O:[],Q=[...new Set($.filter((q)=>typeof q==="string").map((q)=>q.trim().replaceAll("\\","/")).filter(Boolean))];if(Q.length===0||Q.every((q)=>q==="**"))return;if(Q.length>OO)throw Error(`paths exceeds ${OO} patterns`);for(let q of Q){if(q.length>M$||I.posix.isAbsolute(q)||q.split("/").includes("..")||ZO(q))throw Error(`Unsafe project rule path pattern: ${q.slice(0,80)}`);qO(q,{dot:!0})}return Q}function T$(O){if(O.endsWith("/CLAUDE.local.md")||O==="CLAUDE.local.md")return{source:"local",kind:"instruction",scopeDirectory:I.posix.dirname(O).replace(/^\.$/u,""),priority:90};for(let q of[".claude/rules/",".blade/rules/"]){let Z=O.indexOf(q);if(Z>=0)return{source:"project",kind:"rule",scopeDirectory:O.slice(0,Z).replace(/\/$/u,""),priority:q.startsWith(".blade")?80:70}}if(O.endsWith("/.claude/CLAUDE.md")||O===".claude/CLAUDE.md")return{source:"project",kind:"instruction",scopeDirectory:O.slice(0,-17).replace(/\/$/u,""),priority:30};let $=I.posix.basename(O),Q=$==="CLAUDE.md"?20:$==="AGENTS.md"?40:$==="AGENTS.override.md"?50:60;return{source:"project",kind:"instruction",scopeDirectory:I.posix.dirname(O).replace(/^\.$/u,""),priority:Q}}function O1(O,$){let Q=O.scopeDirectory?O.scopeDirectory.split("/").length:0,q=$.scopeDirectory?$.scopeDirectory.split("/").length:0;return Q-q||O.priority-$.priority||O.relativePath.localeCompare($.relativePath)}function w$(O){return{id:O.id,relativePath:O.relativePath,source:O.source,contentSha256:O.contentSha256}}function $0(O,$,Q){let q=O.map((K)=>{let Y=K.conditional?' conditional="true"':"";return`<instruction-file path="${$O(K.relativePath)}" source="${K.source}" sha256="${K.contentSha256}"${Y}>
|
|
624
|
+
(${J} of ${Q} commands shown due to character budget)`;return{text:q,includedCount:J,totalCount:Q}}snapshot(){let O=new w;return O.workspaceRoot=this.workspaceRoot,O.commands=new Map(Array.from(this.commands,([$,Q])=>[$,{...Q,config:{...Q.config,...Q.config.allowedTools?{allowedTools:[...Q.config.allowedTools]}:{}}}])),O.pluginCommands=new Map(Array.from(this.pluginCommands,([$,Q])=>[$,{...Q,config:{...Q.config,...Q.config.allowedTools?{allowedTools:[...Q.config.allowedTools]}:{}}}])),O.initialized=this.initialized,O.lastDiscoveryResult=this.lastDiscoveryResult?{commands:Array.from(O.commands.values()),scannedDirs:[...this.lastDiscoveryResult.scannedDirs],errors:this.lastDiscoveryResult.errors.map(($)=>({...$}))}:null,O}registerPluginCommand(O){this.pluginCommands.set(O.namespacedName,O)}findPluginCommand(O){let $=this.pluginCommands.get(O);if($)return $;let Q=[];for(let q of this.pluginCommands.values())if(q.originalName===O)Q.push(q);if(Q.length===1)return Q[0];return}hasPluginCommand(O){return this.findPluginCommand(O)!==void 0}getAllPluginCommands(){return Array.from(this.pluginCommands.values())}async executePluginCommand(O,$){let Q=this.findPluginCommand(O);if(!Q)return null;let q={name:Q.namespacedName,config:Q.config,content:Q.content,path:Q.path,source:"project",sourceDir:"blade"};return this.executor.execute(q,$)}clearPluginCommands(){this.pluginCommands.clear()}getPluginCommandCount(){return this.pluginCommands.size}hasPluginCommandConflict(O){let $=0;for(let Q of this.pluginCommands.values())if(Q.originalName===O){if($++,$>1)return!0}return!1}getPluginCommandProviders(O){let $=[];for(let Q of this.pluginCommands.values())if(Q.originalName===O)$.push(Q.pluginName);return $}}import $$ from"node:path";import{satisfies as k0,validRange as z1}from"semver";var _O=/^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$|^[a-z0-9]$/;function d0(O){if(O.bladeVersion!==void 0&&(!O.bladeVersion.trim()||!z1(O.bladeVersion)))throw Error(`Invalid Blade version range in plugin "${O.name}": ${O.bladeVersion}`);for(let[$,Q]of Object.entries(O.dependencies??{})){if(!_O.test($))throw Error(`Invalid dependency name in plugin "${O.name}": ${$}`);if($===O.name)throw Error(`Plugin "${O.name}" cannot depend on itself`);if(!Q.trim()||!z1(Q))throw Error(`Invalid dependency range for ${$} in plugin "${O.name}": ${Q}`)}}function x1(O,$=P0()){if(d0(O),O.bladeVersion&&!k0($,O.bladeVersion))throw Error(`Plugin "${O.name}" requires Blade ${O.bladeVersion}, current ${$}`)}function A1(O,$){O.status="error",O.compatibilityIssues=[...O.compatibilityIssues??[],$],O.error=O.compatibilityIssues.map((Q)=>Q.message).join("; ")}function b1(O,$=P0()){let Q=[],q=new Map(O.map((J)=>[J.manifest.name,J]));for(let J of O){if(J.status!=="active")continue;if(J.manifest.bladeVersion&&!k0($,J.manifest.bladeVersion)){let K={code:"blade-version",expected:J.manifest.bladeVersion,actual:$,message:`Requires Blade ${J.manifest.bladeVersion}, current ${$}`};A1(J,K),Q.push(K)}}let Z=!0;while(Z){Z=!1;for(let J of O){if(J.status!=="active")continue;for(let[K,L]of Object.entries(J.manifest.dependencies??{})){let G=q.get(K),W;if(!G)W={code:"dependency-missing",dependency:K,expected:L,message:`Missing dependency ${K}@${L}`};else if(!k0(G.manifest.version,L))W={code:"dependency-version",dependency:K,expected:L,actual:G.manifest.version,message:`Dependency ${K} requires ${L}, found ${G.manifest.version}`};else if(G.status!=="active")W={code:"dependency-inactive",dependency:K,expected:L,actual:G.status,message:`Dependency ${K} is ${G.status}`};if(!W)continue;A1(J,W),Q.push(W),Z=!0;break}}}return Q}import{execFile as mO}from"node:child_process";import{createHash as gO,randomUUID as f1}from"node:crypto";import{constants as e,promises as V}from"node:fs";import{homedir as g0}from"node:os";import S from"node:path";import{promisify as uO}from"node:util";import{Mutex as cO}from"async-mutex";import{satisfies as u0}from"semver";import lO from"write-file-atomic";import*as F0 from"node:fs/promises";import*as v1 from"node:path";var c=Y.String({minLength:2,maxLength:64,pattern:"^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]{1,2}$"}),p0=Y.String({pattern:"^\\d+\\.\\d+\\.\\d+(-[\\w.]+)?(\\+[\\w.]+)?$"}),TO=Y.Object({name:Y.String({minLength:1}),email:Y.Optional(Y.String({format:"email"})),url:Y.Optional(Y.String({format:"uri"}))}),w1=Y.Object({name:c,description:Y.String({minLength:1,maxLength:500}),version:p0,author:Y.Optional(TO),license:Y.Optional(Y.String()),repository:Y.Optional(Y.String({format:"uri"})),homepage:Y.Optional(Y.String({format:"uri"})),keywords:Y.Optional(Y.Array(Y.String())),dependencies:Y.Optional(Y.Record(Y.String(),Y.String())),bladeVersion:Y.Optional(Y.String())}),wO=Y.Union([Y.Object({type:Y.Literal("git"),url:Y.String({minLength:1}),ref:Y.Optional(Y.String({minLength:1}))},{additionalProperties:!1}),Y.Object({type:Y.Literal("local"),path:Y.String({minLength:1})},{additionalProperties:!1}),Y.Object({type:Y.Literal("marketplace"),marketplace:c},{additionalProperties:!1})]),CO=Y.Object({name:c,source:wO,installPath:Y.String({minLength:1}),version:p0,revision:Y.String({minLength:1,maxLength:128}),contentDigest:Y.String({pattern:"^[a-f0-9]{64}$"}),installedAt:Y.String({format:"date-time"}),updatedAt:Y.String({format:"date-time"})},{additionalProperties:!1}),DO=Y.Union([Y.Object({type:Y.Literal("git"),url:Y.String({minLength:1}),ref:Y.Optional(Y.String({minLength:1}))},{additionalProperties:!1}),Y.Object({type:Y.Literal("local"),path:Y.String({minLength:1})},{additionalProperties:!1})]),NO=Y.Object({name:c,source:DO,installPath:Y.String({minLength:1}),revision:Y.String({minLength:1,maxLength:128}),contentDigest:Y.String({pattern:"^[a-f0-9]{64}$"}),addedAt:Y.String({format:"date-time"}),updatedAt:Y.String({format:"date-time"})},{additionalProperties:!1}),RO=Y.Object({version:Y.Literal(1),installed:Y.Record(Y.String(),CO),marketplaces:Y.Record(Y.String(),NO)},{additionalProperties:!1}),C1=Y.Object({name:Y.String({minLength:1}),email:Y.Optional(Y.String({format:"email"})),url:Y.Optional(Y.String({format:"uri"}))}),vO=Y.Object({name:c,description:Y.Optional(Y.String({maxLength:500})),version:Y.Optional(p0),author:Y.Optional(Y.Union([Y.String({minLength:1}),C1])),source:Y.Union([Y.String({minLength:1}),Y.Object({source:Y.Literal("url"),url:Y.String({minLength:1}),ref:Y.Optional(Y.String({minLength:1})),sha:Y.Optional(Y.String({pattern:"^[a-fA-F0-9]{40}$"}))})]),category:Y.Optional(Y.String()),homepage:Y.Optional(Y.String({format:"uri"})),tags:Y.Optional(Y.Array(Y.String()))}),EO=Y.Object({name:c,description:Y.Optional(Y.String({maxLength:500})),owner:Y.Optional(C1),metadata:Y.Optional(Y.Object({description:Y.Optional(Y.String({maxLength:500})),version:Y.Optional(Y.String())})),plugins:Y.Array(vO,{maxItems:5000})}),_1=Y.Object({type:T1(["stdio","sse","http"]),command:Y.Optional(Y.String()),args:Y.Optional(Y.Array(Y.String())),env:Y.Optional(Y.Record(Y.String(),Y.String())),cwd:Y.Optional(Y.String()),url:Y.Optional(Y.String({format:"uri"})),headers:Y.Optional(Y.Record(Y.String(),Y.String())),timeout:Y.Optional(Y.Integer({minimum:1000,maximum:1800000})),idleTimeout:Y.Optional(Y.Integer({minimum:1000,maximum:1800000})),sampling:Y.Optional(Y.Object({enabled:Y.Boolean(),maxTokens:Y.Optional(Y.Integer({minimum:1,maximum:4096})),maxRequestsPerToolCall:Y.Optional(Y.Integer({minimum:1,maximum:8})),maxInputBytes:Y.Optional(Y.Integer({minimum:1,maximum:1048576}))})),oauth:Y.Optional(Y.Object({enabled:Y.Optional(Y.Boolean()),clientId:Y.Optional(Y.String({minLength:1,maxLength:4096})),scopes:Y.Optional(Y.Array(Y.String({minLength:1,maxLength:128}),{maxItems:32})),callbackPort:Y.Optional(Y.Integer({minimum:1024,maximum:65535}))},{additionalProperties:!1})),healthCheck:Y.Optional(Y.Object({enabled:Y.Optional(Y.Boolean()),interval:Y.Optional(Y.Number({exclusiveMinimum:0})),timeout:Y.Optional(Y.Number({exclusiveMinimum:0})),failureThreshold:Y.Optional(Y.Number({exclusiveMinimum:0}))}))}),yO=Y.Union([Y.Object({mcpServers:Y.Record(Y.String(),_1)}),Y.Record(Y.String(),_1)]);function D1(O){return u(yO,O)}function N1(O){return u(RO,O)}function R1(O){return u(EO,O)}var fO=[{dir:".blade-plugin",source:"blade"},{dir:".claude-plugin",source:"claude"}];async function a(O){for(let{dir:$,source:Q}of fO){let q=v1.join(O,$,"plugin.json");try{await F0.access(q)}catch{continue}try{let Z=await F0.readFile(q,"utf-8"),J=JSON.parse(Z),K=u(w1,J);if(!K.success){let L=K.error.issues.map((G)=>`${G.path.join(".")}: ${G.message}`).join("; ");throw Error(`Invalid plugin.json: ${L}`)}return d0(K.data),M.debug(`Parsed plugin manifest from ${q}`),{manifest:K.data,source:Q,manifestPath:q}}catch(Z){if(Z instanceof SyntaxError)throw Error(`Invalid JSON in ${q}: ${Z.message}`);throw Z}}return null}import{realpathSync as hO}from"node:fs";import m0 from"node:path";var kO=/^[a-fA-F0-9]{40}$/;class v extends Error{code;constructor(O,$){super($);this.code=O;this.name="PluginSourcePolicyError"}}function PO(O,$){let Q=(Z)=>{try{return hO.native(Z)}catch{return m0.resolve(Z)}},q=m0.relative(Q(O),Q($));return q===""||!q.startsWith("..")&&!m0.isAbsolute(q)}function dO(O){let $=O.match(/^[^@]+@([^:]+):/);if($?.[1])return $[1].toLowerCase();try{return new URL(O).hostname.toLowerCase()}catch{return}}function pO(O,$){if(!$.startsWith("*."))return O===$;let Q=$.slice(1);return O.endsWith(Q)&&O.length>Q.length}function E1(O,$,Q){if(O.requireGitCommitSha&&(!$.ref||!kO.test($.ref)))throw new v("PLUGIN_GIT_SHA_REQUIRED",`${Q} must pin a full 40-character Git commit SHA`);if(!O.restrictToAllowedSources)return;let q=dO($.url);if(!q||!O.allowedGitHosts.some((Z)=>pO(q,Z)))throw new v("PLUGIN_SOURCE_BLOCKED",`${Q} Git host is not allowed by plugin source policy`)}function y1(O,$,Q){if(!O.restrictToAllowedSources)return;if(!O.allowedLocalRoots.some((q)=>PO(q,$)))throw new v("PLUGIN_SOURCE_BLOCKED",`${Q} local path is not allowed by plugin source policy`)}function l(O,$,Q="Marketplace"){if($.type==="git")E1(O,$,Q);else y1(O,$.path,Q)}function r(O,$,Q,q="Plugin"){if($.type==="git"){E1(O,$,q);return}if($.type==="local"){y1(O,$.path,q);return}if(O.restrictToAllowedSources&&!O.allowedMarketplaces.includes($.marketplace))throw new v("PLUGIN_SOURCE_BLOCKED",`Marketplace "${$.marketplace}" is not allowed by plugin source policy`);let Z=Q[$.marketplace];if(!Z)throw new v("MARKETPLACE_NOT_FOUND",`Marketplace "${$.marketplace}" is not configured`);l(O,Z.source,`Marketplace "${$.marketplace}"`)}var sO=uO(mO),nO=1,h1=2097152,k1=1e4,P1=104857600,oO=120000,iO=15000,tO=600000,s0=/^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$|^[a-z0-9]$/,d1=/^[a-fA-F0-9]{40}$/,aO=[S.join(".blade-plugin","marketplace.json"),S.join(".claude-plugin","marketplace.json"),"marketplace.json"];class F extends Error{code;constructor(O,$){super($);this.code=O;this.name="PluginPackageError"}}function rO(){return{version:nO,installed:{},marketplaces:{}}}function s(O){return O.code==="ENOENT"}function n(O){if(O instanceof F||O instanceof v)return{code:O.code,message:O.message};return{code:"PLUGIN_PACKAGE_ERROR",message:O instanceof Error?O.message:String(O)}}function c0(O){return S.isAbsolute(O)||O.startsWith("./")||O.startsWith("../")||O==="~"||O.startsWith("~/")}function O0(O,$){let Q=S.relative(O,$);return Q===""||!Q.startsWith("..")&&!S.isAbsolute(Q)}function o(O,$="plugin"){if(!s0.test(O))throw new F("INVALID_PLUGIN_NAME",`Invalid ${$} name: ${O}`);return O}function eO(O){if(O.startsWith("git@"))return;let $=O.lastIndexOf("@");if($<=0||$===O.length-1)return;let Q=O.slice(0,$),q=O.slice($+1);if(!s0.test(Q)||!s0.test(q))return;return{pluginName:Q,marketplace:q}}class p1{userPluginsDir;stateRoot;statePath;mutex=new cO;constructor(O,$){this.userPluginsDir=O??S.join(g0(),".blade","plugins"),this.stateRoot=$??S.join(S.dirname(this.userPluginsDir),"plugin-state"),this.statePath=S.join(this.stateRoot,"state.json")}async install(O,$={}){return this.runMutation(async()=>{let Q=[];try{this.requireSourceTrust($.trusted);let q=await this.readState(),Z=await this.resolvePluginSource(O,q,$),J=this.effectivePolicy($.policy);if(r(J,Z.recordSource,q.marketplaces),Z.expectedName&&q.installed[Z.expectedName])throw new F("PLUGIN_ALREADY_INSTALLED",`Plugin "${Z.expectedName}" is already installed`);let K=await this.preparePluginClosure({state:q,source:Z.source,recordSource:Z.recordSource,expectedName:Z.expectedName,policy:J,updateRoot:!1});Q=K.prepared;let L=await this.commitPreparedPlugins(q,Q),G=L.records.get(K.root.name);if(!G)throw new F("PLUGIN_PACKAGE_ERROR","Root plugin was not committed");let W=Q.map((X)=>X.name).filter((X)=>X!==K.root.name);return M.info(`Installed managed plugin ${K.root.name} at revision ${G.revision}`),{success:!0,pluginName:K.root.name,pluginPath:G.installPath,manifest:K.root.manifest,installation:G,installedDependencies:W,changed:L.changed.has(K.root.name)}}catch(q){await this.cleanupPreparedPlugins(Q);let Z=n(q);return{success:!1,code:Z.code,error:Z.message}}})}async update(O,$={}){return this.runMutation(async()=>{let Q=[];try{this.requireSourceTrust($.trusted),o(O);let q=await this.readState(),Z=q.installed[O];if(!Z)throw new F("PLUGIN_NOT_MANAGED",`Plugin "${O}" is not managed by Blade's package store`);let J=this.effectivePolicy($.policy);r(J,Z.source,q.marketplaces);let K=await this.preparePluginClosure({state:q,source:Z.source,recordSource:Z.source,expectedName:O,policy:J,updateRoot:!0});Q=K.prepared;let L=await this.commitPreparedPlugins(q,Q),G=L.records.get(O);if(!G)throw new F("PLUGIN_PACKAGE_ERROR","Updated root plugin was not committed");let W=Q.map((X)=>X.name).filter((X)=>X!==O&&L.changed.has(X));return M.info(`Updated managed plugin ${O} to revision ${G.revision}`),{success:!0,pluginName:O,pluginPath:G.installPath,manifest:K.root.manifest,installation:G,updatedDependencies:W,changed:L.changed.has(O)}}catch(q){await this.cleanupPreparedPlugins(Q);let Z=n(q);return{success:!1,pluginName:O,code:Z.code,error:Z.message}}})}async uninstall(O,$=!1){return this.runMutation(async()=>{try{if(o(O),!$)throw new F("CONFIRMATION_REQUIRED",`Uninstalling "${O}" requires explicit confirmation`);let Q=await this.readState(),q=Q.installed[O];if(!q)throw new F("PLUGIN_NOT_MANAGED",`Plugin "${O}" is not managed by Blade's package store`);return delete Q.installed[O],await this.writeState(Q),M.info(`Uninstalled managed plugin ${O}`),{success:!0,pluginName:O,installation:q}}catch(Q){let q=n(Q);return{success:!1,pluginName:O,code:q.code,error:q.message}}})}async listInstalled(){return Object.keys((await this.readState()).installed).sort()}async listInstallationRecords(){return Object.values((await this.readState()).installed).sort((O,$)=>O.name.localeCompare($.name))}async getInstallation(O){return(await this.readState()).installed[O]}async verifyInstallation(O){let $=await V.lstat(O.installPath);if(!$.isDirectory()||$.isSymbolicLink())throw new F("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" has an invalid package path`);let Q=await V.realpath(this.stateRoot),q=await V.realpath(O.installPath);if(!O0(Q,q))throw new F("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" has an invalid package path`);if(await this.digestTree(q)!==O.contentDigest)throw new F("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" failed content verification`);let J=await a(q);if(!J||J.manifest.name!==O.name||J.manifest.version!==O.version)throw new F("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" failed manifest verification`)}async addMarketplace(O,$={}){return this.runMutation(async()=>{let Q;try{let q=await this.normalizeMarketplaceSource(O,$);l(this.effectivePolicy($.policy),q);let Z=await this.readState();Q=await this.materializeSource(q);let J=await this.readMarketplaceManifest(Q.stagingPath),K=o(J.name,"marketplace");if(Z.marketplaces[K])throw new F("MARKETPLACE_ALREADY_EXISTS",`Marketplace "${K}" is already configured`);let{revision:L,contentDigest:G}=Q,W=await this.publishPackage("marketplaces",K,Q);Q=void 0;let X=new Date().toISOString(),H={name:K,source:q,installPath:W,revision:L,contentDigest:G,addedAt:X,updatedAt:X};return Z.marketplaces[K]=H,await this.writeState(Z),{success:!0,marketplace:H,manifest:J,changed:!0}}catch(q){if(Q)await V.rm(Q.stagingPath,{recursive:!0,force:!0});let Z=n(q);return{success:!1,code:Z.code,error:Z.message}}})}async refreshMarketplace(O,$){return this.runMutation(async()=>{let Q;try{o(O,"marketplace");let q=await this.readState(),Z=q.marketplaces[O];if(!Z)throw new F("MARKETPLACE_NOT_FOUND",`Marketplace "${O}" is not configured`);l(this.effectivePolicy($),Z.source,`Marketplace "${O}"`),Q=await this.materializeSource(Z.source);let J=await this.readMarketplaceManifest(Q.stagingPath);if(J.name!==O)throw new F("MARKETPLACE_IDENTITY_MISMATCH",`Marketplace "${O}" now declares "${J.name}"`);if(Z.revision===Q.revision&&Z.contentDigest===Q.contentDigest)return await V.rm(Q.stagingPath,{recursive:!0,force:!0}),Q=void 0,{success:!0,marketplace:Z,manifest:J,changed:!1};let{revision:K,contentDigest:L}=Q,G=await this.publishPackage("marketplaces",O,Q);Q=void 0;let W={...Z,installPath:G,revision:K,contentDigest:L,updatedAt:new Date().toISOString()};return q.marketplaces[O]=W,await this.writeState(q),{success:!0,marketplace:W,manifest:J,changed:!0}}catch(q){if(Q)await V.rm(Q.stagingPath,{recursive:!0,force:!0});let Z=n(q);return{success:!1,code:Z.code,error:Z.message}}})}async removeMarketplace(O,$=!1){return this.runMutation(async()=>{try{if(o(O,"marketplace"),!$)throw new F("CONFIRMATION_REQUIRED",`Removing marketplace "${O}" requires explicit confirmation`);let Q=await this.readState(),q=Q.marketplaces[O];if(!q)throw new F("MARKETPLACE_NOT_FOUND",`Marketplace "${O}" is not configured`);let Z=Object.values(Q.installed).filter((J)=>J.source.type==="marketplace"&&J.source.marketplace===O).map((J)=>J.name).sort();if(Z.length>0)throw new F("MARKETPLACE_IN_USE",`Marketplace "${O}" still owns installed plugins: ${Z.join(", ")}`);return delete Q.marketplaces[O],await this.writeState(Q),{success:!0,marketplace:q,changed:!0}}catch(Q){let q=n(Q);return{success:!1,code:q.code,error:q.message}}})}async listMarketplaces(){return Object.values((await this.readState()).marketplaces).sort((O,$)=>O.name.localeCompare($.name))}async listCatalogs(){let O=await this.listMarketplaces();return Promise.all(O.map(async($)=>({marketplace:$,manifest:await this.readMarketplaceManifest($.installPath)})))}async runMutation(O){return this.mutex.runExclusive(async()=>{let $=await this.acquireStoreLock();try{return await O()}finally{await $()}})}async acquireStoreLock(){await this.ensureStoreDirectory();let O=S.join(this.stateRoot,".operation.lock"),$=f1(),Q=Date.now()+iO;while(Date.now()<Q)try{let q=await V.open(O,e.O_CREAT|e.O_EXCL|e.O_WRONLY,384);return await q.writeFile(`${JSON.stringify({version:1,pid:process.pid,token:$,createdAt:new Date().toISOString()})}
|
|
625
|
+
`,"utf8"),await q.close(),async()=>{try{if(JSON.parse(await V.readFile(O,"utf8")).token===$)await V.unlink(O)}catch(Z){if(!s(Z))M.warn(`Failed to release plugin package lock: ${Z}`)}}}catch(q){if(q.code!=="EEXIST")throw q;await this.removeStaleStoreLock(O),await new Promise((Z)=>setTimeout(Z,50))}throw new F("PLUGIN_STORE_BUSY","Plugin package store is busy; retry the operation")}async removeStaleStoreLock(O){try{let $=await V.lstat(O);if(!$.isFile()||$.isSymbolicLink())throw new F("PLUGIN_STORE_INVALID","Plugin package lock must be a regular file");if(process.getuid&&$.uid!==process.getuid()||($.mode&511)!==384)throw new F("PLUGIN_STORE_INVALID","Plugin package lock ownership or permissions are invalid");if(Date.now()-$.mtimeMs<tO)return;await V.unlink(O)}catch($){if(!s($)&&$ instanceof F)throw $}}requireSourceTrust(O){if(O!==!0)throw new F("SOURCE_TRUST_REQUIRED","Plugin installation or update requires explicit source trust")}effectivePolicy(O){return O??V0.pluginSourcePolicy}async preparePluginClosure(O){let $=[],Q=new Map,q=[],Z=(K,L,G,W)=>{if(!u0(L,G))throw new F("DEPENDENCY_VERSION_MISMATCH",`Plugin "${W}" requires ${K}@${G}, found ${L}`)},J=async(K)=>{let L=K.expectedName?O.state.installed[K.expectedName]:void 0;if(L&&!K.root&&K.requiredRange){if(u0(L.version,K.requiredRange))return L;if(K.recordSource.type!=="marketplace"||L.source.type!=="marketplace"||L.source.marketplace!==K.recordSource.marketplace)Z(L.name,L.version,K.requiredRange,K.requiredBy??"dependency");K={...K,source:L.source,recordSource:L.source}}if(L&&K.root&&!O.updateRoot)throw new F("PLUGIN_ALREADY_INSTALLED",`Plugin "${L.name}" is already installed`);if(K.expectedName){let j=Q.get(K.expectedName);if(j){if(K.requiredRange)Z(j.name,j.manifest.version,K.requiredRange,K.requiredBy??"dependency");return j}if(q.includes(K.expectedName))throw new F("DEPENDENCY_CYCLE",`Plugin dependency cycle: ${[...q,K.expectedName].join(" -> ")}`)}let G=await this.materializePluginSource(K.source,O.state,K.expectedName,O.policy),W=await a(G.stagingPath);if(!W)throw await V.rm(G.stagingPath,{recursive:!0,force:!0}),new F("INVALID_PLUGIN","Plugin manifest was not found");let X=o(W.manifest.name);if(K.expectedName&&K.expectedName!==X)throw await V.rm(G.stagingPath,{recursive:!0,force:!0}),new F("PLUGIN_IDENTITY_MISMATCH",`Requested "${K.expectedName}" but source declares "${X}"`);if(q.includes(X))throw await V.rm(G.stagingPath,{recursive:!0,force:!0}),new F("DEPENDENCY_CYCLE",`Plugin dependency cycle: ${[...q,X].join(" -> ")}`);if(K.root&&O.state.installed[X]&&!O.updateRoot)throw await V.rm(G.stagingPath,{recursive:!0,force:!0}),new F("PLUGIN_ALREADY_INSTALLED",`Plugin "${X}" is already installed`);try{x1(W.manifest)}catch(j){throw await V.rm(G.stagingPath,{recursive:!0,force:!0}),new F("VERSION_INCOMPATIBLE",j instanceof Error?j.message:String(j))}let H={name:X,source:K.recordSource,manifest:W.manifest,materialized:G,existing:O.state.installed[X]};q.push(X);try{for(let[j,g]of Object.entries(W.manifest.dependencies??{}).sort(([q0],[N])=>q0.localeCompare(N))){let q0=Q.get(j);if(q0){Z(j,q0.manifest.version,g,X);continue}let N=O.state.installed[j];if(N&&u0(N.version,g))continue;if(K.recordSource.type!=="marketplace")throw new F(N?"DEPENDENCY_VERSION_MISMATCH":"DEPENDENCY_MISSING",N?`Plugin "${X}" requires ${j}@${g}, found ${N.version}`:`Plugin "${X}" requires missing dependency ${j}@${g}; direct sources cannot auto-install dependencies`);let _0=await J({source:N?.source.type==="marketplace"&&N.source.marketplace===K.recordSource.marketplace?N.source:{type:"marketplace",marketplace:K.recordSource.marketplace},recordSource:{type:"marketplace",marketplace:K.recordSource.marketplace},expectedName:j,requiredRange:g,requiredBy:X,root:!1});Z(j,"manifest"in _0?_0.manifest.version:_0.version,g,X)}}catch(j){throw await V.rm(G.stagingPath,{recursive:!0,force:!0}),j}finally{q.pop()}return Q.set(X,H),$.push(H),H};try{let K=await J({source:O.source,recordSource:O.recordSource,expectedName:O.expectedName,root:!0});if(!("manifest"in K))throw new F("PLUGIN_PACKAGE_ERROR","Root plugin unexpectedly resolved to an existing dependency");return{root:K,prepared:$}}catch(K){throw await this.cleanupPreparedPlugins($),K}}async commitPreparedPlugins(O,$){let Q=new Map,q=new Set,Z=new Date().toISOString();for(let J of $){let K=J.existing;if(K?.revision===J.materialized.revision&&K.contentDigest===J.materialized.contentDigest){await V.rm(J.materialized.stagingPath,{recursive:!0,force:!0}),Q.set(J.name,K);continue}let L=await this.publishPackage("packages",J.name,J.materialized),G={name:J.name,source:J.source,installPath:L,version:J.manifest.version,revision:J.materialized.revision,contentDigest:J.materialized.contentDigest,installedAt:K?.installedAt??Z,updatedAt:Z};O.installed[J.name]=G,Q.set(J.name,G),q.add(J.name)}if(q.size>0)await this.writeState(O);return{records:Q,changed:q}}async cleanupPreparedPlugins(O){await Promise.all(O.map(($)=>V.rm($.materialized.stagingPath,{recursive:!0,force:!0})))}async resolvePluginSource(O,$,Q){let q=O.trim();if(!q)throw new F("INVALID_SOURCE","Plugin source is required");let Z=eO(q);if(Z){if(!$.marketplaces[Z.marketplace])throw new F("MARKETPLACE_NOT_FOUND",`Marketplace "${Z.marketplace}" is not configured`);return{source:{type:"marketplace",marketplace:Z.marketplace},recordSource:{type:"marketplace",marketplace:Z.marketplace},expectedName:Z.pluginName}}if(!c0(q)&&!this.looksLikeGitSource(q)){let K=[];for(let L of await this.catalogsFromState($))if(L.manifest.plugins.some((G)=>G.name===q))K.push({pluginName:q,marketplace:L.marketplace.name});if(K.length===1)return{source:{type:"marketplace",marketplace:K[0].marketplace},recordSource:{type:"marketplace",marketplace:K[0].marketplace},expectedName:q};if(K.length>1)throw new F("AMBIGUOUS_PLUGIN_SOURCE",`Plugin "${q}" exists in multiple marketplaces; use name@marketplace`)}if(c0(q)){let L={type:"local",path:await this.resolveLocalSource(q,Q.workspaceRoot)};return{source:L,recordSource:L}}let J=this.normalizeGitSource(q,Q.ref);return{source:J,recordSource:J}}async materializePluginSource(O,$,Q,q){if(O.type!=="marketplace")return this.materializeSource(O);let Z=$.marketplaces[O.marketplace];if(!Z)throw new F("MARKETPLACE_NOT_FOUND",`Marketplace "${O.marketplace}" is not configured`);let J=await this.readMarketplaceManifest(Z.installPath),K=Q?J.plugins.filter((L)=>L.name===Q):J.plugins;if(K.length!==1)throw new F("MARKETPLACE_PLUGIN_NOT_FOUND",Q?`Plugin "${Q}" was not found in marketplace "${O.marketplace}"`:`Marketplace "${O.marketplace}" requires an explicit plugin name`);return this.materializeMarketplaceEntry(Z,K[0],q)}async materializeMarketplaceEntry(O,$,Q){if(typeof $.source!=="string"){let K=this.normalizeGitSource($.source.url,$.source.sha??$.source.ref);return l(Q,K,`Plugin "${$.name}"`),this.materializeSource(K)}if($.source.startsWith("https://")||$.source.startsWith("ssh://")||$.source.startsWith("git@")){let K=this.normalizeGitSource($.source);return l(Q,K,`Plugin "${$.name}"`),this.materializeSource(K)}let q=await V.realpath(O.installPath),Z=S.resolve(q,$.source);if(!O0(q,Z)||Z===q)throw new F("MARKETPLACE_PATH_ESCAPE",`Plugin "${$.name}" source escapes its marketplace`);let J=await V.realpath(Z);if(!O0(q,J))throw new F("MARKETPLACE_PATH_ESCAPE",`Plugin "${$.name}" source escapes its marketplace`);return this.materializeSource({type:"local",path:J})}async normalizeMarketplaceSource(O,$){let Q=O.trim();if(!Q)throw new F("INVALID_SOURCE","Marketplace source is required");if(c0(Q))return{type:"local",path:await this.resolveLocalSource(Q,$.workspaceRoot)};return this.normalizeGitSource(Q,$.ref)}normalizeGitSource(O,$){let Q=O;if(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(O))Q=`https://github.com/${O}.git`;else if(O.startsWith("git@")){if(!/^git@[A-Za-z0-9.-]+:[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\.git)?$/.test(O))throw new F("INVALID_SOURCE","Invalid SSH Git source")}else{let q;try{q=new URL(O)}catch{throw new F("INVALID_SOURCE","Invalid Git source")}if(q.protocol!=="https:"&&q.protocol!=="ssh:")throw new F("UNSAFE_GIT_PROTOCOL","Git sources must use HTTPS or SSH");if(q.password||q.protocol==="https:"&&q.username)throw new F("GIT_CREDENTIALS_IN_URL","Git credentials must not be embedded in plugin source URLs");if(q.hash||q.search)throw new F("INVALID_SOURCE","Use the ref option instead of URL query or fragment parameters");Q=q.toString()}if($&&(!$.trim()||$.startsWith("-")||$.includes("\x00")))throw new F("INVALID_GIT_REF","Invalid Git ref");return{type:"git",url:Q,...$?{ref:$}:{}}}looksLikeGitSource(O){return O.startsWith("https://")||O.startsWith("ssh://")||O.startsWith("git@")||/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(O)}async resolveLocalSource(O,$){let Q=O==="~"?g0():O.startsWith("~/")?S.join(g0(),O.slice(2)):O,q=S.resolve($??process.cwd(),Q),Z;try{Z=await V.realpath(q)}catch{throw new F("LOCAL_SOURCE_NOT_FOUND",`Local source does not exist: ${O}`)}let J=await V.lstat(Z);if(!J.isDirectory()||J.isSymbolicLink())throw new F("INVALID_LOCAL_SOURCE","Local plugin sources must be regular directories");return Z}async materializeSource(O){await this.ensureStoreDirectory();let $=S.join(this.stateRoot,"staging",f1());if(await V.mkdir(S.dirname($),{recursive:!0,mode:448}),O.type==="git"){await this.cloneGitSource(O,$);let q=await this.runGit(["rev-parse","HEAD"],$);if(O.ref&&d1.test(O.ref)&&q.toLowerCase()!==O.ref.toLowerCase())throw await V.rm($,{recursive:!0,force:!0}),new F("GIT_PIN_MISMATCH","Plugin checkout does not match the requested Git commit SHA");await V.rm(S.join($,".git"),{recursive:!0,force:!0});let Z=await this.digestTree($);return{stagingPath:$,revision:q,contentDigest:Z}}await this.copyLocalSource(O.path,$);let Q=await this.digestTree($);return{stagingPath:$,revision:`local-${Q.slice(0,40)}`,contentDigest:Q}}async cloneGitSource(O,$){try{if(O.ref&&d1.test(O.ref))await V.mkdir($,{recursive:!0,mode:448}),await this.runGit(["init"],$),await this.runGit(["remote","add","origin",O.url],$),await this.runGit(["fetch","--depth","1","origin",O.ref],$),await this.runGit(["checkout","--detach","FETCH_HEAD"],$);else{let Q=["clone","--depth","1","--no-tags"];if(O.ref)Q.push("--branch",O.ref);Q.push("--",O.url,$),await this.runGit(Q)}}catch(Q){throw await V.rm($,{recursive:!0,force:!0}),new F("GIT_CLONE_FAILED",`Failed to clone plugin source: ${this.sanitizeError(Q)}`)}}async runGit(O,$){let Q=await sO("git",O,{cwd:$,encoding:"utf8",timeout:oO,maxBuffer:4194304,env:{...process.env,GIT_TERMINAL_PROMPT:"0"}}),q=typeof Q==="string"?Q:Q.stdout;if(q===void 0)throw Error("Git process returned an invalid result");return String(q).trim()}sanitizeError(O){return(O instanceof Error?O.message:String(O)).replaceAll(this.stateRoot,"<plugin-store>").slice(0,500)}async copyLocalSource(O,$){await this.digestTree(O),await V.cp(O,$,{recursive:!0,force:!1,errorOnExist:!0,filter:(Q)=>{return!S.relative(O,Q).split(S.sep).includes(".git")}})}async digestTree(O){let $=[],Q=0,q=async(J)=>{let K=await V.readdir(J,{withFileTypes:!0});K.sort((L,G)=>L.name.localeCompare(G.name));for(let L of K){if(L.name===".git")continue;let G=S.join(J,L.name),W=S.relative(O,G),X=await V.lstat(G);if(X.isSymbolicLink())throw new F("PACKAGE_SYMLINK_FORBIDDEN",`Plugin packages may not contain symbolic links: ${W}`);if(X.isDirectory()){await q(G);continue}if(!X.isFile())throw new F("PACKAGE_ENTRY_FORBIDDEN",`Plugin packages may contain only files and directories: ${W}`);if(Q+=X.size,$.push({relative:W,absolute:G,executable:(X.mode&73)!==0}),$.length>k1||Q>P1)throw new F("PACKAGE_TOO_LARGE",`Plugin package exceeds ${k1} files or ${P1} bytes`)}};await q(O);let Z=gO("sha256");for(let J of $)Z.update(J.relative),Z.update("\x00"),Z.update(J.executable?"x":"-"),Z.update("\x00"),Z.update(await V.readFile(J.absolute)),Z.update("\x00");return Z.digest("hex")}async publishPackage(O,$,Q){let q=S.join(this.stateRoot,O,$,Q.contentDigest);await V.mkdir(S.dirname(q),{recursive:!0,mode:448});try{let Z=await V.lstat(q);if(!Z.isDirectory()||Z.isSymbolicLink())throw new F("PACKAGE_STORE_CONFLICT","Plugin package destination is not a regular directory");return await V.rm(Q.stagingPath,{recursive:!0,force:!0}),q}catch(Z){if(!s(Z))throw Z}return await V.rename(Q.stagingPath,q),q}async readMarketplaceManifest(O){for(let $ of aO){let Q=S.join(O,$);try{let q=await V.lstat(Q);if(!q.isFile()||q.isSymbolicLink()||q.size>h1)throw new F("INVALID_MARKETPLACE","Marketplace manifest must be a regular bounded file");let Z=JSON.parse(await V.readFile(Q,"utf8")),J=R1(Z);if(!J.success)throw new F("INVALID_MARKETPLACE",`Invalid marketplace manifest: ${J.error.issues.map((L)=>`${L.path.join(".")}: ${L.message}`).join("; ")}`);let K=new Set;for(let L of J.data.plugins){if(K.has(L.name))throw new F("INVALID_MARKETPLACE",`Marketplace contains duplicate plugin: ${L.name}`);K.add(L.name)}return J.data}catch(q){if(s(q))continue;if(q instanceof SyntaxError)throw new F("INVALID_MARKETPLACE","Marketplace manifest contains invalid JSON");throw q}}throw new F("INVALID_MARKETPLACE","Marketplace manifest was not found")}async catalogsFromState(O){let $=Object.values(O.marketplaces).sort((Q,q)=>Q.name.localeCompare(q.name));return Promise.all($.map(async(Q)=>({marketplace:Q,manifest:await this.readMarketplaceManifest(Q.installPath)})))}async readState(){let O;try{O=await V.open(this.statePath,e.O_RDONLY|e.O_NOFOLLOW)}catch($){if(s($))return rO();throw new F("PLUGIN_STATE_INVALID","Plugin package state must be a regular file")}try{let $=await O.stat();if(!$.isFile()||$.size>h1)throw new F("PLUGIN_STATE_INVALID","Plugin package state is invalid");if(process.getuid&&$.uid!==process.getuid())throw new F("PLUGIN_STATE_INVALID","Plugin package state must be owned by the current user");if(($.mode&511)!==384)throw new F("PLUGIN_STATE_INVALID","Plugin package state permissions must be 0600");let Q=JSON.parse(await O.readFile("utf8")),q=N1(Q);if(!q.success)throw new F("PLUGIN_STATE_INVALID","Plugin package state schema is invalid");let Z=q.data;for(let[J,K]of Object.entries(Z.installed)){let L=S.join(this.stateRoot,"packages",J);if(J!==K.name||!S.isAbsolute(K.installPath)||!O0(L,K.installPath)||S.basename(K.installPath)!==K.contentDigest||K.source.type==="local"&&!S.isAbsolute(K.source.path))throw new F("PLUGIN_STATE_INVALID","Plugin package state contains an invalid installation");if(K.source.type==="git"){let G=this.normalizeGitSource(K.source.url,K.source.ref);if(G.url!==K.source.url||G.ref!==K.source.ref)throw new F("PLUGIN_STATE_INVALID","Plugin package state contains an invalid Git source")}}for(let[J,K]of Object.entries(Z.marketplaces)){let L=S.join(this.stateRoot,"marketplaces",J);if(J!==K.name||!S.isAbsolute(K.installPath)||!O0(L,K.installPath)||S.basename(K.installPath)!==K.contentDigest||K.source.type==="local"&&!S.isAbsolute(K.source.path))throw new F("PLUGIN_STATE_INVALID","Plugin package state contains an invalid marketplace");if(K.source.type==="git"){let G=this.normalizeGitSource(K.source.url,K.source.ref);if(G.url!==K.source.url||G.ref!==K.source.ref)throw new F("PLUGIN_STATE_INVALID","Plugin package state contains an invalid Marketplace Git source")}}for(let J of Object.values(Z.installed))if(J.source.type==="marketplace"&&!Z.marketplaces[J.source.marketplace])throw new F("PLUGIN_STATE_INVALID","Plugin package state references a missing marketplace");return Z}catch($){if($ instanceof SyntaxError)throw new F("PLUGIN_STATE_INVALID","Plugin package state contains invalid JSON");throw $}finally{await O.close()}}async writeState(O){await this.ensureStoreDirectory(),await lO(this.statePath,`${JSON.stringify(O,null,2)}
|
|
626
|
+
`,{mode:384,encoding:"utf8"}),await V.chmod(this.statePath,384)}async ensureStoreDirectory(){try{let O=await V.lstat(this.stateRoot);if(!O.isDirectory()||O.isSymbolicLink())throw new F("PLUGIN_STORE_INVALID","Plugin package store must be a regular directory");if(process.getuid&&O.uid!==process.getuid())throw new F("PLUGIN_STORE_INVALID","Plugin package store must be owned by the current user");if((O.mode&511)!==448)throw new F("PLUGIN_STORE_INVALID","Plugin package store permissions must be 0700")}catch(O){if(!s(O))throw O;await V.mkdir(this.stateRoot,{recursive:!0,mode:448}),await V.chmod(this.stateRoot,448)}}}var l0=null;function n0(O,$){if(!l0)l0=new p1(O,$);return l0}import*as b from"node:fs/promises";import{homedir as O$}from"node:os";import*as I from"node:path";import g1 from"gray-matter";function H0(O,$){return`${O}:${$}`}function m1(O,$){return`${O}__${$}`}class U0{async loadPlugin(O,$,Q={}){let q=await a(O);if(!q)throw Error(`Not a valid plugin directory: ${O}`);let{manifest:Z,source:J}=q,K=Z.name;M.debug(`Loading plugin "${K}" from ${O}`);let L=Q.skipCommands?[]:await this.loadCommands(O,K),G=Q.skipAgents?[]:await this.loadAgents(O,K),W=Q.skipSkills?[]:await this.loadSkills(O,K),X=Q.skipHooks?void 0:await this.loadHooks(O),H=Q.skipMcp?void 0:await this.loadMcpConfig(O,K),j=Q.skipLsp?void 0:await this.loadLspConfig(O,K);return M.debug(`Plugin "${K}" loaded: ${L.length} commands, ${G.length} agents, ${W.length} skills`),{manifest:Z,basePath:O,source:$,manifestSource:J,commands:L,agents:G,skills:W,hooks:X,mcpServers:H,lspServers:j,status:"active",loadedAt:new Date}}async loadCommands(O,$){let Q=I.join(O,"commands"),q=[];if(!await this.dirExists(Q))return q;let Z=await this.scanMarkdownFiles(Q);for(let J of Z)try{let K=await this.parseCommandFile(J,Q,$);if(K)q.push(K)}catch(K){M.warn(`Failed to load command from ${J}: ${K instanceof Error?K.message:String(K)}`)}return q}async parseCommandFile(O,$,Q){let q=await b.readFile(O,"utf-8"),{data:Z,content:J}=g1(q),L=I.relative($,O).split(I.sep),G=L.pop();if(!G)return null;let W=G.replace(/\.md$/i,""),X=W;if(L.length>0)X=[...L,W].join("/");let H=H0(Q,X),j=this.normalizeCommandConfig(Z);return{originalName:X,namespacedName:H,pluginName:Q,config:j,content:J.trim(),path:O}}normalizeCommandConfig(O){return{description:this.asString(O.description),allowedTools:this.parseStringArray(O["allowed-tools"]),argumentHint:this.asString(O["argument-hint"]),model:this.asString(O.model),disableModelInvocation:O["disable-model-invocation"]===!0}}async loadAgents(O,$){let Q=I.join(O,"agents"),q=[];if(!await this.dirExists(Q))return q;let Z=await this.scanMarkdownFiles(Q);for(let J of Z)try{let K=await this.parseAgentFile(J,Q,$);if(K)q.push(K)}catch(K){M.warn(`Failed to load agent from ${J}: ${K instanceof Error?K.message:String(K)}`)}return q}async parseAgentFile(O,$,Q){let q=await b.readFile(O,"utf-8"),{data:Z,content:J}=g1(q),K=I.relative($,O),L=I.basename(K,".md"),G=Z.name||L,W=H0(Q,G),X={name:W,description:this.asString(Z.description)||"",tools:this.parseStringArray(Z.tools),color:this.asString(Z.color),model:this.asString(Z.model),permissionMode:this.asString(Z.permissionMode),skills:this.parseStringArray(Z.skills),systemPrompt:J.trim(),source:`plugin:${Q}`};return{originalName:G,namespacedName:W,pluginName:Q,config:X,path:O}}async loadSkills(O,$){let Q=I.join(O,"skills"),q=[];if(!await this.dirExists(Q))return q;let Z=await b.readdir(Q,{withFileTypes:!0});for(let J of Z){if(!J.isDirectory())continue;let K=I.join(Q,J.name),L=I.join(K,"SKILL.md");try{let G=await W0(L,"project");if(G.success&&G.content){let W=G.content.metadata.name,X=H0($,W);q.push({originalName:W,namespacedName:X,pluginName:$,metadata:{...G.content.metadata,name:X},path:K})}}catch(G){M.warn(`Failed to load skill from ${L}: ${G instanceof Error?G.message:String(G)}`)}}return q}async loadHooks(O){let $=I.join(O,"hooks","hooks.json");try{let Q=await b.readFile($,"utf-8"),q=JSON.parse(Q);return q.hooks||q}catch{return}}async loadMcpConfig(O,$){let Q=I.join(O,".mcp.json");try{let q=await b.readFile(Q,"utf-8"),Z=JSON.parse(q),J=D1(Z);if(!J.success){M.warn(`Invalid .mcp.json in ${O}: ${J.error}`);return}let K="mcpServers"in Z?Z.mcpServers:Z,L={};for(let[G,W]of Object.entries(K)){let X=m1($,G);L[X]=W}return L}catch{return}}async loadLspConfig(O,$){let Q=I.join(O,".lsp.json");try{let q=JSON.parse(await b.readFile(Q,"utf8")),Z=q&&typeof q==="object"&&!Array.isArray(q)&&"lspServers"in q?q.lspServers:q,J=u1(Z),K={};for(let[L,G]of Object.entries(J)){let W=(X)=>X.replaceAll("${CLAUDE_PLUGIN_ROOT}",O).replaceAll("${BLADE_PLUGIN_ROOT}",O);K[`plugin:${$}:${L}`]={...G,command:W(G.command),args:G.args?.map(W),env:G.env?Object.fromEntries(Object.entries(G.env).map(([X,H])=>[X,W(H)])):void 0}}return K}catch(q){if(q.code!=="ENOENT")M.warn(`Invalid .lsp.json in ${O}: ${q instanceof Error?q.message:String(q)}`);return}}async scanMarkdownFiles(O){let $=[],Q=async(q)=>{let Z=await b.readdir(q,{withFileTypes:!0});for(let J of Z){let K=I.join(q,J.name);if(J.isDirectory())await Q(K);else if(J.name.endsWith(".md"))$.push(K)}};return await Q(O),$}async dirExists(O){try{return(await b.stat(O)).isDirectory()}catch{return!1}}asString(O){if(typeof O==="string"&&O.trim())return O.trim();return}parseStringArray(O){if(!O)return;if(Array.isArray(O))return O.map(($)=>String($).trim()).filter(Boolean);if(typeof O==="string")return O.split(",").map(($)=>$.trim()).filter(Boolean);return}static getPluginDirs(O){let $=O$();return[{path:I.join($,".claude","plugins"),source:"user",type:"claude"},{path:I.join($,".blade","plugins"),source:"user",type:"blade"},{path:I.join(O,".claude","plugins"),source:"project",type:"claude"},{path:I.join(O,".blade","plugins"),source:"project",type:"blade"}]}async discoverPluginsInDir(O,$){let Q=[],q=[];try{await b.access(O)}catch{return{plugins:Q,errors:q}}let Z=await b.readdir(O,{withFileTypes:!0});for(let J of Z){if(!J.isDirectory())continue;let K=I.join(O,J.name);try{let L=await this.loadPlugin(K,$);Q.push(L)}catch(L){q.push({path:K,error:L instanceof Error?L.message:String(L)})}}return{plugins:Q,errors:q}}}class f{static instances=new Map;plugins=new Map;loader=new U0;initialized=!1;workspaceRoot="";cliPluginDirs=[];enabledSettings={};sourcePolicy={...V0.pluginSourcePolicy};marketplaces={};constructor(O){this.workspaceRoot=O}static getInstance(O=x()){let $=$$.resolve(O),Q=f.instances.get($);if(!Q)Q=new f($),f.instances.set($,Q);return Q}static resetInstance(){f.instances.clear()}static getInitializedInstances(){return Array.from(f.instances.values()).filter((O)=>O.isInitialized())}async initialize(O,$=[]){this.workspaceRoot=O,this.cliPluginDirs=$,this.enabledSettings=await S0.getInstance().loadWorkspacePluginSettings(O),await this.refreshPolicyContext();let Q=[],q=[];for(let G of $)try{let W=await this.loader.loadPlugin(G,"cli");this.plugins.set(W.manifest.name,W),Q.push(W),M.info(`Loaded CLI plugin: ${W.manifest.name} from ${G}`)}catch(W){q.push({path:G,error:W instanceof Error?W.message:String(W)}),M.warn(`Failed to load CLI plugin from ${G}: ${W}`)}let Z=(await T.getInstance().getStatus(O)).state==="trusted",J=U0.getPluginDirs(O).filter((G)=>G.source!=="project"||Z),K=!1,L=async()=>{if(K)return;K=!0;let G=n0(),W=await G.listInstallationRecords();for(let X of W)try{await G.verifyInstallation(X);let H=await this.loader.loadPlugin(X.installPath,"user");if(H.manifest.name!==X.name)throw Error(`Managed plugin identity mismatch: ${X.name}`);H.installation=Object.freeze({...X});let j=this.plugins.get(H.manifest.name);if(!j||j.source==="user")this.plugins.set(H.manifest.name,H),Q.push(H)}catch(H){q.push({path:X.installPath,code:"IO_ERROR",error:H instanceof Error?H.message:String(H)})}};for(let{path:G,source:W}of J){if(W==="project")await L();let X=await this.loader.discoverPluginsInDir(G,W);for(let H of X.plugins){let j=this.plugins.get(H.manifest.name);if(!j||j.source!=="cli")this.plugins.set(H.manifest.name,H),Q.push(H)}q.push(...X.errors)}await L(),this.applyEnabledSettings();for(let G of this.plugins.values()){if(G.status!=="error")continue;q.push({path:G.basePath,code:G.compatibilityIssues?.some((W)=>W.code==="source-policy")?"SOURCE_POLICY_BLOCKED":G.compatibilityIssues?.some((W)=>W.code==="blade-version")?"VERSION_INCOMPATIBLE":"DEPENDENCY_MISSING",error:G.error??"Plugin compatibility check failed"})}return this.initialized=!0,M.info(`Plugin system initialized: ${this.plugins.size} plugins loaded`+(q.length>0?`, ${q.length} errors`:"")),{plugins:Array.from(this.plugins.values()),errors:q}}isInitialized(){return this.initialized}getWorkspaceRoot(){return this.workspaceRoot}getSourcePolicy(){return{...this.sourcePolicy,allowedGitHosts:[...this.sourcePolicy.allowedGitHosts],allowedMarketplaces:[...this.sourcePolicy.allowedMarketplaces],allowedLocalRoots:[...this.sourcePolicy.allowedLocalRoots]}}getAll(){return Array.from(this.plugins.values())}getActive(){return Array.from(this.plugins.values()).filter((O)=>O.status==="active")}get(O){return this.plugins.get(O)}has(O){return this.plugins.has(O)}async reapplyEnabledSettings(){this.enabledSettings=await S0.getInstance().loadWorkspacePluginSettings(this.workspaceRoot),await this.refreshPolicyContext(),this.applyEnabledSettings()}applyEnabledSettings(){for(let O of this.plugins.values()){if(O.compatibilityIssues=[],O.error=void 0,O.status=O.source==="cli"||this.enabledSettings[O.manifest.name]!==!1?"active":"inactive",O.status!=="active")continue;try{r(this.sourcePolicy,O.installation?.source??{type:"local",path:O.basePath},this.marketplaces,`Plugin "${O.manifest.name}"`)}catch($){if(!($ instanceof v))throw $;O.status="error",O.error=$.message,O.compatibilityIssues=[{code:"source-policy",message:$.message}]}}b1(Array.from(this.plugins.values()))}async refreshPolicyContext(){this.sourcePolicy=await S0.getInstance().loadWorkspacePluginSourcePolicy(this.workspaceRoot),this.marketplaces=Object.fromEntries((await n0().listMarketplaces()).map((O)=>[O.name,O]))}getBySource(){let O={cli:[],project:[],user:[]};for(let $ of this.plugins.values())O[$.source].push($);return O}getAllCommands(){let O=[];for(let $ of this.plugins.values())if($.status==="active")O.push(...$.commands);return O}getAllSkills(){let O=[];for(let $ of this.plugins.values())if($.status==="active")O.push(...$.skills);return O}getAllAgents(){let O=[];for(let $ of this.plugins.values())if($.status==="active")O.push(...$.agents);return O}findCommand(O){for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.commands)if(q.namespacedName===O)return q}let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.commands)if(q.originalName===O)$.push(q)}if($.length===1)return $[0];return}findSkill(O){for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.skills)if(q.namespacedName===O)return q}let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.skills)if(q.originalName===O)$.push(q)}if($.length===1)return $[0];return}findAgent(O){for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.agents)if(q.namespacedName===O)return q}let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.agents)if(q.originalName===O)$.push(q)}if($.length===1)return $[0];return}hasCommandConflict(O){let $=0;for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.commands)if(q.originalName===O){if($++,$>1)return!0}}return!1}getCommandProviders(O){let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let q of Q.commands)if(q.originalName===O){$.push(Q.manifest.name);break}}return $}disable(O){let $=this.plugins.get(O);if($&&$.status==="active")return $.status="inactive",M.info(`Plugin "${O}" disabled`),!0;return!1}enable(O){let $=this.plugins.get(O);if($&&$.status==="inactive")return $.status="active",M.info(`Plugin "${O}" enabled`),!0;return!1}async refresh(){return M.info("Refreshing plugin list..."),this.plugins.clear(),this.initialized=!1,this.initialize(this.workspaceRoot,this.cliPluginDirs)}getStats(){let O=0,$=0,Q=0,q=0,Z=0;for(let J of this.plugins.values())if(J.status==="active")O++,Q+=J.commands.length,q+=J.skills.length,Z+=J.agents.length;else $++;return{total:this.plugins.size,active:O,inactive:$,commands:Q,skills:q,agents:Z}}formatPluginList(){if(this.getAll().length===0)return"没有已加载的插件。";let $=[],Q=this.getBySource();if(Q.cli.length>0){$.push("## CLI 指定的插件");for(let q of Q.cli){let Z=q.status==="inactive"?" (禁用)":"";$.push(`- **${q.manifest.name}** v${q.manifest.version}${Z}`),$.push(` ${q.manifest.description}`)}$.push("")}if(Q.project.length>0){$.push("## 项目级插件");for(let q of Q.project){let Z=q.status==="inactive"?" (禁用)":"";$.push(`- **${q.manifest.name}** v${q.manifest.version}${Z}`),$.push(` ${q.manifest.description}`)}$.push("")}if(Q.user.length>0){$.push("## 用户级插件");for(let q of Q.user){let Z=q.status==="inactive"?" (禁用)":"";$.push(`- **${q.manifest.name}** v${q.manifest.version}${Z}`),$.push(` ${q.manifest.description}`)}}return $.join(`
|
|
627
|
+
`)}}function j0(O=x()){return f.getInstance(O)}var d=new Map,c1=Object.values(M0);function o0(O){let $={...O};for(let Q of c1)$[Q]=(O[Q]??[]).map((q)=>({...q,matcher:q.matcher?{...q.matcher}:void 0,hooks:q.hooks.map((Z)=>({...Z}))}));return $}class i0{commandRegistry;hookManager;skillRegistry;subagentRegistry;workspaceRoot;constructor(O=x()){this.workspaceRoot=Q$.resolve(O),this.commandRegistry=w.getInstance(this.workspaceRoot),this.hookManager=B0.getInstance(),this.skillRegistry=P({cwd:this.workspaceRoot}),this.subagentRegistry=i(this.workspaceRoot)}clearAllPluginResources(){this.commandRegistry.clearPluginCommands(),this.skillRegistry.clearPluginSkills(),this.subagentRegistry.clearPluginAgents();let O=d.get(this.workspaceRoot);if(O)this.hookManager.loadConfig(O,this.workspaceRoot),d.delete(this.workspaceRoot);M.debug("Cleared all plugin resources from subsystems")}async integrateAll(){let $=j0(this.workspaceRoot).getActive(),Q=[],q=[],Z=0,J=0,K=0,L=0,G=0;for(let W of $){let X=this.integratePlugin(W);Q.push(X),Z+=X.commandsRegistered,J+=X.skillsRegistered,K+=X.agentsRegistered,L+=X.mcpServersRegistered,G+=X.lspServersRegistered,q.push(...X.errors)}if(this.integrateHooks($),Z+J+K>0)M.info(`Plugin integration complete: ${Z} commands, ${J} skills, ${K} agents, ${L} MCP servers, ${G} LSP servers`);return{plugins:Q,totalCommands:Z,totalSkills:J,totalAgents:K,totalMcpServers:L,errors:q}}integratePlugin(O){let $={pluginName:O.manifest.name,commandsRegistered:0,skillsRegistered:0,agentsRegistered:0,hooksRegistered:!1,mcpServersRegistered:0,lspServersRegistered:0,errors:[]};try{$.commandsRegistered=this.integrateCommands(O)}catch(Q){$.errors.push(`Failed to integrate commands: ${Q instanceof Error?Q.message:String(Q)}`)}try{$.skillsRegistered=this.integrateSkills(O)}catch(Q){$.errors.push(`Failed to integrate skills: ${Q instanceof Error?Q.message:String(Q)}`)}try{$.agentsRegistered=this.integrateAgents(O)}catch(Q){$.errors.push(`Failed to integrate agents: ${Q instanceof Error?Q.message:String(Q)}`)}$.hooksRegistered=O.hooks!==void 0;try{$.mcpServersRegistered=this.integrateMcp(O)}catch(Q){$.errors.push(`Failed to integrate MCP servers: ${Q instanceof Error?Q.message:String(Q)}`)}return $.lspServersRegistered=Object.keys(O.lspServers??{}).length,M.debug(`Integrated plugin "${O.manifest.name}": ${$.commandsRegistered} commands, ${$.skillsRegistered} skills, ${$.agentsRegistered} agents`),$}integrateCommands(O){let $=0;for(let Q of O.commands)this.commandRegistry.registerPluginCommand(Q),$++;return $}integrateSkills(O){let $=0;for(let Q of O.skills)this.skillRegistry.registerPluginSkill(Q),$++;return $}integrateAgents(O){let $=0;for(let Q of O.agents)this.subagentRegistry.register({...Q.config,name:Q.namespacedName}),$++;return $}integrateHooks(O){let $=O.filter((J)=>J.hooks);if($.length===0){let J=d.get(this.workspaceRoot);if(J)this.hookManager.loadConfig(J,this.workspaceRoot),d.delete(this.workspaceRoot);return}let Q=o0(this.hookManager.getConfig(this.workspaceRoot));if(!d.has(this.workspaceRoot))d.set(this.workspaceRoot,o0(Q));let q=d.get(this.workspaceRoot)??Q,Z=o0(q);Z.enabled=!0;for(let J of c1){let K=q.enabled?[...q[J]??[]]:[],L=[];for(let G of $)for(let W of G.hooks?.[J]??[])L.push({...W,name:W.name?`plugin:${G.manifest.name}:${W.name}`:`plugin:${G.manifest.name}:${J}`,hooks:W.hooks.map((X)=>({...X,source:{kind:"plugin",pluginName:G.manifest.name,pluginSource:G.source,pluginRoot:G.basePath}}))});Z[J]=[...K,...L]}this.hookManager.loadConfig(Z,this.workspaceRoot)}integrateMcp(O){return Object.keys(O.mcpServers??{}).length}}async function t0(O=x()){return new i0(O).integrateAll()}function I0(O=x()){new i0(O).clearAllPluginResources()}import{createHash as q$}from"node:crypto";import*as D from"node:fs/promises";import Z$ from"node:os";import C from"node:path";import J$ from"gray-matter";var z0=32,l1=24576,s1=16384,n1=131072,A0=4,o1=80,i1=256,a1=/^[a-z0-9][a-z0-9._-]{0,63}$/,K$=h("Agent");function a0(O){for(let $ of O){let Q=$.codePointAt(0)??0;if(Q<=8||Q===11||Q===12||Q>=14&&Q<=31||Q===127||Q>=8203&&Q<=8207||Q>=8234&&Q<=8238||Q>=8288&&Q<=8303||Q===65279)return!0}return!1}function G$(O,$,Q){let q=typeof O==="string"?O.trim():"",Z=$.split(/\r?\n/).map((K)=>K.trim()).find(Boolean)?.replace(/^#+\s*/,"")??"",J=q||Z||Q;if(J.length>i1)throw Error(`Description exceeds ${i1} characters`);if(a0(J))throw Error("Description contains hidden control characters");return J}function Y$(O,$){let Q=typeof O==="string"&&O.trim()?O.trim():$;if(Q.length>o1)throw Error(`Name exceeds ${o1} characters`);if(a0(Q))throw Error("Name contains hidden control characters");return Q}function L$(O){let Q=O.replace(/\.md$/i,"").split(C.sep);if(Q.length===0||Q.length>A0||Q.some((q)=>!a1.test(q)))throw Error(`Style path must use 1-${A0} lowercase safe segments`);return Q.join(":")}async function W$(O){let $;try{$=await D.lstat(O)}catch(J){if(J.code==="ENOENT")return[];throw J}if($.isSymbolicLink())throw Error(`Output style root cannot be a symlink: ${O}`);if(!$.isDirectory())return[];let Q=await D.realpath(O),q=[],Z=async(J,K)=>{if(K>A0)throw Error(`Output style directory exceeds depth ${A0}`);let L=await D.readdir(J,{withFileTypes:!0});L.sort((G,W)=>G.name.localeCompare(W.name));for(let G of L){let W=C.join(J,G.name);if(G.isSymbolicLink())throw Error(`Output style path cannot be a symlink: ${W}`);if(G.isDirectory()){await Z(W,K+1);continue}if(!G.isFile()||!G.name.toLowerCase().endsWith(".md"))continue;let X=await D.realpath(W),H=C.relative(Q,X);if(!H||H.startsWith("..")||C.isAbsolute(H))throw Error(`Output style path escapes its source root: ${W}`);if(q.push(X),q.length>z0)throw Error(`Output style catalog exceeds ${z0} files`)}};return await Z(Q,1),q}async function X$(O,$,Q){let q=await D.lstat(Q);if(!q.isFile()||q.isSymbolicLink())throw Error("Output style must be a regular file");if(q.size>l1)throw Error(`Output style exceeds ${l1} bytes`);let Z=await D.readFile(Q,"utf8"),J=J$(Z),K=J.content.trim(),L=Buffer.byteLength(K);if(L===0)throw Error("Output style prompt must not be empty");if(L>s1)throw Error(`Output style prompt exceeds ${s1} bytes`);if(a0(K))throw Error("Output style prompt contains hidden control characters");let G=C.relative($,Q),W=L$(G);return{id:`${O.idPrefix}:${W}`,name:Y$(J.data.name,W),description:G$(J.data.description,K,`Custom ${W} communication style`),source:O.source,contentSha256:q$("sha256").update(K,"utf8").digest("hex"),prompt:K}}async function F$(O,$){let Q=await W$(O.directory);if(Q.length===0)return;let q=await D.realpath(O.directory);for(let Z of Q)try{let J=await X$(O,q,Z);$.set(J.id,J)}catch(J){K$.warn(`[CommunicationStyles] Ignoring ${O.source} style: ${J instanceof Error?J.message:String(J)}`)}}function V$(O,$,Q,q){let Z=[{directory:C.join(q,".claude","output-styles"),source:"user",idPrefix:"user"},{directory:C.join(q,".blade","output-styles"),source:"user",idPrefix:"user"}];if($)Z.push({directory:C.join(O,".claude","output-styles"),source:"project",idPrefix:"project"},{directory:C.join(O,".blade","output-styles"),source:"project",idPrefix:"project"});for(let J of Q){if(J.status!=="active"||!a1.test(J.manifest.name))continue;Z.push({directory:C.join(J.basePath,"output-styles"),source:"plugin",idPrefix:`plugin:${J.manifest.name}`})}return Z}async function r0(O,$){let Q=new Map;for(let Z of V$(C.resolve(O),$.projectTrusted,$.plugins??[],$.homeDirectory??Z$.homedir()))await F$(Z,Q);let q=0;for(let Z of Q.values())q+=Buffer.byteLength(Z.prompt??"");if(Q.size>z0)throw Error(`Output style catalog exceeds ${z0} styles`);if(q>n1)throw Error(`Output style catalog exceeds ${n1} bytes`);return new t1([...Q.values()])}import{createHash as H$}from"node:crypto";import{existsSync as x0,realpathSync as U$}from"node:fs";import*as m from"node:fs/promises";import B from"node:path";import S$ from"fast-glob";import j$ from"gray-matter";import ZO from"picomatch";var r1=128,e1=32768,OO=262144,M$=49152,B$=32768,I$=24,$O=16,z$=256,A$=h("Agent"),x$=["**/CLAUDE.md","**/.claude/CLAUDE.md","**/CLAUDE.local.md","**/AGENTS.md","**/AGENTS.override.md","**/BLADE.md","**/.claude/rules/**/*.md","**/.blade/rules/**/*.md"],b$=["**/.git/**","**/node_modules/**","**/.venv/**","**/dist/**","**/build/**","**/coverage/**","**/target/**"];function b0(O){return H$("sha256").update(O,"utf8").digest("hex")}function JO(O){for(let $ of O){let Q=$.codePointAt(0)??0;if(Q<=8||Q===11||Q===12||Q>=14&&Q<=31||Q===127||Q>=8203&&Q<=8207||Q>=8234&&Q<=8238||Q>=8288&&Q<=8303||Q===65279)return!0}return!1}function Q1(O){return O.split(B.sep).join("/")}function e0(O){let $=B.resolve(O),Q=[];while(!0)try{return B.join(U$($),...Q.reverse())}catch(q){if(q.code!=="ENOENT")return B.resolve(O);let Z=B.dirname($);if(Z===$)return B.resolve(O);Q.push(B.basename($)),$=Z}}function _$(O){let $=B.resolve(O),Q;while(!0){if(x0(B.join($,".git")))return $;if(!Q&&(x0(B.join($,".blade"))||x0(B.join($,".claude"))||x0(B.join($,"package.json"))))Q=$;let q=B.dirname($);if(q===$)return Q??B.resolve(O);$=q}}function q1(O){return Boolean(O)&&O!==".."&&!O.startsWith("../")}function O1(O,$){return O===""||$===O||$.startsWith(`${O}/`)}function QO(O){return O.replaceAll("&","&").replaceAll('"',""").replaceAll("<","<").replaceAll(">",">")}function T$(O){let $=typeof O==="string"?O.split(/[,\n]/u):Array.isArray(O)?O:[],Q=[...new Set($.filter((q)=>typeof q==="string").map((q)=>q.trim().replaceAll("\\","/")).filter(Boolean))];if(Q.length===0||Q.every((q)=>q==="**"))return;if(Q.length>$O)throw Error(`paths exceeds ${$O} patterns`);for(let q of Q){if(q.length>z$||B.posix.isAbsolute(q)||q.split("/").includes("..")||JO(q))throw Error(`Unsafe project rule path pattern: ${q.slice(0,80)}`);ZO(q,{dot:!0})}return Q}function w$(O){if(O.endsWith("/CLAUDE.local.md")||O==="CLAUDE.local.md")return{source:"local",kind:"instruction",scopeDirectory:B.posix.dirname(O).replace(/^\.$/u,""),priority:90};for(let q of[".claude/rules/",".blade/rules/"]){let Z=O.indexOf(q);if(Z>=0)return{source:"project",kind:"rule",scopeDirectory:O.slice(0,Z).replace(/\/$/u,""),priority:q.startsWith(".blade")?80:70}}if(O.endsWith("/.claude/CLAUDE.md")||O===".claude/CLAUDE.md")return{source:"project",kind:"instruction",scopeDirectory:O.slice(0,-17).replace(/\/$/u,""),priority:30};let $=B.posix.basename(O),Q=$==="CLAUDE.md"?20:$==="AGENTS.md"?40:$==="AGENTS.override.md"?50:60;return{source:"project",kind:"instruction",scopeDirectory:B.posix.dirname(O).replace(/^\.$/u,""),priority:Q}}function $1(O,$){let Q=O.scopeDirectory?O.scopeDirectory.split("/").length:0,q=$.scopeDirectory?$.scopeDirectory.split("/").length:0;return Q-q||O.priority-$.priority||O.relativePath.localeCompare($.relativePath)}function C$(O){return{id:O.id,relativePath:O.relativePath,source:O.source,contentSha256:O.contentSha256}}function $0(O,$,Q){let q=O.map((K)=>{let L=K.conditional?' conditional="true"':"";return`<instruction-file path="${QO(K.relativePath)}" source="${K.source}" sha256="${K.contentSha256}"${L}>
|
|
628
628
|
${K.content}
|
|
629
|
-
</instruction-file>`}),Z=Q==="contextual-project-instructions"&&$.length>0?` trigger-paths="${
|
|
629
|
+
</instruction-file>`}),Z=Q==="contextual-project-instructions"&&$.length>0?` trigger-paths="${QO($.join(","))}"`:"",J=q.length>0?`<${Q}${Z}>
|
|
630
630
|
${q.join(`
|
|
631
631
|
|
|
632
632
|
`)}
|
|
633
|
-
</${Q}>`:"";return{content:J,files:[...O],references:O.map(
|
|
634
|
-
`))}}function
|
|
635
|
-
`))}static empty(O){return new p(O)}list(){return this.definitions.map((O)=>({id:O.id,relativePath:O.relativePath,source:O.source,kind:O.kind,conditional:O.conditional,contentSha256:O.contentSha256}))}snapshot(){return new p(this.projectRoot,this.definitions.map((O)=>({...O,patterns:O.patterns?[...O.patterns]:void 0})))}staticRules(O,$=
|
|
636
|
-
export{i as
|
|
633
|
+
</${Q}>`:"";return{content:J,files:[...O],references:O.map(C$),triggerPaths:[...$],contentBytes:Buffer.byteLength(J,"utf8"),provenanceSha256:b0(O.map((K)=>`${K.id}:${K.contentSha256}`).join(`
|
|
634
|
+
`))}}function qO(O,$){let Q=$,q=[];for(let Z=O.length-1;Z>=0;Z-=1){let J=O[Z];if(!J)continue;let K=Buffer.byteLength(J.content,"utf8");if(K>Q)continue;q.push(J),Q-=K}return q.reverse()}class p{projectRoot;catalogSha256;definitions;byId;constructor(O,$=[]){this.projectRoot=O;this.projectRoot=e0(O),this.definitions=Object.freeze([...$].sort($1).map((Q)=>Object.freeze({...Q}))),this.byId=new Map(this.definitions.map((Q)=>[Q.id,Q])),this.catalogSha256=b0(this.definitions.map((Q)=>`${Q.id}:${Q.contentSha256}`).join(`
|
|
635
|
+
`))}static empty(O){return new p(O)}list(){return this.definitions.map((O)=>({id:O.id,relativePath:O.relativePath,source:O.source,kind:O.kind,conditional:O.conditional,contentSha256:O.contentSha256}))}snapshot(){return new p(this.projectRoot,this.definitions.map((O)=>({...O,patterns:O.patterns?[...O.patterns]:void 0})))}staticRules(O,$=M$){if(this.definitions.length===0)return $0([],[],"project-instructions");let Q=this.relativeSourcePath(O),q=this.definitions.filter((Z)=>!Z.conditional&&O1(Z.scopeDirectory,Q));return $0(qO(q,$),[],"project-instructions")}contextualRules(O,$,Q,q=B$){if(this.definitions.length===0)return $0([],[],"contextual-project-instructions");let Z=this.relativeSourcePath(O),J=[...new Set($.map((G)=>Q1(B.relative(this.projectRoot,e0(G)))).filter(q1))].sort(),K=new Map;for(let G of J)for(let W of this.definitions){if(Q.has(W.id))continue;if(!O1(W.scopeDirectory,G))continue;if(W.conditional){let X=B.posix.relative(W.scopeDirectory||".",G);if(!W.patterns?.some((H)=>ZO(H,{dot:!0})(X)))continue}else if(O1(W.scopeDirectory,Z))continue;K.set(W.id,W)}let L=qO([...K.values()].sort($1),q);return $0(L,J,"contextual-project-instructions")}hydrate(O){let $=O.map((Q)=>{let q=this.byId.get(Q.id);if(!q||q.contentSha256!==Q.contentSha256||q.relativePath!==Q.relativePath||q.source!==Q.source)throw Error(`Project rule provenance mismatch: ${Q.relativePath}`);return q});return $0([...new Map($.map((Q)=>[Q.id,Q])).values()].sort($1),[],"contextual-project-instructions")}relativeSourcePath(O){let $=Q1(B.relative(this.projectRoot,e0(O)));if($&&!q1($))throw Error("Project instruction source is outside the catalog root");return $}}async function D$(O,$){let Q=await m.lstat($);if(!Q.isFile()||Q.isSymbolicLink())throw Error("Project rule must be a regular file");if(Q.size>e1)throw Error(`Project rule exceeds ${e1} bytes`);let q=await m.realpath($),Z=Q1(B.relative(O,q));if(!q1(Z))throw Error("Project rule escapes its repository root");let J=await m.readFile(q,"utf8");if(JO(J))throw Error("Project rule contains hidden control characters");let K=j$(J),L=K.content.trim();if(!L)throw Error("Project rule content must not be empty");let G=T$(K.data.paths),W=w$(Z),X=b0(JSON.stringify({relativePath:Z,patterns:G??[],content:L}));return{id:`${W.source}:${b0(Z).slice(0,20)}`,relativePath:Z,source:W.source,kind:W.kind,scopeDirectory:W.scopeDirectory,priority:W.priority,conditional:Boolean(G),...G?{patterns:G}:{},content:L,contentSha256:X}}async function KO(O,$){let Q=_$(O);if(!$.projectTrusted)return p.empty(Q);let q=await m.realpath(Q),Z=S$.stream([...x$],{cwd:q,absolute:!0,dot:!0,deep:I$,onlyFiles:!0,followSymbolicLinks:!1,unique:!0,ignore:[...b$]}),J=[];for await(let W of Z)if(J.push(W.toString()),J.length>r1)throw Error(`Project rule catalog exceeds ${r1} files`);J.sort();let K=new Set(J.filter((W)=>B.basename(W)==="AGENTS.override.md").map((W)=>B.dirname(W))),L=[],G=0;for(let W of J){if(B.basename(W)==="AGENTS.md"&&K.has(B.dirname(W)))continue;try{let X=await D$(q,W);if(G+=Buffer.byteLength(X.content,"utf8"),G>OO)throw Error(`Project rule catalog exceeds ${OO} bytes`);L.push(X)}catch(X){A$.warn(`[ProjectRules] Ignoring rule: ${X instanceof Error?X.message:String(X)}`)}}return new p(q,L)}var Q0=new Map,YO=[];async function N$(O){let $=await T.getInstance().getStatus(O.workspaceRoot);O.communicationStyles=await r0(O.workspaceRoot,{projectTrusted:$.state==="trusted",plugins:O.plugins.getActive()})}function R$(O){let $={...O};for(let Q of Object.values(M0))$[Q]=(O[Q]??[]).map((q)=>({...q,matcher:q.matcher?{...q.matcher}:void 0,hooks:q.hooks.map((Z)=>({...Z}))}));return $}function V2(O){YO=[...new Set(O.map(($)=>Z1.resolve($)))]}async function H2(O,$={}){let Q=Z1.resolve(O),q=Q0.get(Q);if(!q)q=(async()=>{let J=await T.getInstance().getStatus(Q),K=i(Q),L=P({cwd:Q}),G=w.getInstance(Q),W=j0(Q);if(K.getAllNames().length===0)K.loadFromStandardLocations(J.state==="trusted");await Promise.all([L.initialize(),G.isInitialized()?void 0:G.initialize(Q),W.isInitialized()?void 0:W.initialize(Q,[...$.cliPluginDirs??YO])]),I0(Q),await t0(Q);let X=await r0(Q,{projectTrusted:J.state==="trusted",plugins:W.getActive()}),H=await KO(Q,{projectTrusted:J.state==="trusted"});return{workspaceRoot:Q,subagents:K,skills:L,commands:G,plugins:W,communicationStyles:X,projectRules:H}})().catch((J)=>{throw Q0.delete(Q),J}),Q0.set(Q,q);let Z=await q;if($.reconcilePlugins)I0(Q),await t0(Q),await N$(Z);return Z}function U2(O){let $="projectRoot"in O?O.projectRoot:O.workspaceRoot;return{projectRoot:Z1.resolve($),subagents:O.subagents.snapshot(),skills:O.skills.snapshot(),commands:O.commands.snapshot(),communicationStyles:O.communicationStyles?.snapshot()??GO.snapshot(),projectRules:O.projectRules?.snapshot()??p.empty($),hooks:R$("hooks"in O&&O.hooks?O.hooks:B0.getInstance().getConfig($))}}function S2(){for(let O of Q0.keys())I0(O);Q0.clear(),E.resetInstances(),k.resetInstance(),w.resetInstance(),f.resetInstance()}
|
|
636
|
+
export{i as Kc,c$ as Lc,W1 as Mc,P as Nc,IO as Oc,w as Pc,n0 as Qc,U0 as Rc,f as Sc,j0 as Tc,t0 as Uc,I0 as Vc,p as Wc,N$ as Xc,V2 as Yc,H2 as Zc,U2 as _c,S2 as $c};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import"./chunk-
|
|
2
|
-
`);try{let{rebuildProjectionIndex:z}=await import("./chunk-
|
|
1
|
+
import"./chunk-9p5f3ryf.js";import"./chunk-qda309d9.js";import{$d as J}from"./chunk-bn148dr5.js";import"./chunk-tq52pbde.js";import"./chunk-79s052sw.js";import{re as H}from"./chunk-epj56s0n.js";import"./chunk-gxzbv67r.js";import"./chunk-bexgcc2d.js";import"./chunk-jqwntnp1.js";import"./chunk-z906d5tb.js";import"./chunk-948qe2pp.js";import"./chunk-0n97186m.js";import{Ve as B}from"./chunk-9ffr01c3.js";var O={command:"doctor",describe:"Check the health of your Blade installation",builder:(D)=>D.option("rebuild-index",{type:"boolean",default:!1,describe:"Rebuild the SQLite session index from JSONL transcripts"}),handler:async(D)=>{if(D.rebuildIndex){console.log(`Rebuilding session index from JSONL transcripts...
|
|
2
|
+
`);try{let{rebuildProjectionIndex:z}=await import("./chunk-t8x2qpe2.js"),{SessionService:F}=await import("./chunk-8th744ef.js"),G=await z(F.projectionDeriverForSearch());if(G===null)console.log("[WARN] Session index unavailable (SQLite driver missing); reads fall back to JSONL scan.");else console.log(`[OK] Session index rebuilt: ${G} session(s) indexed.`)}catch(z){console.log("[FAIL] Session index rebuild failed"),console.log(` Error: ${z instanceof Error?z.message:"未知错误"}`),process.exit(1)}return}console.log(`Running Blade health check...
|
|
3
3
|
`);let A=0;try{await J.getInstance().initialize(),console.log("[OK] Configuration: OK")}catch(z){console.log("[FAIL] Configuration: FAILED"),console.log(` Error: ${z instanceof Error?z.message:"未知错误"}`),A++}let E=process.version;if(parseInt(E.slice(1).split(".")[0])>=18)console.log(`[OK] Node.js version: ${E}`);else console.log(`[WARN] Node.js version: ${E} (recommended: v18+)`),A++;try{let z=await import("fs/promises"),F=H();await z.access(F,(await import("fs")).constants.R_OK|(await import("fs")).constants.W_OK),console.log("[OK] File system permissions: OK")}catch(z){console.log("[FAIL] File system permissions: FAILED"),console.log(" Error: Cannot read/write in current directory"),A++}try{await import("ink"),console.log("[OK] Dependencies: OK")}catch(z){console.log("[FAIL] Dependencies: FAILED"),console.log(" Error: Missing required dependencies"),A++}if(console.log(`
|
|
4
4
|
Health Check Summary:`),A===0)console.log("All checks passed! Blade is ready to use.");else console.log(`Warning: Found ${A} issue(s). Please resolve them for optimal performance.`),process.exit(1)}};export{O as doctorCommands};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{ia as H}from"./chunk-
|
|
1
|
+
import{ia as H}from"./chunk-wje6bck2.js";import"./chunk-jza1rsan.js";import{oc as $,uc as T}from"./chunk-7hqbwpt5.js";import"./chunk-9p5f3ryf.js";import"./chunk-fvhc1yj8.js";import"./chunk-vwq9s5nj.js";import"./chunk-qm11hjd3.js";import"./chunk-qda309d9.js";import"./chunk-bn148dr5.js";import"./chunk-tq52pbde.js";import"./chunk-79s052sw.js";import{re as k}from"./chunk-epj56s0n.js";import"./chunk-gxzbv67r.js";import"./chunk-bexgcc2d.js";import"./chunk-jqwntnp1.js";import"./chunk-z906d5tb.js";import"./chunk-948qe2pp.js";import"./chunk-0n97186m.js";import"./chunk-9ffr01c3.js";import U from"os";import J from"path";function X(z){return typeof z==="string"?z:void 0}function N(z){if(!Array.isArray(z))return;let q=[];for(let K of z)if(typeof K==="string")q.push(K);else if(K!=null)q.push(String(K));return q}function b(){console.log(`
|
|
2
2
|
blade mcp
|
|
3
3
|
`),console.log(`管理 MCP 服务器
|
|
4
4
|
`),console.log("Commands:"),console.log(" blade mcp add <name> <commandOrUrl> [args...] 添加 MCP 服务器"),console.log(" blade mcp remove <name> 删除 MCP 服务器 [aliases: rm]"),console.log(" blade mcp list 列出所有 MCP 服务器并检查健康状态 [aliases: ls]"),console.log(" blade mcp get <name> 获取服务器详情"),console.log(" blade mcp login <name> 登录远程 MCP OAuth"),console.log(" blade mcp logout <name> 清除远程 MCP OAuth 凭证"),console.log(` blade mcp add-json <name> <json> 从 JSON 字符串添加服务器
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Bb as a,Cb as b,Db as c,Eb as d,Fb as e,Gb as f,Hb as g,Ib as h}from"./chunk-an96c54z.js";import"./chunk-qda309d9.js";import"./chunk-0n97186m.js";import"./chunk-9ffr01c3.js";export{d as syncSession,e as syncAll,h as searchProjectionText,c as resetProjectionDbCache,f as removeSessionFromProjection,g as rebuildProjectionIndex,b as getProjectionDb,a as getIndexDbPath};
|