claude-setup 1.1.9 → 2.0.0
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/add.js +2 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +46 -0
- package/dist/marketplace.js +131 -36
- package/package.json +1 -1
- package/templates/add.md +24 -0
package/dist/commands/add.js
CHANGED
|
@@ -6,6 +6,7 @@ import { updateManifest } from "../manifest.js";
|
|
|
6
6
|
import { buildAddCommand } from "../builder.js";
|
|
7
7
|
import { estimateTokens, estimateCost } from "../tokens.js";
|
|
8
8
|
import { c } from "../output.js";
|
|
9
|
+
import { installMarketplaceFetcher } from "./init.js";
|
|
9
10
|
function ensureDir(dir) {
|
|
10
11
|
if (!existsSync(dir))
|
|
11
12
|
mkdirSync(dir, { recursive: true });
|
|
@@ -46,6 +47,7 @@ capabilities that need documentation, MCP servers, skills, and hooks together.
|
|
|
46
47
|
const tokens = estimateTokens(content);
|
|
47
48
|
const cost = estimateCost(tokens);
|
|
48
49
|
ensureDir(".claude/commands");
|
|
50
|
+
installMarketplaceFetcher();
|
|
49
51
|
writeFileSync(".claude/commands/stack-add.md", content, "utf8");
|
|
50
52
|
await updateManifest("add", collected, {
|
|
51
53
|
input: userInput,
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -71,6 +71,50 @@ No further action needed — the output IS the status.
|
|
|
71
71
|
writeFileSync(filepath, content, "utf8");
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
+
/** Install the marketplace-fetcher subagent so /stack-add can spawn it */
|
|
75
|
+
export function installMarketplaceFetcher(cwd = process.cwd()) {
|
|
76
|
+
const agentsDir = join(cwd, ".claude", "agents");
|
|
77
|
+
ensureDir(agentsDir);
|
|
78
|
+
const filepath = join(agentsDir, "marketplace-fetcher.md");
|
|
79
|
+
// Always overwrite — this is a system agent, not user-authored
|
|
80
|
+
writeFileSync(filepath, `---
|
|
81
|
+
name: marketplace-fetcher
|
|
82
|
+
description: Fetches skills and agents from all 4 marketplace catalogs. Spawned automatically by stack-add. Runs in isolation, writes to disk, returns only the install confirmation.
|
|
83
|
+
tools: Bash
|
|
84
|
+
model: haiku
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
You are a marketplace fetch agent. Your ONLY job is to search the 4 curated catalogs, download the best matching file, and confirm the install. You run in isolation — your context dies when you finish, keeping the main session clean.
|
|
88
|
+
|
|
89
|
+
## Your task
|
|
90
|
+
|
|
91
|
+
You will receive a single install request (e.g., "Stripe integration", "orchestration agent"). Execute the full pipeline below. Return exactly ONE line when done.
|
|
92
|
+
|
|
93
|
+
## Pipeline
|
|
94
|
+
|
|
95
|
+
The main session has already generated a complete marketplace instruction block. It will be passed to you as your task prompt. Follow every step in that instruction block exactly:
|
|
96
|
+
|
|
97
|
+
1. **Classify** the request (agent vs skill, categories, SaaS matches)
|
|
98
|
+
2. **Search catalogs in order** — stop at the first quality match
|
|
99
|
+
3. **Download** the matched file to \`.claude/agents/\` (agents) or \`.claude/skills/<name>/\` (skills)
|
|
100
|
+
4. **Verify** the file has real content (not empty, not a stub)
|
|
101
|
+
5. **Return** exactly one line:
|
|
102
|
+
- \`INSTALLED .claude/agents/<file> <bytes>b\` or
|
|
103
|
+
- \`INSTALLED .claude/skills/<dir>/SKILL.md <bytes>b\` or
|
|
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\`
|
|
107
|
+
|
|
108
|
+
## Rules
|
|
109
|
+
|
|
110
|
+
- Execute every curl yourself. Never ask for confirmation.
|
|
111
|
+
- Use relative paths only. Never \`cd\` to an absolute path.
|
|
112
|
+
- Pipe all curl output through a node parser — never return raw JSON/README to context.
|
|
113
|
+
- A fetch failure is a routing signal to the next catalog, not a stop condition.
|
|
114
|
+
- If a step produces an ambiguous result, document why it failed in one line, then continue.
|
|
115
|
+
- When creating custom skills/agents (last resort), make them production-valid — no stubs.
|
|
116
|
+
`, "utf8");
|
|
117
|
+
}
|
|
74
118
|
function installTokenHook(cwd = process.cwd()) {
|
|
75
119
|
// Write the hook script
|
|
76
120
|
const hooksDir = join(cwd, ".claude", "hooks");
|
|
@@ -139,6 +183,7 @@ export async function runInit(opts = {}) {
|
|
|
139
183
|
writeFileSync(".claude/commands/stack-init.md", content, "utf8");
|
|
140
184
|
writeFileSync(".claude/commands/stack-sync.md", buildBootstrapSync(), "utf8");
|
|
141
185
|
installBootstrapCommands(".claude/commands");
|
|
186
|
+
installMarketplaceFetcher();
|
|
142
187
|
await updateManifest("init", collected, { estimatedTokens: tokens, estimatedCost: cost });
|
|
143
188
|
installTokenHook();
|
|
144
189
|
// Create initial snapshot — collectFilesForSnapshot scans all .claude/ automatically
|
|
@@ -181,6 +226,7 @@ Claude Code will ask 3 questions, then set up your environment.
|
|
|
181
226
|
writeFileSync(".claude/commands/stack-init.md", orchestrator, "utf8");
|
|
182
227
|
writeFileSync(".claude/commands/stack-sync.md", buildBootstrapSync(), "utf8");
|
|
183
228
|
installBootstrapCommands(".claude/commands");
|
|
229
|
+
installMarketplaceFetcher();
|
|
184
230
|
await updateManifest("init", collected, { estimatedTokens: tokens, estimatedCost: cost });
|
|
185
231
|
installTokenHook();
|
|
186
232
|
// Create initial snapshot — collectFilesForSnapshot scans all .claude/ automatically
|
package/dist/marketplace.js
CHANGED
|
@@ -48,6 +48,33 @@ export const SAAS_PACKS = [
|
|
|
48
48
|
"Supabase", "Vercel", "OpenRouter", "GitHub", "Azure", "MongoDB",
|
|
49
49
|
"Playwright", "Tavily", "Stripe", "Slack", "Linear", "Notion",
|
|
50
50
|
];
|
|
51
|
+
// ── Adjacent category map (for fallback when primary SKIP) ─────────────
|
|
52
|
+
const ADJACENT_CATEGORIES = {
|
|
53
|
+
"01-core-development": ["02-language-specialists", "06-developer-experience"],
|
|
54
|
+
"02-language-specialists": ["01-core-development", "06-developer-experience"],
|
|
55
|
+
"03-infrastructure": ["04-quality-security", "09-meta-orchestration"],
|
|
56
|
+
"04-quality-security": ["03-infrastructure", "01-core-development"],
|
|
57
|
+
"05-data-ai": ["10-research-analysis", "07-specialized-domains"],
|
|
58
|
+
"06-developer-experience": ["01-core-development", "02-language-specialists"],
|
|
59
|
+
"07-specialized-domains": ["05-data-ai", "08-business-product"],
|
|
60
|
+
"08-business-product": ["07-specialized-domains", "09-meta-orchestration"],
|
|
61
|
+
"09-meta-orchestration": ["03-infrastructure", "04-quality-security"],
|
|
62
|
+
"10-research-analysis": ["05-data-ai", "07-specialized-domains"],
|
|
63
|
+
};
|
|
64
|
+
/** Expand target categories with their adjacent neighbors, deduplicating */
|
|
65
|
+
function expandWithAdjacent(targets) {
|
|
66
|
+
const seen = new Set(targets);
|
|
67
|
+
const adjacent = [];
|
|
68
|
+
for (const cat of targets) {
|
|
69
|
+
for (const adj of (ADJACENT_CATEGORIES[cat] ?? [])) {
|
|
70
|
+
if (!seen.has(adj)) {
|
|
71
|
+
seen.add(adj);
|
|
72
|
+
adjacent.push(adj);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return adjacent;
|
|
77
|
+
}
|
|
51
78
|
// ── Agent detection keywords ────────────────────────────────────────────
|
|
52
79
|
const AGENT_KEYWORDS = [
|
|
53
80
|
"agent", "subagent", "sub-agent", "orchestrat", "multi-agent",
|
|
@@ -270,6 +297,7 @@ function buildAgentPipeline(lines, input, agentCategories, categoryFilter) {
|
|
|
270
297
|
? agentCategories
|
|
271
298
|
: ["09-meta-orchestration"];
|
|
272
299
|
// ── STEP 1: VoltAgent/awesome-claude-code-subagents ──────────────
|
|
300
|
+
const adjacentCategories = expandWithAdjacent(targetCategories);
|
|
273
301
|
lines.push(`---`);
|
|
274
302
|
lines.push(``);
|
|
275
303
|
lines.push(`### STEP 1 — VoltAgent subagents (PRIMARY source for agents — 127+ specialized agents)`);
|
|
@@ -277,25 +305,39 @@ function buildAgentPipeline(lines, input, agentCategories, categoryFilter) {
|
|
|
277
305
|
lines.push(`This is the preferred source for anything agent-shaped. 10 categories, 127+ agents.`);
|
|
278
306
|
lines.push(`If a match is found here, install to \`.claude/agents/\` (NOT .claude/skills/).`);
|
|
279
307
|
lines.push(``);
|
|
280
|
-
lines.push(`**1a. List agent files in the
|
|
281
|
-
lines.push(`If any curl below fails, skip to the next category or STEP 2 immediately.`);
|
|
308
|
+
lines.push(`**1a. List agent files in the primary categories:**`);
|
|
282
309
|
lines.push(``);
|
|
283
310
|
for (const cat of targetCategories) {
|
|
284
311
|
lines.push(`\`\`\`bash`);
|
|
285
|
-
lines.push(`#
|
|
312
|
+
lines.push(`# Primary category: ${cat}`);
|
|
286
313
|
lines.push(`curl -sf \${GITHUB_TOKEN:+-H "Authorization: token \$GITHUB_TOKEN"} "${VOLTAGENT_SUBAGENTS_API}/${cat}" \\`);
|
|
287
314
|
lines.push(` | node -e "const d=JSON.parse(require('fs').readFileSync(0,'utf8'));` +
|
|
288
|
-
`if(
|
|
289
|
-
`d.filter(x=>x.name.endsWith('.md')&&x.name
|
|
315
|
+
`if(Array.isArray(d)===false){console.log('SKIP');process.exit(0)}` +
|
|
316
|
+
`d.filter(x=>x.name.endsWith('.md')&&(x.name==='README.md')===false).forEach(x=>console.log(x.name))"`);
|
|
290
317
|
lines.push(`\`\`\``);
|
|
291
318
|
lines.push(``);
|
|
292
319
|
}
|
|
293
|
-
|
|
320
|
+
if (adjacentCategories.length > 0) {
|
|
321
|
+
lines.push(`**1b. If ALL primary categories above returned SKIP, try adjacent categories:**`);
|
|
322
|
+
lines.push(`Do NOT skip this step. A SKIP in the primary category does NOT mean VoltAgent has no match.`);
|
|
323
|
+
lines.push(``);
|
|
324
|
+
for (const cat of adjacentCategories) {
|
|
325
|
+
lines.push(`\`\`\`bash`);
|
|
326
|
+
lines.push(`# Adjacent category: ${cat}`);
|
|
327
|
+
lines.push(`curl -sf \${GITHUB_TOKEN:+-H "Authorization: token \$GITHUB_TOKEN"} "${VOLTAGENT_SUBAGENTS_API}/${cat}" \\`);
|
|
328
|
+
lines.push(` | node -e "const d=JSON.parse(require('fs').readFileSync(0,'utf8'));` +
|
|
329
|
+
`if(Array.isArray(d)===false){console.log('SKIP');process.exit(0)}` +
|
|
330
|
+
`d.filter(x=>x.name.endsWith('.md')&&(x.name==='README.md')===false).forEach(x=>console.log(x.name))"`);
|
|
331
|
+
lines.push(`\`\`\``);
|
|
332
|
+
lines.push(``);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
lines.push(`**1c. From ALL file lists above (primary + adjacent), pick the BEST match for "${input}".**`);
|
|
294
336
|
lines.push(`Match by name and relevance. If multiple candidates exist, pick the closest one.`);
|
|
295
337
|
lines.push(``);
|
|
296
|
-
lines.push(`**
|
|
338
|
+
lines.push(`**1d. Download the matched agent file:**`);
|
|
297
339
|
lines.push(`\`\`\`bash`);
|
|
298
|
-
lines.push(`# Replace CATEGORY and AGENT_FILE with actual values from 1a
|
|
340
|
+
lines.push(`# Replace CATEGORY and AGENT_FILE with actual values from 1a-1c`);
|
|
299
341
|
lines.push(`CATEGORY="${targetCategories[0]}"`);
|
|
300
342
|
lines.push(`AGENT_FILE="matched-agent.md"`);
|
|
301
343
|
lines.push(`mkdir -p ".claude/agents"`);
|
|
@@ -303,26 +345,42 @@ function buildAgentPipeline(lines, input, agentCategories, categoryFilter) {
|
|
|
303
345
|
lines.push(` -o ".claude/agents/\${AGENT_FILE}"`);
|
|
304
346
|
lines.push(`\`\`\``);
|
|
305
347
|
lines.push(``);
|
|
306
|
-
lines.push(`**
|
|
348
|
+
lines.push(`**1e. Verify the file has real content (rule 7 — must not be empty):**`);
|
|
307
349
|
lines.push(`\`\`\`bash`);
|
|
308
|
-
lines.push(`# File must have frontmatter (---) and body content`);
|
|
309
350
|
lines.push(`head -3 ".claude/agents/\${AGENT_FILE}"`);
|
|
310
351
|
lines.push(`wc -l ".claude/agents/\${AGENT_FILE}"`);
|
|
311
352
|
lines.push(`\`\`\``);
|
|
312
|
-
lines.push(`If the file is empty or just frontmatter with no body: delete it, log the failure
|
|
353
|
+
lines.push(`If the file is empty or just frontmatter with no body: delete it, log the failure.`);
|
|
313
354
|
lines.push(`If the file has real content: agent is installed. Skip to "Install result format" below.`);
|
|
314
355
|
lines.push(``);
|
|
315
|
-
lines.push(`**README-driven fallback
|
|
316
|
-
lines.push(`
|
|
317
|
-
lines.push(
|
|
318
|
-
lines.push(`
|
|
356
|
+
lines.push(`**1f. README-driven fallback — if ALL category listings (primary + adjacent) returned SKIP:**`);
|
|
357
|
+
lines.push(`You MUST try this before moving to STEP 2. The README contains the full agent listing.`);
|
|
358
|
+
lines.push(``);
|
|
359
|
+
lines.push(`\`\`\`bash`);
|
|
360
|
+
lines.push(`# Step 1: Fetch the VoltAgent subagents README`);
|
|
361
|
+
lines.push(`curl -sf "https://raw.githubusercontent.com/${VOLTAGENT_SUBAGENTS_REPO}/main/README.md" \\`);
|
|
362
|
+
lines.push(` | node -e "const t=require('fs').readFileSync(0,'utf8');` +
|
|
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))"`);
|
|
366
|
+
lines.push(`\`\`\``);
|
|
367
|
+
lines.push(``);
|
|
368
|
+
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
|
+
lines.push(`mkdir -p ".claude/agents"`);
|
|
374
|
+
lines.push(`curl -sf "\${RESOLVED_URL}" -o ".claude/agents/matched-agent.md"`);
|
|
375
|
+
lines.push(`\`\`\``);
|
|
376
|
+
lines.push(`Verify content (rule 7). If empty: delete and continue to STEP 2.`);
|
|
319
377
|
lines.push(``);
|
|
320
378
|
// ── STEP 2: jeremylongshore community skills (fallback for agents) ─
|
|
321
379
|
lines.push(`---`);
|
|
322
380
|
lines.push(``);
|
|
323
381
|
lines.push(`### STEP 2 — Community skills catalog (fallback — 416 plugins)`);
|
|
324
382
|
lines.push(``);
|
|
325
|
-
lines.push(
|
|
383
|
+
lines.push(`**Before continuing:** Document in one line why STEP 1 produced no result.`);
|
|
326
384
|
lines.push(`Search for agent-like skills in the community catalog.`);
|
|
327
385
|
lines.push(`If curl fails, skip to STEP 3.`);
|
|
328
386
|
lines.push(``);
|
|
@@ -332,7 +390,7 @@ function buildAgentPipeline(lines, input, agentCategories, categoryFilter) {
|
|
|
332
390
|
lines.push(``);
|
|
333
391
|
lines.push(`### STEP 3 — ComposioHQ service integrations (1000+ skills — fallback)`);
|
|
334
392
|
lines.push(``);
|
|
335
|
-
lines.push(
|
|
393
|
+
lines.push(`**Before continuing:** Document in one line why STEPs 1 and 2 produced no result.`);
|
|
336
394
|
lines.push(`Strong for API and SaaS automation. Skills live in per-directory SKILL.md files.`);
|
|
337
395
|
lines.push(`If curl fails, skip to STEP 4.`);
|
|
338
396
|
lines.push(``);
|
|
@@ -392,14 +450,14 @@ function buildSkillPipeline(lines, input, categoryFilter, saasMatches) {
|
|
|
392
450
|
lines.push(``);
|
|
393
451
|
lines.push(`### STEP 3 — VoltAgent curated agent skills (production-proven)`);
|
|
394
452
|
lines.push(``);
|
|
395
|
-
lines.push(
|
|
453
|
+
lines.push(`**Before continuing:** Document in one line why STEP 2 produced no result.`);
|
|
396
454
|
lines.push(`Curated real-world skills from engineering teams. If curl fails, skip to STEP 4.`);
|
|
397
455
|
lines.push(``);
|
|
398
456
|
lines.push(`\`\`\`bash`);
|
|
399
457
|
lines.push(`curl -sf \${GITHUB_TOKEN:+-H "Authorization: token \$GITHUB_TOKEN"} "${VOLTAGENT_SKILLS_API}" \\`);
|
|
400
458
|
lines.push(` | node -e "const d=JSON.parse(require('fs').readFileSync(0,'utf8'));` +
|
|
401
|
-
`if(
|
|
402
|
-
`d.filter(x=>x.type==='dir'
|
|
459
|
+
`if(Array.isArray(d)===false){console.log('SKIP');process.exit(0)}` +
|
|
460
|
+
`d.filter(x=>x.type==='dir'&&x.name.startsWith('.')===false).forEach(x=>console.log(x.name))"`);
|
|
403
461
|
lines.push(`\`\`\``);
|
|
404
462
|
lines.push(`If any directory name matches "${input}", fetch its SKILL.md:`);
|
|
405
463
|
lines.push(`\`\`\`bash`);
|
|
@@ -410,17 +468,33 @@ function buildSkillPipeline(lines, input, categoryFilter, saasMatches) {
|
|
|
410
468
|
lines.push(`\`\`\``);
|
|
411
469
|
lines.push(`Verify content is real (not empty). If empty, delete and continue.`);
|
|
412
470
|
lines.push(``);
|
|
413
|
-
lines.push(`**README-driven fallback:** If the API
|
|
414
|
-
lines.push(
|
|
415
|
-
lines.push(`
|
|
416
|
-
lines.push(
|
|
471
|
+
lines.push(`**README-driven fallback:** If the API listing above returned SKIP or no match, fetch the README:`);
|
|
472
|
+
lines.push(``);
|
|
473
|
+
lines.push(`\`\`\`bash`);
|
|
474
|
+
lines.push(`# Step 1: Fetch the VoltAgent skills README`);
|
|
475
|
+
lines.push(`curl -sf "https://raw.githubusercontent.com/${VOLTAGENT_SKILLS_REPO}/main/README.md" \\`);
|
|
476
|
+
lines.push(` | node -e "const t=require('fs').readFileSync(0,'utf8');` +
|
|
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))"`);
|
|
480
|
+
lines.push(`\`\`\``);
|
|
481
|
+
lines.push(``);
|
|
482
|
+
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
|
+
lines.push(`SKILL_DIR="matched-skill"`);
|
|
487
|
+
lines.push(`mkdir -p ".claude/skills/\${SKILL_DIR}"`);
|
|
488
|
+
lines.push(`curl -sf "\${RESOLVED_URL}" -o ".claude/skills/\${SKILL_DIR}/SKILL.md"`);
|
|
489
|
+
lines.push(`\`\`\``);
|
|
490
|
+
lines.push(`Verify content (rule 7). If empty: delete and continue to next STEP.`);
|
|
417
491
|
lines.push(``);
|
|
418
492
|
// ── STEP 4: ComposioHQ service integrations ───────────────────────
|
|
419
493
|
lines.push(`---`);
|
|
420
494
|
lines.push(``);
|
|
421
495
|
lines.push(`### STEP 4 — ComposioHQ service integrations (1000+ skills)`);
|
|
422
496
|
lines.push(``);
|
|
423
|
-
lines.push(
|
|
497
|
+
lines.push(`**Before continuing:** Document in one line why STEPs 2 and 3 produced no result.`);
|
|
424
498
|
lines.push(`Strong for API/SaaS automation: Gmail, Slack, GitHub, Notion, Stripe, Shopify, etc.`);
|
|
425
499
|
lines.push(`If curl fails, skip to STEP 5.`);
|
|
426
500
|
lines.push(``);
|
|
@@ -482,7 +556,16 @@ function buildUniversalRulesBlock(lines) {
|
|
|
482
556
|
lines.push(`- **Uniqueness**: does it duplicate something already installed in .claude/skills/ or .claude/agents/? Deprioritize duplicates.`);
|
|
483
557
|
lines.push(`Install only the highest-scoring candidate. One high-quality tool per task.`);
|
|
484
558
|
lines.push(``);
|
|
485
|
-
lines.push(`**5.
|
|
559
|
+
lines.push(`**5. No absolute paths — never \`cd\` before commands:**`);
|
|
560
|
+
lines.push(`Claude Code already runs in the project working directory. Do NOT prepend \`cd /absolute/path &&\` to any command.`);
|
|
561
|
+
lines.push(`Use relative paths only (e.g., \`.claude/agents/\`, \`.claude/skills/\`). Absolute paths with backslashes break bash on Windows.`);
|
|
562
|
+
lines.push(``);
|
|
563
|
+
lines.push(`**6. Output filtering — cap context cost:**`);
|
|
564
|
+
lines.push(`Every curl result MUST be piped through a node parser that returns at most 5–15 lines of structured data.`);
|
|
565
|
+
lines.push(`The raw JSON/README response must NEVER enter the context unfiltered.`);
|
|
566
|
+
lines.push(`Chain multiple sequential fetch decisions inside a single bash script so they execute as one tool call.`);
|
|
567
|
+
lines.push(``);
|
|
568
|
+
lines.push(`**7. Content verification — never keep stubs:**`);
|
|
486
569
|
lines.push(`After downloading any file, verify ALL of these before accepting:`);
|
|
487
570
|
lines.push(`- Not empty (0 bytes)`);
|
|
488
571
|
lines.push(`- Not only YAML frontmatter with no body (just \`---\\nname: x\\n---\` and nothing after)`);
|
|
@@ -509,7 +592,7 @@ function buildCommunitySkillsFetchBlock(lines, categoryFilter) {
|
|
|
509
592
|
lines.push(`PLUGIN_SOURCE_PATH="plugins/category/plugin-name"`);
|
|
510
593
|
lines.push(`curl -sf \${GITHUB_TOKEN:+-H "Authorization: token \$GITHUB_TOKEN"} "https://api.github.com/repos/${MARKETPLACE_REPO}/contents/\${PLUGIN_SOURCE_PATH}/skills" \\`);
|
|
511
594
|
lines.push(` | node -e "const a=JSON.parse(require('fs').readFileSync(0,'utf8'));` +
|
|
512
|
-
`if(
|
|
595
|
+
`if(Array.isArray(a)===false){console.log('NO_SKILLS_DIR');process.exit(0)}` +
|
|
513
596
|
`a.forEach(x=>console.log(x.name))"`);
|
|
514
597
|
lines.push(`\`\`\``);
|
|
515
598
|
lines.push(`If this fails or shows NO_SKILLS_DIR, the plugin has no installable skills — skip.`);
|
|
@@ -531,8 +614,8 @@ function buildComposioFetchBlock(lines, input) {
|
|
|
531
614
|
lines.push(`\`\`\`bash`);
|
|
532
615
|
lines.push(`curl -sf \${GITHUB_TOKEN:+-H "Authorization: token \$GITHUB_TOKEN"} "${COMPOSIO_API}" \\`);
|
|
533
616
|
lines.push(` | node -e "const d=JSON.parse(require('fs').readFileSync(0,'utf8'));` +
|
|
534
|
-
`if(
|
|
535
|
-
`d.filter(x=>x.type==='dir'
|
|
617
|
+
`if(Array.isArray(d)===false){console.log('SKIP');process.exit(0)}` +
|
|
618
|
+
`d.filter(x=>x.type==='dir'&&x.name.startsWith('.')===false).forEach(x=>console.log(x.name))"`);
|
|
536
619
|
lines.push(`\`\`\``);
|
|
537
620
|
lines.push(``);
|
|
538
621
|
lines.push(`**Stage 2 — Pick the directory that best matches "${input}":**`);
|
|
@@ -545,15 +628,27 @@ function buildComposioFetchBlock(lines, input) {
|
|
|
545
628
|
lines.push(`curl -sf "${COMPOSIO_RAW}/\${SKILL_DIR}/SKILL.md" \\`);
|
|
546
629
|
lines.push(` -o ".claude/skills/\${SKILL_DIR}/SKILL.md"`);
|
|
547
630
|
lines.push(`\`\`\``);
|
|
548
|
-
lines.push(`Verify the file has real content (rule
|
|
631
|
+
lines.push(`Verify the file has real content (rule 7 above). If empty: delete and move on.`);
|
|
632
|
+
lines.push(``);
|
|
633
|
+
lines.push(`**README-driven fallback:** If the directory listing above fails or returns unexpected content:`);
|
|
549
634
|
lines.push(``);
|
|
550
|
-
lines.push(`**README-driven fallback:** If the directory listing above fails or returns unexpected content,`);
|
|
551
|
-
lines.push(`fetch the repo's README.md and parse it for sections matching "${input}":`);
|
|
552
635
|
lines.push(`\`\`\`bash`);
|
|
553
|
-
lines.push(`#
|
|
554
|
-
lines.push(`curl -sf "https://raw.githubusercontent.com/${COMPOSIO_REPO}/master/README.md"
|
|
636
|
+
lines.push(`# Step 1: Fetch the ComposioHQ README and extract entries with links`);
|
|
637
|
+
lines.push(`curl -sf "https://raw.githubusercontent.com/${COMPOSIO_REPO}/master/README.md" \\`);
|
|
638
|
+
lines.push(` | node -e "const t=require('fs').readFileSync(0,'utf8');` +
|
|
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))"`);
|
|
642
|
+
lines.push(`\`\`\``);
|
|
643
|
+
lines.push(``);
|
|
644
|
+
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
|
+
lines.push(`SKILL_DIR="matched-skill"`);
|
|
649
|
+
lines.push(`mkdir -p ".claude/skills/\${SKILL_DIR}"`);
|
|
650
|
+
lines.push(`curl -sf "\${RESOLVED_URL}" -o ".claude/skills/\${SKILL_DIR}/SKILL.md"`);
|
|
555
651
|
lines.push(`\`\`\``);
|
|
556
|
-
lines.push(`
|
|
557
|
-
lines.push(`(rule 2+3 above) to download from the linked repo, regardless of its owner.`);
|
|
652
|
+
lines.push(`Verify content (rule 7). If empty: delete and continue to next STEP.`);
|
|
558
653
|
lines.push(``);
|
|
559
654
|
}
|
package/package.json
CHANGED
package/templates/add.md
CHANGED
|
@@ -30,8 +30,32 @@ Skills: {{SKILLS_LIST}} | Commands: {{COMMANDS_LIST}}
|
|
|
30
30
|
|
|
31
31
|
---
|
|
32
32
|
|
|
33
|
+
## Marketplace search — AUTOMATICALLY spawn the marketplace-fetcher subagent
|
|
34
|
+
|
|
35
|
+
**MANDATORY:** For any request that involves installing a skill, agent, plugin, or integration,
|
|
36
|
+
you MUST use the Agent tool to spawn the `marketplace-fetcher` subagent. Do NOT run catalog
|
|
37
|
+
fetches in your own context. Do NOT ask the user whether to spawn — just do it.
|
|
38
|
+
|
|
39
|
+
**How to spawn:** Use the Agent tool with these parameters:
|
|
40
|
+
- `description`: "Fetch and install: {{USER_INPUT}}"
|
|
41
|
+
- `prompt`: Copy the ENTIRE marketplace instructions block below (between the `BEGIN` and `END` markers) as the subagent's task prompt.
|
|
42
|
+
|
|
43
|
+
The subagent runs in isolation on Haiku: it fetches catalogs, parses READMEs, downloads files to disk,
|
|
44
|
+
and returns exactly one confirmation line. Its context dies when it finishes — zero catalog bloat
|
|
45
|
+
enters your session. This cuts API calls from ~113 to ~6 and cache-read tokens from ~4.8M to ~180K.
|
|
46
|
+
|
|
47
|
+
**Only skip the subagent** if the request is purely a single-file edit (add one MCP server to .mcp.json,
|
|
48
|
+
add a hook to settings.json, or append to CLAUDE.md) with no catalog search needed.
|
|
49
|
+
|
|
50
|
+
--- BEGIN MARKETPLACE INSTRUCTIONS (pass this entire block to the subagent) ---
|
|
51
|
+
|
|
33
52
|
{{MARKETPLACE_INSTRUCTIONS}}
|
|
34
53
|
|
|
54
|
+
--- END MARKETPLACE INSTRUCTIONS ---
|
|
55
|
+
|
|
56
|
+
After the subagent returns its one-line result, continue to the post-install steps below.
|
|
57
|
+
If the subagent returned `FAILED`, create a production-quality custom skill/agent yourself.
|
|
58
|
+
|
|
35
59
|
---
|
|
36
60
|
|
|
37
61
|
## What you must actually do
|