pai-zero 0.11.4 → 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.
package/dist/bin/pai.js CHANGED
@@ -441,6 +441,61 @@ var init_analyzer = __esm({
441
441
  }
442
442
  });
443
443
 
444
+ // src/core/recipes.ts
445
+ var recipes_exports = {};
446
+ __export(recipes_exports, {
447
+ RECIPES: () => RECIPES,
448
+ getRecipe: () => getRecipe,
449
+ listRecipeKeys: () => listRecipeKeys
450
+ });
451
+ function listRecipeKeys() {
452
+ return Object.keys(RECIPES);
453
+ }
454
+ function getRecipe(key) {
455
+ return RECIPES[key.toLowerCase()];
456
+ }
457
+ var RECIPES;
458
+ var init_recipes = __esm({
459
+ "src/core/recipes.ts"() {
460
+ "use strict";
461
+ RECIPES = {
462
+ oauth: {
463
+ label: "\uC0AC\uB0B4 OAuth2 \uC778\uC99D",
464
+ description: "\uC0AC\uB0B4 SSO / \uC778\uC99D \uD3EC\uD138 \uC5F0\uACB0 \uAC00\uC774\uB4DC + \uC0D8\uD50C \uCF54\uB4DC",
465
+ source: {
466
+ repo: "SoInKyu/setup-repo",
467
+ ref: "main",
468
+ path: "repo/oauth"
469
+ },
470
+ target: "docs/recipes/oauth",
471
+ envKeys: [
472
+ { key: "OAUTH_HOST", hint: "\uC608: auth.mycompany.com" },
473
+ { key: "OAUTH_CLIENT_ID", hint: "\uC778\uC99D \uD3EC\uD138\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 Client ID" },
474
+ { key: "OAUTH_CLIENT_SECRET", hint: "\uC778\uC99D \uD3EC\uD138\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 Client Secret" },
475
+ { key: "OAUTH_REDIRECT_URI", hint: "\uC608: http://localhost:3000/auth/callback", default: "http://localhost:3000/auth/callback" }
476
+ ],
477
+ skillDescription: "\uC0AC\uB0B4 OAuth2 \uC778\uC99D / SSO / \uB85C\uADF8\uC778 \uAD6C\uD604 \uC2DC \uC774 \uB808\uC2DC\uD53C \uBB38\uC11C\uB97C \uC6B0\uC120 \uCC38\uC870"
478
+ },
479
+ officechat: {
480
+ label: "\uBA54\uC2E0\uC800 \uCC57\uBD07 (Office Chat)",
481
+ description: "\uC0AC\uB0B4 \uBA54\uC2E0\uC800 \uCC57\uBD07 \uC5F0\uACB0 \uAC00\uC774\uB4DC + \uC0D8\uD50C \uCF54\uB4DC",
482
+ source: {
483
+ repo: "SoInKyu/setup-repo",
484
+ ref: "main",
485
+ path: "repo/officechat"
486
+ },
487
+ target: "docs/recipes/officechat",
488
+ envKeys: [
489
+ { key: "OFFICECHAT_HOST", hint: "\uC0AC\uB0B4 \uBA54\uC2E0\uC800 \uC11C\uBC84 \uC8FC\uC18C" },
490
+ { key: "OFFICECHAT_TOKEN", hint: "\uBD07 \uD1A0\uD070" },
491
+ { key: "OFFICECHAT_CHANNEL", hint: "\uAE30\uBCF8 \uCC44\uB110 ID" }
492
+ ],
493
+ skillDescription: "\uBA54\uC2E0\uC800 \uCC57\uBD07 / \uC54C\uB9BC \uBD07 / Office Chat \uC5F0\uB3D9 \uAD6C\uD604 \uC2DC \uC774 \uB808\uC2DC\uD53C \uBB38\uC11C\uB97C \uC6B0\uC120 \uCC38\uC870"
494
+ }
495
+ };
496
+ }
497
+ });
498
+
444
499
  // src/stages/environment/interviewer.ts
445
500
  async function runInterview(analysis, cwd, projectName) {
446
501
  try {
@@ -531,7 +586,7 @@ function parseAiResult(result, analysis, projectName) {
531
586
  }
532
587
  async function interactiveInterview(analysis, _cwd, projectName) {
533
588
  const { default: inquirer } = await import("inquirer");
534
- const chalk8 = (await import("chalk")).default;
589
+ const chalk9 = (await import("chalk")).default;
535
590
  step(1, 3, "\uD504\uB85C\uC81D\uD2B8 \uC218\uC900\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694");
536
591
  hint("\uC120\uD0DD\uC5D0 \uB530\uB77C \uC124\uCE58\uB418\uB294 \uD50C\uB7EC\uADF8\uC778 \uC138\uD2B8\uAC00 \uB2EC\uB77C\uC9D1\uB2C8\uB2E4.");
537
592
  console.log("");
@@ -593,7 +648,7 @@ async function interactiveInterview(analysis, _cwd, projectName) {
593
648
  { name: "Naver \uB85C\uADF8\uC778", value: "naver" },
594
649
  { name: `\uC0AC\uB0B4 \uC778\uC99D ${colors.dim("(OAuth2)")}`, value: "custom" },
595
650
  new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
596
- { name: chalk8.green("\u21B5 \uC120\uD0DD \uC644\uB8CC"), value: "__done__" }
651
+ { name: chalk9.green("\u21B5 \uC120\uD0DD \uC644\uB8CC"), value: "__done__" }
597
652
  ],
598
653
  validate: (answer) => {
599
654
  const items = answer.filter((a) => a !== "__done__");
@@ -705,6 +760,28 @@ async function interactiveInterview(analysis, _cwd, projectName) {
705
760
  };
706
761
  extraTools.push("mcp");
707
762
  }
763
+ const { RECIPES: RECIPES2 } = await Promise.resolve().then(() => (init_recipes(), recipes_exports));
764
+ const recipeKeys = Object.keys(RECIPES2);
765
+ let selectedRecipes = [];
766
+ if (recipeKeys.length > 0) {
767
+ console.log("");
768
+ hint("Space\uB85C \uC120\uD0DD \xB7 \uC120\uD0DD \uD6C4 Enter\uB85C \uC644\uB8CC (\uAC74\uB108\uB6F0\uB824\uBA74 \uBC14\uB85C Enter)");
769
+ const Separator2 = inquirer.Separator;
770
+ const recipeAnswer = await inquirer.prompt([{
771
+ type: "checkbox",
772
+ name: "recipes",
773
+ message: "\uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9 \uB808\uC2DC\uD53C\uB97C \uC124\uCE58\uD560\uAE4C\uC694?",
774
+ choices: [
775
+ ...recipeKeys.map((k) => ({
776
+ name: `${RECIPES2[k].label} ${colors.dim("\u2500 " + RECIPES2[k].description)}`,
777
+ value: k
778
+ })),
779
+ new Separator2("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
780
+ { name: chalk9.green("\u21B5 \uC120\uD0DD \uC644\uB8CC (\uAC74\uB108\uB6F0\uAE30)"), value: "__done__" }
781
+ ]
782
+ }]);
783
+ selectedRecipes = recipeAnswer.recipes.filter((x) => x !== "__done__");
784
+ }
708
785
  step(3, 3, "\uC124\uCE58 \uD655\uC778");
709
786
  console.log("");
710
787
  console.log(colors.dim(" \uC124\uCE58\uB420 \uD56D\uBAA9"));
@@ -729,6 +806,10 @@ async function interactiveInterview(analysis, _cwd, projectName) {
729
806
  console.log("");
730
807
  console.log(` ${colors.accent("+")} \uB85C\uADF8\uC778: ${authMethods.join(", ")}`);
731
808
  }
809
+ if (selectedRecipes.length > 0) {
810
+ console.log("");
811
+ console.log(` ${colors.accent("+")} \uC0AC\uB0B4 \uC5F0\uB3D9 \uB808\uC2DC\uD53C: ${selectedRecipes.join(", ")}`);
812
+ }
732
813
  console.log("");
733
814
  const { proceed } = await inquirer.prompt([{
734
815
  type: "confirm",
@@ -749,6 +830,7 @@ async function interactiveInterview(analysis, _cwd, projectName) {
749
830
  authMethods,
750
831
  customAuth,
751
832
  extraTools,
833
+ recipes: selectedRecipes,
752
834
  mcp,
753
835
  setupDomains: {
754
836
  claudeEnv: true,
@@ -3263,6 +3345,308 @@ var init_doctor = __esm({
3263
3345
  }
3264
3346
  });
3265
3347
 
3348
+ // src/utils/github-fetch.ts
3349
+ import path4 from "path";
3350
+ import fs10 from "fs-extra";
3351
+ async function httpGet(url, timeoutMs, accept) {
3352
+ const controller = new AbortController();
3353
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
3354
+ try {
3355
+ return await fetch(url, {
3356
+ signal: controller.signal,
3357
+ headers: { "Accept": accept, "User-Agent": "pai-zero" }
3358
+ });
3359
+ } finally {
3360
+ clearTimeout(timer);
3361
+ }
3362
+ }
3363
+ async function listDir(repo, ref, dirPath, timeoutMs) {
3364
+ const url = `https://api.github.com/repos/${repo}/contents/${encodeURI(dirPath)}?ref=${encodeURIComponent(ref)}`;
3365
+ const res = await httpGet(url, timeoutMs, "application/vnd.github+json");
3366
+ if (!res.ok) {
3367
+ throw new Error(`GitHub API ${res.status} ${res.statusText} (${url})`);
3368
+ }
3369
+ const data = await res.json();
3370
+ if (!Array.isArray(data)) {
3371
+ throw new Error(`Expected directory, got single entry at ${dirPath}`);
3372
+ }
3373
+ return data;
3374
+ }
3375
+ async function downloadFile(downloadUrl, destPath, timeoutMs) {
3376
+ const res = await httpGet(downloadUrl, timeoutMs, "*/*");
3377
+ if (!res.ok) {
3378
+ throw new Error(`Download failed ${res.status} ${res.statusText} (${downloadUrl})`);
3379
+ }
3380
+ const buf = Buffer.from(await res.arrayBuffer());
3381
+ await fs10.ensureDir(path4.dirname(destPath));
3382
+ await fs10.writeFile(destPath, buf);
3383
+ }
3384
+ async function fetchGithubDir(opts) {
3385
+ const {
3386
+ repo,
3387
+ ref = "main",
3388
+ srcPath,
3389
+ destDir,
3390
+ overwrite = false,
3391
+ timeoutMs = 1e4
3392
+ } = opts;
3393
+ const result = { written: [], skipped: [], errors: [] };
3394
+ async function walk(currentSrc, currentDest) {
3395
+ let entries;
3396
+ try {
3397
+ entries = await listDir(repo, ref, currentSrc, timeoutMs);
3398
+ } catch (err) {
3399
+ const msg = err instanceof Error ? err.message : String(err);
3400
+ result.errors.push({ path: currentSrc, error: msg });
3401
+ return;
3402
+ }
3403
+ for (const entry of entries) {
3404
+ const relName = entry.name;
3405
+ if (entry.type === "dir") {
3406
+ await walk(`${currentSrc}/${relName}`, path4.join(currentDest, relName));
3407
+ } else if (entry.type === "file") {
3408
+ const destFile = path4.join(currentDest, relName);
3409
+ if (!overwrite && await fs10.pathExists(destFile)) {
3410
+ result.skipped.push(destFile);
3411
+ continue;
3412
+ }
3413
+ if (!entry.download_url) {
3414
+ result.errors.push({ path: entry.path, error: "no download_url" });
3415
+ continue;
3416
+ }
3417
+ try {
3418
+ await downloadFile(entry.download_url, destFile, timeoutMs);
3419
+ result.written.push(destFile);
3420
+ } catch (err) {
3421
+ const msg = err instanceof Error ? err.message : String(err);
3422
+ result.errors.push({ path: entry.path, error: msg });
3423
+ }
3424
+ }
3425
+ }
3426
+ }
3427
+ await walk(srcPath, destDir);
3428
+ return result;
3429
+ }
3430
+ var init_github_fetch = __esm({
3431
+ "src/utils/github-fetch.ts"() {
3432
+ "use strict";
3433
+ }
3434
+ });
3435
+
3436
+ // src/cli/commands/fetch.cmd.ts
3437
+ var fetch_cmd_exports = {};
3438
+ __export(fetch_cmd_exports, {
3439
+ fetchCommand: () => fetchCommand
3440
+ });
3441
+ import path5 from "path";
3442
+ import fs11 from "fs-extra";
3443
+ import chalk4 from "chalk";
3444
+ async function fetchCommand(cwd, recipeKey, options) {
3445
+ if (options.list) {
3446
+ section("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uB808\uC2DC\uD53C");
3447
+ for (const key of listRecipeKeys()) {
3448
+ const r = RECIPES[key];
3449
+ console.log(` ${colors.accent(key.padEnd(14))} ${r.label}`);
3450
+ console.log(` ${colors.dim(" " + r.description)}`);
3451
+ console.log(` ${colors.dim(" \uC18C\uC2A4: github.com/" + r.source.repo + "/tree/" + r.source.ref + "/" + r.source.path)}`);
3452
+ console.log("");
3453
+ }
3454
+ hint("\uC0AC\uC6A9: pai fetch <key> \uB610\uB294 pai fetch --all");
3455
+ return;
3456
+ }
3457
+ let keys;
3458
+ if (options.all) {
3459
+ keys = listRecipeKeys();
3460
+ } else {
3461
+ if (!recipeKey) {
3462
+ error("\uB808\uC2DC\uD53C \uD0A4\uB97C \uC9C0\uC815\uD558\uC138\uC694.");
3463
+ hint(`\uC0AC\uC6A9 \uAC00\uB2A5: ${listRecipeKeys().join(", ")}`);
3464
+ hint("\uBAA9\uB85D: pai fetch --list");
3465
+ process.exitCode = 1;
3466
+ return;
3467
+ }
3468
+ if (!getRecipe(recipeKey)) {
3469
+ error(`\uC54C \uC218 \uC5C6\uB294 \uB808\uC2DC\uD53C: ${recipeKey}`);
3470
+ hint(`\uC0AC\uC6A9 \uAC00\uB2A5: ${listRecipeKeys().join(", ")}`);
3471
+ process.exitCode = 1;
3472
+ return;
3473
+ }
3474
+ keys = [recipeKey.toLowerCase()];
3475
+ }
3476
+ section("\uB808\uC2DC\uD53C \uB2E4\uC6B4\uB85C\uB4DC");
3477
+ const installed = [];
3478
+ for (const key of keys) {
3479
+ const recipe = RECIPES[key];
3480
+ const targetDir = path5.join(cwd, recipe.target);
3481
+ console.log("");
3482
+ console.log(` ${colors.accent(key)} \u2014 ${recipe.label}`);
3483
+ const result = await withSpinner(`\uB2E4\uC6B4\uB85C\uB4DC \uC911...`, async () => {
3484
+ return fetchGithubDir({
3485
+ repo: recipe.source.repo,
3486
+ ref: recipe.source.ref,
3487
+ srcPath: recipe.source.path,
3488
+ destDir: targetDir,
3489
+ overwrite: options.overwrite ?? false
3490
+ });
3491
+ });
3492
+ if (result.errors.length > 0) {
3493
+ error(`\uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328: ${result.errors[0].error}`);
3494
+ for (const e of result.errors.slice(1)) {
3495
+ console.log(chalk4.gray(` ${e.path}: ${e.error}`));
3496
+ }
3497
+ continue;
3498
+ }
3499
+ if (result.written.length > 0) {
3500
+ success(`${result.written.length}\uAC1C \uD30C\uC77C \uC800\uC7A5`);
3501
+ for (const f of result.written) {
3502
+ console.log(chalk4.gray(` ${path5.relative(cwd, f)}`));
3503
+ }
3504
+ }
3505
+ if (result.skipped.length > 0) {
3506
+ info(`${result.skipped.length}\uAC1C \uD30C\uC77C \uAC74\uB108\uB700 (\uC774\uBBF8 \uC874\uC7AC \u2014 \uB36E\uC5B4\uC4F0\uAE30: --overwrite)`);
3507
+ }
3508
+ await appendEnvKeys(cwd, recipe);
3509
+ installed.push(key);
3510
+ }
3511
+ if (installed.length === 0) {
3512
+ return;
3513
+ }
3514
+ await upsertRecipesSkill(cwd, installed);
3515
+ await upsertClaudeMdBlock(cwd, installed);
3516
+ console.log("");
3517
+ success(`\uB808\uC2DC\uD53C ${installed.length}\uAC1C \uC124\uCE58 \uC644\uB8CC: ${installed.join(", ")}`);
3518
+ console.log("");
3519
+ console.log(colors.accent(" \uB2E4\uC74C \uB2E8\uACC4"));
3520
+ console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
3521
+ console.log(` ${colors.success("1.")} ${colors.dim(".env.local \uD30C\uC77C\uC744 \uC5F4\uC5B4 \uD658\uACBD\uBCC0\uC218 \uAC12\uC744 \uCC44\uC6B0\uC138\uC694")}`);
3522
+ for (const key of installed) {
3523
+ const recipe = RECIPES[key];
3524
+ for (const ek of recipe.envKeys) {
3525
+ console.log(` ${chalk4.cyan(ek.key.padEnd(22))} ${colors.dim(ek.hint ?? "")}`);
3526
+ }
3527
+ }
3528
+ console.log("");
3529
+ console.log(` ${colors.success("2.")} ${colors.dim("Claude Code\uC5D0\uC11C \uAD00\uB828 \uAE30\uB2A5 \uAD6C\uD604 \uC2DC \uC790\uB3D9\uC73C\uB85C \uB808\uC2DC\uD53C \uBB38\uC11C\uB97C \uCC38\uC870\uD569\uB2C8\uB2E4")}`);
3530
+ for (const key of installed) {
3531
+ const recipe = RECIPES[key];
3532
+ console.log(` ${chalk4.cyan(recipe.target + "/")}`);
3533
+ }
3534
+ console.log("");
3535
+ }
3536
+ async function appendEnvKeys(cwd, recipe) {
3537
+ if (recipe.envKeys.length === 0) return;
3538
+ const envPath = path5.join(cwd, ".env.local");
3539
+ let content = "";
3540
+ if (await fs11.pathExists(envPath)) {
3541
+ content = await fs11.readFile(envPath, "utf8");
3542
+ }
3543
+ const missingKeys = recipe.envKeys.filter((ek) => !content.includes(`${ek.key}=`));
3544
+ if (missingKeys.length === 0) return;
3545
+ const lines = [];
3546
+ if (content.length > 0 && !content.endsWith("\n")) lines.push("");
3547
+ lines.push("");
3548
+ lines.push(`# \u2500\u2500 ${recipe.label} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
3549
+ lines.push(`# \uAC00\uC774\uB4DC: ${recipe.target}/guideline.md`);
3550
+ for (const ek of missingKeys) {
3551
+ if (ek.hint) lines.push(`# ${ek.hint}`);
3552
+ lines.push(`${ek.key}=${ek.default ?? ""}`);
3553
+ }
3554
+ await fs11.ensureFile(envPath);
3555
+ await fs11.appendFile(envPath, lines.join("\n") + "\n");
3556
+ }
3557
+ async function upsertRecipesSkill(cwd, installedKeys) {
3558
+ const skillDir = path5.join(cwd, ".claude", "skills", "recipes");
3559
+ await fs11.ensureDir(skillDir);
3560
+ const skillPath = path5.join(skillDir, "SKILL.md");
3561
+ const recipes = installedKeys.map((k) => RECIPES[k]);
3562
+ const triggers = recipes.map((r) => r.skillDescription ?? `${r.label} \uAD00\uB828 \uAE30\uB2A5 \uAD6C\uD604 \uC2DC ${r.target}/ \uCC38\uC870`).join("\n- ");
3563
+ const body = [
3564
+ "---",
3565
+ "name: recipes",
3566
+ `description: "\uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9 \uB808\uC2DC\uD53C \u2014 ${recipes.map((r) => r.label).join(", ")}"`,
3567
+ "---",
3568
+ "",
3569
+ "# \uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9 \uB808\uC2DC\uD53C",
3570
+ "",
3571
+ "\uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0\uB294 \uB2E4\uC74C \uC0AC\uB0B4 \uC5F0\uB3D9 \uB808\uC2DC\uD53C\uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.",
3572
+ "\uAD00\uB828 \uC694\uCCAD\uC744 \uBC1B\uC73C\uBA74 \uD574\uB2F9 \uACBD\uB85C\uC758 `guideline.md`\uB97C **\uBC18\uB4DC\uC2DC \uBA3C\uC800 \uC77D\uACE0**",
3573
+ "\uC9C0\uCE68\uC5D0 \uB9DE\uAC8C \uAD6C\uD604\uD558\uC138\uC694.",
3574
+ "",
3575
+ "## \uD2B8\uB9AC\uAC70",
3576
+ "",
3577
+ "- " + triggers,
3578
+ "",
3579
+ "## \uC124\uCE58\uB41C \uB808\uC2DC\uD53C",
3580
+ "",
3581
+ ...recipes.map((r) => [
3582
+ `### ${r.label}`,
3583
+ "",
3584
+ `- \uACBD\uB85C: \`${r.target}/\``,
3585
+ `- \uC8FC\uC694 \uBB38\uC11C: \`${r.target}/guideline.md\``,
3586
+ `- \uD658\uACBD\uBCC0\uC218: ${r.envKeys.map((e) => "`" + e.key + "`").join(", ")}`,
3587
+ ""
3588
+ ].join("\n")),
3589
+ "",
3590
+ "## \uC791\uC5C5 \uC21C\uC11C",
3591
+ "",
3592
+ "1. \uC0AC\uC6A9\uC790\uC758 \uC694\uCCAD\uC774 \uC704 \uD2B8\uB9AC\uAC70 \uC911 \uD558\uB098\uC5D0 \uD574\uB2F9\uD558\uB294\uC9C0 \uD310\uB2E8",
3593
+ "2. \uD574\uB2F9 \uB808\uC2DC\uD53C\uC758 `guideline.md`\uB97C Read \uB3C4\uAD6C\uB85C \uC77D\uAE30",
3594
+ "3. \uC0D8\uD50C \uCF54\uB4DC(`*.html`, `*.ts` \uB4F1)\uAC00 \uC788\uC73C\uBA74 \uD568\uAED8 \uD655\uC778",
3595
+ "4. `.env.local`\uC5D0 \uAD00\uB828 \uD658\uACBD\uBCC0\uC218\uAC00 \uCC44\uC6CC\uC838 \uC788\uB294\uC9C0 \uD655\uC778",
3596
+ "5. \uAC00\uC774\uB4DC \uC21C\uC11C\uC5D0 \uB530\uB77C \uAD6C\uD604 \u2014 \uB808\uC2DC\uD53C \uADDC\uCE59\uC744 \uC808\uB300 \uC6B0\uD68C\uD558\uC9C0 \uB9D0 \uAC83",
3597
+ ""
3598
+ ].join("\n");
3599
+ await fs11.writeFile(skillPath, body);
3600
+ }
3601
+ async function upsertClaudeMdBlock(cwd, installedKeys) {
3602
+ const claudeMdPath = path5.join(cwd, "CLAUDE.md");
3603
+ const BLOCK_START = "<!-- pai:recipes:start -->";
3604
+ const BLOCK_END = "<!-- pai:recipes:end -->";
3605
+ const lines = [];
3606
+ lines.push(BLOCK_START);
3607
+ lines.push("## \uC0AC\uB0B4 \uC2DC\uC2A4\uD15C \uC5F0\uB3D9");
3608
+ lines.push("");
3609
+ lines.push("\uB2E4\uC74C \uB808\uC2DC\uD53C\uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. \uAD00\uB828 \uAE30\uB2A5 \uAD6C\uD604 \uC2DC **\uBC18\uB4DC\uC2DC** \uD574\uB2F9 \uACBD\uB85C\uC758");
3610
+ lines.push("`guideline.md`\uB97C \uBA3C\uC800 \uC77D\uACE0 \uC9C0\uCE68\uC744 \uB530\uB974\uC138\uC694:");
3611
+ lines.push("");
3612
+ for (const key of installedKeys) {
3613
+ const r = RECIPES[key];
3614
+ lines.push(`- **${r.label}** \u2014 \`${r.target}/\``);
3615
+ lines.push(` \uD658\uACBD\uBCC0\uC218: ${r.envKeys.map((e) => "`" + e.key + "`").join(", ")}`);
3616
+ }
3617
+ lines.push("");
3618
+ lines.push("\uD658\uACBD\uBCC0\uC218 \uAC12\uC740 `.env.local`\uC5D0 \uCC44\uC6CC\uC838 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4.");
3619
+ lines.push(BLOCK_END);
3620
+ const block = lines.join("\n");
3621
+ let content = "";
3622
+ if (await fs11.pathExists(claudeMdPath)) {
3623
+ content = await fs11.readFile(claudeMdPath, "utf8");
3624
+ }
3625
+ const startIdx = content.indexOf(BLOCK_START);
3626
+ const endIdx = content.indexOf(BLOCK_END);
3627
+ if (startIdx >= 0 && endIdx > startIdx) {
3628
+ const before = content.slice(0, startIdx);
3629
+ const after = content.slice(endIdx + BLOCK_END.length);
3630
+ content = before + block + after;
3631
+ } else {
3632
+ if (content.length > 0 && !content.endsWith("\n")) content += "\n";
3633
+ if (content.length > 0) content += "\n";
3634
+ content += block + "\n";
3635
+ }
3636
+ await fs11.ensureFile(claudeMdPath);
3637
+ await fs11.writeFile(claudeMdPath, content);
3638
+ }
3639
+ var init_fetch_cmd = __esm({
3640
+ "src/cli/commands/fetch.cmd.ts"() {
3641
+ "use strict";
3642
+ init_ui();
3643
+ init_logger();
3644
+ init_recipes();
3645
+ init_github_fetch();
3646
+ init_progress();
3647
+ }
3648
+ });
3649
+
3266
3650
  // src/stages/environment/index.ts
3267
3651
  var environmentStage;
3268
3652
  var init_environment = __esm({
@@ -3378,6 +3762,19 @@ var init_environment = __esm({
3378
3762
  console.log("");
3379
3763
  const installResults = await installTools(interview.tools, input.cwd);
3380
3764
  printInstallReport(installResults, interview.tools);
3765
+ if (interview.recipes && interview.recipes.length > 0) {
3766
+ console.log("");
3767
+ const { fetchCommand: fetchCommand2 } = await Promise.resolve().then(() => (init_fetch_cmd(), fetch_cmd_exports));
3768
+ for (const recipeKey of interview.recipes) {
3769
+ try {
3770
+ await fetchCommand2(input.cwd, recipeKey, {});
3771
+ } catch (err) {
3772
+ const msg = err instanceof Error ? err.message : String(err);
3773
+ warn(`\uB808\uC2DC\uD53C '${recipeKey}' \uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328 \u2014 ${msg}`);
3774
+ hint(`\uB098\uC911\uC5D0 \uC218\uB3D9: pai fetch ${recipeKey}`);
3775
+ }
3776
+ }
3777
+ }
3381
3778
  console.log("");
3382
3779
  await withSpinner("\uC124\uC815 \uC800\uC7A5 \uC911...", async () => {
3383
3780
  const config = createDefaultConfig(interview.projectName, interview.mode);
@@ -3422,8 +3819,8 @@ __export(detector_exports, {
3422
3819
  pluginsAddedByPromotion: () => pluginsAddedByPromotion,
3423
3820
  scanProjectState: () => scanProjectState
3424
3821
  });
3425
- import path4 from "path";
3426
- import fs10 from "fs-extra";
3822
+ import path6 from "path";
3823
+ import fs12 from "fs-extra";
3427
3824
  async function scanProjectState(cwd) {
3428
3825
  const result = {
3429
3826
  isNewProject: true,
@@ -3433,11 +3830,11 @@ async function scanProjectState(cwd) {
3433
3830
  missingPlugins: [],
3434
3831
  details: {}
3435
3832
  };
3436
- const paiConfigPath = path4.join(cwd, ".pai", "config.json");
3437
- if (await fs10.pathExists(paiConfigPath)) {
3833
+ const paiConfigPath = path6.join(cwd, ".pai", "config.json");
3834
+ if (await fs12.pathExists(paiConfigPath)) {
3438
3835
  result.hasPaiConfig = true;
3439
3836
  try {
3440
- const config = await fs10.readJson(paiConfigPath);
3837
+ const config = await fs12.readJson(paiConfigPath);
3441
3838
  result.projectMode = config.mode != null ? normalizeMode(config.mode) : null;
3442
3839
  } catch {
3443
3840
  }
@@ -3445,7 +3842,7 @@ async function scanProjectState(cwd) {
3445
3842
  for (const [key, signatures] of Object.entries(PLUGIN_SIGNATURES)) {
3446
3843
  const installed = await Promise.any(
3447
3844
  signatures.map(async (sig) => {
3448
- if (await fs10.pathExists(path4.join(cwd, sig))) return true;
3845
+ if (await fs12.pathExists(path6.join(cwd, sig))) return true;
3449
3846
  throw new Error("not found");
3450
3847
  })
3451
3848
  ).catch(() => false);
@@ -3456,7 +3853,7 @@ async function scanProjectState(cwd) {
3456
3853
  result.missingPlugins.push(key);
3457
3854
  }
3458
3855
  }
3459
- const hasAnyContent = result.installedPlugins.length > 0 || await fs10.pathExists(path4.join(cwd, "package.json")) || await fs10.pathExists(path4.join(cwd, "src")) || await fs10.pathExists(path4.join(cwd, "README.md"));
3856
+ const hasAnyContent = result.installedPlugins.length > 0 || await fs12.pathExists(path6.join(cwd, "package.json")) || await fs12.pathExists(path6.join(cwd, "src")) || await fs12.pathExists(path6.join(cwd, "README.md"));
3460
3857
  result.isNewProject = !hasAnyContent;
3461
3858
  return result;
3462
3859
  }
@@ -3603,7 +4000,7 @@ __export(analyzer_exports2, {
3603
4000
  analyzeRepository: () => analyzeRepository
3604
4001
  });
3605
4002
  import { join as join7 } from "path";
3606
- import fs11 from "fs-extra";
4003
+ import fs13 from "fs-extra";
3607
4004
  async function analyzeRepository(repoPath) {
3608
4005
  try {
3609
4006
  return await aiAnalysis(repoPath);
@@ -3664,14 +4061,14 @@ async function checkTestCoverage(repoPath) {
3664
4061
  ".nycrc"
3665
4062
  ];
3666
4063
  for (const f of testConfigs) {
3667
- const found = await fs11.pathExists(join7(repoPath, f));
4064
+ const found = await fs13.pathExists(join7(repoPath, f));
3668
4065
  findings.push({ item: f, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
3669
4066
  if (found) score += 20;
3670
4067
  }
3671
4068
  const testDirs = ["tests", "test", "__tests__", "spec"];
3672
4069
  let hasTestDir = false;
3673
4070
  for (const d of testDirs) {
3674
- if (await fs11.pathExists(join7(repoPath, d))) {
4071
+ if (await fs13.pathExists(join7(repoPath, d))) {
3675
4072
  findings.push({ item: d, found: true, details: "\uD14C\uC2A4\uD2B8 \uB514\uB809\uD1A0\uB9AC \uC874\uC7AC" });
3676
4073
  hasTestDir = true;
3677
4074
  score += 30;
@@ -3694,8 +4091,8 @@ async function checkCiCd(repoPath) {
3694
4091
  { path: "Jenkinsfile", label: "Jenkins" },
3695
4092
  { path: ".circleci", label: "CircleCI" }
3696
4093
  ];
3697
- for (const { path: path6, label } of ciConfigs) {
3698
- const found = await fs11.pathExists(join7(repoPath, path6));
4094
+ for (const { path: path8, label } of ciConfigs) {
4095
+ const found = await fs13.pathExists(join7(repoPath, path8));
3699
4096
  findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
3700
4097
  if (found) score += 40;
3701
4098
  }
@@ -3713,8 +4110,8 @@ async function checkHooks(repoPath) {
3713
4110
  { path: "commitlint.config.js", label: "commitlint" },
3714
4111
  { path: ".claude/settings.json", label: "Claude Code settings" }
3715
4112
  ];
3716
- for (const { path: path6, label } of hookConfigs) {
3717
- const found = await fs11.pathExists(join7(repoPath, path6));
4113
+ for (const { path: path8, label } of hookConfigs) {
4114
+ const found = await fs13.pathExists(join7(repoPath, path8));
3718
4115
  findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
3719
4116
  if (found) score += 20;
3720
4117
  }
@@ -3731,8 +4128,8 @@ async function checkRepoStructure(repoPath) {
3731
4128
  { path: ".env.example", label: "\uD658\uACBD\uBCC0\uC218 \uC608\uC2DC" },
3732
4129
  { path: ".gitignore", label: ".gitignore" }
3733
4130
  ];
3734
- for (const { path: path6, label } of structureChecks) {
3735
- const found = await fs11.pathExists(join7(repoPath, path6));
4131
+ for (const { path: path8, label } of structureChecks) {
4132
+ const found = await fs13.pathExists(join7(repoPath, path8));
3736
4133
  findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
3737
4134
  if (found) score += 25;
3738
4135
  }
@@ -3748,8 +4145,8 @@ async function checkDocumentation(repoPath) {
3748
4145
  { path: "docs", label: "docs/ \uB514\uB809\uD1A0\uB9AC", points: 25 },
3749
4146
  { path: "docs/openspec.md", label: "OpenSpec PRD", points: 25 }
3750
4147
  ];
3751
- for (const { path: path6, label, points } of docChecks) {
3752
- const found = await fs11.pathExists(join7(repoPath, path6));
4148
+ for (const { path: path8, label, points } of docChecks) {
4149
+ const found = await fs13.pathExists(join7(repoPath, path8));
3753
4150
  findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
3754
4151
  if (found) score += points;
3755
4152
  }
@@ -3767,8 +4164,8 @@ async function checkHarnessEngineering(repoPath) {
3767
4164
  { path: ".claude/commands", label: ".claude/commands/", points: 10 },
3768
4165
  { path: ".pai/config.json", label: "PAI config", points: 10 }
3769
4166
  ];
3770
- for (const { path: path6, label, points } of harnessChecks) {
3771
- const found = await fs11.pathExists(join7(repoPath, path6));
4167
+ for (const { path: path8, label, points } of harnessChecks) {
4168
+ const found = await fs13.pathExists(join7(repoPath, path8));
3772
4169
  findings.push({ item: label, found, details: found ? "\uC874\uC7AC" : "\uC5C6\uC74C" });
3773
4170
  if (found) score += points;
3774
4171
  }
@@ -3902,41 +4299,41 @@ __export(reporter_exports, {
3902
4299
  printReport: () => printReport,
3903
4300
  printVerboseFindings: () => printVerboseFindings
3904
4301
  });
3905
- import chalk4 from "chalk";
4302
+ import chalk5 from "chalk";
3906
4303
  function printReport(result) {
3907
4304
  console.log("");
3908
- console.log(chalk4.hex("#7B93DB")(" PAI Evaluation Report"));
3909
- console.log(chalk4.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
4305
+ console.log(chalk5.hex("#7B93DB")(" PAI Evaluation Report"));
4306
+ console.log(chalk5.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
3910
4307
  console.log("");
3911
4308
  const gradeColor = GRADE_COLORS[result.totalGrade];
3912
4309
  console.log(` \uC885\uD569 \uC810\uC218: ${gradeColor(String(result.totalScore))} / 100 \uB4F1\uAE09: ${gradeColor(result.totalGrade)}`);
3913
4310
  if (result.penaltyApplied) {
3914
- console.log(chalk4.red(` \u26A0 ${result.penaltyReason}`));
4311
+ console.log(chalk5.red(` \u26A0 ${result.penaltyReason}`));
3915
4312
  }
3916
4313
  console.log("");
3917
- console.log(chalk4.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
4314
+ console.log(chalk5.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
3918
4315
  for (const cat of result.categories) {
3919
4316
  const color = GRADE_COLORS[cat.grade];
3920
- const tierLabel = cat.tier === "must" ? chalk4.hex("#E06C75")("[\uD544\uC218]") : chalk4.gray("[\uC120\uD0DD]");
4317
+ const tierLabel = cat.tier === "must" ? chalk5.hex("#E06C75")("[\uD544\uC218]") : chalk5.gray("[\uC120\uD0DD]");
3921
4318
  const bar = renderBar(cat.score);
3922
4319
  console.log(` ${tierLabel} ${cat.name.padEnd(16)} ${bar} ${color(`${cat.score}`).padStart(12)} ${color(cat.grade)}`);
3923
4320
  }
3924
4321
  console.log("");
3925
- console.log(chalk4.gray(` ${result.summary}`));
4322
+ console.log(chalk5.gray(` ${result.summary}`));
3926
4323
  console.log("");
3927
4324
  }
3928
4325
  function printVerboseFindings(result) {
3929
4326
  for (const cat of result.categories) {
3930
4327
  console.log("");
3931
- console.log(chalk4.bold(` ${cat.name} (${cat.grade}, ${cat.score}\uC810)`));
4328
+ console.log(chalk5.bold(` ${cat.name} (${cat.grade}, ${cat.score}\uC810)`));
3932
4329
  for (const f of cat.rawFindings) {
3933
- const icon = f.found ? chalk4.green("\u2713") : chalk4.red("\u2717");
4330
+ const icon = f.found ? chalk5.green("\u2713") : chalk5.red("\u2717");
3934
4331
  console.log(` ${icon} ${f.item} \u2014 ${f.details}`);
3935
4332
  }
3936
4333
  for (const r of cat.recommendations) {
3937
- const severity = r.severity === "critical" ? chalk4.red("!") : r.severity === "warning" ? chalk4.yellow("!") : chalk4.gray("i");
4334
+ const severity = r.severity === "critical" ? chalk5.red("!") : r.severity === "warning" ? chalk5.yellow("!") : chalk5.gray("i");
3938
4335
  console.log(` ${severity} ${r.message}`);
3939
- console.log(chalk4.gray(` \u2192 ${r.action}`));
4336
+ console.log(chalk5.gray(` \u2192 ${r.action}`));
3940
4337
  }
3941
4338
  }
3942
4339
  }
@@ -3944,8 +4341,8 @@ function renderBar(score) {
3944
4341
  const width = 20;
3945
4342
  const filled = Math.round(score / 100 * width);
3946
4343
  const empty = width - filled;
3947
- const color = score >= 80 ? chalk4.hex("#6BCB77") : score >= 60 ? chalk4.hex("#E2B340") : chalk4.hex("#E06C75");
3948
- return color("\u2588".repeat(filled)) + chalk4.gray("\u2591".repeat(empty));
4344
+ const color = score >= 80 ? chalk5.hex("#6BCB77") : score >= 60 ? chalk5.hex("#E2B340") : chalk5.hex("#E06C75");
4345
+ return color("\u2588".repeat(filled)) + chalk5.gray("\u2591".repeat(empty));
3949
4346
  }
3950
4347
  function buildMarkdownReport(result) {
3951
4348
  const lines = [
@@ -4136,11 +4533,11 @@ var init_reporter = __esm({
4136
4533
  "src/stages/evaluation/reporter.ts"() {
4137
4534
  "use strict";
4138
4535
  GRADE_COLORS = {
4139
- A: chalk4.hex("#6BCB77"),
4140
- B: chalk4.hex("#7B93DB"),
4141
- C: chalk4.hex("#E2B340"),
4142
- D: chalk4.hex("#E06C75"),
4143
- F: chalk4.hex("#CC4444")
4536
+ A: chalk5.hex("#6BCB77"),
4537
+ B: chalk5.hex("#7B93DB"),
4538
+ C: chalk5.hex("#E2B340"),
4539
+ D: chalk5.hex("#E06C75"),
4540
+ F: chalk5.hex("#CC4444")
4144
4541
  };
4145
4542
  }
4146
4543
  });
@@ -4153,54 +4550,54 @@ __export(shell_cd_exports, {
4153
4550
  });
4154
4551
  import { join as join8 } from "path";
4155
4552
  import { homedir as homedir2 } from "os";
4156
- import fs12 from "fs-extra";
4553
+ import fs14 from "fs-extra";
4157
4554
  async function requestCdAfter(targetDir) {
4158
- await fs12.ensureDir(PAI_DIR);
4159
- await fs12.writeFile(CD_FILE, targetDir);
4555
+ await fs14.ensureDir(PAI_DIR);
4556
+ await fs14.writeFile(CD_FILE, targetDir);
4160
4557
  }
4161
4558
  async function installShellHelper() {
4162
- await fs12.ensureDir(PAI_DIR);
4559
+ await fs14.ensureDir(PAI_DIR);
4163
4560
  if (isWindows) {
4164
4561
  return installPowerShellHelper();
4165
4562
  }
4166
4563
  return installBashHelper();
4167
4564
  }
4168
4565
  async function installBashHelper() {
4169
- await fs12.writeFile(HELPER_FILE_SH, BASH_HELPER);
4566
+ await fs14.writeFile(HELPER_FILE_SH, BASH_HELPER);
4170
4567
  const rcFile = getShellRcPath();
4171
4568
  const sourceLine = 'source "$HOME/.pai/shell-helper.sh"';
4172
- if (await fs12.pathExists(rcFile)) {
4173
- const content = await fs12.readFile(rcFile, "utf8");
4569
+ if (await fs14.pathExists(rcFile)) {
4570
+ const content = await fs14.readFile(rcFile, "utf8");
4174
4571
  if (content.includes("shell-helper.sh")) {
4175
4572
  return true;
4176
4573
  }
4177
- await fs12.appendFile(rcFile, `
4574
+ await fs14.appendFile(rcFile, `
4178
4575
  # PAI \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9
4179
4576
  ${sourceLine}
4180
4577
  `);
4181
4578
  return false;
4182
4579
  }
4183
- await fs12.writeFile(rcFile, `${sourceLine}
4580
+ await fs14.writeFile(rcFile, `${sourceLine}
4184
4581
  `);
4185
4582
  return false;
4186
4583
  }
4187
4584
  async function installPowerShellHelper() {
4188
- await fs12.writeFile(HELPER_FILE_PS1, POWERSHELL_HELPER);
4585
+ await fs14.writeFile(HELPER_FILE_PS1, POWERSHELL_HELPER);
4189
4586
  const rcFile = getShellRcPath();
4190
4587
  const sourceLine = '. "$env:USERPROFILE\\.pai\\shell-helper.ps1"';
4191
- await fs12.ensureDir(join8(rcFile, ".."));
4192
- if (await fs12.pathExists(rcFile)) {
4193
- const content = await fs12.readFile(rcFile, "utf8");
4588
+ await fs14.ensureDir(join8(rcFile, ".."));
4589
+ if (await fs14.pathExists(rcFile)) {
4590
+ const content = await fs14.readFile(rcFile, "utf8");
4194
4591
  if (content.includes("shell-helper.ps1")) {
4195
4592
  return true;
4196
4593
  }
4197
- await fs12.appendFile(rcFile, `
4594
+ await fs14.appendFile(rcFile, `
4198
4595
  # PAI \u2014 \uC790\uB3D9 \uB514\uB809\uD1A0\uB9AC \uC774\uB3D9
4199
4596
  ${sourceLine}
4200
4597
  `);
4201
4598
  return false;
4202
4599
  }
4203
- await fs12.writeFile(rcFile, `${sourceLine}
4600
+ await fs14.writeFile(rcFile, `${sourceLine}
4204
4601
  `);
4205
4602
  return false;
4206
4603
  }
@@ -4263,10 +4660,10 @@ __export(claude_settings_exports, {
4263
4660
  mergeOmcIntoSettings: () => mergeOmcIntoSettings
4264
4661
  });
4265
4662
  import os2 from "os";
4266
- import path5 from "path";
4267
- import fs13 from "fs-extra";
4663
+ import path7 from "path";
4664
+ import fs15 from "fs-extra";
4268
4665
  function getClaudeSettingsPath(homeDir = os2.homedir()) {
4269
- return path5.join(homeDir, ".claude", "settings.json");
4666
+ return path7.join(homeDir, ".claude", "settings.json");
4270
4667
  }
4271
4668
  function parseJsonWithBom(raw) {
4272
4669
  const stripped = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
@@ -4281,13 +4678,13 @@ async function enableOmcPlugin(options = {}) {
4281
4678
  const pluginId = options.pluginId ?? DEFAULT_PLUGIN_ID;
4282
4679
  const wantBackup = options.backup ?? true;
4283
4680
  const settingsPath = getClaudeSettingsPath();
4284
- await fs13.ensureDir(path5.dirname(settingsPath));
4285
- if (!await fs13.pathExists(settingsPath)) {
4681
+ await fs15.ensureDir(path7.dirname(settingsPath));
4682
+ if (!await fs15.pathExists(settingsPath)) {
4286
4683
  const skeleton = buildSkeleton(marketplaceId, marketplaceUrl, pluginId);
4287
- await fs13.writeFile(settingsPath, JSON.stringify(skeleton, null, 2) + "\n", "utf8");
4684
+ await fs15.writeFile(settingsPath, JSON.stringify(skeleton, null, 2) + "\n", "utf8");
4288
4685
  return { action: "created", settingsPath };
4289
4686
  }
4290
- const raw = await fs13.readFile(settingsPath, "utf8");
4687
+ const raw = await fs15.readFile(settingsPath, "utf8");
4291
4688
  let parsed;
4292
4689
  try {
4293
4690
  const value = parseJsonWithBom(raw);
@@ -4297,7 +4694,7 @@ async function enableOmcPlugin(options = {}) {
4297
4694
  parsed = value;
4298
4695
  } catch (err) {
4299
4696
  const backupPath2 = `${settingsPath}.backup-${timestampSuffix()}`;
4300
- await fs13.copy(settingsPath, backupPath2);
4697
+ await fs15.copy(settingsPath, backupPath2);
4301
4698
  throw new ClaudeSettingsError(
4302
4699
  `settings.json \uD30C\uC2F1 \uC2E4\uD328: ${err.message}. \uBC31\uC5C5: ${backupPath2}`,
4303
4700
  backupPath2
@@ -4309,10 +4706,10 @@ async function enableOmcPlugin(options = {}) {
4309
4706
  let backupPath;
4310
4707
  if (wantBackup) {
4311
4708
  backupPath = `${settingsPath}.backup-${timestampSuffix()}`;
4312
- await fs13.copy(settingsPath, backupPath);
4709
+ await fs15.copy(settingsPath, backupPath);
4313
4710
  }
4314
4711
  const merged = mergeOmcIntoSettings(parsed, marketplaceId, marketplaceUrl, pluginId);
4315
- await fs13.writeFile(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
4712
+ await fs15.writeFile(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
4316
4713
  return { action: "added", settingsPath, backupPath };
4317
4714
  }
4318
4715
  function buildSkeleton(marketplaceId, marketplaceUrl, pluginId) {
@@ -4454,7 +4851,7 @@ __export(evaluate_cmd_exports, {
4454
4851
  evaluateCommand: () => evaluateCommand
4455
4852
  });
4456
4853
  import { join as join10, basename } from "path";
4457
- import fs14 from "fs-extra";
4854
+ import fs16 from "fs-extra";
4458
4855
  async function evaluateCommand(cwd, options) {
4459
4856
  const useCache = options.cache !== false;
4460
4857
  let llmOutput = useCache ? getCachedResult(cwd) : null;
@@ -4476,15 +4873,15 @@ async function evaluateCommand(cwd, options) {
4476
4873
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4477
4874
  const reportDir = join10(cwd, "docs", "p-reports");
4478
4875
  const reportPath = join10(reportDir, `${today}.md`);
4479
- await fs14.ensureDir(reportDir);
4876
+ await fs16.ensureDir(reportDir);
4480
4877
  const detailedReport = buildDetailedReport(result, projectName);
4481
- await fs14.writeFile(reportPath, detailedReport, "utf8");
4878
+ await fs16.writeFile(reportPath, detailedReport, "utf8");
4482
4879
  console.log("");
4483
4880
  success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
4484
4881
  console.log("");
4485
4882
  console.log(detailedReport);
4486
4883
  if (options.output) {
4487
- await fs14.writeFile(options.output, detailedReport, "utf8");
4884
+ await fs16.writeFile(options.output, detailedReport, "utf8");
4488
4885
  success(`\uCD94\uAC00 \uC800\uC7A5: ${options.output}`);
4489
4886
  }
4490
4887
  if (options.failUnder && result.totalScore < options.failUnder) {
@@ -4657,7 +5054,7 @@ __export(init_cmd_exports, {
4657
5054
  initCommand: () => initCommand
4658
5055
  });
4659
5056
  import { join as join11, basename as basename2 } from "path";
4660
- import fs15 from "fs-extra";
5057
+ import fs17 from "fs-extra";
4661
5058
  async function initCommand(cwd, nameArg) {
4662
5059
  printWelcomeBanner();
4663
5060
  const { isWindows: isWindows2, diagnoseWindowsEnv: diagnoseWindowsEnv2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
@@ -4695,7 +5092,7 @@ async function initCommand(cwd, nameArg) {
4695
5092
  const isLegacy = await detectLegacyProject(cwd);
4696
5093
  if (isLegacy) {
4697
5094
  const { default: inquirer2 } = await import("inquirer");
4698
- const chalk8 = (await import("chalk")).default;
5095
+ const chalk9 = (await import("chalk")).default;
4699
5096
  console.log("");
4700
5097
  info("\uAE30\uC874 \uD504\uB85C\uC81D\uD2B8\uAC00 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
4701
5098
  console.log("");
@@ -4716,10 +5113,10 @@ async function initCommand(cwd, nameArg) {
4716
5113
  printReport2(evalResult);
4717
5114
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4718
5115
  const reportDir = join11(cwd, "docs", "p-reports");
4719
- await fs15.ensureDir(reportDir);
5116
+ await fs17.ensureDir(reportDir);
4720
5117
  const legacyName = basename2(cwd);
4721
5118
  const detailedReport = buildDetailedReport3(evalResult, legacyName);
4722
- await fs15.writeFile(join11(reportDir, `${today}.md`), detailedReport, "utf8");
5119
+ await fs17.writeFile(join11(reportDir, `${today}.md`), detailedReport, "utf8");
4723
5120
  console.log("");
4724
5121
  success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
4725
5122
  } catch {
@@ -4765,7 +5162,7 @@ async function initCommand(cwd, nameArg) {
4765
5162
  projectName = answer.name.trim();
4766
5163
  }
4767
5164
  const projectDir = join11(cwd, projectName);
4768
- if (await fs15.pathExists(projectDir)) {
5165
+ if (await fs17.pathExists(projectDir)) {
4769
5166
  const existingConfig = await loadConfig(projectDir);
4770
5167
  if (existingConfig) {
4771
5168
  console.log("");
@@ -4778,7 +5175,7 @@ async function initCommand(cwd, nameArg) {
4778
5175
  warn(`${projectName}/ \uD3F4\uB354\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4.`);
4779
5176
  hint("\uC774 \uD3F4\uB354\uC5D0 PAI\uB97C \uC124\uCE58\uD569\uB2C8\uB2E4.");
4780
5177
  } else {
4781
- await fs15.ensureDir(projectDir);
5178
+ await fs17.ensureDir(projectDir);
4782
5179
  success(`${projectName}/ \uD3F4\uB354 \uC0DD\uC131`);
4783
5180
  }
4784
5181
  await setupInDirectory(projectDir, projectName);
@@ -4816,7 +5213,7 @@ async function setupInDirectory(projectDir, projectName) {
4816
5213
  }
4817
5214
  async function showCompletion(projectName, projectDir, extraTools, isCurrentDir) {
4818
5215
  const { default: inquirer } = await import("inquirer");
4819
- const chalk8 = (await import("chalk")).default;
5216
+ const chalk9 = (await import("chalk")).default;
4820
5217
  const { sleep: sleep2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
4821
5218
  step(3, 3, "\uC644\uB8CC");
4822
5219
  console.log(colors.success(" \uD504\uB85C\uC81D\uD2B8\uAC00 \uC900\uBE44\uB418\uC5C8\uC2B5\uB2C8\uB2E4!"));
@@ -4830,26 +5227,26 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
4830
5227
  console.log("");
4831
5228
  console.log(colors.accent(" \uC124\uCE58\uB41C \uD50C\uB7EC\uADF8\uC778 & \uAE30\uB2A5"));
4832
5229
  console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
4833
- console.log(` ${chalk8.cyan("OpenSpec+OMC")} \uC124\uACC4\uB3C4 & \uC9C0\uD615\uB3C4 \u2014 \uBB34\uC5C7\uC744, \uC5B4\uB514\uC5D0 \uB9CC\uB4E4\uC9C0 \uC815\uC758`);
4834
- console.log(` ${chalk8.cyan("RoboCo CLI")} \uD604\uC7A5 \uC18C\uC7A5 \u2014 \uC804\uCCB4 \uACF5\uC815 \uAD00\uB9AC, AI\uC5D0\uAC8C \uC791\uC5C5 \uC9C0\uC2DC`);
4835
- console.log(` ${chalk8.cyan("Vibe-Ready")} \uD488\uC9C8 \uAC80\uC218 \u2014 AI \uAC1C\uBC1C \uC900\uBE44\uB3C4 6\uCE74\uD14C\uACE0\uB9AC \uD3C9\uAC00`);
5230
+ console.log(` ${chalk9.cyan("OpenSpec+OMC")} \uC124\uACC4\uB3C4 & \uC9C0\uD615\uB3C4 \u2014 \uBB34\uC5C7\uC744, \uC5B4\uB514\uC5D0 \uB9CC\uB4E4\uC9C0 \uC815\uC758`);
5231
+ console.log(` ${chalk9.cyan("RoboCo CLI")} \uD604\uC7A5 \uC18C\uC7A5 \u2014 \uC804\uCCB4 \uACF5\uC815 \uAD00\uB9AC, AI\uC5D0\uAC8C \uC791\uC5C5 \uC9C0\uC2DC`);
5232
+ console.log(` ${chalk9.cyan("Vibe-Ready")} \uD488\uC9C8 \uAC80\uC218 \u2014 AI \uAC1C\uBC1C \uC900\uBE44\uB3C4 6\uCE74\uD14C\uACE0\uB9AC \uD3C9\uAC00`);
4836
5233
  if (extraTools.includes("omc")) {
4837
- console.log(` ${chalk8.cyan("OMC")} AI \uC5D0\uC774\uC804\uD2B8 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158 (oh-my-claudecode)`);
5234
+ console.log(` ${chalk9.cyan("OMC")} AI \uC5D0\uC774\uC804\uD2B8 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158 (oh-my-claudecode)`);
4838
5235
  }
4839
5236
  if (extraTools.includes("gstack")) {
4840
- console.log(` ${chalk8.cyan("gstack")} \uD45C\uC900 \uC790\uC7AC & \uACF5\uBC95 \u2014 \uAE30\uC220 \uC2A4\uD0DD \uD45C\uC900 \uAD6C\uC870 \uC81C\uACF5`);
5237
+ console.log(` ${chalk9.cyan("gstack")} \uD45C\uC900 \uC790\uC7AC & \uACF5\uBC95 \u2014 \uAE30\uC220 \uC2A4\uD0DD \uD45C\uC900 \uAD6C\uC870 \uC81C\uACF5`);
4841
5238
  }
4842
5239
  if (extraTools.includes("harness")) {
4843
- console.log(` ${chalk8.cyan("Harness")} \uC548\uC804\uC7A5\uCE58 & \uAC80\uC0AC\uB300 \u2014 AI \uCF54\uB4DC \uC791\uB3D9 \uD14C\uC2A4\uD2B8`);
5240
+ console.log(` ${chalk9.cyan("Harness")} \uC548\uC804\uC7A5\uCE58 & \uAC80\uC0AC\uB300 \u2014 AI \uCF54\uB4DC \uC791\uB3D9 \uD14C\uC2A4\uD2B8`);
4844
5241
  }
4845
5242
  if (extraTools.includes("vercel")) {
4846
- console.log(` ${chalk8.cyan("Vercel")} \uC790\uB3D9 \uBC30\uD3EC \u2014 Preview + Production`);
5243
+ console.log(` ${chalk9.cyan("Vercel")} \uC790\uB3D9 \uBC30\uD3EC \u2014 Preview + Production`);
4847
5244
  }
4848
5245
  if (extraTools.includes("supabase")) {
4849
- console.log(` ${chalk8.cyan("Supabase")} DB + \uC778\uC99D + \uC2A4\uD1A0\uB9AC\uC9C0 (PostgreSQL \uAE30\uBC18)`);
5246
+ console.log(` ${chalk9.cyan("Supabase")} DB + \uC778\uC99D + \uC2A4\uD1A0\uB9AC\uC9C0 (PostgreSQL \uAE30\uBC18)`);
4850
5247
  }
4851
5248
  if (extraTools.includes("mcp")) {
4852
- console.log(` ${chalk8.cyan("MCP \uC11C\uBC84")} AI \uB3C4\uAD6C \u2014 Claude\uAC00 \uD638\uCD9C\uD560 \uCEE4\uC2A4\uD140 \uAE30\uB2A5/\uB370\uC774\uD130`);
5249
+ console.log(` ${chalk9.cyan("MCP \uC11C\uBC84")} AI \uB3C4\uAD6C \u2014 Claude\uAC00 \uD638\uCD9C\uD560 \uCEE4\uC2A4\uD140 \uAE30\uB2A5/\uB370\uC774\uD130`);
4853
5250
  }
4854
5251
  console.log("");
4855
5252
  console.log(colors.accent(" \uC124\uCE58 \uD655\uC778"));
@@ -4863,7 +5260,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
4863
5260
  ...extraTools.includes("omc") ? [{ label: "OMC \uB7F0\uD0C0\uC784", path: ".omc" }] : []
4864
5261
  ];
4865
5262
  for (const check of checks) {
4866
- const exists = await fs15.pathExists(join11(projectDir, check.path));
5263
+ const exists = await fs17.pathExists(join11(projectDir, check.path));
4867
5264
  console.log(` ${exists ? colors.success("\u2713") : colors.err("\u2717")} ${check.label.padEnd(16)} ${colors.dim(check.path)}`);
4868
5265
  }
4869
5266
  console.log("");
@@ -4889,9 +5286,9 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
4889
5286
  await sleep2(500);
4890
5287
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4891
5288
  const reportDir = join11(projectDir, "docs", "p-reports");
4892
- await fs15.ensureDir(reportDir);
5289
+ await fs17.ensureDir(reportDir);
4893
5290
  const detailedReport = buildDetailedReport3(evalResult, projectName);
4894
- await fs15.writeFile(join11(reportDir, `${today}.md`), detailedReport, "utf8");
5291
+ await fs17.writeFile(join11(reportDir, `${today}.md`), detailedReport, "utf8");
4895
5292
  await sleep2(500);
4896
5293
  console.log("");
4897
5294
  success(`\uB9AC\uD3EC\uD2B8 \uC800\uC7A5: docs/p-reports/${today}.md`);
@@ -4909,7 +5306,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
4909
5306
  }
4910
5307
  console.log("");
4911
5308
  if (!isCurrentDir) {
4912
- console.log(` ${chalk8.green("\u2192")} cd ${projectName} ${colors.dim("\uC774\uB3D9 \uC644\uB8CC")}`);
5309
+ console.log(` ${chalk9.green("\u2192")} cd ${projectName} ${colors.dim("\uC774\uB3D9 \uC644\uB8CC")}`);
4913
5310
  }
4914
5311
  console.log("");
4915
5312
  success("\uC774\uC81C Claude Code\uC640 \uD568\uAED8 PRD \uBB38\uC11C\uB97C \uC791\uC131\uD558\uC138\uC694.");
@@ -4942,7 +5339,7 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
4942
5339
  const shellRc = getShellRcPath2();
4943
5340
  let yoloAlreadyAliased = false;
4944
5341
  try {
4945
- const rcContent = await fs15.readFile(shellRc, "utf8");
5342
+ const rcContent = await fs17.readFile(shellRc, "utf8");
4946
5343
  yoloAlreadyAliased = checkYolo(rcContent);
4947
5344
  } catch {
4948
5345
  }
@@ -4962,10 +5359,10 @@ async function showCompletion(projectName, projectDir, extraTools, isCurrentDir)
4962
5359
  try {
4963
5360
  const { getYoloAliasLine: getYoloAliasLine2 } = await Promise.resolve().then(() => (init_platform(), platform_exports));
4964
5361
  const aliasLine = getYoloAliasLine2();
4965
- const rcContent = await fs15.readFile(shellRc, "utf8").catch(() => "");
5362
+ const rcContent = await fs17.readFile(shellRc, "utf8").catch(() => "");
4966
5363
  if (!rcContent.includes("claude-yolo")) {
4967
- await fs15.ensureDir(join11(shellRc, ".."));
4968
- await fs15.appendFile(shellRc, `
5364
+ await fs17.ensureDir(join11(shellRc, ".."));
5365
+ await fs17.appendFile(shellRc, `
4969
5366
  # PAI \u2014 claude-YOLO mode
4970
5367
  ${aliasLine}
4971
5368
  `);
@@ -5004,7 +5401,7 @@ ${aliasLine}
5004
5401
  async function handleExistingProject(cwd, state) {
5005
5402
  const { PLUGIN_META: PLUGIN_META2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
5006
5403
  const { default: inquirer } = await import("inquirer");
5007
- const chalk8 = (await import("chalk")).default;
5404
+ const chalk9 = (await import("chalk")).default;
5008
5405
  section("\uAE30\uC874 \uD504\uB85C\uC81D\uD2B8");
5009
5406
  const allPlugins = Object.entries(PLUGIN_META2).map(([key, meta]) => ({
5010
5407
  key,
@@ -5015,7 +5412,7 @@ async function handleExistingProject(cwd, state) {
5015
5412
  if (p.installed) {
5016
5413
  success(p.label);
5017
5414
  } else {
5018
- console.log(colors.dim(` \xB7 ${p.label} ${chalk8.gray("\uBBF8\uC124\uCE58")}`));
5415
+ console.log(colors.dim(` \xB7 ${p.label} ${chalk9.gray("\uBBF8\uC124\uCE58")}`));
5019
5416
  }
5020
5417
  }
5021
5418
  const currentConfig = await loadConfig(cwd);
@@ -5041,7 +5438,7 @@ async function handleExistingProject(cwd, state) {
5041
5438
  name: `\u{1FA7A} \uD658\uACBD \uC810\uAC80 ${colors.dim("(pai check \u2014 Node/Git/Claude/OMC)")}`,
5042
5439
  value: "doctor"
5043
5440
  },
5044
- { name: chalk8.gray("\u{1F6AA} \uC885\uB8CC"), value: "exit" }
5441
+ { name: chalk9.gray("\u{1F6AA} \uC885\uB8CC"), value: "exit" }
5045
5442
  ]
5046
5443
  }]);
5047
5444
  switch (action) {
@@ -5066,7 +5463,7 @@ async function handleExistingProject(cwd, state) {
5066
5463
  }
5067
5464
  async function installOrchestratorOnly(projectDir, projectName) {
5068
5465
  const { default: inquirer } = await import("inquirer");
5069
- const chalk8 = (await import("chalk")).default;
5466
+ const chalk9 = (await import("chalk")).default;
5070
5467
  const { generateFiles: generateFiles2 } = await Promise.resolve().then(() => (init_generator(), generator_exports));
5071
5468
  const { analyzeProject: analyzeProject2 } = await Promise.resolve().then(() => (init_analyzer(), analyzer_exports));
5072
5469
  const { provisionClaudeCommands: provisionClaudeCommands2 } = await Promise.resolve().then(() => (init_claude_commands(), claude_commands_exports));
@@ -5088,7 +5485,7 @@ async function installOrchestratorOnly(projectDir, projectName) {
5088
5485
  { name: `gstack ${colors.dim("\u2500 \uD45C\uC900 \uC790\uC7AC & \uACF5\uBC95 (\uAE30\uC220 \uC2A4\uD0DD)")}`, value: "gstack" },
5089
5486
  { name: `Harness ${colors.dim("\u2500 \uC548\uC804\uC7A5\uCE58 & \uAC80\uC0AC\uB300 (\uD14C\uC2A4\uD2B8)")}`, value: "harness" },
5090
5487
  new Separator("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
5091
- { name: chalk8.green("\u21B5 \uC120\uD0DD \uC644\uB8CC"), value: "__done__" }
5488
+ { name: chalk9.green("\u21B5 \uC120\uD0DD \uC644\uB8CC"), value: "__done__" }
5092
5489
  ]
5093
5490
  }]);
5094
5491
  const selectedPlugins = plugins.filter((p) => p !== "__done__");
@@ -5159,8 +5556,8 @@ async function installOrchestratorOnly(projectDir, projectName) {
5159
5556
  printReport2(evalResult);
5160
5557
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5161
5558
  const reportDir = join11(projectDir, "docs", "p-reports");
5162
- await fs15.ensureDir(reportDir);
5163
- await fs15.writeFile(join11(reportDir, `${today}.md`), buildDetailedReport3(evalResult, projectName), "utf8");
5559
+ await fs17.ensureDir(reportDir);
5560
+ await fs17.writeFile(join11(reportDir, `${today}.md`), buildDetailedReport3(evalResult, projectName), "utf8");
5164
5561
  console.log("");
5165
5562
  hint(`\uC0C1\uC138 \uB9AC\uD3EC\uD2B8: docs/p-reports/${today}.md`);
5166
5563
  } catch {
@@ -5175,7 +5572,7 @@ async function installOrchestratorOnly(projectDir, projectName) {
5175
5572
  message: "Claude Code\uB97C \uC2DC\uC791\uD560\uAE4C\uC694?",
5176
5573
  choices: [
5177
5574
  { name: `Claude \uBC14\uB85C \uC2DC\uC791 ${colors.dim("\u2500 claude")}`, value: "claude" },
5178
- { name: chalk8.gray("\uB098\uC911\uC5D0 \uC9C1\uC811 \uC2E4\uD589"), value: "none" }
5575
+ { name: chalk9.gray("\uB098\uC911\uC5D0 \uC9C1\uC811 \uC2E4\uD589"), value: "none" }
5179
5576
  ]
5180
5577
  }]);
5181
5578
  if (launch === "none") {
@@ -5210,7 +5607,7 @@ async function detectLegacyProject(cwd) {
5210
5607
  ".gitignore"
5211
5608
  ];
5212
5609
  for (const signal of signals) {
5213
- if (await fs15.pathExists(join11(cwd, signal))) return true;
5610
+ if (await fs17.pathExists(join11(cwd, signal))) return true;
5214
5611
  }
5215
5612
  return false;
5216
5613
  }
@@ -5279,7 +5676,7 @@ async function helpCommand() {
5279
5676
  ["pai grade", "AI \uAC1C\uBC1C \uC900\uBE44\uB3C4 \uD3C9\uAC00 (6\uCE74\uD14C\uACE0\uB9AC)"],
5280
5677
  ["pai run", "\uC804\uCCB4 5\uB2E8\uACC4 \uD30C\uC774\uD504\uB77C\uC778 \uC2E4\uD589"],
5281
5678
  ["pai remove", "PAI \uC0DD\uC131 \uD30C\uC77C \uC0AD\uC81C"],
5282
- ["pai upgrade", "\uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC \uCD5C\uC2E0\uD654"],
5679
+ ["pai upgrade", "CLI + \uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC \uCD5C\uC2E0\uD654 (npm install -g \uC790\uB3D9)"],
5283
5680
  ["pai savetoken", "AI \uD1A0\uD070 \uC808\uAC10 \uBD84\uC11D"],
5284
5681
  ["pai wakeup", "Claude \uC138\uC158 \uC790\uB3D9 \uC2DC\uC791"],
5285
5682
  ["pai help", "\uC774 \uC548\uB0B4"]
@@ -5302,16 +5699,16 @@ var init_help_cmd = __esm({
5302
5699
 
5303
5700
  // src/stages/design/openspec.ts
5304
5701
  import { join as join12 } from "path";
5305
- import fs16 from "fs-extra";
5702
+ import fs18 from "fs-extra";
5306
5703
  async function initOpenSpec(cwd, projectName) {
5307
5704
  const docsDir = join12(cwd, "docs");
5308
- await fs16.ensureDir(docsDir);
5705
+ await fs18.ensureDir(docsDir);
5309
5706
  const openspecPath = join12(docsDir, "openspec.md");
5310
- if (await fs16.pathExists(openspecPath)) {
5707
+ if (await fs18.pathExists(openspecPath)) {
5311
5708
  info("docs/openspec.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
5312
5709
  return;
5313
5710
  }
5314
- await fs16.writeFile(openspecPath, [
5711
+ await fs18.writeFile(openspecPath, [
5315
5712
  `# OpenSpec \u2014 ${projectName}`,
5316
5713
  "",
5317
5714
  "## 1. \uBAA9\uC801 (Purpose)",
@@ -5345,7 +5742,7 @@ async function validateOpenSpec(cwd) {
5345
5742
  ];
5346
5743
  let specPath = null;
5347
5744
  for (const p of candidates) {
5348
- if (await fs16.pathExists(p)) {
5745
+ if (await fs18.pathExists(p)) {
5349
5746
  specPath = p;
5350
5747
  break;
5351
5748
  }
@@ -5359,7 +5756,7 @@ async function validateOpenSpec(cwd) {
5359
5756
  warnings: ["openspec.md \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `pai design init` \uC744 \uC2E4\uD589\uD558\uC138\uC694."]
5360
5757
  };
5361
5758
  }
5362
- const content = await fs16.readFile(specPath, "utf8");
5759
+ const content = await fs18.readFile(specPath, "utf8");
5363
5760
  const missing = [];
5364
5761
  let filled = 0;
5365
5762
  for (const section2 of REQUIRED_SECTIONS) {
@@ -5408,16 +5805,16 @@ var init_openspec = __esm({
5408
5805
 
5409
5806
  // src/stages/design/omc.ts
5410
5807
  import { join as join13 } from "path";
5411
- import fs17 from "fs-extra";
5808
+ import fs19 from "fs-extra";
5412
5809
  async function initOMC(cwd, projectName) {
5413
5810
  const paiDir = join13(cwd, ".pai");
5414
- await fs17.ensureDir(paiDir);
5811
+ await fs19.ensureDir(paiDir);
5415
5812
  const omcPath = join13(paiDir, "omc.md");
5416
- if (await fs17.pathExists(omcPath)) {
5813
+ if (await fs19.pathExists(omcPath)) {
5417
5814
  info(".pai/omc.md \uC774\uBBF8 \uC874\uC7AC \u2014 \uAC74\uB108\uB700");
5418
5815
  return;
5419
5816
  }
5420
- await fs17.writeFile(omcPath, [
5817
+ await fs19.writeFile(omcPath, [
5421
5818
  `# OMC \u2014 Object Model Context (${projectName})`,
5422
5819
  "",
5423
5820
  "> AI\uAC00 \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uB3C4\uBA54\uC778\uC744 \uC774\uD574\uD558\uAE30 \uC704\uD55C \uD575\uC2EC \uAC1D\uCCB4 \uBAA8\uB378",
@@ -5500,14 +5897,14 @@ var init_design_cmd = __esm({
5500
5897
 
5501
5898
  // src/stages/validation/runner.ts
5502
5899
  import { join as join14 } from "path";
5503
- import fs18 from "fs-extra";
5900
+ import fs20 from "fs-extra";
5504
5901
  async function runTests(cwd) {
5505
5902
  const start = Date.now();
5506
5903
  const gstackPath = join14(cwd, ".pai", "gstack.json");
5507
5904
  let runner = "npm test";
5508
- if (await fs18.pathExists(gstackPath)) {
5905
+ if (await fs20.pathExists(gstackPath)) {
5509
5906
  try {
5510
- const config = await fs18.readJson(gstackPath);
5907
+ const config = await fs20.readJson(gstackPath);
5511
5908
  if (config.testRunner === "vitest") runner = "npx vitest run";
5512
5909
  else if (config.testRunner === "jest") runner = "npx jest";
5513
5910
  else if (config.testRunner === "mocha") runner = "npx mocha";
@@ -5515,9 +5912,9 @@ async function runTests(cwd) {
5515
5912
  }
5516
5913
  }
5517
5914
  const pkgPath = join14(cwd, "package.json");
5518
- if (await fs18.pathExists(pkgPath)) {
5915
+ if (await fs20.pathExists(pkgPath)) {
5519
5916
  try {
5520
- const pkg5 = await fs18.readJson(pkgPath);
5917
+ const pkg5 = await fs20.readJson(pkgPath);
5521
5918
  if (!pkg5.scripts?.test || pkg5.scripts.test.includes("no test specified")) {
5522
5919
  return {
5523
5920
  runner,
@@ -5560,15 +5957,15 @@ var init_runner = __esm({
5560
5957
 
5561
5958
  // src/stages/validation/harness.ts
5562
5959
  import { join as join15 } from "path";
5563
- import fs19 from "fs-extra";
5960
+ import fs21 from "fs-extra";
5564
5961
  async function runHarnessCheck(cwd) {
5565
5962
  const harnessPath = join15(cwd, ".pai", "harness.json");
5566
- if (!await fs19.pathExists(harnessPath)) {
5963
+ if (!await fs21.pathExists(harnessPath)) {
5567
5964
  return { enabled: false, specFile: null, rules: [], checks: [] };
5568
5965
  }
5569
5966
  let config;
5570
5967
  try {
5571
- config = await fs19.readJson(harnessPath);
5968
+ config = await fs21.readJson(harnessPath);
5572
5969
  } catch {
5573
5970
  return { enabled: false, specFile: null, rules: [], checks: [] };
5574
5971
  }
@@ -5576,8 +5973,8 @@ async function runHarnessCheck(cwd) {
5576
5973
  const rules = config.rules ?? [];
5577
5974
  const checks = [];
5578
5975
  if (rules.includes("spec-implementation-match")) {
5579
- const specExists = await fs19.pathExists(join15(cwd, specFile));
5580
- const srcExists = await fs19.pathExists(join15(cwd, "src"));
5976
+ const specExists = await fs21.pathExists(join15(cwd, specFile));
5977
+ const srcExists = await fs21.pathExists(join15(cwd, "src"));
5581
5978
  checks.push({
5582
5979
  rule: "spec-implementation-match",
5583
5980
  passed: specExists && srcExists,
@@ -5585,8 +5982,8 @@ async function runHarnessCheck(cwd) {
5585
5982
  });
5586
5983
  }
5587
5984
  if (rules.includes("api-contract-test")) {
5588
- const testDir = await fs19.pathExists(join15(cwd, "tests"));
5589
- const testDir2 = await fs19.pathExists(join15(cwd, "test"));
5985
+ const testDir = await fs21.pathExists(join15(cwd, "tests"));
5986
+ const testDir2 = await fs21.pathExists(join15(cwd, "test"));
5590
5987
  checks.push({
5591
5988
  rule: "api-contract-test",
5592
5989
  passed: testDir || testDir2,
@@ -5701,13 +6098,13 @@ var init_context = __esm({
5701
6098
 
5702
6099
  // src/stages/design/index.ts
5703
6100
  import { join as join16 } from "path";
5704
- import fs20 from "fs-extra";
6101
+ import fs22 from "fs-extra";
5705
6102
  async function autoInstallHarness(cwd) {
5706
6103
  const harnessPath = join16(cwd, ".pai", "harness.json");
5707
- if (await fs20.pathExists(harnessPath)) return;
6104
+ if (await fs22.pathExists(harnessPath)) return;
5708
6105
  await withSpinner("Harness Engineering \uC790\uB3D9 \uC124\uC815 \uC911...", async () => {
5709
- await fs20.ensureDir(join16(cwd, ".pai"));
5710
- await fs20.writeJson(harnessPath, {
6106
+ await fs22.ensureDir(join16(cwd, ".pai"));
6107
+ await fs22.writeJson(harnessPath, {
5711
6108
  version: "1.0",
5712
6109
  specFile: "docs/openspec.md",
5713
6110
  checkOn: ["pre-commit", "ci"],
@@ -6064,7 +6461,7 @@ __export(remove_cmd_exports, {
6064
6461
  removeCommand: () => removeCommand
6065
6462
  });
6066
6463
  import { basename as basename4, dirname } from "path";
6067
- import fs21 from "fs-extra";
6464
+ import fs23 from "fs-extra";
6068
6465
  async function removeCommand(cwd, options) {
6069
6466
  section("\uD504\uB85C\uC81D\uD2B8 \uC0AD\uC81C");
6070
6467
  const config = await loadConfig(cwd);
@@ -6082,7 +6479,7 @@ async function removeCommand(cwd, options) {
6082
6479
  console.log(colors.err(` ${folderName}/ \uD3F4\uB354 \uC804\uCCB4\uAC00 \uC0AD\uC81C\uB429\uB2C8\uB2E4.`));
6083
6480
  hint("\uC774 \uC791\uC5C5\uC740 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
6084
6481
  console.log("");
6085
- const items = await fs21.readdir(cwd);
6482
+ const items = await fs23.readdir(cwd);
6086
6483
  const fileCount = items.filter((i) => !i.startsWith(".")).length;
6087
6484
  const hiddenCount = items.filter((i) => i.startsWith(".")).length;
6088
6485
  info(`\uD30C\uC77C/\uD3F4\uB354 ${fileCount}\uAC1C, \uC228\uAE40 \uD56D\uBAA9 ${hiddenCount}\uAC1C`);
@@ -6101,7 +6498,7 @@ async function removeCommand(cwd, options) {
6101
6498
  }
6102
6499
  process.chdir(parentDir);
6103
6500
  try {
6104
- await fs21.remove(cwd);
6501
+ await fs23.remove(cwd);
6105
6502
  console.log("");
6106
6503
  success(`${folderName}/ \uD504\uB85C\uC81D\uD2B8\uAC00 \uC0AD\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`);
6107
6504
  try {
@@ -6141,41 +6538,62 @@ var init_remove_cmd = __esm({
6141
6538
  }
6142
6539
  });
6143
6540
 
6144
- // src/core/errors.ts
6145
- var PaiError, ConfigNotFoundError;
6146
- var init_errors = __esm({
6147
- "src/core/errors.ts"() {
6148
- "use strict";
6149
- PaiError = class extends Error {
6150
- constructor(message, code, recoverable = false) {
6151
- super(message);
6152
- this.code = code;
6153
- this.recoverable = recoverable;
6154
- this.name = "PaiError";
6155
- }
6156
- code;
6157
- recoverable;
6158
- };
6159
- ConfigNotFoundError = class extends PaiError {
6160
- constructor(path6) {
6161
- super(
6162
- `.pai/config.json not found at ${path6}. Run 'pai init' first.`,
6163
- "CONFIG_NOT_FOUND",
6164
- true
6165
- );
6166
- this.name = "ConfigNotFoundError";
6167
- }
6168
- };
6541
+ // src/utils/npm.ts
6542
+ function detectInstallMode(scriptPath) {
6543
+ const p = scriptPath.replace(/\\/g, "/").toLowerCase();
6544
+ if (p.includes("/_npx/") || p.includes("/.npm/_npx/")) return "npx";
6545
+ const globalMarkers = [
6546
+ "/.nvm/",
6547
+ // nvm 설치
6548
+ "/lib/node_modules/",
6549
+ // POSIX global (/usr/lib, /usr/local/lib 등)
6550
+ "/appdata/roaming/npm/",
6551
+ // Windows global (AppData\Roaming\npm)
6552
+ "/npm/node_modules/",
6553
+ // 기타 Windows 경로
6554
+ "/homebrew/lib/node_modules/",
6555
+ "/opt/homebrew/lib/node_modules/"
6556
+ ];
6557
+ for (const m of globalMarkers) {
6558
+ if (p.includes(m)) return "global";
6169
6559
  }
6170
- });
6171
-
6172
- // src/cli/commands/upgrade.cmd.ts
6173
- var upgrade_cmd_exports = {};
6174
- __export(upgrade_cmd_exports, {
6175
- upgradeCommand: () => upgradeCommand
6176
- });
6177
- import { createRequire as createRequire3 } from "module";
6178
- import chalk5 from "chalk";
6560
+ if (p.includes("/node_modules/")) return "local";
6561
+ return "unknown";
6562
+ }
6563
+ async function fetchLatestVersion(pkgName, timeoutMs = 5e3) {
6564
+ const controller = new AbortController();
6565
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
6566
+ try {
6567
+ const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(pkgName)}/latest`, {
6568
+ signal: controller.signal,
6569
+ headers: { "Accept": "application/json" }
6570
+ });
6571
+ clearTimeout(timer);
6572
+ if (!res.ok) return null;
6573
+ const data = await res.json();
6574
+ return typeof data.version === "string" ? data.version : null;
6575
+ } catch {
6576
+ clearTimeout(timer);
6577
+ return null;
6578
+ }
6579
+ }
6580
+ async function installGlobalLatest(pkgName) {
6581
+ try {
6582
+ const { execa } = await import("execa");
6583
+ const cmd = isWindows ? "npm.cmd" : "npm";
6584
+ await execa(cmd, ["install", "-g", `${pkgName}@latest`], {
6585
+ stdio: "inherit"
6586
+ });
6587
+ return { success: true };
6588
+ } catch (err) {
6589
+ const msg = err instanceof Error ? err.message : String(err);
6590
+ let errorCode = "UNKNOWN";
6591
+ if (/EACCES|permission denied/i.test(msg)) errorCode = "EACCES";
6592
+ else if (/ENOTFOUND|ENETUNREACH|getaddrinfo/i.test(msg)) errorCode = "ENETUNREACH";
6593
+ else if (/E404|not found/i.test(msg)) errorCode = "NOT_FOUND";
6594
+ return { success: false, errorCode, message: msg };
6595
+ }
6596
+ }
6179
6597
  function compareSemver(a, b) {
6180
6598
  const pa = a.split(".").map(Number);
6181
6599
  const pb = b.split(".").map(Number);
@@ -6187,21 +6605,49 @@ function compareSemver(a, b) {
6187
6605
  }
6188
6606
  return 0;
6189
6607
  }
6608
+ var init_npm = __esm({
6609
+ "src/utils/npm.ts"() {
6610
+ "use strict";
6611
+ init_platform();
6612
+ }
6613
+ });
6614
+
6615
+ // src/cli/commands/upgrade.cmd.ts
6616
+ var upgrade_cmd_exports = {};
6617
+ __export(upgrade_cmd_exports, {
6618
+ upgradeCommand: () => upgradeCommand
6619
+ });
6620
+ import { createRequire as createRequire3 } from "module";
6621
+ import { fileURLToPath } from "url";
6622
+ import chalk6 from "chalk";
6190
6623
  async function upgradeCommand(cwd, options) {
6191
6624
  section("PAI \uC5C5\uADF8\uB808\uC774\uB4DC");
6625
+ const currentVersion = pkg3.version;
6626
+ const pkgName = pkg3.name;
6627
+ if (!options.skipCli) {
6628
+ await upgradeCli(currentVersion, pkgName, options.force ?? false);
6629
+ } else {
6630
+ info("CLI \uC5C5\uADF8\uB808\uC774\uB4DC\uB294 \uAC74\uB108\uB701\uB2C8\uB2E4 (--skip-cli).");
6631
+ }
6632
+ if (options.skipProject) {
6633
+ info("\uD504\uB85C\uC81D\uD2B8 \uD30C\uC77C \uC5C5\uADF8\uB808\uC774\uB4DC\uB294 \uAC74\uB108\uB701\uB2C8\uB2E4 (--skip-project).");
6634
+ return;
6635
+ }
6192
6636
  const config = await loadConfig(cwd);
6193
6637
  if (!config) {
6194
- throw new ConfigNotFoundError(cwd);
6638
+ console.log("");
6639
+ info("PAI \uD504\uB85C\uC81D\uD2B8\uAC00 \uC544\uB2C8\uBBC0\uB85C \uD504\uB85C\uC81D\uD2B8 \uD30C\uC77C \uC5C5\uADF8\uB808\uC774\uB4DC\uB294 \uC0DD\uB7B5\uD569\uB2C8\uB2E4.");
6640
+ hint("PAI \uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 \uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC\uB3C4 \uAC31\uC2E0\uB429\uB2C8\uB2E4.");
6641
+ return;
6195
6642
  }
6196
- const currentVersion = pkg3.version;
6197
6643
  const configVersion = config.version;
6198
6644
  console.log("");
6199
- console.log(` \uD604\uC7AC \uBC84\uC804: ${chalk5.gray(`v${configVersion}`)}`);
6200
- console.log(` \uCD5C\uC2E0 \uBC84\uC804: ${chalk5.cyan(`v${currentVersion}`)}`);
6645
+ console.log(` \uD504\uB85C\uC81D\uD2B8 config: ${chalk6.gray(`v${configVersion}`)}`);
6646
+ console.log(` \uD604\uC7AC CLI: ${chalk6.cyan(`v${currentVersion}`)}`);
6201
6647
  if (!options.force && compareSemver(configVersion, currentVersion) >= 0) {
6202
6648
  console.log("");
6203
- success("\uC774\uBBF8 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4.");
6204
- info("\uAC15\uC81C \uC5C5\uADF8\uB808\uC774\uB4DC: npx pai-zero upgrade --force");
6649
+ success("\uD504\uB85C\uC81D\uD2B8 \uD30C\uC77C\uC774 \uC774\uBBF8 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4.");
6650
+ info("\uAC15\uC81C \uAC31\uC2E0: pai upgrade --force");
6205
6651
  return;
6206
6652
  }
6207
6653
  console.log("");
@@ -6211,29 +6657,105 @@ async function upgradeCommand(cwd, options) {
6211
6657
  return r;
6212
6658
  });
6213
6659
  console.log("");
6214
- if (result.skillUpdated) {
6215
- success("SKILL.md \uC5C5\uB370\uC774\uD2B8");
6216
- }
6660
+ if (result.skillUpdated) success("SKILL.md \uC5C5\uB370\uC774\uD2B8");
6217
6661
  if (result.commandsWritten.length > 0) {
6218
6662
  success(`\uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC ${result.commandsWritten.length}\uAC1C \uC5C5\uB370\uC774\uD2B8`);
6219
6663
  for (const f of result.commandsWritten) {
6220
- console.log(chalk5.gray(` ${f}`));
6664
+ console.log(chalk6.gray(` ${f}`));
6221
6665
  }
6222
6666
  }
6223
6667
  if (result.commandErrors.length > 0) {
6224
6668
  console.log("");
6225
- for (const e of result.commandErrors) {
6226
- error(`${e.file}: ${e.error}`);
6227
- }
6669
+ for (const e of result.commandErrors) error(`${e.file}: ${e.error}`);
6228
6670
  error("\uC77C\uBD80 \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC2E4\uD328. config.version\uC744 \uBCC0\uACBD\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
6229
- info("\uBB38\uC81C \uD574\uACB0 \uD6C4 \uB2E4\uC2DC \uC2E4\uD589: npx pai-zero upgrade --force");
6671
+ info("\uBB38\uC81C \uD574\uACB0 \uD6C4 \uB2E4\uC2DC \uC2E4\uD589: pai upgrade --force");
6230
6672
  return;
6231
6673
  }
6232
6674
  config.version = currentVersion;
6233
6675
  await saveConfig(cwd, config);
6234
6676
  console.log("");
6235
- success(`v${configVersion} \u2192 v${currentVersion} \uC5C5\uADF8\uB808\uC774\uB4DC \uC644\uB8CC!`);
6677
+ success(`\uD504\uB85C\uC81D\uD2B8 v${configVersion} \u2192 v${currentVersion} \uC5C5\uADF8\uB808\uC774\uB4DC \uC644\uB8CC!`);
6678
+ console.log("");
6679
+ }
6680
+ async function upgradeCli(currentVersion, pkgName, force) {
6681
+ const scriptPath = fileURLToPath(import.meta.url);
6682
+ const mode = detectInstallMode(scriptPath);
6683
+ console.log("");
6684
+ console.log(` \uD604\uC7AC CLI: ${chalk6.cyan(`v${currentVersion}`)} ${chalk6.gray(`(${mode})`)}`);
6685
+ if (mode === "npx") {
6686
+ info("npx\uB294 \uB9E4\uBC88 \uCD5C\uC2E0 \uBC84\uC804\uC744 \uAC00\uC838\uC624\uBBC0\uB85C \uBCC4\uB3C4 \uC5C5\uADF8\uB808\uC774\uB4DC\uAC00 \uBD88\uD544\uC694\uD569\uB2C8\uB2E4.");
6687
+ hint("\uB2E4\uC74C npx \uC2E4\uD589 \uC2DC \uC790\uB3D9\uC73C\uB85C \uCD5C\uC2E0 \uBC84\uC804\uC744 \uC0AC\uC6A9\uD569\uB2C8\uB2E4.");
6688
+ return;
6689
+ }
6690
+ const latestVersion = await withSpinner("npm registry \uCD5C\uC2E0 \uBC84\uC804 \uC870\uD68C \uC911...", async () => {
6691
+ return fetchLatestVersion(pkgName);
6692
+ });
6693
+ if (!latestVersion) {
6694
+ console.log("");
6695
+ warn("npm registry \uC870\uD68C \uC2E4\uD328 (\uB124\uD2B8\uC6CC\uD06C? \uD504\uB85D\uC2DC?)");
6696
+ hint(`\uC218\uB3D9 \uC5C5\uADF8\uB808\uC774\uB4DC: npm install -g ${pkgName}@latest`);
6697
+ return;
6698
+ }
6699
+ console.log(` \uCD5C\uC2E0: ${chalk6.cyan(`v${latestVersion}`)}`);
6700
+ const cmp = compareSemver(currentVersion, latestVersion);
6701
+ if (!force && cmp >= 0) {
6702
+ console.log("");
6703
+ success("CLI\uAC00 \uC774\uBBF8 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4.");
6704
+ return;
6705
+ }
6706
+ if (mode === "local") {
6707
+ console.log("");
6708
+ info("\uB85C\uCEEC \uC124\uCE58(\uD504\uB85C\uC81D\uD2B8 devDependency)\uB85C \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
6709
+ hint(`\uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C: npm install ${pkgName}@latest --save-dev`);
6710
+ return;
6711
+ }
6712
+ if (mode === "unknown") {
6713
+ console.log("");
6714
+ warn("\uC124\uCE58 \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
6715
+ hint(`\uC218\uB3D9 \uC5C5\uADF8\uB808\uC774\uB4DC: npm install -g ${pkgName}@latest`);
6716
+ return;
6717
+ }
6718
+ const { default: inquirer } = await import("inquirer");
6719
+ console.log("");
6720
+ const { confirm } = await inquirer.prompt([{
6721
+ type: "confirm",
6722
+ name: "confirm",
6723
+ message: `CLI\uB97C v${currentVersion} \u2192 v${latestVersion}\uB85C \uC5C5\uADF8\uB808\uC774\uB4DC\uD560\uAE4C\uC694?`,
6724
+ default: true
6725
+ }]);
6726
+ if (!confirm) {
6727
+ info("CLI \uC5C5\uADF8\uB808\uC774\uB4DC\uB97C \uAC74\uB108\uB701\uB2C8\uB2E4.");
6728
+ return;
6729
+ }
6730
+ console.log("");
6731
+ info(`npm install -g ${pkgName}@latest \uC2E4\uD589 \uC911...`);
6236
6732
  console.log("");
6733
+ const result = await installGlobalLatest(pkgName);
6734
+ console.log("");
6735
+ if (result.success) {
6736
+ success(`CLI \uC5C5\uADF8\uB808\uC774\uB4DC \uC644\uB8CC (v${currentVersion} \u2192 v${latestVersion})`);
6737
+ hint("\uC0C8 \uBC84\uC804\uC740 \uB2E4\uC74C \uD130\uBBF8\uB110 \uB610\uB294 \uC0C8 \uBA85\uB839 \uC2E4\uD589\uBD80\uD130 \uC801\uC6A9\uB429\uB2C8\uB2E4.");
6738
+ hint(`\uD655\uC778: pai --version`);
6739
+ return;
6740
+ }
6741
+ error(`CLI \uC5C5\uADF8\uB808\uC774\uB4DC \uC2E4\uD328 \u2014 ${result.errorCode ?? "UNKNOWN"}`);
6742
+ switch (result.errorCode) {
6743
+ case "EACCES":
6744
+ hint("\uAD8C\uD55C \uBD80\uC871\uC785\uB2C8\uB2E4. \uB2E4\uC74C \uC911 \uD558\uB098\uB97C \uC2DC\uB3C4\uD558\uC138\uC694:");
6745
+ hint(` 1) sudo npm install -g ${pkgName}@latest`);
6746
+ hint(" 2) nvm \uC0AC\uC6A9 (https://github.com/nvm-sh/nvm)");
6747
+ hint(" 3) npm prefix \uBCC0\uACBD: https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally");
6748
+ break;
6749
+ case "ENETUNREACH":
6750
+ hint("\uB124\uD2B8\uC6CC\uD06C \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uC138\uC694.");
6751
+ break;
6752
+ case "NOT_FOUND":
6753
+ hint(`\uD328\uD0A4\uC9C0 '${pkgName}'\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. npm registry \uC124\uC815\uC744 \uD655\uC778\uD558\uC138\uC694.`);
6754
+ break;
6755
+ default:
6756
+ hint(`\uC218\uB3D9 \uC2DC\uB3C4: npm install -g ${pkgName}@latest`);
6757
+ if (result.message) hint(`\uC6D0\uC778: ${result.message.slice(0, 200)}`);
6758
+ }
6237
6759
  }
6238
6760
  var require4, pkg3;
6239
6761
  var init_upgrade_cmd = __esm({
@@ -6242,8 +6764,8 @@ var init_upgrade_cmd = __esm({
6242
6764
  init_ui();
6243
6765
  init_config();
6244
6766
  init_claude_commands();
6245
- init_errors();
6246
6767
  init_progress();
6768
+ init_npm();
6247
6769
  require4 = createRequire3(import.meta.url);
6248
6770
  pkg3 = require4("../../package.json");
6249
6771
  }
@@ -6255,8 +6777,8 @@ __export(savetoken_cmd_exports, {
6255
6777
  savetokenCommand: () => savetokenCommand
6256
6778
  });
6257
6779
  import { join as join17, relative } from "path";
6258
- import fs22 from "fs-extra";
6259
- import chalk6 from "chalk";
6780
+ import fs24 from "fs-extra";
6781
+ import chalk7 from "chalk";
6260
6782
  async function savetokenCommand(cwd) {
6261
6783
  const { createSpinner: createSpinner2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
6262
6784
  console.log("");
@@ -6280,14 +6802,14 @@ async function savetokenCommand(cwd) {
6280
6802
  console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6281
6803
  for (const sdk of sdks) {
6282
6804
  const count = callSites.filter((s) => s.sdk === sdk).length;
6283
- console.log(` ${chalk6.cyan(sdk.padEnd(20))} ${chalk6.white(String(count))}\uAC74`);
6805
+ console.log(` ${chalk7.cyan(sdk.padEnd(20))} ${chalk7.white(String(count))}\uAC74`);
6284
6806
  }
6285
6807
  if (imports.length > 0) {
6286
6808
  console.log("");
6287
6809
  console.log(colors.dim(" SDK \uC784\uD3EC\uD2B8"));
6288
6810
  console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6289
6811
  for (const site of imports) {
6290
- console.log(` ${colors.dim(site.file)}:${chalk6.yellow(String(site.line))}`);
6812
+ console.log(` ${colors.dim(site.file)}:${chalk7.yellow(String(site.line))}`);
6291
6813
  console.log(` ${site.content.trim().substring(0, 80)}`);
6292
6814
  }
6293
6815
  }
@@ -6296,7 +6818,7 @@ async function savetokenCommand(cwd) {
6296
6818
  console.log(colors.dim(" AI API \uD638\uCD9C \uC9C0\uC810"));
6297
6819
  console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6298
6820
  for (const site of apiCalls) {
6299
- console.log(` ${colors.dim(site.file)}:${chalk6.yellow(String(site.line))} ${chalk6.cyan(site.sdk)}`);
6821
+ console.log(` ${colors.dim(site.file)}:${chalk7.yellow(String(site.line))} ${chalk7.cyan(site.sdk)}`);
6300
6822
  console.log(` ${site.content.trim().substring(0, 80)}`);
6301
6823
  }
6302
6824
  }
@@ -6305,7 +6827,7 @@ async function savetokenCommand(cwd) {
6305
6827
  console.log(colors.dim(" Fetch \uAE30\uBC18 AI \uD638\uCD9C"));
6306
6828
  console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6307
6829
  for (const site of fetchCalls) {
6308
- console.log(` ${colors.dim(site.file)}:${chalk6.yellow(String(site.line))} ${chalk6.cyan(site.sdk)}`);
6830
+ console.log(` ${colors.dim(site.file)}:${chalk7.yellow(String(site.line))} ${chalk7.cyan(site.sdk)}`);
6309
6831
  console.log(` ${site.content.trim().substring(0, 80)}`);
6310
6832
  }
6311
6833
  }
@@ -6313,22 +6835,22 @@ async function savetokenCommand(cwd) {
6313
6835
  console.log("");
6314
6836
  console.log(colors.accent(" \uC808\uAC10 \uAC00\uB2A5\uC131 \uCD94\uC815"));
6315
6837
  console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6316
- console.log(` \uCD1D API \uD638\uCD9C \uC9C0\uC810 ${chalk6.white(String(totalApiPoints))}\uAC74`);
6317
- console.log(` \uBD84\uC11D \uD544\uC694 ${chalk6.yellow("Claude Code\uC5D0\uC11C \uC2EC\uCE35 \uBD84\uC11D \uD544\uC694")}`);
6838
+ console.log(` \uCD1D API \uD638\uCD9C \uC9C0\uC810 ${chalk7.white(String(totalApiPoints))}\uAC74`);
6839
+ console.log(` \uBD84\uC11D \uD544\uC694 ${chalk7.yellow("Claude Code\uC5D0\uC11C \uC2EC\uCE35 \uBD84\uC11D \uD544\uC694")}`);
6318
6840
  console.log("");
6319
6841
  console.log(colors.dim(" \uC601\uD5A5\uB3C4\uBCC4 \uB300\uCCB4 \uC804\uB7B5"));
6320
6842
  console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6321
- console.log(` ${chalk6.green("\u25CF")} \uB0AE\uC74C \uD14D\uC2A4\uD2B8 \uD3EC\uB9F7\uD305, \uC720\uD6A8\uC131 \uAC80\uC99D, \uD15C\uD50C\uB9BF \uC0DD\uC131`);
6843
+ console.log(` ${chalk7.green("\u25CF")} \uB0AE\uC74C \uD14D\uC2A4\uD2B8 \uD3EC\uB9F7\uD305, \uC720\uD6A8\uC131 \uAC80\uC99D, \uD15C\uD50C\uB9BF \uC0DD\uC131`);
6322
6844
  console.log(` \u2192 ${colors.dim("regex, JSON schema, \uD15C\uD50C\uB9BF \uC5D4\uC9C4\uC73C\uB85C \uC989\uC2DC \uB300\uCCB4")}`);
6323
- console.log(` ${chalk6.yellow("\u25CF")} \uC911\uAC04 \uB370\uC774\uD130 \uBD84\uB958, \uC694\uC57D, \uAC04\uB2E8\uD55C \uCD94\uCD9C`);
6845
+ console.log(` ${chalk7.yellow("\u25CF")} \uC911\uAC04 \uB370\uC774\uD130 \uBD84\uB958, \uC694\uC57D, \uAC04\uB2E8\uD55C \uCD94\uCD9C`);
6324
6846
  console.log(` \u2192 ${colors.dim("\uB8F0 \uAE30\uBC18 \uB85C\uC9C1, \uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uC73C\uB85C \uAC80\uD1A0 \uD6C4 \uB300\uCCB4")}`);
6325
- console.log(` ${chalk6.red("\u25CF")} \uB192\uC74C \uCF54\uB4DC \uC0DD\uC131, \uBCF5\uC7A1\uD55C \uCD94\uB860, \uCC3D\uC758\uC801 \uC0DD\uC131`);
6847
+ console.log(` ${chalk7.red("\u25CF")} \uB192\uC74C \uCF54\uB4DC \uC0DD\uC131, \uBCF5\uC7A1\uD55C \uCD94\uB860, \uCC3D\uC758\uC801 \uC0DD\uC131`);
6326
6848
  console.log(` \u2192 ${colors.dim("AI \uD544\uC218 \u2014 \uD504\uB86C\uD504\uD2B8 \uCD5C\uC801\uD654\uB85C \uD1A0\uD070 \uC808\uAC10")}`);
6327
6849
  const reportDir = join17(cwd, ".pai");
6328
- await fs22.ensureDir(reportDir);
6850
+ await fs24.ensureDir(reportDir);
6329
6851
  const report = buildReport(callSites, cwd);
6330
6852
  const reportPath = join17(reportDir, "savetoken-report.md");
6331
- await fs22.writeFile(reportPath, report, "utf8");
6853
+ await fs24.writeFile(reportPath, report, "utf8");
6332
6854
  console.log("");
6333
6855
  success("\uC2A4\uCE94 \uB9AC\uD3EC\uD2B8 \uC800\uC7A5: .pai/savetoken-report.md");
6334
6856
  console.log("");
@@ -6486,8 +7008,8 @@ __export(wakeup_cmd_exports, {
6486
7008
  });
6487
7009
  import { join as join18 } from "path";
6488
7010
  import { homedir as homedir3, platform as osPlatform } from "os";
6489
- import fs23 from "fs-extra";
6490
- import chalk7 from "chalk";
7011
+ import fs25 from "fs-extra";
7012
+ import chalk8 from "chalk";
6491
7013
  async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
6492
7014
  if (timeOrAction === "off") {
6493
7015
  await disableWakeup();
@@ -6523,8 +7045,8 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
6523
7045
  name: "launchMode",
6524
7046
  message: "\uC2E4\uD589 \uBAA8\uB4DC:",
6525
7047
  choices: [
6526
- { name: `claude ${chalk7.gray("\uC77C\uBC18 \uBAA8\uB4DC")}`, value: "normal" },
6527
- { name: `claude --dangerously-skip-permissions ${chalk7.gray("claude-YOLO mode")}`, value: "yolo" }
7048
+ { name: `claude ${chalk8.gray("\uC77C\uBC18 \uBAA8\uB4DC")}`, value: "normal" },
7049
+ { name: `claude --dangerously-skip-permissions ${chalk8.gray("claude-YOLO mode")}`, value: "yolo" }
6528
7050
  ]
6529
7051
  }]);
6530
7052
  const config = {
@@ -6533,9 +7055,9 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
6533
7055
  projectDir,
6534
7056
  launchMode
6535
7057
  };
6536
- await fs23.ensureDir(PAI_DIR2);
6537
- await fs23.writeJson(CONFIG_FILE2, config, { spaces: 2 });
6538
- await fs23.writeJson(MESSAGES_FILE, MESSAGES);
7058
+ await fs25.ensureDir(PAI_DIR2);
7059
+ await fs25.writeJson(CONFIG_FILE2, config, { spaces: 2 });
7060
+ await fs25.writeJson(MESSAGES_FILE, MESSAGES);
6539
7061
  await createWakeupScript(config);
6540
7062
  if (osPlatform() === "darwin") {
6541
7063
  await setupMacOS(config);
@@ -6548,12 +7070,12 @@ async function wakeupCommand(timeOrAction, schedule = "\uD3C9\uC77C") {
6548
7070
  success("\u2600\uFE0F \uC6E8\uC774\uD06C\uC5C5 \uC124\uC815 \uC644\uB8CC");
6549
7071
  console.log("");
6550
7072
  console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6551
- console.log(` \uC2DC\uAC04 ${chalk7.white(config.time)}`);
6552
- console.log(` \uC2A4\uCF00\uC904 ${chalk7.white(scheduleToLabel(config.schedule))}`);
6553
- console.log(` \uD504\uB85C\uC81D\uD2B8 ${chalk7.white(config.projectDir)}`);
6554
- console.log(` \uBAA8\uB4DC ${chalk7.white(config.launchMode === "yolo" ? "claude-YOLO mode" : "\uC77C\uBC18 \uBAA8\uB4DC")}`);
7073
+ console.log(` \uC2DC\uAC04 ${chalk8.white(config.time)}`);
7074
+ console.log(` \uC2A4\uCF00\uC904 ${chalk8.white(scheduleToLabel(config.schedule))}`);
7075
+ console.log(` \uD504\uB85C\uC81D\uD2B8 ${chalk8.white(config.projectDir)}`);
7076
+ console.log(` \uBAA8\uB4DC ${chalk8.white(config.launchMode === "yolo" ? "claude-YOLO mode" : "\uC77C\uBC18 \uBAA8\uB4DC")}`);
6555
7077
  if (osPlatform() === "darwin") {
6556
- console.log(` \uC2A4\uCF00\uC904\uB7EC ${chalk7.white("launchd + pmset (wake-from-sleep)")}`);
7078
+ console.log(` \uC2A4\uCF00\uC904\uB7EC ${chalk8.white("launchd + pmset (wake-from-sleep)")}`);
6557
7079
  }
6558
7080
  console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
6559
7081
  console.log("");
@@ -6566,7 +7088,7 @@ async function setupMacOS(config) {
6566
7088
  const { execa } = await import("execa");
6567
7089
  const [hour, minute] = config.time.split(":").map(Number);
6568
7090
  const plistDir = join18(homedir3(), "Library", "LaunchAgents");
6569
- await fs23.ensureDir(plistDir);
7091
+ await fs25.ensureDir(plistDir);
6570
7092
  const weekdays = scheduleToWeekdays(config.schedule);
6571
7093
  let calendarEntries;
6572
7094
  if (weekdays.length === 7) {
@@ -6602,7 +7124,7 @@ ${calendarEntries}
6602
7124
  <string>${PAI_DIR2}/wakeup.log</string>
6603
7125
  </dict>
6604
7126
  </plist>`;
6605
- await fs23.writeFile(PLIST_PATH, plist);
7127
+ await fs25.writeFile(PLIST_PATH, plist);
6606
7128
  await execa("launchctl", ["unload", PLIST_PATH]).catch(() => {
6607
7129
  });
6608
7130
  await execa("launchctl", ["load", PLIST_PATH]);
@@ -6625,7 +7147,7 @@ async function setupWindows(config) {
6625
7147
  const { execa } = await import("execa");
6626
7148
  const [hour, minute] = config.time.split(":").map(Number);
6627
7149
  const psScriptDir = join18(homedir3(), ".pai");
6628
- await fs23.ensureDir(psScriptDir);
7150
+ await fs25.ensureDir(psScriptDir);
6629
7151
  const psScriptPath = join18(psScriptDir, "wakeup.ps1");
6630
7152
  const claudeCmd = config.launchMode === "yolo" ? "claude --dangerously-skip-permissions" : "claude";
6631
7153
  const psScript = `# PAI Wakeup \u2014 Claude Code \uC138\uC158 \uC790\uB3D9 \uC2DC\uC791
@@ -6655,7 +7177,7 @@ $notifier.Show([Windows.UI.Notifications.ToastNotification]::new($xml))
6655
7177
  # Open PowerShell with Claude Code
6656
7178
  Start-Process powershell -ArgumentList "-NoExit", "-Command", "Get-Content '$todayFile'; Write-Host ''; Set-Location '${config.projectDir}'; ${claudeCmd}"
6657
7179
  `;
6658
- await fs23.writeFile(psScriptPath, psScript, "utf8");
7180
+ await fs25.writeFile(psScriptPath, psScript, "utf8");
6659
7181
  const daysMap = {
6660
7182
  "\uD3C9\uC77C": "MON,TUE,WED,THU,FRI",
6661
7183
  "\uB9E4\uC77C": "MON,TUE,WED,THU,FRI,SAT,SUN",
@@ -6703,7 +7225,7 @@ async function disableWakeup() {
6703
7225
  if (osPlatform() === "darwin") {
6704
7226
  await execa("launchctl", ["unload", PLIST_PATH]).catch(() => {
6705
7227
  });
6706
- await fs23.remove(PLIST_PATH).catch(() => {
7228
+ await fs25.remove(PLIST_PATH).catch(() => {
6707
7229
  });
6708
7230
  success("launchd \uC2A4\uCF00\uC904 \uC81C\uAC70");
6709
7231
  console.log("");
@@ -6724,23 +7246,23 @@ async function disableWakeup() {
6724
7246
  } else {
6725
7247
  await removeCronEntry();
6726
7248
  }
6727
- await fs23.remove(CONFIG_FILE2).catch(() => {
7249
+ await fs25.remove(CONFIG_FILE2).catch(() => {
6728
7250
  });
6729
7251
  console.log("");
6730
7252
  success("\u2600\uFE0F \uC6E8\uC774\uD06C\uC5C5 \uD574\uC81C \uC644\uB8CC");
6731
7253
  }
6732
7254
  async function showStatus() {
6733
- if (await fs23.pathExists(CONFIG_FILE2)) {
6734
- const config = await fs23.readJson(CONFIG_FILE2);
7255
+ if (await fs25.pathExists(CONFIG_FILE2)) {
7256
+ const config = await fs25.readJson(CONFIG_FILE2);
6735
7257
  console.log("");
6736
7258
  success("\u2600\uFE0F \uC6E8\uC774\uD06C\uC5C5 \uD65C\uC131\uD654");
6737
- console.log(` \uC2DC\uAC04 ${chalk7.white(config.time)}`);
6738
- console.log(` \uC2A4\uCF00\uC904 ${chalk7.white(scheduleToLabel(config.schedule))}`);
6739
- console.log(` \uD504\uB85C\uC81D\uD2B8 ${chalk7.white(config.projectDir)}`);
6740
- console.log(` \uBAA8\uB4DC ${chalk7.white(config.launchMode === "yolo" ? "claude-YOLO mode" : "\uC77C\uBC18 \uBAA8\uB4DC")}`);
7259
+ console.log(` \uC2DC\uAC04 ${chalk8.white(config.time)}`);
7260
+ console.log(` \uC2A4\uCF00\uC904 ${chalk8.white(scheduleToLabel(config.schedule))}`);
7261
+ console.log(` \uD504\uB85C\uC81D\uD2B8 ${chalk8.white(config.projectDir)}`);
7262
+ console.log(` \uBAA8\uB4DC ${chalk8.white(config.launchMode === "yolo" ? "claude-YOLO mode" : "\uC77C\uBC18 \uBAA8\uB4DC")}`);
6741
7263
  if (osPlatform() === "darwin") {
6742
- const plistExists = await fs23.pathExists(PLIST_PATH);
6743
- console.log(` launchd ${plistExists ? chalk7.green("\uD65C\uC131") : chalk7.red("\uBE44\uD65C\uC131")}`);
7264
+ const plistExists = await fs25.pathExists(PLIST_PATH);
7265
+ console.log(` launchd ${plistExists ? chalk8.green("\uD65C\uC131") : chalk8.red("\uBE44\uD65C\uC131")}`);
6744
7266
  }
6745
7267
  console.log("");
6746
7268
  } else {
@@ -6892,7 +7414,7 @@ fi
6892
7414
 
6893
7415
  echo "[$(date)] PAI Wakeup completed" >> "$LOG_FILE"
6894
7416
  `;
6895
- await fs23.writeFile(SCRIPT_FILE, script, { mode: 493 });
7417
+ await fs25.writeFile(SCRIPT_FILE, script, { mode: 493 });
6896
7418
  }
6897
7419
  var PAI_DIR2, CONFIG_FILE2, MESSAGES_FILE, SCRIPT_FILE, PLIST_NAME, PLIST_PATH, CRON_MARKER, MESSAGES;
6898
7420
  var init_wakeup_cmd = __esm({
@@ -7033,7 +7555,11 @@ function createProgram() {
7033
7555
  const { removeCommand: removeCommand2 } = await Promise.resolve().then(() => (init_remove_cmd(), remove_cmd_exports));
7034
7556
  await removeCommand2(process.cwd(), options);
7035
7557
  });
7036
- program2.command("upgrade").description("PAI \uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC & \uC2A4\uD0AC\uC744 \uCD5C\uC2E0 \uBC84\uC804\uC73C\uB85C \uC5C5\uADF8\uB808\uC774\uB4DC").option("--force", "\uBC84\uC804 \uCCB4\uD06C \uBB34\uC2DC\uD558\uACE0 \uAC15\uC81C \uC5C5\uADF8\uB808\uC774\uB4DC").action(async (options) => {
7558
+ program2.command("fetch [recipe]").description("\uC678\uBD80 GitHub \uC800\uC7A5\uC18C\uC5D0\uC11C \uB808\uC2DC\uD53C(OAuth, \uCC57\uBD07 \uB4F1) \uB2E4\uC6B4\uB85C\uB4DC").option("--list", "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uB808\uC2DC\uD53C \uBAA9\uB85D").option("--all", "\uB4F1\uB85D\uB41C \uBAA8\uB4E0 \uB808\uC2DC\uD53C \uC124\uCE58").option("--overwrite", "\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD30C\uC77C \uB36E\uC5B4\uC4F0\uAE30").action(async (recipe, options) => {
7559
+ const { fetchCommand: fetchCommand2 } = await Promise.resolve().then(() => (init_fetch_cmd(), fetch_cmd_exports));
7560
+ await fetchCommand2(process.cwd(), recipe, options);
7561
+ });
7562
+ program2.command("upgrade").description("CLI + \uD504\uB85C\uC81D\uD2B8 \uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC\uB97C \uCD5C\uC2E0 \uBC84\uC804\uC73C\uB85C \uC5C5\uADF8\uB808\uC774\uB4DC").option("--force", "\uBC84\uC804 \uCCB4\uD06C \uBB34\uC2DC\uD558\uACE0 \uAC15\uC81C \uC5C5\uADF8\uB808\uC774\uB4DC").option("--skip-cli", "CLI \uC5C5\uADF8\uB808\uC774\uB4DC \uC0DD\uB7B5 (\uD504\uB85C\uC81D\uD2B8 \uD30C\uC77C\uB9CC)").option("--skip-project", "\uD504\uB85C\uC81D\uD2B8 \uD30C\uC77C \uC5C5\uADF8\uB808\uC774\uB4DC \uC0DD\uB7B5 (CLI\uB9CC)").action(async (options) => {
7037
7563
  const { upgradeCommand: upgradeCommand2 } = await Promise.resolve().then(() => (init_upgrade_cmd(), upgrade_cmd_exports));
7038
7564
  await upgradeCommand2(process.cwd(), options);
7039
7565
  });