Files
antigravity-skills-reference/tools/scripts/restore_vibeship_skills.js
2026-04-07 20:16:53 +02:00

544 lines
17 KiB
JavaScript

const fs = require("fs");
const path = require("path");
const cp = require("child_process");
const YAML = require("yaml");
const ROOT = process.cwd();
const UPSTREAM_SHA = "70b2e1062fc6a38fce854226c27097a87732cb5f";
const SOURCE_LABEL = "vibeship-spawner-skills (Apache 2.0)";
const LIST_PATH = "/tmp/vibeship_files.txt";
const FILES = fs.existsSync(LIST_PATH)
? fs.readFileSync(LIST_PATH, "utf8").trim().split("\n").filter(Boolean)
: [];
const TREE = JSON.parse(
runCommand(
`gh api 'repos/vibeforge1111/vibeship-spawner-skills/git/trees/${UPSTREAM_SHA}?recursive=1'`,
),
);
const SKILL_PATHS = TREE.tree
.filter((entry) => /(^|\/)skill\.yaml$/.test(entry.path))
.map((entry) => entry.path);
function runCommand(cmd) {
return cp.execSync(cmd, {
encoding: "utf8",
maxBuffer: 32 * 1024 * 1024,
cwd: ROOT,
});
}
function fetchText(url) {
return cp.execFileSync("curl", ["-fsSL", "--max-time", "30", url], {
encoding: "utf8",
maxBuffer: 32 * 1024 * 1024,
cwd: ROOT,
});
}
function parseOptionalYaml(relPath) {
const url = `https://raw.githubusercontent.com/vibeforge1111/vibeship-spawner-skills/${UPSTREAM_SHA}/${relPath}`;
try {
return YAML.parse(fetchText(url));
} catch {
return null;
}
}
function parseFrontmatter(content) {
if (!content.startsWith("---\n")) {
return { data: {}, body: content };
}
const end = content.indexOf("\n---\n", 4);
if (end === -1) {
return { data: {}, body: content };
}
return {
data: YAML.parse(content.slice(4, end)) || {},
body: content.slice(end + 5),
};
}
function stringifyFrontmatter(data) {
return `---\n${YAML.stringify(data).trimEnd()}\n---\n`;
}
function sanitizeText(text) {
return String(text || "")
.replace(/\r/g, "")
.replace(/\/Users\/yourname\//g, "~/")
.replace(/\/Users\/username\//g, "~/")
.replace(/C:\/Users\/yourname\//g, "%USERPROFILE%/")
.replace(/C:\/Users\/username\//g, "%USERPROFILE%/");
}
function clean(text) {
return sanitizeText(String(text || "")).trim();
}
function isScalar(value) {
return ["string", "number", "boolean"].includes(typeof value);
}
function formatInline(value) {
if (value === null || value === undefined || value === "") return "";
if (isScalar(value)) return clean(value);
if (Array.isArray(value)) {
const rendered = value.map((entry) => formatInline(entry)).filter(Boolean);
return rendered.join(", ");
}
if (typeof value === "object") {
const preferredKeys = [
"name",
"title",
"role",
"trigger",
"skill",
"id",
"description",
"summary",
"context",
"action",
"pattern",
"severity",
"provides",
"receives",
];
const orderedKeys = [
...preferredKeys.filter((key) => key in value),
...Object.keys(value).filter((key) => !preferredKeys.includes(key)),
];
const parts = [];
for (const key of orderedKeys) {
const rendered = formatInline(value[key]);
if (!rendered) continue;
if (["name", "title", "role", "trigger", "skill", "id"].includes(key)) {
parts.push(rendered);
} else {
parts.push(`${titleize(key)}: ${rendered}`);
}
}
return parts.join(" | ");
}
return clean(String(value));
}
function renderMarkdown(text) {
return sanitizeText(String(text || "")).replace(/\r/g, "").trim();
}
function titleize(slug) {
return String(slug || "")
.split("-")
.filter(Boolean)
.map((part) => {
const lower = part.toLowerCase();
if (lower === "ai") return "AI";
if (lower === "llm") return "LLM";
if (part.toUpperCase() === part && part.length <= 5) return part;
return part.charAt(0).toUpperCase() + part.slice(1);
})
.join(" ");
}
function summarizeDescription(raw) {
const cleaned = clean(raw).replace(/\n+/g, " ").replace(/\s+/g, " ").trim();
if (cleaned.length <= 280) return cleaned;
const sentences = cleaned.match(/[^.!?]+[.!?]+/g);
if (sentences) {
let acc = "";
for (const sentence of sentences) {
const next = (acc ? `${acc} ` : "") + sentence.trim();
if (next.length > 280) break;
acc = next;
}
if (acc) return acc;
}
return `${cleaned.slice(0, 277).trimEnd()}...`;
}
function bullets(items) {
return items
.map((item) => formatInline(item))
.filter(Boolean)
.map((item) => `- ${item}`)
.join("\n");
}
function codeBlock(text) {
return ["```", clean(text), "```"].join("\n");
}
function objectBullets(obj, indent = "") {
const lines = [];
for (const [key, value] of Object.entries(obj || {})) {
const label = titleize(key);
if (Array.isArray(value)) {
if (!value.length) continue;
if (value.every((entry) => typeof entry === "string")) {
lines.push(`${indent}- ${label}: ${value.join(", ")}`);
continue;
}
lines.push(`${indent}- ${label}:`);
for (const entry of value) {
if (typeof entry === "string") {
lines.push(`${indent} - ${entry}`);
continue;
}
if (!entry || typeof entry !== "object") continue;
const parts = [];
for (const [entryKey, entryValue] of Object.entries(entry)) {
if (entryValue === null || entryValue === undefined || entryValue === "") continue;
parts.push(
`${entryKey}: ${
typeof entryValue === "string" ? entryValue : JSON.stringify(entryValue)
}`,
);
}
lines.push(`${indent} - ${parts.join(" | ")}`);
}
continue;
}
if (value && typeof value === "object") {
lines.push(`${indent}- ${label}:`);
lines.push(objectBullets(value, `${indent} `));
continue;
}
if (value !== null && value !== undefined && value !== "") {
lines.push(`${indent}- ${label}: ${value}`);
}
}
return lines.join("\n");
}
function renderToolingSection(title, obj) {
if (!obj || typeof obj !== "object") return null;
const parts = [];
for (const [key, value] of Object.entries(obj)) {
if (!value || (Array.isArray(value) && !value.length)) continue;
parts.push(`### ${titleize(key)}`);
if (Array.isArray(value)) {
const rows = value
.map((entry) => {
if (typeof entry === "string") return `- ${entry}`;
if (!entry || typeof entry !== "object") return null;
const label = entry.name || entry.skill || entry.id || "Item";
const details = [];
if (entry.when) details.push(`When: ${entry.when}`);
if (entry.note) details.push(`Note: ${entry.note}`);
if (entry.description) details.push(entry.description);
return `- ${label}${details.length ? ` - ${details.join(" ")}` : ""}`;
})
.filter(Boolean);
if (rows.length) {
parts.push(rows.join("\n"));
}
continue;
}
if (typeof value === "object") {
parts.push(objectBullets(value));
continue;
}
parts.push(String(value));
}
if (!parts.length) return null;
return `## ${title}\n\n${parts.join("\n\n")}`;
}
function renderIdentity(identity) {
if (!identity || typeof identity !== "object") return null;
const parts = [];
if (identity.role) parts.push(`**Role**: ${clean(identity.role)}`);
if (identity.personality) parts.push(renderMarkdown(identity.personality));
if (Array.isArray(identity.expertise) && identity.expertise.length) {
parts.push(`### Expertise\n\n${bullets(identity.expertise)}`);
}
for (const [key, value] of Object.entries(identity)) {
if (["role", "personality", "expertise"].includes(key)) continue;
if (!value || (Array.isArray(value) && !value.length)) continue;
if (typeof value === "string") {
parts.push(`### ${titleize(key)}\n\n${renderMarkdown(value)}`);
} else if (Array.isArray(value)) {
parts.push(`### ${titleize(key)}\n\n${bullets(value)}`);
} else if (typeof value === "object") {
parts.push(`### ${titleize(key)}\n\n${objectBullets(value)}`);
}
}
if (!parts.length) return null;
return parts.join("\n\n");
}
function renderPatterns(patterns) {
if (!Array.isArray(patterns) || !patterns.length) return null;
const blocks = patterns.map((pattern) => {
const lines = [`### ${pattern.name || pattern.id || "Pattern"}`];
if (pattern.description) lines.push("", clean(pattern.description));
const whenToUse = pattern.when_to_use || pattern.when;
if (whenToUse) lines.push("", `**When to use**: ${clean(whenToUse)}`);
const implementation = pattern.implementation || pattern.example;
if (implementation) lines.push("", renderMarkdown(implementation));
for (const [key, value] of Object.entries(pattern)) {
if (
["name", "id", "description", "when", "when_to_use", "implementation", "example"].includes(
key,
)
) {
continue;
}
if (!value || (Array.isArray(value) && !value.length)) continue;
if (typeof value === "string") {
lines.push("", `### ${titleize(key)}`, "", renderMarkdown(value));
} else if (Array.isArray(value)) {
lines.push("", `### ${titleize(key)}`, "", bullets(value));
} else if (typeof value === "object") {
lines.push("", `### ${titleize(key)}`, "", objectBullets(value));
}
}
return lines.join("\n");
});
return `## Patterns\n\n${blocks.join("\n\n")}`;
}
function renderSharpEdges(data) {
const edges = data && Array.isArray(data.sharp_edges) ? data.sharp_edges : null;
if (!edges || !edges.length) return null;
const blocks = edges.map((edge) => {
const lines = [`### ${edge.title || edge.summary || edge.id || "Sharp Edge"}`];
if (edge.severity) lines.push("", `Severity: ${String(edge.severity).toUpperCase()}`);
if (edge.situation) lines.push("", `Situation: ${clean(edge.situation)}`);
if (edge.symptom) lines.push("", "Symptoms:", clean(edge.symptom));
if (Array.isArray(edge.symptoms) && edge.symptoms.length) {
lines.push("", "Symptoms:", bullets(edge.symptoms));
}
if (edge.why) lines.push("", "Why this breaks:", clean(edge.why));
if (edge.solution) lines.push("", "Recommended fix:", "", renderMarkdown(edge.solution));
return lines.join("\n");
});
return `## Sharp Edges\n\n${blocks.join("\n\n")}`;
}
function renderValidations(data) {
const validations = data && Array.isArray(data.validations) ? data.validations : null;
if (!validations || !validations.length) return null;
const blocks = validations.slice(0, 10).map((entry) => {
const lines = [`### ${entry.name || entry.id || "Validation"}`];
if (entry.severity) lines.push("", `Severity: ${String(entry.severity).toUpperCase()}`);
if (entry.description) lines.push("", clean(entry.description));
if (entry.message) lines.push("", `Message: ${clean(entry.message)}`);
if (entry.fix_action) lines.push("", `Fix action: ${clean(entry.fix_action)}`);
return lines.join("\n");
});
return `## Validation Checks\n\n${blocks.join("\n\n")}`;
}
function renderCollaboration(data) {
if (!data || typeof data !== "object") return null;
const parts = [];
if (Array.isArray(data.delegation_triggers) && data.delegation_triggers.length) {
const rows = data.delegation_triggers.map(
(entry) =>
`- ${entry.trigger} -> ${entry.delegate_to}${
entry.context ? ` (${entry.context})` : ""
}`,
);
parts.push(`### Delegation Triggers\n\n${rows.join("\n")}`);
}
if (Array.isArray(data.common_combinations) && data.common_combinations.length) {
const combos = data.common_combinations.map((entry) => {
const lines = [`### ${entry.name || "Combination"}`];
if (Array.isArray(entry.skills) && entry.skills.length) {
lines.push("", `Skills: ${entry.skills.join(", ")}`);
}
if (entry.workflow) lines.push("", "Workflow:", "", codeBlock(entry.workflow));
return lines.join("\n");
});
parts.push(combos.join("\n\n"));
}
if (!parts.length) return null;
return `## Collaboration\n\n${parts.join("\n\n")}`;
}
function renderWhenToUse(skill) {
const triggers = Array.isArray(skill.triggers) ? skill.triggers : [];
if (!triggers.length) {
return "## When to Use\n\nUse this skill when the request clearly matches the capabilities and patterns described above.";
}
return `## When to Use\n\n${bullets(
triggers.map((trigger) => `User mentions or implies: ${trigger}`),
)}`;
}
function buildBody(skill, sharp, validations, collaboration) {
const sections = [];
sections.push(`# ${skill.name || titleize(skill.id || "skill")}`);
if (skill.description) sections.push(clean(skill.description));
const identitySection = renderIdentity(skill.identity);
if (identitySection) sections.push(identitySection);
if (Array.isArray(skill.principles) && skill.principles.length) {
sections.push(`## Principles\n\n${bullets(skill.principles)}`);
}
if (Array.isArray(skill.owns) && skill.owns.length) {
sections.push(`## Capabilities\n\n${bullets(skill.owns)}`);
}
const prereq = [];
if (skill.prerequisites && typeof skill.prerequisites === "object") {
prereq.push(objectBullets(skill.prerequisites));
}
if (Array.isArray(skill.requires) && skill.requires.length) {
prereq.push(`- Required skills: ${skill.requires.join(", ")}`);
}
if (prereq.length) sections.push(`## Prerequisites\n\n${prereq.filter(Boolean).join("\n")}`);
const scope = [];
if (skill.limits && typeof skill.limits === "object") {
scope.push(objectBullets(skill.limits));
}
if (skill.does_not_own) {
if (Array.isArray(skill.does_not_own)) scope.push(bullets(skill.does_not_own));
else if (typeof skill.does_not_own === "object") scope.push(objectBullets(skill.does_not_own));
}
if (scope.length) sections.push(`## Scope\n\n${scope.filter(Boolean).join("\n")}`);
const tooling = [
renderToolingSection("Tooling", skill.stack),
renderToolingSection("Ecosystem", skill.ecosystem),
].filter(Boolean);
if (tooling.length) sections.push(tooling.join("\n\n"));
const patterns = renderPatterns(skill.patterns);
if (patterns) sections.push(patterns);
const sharpEdges = renderSharpEdges(sharp);
if (sharpEdges) sections.push(sharpEdges);
const validationChecks = renderValidations(validations);
if (validationChecks) sections.push(validationChecks);
const collaborationSection = renderCollaboration(collaboration);
if (collaborationSection) sections.push(collaborationSection);
const related = Array.isArray(skill.pairs_with) ? skill.pairs_with : [];
if (related.length) {
sections.push(
`## Related Skills\n\nWorks well with: ${related
.map((name) => "`" + name + "`")
.join(", ")}`,
);
}
sections.push(renderWhenToUse(skill));
return `${sections.filter(Boolean).join("\n\n")}\n`;
}
function forceUpstreamDescription(absPath, description) {
const content = fs.readFileSync(absPath, "utf8");
const parsed = parseFrontmatter(content);
const next = { ...parsed.data, description: summarizeDescription(description || parsed.data.description || "") };
fs.writeFileSync(absPath, `${stringifyFrontmatter(next)}\n${parsed.body.replace(/^\n/, "")}`);
}
function loadUpstreamPathBySkillId() {
const map = new Map();
for (const upstreamPath of SKILL_PATHS) {
const skillId = path.posix.basename(path.posix.dirname(upstreamPath));
if (!map.has(skillId)) map.set(skillId, []);
map.get(skillId).push(upstreamPath);
}
return map;
}
function main() {
if (!fs.existsSync(LIST_PATH)) {
throw new Error(`Missing skill list: ${LIST_PATH}`);
}
const skillPathMap = loadUpstreamPathBySkillId();
const touched = [];
const skipped = [];
for (const rel of FILES) {
const skillId = rel.split("/")[1];
const matches = skillPathMap.get(skillId) || [];
if (matches.length !== 1) {
skipped.push({ rel, matches });
continue;
}
const upstreamPath = matches[0];
const baseDir = path.posix.dirname(upstreamPath);
const skill = YAML.parse(
fetchText(
`https://raw.githubusercontent.com/vibeforge1111/vibeship-spawner-skills/${UPSTREAM_SHA}/${upstreamPath}`,
),
);
const sharp = parseOptionalYaml(`${baseDir}/sharp-edges.yaml`);
const validations = parseOptionalYaml(`${baseDir}/validations.yaml`);
const collaboration = parseOptionalYaml(`${baseDir}/collaboration.yaml`);
const abs = path.join(ROOT, rel);
const existing = parseFrontmatter(fs.readFileSync(abs, "utf8"));
const frontmatter = { ...existing.data };
frontmatter.name = frontmatter.name || skill.id || skillId;
frontmatter.description = summarizeDescription(skill.description || existing.data.description || "");
frontmatter.risk = frontmatter.risk || "unknown";
frontmatter.source = existing.data.source || SOURCE_LABEL;
if (existing.data.date_added !== undefined) {
frontmatter.date_added = existing.data.date_added;
}
fs.writeFileSync(
abs,
`${stringifyFrontmatter(frontmatter)}\n${buildBody(skill, sharp, validations, collaboration)}`,
);
forceUpstreamDescription(abs, skill.description);
touched.push(rel);
}
console.log(`Rebuilt ${touched.length} vibeship skill files.`);
if (skipped.length) {
console.log("Skipped mappings:");
for (const entry of skipped) {
console.log(`- ${entry.rel} (${entry.matches.length} upstream matches)`);
}
}
}
if (require.main === module) {
main();
}