clawt 2.14.0 → 2.15.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.
@@ -7,7 +7,7 @@
7
7
  - 命令流程在 `5. 需求场景详细设计` 下,每个命令一个子章节(5.1-5.14)
8
8
  - run 命令对应 `5.2 批量创建 Worktree + 执行 Claude Code 任务`,流程按步骤编号描述
9
9
  - merge 命令对应 `5.6 合并验证过的分支`,-b 可选,支持模糊匹配(与 resume/validate 共享匹配逻辑),流程按步骤编号描述
10
- - config 命令对应 `5.10 查看和管理全局配置`,包含查看配置和 config reset 子命令两部分(使用 `####` 子标题区分)
10
+ - config 命令对应 `5.10 交互式查看和修改全局配置`,包含四个子章节:交互式修改(config / config set 无参数)、直接设置(config set key value)、获取(config get)、恢复默认(config reset),使用 `####` 子标题区分
11
11
  - resume 命令对应 `5.11 在已有 Worktree 中恢复会话`,统一使用多选交互(resolveTargetWorktrees),选 1 个当前终端恢复,选多个在独立终端 Tab 批量恢复(-b 可选)
12
12
  - validate 命令对应 `5.4 在主 Worktree 验证其他分支`,-b 可选,支持模糊匹配(与 resume 共享匹配逻辑)
13
13
  - sync 命令对应 `5.12 将主分支代码同步到目标 Worktree`,-b 可选,支持模糊匹配(与 resume/validate/merge 共享匹配逻辑)
@@ -72,9 +72,9 @@ run 命令有两种模式(自 claudeCodeCommand 特性后):
72
72
  - `formatDuration` 从 `src/utils/progress.ts` 移至 `src/utils/formatter.ts`
73
73
  - 进度面板每个任务行末尾显示 worktree 路径(终端可点击跳转)
74
74
 
75
- ## 命令清单(11 个)
75
+ ## 命令清单(12 个)
76
76
 
77
- `create`、`run`、`resume`、`list`、`remove`、`validate`、`merge`、`config`、`sync`、`reset`、`status`
77
+ `create`、`run`、`resume`、`list`、`remove`、`validate`、`merge`、`config`、`sync`、`reset`、`status`、`alias`
78
78
 
79
79
  Notes:
80
80
  - resume 和 run(交互式模式)共用 `launchInteractiveClaude()`,该函数从 run.ts 提取到 src/utils/claude.ts
@@ -91,6 +91,11 @@ Notes:
91
91
  - `promptMultiSelectBranches` 支持「全选」选项(顶部 [select-all]),通过扩展 MultiSelect 覆写 space() 实现全选 toggle
92
92
  - `SELECT_ALL_NAME` 和 `SELECT_ALL_LABEL` 常量定义在 `src/constants/prompt.ts`
93
93
  - `VALID_TERMINAL_APPS` 和 `ITERM2_APP_PATH` 常量定义在 `src/constants/terminal.ts`
94
+ - config 交互式修改:对象类型配置项(如 `aliases`)在 Enquirer.Select 列表中标灰不可选(`disabled: CONFIG_ALIAS_DISABLED_HINT`),提示用户通过专用命令管理
95
+ - `CONFIG_ALIAS_DISABLED_HINT` 常量定义在 `src/constants/messages/config.ts`,通过 `src/constants/messages/index.ts` 和 `src/constants/index.ts` 导出
96
+ - `parseConfigValue()` 和 `promptConfigValue()` 在 `src/utils/config-strategy.ts`,基于配置项类型和 `allowedValues` 自动分发值解析/提示策略
97
+ - `saveConfig()` 在 `src/utils/config.ts`,通用配置写入函数
98
+ - `ConfigItemDefinition` 支持可选 `allowedValues` 字段(`readonly string[]`),用于 string 类型枚举校验
94
99
 
95
100
  ## validate 快照机制
96
101
 
package/README.md CHANGED
@@ -52,6 +52,29 @@ clawt run -f tasks.md
52
52
 
53
53
  # 从任务文件读取任务,但用 -b 自动编号分支(文件中分支名可省略)
54
54
  clawt run -f tasks.md -b feat
55
+
56
+ # 试运行:仅预览将要创建的 worktree 和任务,不实际执行
57
+ clawt run -b <branch> --tasks "任务1" --tasks "任务2" --dry-run
58
+ ```
59
+
60
+ **`--dry-run` 预览示例:**
61
+
62
+ ```
63
+ ════════════════════════════════════════
64
+ Dry Run 预览
65
+ ════════════════════════════════════════
66
+ 任务数: 2 │ 并发数: 不限制 │ Worktree: ~/.clawt/worktrees/project
67
+ ────────────────────────────────────────
68
+ ✓ [1/2] feat-1
69
+ 路径: ~/.clawt/worktrees/project/feat-1
70
+ 任务: 任务1
71
+
72
+ ✓ [2/2] feat-2
73
+ 路径: ~/.clawt/worktrees/project/feat-2
74
+ 任务: 任务2
75
+
76
+ ════════════════════════════════════════
77
+ ✓ 预览完成,无冲突。移除 --dry-run 即可正式执行。
55
78
  ```
56
79
 
57
80
  **任务文件格式:**
@@ -143,11 +166,28 @@ clawt status --json # JSON 格式
143
166
  clawt reset
144
167
  ```
145
168
 
146
- ### `clawt config` — 查看配置
169
+ ### `clawt config` — 交互式查看和修改配置
147
170
 
148
171
  ```bash
149
- clawt config # 查看当前配置
150
- clawt config reset # 恢复默认配置
172
+ clawt config # 交互式修改配置(选择配置项并修改值)
173
+ clawt config set <key> <value> # 直接设置某个配置项
174
+ clawt config get <key> # 获取某个配置项的值
175
+ clawt config reset # 恢复默认配置
176
+ ```
177
+
178
+ **使用示例:**
179
+
180
+ ```bash
181
+ # 交互式修改(列出所有配置项,方向键选择,根据类型自动提示)
182
+ clawt config
183
+
184
+ # 直接设置
185
+ clawt config set autoDeleteBranch true
186
+ clawt config set maxConcurrency 4
187
+ clawt config set terminalApp iterm2
188
+
189
+ # 查看某项配置
190
+ clawt config get maxConcurrency
151
191
  ```
152
192
 
153
193
  ### `clawt alias` — 管理命令别名
package/dist/index.js CHANGED
@@ -98,7 +98,23 @@ var RUN_MESSAGES = {
98
98
  /** 任务文件加载成功 */
99
99
  TASK_FILE_LOADED: (count, path) => `\u2713 \u4ECE ${path} \u52A0\u8F7D\u4E86 ${count} \u4E2A\u4EFB\u52A1`,
100
100
  /** 未指定 -b 或 -f */
101
- BRANCH_OR_FILE_REQUIRED: "\u8BF7\u6307\u5B9A -b \u5206\u652F\u540D\u6216 -f \u4EFB\u52A1\u6587\u4EF6"
101
+ BRANCH_OR_FILE_REQUIRED: "\u8BF7\u6307\u5B9A -b \u5206\u652F\u540D\u6216 -f \u4EFB\u52A1\u6587\u4EF6",
102
+ /** dry-run 预览标题 */
103
+ DRY_RUN_TITLE: "Dry Run \u9884\u89C8",
104
+ /** dry-run 任务数量 */
105
+ DRY_RUN_TASK_COUNT: (count) => `\u4EFB\u52A1\u6570: ${count}`,
106
+ /** dry-run 并发数 */
107
+ DRY_RUN_CONCURRENCY: (concurrency) => `\u5E76\u53D1\u6570: ${concurrency === 0 ? "\u4E0D\u9650\u5236" : concurrency}`,
108
+ /** dry-run worktree 目录 */
109
+ DRY_RUN_WORKTREE_DIR: (dir) => `Worktree: ${dir}`,
110
+ /** dry-run 分支已存在警告 */
111
+ DRY_RUN_BRANCH_EXISTS_WARNING: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728`,
112
+ /** dry-run 交互式模式提示(无任务描述) */
113
+ DRY_RUN_INTERACTIVE_MODE: "\u6A21\u5F0F: \u4EA4\u4E92\u5F0F\uFF08\u65E0\u9884\u8BBE\u4EFB\u52A1\uFF09",
114
+ /** dry-run 预览完成且无冲突 */
115
+ DRY_RUN_READY: "\u9884\u89C8\u5B8C\u6210\uFF0C\u65E0\u51B2\u7A81\u3002\u79FB\u9664 --dry-run \u5373\u53EF\u6B63\u5F0F\u6267\u884C\u3002",
116
+ /** dry-run 存在分支冲突 */
117
+ DRY_RUN_HAS_CONFLICT: "\u5B58\u5728\u5206\u652F\u51B2\u7A81\uFF0C\u5B9E\u9645\u6267\u884C\u65F6\u5C06\u4F1A\u62A5\u9519\u3002\u8BF7\u5148\u5904\u7406\u51B2\u7A81\u7684\u5206\u652F\u3002"
102
118
  };
103
119
 
104
120
  // src/constants/messages/create.ts
@@ -249,6 +265,7 @@ var RESET_MESSAGES = {
249
265
  };
250
266
 
251
267
  // src/constants/messages/config.ts
268
+ var CONFIG_ALIAS_DISABLED_HINT = "(\u901A\u8FC7 clawt alias \u547D\u4EE4\u7BA1\u7406)";
252
269
  var CONFIG_CMD_MESSAGES = {
253
270
  /** 配置已恢复为默认值 */
254
271
  CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C",
@@ -377,7 +394,7 @@ var CONFIG_DEFINITIONS = {
377
394
  },
378
395
  aliases: {
379
396
  defaultValue: {},
380
- description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04\uFF08\u901A\u8FC7 clawt alias \u547D\u4EE4\u7BA1\u7406\uFF09"
397
+ description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04"
381
398
  }
382
399
  };
383
400
  function deriveDefaultConfig(definitions) {
@@ -968,6 +985,16 @@ function ensureClawtDirs() {
968
985
  ensureDir(LOGS_DIR);
969
986
  ensureDir(WORKTREES_DIR);
970
987
  }
988
+ function parseConcurrency(optionValue, configValue) {
989
+ if (optionValue === void 0) {
990
+ return configValue;
991
+ }
992
+ const parsed = parseInt(optionValue, 10);
993
+ if (Number.isNaN(parsed) || parsed < 0) {
994
+ throw new ClawtError(MESSAGES.CONCURRENCY_INVALID);
995
+ }
996
+ return parsed;
997
+ }
971
998
 
972
999
  // src/utils/prompt.ts
973
1000
  import Enquirer from "enquirer";
@@ -1491,6 +1518,14 @@ import { resolve } from "path";
1491
1518
  import { existsSync as existsSync8, readFileSync as readFileSync3 } from "fs";
1492
1519
  var TASK_BLOCK_REGEX = /<!-- CLAWT-TASKS:START -->([\s\S]*?)<!-- CLAWT-TASKS:END -->/g;
1493
1520
  var BRANCH_LINE_REGEX = /^#\s*branch:\s*(.+)$/;
1521
+ var EMPTY_TASKS_MESSAGE = "\u4EFB\u52A1\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A";
1522
+ function parseTasksFromOptions(rawTasks) {
1523
+ const tasks = rawTasks.map((t) => t.trim()).filter(Boolean);
1524
+ if (tasks.length === 0) {
1525
+ throw new ClawtError(EMPTY_TASKS_MESSAGE);
1526
+ }
1527
+ return tasks;
1528
+ }
1494
1529
  function parseTaskFile(content, options) {
1495
1530
  const branchRequired = options?.branchRequired ?? true;
1496
1531
  const entries = [];
@@ -1741,6 +1776,59 @@ async function executeBatchTasks(worktrees, tasks, concurrency) {
1741
1776
  printTaskSummary(summary);
1742
1777
  }
1743
1778
 
1779
+ // src/utils/dry-run.ts
1780
+ import chalk4 from "chalk";
1781
+ import { join as join5 } from "path";
1782
+ var DRY_RUN_TASK_DESC_MAX_LENGTH = 70;
1783
+ function truncateTaskDesc(task) {
1784
+ const oneLine = task.replace(/\n/g, " ").trim();
1785
+ if (oneLine.length <= DRY_RUN_TASK_DESC_MAX_LENGTH) {
1786
+ return oneLine;
1787
+ }
1788
+ return oneLine.slice(0, DRY_RUN_TASK_DESC_MAX_LENGTH) + "...";
1789
+ }
1790
+ function printDryRunPreview(branchNames, tasks, concurrency) {
1791
+ const projectDir = getProjectWorktreeDir();
1792
+ const isInteractive = tasks.length === 0;
1793
+ printDoubleSeparator();
1794
+ printInfo(` ${chalk4.bold(MESSAGES.DRY_RUN_TITLE)}`);
1795
+ printDoubleSeparator();
1796
+ const summaryParts = [
1797
+ MESSAGES.DRY_RUN_TASK_COUNT(branchNames.length),
1798
+ MESSAGES.DRY_RUN_CONCURRENCY(concurrency),
1799
+ MESSAGES.DRY_RUN_WORKTREE_DIR(projectDir)
1800
+ ];
1801
+ if (isInteractive) {
1802
+ summaryParts.push(MESSAGES.DRY_RUN_INTERACTIVE_MODE);
1803
+ }
1804
+ printInfo(summaryParts.join(chalk4.gray(" \u2502 ")));
1805
+ printSeparator();
1806
+ let hasConflict = false;
1807
+ for (let i = 0; i < branchNames.length; i++) {
1808
+ const branch = branchNames[i];
1809
+ const worktreePath = join5(projectDir, branch);
1810
+ const exists = checkBranchExists(branch);
1811
+ if (exists) hasConflict = true;
1812
+ const indexLabel = `[${i + 1}/${branchNames.length}]`;
1813
+ if (exists) {
1814
+ printInfo(`${chalk4.yellow("\u26A0")} ${indexLabel} ${chalk4.yellow(branch)} ${chalk4.gray("\u2014")} ${chalk4.yellow(MESSAGES.DRY_RUN_BRANCH_EXISTS_WARNING(branch))}`);
1815
+ } else {
1816
+ printInfo(`${chalk4.green("\u2713")} ${indexLabel} ${chalk4.cyan(branch)}`);
1817
+ }
1818
+ printInfo(` ${chalk4.gray("\u8DEF\u5F84:")} ${worktreePath}`);
1819
+ if (!isInteractive) {
1820
+ printInfo(` ${chalk4.gray("\u4EFB\u52A1:")} ${truncateTaskDesc(tasks[i])}`);
1821
+ }
1822
+ printInfo("");
1823
+ }
1824
+ printDoubleSeparator();
1825
+ if (hasConflict) {
1826
+ printInfo(chalk4.yellow(`\u26A0 ${MESSAGES.DRY_RUN_HAS_CONFLICT}`));
1827
+ } else {
1828
+ printInfo(chalk4.green(`\u2713 ${MESSAGES.DRY_RUN_READY}`));
1829
+ }
1830
+ }
1831
+
1744
1832
  // src/utils/alias.ts
1745
1833
  function applyAliases(program2, aliases) {
1746
1834
  for (const [alias, commandName] of Object.entries(aliases)) {
@@ -1755,7 +1843,7 @@ function applyAliases(program2, aliases) {
1755
1843
  }
1756
1844
 
1757
1845
  // src/utils/config-strategy.ts
1758
- import chalk4 from "chalk";
1846
+ import chalk5 from "chalk";
1759
1847
  import Enquirer3 from "enquirer";
1760
1848
  function isValidConfigKey(key) {
1761
1849
  return key in DEFAULT_CONFIG;
@@ -1799,9 +1887,9 @@ async function promptConfigValue(key, currentValue) {
1799
1887
  }
1800
1888
  function formatConfigValue(value) {
1801
1889
  if (typeof value === "boolean") {
1802
- return value ? chalk4.green("true") : chalk4.yellow("false");
1890
+ return value ? chalk5.green("true") : chalk5.yellow("false");
1803
1891
  }
1804
- return chalk4.cyan(String(value));
1892
+ return chalk5.cyan(String(value));
1805
1893
  }
1806
1894
  async function promptBooleanValue(key, currentValue) {
1807
1895
  const choices = [
@@ -1845,7 +1933,7 @@ async function promptStringValue(key, currentValue) {
1845
1933
  }
1846
1934
 
1847
1935
  // src/commands/list.ts
1848
- import chalk5 from "chalk";
1936
+ import chalk6 from "chalk";
1849
1937
  function registerListCommand(program2) {
1850
1938
  program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
1851
1939
  handleList(options);
@@ -1882,12 +1970,12 @@ function printListAsText(projectName, worktrees) {
1882
1970
  for (const wt of worktrees) {
1883
1971
  const status = getWorktreeStatus(wt);
1884
1972
  const isIdle = status ? isWorktreeIdle(status) : false;
1885
- const pathDisplay = isIdle ? chalk5.hex("#FF8C00")(wt.path) : wt.path;
1973
+ const pathDisplay = isIdle ? chalk6.hex("#FF8C00")(wt.path) : wt.path;
1886
1974
  printInfo(` ${pathDisplay} [${wt.branch}]`);
1887
1975
  if (status) {
1888
1976
  printInfo(` ${formatWorktreeStatus(status)}`);
1889
1977
  } else {
1890
- printInfo(` ${chalk5.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
1978
+ printInfo(` ${chalk6.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
1891
1979
  }
1892
1980
  printInfo("");
1893
1981
  }
@@ -1990,19 +2078,16 @@ async function handleRemove(options) {
1990
2078
 
1991
2079
  // src/commands/run.ts
1992
2080
  function registerRunCommand(program2) {
1993
- program2.command("run").description("\u6279\u91CF\u521B\u5EFA worktree \u5E76\u542F\u52A8 Claude Code \u6267\u884C\u4EFB\u52A1").option("-b, --branch <branchName>", "\u5206\u652F\u540D").option("--tasks <task...>", "\u4EFB\u52A1\u5217\u8868\uFF08\u53EF\u591A\u6B21\u6307\u5B9A\uFF09\uFF0C\u4E0D\u4F20\u5219\u5728 worktree \u4E2D\u6253\u5F00 Claude Code \u4EA4\u4E92\u5F0F\u754C\u9762").option("-c, --concurrency <n>", "\u6700\u5927\u5E76\u53D1\u6570\uFF0C0 \u8868\u793A\u4E0D\u9650\u5236").option("-f, --file <path>", "\u4ECE\u4EFB\u52A1\u6587\u4EF6\u8BFB\u53D6\u4EFB\u52A1\u5217\u8868\uFF08\u4E0E --tasks \u4E92\u65A5\uFF09").action(async (options) => {
2081
+ program2.command("run").description("\u6279\u91CF\u521B\u5EFA worktree \u5E76\u542F\u52A8 Claude Code \u6267\u884C\u4EFB\u52A1").option("-b, --branch <branchName>", "\u5206\u652F\u540D").option("--tasks <task...>", "\u4EFB\u52A1\u5217\u8868\uFF08\u53EF\u591A\u6B21\u6307\u5B9A\uFF09\uFF0C\u4E0D\u4F20\u5219\u5728 worktree \u4E2D\u6253\u5F00 Claude Code \u4EA4\u4E92\u5F0F\u754C\u9762").option("-c, --concurrency <n>", "\u6700\u5927\u5E76\u53D1\u6570\uFF0C0 \u8868\u793A\u4E0D\u9650\u5236").option("-f, --file <path>", "\u4ECE\u4EFB\u52A1\u6587\u4EF6\u8BFB\u53D6\u4EFB\u52A1\u5217\u8868\uFF08\u4E0E --tasks \u4E92\u65A5\uFF09").option("-d, --dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4EC5\u5C55\u793A\u4EFB\u52A1\u8BA1\u5212\u4E0D\u5B9E\u9645\u6267\u884C").action(async (options) => {
1994
2082
  await handleRun(options);
1995
2083
  });
1996
2084
  }
1997
- function parseConcurrency(optionValue, configValue) {
1998
- if (optionValue === void 0) {
1999
- return configValue;
2000
- }
2001
- const parsed = parseInt(optionValue, 10);
2002
- if (Number.isNaN(parsed) || parsed < 0) {
2003
- throw new ClawtError(MESSAGES.CONCURRENCY_INVALID);
2085
+ function resolveBranchNamesFromFile(options, entryCount, entries) {
2086
+ if (options.branch) {
2087
+ const sanitized = sanitizeBranchName(options.branch);
2088
+ return generateBranchNames(sanitized, entryCount);
2004
2089
  }
2005
- return parsed;
2090
+ return entries.map((e) => sanitizeBranchName(e.branch));
2006
2091
  }
2007
2092
  async function handleRunFromFile(options) {
2008
2093
  const branchRequired = !options.branch;
@@ -2020,12 +2105,39 @@ async function handleRunFromFile(options) {
2020
2105
  logger.info(`run \u547D\u4EE4\uFF08\u6587\u4EF6\u6A21\u5F0F\uFF09\u6267\u884C\uFF0C\u4EFB\u52A1\u6570: ${entries.length}\uFF0C\u5E76\u53D1\u6570: ${concurrency || "\u4E0D\u9650\u5236"}`);
2021
2106
  await executeBatchTasks(worktrees, tasks, concurrency);
2022
2107
  }
2108
+ function handleDryRunFromFile(options) {
2109
+ const branchRequired = !options.branch;
2110
+ const entries = loadTaskFile(options.file, { branchRequired });
2111
+ printSuccess(MESSAGES.TASK_FILE_LOADED(entries.length, options.file));
2112
+ const tasks = entries.map((e) => e.task);
2113
+ const branchNames = resolveBranchNamesFromFile(options, entries.length, entries);
2114
+ const concurrency = parseConcurrency(options.concurrency, getConfigValue("maxConcurrency"));
2115
+ printDryRunPreview(branchNames, tasks, concurrency);
2116
+ }
2023
2117
  async function handleRun(options) {
2024
2118
  validateMainWorktree();
2025
- validateClaudeCodeInstalled();
2026
2119
  if (options.file && options.tasks) {
2027
2120
  throw new ClawtError(MESSAGES.FILE_AND_TASKS_CONFLICT);
2028
2121
  }
2122
+ if (options.dryRun) {
2123
+ if (options.file) {
2124
+ return handleDryRunFromFile(options);
2125
+ }
2126
+ if (!options.branch) {
2127
+ throw new ClawtError(MESSAGES.BRANCH_OR_FILE_REQUIRED);
2128
+ }
2129
+ const sanitized = sanitizeBranchName(options.branch);
2130
+ if (!options.tasks || options.tasks.length === 0) {
2131
+ printDryRunPreview([sanitized], [], 0);
2132
+ return;
2133
+ }
2134
+ const tasks2 = parseTasksFromOptions(options.tasks);
2135
+ const branchNames = generateBranchNames(sanitized, tasks2.length);
2136
+ const concurrency2 = parseConcurrency(options.concurrency, getConfigValue("maxConcurrency"));
2137
+ printDryRunPreview(branchNames, tasks2, concurrency2);
2138
+ return;
2139
+ }
2140
+ validateClaudeCodeInstalled();
2029
2141
  if (options.file) {
2030
2142
  return handleRunFromFile(options);
2031
2143
  }
@@ -2043,10 +2155,7 @@ async function handleRun(options) {
2043
2155
  launchInteractiveClaude(worktree);
2044
2156
  return;
2045
2157
  }
2046
- const tasks = options.tasks.map((t) => t.trim()).filter(Boolean);
2047
- if (tasks.length === 0) {
2048
- throw new ClawtError("\u4EFB\u52A1\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A");
2049
- }
2158
+ const tasks = parseTasksFromOptions(options.tasks);
2050
2159
  const count = tasks.length;
2051
2160
  const concurrency = parseConcurrency(options.concurrency, getConfigValue("maxConcurrency"));
2052
2161
  logger.info(`run \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch}\uFF0C\u4EFB\u52A1\u6570: ${count}\uFF0C\u5E76\u53D1\u6570: ${concurrency || "\u4E0D\u9650\u5236"}`);
@@ -2412,7 +2521,7 @@ async function handleMerge(options) {
2412
2521
  }
2413
2522
 
2414
2523
  // src/commands/config.ts
2415
- import chalk6 from "chalk";
2524
+ import chalk7 from "chalk";
2416
2525
  import Enquirer5 from "enquirer";
2417
2526
  function registerConfigCommand(program2) {
2418
2527
  const configCmd = program2.command("config").description("\u4EA4\u4E92\u5F0F\u67E5\u770B\u548C\u4FEE\u6539\u5168\u5C40\u914D\u7F6E").action(async () => {
@@ -2469,10 +2578,14 @@ async function handleInteractiveConfigSet() {
2469
2578
  const config2 = loadConfig();
2470
2579
  const keys = Object.keys(DEFAULT_CONFIG);
2471
2580
  logger.info("config set \u547D\u4EE4\u6267\u884C\uFF0C\u8FDB\u5165\u4EA4\u4E92\u5F0F\u914D\u7F6E");
2472
- const choices = keys.map((k) => ({
2473
- name: k,
2474
- message: `${k}: ${formatConfigValue(config2[k])} ${chalk6.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`
2475
- }));
2581
+ const choices = keys.map((k) => {
2582
+ const isObject = typeof DEFAULT_CONFIG[k] === "object";
2583
+ return {
2584
+ name: k,
2585
+ message: `${k}: ${isObject ? chalk7.dim(JSON.stringify(config2[k])) : formatConfigValue(config2[k])} ${chalk7.dim(`\u2014 ${CONFIG_DESCRIPTIONS[k]}`)}`,
2586
+ ...isObject && { disabled: CONFIG_ALIAS_DISABLED_HINT }
2587
+ };
2588
+ });
2476
2589
  const selectedKey = await new Enquirer5.Select({
2477
2590
  message: MESSAGES.CONFIG_SELECT_PROMPT,
2478
2591
  choices
@@ -2578,7 +2691,7 @@ async function handleReset() {
2578
2691
  }
2579
2692
 
2580
2693
  // src/commands/status.ts
2581
- import chalk7 from "chalk";
2694
+ import chalk8 from "chalk";
2582
2695
  function registerStatusCommand(program2) {
2583
2696
  program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
2584
2697
  handleStatus(options);
@@ -2674,7 +2787,7 @@ function printStatusAsJson(result) {
2674
2787
  }
2675
2788
  function printStatusAsText(result) {
2676
2789
  printDoubleSeparator();
2677
- printInfo(` ${chalk7.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
2790
+ printInfo(` ${chalk8.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
2678
2791
  printDoubleSeparator();
2679
2792
  printInfo("");
2680
2793
  printMainSection(result.main);
@@ -2687,17 +2800,17 @@ function printStatusAsText(result) {
2687
2800
  printDoubleSeparator();
2688
2801
  }
2689
2802
  function printMainSection(main) {
2690
- printInfo(` ${chalk7.bold("\u25C6")} ${chalk7.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
2691
- printInfo(` \u5206\u652F: ${chalk7.bold(main.branch)}`);
2803
+ printInfo(` ${chalk8.bold("\u25C6")} ${chalk8.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
2804
+ printInfo(` \u5206\u652F: ${chalk8.bold(main.branch)}`);
2692
2805
  if (main.isClean) {
2693
- printInfo(` \u72B6\u6001: ${chalk7.green("\u2713 \u5E72\u51C0")}`);
2806
+ printInfo(` \u72B6\u6001: ${chalk8.green("\u2713 \u5E72\u51C0")}`);
2694
2807
  } else {
2695
- printInfo(` \u72B6\u6001: ${chalk7.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
2808
+ printInfo(` \u72B6\u6001: ${chalk8.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
2696
2809
  }
2697
2810
  printInfo("");
2698
2811
  }
2699
2812
  function printWorktreesSection(worktrees, total) {
2700
- printInfo(` ${chalk7.bold("\u25C6")} ${chalk7.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
2813
+ printInfo(` ${chalk8.bold("\u25C6")} ${chalk8.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
2701
2814
  printInfo("");
2702
2815
  if (worktrees.length === 0) {
2703
2816
  printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
@@ -2709,39 +2822,39 @@ function printWorktreesSection(worktrees, total) {
2709
2822
  }
2710
2823
  function printWorktreeItem(wt) {
2711
2824
  const statusLabel = formatChangeStatusLabel(wt.changeStatus);
2712
- printInfo(` ${chalk7.bold("\u25CF")} ${chalk7.bold(wt.branch)} [${statusLabel}]`);
2825
+ printInfo(` ${chalk8.bold("\u25CF")} ${chalk8.bold(wt.branch)} [${statusLabel}]`);
2713
2826
  const parts = [];
2714
2827
  if (wt.insertions > 0 || wt.deletions > 0) {
2715
- parts.push(`${chalk7.green(`+${wt.insertions}`)} ${chalk7.red(`-${wt.deletions}`)}`);
2828
+ parts.push(`${chalk8.green(`+${wt.insertions}`)} ${chalk8.red(`-${wt.deletions}`)}`);
2716
2829
  }
2717
2830
  if (wt.commitsAhead > 0) {
2718
- parts.push(chalk7.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`));
2831
+ parts.push(chalk8.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`));
2719
2832
  }
2720
2833
  if (wt.commitsBehind > 0) {
2721
- parts.push(chalk7.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`));
2834
+ parts.push(chalk8.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`));
2722
2835
  } else {
2723
- parts.push(chalk7.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65"));
2836
+ parts.push(chalk8.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65"));
2724
2837
  }
2725
2838
  printInfo(` ${parts.join(" ")}`);
2726
2839
  if (wt.hasSnapshot) {
2727
- printInfo(` ${chalk7.blue("\u6709 validate \u5FEB\u7167")}`);
2840
+ printInfo(` ${chalk8.blue("\u6709 validate \u5FEB\u7167")}`);
2728
2841
  }
2729
2842
  printInfo("");
2730
2843
  }
2731
2844
  function formatChangeStatusLabel(status) {
2732
2845
  switch (status) {
2733
2846
  case "committed":
2734
- return chalk7.green(MESSAGES.STATUS_CHANGE_COMMITTED);
2847
+ return chalk8.green(MESSAGES.STATUS_CHANGE_COMMITTED);
2735
2848
  case "uncommitted":
2736
- return chalk7.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
2849
+ return chalk8.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
2737
2850
  case "conflict":
2738
- return chalk7.red(MESSAGES.STATUS_CHANGE_CONFLICT);
2851
+ return chalk8.red(MESSAGES.STATUS_CHANGE_CONFLICT);
2739
2852
  case "clean":
2740
- return chalk7.gray(MESSAGES.STATUS_CHANGE_CLEAN);
2853
+ return chalk8.gray(MESSAGES.STATUS_CHANGE_CLEAN);
2741
2854
  }
2742
2855
  }
2743
2856
  function printSnapshotsSection(snapshots) {
2744
- printInfo(` ${chalk7.bold("\u25C6")} ${chalk7.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.length} \u4E2A)`);
2857
+ printInfo(` ${chalk8.bold("\u25C6")} ${chalk8.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.length} \u4E2A)`);
2745
2858
  printInfo("");
2746
2859
  if (snapshots.length === 0) {
2747
2860
  printInfo(` ${MESSAGES.STATUS_NO_SNAPSHOTS}`);
@@ -2749,15 +2862,15 @@ function printSnapshotsSection(snapshots) {
2749
2862
  return;
2750
2863
  }
2751
2864
  for (const snap of snapshots) {
2752
- const orphanLabel = snap.worktreeExists ? "" : ` ${chalk7.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED)}`;
2753
- const icon = snap.worktreeExists ? chalk7.blue("\u25CF") : chalk7.yellow("\u26A0");
2865
+ const orphanLabel = snap.worktreeExists ? "" : ` ${chalk8.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED)}`;
2866
+ const icon = snap.worktreeExists ? chalk8.blue("\u25CF") : chalk8.yellow("\u26A0");
2754
2867
  printInfo(` ${icon} ${snap.branch}${orphanLabel}`);
2755
2868
  }
2756
2869
  printInfo("");
2757
2870
  }
2758
2871
 
2759
2872
  // src/commands/alias.ts
2760
- import chalk8 from "chalk";
2873
+ import chalk9 from "chalk";
2761
2874
  function getRegisteredCommandNames(program2) {
2762
2875
  return program2.commands.map((cmd) => cmd.name());
2763
2876
  }
@@ -2778,7 +2891,7 @@ ${MESSAGES.ALIAS_LIST_TITLE}
2778
2891
  `);
2779
2892
  printSeparator();
2780
2893
  for (const [alias, command] of entries) {
2781
- printInfo(` ${chalk8.bold(alias)} \u2192 ${chalk8.cyan(command)}`);
2894
+ printInfo(` ${chalk9.bold(alias)} \u2192 ${chalk9.cyan(command)}`);
2782
2895
  }
2783
2896
  printInfo("");
2784
2897
  printSeparator();
@@ -90,7 +90,23 @@ var RUN_MESSAGES = {
90
90
  /** 任务文件加载成功 */
91
91
  TASK_FILE_LOADED: (count, path) => `\u2713 \u4ECE ${path} \u52A0\u8F7D\u4E86 ${count} \u4E2A\u4EFB\u52A1`,
92
92
  /** 未指定 -b 或 -f */
93
- BRANCH_OR_FILE_REQUIRED: "\u8BF7\u6307\u5B9A -b \u5206\u652F\u540D\u6216 -f \u4EFB\u52A1\u6587\u4EF6"
93
+ BRANCH_OR_FILE_REQUIRED: "\u8BF7\u6307\u5B9A -b \u5206\u652F\u540D\u6216 -f \u4EFB\u52A1\u6587\u4EF6",
94
+ /** dry-run 预览标题 */
95
+ DRY_RUN_TITLE: "Dry Run \u9884\u89C8",
96
+ /** dry-run 任务数量 */
97
+ DRY_RUN_TASK_COUNT: (count) => `\u4EFB\u52A1\u6570: ${count}`,
98
+ /** dry-run 并发数 */
99
+ DRY_RUN_CONCURRENCY: (concurrency) => `\u5E76\u53D1\u6570: ${concurrency === 0 ? "\u4E0D\u9650\u5236" : concurrency}`,
100
+ /** dry-run worktree 目录 */
101
+ DRY_RUN_WORKTREE_DIR: (dir) => `Worktree: ${dir}`,
102
+ /** dry-run 分支已存在警告 */
103
+ DRY_RUN_BRANCH_EXISTS_WARNING: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728`,
104
+ /** dry-run 交互式模式提示(无任务描述) */
105
+ DRY_RUN_INTERACTIVE_MODE: "\u6A21\u5F0F: \u4EA4\u4E92\u5F0F\uFF08\u65E0\u9884\u8BBE\u4EFB\u52A1\uFF09",
106
+ /** dry-run 预览完成且无冲突 */
107
+ DRY_RUN_READY: "\u9884\u89C8\u5B8C\u6210\uFF0C\u65E0\u51B2\u7A81\u3002\u79FB\u9664 --dry-run \u5373\u53EF\u6B63\u5F0F\u6267\u884C\u3002",
108
+ /** dry-run 存在分支冲突 */
109
+ DRY_RUN_HAS_CONFLICT: "\u5B58\u5728\u5206\u652F\u51B2\u7A81\uFF0C\u5B9E\u9645\u6267\u884C\u65F6\u5C06\u4F1A\u62A5\u9519\u3002\u8BF7\u5148\u5904\u7406\u51B2\u7A81\u7684\u5206\u652F\u3002"
94
110
  };
95
111
 
96
112
  // src/constants/messages/create.ts
@@ -357,7 +373,7 @@ var CONFIG_DEFINITIONS = {
357
373
  },
358
374
  aliases: {
359
375
  defaultValue: {},
360
- description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04\uFF08\u901A\u8FC7 clawt alias \u547D\u4EE4\u7BA1\u7406\uFF09"
376
+ description: "\u547D\u4EE4\u522B\u540D\u6620\u5C04"
361
377
  }
362
378
  };
363
379
  function deriveDefaultConfig(definitions) {