@channlize/skills 0.0.0-stage → 0.5.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/catalog.ts","../src/frontmatter.ts","../src/install.ts","../src/validate.ts"],"sourcesContent":["import { writeFileSync } from 'node:fs';\nimport { createInterface } from 'node:readline/promises';\nimport { join, relative, resolve } from 'node:path';\nimport {\n buildCatalog,\n bundledSkillsDir,\n fillPrompt,\n loadPrompts,\n loadSkills,\n packageRoot,\n packageVersion,\n} from './catalog';\nimport {\n ALL_TARGETS,\n installSkills,\n TARGET_IDS,\n TARGETS,\n targetDir,\n type FileResult,\n type Scope,\n type TargetId,\n} from './install';\nimport {\n checkUrls,\n validateMarketplace,\n validatePrompts,\n validateSkillsDir,\n type Issue,\n} from './validate';\n\nconst HELP = `Channlize Agent Skills: teach AI coding agents to integrate Channlize.\n\nUsage:\n npx @channlize/skills install [targets] [--project | --user] [options]\n npx @channlize/skills list\n npx @channlize/skills prompts [id] [--project-id <id>] [--widget-key <wk_…>] [--category <c>] [--json]\n npx @channlize/skills validate [skills-dir] [--marketplace <dir>] [--check-urls]\n\nInstall targets (default: --claude):\n --claude .claude/skills (Claude Code)\n --cursor .cursor/skills (Cursor)\n --codex .agents/skills (Codex; Cursor and VS Code read it too)\n --copilot .github/skills (VS Code / GitHub Copilot)\n --all --claude, --codex and --copilot\n --dir <path> any other skills folder\n\nInstall options:\n --project into the current directory (default)\n --user into your home directory (~/.claude/skills, ~/.agents/skills, ~/.copilot/skills…)\n --skill <name> only this skill (repeatable)\n --force overwrite files you changed, without asking\n --dry-run show what would change\n -y, --yes same as --force\n\nFiles you edited after installing are never replaced silently: the CLI asks\nin a terminal and keeps them otherwise (or use --force).`;\n\ninterface Args {\n _: string[];\n flags: Set<string>;\n values: Record<string, string[]>;\n}\n\nconst VALUE_FLAGS = new Set([\n 'dir',\n 'skill',\n 'project-id',\n 'widget-key',\n 'category',\n 'marketplace',\n 'out',\n]);\n\nexport function parseArgs(argv: string[]): Args {\n const args: Args = { _: [], flags: new Set(), values: {} };\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!;\n if (a === '-y') args.flags.add('yes');\n else if (a === '-h') args.flags.add('help');\n else if (a.startsWith('--')) {\n const [key, inline] = a.slice(2).split(/=(.*)/s, 2) as [\n string,\n string | undefined,\n ];\n if (VALUE_FLAGS.has(key)) {\n const value = inline ?? argv[++i];\n if (value === undefined) throw new UsageError(`--${key} needs a value`);\n (args.values[key] ??= []).push(value);\n } else args.flags.add(key);\n } else args._.push(a);\n }\n return args;\n}\n\nclass UsageError extends Error {}\n\nconst out = (s = '') => process.stdout.write(s + '\\n');\nconst err = (s: string) => process.stderr.write(s + '\\n');\n\nfunction printIssues(issues: Issue[]) {\n for (const i of issues) err(` ✗ ${i.file}: ${i.message}`);\n}\n\nasync function install(args: Args): Promise<number> {\n const known = new Set([\n ...TARGET_IDS,\n 'all',\n 'project',\n 'user',\n 'force',\n 'yes',\n 'dry-run',\n ]);\n for (const f of args.flags)\n if (!known.has(f)) throw new UsageError(`unknown option --${f}`);\n if (args.flags.has('project') && args.flags.has('user'))\n throw new UsageError('use --project or --user, not both');\n const scope: Scope = args.flags.has('user') ? 'user' : 'project';\n\n let targets: TargetId[] = args.flags.has('all')\n ? [...ALL_TARGETS]\n : TARGET_IDS.filter(t => args.flags.has(t));\n const dirs: Array<{ label: string; dir: string }> = [];\n for (const d of args.values.dir ?? [])\n dirs.push({ label: 'custom', dir: resolve(d) });\n if (targets.length === 0 && dirs.length === 0) targets = ['claude'];\n for (const t of targets)\n dirs.push({ label: TARGETS[t].label, dir: targetDir(t, scope) });\n\n const all = loadSkills();\n const wanted = args.values.skill ?? [];\n for (const w of wanted) {\n if (!all.some(s => s.name === w))\n throw new UsageError(\n `unknown skill \"${w}\" (see: npx @channlize/skills list)`\n );\n }\n const skills = wanted.length ? all.filter(s => wanted.includes(s.name)) : all;\n const force = args.flags.has('force') || args.flags.has('yes');\n const dryRun = args.flags.has('dry-run');\n\n const interactive =\n process.stdin.isTTY && process.stdout.isTTY && !force && !dryRun;\n const rl = interactive\n ? createInterface({ input: process.stdin, output: process.stdout })\n : null;\n let overwriteAll = false;\n const confirm = rl\n ? async (f: FileResult) => {\n if (overwriteAll) return true;\n const answer = (\n await rl.question(\n ` ${shown(f.dest)} was changed since it was installed. Overwrite? [y/N/a(ll)] `\n )\n )\n .trim()\n .toLowerCase();\n if (answer === 'a' || answer === 'all') overwriteAll = true;\n return answer === 'y' || answer === 'yes' || overwriteAll;\n }\n : undefined;\n\n let kept = 0;\n try {\n for (const { label, dir } of dirs) {\n out(`${dryRun ? '[dry run] ' : ''}${label}: ${shown(dir)}`);\n const results = await installSkills({\n skills,\n dir,\n version: packageVersion(),\n force,\n dryRun,\n confirm,\n });\n for (const r of results) {\n if (r.action === 'unchanged') continue;\n out(\n ` ${symbol[r.action]} ${r.skill}/${r.path}${r.action === 'kept' ? ' (kept your changes)' : ''}`\n );\n }\n const n = (a: FileResult['action']) =>\n results.filter(r => r.action === a).length;\n kept += n('kept');\n out(\n ` ${skills.length} skills: ${n('created')} created, ${n('updated') + n('overwritten')} updated, ${n('unchanged')} unchanged, ${n('kept')} kept`\n );\n }\n } finally {\n rl?.close();\n }\n if (kept > 0)\n out(\n `\\n${kept} file(s) you edited were kept. Re-run with --force to replace them.`\n );\n if (!dryRun) {\n out(\n `\\nDone. Start a new agent session so it picks up the skills, then ask it, for example:`\n );\n out(` \"Add the Channlize chat widget to this app\"`);\n out(`Prompts to copy: npx @channlize/skills prompts`);\n }\n return 0;\n}\n\nconst symbol: Record<FileResult['action'], string> = {\n created: '+',\n updated: '~',\n overwritten: '!',\n unchanged: '=',\n kept: '·',\n};\n\nfunction shown(p: string): string {\n const rel = relative(process.cwd(), p);\n return rel && !rel.startsWith('..') ? rel : p;\n}\n\nfunction list(): number {\n for (const s of loadSkills()) {\n out(s.name);\n out(` ${s.description}`);\n }\n return 0;\n}\n\nfunction prompts(args: Args): number {\n const lib = loadPrompts();\n const category = args.values.category?.[0];\n if (category && !lib.categories[category]) {\n throw new UsageError(\n `unknown category \"${category}\" (${Object.keys(lib.categories).join(', ')})`\n );\n }\n const id = args._[1];\n let entries = lib.prompts.filter(p => !category || p.category === category);\n if (id) {\n entries = entries.filter(p => p.id === id);\n if (entries.length === 0) throw new UsageError(`unknown prompt \"${id}\"`);\n }\n const values = {\n projectId: args.values['project-id']?.[0],\n widgetKey: args.values['widget-key']?.[0],\n };\n if (values.widgetKey && !values.widgetKey.startsWith('wk_')) {\n throw new UsageError(\n '--widget-key takes the public widget key (wk_…). Server keys never go in a prompt.'\n );\n }\n const filled = entries.map(p => ({\n ...p,\n prompt: fillPrompt(p.prompt, lib, values),\n }));\n if (args.flags.has('json')) {\n out(JSON.stringify(filled, null, 2));\n return 0;\n }\n if (!id) {\n for (const p of filled)\n out(`${p.id.padEnd(26)} ${lib.categories[p.category]} · ${p.title}`);\n out(\n `\\nPrint one: npx @channlize/skills prompts <id> --project-id <id> --widget-key <wk_…>`\n );\n return 0;\n }\n out(filled[0]!.prompt);\n return 0;\n}\n\nasync function validate(args: Args): Promise<number> {\n const dir = resolve(args._[1] ?? bundledSkillsDir());\n const { issues, urls, count } = validateSkillsDir(dir);\n const isBundled = dir === resolve(bundledSkillsDir());\n if (isBundled) {\n const names = loadSkills(dir).map(s => s.name);\n issues.push(...validatePrompts(loadPrompts(), names, 'prompts.json'));\n }\n for (const m of args.values.marketplace ?? [])\n issues.push(...validateMarketplace(resolve(m)));\n if (args.flags.has('check-urls')) issues.push(...(await checkUrls(urls)));\n\n if (issues.length) {\n err(`✗ ${issues.length} problem(s) in ${shown(dir)}:`);\n printIssues(issues);\n return 1;\n }\n out(\n `✓ ${count} skills valid in ${shown(dir)}${args.flags.has('check-urls') ? `, ${urls.size} URLs reachable` : ''}`\n );\n return 0;\n}\n\nfunction catalog(args: Args): number {\n const json = JSON.stringify(buildCatalog(), null, 2) + '\\n';\n const file = args.values.out?.[0];\n if (file) writeFileSync(resolve(file), json);\n else process.stdout.write(json);\n return 0;\n}\n\nexport async function main(argv: string[]): Promise<number> {\n try {\n const args = parseArgs(argv);\n if (args.flags.has('version')) {\n out(packageVersion());\n return 0;\n }\n const command = args._[0];\n if (!command || args.flags.has('help') || command === 'help') {\n out(HELP);\n return command || args.flags.has('help') ? 0 : 1;\n }\n switch (command) {\n case 'install':\n return await install(args);\n case 'list':\n return list();\n case 'prompts':\n return prompts(args);\n case 'validate':\n return await validate(args);\n case 'catalog':\n return catalog(args);\n case 'path':\n out(join(packageRoot(), 'skills'));\n return 0;\n default:\n throw new UsageError(`unknown command \"${command}\"`);\n }\n } catch (e) {\n if (e instanceof UsageError) {\n err(`error: ${e.message}\\nRun npx @channlize/skills --help for usage.`);\n return 2;\n }\n throw e;\n }\n}\n\nif (require.main === module) {\n // `npx @channlize/skills prompts | head`: a closed pipe is not an error.\n process.stdout.on('error', (e: NodeJS.ErrnoException) => {\n if (e.code === 'EPIPE') process.exit(0);\n throw e;\n });\n main(process.argv.slice(2)).then(\n code => process.exit(code),\n e => {\n err(String(e?.stack ?? e));\n process.exit(1);\n }\n );\n}\n","import { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { join, relative, resolve, sep } from 'node:path';\nimport { parseSkillFile } from './frontmatter';\n\n/** The package root (one level above dist/ or src/). */\nexport function packageRoot(): string {\n return resolve(__dirname, '..');\n}\n\nexport function bundledSkillsDir(): string {\n return join(packageRoot(), 'skills');\n}\n\nexport function packageVersion(): string {\n const pkg = JSON.parse(\n readFileSync(join(packageRoot(), 'package.json'), 'utf8')\n ) as { version: string };\n return pkg.version;\n}\n\nexport interface SkillFile {\n /** Path inside the skill folder, with forward slashes (`SKILL.md`, `references/x.md`). */\n path: string;\n content: string;\n}\n\nexport interface Skill {\n name: string;\n description: string;\n /** Absolute path of the skill folder. */\n dir: string;\n metadata: Record<string, string>;\n files: SkillFile[];\n}\n\n/** Every file of a skill folder, recursively, SKILL.md first. Dotfiles are skipped. */\nexport function listSkillFiles(dir: string): string[] {\n const out: string[] = [];\n const walk = (d: string) => {\n for (const entry of readdirSync(d).sort()) {\n if (entry.startsWith('.')) continue;\n const full = join(d, entry);\n if (statSync(full).isDirectory()) walk(full);\n else out.push(relative(dir, full).split(sep).join('/'));\n }\n };\n walk(dir);\n return out.sort((a, b) =>\n a === 'SKILL.md' ? -1 : b === 'SKILL.md' ? 1 : a.localeCompare(b)\n );\n}\n\n/** Folders of `root` that contain a SKILL.md. */\nexport function skillFolders(root: string): string[] {\n return readdirSync(root)\n .filter(e => !e.startsWith('.') && statSync(join(root, e)).isDirectory())\n .sort();\n}\n\nexport function loadSkills(root: string = bundledSkillsDir()): Skill[] {\n return skillFolders(root).flatMap(folder => {\n const dir = join(root, folder);\n let text: string;\n try {\n text = readFileSync(join(dir, 'SKILL.md'), 'utf8');\n } catch {\n return [];\n }\n const { data } = parseSkillFile(text);\n const name = typeof data?.name === 'string' ? data.name : folder;\n const description =\n typeof data?.description === 'string' ? data.description : '';\n const metadata =\n data && typeof data.metadata === 'object' ? data.metadata : {};\n const files = listSkillFiles(dir).map(path => ({\n path,\n content: readFileSync(join(dir, path), 'utf8'),\n }));\n return [{ name, description, dir, metadata, files }];\n });\n}\n\n// ---------- prompts ----------\n\nexport interface PromptEntry {\n id: string;\n title: string;\n category: string;\n frameworks: string[];\n skills: string[];\n prompt: string;\n}\n\nexport interface PromptLibrary {\n versions: Record<string, string>;\n categories: Record<string, string>;\n prompts: PromptEntry[];\n}\n\nexport function loadPrompts(\n file: string = join(packageRoot(), 'prompts.json')\n): PromptLibrary {\n return JSON.parse(readFileSync(file, 'utf8')) as PromptLibrary;\n}\n\nexport interface PromptValues {\n projectId?: string;\n widgetKey?: string;\n}\n\nexport const PROMPT_PLACEHOLDERS = [\n 'projectId',\n 'widgetKey',\n 'react',\n 'core',\n 'mcp',\n] as const;\n\n/** Placeholders (`{{name}}`) used in a template. */\nexport function placeholdersOf(template: string): string[] {\n return [...template.matchAll(/\\{\\{\\s*([A-Za-z0-9_]+)\\s*\\}\\}/g)].map(\n m => m[1]!\n );\n}\n\n/**\n * Fills a prompt template. Public values only: the project ID and widget key\n * fall back to visible placeholders; server keys are never part of a prompt.\n */\nexport function fillPrompt(\n template: string,\n library: Pick<PromptLibrary, 'versions'>,\n values: PromptValues = {}\n): string {\n const map: Record<string, string> = {\n projectId: values.projectId || '<your-project-id>',\n widgetKey: values.widgetKey || '<your-widget-key (wk_…)>',\n ...library.versions,\n };\n return template.replace(\n /\\{\\{\\s*([A-Za-z0-9_]+)\\s*\\}\\}/g,\n (whole, key: string) => map[key] ?? whole\n );\n}\n\n// ---------- catalog (for the dashboard) ----------\n\nexport interface Catalog {\n package: string;\n version: string;\n skills: Array<Omit<Skill, 'dir'>>;\n prompts: PromptLibrary;\n}\n\nexport function buildCatalog(): Catalog {\n return {\n package: '@channlize/skills',\n version: packageVersion(),\n skills: loadSkills().map(({ dir: _dir, ...rest }) => rest),\n prompts: loadPrompts(),\n };\n}\n","/**\n * A deliberately small YAML-frontmatter reader for SKILL.md files: single-line\n * `key: value` scalars (optionally quoted) and one level of nested string maps\n * (`metadata:`). That is everything the Agent Skills spec uses, and anything\n * fancier (block scalars, lists, anchors) is reported instead of guessed.\n */\n\nexport type FrontmatterValue = string | Record<string, string>;\n\nexport interface ParsedSkillFile {\n /** Null when the file doesn't start with a `---` frontmatter block. */\n data: Record<string, FrontmatterValue> | null;\n body: string;\n errors: string[];\n}\n\nfunction unquote(raw: string): string {\n const v = raw.trim();\n if (v.length >= 2 && v.startsWith('\"') && v.endsWith('\"')) {\n return v.slice(1, -1).replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, '\\\\');\n }\n if (v.length >= 2 && v.startsWith(\"'\") && v.endsWith(\"'\")) {\n return v.slice(1, -1).replace(/''/g, \"'\");\n }\n return v;\n}\n\nexport function parseSkillFile(text: string): ParsedSkillFile {\n const src = text.replace(/^\\uFEFF/, '').replace(/\\r\\n/g, '\\n');\n if (!src.startsWith('---\\n')) {\n return { data: null, body: src, errors: [] };\n }\n const end = src.indexOf('\\n---', 4);\n if (end === -1) {\n return {\n data: null,\n body: src,\n errors: ['frontmatter is not closed with ---'],\n };\n }\n const block = src.slice(4, end);\n const afterFence = src.indexOf('\\n', end + 1);\n const body = afterFence === -1 ? '' : src.slice(afterFence + 1);\n\n const data: Record<string, FrontmatterValue> = {};\n const errors: string[] = [];\n let currentMap: Record<string, string> | null = null;\n\n block.split('\\n').forEach((line, i) => {\n const lineNo = i + 2;\n if (!line.trim() || line.trimStart().startsWith('#')) return;\n\n const nested = /^\\s+([A-Za-z0-9_.-]+):\\s*(.*)$/.exec(line);\n if (nested && currentMap) {\n currentMap[nested[1]!] = unquote(nested[2]!);\n return;\n }\n\n const top = /^([A-Za-z0-9_-]+):\\s*(.*)$/.exec(line);\n if (!top) {\n errors.push(`line ${lineNo}: expected \"key: value\"`);\n return;\n }\n const [, key, rawValue] = top as unknown as [string, string, string];\n if (key in data) errors.push(`line ${lineNo}: duplicate key \"${key}\"`);\n if (rawValue === '') {\n currentMap = {};\n data[key] = currentMap;\n return;\n }\n currentMap = null;\n if (/^[>|][+-]?$/.test(rawValue.trim())) {\n errors.push(\n `line ${lineNo}: \"${key}\" uses a block scalar; write it on one line`\n );\n return;\n }\n if (rawValue.trim().startsWith('[') || rawValue.trim().startsWith('{')) {\n errors.push(`line ${lineNo}: \"${key}\" must be a plain string`);\n return;\n }\n data[key] = unquote(rawValue);\n });\n\n return { data, body, errors };\n}\n","import { createHash } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport type { Skill } from './catalog';\n\n/**\n * Where each agent reads Agent Skills (folders with a SKILL.md), per its docs:\n * - Claude Code: `.claude/skills`, `~/.claude/skills`\n * - Cursor: `.cursor/skills`, `~/.cursor/skills` (it also reads `.agents` and `.claude`)\n * - Codex: `.agents/skills`, `~/.agents/skills`\n * - VS Code / GitHub Copilot: `.github/skills`, `~/.copilot/skills`\n */\nexport const TARGETS = {\n claude: {\n label: 'Claude Code',\n project: '.claude/skills',\n user: '.claude/skills',\n },\n cursor: {\n label: 'Cursor',\n project: '.cursor/skills',\n user: '.cursor/skills',\n },\n codex: {\n label: 'Codex (and other .agents readers)',\n project: '.agents/skills',\n user: '.agents/skills',\n },\n copilot: {\n label: 'VS Code / GitHub Copilot',\n project: '.github/skills',\n user: '.copilot/skills',\n },\n} as const;\n\nexport type TargetId = keyof typeof TARGETS;\nexport const TARGET_IDS = Object.keys(TARGETS) as TargetId[];\n/**\n * `--all`: Claude Code, `.agents` (Codex; Cursor reads it too) and Copilot.\n * `.cursor/skills` is left out because Cursor already reads `.agents/skills`\n * and `.claude/skills`, and a third copy would only list each skill again.\n */\nexport const ALL_TARGETS: TargetId[] = ['claude', 'codex', 'copilot'];\n\nexport const MANIFEST = '.channlize-skill.json';\n\nexport type Scope = 'project' | 'user';\n\nexport function targetDir(\n target: TargetId,\n scope: Scope,\n cwd = process.cwd(),\n home = homedir()\n): string {\n const t = TARGETS[target];\n return scope === 'user' ? join(home, t.user) : resolve(cwd, t.project);\n}\n\nconst sha256 = (s: string) => createHash('sha256').update(s).digest('hex');\n\ninterface Manifest {\n package: string;\n version: string;\n files: Record<string, string>;\n}\n\nfunction readManifest(skillDir: string): Manifest | null {\n try {\n return JSON.parse(\n readFileSync(join(skillDir, MANIFEST), 'utf8')\n ) as Manifest;\n } catch {\n return null;\n }\n}\n\nexport type FileAction =\n 'created' | 'updated' | 'unchanged' | 'overwritten' | 'kept';\n\nexport interface FileResult {\n skill: string;\n path: string;\n dest: string;\n action: FileAction;\n}\n\n/**\n * Asked before replacing a file the user changed since it was installed (or\n * one that was not installed by this package). Return true to overwrite.\n */\nexport type ConfirmOverwrite = (file: FileResult) => Promise<boolean> | boolean;\n\nexport interface InstallOptions {\n skills: Skill[];\n /** Absolute skills directory, e.g. `<repo>/.claude/skills`. */\n dir: string;\n version: string;\n force?: boolean;\n dryRun?: boolean;\n confirm?: ConfirmOverwrite;\n}\n\n/**\n * Copies skills into `dir/<skill>/`, never silently replacing a file the user\n * edited: an unchanged previous install (same hash as recorded in\n * `.channlize-skill.json`) is updated; anything else needs `force` or a yes\n * from `confirm`, and is kept otherwise.\n */\nexport async function installSkills(\n opts: InstallOptions\n): Promise<FileResult[]> {\n const results: FileResult[] = [];\n for (const skill of opts.skills) {\n const skillDir = join(opts.dir, skill.name);\n const previous = readManifest(skillDir);\n const manifest: Manifest = {\n package: '@channlize/skills',\n version: opts.version,\n files: {},\n };\n\n for (const file of skill.files) {\n const dest = join(skillDir, file.path);\n const next = sha256(file.content);\n const result: FileResult = {\n skill: skill.name,\n path: file.path,\n dest,\n action: 'created',\n };\n\n if (existsSync(dest)) {\n const current = sha256(readFileSync(dest, 'utf8'));\n const recorded = previous?.files[file.path];\n if (current === next) {\n result.action = 'unchanged';\n } else if (recorded && current === recorded) {\n result.action = 'updated';\n } else if (\n opts.force ||\n (opts.confirm &&\n (await opts.confirm({ ...result, action: 'overwritten' })))\n ) {\n result.action = 'overwritten';\n } else {\n result.action = 'kept';\n }\n }\n\n if (\n !opts.dryRun &&\n (result.action === 'created' ||\n result.action === 'updated' ||\n result.action === 'overwritten')\n ) {\n mkdirSync(dirname(dest), { recursive: true });\n writeFileSync(dest, file.content);\n }\n // A kept file keeps its old recorded hash, so it still counts as edited next time.\n manifest.files[file.path] =\n result.action === 'kept' ? (previous?.files[file.path] ?? '') : next;\n results.push(result);\n }\n\n if (!opts.dryRun) {\n mkdirSync(skillDir, { recursive: true });\n writeFileSync(\n join(skillDir, MANIFEST),\n JSON.stringify(manifest, null, 2) + '\\n'\n );\n }\n }\n return results;\n}\n","import { existsSync, readFileSync, statSync } from 'node:fs';\nimport { dirname, join, normalize, relative, resolve, sep } from 'node:path';\nimport {\n listSkillFiles,\n placeholdersOf,\n PROMPT_PLACEHOLDERS,\n skillFolders,\n type PromptLibrary,\n} from './catalog';\nimport { parseSkillFile } from './frontmatter';\n\n/**\n * Checks for the Agent Skills format (https://agentskills.io/specification),\n * plus the stricter rules claude.ai uploads apply: only the spec's\n * frontmatter keys are accepted.\n */\nexport const NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;\nexport const NAME_MAX = 64;\nexport const DESCRIPTION_MAX = 1024;\nexport const COMPATIBILITY_MAX = 500;\nexport const BODY_MAX_LINES = 500;\nexport const SPEC_KEYS = [\n 'name',\n 'description',\n 'license',\n 'compatibility',\n 'metadata',\n 'allowed-tools',\n];\n\n/** Relative to the working directory when inside it, absolute otherwise. */\nexport function displayPath(p: string): string {\n const rel = relative(process.cwd(), p);\n return rel && !rel.startsWith('..') ? rel : p;\n}\n\nexport interface Issue {\n file: string;\n message: string;\n}\n\n/** Markdown links and bare URLs outside code (fenced blocks and inline code are skipped). */\nexport function extractLinks(markdown: string): string[] {\n const prose = markdown\n .replace(/^[ \\t]*(```|~~~)[^\\n]*\\n[\\s\\S]*?^[ \\t]*\\1[^\\n]*$/gm, '')\n .replace(/`[^`\\n]*`/g, '');\n const links = new Set<string>();\n for (const m of prose.matchAll(\n /\\[[^\\]]*\\]\\(\\s*<?([^)\\s>]+)>?(?:\\s+\"[^\"]*\")?\\s*\\)/g\n ))\n links.add(m[1]!);\n for (const m of prose.matchAll(/https?:\\/\\/[^\\s<>)\\]\"'`]+/g))\n links.add(m[0].replace(/[.,;:!?]+$/, ''));\n return [...links];\n}\n\nexport function validateSkill(dir: string): {\n issues: Issue[];\n urls: Map<string, string>;\n} {\n const folder = dir.split(sep).pop() ?? dir;\n const issues: Issue[] = [];\n const urls = new Map<string, string>();\n const skillFile = join(dir, 'SKILL.md');\n const at = (file: string) => displayPath(join(dir, file));\n\n if (!existsSync(skillFile)) {\n issues.push({ file: at('SKILL.md'), message: 'missing SKILL.md' });\n return { issues, urls };\n }\n\n const { data, body, errors } = parseSkillFile(\n readFileSync(skillFile, 'utf8')\n );\n for (const e of errors)\n issues.push({ file: at('SKILL.md'), message: `frontmatter ${e}` });\n if (!data) {\n issues.push({\n file: at('SKILL.md'),\n message: 'no YAML frontmatter (the file must start with ---)',\n });\n } else {\n const { name, description, compatibility, metadata } = data;\n if (typeof name !== 'string' || !name) {\n issues.push({ file: at('SKILL.md'), message: '\"name\" is required' });\n } else {\n if (name.length > NAME_MAX)\n issues.push({\n file: at('SKILL.md'),\n message: `\"name\" is over ${NAME_MAX} characters`,\n });\n if (!NAME_RE.test(name)) {\n issues.push({\n file: at('SKILL.md'),\n message: `\"name\" must be lowercase letters, digits and single hyphens: \"${name}\"`,\n });\n }\n if (name !== folder)\n issues.push({\n file: at('SKILL.md'),\n message: `\"name\" (${name}) must match its folder (${folder})`,\n });\n }\n if (typeof description !== 'string' || !description.trim()) {\n issues.push({\n file: at('SKILL.md'),\n message: '\"description\" is required',\n });\n } else if (description.length > DESCRIPTION_MAX) {\n issues.push({\n file: at('SKILL.md'),\n message: `\"description\" is ${description.length} characters; the limit is ${DESCRIPTION_MAX}`,\n });\n }\n if (\n compatibility !== undefined &&\n (typeof compatibility !== 'string' ||\n compatibility.length > COMPATIBILITY_MAX)\n ) {\n issues.push({\n file: at('SKILL.md'),\n message: `\"compatibility\" must be a string of at most ${COMPATIBILITY_MAX} characters`,\n });\n }\n if (metadata !== undefined && typeof metadata !== 'object') {\n issues.push({\n file: at('SKILL.md'),\n message: '\"metadata\" must be a map of strings',\n });\n }\n for (const key of Object.keys(data)) {\n if (!SPEC_KEYS.includes(key)) {\n issues.push({\n file: at('SKILL.md'),\n message: `unknown frontmatter key \"${key}\" (allowed: ${SPEC_KEYS.join(', ')})`,\n });\n }\n }\n }\n if (!body.trim())\n issues.push({ file: at('SKILL.md'), message: 'the body is empty' });\n const lines = body.split('\\n').length;\n if (lines > BODY_MAX_LINES)\n issues.push({\n file: at('SKILL.md'),\n message: `the body has ${lines} lines; keep it under ${BODY_MAX_LINES}`,\n });\n\n for (const file of listSkillFiles(dir).filter(f => f.endsWith('.md'))) {\n const text = readFileSync(join(dir, file), 'utf8');\n for (const link of extractLinks(text)) {\n if (/^https?:\\/\\//.test(link)) {\n if (!urls.has(link)) urls.set(link, at(file));\n continue;\n }\n if (/^(mailto:|#)/.test(link)) continue;\n const target = normalize(\n join(dirname(join(dir, file)), decodeURI(link.split('#')[0]!))\n );\n if (!target.startsWith(resolve(dir) + sep)) {\n issues.push({\n file: at(file),\n message: `link \"${link}\" points outside the skill folder`,\n });\n } else if (!existsSync(target)) {\n issues.push({ file: at(file), message: `broken link \"${link}\"` });\n }\n }\n }\n return { issues, urls };\n}\n\nexport function validatePrompts(\n library: PromptLibrary,\n skillNames: string[],\n file = 'prompts.json'\n): Issue[] {\n const issues: Issue[] = [];\n const ids = new Set<string>();\n for (const [i, p] of library.prompts.entries()) {\n const where = `${file} prompts[${i}] (${p.id ?? '?'})`;\n const add = (message: string) => issues.push({ file: where, message });\n if (!p.id || !NAME_RE.test(p.id)) add('\"id\" must be kebab-case');\n if (ids.has(p.id)) add('duplicate id');\n ids.add(p.id);\n if (!p.title?.trim()) add('\"title\" is required');\n if (!library.categories[p.category])\n add(`unknown category \"${p.category}\"`);\n if (!Array.isArray(p.frameworks)) add('\"frameworks\" must be an array');\n if (!Array.isArray(p.skills) || p.skills.length === 0)\n add('\"skills\" must list at least one skill');\n for (const s of p.skills ?? [])\n if (!skillNames.includes(s)) add(`unknown skill \"${s}\"`);\n for (const ph of placeholdersOf(p.prompt ?? '')) {\n if (!(PROMPT_PLACEHOLDERS as readonly string[]).includes(ph))\n add(`unknown placeholder {{${ph}}}`);\n }\n const text = p.prompt ?? '';\n for (const s of p.skills ?? [])\n if (!text.includes(s))\n add(`the prompt should tell the assistant to read the ${s} skill`);\n if (!/\\bverify\\b/i.test(text))\n add('the prompt should end with a verification step (\"Verify: …\")');\n if (/ch_live_[A-Za-z0-9]{6,}/.test(text))\n add('the prompt contains what looks like a real server key');\n }\n for (const key of PROMPT_PLACEHOLDERS.slice(2)) {\n if (!library.versions?.[key])\n issues.push({ file, message: `versions.${key} is missing` });\n }\n return issues;\n}\n\n/**\n * A Claude Code marketplace file (`.claude-plugin/marketplace.json`):\n * required fields, and relative plugin sources that resolve to a plugin\n * whose `plugin.json` name matches the entry.\n */\nexport function validateMarketplace(root: string): Issue[] {\n const file = join(root, '.claude-plugin', 'marketplace.json');\n const issues: Issue[] = [];\n const add = (message: string) =>\n issues.push({ file: displayPath(file), message });\n let m: {\n name?: unknown;\n owner?: { name?: unknown };\n plugins?: Array<{ name?: unknown; source?: unknown }>;\n };\n try {\n m = JSON.parse(readFileSync(file, 'utf8'));\n } catch (e) {\n add(`cannot read: ${(e as Error).message}`);\n return issues;\n }\n const idPart = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n if (\n typeof m.name !== 'string' ||\n !idPart.test(m.name) ||\n m.name.includes('..')\n )\n add('\"name\" is missing or invalid');\n if (typeof m.owner?.name !== 'string' || !m.owner.name)\n add('\"owner.name\" is required');\n if (!Array.isArray(m.plugins) || m.plugins.length === 0)\n add('\"plugins\" must list at least one plugin');\n for (const [i, p] of (m.plugins ?? []).entries()) {\n if (typeof p.name !== 'string' || !idPart.test(p.name))\n add(`plugins[${i}].name is invalid`);\n if (typeof p.source === 'string') {\n if (!p.source.startsWith('./') && p.source !== '.')\n add(`plugins[${i}].source must start with \"./\"`);\n if (p.source.split('/').includes('..'))\n add(`plugins[${i}].source must not contain \"..\"`);\n const dir = resolve(root, p.source);\n const manifest = join(dir, '.claude-plugin', 'plugin.json');\n if (!existsSync(dir) || !statSync(dir).isDirectory()) {\n add(`plugins[${i}].source ${p.source} does not exist`);\n } else if (existsSync(manifest)) {\n const pj = JSON.parse(readFileSync(manifest, 'utf8')) as {\n name?: string;\n };\n if (pj.name !== p.name)\n add(\n `plugins[${i}].name (${String(p.name)}) differs from plugin.json name (${pj.name})`\n );\n }\n } else if (\n !p.source ||\n typeof p.source !== 'object' ||\n typeof (p.source as { source?: unknown }).source !== 'string'\n ) {\n add(`plugins[${i}].source must be a \"./\" path or a source object`);\n }\n }\n return issues;\n}\n\n/** Checks external URLs; GET after a failed HEAD, with retries for flaky hosts. */\nexport async function checkUrls(\n urls: Map<string, string>,\n { retries = 2, timeoutMs = 15000 } = {}\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n const probe = async (url: string): Promise<number> => {\n // npmjs.com answers bots with 403; the registry has the same package.\n const npm = /^https:\\/\\/www\\.npmjs\\.com\\/package\\/(.+)$/.exec(url);\n if (npm) url = `https://registry.npmjs.org/${npm[1]!.replace('/', '%2F')}`;\n for (const method of ['HEAD', 'GET']) {\n const res = await fetch(url, {\n method,\n redirect: 'follow',\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (res.status < 400) return res.status;\n if (method === 'GET') return res.status;\n }\n return 0;\n };\n await Promise.all(\n [...urls].map(async ([url, file]) => {\n let last = '';\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const status = await probe(url);\n if (status < 400) return;\n last = `HTTP ${status}`;\n if (status < 500 && status !== 429) break;\n } catch (e) {\n last = (e as Error).message;\n }\n await new Promise(r => setTimeout(r, 1000 * (attempt + 1)));\n }\n issues.push({ file, message: `broken URL ${url} (${last})` });\n })\n );\n return issues;\n}\n\nexport function validateSkillsDir(root: string): {\n issues: Issue[];\n urls: Map<string, string>;\n count: number;\n} {\n const issues: Issue[] = [];\n const urls = new Map<string, string>();\n const folders = skillFolders(root);\n const names = new Set<string>();\n for (const folder of folders) {\n const r = validateSkill(join(root, folder));\n issues.push(...r.issues);\n for (const [u, f] of r.urls) if (!urls.has(u)) urls.set(u, f);\n if (names.has(folder))\n issues.push({ file: folder, message: 'duplicate skill name' });\n names.add(folder);\n }\n if (folders.length === 0)\n issues.push({ file: root, message: 'no skill folders found' });\n return { issues, urls, count: folders.length };\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","out","import_node_fs","import_node_path","import_node_fs","import_node_path"]}
@@ -0,0 +1,139 @@
1
+ declare function bundledSkillsDir(): string;
2
+ declare function packageVersion(): string;
3
+ interface SkillFile {
4
+ /** Path inside the skill folder, with forward slashes (`SKILL.md`, `references/x.md`). */
5
+ path: string;
6
+ content: string;
7
+ }
8
+ interface Skill {
9
+ name: string;
10
+ description: string;
11
+ /** Absolute path of the skill folder. */
12
+ dir: string;
13
+ metadata: Record<string, string>;
14
+ files: SkillFile[];
15
+ }
16
+ declare function loadSkills(root?: string): Skill[];
17
+ interface PromptEntry {
18
+ id: string;
19
+ title: string;
20
+ category: string;
21
+ frameworks: string[];
22
+ skills: string[];
23
+ prompt: string;
24
+ }
25
+ interface PromptLibrary {
26
+ versions: Record<string, string>;
27
+ categories: Record<string, string>;
28
+ prompts: PromptEntry[];
29
+ }
30
+ declare function loadPrompts(file?: string): PromptLibrary;
31
+ interface PromptValues {
32
+ projectId?: string;
33
+ widgetKey?: string;
34
+ }
35
+ /** Placeholders (`{{name}}`) used in a template. */
36
+ declare function placeholdersOf(template: string): string[];
37
+ /**
38
+ * Fills a prompt template. Public values only: the project ID and widget key
39
+ * fall back to visible placeholders; server keys are never part of a prompt.
40
+ */
41
+ declare function fillPrompt(template: string, library: Pick<PromptLibrary, 'versions'>, values?: PromptValues): string;
42
+ interface Catalog {
43
+ package: string;
44
+ version: string;
45
+ skills: Array<Omit<Skill, 'dir'>>;
46
+ prompts: PromptLibrary;
47
+ }
48
+ declare function buildCatalog(): Catalog;
49
+
50
+ /**
51
+ * Where each agent reads Agent Skills (folders with a SKILL.md), per its docs:
52
+ * - Claude Code: `.claude/skills`, `~/.claude/skills`
53
+ * - Cursor: `.cursor/skills`, `~/.cursor/skills` (it also reads `.agents` and `.claude`)
54
+ * - Codex: `.agents/skills`, `~/.agents/skills`
55
+ * - VS Code / GitHub Copilot: `.github/skills`, `~/.copilot/skills`
56
+ */
57
+ declare const TARGETS: {
58
+ readonly claude: {
59
+ readonly label: "Claude Code";
60
+ readonly project: ".claude/skills";
61
+ readonly user: ".claude/skills";
62
+ };
63
+ readonly cursor: {
64
+ readonly label: "Cursor";
65
+ readonly project: ".cursor/skills";
66
+ readonly user: ".cursor/skills";
67
+ };
68
+ readonly codex: {
69
+ readonly label: "Codex (and other .agents readers)";
70
+ readonly project: ".agents/skills";
71
+ readonly user: ".agents/skills";
72
+ };
73
+ readonly copilot: {
74
+ readonly label: "VS Code / GitHub Copilot";
75
+ readonly project: ".github/skills";
76
+ readonly user: ".copilot/skills";
77
+ };
78
+ };
79
+ type TargetId = keyof typeof TARGETS;
80
+ /**
81
+ * `--all`: Claude Code, `.agents` (Codex; Cursor reads it too) and Copilot.
82
+ * `.cursor/skills` is left out because Cursor already reads `.agents/skills`
83
+ * and `.claude/skills`, and a third copy would only list each skill again.
84
+ */
85
+ declare const ALL_TARGETS: TargetId[];
86
+ type Scope = 'project' | 'user';
87
+ declare function targetDir(target: TargetId, scope: Scope, cwd?: string, home?: string): string;
88
+ type FileAction = 'created' | 'updated' | 'unchanged' | 'overwritten' | 'kept';
89
+ interface FileResult {
90
+ skill: string;
91
+ path: string;
92
+ dest: string;
93
+ action: FileAction;
94
+ }
95
+ /**
96
+ * Asked before replacing a file the user changed since it was installed (or
97
+ * one that was not installed by this package). Return true to overwrite.
98
+ */
99
+ type ConfirmOverwrite = (file: FileResult) => Promise<boolean> | boolean;
100
+ interface InstallOptions {
101
+ skills: Skill[];
102
+ /** Absolute skills directory, e.g. `<repo>/.claude/skills`. */
103
+ dir: string;
104
+ version: string;
105
+ force?: boolean;
106
+ dryRun?: boolean;
107
+ confirm?: ConfirmOverwrite;
108
+ }
109
+ /**
110
+ * Copies skills into `dir/<skill>/`, never silently replacing a file the user
111
+ * edited: an unchanged previous install (same hash as recorded in
112
+ * `.channlize-skill.json`) is updated; anything else needs `force` or a yes
113
+ * from `confirm`, and is kept otherwise.
114
+ */
115
+ declare function installSkills(opts: InstallOptions): Promise<FileResult[]>;
116
+
117
+ interface Issue {
118
+ file: string;
119
+ message: string;
120
+ }
121
+ declare function validateSkill(dir: string): {
122
+ issues: Issue[];
123
+ urls: Map<string, string>;
124
+ };
125
+ declare function validatePrompts(library: PromptLibrary, skillNames: string[], file?: string): Issue[];
126
+ /**
127
+ * A Claude Code marketplace file (`.claude-plugin/marketplace.json`):
128
+ * required fields, and relative plugin sources that resolve to a plugin
129
+ * whose `plugin.json` name matches the entry.
130
+ */
131
+ declare function validateMarketplace(root: string): Issue[];
132
+ declare function validateSkillsDir(root: string): {
133
+ issues: Issue[];
134
+ urls: Map<string, string>;
135
+ count: number;
136
+ };
137
+
138
+ export { ALL_TARGETS, TARGETS, buildCatalog, bundledSkillsDir, fillPrompt, installSkills, loadPrompts, loadSkills, packageVersion, placeholdersOf, targetDir, validateMarketplace, validatePrompts, validateSkill, validateSkillsDir };
139
+ export type { Catalog, FileResult, InstallOptions, Issue, PromptEntry, PromptLibrary, PromptValues, Scope, Skill, SkillFile, TargetId };
@@ -0,0 +1,139 @@
1
+ declare function bundledSkillsDir(): string;
2
+ declare function packageVersion(): string;
3
+ interface SkillFile {
4
+ /** Path inside the skill folder, with forward slashes (`SKILL.md`, `references/x.md`). */
5
+ path: string;
6
+ content: string;
7
+ }
8
+ interface Skill {
9
+ name: string;
10
+ description: string;
11
+ /** Absolute path of the skill folder. */
12
+ dir: string;
13
+ metadata: Record<string, string>;
14
+ files: SkillFile[];
15
+ }
16
+ declare function loadSkills(root?: string): Skill[];
17
+ interface PromptEntry {
18
+ id: string;
19
+ title: string;
20
+ category: string;
21
+ frameworks: string[];
22
+ skills: string[];
23
+ prompt: string;
24
+ }
25
+ interface PromptLibrary {
26
+ versions: Record<string, string>;
27
+ categories: Record<string, string>;
28
+ prompts: PromptEntry[];
29
+ }
30
+ declare function loadPrompts(file?: string): PromptLibrary;
31
+ interface PromptValues {
32
+ projectId?: string;
33
+ widgetKey?: string;
34
+ }
35
+ /** Placeholders (`{{name}}`) used in a template. */
36
+ declare function placeholdersOf(template: string): string[];
37
+ /**
38
+ * Fills a prompt template. Public values only: the project ID and widget key
39
+ * fall back to visible placeholders; server keys are never part of a prompt.
40
+ */
41
+ declare function fillPrompt(template: string, library: Pick<PromptLibrary, 'versions'>, values?: PromptValues): string;
42
+ interface Catalog {
43
+ package: string;
44
+ version: string;
45
+ skills: Array<Omit<Skill, 'dir'>>;
46
+ prompts: PromptLibrary;
47
+ }
48
+ declare function buildCatalog(): Catalog;
49
+
50
+ /**
51
+ * Where each agent reads Agent Skills (folders with a SKILL.md), per its docs:
52
+ * - Claude Code: `.claude/skills`, `~/.claude/skills`
53
+ * - Cursor: `.cursor/skills`, `~/.cursor/skills` (it also reads `.agents` and `.claude`)
54
+ * - Codex: `.agents/skills`, `~/.agents/skills`
55
+ * - VS Code / GitHub Copilot: `.github/skills`, `~/.copilot/skills`
56
+ */
57
+ declare const TARGETS: {
58
+ readonly claude: {
59
+ readonly label: "Claude Code";
60
+ readonly project: ".claude/skills";
61
+ readonly user: ".claude/skills";
62
+ };
63
+ readonly cursor: {
64
+ readonly label: "Cursor";
65
+ readonly project: ".cursor/skills";
66
+ readonly user: ".cursor/skills";
67
+ };
68
+ readonly codex: {
69
+ readonly label: "Codex (and other .agents readers)";
70
+ readonly project: ".agents/skills";
71
+ readonly user: ".agents/skills";
72
+ };
73
+ readonly copilot: {
74
+ readonly label: "VS Code / GitHub Copilot";
75
+ readonly project: ".github/skills";
76
+ readonly user: ".copilot/skills";
77
+ };
78
+ };
79
+ type TargetId = keyof typeof TARGETS;
80
+ /**
81
+ * `--all`: Claude Code, `.agents` (Codex; Cursor reads it too) and Copilot.
82
+ * `.cursor/skills` is left out because Cursor already reads `.agents/skills`
83
+ * and `.claude/skills`, and a third copy would only list each skill again.
84
+ */
85
+ declare const ALL_TARGETS: TargetId[];
86
+ type Scope = 'project' | 'user';
87
+ declare function targetDir(target: TargetId, scope: Scope, cwd?: string, home?: string): string;
88
+ type FileAction = 'created' | 'updated' | 'unchanged' | 'overwritten' | 'kept';
89
+ interface FileResult {
90
+ skill: string;
91
+ path: string;
92
+ dest: string;
93
+ action: FileAction;
94
+ }
95
+ /**
96
+ * Asked before replacing a file the user changed since it was installed (or
97
+ * one that was not installed by this package). Return true to overwrite.
98
+ */
99
+ type ConfirmOverwrite = (file: FileResult) => Promise<boolean> | boolean;
100
+ interface InstallOptions {
101
+ skills: Skill[];
102
+ /** Absolute skills directory, e.g. `<repo>/.claude/skills`. */
103
+ dir: string;
104
+ version: string;
105
+ force?: boolean;
106
+ dryRun?: boolean;
107
+ confirm?: ConfirmOverwrite;
108
+ }
109
+ /**
110
+ * Copies skills into `dir/<skill>/`, never silently replacing a file the user
111
+ * edited: an unchanged previous install (same hash as recorded in
112
+ * `.channlize-skill.json`) is updated; anything else needs `force` or a yes
113
+ * from `confirm`, and is kept otherwise.
114
+ */
115
+ declare function installSkills(opts: InstallOptions): Promise<FileResult[]>;
116
+
117
+ interface Issue {
118
+ file: string;
119
+ message: string;
120
+ }
121
+ declare function validateSkill(dir: string): {
122
+ issues: Issue[];
123
+ urls: Map<string, string>;
124
+ };
125
+ declare function validatePrompts(library: PromptLibrary, skillNames: string[], file?: string): Issue[];
126
+ /**
127
+ * A Claude Code marketplace file (`.claude-plugin/marketplace.json`):
128
+ * required fields, and relative plugin sources that resolve to a plugin
129
+ * whose `plugin.json` name matches the entry.
130
+ */
131
+ declare function validateMarketplace(root: string): Issue[];
132
+ declare function validateSkillsDir(root: string): {
133
+ issues: Issue[];
134
+ urls: Map<string, string>;
135
+ count: number;
136
+ };
137
+
138
+ export { ALL_TARGETS, TARGETS, buildCatalog, bundledSkillsDir, fillPrompt, installSkills, loadPrompts, loadSkills, packageVersion, placeholdersOf, targetDir, validateMarketplace, validatePrompts, validateSkill, validateSkillsDir };
139
+ export type { Catalog, FileResult, InstallOptions, Issue, PromptEntry, PromptLibrary, PromptValues, Scope, Skill, SkillFile, TargetId };