🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

claude-setup

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

claude-setup - npm Package Compare versions

Comparing version
2.0.0
to
2.0.3
+43
-22
dist/commands/init.js

@@ -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 @@ }

@@ -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(``);
}
{
"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",