clawt 2.10.1 → 2.11.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/index.js CHANGED
@@ -71,7 +71,33 @@ var RUN_MESSAGES = {
71
71
  /** 中断后清理完成 */
72
72
  INTERRUPT_CLEANED: (count) => `\u2713 \u5DF2\u6E05\u7406 ${count} \u4E2A worktree \u548C\u5BF9\u5E94\u5206\u652F`,
73
73
  /** 中断后保留 worktree */
74
- INTERRUPT_KEPT: "\u5DF2\u4FDD\u7559 worktree\uFF0C\u53EF\u7A0D\u540E\u4F7F\u7528 clawt remove \u624B\u52A8\u6E05\u7406"
74
+ INTERRUPT_KEPT: "\u5DF2\u4FDD\u7559 worktree\uFF0C\u53EF\u7A0D\u540E\u4F7F\u7528 clawt remove \u624B\u52A8\u6E05\u7406",
75
+ /** 非 TTY 环境降级输出:任务启动 */
76
+ PROGRESS_TASK_STARTED: (index, total, branch, path) => `[${index}/${total}] ${branch} \u542F\u52A8 ${path}`,
77
+ /** 非 TTY 环境降级输出:任务完成 */
78
+ PROGRESS_TASK_DONE: (index, total, branch, duration, cost, path) => `[${index}/${total}] ${branch} \u2713 \u5B8C\u6210 ${duration} ${cost} ${path}`,
79
+ /** 非 TTY 环境降级输出:任务失败 */
80
+ PROGRESS_TASK_FAILED: (index, total, branch, duration, path) => `[${index}/${total}] ${branch} \u2717 \u5931\u8D25 ${duration} ${path}`,
81
+ /** 并发限制提示 */
82
+ CONCURRENCY_INFO: (concurrency, total) => `\u5E76\u53D1\u9650\u5236: ${concurrency}\uFF0C\u5171 ${total} \u4E2A\u4EFB\u52A1`,
83
+ /** 并发数无效提示 */
84
+ CONCURRENCY_INVALID: "\u5E76\u53D1\u6570\u5FC5\u987B\u4E3A\u6B63\u6574\u6570",
85
+ /** 任务文件不存在 */
86
+ TASK_FILE_NOT_FOUND: (path) => `\u4EFB\u52A1\u6587\u4EF6\u4E0D\u5B58\u5728: ${path}`,
87
+ /** 任务文件中没有解析到有效任务 */
88
+ TASK_FILE_EMPTY: "\u4EFB\u52A1\u6587\u4EF6\u4E2D\u6CA1\u6709\u89E3\u6790\u5230\u6709\u6548\u4EFB\u52A1",
89
+ /** 任务文件中某个任务块缺少分支名 */
90
+ TASK_FILE_MISSING_BRANCH: (blockIndex) => `\u4EFB\u52A1\u6587\u4EF6\u7B2C ${blockIndex} \u4E2A\u4EFB\u52A1\u5757\u7F3A\u5C11\u5206\u652F\u540D\uFF08# branch: ...\uFF09`,
91
+ /** 任务文件中某个任务块缺少任务描述 */
92
+ TASK_FILE_MISSING_TASK: (branch) => `\u4EFB\u52A1\u6587\u4EF6\u4E2D\u5206\u652F ${branch} \u7F3A\u5C11\u4EFB\u52A1\u63CF\u8FF0`,
93
+ /** 任务文件中某个任务块缺少任务描述(无分支名时按索引定位) */
94
+ TASK_FILE_MISSING_TASK_BY_INDEX: (blockIndex) => `\u4EFB\u52A1\u6587\u4EF6\u7B2C ${blockIndex} \u4E2A\u4EFB\u52A1\u5757\u7F3A\u5C11\u4EFB\u52A1\u63CF\u8FF0`,
95
+ /** --file 和 --tasks 不能同时使用 */
96
+ FILE_AND_TASKS_CONFLICT: "--file \u548C --tasks \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528",
97
+ /** 任务文件加载成功 */
98
+ TASK_FILE_LOADED: (count, path) => `\u2713 \u4ECE ${path} \u52A0\u8F7D\u4E86 ${count} \u4E2A\u4EFB\u52A1`,
99
+ /** 未指定 -b 或 -f */
100
+ BRANCH_OR_FILE_REQUIRED: "\u8BF7\u6307\u5B9A -b \u5206\u652F\u540D\u6216 -f \u4EFB\u52A1\u6587\u4EF6"
75
101
  };
76
102
 
77
103
  // src/constants/messages/create.ts
@@ -288,6 +314,10 @@ var CONFIG_DEFINITIONS = {
288
314
  confirmDestructiveOps: {
289
315
  defaultValue: true,
290
316
  description: "\u6267\u884C\u7834\u574F\u6027\u64CD\u4F5C\uFF08reset\u3001validate --clean\uFF09\u524D\u662F\u5426\u63D0\u793A\u786E\u8BA4"
317
+ },
318
+ maxConcurrency: {
319
+ defaultValue: 0,
320
+ description: "run \u547D\u4EE4\u9ED8\u8BA4\u6700\u5927\u5E76\u53D1\u6570\uFF0C0 \u8868\u793A\u4E0D\u9650\u5236"
291
321
  }
292
322
  };
293
323
  function deriveDefaultConfig(definitions) {
@@ -311,6 +341,32 @@ var AUTO_SAVE_COMMIT_MESSAGE = "chore: auto-save before sync";
311
341
  // src/constants/logger.ts
312
342
  var DEBUG_TIMESTAMP_FORMAT = "HH:mm:ss.SSS";
313
343
 
344
+ // src/constants/progress.ts
345
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
346
+ var SPINNER_INTERVAL_MS = 100;
347
+ var CURSOR_UP = (n) => `\x1B[${n}A`;
348
+ var CLEAR_LINE = "\x1B[0K";
349
+ var CURSOR_HIDE = "\x1B[?25l";
350
+ var CURSOR_SHOW = "\x1B[?25h";
351
+ var TASK_STATUS_ICONS = {
352
+ /** 排队中 */
353
+ PENDING: "\u25E6",
354
+ /** 完成 */
355
+ DONE: "\u2713",
356
+ /** 失败 */
357
+ FAILED: "\u2717"
358
+ };
359
+ var TASK_STATUS_LABELS = {
360
+ /** 排队中 */
361
+ PENDING: "\u6392\u961F\u4E2D",
362
+ /** 运行中 */
363
+ RUNNING: "\u8FD0\u884C\u4E2D",
364
+ /** 完成 */
365
+ DONE: "\u5B8C\u6210",
366
+ /** 失败 */
367
+ FAILED: "\u5931\u8D25"
368
+ };
369
+
314
370
  // src/errors/index.ts
315
371
  var ClawtError = class extends Error {
316
372
  /** 退出码 */
@@ -616,14 +672,14 @@ function printDoubleSeparator() {
616
672
  console.log(MESSAGES.DOUBLE_SEPARATOR);
617
673
  }
618
674
  function confirmAction(question) {
619
- return new Promise((resolve) => {
675
+ return new Promise((resolve2) => {
620
676
  const rl = createInterface({
621
677
  input: process.stdin,
622
678
  output: process.stdout
623
679
  });
624
680
  rl.question(`${question} (y/N) `, (answer) => {
625
681
  rl.close();
626
- resolve(answer.toLowerCase() === "y");
682
+ resolve2(answer.toLowerCase() === "y");
627
683
  });
628
684
  });
629
685
  }
@@ -650,6 +706,15 @@ function formatWorktreeStatus(status) {
650
706
  }
651
707
  return parts.join(" ");
652
708
  }
709
+ function formatDuration(ms) {
710
+ const totalSeconds = ms / 1e3;
711
+ if (totalSeconds < 60) {
712
+ return `${totalSeconds.toFixed(1)}s`;
713
+ }
714
+ const minutes = Math.floor(totalSeconds / 60);
715
+ const seconds = Math.floor(totalSeconds % 60);
716
+ return `${minutes}m${String(seconds).padStart(2, "0")}s`;
717
+ }
653
718
 
654
719
  // src/utils/branch.ts
655
720
  function sanitizeBranchName(branchName) {
@@ -742,6 +807,19 @@ function createWorktrees(branchName, count) {
742
807
  }
743
808
  return results;
744
809
  }
810
+ function createWorktreesByBranches(branchNames) {
811
+ validateBranchesNotExist(branchNames);
812
+ const projectDir = getProjectWorktreeDir();
813
+ ensureDir(projectDir);
814
+ const results = [];
815
+ for (const name of branchNames) {
816
+ const worktreePath = join2(projectDir, name);
817
+ createWorktree(name, worktreePath);
818
+ results.push({ path: worktreePath, branch: name });
819
+ logger.info(`worktree \u521B\u5EFA\u5B8C\u6210: ${worktreePath} (\u5206\u652F: ${name})`);
820
+ }
821
+ return results;
822
+ }
745
823
  function getProjectWorktrees() {
746
824
  const projectDir = getProjectWorktreeDir();
747
825
  if (!existsSync3(projectDir)) {
@@ -999,8 +1077,467 @@ async function resolveTargetWorktree(worktrees, messages, branchName) {
999
1077
  throw new ClawtError(messages.noMatch(branchName, allBranches));
1000
1078
  }
1001
1079
 
1002
- // src/commands/list.ts
1080
+ // src/utils/progress-render.ts
1003
1081
  import chalk3 from "chalk";
1082
+ function getMaxBranchWidth(tasks) {
1083
+ return Math.max(...tasks.map((t) => t.branch.length));
1084
+ }
1085
+ function renderTaskLine(task, total, maxBranchWidth, spinnerChar) {
1086
+ const indexStr = `[${task.index}/${total}]`;
1087
+ const branchStr = task.branch.padEnd(maxBranchWidth);
1088
+ switch (task.status) {
1089
+ case "pending": {
1090
+ return `${indexStr} ${branchStr} ${chalk3.gray(TASK_STATUS_ICONS.PENDING)} ${chalk3.gray(TASK_STATUS_LABELS.PENDING)} ${chalk3.dim(task.path)}`;
1091
+ }
1092
+ case "running": {
1093
+ const elapsed = formatDuration(Date.now() - task.startedAt);
1094
+ return `${indexStr} ${branchStr} ${chalk3.cyan(spinnerChar)} ${chalk3.cyan(TASK_STATUS_LABELS.RUNNING)} ${chalk3.gray(elapsed)} ${chalk3.dim(task.path)}`;
1095
+ }
1096
+ case "done": {
1097
+ const duration = task.durationMs != null ? formatDuration(task.durationMs) : "N/A";
1098
+ const cost = task.costUsd != null ? `$${task.costUsd.toFixed(2)}` : "";
1099
+ return `${indexStr} ${branchStr} ${chalk3.green(TASK_STATUS_ICONS.DONE)} ${chalk3.green(TASK_STATUS_LABELS.DONE)} ${chalk3.gray(duration)} ${chalk3.yellow(cost)} ${chalk3.dim(task.path)}`;
1100
+ }
1101
+ case "failed": {
1102
+ const duration = task.durationMs != null ? formatDuration(task.durationMs) : "N/A";
1103
+ return `${indexStr} ${branchStr} ${chalk3.red(TASK_STATUS_ICONS.FAILED)} ${chalk3.red(TASK_STATUS_LABELS.FAILED)} ${chalk3.gray(duration)} ${chalk3.dim(task.path)}`;
1104
+ }
1105
+ }
1106
+ }
1107
+ function renderSummaryLine(tasks, total) {
1108
+ const running = tasks.filter((t) => t.status === "running").length;
1109
+ const done = tasks.filter((t) => t.status === "done").length;
1110
+ const failed = tasks.filter((t) => t.status === "failed").length;
1111
+ const pending = tasks.filter((t) => t.status === "pending").length;
1112
+ const parts = [];
1113
+ if (running > 0) parts.push(chalk3.cyan(`${running}/${total} ${TASK_STATUS_LABELS.RUNNING}`));
1114
+ if (done > 0) parts.push(chalk3.green(`${done}/${total} ${TASK_STATUS_LABELS.DONE}`));
1115
+ if (failed > 0) parts.push(chalk3.red(`${failed}/${total} ${TASK_STATUS_LABELS.FAILED}`));
1116
+ if (pending > 0) parts.push(chalk3.gray(`${pending}/${total} ${TASK_STATUS_LABELS.PENDING}`));
1117
+ return `[${parts.join(", ")}]`;
1118
+ }
1119
+
1120
+ // src/utils/progress.ts
1121
+ var ProgressRenderer = class {
1122
+ /** 所有任务的进度状态 */
1123
+ tasks;
1124
+ /** 总任务数 */
1125
+ total;
1126
+ /** 当前 spinner 帧索引 */
1127
+ frameIndex;
1128
+ /** 定时器引用 */
1129
+ timer;
1130
+ /** 是否为 TTY 环境 */
1131
+ isTTY;
1132
+ /** 已渲染的行数(用于回退光标) */
1133
+ renderedLineCount;
1134
+ /** 是否已停止 */
1135
+ stopped;
1136
+ /** 是否存在排队任务(启用汇总行渲染) */
1137
+ hasPendingTasks;
1138
+ /**
1139
+ * 创建进度面板渲染器
1140
+ * @param {string[]} branches - 分支名列表,顺序对应任务列表
1141
+ * @param {string[]} paths - worktree 路径列表,完成/失败后显示
1142
+ * @param {boolean} [allRunning=true] - 是否将所有任务初始化为 running 状态,false 时初始化为 pending
1143
+ */
1144
+ constructor(branches, paths, allRunning = true) {
1145
+ const now = Date.now();
1146
+ this.total = branches.length;
1147
+ this.frameIndex = 0;
1148
+ this.timer = null;
1149
+ this.isTTY = !!process.stdout.isTTY;
1150
+ this.renderedLineCount = 0;
1151
+ this.stopped = false;
1152
+ this.hasPendingTasks = !allRunning;
1153
+ this.tasks = branches.map((branch, i) => ({
1154
+ index: i + 1,
1155
+ branch,
1156
+ path: paths[i],
1157
+ status: allRunning ? "running" : "pending",
1158
+ startedAt: allRunning ? now : 0,
1159
+ finishedAt: null,
1160
+ lastActiveAt: allRunning ? now : 0,
1161
+ durationMs: null,
1162
+ costUsd: null
1163
+ }));
1164
+ }
1165
+ /**
1166
+ * 启动定时渲染循环
1167
+ * TTY 模式下每 SPINNER_INTERVAL_MS 毫秒刷新一次面板
1168
+ * 非 TTY 模式下输出状态为 running 的任务的启动信息
1169
+ */
1170
+ start() {
1171
+ if (this.stopped) return;
1172
+ if (!this.isTTY) {
1173
+ for (const task of this.tasks) {
1174
+ if (task.status === "running") {
1175
+ console.log(MESSAGES.PROGRESS_TASK_STARTED(task.index, this.total, task.branch, task.path));
1176
+ }
1177
+ }
1178
+ return;
1179
+ }
1180
+ process.stdout.write(CURSOR_HIDE);
1181
+ this.render();
1182
+ this.timer = setInterval(() => {
1183
+ this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
1184
+ this.render();
1185
+ }, SPINNER_INTERVAL_MS);
1186
+ if (this.timer.unref) {
1187
+ this.timer.unref();
1188
+ }
1189
+ }
1190
+ /**
1191
+ * 更新指定任务的最后活动时间戳
1192
+ * 当 child.stderr 有输出时调用,表示任务仍然活跃
1193
+ * @param {number} index - 任务索引(从 0 开始)
1194
+ */
1195
+ updateActivity(index) {
1196
+ this.tasks[index].lastActiveAt = Date.now();
1197
+ }
1198
+ /**
1199
+ * 标记指定任务为运行中状态
1200
+ * 将 pending 任务标记为 running 并设置启动时间戳
1201
+ * @param {number} index - 任务索引(从 0 开始)
1202
+ */
1203
+ markRunning(index) {
1204
+ const task = this.tasks[index];
1205
+ const now = Date.now();
1206
+ task.status = "running";
1207
+ task.startedAt = now;
1208
+ task.lastActiveAt = now;
1209
+ if (!this.isTTY) {
1210
+ console.log(MESSAGES.PROGRESS_TASK_STARTED(task.index, this.total, task.branch, task.path));
1211
+ }
1212
+ }
1213
+ /**
1214
+ * 标记指定任务为完成状态
1215
+ * @param {number} index - 任务索引(从 0 开始)
1216
+ * @param {number} durationMs - 耗时(毫秒)
1217
+ * @param {number} costUsd - 费用(美元)
1218
+ */
1219
+ markDone(index, durationMs, costUsd) {
1220
+ const task = this.tasks[index];
1221
+ task.status = "done";
1222
+ task.finishedAt = Date.now();
1223
+ task.durationMs = durationMs;
1224
+ task.costUsd = costUsd;
1225
+ if (!this.isTTY) {
1226
+ const duration = formatDuration(durationMs);
1227
+ const cost = `$${costUsd.toFixed(2)}`;
1228
+ console.log(MESSAGES.PROGRESS_TASK_DONE(task.index, this.total, task.branch, duration, cost, task.path));
1229
+ }
1230
+ }
1231
+ /**
1232
+ * 标记指定任务为失败状态
1233
+ * @param {number} index - 任务索引(从 0 开始)
1234
+ * @param {number} durationMs - 耗时(毫秒)
1235
+ */
1236
+ markFailed(index, durationMs) {
1237
+ const task = this.tasks[index];
1238
+ task.status = "failed";
1239
+ task.finishedAt = Date.now();
1240
+ task.durationMs = durationMs;
1241
+ if (!this.isTTY) {
1242
+ const duration = formatDuration(durationMs);
1243
+ console.log(MESSAGES.PROGRESS_TASK_FAILED(task.index, this.total, task.branch, duration, task.path));
1244
+ }
1245
+ }
1246
+ /**
1247
+ * 停止渲染循环并恢复光标
1248
+ * 在所有任务完成或 SIGINT 中断时调用
1249
+ */
1250
+ stop() {
1251
+ if (this.stopped) return;
1252
+ this.stopped = true;
1253
+ if (this.timer) {
1254
+ clearInterval(this.timer);
1255
+ this.timer = null;
1256
+ }
1257
+ if (this.isTTY) {
1258
+ this.render();
1259
+ process.stdout.write(CURSOR_SHOW);
1260
+ }
1261
+ }
1262
+ /**
1263
+ * 执行一次完整的面板渲染
1264
+ * 先回退光标到面板起始位置,再逐行输出
1265
+ */
1266
+ render() {
1267
+ const maxBranchWidth = getMaxBranchWidth(this.tasks);
1268
+ const spinnerChar = SPINNER_FRAMES[this.frameIndex];
1269
+ const lines = this.tasks.map((task) => renderTaskLine(task, this.total, maxBranchWidth, spinnerChar));
1270
+ if (this.hasPendingTasks) {
1271
+ lines.push(renderSummaryLine(this.tasks, this.total));
1272
+ }
1273
+ if (this.renderedLineCount > 0) {
1274
+ process.stdout.write(CURSOR_UP(this.renderedLineCount));
1275
+ }
1276
+ for (const line of lines) {
1277
+ process.stdout.write(`${line}${CLEAR_LINE}
1278
+ `);
1279
+ }
1280
+ this.renderedLineCount = lines.length;
1281
+ }
1282
+ };
1283
+
1284
+ // src/utils/task-file.ts
1285
+ import { resolve } from "path";
1286
+ import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
1287
+ var TASK_BLOCK_REGEX = /<!-- CLAWT-TASKS:START -->([\s\S]*?)<!-- CLAWT-TASKS:END -->/g;
1288
+ var BRANCH_LINE_REGEX = /^#\s*branch:\s*(.+)$/;
1289
+ function parseTaskFile(content, options) {
1290
+ const branchRequired = options?.branchRequired ?? true;
1291
+ const entries = [];
1292
+ let match;
1293
+ let blockIndex = 0;
1294
+ TASK_BLOCK_REGEX.lastIndex = 0;
1295
+ while ((match = TASK_BLOCK_REGEX.exec(content)) !== null) {
1296
+ blockIndex++;
1297
+ const blockContent = match[1].trim();
1298
+ const lines = blockContent.split("\n");
1299
+ let branch;
1300
+ const taskLines = [];
1301
+ for (const line of lines) {
1302
+ const branchMatch = line.trim().match(BRANCH_LINE_REGEX);
1303
+ if (branchMatch) {
1304
+ branch = branchMatch[1].trim();
1305
+ } else {
1306
+ taskLines.push(line);
1307
+ }
1308
+ }
1309
+ if (branchRequired && !branch) {
1310
+ throw new ClawtError(MESSAGES.TASK_FILE_MISSING_BRANCH(blockIndex));
1311
+ }
1312
+ const task = taskLines.join("\n").trim();
1313
+ if (!task) {
1314
+ throw new ClawtError(
1315
+ branch ? MESSAGES.TASK_FILE_MISSING_TASK(branch) : MESSAGES.TASK_FILE_MISSING_TASK_BY_INDEX(blockIndex)
1316
+ );
1317
+ }
1318
+ entries.push({ branch, task });
1319
+ }
1320
+ return entries;
1321
+ }
1322
+ function loadTaskFile(filePath, options) {
1323
+ const absolutePath = resolve(filePath);
1324
+ if (!existsSync6(absolutePath)) {
1325
+ throw new ClawtError(MESSAGES.TASK_FILE_NOT_FOUND(absolutePath));
1326
+ }
1327
+ const content = readFileSync3(absolutePath, "utf-8");
1328
+ const entries = parseTaskFile(content, options);
1329
+ if (entries.length === 0) {
1330
+ throw new ClawtError(MESSAGES.TASK_FILE_EMPTY);
1331
+ }
1332
+ return entries;
1333
+ }
1334
+
1335
+ // src/utils/task-executor.ts
1336
+ function executeClaudeTask(worktree, task) {
1337
+ const child = spawnProcess(
1338
+ "claude",
1339
+ ["-p", task, "--output-format", "json", "--permission-mode", "bypassPermissions"],
1340
+ {
1341
+ cwd: worktree.path,
1342
+ // stdin 必须设置为 'ignore',不能用 'pipe'
1343
+ // 原因:claude -p 是非交互模式,不需要 stdin 输入。如果 stdin 为 'pipe',
1344
+ // 父进程会创建一个可写流连接到子进程但从不写入也不关闭,
1345
+ // claude 检测到 stdin 是管道后会尝试读取输入,导致进程永远卡住
1346
+ stdio: ["ignore", "pipe", "pipe"]
1347
+ }
1348
+ );
1349
+ const promise = new Promise((resolve2) => {
1350
+ let stdout = "";
1351
+ let stderr = "";
1352
+ child.stdout?.on("data", (data) => {
1353
+ stdout += data.toString();
1354
+ });
1355
+ child.stderr?.on("data", (data) => {
1356
+ stderr += data.toString();
1357
+ });
1358
+ child.on("close", (code) => {
1359
+ let result = null;
1360
+ let success = code === 0;
1361
+ try {
1362
+ if (stdout.trim()) {
1363
+ result = JSON.parse(stdout.trim());
1364
+ success = !result.is_error;
1365
+ }
1366
+ } catch {
1367
+ logger.warn(`\u89E3\u6790 Claude Code \u8F93\u51FA\u5931\u8D25: ${stdout.substring(0, 200)}`);
1368
+ }
1369
+ resolve2({
1370
+ task,
1371
+ branch: worktree.branch,
1372
+ worktreePath: worktree.path,
1373
+ success,
1374
+ result,
1375
+ error: success ? void 0 : stderr || "\u4EFB\u52A1\u6267\u884C\u5931\u8D25"
1376
+ });
1377
+ });
1378
+ child.on("error", (err) => {
1379
+ resolve2({
1380
+ task,
1381
+ branch: worktree.branch,
1382
+ worktreePath: worktree.path,
1383
+ success: false,
1384
+ result: null,
1385
+ error: err.message
1386
+ });
1387
+ });
1388
+ });
1389
+ return { child, promise };
1390
+ }
1391
+ function printTaskSummary(summary) {
1392
+ printDoubleSeparator();
1393
+ printInfo(`\u5168\u90E8\u4EFB\u52A1\u5DF2\u5B8C\u6210 (${summary.total}/${summary.total})`);
1394
+ printInfo(` \u6210\u529F: ${summary.succeeded}`);
1395
+ printInfo(` \u5931\u8D25: ${summary.failed}`);
1396
+ printInfo(` \u603B\u8017\u65F6: ${(summary.totalDurationMs / 1e3).toFixed(1)}s`);
1397
+ printInfo(` \u603B\u82B1\u8D39: $${summary.totalCostUsd.toFixed(2)}`);
1398
+ printDoubleSeparator();
1399
+ }
1400
+ async function handleInterruptCleanup(worktrees) {
1401
+ const autoDelete = getConfigValue("autoDeleteBranch");
1402
+ if (autoDelete) {
1403
+ cleanupWorktrees(worktrees);
1404
+ printSuccess(MESSAGES.INTERRUPT_AUTO_CLEANED(worktrees.length));
1405
+ return;
1406
+ }
1407
+ const shouldClean = await confirmAction(MESSAGES.INTERRUPT_CONFIRM_CLEANUP);
1408
+ if (shouldClean) {
1409
+ cleanupWorktrees(worktrees);
1410
+ printSuccess(MESSAGES.INTERRUPT_CLEANED(worktrees.length));
1411
+ } else {
1412
+ printInfo(MESSAGES.INTERRUPT_KEPT);
1413
+ }
1414
+ }
1415
+ function updateRendererStatus(renderer, index, result, startTime) {
1416
+ if (result.success) {
1417
+ renderer.markDone(
1418
+ index,
1419
+ result.result?.duration_ms ?? Date.now() - startTime,
1420
+ result.result?.total_cost_usd ?? 0
1421
+ );
1422
+ } else {
1423
+ renderer.markFailed(
1424
+ index,
1425
+ result.result?.duration_ms ?? Date.now() - startTime
1426
+ );
1427
+ }
1428
+ }
1429
+ async function executeWithConcurrency(worktrees, tasks, concurrency, renderer, startTime, isInterrupted, childProcesses) {
1430
+ const total = tasks.length;
1431
+ const results = new Array(total);
1432
+ let nextIndex = 0;
1433
+ let completedCount = 0;
1434
+ return new Promise((resolve2) => {
1435
+ function launchNext() {
1436
+ if (nextIndex >= total || isInterrupted()) return;
1437
+ const index = nextIndex;
1438
+ nextIndex++;
1439
+ const wt = worktrees[index];
1440
+ const task = tasks[index];
1441
+ logger.info(`\u542F\u52A8\u4EFB\u52A1 ${index + 1}: ${task} (worktree: ${wt.path})`);
1442
+ renderer.markRunning(index);
1443
+ const handle = executeClaudeTask(wt, task);
1444
+ childProcesses.push(handle.child);
1445
+ handle.child.stderr?.on("data", () => {
1446
+ renderer.updateActivity(index);
1447
+ });
1448
+ handle.promise.then((result) => {
1449
+ results[index] = result;
1450
+ completedCount++;
1451
+ if (!isInterrupted()) {
1452
+ updateRendererStatus(renderer, index, result, startTime);
1453
+ }
1454
+ launchNext();
1455
+ if (completedCount === total) {
1456
+ resolve2(results);
1457
+ }
1458
+ });
1459
+ }
1460
+ const initialBatch = Math.min(concurrency, total);
1461
+ for (let i = 0; i < initialBatch; i++) {
1462
+ launchNext();
1463
+ }
1464
+ });
1465
+ }
1466
+ async function executeAllParallel(worktrees, tasks, renderer, startTime, isInterrupted, childProcesses) {
1467
+ const handles = worktrees.map((wt, index) => {
1468
+ const task = tasks[index];
1469
+ logger.info(`\u542F\u52A8\u4EFB\u52A1 ${index + 1}: ${task} (worktree: ${wt.path})`);
1470
+ const handle = executeClaudeTask(wt, task);
1471
+ childProcesses.push(handle.child);
1472
+ handle.child.stderr?.on("data", () => {
1473
+ renderer.updateActivity(index);
1474
+ });
1475
+ return handle;
1476
+ });
1477
+ const results = await Promise.all(
1478
+ handles.map(
1479
+ (handle, index) => handle.promise.then((result) => {
1480
+ if (!isInterrupted()) {
1481
+ updateRendererStatus(renderer, index, result, startTime);
1482
+ }
1483
+ return result;
1484
+ })
1485
+ )
1486
+ );
1487
+ return results;
1488
+ }
1489
+ async function executeBatchTasks(worktrees, tasks, concurrency) {
1490
+ const count = tasks.length;
1491
+ if (concurrency > 0) {
1492
+ printInfo(MESSAGES.CONCURRENCY_INFO(concurrency, count));
1493
+ printInfo("");
1494
+ }
1495
+ const startTime = Date.now();
1496
+ const branches = worktrees.map((wt) => wt.branch);
1497
+ const paths = worktrees.map((wt) => wt.path);
1498
+ const allRunning = concurrency === 0;
1499
+ const renderer = new ProgressRenderer(branches, paths, allRunning);
1500
+ renderer.start();
1501
+ let interrupted = false;
1502
+ const isInterrupted = () => interrupted;
1503
+ const childProcesses = [];
1504
+ const sigintHandler = async () => {
1505
+ if (interrupted) return;
1506
+ interrupted = true;
1507
+ renderer.stop();
1508
+ printInfo("");
1509
+ printWarning(MESSAGES.INTERRUPTED);
1510
+ killAllChildProcesses(childProcesses);
1511
+ await Promise.allSettled(childProcesses.map(
1512
+ (cp) => new Promise((resolve2) => {
1513
+ if (cp.exitCode !== null) {
1514
+ resolve2();
1515
+ } else {
1516
+ cp.on("close", () => resolve2());
1517
+ }
1518
+ })
1519
+ ));
1520
+ await handleInterruptCleanup(worktrees);
1521
+ process.exit(1);
1522
+ };
1523
+ process.on("SIGINT", sigintHandler);
1524
+ const results = concurrency > 0 ? await executeWithConcurrency(worktrees, tasks, concurrency, renderer, startTime, isInterrupted, childProcesses) : await executeAllParallel(worktrees, tasks, renderer, startTime, isInterrupted, childProcesses);
1525
+ renderer.stop();
1526
+ process.removeListener("SIGINT", sigintHandler);
1527
+ if (interrupted) return;
1528
+ const totalDurationMs = Date.now() - startTime;
1529
+ const summary = {
1530
+ total: results.length,
1531
+ succeeded: results.filter((r) => r.success).length,
1532
+ failed: results.filter((r) => !r.success).length,
1533
+ totalDurationMs,
1534
+ totalCostUsd: results.reduce((sum, r) => sum + (r.result?.total_cost_usd ?? 0), 0)
1535
+ };
1536
+ printTaskSummary(summary);
1537
+ }
1538
+
1539
+ // src/commands/list.ts
1540
+ import chalk4 from "chalk";
1004
1541
  function registerListCommand(program2) {
1005
1542
  program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
1006
1543
  handleList(options);
@@ -1037,12 +1574,12 @@ function printListAsText(projectName, worktrees) {
1037
1574
  for (const wt of worktrees) {
1038
1575
  const status = getWorktreeStatus(wt);
1039
1576
  const isIdle = status ? isWorktreeIdle(status) : false;
1040
- const pathDisplay = isIdle ? chalk3.hex("#FF8C00")(wt.path) : wt.path;
1577
+ const pathDisplay = isIdle ? chalk4.hex("#FF8C00")(wt.path) : wt.path;
1041
1578
  printInfo(` ${pathDisplay} [${wt.branch}]`);
1042
1579
  if (status) {
1043
1580
  printInfo(` ${formatWorktreeStatus(status)}`);
1044
1581
  } else {
1045
- printInfo(` ${chalk3.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
1582
+ printInfo(` ${chalk4.yellow(MESSAGES.WORKTREE_STATUS_UNAVAILABLE)}`);
1046
1583
  }
1047
1584
  printInfo("");
1048
1585
  }
@@ -1145,110 +1682,48 @@ async function handleRemove(options) {
1145
1682
 
1146
1683
  // src/commands/run.ts
1147
1684
  function registerRunCommand(program2) {
1148
- program2.command("run").description("\u6279\u91CF\u521B\u5EFA worktree \u5E76\u542F\u52A8 Claude Code \u6267\u884C\u4EFB\u52A1").requiredOption("-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").action(async (options) => {
1685
+ 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) => {
1149
1686
  await handleRun(options);
1150
1687
  });
1151
1688
  }
1152
- function executeClaudeTask(worktree, task) {
1153
- const child = spawnProcess(
1154
- "claude",
1155
- ["-p", task, "--output-format", "json", "--permission-mode", "bypassPermissions"],
1156
- {
1157
- cwd: worktree.path,
1158
- // stdin 必须设置为 'ignore',不能用 'pipe'
1159
- // 原因:claude -p 是非交互模式,不需要 stdin 输入。如果 stdin 为 'pipe',
1160
- // 父进程会创建一个可写流连接到子进程但从不写入也不关闭,
1161
- // claude 检测到 stdin 是管道后会尝试读取输入,导致进程永远卡住
1162
- stdio: ["ignore", "pipe", "pipe"]
1163
- }
1164
- );
1165
- const promise = new Promise((resolve) => {
1166
- let stdout = "";
1167
- let stderr = "";
1168
- child.stdout?.on("data", (data) => {
1169
- stdout += data.toString();
1170
- });
1171
- child.stderr?.on("data", (data) => {
1172
- stderr += data.toString();
1173
- });
1174
- child.on("close", (code) => {
1175
- let result = null;
1176
- let success = code === 0;
1177
- try {
1178
- if (stdout.trim()) {
1179
- result = JSON.parse(stdout.trim());
1180
- success = !result.is_error;
1181
- }
1182
- } catch {
1183
- logger.warn(`\u89E3\u6790 Claude Code \u8F93\u51FA\u5931\u8D25: ${stdout.substring(0, 200)}`);
1184
- }
1185
- resolve({
1186
- task,
1187
- branch: worktree.branch,
1188
- worktreePath: worktree.path,
1189
- success,
1190
- result,
1191
- error: success ? void 0 : stderr || "\u4EFB\u52A1\u6267\u884C\u5931\u8D25"
1192
- });
1193
- });
1194
- child.on("error", (err) => {
1195
- resolve({
1196
- task,
1197
- branch: worktree.branch,
1198
- worktreePath: worktree.path,
1199
- success: false,
1200
- result: null,
1201
- error: err.message
1202
- });
1203
- });
1204
- });
1205
- return { child, promise };
1206
- }
1207
- function printTaskNotification(taskResult) {
1208
- const { success, worktreePath, branch, result } = taskResult;
1209
- const status = success ? "\u5B8C\u6210" : "\u5931\u8D25";
1210
- const icon = success ? "\u2713" : "\u2717";
1211
- const durationStr = result ? `${(result.duration_ms / 1e3).toFixed(1)}s` : "N/A";
1212
- const costStr = result ? `$${result.total_cost_usd.toFixed(2)}` : "N/A";
1213
- const resultStr = success ? "success" : "failed";
1214
- if (success) {
1215
- printSuccess(`${icon} [${status}] worktree: ${worktreePath}`);
1216
- } else {
1217
- printError(`${icon} [${status}] worktree: ${worktreePath}`);
1689
+ function parseConcurrency(optionValue, configValue) {
1690
+ if (optionValue === void 0) {
1691
+ return configValue;
1218
1692
  }
1219
- printInfo(` \u5206\u652F: ${branch}`);
1220
- printInfo(` \u8017\u65F6: ${durationStr}`);
1221
- printInfo(` \u82B1\u8D39: ${costStr}`);
1222
- printInfo(` \u7ED3\u679C: ${resultStr}`);
1223
- printSeparator();
1224
- }
1225
- function printTaskSummary(summary) {
1226
- printDoubleSeparator();
1227
- printInfo(`\u5168\u90E8\u4EFB\u52A1\u5DF2\u5B8C\u6210 (${summary.total}/${summary.total})`);
1228
- printInfo(` \u6210\u529F: ${summary.succeeded}`);
1229
- printInfo(` \u5931\u8D25: ${summary.failed}`);
1230
- printInfo(` \u603B\u8017\u65F6: ${(summary.totalDurationMs / 1e3).toFixed(1)}s`);
1231
- printInfo(` \u603B\u82B1\u8D39: $${summary.totalCostUsd.toFixed(2)}`);
1232
- printDoubleSeparator();
1233
- }
1234
- async function handleInterruptCleanup(worktrees) {
1235
- const autoDelete = getConfigValue("autoDeleteBranch");
1236
- if (autoDelete) {
1237
- cleanupWorktrees(worktrees);
1238
- printSuccess(MESSAGES.INTERRUPT_AUTO_CLEANED(worktrees.length));
1239
- return;
1693
+ const parsed = parseInt(optionValue, 10);
1694
+ if (Number.isNaN(parsed) || parsed < 0) {
1695
+ throw new ClawtError(MESSAGES.CONCURRENCY_INVALID);
1240
1696
  }
1241
- const shouldClean = await confirmAction(MESSAGES.INTERRUPT_CONFIRM_CLEANUP);
1242
- if (shouldClean) {
1243
- cleanupWorktrees(worktrees);
1244
- printSuccess(MESSAGES.INTERRUPT_CLEANED(worktrees.length));
1697
+ return parsed;
1698
+ }
1699
+ async function handleRunFromFile(options) {
1700
+ const branchRequired = !options.branch;
1701
+ const entries = loadTaskFile(options.file, { branchRequired });
1702
+ printSuccess(MESSAGES.TASK_FILE_LOADED(entries.length, options.file));
1703
+ const tasks = entries.map((e) => e.task);
1704
+ let worktrees;
1705
+ if (options.branch) {
1706
+ worktrees = createWorktrees(options.branch, entries.length);
1245
1707
  } else {
1246
- printInfo(MESSAGES.INTERRUPT_KEPT);
1708
+ const branches = entries.map((e) => sanitizeBranchName(e.branch));
1709
+ worktrees = createWorktreesByBranches(branches);
1247
1710
  }
1711
+ const concurrency = parseConcurrency(options.concurrency, getConfigValue("maxConcurrency"));
1712
+ 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"}`);
1713
+ await executeBatchTasks(worktrees, tasks, concurrency);
1248
1714
  }
1249
1715
  async function handleRun(options) {
1250
1716
  validateMainWorktree();
1251
1717
  validateClaudeCodeInstalled();
1718
+ if (options.file && options.tasks) {
1719
+ throw new ClawtError(MESSAGES.FILE_AND_TASKS_CONFLICT);
1720
+ }
1721
+ if (options.file) {
1722
+ return handleRunFromFile(options);
1723
+ }
1724
+ if (!options.branch) {
1725
+ throw new ClawtError(MESSAGES.BRANCH_OR_FILE_REQUIRED);
1726
+ }
1252
1727
  if (!options.tasks || options.tasks.length === 0) {
1253
1728
  const sanitized = sanitizeBranchName(options.branch);
1254
1729
  if (checkBranchExists(sanitized)) {
@@ -1265,52 +1740,10 @@ async function handleRun(options) {
1265
1740
  throw new ClawtError("\u4EFB\u52A1\u5217\u8868\u4E0D\u80FD\u4E3A\u7A7A");
1266
1741
  }
1267
1742
  const count = tasks.length;
1268
- logger.info(`run \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch}\uFF0C\u4EFB\u52A1\u6570: ${count}`);
1743
+ const concurrency = parseConcurrency(options.concurrency, getConfigValue("maxConcurrency"));
1744
+ 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"}`);
1269
1745
  const worktrees = createWorktrees(options.branch, count);
1270
- printSuccess(MESSAGES.WORKTREE_CREATED(worktrees.length));
1271
- for (const wt of worktrees) {
1272
- printInfo(` \u5206\u652F: ${wt.branch} \u8DEF\u5F84: ${wt.path}`);
1273
- }
1274
- printInfo("");
1275
- const startTime = Date.now();
1276
- const handles = worktrees.map((wt, index) => {
1277
- const task = tasks[index];
1278
- logger.info(`\u542F\u52A8\u4EFB\u52A1 ${index + 1}: ${task} (worktree: ${wt.path})`);
1279
- return executeClaudeTask(wt, task);
1280
- });
1281
- const childProcesses = handles.map((h) => h.child);
1282
- let interrupted = false;
1283
- const sigintHandler = async () => {
1284
- if (interrupted) return;
1285
- interrupted = true;
1286
- printInfo("");
1287
- printWarning(MESSAGES.INTERRUPTED);
1288
- killAllChildProcesses(childProcesses);
1289
- await Promise.allSettled(handles.map((h) => h.promise));
1290
- await handleInterruptCleanup(worktrees);
1291
- process.exit(1);
1292
- };
1293
- process.on("SIGINT", sigintHandler);
1294
- const taskPromises = handles.map(
1295
- (handle) => handle.promise.then((result) => {
1296
- if (!interrupted) {
1297
- printTaskNotification(result);
1298
- }
1299
- return result;
1300
- })
1301
- );
1302
- const results = await Promise.all(taskPromises);
1303
- process.removeListener("SIGINT", sigintHandler);
1304
- if (interrupted) return;
1305
- const totalDurationMs = Date.now() - startTime;
1306
- const summary = {
1307
- total: results.length,
1308
- succeeded: results.filter((r) => r.success).length,
1309
- failed: results.filter((r) => !r.success).length,
1310
- totalDurationMs,
1311
- totalCostUsd: results.reduce((sum, r) => sum + (r.result?.total_cost_usd ?? 0), 0)
1312
- };
1313
- printTaskSummary(summary);
1746
+ await executeBatchTasks(worktrees, tasks, concurrency);
1314
1747
  }
1315
1748
 
1316
1749
  // src/commands/resume.ts
@@ -1637,7 +2070,7 @@ async function handleMerge(options) {
1637
2070
  }
1638
2071
 
1639
2072
  // src/commands/config.ts
1640
- import chalk4 from "chalk";
2073
+ import chalk5 from "chalk";
1641
2074
  function registerConfigCommand(program2) {
1642
2075
  const configCmd = program2.command("config").description("\u67E5\u770B\u548C\u7BA1\u7406\u5168\u5C40\u914D\u7F6E").action(() => {
1643
2076
  handleConfig();
@@ -1650,7 +2083,7 @@ function handleConfig() {
1650
2083
  const config = loadConfig();
1651
2084
  logger.info("config \u547D\u4EE4\u6267\u884C\uFF0C\u5C55\u793A\u5168\u5C40\u914D\u7F6E");
1652
2085
  printInfo(`
1653
- ${chalk4.dim("\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84:")} ${CONFIG_PATH}
2086
+ ${chalk5.dim("\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84:")} ${CONFIG_PATH}
1654
2087
  `);
1655
2088
  printSeparator();
1656
2089
  const keys = Object.keys(DEFAULT_CONFIG);
@@ -1660,8 +2093,8 @@ ${chalk4.dim("\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84:")} ${CONFIG_PATH}
1660
2093
  const description = CONFIG_DESCRIPTIONS[key];
1661
2094
  const formattedValue = formatConfigValue(value);
1662
2095
  if (i === 0) printInfo("");
1663
- printInfo(` ${chalk4.bold(key)}: ${formattedValue}`);
1664
- printInfo(` ${chalk4.dim(description)}`);
2096
+ printInfo(` ${chalk5.bold(key)}: ${formattedValue}`);
2097
+ printInfo(` ${chalk5.dim(description)}`);
1665
2098
  printInfo("");
1666
2099
  }
1667
2100
  printSeparator();
@@ -1681,9 +2114,9 @@ async function handleConfigReset() {
1681
2114
  }
1682
2115
  function formatConfigValue(value) {
1683
2116
  if (typeof value === "boolean") {
1684
- return value ? chalk4.green("true") : chalk4.yellow("false");
2117
+ return value ? chalk5.green("true") : chalk5.yellow("false");
1685
2118
  }
1686
- return chalk4.cyan(String(value));
2119
+ return chalk5.cyan(String(value));
1687
2120
  }
1688
2121
 
1689
2122
  // src/commands/sync.ts
@@ -1770,7 +2203,7 @@ async function handleReset() {
1770
2203
  }
1771
2204
 
1772
2205
  // src/commands/status.ts
1773
- import chalk5 from "chalk";
2206
+ import chalk6 from "chalk";
1774
2207
  function registerStatusCommand(program2) {
1775
2208
  program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
1776
2209
  handleStatus(options);
@@ -1866,7 +2299,7 @@ function printStatusAsJson(result) {
1866
2299
  }
1867
2300
  function printStatusAsText(result) {
1868
2301
  printDoubleSeparator();
1869
- printInfo(` ${chalk5.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
2302
+ printInfo(` ${chalk6.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
1870
2303
  printDoubleSeparator();
1871
2304
  printInfo("");
1872
2305
  printMainSection(result.main);
@@ -1879,17 +2312,17 @@ function printStatusAsText(result) {
1879
2312
  printDoubleSeparator();
1880
2313
  }
1881
2314
  function printMainSection(main) {
1882
- printInfo(` ${chalk5.bold("\u25C6")} ${chalk5.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
1883
- printInfo(` \u5206\u652F: ${chalk5.bold(main.branch)}`);
2315
+ printInfo(` ${chalk6.bold("\u25C6")} ${chalk6.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
2316
+ printInfo(` \u5206\u652F: ${chalk6.bold(main.branch)}`);
1884
2317
  if (main.isClean) {
1885
- printInfo(` \u72B6\u6001: ${chalk5.green("\u2713 \u5E72\u51C0")}`);
2318
+ printInfo(` \u72B6\u6001: ${chalk6.green("\u2713 \u5E72\u51C0")}`);
1886
2319
  } else {
1887
- printInfo(` \u72B6\u6001: ${chalk5.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
2320
+ printInfo(` \u72B6\u6001: ${chalk6.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
1888
2321
  }
1889
2322
  printInfo("");
1890
2323
  }
1891
2324
  function printWorktreesSection(worktrees, total) {
1892
- printInfo(` ${chalk5.bold("\u25C6")} ${chalk5.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
2325
+ printInfo(` ${chalk6.bold("\u25C6")} ${chalk6.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
1893
2326
  printInfo("");
1894
2327
  if (worktrees.length === 0) {
1895
2328
  printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
@@ -1901,39 +2334,39 @@ function printWorktreesSection(worktrees, total) {
1901
2334
  }
1902
2335
  function printWorktreeItem(wt) {
1903
2336
  const statusLabel = formatChangeStatusLabel(wt.changeStatus);
1904
- printInfo(` ${chalk5.bold("\u25CF")} ${chalk5.bold(wt.branch)} [${statusLabel}]`);
2337
+ printInfo(` ${chalk6.bold("\u25CF")} ${chalk6.bold(wt.branch)} [${statusLabel}]`);
1905
2338
  const parts = [];
1906
2339
  if (wt.insertions > 0 || wt.deletions > 0) {
1907
- parts.push(`${chalk5.green(`+${wt.insertions}`)} ${chalk5.red(`-${wt.deletions}`)}`);
2340
+ parts.push(`${chalk6.green(`+${wt.insertions}`)} ${chalk6.red(`-${wt.deletions}`)}`);
1908
2341
  }
1909
2342
  if (wt.commitsAhead > 0) {
1910
- parts.push(chalk5.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`));
2343
+ parts.push(chalk6.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`));
1911
2344
  }
1912
2345
  if (wt.commitsBehind > 0) {
1913
- parts.push(chalk5.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`));
2346
+ parts.push(chalk6.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`));
1914
2347
  } else {
1915
- parts.push(chalk5.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65"));
2348
+ parts.push(chalk6.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65"));
1916
2349
  }
1917
2350
  printInfo(` ${parts.join(" ")}`);
1918
2351
  if (wt.hasSnapshot) {
1919
- printInfo(` ${chalk5.blue("\u6709 validate \u5FEB\u7167")}`);
2352
+ printInfo(` ${chalk6.blue("\u6709 validate \u5FEB\u7167")}`);
1920
2353
  }
1921
2354
  printInfo("");
1922
2355
  }
1923
2356
  function formatChangeStatusLabel(status) {
1924
2357
  switch (status) {
1925
2358
  case "committed":
1926
- return chalk5.green(MESSAGES.STATUS_CHANGE_COMMITTED);
2359
+ return chalk6.green(MESSAGES.STATUS_CHANGE_COMMITTED);
1927
2360
  case "uncommitted":
1928
- return chalk5.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
2361
+ return chalk6.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
1929
2362
  case "conflict":
1930
- return chalk5.red(MESSAGES.STATUS_CHANGE_CONFLICT);
2363
+ return chalk6.red(MESSAGES.STATUS_CHANGE_CONFLICT);
1931
2364
  case "clean":
1932
- return chalk5.gray(MESSAGES.STATUS_CHANGE_CLEAN);
2365
+ return chalk6.gray(MESSAGES.STATUS_CHANGE_CLEAN);
1933
2366
  }
1934
2367
  }
1935
2368
  function printSnapshotsSection(snapshots) {
1936
- printInfo(` ${chalk5.bold("\u25C6")} ${chalk5.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.length} \u4E2A)`);
2369
+ printInfo(` ${chalk6.bold("\u25C6")} ${chalk6.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.length} \u4E2A)`);
1937
2370
  printInfo("");
1938
2371
  if (snapshots.length === 0) {
1939
2372
  printInfo(` ${MESSAGES.STATUS_NO_SNAPSHOTS}`);
@@ -1941,8 +2374,8 @@ function printSnapshotsSection(snapshots) {
1941
2374
  return;
1942
2375
  }
1943
2376
  for (const snap of snapshots) {
1944
- const orphanLabel = snap.worktreeExists ? "" : ` ${chalk5.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED)}`;
1945
- const icon = snap.worktreeExists ? chalk5.blue("\u25CF") : chalk5.yellow("\u26A0");
2377
+ const orphanLabel = snap.worktreeExists ? "" : ` ${chalk6.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED)}`;
2378
+ const icon = snap.worktreeExists ? chalk6.blue("\u25CF") : chalk6.yellow("\u26A0");
1946
2379
  printInfo(` ${icon} ${snap.branch}${orphanLabel}`);
1947
2380
  }
1948
2381
  printInfo("");