sayren 0.15.1 → 0.16.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.
Files changed (35) hide show
  1. package/dist/index.mjs +226 -561
  2. package/package.json +4 -4
  3. package/dist/boilerplate/.claude/skills/sayren/SKILL.md +0 -18
  4. package/dist/boilerplate/.mcp.json +0 -8
  5. package/dist/boilerplate/CLAUDE.md +0 -26
  6. package/dist/boilerplate/README.md +0 -12
  7. package/dist/boilerplate/_gitignore +0 -8
  8. package/dist/boilerplate/biome.json +0 -45
  9. package/dist/boilerplate/messages/en.json +0 -9
  10. package/dist/boilerplate/messages/ko.json +0 -9
  11. package/dist/boilerplate/package.json +0 -46
  12. package/dist/boilerplate/public/_headers +0 -5
  13. package/dist/boilerplate/public/favicon.svg +0 -1
  14. package/dist/boilerplate/src/hooks/use-catalog.ts +0 -28
  15. package/dist/boilerplate/src/i18n/index.ts +0 -99
  16. package/dist/boilerplate/src/i18n/locale.server.ts +0 -10
  17. package/dist/boilerplate/src/lib/analytics.ts +0 -60
  18. package/dist/boilerplate/src/lib/api.server.ts +0 -27
  19. package/dist/boilerplate/src/lib/api.ts +0 -7
  20. package/dist/boilerplate/src/lib/config.server.ts +0 -23
  21. package/dist/boilerplate/src/lib/config.test.ts +0 -16
  22. package/dist/boilerplate/src/lib/config.ts +0 -19
  23. package/dist/boilerplate/src/lib/cookies.server.ts +0 -35
  24. package/dist/boilerplate/src/lib/format.ts +0 -19
  25. package/dist/boilerplate/src/lib/queries.ts +0 -61
  26. package/dist/boilerplate/src/routeTree.gen.ts +0 -69
  27. package/dist/boilerplate/src/router.tsx +0 -44
  28. package/dist/boilerplate/src/routes/__root.tsx +0 -119
  29. package/dist/boilerplate/src/routes/index.tsx +0 -52
  30. package/dist/boilerplate/src/start.ts +0 -20
  31. package/dist/boilerplate/src/styles.css +0 -29
  32. package/dist/boilerplate/src/theme.css +0 -38
  33. package/dist/boilerplate/tsconfig.json +0 -16
  34. package/dist/boilerplate/vite.config.ts +0 -12
  35. package/dist/boilerplate/vitest.config.ts +0 -5
package/dist/index.mjs CHANGED
@@ -6,7 +6,7 @@ import { APP_DEPLOY_KEY_PATTERN, APP_README_MAX_BYTES, APP_SECRET_PATTERNS, APP_
6
6
  import { chmodSync, closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
7
7
  import { createServer } from "node:http";
8
8
  import { dirname, extname, join, relative, resolve, sep } from "node:path";
9
- import { fileURLToPath, pathToFileURL } from "node:url";
9
+ import { pathToFileURL } from "node:url";
10
10
  import { gzipSync } from "node:zlib";
11
11
  import { createHash, randomBytes } from "node:crypto";
12
12
 
@@ -1082,7 +1082,7 @@ const mcp_report_next_reverify_remote = ((inputs = {}, options = {}) => {
1082
1082
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1083
1083
  /** @typedef {{ files: NonNullable<unknown> }} Mcp_Report_Template_Hint_LocalInputs */
1084
1084
  const ko_mcp_report_template_hint_local = (i) => {
1085
- return `손대지 않아야 하는 파일을 고친 결과일 수 있다 — \`get_template_file\`로 ${i?.files}의 원본을 받아 대조한다.`;
1085
+ return `손대지 않아야 하는 파일을 고친 결과일 수 있다 — ${i?.files}을(를) 규칙의 \`example\`이나 git 기록과 대조해 되돌린다.`;
1086
1086
  };
1087
1087
  /**
1088
1088
 
@@ -1100,7 +1100,7 @@ const mcp_report_template_hint_local = ((inputs, options = {}) => {
1100
1100
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1101
1101
  /** @typedef {{ files: NonNullable<unknown> }} Mcp_Report_Template_Hint_RemoteInputs */
1102
1102
  const ko_mcp_report_template_hint_remote = (i) => {
1103
- return `손대지 않아야 하는 파일을 고친 결과일 수 있다 — ${i?.files}을(를) \`read_file\`로 읽어 \`get_template_file\`의 템플릿 원본(지금 카탈로그 버전)이나 규칙의 \`example\`과 대조하고 되돌린다.`;
1103
+ return `손대지 않아야 하는 파일을 고친 결과일 수 있다 — ${i?.files}을(를) \`read_file\`로 읽어 규칙의 \`example\`과 대조하고 되돌린다.`;
1104
1104
  };
1105
1105
  /**
1106
1106
 
@@ -1985,7 +1985,7 @@ const mcp_rule_local_import_exists_example = ((inputs = {}, options = {}) => {
1985
1985
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1986
1986
  /** @typedef {{}} Mcp_Rule_Local_Import_Exists_FixInputs */
1987
1987
  const ko_mcp_rule_local_import_exists_fix = () => {
1988
- return `빠진 파일을 템플릿 원본(\`get_template_file\`, 또는 \`npx @sayren/mcp create\`로 받은 사본)에서 가져와 같은 경로에 둔다. 호스팅 사이트는 \`write_files\`(\`expectedHash: null\`)나 \`sayren deploy\`로 올린다. 쓰지 않는 import면 지운다. 템플릿의 화면 문구는 \`messages/ko.json\`이다`;
1988
+ return `빠진 파일을 같은 경로에 다시 만든다(git 기록에 있으면 되살린다). 호스팅 사이트는 \`write_files\`(\`expectedHash: null\`)나 \`sayren deploy\`로 올린다. 쓰지 않는 import면 지운다. 화면 문구 카탈로그는 \`messages/ko.json\`이다`;
1989
1989
  };
1990
1990
  /**
1991
1991
 
@@ -2751,42 +2751,6 @@ const mcp_rule_tenant_header_why = ((inputs = {}, options = {}) => {
2751
2751
  return ko_mcp_rule_tenant_header_why(inputs);
2752
2752
  });
2753
2753
 
2754
- //#endregion
2755
- //#region ../mcp/src/paraglide/messages/mcp_template_catalog_version_missing.js
2756
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
2757
- /** @typedef {{ name: NonNullable<unknown> }} Mcp_Template_Catalog_Version_MissingInputs */
2758
- const ko_mcp_template_catalog_version_missing = (i) => {
2759
- return `catalog에 ${i?.name} 버전이 없습니다`;
2760
- };
2761
- /**
2762
-
2763
- * @param {Mcp_Template_Catalog_Version_MissingInputs} inputs
2764
- * @param {{ locale?: "ko" }} options
2765
- * @returns {LocalizedString}
2766
- */
2767
- const mcp_template_catalog_version_missing = ((inputs, options = {}) => {
2768
- experimentalStaticLocale$1 ?? options.locale ?? getLocale$1();
2769
- return ko_mcp_template_catalog_version_missing(inputs);
2770
- });
2771
-
2772
- //#endregion
2773
- //#region ../mcp/src/paraglide/messages/mcp_template_published_version_unknown.js
2774
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
2775
- /** @typedef {{ name: NonNullable<unknown> }} Mcp_Template_Published_Version_UnknownInputs */
2776
- const ko_mcp_template_published_version_unknown = (i) => {
2777
- return `${i?.name}의 배포 버전을 모르겠습니다`;
2778
- };
2779
- /**
2780
-
2781
- * @param {Mcp_Template_Published_Version_UnknownInputs} inputs
2782
- * @param {{ locale?: "ko" }} options
2783
- * @returns {LocalizedString}
2784
- */
2785
- const mcp_template_published_version_unknown = ((inputs, options = {}) => {
2786
- experimentalStaticLocale$1 ?? options.locale ?? getLocale$1();
2787
- return ko_mcp_template_published_version_unknown(inputs);
2788
- });
2789
-
2790
2754
  //#endregion
2791
2755
  //#region ../mcp/src/paraglide/messages/mcp_verify_analytics_ids.js
2792
2756
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
@@ -3519,7 +3483,7 @@ function lazyMessages(table) {
3519
3483
  /**
3520
3484
  * 셀러 프로젝트의 `.mcp.json`(Claude Code 형식) — 원격 MCP(`mcp.sayren.io`)에 붙는 `sayren` 항목을 만들고 읽는다.
3521
3485
  *
3522
- * `npx sayren pull`·`login`(packages/cli)과 `sayren-mcp create`가 같은 함수로 쓴다. 파일 입출력은 부르는 쪽이 하고 여기는 문자열만
3486
+ * `npx sayren pull`·`link`·`login`(packages/cli)이 같은 함수로 쓴다. 파일 입출력은 부르는 쪽이 하고 여기는 문자열만
3523
3487
  * 다룬다 — 게이트웨이(Worker)가 헤더 이름을 이 파일에서 가져가므로 Node 전용 API를 쓰지 않는다.
3524
3488
  *
3525
3489
  * - **토큰을 쓰지 않는다(#71)** — 항목은 주소와 `X-Store-Id`뿐이고 MCP 클라이언트가 게이트웨이의 401 + `resource_metadata`를 보고
@@ -3609,7 +3573,7 @@ function sayrenStoreIdOf(text) {
3609
3573
  //#endregion
3610
3574
  //#region ../mcp/src/mcp-json-file.ts
3611
3575
  /**
3612
- * `.mcp.json` 파일 쓰기(Node) — `sayren` CLI(packages/cli)와 `sayren-mcp create`가 같이 쓴다. 형식은 `mcp-json.ts`(Node 없음)다.
3576
+ * `.mcp.json` 파일 쓰기(Node) — `sayren` CLI(packages/cli)가 쓴다. 형식은 `mcp-json.ts`(Node 없음)다.
3613
3577
  * 항목에 토큰을 쓰지 않는다(#71 R1). 심볼릭 링크는 따라가지 않는다(폴더 밖 파일을 덮지 않게).
3614
3578
  */
3615
3579
  function git(dir, args) {
@@ -4403,7 +4367,7 @@ const cli_app_default_export_missing = ((inputs = {}, options = {}) => {
4403
4367
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
4404
4368
  /** @typedef {{ number: NonNullable<unknown> }} Cli_App_Deploy_DoneInputs */
4405
4369
  const ko_cli_app_deploy_done = (i) => {
4406
- return `버전 ${i?.number}을 만들었습니다. 적용하려면: npx sayren app release ${i?.number}`;
4370
+ return `${i?.number}번 버전을 만들었습니다. 적용하려면: npx sayren app release ${i?.number}`;
4407
4371
  };
4408
4372
  /**
4409
4373
 
@@ -4774,7 +4738,7 @@ const cli_app_manifest_legacy = ((inputs = {}, options = {}) => {
4774
4738
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
4775
4739
  /** @typedef {{ file: NonNullable<unknown> }} Cli_App_Manifest_MissingInputs */
4776
4740
  const ko_cli_app_manifest_missing = (i) => {
4777
- return `${i?.file}이 없습니다. 앱 폴더에서 실행하거나 --dir을 주십시오`;
4741
+ return `${i?.file} 파일이 없습니다. 앱 폴더에서 실행하거나 --dir을 주십시오`;
4778
4742
  };
4779
4743
  /**
4780
4744
 
@@ -4792,7 +4756,7 @@ const cli_app_manifest_missing = ((inputs, options = {}) => {
4792
4756
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
4793
4757
  /** @typedef {{ file: NonNullable<unknown> }} Cli_App_Not_RegisteredInputs */
4794
4758
  const ko_cli_app_not_registered = (i) => {
4795
- return `등록된 앱이 아닙니다(${i?.file}은 있지만 .sayren/app.json이 없습니다). npx sayren app init <handle>로 만든 폴더에서 실행하십시오`;
4759
+ return `등록된 앱이 아닙니다(${i?.file} 파일은 있지만 .sayren/app.json이 없습니다). npx sayren app init <handle>로 만든 폴더에서 실행하십시오`;
4796
4760
  };
4797
4761
  /**
4798
4762
 
@@ -4864,7 +4828,7 @@ const cli_app_readme_too_large = ((inputs, options = {}) => {
4864
4828
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
4865
4829
  /** @typedef {{ number: NonNullable<unknown>, applied: NonNullable<unknown>, pending: NonNullable<unknown> }} Cli_App_Release_DoneInputs */
4866
4830
  const ko_cli_app_release_done = (i) => {
4867
- return `버전 ${i?.number}이 현재 버전입니다 — 곧바로 적용 ${i?.applied}, 재동의 대기 ${i?.pending}`;
4831
+ return `${i?.number}번 버전이 현재 버전입니다 — 곧바로 적용 ${i?.applied}, 재동의 대기 ${i?.pending}`;
4868
4832
  };
4869
4833
  /**
4870
4834
 
@@ -4882,7 +4846,7 @@ const cli_app_release_done = ((inputs, options = {}) => {
4882
4846
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
4883
4847
  /** @typedef {{ name: NonNullable<unknown> }} Cli_App_Secret_DeletedInputs */
4884
4848
  const ko_cli_app_secret_deleted = (i) => {
4885
- return `비밀 값 ${i?.name}을 지웠습니다`;
4849
+ return `${i?.name} 비밀 값을 지웠습니다`;
4886
4850
  };
4887
4851
  /**
4888
4852
 
@@ -4954,7 +4918,7 @@ const cli_app_secret_name_required = ((inputs = {}, options = {}) => {
4954
4918
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
4955
4919
  /** @typedef {{ name: NonNullable<unknown> }} Cli_App_Secret_SavedInputs */
4956
4920
  const ko_cli_app_secret_saved = (i) => {
4957
- return `비밀 값 ${i?.name}을 저장했습니다`;
4921
+ return `${i?.name} 비밀 값을 저장했습니다`;
4958
4922
  };
4959
4923
  /**
4960
4924
 
@@ -5008,7 +4972,7 @@ const cli_app_since_invalid = ((inputs, options = {}) => {
5008
4972
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
5009
4973
  /** @typedef {{ file: NonNullable<unknown> }} Cli_App_State_InvalidInputs */
5010
4974
  const ko_cli_app_state_invalid = (i) => {
5011
- return `${i?.file}을 읽을 수 없습니다. 파일을 지우고 npx sayren app init을 다시 실행하십시오`;
4975
+ return `${i?.file} 파일을 읽을 수 없습니다. 파일을 지우고 npx sayren app init을 다시 실행하십시오`;
5012
4976
  };
5013
4977
  /**
5014
4978
 
@@ -5568,14 +5532,14 @@ const cli_create_dir_not_empty = ((inputs, options = {}) => {
5568
5532
  //#endregion
5569
5533
  //#region src/paraglide/messages/cli_create_done.js
5570
5534
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
5571
- /** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown> }} Cli_Create_DoneInputs */
5535
+ /** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown>, install: NonNullable<unknown>, dev: NonNullable<unknown> }} Cli_Create_DoneInputs */
5572
5536
  const ko_cli_create_done = (i) => {
5573
5537
  return `시작 코드를 만들었습니다 — ${i?.dir} (파일 ${i?.count}개)
5574
5538
  다음:
5575
5539
  cd ${i?.dir}
5576
- npm install
5540
+ ${i?.install}
5577
5541
  npx sayren link # 상점에 연결하고 .env에 상점 값을 씁니다
5578
- npm run dev`;
5542
+ ${i?.dev}`;
5579
5543
  };
5580
5544
  /**
5581
5545
 
@@ -5591,13 +5555,13 @@ const cli_create_done = ((inputs, options = {}) => {
5591
5555
  //#endregion
5592
5556
  //#region src/paraglide/messages/cli_create_done_store_code.js
5593
5557
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
5594
- /** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown>, storeCode: NonNullable<unknown> }} Cli_Create_Done_Store_CodeInputs */
5558
+ /** @typedef {{ dir: NonNullable<unknown>, count: NonNullable<unknown>, storeCode: NonNullable<unknown>, install: NonNullable<unknown>, dev: NonNullable<unknown> }} Cli_Create_Done_Store_CodeInputs */
5595
5559
  const ko_cli_create_done_store_code = (i) => {
5596
5560
  return `시작 코드를 만들었습니다 — ${i?.dir} (파일 ${i?.count}개, .env에 상점 ${i?.storeCode} 연결 값)
5597
5561
  다음:
5598
5562
  cd ${i?.dir}
5599
- npm install
5600
- npm run dev
5563
+ ${i?.install}
5564
+ ${i?.dev}
5601
5565
  npx sayren deploy # 처음이면 상점 사이트에 연결한 뒤 배포합니다`;
5602
5566
  };
5603
5567
  /**
@@ -5629,6 +5593,42 @@ const cli_create_name_invalid = ((inputs, options = {}) => {
5629
5593
  return ko_cli_create_name_invalid(inputs);
5630
5594
  });
5631
5595
 
5596
+ //#endregion
5597
+ //#region src/paraglide/messages/cli_create_starter_no_release.js
5598
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
5599
+ /** @typedef {{ status: NonNullable<unknown> }} Cli_Create_Starter_No_ReleaseInputs */
5600
+ const ko_cli_create_starter_no_release = (i) => {
5601
+ return `시작 코드의 최신 릴리스를 찾지 못했습니다(GitHub avarcorp/sayren-starter, HTTP ${i?.status}). 잠시 뒤 다시 시도하십시오`;
5602
+ };
5603
+ /**
5604
+
5605
+ * @param {Cli_Create_Starter_No_ReleaseInputs} inputs
5606
+ * @param {{ locale?: "ko" }} options
5607
+ * @returns {LocalizedString}
5608
+ */
5609
+ const cli_create_starter_no_release = ((inputs, options = {}) => {
5610
+ experimentalStaticLocale ?? options.locale ?? getLocale();
5611
+ return ko_cli_create_starter_no_release(inputs);
5612
+ });
5613
+
5614
+ //#endregion
5615
+ //#region src/paraglide/messages/cli_create_starter_unreachable.js
5616
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
5617
+ /** @typedef {{}} Cli_Create_Starter_UnreachableInputs */
5618
+ const ko_cli_create_starter_unreachable = () => {
5619
+ return `시작 코드의 최신 버전을 확인하지 못했습니다. 시작 코드는 GitHub(avarcorp/sayren-starter)에서 받으므로 네트워크 연결을 확인하십시오`;
5620
+ };
5621
+ /**
5622
+
5623
+ * @param {Cli_Create_Starter_UnreachableInputs} inputs
5624
+ * @param {{ locale?: "ko" }} options
5625
+ * @returns {LocalizedString}
5626
+ */
5627
+ const cli_create_starter_unreachable = ((inputs = {}, options = {}) => {
5628
+ experimentalStaticLocale ?? options.locale ?? getLocale();
5629
+ return ko_cli_create_starter_unreachable(inputs);
5630
+ });
5631
+
5632
5632
  //#endregion
5633
5633
  //#region src/paraglide/messages/cli_create_store_code_invalid.js
5634
5634
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
@@ -5832,7 +5832,7 @@ const cli_create_usage = ((inputs = {}, options = {}) => {
5832
5832
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
5833
5833
  /** @typedef {{ path: NonNullable<unknown> }} Cli_Credentials_Git_TrackedInputs */
5834
5834
  const ko_cli_credentials_git_tracked = (i) => {
5835
- return `${i?.path}가 git에 커밋돼 있어 토큰을 쓰지 않았습니다. 추적을 해제하거나 XDG_CONFIG_HOME으로 다른 폴더를 지정하십시오`;
5835
+ return `${i?.path} 파일이 git에 커밋돼 있어 토큰을 쓰지 않았습니다. 추적을 해제하거나 XDG_CONFIG_HOME으로 다른 폴더를 지정하십시오`;
5836
5836
  };
5837
5837
  /**
5838
5838
 
@@ -5886,7 +5886,7 @@ const cli_credentials_no_config_dir = ((inputs = {}, options = {}) => {
5886
5886
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
5887
5887
  /** @typedef {{ path: NonNullable<unknown> }} Cli_Credentials_SymlinkInputs */
5888
5888
  const ko_cli_credentials_symlink = (i) => {
5889
- return `${i?.path}가 심볼릭 링크라 읽지 않았습니다. 링크를 지운 뒤 다시 로그인하십시오`;
5889
+ return `${i?.path} 파일이 심볼릭 링크라 읽지 않았습니다. 링크를 지운 뒤 다시 로그인하십시오`;
5890
5890
  };
5891
5891
  /**
5892
5892
 
@@ -5904,7 +5904,7 @@ const cli_credentials_symlink = ((inputs, options = {}) => {
5904
5904
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
5905
5905
  /** @typedef {{ path: NonNullable<unknown> }} Cli_Credentials_UnreadableInputs */
5906
5906
  const ko_cli_credentials_unreadable = (i) => {
5907
- return `${i?.path}를 읽지 못했습니다. 파일을 지우고 다시 로그인하십시오`;
5907
+ return `${i?.path} 파일을 읽지 못했습니다. 파일을 지우고 다시 로그인하십시오`;
5908
5908
  };
5909
5909
  /**
5910
5910
 
@@ -6011,9 +6011,9 @@ const cli_deploy_build_local_failed = ((inputs, options = {}) => {
6011
6011
  //#endregion
6012
6012
  //#region src/paraglide/messages/cli_deploy_build_local_no_deps.js
6013
6013
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6014
- /** @typedef {{}} Cli_Deploy_Build_Local_No_DepsInputs */
6015
- const ko_cli_deploy_build_local_no_deps = () => {
6016
- return `의존성이 설치되지 않아(node_modules 없음) 로컬 빌드를 건너뜁니다. npm install --ignore-scripts 뒤 배포하면 올리기 전에 빌드를 확인합니다`;
6014
+ /** @typedef {{ install: NonNullable<unknown> }} Cli_Deploy_Build_Local_No_DepsInputs */
6015
+ const ko_cli_deploy_build_local_no_deps = (i) => {
6016
+ return `의존성이 설치되지 않아(node_modules 없음) 로컬 빌드를 건너뜁니다. ${i?.install} 뒤 배포하면 올리기 전에 빌드를 확인합니다`;
6017
6017
  };
6018
6018
  /**
6019
6019
 
@@ -6021,7 +6021,7 @@ const ko_cli_deploy_build_local_no_deps = () => {
6021
6021
  * @param {{ locale?: "ko" }} options
6022
6022
  * @returns {LocalizedString}
6023
6023
  */
6024
- const cli_deploy_build_local_no_deps = ((inputs = {}, options = {}) => {
6024
+ const cli_deploy_build_local_no_deps = ((inputs, options = {}) => {
6025
6025
  experimentalStaticLocale ?? options.locale ?? getLocale();
6026
6026
  return ko_cli_deploy_build_local_no_deps(inputs);
6027
6027
  });
@@ -6247,7 +6247,7 @@ const cli_deploy_force_yes_required = ((inputs = {}, options = {}) => {
6247
6247
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6248
6248
  /** @typedef {{ dir: NonNullable<unknown> }} Cli_Deploy_LinkingInputs */
6249
6249
  const ko_cli_deploy_linking = (i) => {
6250
- return `${i?.dir}는 아직 상점에 연결되지 않았습니다 — 먼저 연결합니다(npx sayren link)`;
6250
+ return `${i?.dir} 폴더는 아직 상점에 연결되지 않았습니다 — 먼저 연결합니다(npx sayren link)`;
6251
6251
  };
6252
6252
  /**
6253
6253
 
@@ -6284,7 +6284,7 @@ const cli_deploy_merged = ((inputs = {}, options = {}) => {
6284
6284
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6285
6285
  /** @typedef {{ version: NonNullable<unknown> }} Cli_Deploy_Not_PublishedInputs */
6286
6286
  const ko_cli_deploy_not_published = (i) => {
6287
- return `발행하지 않았습니다(--no-publish). 상점 플랫폼 › 스토어프론트 › 버전에서 v${i?.version}를 발행할 수 있습니다`;
6287
+ return `발행하지 않았습니다(--no-publish). 상점 플랫폼 › 스토어프론트 › 버전에서 v${i?.version} 버전을 발행할 수 있습니다`;
6288
6288
  };
6289
6289
  /**
6290
6290
 
@@ -6302,7 +6302,7 @@ const cli_deploy_not_published = ((inputs, options = {}) => {
6302
6302
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6303
6303
  /** @typedef {{ version: NonNullable<unknown> }} Cli_Deploy_Publish_ApprovalInputs */
6304
6304
  const ko_cli_deploy_publish_approval = (i) => {
6305
- return `발행은 승인이 필요합니다(에이전트 토큰). 셀러가 승인하면 v${i?.version}가 발행됩니다`;
6305
+ return `발행은 승인이 필요합니다(에이전트 토큰). 셀러가 승인하면 v${i?.version} 버전이 발행됩니다`;
6306
6306
  };
6307
6307
  /**
6308
6308
 
@@ -6374,7 +6374,7 @@ const cli_deploy_seller_git = ((inputs, options = {}) => {
6374
6374
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6375
6375
  /** @typedef {{ version: NonNullable<unknown> }} Cli_Deploy_Site_ChangedInputs */
6376
6376
  const ko_cli_deploy_site_changed = (i) => {
6377
- return `다른 사람이 그 사이 다른 버전을 발행했습니다. v${i?.version}는 만들어 뒀습니다 — 상점 플랫폼에서 확인한 뒤 발행하십시오`;
6377
+ return `다른 사람이 그 사이 다른 버전을 발행했습니다. v${i?.version} 버전은 만들어 뒀습니다 — 상점 플랫폼에서 확인한 뒤 발행하십시오`;
6378
6378
  };
6379
6379
  /**
6380
6380
 
@@ -6856,274 +6856,21 @@ const cli_flag_value_required = ((inputs, options = {}) => {
6856
6856
  });
6857
6857
 
6858
6858
  //#endregion
6859
- //#region src/paraglide/messages/cli_init_confirm.js
6859
+ //#region src/paraglide/messages/cli_init_retired.js
6860
6860
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6861
- /** @typedef {{}} Cli_Init_ConfirmInputs */
6862
- const ko_cli_init_confirm = () => {
6863
- return `사이트를 만드시겠습니까?`;
6861
+ /** @typedef {{}} Cli_Init_RetiredInputs */
6862
+ const ko_cli_init_retired = () => {
6863
+ return `npx sayren init은 더는 사이트를 만들지 않습니다. npx sayren create <이름>으로 시작 코드를 만든 뒤 그 폴더에서 npx sayren link로 사이트를 만들고 연결하십시오`;
6864
6864
  };
6865
6865
  /**
6866
6866
 
6867
- * @param {Cli_Init_ConfirmInputs} inputs
6867
+ * @param {Cli_Init_RetiredInputs} inputs
6868
6868
  * @param {{ locale?: "ko" }} options
6869
6869
  * @returns {LocalizedString}
6870
6870
  */
6871
- const cli_init_confirm = ((inputs = {}, options = {}) => {
6871
+ const cli_init_retired = ((inputs = {}, options = {}) => {
6872
6872
  experimentalStaticLocale ?? options.locale ?? getLocale();
6873
- return ko_cli_init_confirm(inputs);
6874
- });
6875
-
6876
- //#endregion
6877
- //#region src/paraglide/messages/cli_init_created.js
6878
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6879
- /** @typedef {{ url: NonNullable<unknown> }} Cli_Init_CreatedInputs */
6880
- const ko_cli_init_created = (i) => {
6881
- return `사이트를 만들었습니다 — ${i?.url} (오픈 준비 중, 반영까지 1분 안팎)`;
6882
- };
6883
- /**
6884
-
6885
- * @param {Cli_Init_CreatedInputs} inputs
6886
- * @param {{ locale?: "ko" }} options
6887
- * @returns {LocalizedString}
6888
- */
6889
- const cli_init_created = ((inputs, options = {}) => {
6890
- experimentalStaticLocale ?? options.locale ?? getLocale();
6891
- return ko_cli_init_created(inputs);
6892
- });
6893
-
6894
- //#endregion
6895
- //#region src/paraglide/messages/cli_init_dir_not_empty.js
6896
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6897
- /** @typedef {{ dir: NonNullable<unknown> }} Cli_Init_Dir_Not_EmptyInputs */
6898
- const ko_cli_init_dir_not_empty = (i) => {
6899
- return `폴더가 비어 있지 않습니다: ${i?.dir}. 빈 폴더나 새 폴더 이름을 주십시오`;
6900
- };
6901
- /**
6902
-
6903
- * @param {Cli_Init_Dir_Not_EmptyInputs} inputs
6904
- * @param {{ locale?: "ko" }} options
6905
- * @returns {LocalizedString}
6906
- */
6907
- const cli_init_dir_not_empty = ((inputs, options = {}) => {
6908
- experimentalStaticLocale ?? options.locale ?? getLocale();
6909
- return ko_cli_init_dir_not_empty(inputs);
6910
- });
6911
-
6912
- //#endregion
6913
- //#region src/paraglide/messages/cli_init_inside_pulled.js
6914
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6915
- /** @typedef {{}} Cli_Init_Inside_PulledInputs */
6916
- const ko_cli_init_inside_pulled = () => {
6917
- return `지금 폴더는 이미 받은 사이트 폴더입니다. 새 사이트는 다른 폴더에서 npx sayren init <폴더>로 만들고, 이 사이트를 다시 받으려면 npx sayren pull --force를 쓰십시오`;
6918
- };
6919
- /**
6920
-
6921
- * @param {Cli_Init_Inside_PulledInputs} inputs
6922
- * @param {{ locale?: "ko" }} options
6923
- * @returns {LocalizedString}
6924
- */
6925
- const cli_init_inside_pulled = ((inputs = {}, options = {}) => {
6926
- experimentalStaticLocale ?? options.locale ?? getLocale();
6927
- return ko_cli_init_inside_pulled(inputs);
6928
- });
6929
-
6930
- //#endregion
6931
- //#region src/paraglide/messages/cli_init_no_templates.js
6932
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6933
- /** @typedef {{}} Cli_Init_No_TemplatesInputs */
6934
- const ko_cli_init_no_templates = () => {
6935
- return `쓸 수 있는 템플릿이 없습니다`;
6936
- };
6937
- /**
6938
-
6939
- * @param {Cli_Init_No_TemplatesInputs} inputs
6940
- * @param {{ locale?: "ko" }} options
6941
- * @returns {LocalizedString}
6942
- */
6943
- const cli_init_no_templates = ((inputs = {}, options = {}) => {
6944
- experimentalStaticLocale ?? options.locale ?? getLocale();
6945
- return ko_cli_init_no_templates(inputs);
6946
- });
6947
-
6948
- //#endregion
6949
- //#region src/paraglide/messages/cli_init_pull_failed.js
6950
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6951
- /** @typedef {{ storeId: NonNullable<unknown> }} Cli_Init_Pull_FailedInputs */
6952
- const ko_cli_init_pull_failed = (i) => {
6953
- return `사이트는 만들었지만 소스를 받지 못했습니다. 다시 만들지 말고 npx sayren pull --project ${i?.storeId}로 받으십시오`;
6954
- };
6955
- /**
6956
-
6957
- * @param {Cli_Init_Pull_FailedInputs} inputs
6958
- * @param {{ locale?: "ko" }} options
6959
- * @returns {LocalizedString}
6960
- */
6961
- const cli_init_pull_failed = ((inputs, options = {}) => {
6962
- experimentalStaticLocale ?? options.locale ?? getLocale();
6963
- return ko_cli_init_pull_failed(inputs);
6964
- });
6965
-
6966
- //#endregion
6967
- //#region src/paraglide/messages/cli_init_site_exists.js
6968
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6969
- /** @typedef {{ storeId: NonNullable<unknown> }} Cli_Init_Site_ExistsInputs */
6970
- const ko_cli_init_site_exists = (i) => {
6971
- return `이 상점에는 이미 호스팅 사이트가 있어 새로 만들지 않았습니다. npx sayren pull --project ${i?.storeId}로 소스를 받으십시오`;
6972
- };
6973
- /**
6974
-
6975
- * @param {Cli_Init_Site_ExistsInputs} inputs
6976
- * @param {{ locale?: "ko" }} options
6977
- * @returns {LocalizedString}
6978
- */
6979
- const cli_init_site_exists = ((inputs, options = {}) => {
6980
- experimentalStaticLocale ?? options.locale ?? getLocale();
6981
- return ko_cli_init_site_exists(inputs);
6982
- });
6983
-
6984
- //#endregion
6985
- //#region src/paraglide/messages/cli_init_site_url.js
6986
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
6987
- /** @typedef {{ url: NonNullable<unknown> }} Cli_Init_Site_UrlInputs */
6988
- const ko_cli_init_site_url = (i) => {
6989
- return `지금 사이트: ${i?.url}`;
6990
- };
6991
- /**
6992
-
6993
- * @param {Cli_Init_Site_UrlInputs} inputs
6994
- * @param {{ locale?: "ko" }} options
6995
- * @returns {LocalizedString}
6996
- */
6997
- const cli_init_site_url = ((inputs, options = {}) => {
6998
- experimentalStaticLocale ?? options.locale ?? getLocale();
6999
- return ko_cli_init_site_url(inputs);
7000
- });
7001
-
7002
- //#endregion
7003
- //#region src/paraglide/messages/cli_init_slug_taken.js
7004
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7005
- /** @typedef {{}} Cli_Init_Slug_TakenInputs */
7006
- const ko_cli_init_slug_taken = () => {
7007
- return `상점 코드와 같은 주소를 다른 사이트가 쓰고 있어 만들지 못했습니다. 상점 플랫폼 › 스토어프론트에서 다른 주소로 만든 뒤 npx sayren pull로 받으십시오`;
7008
- };
7009
- /**
7010
-
7011
- * @param {Cli_Init_Slug_TakenInputs} inputs
7012
- * @param {{ locale?: "ko" }} options
7013
- * @returns {LocalizedString}
7014
- */
7015
- const cli_init_slug_taken = ((inputs = {}, options = {}) => {
7016
- experimentalStaticLocale ?? options.locale ?? getLocale();
7017
- return ko_cli_init_slug_taken(inputs);
7018
- });
7019
-
7020
- //#endregion
7021
- //#region src/paraglide/messages/cli_init_summary.js
7022
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7023
- /** @typedef {{ storeId: NonNullable<unknown>, name: NonNullable<unknown>, templateId: NonNullable<unknown>, version: NonNullable<unknown> }} Cli_Init_SummaryInputs */
7024
- const ko_cli_init_summary = (i) => {
7025
- return `상점 ${i?.storeId}에 템플릿 ${i?.name}(${i?.templateId} v${i?.version})로 호스팅 사이트를 만듭니다. 주소는 상점 코드이고 오픈 준비 중으로 시작합니다. 만들기는 상점당 30일에 3번까지입니다`;
7026
- };
7027
- /**
7028
-
7029
- * @param {Cli_Init_SummaryInputs} inputs
7030
- * @param {{ locale?: "ko" }} options
7031
- * @returns {LocalizedString}
7032
- */
7033
- const cli_init_summary = ((inputs, options = {}) => {
7034
- experimentalStaticLocale ?? options.locale ?? getLocale();
7035
- return ko_cli_init_summary(inputs);
7036
- });
7037
-
7038
- //#endregion
7039
- //#region src/paraglide/messages/cli_init_template_option.js
7040
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7041
- /** @typedef {{ templateId: NonNullable<unknown>, name: NonNullable<unknown>, version: NonNullable<unknown>, description: NonNullable<unknown> }} Cli_Init_Template_OptionInputs */
7042
- const ko_cli_init_template_option = (i) => {
7043
- return `${i?.templateId} — ${i?.name} v${i?.version}: ${i?.description}`;
7044
- };
7045
- /**
7046
-
7047
- * @param {Cli_Init_Template_OptionInputs} inputs
7048
- * @param {{ locale?: "ko" }} options
7049
- * @returns {LocalizedString}
7050
- */
7051
- const cli_init_template_option = ((inputs, options = {}) => {
7052
- experimentalStaticLocale ?? options.locale ?? getLocale();
7053
- return ko_cli_init_template_option(inputs);
7054
- });
7055
-
7056
- //#endregion
7057
- //#region src/paraglide/messages/cli_init_template_prompt.js
7058
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7059
- /** @typedef {{}} Cli_Init_Template_PromptInputs */
7060
- const ko_cli_init_template_prompt = () => {
7061
- return `템플릿을 고르십시오:`;
7062
- };
7063
- /**
7064
-
7065
- * @param {Cli_Init_Template_PromptInputs} inputs
7066
- * @param {{ locale?: "ko" }} options
7067
- * @returns {LocalizedString}
7068
- */
7069
- const cli_init_template_prompt = ((inputs = {}, options = {}) => {
7070
- experimentalStaticLocale ?? options.locale ?? getLocale();
7071
- return ko_cli_init_template_prompt(inputs);
7072
- });
7073
-
7074
- //#endregion
7075
- //#region src/paraglide/messages/cli_init_template_required.js
7076
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7077
- /** @typedef {{ list: NonNullable<unknown> }} Cli_Init_Template_RequiredInputs */
7078
- const ko_cli_init_template_required = (i) => {
7079
- return `템플릿을 고르지 않았습니다. --template <id>로 주십시오(비대화형은 필수입니다):
7080
- ${i?.list}`;
7081
- };
7082
- /**
7083
-
7084
- * @param {Cli_Init_Template_RequiredInputs} inputs
7085
- * @param {{ locale?: "ko" }} options
7086
- * @returns {LocalizedString}
7087
- */
7088
- const cli_init_template_required = ((inputs, options = {}) => {
7089
- experimentalStaticLocale ?? options.locale ?? getLocale();
7090
- return ko_cli_init_template_required(inputs);
7091
- });
7092
-
7093
- //#endregion
7094
- //#region src/paraglide/messages/cli_init_template_unknown.js
7095
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7096
- /** @typedef {{ templateId: NonNullable<unknown>, ids: NonNullable<unknown> }} Cli_Init_Template_UnknownInputs */
7097
- const ko_cli_init_template_unknown = (i) => {
7098
- return `템플릿을 찾지 못했습니다: ${i?.templateId}. 쓸 수 있는 템플릿: ${i?.ids}`;
7099
- };
7100
- /**
7101
-
7102
- * @param {Cli_Init_Template_UnknownInputs} inputs
7103
- * @param {{ locale?: "ko" }} options
7104
- * @returns {LocalizedString}
7105
- */
7106
- const cli_init_template_unknown = ((inputs, options = {}) => {
7107
- experimentalStaticLocale ?? options.locale ?? getLocale();
7108
- return ko_cli_init_template_unknown(inputs);
7109
- });
7110
-
7111
- //#endregion
7112
- //#region src/paraglide/messages/cli_init_yes_required.js
7113
- /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7114
- /** @typedef {{}} Cli_Init_Yes_RequiredInputs */
7115
- const ko_cli_init_yes_required = () => {
7116
- return `사이트 만들기를 확인할 수 없는 환경입니다. --yes를 함께 주십시오`;
7117
- };
7118
- /**
7119
-
7120
- * @param {Cli_Init_Yes_RequiredInputs} inputs
7121
- * @param {{ locale?: "ko" }} options
7122
- * @returns {LocalizedString}
7123
- */
7124
- const cli_init_yes_required = ((inputs = {}, options = {}) => {
7125
- experimentalStaticLocale ?? options.locale ?? getLocale();
7126
- return ko_cli_init_yes_required(inputs);
6873
+ return ko_cli_init_retired(inputs);
7127
6874
  });
7128
6875
 
7129
6876
  //#endregion
@@ -7221,7 +6968,7 @@ const cli_legacy_env_token_removed = ((inputs, options = {}) => {
7221
6968
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7222
6969
  /** @typedef {{ from: NonNullable<unknown>, to: NonNullable<unknown> }} Cli_Legacy_Rc_TrackedInputs */
7223
6970
  const ko_cli_legacy_rc_tracked = (i) => {
7224
- return `${i?.from} → ${i?.to}(${i?.from}는 더 쓰지 않으니 커밋에서 지워도 됩니다)`;
6971
+ return `${i?.from} → ${i?.to}(${i?.from} 파일은 더 쓰지 않으니 커밋에서 지워도 됩니다)`;
7225
6972
  };
7226
6973
  /**
7227
6974
 
@@ -7293,7 +7040,7 @@ const cli_link_editing_disabled = ((inputs = {}, options = {}) => {
7293
7040
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7294
7041
  /** @typedef {{ lines: NonNullable<unknown> }} Cli_Link_GitignoreInputs */
7295
7042
  const ko_cli_link_gitignore = (i) => {
7296
- return `.gitignore에 ${i?.lines}를 더했습니다(배포에 올리지 않습니다)`;
7043
+ return `.gitignore에 ${i?.lines} 항목을 더했습니다(배포에 올리지 않습니다)`;
7297
7044
  };
7298
7045
  /**
7299
7046
 
@@ -7309,11 +7056,11 @@ const cli_link_gitignore = ((inputs, options = {}) => {
7309
7056
  //#endregion
7310
7057
  //#region src/paraglide/messages/cli_link_next_steps.js
7311
7058
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7312
- /** @typedef {{}} Cli_Link_Next_StepsInputs */
7313
- const ko_cli_link_next_steps = () => {
7059
+ /** @typedef {{ install: NonNullable<unknown>, dev: NonNullable<unknown> }} Cli_Link_Next_StepsInputs */
7060
+ const ko_cli_link_next_steps = (i) => {
7314
7061
  return `다음:
7315
- npm install
7316
- npm run dev # http://localhost:4010 — 운영 상점 데이터로 동작합니다
7062
+ ${i?.install} # 의존성의 설치 스크립트를 돌리지 않습니다
7063
+ ${i?.dev} # http://localhost:4010 — 운영 상점 데이터로 동작합니다
7317
7064
  npx sayren deploy # 빌드 → 버전 → 발행`;
7318
7065
  };
7319
7066
  /**
@@ -7322,7 +7069,7 @@ const ko_cli_link_next_steps = () => {
7322
7069
  * @param {{ locale?: "ko" }} options
7323
7070
  * @returns {LocalizedString}
7324
7071
  */
7325
- const cli_link_next_steps = ((inputs = {}, options = {}) => {
7072
+ const cli_link_next_steps = ((inputs, options = {}) => {
7326
7073
  experimentalStaticLocale ?? options.locale ?? getLocale();
7327
7074
  return ko_cli_link_next_steps(inputs);
7328
7075
  });
@@ -7980,7 +7727,7 @@ const cli_pull_adopt_confirm = ((inputs = {}, options = {}) => {
7980
7727
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7981
7728
  /** @typedef {{ dir: NonNullable<unknown>, stateFile: NonNullable<unknown>, siteId: NonNullable<unknown> }} Cli_Pull_Adopt_DirInputs */
7982
7729
  const ko_cli_pull_adopt_dir = (i) => {
7983
- return `${i?.dir}에 ${i?.stateFile}가 없습니다. 이 폴더를 사이트 ${i?.siteId}의 소스 폴더로 받아들이고 원격 작업 트리로 맞춥니다`;
7730
+ return `${i?.dir} 폴더에 ${i?.stateFile} 파일이 없습니다. 이 폴더를 사이트 ${i?.siteId}의 소스 폴더로 받아들이고 원격 작업 트리로 맞춥니다`;
7984
7731
  };
7985
7732
  /**
7986
7733
 
@@ -8070,7 +7817,7 @@ const cli_pull_dir_not_empty = ((inputs, options = {}) => {
8070
7817
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
8071
7818
  /** @typedef {{ dir: NonNullable<unknown>, siteId: NonNullable<unknown>, stateFile: NonNullable<unknown>, storeId: NonNullable<unknown> }} Cli_Pull_Dir_Not_PulledInputs */
8072
7819
  const ko_cli_pull_dir_not_pulled = (i) => {
8073
- return `${i?.dir}는 이 사이트(${i?.siteId})를 받은 폴더가 아닙니다(${i?.stateFile} 없음). 이 사이트 소스를 clone한 폴더면 --project ${i?.storeId}를 함께 주십시오. 아니면 빈 폴더를 지정하십시오`;
7820
+ return `${i?.dir} 폴더는 이 사이트(${i?.siteId})를 받은 폴더가 아닙니다(${i?.stateFile} 없음). 이 사이트 소스를 clone한 폴더면 --project ${i?.storeId}를 함께 주십시오. 아니면 빈 폴더를 지정하십시오`;
8074
7821
  };
8075
7822
  /**
8076
7823
 
@@ -8088,7 +7835,7 @@ const cli_pull_dir_not_pulled = ((inputs, options = {}) => {
8088
7835
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
8089
7836
  /** @typedef {{ dir: NonNullable<unknown>, siteId: NonNullable<unknown> }} Cli_Pull_Dir_Other_SiteInputs */
8090
7837
  const ko_cli_pull_dir_other_site = (i) => {
8091
- return `${i?.dir}는 다른 사이트(${i?.siteId})를 받은 폴더입니다. 빈 폴더를 지정하십시오`;
7838
+ return `${i?.dir} 폴더는 다른 사이트(${i?.siteId})를 받은 폴더입니다. 빈 폴더를 지정하십시오`;
8092
7839
  };
8093
7840
  /**
8094
7841
 
@@ -8248,13 +7995,13 @@ const cli_pull_mcp_written = ((inputs, options = {}) => {
8248
7995
  //#endregion
8249
7996
  //#region src/paraglide/messages/cli_pull_next_steps.js
8250
7997
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
8251
- /** @typedef {{ dir: NonNullable<unknown> }} Cli_Pull_Next_StepsInputs */
7998
+ /** @typedef {{ dir: NonNullable<unknown>, install: NonNullable<unknown>, dev: NonNullable<unknown> }} Cli_Pull_Next_StepsInputs */
8252
7999
  const ko_cli_pull_next_steps = (i) => {
8253
8000
  return `
8254
8001
  다음:
8255
8002
  cd ${i?.dir}
8256
- npm install --ignore-scripts # 의존성의 설치 스크립트를 돌리지 않습니다
8257
- npm run dev # http://localhost:4010 — 운영 상점 데이터로 동작합니다
8003
+ ${i?.install} # 의존성의 설치 스크립트를 돌리지 않습니다
8004
+ ${i?.dev} # http://localhost:4010 — 운영 상점 데이터로 동작합니다
8258
8005
  npx sayren deploy # 빌드 → 버전 → 발행`;
8259
8006
  };
8260
8007
  /**
@@ -8273,7 +8020,7 @@ const cli_pull_next_steps = ((inputs, options = {}) => {
8273
8020
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
8274
8021
  /** @typedef {{}} Cli_Pull_No_SiteInputs */
8275
8022
  const ko_cli_pull_no_site = () => {
8276
- return `이 상점에는 호스팅 사이트가 없습니다. npx sayren init으로 만들거나 상점 플랫폼 › 스토어프론트에서 만든 뒤 다시 받으십시오`;
8023
+ return `이 상점에는 호스팅 사이트가 없습니다. npx sayren link로 만들거나 상점 플랫폼 › 스토어프론트에서 만든 뒤 다시 받으십시오`;
8277
8024
  };
8278
8025
  /**
8279
8026
 
@@ -8379,9 +8126,9 @@ const cli_pull_store_mismatch = ((inputs, options = {}) => {
8379
8126
  //#endregion
8380
8127
  //#region src/paraglide/messages/cli_pull_watched_changed.js
8381
8128
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
8382
- /** @typedef {{ path: NonNullable<unknown> }} Cli_Pull_Watched_ChangedInputs */
8129
+ /** @typedef {{ path: NonNullable<unknown>, install: NonNullable<unknown> }} Cli_Pull_Watched_ChangedInputs */
8383
8130
  const ko_cli_pull_watched_changed = (i) => {
8384
- return `알림: 원격의 ${i?.path}가 이 폴더와 다릅니다. 설치 전에 내용을 확인하십시오(npm install --ignore-scripts 권장)`;
8131
+ return `알림: 원격의 ${i?.path} 파일이 이 폴더와 다릅니다. 설치 전에 내용을 확인하십시오(${i?.install} 권장)`;
8385
8132
  };
8386
8133
  /**
8387
8134
 
@@ -8764,7 +8511,7 @@ const cli_state_missing_fields = ((inputs, options = {}) => {
8764
8511
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
8765
8512
  /** @typedef {{ name: NonNullable<unknown> }} Cli_State_UnreadableInputs */
8766
8513
  const ko_cli_state_unreadable = (i) => {
8767
- return `${i?.name}를 읽지 못했습니다. JSON 형식을 확인하십시오`;
8514
+ return `${i?.name} 파일을 읽지 못했습니다. JSON 형식을 확인하십시오`;
8768
8515
  };
8769
8516
  /**
8770
8517
 
@@ -9089,7 +8836,7 @@ const cli_store_none = ((inputs = {}, options = {}) => {
9089
8836
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
9090
8837
  /** @typedef {{ storeId: NonNullable<unknown> }} Cli_Store_Out_Of_ScopeInputs */
9091
8838
  const ko_cli_store_out_of_scope = (i) => {
9092
- return `상점 ${i?.storeId}가 이 토큰의 상점 범위 밖입니다. 그 상점이 포함되게 다시 로그인하거나(npx sayren login) 다른 토큰을 쓰십시오`;
8839
+ return `${i?.storeId} 상점은 이 토큰의 상점 범위 밖입니다. 그 상점이 포함되게 다시 로그인하거나(npx sayren login) 다른 토큰을 쓰십시오`;
9093
8840
  };
9094
8841
  /**
9095
8842
 
@@ -9355,7 +9102,6 @@ const ko_cli_usage = (i) => {
9355
9102
  npx sayren create <이름> [--template <템플릿>] # 시작 코드를 새 폴더에 만듭니다(파일만, 로그인 없음)
9356
9103
  npx sayren link [폴더] [--store <storeId>] # 폴더를 상점 사이트에 연결합니다(없으면 사이트를 만듭니다)
9357
9104
  npx sayren env pull # 연결된 상점 값을 .env에 씁니다
9358
- npx sayren init [폴더] [--template <id>] [--store <storeId>] [--yes]
9359
9105
  npx sayren pull --project <storeId> [폴더] [--force [--yes]]
9360
9106
  npx sayren deploy [--no-publish] [--skip-build] [--message <메모>] [--force [--yes]]
9361
9107
  npx sayren status
@@ -9370,7 +9116,6 @@ const ko_cli_usage = (i) => {
9370
9116
  create 시작 코드(상점 이름·상품 목록 홈, SDK·문구·분석·에이전트 안내)를 <이름> 폴더에 만듭니다. 상점을 묻지 않고 호스팅 사이트도 만들지 않습니다
9371
9117
  link 상점을 고르고(--store → 토큰 범위 → 선택) 사이트가 없으면 만든 뒤 .sayren/project.json·.env·.mcp.json을 씁니다
9372
9118
  env env pull: 연결된 상점의 SAYREN_STORE_CODE·SAYREN_API_URL을 .env에 씁니다
9373
- init 템플릿을 골라 호스팅 사이트를 만들고 pull과 같은 폴더(기본: 상점 코드)로 받습니다. 사이트가 이미 있으면 만들지 않습니다
9374
9119
  pull 호스팅 중인 사이트의 작업 트리를 폴더(기본: 상점 코드)로 받고 .mcp.json·.env·.sayren/project.json을 씁니다
9375
9120
  deploy 로컬 검사 → 로컬 빌드 → 작업 트리 올리기 → 빌드 → 발행. 연결이 없으면 link부터 하고 다음 버전으로 교체합니다. 원격이 마지막 받기·올리기 이후 바뀌었으면 덮어쓰지 않습니다
9376
9121
  status 사이트·발행 버전·최근 빌드·편집 환경
@@ -9386,12 +9131,11 @@ const ko_cli_usage = (i) => {
9386
9131
  옵션:
9387
9132
  --project <storeId> pull: 받을 상점(--store와 같습니다)
9388
9133
  --template <템플릿> create: github:owner/repo[/경로][#ref](공개 GitHub 저장소) 또는 템플릿 id. 없으면 CLI에 든 시작 코드
9389
- init: 호스팅 템플릿 id(비대화형은 필수)
9390
9134
  --store-code <코드> create: 이 상점 코드로 .env를 씁니다(없으면 link·env pull이 씁니다)
9391
- --dir <폴더> 작업 폴더(기본: 지금 폴더, pull·init은 상점 코드)
9135
+ --dir <폴더> 작업 폴더(기본: 지금 폴더, pull은 상점 코드)
9392
9136
  --force pull: 같은 사이트를 받은 폴더에 원격을 다시 받습니다(지울·덮을 파일을 보여 주고 묻습니다)
9393
9137
  deploy: 원격 변경·열린 편집 환경을 무시하고 덮어씁니다(확인을 묻습니다)
9394
- --yes --force·init의 확인을 묻지 않습니다(비대화형)
9138
+ --yes --force의 확인을 묻지 않습니다(비대화형)
9395
9139
  --no-publish deploy: 버전까지만 만들고 발행하지 않습니다
9396
9140
  --skip-build deploy: 올리기 전 로컬 빌드(npm run build)를 건너뜁니다
9397
9141
  --message <메모> deploy: 버전 메모
@@ -10326,7 +10070,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
10326
10070
 
10327
10071
  //#endregion
10328
10072
  //#region package.json
10329
- var version = "0.15.1";
10073
+ var version = "0.16.1";
10330
10074
 
10331
10075
  //#endregion
10332
10076
  //#region src/client.ts
@@ -12204,7 +11948,7 @@ function isNeverUpload(path) {
12204
11948
  return segments.some((segment) => segment.startsWith(".env") || segment === ".sayren" || segment === ".dev.vars" || segment.startsWith(".dev.vars.")) || name === ".mcp.json" || name === ".sayrenrc" || name === ".npmrc";
12205
11949
  }
12206
11950
  /** 걸어 들어가지 않는 폴더 — 설치물·산출물·캐시·VCS(경로 규칙이 어차피 막는다. 크기 때문에 읽지도 않는다) */
12207
- const SKIP_DIRS$1 = new Set([
11951
+ const SKIP_DIRS = new Set([
12208
11952
  "node_modules",
12209
11953
  "dist",
12210
11954
  ".output",
@@ -12225,7 +11969,7 @@ function collectTree(root) {
12225
11969
  const full = join(dir, entry.name);
12226
11970
  const rel = normalizeStorefrontPath(relative(root, full).split(sep).join("/"));
12227
11971
  if (entry.isDirectory()) {
12228
- if (SKIP_DIRS$1.has(entry.name)) continue;
11972
+ if (SKIP_DIRS.has(entry.name)) continue;
12229
11973
  walk(full);
12230
11974
  continue;
12231
11975
  }
@@ -12436,7 +12180,8 @@ function lockImporterSpecifiers(lock) {
12436
12180
  //#region src/guide-skill.ts
12437
12181
  /**
12438
12182
  * 받은 폴더의 에이전트 안내 스킬(2026-10-16 사용자 결정) — 셀러의 Claude Code가 기능·화면을 고치기 전에 sayren 중앙 스킬을 `npx sayren skill`로 읽게 한다(스킬은 이 하나만 설치한다).
12439
- * 보일러플레이트(`npx sayren create`, #271)가 싣는 `packages/cli/boilerplate/.claude/skills/sayren/SKILL.md`와 같은 내용이다(`guide-skill.test.ts`가 고정한다).
12183
+ * 이 파일이 원천이다 — `create`는 받은 트리에 늘 이 내용을 쓰고(시작 코드 저장소에는 두지 않는다), `pull`·`init`은 없을 때만 넣는다(`withGuideSkill`).
12184
+ * sayren.io 디스커버리(`apps/website/app/lib/agent-skills.ts`)가 같은 내용을 내준다(`agent-skills.test.ts`가 고정한다).
12440
12185
  */
12441
12186
  const GUIDE_SKILL_PATH = ".claude/skills/sayren/SKILL.md";
12442
12187
  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";
@@ -12499,6 +12244,60 @@ function tailOf(output, lines = OUTPUT_TAIL_LINES) {
12499
12244
  return output.trimEnd().split(/\r?\n/).slice(-lines).join("\n");
12500
12245
  }
12501
12246
 
12247
+ //#endregion
12248
+ //#region src/package-manager.ts
12249
+ const KNOWN = [
12250
+ "npm",
12251
+ "pnpm",
12252
+ "yarn",
12253
+ "bun"
12254
+ ];
12255
+ const LOCKFILES = [
12256
+ ["pnpm-lock.yaml", "pnpm"],
12257
+ ["yarn.lock", "yarn"],
12258
+ ["bun.lock", "bun"],
12259
+ ["bun.lockb", "bun"],
12260
+ ["package-lock.json", "npm"]
12261
+ ];
12262
+ /** `has(path)`는 그 파일이 있는지, `read(path)`는 텍스트(없으면 null)다 — 받은 트리(Map)와 디스크 폴더가 같은 판정을 쓴다 */
12263
+ function packageManagerOf(source) {
12264
+ const pkg = source.read("package.json");
12265
+ if (pkg) try {
12266
+ const field = JSON.parse(pkg).packageManager;
12267
+ const name = typeof field === "string" ? field.split("@")[0]?.trim() : void 0;
12268
+ if (name && KNOWN.includes(name)) return name;
12269
+ } catch {}
12270
+ for (const [file, manager] of LOCKFILES) if (source.has(file)) return manager;
12271
+ return "npm";
12272
+ }
12273
+ /** 받은 트리(경로 → 내용) */
12274
+ function packageManagerOfFiles(files) {
12275
+ return packageManagerOf({
12276
+ has: (path) => files.has(path),
12277
+ read: (path) => {
12278
+ const bytes = files.get(path);
12279
+ return bytes ? new TextDecoder().decode(bytes) : null;
12280
+ }
12281
+ });
12282
+ }
12283
+ /** 디스크 폴더 */
12284
+ function packageManagerOfDir(dir) {
12285
+ return packageManagerOf({
12286
+ has: (path) => existsSync(join(dir, path)),
12287
+ read: (path) => {
12288
+ try {
12289
+ return readFileSync(join(dir, path), "utf8");
12290
+ } catch {
12291
+ return null;
12292
+ }
12293
+ }
12294
+ });
12295
+ }
12296
+ /** 설치 스크립트를 돌리지 않는 설치 명령 */
12297
+ const installCommand = (manager) => `${manager} install --ignore-scripts`;
12298
+ /** 개발 서버 명령 — npm·bun은 `run`이 있어야 스크립트를 부른다 */
12299
+ const devCommand = (manager) => manager === "npm" || manager === "bun" ? `${manager} run dev` : `${manager} dev`;
12300
+
12502
12301
  //#endregion
12503
12302
  //#region src/commands.ts
12504
12303
  /** 종료 코드 — 0 성공 · 1 실패 · 3 발행이 승인 대기(에이전트 토큰) */
@@ -12620,12 +12419,16 @@ async function receiveSource(client, auth, source, flags, io, migratedDir) {
12620
12419
  if (sha256Hex(tgz) !== source.archive.sha256) throw new CliError(cli_pull_archive_corrupt());
12621
12420
  const files = await unpackArchive(tgz);
12622
12421
  withGuideSkill(files);
12422
+ const manager = packageManagerOfFiles(files);
12623
12423
  const collisions = caseCollisions(files.keys());
12624
12424
  if (collisions.length > 0) throw new CliError(cli_pull_case_collisions({ groups: collisions.map((group) => group.join(" · ")).join(", ") }));
12625
12425
  const diff = diffLocal(target, files);
12626
12426
  if (existing.length > 0) {
12627
12427
  const watched = diff.overwrite.filter((path) => path === "package.json" || path === "vite.config.ts");
12628
- for (const path of watched) io.out(cli_pull_watched_changed({ path }));
12428
+ for (const path of watched) io.out(cli_pull_watched_changed({
12429
+ path,
12430
+ install: installCommand(manager)
12431
+ }));
12629
12432
  if (diff.remove.length + diff.overwrite.length > 0) {
12630
12433
  io.out(cli_pull_replace_summary({
12631
12434
  remove: diff.remove.length,
@@ -12688,81 +12491,19 @@ async function receiveSource(client, auth, source, flags, io, migratedDir) {
12688
12491
  siteId: source.siteId
12689
12492
  }));
12690
12493
  if (source.editSessionOpen) io.out(cli_pull_edit_session_open());
12691
- io.out(cli_pull_next_steps({ dir: pulledDir }));
12494
+ io.out(cli_pull_next_steps({
12495
+ dir: pulledDir,
12496
+ install: installCommand(manager),
12497
+ dev: devCommand(manager)
12498
+ }));
12692
12499
  return 0;
12693
12500
  }
12694
12501
  /**
12695
- * `sayren init [폴더]`(#101) — 템플릿을 골라 호스팅 사이트를 만들고(`POST /v1/storefront/site`) `pull`과 같은 폴더를 만든다.
12696
- * 사이트가 이미 있으면 만들지 않고 `pull`을 안내한다(상점당 열린 사이트는 하나다). 만들기는 상점 한도(30일 3회)를 쓰므로 확인을 묻는다.
12697
- * 토큰은 `storefront:rw`가 있어야 한다(로그인 토큰 `CLI_LOGIN_SCOPE`에 있다). 에이전트 토큰이어도 만들기는 승인 대상이 아니다
12698
- * (쓰기 정책 표에 없다 — 오픈 준비 중으로 시작해 구매자 화면을 바꾸지 않는다).
12502
+ * `sayren init`(#101)은 템플릿을 골라 호스팅 사이트를 만들던 명령이다. 템플릿 사이트를 만들지 않으므로(#315 2차, 공개 API 템플릿 삭제)
12503
+ * 사이트를 만들지 않고 시작 경로(`create` → `link`)를 안내하고 끝낸다
12699
12504
  */
12700
- async function init(flags, io) {
12701
- const target = flags.dir ? resolve(io.cwd, flags.dir) : null;
12702
- if (target && existsSync(target) && readdirSync(target).length > 0) throw new CliError(cli_init_dir_not_empty({ dir: relativeOf(io, target) }));
12703
- if (!target && isPulledDir(io.cwd)) throw new CliError(cli_init_inside_pulled());
12704
- const authDir = target && existsSync(target) ? target : io.cwd;
12705
- const { auth, client } = await apiFor(flags, authDir, null, io);
12706
- const storeId = auth.storeId ?? "<storeId>";
12707
- const hosting = await client.storefront.get();
12708
- if (hosting.site) {
12709
- io.out(cli_init_site_url({ url: hosting.site.url }));
12710
- throw new CliError(cli_init_site_exists({ storeId }));
12711
- }
12712
- if (!hosting.editingEnabled) throw new CliError(cli_pull_editing_disabled());
12713
- const template = await pickTemplate(await client.storefront.listTemplates(), flags.template, io);
12714
- io.out(cli_init_summary({
12715
- name: template.name,
12716
- templateId: template.templateId,
12717
- version: template.version,
12718
- storeId
12719
- }));
12720
- if (!flags.yes) {
12721
- if (!io.confirm) throw new CliError(cli_init_yes_required());
12722
- if (!await io.confirm(cli_init_confirm())) {
12723
- io.out(cli_cancelled());
12724
- return 1;
12725
- }
12726
- }
12727
- let site;
12728
- try {
12729
- site = await client.storefront.create({ templateId: template.templateId });
12730
- } catch (error) {
12731
- const code = codeOf(error);
12732
- if (code === "STOREFRONT_SITE_EXISTS") throw new CliError(cli_init_site_exists({ storeId }));
12733
- if (code === "STOREFRONT_SLUG_TAKEN") throw new CliError(cli_init_slug_taken());
12734
- throw error;
12735
- }
12736
- io.out(cli_init_created({ url: site.url }));
12737
- try {
12738
- return await receiveSource(client, auth, await client.storefront.getSource(), { dir: flags.dir }, io, authDir);
12739
- } catch (error) {
12740
- io.err(cli_init_pull_failed({ storeId }));
12741
- throw error;
12742
- }
12743
- }
12744
- /** `--template`이 있으면 그 템플릿, 없으면 대화형 선택. 비대화형이면 `--template`이 있어야 한다 */
12745
- async function pickTemplate(templates, wanted, io) {
12746
- if (templates.length === 0) throw new CliError(cli_init_no_templates());
12747
- const ids = templates.map((t) => t.templateId).join(", ");
12748
- if (wanted) {
12749
- const found = templates.find((t) => t.templateId === wanted);
12750
- if (!found) throw new CliError(cli_init_template_unknown({
12751
- templateId: wanted,
12752
- ids
12753
- }));
12754
- return found;
12755
- }
12756
- const labels = templates.map((t) => cli_init_template_option({
12757
- templateId: t.templateId,
12758
- name: t.name,
12759
- version: t.version,
12760
- description: t.description
12761
- }));
12762
- const picked = io.select ? await io.select(cli_init_template_prompt(), labels) : null;
12763
- const index = picked ? labels.indexOf(picked) : -1;
12764
- if (index < 0) throw new CliError(cli_init_template_required({ list: labels.map((label) => ` ${label}`).join("\n") }));
12765
- return templates[index];
12505
+ function init() {
12506
+ throw new CliError(cli_init_retired());
12766
12507
  }
12767
12508
  /**
12768
12509
  * `sayren link [폴더]`(#315 C6) — 폴더를 상점의 호스팅 사이트에 연결한다. 상점은 `--store` → 토큰 범위 → 대화형 선택이고, 사이트가 없으면
@@ -12774,7 +12515,11 @@ async function link(flags, io) {
12774
12515
  if (!existsSync(dir)) throw new CliError(cli_link_dir_missing({ dir: relativeOf(io, dir) }));
12775
12516
  migrateLegacy(dir, io.out);
12776
12517
  await linkDir(flags, dir, io);
12777
- io.out(cli_link_next_steps());
12518
+ const manager = packageManagerOfDir(dir);
12519
+ io.out(cli_link_next_steps({
12520
+ install: installCommand(manager),
12521
+ dev: devCommand(manager)
12522
+ }));
12778
12523
  return 0;
12779
12524
  }
12780
12525
  /** link 본체 — `deploy`도 연결이 없으면 이것부터 부른다 */
@@ -13023,7 +12768,7 @@ async function localBuild(dir, skip, io) {
13023
12768
  return;
13024
12769
  }
13025
12770
  if (plan.kind === "no-deps") {
13026
- io.out(cli_deploy_build_local_no_deps());
12771
+ io.out(cli_deploy_build_local_no_deps({ install: installCommand(packageManagerOfDir(dir)) }));
13027
12772
  return;
13028
12773
  }
13029
12774
  io.out(cli_deploy_build_local_running());
@@ -13372,9 +13117,10 @@ var StarterTransformError = class extends Error {
13372
13117
  this.code = code;
13373
13118
  }
13374
13119
  };
13375
- /** 새 프로젝트로 옮기지 않는 루트 경로인가 — 로컬 env·CLI 연결 정보 */
13120
+ /** 새 프로젝트로 옮기지 않는 루트 경로인가 — 로컬 env·CLI 연결 정보·원본 저장소의 CI */
13376
13121
  function isStarterLocalOnly(path) {
13377
13122
  if (path === ".sayren" || path.startsWith(".sayren/")) return true;
13123
+ if (path === ".github" || path.startsWith(".github/")) return true;
13378
13124
  if (path.includes("/")) return false;
13379
13125
  return (path === ".env" || path.startsWith(".env.")) && path !== ".env.example";
13380
13126
  }
@@ -13413,140 +13159,45 @@ function withPackageName(bytes, name, fields = {}) {
13413
13159
  return new TextEncoder().encode(`${JSON.stringify(next, null, 2)}\n`);
13414
13160
  }
13415
13161
 
13416
- //#endregion
13417
- //#region ../mcp/src/template-package.ts
13418
- /** package.json의 기준 템플릿 표식 필드 — 원천은 이 함수다(api `template-marker.ts`가 같은 모양을 읽는다) */
13419
- function withTemplateMarker(pkg, marker) {
13420
- const current = pkg.sayren;
13421
- pkg.sayren = {
13422
- ...current && typeof current === "object" && !Array.isArray(current) ? current : {},
13423
- template: marker.id,
13424
- templateVersion: marker.version
13425
- };
13426
- return pkg;
13427
- }
13428
- const DEP_FIELDS = [
13429
- "dependencies",
13430
- "devDependencies",
13431
- "peerDependencies"
13432
- ];
13433
- function renderTemplatePackageJson(raw, meta, options = {}) {
13434
- const pkg = JSON.parse(raw);
13435
- for (const field of DEP_FIELDS) {
13436
- const deps = pkg[field];
13437
- if (!deps) continue;
13438
- for (const [name, spec] of Object.entries(deps)) deps[name] = resolveSpec(name, spec, meta);
13439
- }
13440
- if (options.name) pkg.name = options.name;
13441
- const marker = options.template ?? meta.template;
13442
- if (marker) withTemplateMarker(pkg, marker);
13443
- return `${JSON.stringify(pkg, null, 2)}\n`;
13444
- }
13445
- function resolveSpec(name, spec, meta) {
13446
- if (spec === "catalog:" || spec === "catalog:default") {
13447
- const version$1 = meta.catalog[name];
13448
- if (!version$1) throw new Error(mcp_template_catalog_version_missing({ name }));
13449
- return version$1;
13450
- }
13451
- if (spec.startsWith("workspace:")) return workspaceRange(name, spec.slice(10), meta);
13452
- return spec;
13453
- }
13454
- /**
13455
- * `workspace:` 뒤 표기 → 셀러 프로젝트의 범위(#232). 템플릿은 SDK를 범위(`workspace:^0.35.0`)로 적고 `workspace:`만 뗀다 —
13456
- * SDK patch가 올라도 풀어 쓴 package.json·첫 작업 트리가 그대로라 템플릿 버전을 올리지 않는다. 범위가 아닌 `*`·`^`·`~`만
13457
- * 지금 워크스페이스 버전으로 푼다(`*`는 `^{버전}`)
13458
- */
13459
- function workspaceRange(name, rest, meta) {
13460
- if (rest !== "*" && rest !== "^" && rest !== "~" && rest !== "") return rest;
13461
- const version$1 = meta.versions[name];
13462
- if (!version$1) throw new Error(mcp_template_published_version_unknown({ name }));
13463
- return `${rest === "~" ? "~" : "^"}${version$1}`;
13464
- }
13465
- /** pnpm-workspace.yaml의 최상위 `catalog:` 블록만 읽는다(` key: value`, 따옴표 허용) */
13466
- function parseCatalog(yaml) {
13467
- const out = {};
13468
- let inCatalog = false;
13469
- for (const line of yaml.split("\n")) {
13470
- if (/^catalog:\s*$/.test(line)) {
13471
- inCatalog = true;
13472
- continue;
13473
- }
13474
- if (!inCatalog) continue;
13475
- if (/^\S/.test(line)) break;
13476
- const match = line.match(/^\s+["']?([^"':]+)["']?\s*:\s*["']?([^"'#\s]+)["']?/);
13477
- if (match?.[1] && match[2]) out[match[1].trim()] = match[2];
13478
- }
13479
- return out;
13480
- }
13481
- function readWorkspaceVersions(packagesDir) {
13482
- const out = {};
13483
- for (const entry of readdirSync(packagesDir, { withFileTypes: true })) {
13484
- if (!entry.isDirectory()) continue;
13485
- const file = join(packagesDir, entry.name, "package.json");
13486
- if (!existsSync(file)) continue;
13487
- const pkg = JSON.parse(readFileSync(file, "utf8"));
13488
- if (pkg.name && pkg.version) out[pkg.name] = pkg.version;
13489
- }
13490
- return out;
13491
- }
13492
-
13493
13162
  //#endregion
13494
13163
  //#region src/create-command.ts
13495
13164
  /**
13496
13165
  * `npx sayren create <이름> [--template <템플릿>]`(#271, #315 C7) — 시작 코드를 새 폴더에 쓴다. 파일만 만들고 상점을 묻지 않는다.
13497
13166
  * 상점 연결은 `sayren link`(사이트·`.sayren/project.json`)와 `sayren env pull`(`.env`)이 한다. `--store-code`를 주면 그 값으로 `.env`만 쓴다.
13498
13167
  *
13499
- * 템플릿: 없으면 CLI에 묶은 시작 코드(네트워크 없음), `github:owner/repo[/경로][#ref]`는 codeload tarball, 그 밖의 `<id>`는 sayren 레지스트리
13500
- * 자리다(마켓 전까지 늘 「템플릿을 찾을 수 없습니다」). 받은 소스는 변환 함수 하나(`starter-transform.ts`, 서버와 공유)를 지난다.
13168
+ * 템플릿: 없으면 시작 코드 공개 저장소(`avarcorp/sayren-starter`)의 최신 릴리스 태그, `github:owner/repo[/경로][#ref]`는 그 저장소다.
13169
+ * 둘 다 codeload tarball로 받는다(CLI에 시작 코드를 묶지 않는다 — 네트워크가 필요하다). 그 밖의 `<id>`는 sayren 레지스트리 자리다
13170
+ * (마켓 전까지 늘 「템플릿을 찾을 수 없습니다」).
13171
+ *
13172
+ * 받은 소스는 변환 함수 하나(`starter-transform.ts`, 서버와 공유)를 지난다. 변환(`package.json` `name`·`private`, `_gitignore` → `.gitignore`,
13173
+ * 로컬 전용 파일 빼기)은 **고정 계약이다**: 이미 설치된 옛 CLI도 늘 최신 시작 코드를 받으므로 시작 코드 저장소는 이 변환만 믿고 쓰고,
13174
+ * CLI는 변환을 바꾸거나 새 전제를 더하지 않는다. 안내 스킬(`.claude/skills/sayren/SKILL.md`)은 시작 코드 저장소에 두지 않고 CLI가 써 넣는다.
13501
13175
  */
13502
- /** 묶은 시작 코드 위치 — 게시본은 빌드가 묶은 `dist/boilerplate`, 저장소에서 바로 돌리면 원천 `packages/cli/boilerplate` */
13503
- function boilerplateRoot() {
13504
- const here = dirname(fileURLToPath(import.meta.url));
13505
- const candidates = [join(here, "boilerplate"), join(here, "..", "boilerplate")];
13506
- const found = candidates.find((dir) => existsSync(join(dir, "package.json")));
13507
- if (!found) throw new Error(`boilerplate not found: ${candidates.join(", ")}`);
13508
- return found;
13509
- }
13510
- /** 싣지 않는 것 — 설치물·산출물. 점 파일은 에이전트 안내(`.claude/`·`.mcp.json`)와 `.gitignore`만(scripts/starter-bundle.mjs와 같다) */
13511
- const SKIP_DIRS = new Set([
13512
- "node_modules",
13513
- "dist",
13514
- ".output",
13515
- ".tanstack",
13516
- ".nitro",
13517
- ".wrangler"
13518
- ]);
13519
- const DOT_KEPT = new Set([
13520
- ".claude",
13521
- ".mcp.json",
13522
- ".gitignore"
13523
- ]);
13176
+ /** 기본 시작 코드 — 공개 저장소. 릴리스(태그 v*)는 그 저장소 Actions의 검사를 통과해야 생긴다 */
13177
+ const STARTER_REPO = {
13178
+ owner: "avarcorp",
13179
+ repo: "sayren-starter"
13180
+ };
13524
13181
  /**
13525
- * 묶은 시작 코드 — 상대 경로 → 내용. 게시본은 빌드가 이미 npm 범위로 풀었다. 저장소 원천(워크스페이스 구성원)에서 바로 돌리면
13526
- * `catalog:`·`workspace:`를 여기서 푼다(게시본과 같은 결과).
13182
+ * 기본 시작 코드의 최신 릴리스 태그 — `releases/latest`가 302로 가리키는 `/releases/tag/{태그}`에서 읽는다(GitHub API rate limit을 쓰지 않는다).
13183
+ * 릴리스가 없으면 GitHub은 `/releases`로 보내거나 404다
13527
13184
  */
13528
- function bundledStarterFiles(root) {
13529
- const files = /* @__PURE__ */ new Map();
13530
- const walk = (dir) => {
13531
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
13532
- if (SKIP_DIRS.has(entry.name)) continue;
13533
- if (entry.name.startsWith(".") && !DOT_KEPT.has(entry.name)) continue;
13534
- const full = join(dir, entry.name);
13535
- if (entry.isDirectory()) walk(full);
13536
- else if (entry.isFile()) files.set(relative(root, full).split(sep).join("/"), readFileSync(full));
13537
- }
13538
- };
13539
- walk(root);
13540
- const repo = join(root, "..", "..", "..");
13541
- const pkg = files.get("package.json");
13542
- if (pkg && existsSync(join(repo, "pnpm-workspace.yaml"))) {
13543
- const meta = {
13544
- catalog: parseCatalog(readFileSync(join(repo, "pnpm-workspace.yaml"), "utf8")),
13545
- versions: readWorkspaceVersions(join(repo, "packages"))
13546
- };
13547
- files.set("package.json", Buffer.from(renderTemplatePackageJson(Buffer.from(pkg).toString("utf8"), meta)));
13185
+ async function latestStarterTag(io) {
13186
+ const url = `https://github.com/${STARTER_REPO.owner}/${STARTER_REPO.repo}/releases/latest`;
13187
+ let res;
13188
+ try {
13189
+ res = await io.fetch(url, {
13190
+ method: "HEAD",
13191
+ redirect: "manual"
13192
+ });
13193
+ } catch {
13194
+ throw new CliError(cli_create_starter_unreachable());
13548
13195
  }
13549
- return files;
13196
+ const location = res.status >= 300 && res.status < 400 ? res.headers.get("location") : null;
13197
+ const tag = location ? /\/releases\/tag\/([^/?#]+)$/.exec(new URL(location, url).pathname)?.[1] : void 0;
13198
+ const decoded = tag ? decodeURIComponent(tag) : "";
13199
+ if (!GIT_REF.test(decoded) || decoded.includes("..")) throw new CliError(cli_create_starter_no_release({ status: String(res.status) }));
13200
+ return decoded;
13550
13201
  }
13551
13202
  const GITHUB_OWNER = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/;
13552
13203
  const GITHUB_REPO = /^[A-Za-z0-9._-]{1,100}$/;
@@ -13555,7 +13206,7 @@ const GIT_REF = /^[A-Za-z0-9._/+-]{1,255}$/;
13555
13206
  /** `--template` 값 → 받을 곳. 형식이 틀리면 api·네트워크를 부르기 전에 멈춘다 */
13556
13207
  function parseTemplateSpec(value) {
13557
13208
  const raw = value?.trim();
13558
- if (!raw) return { kind: "bundled" };
13209
+ if (!raw) return { kind: "starter" };
13559
13210
  if (!raw.startsWith("github:")) return {
13560
13211
  kind: "registry",
13561
13212
  id: raw
@@ -13627,7 +13278,17 @@ async function create(flags, io) {
13627
13278
  if (storeCode !== void 0 && !STORE_CODE_PATTERN.test(storeCode)) throw new CliError(cli_create_store_code_invalid({ storeCode }));
13628
13279
  const spec = parseTemplateSpec(flags.template);
13629
13280
  if (spec.kind === "registry") throw new CliError(cli_create_template_not_found({ template: spec.id }));
13630
- const source = spec.kind === "github" ? await githubTemplateFiles(spec, flags.template?.trim() ?? "", io) : bundledStarterFiles(boilerplateRoot());
13281
+ let source;
13282
+ if (spec.kind === "github") source = await githubTemplateFiles(spec, flags.template?.trim() ?? "", io);
13283
+ else {
13284
+ const ref = await latestStarterTag(io);
13285
+ source = await githubTemplateFiles({
13286
+ kind: "github",
13287
+ ...STARTER_REPO,
13288
+ path: "",
13289
+ ref
13290
+ }, `github:${STARTER_REPO.owner}/${STARTER_REPO.repo}#${ref}`, io);
13291
+ }
13631
13292
  let files;
13632
13293
  try {
13633
13294
  files = transformStarterFiles(source, { name });
@@ -13635,7 +13296,13 @@ async function create(flags, io) {
13635
13296
  if (error instanceof StarterTransformError) throw new CliError(cli_create_template_no_package_json({ reason: error.message }));
13636
13297
  throw error;
13637
13298
  }
13299
+ files.set(GUIDE_SKILL_PATH, new TextEncoder().encode(GUIDE_SKILL));
13638
13300
  writeTree(target, files);
13301
+ const manager = packageManagerOfFiles(files);
13302
+ const steps = {
13303
+ install: installCommand(manager),
13304
+ dev: devCommand(manager)
13305
+ };
13639
13306
  if (storeCode) {
13640
13307
  const { apiOrigin } = resolveApiOrigin(flags, target, null, io.env);
13641
13308
  updateEnvFile(target, {
@@ -13645,11 +13312,13 @@ async function create(flags, io) {
13645
13312
  io.out(cli_create_done_store_code({
13646
13313
  dir: name,
13647
13314
  count: files.size,
13648
- storeCode
13315
+ storeCode,
13316
+ ...steps
13649
13317
  }));
13650
13318
  } else io.out(cli_create_done({
13651
13319
  dir: name,
13652
- count: files.size
13320
+ count: files.size,
13321
+ ...steps
13653
13322
  }));
13654
13323
  return 0;
13655
13324
  }
@@ -13808,11 +13477,7 @@ async function main(argv, io) {
13808
13477
  ...common,
13809
13478
  dir: str(parsed.flags.dir) ?? parsed.positional[1]
13810
13479
  }, io);
13811
- case "init": return init({
13812
- ...common,
13813
- template: str(parsed.flags.template),
13814
- yes: parsed.flags.yes === true
13815
- }, io);
13480
+ case "init": return init();
13816
13481
  case "pull": return pull({
13817
13482
  ...common,
13818
13483
  project: str(parsed.flags.project),