Some checks are pending
ci / build (push) Waiting to run
- 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 로 수정
115 lines
3.8 KiB
TypeScript
115 lines
3.8 KiB
TypeScript
import {
|
|
applyPlan,
|
|
planInstall,
|
|
tildify,
|
|
type Scope,
|
|
type TargetId,
|
|
} from "@designpaca/core";
|
|
import { getSkill } from "../skill.ts";
|
|
import { bad, dim, fold, heading, info, ok, warn } from "../ui.ts";
|
|
|
|
export interface InstallOptions {
|
|
targets: TargetId[];
|
|
scope: Scope;
|
|
force?: boolean;
|
|
/** 실제로 쓰지 않고 계획만 출력 */
|
|
dryRun?: boolean;
|
|
}
|
|
|
|
export interface InstallOutcome {
|
|
target: TargetId;
|
|
root: string;
|
|
written: number;
|
|
skipped: string[];
|
|
blocked?: string;
|
|
}
|
|
|
|
/** install/update/TUI 가 공유하는 실제 설치 실행부 */
|
|
export async function runInstall(opts: InstallOptions): Promise<InstallOutcome[]> {
|
|
const skill = await getSkill();
|
|
const outcomes: InstallOutcome[] = [];
|
|
|
|
for (const target of opts.targets) {
|
|
const plan = await planInstall(target, opts.scope, skill);
|
|
if (plan.blocked) {
|
|
outcomes.push({ target, root: "", written: 0, skipped: [], blocked: plan.blocked });
|
|
continue;
|
|
}
|
|
if (opts.dryRun) {
|
|
outcomes.push({ target, root: plan.root, written: plan.actions.length, skipped: [] });
|
|
continue;
|
|
}
|
|
const res = await applyPlan(plan, skill.version, { force: opts.force });
|
|
outcomes.push({
|
|
target,
|
|
root: plan.root,
|
|
written: res.written.length,
|
|
skipped: res.skipped,
|
|
});
|
|
}
|
|
return outcomes;
|
|
}
|
|
|
|
export function printOutcomes(outcomes: InstallOutcome[], dryRun = false): void {
|
|
console.log(heading(dryRun ? "설치 계획" : "설치 결과"));
|
|
for (const o of outcomes) {
|
|
if (o.blocked) {
|
|
console.log(bad(`${o.target}: ${o.blocked}`));
|
|
continue;
|
|
}
|
|
const verb = dryRun ? "쓸 파일" : "설치됨";
|
|
console.log(ok(`${o.target} — ${verb} ${o.written}개 ${dim(tildify(o.root))}`));
|
|
if (o.skipped.length > 0) {
|
|
console.log(warn(` 직접 수정하신 파일이라 그대로 두었습니다 (--force 로 덮어쓸 수 있습니다):`));
|
|
for (const s of fold(o.skipped)) console.log(dim(` ${tildify(s)}`));
|
|
}
|
|
}
|
|
}
|
|
|
|
/** 설치 후 안내 — 여기서 끝내지 말고 다음 행동을 알려준다 */
|
|
export function printNextSteps(targets: TargetId[]): void {
|
|
console.log(heading("다음 단계"));
|
|
if (targets.includes("claude-code")) {
|
|
console.log(info(`Claude Code 를 새로 연 뒤 ${dim("/designpaca")} 를 실행해 주세요`));
|
|
}
|
|
if (targets.includes("codex")) {
|
|
console.log(info(`Codex CLI 에서 designpaca 스킬이 자동으로 잡힌다`));
|
|
}
|
|
if (targets.includes("zcode")) {
|
|
console.log(info(`ZCode 는 새 세션부터 designpaca 스킬이 자동으로 잡힌다`));
|
|
}
|
|
if (targets.includes("cursor")) {
|
|
console.log(info(`Cursor 는 새 창을 열 때 ${dim(".cursor/skills/designpaca")} 스킬을 읽는다`));
|
|
}
|
|
if (targets.includes("agents")) {
|
|
console.log(
|
|
info(
|
|
`${dim(".agents/skills")} 는 중립 경로다 — Cursor · Antigravity · ZCode 등 규약을 읽는 하네스가 함께 잡는다`,
|
|
),
|
|
);
|
|
}
|
|
if (targets.includes("antigravity")) {
|
|
console.log(
|
|
info(`Antigravity 는 전역 ${dim("~/.gemini/antigravity-cli/skills")} 를 읽는다 (워크스페이스는 .agents/skills)`),
|
|
);
|
|
}
|
|
if (targets.includes("gemini-cli")) {
|
|
console.log(info(`Gemini CLI 는 ${dim("~/.gemini/skills")} 에서 스킬을 읽는다`));
|
|
}
|
|
if (targets.includes("windsurf")) {
|
|
console.log(
|
|
info(`Windsurf(Cascade) 는 ${dim(".windsurf/skills/designpaca")} 스킬을 읽는다`),
|
|
);
|
|
}
|
|
if (targets.includes("copilot")) {
|
|
console.log(
|
|
info(`GitHub Copilot 은 ${dim(".github/skills")} (~/.copilot/skills) 의 SKILL.md 를 읽는다`),
|
|
);
|
|
}
|
|
if (targets.includes("agents-md")) {
|
|
console.log(
|
|
info(`AGENTS.md 에는 포인터만 넣었다 — 본문은 ${dim(".designpaca/SKILL.md")} 에 있다`),
|
|
);
|
|
}
|
|
console.log(info(`문서: ${dim("https://designpaca.chanpaca.net")}`));
|
|
}
|