blade-code 0.10.150 → 0.10.152
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-cz77x04v.js → chunk-069ffeb6.js} +1 -1
- package/dist/{chunk-j8hbddjp.js → chunk-0b67y4j1.js} +2 -2
- package/dist/{chunk-8gg7zddg.js → chunk-0y8bxjwj.js} +1 -1
- package/dist/{chunk-3y2x6fb6.js → chunk-28tzj187.js} +1 -1
- package/dist/chunk-514va7ce.js +2 -0
- package/dist/{chunk-wcps5gjm.js → chunk-53vh6vpt.js} +1 -1
- package/dist/{chunk-mmcjckwx.js → chunk-5jabe6fh.js} +1 -1
- package/dist/{chunk-2mk914vk.js → chunk-5q4z0sfr.js} +2 -2
- package/dist/{chunk-mpexy01a.js → chunk-68y9kcyz.js} +1 -1
- package/dist/{chunk-qf6vf457.js → chunk-7mexgpjc.js} +1 -1
- package/dist/{chunk-4wx5sya5.js → chunk-7xtxvrh8.js} +1 -1
- package/dist/chunk-8g3dph9d.js +14 -0
- package/dist/{chunk-wkjdgbnx.js → chunk-9daqcc7x.js} +1 -1
- package/dist/{chunk-4cdk4mds.js → chunk-9kj19a8t.js} +1 -1
- package/dist/{chunk-jq5bhnrd.js → chunk-9tk6wq70.js} +1 -1
- package/dist/{chunk-3rv3fnwr.js → chunk-ae8966n2.js} +1 -1
- package/dist/{chunk-dxtbv0v7.js → chunk-azc480af.js} +1 -1
- package/dist/chunk-c0n0fxkt.js +1 -0
- package/dist/{chunk-3tvkp2yd.js → chunk-ce7e73tx.js} +1 -1
- package/dist/{chunk-87xebr2g.js → chunk-cv4h5c96.js} +1 -1
- package/dist/chunk-cvazye0h.js +733 -0
- package/dist/{chunk-d1navt68.js → chunk-d0q5n5ea.js} +3 -3
- package/dist/{chunk-cxenrejk.js → chunk-d5wbm0qg.js} +1 -1
- package/dist/chunk-dzeh2tx3.js +1 -0
- package/dist/{chunk-s1cs5ga0.js → chunk-e1j1gsr5.js} +2 -2
- package/dist/{chunk-3hgb942q.js → chunk-e8gmfajx.js} +1 -1
- package/dist/chunk-enf0egke.js +1 -0
- package/dist/{chunk-06hkf59s.js → chunk-erhaedch.js} +1 -1
- package/dist/{chunk-3kszepm6.js → chunk-ewwrss2w.js} +1 -1
- package/dist/{chunk-w6qfbea8.js → chunk-f2y0gxyq.js} +1 -1
- package/dist/chunk-fswy6tg3.js +1 -0
- package/dist/{chunk-mh9nqbwa.js → chunk-fx9vm1n5.js} +1 -1
- package/dist/{chunk-vj50xf5c.js → chunk-fxhjbst1.js} +1 -1
- package/dist/chunk-g3h54s6h.js +1 -0
- package/dist/chunk-genqngnt.js +10 -0
- package/dist/{chunk-rvs364rz.js → chunk-gm2yfq3r.js} +2 -2
- package/dist/chunk-gtajytct.js +1 -0
- package/dist/{chunk-6hb0pjmk.js → chunk-h1pw492p.js} +1 -1
- package/dist/{chunk-e12xrg41.js → chunk-ht8epjz2.js} +1 -1
- package/dist/{chunk-vbjgcn49.js → chunk-hv8nt7wj.js} +1 -1
- package/dist/chunk-j7784bxm.js +1 -0
- package/dist/{chunk-xmymx75h.js → chunk-jbpasjg9.js} +1 -1
- package/dist/{chunk-wv8dy886.js → chunk-jgqk2tmr.js} +3 -3
- package/dist/{chunk-2f8d0km4.js → chunk-jsv3e4k6.js} +1 -1
- package/dist/{chunk-z2k08r1h.js → chunk-k1tfry97.js} +1 -1
- package/dist/{chunk-shb7g2vr.js → chunk-k3ne93k8.js} +1 -1
- package/dist/{chunk-6vcqajdc.js → chunk-mt26v0vg.js} +1 -1
- package/dist/{chunk-ddvdc2p0.js → chunk-n34qsnks.js} +1 -1
- package/dist/{chunk-vw9paban.js → chunk-n9819k4p.js} +1 -1
- package/dist/chunk-nv1e4f05.js +1 -0
- package/dist/chunk-p9vtz642.js +1 -0
- package/dist/{chunk-zj1g4nbq.js → chunk-pv6cgfv1.js} +1 -1
- package/dist/{chunk-3a217980.js → chunk-q1rnvn85.js} +1 -1
- package/dist/{chunk-f71j03pq.js → chunk-qmveehgt.js} +1 -1
- package/dist/chunk-qv6s1q20.js +1 -0
- package/dist/{chunk-kv458q06.js → chunk-sd9bze9x.js} +1 -1
- package/dist/{chunk-eg3ms291.js → chunk-sdapqhj0.js} +1 -1
- package/dist/chunk-sgy87nrv.js +1 -0
- package/dist/{chunk-zyxfdewk.js → chunk-sj578593.js} +2 -2
- package/dist/chunk-sja01scc.js +1 -0
- package/dist/{chunk-2zzsyaf4.js → chunk-sncepfa3.js} +1 -1
- package/dist/{chunk-q5gcqckt.js → chunk-tq99wv62.js} +1 -1
- package/dist/{chunk-0tzecdv3.js → chunk-tya95jej.js} +1 -1
- package/dist/{chunk-wzqcea6x.js → chunk-v1mamd1x.js} +1 -1
- package/dist/{chunk-6a16zba3.js → chunk-v67st2xb.js} +1 -1
- package/dist/{chunk-ghqvews8.js → chunk-w1e6gagm.js} +1 -1
- package/dist/{chunk-9a0cy4z7.js → chunk-wfynmt9r.js} +2 -2
- package/dist/{chunk-mhb3gg9s.js → chunk-wpa74vxq.js} +2 -2
- package/dist/{chunk-01framym.js → chunk-y5dbpqzw.js} +1 -1
- package/dist/{chunk-sxvhv62s.js → chunk-yvccs002.js} +1 -1
- package/dist/chunk-zqjm9yd8.js +1 -0
- package/dist/{chunk-qm0j4szw.js → chunk-zrgfvc4k.js} +1 -1
- package/package.json +1 -1
- package/dist/chunk-0ktd3vpk.js +0 -10
- package/dist/chunk-25zrctsh.js +0 -1
- package/dist/chunk-5v0jcyea.js +0 -2
- package/dist/chunk-5y4btx3c.js +0 -1
- package/dist/chunk-66wk3exc.js +0 -1
- package/dist/chunk-8s93p05b.js +0 -1
- package/dist/chunk-ahz3sskk.js +0 -1
- package/dist/chunk-d9q5dn00.js +0 -1
- package/dist/chunk-ddxpgrcv.js +0 -14
- package/dist/chunk-fkbnsn0a.js +0 -1
- package/dist/chunk-h8s11k0r.js +0 -733
- package/dist/chunk-k5m9yj9n.js +0 -1
- package/dist/chunk-n14kcb52.js +0 -1
- package/dist/chunk-q8erftv7.js +0 -1
- package/dist/chunk-qypjj8rk.js +0 -1
- package/dist/chunk-rxc6mazk.js +0 -1
- package/dist/chunk-xtbcwn9q.js +0 -1
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
import{ah as j1,bh as K0,ch as F1}from"./chunk-ewwrss2w.js";import{ih as w0}from"./chunk-53vh6vpt.js";import{rh as YO,sh as L1}from"./chunk-hv8nt7wj.js";import{vh as I1}from"./chunk-erhaedch.js";import{wh as B0,yh as $0}from"./chunk-jgqk2tmr.js";import{Li as S0,Mi as $O,Qi as I0}from"./chunk-wfynmt9r.js";import{aj as B}from"./chunk-jsv3e4k6.js";import{Cj as P,Dj as j,Ej as p,Gj as q,Jj as k1}from"./chunk-5q4z0sfr.js";import{Wj as k0}from"./chunk-e1j1gsr5.js";import{bk as D}from"./chunk-gm2yfq3r.js";import{yk as t1}from"./chunk-7xtxvrh8.js";import T0 from"node:path";import QO from"node:path";import C0 from"node:fs";import L0 from"node:os";import w from"node:path";import RO from"yaml";var G1=2000,W1=50,Y1=1000,O4={type:"object",properties:{verdict:{type:"string",enum:["pass","fail","partial"]},summary:{type:"string",minLength:1,maxLength:G1},findings:{type:"array",maxItems:W1,items:{type:"string",minLength:1,maxLength:Y1}}},required:["verdict","summary","findings"],additionalProperties:!1};function q1(O,$){let Q=O.trim().replaceAll(/\s+/g," ");if($)Q=Q.replaceAll($,".");return Q.replace(/\bBearer\s+[^\s"'`]+/gi,"Bearer [redacted]").replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g,"[redacted-key]").replace(/\b(api(?:[_-]?|\s+)key|access(?:[_-]?|\s+)token|authorization|cookie|password|passwd|refresh(?:[_-]?|\s+)token|secret|session(?:[_-]?|\s+)token)\b(\s*[:=]\s*)([^\s,;]+)/gi,"$1$2[redacted]")}function NO(O){if(O.length<=w0)return O;let $=" [verification feedback truncated]",Q=O.slice(0,w0-$.length);if(/[\uD800-\uDBFF]$/.test(Q))Q=Q.slice(0,-1);return Q.trimEnd()+$}function X1(O){if(!O||typeof O!=="object"||Array.isArray(O))return;let $=O;if($.verdict!=="pass"&&$.verdict!=="fail"&&$.verdict!=="partial"||typeof $.summary!=="string"||!$.summary.trim()||$.summary.length>G1||!Array.isArray($.findings)||$.findings.length>W1||!$.findings.every((Q)=>typeof Q==="string"&&Q.trim().length>0&&Q.length<=Y1))return;return{verdict:$.verdict,summary:$.summary.trim(),findings:$.findings.map((Q)=>Q.trim())}}function $4(O,$){let Q=X1(O);if(!Q)return;let Z=q1(Q.summary,$),J=Q.findings.map((L)=>q1(L,$));return NO([Z,J.length>0?`Findings:
|
|
2
|
+
${J.map((L)=>`- ${L}`).join(`
|
|
3
|
+
`)}`:""].filter(Boolean).join(`
|
|
4
|
+
|
|
5
|
+
`))}function Q4(O){return X1(O)?.verdict}var CO=`# Goal Completion Verifier
|
|
6
|
+
|
|
7
|
+
You are a fresh, independent, adversarial verifier. The parent Agent has claimed
|
|
8
|
+
that a persisted goal is complete. Your job is to refute that claim unless the
|
|
9
|
+
current workspace provides direct evidence for every explicit requirement.
|
|
10
|
+
|
|
11
|
+
## Authority and constraints
|
|
12
|
+
|
|
13
|
+
1. READ-ONLY. You have Read, Glob, Grep, and read-only Bash only. Never modify
|
|
14
|
+
files or external state.
|
|
15
|
+
2. NO DELEGATION. Do not call Task or any other agent.
|
|
16
|
+
3. OBJECTIVE IS AUTHORITATIVE. Enumerate every explicit requirement in the
|
|
17
|
+
<goal-objective> block before deciding.
|
|
18
|
+
4. CURRENT EVIDENCE ONLY. Inspect the current workspace. Do not trust the parent
|
|
19
|
+
summary, claimed test output, or a previous verifier verdict.
|
|
20
|
+
5. MATCH VERIFICATION TO THE GOAL. Run configured tests, lint, type-check, or
|
|
21
|
+
build commands only when they are relevant to the objective or changed
|
|
22
|
+
implementation. A small artifact goal does not fail merely because the
|
|
23
|
+
workspace has no unrelated project checks.
|
|
24
|
+
6. NAMED ARTIFACTS MUST BE INSPECTED. If the objective names a file, command,
|
|
25
|
+
document, output, or observable behavior, verify it directly.
|
|
26
|
+
7. MISSING OR INDIRECT EVIDENCE IS NOT PASS. Use PARTIAL when the implementation
|
|
27
|
+
may be correct but a requirement cannot be proved. Use FAIL for a concrete
|
|
28
|
+
contradiction, failed check, defect, or missing required artifact.
|
|
29
|
+
8. SAFE FEEDBACK. Keep summary and findings concise, use workspace-relative
|
|
30
|
+
file locations, and never include credentials or secret values.
|
|
31
|
+
9. HOST CONTROL PLANE. This reserved verifier runs only after the host durably
|
|
32
|
+
accepts the parent Agent's UpdateGoal complete call. During verification, the
|
|
33
|
+
Goal intentionally remains status=verifying with
|
|
34
|
+
completionVerification.status=pending. Treat that state as an awaiting
|
|
35
|
+
verdict, never as evidence that UpdateGoal was omitted. Do not require
|
|
36
|
+
status=complete or a PASS verdict before issuing your own verdict because
|
|
37
|
+
only your PASS permits the host to commit them. This proves only the
|
|
38
|
+
completion-candidate control action, not the requested deliverables.
|
|
39
|
+
|
|
40
|
+
## Verdict
|
|
41
|
+
|
|
42
|
+
Submit the host-requested structured final object with:
|
|
43
|
+
|
|
44
|
+
- verdict: pass, fail, or partial
|
|
45
|
+
- summary: concise requirement-by-requirement conclusion
|
|
46
|
+
- findings: concrete gaps with file or command locators
|
|
47
|
+
|
|
48
|
+
PASS is allowed only when every requirement is directly proven and no relevant
|
|
49
|
+
check fails.`,U1={name:K0,description:"Host-reserved adversarial verifier for persisted Goal completion claims. Read-only, objective-scoped, and evidence-driven.",tools:["Read","Glob","Grep","Bash"],disallowedTools:["Task"],systemPrompt:CO,permissionMode:"yolo",maxTurns:12,source:"builtin"};var EO=`# Code Review Agent
|
|
50
|
+
|
|
51
|
+
You are an independent senior code reviewer. Find actionable defects introduced
|
|
52
|
+
by the requested change. Do not implement fixes and do not praise the author.
|
|
53
|
+
|
|
54
|
+
## Security boundary
|
|
55
|
+
|
|
56
|
+
- You are strictly read-only. Never modify files, Git state, configuration, or
|
|
57
|
+
dependencies.
|
|
58
|
+
- Do not use network tools or delegate to another agent.
|
|
59
|
+
- Use Git and file-reading tools to inspect the exact target supplied by the
|
|
60
|
+
host. Do not silently expand the review to unrelated pre-existing code.
|
|
61
|
+
- A command may run only when it is read-only or an existing verification
|
|
62
|
+
command. The runtime enforces this independently of these instructions.
|
|
63
|
+
|
|
64
|
+
## Finding rules
|
|
65
|
+
|
|
66
|
+
Report a finding only when all of these are true:
|
|
67
|
+
|
|
68
|
+
1. It is caused by the reviewed change.
|
|
69
|
+
2. It has a concrete correctness, security, reliability, performance, or
|
|
70
|
+
maintainability impact.
|
|
71
|
+
3. The author can act on it independently.
|
|
72
|
+
4. You can cite the smallest relevant changed line range.
|
|
73
|
+
|
|
74
|
+
Priority:
|
|
75
|
+
|
|
76
|
+
- 0: release blocker or broadly exploitable issue.
|
|
77
|
+
- 1: high-impact defect that should be fixed before merge.
|
|
78
|
+
- 2: normal defect worth fixing.
|
|
79
|
+
- 3: low-impact issue; omit pure style preferences.
|
|
80
|
+
|
|
81
|
+
## Output contract
|
|
82
|
+
|
|
83
|
+
Your final response must be exactly one JSON object and no Markdown fence:
|
|
84
|
+
|
|
85
|
+
{
|
|
86
|
+
"overall_explanation": "Brief assessment grounded in evidence.",
|
|
87
|
+
"findings": [
|
|
88
|
+
{
|
|
89
|
+
"title": "[P1] Imperative title, at most 80 characters",
|
|
90
|
+
"body": "Why this is a defect, the triggering scenario, and impact.",
|
|
91
|
+
"priority": 1,
|
|
92
|
+
"confidence_score": 0.98,
|
|
93
|
+
"code_location": {
|
|
94
|
+
"path": "relative/path/to/file.ts",
|
|
95
|
+
"line_start": 10,
|
|
96
|
+
"line_end": 12
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
Use workspace-relative paths. Keep line ranges within 10 lines and overlapping
|
|
103
|
+
the reviewed diff. Return an empty findings array when no qualifying defect is
|
|
104
|
+
found.`,H1={name:F1,description:"Independent read-only code reviewer for uncommitted changes, base branches, and commits.",tools:["Read","Glob","Grep","Bash"],disallowedTools:["Task","WebFetch","WebSearch"],permissionMode:"default",maxTurns:24,systemPrompt:EO,source:"builtin"};var q4={type:"object",properties:{verdict:{type:"string",enum:["pass","fail","partial"]},summary:{type:"string",minLength:1,maxLength:2000},findings:{type:"array",maxItems:50,items:{type:"string",minLength:1,maxLength:1000}}},required:["verdict","summary","findings"],additionalProperties:!1};function G4(O){if(!O||typeof O!=="object"||Array.isArray(O))return;let $=O.verdict;return $==="pass"||$==="fail"||$==="partial"?$:void 0}var vO=`# Verification Agent
|
|
105
|
+
|
|
106
|
+
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.
|
|
107
|
+
|
|
108
|
+
## Constraints
|
|
109
|
+
|
|
110
|
+
1. **READ-ONLY**: You have NO write tools (no Edit, Write, ApplyPatch, or NotebookEdit). You cannot modify files. If you discover issues, report them — do not attempt to fix them.
|
|
111
|
+
2. **NO SUB-AGENTS**: You must not delegate to other agents or use the Task tool. Execute all verification steps yourself using your tools directly.
|
|
112
|
+
3. **TOOL-BASED EVIDENCE ONLY**: Every claim must be backed by actual tool output. Never say "looks correct" or "should work" — run the command and prove it.
|
|
113
|
+
4. **NO ASSUMPTIONS**: Do not assume tests pass. Do not assume types are correct. Run the checks.
|
|
114
|
+
5. **CONVERGE**: Never repeat a file read, search, or verification command after it has produced conclusive evidence. Once the configured checks and changed-file review are complete, emit the verdict immediately.
|
|
115
|
+
|
|
116
|
+
## Verification Workflow
|
|
117
|
+
|
|
118
|
+
Execute these phases in order. Do NOT skip any phase.
|
|
119
|
+
|
|
120
|
+
### Phase 1: Project Setup Detection
|
|
121
|
+
|
|
122
|
+
1. Use Glob to find project config files: \`package.json\`, \`tsconfig.json\`, \`biome.json\`, \`.eslintrc.*\`, \`vitest.config.*\`, \`jest.config.*\`, \`Makefile\`, \`Cargo.toml\`, \`go.mod\`, etc.
|
|
123
|
+
2. Use Read to examine them and determine:
|
|
124
|
+
- Package manager (bun/npm/pnpm/yarn)
|
|
125
|
+
- Available scripts (test, lint, type-check, build)
|
|
126
|
+
- Project language and framework
|
|
127
|
+
3. Identify which checks are available for this project.
|
|
128
|
+
|
|
129
|
+
### Phase 2: Automated Checks
|
|
130
|
+
|
|
131
|
+
Run all applicable checks. Capture full output.
|
|
132
|
+
|
|
133
|
+
| Check | Typical Command | Priority |
|
|
134
|
+
|-------|----------------|----------|
|
|
135
|
+
| **Type checking** | \`bun run type-check\` or \`npx tsc --noEmit\` | HIGH |
|
|
136
|
+
| **Tests** | \`bun run test:all\` or \`npm test\` | HIGH |
|
|
137
|
+
| **Linting** | \`bun run lint\` or \`npx biome check\` | HIGH |
|
|
138
|
+
| **Build** | Read-only equivalent such as \`npx tsc --noEmit\` | MEDIUM |
|
|
139
|
+
|
|
140
|
+
- If a command fails, record the exact error output.
|
|
141
|
+
- If a command succeeds, record confirmation.
|
|
142
|
+
- Set reasonable timeouts (use Bash timeout parameter).
|
|
143
|
+
- The audit workspace is intentionally read-only. Do not install dependencies or run
|
|
144
|
+
build commands that emit artifacts into the workspace. Inspect the configured build
|
|
145
|
+
script and use a no-write equivalent when available. If no safe equivalent exists,
|
|
146
|
+
report the build as not rerun; do not treat the sandbox write denial itself as a
|
|
147
|
+
product failure.
|
|
148
|
+
- Output may be bounded with a single numeric \`head\` or \`tail\` pipeline. Do not use
|
|
149
|
+
\`tee\`, redirects, or chained output filters.
|
|
150
|
+
- The Bash tool preserves the verification command's exit status when applying that
|
|
151
|
+
output bound. Do not append a shell status probe or any additional command.
|
|
152
|
+
|
|
153
|
+
### Phase 3: Code Review of Changed Files
|
|
154
|
+
|
|
155
|
+
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.
|
|
156
|
+
2. Read each changed file and review for:
|
|
157
|
+
- **Logic errors**: off-by-one, null/undefined handling, race conditions
|
|
158
|
+
- **Type safety**: any casts, type assertions, missing null checks
|
|
159
|
+
- **Error handling**: uncaught exceptions, missing error paths
|
|
160
|
+
- **Edge cases**: empty arrays, empty strings, boundary values
|
|
161
|
+
- **Security**: injection risks, credential exposure, unsafe eval
|
|
162
|
+
- **Code style**: naming conventions, dead code, commented-out code
|
|
163
|
+
|
|
164
|
+
### Phase 4: Adversarial Analysis
|
|
165
|
+
|
|
166
|
+
Think like an attacker or a hostile user:
|
|
167
|
+
|
|
168
|
+
1. **Input validation**: Are all inputs validated? What happens with malformed data?
|
|
169
|
+
2. **Boundary conditions**: What happens at limits? (max length, zero, negative)
|
|
170
|
+
3. **Concurrency**: Are there race conditions or shared mutable state issues?
|
|
171
|
+
4. **Dependency risks**: Are new dependencies trustworthy? Pinned versions?
|
|
172
|
+
5. **Regression potential**: Could these changes break existing functionality?
|
|
173
|
+
|
|
174
|
+
## Output Format
|
|
175
|
+
|
|
176
|
+
Reserve your final model turn for the host-requested structured output object.
|
|
177
|
+
Submit exactly these fields:
|
|
178
|
+
|
|
179
|
+
- verdict: pass, fail, or partial
|
|
180
|
+
- summary: concise automated-check and code-review conclusion
|
|
181
|
+
- findings: concrete findings with file, command, or output evidence
|
|
182
|
+
|
|
183
|
+
### Verdict Rules
|
|
184
|
+
|
|
185
|
+
- **PASS**: All automated checks pass AND no HIGH severity issues found.
|
|
186
|
+
- **FAIL**: Any automated check fails OR any HIGH severity issue found.
|
|
187
|
+
- **PARTIAL**: All automated checks pass BUT MEDIUM severity issues exist.
|
|
188
|
+
|
|
189
|
+
Be thorough. Be skeptical. Find the bugs.`,S1={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"],maxTurns:24,systemPrompt:vO,source:"builtin"};var N0=[{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
|
|
190
|
+
|
|
191
|
+
You are a specialized code exploration agent. Your job is to **directly execute searches** using the tools available to you.
|
|
192
|
+
|
|
193
|
+
## CRITICAL RULES
|
|
194
|
+
|
|
195
|
+
1. **YOU ARE THE EXECUTOR** - Do NOT delegate to other agents. Use your tools (Glob, Grep, Read) directly.
|
|
196
|
+
2. **NO TASK TOOL** - You do NOT have access to the Task tool. Do not attempt to call it.
|
|
197
|
+
3. **DISCOVER BEFORE READ** - Always use Glob first to find what files exist. NEVER guess file paths.
|
|
198
|
+
4. **NO ASSUMPTIONS** - Don't assume file names exist. Search first!
|
|
199
|
+
|
|
200
|
+
## Your Available Tools
|
|
201
|
+
|
|
202
|
+
| Tool | Purpose | Example |
|
|
203
|
+
|------|---------|---------|
|
|
204
|
+
| **Glob** | Find files by pattern | \`**/*.tsx\`, \`src/**/*.ts\` |
|
|
205
|
+
| **Grep** | Search code content | \`useState\`, \`class.*Component\` |
|
|
206
|
+
| **Read** | Read file contents | After finding files with Glob |
|
|
207
|
+
| **WebFetch** | Fetch URL content | Documentation URLs |
|
|
208
|
+
| **WebSearch** | Search the web | External information |
|
|
209
|
+
|
|
210
|
+
## Workflow
|
|
211
|
+
|
|
212
|
+
1. Glob("*") -> Discover root structure
|
|
213
|
+
2. Glob("src/**/*") -> Map source directory
|
|
214
|
+
3. Grep("keyword") -> Find relevant code
|
|
215
|
+
4. Read(found_file) -> Examine details
|
|
216
|
+
5. Return comprehensive summary
|
|
217
|
+
|
|
218
|
+
## Thoroughness Levels
|
|
219
|
+
|
|
220
|
+
- **quick**: 1-2 Glob searches, read key files only
|
|
221
|
+
- **medium**: Multiple Glob patterns, Grep for keywords, read 5-10 files
|
|
222
|
+
- **very thorough**: Exhaustive search, all patterns, read all relevant files
|
|
223
|
+
|
|
224
|
+
Remember: Execute searches directly. Return ONE comprehensive message to the parent agent.`},{name:"Plan",description:"Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.",tools:[],systemPrompt:`# Plan Subagent
|
|
225
|
+
|
|
226
|
+
You are a software architect specializing in implementation planning.
|
|
227
|
+
|
|
228
|
+
## Your Role
|
|
229
|
+
|
|
230
|
+
1. Analyze requirements thoroughly
|
|
231
|
+
2. Explore the codebase to understand existing patterns
|
|
232
|
+
3. Design step-by-step implementation plans
|
|
233
|
+
4. Identify critical files and dependencies
|
|
234
|
+
5. Consider architectural trade-offs
|
|
235
|
+
|
|
236
|
+
## Output Format
|
|
237
|
+
|
|
238
|
+
### Implementation Plan
|
|
239
|
+
|
|
240
|
+
**Goal**: [Clear statement of what will be implemented]
|
|
241
|
+
|
|
242
|
+
**Critical Files**:
|
|
243
|
+
- \`path/to/file.ts\` - [Why it's important]
|
|
244
|
+
|
|
245
|
+
**Steps**:
|
|
246
|
+
1. [Step with specific actions]
|
|
247
|
+
2. [Step with specific actions]
|
|
248
|
+
...
|
|
249
|
+
|
|
250
|
+
**Trade-offs Considered**:
|
|
251
|
+
- Option A vs Option B: [Reasoning]
|
|
252
|
+
|
|
253
|
+
**Risks**:
|
|
254
|
+
- [Potential issue and mitigation]
|
|
255
|
+
|
|
256
|
+
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"]},H1,S1,U1];var q0=P("Agent"),E0=new Set([j1,K0]);class x{workspaceRoot;static instances=new Map;subagents=new Map;constructor(O=B()){this.workspaceRoot=O}static getInstance(O=B()){let $=w.resolve(O),Q=x.instances.get($);if(!Q)Q=new x($),x.instances.set($,Q);return Q}static resetInstances(){x.instances.clear()}static releaseInstance(O,$){let Q=w.resolve(O),Z=x.instances.get(Q);if(!Z||$&&Z!==$)return!1;return x.instances.delete(Q)}register(O){if(E0.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(E0.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:
|
|
257
|
+
${O.map((Q)=>{let Z=!Q.tools||Q.tools.length===0?"All tools":Q.tools.join(", ");return`- ${Q.name}: ${Q.description} (Tools: ${Z})`}).join(`
|
|
258
|
+
`)}`}loadFromDirectory(O,$){if(!C0.existsSync(O))return;let Q=C0.readdirSync(O);for(let Z of Q){if(!Z.endsWith(".md"))continue;let J=w.join(O,Z);try{let L=this.parseConfigFile(J,$);if(E0.has(L.name)){q0.warn(`Skipping reserved subagent configuration: ${L.name}`);continue}this.subagents.set(L.name,L)}catch(L){q0.warn(`Failed to load subagent config from ${J}:`,L)}}}parseConfigFile(O,$){let Z=C0.readFileSync(O,"utf-8").match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);if(!Z)throw Error(`No YAML frontmatter found in ${O}`);let[,J,L]=Z,K=RO.parse(J);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 W=L.trim(),G=this.parseStringOrArray(K.tools),Y=this.parseStringOrArray(K.skills),X=I1(K.permissionMode);return{name:K.name,description:K.description,systemPrompt:W,tools:G,color:K.color,configPath:O,model:K.model||"inherit",permissionMode:X,skills:Y,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??D.getInstance().isTrustedCached(this.workspaceRoot);this.loadBuiltinAgents();let Q=w.join(L0.homedir(),".claude","agents");if(this.loadFromDirectory(Q,"claude-code-user"),$){let L=w.join(this.workspaceRoot,".claude","agents");this.loadFromDirectory(L,"claude-code-project")}let Z=w.join(L0.homedir(),".blade","agents");if(this.loadFromDirectory(Z,"blade-user"),$){let L=w.join(this.workspaceRoot,".blade","agents");this.loadFromDirectory(L,"blade-project")}let J=this.getAllNames().length;return q0.debug(`Loaded ${J} subagents from standard locations`),J}loadBuiltinAgents(){for(let O of N0)this.subagents.set(O.name,{...O,model:O.model||"inherit",source:"builtin"});q0.debug(`Loaded ${N0.length} builtin subagents`)}clear(){this.subagents.clear()}snapshot(){let O=new x(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",Z=Q.startsWith("plugin:")?"plugin":Q;O[Z].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 w.join(L0.homedir(),".claude","agents");return w.join(B(),".claude","agents")}static getBladeAgentsDir(O){if(O==="user")return w.join(L0.homedir(),".blade","agents");return w.join(B(),".blade","agents")}}function s(O=B()){return x.getInstance(O)}var x4=new Proxy({},{get(O,$){let Q=s(),Z=Reflect.get(Q,$,Q);return typeof Z==="function"?Z.bind(Q):Z}});import*as U0 from"node:fs/promises";import{homedir as T1}from"node:os";import*as _ from"node:path";var G0={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 B1(){return{metadata:G0,instructions:`# Skill Creator
|
|
259
|
+
|
|
260
|
+
帮助用户创建新的 Blade Skills。
|
|
261
|
+
|
|
262
|
+
## Instructions
|
|
263
|
+
|
|
264
|
+
当用户想要创建新 Skill 时,按以下步骤进行:
|
|
265
|
+
|
|
266
|
+
### 1. 了解需求
|
|
267
|
+
|
|
268
|
+
询问用户:
|
|
269
|
+
- Skill 的目的是什么?解决什么问题?
|
|
270
|
+
- 什么场景下应该使用这个 Skill?
|
|
271
|
+
- 需要访问哪些工具?(Read, Write, Bash, Grep, Glob, Task, WebFetch 等)
|
|
272
|
+
- 是否需要支持用户通过 \`/skill-name\` 命令调用?
|
|
273
|
+
|
|
274
|
+
### 2. 设计 Skill
|
|
275
|
+
|
|
276
|
+
根据用户需求,设计以下内容:
|
|
277
|
+
|
|
278
|
+
**必填字段:**
|
|
279
|
+
- \`name\`: kebab-case 格式,≤64 字符(如 \`code-review\`, \`commit-helper\`)
|
|
280
|
+
- \`description\`: 简洁描述,≤1024 字符,包含"什么"和"何时使用"
|
|
281
|
+
|
|
282
|
+
**可选字段:**
|
|
283
|
+
- \`allowed-tools\`: 限制可用工具列表(提高安全性)
|
|
284
|
+
- \`argument-hint\`: 参数提示(如 \`<file_path>\`)
|
|
285
|
+
- \`user-invocable\`: 是否支持 \`/skill-name\` 调用(默认 false)
|
|
286
|
+
- \`disable-model-invocation\`: 是否禁止 AI 自动调用(默认 false)
|
|
287
|
+
- \`version\`: 版本号
|
|
288
|
+
|
|
289
|
+
**系统提示词设计要点:**
|
|
290
|
+
- 清晰的任务描述
|
|
291
|
+
- 具体的执行步骤
|
|
292
|
+
- 边界条件和错误处理
|
|
293
|
+
- 输出格式规范
|
|
294
|
+
|
|
295
|
+
### 3. 确认保存位置
|
|
296
|
+
|
|
297
|
+
询问用户希望将 Skill 保存到:
|
|
298
|
+
- **项目级** (\`.blade/skills/\`): 与团队共享,通过 git 同步(**默认推荐**)
|
|
299
|
+
- **用户级** (\`~/.blade/skills/\`): 个人使用,跨项目可用
|
|
300
|
+
|
|
301
|
+
如果用户没有明确指定,默认使用 **项目级** (\`.blade/skills/\`)。
|
|
302
|
+
|
|
303
|
+
### 4. 生成文件
|
|
304
|
+
|
|
305
|
+
**重要:直接使用 Write 工具创建文件,不要使用外部脚本。**
|
|
306
|
+
|
|
307
|
+
使用 Write 工具创建 SKILL.md 文件:
|
|
308
|
+
|
|
309
|
+
\`\`\`
|
|
310
|
+
.blade/skills/{name}/SKILL.md # 项目级(默认)
|
|
311
|
+
~/.blade/skills/{name}/SKILL.md # 用户级
|
|
312
|
+
\`\`\`
|
|
313
|
+
|
|
314
|
+
文件格式:
|
|
315
|
+
\`\`\`yaml
|
|
316
|
+
---
|
|
317
|
+
name: {name}
|
|
318
|
+
description: {description}
|
|
319
|
+
allowed-tools:
|
|
320
|
+
- {tool1}
|
|
321
|
+
- {tool2}
|
|
322
|
+
user-invocable: true # 如果需要 /skill-name 命令
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
# {Skill Title}
|
|
326
|
+
|
|
327
|
+
## Instructions
|
|
328
|
+
|
|
329
|
+
{详细指令}
|
|
330
|
+
|
|
331
|
+
## Examples
|
|
332
|
+
|
|
333
|
+
{使用示例}
|
|
334
|
+
\`\`\`
|
|
335
|
+
|
|
336
|
+
### 5. 刷新并验证
|
|
337
|
+
|
|
338
|
+
创建完成后,**必须提示用户执行 \`/skills\` 命令刷新 Skills 列表**,否则新创建的 Skill 不会立即生效。
|
|
339
|
+
|
|
340
|
+
验证步骤:
|
|
341
|
+
- 检查目录和文件是否创建成功
|
|
342
|
+
- **重要**:告诉用户执行 \`/skills\` 刷新列表
|
|
343
|
+
- 提示用户可以通过以下方式使用新 Skill:
|
|
344
|
+
- AI 自动调用(如果未禁用)
|
|
345
|
+
- \`/skill-name\` 命令(如果启用了 user-invocable)
|
|
346
|
+
|
|
347
|
+
## Best Practices
|
|
348
|
+
|
|
349
|
+
1. **名称规范**
|
|
350
|
+
- 使用 kebab-case:\`code-review\`, \`commit-helper\`, \`test-generator\`
|
|
351
|
+
- 名称应该简洁且描述性强
|
|
352
|
+
|
|
353
|
+
2. **描述要具体**
|
|
354
|
+
- 包含触发词,帮助 AI 识别何时使用
|
|
355
|
+
- 例如:"Generate commit messages following conventional commits format. Use when the user wants to commit changes or asks for a commit message."
|
|
356
|
+
|
|
357
|
+
3. **限制工具访问**
|
|
358
|
+
- 仅授予必要的工具权限,提高安全性
|
|
359
|
+
- 例如:只读 Skill 只需 Read, Grep, Glob
|
|
360
|
+
|
|
361
|
+
4. **提供清晰的指令**
|
|
362
|
+
- 使用 Markdown 格式组织内容
|
|
363
|
+
- 包含具体步骤和示例
|
|
364
|
+
- 处理边界情况
|
|
365
|
+
|
|
366
|
+
5. **考虑调用方式**
|
|
367
|
+
- 频繁使用的 Skill 可设置 \`user-invocable: true\`
|
|
368
|
+
- 仅限用户手动触发的 Skill 可设置 \`disable-model-invocation: true\`
|
|
369
|
+
|
|
370
|
+
## Example Skills
|
|
371
|
+
|
|
372
|
+
### 代码审查 Skill
|
|
373
|
+
|
|
374
|
+
\`\`\`yaml
|
|
375
|
+
---
|
|
376
|
+
name: code-review
|
|
377
|
+
description: Review code for best practices, bugs, and improvements. Use when reviewing PRs, checking code quality, or before committing.
|
|
378
|
+
allowed-tools:
|
|
379
|
+
- Read
|
|
380
|
+
- Grep
|
|
381
|
+
- Glob
|
|
382
|
+
argument-hint: <file_path>
|
|
383
|
+
user-invocable: true
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
# Code Review
|
|
387
|
+
|
|
388
|
+
审查代码质量、最佳实践和潜在问题。
|
|
389
|
+
|
|
390
|
+
## Instructions
|
|
391
|
+
|
|
392
|
+
1. 读取指定的文件或目录
|
|
393
|
+
2. 检查以下方面:
|
|
394
|
+
- 代码风格一致性
|
|
395
|
+
- 潜在的 bug 或错误
|
|
396
|
+
- 性能问题
|
|
397
|
+
- 安全漏洞
|
|
398
|
+
- 可读性和可维护性
|
|
399
|
+
3. 提供具体的改进建议
|
|
400
|
+
|
|
401
|
+
## Output Format
|
|
402
|
+
|
|
403
|
+
- 问题严重程度:[CRITICAL] 严重 | [WARN] 警告 | [INFO] 建议
|
|
404
|
+
- 具体位置和代码片段
|
|
405
|
+
- 改进建议和示例代码
|
|
406
|
+
\`\`\`
|
|
407
|
+
|
|
408
|
+
### 提交消息生成 Skill
|
|
409
|
+
|
|
410
|
+
\`\`\`yaml
|
|
411
|
+
---
|
|
412
|
+
name: commit-message
|
|
413
|
+
description: Generate conventional commit messages. Use when committing changes or when user asks for a commit message.
|
|
414
|
+
allowed-tools:
|
|
415
|
+
- Bash
|
|
416
|
+
- Read
|
|
417
|
+
user-invocable: true
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
# Commit Message Generator
|
|
421
|
+
|
|
422
|
+
生成符合 Conventional Commits 规范的提交消息。
|
|
423
|
+
|
|
424
|
+
## Instructions
|
|
425
|
+
|
|
426
|
+
1. 运行 \`git diff --staged\` 查看暂存的更改
|
|
427
|
+
2. 分析更改类型(feat/fix/docs/style/refactor/test/chore)
|
|
428
|
+
3. 生成简洁的提交消息
|
|
429
|
+
4. 询问用户是否满意或需要调整
|
|
430
|
+
|
|
431
|
+
## Output Format
|
|
432
|
+
|
|
433
|
+
\`\`\`
|
|
434
|
+
<type>(<scope>): <subject>
|
|
435
|
+
|
|
436
|
+
<body>
|
|
437
|
+
|
|
438
|
+
<footer>
|
|
439
|
+
\`\`\`
|
|
440
|
+
\`\`\`
|
|
441
|
+
`}}var W0={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 M1(){return{metadata:W0,instructions:`# Update Config Skill
|
|
442
|
+
|
|
443
|
+
配置 Blade 运行环境。管理 settings、hooks、permissions、环境变量等。
|
|
444
|
+
|
|
445
|
+
## 三层配置体系
|
|
446
|
+
|
|
447
|
+
Blade 采用三层配置,优先级从低到高:
|
|
448
|
+
|
|
449
|
+
| 层级 | 路径 | 用途 | scope 值 |
|
|
450
|
+
|------|------|------|----------|
|
|
451
|
+
| Global | \`~/.blade/config.json\` + \`~/.blade/settings.json\` | 用户全局默认 | \`global\` |
|
|
452
|
+
| Project | \`.blade/settings.json\` | 项目级设置,提交到 git | \`project\` |
|
|
453
|
+
| Local | \`.blade/settings.local.json\` | 本地覆盖,.gitignore | \`local\` |
|
|
454
|
+
|
|
455
|
+
**选择原则:**
|
|
456
|
+
- 个人偏好(codeTheme, language, fontSize)→ \`global\`
|
|
457
|
+
- 团队共享(hooks, permissions, env)→ \`project\`
|
|
458
|
+
- 临时调试(debug, maxTurns)→ \`local\`
|
|
459
|
+
|
|
460
|
+
## ConfigTool 使用
|
|
461
|
+
|
|
462
|
+
### GET - 读取配置
|
|
463
|
+
|
|
464
|
+
\`\`\`
|
|
465
|
+
# 获取全部配置
|
|
466
|
+
ConfigTool({ operation: "get", key: "*" })
|
|
467
|
+
|
|
468
|
+
# 获取特定配置
|
|
469
|
+
ConfigTool({ operation: "get", key: "hooks" })
|
|
470
|
+
|
|
471
|
+
# 获取嵌套值
|
|
472
|
+
ConfigTool({ operation: "get", key: "hooks.PreToolUse" })
|
|
473
|
+
|
|
474
|
+
# 获取权限设置
|
|
475
|
+
ConfigTool({ operation: "get", key: "permissions" })
|
|
476
|
+
\`\`\`
|
|
477
|
+
|
|
478
|
+
### SET - 设置配置
|
|
479
|
+
|
|
480
|
+
\`\`\`
|
|
481
|
+
# 设置温度
|
|
482
|
+
ConfigTool({ operation: "set", key: "temperature", value: 0.7, scope: "global" })
|
|
483
|
+
|
|
484
|
+
# 设置语言
|
|
485
|
+
ConfigTool({ operation: "set", key: "language", value: "zh-CN", scope: "global" })
|
|
486
|
+
|
|
487
|
+
# 设置最大轮次
|
|
488
|
+
ConfigTool({ operation: "set", key: "maxTurns", value: 50, scope: "local" })
|
|
489
|
+
|
|
490
|
+
# 启用调试
|
|
491
|
+
ConfigTool({ operation: "set", key: "debug", value: true, scope: "local" })
|
|
492
|
+
|
|
493
|
+
# 设置环境变量
|
|
494
|
+
ConfigTool({ operation: "set", key: "env", value: { "NODE_ENV": "development" }, scope: "project" })
|
|
495
|
+
\`\`\`
|
|
496
|
+
|
|
497
|
+
### LIST - 列举可配置项
|
|
498
|
+
|
|
499
|
+
\`\`\`
|
|
500
|
+
ConfigTool({ operation: "list" })
|
|
501
|
+
\`\`\`
|
|
502
|
+
|
|
503
|
+
返回所有白名单配置项及其当前值。
|
|
504
|
+
|
|
505
|
+
## 白名单字段
|
|
506
|
+
|
|
507
|
+
以下字段可通过 ConfigTool SET 修改:
|
|
508
|
+
|
|
509
|
+
| 字段 | 类型 | 说明 |
|
|
510
|
+
|------|------|------|
|
|
511
|
+
| temperature | number | 模型温度 |
|
|
512
|
+
| maxContextTokens | number | 上下文窗口大小 |
|
|
513
|
+
| maxOutputTokens | number | 输出 token 限制 |
|
|
514
|
+
| timeout | number | HTTP 请求超时(毫秒)|
|
|
515
|
+
| codeTheme | string | 代码与终端主题 |
|
|
516
|
+
| uiTheme | string | Web UI 主题 |
|
|
517
|
+
| language | string | 界面语言 |
|
|
518
|
+
| fontSize | number | 字体大小 |
|
|
519
|
+
| debug | boolean/string | 调试模式 |
|
|
520
|
+
| autoSaveSessions | boolean | 自动保存会话 |
|
|
521
|
+
| maxTurns | number | Agent 最大轮次 |
|
|
522
|
+
| disableAllHooks | boolean | 禁用所有 hooks |
|
|
523
|
+
| permissions | object | 权限规则 |
|
|
524
|
+
| hooks | object | Hooks 配置 |
|
|
525
|
+
| env | object | 环境变量 |
|
|
526
|
+
| mcpServers | object | MCP 服务器配置 |
|
|
527
|
+
|
|
528
|
+
**禁止修改的字段:** models、currentModelId(保护 API Key 安全)
|
|
529
|
+
|
|
530
|
+
## Hooks 安装指南
|
|
531
|
+
|
|
532
|
+
Hooks 是 Blade 的自动化机制,在特定事件发生时执行 shell 命令。
|
|
533
|
+
|
|
534
|
+
### HookEvent 类型
|
|
535
|
+
|
|
536
|
+
| 事件 | 触发时机 | 典型用途 |
|
|
537
|
+
|------|----------|----------|
|
|
538
|
+
| PreToolUse | 工具执行前 | 代码检查、格式化验证 |
|
|
539
|
+
| PostToolUse | 工具执行后 | 自动运行 lint/test |
|
|
540
|
+
| PostToolUseFailure | 工具执行失败后 | 错误日志 |
|
|
541
|
+
| PermissionRequest | 权限请求时 | 自动批准/拒绝 |
|
|
542
|
+
| UserPromptSubmit | 用户提交提示时 | 注入上下文 |
|
|
543
|
+
| SessionStart | 会话启动时 | 环境初始化 |
|
|
544
|
+
| SessionEnd | 会话结束时 | 清理操作 |
|
|
545
|
+
| Stop | Agent 停止时 | 阻止过早停止 |
|
|
546
|
+
| SubagentStop | 子 Agent 停止时 | 同上 |
|
|
547
|
+
| Notification | 通知事件时 | 自定义通知 |
|
|
548
|
+
| Compaction | 上下文压缩时 | 阻止压缩 |
|
|
549
|
+
|
|
550
|
+
### HookMatcher 结构
|
|
551
|
+
|
|
552
|
+
每个 HookMatcher 包含:
|
|
553
|
+
- \`name\`(可选): 名称,用于日志
|
|
554
|
+
- \`matcher\`(可选): 匹配器,不指定则匹配所有
|
|
555
|
+
- \`tools\`: 工具名匹配(支持字符串或数组,如 \`"Edit"\` 或 \`["Edit", "Write"]\`)
|
|
556
|
+
- \`paths\`: 文件路径匹配(glob 模式,如 \`"**/*.ts"\`)
|
|
557
|
+
- \`commands\`: 命令匹配(正则,如 \`"^git"\`)
|
|
558
|
+
- \`hooks\`: Hook 列表
|
|
559
|
+
|
|
560
|
+
### CommandHook 结构
|
|
561
|
+
|
|
562
|
+
\`\`\`json
|
|
563
|
+
{
|
|
564
|
+
"type": "command",
|
|
565
|
+
"command": "shell command to execute",
|
|
566
|
+
"timeout": 30,
|
|
567
|
+
"statusMessage": "Running check..."
|
|
568
|
+
}
|
|
569
|
+
\`\`\`
|
|
570
|
+
|
|
571
|
+
Hook 接收 JSON 格式的输入通过 stdin,输出 JSON 到 stdout。
|
|
572
|
+
|
|
573
|
+
### 完整安装示例
|
|
574
|
+
|
|
575
|
+
**示例 1:安装 biome check 作为 PostToolUse hook**
|
|
576
|
+
|
|
577
|
+
当 Edit 或 Write 工具修改 .ts/.tsx 文件后,自动运行 biome check:
|
|
578
|
+
|
|
579
|
+
\`\`\`
|
|
580
|
+
ConfigTool({
|
|
581
|
+
operation: "set",
|
|
582
|
+
key: "hooks",
|
|
583
|
+
value: {
|
|
584
|
+
"PostToolUse": [
|
|
585
|
+
{
|
|
586
|
+
"name": "biome-check",
|
|
587
|
+
"matcher": {
|
|
588
|
+
"tools": ["Edit", "Write"],
|
|
589
|
+
"paths": ["**/*.ts", "**/*.tsx"]
|
|
590
|
+
},
|
|
591
|
+
"hooks": [
|
|
592
|
+
{
|
|
593
|
+
"type": "command",
|
|
594
|
+
"command": "biome check --write $(echo $TOOL_INPUT | jq -r '.file_path // empty')",
|
|
595
|
+
"timeout": 30,
|
|
596
|
+
"statusMessage": "Running biome check..."
|
|
597
|
+
}
|
|
598
|
+
]
|
|
599
|
+
}
|
|
600
|
+
]
|
|
601
|
+
},
|
|
602
|
+
scope: "project"
|
|
603
|
+
})
|
|
604
|
+
\`\`\`
|
|
605
|
+
|
|
606
|
+
**示例 2:安装 eslint 作为 PostToolUse hook**
|
|
607
|
+
|
|
608
|
+
\`\`\`
|
|
609
|
+
ConfigTool({
|
|
610
|
+
operation: "set",
|
|
611
|
+
key: "hooks",
|
|
612
|
+
value: {
|
|
613
|
+
"PostToolUse": [
|
|
614
|
+
{
|
|
615
|
+
"name": "eslint-fix",
|
|
616
|
+
"matcher": {
|
|
617
|
+
"tools": ["Edit", "Write"],
|
|
618
|
+
"paths": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
|
|
619
|
+
},
|
|
620
|
+
"hooks": [
|
|
621
|
+
{
|
|
622
|
+
"type": "command",
|
|
623
|
+
"command": "eslint --fix $(echo $TOOL_INPUT | jq -r '.file_path // empty')",
|
|
624
|
+
"timeout": 30,
|
|
625
|
+
"statusMessage": "Running ESLint..."
|
|
626
|
+
}
|
|
627
|
+
]
|
|
628
|
+
}
|
|
629
|
+
]
|
|
630
|
+
},
|
|
631
|
+
scope: "project"
|
|
632
|
+
})
|
|
633
|
+
\`\`\`
|
|
634
|
+
|
|
635
|
+
**示例 3:安装 SessionStart hook 打印环境信息**
|
|
636
|
+
|
|
637
|
+
\`\`\`
|
|
638
|
+
ConfigTool({
|
|
639
|
+
operation: "set",
|
|
640
|
+
key: "hooks",
|
|
641
|
+
value: {
|
|
642
|
+
"SessionStart": [
|
|
643
|
+
{
|
|
644
|
+
"name": "env-info",
|
|
645
|
+
"hooks": [
|
|
646
|
+
{
|
|
647
|
+
"type": "command",
|
|
648
|
+
"command": "echo 'Node:' $(node -v) '| Git branch:' $(git branch --show-current)",
|
|
649
|
+
"timeout": 10,
|
|
650
|
+
"statusMessage": "Gathering environment info..."
|
|
651
|
+
}
|
|
652
|
+
]
|
|
653
|
+
}
|
|
654
|
+
]
|
|
655
|
+
},
|
|
656
|
+
scope: "project"
|
|
657
|
+
})
|
|
658
|
+
\`\`\`
|
|
659
|
+
|
|
660
|
+
**示例 4:设置权限白名单**
|
|
661
|
+
|
|
662
|
+
\`\`\`
|
|
663
|
+
ConfigTool({
|
|
664
|
+
operation: "set",
|
|
665
|
+
key: "permissions",
|
|
666
|
+
value: {
|
|
667
|
+
"allow": [
|
|
668
|
+
"Bash(git:*)",
|
|
669
|
+
"Bash(npm:*)",
|
|
670
|
+
"Bash(bun:*)",
|
|
671
|
+
"Read(**/*.ts)"
|
|
672
|
+
],
|
|
673
|
+
"ask": [],
|
|
674
|
+
"deny": [
|
|
675
|
+
"Bash(rm -rf:*)"
|
|
676
|
+
]
|
|
677
|
+
},
|
|
678
|
+
scope: "project"
|
|
679
|
+
})
|
|
680
|
+
\`\`\`
|
|
681
|
+
|
|
682
|
+
## 自动行为关键词映射
|
|
683
|
+
|
|
684
|
+
当用户使用以下关键词时,映射到对应的配置操作:
|
|
685
|
+
|
|
686
|
+
| 用户说的 | 映射到 |
|
|
687
|
+
|----------|--------|
|
|
688
|
+
| "从现在起 lint 每个文件" | PostToolUse hook(Edit/Write 后运行 lint)|
|
|
689
|
+
| "每次提交前运行测试" | PreToolUse hook(Bash git commit 前运行 test)|
|
|
690
|
+
| "当编辑 TS 文件时检查类型" | PostToolUse hook(Edit 后运行 tsc)|
|
|
691
|
+
| "允许所有 git 命令" | permissions.allow 添加 \`Bash(git:*)\` |
|
|
692
|
+
| "设置调试模式" | debug = true |
|
|
693
|
+
| "改成中文" | language = "zh-CN" |
|
|
694
|
+
| "提高温度" | temperature 调整 |
|
|
695
|
+
| "限制 50 轮" | maxTurns = 50 |
|
|
696
|
+
| "禁用 hooks" | disableAllHooks = true |
|
|
697
|
+
| "添加环境变量" | env 对象更新 |
|
|
698
|
+
|
|
699
|
+
## 验证流程
|
|
700
|
+
|
|
701
|
+
每次安装 hook 或修改配置后,**必须**验证:
|
|
702
|
+
|
|
703
|
+
1. 用 \`ConfigTool({ operation: "get", key: "<modified-key>" })\` 确认值已生效
|
|
704
|
+
2. 如果是 hooks,检查结构是否正确(包含 matcher 和 hooks 数组)
|
|
705
|
+
3. 如果是 permissions,确认 allow/ask/deny 数组格式正确
|
|
706
|
+
4. 告知用户配置已保存到哪个文件(根据 scope 判断)
|
|
707
|
+
|
|
708
|
+
## 注意事项
|
|
709
|
+
|
|
710
|
+
1. **hooks 字段使用 deep-merge 策略**:设置新的 hook 不会覆盖已有的 hook。但如果同一事件下设置新的 matcher 数组,会替换该事件的整个 matcher 列表
|
|
711
|
+
2. **permissions 字段使用 replace 策略**:设置 permissions 会完全替换现有值。如果只想添加规则,先 GET 当前值,合并后再 SET
|
|
712
|
+
3. **env 字段使用 deep-merge 策略**:可以逐个添加环境变量
|
|
713
|
+
4. **scope 默认值**:不指定 scope 时,根据字段路由表决定(hooks/permissions/env 默认 local,temperature/codeTheme 等默认 global)
|
|
714
|
+
`}}import*as n from"node:fs/promises";import*as Y0 from"node:path";import{parse as yO}from"yaml";var hO=/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/,fO=/^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]?$/,z1=1024;function kO(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 b1(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 PO(O,$){if(!O.name)return{valid:!1,error:"Missing required field: name"};if(!fO.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>z1)return{valid:!1,error:`Description too long: ${O.description.length} characters (max ${z1})`};let Q;if(O.model)Q=O.model==="inherit"?"inherit":O.model;return{valid:!0,metadata:{name:O.name,description:O.description.trim(),allowedTools:kO(O["allowed-tools"]),version:O.version,argumentHint:O["argument-hint"]?.trim(),userInvocable:b1(O["user-invocable"]),disableModelInvocation:b1(O["disable-model-invocation"]),model:Q,whenToUse:O.when_to_use?.trim()}}}function A1(O,$,Q){let Z=O.match(hO);if(!Z)return{success:!1,error:"Invalid SKILL.md format: missing YAML frontmatter (must start with ---)"};let[,J,L]=Z,K;try{K=yO(J)}catch(Y){return{success:!1,error:`Failed to parse YAML frontmatter: ${Y instanceof Error?Y.message:String(Y)}`}}let W=PO(K,$);if(!W.valid)return{success:!1,error:W.error};let G=Y0.dirname($);return{success:!0,content:{metadata:{...W.metadata,path:$,basePath:G,source:Q},instructions:L.trim()}}}async function X0(O,$){try{let Q=await n.readFile(O,"utf-8");return A1(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 x1(O){try{let $=await n.readFile(O.path,"utf-8"),Q=A1($,O.path,O.source);return Q.success?Q.content:null}catch{return null}}async function _1(O){try{return await n.access(Y0.join(O,"SKILL.md")),!0}catch{return!1}}var mO={userSkillsDir:_.join(T1(),".blade","skills"),projectSkillsDir:".blade/skills",claudeUserSkillsDir:_.join(T1(),".claude","skills"),claudeProjectSkillsDir:".claude/skills"},i=new Map;function D1(O){return _.resolve(O?.cwd??B())}class R{skills=new Map;pluginSkills=new Map;config;initialized=!1;constructor(O){this.config={...mO,cwd:B(),...O}}static getInstance(O){let $=D1(O),Q=i.get($);if(!Q)Q=new R({...O,cwd:_.resolve(O?.cwd??B())}),i.set($,Q);return Q}static resetInstance(){i.clear()}static releaseInstance(O,$){let Q=D1(O),Z=i.get(Q);if(!Z||$&&Z!==$)return!1;return i.delete(Q)}async initialize(){if(this.initialized)return{skills:Array.from(this.skills.values()),errors:[]};let O=[],$=[];this.loadBuiltinSkills();let Q=await this.scanDirectory(this.config.claudeUserSkillsDir,"user");$.push(...Q.skills),O.push(...Q.errors);let Z=await this.scanDirectory(this.config.userSkillsDir,"user");if($.push(...Z.skills),O.push(...Z.errors),(await D.getInstance().getStatus(this.config.cwd)).state==="trusted"){let L=_.isAbsolute(this.config.claudeProjectSkillsDir)?this.config.claudeProjectSkillsDir:_.join(this.config.cwd,this.config.claudeProjectSkillsDir),K=await this.scanDirectory(L,"project");$.push(...K.skills),O.push(...K.errors);let W=_.isAbsolute(this.config.projectSkillsDir)?this.config.projectSkillsDir:_.join(this.config.cwd,this.config.projectSkillsDir),G=await this.scanDirectory(W,"project");$.push(...G.skills),O.push(...G.errors)}for(let L of $)this.skills.set(L.name,L);return this.initialized=!0,{skills:Array.from(this.skills.values()),errors:O}}loadBuiltinSkills(){this.skills.set(G0.name,G0),this.skills.set(W0.name,W0)}async scanDirectory(O,$){let Q=[],Z=[];try{await U0.access(O)}catch{return{skills:Q,errors:Z}}try{let J=await U0.readdir(O,{withFileTypes:!0});for(let L of J){if(!L.isDirectory()&&!L.isSymbolicLink())continue;let K=_.join(O,L.name),W=_.join(K,"SKILL.md");if(!await _1(K))continue;let G=await X0(W,$);if(G.success&&G.content)Q.push(G.content.metadata);else Z.push({path:W,error:G.error||"Unknown error"})}}catch(J){Z.push({path:O,error:`Failed to scan directory: ${J instanceof Error?J.message:String(J)}`})}return{skills:Q,errors:Z}}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 x1($)}loadBuiltinContent(O){switch(O){case"skill-creator":return B1();case"update-config":return M1();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().sort((Q,Z)=>Q.name<Z.name?-1:Q.name>Z.name?1:0);if(O.length===0)return"";let $=[];for(let Q of O){let Z=Q.description.length>100?`${Q.description.substring(0,97)}...`:Q.description,J=Q.argumentHint?`${Q.name} ${Q.argumentHint}`:Q.name;$.push(`- ${J}: ${Z}`)}return $.join(`
|
|
715
|
+
`)}get size(){return this.skills.size}async refresh(){return this.skills.clear(),this.pluginSkills.clear(),this.initialized=!1,this.initialize()}snapshot(){let O=new R({...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 Z of this.pluginSkills.values())if(Z.originalName===O)Q.push(Z);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 m(O){return R.getInstance(O)}import F0 from"node:path";import{execSync as pO}from"node:child_process";import w1 from"node:fs";import N1 from"node:path";class v0{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 Z=`$${Q}`,J=$[Q-1]??"";O=O.split(Z).join(J)}return O}async executeBashEmbeds(O,$){let Q=/!`([^`]+)`/g,Z=[];for(let L of O.matchAll(Q))Z.push({match:L[0],command:L[1]});let J=O;for(let{match:L,command:K}of Z){if($.signal?.aborted){J=J.replace(L,"[Execution aborted]");continue}try{let W=pO(K,{cwd:$.workspaceRoot,encoding:"utf-8",timeout:30000,maxBuffer:1048576,stdio:["pipe","pipe","pipe"]}).trim();J=J.replace(L,W)}catch(W){let G=W instanceof Error?W.message:String(W);J=J.replace(L,`[Error executing '${K}': ${G}]`)}}return J}async resolveFileReferences(O,$){let Q=/@([\w./-]+(?:\/[\w./-]+|\.[\w]+))/g,Z=[];for(let L of O.matchAll(Q))Z.push({match:L[0],relativePath:L[1]});let J=O;for(let{match:L,relativePath:K}of Z){let W=N1.resolve($,K);try{if(w1.statSync(W).isFile()){let Y=w1.readFileSync(W,"utf-8"),H=`\`\`\`${N1.extname(K).slice(1)||"text"}
|
|
716
|
+
${Y}
|
|
717
|
+
\`\`\``;J=J.replace(L,H)}}catch{}}return J}hasDynamicContent(O){return{hasArgs:/\$ARGUMENTS|\$\d/.test(O),hasBashEmbeds:/!`[^`]+`/.test(O),hasFileRefs:/@[\w./-]+(?:\/[\w./-]+|\.[\w]+)/.test(O)}}}import y0 from"node:fs";import E1 from"node:os";import y from"node:path";import dO from"node:fs";import C1 from"node:path";import gO from"gray-matter";class R0{parse(O,$,Q,Z){try{let J=dO.readFileSync(O,"utf-8"),{data:L,content:K}=gO(J),{name:W,namespace:G}=this.extractNameAndNamespace(O,$);if(!W)return null;return{name:W,namespace:G,config:this.normalizeConfig(L),content:K.trim(),path:O,source:Q,sourceDir:Z}}catch(J){return console.error(`Failed to parse command file: ${O}`,J),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 Z=C1.relative($,O).split(C1.sep),J=Z.pop();if(!J)return{name:""};let L=J.replace(/\.md$/i,""),K=Z.length>0?Z.join("/"):void 0;return{name:L,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 R0;async discover(O){let $=[],Q=[],Z=[],J=this.getSearchDirs(O);for(let L of J){if(!y0.existsSync(L.path))continue;Q.push(L.path);try{let K=await this.scanDirectory(L.path);for(let W of K)try{let G=this.parser.parse(W,L.path,L.source,L.sourceDir);if(G)$.push(G)}catch(G){Z.push({path:W,error:G instanceof Error?G.message:String(G)})}}catch(K){Z.push({path:L.path,error:K instanceof Error?K.message:String(K)})}}return{commands:$,scannedDirs:Q,errors:Z}}getSearchDirs(O){let $=E1.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(Z)=>{let J=await y0.promises.readdir(Z,{withFileTypes:!0});for(let L of J){let K=y.join(Z,L.name);if(L.isDirectory())await Q(K);else if(L.isFile()&&L.name.endsWith(".md"))$.push(K)}};return await Q(O),$}async hasCommands(O){let $=this.getSearchDirs(O);for(let Q of $){if(!y0.existsSync(Q.path))continue;if((await this.scanDirectory(Q.path)).length>0)return!0}return!1}getCommandDirs(O){let $=E1.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 z{static instances=new Map;commands=new Map;pluginCommands=new Map;loader=new h0;executor=new v0;initialized=!1;workspaceRoot="";lastDiscoveryResult=null;static getInstance(O=B()){let $=F0.resolve(O),Q=z.instances.get($);if(!Q)Q=new z,z.instances.set($,Q);return Q}static empty(O){let $=new z;return $.workspaceRoot=F0.resolve(O),$.initialized=!0,$}static resetInstance(){z.instances.clear()}static releaseInstance(O,$){let Q=F0.resolve(O),Z=z.instances.get(Q);if(!Z||$&&Z!==$)return!1;return z.instances.delete(Q)}static getExistingInstance(O=B()){return z.instances.get(F0.resolve(O))}constructor(){}async initialize(O){this.workspaceRoot=O;let $=await this.loader.discover(O),Q=(await D.getInstance().getStatus(O)).state==="trusted",Z={...$,commands:$.commands.filter((J)=>J.source!=="project"||Q)};this.lastDiscoveryResult=Z,this.commands.clear();for(let J of Z.commands)this.commands.set(J.name,J);return this.initialized=!0,Z}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 Z=`Available custom commands:
|
|
718
|
+
|
|
719
|
+
`,J=Z.length,L=0;for(let K of $){let W=K.argumentHint?` ${K.argumentHint}`:"",G=`- /${K.name}${W}: ${K.description} ${K.label}
|
|
720
|
+
`;if(J+G.length>O)break;Z+=G,J+=G.length,L++}if(L<Q)Z+=`
|
|
721
|
+
(${L} of ${Q} commands shown due to character budget)`;return{text:Z,includedCount:L,totalCount:Q}}snapshot(){let O=new z;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 Z of this.pluginCommands.values())if(Z.originalName===O)Q.push(Z);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 Z={name:Q.namespacedName,config:Q.config,content:Q.content,path:Q.path,source:"project",sourceDir:"blade"};return this.executor.execute(Z,$)}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 n0 from"node:path";import{satisfies as f0,validRange as v1}from"semver";var uO=/^[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()||!v1(O.bladeVersion)))throw Error(`Invalid Blade version range in plugin "${O.name}": ${O.bladeVersion}`);for(let[$,Q]of Object.entries(O.dependencies??{})){if(!uO.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()||!v1(Q))throw Error(`Invalid dependency range for ${$} in plugin "${O.name}": ${Q}`)}}function y1(O,$=k0()){if(P0(O),O.bladeVersion&&!f0($,O.bladeVersion))throw Error(`Plugin "${O.name}" requires Blade ${O.bladeVersion}, current ${$}`)}function R1(O,$){O.status="error",O.compatibilityIssues=[...O.compatibilityIssues??[],$],O.error=O.compatibilityIssues.map((Q)=>Q.message).join("; ")}function h1(O,$=k0()){let Q=[],Z=new Map(O.map((L)=>[L.manifest.name,L]));for(let L of O){if(L.status!=="active")continue;if(L.manifest.bladeVersion&&!f0($,L.manifest.bladeVersion)){let K={code:"blade-version",expected:L.manifest.bladeVersion,actual:$,message:`Requires Blade ${L.manifest.bladeVersion}, current ${$}`};R1(L,K),Q.push(K)}}let J=!0;while(J){J=!1;for(let L of O){if(L.status!=="active")continue;for(let[K,W]of Object.entries(L.manifest.dependencies??{})){let G=Z.get(K),Y;if(!G)Y={code:"dependency-missing",dependency:K,expected:W,message:`Missing dependency ${K}@${W}`};else if(!f0(G.manifest.version,W))Y={code:"dependency-version",dependency:K,expected:W,actual:G.manifest.version,message:`Dependency ${K} requires ${W}, found ${G.manifest.version}`};else if(G.status!=="active")Y={code:"dependency-inactive",dependency:K,expected:W,actual:G.status,message:`Dependency ${K} is ${G.status}`};if(!Y)continue;R1(L,Y),Q.push(Y),J=!0;break}}}return Q}import{execFile as K$}from"node:child_process";import{createHash as L$,randomUUID as s1}from"node:crypto";import{constants as t,promises as F}from"node:fs";import{homedir as d0}from"node:os";import S from"node:path";import{promisify as q$}from"node:util";import{Mutex as G$}from"async-mutex";import{satisfies as g0}from"semver";import W$ from"write-file-atomic";import*as H0 from"node:fs/promises";import*as u1 from"node:path";var d=q.String({minLength:2,maxLength:64,pattern:"^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]{1,2}$"}),m0=q.String({pattern:"^\\d+\\.\\d+\\.\\d+(-[\\w.]+)?(\\+[\\w.]+)?$"}),cO=q.Object({name:q.String({minLength:1}),email:q.Optional(q.String({format:"email"})),url:q.Optional(q.String({format:"uri"}))}),P1=q.Object({name:d,description:q.String({minLength:1,maxLength:500}),version:m0,author:q.Optional(cO),license:q.Optional(q.String()),repository:q.Optional(q.String({format:"uri"})),homepage:q.Optional(q.String({format:"uri"})),keywords:q.Optional(q.Array(q.String())),dependencies:q.Optional(q.Record(q.String(),q.String())),bladeVersion:q.Optional(q.String())}),lO=q.Union([q.Object({type:q.Literal("git"),url:q.String({minLength:1}),ref:q.Optional(q.String({minLength:1}))},{additionalProperties:!1}),q.Object({type:q.Literal("local"),path:q.String({minLength:1})},{additionalProperties:!1}),q.Object({type:q.Literal("marketplace"),marketplace:d},{additionalProperties:!1})]),sO=q.Object({name:d,source:lO,installPath:q.String({minLength:1}),version:m0,revision:q.String({minLength:1,maxLength:128}),contentDigest:q.String({pattern:"^[a-f0-9]{64}$"}),installedAt:q.String({format:"date-time"}),updatedAt:q.String({format:"date-time"})},{additionalProperties:!1}),nO=q.Union([q.Object({type:q.Literal("git"),url:q.String({minLength:1}),ref:q.Optional(q.String({minLength:1}))},{additionalProperties:!1}),q.Object({type:q.Literal("local"),path:q.String({minLength:1})},{additionalProperties:!1})]),iO=q.Object({name:d,source:nO,installPath:q.String({minLength:1}),revision:q.String({minLength:1,maxLength:128}),contentDigest:q.String({pattern:"^[a-f0-9]{64}$"}),addedAt:q.String({format:"date-time"}),updatedAt:q.String({format:"date-time"})},{additionalProperties:!1}),oO=q.Object({version:q.Literal(1),installed:q.Record(q.String(),sO),marketplaces:q.Record(q.String(),iO)},{additionalProperties:!1}),m1=q.Object({name:q.String({minLength:1}),email:q.Optional(q.String({format:"email"})),url:q.Optional(q.String({format:"uri"}))}),aO=q.Object({name:d,description:q.Optional(q.String({maxLength:500})),version:q.Optional(m0),author:q.Optional(q.Union([q.String({minLength:1}),m1])),source:q.Union([q.String({minLength:1}),q.Object({source:q.Literal("url"),url:q.String({minLength:1}),ref:q.Optional(q.String({minLength:1})),sha:q.Optional(q.String({pattern:"^[a-fA-F0-9]{40}$"}))})]),category:q.Optional(q.String()),homepage:q.Optional(q.String({format:"uri"})),tags:q.Optional(q.Array(q.String()))}),tO=q.Object({name:d,description:q.Optional(q.String({maxLength:500})),owner:q.Optional(m1),metadata:q.Optional(q.Object({description:q.Optional(q.String({maxLength:500})),version:q.Optional(q.String())})),plugins:q.Array(aO,{maxItems:5000})}),f1=q.Object({type:k1(["stdio","sse","http"]),command:q.Optional(q.String()),args:q.Optional(q.Array(q.String())),env:q.Optional(q.Record(q.String(),q.String())),cwd:q.Optional(q.String()),url:q.Optional(q.String({format:"uri"})),headers:q.Optional(q.Record(q.String(),q.String())),timeout:q.Optional(q.Integer({minimum:1000,maximum:1800000})),idleTimeout:q.Optional(q.Integer({minimum:1000,maximum:1800000})),sampling:q.Optional(q.Object({enabled:q.Boolean(),maxTokens:q.Optional(q.Integer({minimum:1,maximum:4096})),maxRequestsPerToolCall:q.Optional(q.Integer({minimum:1,maximum:8})),maxInputBytes:q.Optional(q.Integer({minimum:1,maximum:1048576}))})),oauth:q.Optional(q.Object({enabled:q.Optional(q.Boolean()),clientId:q.Optional(q.String({minLength:1,maxLength:4096})),scopes:q.Optional(q.Array(q.String({minLength:1,maxLength:128}),{maxItems:32})),callbackPort:q.Optional(q.Integer({minimum:1024,maximum:65535}))},{additionalProperties:!1})),healthCheck:q.Optional(q.Object({enabled:q.Optional(q.Boolean()),interval:q.Optional(q.Number({exclusiveMinimum:0})),timeout:q.Optional(q.Number({exclusiveMinimum:0})),failureThreshold:q.Optional(q.Number({exclusiveMinimum:0}))}))}),rO=q.Union([q.Object({mcpServers:q.Record(q.String(),f1)}),q.Record(q.String(),f1)]);function p1(O){return p(rO,O)}function d1(O){return p(oO,O)}function g1(O){return p(tO,O)}var eO=[{dir:".blade-plugin",source:"blade"},{dir:".claude-plugin",source:"claude"}];async function o(O){for(let{dir:$,source:Q}of eO){let Z=u1.join(O,$,"plugin.json");try{await H0.access(Z)}catch{continue}try{let J=await H0.readFile(Z,"utf-8"),L=JSON.parse(J),K=p(P1,L);if(!K.success){let W=K.error.issues.map((G)=>`${G.path.join(".")}: ${G.message}`).join("; ");throw Error(`Invalid plugin.json: ${W}`)}return P0(K.data),j.debug(`Parsed plugin manifest from ${Z}`),{manifest:K.data,source:Q,manifestPath:Z}}catch(J){if(J instanceof SyntaxError)throw Error(`Invalid JSON in ${Z}: ${J.message}`);throw J}}return null}import{realpathSync as O$}from"node:fs";import p0 from"node:path";var $$=/^[a-fA-F0-9]{40}$/;class v extends Error{code;constructor(O,$){super($);this.code=O;this.name="PluginSourcePolicyError"}}function Q$(O,$){let Q=(J)=>{try{return O$.native(J)}catch{return p0.resolve(J)}},Z=p0.relative(Q(O),Q($));return Z===""||!Z.startsWith("..")&&!p0.isAbsolute(Z)}function Z$(O){let $=O.match(/^[^@]+@([^:]+):/);if($?.[1])return $[1].toLowerCase();try{return new URL(O).hostname.toLowerCase()}catch{return}}function J$(O,$){if(!$.startsWith("*."))return O===$;let Q=$.slice(1);return O.endsWith(Q)&&O.length>Q.length}function c1(O,$,Q){if(O.requireGitCommitSha&&(!$.ref||!$$.test($.ref)))throw new v("PLUGIN_GIT_SHA_REQUIRED",`${Q} must pin a full 40-character Git commit SHA`);if(!O.restrictToAllowedSources)return;let Z=Z$($.url);if(!Z||!O.allowedGitHosts.some((J)=>J$(Z,J)))throw new v("PLUGIN_SOURCE_BLOCKED",`${Q} Git host is not allowed by plugin source policy`)}function l1(O,$,Q){if(!O.restrictToAllowedSources)return;if(!O.allowedLocalRoots.some((Z)=>Q$(Z,$)))throw new v("PLUGIN_SOURCE_BLOCKED",`${Q} local path is not allowed by plugin source policy`)}function g(O,$,Q="Marketplace"){if($.type==="git")c1(O,$,Q);else l1(O,$.path,Q)}function a(O,$,Q,Z="Plugin"){if($.type==="git"){c1(O,$,Z);return}if($.type==="local"){l1(O,$.path,Z);return}if(O.restrictToAllowedSources&&!O.allowedMarketplaces.includes($.marketplace))throw new v("PLUGIN_SOURCE_BLOCKED",`Marketplace "${$.marketplace}" is not allowed by plugin source policy`);let J=Q[$.marketplace];if(!J)throw new v("MARKETPLACE_NOT_FOUND",`Marketplace "${$.marketplace}" is not configured`);g(O,J.source,`Marketplace "${$.marketplace}"`)}var Y$=q$(K$),X$=1,n1=2097152,i1=1e4,o1=104857600,U$=120000,F$=15000,H$=600000,l0=/^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$|^[a-z0-9]$/,a1=/^[a-fA-F0-9]{40}$/,S$=[S.join(".blade-plugin","marketplace.json"),S.join(".claude-plugin","marketplace.json"),"marketplace.json"];class U extends Error{code;constructor(O,$){super($);this.code=O;this.name="PluginPackageError"}}function V$(){return{version:X$,installed:{},marketplaces:{}}}function u(O){return O.code==="ENOENT"}function c(O){if(O instanceof U||O instanceof v)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 r(O,$){let Q=S.relative(O,$);return Q===""||!Q.startsWith("..")&&!S.isAbsolute(Q)}function l(O,$="plugin"){if(!l0.test(O))throw new U("INVALID_PLUGIN_NAME",`Invalid ${$} name: ${O}`);return O}function j$(O){if(O.startsWith("git@"))return;let $=O.lastIndexOf("@");if($<=0||$===O.length-1)return;let Q=O.slice(0,$),Z=O.slice($+1);if(!l0.test(Q)||!l0.test(Z))return;return{pluginName:Q,marketplace:Z}}class r1{userPluginsDir;stateRoot;statePath;mutex=new G$;constructor(O,$){this.userPluginsDir=O??S.join(d0(),".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 Z=await this.readState(),J=await this.resolvePluginSource(O,Z,$),L=this.effectivePolicy($.policy);if(a(L,J.recordSource,Z.marketplaces),J.expectedName&&Z.installed[J.expectedName])throw new U("PLUGIN_ALREADY_INSTALLED",`Plugin "${J.expectedName}" is already installed`);let K=await this.preparePluginClosure({state:Z,source:J.source,recordSource:J.recordSource,expectedName:J.expectedName,policy:L,updateRoot:!1});Q=K.prepared;let W=await this.commitPreparedPlugins(Z,Q),G=W.records.get(K.root.name);if(!G)throw new U("PLUGIN_PACKAGE_ERROR","Root plugin was not committed");let Y=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:Y,changed:W.changed.has(K.root.name)}}catch(Z){await this.cleanupPreparedPlugins(Q);let J=c(Z);return{success:!1,code:J.code,error:J.message}}})}async update(O,$={}){return this.runMutation(async()=>{let Q=[];try{this.requireSourceTrust($.trusted),l(O);let Z=await this.readState(),J=Z.installed[O];if(!J)throw new U("PLUGIN_NOT_MANAGED",`Plugin "${O}" is not managed by Blade's package store`);let L=this.effectivePolicy($.policy);a(L,J.source,Z.marketplaces);let K=await this.preparePluginClosure({state:Z,source:J.source,recordSource:J.source,expectedName:O,policy:L,updateRoot:!0});Q=K.prepared;let W=await this.commitPreparedPlugins(Z,Q),G=W.records.get(O);if(!G)throw new U("PLUGIN_PACKAGE_ERROR","Updated root plugin was not committed");let Y=Q.map((X)=>X.name).filter((X)=>X!==O&&W.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:Y,changed:W.changed.has(O)}}catch(Z){await this.cleanupPreparedPlugins(Q);let J=c(Z);return{success:!1,pluginName:O,code:J.code,error:J.message}}})}async uninstall(O,$=!1){return this.runMutation(async()=>{try{if(l(O),!$)throw new U("CONFIRMATION_REQUIRED",`Uninstalling "${O}" requires explicit confirmation`);let Q=await this.readState(),Z=Q.installed[O];if(!Z)throw new U("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:Z}}catch(Q){let Z=c(Q);return{success:!1,pluginName:O,code:Z.code,error:Z.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 U("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" has an invalid package path`);let Q=await F.realpath(this.stateRoot),Z=await F.realpath(O.installPath);if(!r(Q,Z))throw new U("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" has an invalid package path`);if(await this.digestTree(Z)!==O.contentDigest)throw new U("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" failed content verification`);let L=await o(Z);if(!L||L.manifest.name!==O.name||L.manifest.version!==O.version)throw new U("PLUGIN_PACKAGE_TAMPERED",`Managed plugin "${O.name}" failed manifest verification`)}async addMarketplace(O,$={}){return this.runMutation(async()=>{let Q;try{let Z=await this.normalizeMarketplaceSource(O,$);g(this.effectivePolicy($.policy),Z);let J=await this.readState();Q=await this.materializeSource(Z);let L=await this.readMarketplaceManifest(Q.stagingPath),K=l(L.name,"marketplace");if(J.marketplaces[K])throw new U("MARKETPLACE_ALREADY_EXISTS",`Marketplace "${K}" is already configured`);let{revision:W,contentDigest:G}=Q,Y=await this.publishPackage("marketplaces",K,Q);Q=void 0;let X=new Date().toISOString(),H={name:K,source:Z,installPath:Y,revision:W,contentDigest:G,addedAt:X,updatedAt:X};return J.marketplaces[K]=H,await this.writeState(J),{success:!0,marketplace:H,manifest:L,changed:!0}}catch(Z){if(Q)await F.rm(Q.stagingPath,{recursive:!0,force:!0});let J=c(Z);return{success:!1,code:J.code,error:J.message}}})}async refreshMarketplace(O,$){return this.runMutation(async()=>{let Q;try{l(O,"marketplace");let Z=await this.readState(),J=Z.marketplaces[O];if(!J)throw new U("MARKETPLACE_NOT_FOUND",`Marketplace "${O}" is not configured`);g(this.effectivePolicy($),J.source,`Marketplace "${O}"`),Q=await this.materializeSource(J.source);let L=await this.readMarketplaceManifest(Q.stagingPath);if(L.name!==O)throw new U("MARKETPLACE_IDENTITY_MISMATCH",`Marketplace "${O}" now declares "${L.name}"`);if(J.revision===Q.revision&&J.contentDigest===Q.contentDigest)return await F.rm(Q.stagingPath,{recursive:!0,force:!0}),Q=void 0,{success:!0,marketplace:J,manifest:L,changed:!1};let{revision:K,contentDigest:W}=Q,G=await this.publishPackage("marketplaces",O,Q);Q=void 0;let Y={...J,installPath:G,revision:K,contentDigest:W,updatedAt:new Date().toISOString()};return Z.marketplaces[O]=Y,await this.writeState(Z),{success:!0,marketplace:Y,manifest:L,changed:!0}}catch(Z){if(Q)await F.rm(Q.stagingPath,{recursive:!0,force:!0});let J=c(Z);return{success:!1,code:J.code,error:J.message}}})}async removeMarketplace(O,$=!1){return this.runMutation(async()=>{try{if(l(O,"marketplace"),!$)throw new U("CONFIRMATION_REQUIRED",`Removing marketplace "${O}" requires explicit confirmation`);let Q=await this.readState(),Z=Q.marketplaces[O];if(!Z)throw new U("MARKETPLACE_NOT_FOUND",`Marketplace "${O}" is not configured`);let J=Object.values(Q.installed).filter((L)=>L.source.type==="marketplace"&&L.source.marketplace===O).map((L)=>L.name).sort();if(J.length>0)throw new U("MARKETPLACE_IN_USE",`Marketplace "${O}" still owns installed plugins: ${J.join(", ")}`);return delete Q.marketplaces[O],await this.writeState(Q),{success:!0,marketplace:Z,changed:!0}}catch(Q){let Z=c(Q);return{success:!1,code:Z.code,error:Z.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"),$=s1(),Q=Date.now()+F$;while(Date.now()<Q)try{let Z=await F.open(O,t.O_CREAT|t.O_EXCL|t.O_WRONLY,384);return await Z.writeFile(`${JSON.stringify({version:1,pid:process.pid,token:$,createdAt:new Date().toISOString()})}
|
|
722
|
+
`,"utf8"),await Z.close(),async()=>{try{if(JSON.parse(await F.readFile(O,"utf8")).token===$)await F.unlink(O)}catch(J){if(!u(J))j.warn(`Failed to release plugin package lock: ${J}`)}}}catch(Z){if(Z.code!=="EEXIST")throw Z;await this.removeStaleStoreLock(O),await new Promise((J)=>setTimeout(J,50))}throw new U("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 U("PLUGIN_STORE_INVALID","Plugin package lock must be a regular file");if(process.getuid&&$.uid!==process.getuid()||($.mode&511)!==384)throw new U("PLUGIN_STORE_INVALID","Plugin package lock ownership or permissions are invalid");if(Date.now()-$.mtimeMs<H$)return;await F.unlink(O)}catch($){if(!u($)&&$ instanceof U)throw $}}requireSourceTrust(O){if(O!==!0)throw new U("SOURCE_TRUST_REQUIRED","Plugin installation or update requires explicit source trust")}effectivePolicy(O){return O??S0.pluginSourcePolicy}async preparePluginClosure(O){let $=[],Q=new Map,Z=[],J=(K,W,G,Y)=>{if(!g0(W,G))throw new U("DEPENDENCY_VERSION_MISMATCH",`Plugin "${Y}" requires ${K}@${G}, found ${W}`)},L=async(K)=>{let W=K.expectedName?O.state.installed[K.expectedName]:void 0;if(W&&!K.root&&K.requiredRange){if(g0(W.version,K.requiredRange))return W;if(K.recordSource.type!=="marketplace"||W.source.type!=="marketplace"||W.source.marketplace!==K.recordSource.marketplace)J(W.name,W.version,K.requiredRange,K.requiredBy??"dependency");K={...K,source:W.source,recordSource:W.source}}if(W&&K.root&&!O.updateRoot)throw new U("PLUGIN_ALREADY_INSTALLED",`Plugin "${W.name}" is already installed`);if(K.expectedName){let V=Q.get(K.expectedName);if(V){if(K.requiredRange)J(V.name,V.manifest.version,K.requiredRange,K.requiredBy??"dependency");return V}if(Z.includes(K.expectedName))throw new U("DEPENDENCY_CYCLE",`Plugin dependency cycle: ${[...Z,K.expectedName].join(" -> ")}`)}let G=await this.materializePluginSource(K.source,O.state,K.expectedName,O.policy),Y=await o(G.stagingPath);if(!Y)throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),new U("INVALID_PLUGIN","Plugin manifest was not found");let X=l(Y.manifest.name);if(K.expectedName&&K.expectedName!==X)throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),new U("PLUGIN_IDENTITY_MISMATCH",`Requested "${K.expectedName}" but source declares "${X}"`);if(Z.includes(X))throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),new U("DEPENDENCY_CYCLE",`Plugin dependency cycle: ${[...Z,X].join(" -> ")}`);if(K.root&&O.state.installed[X]&&!O.updateRoot)throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),new U("PLUGIN_ALREADY_INSTALLED",`Plugin "${X}" is already installed`);try{y1(Y.manifest)}catch(V){throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),new U("VERSION_INCOMPATIBLE",V instanceof Error?V.message:String(V))}let H={name:X,source:K.recordSource,manifest:Y.manifest,materialized:G,existing:O.state.installed[X]};Z.push(X);try{for(let[V,k]of Object.entries(Y.manifest.dependencies??{}).sort(([J0],[E])=>J0.localeCompare(E))){let J0=Q.get(V);if(J0){J(V,J0.manifest.version,k,X);continue}let E=O.state.installed[V];if(E&&g0(E.version,k))continue;if(K.recordSource.type!=="marketplace")throw new U(E?"DEPENDENCY_VERSION_MISMATCH":"DEPENDENCY_MISSING",E?`Plugin "${X}" requires ${V}@${k}, found ${E.version}`:`Plugin "${X}" requires missing dependency ${V}@${k}; direct sources cannot auto-install dependencies`);let D0=await L({source:E?.source.type==="marketplace"&&E.source.marketplace===K.recordSource.marketplace?E.source:{type:"marketplace",marketplace:K.recordSource.marketplace},recordSource:{type:"marketplace",marketplace:K.recordSource.marketplace},expectedName:V,requiredRange:k,requiredBy:X,root:!1});J(V,"manifest"in D0?D0.manifest.version:D0.version,k,X)}}catch(V){throw await F.rm(G.stagingPath,{recursive:!0,force:!0}),V}finally{Z.pop()}return Q.set(X,H),$.push(H),H};try{let K=await L({source:O.source,recordSource:O.recordSource,expectedName:O.expectedName,root:!0});if(!("manifest"in K))throw new U("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,Z=new Set,J=new Date().toISOString();for(let L of $){let K=L.existing;if(K?.revision===L.materialized.revision&&K.contentDigest===L.materialized.contentDigest){await F.rm(L.materialized.stagingPath,{recursive:!0,force:!0}),Q.set(L.name,K);continue}let W=await this.publishPackage("packages",L.name,L.materialized),G={name:L.name,source:L.source,installPath:W,version:L.manifest.version,revision:L.materialized.revision,contentDigest:L.materialized.contentDigest,installedAt:K?.installedAt??J,updatedAt:J};O.installed[L.name]=G,Q.set(L.name,G),Z.add(L.name)}if(Z.size>0)await this.writeState(O);return{records:Q,changed:Z}}async cleanupPreparedPlugins(O){await Promise.all(O.map(($)=>F.rm($.materialized.stagingPath,{recursive:!0,force:!0})))}async resolvePluginSource(O,$,Q){let Z=O.trim();if(!Z)throw new U("INVALID_SOURCE","Plugin source is required");let J=j$(Z);if(J){if(!$.marketplaces[J.marketplace])throw new U("MARKETPLACE_NOT_FOUND",`Marketplace "${J.marketplace}" is not configured`);return{source:{type:"marketplace",marketplace:J.marketplace},recordSource:{type:"marketplace",marketplace:J.marketplace},expectedName:J.pluginName}}if(!u0(Z)&&!this.looksLikeGitSource(Z)){let K=[];for(let W of await this.catalogsFromState($))if(W.manifest.plugins.some((G)=>G.name===Z))K.push({pluginName:Z,marketplace:W.marketplace.name});if(K.length===1)return{source:{type:"marketplace",marketplace:K[0].marketplace},recordSource:{type:"marketplace",marketplace:K[0].marketplace},expectedName:Z};if(K.length>1)throw new U("AMBIGUOUS_PLUGIN_SOURCE",`Plugin "${Z}" exists in multiple marketplaces; use name@marketplace`)}if(u0(Z)){let W={type:"local",path:await this.resolveLocalSource(Z,Q.workspaceRoot)};return{source:W,recordSource:W}}let L=this.normalizeGitSource(Z,Q.ref);return{source:L,recordSource:L}}async materializePluginSource(O,$,Q,Z){if(O.type!=="marketplace")return this.materializeSource(O);let J=$.marketplaces[O.marketplace];if(!J)throw new U("MARKETPLACE_NOT_FOUND",`Marketplace "${O.marketplace}" is not configured`);let L=await this.readMarketplaceManifest(J.installPath),K=Q?L.plugins.filter((W)=>W.name===Q):L.plugins;if(K.length!==1)throw new U("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(J,K[0],Z)}async materializeMarketplaceEntry(O,$,Q){if(typeof $.source!=="string"){let K=this.normalizeGitSource($.source.url,$.source.sha??$.source.ref);return g(Q,K,`Plugin "${$.name}"`),this.materializeSource(K)}if($.source.startsWith("https://")||$.source.startsWith("ssh://")||$.source.startsWith("git@")){let K=this.normalizeGitSource($.source);return g(Q,K,`Plugin "${$.name}"`),this.materializeSource(K)}let Z=await F.realpath(O.installPath),J=S.resolve(Z,$.source);if(!r(Z,J)||J===Z)throw new U("MARKETPLACE_PATH_ESCAPE",`Plugin "${$.name}" source escapes its marketplace`);let L=await F.realpath(J);if(!r(Z,L))throw new U("MARKETPLACE_PATH_ESCAPE",`Plugin "${$.name}" source escapes its marketplace`);return this.materializeSource({type:"local",path:L})}async normalizeMarketplaceSource(O,$){let Q=O.trim();if(!Q)throw new U("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 U("INVALID_SOURCE","Invalid SSH Git source")}else{let Z;try{Z=new URL(O)}catch{throw new U("INVALID_SOURCE","Invalid Git source")}if(Z.protocol!=="https:"&&Z.protocol!=="ssh:")throw new U("UNSAFE_GIT_PROTOCOL","Git sources must use HTTPS or SSH");if(Z.password||Z.protocol==="https:"&&Z.username)throw new U("GIT_CREDENTIALS_IN_URL","Git credentials must not be embedded in plugin source URLs");if(Z.hash||Z.search)throw new U("INVALID_SOURCE","Use the ref option instead of URL query or fragment parameters");Q=Z.toString()}if($&&(!$.trim()||$.startsWith("-")||$.includes("\x00")))throw new U("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==="~"?d0():O.startsWith("~/")?S.join(d0(),O.slice(2)):O,Z=S.resolve($??process.cwd(),Q);this.assertAllowedLocalSource(Z);let J;try{J=await F.realpath(Z)}catch{throw new U("LOCAL_SOURCE_NOT_FOUND",`Local source does not exist: ${O}`)}let L=await F.lstat(J);if(!L.isDirectory()||L.isSymbolicLink())throw new U("INVALID_LOCAL_SOURCE","Local plugin sources must be regular directories");return this.assertAllowedLocalSource(J),J}assertAllowedLocalSource(O){try{t1(O,"sourcePath")}catch{throw new U("INVALID_LOCAL_SOURCE","Local plugin source must reference a local workspace")}}async materializeSource(O){if(O.type==="local")this.assertAllowedLocalSource(O.path);await this.ensureStoreDirectory();let $=S.join(this.stateRoot,"staging",s1());if(await F.mkdir(S.dirname($),{recursive:!0,mode:448}),O.type==="git"){await this.cloneGitSource(O,$);let Z=await this.runGit(["rev-parse","HEAD"],$);if(O.ref&&a1.test(O.ref)&&Z.toLowerCase()!==O.ref.toLowerCase())throw await F.rm($,{recursive:!0,force:!0}),new U("GIT_PIN_MISMATCH","Plugin checkout does not match the requested Git commit SHA");await F.rm(S.join($,".git"),{recursive:!0,force:!0});let J=await this.digestTree($);return{stagingPath:$,revision:Z,contentDigest:J}}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&&a1.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 U("GIT_CLONE_FAILED",`Failed to clone plugin source: ${this.sanitizeError(Q)}`)}}async runGit(O,$){let Q=await Y$("git",O,{cwd:$,encoding:"utf8",timeout:U$,maxBuffer:4194304,env:{...process.env,GIT_TERMINAL_PROMPT:"0"}}),Z=typeof Q==="string"?Q:Q.stdout;if(Z===void 0)throw Error("Git process returned an invalid result");return String(Z).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,Z=async(L)=>{let K=await F.readdir(L,{withFileTypes:!0});K.sort((W,G)=>W.name.localeCompare(G.name));for(let W of K){if(W.name===".git")continue;let G=S.join(L,W.name),Y=S.relative(O,G),X=await F.lstat(G);if(X.isSymbolicLink())throw new U("PACKAGE_SYMLINK_FORBIDDEN",`Plugin packages may not contain symbolic links: ${Y}`);if(X.isDirectory()){await Z(G);continue}if(!X.isFile())throw new U("PACKAGE_ENTRY_FORBIDDEN",`Plugin packages may contain only files and directories: ${Y}`);if(Q+=X.size,$.push({relative:Y,absolute:G,executable:(X.mode&73)!==0}),$.length>i1||Q>o1)throw new U("PACKAGE_TOO_LARGE",`Plugin package exceeds ${i1} files or ${o1} bytes`)}};await Z(O);let J=L$("sha256");for(let L of $)J.update(L.relative),J.update("\x00"),J.update(L.executable?"x":"-"),J.update("\x00"),J.update(await F.readFile(L.absolute)),J.update("\x00");return J.digest("hex")}async publishPackage(O,$,Q){let Z=S.join(this.stateRoot,O,$,Q.contentDigest);await F.mkdir(S.dirname(Z),{recursive:!0,mode:448});try{let J=await F.lstat(Z);if(!J.isDirectory()||J.isSymbolicLink())throw new U("PACKAGE_STORE_CONFLICT","Plugin package destination is not a regular directory");return await F.rm(Q.stagingPath,{recursive:!0,force:!0}),Z}catch(J){if(!u(J))throw J}return await F.rename(Q.stagingPath,Z),Z}async readMarketplaceManifest(O){for(let $ of S$){let Q=S.join(O,$);try{let Z=await F.lstat(Q);if(!Z.isFile()||Z.isSymbolicLink()||Z.size>n1)throw new U("INVALID_MARKETPLACE","Marketplace manifest must be a regular bounded file");let J=JSON.parse(await F.readFile(Q,"utf8")),L=g1(J);if(!L.success)throw new U("INVALID_MARKETPLACE",`Invalid marketplace manifest: ${L.error.issues.map((W)=>`${W.path.join(".")}: ${W.message}`).join("; ")}`);let K=new Set;for(let W of L.data.plugins){if(K.has(W.name))throw new U("INVALID_MARKETPLACE",`Marketplace contains duplicate plugin: ${W.name}`);K.add(W.name)}return L.data}catch(Z){if(u(Z))continue;if(Z instanceof SyntaxError)throw new U("INVALID_MARKETPLACE","Marketplace manifest contains invalid JSON");throw Z}}throw new U("INVALID_MARKETPLACE","Marketplace manifest was not found")}async catalogsFromState(O){let $=Object.values(O.marketplaces).sort((Q,Z)=>Q.name.localeCompare(Z.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,t.O_RDONLY|t.O_NOFOLLOW)}catch($){if(u($))return V$();throw new U("PLUGIN_STATE_INVALID","Plugin package state must be a regular file")}try{let $=await O.stat();if(!$.isFile()||$.size>n1)throw new U("PLUGIN_STATE_INVALID","Plugin package state is invalid");if(process.getuid&&$.uid!==process.getuid())throw new U("PLUGIN_STATE_INVALID","Plugin package state must be owned by the current user");if(($.mode&511)!==384)throw new U("PLUGIN_STATE_INVALID","Plugin package state permissions must be 0600");let Q=JSON.parse(await O.readFile("utf8")),Z=d1(Q);if(!Z.success)throw new U("PLUGIN_STATE_INVALID","Plugin package state schema is invalid");let J=Z.data;for(let[L,K]of Object.entries(J.installed)){let W=S.join(this.stateRoot,"packages",L);if(L!==K.name||!S.isAbsolute(K.installPath)||!r(W,K.installPath)||S.basename(K.installPath)!==K.contentDigest||K.source.type==="local"&&!S.isAbsolute(K.source.path))throw new U("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 U("PLUGIN_STATE_INVALID","Plugin package state contains an invalid Git source")}}for(let[L,K]of Object.entries(J.marketplaces)){let W=S.join(this.stateRoot,"marketplaces",L);if(L!==K.name||!S.isAbsolute(K.installPath)||!r(W,K.installPath)||S.basename(K.installPath)!==K.contentDigest||K.source.type==="local"&&!S.isAbsolute(K.source.path))throw new U("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 U("PLUGIN_STATE_INVALID","Plugin package state contains an invalid Marketplace Git source")}}for(let L of Object.values(J.installed))if(L.source.type==="marketplace"&&!J.marketplaces[L.source.marketplace])throw new U("PLUGIN_STATE_INVALID","Plugin package state references a missing marketplace");return J}catch($){if($ instanceof SyntaxError)throw new U("PLUGIN_STATE_INVALID","Plugin package state contains invalid JSON");throw $}finally{await O.close()}}async writeState(O){await this.ensureStoreDirectory(),await W$(this.statePath,`${JSON.stringify(O,null,2)}
|
|
723
|
+
`,{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 U("PLUGIN_STORE_INVALID","Plugin package store must be a regular directory");if(process.getuid&&O.uid!==process.getuid())throw new U("PLUGIN_STORE_INVALID","Plugin package store must be owned by the current user");if((O.mode&511)!==448)throw new U("PLUGIN_STORE_INVALID","Plugin package store permissions must be 0700")}catch(O){if(!u(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 r1(O,$);return c0}import*as A from"node:fs/promises";import{homedir as I$}from"node:os";import*as M from"node:path";import OO from"gray-matter";function V0(O,$){return`${O}:${$}`}function e1(O,$){return`${O}__${$}`}class j0{async loadPlugin(O,$,Q={}){let Z=await o(O);if(!Z)throw Error(`Not a valid plugin directory: ${O}`);let{manifest:J,source:L}=Z,K=J.name;j.debug(`Loading plugin "${K}" from ${O}`);let W=Q.skipCommands?[]:await this.loadCommands(O,K),G=Q.skipAgents?[]:await this.loadAgents(O,K),Y=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),V=Q.skipLsp?void 0:await this.loadLspConfig(O,K);return j.debug(`Plugin "${K}" loaded: ${W.length} commands, ${G.length} agents, ${Y.length} skills`),{manifest:J,basePath:O,source:$,manifestSource:L,commands:W,agents:G,skills:Y,hooks:X,mcpServers:H,lspServers:V,status:"active",loadedAt:new Date}}async loadCommands(O,$){let Q=M.join(O,"commands"),Z=[];if(!await this.dirExists(Q))return Z;let J=await this.scanMarkdownFiles(Q);for(let L of J)try{let K=await this.parseCommandFile(L,Q,$);if(K)Z.push(K)}catch(K){j.warn(`Failed to load command from ${L}: ${K instanceof Error?K.message:String(K)}`)}return Z}async parseCommandFile(O,$,Q){let Z=await A.readFile(O,"utf-8"),{data:J,content:L}=OO(Z),W=M.relative($,O).split(M.sep),G=W.pop();if(!G)return null;let Y=G.replace(/\.md$/i,""),X=Y;if(W.length>0)X=[...W,Y].join("/");let H=V0(Q,X),V=this.normalizeCommandConfig(J);return{originalName:X,namespacedName:H,pluginName:Q,config:V,content:L.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"),Z=[];if(!await this.dirExists(Q))return Z;let J=await this.scanMarkdownFiles(Q);for(let L of J)try{let K=await this.parseAgentFile(L,Q,$);if(K)Z.push(K)}catch(K){j.warn(`Failed to load agent from ${L}: ${K instanceof Error?K.message:String(K)}`)}return Z}async parseAgentFile(O,$,Q){let Z=await A.readFile(O,"utf-8"),{data:J,content:L}=OO(Z),K=M.relative($,O),W=M.basename(K,".md"),G=J.name||W,Y=V0(Q,G),X={name:Y,description:this.asString(J.description)||"",tools:this.parseStringArray(J.tools),color:this.asString(J.color),model:this.asString(J.model),permissionMode:this.asString(J.permissionMode),skills:this.parseStringArray(J.skills),systemPrompt:L.trim(),source:`plugin:${Q}`};return{originalName:G,namespacedName:Y,pluginName:Q,config:X,path:O}}async loadSkills(O,$){let Q=M.join(O,"skills"),Z=[];if(!await this.dirExists(Q))return Z;let J=await A.readdir(Q,{withFileTypes:!0});for(let L of J){if(!L.isDirectory())continue;let K=M.join(Q,L.name),W=M.join(K,"SKILL.md");try{let G=await X0(W,"project");if(G.success&&G.content){let Y=G.content.metadata.name,X=V0($,Y);Z.push({originalName:Y,namespacedName:X,pluginName:$,metadata:{...G.content.metadata,name:X},path:K})}}catch(G){j.warn(`Failed to load skill from ${W}: ${G instanceof Error?G.message:String(G)}`)}}return Z}async loadHooks(O){let $=M.join(O,"hooks","hooks.json");try{let Q=await A.readFile($,"utf-8"),Z=JSON.parse(Q);return Z.hooks||Z}catch{return}}async loadMcpConfig(O,$){let Q=M.join(O,".mcp.json");try{let Z=await A.readFile(Q,"utf-8"),J=JSON.parse(Z),L=p1(J);if(!L.success){j.warn(`Invalid .mcp.json in ${O}: ${L.error}`);return}let K="mcpServers"in J?J.mcpServers:J,W={};for(let[G,Y]of Object.entries(K)){let X=e1($,G);W[X]=Y}return W}catch{return}}async loadLspConfig(O,$){let Q=M.join(O,".lsp.json");try{let Z=JSON.parse(await A.readFile(Q,"utf8")),J=Z&&typeof Z==="object"&&!Array.isArray(Z)&&"lspServers"in Z?Z.lspServers:Z,L=$O(J),K={};for(let[W,G]of Object.entries(L)){let Y=(X)=>X.replaceAll("${CLAUDE_PLUGIN_ROOT}",O).replaceAll("${BLADE_PLUGIN_ROOT}",O);K[`plugin:${$}:${W}`]={...G,command:Y(G.command),args:G.args?.map(Y),env:G.env?Object.fromEntries(Object.entries(G.env).map(([X,H])=>[X,Y(H)])):void 0}}return K}catch(Z){if(Z.code!=="ENOENT")j.warn(`Invalid .lsp.json in ${O}: ${Z instanceof Error?Z.message:String(Z)}`);return}}async scanMarkdownFiles(O){let $=[],Q=async(Z)=>{let J=await A.readdir(Z,{withFileTypes:!0});for(let L of J){let K=M.join(Z,L.name);if(L.isDirectory())await Q(K);else if(L.name.endsWith(".md"))$.push(K)}};return await Q(O),$}async dirExists(O){try{return(await A.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 $=I$();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=[],Z=[];try{await A.access(O)}catch{return{plugins:Q,errors:Z}}let J=await A.readdir(O,{withFileTypes:!0});for(let L of J){if(!L.isDirectory())continue;let K=M.join(O,L.name);try{let W=await this.loadPlugin(K,$);Q.push(W)}catch(W){Z.push({path:K,error:W instanceof Error?W.message:String(W)})}}return{plugins:Q,errors:Z}}}class T{static instances=new Map;plugins=new Map;loader=new j0;initialized=!1;workspaceRoot="";cliPluginDirs=[];enabledSettings={};sourcePolicy={...S0.pluginSourcePolicy};marketplaces={};constructor(O){this.workspaceRoot=O}static getInstance(O=B()){let $=n0.resolve(O),Q=T.instances.get($);if(!Q)Q=new T($),T.instances.set($,Q);return Q}static resetInstance(){T.instances.clear()}static releaseInstance(O,$){let Q=n0.resolve(O),Z=T.instances.get(Q);if(!Z||$&&Z!==$)return!1;return T.instances.delete(Q)}static getExistingInstance(O=B()){return T.instances.get(n0.resolve(O))}static getInitializedInstances(){return Array.from(T.instances.values()).filter((O)=>O.isInitialized())}async initialize(O,$=[]){this.workspaceRoot=O,this.cliPluginDirs=$,this.enabledSettings=await I0.getInstance().loadWorkspacePluginSettings(O),await this.refreshPolicyContext();let Q=[],Z=[];for(let G of $)try{let Y=await this.loader.loadPlugin(G,"cli");this.plugins.set(Y.manifest.name,Y),Q.push(Y),j.info(`Loaded CLI plugin: ${Y.manifest.name} from ${G}`)}catch(Y){Z.push({path:G,error:Y instanceof Error?Y.message:String(Y)}),j.warn(`Failed to load CLI plugin from ${G}: ${Y}`)}let J=(await D.getInstance().getStatus(O)).state==="trusted",L=j0.getPluginDirs(O).filter((G)=>G.source!=="project"||J),K=!1,W=async()=>{if(K)return;K=!0;let G=s0(),Y=await G.listInstallationRecords();for(let X of Y)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 V=this.plugins.get(H.manifest.name);if(!V||V.source==="user")this.plugins.set(H.manifest.name,H),Q.push(H)}catch(H){Z.push({path:X.installPath,code:"IO_ERROR",error:H instanceof Error?H.message:String(H)})}};for(let{path:G,source:Y}of L){if(Y==="project")await W();let X=await this.loader.discoverPluginsInDir(G,Y);for(let H of X.plugins){let V=this.plugins.get(H.manifest.name);if(!V||V.source!=="cli")this.plugins.set(H.manifest.name,H),Q.push(H)}Z.push(...X.errors)}await W(),this.applyEnabledSettings();for(let G of this.plugins.values()){if(G.status!=="error")continue;Z.push({path:G.basePath,code:G.compatibilityIssues?.some((Y)=>Y.code==="source-policy")?"SOURCE_POLICY_BLOCKED":G.compatibilityIssues?.some((Y)=>Y.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`+(Z.length>0?`, ${Z.length} errors`:"")),{plugins:Array.from(this.plugins.values()),errors:Z}}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 I0.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{a(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}]}}h1(Array.from(this.plugins.values()))}async refreshPolicyContext(){this.sourcePolicy=await I0.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 Z of Q.commands)if(Z.namespacedName===O)return Z}let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let Z of Q.commands)if(Z.originalName===O)$.push(Z)}if($.length===1)return $[0];return}findSkill(O){for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let Z of Q.skills)if(Z.namespacedName===O)return Z}let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let Z of Q.skills)if(Z.originalName===O)$.push(Z)}if($.length===1)return $[0];return}findAgent(O){for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let Z of Q.agents)if(Z.namespacedName===O)return Z}let $=[];for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let Z of Q.agents)if(Z.originalName===O)$.push(Z)}if($.length===1)return $[0];return}hasCommandConflict(O){let $=0;for(let Q of this.plugins.values()){if(Q.status!=="active")continue;for(let Z of Q.commands)if(Z.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 Z of Q.commands)if(Z.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,Z=0,J=0;for(let L of this.plugins.values())if(L.status==="active")O++,Q+=L.commands.length,Z+=L.skills.length,J+=L.agents.length;else $++;return{total:this.plugins.size,active:O,inactive:$,commands:Q,skills:Z,agents:J}}formatPluginList(){if(this.getAll().length===0)return"没有已加载的插件。";let $=[],Q=this.getBySource();if(Q.cli.length>0){$.push("## CLI 指定的插件");for(let Z of Q.cli){let J=Z.status==="inactive"?" (禁用)":"";$.push(`- **${Z.manifest.name}** v${Z.manifest.version}${J}`),$.push(` ${Z.manifest.description}`)}$.push("")}if(Q.project.length>0){$.push("## 项目级插件");for(let Z of Q.project){let J=Z.status==="inactive"?" (禁用)":"";$.push(`- **${Z.manifest.name}** v${Z.manifest.version}${J}`),$.push(` ${Z.manifest.description}`)}$.push("")}if(Q.user.length>0){$.push("## 用户级插件");for(let Z of Q.user){let J=Z.status==="inactive"?" (禁用)":"";$.push(`- **${Z.manifest.name}** v${Z.manifest.version}${J}`),$.push(` ${Z.manifest.description}`)}}return $.join(`
|
|
724
|
+
`)}}function e(O=B()){return T.getInstance(O)}var O0=new Map,ZO=Object.values(B0);function o0(O,$){let Q=QO.resolve(O),Z=O0.get(Q);if(!Z||$&&Z.registry!==$)return!1;return $0.getInstance().loadConfig(Z.base,Q),O0.delete(Q),!0}function i0(O){let $={...O};for(let Q of ZO)$[Q]=(O[Q]??[]).map((Z)=>({...Z,matcher:Z.matcher?{...Z.matcher}:void 0,hooks:Z.hooks.map((J)=>({...J}))}));return $}class a0{commandRegistry;hookManager;skillRegistry;subagentRegistry;workspaceRoot;constructor(O=B()){this.workspaceRoot=QO.resolve(O),this.commandRegistry=z.getInstance(this.workspaceRoot),this.hookManager=$0.getInstance(),this.skillRegistry=m({cwd:this.workspaceRoot}),this.subagentRegistry=s(this.workspaceRoot)}clearAllPluginResources(){this.commandRegistry.clearPluginCommands(),this.skillRegistry.clearPluginSkills(),this.subagentRegistry.clearPluginAgents(),o0(this.workspaceRoot,e(this.workspaceRoot)),j.debug("Cleared all plugin resources from subsystems")}async integrateAll(){let O=e(this.workspaceRoot),$=O.getActive(),Q=[],Z=[],J=0,L=0,K=0,W=0,G=0;for(let Y of $){let X=this.integratePlugin(Y);Q.push(X),J+=X.commandsRegistered,L+=X.skillsRegistered,K+=X.agentsRegistered,W+=X.mcpServersRegistered,G+=X.lspServersRegistered,Z.push(...X.errors)}if(this.integrateHooks($,O),J+L+K>0)j.info(`Plugin integration complete: ${J} commands, ${L} skills, ${K} agents, ${W} MCP servers, ${G} LSP servers`);return{plugins:Q,totalCommands:J,totalSkills:L,totalAgents:K,totalMcpServers:W,errors:Z}}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 Q=O.filter((K)=>K.hooks);if(Q.length===0){o0(this.workspaceRoot,$);return}let Z=i0(this.hookManager.getConfig(this.workspaceRoot));if(!O0.has(this.workspaceRoot))O0.set(this.workspaceRoot,{base:i0(Z),registry:$});let J=O0.get(this.workspaceRoot)?.base??Z,L=i0(J);L.enabled=!0;for(let K of ZO){let W=J.enabled?[...J[K]??[]]:[],G=[];for(let Y of Q)for(let X of Y.hooks?.[K]??[])G.push({...X,name:X.name?`plugin:${Y.manifest.name}:${X.name}`:`plugin:${Y.manifest.name}:${K}`,hooks:X.hooks.map((H)=>({...H,source:{kind:"plugin",pluginName:Y.manifest.name,pluginSource:Y.source,pluginRoot:Y.basePath}}))});L[K]=[...W,...G]}this.hookManager.loadConfig(L,this.workspaceRoot)}integrateMcp(O){return Object.keys(O.mcpServers??{}).length}}async function t0(O=B()){return new a0(O).integrateAll()}function M0(O=B()){new a0(O).clearAllPluginResources()}function JO(O=B(),$){o0(O,$)}import{createHash as B$}from"node:crypto";import*as C from"node:fs/promises";import M$ from"node:os";import N from"node:path";import z$ from"gray-matter";var z0=32,KO=24576,LO=16384,qO=131072,b0=4,GO=80,WO=256,XO=/^[a-z0-9][a-z0-9._-]{0,63}$/,b$=P("Agent");function r0(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 A$(O,$,Q){let Z=typeof O==="string"?O.trim():"",J=$.split(/\r?\n/).map((K)=>K.trim()).find(Boolean)?.replace(/^#+\s*/,"")??"",L=Z||J||Q;if(L.length>WO)throw Error(`Description exceeds ${WO} characters`);if(r0(L))throw Error("Description contains hidden control characters");return L}function x$(O,$){let Q=typeof O==="string"&&O.trim()?O.trim():$;if(Q.length>GO)throw Error(`Name exceeds ${GO} characters`);if(r0(Q))throw Error("Name contains hidden control characters");return Q}function _$(O){let Q=O.replace(/\.md$/i,"").split(N.sep);if(Q.length===0||Q.length>b0||Q.some((Z)=>!XO.test(Z)))throw Error(`Style path must use 1-${b0} lowercase safe segments`);return Q.join(":")}async function T$(O){let $;try{$=await C.lstat(O)}catch(L){if(L.code==="ENOENT")return[];throw L}if($.isSymbolicLink())throw Error(`Output style root cannot be a symlink: ${O}`);if(!$.isDirectory())return[];let Q=await C.realpath(O),Z=[],J=async(L,K)=>{if(K>b0)throw Error(`Output style directory exceeds depth ${b0}`);let W=await C.readdir(L,{withFileTypes:!0});W.sort((G,Y)=>G.name.localeCompare(Y.name));for(let G of W){let Y=N.join(L,G.name);if(G.isSymbolicLink())throw Error(`Output style path cannot be a symlink: ${Y}`);if(G.isDirectory()){await J(Y,K+1);continue}if(!G.isFile()||!G.name.toLowerCase().endsWith(".md"))continue;let X=await C.realpath(Y),H=N.relative(Q,X);if(!H||H.startsWith("..")||N.isAbsolute(H))throw Error(`Output style path escapes its source root: ${Y}`);if(Z.push(X),Z.length>z0)throw Error(`Output style catalog exceeds ${z0} files`)}};return await J(Q,1),Z}async function D$(O,$,Q){let Z=await C.lstat(Q);if(!Z.isFile()||Z.isSymbolicLink())throw Error("Output style must be a regular file");if(Z.size>KO)throw Error(`Output style exceeds ${KO} bytes`);let J=await C.readFile(Q,"utf8"),L=z$(J),K=L.content.trim(),W=Buffer.byteLength(K);if(W===0)throw Error("Output style prompt must not be empty");if(W>LO)throw Error(`Output style prompt exceeds ${LO} bytes`);if(r0(K))throw Error("Output style prompt contains hidden control characters");let G=N.relative($,Q),Y=_$(G);return{id:`${O.idPrefix}:${Y}`,name:x$(L.data.name,Y),description:A$(L.data.description,K,`Custom ${Y} communication style`),source:O.source,contentSha256:B$("sha256").update(K,"utf8").digest("hex"),prompt:K}}async function w$(O,$){let Q=await T$(O.directory);if(Q.length===0)return;let Z=await C.realpath(O.directory);for(let J of Q)try{let L=await D$(O,Z,J);$.set(L.id,L)}catch(L){b$.warn(`[CommunicationStyles] Ignoring ${O.source} style: ${L instanceof Error?L.message:String(L)}`)}}function N$(O,$,Q,Z){let J=[{directory:N.join(Z,".claude","output-styles"),source:"user",idPrefix:"user"},{directory:N.join(Z,".blade","output-styles"),source:"user",idPrefix:"user"}];if($)J.push({directory:N.join(O,".claude","output-styles"),source:"project",idPrefix:"project"},{directory:N.join(O,".blade","output-styles"),source:"project",idPrefix:"project"});for(let L of Q){if(L.status!=="active"||!XO.test(L.manifest.name))continue;J.push({directory:N.join(L.basePath,"output-styles"),source:"plugin",idPrefix:`plugin:${L.manifest.name}`})}return J}async function e0(O,$){let Q=new Map;for(let J of N$(N.resolve(O),$.projectTrusted,$.plugins??[],$.homeDirectory??M$.homedir()))await w$(J,Q);let Z=0;for(let J of Q.values())Z+=Buffer.byteLength(J.prompt??"");if(Q.size>z0)throw Error(`Output style catalog exceeds ${z0} styles`);if(Z>qO)throw Error(`Output style catalog exceeds ${qO} bytes`);return new YO([...Q.values()])}import{createHash as C$}from"node:crypto";import{existsSync as A0,realpathSync as E$}from"node:fs";import*as f from"node:fs/promises";import I from"node:path";import v$ from"fast-glob";import R$ from"gray-matter";import IO from"picomatch";var UO=128,FO=32768,HO=262144,y$=49152,h$=32768,f$=24,SO=16,k$=256,P$=P("Agent"),m$=["**/CLAUDE.md","**/.claude/CLAUDE.md","**/CLAUDE.local.md","**/AGENTS.md","**/AGENTS.override.md","**/BLADE.md","**/.claude/rules/**/*.md","**/.blade/rules/**/*.md"],p$=["**/.git/**","**/node_modules/**","**/.venv/**","**/dist/**","**/build/**","**/coverage/**","**/target/**"];function x0(O){return C$("sha256").update(O,"utf8").digest("hex")}function BO(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 Z1(O){return O.split(I.sep).join("/")}function O1(O){let $=I.resolve(O),Q=[];while(!0)try{return I.join(E$($),...Q.reverse())}catch(Z){if(Z.code!=="ENOENT")return I.resolve(O);let J=I.dirname($);if(J===$)return I.resolve(O);Q.push(I.basename($)),$=J}}function d$(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 Z=I.dirname($);if(Z===$)return Q??I.resolve(O);$=Z}}function J1(O){return Boolean(O)&&O!==".."&&!O.startsWith("../")}function $1(O,$){return O===""||$===O||$.startsWith(`${O}/`)}function VO(O){return O.replaceAll("&","&").replaceAll('"',""").replaceAll("<","<").replaceAll(">",">")}function g$(O){let $=typeof O==="string"?O.split(/[,\n]/u):Array.isArray(O)?O:[],Q=[...new Set($.filter((Z)=>typeof Z==="string").map((Z)=>Z.trim().replaceAll("\\","/")).filter(Boolean))];if(Q.length===0||Q.every((Z)=>Z==="**"))return;if(Q.length>SO)throw Error(`paths exceeds ${SO} patterns`);for(let Z of Q){if(Z.length>k$||I.posix.isAbsolute(Z)||Z.split("/").includes("..")||BO(Z))throw Error(`Unsafe project rule path pattern: ${Z.slice(0,80)}`);IO(Z,{dot:!0})}return Q}function u$(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 Z of[".claude/rules/",".blade/rules/"]){let J=O.indexOf(Z);if(J>=0)return{source:"project",kind:"rule",scopeDirectory:O.slice(0,J).replace(/\/$/u,""),priority:Z.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 Q1(O,$){let Q=O.scopeDirectory?O.scopeDirectory.split("/").length:0,Z=$.scopeDirectory?$.scopeDirectory.split("/").length:0;return Q-Z||O.priority-$.priority||O.relativePath.localeCompare($.relativePath)}function c$(O){return{id:O.id,relativePath:O.relativePath,source:O.source,contentSha256:O.contentSha256}}function Q0(O,$,Q){let Z=O.map((K)=>{let W=K.conditional?' conditional="true"':"";return`<instruction-file path="${VO(K.relativePath)}" source="${K.source}" sha256="${K.contentSha256}"${W}>
|
|
725
|
+
${K.content}
|
|
726
|
+
</instruction-file>`}),J=Q==="contextual-project-instructions"&&$.length>0?` trigger-paths="${VO($.join(","))}"`:"",L=Z.length>0?`<${Q}${J}>
|
|
727
|
+
${Z.join(`
|
|
728
|
+
|
|
729
|
+
`)}
|
|
730
|
+
</${Q}>`:"";return{content:L,files:[...O],references:O.map(c$),triggerPaths:[...$],contentBytes:Buffer.byteLength(L,"utf8"),provenanceSha256:x0(O.map((K)=>`${K.id}:${K.contentSha256}`).join(`
|
|
731
|
+
`))}}function jO(O,$){let Q=$,Z=[];for(let J=O.length-1;J>=0;J-=1){let L=O[J];if(!L)continue;let K=Buffer.byteLength(L.content,"utf8");if(K>Q)continue;Z.push(L),Q-=K}return Z.reverse()}class h{projectRoot;catalogSha256;definitions;byId;constructor(O,$=[]){this.projectRoot=O;this.projectRoot=O1(O),this.definitions=Object.freeze([...$].sort(Q1).map((Q)=>Object.freeze({...Q}))),this.byId=new Map(this.definitions.map((Q)=>[Q.id,Q])),this.catalogSha256=x0(this.definitions.map((Q)=>`${Q.id}:${Q.contentSha256}`).join(`
|
|
732
|
+
`))}static empty(O){return new h(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 h(this.projectRoot,this.definitions.map((O)=>({...O,patterns:O.patterns?[...O.patterns]:void 0})))}staticRules(O,$=y$){if(this.definitions.length===0)return Q0([],[],"project-instructions");let Q=this.relativeSourcePath(O),Z=this.definitions.filter((J)=>!J.conditional&&$1(J.scopeDirectory,Q));return Q0(jO(Z,$),[],"project-instructions")}contextualRules(O,$,Q,Z=h$){if(this.definitions.length===0)return Q0([],[],"contextual-project-instructions");let J=this.relativeSourcePath(O),L=[...new Set($.map((G)=>Z1(I.relative(this.projectRoot,O1(G)))).filter(J1))].sort(),K=new Map;for(let G of L)for(let Y of this.definitions){if(Q.has(Y.id))continue;if(!$1(Y.scopeDirectory,G))continue;if(Y.conditional){let X=I.posix.relative(Y.scopeDirectory||".",G);if(!Y.patterns?.some((H)=>IO(H,{dot:!0})(X)))continue}else if($1(Y.scopeDirectory,J))continue;K.set(Y.id,Y)}let W=jO([...K.values()].sort(Q1),Z);return Q0(W,L,"contextual-project-instructions")}hydrate(O){let $=O.map((Q)=>{let Z=this.byId.get(Q.id);if(!Z||Z.contentSha256!==Q.contentSha256||Z.relativePath!==Q.relativePath||Z.source!==Q.source)throw Error(`Project rule provenance mismatch: ${Q.relativePath}`);return Z});return Q0([...new Map($.map((Q)=>[Q.id,Q])).values()].sort(Q1),[],"contextual-project-instructions")}relativeSourcePath(O){let $=Z1(I.relative(this.projectRoot,O1(O)));if($&&!J1($))throw Error("Project instruction source is outside the catalog root");return $}}async function l$(O,$){let Q=await f.lstat($);if(!Q.isFile()||Q.isSymbolicLink())throw Error("Project rule must be a regular file");if(Q.size>FO)throw Error(`Project rule exceeds ${FO} bytes`);let Z=await f.realpath($),J=Z1(I.relative(O,Z));if(!J1(J))throw Error("Project rule escapes its repository root");let L=await f.readFile(Z,"utf8");if(BO(L))throw Error("Project rule contains hidden control characters");let K=R$(L),W=K.content.trim();if(!W)throw Error("Project rule content must not be empty");let G=g$(K.data.paths),Y=u$(J),X=x0(JSON.stringify({relativePath:J,patterns:G??[],content:W}));return{id:`${Y.source}:${x0(J).slice(0,20)}`,relativePath:J,source:Y.source,kind:Y.kind,scopeDirectory:Y.scopeDirectory,priority:Y.priority,conditional:Boolean(G),...G?{patterns:G}:{},content:W,contentSha256:X}}async function MO(O,$){let Q=d$(O);if(!$.projectTrusted)return h.empty(Q);let Z=await f.realpath(Q),J=v$.stream([...m$],{cwd:Z,absolute:!0,dot:!0,deep:f$,onlyFiles:!0,followSymbolicLinks:!1,unique:!0,ignore:[...p$]}),L=[];for await(let Y of J)if(L.push(Y.toString()),L.length>UO)throw Error(`Project rule catalog exceeds ${UO} files`);L.sort();let K=new Set(L.filter((Y)=>I.basename(Y)==="AGENTS.override.md").map((Y)=>I.dirname(Y))),W=[],G=0;for(let Y of L){if(I.basename(Y)==="AGENTS.md"&&K.has(I.dirname(Y)))continue;try{let X=await l$(Z,Y);if(G+=Buffer.byteLength(X.content,"utf8"),G>HO)throw Error(`Project rule catalog exceeds ${HO} bytes`);W.push(X)}catch(X){P$.warn(`[ProjectRules] Ignoring rule: ${X instanceof Error?X.message:String(X)}`)}}return new h(Z,W)}var zO=32,_0=64;class bO extends Error{capacity=_0;constructor(){super(`Workspace agent resource capacity is full (${_0})`);this.name="WorkspaceAgentResourceCapacityError"}}var b=new Map,K1=0,AO=[];async function s$(O){let $=await D.getInstance().getStatus(O.workspaceRoot);O.communicationStyles=await e0(O.workspaceRoot,{projectTrusted:$.state==="trusted",plugins:O.plugins.getActive()})}function n$(O){let $={...O};for(let Q of Object.values(B0))$[Q]=(O[Q]??[]).map((Z)=>({...Z,matcher:Z.matcher?{...Z.matcher}:void 0,hooks:Z.hooks.map((J)=>({...J}))}));return $}function u2(O){AO=[...new Set(O.map(($)=>T0.resolve($)))]}function xO(O,$){JO(O,$.plugins),x.releaseInstance(O,$.subagents),R.releaseInstance({cwd:O},$.skills),z.releaseInstance(O,$.commands),T.releaseInstance(O,$.plugins)}function _O(O){xO(O.workspaceRoot,O)}function Z0(){while(b.size>zO){let O=!1;for(let[$,Q]of b){if(!Q.resources||Q.users>0)continue;if(b.get($)!==Q)continue;b.delete($),_O(Q.resources),O=!0;break}if(!O)return}}function i$(O){setImmediate(()=>{O.users=Math.max(0,O.users-1),Z0()}).unref?.()}async function o$(O,$){let Q=await D.getInstance().getStatus(O),Z=s(O),J=m({cwd:O}),L=z.getInstance(O),K=e(O);try{if(Z.getAllNames().length===0)Z.loadFromStandardLocations(Q.state==="trusted");await Promise.all([J.initialize(),L.isInitialized()?void 0:L.initialize(O),K.isInitialized()?void 0:K.initialize(O,[...$.cliPluginDirs??AO])]),M0(O),await t0(O);let W=await e0(O,{projectTrusted:Q.state==="trusted",plugins:K.getActive()}),G=await MO(O,{projectTrusted:Q.state==="trusted"});return{workspaceRoot:O,subagents:Z,skills:J,commands:L,plugins:K,communicationStyles:W,projectRules:G}}catch(W){throw xO(O,{subagents:Z,skills:J,commands:L,plugins:K}),W}}function a$(O,$){let Q={generation:K1,users:0};return Q.promise=o$(O,$).then((Z)=>{if(Q.generation!==K1||b.get(O)!==Q)return _O(Z),Z;return Q.resources=Z,Z0(),Z},(Z)=>{if(b.get(O)===Q)b.delete(O);throw Z}),b.set(O,Q),Q}async function TO(O,$){let Q=T0.resolve(O),Z=b.get(Q);if(!Z){if(Z0(),b.size>=_0)throw new bO;Z=a$(Q,$)}else b.delete(Q),b.set(Q,Z);Z.users++;let J;try{J=await Z.promise}catch(L){throw Z.users=Math.max(0,Z.users-1),Z0(),L}return{entry:Z,resources:J}}async function DO(O,$){if(!$)return;M0(O.workspaceRoot),await t0(O.workspaceRoot),await s$(O)}async function c2(O,$={}){let Q=await TO(O,$);try{return await DO(Q.resources,$.reconcilePlugins),Q.resources}finally{i$(Q.entry)}}async function l2(O,$,Q={}){let Z=await TO(O,Q);try{return await DO(Z.resources,Q.reconcilePlugins),await $(Z.resources)}finally{Z.entry.users=Math.max(0,Z.entry.users-1),Z0()}}function s2(){let O=0,$=0;for(let Q of b.values()){if(Q.resources)O++;$+=Q.users}return{capacity:zO,activeCapacity:_0,entries:b.size,initialized:O,inFlight:b.size-O,activeUsers:$}}function n2(O){let $="projectRoot"in O?O.projectRoot:O.workspaceRoot;return{projectRoot:T0.resolve($),subagents:O.subagents.snapshot(),skills:O.skills.snapshot(),commands:O.commands.snapshot(),communicationStyles:O.communicationStyles?.snapshot()??L1.snapshot(),projectRules:O.projectRules?.snapshot()??h.empty($),hooks:n$("hooks"in O&&O.hooks?O.hooks:$0.getInstance().getConfig($))}}function i2(O){let $=T0.resolve(O);return{projectRoot:$,subagents:new x,skills:new R,commands:z.empty($),hooks:{enabled:!1},communicationStyles:L1.snapshot(),projectRules:h.empty($)}}function o2(){K1++;for(let[O,$]of b)if($.resources)M0(O);b.clear(),x.resetInstances(),R.resetInstance(),z.resetInstance(),T.resetInstance()}
|
|
733
|
+
export{O4 as Ag,$4 as Bg,Q4 as Cg,H1 as Dg,q4 as Eg,G4 as Fg,s as Gg,x4 as Hg,m as Ig,z as Jg,s0 as Kg,j0 as Lg,T as Mg,e as Ng,t0 as Og,M0 as Pg,h as Qg,zO as Rg,_0 as Sg,bO as Tg,s$ as Ug,u2 as Vg,c2 as Wg,l2 as Xg,s2 as Yg,n2 as Zg,i2 as _g,o2 as $g};
|