sayren 0.11.0 → 0.12.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.
Files changed (2) hide show
  1. package/dist/index.mjs +102 -164
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -8039,58 +8039,57 @@ const cli_retired_token = ((inputs = {}, options = {}) => {
8039
8039
  });
8040
8040
 
8041
8041
  //#endregion
8042
- //#region src/paraglide/messages/cli_skills_install_failed.js
8042
+ //#region src/paraglide/messages/cli_skill_failed.js
8043
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}`;
8044
+ /** @typedef {{ status: NonNullable<unknown> }} Cli_Skill_FailedInputs */
8045
+ const ko_cli_skill_failed = (i) => {
8046
+ return `스킬을 읽지 못했습니다(HTTP ${i?.status})`;
8048
8047
  };
8049
8048
  /**
8050
8049
 
8051
- * @param {Cli_Skills_Install_FailedInputs} inputs
8050
+ * @param {Cli_Skill_FailedInputs} inputs
8052
8051
  * @param {{ locale?: "ko" }} options
8053
8052
  * @returns {LocalizedString}
8054
8053
  */
8055
- const cli_skills_install_failed = ((inputs, options = {}) => {
8054
+ const cli_skill_failed = ((inputs, options = {}) => {
8056
8055
  experimentalStaticLocale ?? options.locale ?? getLocale();
8057
- return ko_cli_skills_install_failed(inputs);
8056
+ return ko_cli_skill_failed(inputs);
8058
8057
  });
8059
8058
 
8060
8059
  //#endregion
8061
- //#region src/paraglide/messages/cli_skills_installed.js
8060
+ //#region src/paraglide/messages/cli_skill_not_found.js
8062
8061
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
8063
- /** @typedef {{}} Cli_Skills_InstalledInputs */
8064
- const ko_cli_skills_installed = () => {
8065
- return `에이전트 스킬 설치 완료 — .claude/skills/ (배포 때 올리지 않습니다)`;
8062
+ /** @typedef {{ name: NonNullable<unknown> }} Cli_Skill_Not_FoundInputs */
8063
+ const ko_cli_skill_not_found = (i) => {
8064
+ return `스킬 ${i?.name}이(가) 없습니다. npx sayren skill list로 목록을 확인하십시오`;
8066
8065
  };
8067
8066
  /**
8068
8067
 
8069
- * @param {Cli_Skills_InstalledInputs} inputs
8068
+ * @param {Cli_Skill_Not_FoundInputs} inputs
8070
8069
  * @param {{ locale?: "ko" }} options
8071
8070
  * @returns {LocalizedString}
8072
8071
  */
8073
- const cli_skills_installed = ((inputs = {}, options = {}) => {
8072
+ const cli_skill_not_found = ((inputs, options = {}) => {
8074
8073
  experimentalStaticLocale ?? options.locale ?? getLocale();
8075
- return ko_cli_skills_installed(inputs);
8074
+ return ko_cli_skill_not_found(inputs);
8076
8075
  });
8077
8076
 
8078
8077
  //#endregion
8079
- //#region src/paraglide/messages/cli_skills_installing.js
8078
+ //#region src/paraglide/messages/cli_skill_usage.js
8080
8079
  /** @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}`;
8080
+ /** @typedef {{}} Cli_Skill_UsageInputs */
8081
+ const ko_cli_skill_usage = () => {
8082
+ return `스킬을 지정하십시오: npx sayren skill list 또는 npx sayren skill <이름>`;
8084
8083
  };
8085
8084
  /**
8086
8085
 
8087
- * @param {Cli_Skills_InstallingInputs} inputs
8086
+ * @param {Cli_Skill_UsageInputs} inputs
8088
8087
  * @param {{ locale?: "ko" }} options
8089
8088
  * @returns {LocalizedString}
8090
8089
  */
8091
- const cli_skills_installing = ((inputs, options = {}) => {
8090
+ const cli_skill_usage = ((inputs = {}, options = {}) => {
8092
8091
  experimentalStaticLocale ?? options.locale ?? getLocale();
8093
- return ko_cli_skills_installing(inputs);
8092
+ return ko_cli_skill_usage(inputs);
8094
8093
  });
8095
8094
 
8096
8095
  //#endregion
@@ -8801,6 +8800,7 @@ const ko_cli_usage = (i) => {
8801
8800
  npx sayren deploy [--no-publish] [--skip-build] [--message <메모>] [--force [--yes]]
8802
8801
  npx sayren status
8803
8802
  npx sayren verify
8803
+ npx sayren skill list | <이름> # sayren 스킬 목록·본문(에이전트용, 로그인 없음)
8804
8804
  npx sayren app <명령> # 서드파티 앱(init·dev·trigger·verify·deploy·release·status·logs·secret·dev-store)
8805
8805
 
8806
8806
  명령:
@@ -8812,6 +8812,7 @@ const ko_cli_usage = (i) => {
8812
8812
  deploy 로컬 검사 → 로컬 빌드 → 작업 트리 올리기 → 빌드 → 발행. 원격이 pull 이후 바뀌었으면 덮어쓰지 않습니다
8813
8813
  status 사이트·발행 버전·최근 빌드·편집 환경
8814
8814
  verify 로컬 규칙·경로·크기 검사(서버 빌드와 같은 규칙)
8815
+ skill sayren 스킬 목록(list)과 본문(<이름>)을 출력합니다. 스킬은 중앙에서 고치므로 늘 최신입니다
8815
8816
  app 서드파티 앱 — init <handle>로 만들고 dev로 로컬 실행, deploy로 버전을 올리고 release로 적용합니다. 자세히: https://docs.sayren.io/guides/apps-getting-started
8816
8817
 
8817
8818
  토큰은 Sayren 계정 토큰만 받고 --token → SAYREN_TOKEN 환경 변수 → 로그인(credentials.json) 순서로 읽습니다.
@@ -9743,7 +9744,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
9743
9744
 
9744
9745
  //#endregion
9745
9746
  //#region package.json
9746
- var version = "0.11.0";
9747
+ var version = "0.12.0";
9747
9748
 
9748
9749
  //#endregion
9749
9750
  //#region src/client.ts
@@ -11586,116 +11587,6 @@ async function gunzip(bytes, maxBytes) {
11586
11587
  return out;
11587
11588
  }
11588
11589
 
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
11590
  //#endregion
11700
11591
  //#region src/tree.ts
11701
11592
  /**
@@ -11729,11 +11620,6 @@ const sha256Hex = (bytes) => createHash("sha256").update(bytes).digest("hex");
11729
11620
  function collectTree(root) {
11730
11621
  const files = [];
11731
11622
  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
11623
  const walk = (dir) => {
11738
11624
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
11739
11625
  const full = join(dir, entry.name);
@@ -11747,7 +11633,7 @@ function collectTree(root) {
11747
11633
  skipped.push(rel);
11748
11634
  continue;
11749
11635
  }
11750
- if (isNeverUpload(rel) || accessOf(rel) === null || isInstalledSkill(rel)) {
11636
+ if (isNeverUpload(rel) || accessOf(rel) === null) {
11751
11637
  skipped.push(rel);
11752
11638
  continue;
11753
11639
  }
@@ -11949,11 +11835,11 @@ function lockImporterSpecifiers(lock) {
11949
11835
  //#endregion
11950
11836
  //#region src/guide-skill.ts
11951
11837
  /**
11952
- * 받은 폴더의 에이전트 안내 스킬(2026-10-16 사용자 결정) — 셀러의 Claude Code가 sayren 중앙 스킬을 HTTP로 읽게 한다.
11838
+ * 받은 폴더의 에이전트 안내 스킬(2026-10-16 사용자 결정) — 셀러의 Claude Code가 기능·화면을 고치기 전에 sayren 중앙 스킬을 `npx sayren skill`로 읽게 한다(스킬은 이 하나만 설치한다).
11953
11839
  * 빈 템플릿이 싣는 `templates/storefront-blank/.claude/skills/sayren/SKILL.md`와 같은 내용이다(`guide-skill.test.ts`가 고정한다).
11954
11840
  */
11955
11841
  const GUIDE_SKILL_PATH = ".claude/skills/sayren/SKILL.md";
11956
- const GUIDE_SKILL = "---\nname: sayren\ndescription: Use for any work on this sayren storefront — pages, product display, cart, checkout, payments, buyer login, account pages, analytics, design and code quality. Loads the current sayren skills over HTTP.\n---\n\n# sayren skills (central)\n\nThe sayren skills are maintained centrally and change without updating this project. Read them over HTTP at the start of a task instead of relying on memory.\n\n1. List the skills: `GET https://api.sayren.io/v1/public/agent-skills`\n The response is `{ \"data\": [{ \"name\", \"description\", \"version\", \"updatedAt\" }] }`.\n2. Read the skills that match the task: `GET https://api.sayren.io/v1/public/agent-skills/{name}`\n The response is `{ \"data\": { \"name\", \"description\", \"body\", \"version\", \"updatedAt\" } }`; `body` is the guide (markdown).\n\nStart points:\n- Any code change: `code-quality`, `sayren-storefront-rules`, `sdk-client`, `template-stack`\n- Product pages: `sayren-product-display`, `sdk-catalog`\n- Cart, checkout, payment: `sdk-cart`, `sdk-checkout`, `sdk-payments`\n- Buyer login and account pages: `sdk-buyer-auth`, `sdk-account`\n- Visual design: `design-quality`\n\nFollow the project's `CLAUDE.md` first; the skills add the sayren-specific details.\n";
11842
+ 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
11843
  /**
11958
11844
  * 받은 트리에 안내 스킬이 없으면 넣는다 — 템플릿이 싣거나 셀러가 올린 내용이 우선이다. 트리에 넣으므로 다시 받을 때 지울 파일로 잡히지 않고
11959
11845
  * `deploy`가 원격 작업 트리에도 올린다(AI 상점 도우미가 같은 안내를 읽는다). 넣었으면 true
@@ -11964,6 +11850,55 @@ function withGuideSkill(files) {
11964
11850
  return true;
11965
11851
  }
11966
11852
 
11853
+ //#endregion
11854
+ //#region src/local-build.ts
11855
+ /** 실패할 때 보여 주는 빌드 출력 줄 수 */
11856
+ const OUTPUT_TAIL_LINES = 40;
11857
+ /** 모아 두는 출력 상한(문자) — 긴 빌드 로그를 다 쥐고 있지 않는다 */
11858
+ const OUTPUT_KEEP_CHARS = 64 * 1024;
11859
+ /** 빌드를 돌릴 수 있는 폴더인가 — `build` 스크립트와 설치된 의존성(`node_modules`)이 있어야 한다 */
11860
+ function planLocalBuild(dir) {
11861
+ let scripts;
11862
+ try {
11863
+ scripts = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).scripts;
11864
+ } catch {
11865
+ return { kind: "no-script" };
11866
+ }
11867
+ const build = scripts && typeof scripts === "object" ? scripts.build : void 0;
11868
+ if (typeof build !== "string" || !build.trim()) return { kind: "no-script" };
11869
+ if (!existsSync(join(dir, "node_modules"))) return { kind: "no-deps" };
11870
+ return { kind: "run" };
11871
+ }
11872
+ /** `npm run build`를 돌린다. 출력은 모아 두고(실패할 때 꼬리를 보여 준다) 화면에 흘리지 않는다 */
11873
+ function runNpmBuild(dir, env) {
11874
+ return new Promise((resolve$1, reject) => {
11875
+ let output = "";
11876
+ const keep = (chunk) => {
11877
+ output = (output + chunk.toString("utf8")).slice(-OUTPUT_KEEP_CHARS);
11878
+ };
11879
+ const child = spawn("npm", ["run", "build"], {
11880
+ cwd: dir,
11881
+ env,
11882
+ stdio: [
11883
+ "ignore",
11884
+ "pipe",
11885
+ "pipe"
11886
+ ],
11887
+ shell: process.platform === "win32"
11888
+ });
11889
+ child.stdout?.on("data", keep);
11890
+ child.stderr?.on("data", keep);
11891
+ child.once("error", reject);
11892
+ child.once("close", (code) => resolve$1({
11893
+ code,
11894
+ output: tailOf(output)
11895
+ }));
11896
+ });
11897
+ }
11898
+ function tailOf(output, lines = OUTPUT_TAIL_LINES) {
11899
+ return output.trimEnd().split(/\r?\n/).slice(-lines).join("\n");
11900
+ }
11901
+
11967
11902
  //#endregion
11968
11903
  //#region src/commands.ts
11969
11904
  /** 종료 코드 — 0 성공 · 1 실패 · 3 발행이 승인 대기(에이전트 토큰) */
@@ -12152,33 +12087,10 @@ async function receiveSource(client, auth, source, flags, io, migratedDir) {
12152
12087
  file: STATE_FILE,
12153
12088
  siteId: source.siteId
12154
12089
  }));
12155
- await installSkills(target, io);
12156
12090
  if (source.editSessionOpen) io.out(cli_pull_edit_session_open());
12157
12091
  io.out(cli_pull_next_steps({ dir: pulledDir }));
12158
12092
  return 0;
12159
12093
  }
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
12094
  /**
12183
12095
  * `sayren init [폴더]`(#101) — 템플릿을 골라 호스팅 사이트를 만들고(`POST /v1/storefront/site`) `pull`과 같은 폴더를 만든다.
12184
12096
  * 사이트가 이미 있으면 만들지 않고 `pull`을 안내한다(상점당 열린 사이트는 하나다). 만들기는 상점 한도(30일 3회)를 쓰므로 확인을 묻는다.
@@ -12730,6 +12642,31 @@ function relativeOf(io, path) {
12730
12642
  return relative(io.cwd, path) || ".";
12731
12643
  }
12732
12644
 
12645
+ //#endregion
12646
+ //#region src/skill-commands.ts
12647
+ /**
12648
+ * `sayren skill list`·`sayren skill <name>`(2026-10-16 사용자 결정) — 안내 스킬 `sayren`이 에이전트에게 부르게 하는 중앙 스킬 읽기.
12649
+ * 공개 스킬 API(`/v1/public/agent-skills`, 인증 없음)를 읽어 원문을 출력한다. 셸·OS와 무관하게 같은 명령이고 요약되지 않는다
12650
+ */
12651
+ async function skill(args, flags, io) {
12652
+ const [target] = args;
12653
+ if (!target) throw new CliError(cli_skill_usage());
12654
+ const { apiOrigin } = resolveApiOrigin(flags, io.cwd, null, io.env);
12655
+ const path = target === "list" ? "" : `/${encodeURIComponent(target)}`;
12656
+ const res = await io.fetch(`${apiOrigin}/v1/public/agent-skills${path}`, { headers: { "User-Agent": CLI_USER_AGENT } });
12657
+ if (res.status === 404) throw new CliError(cli_skill_not_found({ name: target }));
12658
+ if (!res.ok) throw new CliError(cli_skill_failed({ status: res.status }));
12659
+ const { data } = await res.json();
12660
+ if (target === "list") {
12661
+ const skills = data;
12662
+ const width = Math.max(...skills.map((s) => s.name.length));
12663
+ for (const s of skills) io.out(`${s.name.padEnd(width)} ${s.description}`);
12664
+ return 0;
12665
+ }
12666
+ io.out(data.body);
12667
+ return 0;
12668
+ }
12669
+
12733
12670
  //#endregion
12734
12671
  //#region src/index.ts
12735
12672
  /** 도움말 — 쓰는 자리에서 만든다(로케일이 정해진 뒤) */
@@ -12869,6 +12806,7 @@ async function main(argv, io) {
12869
12806
  case "login": return login(loginFlags, io, () => ask(cli_prompt_access_token()));
12870
12807
  case "logout": return logout(loginFlags, io);
12871
12808
  case "whoami": return whoami(common, io);
12809
+ case "skill": return skill(parsed.positional, { apiOrigin: common.apiOrigin }, io);
12872
12810
  default: throw new CliError(cli_unknown_command({
12873
12811
  command: parsed.command,
12874
12812
  usage: usage()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sayren",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "imports": {
6
6
  "#paraglide/*": "./src/paraglide/*"