import * as FS from 'node:fs'; import * as PATH from 'node:path'; import { fileURLToPath } from 'node:url'; // The Claude Code skills this package ships, and installing them into a working // directory. // // A signature answers *who produced these bytes*; it does not answer *are these // bytes safe*. `audit-bundle` is the second question written down as a // procedure — verify, extract, review — and it belongs next to whoever is about // to run an archive rather than in this repository, so the CLI can write it // out: `bundle skill` drops it into `.claude/skills/` where Claude Code will // find it. // // The skill files are ordinary package members, read from disk relative to this // module. That holds inside a mounted bundle too, as long as they were recorded // in the manifest the bundle was built from — which is why the build's // observation run installs a skill. /** Where a skill's files live in this package. */ const ROOT = new URL('../skills/', import.meta.url); /** The conventional place Claude Code looks for project-local skills. */ export const DEFAULT_SKILLS_DIR = PATH.join('.claude', 'skills'); export interface SkillInfo { /** Directory name, which is also the name Claude Code invokes it by. */ name: string; /** The `description:` from the skill's front matter, when it has one. */ description: string; /** Absolute path to the skill's directory inside this package. */ source: string; /** File names the skill consists of, relative to its directory. */ files: string[]; } export interface InstallResult { name: string; /** Absolute path of the installed skill's directory. */ path: string; /** Absolute paths of the files written. */ written: string[]; /** Absolute paths of files that already existed and were left alone. */ skipped: string[]; } /** Every skill this package carries, in name order. */ export function skills(): SkillInfo[] { const dir = fileURLToPath(ROOT); let names: string[]; try { names = FS.readdirSync(dir).sort(); } catch { return []; } const found: SkillInfo[] = []; for (const name of names) { const source = PATH.join(dir, name); let files: string[]; try { if (!FS.statSync(source).isDirectory()) continue; files = walk(source); } catch { continue; } if (!files.includes('SKILL.md')) continue; found.push({ name, description: describe(PATH.join(source, 'SKILL.md')), source, files }); } return found; } /** One skill by name, or null when this package does not carry it. */ export function skill(name: string): SkillInfo | null { return skills().find((entry) => entry.name === name) ?? null; } /** * Write a skill into `dir` (default `.claude/skills`), as `