diff --git a/scripts/generate-skills.mjs b/scripts/generate-skills.mjs index a808062..fcf6697 100644 --- a/scripts/generate-skills.mjs +++ b/scripts/generate-skills.mjs @@ -512,7 +512,7 @@ async function clearGeneratedRoot(rootDir) { // Remove the directory only if nothing protected remains inside it. const remaining = await readdir(fullPath).catch(() => []); if (remaining.length === 0) { - await rm(fullPath, { force: true }); + await rm(fullPath, { recursive: true, force: true }); } } else { await rm(fullPath, { force: true }); diff --git a/scripts/manage-skills.mjs b/scripts/manage-skills.mjs index c63bcf2..511588a 100755 --- a/scripts/manage-skills.mjs +++ b/scripts/manage-skills.mjs @@ -203,6 +203,15 @@ async function interactiveAnswers({ dryRun = false } = {}) { } } +async function readAnswers(source) { + if (source === "-") { + let content = ""; + for await (const chunk of input) content += chunk; + return JSON.parse(content); + } + return JSON.parse(await readFile(path.resolve(source), "utf8")); +} + async function main() { const args = parseArgs(process.argv.slice(2)); if (args.help) { @@ -216,7 +225,7 @@ async function main() { let answers; if (args.answers) { - answers = JSON.parse(await readFile(path.resolve(args.answers), "utf8")); + answers = await readAnswers(args.answers); } else { answers = await buildCliSelection(args); } diff --git a/scripts/tests/generate-skills.test.mjs b/scripts/tests/generate-skills.test.mjs index 90aa27a..1628adf 100644 --- a/scripts/tests/generate-skills.test.mjs +++ b/scripts/tests/generate-skills.test.mjs @@ -9,7 +9,7 @@ */ import assert from "node:assert/strict"; -import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises"; +import { mkdtemp, mkdir, writeFile, rm, readFile } from "node:fs/promises"; import crypto from "node:crypto"; import { tmpdir } from "node:os"; import path from "node:path"; @@ -25,6 +25,7 @@ const { makePackageJsonContent, getGeneratedRoots, buildManifest, + generateSkills, } = await import(`${SCRIPTS_DIR}/generate-skills.mjs`); // ── detectFileType ──────────────────────────────────────────────────────── @@ -348,3 +349,17 @@ test("buildManifest: sha256 matches actual file content", async () => { await rm(dir, { recursive: true, force: true }); } }); + +test("generateSkills: clears pre-existing empty generated directories without EISDIR", async () => { + const targetRoot = await mkdtemp(path.join(tmpdir(), "generate-skills-target-")); + try { + await mkdir(path.join(targetRoot, "skills", "create-plan", "claude-code", "templates"), { recursive: true }); + + await generateSkills(path.resolve(SCRIPTS_DIR, ".."), { targetRoot }); + + await readFile(path.join(targetRoot, "skills", "create-plan", "claude-code", "SKILL.md"), "utf8"); + await readFile(path.join(targetRoot, "skills", "create-plan", "claude-code", "templates", "milestone-plan.md"), "utf8"); + } finally { + await rm(targetRoot, { recursive: true, force: true }); + } +}); diff --git a/scripts/tests/skill-manager-core.test.mjs b/scripts/tests/skill-manager-core.test.mjs index b25e82a..199273d 100644 --- a/scripts/tests/skill-manager-core.test.mjs +++ b/scripts/tests/skill-manager-core.test.mjs @@ -404,7 +404,7 @@ test("cli exits without confirmation when no operations are planned", () => { const output = execFileSync(process.execPath, [ path.join(REPO_ROOT, "scripts", "manage-skills.mjs"), "--answers", - "/dev/stdin", + "-", ], { cwd: REPO_ROOT, encoding: "utf8", diff --git a/skills/reviewer-runtime/pi/.generated-manifest.json b/skills/reviewer-runtime/pi/.generated-manifest.json index 765b757..cb54e6f 100644 --- a/skills/reviewer-runtime/pi/.generated-manifest.json +++ b/skills/reviewer-runtime/pi/.generated-manifest.json @@ -13,7 +13,7 @@ "path": "run-review.sh", "kind": "file", "mode": "755", - "sha256": "c5e4fd082ee1a14059183d2cd2c45653e16d63821edf9cf82e548c9307d29e75" + "sha256": "1af5076f0e4451c0870109216ee5780553ef7d402ec1359f38afefdaf53627c1" } ] } diff --git a/skills/reviewer-runtime/pi/run-review.sh b/skills/reviewer-runtime/pi/run-review.sh index 853b653..27a88cb 100755 --- a/skills/reviewer-runtime/pi/run-review.sh +++ b/skills/reviewer-runtime/pi/run-review.sh @@ -145,7 +145,7 @@ kill_child_process_group() { fi } -# shellcheck disable=SC2329 +# shellcheck disable=SC2317,SC2329 handle_signal() { local signal_name=$1 INTERRUPTED=1 diff --git a/skills/reviewer-runtime/run-review.sh b/skills/reviewer-runtime/run-review.sh index 40f102b..8bdafc6 100755 --- a/skills/reviewer-runtime/run-review.sh +++ b/skills/reviewer-runtime/run-review.sh @@ -144,7 +144,7 @@ kill_child_process_group() { fi } -# shellcheck disable=SC2329 +# shellcheck disable=SC2317,SC2329 handle_signal() { local signal_name=$1 INTERRUPTED=1