@shi_zhen/code-helper 0.1.1 → 0.1.3

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/cli/index.js CHANGED
@@ -38,7 +38,7 @@ var require_package = __commonJS({
38
38
  "package.json"(exports2, module2) {
39
39
  module2.exports = {
40
40
  name: "@shi_zhen/code-helper",
41
- version: "0.1.1",
41
+ version: "0.1.3",
42
42
  description: "Code Helper - A unified tool to manage your Claude Code",
43
43
  repository: {
44
44
  type: "git",
@@ -198,10 +198,10 @@ async function getRemoteVersion() {
198
198
  }
199
199
  function readCache() {
200
200
  try {
201
- if (!fs9.existsSync(CACHE_FILE)) {
201
+ if (!fs10.existsSync(CACHE_FILE)) {
202
202
  return null;
203
203
  }
204
- const content = fs9.readFileSync(CACHE_FILE, "utf-8");
204
+ const content = fs10.readFileSync(CACHE_FILE, "utf-8");
205
205
  return JSON.parse(content);
206
206
  } catch {
207
207
  return null;
@@ -209,10 +209,10 @@ function readCache() {
209
209
  }
210
210
  function writeCache(data) {
211
211
  try {
212
- if (!fs9.existsSync(CACHE_DIR)) {
213
- fs9.mkdirSync(CACHE_DIR, { recursive: true });
212
+ if (!fs10.existsSync(CACHE_DIR)) {
213
+ fs10.mkdirSync(CACHE_DIR, { recursive: true });
214
214
  }
215
- fs9.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
215
+ fs10.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
216
216
  } catch (error) {
217
217
  }
218
218
  }
@@ -312,18 +312,18 @@ async function testUpdate() {
312
312
  import_chalk3.default.cyan("\n\u{1F4A1} \u63D0\u793A: \u53D1\u5E03\u65B0\u7248\u672C\u540E\uFF0C\u7528\u6237\u9996\u6B21\u542F\u52A8\u65F6\u4F1A\u81EA\u52A8\u66F4\u65B0\n")
313
313
  );
314
314
  }
315
- var import_chalk3, import_ora7, os10, path10, fs9, CACHE_DIR, CACHE_FILE, CHECK_INTERVAL;
315
+ var import_chalk3, import_ora7, os11, path11, fs10, CACHE_DIR, CACHE_FILE, CHECK_INTERVAL;
316
316
  var init_version = __esm({
317
317
  "src/cli/utils/version.ts"() {
318
318
  "use strict";
319
319
  import_chalk3 = __toESM(require("chalk"));
320
320
  import_ora7 = __toESM(require("ora"));
321
321
  init_exec();
322
- os10 = require("os");
323
- path10 = require("path");
324
- fs9 = require("fs");
325
- CACHE_DIR = path10.join(os10.homedir(), ".code-helper");
326
- CACHE_FILE = path10.join(CACHE_DIR, "version-check.json");
322
+ os11 = require("os");
323
+ path11 = require("path");
324
+ fs10 = require("fs");
325
+ CACHE_DIR = path11.join(os11.homedir(), ".code-helper");
326
+ CACHE_FILE = path11.join(CACHE_DIR, "version-check.json");
327
327
  CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
328
328
  }
329
329
  });
@@ -1345,7 +1345,7 @@ var COMMON_PROXY_PORTS = [
1345
1345
  var INSTALL_METHODS = [
1346
1346
  {
1347
1347
  name: "\u5B98\u65B9\u811A\u672C\u5B89\u88C5",
1348
- command: "curl -fsSL https://claude.ai/install.sh | bash",
1348
+ command: 'temp=$(mktemp) && trap "rm -f $temp" EXIT && curl -fsSL https://claude.ai/install.sh -o "$temp" && bash "$temp"',
1349
1349
  description: "\u9700\u8981\u4EE3\u7406\u6216\u5916\u7F51\u8BBF\u95EE",
1350
1350
  platforms: ["darwin", "linux"],
1351
1351
  recommended: true
@@ -1589,7 +1589,7 @@ async function showInstallMenu() {
1589
1589
  let envVars = {};
1590
1590
  if (proxyUrl) {
1591
1591
  if (finalCommand.includes("curl")) {
1592
- finalCommand = finalCommand.replace("curl ", `curl --proxy ${proxyUrl} `);
1592
+ finalCommand = finalCommand.replace(/curl\s+/g, `curl --proxy ${proxyUrl} `);
1593
1593
  } else {
1594
1594
  envVars = {
1595
1595
  http_proxy: proxyUrl,
@@ -3259,11 +3259,248 @@ async function handleInstallAll(marketplacePlugins) {
3259
3259
  }
3260
3260
 
3261
3261
  // src/cli/menus/settings.ts
3262
- var import_fs8 = __toESM(require("fs"));
3262
+ var import_fs9 = __toESM(require("fs"));
3263
3263
  var import_inquirer7 = __toESM(require("inquirer"));
3264
+ var import_os10 = __toESM(require("os"));
3265
+ var import_path10 = __toESM(require("path"));
3266
+
3267
+ // src/cli/utils/permissions.ts
3268
+ var import_fs8 = __toESM(require("fs"));
3264
3269
  var import_os9 = __toESM(require("os"));
3265
3270
  var import_path9 = __toESM(require("path"));
3266
3271
  var SETTINGS_FILE2 = import_path9.default.join(import_os9.default.homedir(), ".claude", "settings.json");
3272
+ var PERMISSION_RULES = {
3273
+ // 中等权限配置
3274
+ moderate: {
3275
+ allow: [
3276
+ // 文件操作
3277
+ "ReadFile(*)",
3278
+ "WriteFile(*)",
3279
+ "EditFile(*)",
3280
+ "Glob(*)",
3281
+ "Grep(*)",
3282
+ // Git 常规操作
3283
+ "Bash(git:status*)",
3284
+ "Bash(git:add*)",
3285
+ "Bash(git:commit*)",
3286
+ "Bash(git:pull*)",
3287
+ "Bash(git:branch*)",
3288
+ "Bash(git:checkout*)",
3289
+ "Bash(git:diff*)",
3290
+ "Bash(git:log*)",
3291
+ "Bash(git:clone*)",
3292
+ "Bash(git:stash*)",
3293
+ "Bash(git:show*)",
3294
+ "Bash(git:fetch*)",
3295
+ // Node/NPM
3296
+ "Bash(node:*)",
3297
+ "Bash(npm:run*)",
3298
+ "Bash(npm:test*)",
3299
+ "Bash(npm:install*)",
3300
+ "Bash(npx:*)",
3301
+ // Python
3302
+ "Bash(python:*)",
3303
+ "Bash(python3:*)",
3304
+ "Bash(pip:install*)",
3305
+ "Bash(pip3:install*)",
3306
+ // 常用工具
3307
+ "Bash(cat:*)",
3308
+ "Bash(ls:*)",
3309
+ "Bash(grep:*)",
3310
+ "Bash(find:*)",
3311
+ "Bash(curl:*)",
3312
+ "Bash(wget:*)",
3313
+ "Bash(echo:*)",
3314
+ "Bash(mkdir:*)",
3315
+ "Bash(cp:*)",
3316
+ "Bash(mv:*)",
3317
+ "Bash(touch:*)",
3318
+ "Bash(pwd:*)",
3319
+ "Bash(cd:*)"
3320
+ ],
3321
+ deny: [
3322
+ // 高危操作
3323
+ "Bash(rm:-rf*)",
3324
+ "Bash(rm:*-rf*)",
3325
+ "Bash(rm:*-r*-f*)",
3326
+ "Bash(sudo:*)",
3327
+ "Bash(git:push*--force*)",
3328
+ "Bash(git:push*-f*)",
3329
+ "Bash(chmod:*)",
3330
+ "Bash(chown:*)",
3331
+ // 反黑客操作
3332
+ "Bash(nc:*-e*)",
3333
+ // 反向 shell
3334
+ "Bash(bash:*-i*)",
3335
+ // 交互式 bash
3336
+ "Bash(*>&*)",
3337
+ // 重定向到网络
3338
+ "Bash(*base64*-d*)",
3339
+ // 可疑的 base64 解码
3340
+ "Bash(wget:*&&*)",
3341
+ // 下载并执行
3342
+ "Bash(curl:*|*sh*)",
3343
+ // 管道执行
3344
+ "Bash(curl:*|*bash*)"
3345
+ ]
3346
+ },
3347
+ // 最大权限配置
3348
+ maximum: {
3349
+ allow: [
3350
+ // 继承中等权限的所有规则
3351
+ "ReadFile(*)",
3352
+ "WriteFile(*)",
3353
+ "EditFile(*)",
3354
+ "Glob(*)",
3355
+ "Grep(*)",
3356
+ // Git 所有操作(除了强制推送)
3357
+ "Bash(git:*)",
3358
+ // Node/NPM
3359
+ "Bash(node:*)",
3360
+ "Bash(npm:*)",
3361
+ "Bash(npx:*)",
3362
+ // Python
3363
+ "Bash(python:*)",
3364
+ "Bash(python3:*)",
3365
+ "Bash(pip:*)",
3366
+ "Bash(pip3:*)",
3367
+ // 常用工具
3368
+ "Bash(cat:*)",
3369
+ "Bash(ls:*)",
3370
+ "Bash(grep:*)",
3371
+ "Bash(find:*)",
3372
+ "Bash(curl:*)",
3373
+ "Bash(wget:*)",
3374
+ "Bash(echo:*)",
3375
+ "Bash(mkdir:*)",
3376
+ "Bash(cp:*)",
3377
+ "Bash(mv:*)",
3378
+ "Bash(touch:*)",
3379
+ "Bash(pwd:*)",
3380
+ "Bash(cd:*)",
3381
+ // 额外允许的操作
3382
+ "Bash(rm:*)"
3383
+ // 允许删除,但不包括 rm -rf
3384
+ ],
3385
+ deny: [
3386
+ // 高危操作(需要确认)
3387
+ "Bash(rm:-rf*)",
3388
+ "Bash(rm:*-rf*)",
3389
+ "Bash(rm:*-r*-f*)",
3390
+ "Bash(sudo:*)",
3391
+ "Bash(git:push*--force*)",
3392
+ "Bash(git:push*-f*)",
3393
+ "Bash(dd:*)",
3394
+ "Bash(mkfs:*)",
3395
+ // 反黑客操作
3396
+ "Bash(nc:*-e*)",
3397
+ // 反向 shell
3398
+ "Bash(bash:*-i*)",
3399
+ // 交互式 bash
3400
+ "Bash(*>&*)",
3401
+ // 重定向到网络
3402
+ "Bash(*base64*-d*)",
3403
+ // 可疑的 base64 解码
3404
+ "Bash(wget:*&&*)",
3405
+ // 下载并执行
3406
+ "Bash(curl:*|*sh*)",
3407
+ // 管道执行
3408
+ "Bash(curl:*|*bash*)"
3409
+ ]
3410
+ }
3411
+ };
3412
+ var PERMISSION_DESCRIPTIONS = {
3413
+ ["default" /* DEFAULT */]: {
3414
+ name: "\u9ED8\u8BA4",
3415
+ description: "\u6BCF\u6B21\u5371\u9669\u64CD\u4F5C\u90FD\u9700\u8981\u786E\u8BA4\uFF08Claude Code \u539F\u59CB\u884C\u4E3A\uFF09",
3416
+ autoAllow: [],
3417
+ needConfirm: ["\u6240\u6709\u53EF\u80FD\u5371\u9669\u7684\u64CD\u4F5C"]
3418
+ },
3419
+ ["moderate" /* MODERATE */]: {
3420
+ name: "\u4E2D\u7B49\u6743\u9650",
3421
+ description: "\u81EA\u52A8\u5141\u8BB8\u5E38\u89C4\u64CD\u4F5C\uFF0C\u53EA\u786E\u8BA4\u5220\u9664\u548C\u5371\u9669\u547D\u4EE4",
3422
+ autoAllow: [
3423
+ "\u6587\u4EF6\u64CD\u4F5C\uFF08\u8BFB/\u5199/\u7F16\u8F91\uFF09",
3424
+ "Git \u5E38\u89C4\u64CD\u4F5C\uFF08status/add/commit/pull\u7B49\uFF09",
3425
+ "Node/NPM \u64CD\u4F5C",
3426
+ "Python/pip \u64CD\u4F5C",
3427
+ "\u5E38\u7528\u5DE5\u5177\uFF08cat/ls/grep/curl\u7B49\uFF09"
3428
+ ],
3429
+ needConfirm: [
3430
+ "rm -rf\uFF08\u5F3A\u5236\u9012\u5F52\u5220\u9664\uFF09",
3431
+ "sudo\uFF08\u7CFB\u7EDF\u6743\u9650\uFF09",
3432
+ "git push --force\uFF08\u5F3A\u5236\u63A8\u9001\uFF09",
3433
+ "chmod/chown\uFF08\u6743\u9650\u4FEE\u6539\uFF09"
3434
+ ]
3435
+ },
3436
+ ["maximum" /* MAXIMUM */]: {
3437
+ name: "\u6700\u5927\u6743\u9650",
3438
+ description: "\u81EA\u52A8\u5141\u8BB8\u51E0\u4E4E\u6240\u6709\u64CD\u4F5C\uFF0C\u53EA\u786E\u8BA4\u6781\u5EA6\u5371\u9669\u7684\u547D\u4EE4",
3439
+ autoAllow: [
3440
+ "\u6240\u6709\u4E2D\u7B49\u6743\u9650\u7684\u64CD\u4F5C",
3441
+ "git push\uFF08\u666E\u901A\u63A8\u9001\uFF09",
3442
+ "git rebase/merge",
3443
+ "\u6587\u4EF6\u5220\u9664\uFF08rm\uFF0C\u975E\u9012\u5F52\u5F3A\u5236\uFF09"
3444
+ ],
3445
+ needConfirm: [
3446
+ "rm -rf\uFF08\u5F3A\u5236\u9012\u5F52\u5220\u9664\uFF09",
3447
+ "sudo\uFF08\u7CFB\u7EDF\u6743\u9650\uFF09",
3448
+ "git push --force\uFF08\u5F3A\u5236\u63A8\u9001\uFF09",
3449
+ "dd/mkfs\uFF08\u78C1\u76D8\u64CD\u4F5C\uFF09",
3450
+ "curl | bash\uFF08\u7BA1\u9053\u6267\u884C\uFF09"
3451
+ ]
3452
+ }
3453
+ };
3454
+ function getCurrentPermissionLevel() {
3455
+ try {
3456
+ if (!import_fs8.default.existsSync(SETTINGS_FILE2)) {
3457
+ return "default" /* DEFAULT */;
3458
+ }
3459
+ const content = import_fs8.default.readFileSync(SETTINGS_FILE2, "utf-8");
3460
+ const settings = JSON.parse(content);
3461
+ if (!settings.permissions || !settings.permissionLevel) {
3462
+ return "default" /* DEFAULT */;
3463
+ }
3464
+ return settings.permissionLevel;
3465
+ } catch {
3466
+ return "default" /* DEFAULT */;
3467
+ }
3468
+ }
3469
+ function applyPermissionLevel(level) {
3470
+ let settings = {};
3471
+ try {
3472
+ if (import_fs8.default.existsSync(SETTINGS_FILE2)) {
3473
+ const content = import_fs8.default.readFileSync(SETTINGS_FILE2, "utf-8");
3474
+ settings = JSON.parse(content);
3475
+ }
3476
+ } catch {
3477
+ }
3478
+ if (level === "default" /* DEFAULT */) {
3479
+ delete settings.permissions;
3480
+ delete settings.permissionLevel;
3481
+ } else {
3482
+ const rules = PERMISSION_RULES[level];
3483
+ settings.permissions = {
3484
+ allow: rules.allow,
3485
+ deny: rules.deny
3486
+ };
3487
+ settings.permissionLevel = level;
3488
+ }
3489
+ const settingsDir = import_path9.default.dirname(SETTINGS_FILE2);
3490
+ if (!import_fs8.default.existsSync(settingsDir)) {
3491
+ import_fs8.default.mkdirSync(settingsDir, { recursive: true });
3492
+ }
3493
+ import_fs8.default.writeFileSync(SETTINGS_FILE2, JSON.stringify(settings, null, 2), "utf-8");
3494
+ }
3495
+ function getPermissionLevelName(level) {
3496
+ return PERMISSION_DESCRIPTIONS[level].name;
3497
+ }
3498
+ function getPermissionLevelDescription(level) {
3499
+ return PERMISSION_DESCRIPTIONS[level];
3500
+ }
3501
+
3502
+ // src/cli/menus/settings.ts
3503
+ var SETTINGS_FILE3 = import_path10.default.join(import_os10.default.homedir(), ".claude", "settings.json");
3267
3504
  var LANGUAGE_OPTIONS = [
3268
3505
  { name: "\u4E2D\u6587 (chinese)", value: "chinese" },
3269
3506
  { name: "English", value: "english" },
@@ -3289,6 +3526,10 @@ async function showSettingsMenu() {
3289
3526
  name: `\u57FA\u7840\u8BBE\u7F6E`,
3290
3527
  value: "basic" /* BASIC */
3291
3528
  },
3529
+ {
3530
+ name: `\u6743\u9650\u914D\u7F6E`,
3531
+ value: "permissions" /* PERMISSIONS */
3532
+ },
3292
3533
  {
3293
3534
  name: `\u67E5\u770B\u914D\u7F6E\u6587\u4EF6`,
3294
3535
  value: "view_config" /* VIEW_CONFIG */
@@ -3309,6 +3550,9 @@ async function showSettingsMenu() {
3309
3550
  case "basic" /* BASIC */:
3310
3551
  await showBasicSettings();
3311
3552
  break;
3553
+ case "permissions" /* PERMISSIONS */:
3554
+ await showPermissionsSettings();
3555
+ break;
3312
3556
  case "view_config" /* VIEW_CONFIG */:
3313
3557
  await handleViewConfig();
3314
3558
  break;
@@ -3507,11 +3751,11 @@ async function handleBooleanSetting(key, label) {
3507
3751
  }
3508
3752
  async function handleViewConfig() {
3509
3753
  console.log();
3510
- console.log(theme.primary(`\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84: ${SETTINGS_FILE2}`));
3754
+ console.log(theme.primary(`\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84: ${SETTINGS_FILE3}`));
3511
3755
  console.log();
3512
3756
  try {
3513
- if (import_fs8.default.existsSync(SETTINGS_FILE2)) {
3514
- const content = import_fs8.default.readFileSync(SETTINGS_FILE2, "utf-8");
3757
+ if (import_fs9.default.existsSync(SETTINGS_FILE3)) {
3758
+ const content = import_fs9.default.readFileSync(SETTINGS_FILE3, "utf-8");
3515
3759
  console.log(theme.dim("\u2500".repeat(50)));
3516
3760
  console.log(content);
3517
3761
  console.log(theme.dim("\u2500".repeat(50)));
@@ -3526,6 +3770,106 @@ async function handleViewConfig() {
3526
3770
  { type: "input", name: "continue", message: "\u6309\u56DE\u8F66\u952E\u7EE7\u7EED..." }
3527
3771
  ]);
3528
3772
  }
3773
+ async function showPermissionsSettings() {
3774
+ while (true) {
3775
+ showHeader();
3776
+ const currentLevel = getCurrentPermissionLevel();
3777
+ const currentLevelName = getPermissionLevelName(currentLevel);
3778
+ console.log(createBoxTitle(`\u6743\u9650\u914D\u7F6E [\u5F53\u524D: ${currentLevelName}]`));
3779
+ console.log();
3780
+ const { choice } = await import_inquirer7.default.prompt([
3781
+ {
3782
+ type: "list",
3783
+ name: "choice",
3784
+ message: "\u8BF7\u9009\u62E9\u6743\u9650\u7EA7\u522B\uFF1A",
3785
+ choices: [
3786
+ {
3787
+ name: `\u9ED8\u8BA4\uFF08\u6BCF\u6B21\u90FD\u786E\u8BA4\uFF09${currentLevel === "default" /* DEFAULT */ ? theme.success(" \u2713") : ""}`,
3788
+ value: "default" /* DEFAULT */
3789
+ },
3790
+ {
3791
+ name: `\u4E2D\u7B49\u6743\u9650\uFF08\u53EA\u786E\u8BA4\u5220\u9664\u548C\u5371\u9669\u547D\u4EE4\uFF09${currentLevel === "moderate" /* MODERATE */ ? theme.success(" \u2713") : ""}`,
3792
+ value: "moderate" /* MODERATE */
3793
+ },
3794
+ {
3795
+ name: `\u6700\u5927\u6743\u9650\uFF08\u53EA\u786E\u8BA4\u9AD8\u5371\u64CD\u4F5C\uFF09${currentLevel === "maximum" /* MAXIMUM */ ? theme.success(" \u2713") : ""}`,
3796
+ value: "maximum" /* MAXIMUM */
3797
+ },
3798
+ new import_inquirer7.default.Separator(),
3799
+ {
3800
+ name: `${theme.dim("<-")} \u8FD4\u56DE`,
3801
+ value: "__back__"
3802
+ },
3803
+ {
3804
+ name: `${theme.dim("X")} \u9000\u51FA`,
3805
+ value: "__exit__"
3806
+ }
3807
+ ]
3808
+ }
3809
+ ]);
3810
+ if (choice === "__back__") {
3811
+ return;
3812
+ }
3813
+ if (choice === "__exit__") {
3814
+ process.exit(0);
3815
+ }
3816
+ await handlePermissionLevelChange(choice);
3817
+ }
3818
+ }
3819
+ async function handlePermissionLevelChange(level) {
3820
+ const currentLevel = getCurrentPermissionLevel();
3821
+ if (level === currentLevel) {
3822
+ showInfo("\u5DF2\u7ECF\u662F\u5F53\u524D\u6743\u9650\u7EA7\u522B");
3823
+ console.log();
3824
+ await import_inquirer7.default.prompt([
3825
+ { type: "input", name: "continue", message: "\u6309\u56DE\u8F66\u952E\u7EE7\u7EED..." }
3826
+ ]);
3827
+ return;
3828
+ }
3829
+ if (level !== "default" /* DEFAULT */) {
3830
+ const confirmed = await showPermissionConfirmation(level);
3831
+ if (!confirmed) {
3832
+ return;
3833
+ }
3834
+ }
3835
+ try {
3836
+ applyPermissionLevel(level);
3837
+ const levelName = getPermissionLevelName(level);
3838
+ showSuccess(`\u6743\u9650\u914D\u7F6E\u5DF2\u66F4\u65B0\u4E3A\uFF1A${levelName}`);
3839
+ showInfo(`\u914D\u7F6E\u6587\u4EF6\uFF1A${SETTINGS_FILE3}`);
3840
+ } catch (error) {
3841
+ showError(`\u4FDD\u5B58\u5931\u8D25: ${error.message}`);
3842
+ }
3843
+ console.log();
3844
+ await import_inquirer7.default.prompt([
3845
+ { type: "input", name: "continue", message: "\u6309\u56DE\u8F66\u952E\u7EE7\u7EED..." }
3846
+ ]);
3847
+ }
3848
+ async function showPermissionConfirmation(level) {
3849
+ console.log();
3850
+ const desc = getPermissionLevelDescription(level);
3851
+ console.log(theme.warning(`\u4F60\u5373\u5C06\u5207\u6362\u5230\u300C${desc.name}\u300D\u6A21\u5F0F`));
3852
+ console.log();
3853
+ console.log(theme.success("\u5C06\u81EA\u52A8\u5141\u8BB8\u7684\u64CD\u4F5C\uFF1A"));
3854
+ desc.autoAllow.forEach((item) => {
3855
+ console.log(theme.success(` \u2713 ${item}`));
3856
+ });
3857
+ console.log();
3858
+ console.log(theme.warning("\u4ECD\u9700\u786E\u8BA4\u7684\u64CD\u4F5C\uFF1A"));
3859
+ desc.needConfirm.forEach((item) => {
3860
+ console.log(theme.warning(` \u26A0 ${item}`));
3861
+ });
3862
+ console.log();
3863
+ const { confirm } = await import_inquirer7.default.prompt([
3864
+ {
3865
+ type: "confirm",
3866
+ name: "confirm",
3867
+ message: "\u786E\u5B9A\u5207\u6362\u5417\uFF1F",
3868
+ default: false
3869
+ }
3870
+ ]);
3871
+ return confirm;
3872
+ }
3529
3873
 
3530
3874
  // src/cli/menus/main.ts
3531
3875
  async function showMainMenu() {