sayren 0.9.0 → 0.10.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 +39 -114
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -2261,84 +2261,6 @@ const mcp_rule_payment_browser_launch_why = ((inputs = {}, options = {}) => {
2261
2261
  return ko_mcp_rule_payment_browser_launch_why(inputs);
2262
2262
  });
2263
2263
 
2264
- //#endregion
2265
- //#region ../mcp/src/paraglide/messages/mcp_rule_payment_popup_prepare_example.js
2266
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
2267
- /** @typedef {{}} Mcp_Rule_Payment_Popup_Prepare_ExampleInputs */
2268
- const ko_mcp_rule_payment_popup_prepare_example = () => {
2269
- return `const paymentWindow = payments.prepareWindow(); // 클릭 시점에 먼저 연다
2270
- const { start, error } = await startPayment({ data });
2271
- if (!start) {
2272
- paymentWindow.close();
2273
- return;
2274
- }
2275
- await payments.open(parsed, { window: paymentWindow });`;
2276
- };
2277
- /**
2278
-
2279
- * @param {Mcp_Rule_Payment_Popup_Prepare_ExampleInputs} inputs
2280
- * @param {{ locale?: "ko" }} options
2281
- * @returns {LocalizedString}
2282
- */
2283
- const mcp_rule_payment_popup_prepare_example = ((inputs = {}, options = {}) => {
2284
- experimentalStaticLocale$1 ?? options.locale ?? getLocale$1();
2285
- return ko_mcp_rule_payment_popup_prepare_example(inputs);
2286
- });
2287
-
2288
- //#endregion
2289
- //#region ../mcp/src/paraglide/messages/mcp_rule_payment_popup_prepare_fix.js
2290
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
2291
- /** @typedef {{}} Mcp_Rule_Payment_Popup_Prepare_FixInputs */
2292
- const ko_mcp_rule_payment_popup_prepare_fix = () => {
2293
- return `클릭 핸들러 첫 줄에서 \`payments.prepareWindow()\`로 빈 창을 열고, 서버 함수가 끝나면 그 핸들을 \`payments.open(start, { window })\`에 넘긴다. 결제 시작이 실패하면 \`window.close()\`로 닫는다`;
2294
- };
2295
- /**
2296
-
2297
- * @param {Mcp_Rule_Payment_Popup_Prepare_FixInputs} inputs
2298
- * @param {{ locale?: "ko" }} options
2299
- * @returns {LocalizedString}
2300
- */
2301
- const mcp_rule_payment_popup_prepare_fix = ((inputs = {}, options = {}) => {
2302
- experimentalStaticLocale$1 ?? options.locale ?? getLocale$1();
2303
- return ko_mcp_rule_payment_popup_prepare_fix(inputs);
2304
- });
2305
-
2306
- //#endregion
2307
- //#region ../mcp/src/paraglide/messages/mcp_rule_payment_popup_prepare_title.js
2308
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
2309
- /** @typedef {{}} Mcp_Rule_Payment_Popup_Prepare_TitleInputs */
2310
- const ko_mcp_rule_payment_popup_prepare_title = () => {
2311
- return `결제 시작이 서버 함수를 거치면 클릭 시점에 \`payments.prepareWindow()\`로 창을 먼저 연다`;
2312
- };
2313
- /**
2314
-
2315
- * @param {Mcp_Rule_Payment_Popup_Prepare_TitleInputs} inputs
2316
- * @param {{ locale?: "ko" }} options
2317
- * @returns {LocalizedString}
2318
- */
2319
- const mcp_rule_payment_popup_prepare_title = ((inputs = {}, options = {}) => {
2320
- experimentalStaticLocale$1 ?? options.locale ?? getLocale$1();
2321
- return ko_mcp_rule_payment_popup_prepare_title(inputs);
2322
- });
2323
-
2324
- //#endregion
2325
- //#region ../mcp/src/paraglide/messages/mcp_rule_payment_popup_prepare_why.js
2326
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
2327
- /** @typedef {{}} Mcp_Rule_Payment_Popup_Prepare_WhyInputs */
2328
- const ko_mcp_rule_payment_popup_prepare_why = () => {
2329
- return `팝업은 사용자 클릭에서 동기로 열어야 차단되지 않는다. 서버 응답을 기다린 뒤 열면 브라우저가 막아 결제가 시작되지 않는다`;
2330
- };
2331
- /**
2332
-
2333
- * @param {Mcp_Rule_Payment_Popup_Prepare_WhyInputs} inputs
2334
- * @param {{ locale?: "ko" }} options
2335
- * @returns {LocalizedString}
2336
- */
2337
- const mcp_rule_payment_popup_prepare_why = ((inputs = {}, options = {}) => {
2338
- experimentalStaticLocale$1 ?? options.locale ?? getLocale$1();
2339
- return ko_mcp_rule_payment_popup_prepare_why(inputs);
2340
- });
2341
-
2342
2264
  //#endregion
2343
2265
  //#region ../mcp/src/paraglide/messages/mcp_rule_payment_processing_status_example.js
2344
2266
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
@@ -3297,24 +3219,6 @@ const mcp_verify_payment_browser_launch_server_file = ((inputs = {}, options = {
3297
3219
  return ko_mcp_verify_payment_browser_launch_server_file(inputs);
3298
3220
  });
3299
3221
 
3300
- //#endregion
3301
- //#region ../mcp/src/paraglide/messages/mcp_verify_payment_popup_prepare.js
3302
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
3303
- /** @typedef {{}} Mcp_Verify_Payment_Popup_PrepareInputs */
3304
- const ko_mcp_verify_payment_popup_prepare = () => {
3305
- return `받아 둔 결제 시작 값으로 결제창을 열지만 클릭 시점에 \`payments.prepareWindow()\`로 창을 먼저 여는 코드가 없다`;
3306
- };
3307
- /**
3308
-
3309
- * @param {Mcp_Verify_Payment_Popup_PrepareInputs} inputs
3310
- * @param {{ locale?: "ko" }} options
3311
- * @returns {LocalizedString}
3312
- */
3313
- const mcp_verify_payment_popup_prepare = ((inputs = {}, options = {}) => {
3314
- experimentalStaticLocale$1 ?? options.locale ?? getLocale$1();
3315
- return ko_mcp_verify_payment_popup_prepare(inputs);
3316
- });
3317
-
3318
3222
  //#endregion
3319
3223
  //#region ../mcp/src/paraglide/messages/mcp_verify_payment_processing_status.js
3320
3224
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
@@ -9784,7 +9688,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
9784
9688
 
9785
9689
  //#endregion
9786
9690
  //#region package.json
9787
- var version = "0.9.0";
9691
+ var version = "0.10.0";
9788
9692
 
9789
9693
  //#endregion
9790
9694
  //#region src/client.ts
@@ -10870,21 +10774,12 @@ const RULES = [
10870
10774
  doc: "/guides/checkout-flow",
10871
10775
  templateFile: "src/lib/payments.ts"
10872
10776
  }),
10873
- defineRule({
10874
- id: "payment-popup-prepare",
10875
- title: mcp_rule_payment_popup_prepare_title,
10876
- why: mcp_rule_payment_popup_prepare_why,
10877
- fix: mcp_rule_payment_popup_prepare_fix,
10878
- example: mcp_rule_payment_popup_prepare_example,
10879
- doc: "/guides/checkout-flow",
10880
- templateFile: "src/routes/checkout.index.tsx"
10881
- }),
10882
10777
  defineRule({
10883
10778
  id: "payment-return-route",
10884
10779
  title: mcp_rule_payment_return_route_title,
10885
10780
  why: mcp_rule_payment_return_route_why,
10886
10781
  fix: mcp_rule_payment_return_route_fix,
10887
- example: "useEffect(() => {\n void payments.result(window.location.href).then((result) => {\n if (result.status === \"COMPLETED\") navigate({ to: \"/checkout/complete\", search: { orderId: result.orderId } });\n });\n}, []);",
10782
+ example: "useEffect(() => {\n void payments.result().then((result) => {\n if (result.status === \"COMPLETED\") navigate({ to: \"/checkout/complete\", search: { orderId: result.orderId } });\n });\n}, []);",
10888
10783
  doc: "/guides/checkout-flow",
10889
10784
  templateFile: "src/routes/checkout.return.tsx"
10890
10785
  }),
@@ -11128,7 +11023,6 @@ function verifySources(files, options = {}) {
11128
11023
  }
11129
11024
  if (has("startPayment(") && !has("@sayren/storefront-sdk/payments")) inProject("payment-browser-launch", mcp_verify_payment_browser_launch_missing());
11130
11025
  const opensPayment = matches(/\bpayments\.open\(|\.open\(\s*(start|paymentStart)\b/);
11131
- if (opensPayment && !has("prepareWindow(")) inFile("payment-popup-prepare", opensPayment, mcp_verify_payment_popup_prepare(), /\bpayments\.open\(|\.open\(\s*(start|paymentStart)\b/);
11132
11026
  if (opensPayment && !/amounts/.test(opensPayment.content)) inFile("payment-amounts-final", opensPayment, mcp_verify_payment_amounts_final(), /\bpayments\.open\(/);
11133
11027
  const fixedReturnUrl = matches(/returnUrl\s*[:=]\s*["'`]https?:\/\//);
11134
11028
  if (fixedReturnUrl) inFile("checkout-origin", fixedReturnUrl, mcp_verify_checkout_origin(), /returnUrl\s*[:=]\s*["'`]https?:\/\//);
@@ -11476,8 +11370,18 @@ const WRITABLE_ROOT_FILES = new Set([
11476
11370
  "biome.json",
11477
11371
  ".gitignore"
11478
11372
  ]);
11479
- /** 읽기만 되는 루트 파일 — 의존성은 전용 경로로만 바꾼다(레지스트리 프록시와 잠금 파일을 함께 맞춘다) */
11480
- const READONLY_ROOT_FILES = new Set(["package.json", "pnpm-lock.yaml"]);
11373
+ /**
11374
+ * 읽기만 되는 루트 파일 — 의존성은 전용 경로로만 바꾼다(레지스트리 프록시와 잠금 파일을 함께 맞춘다).
11375
+ * `CLAUDE.md`·`.mcp.json`은 템플릿이 싣는 에이전트 안내·MCP 연결 설정이다(2026-10-14, blank 템플릿) — 편집 도구는 바꾸지 않는다
11376
+ */
11377
+ const READONLY_ROOT_FILES = new Set([
11378
+ "package.json",
11379
+ "pnpm-lock.yaml",
11380
+ "CLAUDE.md",
11381
+ ".mcp.json"
11382
+ ]);
11383
+ /** 읽기만 되는 폴더 — 템플릿이 싣는 Claude 스킬(`.claude/skills/…`, 2026-10-14). 셀러 로컬 Claude Code와 AI 상점 도우미가 같은 기준을 읽는다 */
11384
+ const READONLY_DIRS = new Set([".claude"]);
11481
11385
  /**
11482
11386
  * 쓸 수 있는 폴더 — 코드(`src`), 정적 파일(`public`), 화면 문구 카탈로그(`messages`, 템플릿 `src/i18n`이 `messages/ko.json`을 import한다, #73).
11483
11387
  * 템플릿이 import하는 파일이 이 밖에 있으면 첫 작업 트리(source.tgz)를 풀 때 빠져 빌드·검사가 깨진다(#78)
@@ -11490,6 +11394,7 @@ const WRITABLE_DIRS = new Set([
11490
11394
  /** 작업 트리 루트에 둘 수 있는 이름(폴더·파일) — 컨테이너 스냅샷이 이 이름만 묶는다(`container-tree.ts`) */
11491
11395
  const TRACKED_ROOT_NAMES = [
11492
11396
  ...WRITABLE_DIRS,
11397
+ ...READONLY_DIRS,
11493
11398
  ...WRITABLE_ROOT_FILES,
11494
11399
  ...READONLY_ROOT_FILES
11495
11400
  ];
@@ -11519,9 +11424,10 @@ function accessOf(path) {
11519
11424
  if (READONLY_ROOT_FILES.has(first)) return "read";
11520
11425
  return null;
11521
11426
  }
11522
- if (!WRITABLE_DIRS.has(first)) return null;
11523
- if (segments.some((s) => s.startsWith("."))) return null;
11524
- return "write";
11427
+ const readonlyDir = READONLY_DIRS.has(first);
11428
+ if (!readonlyDir && !WRITABLE_DIRS.has(first)) return null;
11429
+ if (segments.slice(1).some((s) => s.startsWith("."))) return null;
11430
+ return readonlyDir ? "read" : "write";
11525
11431
  }
11526
11432
 
11527
11433
  //#endregion
@@ -11870,6 +11776,24 @@ function lockImporterSpecifiers(lock) {
11870
11776
  return out;
11871
11777
  }
11872
11778
 
11779
+ //#endregion
11780
+ //#region src/guide-skill.ts
11781
+ /**
11782
+ * 받은 폴더의 에이전트 안내 스킬(2026-10-16 사용자 결정) — 셀러의 Claude Code가 sayren 중앙 스킬을 HTTP로 읽게 한다.
11783
+ * 빈 템플릿이 싣는 `templates/storefront-blank/.claude/skills/sayren/SKILL.md`와 같은 내용이다(`guide-skill.test.ts`가 고정한다).
11784
+ */
11785
+ const GUIDE_SKILL_PATH = ".claude/skills/sayren/SKILL.md";
11786
+ 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";
11787
+ /**
11788
+ * 받은 트리에 안내 스킬이 없으면 넣는다 — 템플릿이 싣거나 셀러가 올린 내용이 우선이다. 트리에 넣으므로 다시 받을 때 지울 파일로 잡히지 않고
11789
+ * `deploy`가 원격 작업 트리에도 올린다(AI 상점 도우미가 같은 안내를 읽는다). 넣었으면 true
11790
+ */
11791
+ function withGuideSkill(files) {
11792
+ if (files.has(GUIDE_SKILL_PATH)) return false;
11793
+ files.set(GUIDE_SKILL_PATH, new TextEncoder().encode(GUIDE_SKILL));
11794
+ return true;
11795
+ }
11796
+
11873
11797
  //#endregion
11874
11798
  //#region src/local-build.ts
11875
11799
  /** 실패할 때 보여 주는 빌드 출력 줄 수 */
@@ -11978,7 +11902,7 @@ async function pull(flags, io) {
11978
11902
  return receiveSource(client, auth, source, flags, io, authDir);
11979
11903
  }
11980
11904
  /**
11981
- * 받은 작업 트리를 폴더에 쓰고 받은 폴더 파일(`.sayren/state.json`·`.env`·`.mcp.json`·`.gitignore`)을 맞춘다. `pull`과 `init`(#101)이 같이 쓴다.
11905
+ * 받은 작업 트리(+ 에이전트 안내 스킬 `withGuideSkill`)를 폴더에 쓰고 받은 폴더 파일(`.sayren/state.json`·`.env`·`.mcp.json`·`.gitignore`)을 맞춘다. `pull`과 `init`(#101)이 같이 쓴다.
11982
11906
  * 대상은 `--dir` → 받은 폴더 안이면 그 폴더 → 상점 코드다. 비어 있지 않은 폴더는 `--force`일 때 같은 사이트를 받은 폴더만 다시 받는다.
11983
11907
  */
11984
11908
  async function receiveSource(client, auth, source, flags, io, migratedDir) {
@@ -12039,6 +11963,7 @@ async function receiveSource(client, auth, source, flags, io, migratedDir) {
12039
11963
  const tgz = new Uint8Array(Buffer.concat(chunks));
12040
11964
  if (sha256Hex(tgz) !== source.archive.sha256) throw new CliError(cli_pull_archive_corrupt());
12041
11965
  const files = await unpackArchive(tgz);
11966
+ withGuideSkill(files);
12042
11967
  const collisions = caseCollisions(files.keys());
12043
11968
  if (collisions.length > 0) throw new CliError(cli_pull_case_collisions({ groups: collisions.map((group) => group.join(" · ")).join(", ") }));
12044
11969
  const diff = diffLocal(target, files);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sayren",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "imports": {
6
6
  "#paraglide/*": "./src/paraglide/*"
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "esbuild": "^0.28.2",
20
- "@sayren/store-sdk": "^0.30.16"
20
+ "@sayren/store-sdk": "^0.33.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@biomejs/biome": "^2.5.14",
@@ -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.24.0"
31
+ "@sayren/mcp": "0.26.0"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"