claude-setup 2.0.0 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/init.js +43 -22
- package/dist/marketplace.js +110 -66
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -84,35 +84,56 @@ tools: Bash
|
|
|
84
84
|
model: haiku
|
|
85
85
|
---
|
|
86
86
|
|
|
87
|
-
You are a marketplace fetch agent.
|
|
87
|
+
You are a marketplace fetch agent. You receive a prompt containing marketplace instructions with bash code blocks. Your job is to RUN those bash commands, use the output to find matching files, download them, and confirm the install.
|
|
88
88
|
|
|
89
|
-
##
|
|
89
|
+
## CRITICAL RULES — read before doing anything
|
|
90
90
|
|
|
91
|
-
You
|
|
91
|
+
1. **You MUST run curl commands.** Your entire purpose is to fetch from remote catalogs. If you finish without running at least 3 curl commands, you have failed.
|
|
92
|
+
2. **NEVER skip to custom creation early.** You may only create custom content after you have attempted ALL catalog steps with real curl calls and each one returned empty or errored.
|
|
93
|
+
3. **Use relative paths only.** Never prepend \`cd /some/path &&\` to commands. You are already in the project directory.
|
|
94
|
+
4. **Pipe curl output through node parsers.** Never let raw JSON/README enter the context. Every curl MUST pipe through \`| node -e "..."\` to extract only the lines you need.
|
|
95
|
+
5. **A curl failure or empty result means: try the next step.** It does NOT mean stop. Log what failed in one line, then continue to the next step.
|
|
96
|
+
6. **Fill in template variables.** The instructions contain placeholder variables like \`CATEGORY="09-meta-orchestration"\` and \`SKILL_DIR="matched-directory"\`. Replace these with ACTUAL values from the previous step's output.
|
|
97
|
+
7. **Verify downloads.** After every curl -o, check the file exists and has >50 characters of content. If empty or stub: delete it, log failure, continue.
|
|
92
98
|
|
|
93
|
-
##
|
|
99
|
+
## How to execute the instructions
|
|
94
100
|
|
|
95
|
-
|
|
101
|
+
Your prompt contains numbered STEPs with bash code blocks. For each STEP:
|
|
96
102
|
|
|
97
|
-
1.
|
|
98
|
-
2.
|
|
99
|
-
3.
|
|
100
|
-
4.
|
|
101
|
-
5.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
- \`CREATED .claude/agents/<file> <bytes>b\` (if custom-created after all catalogs exhausted) or
|
|
105
|
-
- \`CREATED .claude/skills/<dir>/SKILL.md <bytes>b\` or
|
|
106
|
-
- \`FAILED no match in any catalog and custom creation not possible\`
|
|
103
|
+
1. Run the first bash command (the catalog listing/search)
|
|
104
|
+
2. Read the output — it shows available files/directories
|
|
105
|
+
3. Pick the entry that best matches the user's request
|
|
106
|
+
4. Substitute that entry name into the download command
|
|
107
|
+
5. Run the download command
|
|
108
|
+
6. Verify the downloaded file has real content
|
|
109
|
+
7. If it works → return the success line. If not → go to next STEP.
|
|
107
110
|
|
|
108
|
-
##
|
|
111
|
+
## Return format
|
|
109
112
|
|
|
110
|
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
113
|
+
Return exactly ONE line:
|
|
114
|
+
- \`INSTALLED .claude/agents/<file> <bytes>b\` or
|
|
115
|
+
- \`INSTALLED .claude/skills/<dir>/SKILL.md <bytes>b\` or
|
|
116
|
+
- \`CREATED .claude/agents/<file> <bytes>b\` (only after ALL catalogs exhausted) or
|
|
117
|
+
- \`CREATED .claude/skills/<dir>/SKILL.md <bytes>b\` or
|
|
118
|
+
- \`FAILED no match in any catalog and custom creation not possible\`
|
|
119
|
+
|
|
120
|
+
## Example of correct behavior
|
|
121
|
+
|
|
122
|
+
If the instructions say:
|
|
123
|
+
\`\`\`bash
|
|
124
|
+
curl -sf "https://api.github.com/repos/X/Y/contents/categories/03-infrastructure" | node -e "..."
|
|
125
|
+
\`\`\`
|
|
126
|
+
And the output shows: \`docker-compose-agent.md\`, \`kubernetes-agent.md\`, \`terraform-agent.md\`
|
|
127
|
+
|
|
128
|
+
Then for the download command that says \`AGENT_FILE="matched-agent.md"\`, you substitute:
|
|
129
|
+
\`\`\`bash
|
|
130
|
+
AGENT_FILE="kubernetes-agent.md"
|
|
131
|
+
curl -sf "https://raw.githubusercontent.com/X/Y/main/categories/03-infrastructure/kubernetes-agent.md" -o ".claude/agents/kubernetes-agent.md"
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
Then verify: \`wc -c ".claude/agents/kubernetes-agent.md"\`
|
|
135
|
+
|
|
136
|
+
Now execute the instructions in your prompt. Start with STEP 1 immediately.
|
|
116
137
|
`, "utf8");
|
|
117
138
|
}
|
|
118
139
|
function installTokenHook(cwd = process.cwd()) {
|
package/dist/marketplace.js
CHANGED
|
@@ -235,6 +235,49 @@ export function classifyRequest(input) {
|
|
|
235
235
|
agentCategories: matchAgentCategories(input),
|
|
236
236
|
};
|
|
237
237
|
}
|
|
238
|
+
// ── Query keyword extraction (for inline filtering in node parsers) ─────
|
|
239
|
+
/** Extract meaningful search keywords from user input for inline node filtering */
|
|
240
|
+
function buildQueryRegex(input) {
|
|
241
|
+
const stopWords = new Set(["a", "an", "the", "and", "or", "for", "to", "in", "of", "with", "add", "install", "setup", "get", "find", "need", "want", "skill", "agent", "plugin", "tool"]);
|
|
242
|
+
const words = input.toLowerCase()
|
|
243
|
+
.replace(/[^a-z0-9\s-]/g, "")
|
|
244
|
+
.split(/\s+/)
|
|
245
|
+
.filter(w => w.length > 2 && !stopWords.has(w));
|
|
246
|
+
if (words.length === 0)
|
|
247
|
+
return "."; // match everything if no keywords
|
|
248
|
+
return words.join("|");
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Build a universal section-aware README parser as an inline node -e script.
|
|
252
|
+
* Rules (no hardcoding):
|
|
253
|
+
* 1. Split README into sections by any heading (## / ### / **Bold**)
|
|
254
|
+
* 2. Find sections whose heading matches the query keywords
|
|
255
|
+
* 3. Extract ALL links from matching sections (relative ./ AND absolute github.com)
|
|
256
|
+
* 4. If no section matches, fall back to all links in the entire README
|
|
257
|
+
* This works for any README structure — ComposioHQ, VoltAgent, or anything else.
|
|
258
|
+
*/
|
|
259
|
+
function buildSectionAwareParser(queryRegex) {
|
|
260
|
+
// Each part is carefully escaped for: TypeScript template → bash double-quotes → node -e
|
|
261
|
+
return (`const t=require('fs').readFileSync(0,'utf8');` +
|
|
262
|
+
`const q=/${queryRegex}/i;` +
|
|
263
|
+
// Split by ## headings, ### headings, and **Bold** subsection headers
|
|
264
|
+
`const secs=t.split(/\\n(?=#{2,3}\\s|\\*\\*[A-Z])/);` +
|
|
265
|
+
// 1st pass: sections whose heading matches query
|
|
266
|
+
`let hit=secs.filter(s=>q.test(s.split('\\n')[0]));` +
|
|
267
|
+
// 2nd pass: if no heading matched, find sections that have a link whose name matches
|
|
268
|
+
`if(hit.length===0)hit=secs.filter(s=>{const lk=/\\[([^\\]]+)\\]/g;let x;while((x=lk.exec(s))!=null){if(q.test(x[1]))return true}return false});` +
|
|
269
|
+
// Use matching sections, or fall back to entire README
|
|
270
|
+
`const src=hit.length>0?hit.join('\\n'):t;` +
|
|
271
|
+
// Extract all links — relative (./dir/) and absolute (github.com) and SKILL.md
|
|
272
|
+
`const re=/\\[([^\\]]+)\\]\\(([^)]+)\\)/g;let m;const r=[];` +
|
|
273
|
+
`while((m=re.exec(src))!=null){` +
|
|
274
|
+
`const u=m[2];` +
|
|
275
|
+
`if(u.startsWith('./')||u.includes('github.com')||u.includes('SKILL.md'))` +
|
|
276
|
+
`r.push(m[1]+' | '+u)` +
|
|
277
|
+
`}` +
|
|
278
|
+
`r.slice(0,10).forEach(x=>console.log(x));` +
|
|
279
|
+
`if(r.length===0)console.log('NO_README_MATCHES')`);
|
|
280
|
+
}
|
|
238
281
|
// ── Marketplace instruction builder ─────────────────────────────────────
|
|
239
282
|
// Implements Rule 6 (4-catalog exhaustion) and Rule 7 (3-stage fetch).
|
|
240
283
|
// Agent requests route to VoltAgent subagents first.
|
|
@@ -293,6 +336,7 @@ export function buildMarketplaceInstructions(input) {
|
|
|
293
336
|
// ── Agent pipeline (VoltAgent subagents → skills fallback) ──────────
|
|
294
337
|
function buildAgentPipeline(lines, input, agentCategories, categoryFilter) {
|
|
295
338
|
const safeName = input.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
339
|
+
const queryRegex = buildQueryRegex(input);
|
|
296
340
|
const targetCategories = agentCategories.length > 0
|
|
297
341
|
? agentCategories
|
|
298
342
|
: ["09-meta-orchestration"];
|
|
@@ -333,47 +377,39 @@ function buildAgentPipeline(lines, input, agentCategories, categoryFilter) {
|
|
|
333
377
|
}
|
|
334
378
|
}
|
|
335
379
|
lines.push(`**1c. From ALL file lists above (primary + adjacent), pick the BEST match for "${input}".**`);
|
|
336
|
-
lines.push(`
|
|
380
|
+
lines.push(`Pick the filename closest to the request. If multiple candidates: pick the first relevant one.`);
|
|
337
381
|
lines.push(``);
|
|
338
|
-
lines.push(`**1d. Download the matched agent file:**`);
|
|
382
|
+
lines.push(`**1d. Download and verify the matched agent file (single step):**`);
|
|
339
383
|
lines.push(`\`\`\`bash`);
|
|
340
384
|
lines.push(`# Replace CATEGORY and AGENT_FILE with actual values from 1a-1c`);
|
|
341
385
|
lines.push(`CATEGORY="${targetCategories[0]}"`);
|
|
342
386
|
lines.push(`AGENT_FILE="matched-agent.md"`);
|
|
343
387
|
lines.push(`mkdir -p ".claude/agents"`);
|
|
344
388
|
lines.push(`curl -sf "${VOLTAGENT_SUBAGENTS_RAW}/\${CATEGORY}/\${AGENT_FILE}" \\`);
|
|
345
|
-
lines.push(` -o ".claude/agents/\${AGENT_FILE}"`);
|
|
346
|
-
lines.push(`\`\`\``);
|
|
347
|
-
lines.push(``);
|
|
348
|
-
lines.push(`**1e. Verify the file has real content (rule 7 — must not be empty):**`);
|
|
349
|
-
lines.push(`\`\`\`bash`);
|
|
350
|
-
lines.push(`head -3 ".claude/agents/\${AGENT_FILE}"`);
|
|
351
|
-
lines.push(`wc -l ".claude/agents/\${AGENT_FILE}"`);
|
|
389
|
+
lines.push(` -o ".claude/agents/\${AGENT_FILE}" && wc -c ".claude/agents/\${AGENT_FILE}"`);
|
|
352
390
|
lines.push(`\`\`\``);
|
|
353
|
-
lines.push(`If
|
|
354
|
-
lines.push(`If
|
|
391
|
+
lines.push(`If >50 bytes: agent is installed. Return the success line.`);
|
|
392
|
+
lines.push(`If empty or 0 bytes: delete it, log the failure, try README fallback below.`);
|
|
355
393
|
lines.push(``);
|
|
356
394
|
lines.push(`**1f. README-driven fallback — if ALL category listings (primary + adjacent) returned SKIP:**`);
|
|
357
|
-
lines.push(`You MUST
|
|
395
|
+
lines.push(`You MUST run this before moving to STEP 2. The README contains the full agent listing.`);
|
|
358
396
|
lines.push(``);
|
|
359
397
|
lines.push(`\`\`\`bash`);
|
|
360
|
-
lines.push(`#
|
|
398
|
+
lines.push(`# Fetch README — parser finds sections matching your query, extracts all links`);
|
|
361
399
|
lines.push(`curl -sf "https://raw.githubusercontent.com/${VOLTAGENT_SUBAGENTS_REPO}/main/README.md" \\`);
|
|
362
|
-
lines.push(` | node -e "
|
|
363
|
-
`const re=/\\[([^\\]]+)\\]\\(([^)]+)\\)/g;let m;const r=[];` +
|
|
364
|
-
`while((m=re.exec(t))!==null){if(m[2].includes('.md')||m[2].includes('github.com'))r.push(m[1]+' | '+m[2])}` +
|
|
365
|
-
`r.slice(0,15).forEach(x=>console.log(x))"`);
|
|
400
|
+
lines.push(` | node -e "${buildSectionAwareParser(queryRegex)}"`);
|
|
366
401
|
lines.push(`\`\`\``);
|
|
367
402
|
lines.push(``);
|
|
403
|
+
lines.push(`From the output, pick the FIRST entry. Extract the URL (after " | "). Resolve it:`);
|
|
404
|
+
lines.push(`- If URL starts with \`./\`: it is relative to the repo root. Prepend \`https://raw.githubusercontent.com/${VOLTAGENT_SUBAGENTS_REPO}/main/\``);
|
|
405
|
+
lines.push(`- If URL contains \`github.com/OWNER/REPO/blob/BRANCH/\`: replace with \`raw.githubusercontent.com/OWNER/REPO/BRANCH/\``);
|
|
406
|
+
lines.push(`- If URL contains \`github.com/OWNER/REPO/tree/BRANCH/\`: use API \`api.github.com/repos/OWNER/REPO/contents/PATH\` to list files, then download the .md file`);
|
|
407
|
+
lines.push(`Then download:`);
|
|
368
408
|
lines.push(`\`\`\`bash`);
|
|
369
|
-
lines.push(`# Step 2: Pick the best entry for "${input}" from the list above.`);
|
|
370
|
-
lines.push(`# Extract the URL, convert github.com URLs to raw.githubusercontent.com (rule 2).`);
|
|
371
|
-
lines.push(`# Step 3: Download the resolved file`);
|
|
372
|
-
lines.push(`RESOLVED_URL="raw-url-from-step-2"`);
|
|
373
409
|
lines.push(`mkdir -p ".claude/agents"`);
|
|
374
|
-
lines.push(`curl -sf "
|
|
410
|
+
lines.push(`curl -sf "RESOLVED_URL" -o ".claude/agents/AGENT_FILE.md" && wc -c ".claude/agents/AGENT_FILE.md"`);
|
|
375
411
|
lines.push(`\`\`\``);
|
|
376
|
-
lines.push(`
|
|
412
|
+
lines.push(`If >50 bytes: installed. If empty or NO_README_MATCHES: continue to STEP 2.`);
|
|
377
413
|
lines.push(``);
|
|
378
414
|
// ── STEP 2: jeremylongshore community skills (fallback for agents) ─
|
|
379
415
|
lines.push(`---`);
|
|
@@ -423,6 +459,7 @@ function buildAgentPipeline(lines, input, agentCategories, categoryFilter) {
|
|
|
423
459
|
// ── Skill pipeline (community → VoltAgent skills → ComposioHQ) ──────
|
|
424
460
|
function buildSkillPipeline(lines, input, categoryFilter, saasMatches) {
|
|
425
461
|
const safeName = input.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
462
|
+
const queryRegex = buildQueryRegex(input);
|
|
426
463
|
// ── STEP 1: Official Anthropic plugins ────────────────────────────
|
|
427
464
|
lines.push(`---`);
|
|
428
465
|
lines.push(``);
|
|
@@ -457,37 +494,41 @@ function buildSkillPipeline(lines, input, categoryFilter, saasMatches) {
|
|
|
457
494
|
lines.push(`curl -sf \${GITHUB_TOKEN:+-H "Authorization: token \$GITHUB_TOKEN"} "${VOLTAGENT_SKILLS_API}" \\`);
|
|
458
495
|
lines.push(` | node -e "const d=JSON.parse(require('fs').readFileSync(0,'utf8'));` +
|
|
459
496
|
`if(Array.isArray(d)===false){console.log('SKIP');process.exit(0)}` +
|
|
460
|
-
`
|
|
497
|
+
`const q=/${queryRegex}/i;` +
|
|
498
|
+
`const all=d.filter(x=>x.type==='dir'&&(x.name[0]==='.')===false).map(x=>x.name);` +
|
|
499
|
+
`const matched=all.filter(n=>q.test(n));` +
|
|
500
|
+
`const out=matched.length>0?matched:all;` +
|
|
501
|
+
`out.slice(0,10).forEach(x=>console.log(x))"`);
|
|
461
502
|
lines.push(`\`\`\``);
|
|
462
|
-
lines.push(`
|
|
503
|
+
lines.push(`Pick the FIRST directory from output that matches "${input}" and download:`);
|
|
463
504
|
lines.push(`\`\`\`bash`);
|
|
505
|
+
lines.push(`# Replace SKILL_DIR with the actual directory name from the listing above`);
|
|
464
506
|
lines.push(`SKILL_DIR="matched-skill"`);
|
|
465
507
|
lines.push(`mkdir -p ".claude/skills/\${SKILL_DIR}"`);
|
|
466
508
|
lines.push(`curl -sf "https://raw.githubusercontent.com/${VOLTAGENT_SKILLS_REPO}/main/\${SKILL_DIR}/SKILL.md" \\`);
|
|
467
509
|
lines.push(` -o ".claude/skills/\${SKILL_DIR}/SKILL.md"`);
|
|
510
|
+
lines.push(`wc -c ".claude/skills/\${SKILL_DIR}/SKILL.md"`);
|
|
468
511
|
lines.push(`\`\`\``);
|
|
469
|
-
lines.push(`
|
|
512
|
+
lines.push(`If >50 bytes: installed. If empty: delete and try README fallback below.`);
|
|
470
513
|
lines.push(``);
|
|
471
|
-
lines.push(`**README-driven fallback:** If the API listing above returned SKIP or no match
|
|
514
|
+
lines.push(`**README-driven fallback:** If the API listing above returned SKIP or no match:`);
|
|
472
515
|
lines.push(``);
|
|
473
516
|
lines.push(`\`\`\`bash`);
|
|
474
|
-
lines.push(`#
|
|
517
|
+
lines.push(`# Fetch README — parser finds sections matching your query, extracts all links`);
|
|
475
518
|
lines.push(`curl -sf "https://raw.githubusercontent.com/${VOLTAGENT_SKILLS_REPO}/main/README.md" \\`);
|
|
476
|
-
lines.push(` | node -e "
|
|
477
|
-
`const re=/\\[([^\\]]+)\\]\\(([^)]+)\\)/g;let m;const r=[];` +
|
|
478
|
-
`while((m=re.exec(t))!==null){if(m[2].includes('SKILL.md')||m[2].includes('github.com'))r.push(m[1]+' | '+m[2])}` +
|
|
479
|
-
`r.slice(0,10).forEach(x=>console.log(x))"`);
|
|
519
|
+
lines.push(` | node -e "${buildSectionAwareParser(queryRegex)}"`);
|
|
480
520
|
lines.push(`\`\`\``);
|
|
481
521
|
lines.push(``);
|
|
522
|
+
lines.push(`From the output, pick the FIRST entry. Extract the URL (after " | "). Resolve it:`);
|
|
523
|
+
lines.push(`- If URL starts with \`./\`: prepend \`https://raw.githubusercontent.com/${VOLTAGENT_SKILLS_REPO}/main/\``);
|
|
524
|
+
lines.push(`- If URL contains \`github.com/OWNER/REPO/blob/BRANCH/\`: replace with \`raw.githubusercontent.com/OWNER/REPO/BRANCH/\``);
|
|
525
|
+
lines.push(`- If URL contains \`github.com/OWNER/REPO/tree/BRANCH/\`: use API to list files, then download the SKILL.md`);
|
|
482
526
|
lines.push(`\`\`\`bash`);
|
|
483
|
-
lines.push(`# Step 2: Pick the entry best matching "${input}". Resolve the URL (rule 2).`);
|
|
484
|
-
lines.push(`# Step 3: Download the resolved skill file`);
|
|
485
|
-
lines.push(`RESOLVED_URL="raw-url-from-step-2"`);
|
|
486
527
|
lines.push(`SKILL_DIR="matched-skill"`);
|
|
487
528
|
lines.push(`mkdir -p ".claude/skills/\${SKILL_DIR}"`);
|
|
488
|
-
lines.push(`curl -sf "
|
|
529
|
+
lines.push(`curl -sf "RESOLVED_URL" -o ".claude/skills/\${SKILL_DIR}/SKILL.md" && wc -c ".claude/skills/\${SKILL_DIR}/SKILL.md"`);
|
|
489
530
|
lines.push(`\`\`\``);
|
|
490
|
-
lines.push(`
|
|
531
|
+
lines.push(`If >50 bytes: installed. If empty or NO_README_MATCHES: continue to next STEP.`);
|
|
491
532
|
lines.push(``);
|
|
492
533
|
// ── STEP 4: ComposioHQ service integrations ───────────────────────
|
|
493
534
|
lines.push(`---`);
|
|
@@ -575,18 +616,18 @@ function buildUniversalRulesBlock(lines) {
|
|
|
575
616
|
}
|
|
576
617
|
// ── Shared fetch blocks ─────────────────────────────────────────────────
|
|
577
618
|
function buildCommunitySkillsFetchBlock(lines, categoryFilter) {
|
|
578
|
-
lines.push(`**Stage 1 — Fetch catalog and find matching
|
|
619
|
+
lines.push(`**Stage 1 — Fetch catalog and find matching plugins (compact output — one line per match):**`);
|
|
579
620
|
lines.push(`\`\`\`bash`);
|
|
580
621
|
lines.push(`curl -sf "${MARKETPLACE_CATALOG_URL}" \\`);
|
|
581
622
|
lines.push(` | node -e "const d=JSON.parse(require('fs').readFileSync(0,'utf8'));` +
|
|
582
|
-
`const q='${categoryFilter}';` +
|
|
583
|
-
`const r=d.plugins.filter(p=>(q===''||p.category.includes(q))&&p.name&&p.source).slice(0,10)
|
|
584
|
-
|
|
585
|
-
`console.log(
|
|
623
|
+
`const q='${categoryFilter}'.replace(/^\\\\d+-/,'');` +
|
|
624
|
+
`const r=d.plugins.filter(p=>(q===''||p.category.includes(q)||q.includes(p.category))&&p.name&&p.source).slice(0,10);` +
|
|
625
|
+
`r.forEach(p=>console.log(p.name+' | '+p.source));` +
|
|
626
|
+
`if(r.length===0)console.log('NO_PLUGINS')"`);
|
|
586
627
|
lines.push(`\`\`\``);
|
|
587
|
-
lines.push(`If
|
|
628
|
+
lines.push(`If NO_PLUGINS or error: skip to next STEP.`);
|
|
588
629
|
lines.push(``);
|
|
589
|
-
lines.push(`**Stage 2 —
|
|
630
|
+
lines.push(`**Stage 2 — Pick the FIRST match from output. Extract the source path (after " | "). List its skills:**`);
|
|
590
631
|
lines.push(`\`\`\`bash`);
|
|
591
632
|
lines.push(`# Replace PLUGIN_SOURCE_PATH with the "source" value from Stage 1`);
|
|
592
633
|
lines.push(`PLUGIN_SOURCE_PATH="plugins/category/plugin-name"`);
|
|
@@ -595,60 +636,63 @@ function buildCommunitySkillsFetchBlock(lines, categoryFilter) {
|
|
|
595
636
|
`if(Array.isArray(a)===false){console.log('NO_SKILLS_DIR');process.exit(0)}` +
|
|
596
637
|
`a.forEach(x=>console.log(x.name))"`);
|
|
597
638
|
lines.push(`\`\`\``);
|
|
598
|
-
lines.push(`If
|
|
639
|
+
lines.push(`If NO_SKILLS_DIR: try the next plugin from Stage 1, or skip to next STEP.`);
|
|
599
640
|
lines.push(``);
|
|
600
|
-
lines.push(`**Stage 3 — Download
|
|
641
|
+
lines.push(`**Stage 3 — Download the skill file:**`);
|
|
601
642
|
lines.push(`\`\`\`bash`);
|
|
602
|
-
lines.push(`# Replace PLUGIN_SOURCE_PATH and SKILL_NAME with actual values`);
|
|
643
|
+
lines.push(`# Replace PLUGIN_SOURCE_PATH and SKILL_NAME with actual values from above`);
|
|
603
644
|
lines.push(`PLUGIN_SOURCE_PATH="plugins/category/plugin-name"`);
|
|
604
645
|
lines.push(`SKILL_NAME="skill-directory-name"`);
|
|
605
646
|
lines.push(`mkdir -p ".claude/skills/\${SKILL_NAME}"`);
|
|
606
647
|
lines.push(`curl -sf "https://raw.githubusercontent.com/${MARKETPLACE_REPO}/main/\${PLUGIN_SOURCE_PATH}/skills/\${SKILL_NAME}/SKILL.md" \\`);
|
|
607
648
|
lines.push(` -o ".claude/skills/\${SKILL_NAME}/SKILL.md"`);
|
|
649
|
+
lines.push(`wc -c ".claude/skills/\${SKILL_NAME}/SKILL.md"`);
|
|
608
650
|
lines.push(`\`\`\``);
|
|
609
|
-
lines.push(`
|
|
651
|
+
lines.push(`If >50 bytes: installed. If empty: delete and move on to next plugin or next STEP.`);
|
|
610
652
|
lines.push(``);
|
|
611
653
|
}
|
|
612
654
|
function buildComposioFetchBlock(lines, input) {
|
|
613
|
-
|
|
655
|
+
const queryRegex = buildQueryRegex(input);
|
|
656
|
+
lines.push(`**Stage 1 — List available skill directories and filter by query:**`);
|
|
614
657
|
lines.push(`\`\`\`bash`);
|
|
615
658
|
lines.push(`curl -sf \${GITHUB_TOKEN:+-H "Authorization: token \$GITHUB_TOKEN"} "${COMPOSIO_API}" \\`);
|
|
616
659
|
lines.push(` | node -e "const d=JSON.parse(require('fs').readFileSync(0,'utf8'));` +
|
|
617
660
|
`if(Array.isArray(d)===false){console.log('SKIP');process.exit(0)}` +
|
|
618
|
-
`
|
|
661
|
+
`const q=/${queryRegex}/i;` +
|
|
662
|
+
`const all=d.filter(x=>x.type==='dir'&&(x.name[0]==='.')===false).map(x=>x.name);` +
|
|
663
|
+
`const matched=all.filter(n=>q.test(n));` +
|
|
664
|
+
`const out=matched.length>0?matched:all;` +
|
|
665
|
+
`out.slice(0,10).forEach(x=>console.log(x))"`);
|
|
619
666
|
lines.push(`\`\`\``);
|
|
620
667
|
lines.push(``);
|
|
621
|
-
lines.push(`**Stage 2 — Pick the directory that
|
|
622
|
-
lines.push(`Match by name similarity. If no directory name is relevant, skip.`);
|
|
623
|
-
lines.push(``);
|
|
624
|
-
lines.push(`**Stage 3 — Download the SKILL.md from the matched directory:**`);
|
|
668
|
+
lines.push(`**Stage 2 — Pick the FIRST directory from the output that matches "${input}" and download:**`);
|
|
625
669
|
lines.push(`\`\`\`bash`);
|
|
670
|
+
lines.push(`# Replace SKILL_DIR with the actual directory name from Stage 1 output`);
|
|
626
671
|
lines.push(`SKILL_DIR="matched-directory"`);
|
|
627
672
|
lines.push(`mkdir -p ".claude/skills/\${SKILL_DIR}"`);
|
|
628
673
|
lines.push(`curl -sf "${COMPOSIO_RAW}/\${SKILL_DIR}/SKILL.md" \\`);
|
|
629
674
|
lines.push(` -o ".claude/skills/\${SKILL_DIR}/SKILL.md"`);
|
|
675
|
+
lines.push(`wc -c ".claude/skills/\${SKILL_DIR}/SKILL.md"`);
|
|
630
676
|
lines.push(`\`\`\``);
|
|
631
|
-
lines.push(`
|
|
677
|
+
lines.push(`If >50 bytes: installed. If empty: delete and try README fallback below.`);
|
|
632
678
|
lines.push(``);
|
|
633
|
-
lines.push(`**README-driven fallback:** If the directory listing
|
|
679
|
+
lines.push(`**README-driven fallback:** If the directory listing returned SKIP or no match:`);
|
|
634
680
|
lines.push(``);
|
|
635
681
|
lines.push(`\`\`\`bash`);
|
|
636
|
-
lines.push(`#
|
|
682
|
+
lines.push(`# Fetch README — parser finds sections matching your query, extracts all links`);
|
|
637
683
|
lines.push(`curl -sf "https://raw.githubusercontent.com/${COMPOSIO_REPO}/master/README.md" \\`);
|
|
638
|
-
lines.push(` | node -e "
|
|
639
|
-
`const re=/\\[([^\\]]+)\\]\\(([^)]+)\\)/g;let m;const r=[];` +
|
|
640
|
-
`while((m=re.exec(t))!==null){if(m[2].includes('SKILL.md')||m[2].includes('github.com'))r.push(m[1]+' | '+m[2])}` +
|
|
641
|
-
`r.slice(0,10).forEach(x=>console.log(x))"`);
|
|
684
|
+
lines.push(` | node -e "${buildSectionAwareParser(queryRegex)}"`);
|
|
642
685
|
lines.push(`\`\`\``);
|
|
643
686
|
lines.push(``);
|
|
687
|
+
lines.push(`From the output, pick the FIRST entry. Extract the URL (after " | "). Resolve it:`);
|
|
688
|
+
lines.push(`- If URL starts with \`./\`: prepend \`https://raw.githubusercontent.com/${COMPOSIO_REPO}/master/\` and append \`SKILL.md\` if the URL is a directory`);
|
|
689
|
+
lines.push(`- If URL contains \`github.com/OWNER/REPO/blob/BRANCH/\`: replace with \`raw.githubusercontent.com/OWNER/REPO/BRANCH/\``);
|
|
690
|
+
lines.push(`- If URL contains \`github.com/OWNER/REPO/tree/BRANCH/\`: use API to list files, then download the SKILL.md`);
|
|
644
691
|
lines.push(`\`\`\`bash`);
|
|
645
|
-
lines.push(`# Step 2: Pick the entry best matching "${input}". Resolve the URL (rule 2).`);
|
|
646
|
-
lines.push(`# Step 3: Download the resolved skill file`);
|
|
647
|
-
lines.push(`RESOLVED_URL="raw-url-from-step-2"`);
|
|
648
692
|
lines.push(`SKILL_DIR="matched-skill"`);
|
|
649
693
|
lines.push(`mkdir -p ".claude/skills/\${SKILL_DIR}"`);
|
|
650
|
-
lines.push(`curl -sf "
|
|
694
|
+
lines.push(`curl -sf "RESOLVED_URL" -o ".claude/skills/\${SKILL_DIR}/SKILL.md" && wc -c ".claude/skills/\${SKILL_DIR}/SKILL.md"`);
|
|
651
695
|
lines.push(`\`\`\``);
|
|
652
|
-
lines.push(`
|
|
696
|
+
lines.push(`If >50 bytes: installed. If empty or NO_README_MATCHES: continue to next STEP.`);
|
|
653
697
|
lines.push(``);
|
|
654
698
|
}
|