diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 435d39a..612d74e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,6 +34,7 @@ Every skill is a single `SKILL.md` file with YAML frontmatter and detailed instr --- name: your-skill-name description: One-line description of what this skill does (shown in skill list) +allowed-tools: Bash --- # Your Skill Name @@ -57,6 +58,21 @@ Step-by-step instructions for the agent: List any API keys needed and how to configure them. ``` +### Frontmatter fields + +| Field | Required | Description | +|-------|----------|-------------| +| `name` | Yes | Skill name (lowercase, hyphenated). Becomes the slash command: `/your-skill-name` | +| `description` | Yes | One-line description shown in `npx openclaudia list` | +| `allowed-tools` | No | Comma-separated list of tools the skill needs access to (e.g., `Bash`, `Read`, `Write`). Use this when your skill needs to run shell commands (curl, API calls) or perform file operations. If omitted, the skill can only generate text responses. | + +**When to use `allowed-tools`:** +- `Bash` — skill makes API calls via curl, runs CLI tools, or executes shell commands +- `Read` — skill needs to read files from the user's project +- `Write` — skill creates or modifies files + +Example: a skill that sends emails via the Resend API needs `allowed-tools: Bash` to execute curl commands. + ### 4. Skill authoring guidelines - **Be specific** — the more precise your instructions, the better the agent performs diff --git a/cli/index.mjs b/cli/index.mjs index 5db984d..c0f515e 100755 --- a/cli/index.mjs +++ b/cli/index.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node import { execSync } from "node:child_process"; -import { existsSync, mkdirSync, cpSync, rmSync } from "node:fs"; +import { existsSync, mkdirSync, cpSync, rmSync, readFileSync } from "node:fs"; import { readdirSync } from "node:fs"; import { join } from "node:path"; import { homedir, tmpdir } from "node:os"; @@ -48,10 +48,22 @@ function getAvailableSkills(repoDir) { }); } -function installSkills(skillNames) { - const tmpDir = cloneRepo(); - const repoSkillsDir = join(tmpDir, "skills"); - const available = getAvailableSkills(tmpDir); +function getSkillDescription(repoDir, skillName) { + const skillFile = join(repoDir, "skills", skillName, "SKILL.md"); + try { + const content = readFileSync(skillFile, "utf-8"); + const match = content.match(/^---\s*\n([\s\S]*?)\n---/); + if (match) { + const descMatch = match[1].match(/^description:\s*(.+)$/m); + if (descMatch) return descMatch[1].trim().replace(/^["']|["']$/g, ""); + } + } catch {} + return ""; +} + +function installSkillsFromDir(repoDir, skillNames) { + const repoSkillsDir = join(repoDir, "skills"); + const available = getAvailableSkills(repoDir); if (!existsSync(SKILLS_DIR)) { mkdirSync(SKILLS_DIR, { recursive: true }); @@ -74,7 +86,6 @@ function installSkills(skillNames) { installed++; } - rmSync(tmpDir, { recursive: true, force: true }); return installed; } @@ -89,7 +100,10 @@ if (command === "list") { console.log(`\n ${skills.length} available skills:\n`); for (const s of skills.sort()) { const installed = existsSync(join(SKILLS_DIR, s)); - console.log(` ${installed ? "[installed]" : " "} ${s}`); + const desc = getSkillDescription(tmpDir, s); + const status = installed ? "[installed]" : " "; + const descStr = desc ? ` ${desc}` : ""; + console.log(` ${status} ${s.padEnd(24)}${descStr}`); } console.log(); rmSync(tmpDir, { recursive: true, force: true }); @@ -104,20 +118,22 @@ if (command === "install") { process.exit(1); } + const tmpDir = cloneRepo(); + if (targets.includes("--all")) { - const tmpDir = cloneRepo(); const all = getAvailableSkills(tmpDir); - rmSync(tmpDir, { recursive: true, force: true }); console.log(`\nInstalling all ${all.length} skills to ${SKILLS_DIR}...\n`); - const count = installSkills(all); + const count = installSkillsFromDir(tmpDir, all); console.log(`\nDone! ${count} skills installed.`); console.log(`Skills directory: ${SKILLS_DIR}`); console.log(`\nRun "claude" and try /write-blog or /seo-audit to get started.\n`); } else { console.log(`\nInstalling ${targets.length} skill(s) to ${SKILLS_DIR}...\n`); - const count = installSkills(targets); + const count = installSkillsFromDir(tmpDir, targets); console.log(`\nDone! ${count} skill(s) installed.\n`); } + + rmSync(tmpDir, { recursive: true, force: true }); process.exit(0); }