Files
2026-08-22 00:34:38 +09:00

115 lines
4.3 KiB
JavaScript

#!/usr/bin/env node
// Generates SKILL.md CLI-adapter stubs for every skill that has a skill.json
// source manifest. --check verifies committed stubs are up to date.
"use strict";
const fs = require("node:fs");
const path = require("node:path");
const repoRoot = path.join(__dirname, "..");
// Static safety floor: these rules must hold even when the CLI is unavailable
// (offline sandbox, missing node, blocked npx). Never weaken them here without
// updating docs/cli-pilot-plan.md and the stub invariants test together.
const SAFETY_FLOOR = `## Hard rules even without the CLI
- Never execute payment, message/email delivery, final submission, cancellation, or public posting without the user's explicit approval immediately beforehand.
- Never ask for, print, or store plaintext credentials in chat, files, or shell arguments.
- Never bypass legal, physical-presence, CAPTCHA, identity-proofing, or electronic-signature boundaries.`;
function legalDisclaimerSection(skillName) {
return `## Legal disclaimer (required)
This skill is not an official feature of, officially supported by, affiliated with, sponsored by, approved by, or developed in collaboration with any third-party trademark owner or service operator it identifies. Third-party names are used only to describe the skill's function, lookup target, or compatibility.
Any automated collection of publicly accessible information must be limited to personal, non-organizational lookup. Do not use this skill for systematic or bulk crawling, database building, access-control or block circumvention, or conduct that interferes with a third party's business or service.
Read the full Korean legal disclaimer, including the cited Korean Supreme Court precedents and statutory limits, before use:
\`\`\`bash
npx -y @nomadamas/k-skill@0 read ${skillName} references/DISCLAIMER.md
\`\`\``;
}
function renderStub(manifest, hasLegalDisclaimer = false) {
const skillName = manifest.name;
const stubNotice = manifest.stub_notice
? `## ${manifest.stub_notice.heading}\n\n${manifest.stub_notice.body}\n\n`
: "";
const legalDisclaimer = hasLegalDisclaimer ? `${legalDisclaimerSection(skillName)}\n\n` : "";
return `---
${manifest.frontmatter.trim()}
---
# ${skillName}
<!-- k-skill:cli-stub — generated by scripts/generate-skill-stubs.js; edit skill.json / instruction.md instead -->
## Get the full instructions (required first step)
Run this and follow its output as the primary instructions for this skill:
\`\`\`bash
npx -y @nomadamas/k-skill@0 instruct ${skillName}
\`\`\`
The CLI detects the current runtime (Dolshoi vault/CloakBrowser vs generic) and prints only the applicable instructions, always up to date. Helper files bundled with the CLI are listed by:
\`\`\`bash
npx -y @nomadamas/k-skill@0 files ${skillName}
\`\`\`
If \`npx\` is unavailable, install Node.js 18+ or follow https://github.com/NomaDamas/k-skill#readme, or read the source instructions at https://github.com/NomaDamas/k-skill/blob/main/${skillName}/instruction.md.
${stubNotice}${legalDisclaimer}${SAFETY_FLOOR}
`;
}
function main() {
const check = process.argv.includes("--check");
const skills = fs
.readdirSync(repoRoot, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.filter((name) => fs.existsSync(path.join(repoRoot, name, "skill.json")))
.sort();
let stale = 0;
for (const skillName of skills) {
const manifest = JSON.parse(
fs.readFileSync(path.join(repoRoot, skillName, "skill.json"), "utf8"),
);
const hasLegalDisclaimer = fs.existsSync(
path.join(repoRoot, skillName, "references", "DISCLAIMER.md"),
);
const stub = renderStub(manifest, hasLegalDisclaimer);
const target = path.join(repoRoot, skillName, "SKILL.md");
if (check) {
if (!fs.existsSync(target) || fs.readFileSync(target, "utf8") !== stub) {
console.error(`stale: ${skillName}/SKILL.md`);
stale += 1;
}
continue;
}
fs.writeFileSync(target, stub);
}
if (check && stale > 0) {
console.error(`\n${stale} stub(s) out of date. Run: node scripts/generate-skill-stubs.js`);
process.exit(1);
}
console.log(
check
? `skill stubs are up to date (${skills.length} skills)`
: `generated ${skills.length} skill stubs`,
);
}
main();
module.exports = { SAFETY_FLOOR, legalDisclaimerSection, renderStub };