- update only touches the recorded project root; project installs do not follow symlinks or junctions; repeated --force rotates .orig backups; uninstall never deletes outside the recorded root - run npm/npx without a shell on Windows and pin optional tool versions - skip the update check in CI and non-interactive output - lint: cross-document section pointers, preflight row ids, gate rule numbers, per-document size budgets, gate copy equality - eval: scenarios S19-S21, route and read-cost rejudging, interpretation card judging, temp-dir previews and table-form cards no longer fail
54 lines
2.3 KiB
JavaScript
54 lines
2.3 KiB
JavaScript
// packages/skill 의 내용을 CLI 배포물 안(dist/skill)으로 복사한다.
|
|
// 스킬 본문이 npm 패키지에 함께 실려야 npx 한 번으로 설치가 끝난다.
|
|
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { isAllowedSkillFile } from "./skill-allowlist.mjs";
|
|
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
const src = path.resolve(here, "../../skill");
|
|
const dest = path.resolve(here, "../dist/skill");
|
|
|
|
// 텍스트는 LF 로 정규화해서 싣는다.
|
|
// Windows 에서 작업하면 파일이 CRLF 로 저장되고, 그대로 tarball 에 들어가면
|
|
// SKILL.md 의 YAML 프론트매터를 파서에 따라 못 읽는다.
|
|
// 배포물은 어느 OS 에서 만들었는지와 무관하게 같아야 한다.
|
|
const TEXT = new Set([".md", ".txt", ".json", ".yml", ".yaml"]);
|
|
|
|
const excluded = [];
|
|
|
|
async function copyDir(from, to, rel = "") {
|
|
for (const e of await fs.readdir(from, { withFileTypes: true })) {
|
|
if (e.name === "node_modules") continue;
|
|
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
const s = path.join(from, e.name);
|
|
const d = path.join(to, e.name);
|
|
if (e.isDirectory()) {
|
|
await copyDir(s, d, r);
|
|
continue;
|
|
}
|
|
// 허용 목록 밖(테스트·스크래치·dotfile·package.json 등)은 사용자 설치물에 싣지 않는다
|
|
if (!e.isFile() || !isAllowedSkillFile(r)) {
|
|
if (e.name !== "package.json") excluded.push(r);
|
|
continue;
|
|
}
|
|
await fs.mkdir(to, { recursive: true });
|
|
if (TEXT.has(path.extname(e.name))) {
|
|
const text = await fs.readFile(s, "utf8");
|
|
await fs.writeFile(d, text.replace(/\r\n/g, "\n"), "utf8");
|
|
} else {
|
|
await fs.copyFile(s, d);
|
|
}
|
|
}
|
|
}
|
|
|
|
await fs.rm(dest, { recursive: true, force: true });
|
|
await copyDir(src, dest);
|
|
|
|
// 스킬 버전은 CLI 버전과 항상 같다(changesets fixed 그룹) — 여기서 스탬프를 찍어 런타임 조회를 없앤다
|
|
const pkg = JSON.parse(await fs.readFile(path.resolve(here, "../package.json"), "utf8"));
|
|
await fs.writeFile(path.join(dest, ".designpaca_version"), `${pkg.version}\n`, "utf8");
|
|
|
|
const count = (await fs.readdir(dest, { recursive: true })).length;
|
|
console.log(`스킬 번들 완료: ${count}개 항목 → dist/skill`);
|
|
if (excluded.length > 0) console.log(` 설치물에서 제외: ${excluded.join(", ")}`);
|