sayren 0.11.0 → 0.12.1
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 +102 -183
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -6502,24 +6502,6 @@ const cli_init_dir_not_empty = ((inputs, options = {}) => {
|
|
|
6502
6502
|
return ko_cli_init_dir_not_empty(inputs);
|
|
6503
6503
|
});
|
|
6504
6504
|
|
|
6505
|
-
//#endregion
|
|
6506
|
-
//#region src/paraglide/messages/cli_init_hosting_disabled.js
|
|
6507
|
-
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6508
|
-
/** @typedef {{}} Cli_Init_Hosting_DisabledInputs */
|
|
6509
|
-
const ko_cli_init_hosting_disabled = () => {
|
|
6510
|
-
return `지금은 새 호스팅 사이트를 만들 수 없습니다(호스팅 기능이 꺼져 있습니다)`;
|
|
6511
|
-
};
|
|
6512
|
-
/**
|
|
6513
|
-
|
|
6514
|
-
* @param {Cli_Init_Hosting_DisabledInputs} inputs
|
|
6515
|
-
* @param {{ locale?: "ko" }} options
|
|
6516
|
-
* @returns {LocalizedString}
|
|
6517
|
-
*/
|
|
6518
|
-
const cli_init_hosting_disabled = ((inputs = {}, options = {}) => {
|
|
6519
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6520
|
-
return ko_cli_init_hosting_disabled(inputs);
|
|
6521
|
-
});
|
|
6522
|
-
|
|
6523
6505
|
//#endregion
|
|
6524
6506
|
//#region src/paraglide/messages/cli_init_inside_pulled.js
|
|
6525
6507
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -8039,58 +8021,57 @@ const cli_retired_token = ((inputs = {}, options = {}) => {
|
|
|
8039
8021
|
});
|
|
8040
8022
|
|
|
8041
8023
|
//#endregion
|
|
8042
|
-
//#region src/paraglide/messages/
|
|
8024
|
+
//#region src/paraglide/messages/cli_skill_failed.js
|
|
8043
8025
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8044
|
-
/** @typedef {{
|
|
8045
|
-
const
|
|
8046
|
-
return
|
|
8047
|
-
${i?.detail}`;
|
|
8026
|
+
/** @typedef {{ status: NonNullable<unknown> }} Cli_Skill_FailedInputs */
|
|
8027
|
+
const ko_cli_skill_failed = (i) => {
|
|
8028
|
+
return `스킬을 읽지 못했습니다(HTTP ${i?.status})`;
|
|
8048
8029
|
};
|
|
8049
8030
|
/**
|
|
8050
8031
|
|
|
8051
|
-
* @param {
|
|
8032
|
+
* @param {Cli_Skill_FailedInputs} inputs
|
|
8052
8033
|
* @param {{ locale?: "ko" }} options
|
|
8053
8034
|
* @returns {LocalizedString}
|
|
8054
8035
|
*/
|
|
8055
|
-
const
|
|
8036
|
+
const cli_skill_failed = ((inputs, options = {}) => {
|
|
8056
8037
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
8057
|
-
return
|
|
8038
|
+
return ko_cli_skill_failed(inputs);
|
|
8058
8039
|
});
|
|
8059
8040
|
|
|
8060
8041
|
//#endregion
|
|
8061
|
-
//#region src/paraglide/messages/
|
|
8042
|
+
//#region src/paraglide/messages/cli_skill_not_found.js
|
|
8062
8043
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8063
|
-
/** @typedef {{}}
|
|
8064
|
-
const
|
|
8065
|
-
return
|
|
8044
|
+
/** @typedef {{ name: NonNullable<unknown> }} Cli_Skill_Not_FoundInputs */
|
|
8045
|
+
const ko_cli_skill_not_found = (i) => {
|
|
8046
|
+
return `스킬 ${i?.name}이(가) 없습니다. npx sayren skill list로 목록을 확인하십시오`;
|
|
8066
8047
|
};
|
|
8067
8048
|
/**
|
|
8068
8049
|
|
|
8069
|
-
* @param {
|
|
8050
|
+
* @param {Cli_Skill_Not_FoundInputs} inputs
|
|
8070
8051
|
* @param {{ locale?: "ko" }} options
|
|
8071
8052
|
* @returns {LocalizedString}
|
|
8072
8053
|
*/
|
|
8073
|
-
const
|
|
8054
|
+
const cli_skill_not_found = ((inputs, options = {}) => {
|
|
8074
8055
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
8075
|
-
return
|
|
8056
|
+
return ko_cli_skill_not_found(inputs);
|
|
8076
8057
|
});
|
|
8077
8058
|
|
|
8078
8059
|
//#endregion
|
|
8079
|
-
//#region src/paraglide/messages/
|
|
8060
|
+
//#region src/paraglide/messages/cli_skill_usage.js
|
|
8080
8061
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8081
|
-
/** @typedef {{
|
|
8082
|
-
const
|
|
8083
|
-
return
|
|
8062
|
+
/** @typedef {{}} Cli_Skill_UsageInputs */
|
|
8063
|
+
const ko_cli_skill_usage = () => {
|
|
8064
|
+
return `스킬을 지정하십시오: npx sayren skill list 또는 npx sayren skill <이름>`;
|
|
8084
8065
|
};
|
|
8085
8066
|
/**
|
|
8086
8067
|
|
|
8087
|
-
* @param {
|
|
8068
|
+
* @param {Cli_Skill_UsageInputs} inputs
|
|
8088
8069
|
* @param {{ locale?: "ko" }} options
|
|
8089
8070
|
* @returns {LocalizedString}
|
|
8090
8071
|
*/
|
|
8091
|
-
const
|
|
8072
|
+
const cli_skill_usage = ((inputs = {}, options = {}) => {
|
|
8092
8073
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
8093
|
-
return
|
|
8074
|
+
return ko_cli_skill_usage(inputs);
|
|
8094
8075
|
});
|
|
8095
8076
|
|
|
8096
8077
|
//#endregion
|
|
@@ -8801,6 +8782,7 @@ const ko_cli_usage = (i) => {
|
|
|
8801
8782
|
npx sayren deploy [--no-publish] [--skip-build] [--message <메모>] [--force [--yes]]
|
|
8802
8783
|
npx sayren status
|
|
8803
8784
|
npx sayren verify
|
|
8785
|
+
npx sayren skill list | <이름> # sayren 스킬 목록·본문(에이전트용, 로그인 없음)
|
|
8804
8786
|
npx sayren app <명령> # 서드파티 앱(init·dev·trigger·verify·deploy·release·status·logs·secret·dev-store)
|
|
8805
8787
|
|
|
8806
8788
|
명령:
|
|
@@ -8812,6 +8794,7 @@ const ko_cli_usage = (i) => {
|
|
|
8812
8794
|
deploy 로컬 검사 → 로컬 빌드 → 작업 트리 올리기 → 빌드 → 발행. 원격이 pull 이후 바뀌었으면 덮어쓰지 않습니다
|
|
8813
8795
|
status 사이트·발행 버전·최근 빌드·편집 환경
|
|
8814
8796
|
verify 로컬 규칙·경로·크기 검사(서버 빌드와 같은 규칙)
|
|
8797
|
+
skill sayren 스킬 목록(list)과 본문(<이름>)을 출력합니다. 스킬은 중앙에서 고치므로 늘 최신입니다
|
|
8815
8798
|
app 서드파티 앱 — init <handle>로 만들고 dev로 로컬 실행, deploy로 버전을 올리고 release로 적용합니다. 자세히: https://docs.sayren.io/guides/apps-getting-started
|
|
8816
8799
|
|
|
8817
8800
|
토큰은 Sayren 계정 토큰만 받고 --token → SAYREN_TOKEN 환경 변수 → 로그인(credentials.json) 순서로 읽습니다.
|
|
@@ -9743,7 +9726,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
|
|
|
9743
9726
|
|
|
9744
9727
|
//#endregion
|
|
9745
9728
|
//#region package.json
|
|
9746
|
-
var version = "0.
|
|
9729
|
+
var version = "0.12.1";
|
|
9747
9730
|
|
|
9748
9731
|
//#endregion
|
|
9749
9732
|
//#region src/client.ts
|
|
@@ -11586,116 +11569,6 @@ async function gunzip(bytes, maxBytes) {
|
|
|
11586
11569
|
return out;
|
|
11587
11570
|
}
|
|
11588
11571
|
|
|
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
|
-
|
|
11699
11572
|
//#endregion
|
|
11700
11573
|
//#region src/tree.ts
|
|
11701
11574
|
/**
|
|
@@ -11729,11 +11602,6 @@ const sha256Hex = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
|
11729
11602
|
function collectTree(root) {
|
|
11730
11603
|
const files = [];
|
|
11731
11604
|
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
|
-
};
|
|
11737
11605
|
const walk = (dir) => {
|
|
11738
11606
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
11739
11607
|
const full = join(dir, entry.name);
|
|
@@ -11747,7 +11615,7 @@ function collectTree(root) {
|
|
|
11747
11615
|
skipped.push(rel);
|
|
11748
11616
|
continue;
|
|
11749
11617
|
}
|
|
11750
|
-
if (isNeverUpload(rel) || accessOf(rel) === null
|
|
11618
|
+
if (isNeverUpload(rel) || accessOf(rel) === null) {
|
|
11751
11619
|
skipped.push(rel);
|
|
11752
11620
|
continue;
|
|
11753
11621
|
}
|
|
@@ -11949,11 +11817,11 @@ function lockImporterSpecifiers(lock) {
|
|
|
11949
11817
|
//#endregion
|
|
11950
11818
|
//#region src/guide-skill.ts
|
|
11951
11819
|
/**
|
|
11952
|
-
* 받은 폴더의 에이전트 안내 스킬(2026-10-16 사용자 결정) — 셀러의 Claude Code가 sayren 중앙 스킬을
|
|
11820
|
+
* 받은 폴더의 에이전트 안내 스킬(2026-10-16 사용자 결정) — 셀러의 Claude Code가 기능·화면을 고치기 전에 sayren 중앙 스킬을 `npx sayren skill`로 읽게 한다(스킬은 이 하나만 설치한다).
|
|
11953
11821
|
* 빈 템플릿이 싣는 `templates/storefront-blank/.claude/skills/sayren/SKILL.md`와 같은 내용이다(`guide-skill.test.ts`가 고정한다).
|
|
11954
11822
|
*/
|
|
11955
11823
|
const GUIDE_SKILL_PATH = ".claude/skills/sayren/SKILL.md";
|
|
11956
|
-
const GUIDE_SKILL = "---\nname: sayren\ndescription:
|
|
11824
|
+
const GUIDE_SKILL = "---\nname: sayren\ndescription: Load before adding, implementing or changing any feature or page of this sayren storefront. Lists the current sayren skills and loads the ones the task needs.\n---\n\n# sayren skills\n\nThe sayren skills are maintained centrally and change without updating this project.\nDo not rely on memory or earlier copies. Load them at the start of each task.\n\n1. List the skills (name and when to use each):\n `npx -y sayren@latest skill list`\n2. Read every skill that matches the task, in full, and follow it:\n `npx -y sayren@latest skill <name>`\n\nIf the sayren MCP server is connected, the tools `list_skills` and `get_skill` return the same content.\n\nFollow the project's `CLAUDE.md` first. The skills add the sayren-specific details.\n";
|
|
11957
11825
|
/**
|
|
11958
11826
|
* 받은 트리에 안내 스킬이 없으면 넣는다 — 템플릿이 싣거나 셀러가 올린 내용이 우선이다. 트리에 넣으므로 다시 받을 때 지울 파일로 잡히지 않고
|
|
11959
11827
|
* `deploy`가 원격 작업 트리에도 올린다(AI 상점 도우미가 같은 안내를 읽는다). 넣었으면 true
|
|
@@ -11964,6 +11832,55 @@ function withGuideSkill(files) {
|
|
|
11964
11832
|
return true;
|
|
11965
11833
|
}
|
|
11966
11834
|
|
|
11835
|
+
//#endregion
|
|
11836
|
+
//#region src/local-build.ts
|
|
11837
|
+
/** 실패할 때 보여 주는 빌드 출력 줄 수 */
|
|
11838
|
+
const OUTPUT_TAIL_LINES = 40;
|
|
11839
|
+
/** 모아 두는 출력 상한(문자) — 긴 빌드 로그를 다 쥐고 있지 않는다 */
|
|
11840
|
+
const OUTPUT_KEEP_CHARS = 64 * 1024;
|
|
11841
|
+
/** 빌드를 돌릴 수 있는 폴더인가 — `build` 스크립트와 설치된 의존성(`node_modules`)이 있어야 한다 */
|
|
11842
|
+
function planLocalBuild(dir) {
|
|
11843
|
+
let scripts;
|
|
11844
|
+
try {
|
|
11845
|
+
scripts = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).scripts;
|
|
11846
|
+
} catch {
|
|
11847
|
+
return { kind: "no-script" };
|
|
11848
|
+
}
|
|
11849
|
+
const build = scripts && typeof scripts === "object" ? scripts.build : void 0;
|
|
11850
|
+
if (typeof build !== "string" || !build.trim()) return { kind: "no-script" };
|
|
11851
|
+
if (!existsSync(join(dir, "node_modules"))) return { kind: "no-deps" };
|
|
11852
|
+
return { kind: "run" };
|
|
11853
|
+
}
|
|
11854
|
+
/** `npm run build`를 돌린다. 출력은 모아 두고(실패할 때 꼬리를 보여 준다) 화면에 흘리지 않는다 */
|
|
11855
|
+
function runNpmBuild(dir, env) {
|
|
11856
|
+
return new Promise((resolve$1, reject) => {
|
|
11857
|
+
let output = "";
|
|
11858
|
+
const keep = (chunk) => {
|
|
11859
|
+
output = (output + chunk.toString("utf8")).slice(-OUTPUT_KEEP_CHARS);
|
|
11860
|
+
};
|
|
11861
|
+
const child = spawn("npm", ["run", "build"], {
|
|
11862
|
+
cwd: dir,
|
|
11863
|
+
env,
|
|
11864
|
+
stdio: [
|
|
11865
|
+
"ignore",
|
|
11866
|
+
"pipe",
|
|
11867
|
+
"pipe"
|
|
11868
|
+
],
|
|
11869
|
+
shell: process.platform === "win32"
|
|
11870
|
+
});
|
|
11871
|
+
child.stdout?.on("data", keep);
|
|
11872
|
+
child.stderr?.on("data", keep);
|
|
11873
|
+
child.once("error", reject);
|
|
11874
|
+
child.once("close", (code) => resolve$1({
|
|
11875
|
+
code,
|
|
11876
|
+
output: tailOf(output)
|
|
11877
|
+
}));
|
|
11878
|
+
});
|
|
11879
|
+
}
|
|
11880
|
+
function tailOf(output, lines = OUTPUT_TAIL_LINES) {
|
|
11881
|
+
return output.trimEnd().split(/\r?\n/).slice(-lines).join("\n");
|
|
11882
|
+
}
|
|
11883
|
+
|
|
11967
11884
|
//#endregion
|
|
11968
11885
|
//#region src/commands.ts
|
|
11969
11886
|
/** 종료 코드 — 0 성공 · 1 실패 · 3 발행이 승인 대기(에이전트 토큰) */
|
|
@@ -12152,33 +12069,10 @@ async function receiveSource(client, auth, source, flags, io, migratedDir) {
|
|
|
12152
12069
|
file: STATE_FILE,
|
|
12153
12070
|
siteId: source.siteId
|
|
12154
12071
|
}));
|
|
12155
|
-
await installSkills(target, io);
|
|
12156
12072
|
if (source.editSessionOpen) io.out(cli_pull_edit_session_open());
|
|
12157
12073
|
io.out(cli_pull_next_steps({ dir: pulledDir }));
|
|
12158
12074
|
return 0;
|
|
12159
12075
|
}
|
|
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
|
-
}
|
|
12182
12076
|
/**
|
|
12183
12077
|
* `sayren init [폴더]`(#101) — 템플릿을 골라 호스팅 사이트를 만들고(`POST /v1/storefront/site`) `pull`과 같은 폴더를 만든다.
|
|
12184
12078
|
* 사이트가 이미 있으면 만들지 않고 `pull`을 안내한다(상점당 열린 사이트는 하나다). 만들기는 상점 한도(30일 3회)를 쓰므로 확인을 묻는다.
|
|
@@ -12197,7 +12091,6 @@ async function init(flags, io) {
|
|
|
12197
12091
|
io.out(cli_init_site_url({ url: hosting.site.url }));
|
|
12198
12092
|
throw new CliError(cli_init_site_exists({ storeId }));
|
|
12199
12093
|
}
|
|
12200
|
-
if (!hosting.enabled) throw new CliError(cli_init_hosting_disabled());
|
|
12201
12094
|
if (!hosting.editingEnabled) throw new CliError(cli_pull_editing_disabled());
|
|
12202
12095
|
const template = await pickTemplate(await client.storefront.listTemplates(), flags.template, io);
|
|
12203
12096
|
io.out(cli_init_summary({
|
|
@@ -12730,6 +12623,31 @@ function relativeOf(io, path) {
|
|
|
12730
12623
|
return relative(io.cwd, path) || ".";
|
|
12731
12624
|
}
|
|
12732
12625
|
|
|
12626
|
+
//#endregion
|
|
12627
|
+
//#region src/skill-commands.ts
|
|
12628
|
+
/**
|
|
12629
|
+
* `sayren skill list`·`sayren skill <name>`(2026-10-16 사용자 결정) — 안내 스킬 `sayren`이 에이전트에게 부르게 하는 중앙 스킬 읽기.
|
|
12630
|
+
* 공개 스킬 API(`/v1/public/agent-skills`, 인증 없음)를 읽어 원문을 출력한다. 셸·OS와 무관하게 같은 명령이고 요약되지 않는다
|
|
12631
|
+
*/
|
|
12632
|
+
async function skill(args, flags, io) {
|
|
12633
|
+
const [target] = args;
|
|
12634
|
+
if (!target) throw new CliError(cli_skill_usage());
|
|
12635
|
+
const { apiOrigin } = resolveApiOrigin(flags, io.cwd, null, io.env);
|
|
12636
|
+
const path = target === "list" ? "" : `/${encodeURIComponent(target)}`;
|
|
12637
|
+
const res = await io.fetch(`${apiOrigin}/v1/public/agent-skills${path}`, { headers: { "User-Agent": CLI_USER_AGENT } });
|
|
12638
|
+
if (res.status === 404) throw new CliError(cli_skill_not_found({ name: target }));
|
|
12639
|
+
if (!res.ok) throw new CliError(cli_skill_failed({ status: res.status }));
|
|
12640
|
+
const { data } = await res.json();
|
|
12641
|
+
if (target === "list") {
|
|
12642
|
+
const skills = data;
|
|
12643
|
+
const width = Math.max(...skills.map((s) => s.name.length));
|
|
12644
|
+
for (const s of skills) io.out(`${s.name.padEnd(width)} ${s.description}`);
|
|
12645
|
+
return 0;
|
|
12646
|
+
}
|
|
12647
|
+
io.out(data.body);
|
|
12648
|
+
return 0;
|
|
12649
|
+
}
|
|
12650
|
+
|
|
12733
12651
|
//#endregion
|
|
12734
12652
|
//#region src/index.ts
|
|
12735
12653
|
/** 도움말 — 쓰는 자리에서 만든다(로케일이 정해진 뒤) */
|
|
@@ -12869,6 +12787,7 @@ async function main(argv, io) {
|
|
|
12869
12787
|
case "login": return login(loginFlags, io, () => ask(cli_prompt_access_token()));
|
|
12870
12788
|
case "logout": return logout(loginFlags, io);
|
|
12871
12789
|
case "whoami": return whoami(common, io);
|
|
12790
|
+
case "skill": return skill(parsed.positional, { apiOrigin: common.apiOrigin }, io);
|
|
12872
12791
|
default: throw new CliError(cli_unknown_command({
|
|
12873
12792
|
command: parsed.command,
|
|
12874
12793
|
usage: usage()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sayren",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"imports": {
|
|
6
6
|
"#paraglide/*": "./src/paraglide/*"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"tsx": "^4.20.3",
|
|
29
29
|
"typescript": "^6.0.3",
|
|
30
30
|
"vitest": "^5.0.1",
|
|
31
|
-
"@sayren/mcp": "0.26.
|
|
31
|
+
"@sayren/mcp": "0.26.1"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|