sayren 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +195 -51
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -8038,6 +8038,61 @@ const cli_retired_token = ((inputs = {}, options = {}) => {
|
|
|
8038
8038
|
return ko_cli_retired_token(inputs);
|
|
8039
8039
|
});
|
|
8040
8040
|
|
|
8041
|
+
//#endregion
|
|
8042
|
+
//#region src/paraglide/messages/cli_skills_install_failed.js
|
|
8043
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8044
|
+
/** @typedef {{ command: NonNullable<unknown>, detail: NonNullable<unknown> }} Cli_Skills_Install_FailedInputs */
|
|
8045
|
+
const ko_cli_skills_install_failed = (i) => {
|
|
8046
|
+
return `에이전트 스킬을 설치하지 못했습니다. 받은 폴더에서 직접 실행하십시오: ${i?.command}
|
|
8047
|
+
${i?.detail}`;
|
|
8048
|
+
};
|
|
8049
|
+
/**
|
|
8050
|
+
|
|
8051
|
+
* @param {Cli_Skills_Install_FailedInputs} inputs
|
|
8052
|
+
* @param {{ locale?: "ko" }} options
|
|
8053
|
+
* @returns {LocalizedString}
|
|
8054
|
+
*/
|
|
8055
|
+
const cli_skills_install_failed = ((inputs, options = {}) => {
|
|
8056
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
8057
|
+
return ko_cli_skills_install_failed(inputs);
|
|
8058
|
+
});
|
|
8059
|
+
|
|
8060
|
+
//#endregion
|
|
8061
|
+
//#region src/paraglide/messages/cli_skills_installed.js
|
|
8062
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8063
|
+
/** @typedef {{}} Cli_Skills_InstalledInputs */
|
|
8064
|
+
const ko_cli_skills_installed = () => {
|
|
8065
|
+
return `에이전트 스킬 설치 완료 — .claude/skills/ (배포 때 올리지 않습니다)`;
|
|
8066
|
+
};
|
|
8067
|
+
/**
|
|
8068
|
+
|
|
8069
|
+
* @param {Cli_Skills_InstalledInputs} inputs
|
|
8070
|
+
* @param {{ locale?: "ko" }} options
|
|
8071
|
+
* @returns {LocalizedString}
|
|
8072
|
+
*/
|
|
8073
|
+
const cli_skills_installed = ((inputs = {}, options = {}) => {
|
|
8074
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
8075
|
+
return ko_cli_skills_installed(inputs);
|
|
8076
|
+
});
|
|
8077
|
+
|
|
8078
|
+
//#endregion
|
|
8079
|
+
//#region src/paraglide/messages/cli_skills_installing.js
|
|
8080
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8081
|
+
/** @typedef {{ source: NonNullable<unknown> }} Cli_Skills_InstallingInputs */
|
|
8082
|
+
const ko_cli_skills_installing = (i) => {
|
|
8083
|
+
return `에이전트 스킬 설치 중 — ${i?.source}`;
|
|
8084
|
+
};
|
|
8085
|
+
/**
|
|
8086
|
+
|
|
8087
|
+
* @param {Cli_Skills_InstallingInputs} inputs
|
|
8088
|
+
* @param {{ locale?: "ko" }} options
|
|
8089
|
+
* @returns {LocalizedString}
|
|
8090
|
+
*/
|
|
8091
|
+
const cli_skills_installing = ((inputs, options = {}) => {
|
|
8092
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
8093
|
+
return ko_cli_skills_installing(inputs);
|
|
8094
|
+
});
|
|
8095
|
+
|
|
8041
8096
|
//#endregion
|
|
8042
8097
|
//#region src/paraglide/messages/cli_skipped_outside.js
|
|
8043
8098
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -9688,7 +9743,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
|
|
|
9688
9743
|
|
|
9689
9744
|
//#endregion
|
|
9690
9745
|
//#region package.json
|
|
9691
|
-
var version = "0.
|
|
9746
|
+
var version = "0.11.0";
|
|
9692
9747
|
|
|
9693
9748
|
//#endregion
|
|
9694
9749
|
//#region src/client.ts
|
|
@@ -11531,6 +11586,116 @@ async function gunzip(bytes, maxBytes) {
|
|
|
11531
11586
|
return out;
|
|
11532
11587
|
}
|
|
11533
11588
|
|
|
11589
|
+
//#endregion
|
|
11590
|
+
//#region src/local-build.ts
|
|
11591
|
+
/** 실패할 때 보여 주는 빌드 출력 줄 수 */
|
|
11592
|
+
const OUTPUT_TAIL_LINES = 40;
|
|
11593
|
+
/** 모아 두는 출력 상한(문자) — 긴 빌드 로그를 다 쥐고 있지 않는다 */
|
|
11594
|
+
const OUTPUT_KEEP_CHARS$1 = 64 * 1024;
|
|
11595
|
+
/** 빌드를 돌릴 수 있는 폴더인가 — `build` 스크립트와 설치된 의존성(`node_modules`)이 있어야 한다 */
|
|
11596
|
+
function planLocalBuild(dir) {
|
|
11597
|
+
let scripts;
|
|
11598
|
+
try {
|
|
11599
|
+
scripts = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).scripts;
|
|
11600
|
+
} catch {
|
|
11601
|
+
return { kind: "no-script" };
|
|
11602
|
+
}
|
|
11603
|
+
const build = scripts && typeof scripts === "object" ? scripts.build : void 0;
|
|
11604
|
+
if (typeof build !== "string" || !build.trim()) return { kind: "no-script" };
|
|
11605
|
+
if (!existsSync(join(dir, "node_modules"))) return { kind: "no-deps" };
|
|
11606
|
+
return { kind: "run" };
|
|
11607
|
+
}
|
|
11608
|
+
/** `npm run build`를 돌린다. 출력은 모아 두고(실패할 때 꼬리를 보여 준다) 화면에 흘리지 않는다 */
|
|
11609
|
+
function runNpmBuild(dir, env) {
|
|
11610
|
+
return new Promise((resolve$1, reject) => {
|
|
11611
|
+
let output = "";
|
|
11612
|
+
const keep = (chunk) => {
|
|
11613
|
+
output = (output + chunk.toString("utf8")).slice(-OUTPUT_KEEP_CHARS$1);
|
|
11614
|
+
};
|
|
11615
|
+
const child = spawn("npm", ["run", "build"], {
|
|
11616
|
+
cwd: dir,
|
|
11617
|
+
env,
|
|
11618
|
+
stdio: [
|
|
11619
|
+
"ignore",
|
|
11620
|
+
"pipe",
|
|
11621
|
+
"pipe"
|
|
11622
|
+
],
|
|
11623
|
+
shell: process.platform === "win32"
|
|
11624
|
+
});
|
|
11625
|
+
child.stdout?.on("data", keep);
|
|
11626
|
+
child.stderr?.on("data", keep);
|
|
11627
|
+
child.once("error", reject);
|
|
11628
|
+
child.once("close", (code) => resolve$1({
|
|
11629
|
+
code,
|
|
11630
|
+
output: tailOf(output)
|
|
11631
|
+
}));
|
|
11632
|
+
});
|
|
11633
|
+
}
|
|
11634
|
+
function tailOf(output, lines = OUTPUT_TAIL_LINES) {
|
|
11635
|
+
return output.trimEnd().split(/\r?\n/).slice(-lines).join("\n");
|
|
11636
|
+
}
|
|
11637
|
+
|
|
11638
|
+
//#endregion
|
|
11639
|
+
//#region src/skills-install.ts
|
|
11640
|
+
/**
|
|
11641
|
+
* sayren 중앙 스킬 설치(2026-10-16 사용자 결정) — `pull`·`init`이 받은 폴더에서 `npx skills add https://sayren.io`를 돌려
|
|
11642
|
+
* `.claude/skills/{name}/`에 스킬을 넣는다. 설치는 실패해도 받기를 실패시키지 않는다(명령을 알려 준다).
|
|
11643
|
+
* 설치된 스킬은 로컬 설치물이라 올리지 않는다(`installedSkillNames` → `collectTree`).
|
|
11644
|
+
*/
|
|
11645
|
+
const SKILLS_SOURCE = "https://sayren.io";
|
|
11646
|
+
/** 확인한 skills CLI 버전 — 디스커버리 0.2.0 색인을 읽는다 */
|
|
11647
|
+
const SKILLS_CLI = "skills@1.7.2";
|
|
11648
|
+
const SKILLS_ARGS = [
|
|
11649
|
+
"-y",
|
|
11650
|
+
SKILLS_CLI,
|
|
11651
|
+
"add",
|
|
11652
|
+
SKILLS_SOURCE,
|
|
11653
|
+
"--agent",
|
|
11654
|
+
"claude-code",
|
|
11655
|
+
"-y"
|
|
11656
|
+
];
|
|
11657
|
+
const SKILLS_COMMAND = `npx ${SKILLS_CLI} add ${SKILLS_SOURCE} --agent claude-code`;
|
|
11658
|
+
/** skills CLI가 설치한 스킬을 적는 파일 */
|
|
11659
|
+
const SKILLS_LOCK_FILE = "skills-lock.json";
|
|
11660
|
+
const OUTPUT_KEEP_CHARS = 64 * 1024;
|
|
11661
|
+
/** `npx skills add`를 돌린다. 출력은 모아 두고(실패할 때 꼬리를 보여 준다) 화면에 흘리지 않는다 */
|
|
11662
|
+
function runSkillsInstall(dir, env) {
|
|
11663
|
+
return new Promise((resolve$1, reject) => {
|
|
11664
|
+
let output = "";
|
|
11665
|
+
const keep = (chunk) => {
|
|
11666
|
+
output = (output + chunk.toString("utf8")).slice(-OUTPUT_KEEP_CHARS);
|
|
11667
|
+
};
|
|
11668
|
+
const child = spawn("npx", SKILLS_ARGS, {
|
|
11669
|
+
cwd: dir,
|
|
11670
|
+
env,
|
|
11671
|
+
stdio: [
|
|
11672
|
+
"ignore",
|
|
11673
|
+
"pipe",
|
|
11674
|
+
"pipe"
|
|
11675
|
+
],
|
|
11676
|
+
shell: process.platform === "win32"
|
|
11677
|
+
});
|
|
11678
|
+
child.stdout?.on("data", keep);
|
|
11679
|
+
child.stderr?.on("data", keep);
|
|
11680
|
+
child.once("error", reject);
|
|
11681
|
+
child.once("close", (code) => resolve$1({
|
|
11682
|
+
code,
|
|
11683
|
+
output: tailOf(output, OUTPUT_TAIL_LINES)
|
|
11684
|
+
}));
|
|
11685
|
+
});
|
|
11686
|
+
}
|
|
11687
|
+
/** skills CLI로 설치한 스킬 이름(`skills-lock.json`) — 없거나 읽지 못하면 빈 집합 */
|
|
11688
|
+
function installedSkillNames(root) {
|
|
11689
|
+
const path = join(root, SKILLS_LOCK_FILE);
|
|
11690
|
+
if (!existsSync(path)) return /* @__PURE__ */ new Set();
|
|
11691
|
+
try {
|
|
11692
|
+
const skills = JSON.parse(readFileSync(path, "utf8")).skills;
|
|
11693
|
+
return new Set(skills && typeof skills === "object" ? Object.keys(skills) : []);
|
|
11694
|
+
} catch {
|
|
11695
|
+
return /* @__PURE__ */ new Set();
|
|
11696
|
+
}
|
|
11697
|
+
}
|
|
11698
|
+
|
|
11534
11699
|
//#endregion
|
|
11535
11700
|
//#region src/tree.ts
|
|
11536
11701
|
/**
|
|
@@ -11564,6 +11729,11 @@ const sha256Hex = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
|
11564
11729
|
function collectTree(root) {
|
|
11565
11730
|
const files = [];
|
|
11566
11731
|
const skipped = [];
|
|
11732
|
+
const installedSkills = installedSkillNames(root);
|
|
11733
|
+
const isInstalledSkill = (rel) => {
|
|
11734
|
+
const [first, second, name] = rel.split("/");
|
|
11735
|
+
return first === ".claude" && second === "skills" && name !== void 0 && installedSkills.has(name);
|
|
11736
|
+
};
|
|
11567
11737
|
const walk = (dir) => {
|
|
11568
11738
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
11569
11739
|
const full = join(dir, entry.name);
|
|
@@ -11577,7 +11747,7 @@ function collectTree(root) {
|
|
|
11577
11747
|
skipped.push(rel);
|
|
11578
11748
|
continue;
|
|
11579
11749
|
}
|
|
11580
|
-
if (isNeverUpload(rel) || accessOf(rel) === null) {
|
|
11750
|
+
if (isNeverUpload(rel) || accessOf(rel) === null || isInstalledSkill(rel)) {
|
|
11581
11751
|
skipped.push(rel);
|
|
11582
11752
|
continue;
|
|
11583
11753
|
}
|
|
@@ -11794,55 +11964,6 @@ function withGuideSkill(files) {
|
|
|
11794
11964
|
return true;
|
|
11795
11965
|
}
|
|
11796
11966
|
|
|
11797
|
-
//#endregion
|
|
11798
|
-
//#region src/local-build.ts
|
|
11799
|
-
/** 실패할 때 보여 주는 빌드 출력 줄 수 */
|
|
11800
|
-
const OUTPUT_TAIL_LINES = 40;
|
|
11801
|
-
/** 모아 두는 출력 상한(문자) — 긴 빌드 로그를 다 쥐고 있지 않는다 */
|
|
11802
|
-
const OUTPUT_KEEP_CHARS = 64 * 1024;
|
|
11803
|
-
/** 빌드를 돌릴 수 있는 폴더인가 — `build` 스크립트와 설치된 의존성(`node_modules`)이 있어야 한다 */
|
|
11804
|
-
function planLocalBuild(dir) {
|
|
11805
|
-
let scripts;
|
|
11806
|
-
try {
|
|
11807
|
-
scripts = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).scripts;
|
|
11808
|
-
} catch {
|
|
11809
|
-
return { kind: "no-script" };
|
|
11810
|
-
}
|
|
11811
|
-
const build = scripts && typeof scripts === "object" ? scripts.build : void 0;
|
|
11812
|
-
if (typeof build !== "string" || !build.trim()) return { kind: "no-script" };
|
|
11813
|
-
if (!existsSync(join(dir, "node_modules"))) return { kind: "no-deps" };
|
|
11814
|
-
return { kind: "run" };
|
|
11815
|
-
}
|
|
11816
|
-
/** `npm run build`를 돌린다. 출력은 모아 두고(실패할 때 꼬리를 보여 준다) 화면에 흘리지 않는다 */
|
|
11817
|
-
function runNpmBuild(dir, env) {
|
|
11818
|
-
return new Promise((resolve$1, reject) => {
|
|
11819
|
-
let output = "";
|
|
11820
|
-
const keep = (chunk) => {
|
|
11821
|
-
output = (output + chunk.toString("utf8")).slice(-OUTPUT_KEEP_CHARS);
|
|
11822
|
-
};
|
|
11823
|
-
const child = spawn("npm", ["run", "build"], {
|
|
11824
|
-
cwd: dir,
|
|
11825
|
-
env,
|
|
11826
|
-
stdio: [
|
|
11827
|
-
"ignore",
|
|
11828
|
-
"pipe",
|
|
11829
|
-
"pipe"
|
|
11830
|
-
],
|
|
11831
|
-
shell: process.platform === "win32"
|
|
11832
|
-
});
|
|
11833
|
-
child.stdout?.on("data", keep);
|
|
11834
|
-
child.stderr?.on("data", keep);
|
|
11835
|
-
child.once("error", reject);
|
|
11836
|
-
child.once("close", (code) => resolve$1({
|
|
11837
|
-
code,
|
|
11838
|
-
output: tailOf(output)
|
|
11839
|
-
}));
|
|
11840
|
-
});
|
|
11841
|
-
}
|
|
11842
|
-
function tailOf(output, lines = OUTPUT_TAIL_LINES) {
|
|
11843
|
-
return output.trimEnd().split(/\r?\n/).slice(-lines).join("\n");
|
|
11844
|
-
}
|
|
11845
|
-
|
|
11846
11967
|
//#endregion
|
|
11847
11968
|
//#region src/commands.ts
|
|
11848
11969
|
/** 종료 코드 — 0 성공 · 1 실패 · 3 발행이 승인 대기(에이전트 토큰) */
|
|
@@ -12031,10 +12152,33 @@ async function receiveSource(client, auth, source, flags, io, migratedDir) {
|
|
|
12031
12152
|
file: STATE_FILE,
|
|
12032
12153
|
siteId: source.siteId
|
|
12033
12154
|
}));
|
|
12155
|
+
await installSkills(target, io);
|
|
12034
12156
|
if (source.editSessionOpen) io.out(cli_pull_edit_session_open());
|
|
12035
12157
|
io.out(cli_pull_next_steps({ dir: pulledDir }));
|
|
12036
12158
|
return 0;
|
|
12037
12159
|
}
|
|
12160
|
+
/** sayren 중앙 스킬을 받은 폴더에 설치한다 — 실패해도 받기는 성공이다(직접 돌릴 명령을 알린다) */
|
|
12161
|
+
async function installSkills(dir, io) {
|
|
12162
|
+
io.out(cli_skills_installing({ source: SKILLS_SOURCE }));
|
|
12163
|
+
let result;
|
|
12164
|
+
try {
|
|
12165
|
+
result = await (io.installSkills ?? ((at) => runSkillsInstall(at, io.env)))(dir);
|
|
12166
|
+
} catch (error) {
|
|
12167
|
+
io.err(cli_skills_install_failed({
|
|
12168
|
+
command: SKILLS_COMMAND,
|
|
12169
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
12170
|
+
}));
|
|
12171
|
+
return;
|
|
12172
|
+
}
|
|
12173
|
+
if (result.code === 0) {
|
|
12174
|
+
io.out(cli_skills_installed());
|
|
12175
|
+
return;
|
|
12176
|
+
}
|
|
12177
|
+
io.err(cli_skills_install_failed({
|
|
12178
|
+
command: SKILLS_COMMAND,
|
|
12179
|
+
detail: result.output || `exit ${result.code}`
|
|
12180
|
+
}));
|
|
12181
|
+
}
|
|
12038
12182
|
/**
|
|
12039
12183
|
* `sayren init [폴더]`(#101) — 템플릿을 골라 호스팅 사이트를 만들고(`POST /v1/storefront/site`) `pull`과 같은 폴더를 만든다.
|
|
12040
12184
|
* 사이트가 이미 있으면 만들지 않고 `pull`을 안내한다(상점당 열린 사이트는 하나다). 만들기는 상점 한도(30일 3회)를 쓰므로 확인을 묻는다.
|