claude-setup
Advanced tools
+43
-22
@@ -87,31 +87,52 @@ import { writeFileSync, mkdirSync, existsSync, readFileSync } from "fs"; | ||
| 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. | ||
| 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. | ||
| ## Your task | ||
| ## CRITICAL RULES — read before doing anything | ||
| You will receive a single install request (e.g., "Stripe integration", "orchestration agent"). Execute the full pipeline below. Return exactly ONE line when done. | ||
| 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. | ||
| 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. | ||
| 3. **Use relative paths only.** Never prepend \`cd /some/path &&\` to commands. You are already in the project directory. | ||
| 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. | ||
| 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. | ||
| 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. | ||
| 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. | ||
| ## Pipeline | ||
| ## How to execute the instructions | ||
| 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: | ||
| Your prompt contains numbered STEPs with bash code blocks. For each STEP: | ||
| 1. **Classify** the request (agent vs skill, categories, SaaS matches) | ||
| 2. **Search catalogs in order** — stop at the first quality match | ||
| 3. **Download** the matched file to \`.claude/agents/\` (agents) or \`.claude/skills/<name>/\` (skills) | ||
| 4. **Verify** the file has real content (not empty, not a stub) | ||
| 5. **Return** exactly one line: | ||
| - \`INSTALLED .claude/agents/<file> <bytes>b\` or | ||
| - \`INSTALLED .claude/skills/<dir>/SKILL.md <bytes>b\` or | ||
| - \`CREATED .claude/agents/<file> <bytes>b\` (if custom-created after all catalogs exhausted) or | ||
| - \`CREATED .claude/skills/<dir>/SKILL.md <bytes>b\` or | ||
| - \`FAILED no match in any catalog and custom creation not possible\` | ||
| 1. Run the first bash command (the catalog listing/search) | ||
| 2. Read the output — it shows available files/directories | ||
| 3. Pick the entry that best matches the user's request | ||
| 4. Substitute that entry name into the download command | ||
| 5. Run the download command | ||
| 6. Verify the downloaded file has real content | ||
| 7. If it works → return the success line. If not → go to next STEP. | ||
| ## Rules | ||
| ## Return format | ||
| - Execute every curl yourself. Never ask for confirmation. | ||
| - Use relative paths only. Never \`cd\` to an absolute path. | ||
| - Pipe all curl output through a node parser — never return raw JSON/README to context. | ||
| - A fetch failure is a routing signal to the next catalog, not a stop condition. | ||
| - If a step produces an ambiguous result, document why it failed in one line, then continue. | ||
| - When creating custom skills/agents (last resort), make them production-valid — no stubs. | ||
| Return exactly ONE line: | ||
| - \`INSTALLED .claude/agents/<file> <bytes>b\` or | ||
| - \`INSTALLED .claude/skills/<dir>/SKILL.md <bytes>b\` or | ||
| - \`CREATED .claude/agents/<file> <bytes>b\` (only after ALL catalogs exhausted) or | ||
| - \`CREATED .claude/skills/<dir>/SKILL.md <bytes>b\` or | ||
| - \`FAILED no match in any catalog and custom creation not possible\` | ||
| ## Example of correct behavior | ||
| If the instructions say: | ||
| \`\`\`bash | ||
| curl -sf "https://api.github.com/repos/X/Y/contents/categories/03-infrastructure" | node -e "..." | ||
| \`\`\` | ||
| And the output shows: \`docker-compose-agent.md\`, \`kubernetes-agent.md\`, \`terraform-agent.md\` | ||
| Then for the download command that says \`AGENT_FILE="matched-agent.md"\`, you substitute: | ||
| \`\`\`bash | ||
| AGENT_FILE="kubernetes-agent.md" | ||
| curl -sf "https://raw.githubusercontent.com/X/Y/main/categories/03-infrastructure/kubernetes-agent.md" -o ".claude/agents/kubernetes-agent.md" | ||
| \`\`\` | ||
| Then verify: \`wc -c ".claude/agents/kubernetes-agent.md"\` | ||
| Now execute the instructions in your prompt. Start with STEP 1 immediately. | ||
| `, "utf8"); | ||
@@ -118,0 +139,0 @@ } |
+110
-66
@@ -238,2 +238,45 @@ /** | ||
| } | ||
| // ── Query keyword extraction (for inline filtering in node parsers) ───── | ||
| /** Extract meaningful search keywords from user input for inline node filtering */ | ||
| function buildQueryRegex(input) { | ||
| 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"]); | ||
| const words = input.toLowerCase() | ||
| .replace(/[^a-z0-9\s-]/g, "") | ||
| .split(/\s+/) | ||
| .filter(w => w.length > 2 && !stopWords.has(w)); | ||
| if (words.length === 0) | ||
| return "."; // match everything if no keywords | ||
| return words.join("|"); | ||
| } | ||
| /** | ||
| * Build a universal section-aware README parser as an inline node -e script. | ||
| * Rules (no hardcoding): | ||
| * 1. Split README into sections by any heading (## / ### / **Bold**) | ||
| * 2. Find sections whose heading matches the query keywords | ||
| * 3. Extract ALL links from matching sections (relative ./ AND absolute github.com) | ||
| * 4. If no section matches, fall back to all links in the entire README | ||
| * This works for any README structure — ComposioHQ, VoltAgent, or anything else. | ||
| */ | ||
| function buildSectionAwareParser(queryRegex) { | ||
| // Each part is carefully escaped for: TypeScript template → bash double-quotes → node -e | ||
| return (`const t=require('fs').readFileSync(0,'utf8');` + | ||
| `const q=/${queryRegex}/i;` + | ||
| // Split by ## headings, ### headings, and **Bold** subsection headers | ||
| `const secs=t.split(/\\n(?=#{2,3}\\s|\\*\\*[A-Z])/);` + | ||
| // 1st pass: sections whose heading matches query | ||
| `let hit=secs.filter(s=>q.test(s.split('\\n')[0]));` + | ||
| // 2nd pass: if no heading matched, find sections that have a link whose name matches | ||
| `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});` + | ||
| // Use matching sections, or fall back to entire README | ||
| `const src=hit.length>0?hit.join('\\n'):t;` + | ||
| // Extract all links — relative (./dir/) and absolute (github.com) and SKILL.md | ||
| `const re=/\\[([^\\]]+)\\]\\(([^)]+)\\)/g;let m;const r=[];` + | ||
| `while((m=re.exec(src))!=null){` + | ||
| `const u=m[2];` + | ||
| `if(u.startsWith('./')||u.includes('github.com')||u.includes('SKILL.md'))` + | ||
| `r.push(m[1]+' | '+u)` + | ||
| `}` + | ||
| `r.slice(0,10).forEach(x=>console.log(x));` + | ||
| `if(r.length===0)console.log('NO_README_MATCHES')`); | ||
| } | ||
| // ── Marketplace instruction builder ───────────────────────────────────── | ||
@@ -297,2 +340,3 @@ // Implements Rule 6 (4-catalog exhaustion) and Rule 7 (3-stage fetch). | ||
| const safeName = input.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); | ||
| const queryRegex = buildQueryRegex(input); | ||
| const targetCategories = agentCategories.length > 0 | ||
@@ -338,5 +382,5 @@ ? agentCategories | ||
| lines.push(`**1c. From ALL file lists above (primary + adjacent), pick the BEST match for "${input}".**`); | ||
| lines.push(`Match by name and relevance. If multiple candidates exist, pick the closest one.`); | ||
| lines.push(`Pick the filename closest to the request. If multiple candidates: pick the first relevant one.`); | ||
| lines.push(``); | ||
| lines.push(`**1d. Download the matched agent file:**`); | ||
| lines.push(`**1d. Download and verify the matched agent file (single step):**`); | ||
| lines.push(`\`\`\`bash`); | ||
@@ -348,34 +392,26 @@ lines.push(`# Replace CATEGORY and AGENT_FILE with actual values from 1a-1c`); | ||
| lines.push(`curl -sf "${VOLTAGENT_SUBAGENTS_RAW}/\${CATEGORY}/\${AGENT_FILE}" \\`); | ||
| lines.push(` -o ".claude/agents/\${AGENT_FILE}"`); | ||
| lines.push(` -o ".claude/agents/\${AGENT_FILE}" && wc -c ".claude/agents/\${AGENT_FILE}"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`If >50 bytes: agent is installed. Return the success line.`); | ||
| lines.push(`If empty or 0 bytes: delete it, log the failure, try README fallback below.`); | ||
| lines.push(``); | ||
| lines.push(`**1e. Verify the file has real content (rule 7 — must not be empty):**`); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`head -3 ".claude/agents/\${AGENT_FILE}"`); | ||
| lines.push(`wc -l ".claude/agents/\${AGENT_FILE}"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`If the file is empty or just frontmatter with no body: delete it, log the failure.`); | ||
| lines.push(`If the file has real content: agent is installed. Skip to "Install result format" below.`); | ||
| lines.push(``); | ||
| lines.push(`**1f. README-driven fallback — if ALL category listings (primary + adjacent) returned SKIP:**`); | ||
| lines.push(`You MUST try this before moving to STEP 2. The README contains the full agent listing.`); | ||
| lines.push(`You MUST run this before moving to STEP 2. The README contains the full agent listing.`); | ||
| lines.push(``); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`# Step 1: Fetch the VoltAgent subagents README`); | ||
| lines.push(`# Fetch README — parser finds sections matching your query, extracts all links`); | ||
| lines.push(`curl -sf "https://raw.githubusercontent.com/${VOLTAGENT_SUBAGENTS_REPO}/main/README.md" \\`); | ||
| lines.push(` | node -e "const t=require('fs').readFileSync(0,'utf8');` + | ||
| `const re=/\\[([^\\]]+)\\]\\(([^)]+)\\)/g;let m;const r=[];` + | ||
| `while((m=re.exec(t))!==null){if(m[2].includes('.md')||m[2].includes('github.com'))r.push(m[1]+' | '+m[2])}` + | ||
| `r.slice(0,15).forEach(x=>console.log(x))"`); | ||
| lines.push(` | node -e "${buildSectionAwareParser(queryRegex)}"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(``); | ||
| lines.push(`From the output, pick the FIRST entry. Extract the URL (after " | "). Resolve it:`); | ||
| lines.push(`- If URL starts with \`./\`: it is relative to the repo root. Prepend \`https://raw.githubusercontent.com/${VOLTAGENT_SUBAGENTS_REPO}/main/\``); | ||
| lines.push(`- If URL contains \`github.com/OWNER/REPO/blob/BRANCH/\`: replace with \`raw.githubusercontent.com/OWNER/REPO/BRANCH/\``); | ||
| 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`); | ||
| lines.push(`Then download:`); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`# Step 2: Pick the best entry for "${input}" from the list above.`); | ||
| lines.push(`# Extract the URL, convert github.com URLs to raw.githubusercontent.com (rule 2).`); | ||
| lines.push(`# Step 3: Download the resolved file`); | ||
| lines.push(`RESOLVED_URL="raw-url-from-step-2"`); | ||
| lines.push(`mkdir -p ".claude/agents"`); | ||
| lines.push(`curl -sf "\${RESOLVED_URL}" -o ".claude/agents/matched-agent.md"`); | ||
| lines.push(`curl -sf "RESOLVED_URL" -o ".claude/agents/AGENT_FILE.md" && wc -c ".claude/agents/AGENT_FILE.md"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`Verify content (rule 7). If empty: delete and continue to STEP 2.`); | ||
| lines.push(`If >50 bytes: installed. If empty or NO_README_MATCHES: continue to STEP 2.`); | ||
| lines.push(``); | ||
@@ -430,2 +466,3 @@ // ── STEP 2: jeremylongshore community skills (fallback for agents) ─ | ||
| const safeName = input.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); | ||
| const queryRegex = buildQueryRegex(input); | ||
| // ── STEP 1: Official Anthropic plugins ──────────────────────────── | ||
@@ -465,6 +502,11 @@ lines.push(`---`); | ||
| `if(Array.isArray(d)===false){console.log('SKIP');process.exit(0)}` + | ||
| `d.filter(x=>x.type==='dir'&&x.name.startsWith('.')===false).forEach(x=>console.log(x.name))"`); | ||
| `const q=/${queryRegex}/i;` + | ||
| `const all=d.filter(x=>x.type==='dir'&&(x.name[0]==='.')===false).map(x=>x.name);` + | ||
| `const matched=all.filter(n=>q.test(n));` + | ||
| `const out=matched.length>0?matched:all;` + | ||
| `out.slice(0,10).forEach(x=>console.log(x))"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`If any directory name matches "${input}", fetch its SKILL.md:`); | ||
| lines.push(`Pick the FIRST directory from output that matches "${input}" and download:`); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`# Replace SKILL_DIR with the actual directory name from the listing above`); | ||
| lines.push(`SKILL_DIR="matched-skill"`); | ||
@@ -474,25 +516,24 @@ lines.push(`mkdir -p ".claude/skills/\${SKILL_DIR}"`); | ||
| lines.push(` -o ".claude/skills/\${SKILL_DIR}/SKILL.md"`); | ||
| lines.push(`wc -c ".claude/skills/\${SKILL_DIR}/SKILL.md"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`Verify content is real (not empty). If empty, delete and continue.`); | ||
| lines.push(`If >50 bytes: installed. If empty: delete and try README fallback below.`); | ||
| lines.push(``); | ||
| lines.push(`**README-driven fallback:** If the API listing above returned SKIP or no match, fetch the README:`); | ||
| lines.push(`**README-driven fallback:** If the API listing above returned SKIP or no match:`); | ||
| lines.push(``); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`# Step 1: Fetch the VoltAgent skills README`); | ||
| lines.push(`# Fetch README — parser finds sections matching your query, extracts all links`); | ||
| lines.push(`curl -sf "https://raw.githubusercontent.com/${VOLTAGENT_SKILLS_REPO}/main/README.md" \\`); | ||
| lines.push(` | node -e "const t=require('fs').readFileSync(0,'utf8');` + | ||
| `const re=/\\[([^\\]]+)\\]\\(([^)]+)\\)/g;let m;const r=[];` + | ||
| `while((m=re.exec(t))!==null){if(m[2].includes('SKILL.md')||m[2].includes('github.com'))r.push(m[1]+' | '+m[2])}` + | ||
| `r.slice(0,10).forEach(x=>console.log(x))"`); | ||
| lines.push(` | node -e "${buildSectionAwareParser(queryRegex)}"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(``); | ||
| lines.push(`From the output, pick the FIRST entry. Extract the URL (after " | "). Resolve it:`); | ||
| lines.push(`- If URL starts with \`./\`: prepend \`https://raw.githubusercontent.com/${VOLTAGENT_SKILLS_REPO}/main/\``); | ||
| lines.push(`- If URL contains \`github.com/OWNER/REPO/blob/BRANCH/\`: replace with \`raw.githubusercontent.com/OWNER/REPO/BRANCH/\``); | ||
| lines.push(`- If URL contains \`github.com/OWNER/REPO/tree/BRANCH/\`: use API to list files, then download the SKILL.md`); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`# Step 2: Pick the entry best matching "${input}". Resolve the URL (rule 2).`); | ||
| lines.push(`# Step 3: Download the resolved skill file`); | ||
| lines.push(`RESOLVED_URL="raw-url-from-step-2"`); | ||
| lines.push(`SKILL_DIR="matched-skill"`); | ||
| lines.push(`mkdir -p ".claude/skills/\${SKILL_DIR}"`); | ||
| lines.push(`curl -sf "\${RESOLVED_URL}" -o ".claude/skills/\${SKILL_DIR}/SKILL.md"`); | ||
| lines.push(`curl -sf "RESOLVED_URL" -o ".claude/skills/\${SKILL_DIR}/SKILL.md" && wc -c ".claude/skills/\${SKILL_DIR}/SKILL.md"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`Verify content (rule 7). If empty: delete and continue to next STEP.`); | ||
| lines.push(`If >50 bytes: installed. If empty or NO_README_MATCHES: continue to next STEP.`); | ||
| lines.push(``); | ||
@@ -585,14 +626,14 @@ // ── STEP 4: ComposioHQ service integrations ─────────────────────── | ||
| function buildCommunitySkillsFetchBlock(lines, categoryFilter) { | ||
| lines.push(`**Stage 1 — Fetch catalog and find matching plugin:**`); | ||
| lines.push(`**Stage 1 — Fetch catalog and find matching plugins (compact output — one line per match):**`); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`curl -sf "${MARKETPLACE_CATALOG_URL}" \\`); | ||
| lines.push(` | node -e "const d=JSON.parse(require('fs').readFileSync(0,'utf8'));` + | ||
| `const q='${categoryFilter}';` + | ||
| `const r=d.plugins.filter(p=>(q===''||p.category.includes(q))&&p.name&&p.source).slice(0,10)` + | ||
| `.map(p=>({name:p.name,source:p.source,desc:p.description}));` + | ||
| `console.log(JSON.stringify(r,null,2));"`); | ||
| `const q='${categoryFilter}'.replace(/^\\\\d+-/,'');` + | ||
| `const r=d.plugins.filter(p=>(q===''||p.category.includes(q)||q.includes(p.category))&&p.name&&p.source).slice(0,10);` + | ||
| `r.forEach(p=>console.log(p.name+' | '+p.source));` + | ||
| `if(r.length===0)console.log('NO_PLUGINS')"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`If this returns an error or empty array, skip to the next STEP.`); | ||
| lines.push(`If NO_PLUGINS or error: skip to next STEP.`); | ||
| lines.push(``); | ||
| lines.push(`**Stage 2 — Score candidates and pick the best match (see rule 4: Relevance > Scope > Uniqueness):**`); | ||
| lines.push(`**Stage 2 — Pick the FIRST match from output. Extract the source path (after " | "). List its skills:**`); | ||
| lines.push(`\`\`\`bash`); | ||
@@ -606,7 +647,7 @@ lines.push(`# Replace PLUGIN_SOURCE_PATH with the "source" value from Stage 1`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`If this fails or shows NO_SKILLS_DIR, the plugin has no installable skills — skip.`); | ||
| lines.push(`If NO_SKILLS_DIR: try the next plugin from Stage 1, or skip to next STEP.`); | ||
| lines.push(``); | ||
| lines.push(`**Stage 3 — Download each skill file and install:**`); | ||
| lines.push(`**Stage 3 — Download the skill file:**`); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`# Replace PLUGIN_SOURCE_PATH and SKILL_NAME with actual values`); | ||
| lines.push(`# Replace PLUGIN_SOURCE_PATH and SKILL_NAME with actual values from above`); | ||
| lines.push(`PLUGIN_SOURCE_PATH="plugins/category/plugin-name"`); | ||
@@ -617,8 +658,10 @@ lines.push(`SKILL_NAME="skill-directory-name"`); | ||
| lines.push(` -o ".claude/skills/\${SKILL_NAME}/SKILL.md"`); | ||
| lines.push(`wc -c ".claude/skills/\${SKILL_NAME}/SKILL.md"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`Verify the file has real content (not empty, not just frontmatter). If empty: delete and move on.`); | ||
| lines.push(`If >50 bytes: installed. If empty: delete and move on to next plugin or next STEP.`); | ||
| lines.push(``); | ||
| } | ||
| function buildComposioFetchBlock(lines, input) { | ||
| lines.push(`**Stage 1 — List available skill directories:**`); | ||
| const queryRegex = buildQueryRegex(input); | ||
| lines.push(`**Stage 1 — List available skill directories and filter by query:**`); | ||
| lines.push(`\`\`\`bash`); | ||
@@ -628,10 +671,12 @@ lines.push(`curl -sf \${GITHUB_TOKEN:+-H "Authorization: token \$GITHUB_TOKEN"} "${COMPOSIO_API}" \\`); | ||
| `if(Array.isArray(d)===false){console.log('SKIP');process.exit(0)}` + | ||
| `d.filter(x=>x.type==='dir'&&x.name.startsWith('.')===false).forEach(x=>console.log(x.name))"`); | ||
| `const q=/${queryRegex}/i;` + | ||
| `const all=d.filter(x=>x.type==='dir'&&(x.name[0]==='.')===false).map(x=>x.name);` + | ||
| `const matched=all.filter(n=>q.test(n));` + | ||
| `const out=matched.length>0?matched:all;` + | ||
| `out.slice(0,10).forEach(x=>console.log(x))"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(``); | ||
| lines.push(`**Stage 2 — Pick the directory that best matches "${input}":**`); | ||
| lines.push(`Match by name similarity. If no directory name is relevant, skip.`); | ||
| lines.push(``); | ||
| lines.push(`**Stage 3 — Download the SKILL.md from the matched directory:**`); | ||
| lines.push(`**Stage 2 — Pick the FIRST directory from the output that matches "${input}" and download:**`); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`# Replace SKILL_DIR with the actual directory name from Stage 1 output`); | ||
| lines.push(`SKILL_DIR="matched-directory"`); | ||
@@ -641,26 +686,25 @@ lines.push(`mkdir -p ".claude/skills/\${SKILL_DIR}"`); | ||
| lines.push(` -o ".claude/skills/\${SKILL_DIR}/SKILL.md"`); | ||
| lines.push(`wc -c ".claude/skills/\${SKILL_DIR}/SKILL.md"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`Verify the file has real content (rule 7 above). If empty: delete and move on.`); | ||
| lines.push(`If >50 bytes: installed. If empty: delete and try README fallback below.`); | ||
| lines.push(``); | ||
| lines.push(`**README-driven fallback:** If the directory listing above fails or returns unexpected content:`); | ||
| lines.push(`**README-driven fallback:** If the directory listing returned SKIP or no match:`); | ||
| lines.push(``); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`# Step 1: Fetch the ComposioHQ README and extract entries with links`); | ||
| lines.push(`# Fetch README — parser finds sections matching your query, extracts all links`); | ||
| lines.push(`curl -sf "https://raw.githubusercontent.com/${COMPOSIO_REPO}/master/README.md" \\`); | ||
| lines.push(` | node -e "const t=require('fs').readFileSync(0,'utf8');` + | ||
| `const re=/\\[([^\\]]+)\\]\\(([^)]+)\\)/g;let m;const r=[];` + | ||
| `while((m=re.exec(t))!==null){if(m[2].includes('SKILL.md')||m[2].includes('github.com'))r.push(m[1]+' | '+m[2])}` + | ||
| `r.slice(0,10).forEach(x=>console.log(x))"`); | ||
| lines.push(` | node -e "${buildSectionAwareParser(queryRegex)}"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(``); | ||
| lines.push(`From the output, pick the FIRST entry. Extract the URL (after " | "). Resolve it:`); | ||
| lines.push(`- If URL starts with \`./\`: prepend \`https://raw.githubusercontent.com/${COMPOSIO_REPO}/master/\` and append \`SKILL.md\` if the URL is a directory`); | ||
| lines.push(`- If URL contains \`github.com/OWNER/REPO/blob/BRANCH/\`: replace with \`raw.githubusercontent.com/OWNER/REPO/BRANCH/\``); | ||
| lines.push(`- If URL contains \`github.com/OWNER/REPO/tree/BRANCH/\`: use API to list files, then download the SKILL.md`); | ||
| lines.push(`\`\`\`bash`); | ||
| lines.push(`# Step 2: Pick the entry best matching "${input}". Resolve the URL (rule 2).`); | ||
| lines.push(`# Step 3: Download the resolved skill file`); | ||
| lines.push(`RESOLVED_URL="raw-url-from-step-2"`); | ||
| lines.push(`SKILL_DIR="matched-skill"`); | ||
| lines.push(`mkdir -p ".claude/skills/\${SKILL_DIR}"`); | ||
| lines.push(`curl -sf "\${RESOLVED_URL}" -o ".claude/skills/\${SKILL_DIR}/SKILL.md"`); | ||
| lines.push(`curl -sf "RESOLVED_URL" -o ".claude/skills/\${SKILL_DIR}/SKILL.md" && wc -c ".claude/skills/\${SKILL_DIR}/SKILL.md"`); | ||
| lines.push(`\`\`\``); | ||
| lines.push(`Verify content (rule 7). If empty: delete and continue to next STEP.`); | ||
| lines.push(`If >50 bytes: installed. If empty or NO_README_MATCHES: continue to next STEP.`); | ||
| lines.push(``); | ||
| } |
+1
-1
| { | ||
| "name": "claude-setup", | ||
| "version": "2.0.0", | ||
| "version": "2.0.3", | ||
| "description": "Setup layer for Claude Code — reads your project, writes command files, Claude Code does the rest", | ||
@@ -5,0 +5,0 @@ "type": "module", |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
320267
1.48%6431
0.94%