- ZCode(~/.zcode/skills) 타깃 추가 - 중립 경로 agents(~/.agents/skills · .agents/skills) 타깃 추가 - gemini-cli(.gemini/skills)·copilot(~/.copilot/skills · .github/skills) 타깃 추가 - Cursor·Windsurf 를 네이티브 스킬 경로로 전환 — 규칙 파일 12,000자 상한 때문에 Windsurf 는 실제 스킬이 아예 설치되지 못했었다. 전역 설치도 가능해진다 - Antigravity 전역 경로를 공식 문서대로 ~/.gemini/antigravity-cli/skills 로 수정
This commit is contained in:
parent
30dc6687dd
commit
8d02820fa5
14 changed files with 379 additions and 136 deletions
|
|
@ -118,57 +118,49 @@ test("agents-md: 포인터만 주입하고 본문은 별도 디렉터리에 푼
|
|||
assert.ok(!after2.includes("designpaca:start"));
|
||||
});
|
||||
|
||||
test("cursor 는 user 범위를 거부한다", async () => {
|
||||
const plan = await planInstall("cursor", "user", skill, env);
|
||||
assert.ok(plan.blocked);
|
||||
await assert.rejects(() => applyPlan(plan, skill.version, { env }));
|
||||
});
|
||||
test("cursor: 네이티브 스킬 경로에 SKILL.md 그대로 싣는다 (전역·프로젝트)", async () => {
|
||||
const user = await planInstall("cursor", "user", skill, env);
|
||||
assert.equal(user.root, path.join(home, ".cursor", "skills", "designpaca"));
|
||||
const proj = await planInstall("cursor", "project", skill, env);
|
||||
assert.equal(proj.root, path.join(cwd, ".cursor", "skills", "designpaca"));
|
||||
|
||||
test("cursor: .mdc 프론트매터로 변환하고 참조 경로를 재작성한다", async () => {
|
||||
const plan = await planInstall("cursor", "project", skill, env);
|
||||
const res = await applyPlan(plan, skill.version, { env });
|
||||
|
||||
const mdc = await fs.readFile(path.join(cwd, ".cursor", "rules", "designpaca.mdc"), "utf8");
|
||||
assert.ok(mdc.startsWith("---\ndescription: 테스트용"));
|
||||
assert.ok(mdc.includes("alwaysApply: false"));
|
||||
assert.ok(mdc.includes("# 본문"));
|
||||
// 본문과 references 가 다른 디렉터리에 놓이므로 경로가 재작성돼야 한다
|
||||
assert.ok(
|
||||
mdc.includes(".cursor/rules/designpaca/references/tokens.md"),
|
||||
"참조 경로가 재작성되지 않았다",
|
||||
);
|
||||
await fs.access(path.join(cwd, ".cursor", "rules", "designpaca", "references", "tokens.md"));
|
||||
const res = await applyPlan(proj, skill.version, { env });
|
||||
const md = await fs.readFile(path.join(proj.root, "SKILL.md"), "utf8");
|
||||
assert.match(md, /^---\n/, "프론트매터가 그대로 있어야 한다");
|
||||
assert.match(md, /`references\/tokens\.md`/, "참조 경로를 재작성하면 안 된다");
|
||||
await fs.access(path.join(proj.root, "references", "tokens.md"));
|
||||
|
||||
await removeInstall(res.record, { env });
|
||||
});
|
||||
|
||||
test("windsurf: Cursor 와 다른 경로·프론트매터를 쓴다", async () => {
|
||||
const plan = await planInstall("windsurf", "project", skill, env);
|
||||
const res = await applyPlan(plan, skill.version, { env });
|
||||
test("windsurf: 네이티브 스킬 경로에 싣는다 — 본문 크기 제한이 없다", async () => {
|
||||
const proj = await planInstall("windsurf", "project", skill, env);
|
||||
assert.equal(proj.root, path.join(cwd, ".windsurf", "skills", "designpaca"));
|
||||
const user = await planInstall("windsurf", "user", skill, env);
|
||||
assert.equal(user.root, path.join(home, ".codeium", "windsurf", "skills", "designpaca"));
|
||||
|
||||
const rule = await fs.readFile(path.join(cwd, ".windsurf", "rules", "designpaca.md"), "utf8");
|
||||
// always_on 이면 디자인 스킬이 모든 메시지의 시스템 프롬프트에 상주한다
|
||||
assert.ok(rule.includes("trigger: model_decision"));
|
||||
assert.ok(!rule.includes("alwaysApply"));
|
||||
assert.ok(rule.includes(".windsurf/rules/designpaca/references/tokens.md"));
|
||||
|
||||
await removeInstall(res.record, { env });
|
||||
});
|
||||
|
||||
test("windsurf: 12,000자 상한을 넘으면 설치를 막는다", async () => {
|
||||
// 예전 규칙 파일 방식은 12,000자 상한 때문에 실제 스킬(본문 25KB)이 막혔다.
|
||||
// 네이티브 스킬은 본문을 통째로 프롬프트에 넣지 않으므로 큰 본문도 설치돼야 한다.
|
||||
const huge: SkillSource = { ...skill, body: "가".repeat(13_000) };
|
||||
const plan = await planInstall("windsurf", "project", huge, env);
|
||||
assert.ok(plan.blocked, "상한 초과인데 막지 않았다");
|
||||
assert.match(plan.blocked, /상한/);
|
||||
const hugePlan = await planInstall("windsurf", "project", huge, env);
|
||||
assert.ok(!hugePlan.blocked, "본문이 크다고 막히면 안 된다");
|
||||
|
||||
const res = await applyPlan(proj, skill.version, { env });
|
||||
const md = await fs.readFile(path.join(proj.root, "SKILL.md"), "utf8");
|
||||
assert.match(md, /`references\/tokens\.md`/, "참조 경로를 재작성하면 안 된다");
|
||||
await fs.access(path.join(proj.root, "references", "tokens.md"));
|
||||
|
||||
await removeInstall(res.record, { env });
|
||||
});
|
||||
|
||||
test("antigravity: 프로젝트는 .agents/skills, 전역은 ~/.gemini/config/skills", async () => {
|
||||
test("antigravity: 프로젝트는 .agents/skills, 전역은 ~/.gemini/antigravity-cli/skills", async () => {
|
||||
// 두 경로가 다른 제품 규약이라 하나만 맞으면 다른 쪽에서 스킬이 안 잡힌다.
|
||||
const proj = await planInstall("antigravity", "project", skill, env);
|
||||
assert.equal(proj.root, path.join(cwd, ".agents", "skills", "designpaca"));
|
||||
|
||||
// 전역은 공식 마이그레이션 문서가 정한 현재 경로다 (~/.gemini/config/skills 는 레거시).
|
||||
const user = await planInstall("antigravity", "user", skill, env);
|
||||
assert.equal(user.root, path.join(home, ".gemini", "config", "skills", "designpaca"));
|
||||
assert.equal(user.root, path.join(home, ".gemini", "antigravity-cli", "skills", "designpaca"));
|
||||
});
|
||||
|
||||
test("antigravity: SKILL.md 를 변환 없이 그대로 싣는다", async () => {
|
||||
|
|
@ -187,3 +179,57 @@ test("antigravity: SKILL.md 를 변환 없이 그대로 싣는다", async () =>
|
|||
|
||||
await removeInstall(res.record, { env });
|
||||
});
|
||||
|
||||
test("agents: 중립 경로 — 전역 ~/.agents/skills, 프로젝트 .agents/skills", async () => {
|
||||
const user = await planInstall("agents", "user", skill, env);
|
||||
assert.equal(user.root, path.join(home, ".agents", "skills", "designpaca"));
|
||||
const proj = await planInstall("agents", "project", skill, env);
|
||||
assert.equal(proj.root, path.join(cwd, ".agents", "skills", "designpaca"));
|
||||
|
||||
const res = await applyPlan(proj, skill.version, { env });
|
||||
const md = await fs.readFile(path.join(proj.root, "SKILL.md"), "utf8");
|
||||
assert.match(md, /^---\n/, "프론트매터가 있어야 한다");
|
||||
assert.match(md, /`references\/tokens\.md`/, "참조 경로를 재작성하면 안 된다");
|
||||
await fs.access(path.join(proj.root, "references", "tokens.md"));
|
||||
|
||||
await removeInstall(res.record, { env });
|
||||
await assert.rejects(() => fs.access(proj.root));
|
||||
});
|
||||
|
||||
test("gemini-cli: 전역 ~/.gemini/skills, 프로젝트 .gemini/skills", async () => {
|
||||
const user = await planInstall("gemini-cli", "user", skill, env);
|
||||
assert.equal(user.root, path.join(home, ".gemini", "skills", "designpaca"));
|
||||
const proj = await planInstall("gemini-cli", "project", skill, env);
|
||||
assert.equal(proj.root, path.join(cwd, ".gemini", "skills", "designpaca"));
|
||||
});
|
||||
|
||||
test("copilot: 전역 ~/.copilot/skills, 프로젝트 .github/skills", async () => {
|
||||
const user = await planInstall("copilot", "user", skill, env);
|
||||
assert.equal(user.root, path.join(home, ".copilot", "skills", "designpaca"));
|
||||
const proj = await planInstall("copilot", "project", skill, env);
|
||||
assert.equal(proj.root, path.join(cwd, ".github", "skills", "designpaca"));
|
||||
});
|
||||
|
||||
test("zcode: 전역은 ~/.zcode/skills, 프로젝트는 .zcode/skills", async () => {
|
||||
// ZCode 는 .zcode/skills 와 .agents/skills 를 모두 읽지만 중립 경로 쪽은
|
||||
// agents 타깃이 쓰는 영역이라 zcode 타깃은 건드리지 않는다.
|
||||
const user = await planInstall("zcode", "user", skill, env);
|
||||
assert.equal(user.root, path.join(home, ".zcode", "skills", "designpaca"));
|
||||
|
||||
const proj = await planInstall("zcode", "project", skill, env);
|
||||
assert.equal(proj.root, path.join(cwd, ".zcode", "skills", "designpaca"));
|
||||
});
|
||||
|
||||
test("zcode: SKILL.md 를 변환 없이 그대로 싣는다", async () => {
|
||||
const plan = await planInstall("zcode", "user", skill, env);
|
||||
const res = await applyPlan(plan, skill.version, { env });
|
||||
|
||||
const md = await fs.readFile(path.join(plan.root, "SKILL.md"), "utf8");
|
||||
assert.match(md, /^---\n/, "프론트매터가 있어야 한다");
|
||||
assert.match(md, /`references\/tokens\.md`/, "참조 경로를 재작성하면 안 된다");
|
||||
await fs.access(path.join(plan.root, "references", "tokens.md"));
|
||||
await fs.access(path.join(plan.root, ".designpaca_version"));
|
||||
|
||||
await removeInstall(res.record, { env });
|
||||
await assert.rejects(() => fs.access(plan.root));
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue