Files
SkillCompiler/data/skills-bench/website/scripts/generate-skills-registry.ts
T
2026-09-04 14:58:42 +08:00

174 lines
4.9 KiBLFS
TypeScript

import fs from "fs";
import path from "path";
import matter from "gray-matter";
interface FileNode {
name: string;
type: "file" | "directory";
children?: FileNode[];
}
interface SkillEntry {
id: string;
name: string;
description: string;
fileStructure: FileNode[];
path: string;
relatedTasks: { title: string; path: string }[];
}
function getFileStructure(dirPath: string): FileNode[] {
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
const nodes: FileNode[] = [];
for (const entry of entries) {
if (
entry.name === "node_modules" ||
entry.name === ".git" ||
entry.name === ".DS_Store"
)
continue;
if (entry.isDirectory()) {
nodes.push({
name: entry.name,
type: "directory",
children: getFileStructure(path.join(dirPath, entry.name)),
});
} else {
nodes.push({ name: entry.name, type: "file" });
}
}
return nodes.sort((a, b) => {
if (a.type === b.type) return a.name.localeCompare(b.name);
return a.type === "directory" ? -1 : 1;
});
}
function generateSkillsRegistry(): void {
// Look for tasks in common locations
const candidates = [
path.join(__dirname, "..", "..", "tasks"),
path.join(__dirname, "..", "..", "skillsbench", "tasks"),
path.join(__dirname, "..", "tasks"),
];
let tasksDir = candidates.find((d) => fs.existsSync(d)) || candidates[0];
const outputPath = path.join(
__dirname,
"..",
"src",
"data",
"skills-registry.json"
);
const skillsMap = new Map<string, SkillEntry>();
if (!fs.existsSync(tasksDir)) {
console.warn(
"Tasks directory not found, skipping skills registry generation:",
tasksDir
);
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, "[]");
return;
}
// Find all SKILL.md files recursively
function findSkillFiles(dir: string): string[] {
const results: string[] = [];
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (
entry.name === "node_modules" ||
entry.name === ".git" ||
entry.name === ".next"
)
continue;
if (entry.isDirectory()) {
results.push(...findSkillFiles(fullPath));
} else if (entry.name === "SKILL.md") {
results.push(fullPath);
}
}
return results;
}
const skillFiles = findSkillFiles(tasksDir);
console.log(`Found ${skillFiles.length} SKILL.md files`);
for (const file of skillFiles) {
try {
const fileContent = fs.readFileSync(file, "utf-8");
const { data, content } = matter(fileContent);
const skillDir = path.dirname(file);
const skillFolderName = path.basename(skillDir);
const skillName = data.name || skillFolderName;
const skillId = skillName.toLowerCase().replace(/\s+/g, "-");
// Extract task name from path
// Pattern: tasks/<task-name>/environment/skills/<skill-name>/SKILL.md
const relPath = path.relative(tasksDir, file);
const parts = relPath.split(path.sep);
const taskName = parts[0]; // First component is the task name
let skill = skillsMap.get(skillId);
if (!skill) {
// Get description from frontmatter or first paragraph of content
let description = data.description || "";
if (!description && content) {
// Skip heading lines, get first non-empty paragraph
const lines = content.split("\n");
const nonHeadingLines: string[] = [];
for (const line of lines) {
if (line.startsWith("#")) continue;
if (line.trim() === "" && nonHeadingLines.length > 0) break;
if (line.trim()) nonHeadingLines.push(line.trim());
}
description = nonHeadingLines.join(" ").slice(0, 300);
}
skill = {
id: skillId,
name: skillName,
description,
fileStructure: getFileStructure(skillDir),
path: `tasks/${path.relative(tasksDir, skillDir).split(path.sep).join("/")}`,
relatedTasks: [],
};
skillsMap.set(skillId, skill);
}
// Add related task
if (taskName && !skill.relatedTasks.some((t) => t.title === taskName)) {
skill.relatedTasks.push({
title: taskName,
path: `/tasks/${taskName}`,
});
}
} catch (error) {
console.error(`Error processing skill at ${file}:`, error);
}
}
const skills = Array.from(skillsMap.values()).sort((a, b) =>
a.name.localeCompare(b.name)
);
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
fs.writeFileSync(outputPath, JSON.stringify(skills, null, 2));
console.log(
`Generated skills registry with ${skills.length} unique skills at ${outputPath}`
);
}
generateSkillsRegistry();