clawt 3.1.2 → 3.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/README.md CHANGED
@@ -43,10 +43,10 @@ clawt merge -b branch-1 -m "feat: 实现xxx功能"
43
43
  ```bash
44
44
  clawt init # 以当前分支作为主工作分支进行初始化
45
45
  clawt init -b <branch> # 指定主工作分支名
46
- clawt init show # 查看当前项目的 init 配置(JSON 格式)
46
+ clawt init show # 交互式查看和修改项目配置
47
47
  ```
48
48
 
49
- 设置项目的主工作分支。重复执行会更新主工作分支配置。
49
+ 设置项目的主工作分支。重复执行会更新主工作分支配置。`init show` 提供交互式面板,可查看和修改项目配置项(如 validate 成功后自动执行的命令)。
50
50
 
51
51
  ### `clawt run` — 创建 worktree 并执行任务
52
52
 
@@ -140,7 +140,7 @@ clawt validate -b <branch> -r "pnpm test & pnpm build" # 并行执行多个命
140
140
 
141
141
  当 patch apply 失败(目标分支与主分支差异过大)时,会自动询问是否执行 `sync` 同步主分支到目标 worktree,无需手动操作。
142
142
 
143
- `-r, --run` 选项可在 validate 成功后自动在主 worktree 中执行指定命令(如测试、构建等),命令执行失败不影响 validate 结果。支持用 `&` 分隔多个命令并行执行:
143
+ `-r, --run` 选项可在 validate 成功后自动在主 worktree 中执行指定命令(如测试、构建等),命令执行失败不影响 validate 结果。不传 `-r` 时会自动从项目配置的 `validateRunCommand` 读取(可通过 `clawt init show` 设置)。支持用 `&` 分隔多个命令并行执行:
144
144
 
145
145
  | 用法 | 行为 |
146
146
  | ---- | ---- |
package/dist/index.js CHANGED
@@ -460,7 +460,11 @@ var INIT_MESSAGES = {
460
460
  /** 项目未初始化(requireProjectConfig 使用) */
461
461
  PROJECT_NOT_INITIALIZED: "\u9879\u76EE\u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F",
462
462
  /** 项目配置缺少 clawtMainWorkBranch 字段 */
463
- PROJECT_CONFIG_MISSING_BRANCH: "\u9879\u76EE\u914D\u7F6E\u7F3A\u5C11\u4E3B\u5DE5\u4F5C\u5206\u652F\u4FE1\u606F\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F"
463
+ PROJECT_CONFIG_MISSING_BRANCH: "\u9879\u76EE\u914D\u7F6E\u7F3A\u5C11\u4E3B\u5DE5\u4F5C\u5206\u652F\u4FE1\u606F\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F",
464
+ /** init show 交互式面板选择配置项提示 */
465
+ INIT_SELECT_PROMPT: "\u9009\u62E9\u8981\u4FEE\u6539\u7684\u9879\u76EE\u914D\u7F6E\u9879",
466
+ /** init show 交互式面板配置项修改成功 */
467
+ INIT_SET_SUCCESS: (key, value) => `\u2713 \u9879\u76EE\u914D\u7F6E ${key} \u5DF2\u8BBE\u7F6E\u4E3A ${value}`
464
468
  };
465
469
 
466
470
  // src/constants/messages/interactive-panel.ts
@@ -608,6 +612,32 @@ function deriveConfigDescriptions(definitions) {
608
612
  var DEFAULT_CONFIG = deriveDefaultConfig(CONFIG_DEFINITIONS);
609
613
  var CONFIG_DESCRIPTIONS = deriveConfigDescriptions(CONFIG_DEFINITIONS);
610
614
 
615
+ // src/constants/project-config.ts
616
+ var PROJECT_CONFIG_DEFINITIONS = {
617
+ clawtMainWorkBranch: {
618
+ defaultValue: "",
619
+ description: "\u4E3B worktree \u7684\u5DE5\u4F5C\u5206\u652F\u540D"
620
+ },
621
+ validateRunCommand: {
622
+ defaultValue: void 0,
623
+ description: "validate \u6210\u529F\u540E\u81EA\u52A8\u6267\u884C\u7684\u547D\u4EE4\uFF08-r \u7684\u9ED8\u8BA4\u503C\uFF09"
624
+ }
625
+ };
626
+ function deriveDefaultConfig2(definitions) {
627
+ const entries = Object.entries(definitions).map(
628
+ ([key, def]) => [key, def.defaultValue]
629
+ );
630
+ return Object.fromEntries(entries);
631
+ }
632
+ function deriveConfigDescriptions2(definitions) {
633
+ const entries = Object.entries(definitions).map(
634
+ ([key, def]) => [key, def.description]
635
+ );
636
+ return Object.fromEntries(entries);
637
+ }
638
+ var PROJECT_DEFAULT_CONFIG = deriveDefaultConfig2(PROJECT_CONFIG_DEFINITIONS);
639
+ var PROJECT_CONFIG_DESCRIPTIONS = deriveConfigDescriptions2(PROJECT_CONFIG_DEFINITIONS);
640
+
611
641
  // src/constants/git.ts
612
642
  var AUTO_SAVE_COMMIT_MESSAGE = "chore: auto-save before sync";
613
643
 
@@ -1259,6 +1289,10 @@ function getMainWorkBranch() {
1259
1289
  const config2 = requireProjectConfig();
1260
1290
  return config2.clawtMainWorkBranch;
1261
1291
  }
1292
+ function getValidateRunCommand() {
1293
+ const config2 = loadProjectConfig();
1294
+ return config2?.validateRunCommand || void 0;
1295
+ }
1262
1296
 
1263
1297
  // src/utils/validate-branch.ts
1264
1298
  function getValidateBranchName(branchName) {
@@ -2717,7 +2751,7 @@ function parseConfigValue(key, rawValue) {
2717
2751
  }
2718
2752
  return { success: true, value: rawValue };
2719
2753
  }
2720
- async function promptConfigValue(key, currentValue) {
2754
+ async function promptConfigValue(key, currentValue, allowedValues) {
2721
2755
  const expectedType = typeof currentValue;
2722
2756
  if (expectedType === "boolean") {
2723
2757
  return promptBooleanValue(key, currentValue);
@@ -2725,18 +2759,43 @@ async function promptConfigValue(key, currentValue) {
2725
2759
  if (expectedType === "number") {
2726
2760
  return promptNumberValue(key, currentValue);
2727
2761
  }
2728
- const definition = CONFIG_DEFINITIONS[key];
2729
- if (definition.allowedValues) {
2730
- return promptEnumValue(key, currentValue, definition.allowedValues);
2762
+ if (allowedValues) {
2763
+ return promptEnumValue(key, currentValue, allowedValues);
2731
2764
  }
2732
2765
  return promptStringValue(key, currentValue);
2733
2766
  }
2734
2767
  function formatConfigValue(value) {
2768
+ if (value === void 0 || value === null) {
2769
+ return chalk7.dim("(\u672A\u8BBE\u7F6E)");
2770
+ }
2735
2771
  if (typeof value === "boolean") {
2736
2772
  return value ? chalk7.green("true") : chalk7.yellow("false");
2737
2773
  }
2738
2774
  return chalk7.cyan(String(value));
2739
2775
  }
2776
+ async function interactiveConfigEditor(config2, definitions, options) {
2777
+ const keys = Object.keys(definitions);
2778
+ const disabledKeys = options?.disabledKeys ?? {};
2779
+ const configRecord = config2;
2780
+ const choices = keys.map((k) => {
2781
+ const isDisabled = k in disabledKeys;
2782
+ const value = configRecord[k];
2783
+ const isObject = typeof value === "object" && value !== null;
2784
+ return {
2785
+ name: k,
2786
+ message: `${k}: ${isObject || isDisabled ? chalk7.dim(isObject ? JSON.stringify(value) : String(value ?? "")) : formatConfigValue(value)} ${chalk7.dim(`\u2014 ${definitions[k].description}`)}`,
2787
+ ...isDisabled && { disabled: disabledKeys[k] }
2788
+ };
2789
+ });
2790
+ const selectedKey = await new Enquirer4.Select({
2791
+ message: options?.selectPrompt ?? MESSAGES.CONFIG_SELECT_PROMPT,
2792
+ choices
2793
+ }).run();
2794
+ const currentValue = configRecord[selectedKey];
2795
+ const definition = definitions[selectedKey];
2796
+ const newValue = await promptConfigValue(selectedKey, currentValue, definition.allowedValues);
2797
+ return { key: selectedKey, newValue };
2798
+ }
2740
2799
  async function promptBooleanValue(key, currentValue) {
2741
2800
  const choices = [
2742
2801
  { name: "true", message: "true" },
@@ -2774,7 +2833,7 @@ async function promptEnumValue(key, currentValue, allowedValues) {
2774
2833
  async function promptStringValue(key, currentValue) {
2775
2834
  return await new Enquirer4.Input({
2776
2835
  message: MESSAGES.CONFIG_INPUT_PROMPT(key),
2777
- initial: currentValue
2836
+ initial: currentValue || ""
2778
2837
  }).run();
2779
2838
  }
2780
2839
 
@@ -2909,56 +2968,6 @@ async function checkForUpdates(currentVersion) {
2909
2968
  }
2910
2969
  }
2911
2970
 
2912
- // src/utils/json.ts
2913
- function primitiveToString(value) {
2914
- if (value === void 0) {
2915
- return "undefined";
2916
- }
2917
- if (value === null) {
2918
- return "null";
2919
- }
2920
- if (typeof value === "symbol") {
2921
- return value.toString();
2922
- }
2923
- if (typeof value === "function") {
2924
- return `[Function: ${value.name || "anonymous"}]`;
2925
- }
2926
- return String(value);
2927
- }
2928
- function safeStringify(value, indent = 2) {
2929
- if (value === null || typeof value !== "object") {
2930
- return primitiveToString(value);
2931
- }
2932
- try {
2933
- const seen = /* @__PURE__ */ new WeakSet();
2934
- return JSON.stringify(
2935
- value,
2936
- (_key, val) => {
2937
- if (typeof val === "bigint") {
2938
- return val.toString();
2939
- }
2940
- if (typeof val === "undefined" || typeof val === "function" || typeof val === "symbol") {
2941
- return primitiveToString(val);
2942
- }
2943
- if (typeof val === "object" && val !== null) {
2944
- if (seen.has(val)) {
2945
- return "[Circular]";
2946
- }
2947
- seen.add(val);
2948
- }
2949
- return val;
2950
- },
2951
- indent
2952
- );
2953
- } catch {
2954
- try {
2955
- return JSON.stringify(String(value), null, indent);
2956
- } catch {
2957
- return "[Unserializable]";
2958
- }
2959
- }
2960
- }
2961
-
2962
2971
  // src/utils/interactive-panel.ts
2963
2972
  import { createInterface as createInterface2 } from "readline";
2964
2973
 
@@ -4116,6 +4125,12 @@ async function executeRunCommand(command, mainWorktreePath) {
4116
4125
  await executeParallelCommands(commands, mainWorktreePath);
4117
4126
  }
4118
4127
  }
4128
+ function resolveRunCommand(optionRun) {
4129
+ if (optionRun) {
4130
+ return optionRun;
4131
+ }
4132
+ return getValidateRunCommand();
4133
+ }
4119
4134
  async function handleValidate(options) {
4120
4135
  if (options.clean) {
4121
4136
  await handleValidateClean(options);
@@ -4148,8 +4163,9 @@ async function handleValidate(options) {
4148
4163
  }
4149
4164
  await handleFirstValidate(targetWorktreePath, mainWorktreePath, projectName, branchName, hasUncommitted);
4150
4165
  }
4151
- if (options.run) {
4152
- await executeRunCommand(options.run, mainWorktreePath);
4166
+ const runCommand = resolveRunCommand(options.run);
4167
+ if (runCommand) {
4168
+ await executeRunCommand(runCommand, mainWorktreePath);
4153
4169
  }
4154
4170
  }
4155
4171
 
@@ -4274,8 +4290,6 @@ async function handleMerge(options) {
4274
4290
  }
4275
4291
 
4276
4292
  // src/commands/config.ts
4277
- import chalk11 from "chalk";
4278
- import Enquirer5 from "enquirer";
4279
4293
  function registerConfigCommand(program2) {
4280
4294
  const configCmd = program2.command("config").description("\u4EA4\u4E92\u5F0F\u67E5\u770B\u548C\u4FEE\u6539\u5168\u5C40\u914D\u7F6E").action(async () => {
4281
4295
  await handleConfigSet();
@@ -4329,25 +4343,19 @@ async function handleConfigSet(key, value) {
4329
4343
  }
4330
4344
  async function handleInteractiveConfigSet() {
4331
4345
  const config2 = loadConfig();
4332
- const keys = Object.keys(DEFAULT_CONFIG);
4333
4346
  logger.info("config set \u547D\u4EE4\u6267\u884C\uFF0C\u8FDB\u5165\u4EA4\u4E92\u5F0F\u914D\u7F6E");
4334
- const choices = keys.map((k) => {
4335
- const isObject = typeof DEFAULT_CONFIG[k] === "object";
4336
- return {
4337
- name: k,
4338
- message: `${k}: ${isObject ? chalk11.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk11.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
4339
- ...isObject && { disabled: CONFIG_ALIAS_DISABLED_HINT }
4340
- };
4347
+ const disabledKeys = {};
4348
+ for (const k of Object.keys(DEFAULT_CONFIG)) {
4349
+ if (typeof DEFAULT_CONFIG[k] === "object") {
4350
+ disabledKeys[k] = CONFIG_ALIAS_DISABLED_HINT;
4351
+ }
4352
+ }
4353
+ const { key, newValue } = await interactiveConfigEditor(config2, CONFIG_DEFINITIONS, {
4354
+ disabledKeys
4341
4355
  });
4342
- const selectedKey = await new Enquirer5.Select({
4343
- message: MESSAGES.CONFIG_SELECT_PROMPT,
4344
- choices
4345
- }).run();
4346
- const currentValue = config2[selectedKey];
4347
- const newValue = await promptConfigValue(selectedKey, currentValue);
4348
- config2[selectedKey] = newValue;
4356
+ config2[key] = newValue;
4349
4357
  saveConfig(config2);
4350
- printSuccess(MESSAGES.CONFIG_SET_SUCCESS(selectedKey, String(newValue)));
4358
+ printSuccess(MESSAGES.CONFIG_SET_SUCCESS(key, String(newValue)));
4351
4359
  }
4352
4360
  function handleConfigGet(key) {
4353
4361
  if (!isValidConfigKey(key)) {
@@ -4391,7 +4399,7 @@ async function handleReset() {
4391
4399
  }
4392
4400
 
4393
4401
  // src/commands/status.ts
4394
- import chalk12 from "chalk";
4402
+ import chalk11 from "chalk";
4395
4403
  function registerStatusCommand(program2) {
4396
4404
  program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8\uFF08\u652F\u6301 --json \u683C\u5F0F\u8F93\u51FA\uFF09").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9762\u677F\u6A21\u5F0F").action(async (options) => {
4397
4405
  await handleStatus(options);
@@ -4509,7 +4517,7 @@ function printStatusAsJson(result) {
4509
4517
  }
4510
4518
  function printStatusAsText(result) {
4511
4519
  printDoubleSeparator();
4512
- printInfo(` ${chalk12.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
4520
+ printInfo(` ${chalk11.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
4513
4521
  printDoubleSeparator();
4514
4522
  printInfo("");
4515
4523
  printMainSection(result.main);
@@ -4522,17 +4530,17 @@ function printStatusAsText(result) {
4522
4530
  printDoubleSeparator();
4523
4531
  }
4524
4532
  function printMainSection(main2) {
4525
- printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
4526
- printInfo(` \u5206\u652F: ${chalk12.bold(main2.branch)}`);
4533
+ printInfo(` ${chalk11.bold("\u25C6")} ${chalk11.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
4534
+ printInfo(` \u5206\u652F: ${chalk11.bold(main2.branch)}`);
4527
4535
  if (main2.isClean) {
4528
- printInfo(` \u72B6\u6001: ${chalk12.green("\u2713 \u5E72\u51C0")}`);
4536
+ printInfo(` \u72B6\u6001: ${chalk11.green("\u2713 \u5E72\u51C0")}`);
4529
4537
  } else {
4530
- printInfo(` \u72B6\u6001: ${chalk12.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
4538
+ printInfo(` \u72B6\u6001: ${chalk11.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
4531
4539
  }
4532
4540
  printInfo("");
4533
4541
  }
4534
4542
  function printWorktreesSection(worktrees, total) {
4535
- printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
4543
+ printInfo(` ${chalk11.bold("\u25C6")} ${chalk11.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
4536
4544
  printInfo("");
4537
4545
  if (worktrees.length === 0) {
4538
4546
  printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
@@ -4544,56 +4552,56 @@ function printWorktreesSection(worktrees, total) {
4544
4552
  }
4545
4553
  function printWorktreeItem(wt) {
4546
4554
  const statusLabel = formatChangeStatusLabel2(wt.changeStatus);
4547
- printInfo(` ${chalk12.bold("\u25CF")} ${chalk12.bold(wt.branch)} [${statusLabel}]`);
4555
+ printInfo(` ${chalk11.bold("\u25CF")} ${chalk11.bold(wt.branch)} [${statusLabel}]`);
4548
4556
  if (wt.insertions > 0 || wt.deletions > 0) {
4549
- printInfo(` ${chalk12.green(`+${wt.insertions}`)} ${chalk12.red(`-${wt.deletions}`)}`);
4557
+ printInfo(` ${chalk11.green(`+${wt.insertions}`)} ${chalk11.red(`-${wt.deletions}`)}`);
4550
4558
  }
4551
4559
  if (wt.commitsAhead > 0) {
4552
- printInfo(` ${chalk12.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
4560
+ printInfo(` ${chalk11.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`)}`);
4553
4561
  }
4554
4562
  if (wt.commitsBehind > 0) {
4555
- printInfo(` ${chalk12.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
4563
+ printInfo(` ${chalk11.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`)}`);
4556
4564
  } else {
4557
- printInfo(` ${chalk12.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
4565
+ printInfo(` ${chalk11.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65")}`);
4558
4566
  }
4559
4567
  if (wt.createdAt) {
4560
4568
  const relativeTime = formatRelativeTime(wt.createdAt);
4561
4569
  if (relativeTime) {
4562
- printInfo(` ${chalk12.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
4570
+ printInfo(` ${chalk11.gray(MESSAGES.STATUS_CREATED_AT(relativeTime))}`);
4563
4571
  }
4564
4572
  }
4565
4573
  if (wt.snapshotTime) {
4566
4574
  const relativeTime = formatRelativeTime(wt.snapshotTime);
4567
4575
  if (relativeTime) {
4568
- printInfo(` ${chalk12.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
4576
+ printInfo(` ${chalk11.green(MESSAGES.STATUS_LAST_VALIDATED(relativeTime))}`);
4569
4577
  }
4570
4578
  } else {
4571
- printInfo(` ${chalk12.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
4579
+ printInfo(` ${chalk11.red(MESSAGES.STATUS_NOT_VALIDATED)}`);
4572
4580
  }
4573
4581
  printInfo("");
4574
4582
  }
4575
4583
  function formatChangeStatusLabel2(status) {
4576
4584
  switch (status) {
4577
4585
  case "committed":
4578
- return chalk12.green(MESSAGES.STATUS_CHANGE_COMMITTED);
4586
+ return chalk11.green(MESSAGES.STATUS_CHANGE_COMMITTED);
4579
4587
  case "uncommitted":
4580
- return chalk12.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
4588
+ return chalk11.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
4581
4589
  case "conflict":
4582
- return chalk12.red(MESSAGES.STATUS_CHANGE_CONFLICT);
4590
+ return chalk11.red(MESSAGES.STATUS_CHANGE_CONFLICT);
4583
4591
  case "clean":
4584
- return chalk12.gray(MESSAGES.STATUS_CHANGE_CLEAN);
4592
+ return chalk11.gray(MESSAGES.STATUS_CHANGE_CLEAN);
4585
4593
  }
4586
4594
  }
4587
4595
  function printSnapshotsSection(snapshots) {
4588
- printInfo(` ${chalk12.bold("\u25C6")} ${chalk12.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
4596
+ printInfo(` ${chalk11.bold("\u25C6")} ${chalk11.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.total} \u4E2A)`);
4589
4597
  if (snapshots.orphaned > 0) {
4590
- printInfo(` ${chalk12.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
4598
+ printInfo(` ${chalk11.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED(snapshots.orphaned))}`);
4591
4599
  }
4592
4600
  printInfo("");
4593
4601
  }
4594
4602
 
4595
4603
  // src/commands/alias.ts
4596
- import chalk13 from "chalk";
4604
+ import chalk12 from "chalk";
4597
4605
  function getRegisteredCommandNames(program2) {
4598
4606
  return program2.commands.map((cmd) => cmd.name());
4599
4607
  }
@@ -4614,7 +4622,7 @@ ${MESSAGES.ALIAS_LIST_TITLE}
4614
4622
  `);
4615
4623
  printSeparator();
4616
4624
  for (const [alias, command] of entries) {
4617
- printInfo(` ${chalk13.bold(alias)} \u2192 ${chalk13.cyan(command)}`);
4625
+ printInfo(` ${chalk12.bold(alias)} \u2192 ${chalk12.cyan(command)}`);
4618
4626
  }
4619
4627
  printInfo("");
4620
4628
  printSeparator();
@@ -4663,7 +4671,7 @@ function registerAliasCommand(program2) {
4663
4671
  // src/commands/projects.ts
4664
4672
  import { existsSync as existsSync10, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
4665
4673
  import { join as join8 } from "path";
4666
- import chalk14 from "chalk";
4674
+ import chalk13 from "chalk";
4667
4675
  function registerProjectsCommand(program2) {
4668
4676
  program2.command("projects [name]").description("\u5C55\u793A\u6240\u6709\u9879\u76EE\u7684 worktree \u6982\u89C8\uFF0C\u6216\u67E5\u770B\u6307\u5B9A\u9879\u76EE\u7684 worktree \u8BE6\u60C5").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((name, options) => {
4669
4677
  handleProjects({ name, json: options.json });
@@ -4782,7 +4790,7 @@ function sortByLastActiveTimeDesc(projects) {
4782
4790
  }
4783
4791
  function printProjectsOverviewAsText(result) {
4784
4792
  printDoubleSeparator();
4785
- printInfo(` ${chalk14.bold.cyan(MESSAGES.PROJECTS_OVERVIEW_TITLE)}`);
4793
+ printInfo(` ${chalk13.bold.cyan(MESSAGES.PROJECTS_OVERVIEW_TITLE)}`);
4786
4794
  printDoubleSeparator();
4787
4795
  printInfo("");
4788
4796
  if (result.projects.length === 0) {
@@ -4796,7 +4804,7 @@ function printProjectsOverviewAsText(result) {
4796
4804
  }
4797
4805
  printSeparator();
4798
4806
  printInfo("");
4799
- printInfo(` \u5171 ${chalk14.bold(String(result.totalProjects))} \u4E2A\u9879\u76EE ${chalk14.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
4807
+ printInfo(` \u5171 ${chalk13.bold(String(result.totalProjects))} \u4E2A\u9879\u76EE ${chalk13.gray(MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage)))}`);
4800
4808
  printInfo("");
4801
4809
  printDoubleSeparator();
4802
4810
  }
@@ -4804,16 +4812,16 @@ function printProjectOverviewItem(project) {
4804
4812
  const relativeTime = formatRelativeTime(project.lastActiveTime);
4805
4813
  const activeLabel = relativeTime ? MESSAGES.PROJECTS_LAST_ACTIVE(relativeTime) : "";
4806
4814
  const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(project.diskUsage));
4807
- printInfo(` ${chalk14.bold("\u25CF")} ${chalk14.bold(project.name)}`);
4808
- printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${chalk14.gray(activeLabel)} ${chalk14.gray(diskLabel)}`);
4815
+ printInfo(` ${chalk13.bold("\u25CF")} ${chalk13.bold(project.name)}`);
4816
+ printInfo(` ${MESSAGES.PROJECTS_WORKTREE_COUNT(project.worktreeCount)} ${chalk13.gray(activeLabel)} ${chalk13.gray(diskLabel)}`);
4809
4817
  printInfo("");
4810
4818
  }
4811
4819
  function printProjectDetailAsText(result) {
4812
4820
  printDoubleSeparator();
4813
- printInfo(` ${chalk14.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
4821
+ printInfo(` ${chalk13.bold.cyan(MESSAGES.PROJECTS_DETAIL_TITLE(result.name))}`);
4814
4822
  printDoubleSeparator();
4815
4823
  printInfo("");
4816
- printInfo(` ${chalk14.bold("\u25C6")} ${chalk14.bold(MESSAGES.PROJECTS_PATH(result.projectDir))}`);
4824
+ printInfo(` ${chalk13.bold("\u25C6")} ${chalk13.bold(MESSAGES.PROJECTS_PATH(result.projectDir))}`);
4817
4825
  printInfo(` ${MESSAGES.PROJECTS_TOTAL_DISK_USAGE(formatDiskSize(result.totalDiskUsage))}`);
4818
4826
  printInfo("");
4819
4827
  printSeparator();
@@ -4833,9 +4841,9 @@ function printWorktreeDetailItem(wt) {
4833
4841
  const relativeTime = formatRelativeTime(wt.lastModifiedTime);
4834
4842
  const modifiedLabel = relativeTime ? MESSAGES.PROJECTS_LAST_MODIFIED(relativeTime) : "";
4835
4843
  const diskLabel = MESSAGES.PROJECTS_DISK_USAGE(formatDiskSize(wt.diskUsage));
4836
- printInfo(` ${chalk14.bold("\u25CF")} ${chalk14.bold(wt.branch)}`);
4844
+ printInfo(` ${chalk13.bold("\u25CF")} ${chalk13.bold(wt.branch)}`);
4837
4845
  printInfo(` ${wt.path}`);
4838
- printInfo(` ${chalk14.gray(modifiedLabel)} ${chalk14.gray(diskLabel)}`);
4846
+ printInfo(` ${chalk13.gray(modifiedLabel)} ${chalk13.gray(diskLabel)}`);
4839
4847
  printInfo("");
4840
4848
  }
4841
4849
 
@@ -5058,16 +5066,23 @@ function registerInitCommand(program2) {
5058
5066
  await handleInit(options);
5059
5067
  });
5060
5068
  initCmd.addCommand(
5061
- new Cmd("show").description("\u5C55\u793A\u5F53\u524D\u9879\u76EE\u7684 init \u914D\u7F6E").action(() => {
5062
- handleInitShow();
5069
+ new Cmd("show").description("\u4EA4\u4E92\u5F0F\u67E5\u770B\u548C\u4FEE\u6539\u9879\u76EE\u914D\u7F6E").action(async () => {
5070
+ await handleInitShow();
5063
5071
  })
5064
5072
  );
5065
5073
  }
5066
- function handleInitShow() {
5074
+ async function handleInitShow() {
5067
5075
  validateMainWorktree();
5068
5076
  const config2 = requireProjectConfig();
5069
- const configJson = safeStringify(config2);
5070
- printInfo(MESSAGES.INIT_SHOW(configJson));
5077
+ logger.info("init show \u547D\u4EE4\u6267\u884C\uFF0C\u8FDB\u5165\u4EA4\u4E92\u5F0F\u9879\u76EE\u914D\u7F6E");
5078
+ const { key, newValue } = await interactiveConfigEditor(
5079
+ config2,
5080
+ PROJECT_CONFIG_DEFINITIONS,
5081
+ { selectPrompt: MESSAGES.INIT_SELECT_PROMPT }
5082
+ );
5083
+ const updatedConfig = { ...config2, [key]: newValue };
5084
+ saveProjectConfig(updatedConfig);
5085
+ printSuccess(MESSAGES.INIT_SET_SUCCESS(key, String(newValue)));
5071
5086
  }
5072
5087
  async function handleInit(options) {
5073
5088
  validateMainWorktree();
@@ -437,7 +437,11 @@ var INIT_MESSAGES = {
437
437
  /** 项目未初始化(requireProjectConfig 使用) */
438
438
  PROJECT_NOT_INITIALIZED: "\u9879\u76EE\u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F",
439
439
  /** 项目配置缺少 clawtMainWorkBranch 字段 */
440
- PROJECT_CONFIG_MISSING_BRANCH: "\u9879\u76EE\u914D\u7F6E\u7F3A\u5C11\u4E3B\u5DE5\u4F5C\u5206\u652F\u4FE1\u606F\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F"
440
+ PROJECT_CONFIG_MISSING_BRANCH: "\u9879\u76EE\u914D\u7F6E\u7F3A\u5C11\u4E3B\u5DE5\u4F5C\u5206\u652F\u4FE1\u606F\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F",
441
+ /** init show 交互式面板选择配置项提示 */
442
+ INIT_SELECT_PROMPT: "\u9009\u62E9\u8981\u4FEE\u6539\u7684\u9879\u76EE\u914D\u7F6E\u9879",
443
+ /** init show 交互式面板配置项修改成功 */
444
+ INIT_SET_SUCCESS: (key, value) => `\u2713 \u9879\u76EE\u914D\u7F6E ${key} \u5DF2\u8BBE\u7F6E\u4E3A ${value}`
441
445
  };
442
446
 
443
447
  // src/constants/messages/interactive-panel.ts
@@ -553,6 +557,32 @@ function deriveConfigDescriptions(definitions) {
553
557
  var DEFAULT_CONFIG = deriveDefaultConfig(CONFIG_DEFINITIONS);
554
558
  var CONFIG_DESCRIPTIONS = deriveConfigDescriptions(CONFIG_DEFINITIONS);
555
559
 
560
+ // src/constants/project-config.ts
561
+ var PROJECT_CONFIG_DEFINITIONS = {
562
+ clawtMainWorkBranch: {
563
+ defaultValue: "",
564
+ description: "\u4E3B worktree \u7684\u5DE5\u4F5C\u5206\u652F\u540D"
565
+ },
566
+ validateRunCommand: {
567
+ defaultValue: void 0,
568
+ description: "validate \u6210\u529F\u540E\u81EA\u52A8\u6267\u884C\u7684\u547D\u4EE4\uFF08-r \u7684\u9ED8\u8BA4\u503C\uFF09"
569
+ }
570
+ };
571
+ function deriveDefaultConfig2(definitions) {
572
+ const entries = Object.entries(definitions).map(
573
+ ([key, def]) => [key, def.defaultValue]
574
+ );
575
+ return Object.fromEntries(entries);
576
+ }
577
+ function deriveConfigDescriptions2(definitions) {
578
+ const entries = Object.entries(definitions).map(
579
+ ([key, def]) => [key, def.description]
580
+ );
581
+ return Object.fromEntries(entries);
582
+ }
583
+ var PROJECT_DEFAULT_CONFIG = deriveDefaultConfig2(PROJECT_CONFIG_DEFINITIONS);
584
+ var PROJECT_CONFIG_DESCRIPTIONS = deriveConfigDescriptions2(PROJECT_CONFIG_DEFINITIONS);
585
+
556
586
  // src/constants/update.ts
557
587
  var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
558
588
 
package/docs/config.md CHANGED
@@ -85,7 +85,8 @@ clawt config reset
85
85
 
86
86
  - 配置项类型定义:`ConfigItemDefinition` 新增可选字段 `allowedValues`(`readonly string[]`),仅对 string 类型有效,用于枚举值校验和交互式 Select 提示
87
87
  - 值解析与提示策略:`src/utils/config-strategy.ts` 中的 `parseConfigValue()`(CLI 字符串解析)和 `promptConfigValue()`(交互式提示),基于类型和 `allowedValues` 自动分发
88
+ - 交互式配置编辑:`handleInteractiveConfigSet` 调用通用的 `interactiveConfigEditor`(`src/utils/config-strategy.ts`),传入 `CONFIG_DEFINITIONS` 和 `disabledKeys`(对象类型配置项禁用映射),不再在 config 命令中直接构建选择列表和调用 `promptConfigValue`
88
89
  - `saveConfig(config)`:`src/utils/config.ts` 中新增的通用配置写入函数,将完整配置对象持久化到文件
89
- - `formatConfigValue(value)`:支持 boolean(绿色/黄色)和 string/number(青色)的格式化显示。对象类型配置项(如 `aliases`)在交互式列表中通过 `JSON.stringify` 以暗淡色显示
90
+ - `formatConfigValue(value)`:支持 boolean(绿色/黄色)和 string/number(青色)的格式化显示。`undefined` / `null` 值显示为暗淡色的 `(未设置)`。对象类型配置项(如 `aliases`)在交互式列表中通过 `JSON.stringify` 以暗淡色显示
90
91
 
91
92
  ---
package/docs/init.md CHANGED
@@ -18,11 +18,11 @@ clawt init show
18
18
  | 参数/子命令 | 必填 | 说明 |
19
19
  | --- | --- | --- |
20
20
  | `-b` | 否 | 指定主工作分支名。不传则使用当前分支 |
21
- | `show` | 否 | 查看当前项目的 init 配置 |
21
+ | `show` | 否 | 交互式查看和修改项目配置 |
22
22
 
23
23
  **功能说明:**
24
24
 
25
- 初始化项目级配置,将指定分支记录为该项目的主工作分支(`clawtMainWorkBranch`)。该配置用于 `create` / `run` 时检测当前分支是否为主工作分支,并在偏离时提醒用户。详见 [2.6 项目级配置](#26-项目级配置)。
25
+ 初始化项目级配置,将指定分支记录为该项目的主工作分支(`clawtMainWorkBranch`)。该配置用于 `create` / `run` 时检测当前分支是否为主工作分支,并在偏离时提醒用户。`init show` 子命令提供交互式面板,可查看和修改所有项目配置项(如 `validateRunCommand`)。项目级配置的完整说明见 [project-config.md](./project-config.md)。
26
26
 
27
27
  **运行流程(设置模式):**
28
28
 
@@ -40,7 +40,12 @@ clawt init show
40
40
  1. **主 worktree 校验** (2.1)
41
41
  2. **读取项目级配置**:读取 `~/.clawt/projects/<projectName>/config.json`
42
42
  - 配置不存在 → 抛出错误 `项目尚未初始化,请先执行 clawt init 设置主工作分支`
43
- - 配置存在 → 以 JSON 格式输出配置内容
43
+ - 配置存在 → 进入交互式面板
44
+ 3. **交互式配置编辑**:调用 `interactiveConfigEditor`(`src/utils/config-strategy.ts`),基于 `PROJECT_CONFIG_DEFINITIONS` 构建配置项列表(详见 [project-config.md](./project-config.md))
45
+ - 列出所有项目配置项,显示名称、当前值和描述
46
+ - 用户选择配置项后,根据值类型自动选择输入方式(与全局配置的交互式编辑逻辑一致)
47
+ 4. **持久化修改**:将修改后的值合并到当前配置并写入配置文件
48
+ 5. **输出成功提示**:`✓ 项目配置 <key> 已设置为 <value>`
44
49
 
45
50
  **输出格式:**
46
51
 
@@ -51,10 +56,8 @@ clawt init show
51
56
  # 更新已有配置
52
57
  ✓ 已将主工作分支从 develop 更新为 main
53
58
 
54
- # show 查看配置(JSON 格式输出)
55
- {
56
- "clawtMainWorkBranch": "main"
57
- }
59
+ # show 交互式修改成功
60
+ ✓ 项目配置 validateRunCommand 已设置为 npm test
58
61
 
59
62
  # show 未初始化(抛出错误)
60
63
  项目尚未初始化,请先执行 clawt init 设置主工作分支
@@ -62,4 +65,10 @@ clawt init show
62
65
 
63
66
  **重复执行:** 支持重复执行,后一次覆盖前一次的配置。
64
67
 
68
+ **实现要点:**
69
+
70
+ - `init show` 子命令从 JSON 展示改为交互式面板,调用 `interactiveConfigEditor`(`src/utils/config-strategy.ts`)实现通用交互式配置编辑
71
+ - 配置项定义来自 `PROJECT_CONFIG_DEFINITIONS`(`src/constants/project-config.ts`),详见 [项目级配置文档](./project-config.md)
72
+ - 消息常量:`MESSAGES.INIT_SELECT_PROMPT`(选择配置项提示语)、`MESSAGES.INIT_SET_SUCCESS`(修改成功提示),定义在 `src/constants/messages/init.ts`
73
+
65
74
  ---