src/lib/skills.ts
Rama main · réplica de lectura del repositorio original.
import { readFile, readdir } from "node:fs/promises";
import path from "node:path";
/**
* The Claude Skills that ship with an instance.
*
* They are real files in `skills/`, read from disk rather than pasted into a
* TypeScript constant, so the copy someone downloads from the docs page is
* byte-for-byte the copy in the repository. A skill duplicated into source is a
* skill that drifts from the one people actually have installed.
*/
export type Skill = {
slug: string;
name: string;
description: string;
/** The whole file, front matter included — this is what gets installed. */
body: string;
};
const SKILLS_DIR = path.join(process.cwd(), "skills");
/** Pull `name` and `description` out of the YAML front matter, without a parser. */
function frontMatter(body: string) {
const match = /^---\n([\s\S]*?)\n---/.exec(body);
const block = match?.[1] ?? "";
const field = (key: string) => {
const line = new RegExp(`^${key}:\\s*(.+)$`, "m").exec(block);
return line?.[1].trim().replace(/^["']|["']$/g, "") ?? "";
};
return { name: field("name"), description: field("description") };
}
export async function listSkills(): Promise<Skill[]> {
let entries: string[];
try {
entries = (await readdir(SKILLS_DIR, { withFileTypes: true }))
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name);
} catch {
// A deployment that somehow shipped without the folder should render an
// empty section, not a 500 on the docs page.
return [];
}
const skills = await Promise.all(
entries.map(async (slug) => {
try {
const body = await readFile(path.join(SKILLS_DIR, slug, "SKILL.md"), "utf8");
const { name, description } = frontMatter(body);
return { slug, name: name || slug, description, body };
} catch {
return null;
}
}),
);
return skills
.filter((skill): skill is Skill => skill !== null)
.sort((a, b) => ORDER.indexOf(a.slug) - ORDER.indexOf(b.slug));
}
/** Orientation first; the task skills read as if you have already seen it. */
const ORDER = ["hired", "tailor-a-resume", "run-the-search"];
export async function getSkill(slug: string): Promise<Skill | null> {
// Never let a path segment escape the skills directory.
if (!/^[a-z0-9-]+$/.test(slug)) return null;
return (await listSkills()).find((skill) => skill.slug === slug) ?? null;
}