clawt 3.4.4 → 3.4.6

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
@@ -535,6 +535,8 @@ var PANEL_SHORTCUT_KEYS = {
535
535
  RESUME: "r",
536
536
  /** 同步 */
537
537
  SYNC: "s",
538
+ /** 覆盖 */
539
+ COVER: "c",
538
540
  /** 手动刷新 */
539
541
  REFRESH: "f",
540
542
  /** 退出 */
@@ -550,6 +552,7 @@ var SHORTCUT_LABELS = {
550
552
  DELETE: "\u5220\u9664",
551
553
  RESUME: "\u6062\u590D",
552
554
  SYNC: "\u540C\u6B65",
555
+ COVER: "\u8986\u76D6",
553
556
  REFRESH: "\u5237\u65B0",
554
557
  QUIT: "\u9000\u51FA"
555
558
  };
@@ -748,6 +751,32 @@ var GROUP_SEPARATOR_LABEL = (dateLabel, relativeTime) => `\u2550\u2550\u2550\u25
748
751
  var UNKNOWN_DATE_GROUP = "\u672A\u77E5\u65E5\u671F";
749
752
  var UNKNOWN_DATE_SEPARATOR_LABEL = `\u2550\u2550\u2550\u2550 ${chalk2.bold.hex("#FF8C00")("\u672A\u77E5\u65E5\u671F")} \u2550\u2550\u2550\u2550`;
750
753
 
754
+ // src/constants/pre-checks.ts
755
+ var PRE_CHECK_CREATE = {
756
+ requireMainWorktree: true,
757
+ requireHead: true,
758
+ ensureOnClawtMainWorkBranch: true,
759
+ requireCleanWorkingDir: true
760
+ };
761
+ var PRE_CHECK_RUN = { ...PRE_CHECK_CREATE };
762
+ var PRE_CHECK_DRY_RUN = { requireMainWorktree: true, requireHead: true };
763
+ var PRE_CHECK_MERGE = {
764
+ requireMainWorktree: true,
765
+ requireHead: true,
766
+ ensureOnClawtMainWorkBranch: true
767
+ };
768
+ var PRE_CHECK_SYNC = {
769
+ requireMainWorktree: true,
770
+ requireHead: true,
771
+ requireProjectConfig: true,
772
+ ensureOnClawtMainWorkBranch: true
773
+ };
774
+ var PRE_CHECK_RESUME = {
775
+ requireMainWorktree: true,
776
+ requireHead: true,
777
+ requireClaudeCode: true
778
+ };
779
+
751
780
  // src/errors/index.ts
752
781
  var ClawtError = class extends Error {
753
782
  /** 退出码 */
@@ -1063,17 +1092,6 @@ function gitApplyCachedCheck(patchContent, cwd) {
1063
1092
  return false;
1064
1093
  }
1065
1094
  }
1066
- function getBranchCreatedAt(branchName, cwd) {
1067
- try {
1068
- const output = execCommand(`git reflog show ${branchName} --format=%cI`, { cwd });
1069
- if (!output.trim()) return null;
1070
- const lines = output.trim().split("\n");
1071
- const lastLine = lines[lines.length - 1];
1072
- return lastLine || null;
1073
- } catch {
1074
- return null;
1075
- }
1076
- }
1077
1095
  function gitCheckout(branchName, cwd) {
1078
1096
  execCommand(`git checkout ${branchName}`, { cwd });
1079
1097
  }
@@ -1321,76 +1339,11 @@ function guardMainWorkBranchExists(cwd) {
1321
1339
  throw new ClawtError(MESSAGES.GUARD_BRANCH_NOT_EXISTS(mainBranch));
1322
1340
  }
1323
1341
  }
1324
- async function guardMainWorkBranch(cwd) {
1325
- guardMainWorkBranchExists(cwd);
1326
- const config2 = requireProjectConfig();
1327
- const mainBranch = config2.clawtMainWorkBranch;
1328
- const currentBranch = getCurrentBranch(cwd);
1329
- if (currentBranch !== mainBranch && !currentBranch.startsWith(VALIDATE_BRANCH_PREFIX)) {
1330
- printWarning(MESSAGES.GUARD_BRANCH_MISMATCH(mainBranch, currentBranch));
1331
- const confirmed = await confirmAction("\u662F\u5426\u7EE7\u7EED\u6267\u884C\uFF1F");
1332
- if (!confirmed) {
1333
- throw new ClawtError(MESSAGES.DESTRUCTIVE_OP_CANCELLED);
1334
- }
1335
- }
1336
- }
1337
1342
  function getValidateRunCommand() {
1338
1343
  const config2 = loadProjectConfig();
1339
1344
  return config2?.validateRunCommand || void 0;
1340
1345
  }
1341
1346
 
1342
- // src/utils/validation.ts
1343
- function validateMainWorktree() {
1344
- try {
1345
- const gitCommonDir = getGitCommonDir();
1346
- if (gitCommonDir !== ".git") {
1347
- throw new ClawtError(MESSAGES.NOT_MAIN_WORKTREE);
1348
- }
1349
- } catch (error) {
1350
- if (error instanceof ClawtError) {
1351
- throw error;
1352
- }
1353
- throw new ClawtError(MESSAGES.NOT_MAIN_WORKTREE);
1354
- }
1355
- }
1356
- function validateClaudeCodeInstalled() {
1357
- try {
1358
- execCommand("claude --version");
1359
- } catch {
1360
- throw new ClawtError(MESSAGES.CLAUDE_NOT_INSTALLED);
1361
- }
1362
- }
1363
- function validateHeadExists() {
1364
- try {
1365
- execCommand("git rev-parse --verify HEAD");
1366
- } catch {
1367
- throw new ClawtError(MESSAGES.HEAD_NOT_FOUND);
1368
- }
1369
- }
1370
- function validateWorkingDirClean() {
1371
- if (!isWorkingDirClean()) {
1372
- throw new ClawtError(MESSAGES.MAIN_WORKTREE_DIRTY);
1373
- }
1374
- }
1375
- function runPreChecks(options) {
1376
- if (options.mainWorktree) {
1377
- validateMainWorktree();
1378
- }
1379
- if (options.headExists) {
1380
- validateHeadExists();
1381
- }
1382
- if (options.projectConfig) {
1383
- requireProjectConfig();
1384
- }
1385
- if (options.branchExists) {
1386
- guardMainWorkBranchExists();
1387
- }
1388
- }
1389
-
1390
- // src/utils/worktree.ts
1391
- import { join as join4 } from "path";
1392
- import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
1393
-
1394
1347
  // src/utils/validate-branch.ts
1395
1348
  import Enquirer from "enquirer";
1396
1349
  function getValidateBranchName(branchName) {
@@ -1481,6 +1434,11 @@ async function ensureOnMainWorkBranch(cwd) {
1481
1434
  gitCheckout(mainBranch, cwd);
1482
1435
  return;
1483
1436
  }
1437
+ printWarning(MESSAGES.GUARD_BRANCH_MISMATCH(mainBranch, currentBranch));
1438
+ const confirmed = await confirmAction("\u662F\u5426\u7EE7\u7EED\u6267\u884C\uFF1F");
1439
+ if (!confirmed) {
1440
+ throw new ClawtError(MESSAGES.DESTRUCTIVE_OP_CANCELLED);
1441
+ }
1484
1442
  logger.info(`\u5F53\u524D\u5728\u5206\u652F ${currentBranch} \u4E0A\uFF0C\u9700\u5207\u6362\u5230\u4E3B\u5DE5\u4F5C\u5206\u652F ${mainBranch}`);
1485
1443
  if (!isWorkingDirClean(cwd)) {
1486
1444
  await handleDirtyWorkingDir(cwd);
@@ -1488,7 +1446,66 @@ async function ensureOnMainWorkBranch(cwd) {
1488
1446
  gitCheckout(mainBranch, cwd);
1489
1447
  }
1490
1448
 
1449
+ // src/utils/validation.ts
1450
+ function validateMainWorktree() {
1451
+ try {
1452
+ const gitCommonDir = getGitCommonDir();
1453
+ if (gitCommonDir !== ".git") {
1454
+ throw new ClawtError(MESSAGES.NOT_MAIN_WORKTREE);
1455
+ }
1456
+ } catch (error) {
1457
+ if (error instanceof ClawtError) {
1458
+ throw error;
1459
+ }
1460
+ throw new ClawtError(MESSAGES.NOT_MAIN_WORKTREE);
1461
+ }
1462
+ }
1463
+ function validateClaudeCodeInstalled() {
1464
+ try {
1465
+ execCommand("claude --version");
1466
+ } catch {
1467
+ throw new ClawtError(MESSAGES.CLAUDE_NOT_INSTALLED);
1468
+ }
1469
+ }
1470
+ function validateHeadExists() {
1471
+ try {
1472
+ execCommand("git rev-parse --verify HEAD");
1473
+ } catch {
1474
+ throw new ClawtError(MESSAGES.HEAD_NOT_FOUND);
1475
+ }
1476
+ }
1477
+ function validateWorkingDirClean() {
1478
+ if (!isWorkingDirClean()) {
1479
+ throw new ClawtError(MESSAGES.MAIN_WORKTREE_DIRTY);
1480
+ }
1481
+ }
1482
+ async function runPreChecks(options) {
1483
+ if (options.requireMainWorktree) {
1484
+ validateMainWorktree();
1485
+ }
1486
+ if (options.requireHead) {
1487
+ validateHeadExists();
1488
+ }
1489
+ if (options.requireProjectConfig) {
1490
+ requireProjectConfig();
1491
+ }
1492
+ if (options.requireMainBranchExists) {
1493
+ guardMainWorkBranchExists();
1494
+ }
1495
+ if (options.ensureOnClawtMainWorkBranch) {
1496
+ await ensureOnMainWorkBranch();
1497
+ }
1498
+ if (options.requireCleanWorkingDir) {
1499
+ validateWorkingDirClean();
1500
+ }
1501
+ if (options.requireClaudeCode) {
1502
+ validateClaudeCodeInstalled();
1503
+ }
1504
+ }
1505
+
1491
1506
  // src/utils/worktree.ts
1507
+ import { join as join4 } from "path";
1508
+ import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
1492
1509
  function getProjectWorktreeDir() {
1493
1510
  const projectName = getProjectName();
1494
1511
  return join4(WORKTREES_DIR, projectName);
@@ -1968,6 +1985,14 @@ function getWorktreeCreatedDate(dirPath) {
1968
1985
  return null;
1969
1986
  }
1970
1987
  }
1988
+ function getWorktreeCreatedTime(dirPath) {
1989
+ try {
1990
+ const stat = statSync3(dirPath);
1991
+ return stat.birthtime.toISOString();
1992
+ } catch {
1993
+ return null;
1994
+ }
1995
+ }
1971
1996
  function formatRelativeDate(dateStr) {
1972
1997
  const today = formatLocalDate(/* @__PURE__ */ new Date());
1973
1998
  const todayMs = new Date(today).getTime();
@@ -3585,6 +3610,10 @@ var InteractivePanel = class {
3585
3610
  this.executeOperation(() => this.handleSync());
3586
3611
  return;
3587
3612
  }
3613
+ if (key === PANEL_SHORTCUT_KEYS.COVER) {
3614
+ this.executeOperation(() => this.handleCover());
3615
+ return;
3616
+ }
3588
3617
  }
3589
3618
  /**
3590
3619
  * 向上导航,选中显示顺序中的上一个 worktree
@@ -3797,6 +3826,13 @@ var InteractivePanel = class {
3797
3826
  const branch = this.getSelectedBranch();
3798
3827
  runCommandInherited(`clawt sync -b ${branch}`);
3799
3828
  }
3829
+ /**
3830
+ * 执行覆盖操作
3831
+ * cover 命令从主 worktree 当前所在的验证分支名自动推导目标分支
3832
+ */
3833
+ handleCover() {
3834
+ runCommandInherited("clawt cover");
3835
+ }
3800
3836
  /**
3801
3837
  * 等待用户按回车键
3802
3838
  * @returns {Promise<void>} 用户按回车时 resolve
@@ -3818,12 +3854,12 @@ var InteractivePanel = class {
3818
3854
  // src/commands/list.ts
3819
3855
  import chalk10 from "chalk";
3820
3856
  function registerListCommand(program2) {
3821
- program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree\uFF08\u652F\u6301 --json \u683C\u5F0F\u8F93\u51FA\uFF09").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
3822
- handleList(options);
3857
+ program2.command("list").description("\u5217\u51FA\u5F53\u524D\u9879\u76EE\u6240\u6709 worktree\uFF08\u652F\u6301 --json \u683C\u5F0F\u8F93\u51FA\uFF09").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action(async (options) => {
3858
+ await handleList(options);
3823
3859
  });
3824
3860
  }
3825
- function handleList(options) {
3826
- runPreChecks({ mainWorktree: true });
3861
+ async function handleList(options) {
3862
+ await runPreChecks({ requireMainWorktree: true });
3827
3863
  const projectName = getProjectName();
3828
3864
  const worktrees = getProjectWorktrees();
3829
3865
  logger.info(`list \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}\uFF0C\u5171 ${worktrees.length} \u4E2A worktree`);
@@ -3873,10 +3909,7 @@ function registerCreateCommand(program2) {
3873
3909
  });
3874
3910
  }
3875
3911
  async function handleCreate(options) {
3876
- runPreChecks({ mainWorktree: true, headExists: true });
3877
- await guardMainWorkBranch();
3878
- await ensureOnMainWorkBranch();
3879
- validateWorkingDirClean();
3912
+ await runPreChecks(PRE_CHECK_CREATE);
3880
3913
  const count = Number(options.number);
3881
3914
  if (!Number.isInteger(count) || count <= 0) {
3882
3915
  throw new ClawtError(
@@ -3910,7 +3943,7 @@ function registerRemoveCommand(program2) {
3910
3943
  });
3911
3944
  }
3912
3945
  async function handleRemove(options) {
3913
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
3946
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
3914
3947
  const projectName = getProjectName();
3915
3948
  logger.info(`remove \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}`);
3916
3949
  const allWorktrees = getProjectWorktrees();
@@ -4016,12 +4049,8 @@ function handleDryRunFromFile(options) {
4016
4049
  printDryRunPreview(branchNames, tasks, concurrency);
4017
4050
  }
4018
4051
  async function handleRun(options) {
4019
- runPreChecks({ mainWorktree: true, headExists: true });
4020
- if (!options.dryRun) {
4021
- await guardMainWorkBranch();
4022
- await ensureOnMainWorkBranch();
4023
- validateWorkingDirClean();
4024
- }
4052
+ const preChecks = options.dryRun ? PRE_CHECK_DRY_RUN : PRE_CHECK_RUN;
4053
+ await runPreChecks(preChecks);
4025
4054
  if (options.file && options.tasks) {
4026
4055
  throw new ClawtError(MESSAGES.FILE_AND_TASKS_CONFLICT);
4027
4056
  }
@@ -4082,8 +4111,7 @@ function registerResumeCommand(program2) {
4082
4111
  });
4083
4112
  }
4084
4113
  async function handleResume(options) {
4085
- runPreChecks({ mainWorktree: true, headExists: true });
4086
- validateClaudeCodeInstalled();
4114
+ await runPreChecks(PRE_CHECK_RESUME);
4087
4115
  logger.info(`resume \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F\u8FC7\u6EE4: ${options.branch ?? "(\u65E0)"}`);
4088
4116
  const worktrees = getProjectWorktrees();
4089
4117
  let targetWorktrees;
@@ -4183,8 +4211,7 @@ async function executeSyncForBranch(targetWorktreePath, branch) {
4183
4211
  return { success: true, hasConflict: false };
4184
4212
  }
4185
4213
  async function handleSync(options) {
4186
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
4187
- await guardMainWorkBranch();
4214
+ await runPreChecks(PRE_CHECK_SYNC);
4188
4215
  logger.info(`sync \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch ?? "(\u672A\u6307\u5B9A)"}`);
4189
4216
  const worktrees = getProjectWorktrees();
4190
4217
  const worktree = await resolveTargetWorktree(worktrees, SYNC_RESOLVE_MESSAGES, options.branch);
@@ -4217,7 +4244,7 @@ async function handlePatchApplyFailure(targetWorktreePath, branchName) {
4217
4244
  const syncResult = await executeSyncForBranch(targetWorktreePath, branchName);
4218
4245
  }
4219
4246
  async function handleValidateClean(options) {
4220
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
4247
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
4221
4248
  const projectName = getProjectName();
4222
4249
  const mainWorktreePath = getGitTopLevel();
4223
4250
  const worktrees = getProjectWorktrees();
@@ -4302,7 +4329,7 @@ async function handleValidate(options) {
4302
4329
  await handleValidateClean(options);
4303
4330
  return;
4304
4331
  }
4305
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
4332
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
4306
4333
  const projectName = getProjectName();
4307
4334
  const mainWorktreePath = getGitTopLevel();
4308
4335
  const worktrees = getProjectWorktrees();
@@ -4367,7 +4394,7 @@ function computeIncrementalPatch(snapshotTreeHash, mainWorktreePath) {
4367
4394
  return { patch, currentTreeHash };
4368
4395
  }
4369
4396
  async function handleCoverValidate() {
4370
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
4397
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
4371
4398
  const projectName = getProjectName();
4372
4399
  const mainWorktreePath = getGitTopLevel();
4373
4400
  const currentBranch = getCurrentBranch(mainWorktreePath);
@@ -4446,11 +4473,8 @@ function cleanupWorktreeAndBranch(worktreePath, branchName) {
4446
4473
  printSuccess(MESSAGES.WORKTREE_CLEANED(branchName));
4447
4474
  }
4448
4475
  async function handleMerge(options) {
4449
- runPreChecks({ mainWorktree: true, headExists: true });
4450
- await guardMainWorkBranch();
4451
- await guardMainWorkBranch();
4476
+ await runPreChecks(PRE_CHECK_MERGE);
4452
4477
  const mainWorktreePath = getGitTopLevel();
4453
- await ensureOnMainWorkBranch(mainWorktreePath);
4454
4478
  logger.info(`merge \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch ?? "(\u672A\u6307\u5B9A)"}\uFF0C\u63D0\u4EA4\u4FE1\u606F: ${options.message ?? "(\u672A\u63D0\u4F9B)"}`);
4455
4479
  const worktrees = getProjectWorktrees();
4456
4480
  const worktree = await resolveTargetWorktree(worktrees, MERGE_RESOLVE_MESSAGES, options.branch);
@@ -4609,7 +4633,7 @@ function registerResetCommand(program2) {
4609
4633
  });
4610
4634
  }
4611
4635
  async function handleReset() {
4612
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
4636
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
4613
4637
  const mainWorktreePath = getGitTopLevel();
4614
4638
  logger.info("reset \u547D\u4EE4\u6267\u884C");
4615
4639
  if (!isWorkingDirClean(mainWorktreePath)) {
@@ -4639,7 +4663,7 @@ function registerStatusCommand(program2) {
4639
4663
  });
4640
4664
  }
4641
4665
  async function handleStatus(options) {
4642
- runPreChecks({ mainWorktree: true, headExists: true });
4666
+ await runPreChecks({ requireMainWorktree: true, requireHead: true });
4643
4667
  if (options.interactive) {
4644
4668
  const panel = new InteractivePanel(collectStatus);
4645
4669
  await panel.start();
@@ -4681,7 +4705,7 @@ function collectWorktreeDetailedStatus(worktree, projectName) {
4681
4705
  const changeStatus = detectChangeStatus(worktree);
4682
4706
  const { commitsAhead, commitsBehind } = countCommitDivergence(worktree.branch);
4683
4707
  const { insertions, deletions } = countDiffStat(worktree.path);
4684
- const createdAt = resolveBranchCreatedAt(worktree.branch);
4708
+ const createdAt = getWorktreeCreatedTime(worktree.path);
4685
4709
  return {
4686
4710
  path: worktree.path,
4687
4711
  branch: worktree.branch,
@@ -4727,13 +4751,6 @@ function countDiffStat(worktreePath) {
4727
4751
  return { insertions: 0, deletions: 0 };
4728
4752
  }
4729
4753
  }
4730
- function resolveBranchCreatedAt(branchName) {
4731
- try {
4732
- return getBranchCreatedAt(branchName);
4733
- } catch {
4734
- return null;
4735
- }
4736
- }
4737
4754
  function resolveSnapshotTime(projectName, branchName) {
4738
4755
  try {
4739
4756
  return getSnapshotModifiedTime(projectName, branchName);
@@ -5319,7 +5336,7 @@ function registerInitCommand(program2) {
5319
5336
  );
5320
5337
  }
5321
5338
  async function handleInitShow() {
5322
- runPreChecks({ mainWorktree: true, projectConfig: true });
5339
+ await runPreChecks({ requireMainWorktree: true, requireProjectConfig: true });
5323
5340
  const config2 = requireProjectConfig();
5324
5341
  logger.info("init show \u547D\u4EE4\u6267\u884C\uFF0C\u8FDB\u5165\u4EA4\u4E92\u5F0F\u9879\u76EE\u914D\u7F6E");
5325
5342
  const { key, newValue } = await interactiveConfigEditor(
@@ -5332,14 +5349,18 @@ async function handleInitShow() {
5332
5349
  printSuccess(MESSAGES.INIT_SET_SUCCESS(key, String(newValue)));
5333
5350
  }
5334
5351
  async function handleInit(options) {
5335
- runPreChecks({ mainWorktree: true });
5352
+ await runPreChecks({ requireMainWorktree: true });
5336
5353
  const existingConfig = loadProjectConfig();
5337
5354
  if (!options.branch) {
5338
5355
  validateHeadExists();
5339
5356
  }
5340
5357
  const branchName = options.branch || getCurrentBranch();
5341
5358
  logger.info(`init \u547D\u4EE4\u6267\u884C\uFF0C\u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}`);
5342
- saveProjectConfig({ clawtMainWorkBranch: branchName });
5359
+ const updatedConfig = {
5360
+ ...existingConfig,
5361
+ clawtMainWorkBranch: branchName
5362
+ };
5363
+ saveProjectConfig(updatedConfig);
5343
5364
  if (existingConfig) {
5344
5365
  printSuccess(MESSAGES.INIT_UPDATED(existingConfig.clawtMainWorkBranch, branchName));
5345
5366
  } else {
@@ -5354,7 +5375,7 @@ function registerHomeCommand(program2) {
5354
5375
  });
5355
5376
  }
5356
5377
  async function handleHome() {
5357
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true, branchExists: true });
5378
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true, requireMainBranchExists: true });
5358
5379
  const mainBranch = getMainWorkBranch();
5359
5380
  const currentBranch = getCurrentBranch();
5360
5381
  if (currentBranch === mainBranch) {
@@ -505,6 +505,8 @@ var PANEL_SHORTCUT_KEYS = {
505
505
  RESUME: "r",
506
506
  /** 同步 */
507
507
  SYNC: "s",
508
+ /** 覆盖 */
509
+ COVER: "c",
508
510
  /** 手动刷新 */
509
511
  REFRESH: "f",
510
512
  /** 退出 */
@@ -518,6 +520,7 @@ var SHORTCUT_LABELS = {
518
520
  DELETE: "\u5220\u9664",
519
521
  RESUME: "\u6062\u590D",
520
522
  SYNC: "\u540C\u6B65",
523
+ COVER: "\u8986\u76D6",
521
524
  REFRESH: "\u5237\u65B0",
522
525
  QUIT: "\u9000\u51FA"
523
526
  };
@@ -639,6 +642,15 @@ var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
639
642
  import chalk2 from "chalk";
640
643
  var UNKNOWN_DATE_SEPARATOR_LABEL = `\u2550\u2550\u2550\u2550 ${chalk2.bold.hex("#FF8C00")("\u672A\u77E5\u65E5\u671F")} \u2550\u2550\u2550\u2550`;
641
644
 
645
+ // src/constants/pre-checks.ts
646
+ var PRE_CHECK_CREATE = {
647
+ requireMainWorktree: true,
648
+ requireHead: true,
649
+ ensureOnClawtMainWorkBranch: true,
650
+ requireCleanWorkingDir: true
651
+ };
652
+ var PRE_CHECK_RUN = { ...PRE_CHECK_CREATE };
653
+
642
654
  // scripts/postinstall.ts
643
655
  function ensureDirectory(dirPath) {
644
656
  if (!existsSync(dirPath)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawt",
3
- "version": "3.4.4",
3
+ "version": "3.4.6",
4
4
  "description": "本地并行执行多个Claude Code Agent任务,融合 Git Worktree 与 Claude Code CLI 的命令行工具",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -97,7 +97,7 @@ export function computeIncrementalPatch(snapshotTreeHash: string, mainWorktreePa
97
97
  */
98
98
  async function handleCoverValidate(): Promise<void> {
99
99
  // 步骤 1:前置校验
100
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
100
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
101
101
  const projectName = getProjectName();
102
102
  const mainWorktreePath = getGitTopLevel();
103
103
  const currentBranch = getCurrentBranch(mainWorktreePath);
@@ -3,16 +3,14 @@ import { MESSAGES, EXIT_CODES } from '../constants/index.js';
3
3
  import { ClawtError } from '../errors/index.js';
4
4
  import { logger } from '../logger/index.js';
5
5
  import type { CreateOptions } from '../types/index.js';
6
+ import { PRE_CHECK_CREATE } from '../constants/index.js';
6
7
  import {
7
8
  runPreChecks,
8
9
  createWorktrees,
9
- ensureOnMainWorkBranch,
10
- validateWorkingDirClean,
11
10
  getValidateBranchName,
12
11
  printSuccess,
13
12
  printInfo,
14
13
  printSeparator,
15
- guardMainWorkBranch,
16
14
  } from '../utils/index.js';
17
15
 
18
16
  /**
@@ -35,13 +33,7 @@ export function registerCreateCommand(program: Command): void {
35
33
  * @param {CreateOptions} options - 命令选项
36
34
  */
37
35
  async function handleCreate(options: CreateOptions): Promise<void> {
38
- runPreChecks({ mainWorktree: true, headExists: true });
39
-
40
- await guardMainWorkBranch();
41
-
42
- await ensureOnMainWorkBranch();
43
-
44
- validateWorkingDirClean();
36
+ await runPreChecks(PRE_CHECK_CREATE);
45
37
 
46
38
  const count = Number(options.number);
47
39
 
@@ -27,7 +27,7 @@ export function registerHomeCommand(program: Command): void {
27
27
  * 执行 home 命令:切换回主工作分支
28
28
  */
29
29
  async function handleHome(): Promise<void> {
30
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true, branchExists: true });
30
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true, requireMainBranchExists: true });
31
31
 
32
32
  const mainBranch = getMainWorkBranch();
33
33
  const currentBranch = getCurrentBranch();
@@ -41,7 +41,7 @@ export function registerInitCommand(program: Command): void {
41
41
  * 处理 init show 子命令:交互式面板展示和修改项目配置
42
42
  */
43
43
  async function handleInitShow(): Promise<void> {
44
- runPreChecks({ mainWorktree: true, projectConfig: true });
44
+ await runPreChecks({ requireMainWorktree: true, requireProjectConfig: true });
45
45
  const config = requireProjectConfig();
46
46
 
47
47
  logger.info('init show 命令执行,进入交互式项目配置');
@@ -67,7 +67,7 @@ async function handleInitShow(): Promise<void> {
67
67
  * @param {InitOptions} options - 命令选项
68
68
  */
69
69
  async function handleInit(options: InitOptions): Promise<void> {
70
- runPreChecks({ mainWorktree: true });
70
+ await runPreChecks({ requireMainWorktree: true });
71
71
 
72
72
  const existingConfig = loadProjectConfig();
73
73
 
@@ -79,8 +79,12 @@ async function handleInit(options: InitOptions): Promise<void> {
79
79
 
80
80
  logger.info(`init 命令执行,主工作分支: ${branchName}`);
81
81
 
82
- // 保存项目配置
83
- saveProjectConfig({ clawtMainWorkBranch: branchName });
82
+ // 合并现有配置,仅更新 clawtMainWorkBranch
83
+ const updatedConfig: ProjectConfig = {
84
+ ...existingConfig,
85
+ clawtMainWorkBranch: branchName,
86
+ };
87
+ saveProjectConfig(updatedConfig);
84
88
 
85
89
  if (existingConfig) {
86
90
  printSuccess(MESSAGES.INIT_UPDATED(existingConfig.clawtMainWorkBranch, branchName));
@@ -23,8 +23,8 @@ export function registerListCommand(program: Command): void {
23
23
  .command('list')
24
24
  .description('列出当前项目所有 worktree(支持 --json 格式输出)')
25
25
  .option('--json', '以 JSON 格式输出')
26
- .action((options: ListOptions) => {
27
- handleList(options);
26
+ .action(async (options: ListOptions) => {
27
+ await handleList(options);
28
28
  });
29
29
  }
30
30
 
@@ -32,8 +32,8 @@ export function registerListCommand(program: Command): void {
32
32
  * 执行 list 命令的核心逻辑
33
33
  * @param {ListOptions} options - 命令选项
34
34
  */
35
- function handleList(options: ListOptions): void {
36
- runPreChecks({ mainWorktree: true });
35
+ async function handleList(options: ListOptions): Promise<void> {
36
+ await runPreChecks({ requireMainWorktree: true });
37
37
 
38
38
  const projectName = getProjectName();
39
39
  const worktrees = getProjectWorktrees();
@@ -3,6 +3,7 @@ import { logger } from '../logger/index.js';
3
3
  import { ClawtError } from '../errors/index.js';
4
4
  import { MESSAGES, AUTO_SAVE_COMMIT_MESSAGE } from '../constants/index.js';
5
5
  import type { MergeOptions } from '../types/index.js';
6
+ import { PRE_CHECK_MERGE } from '../constants/index.js';
6
7
  import {
7
8
  runPreChecks,
8
9
  getProjectName,
@@ -32,8 +33,6 @@ import {
32
33
  gitCheckout,
33
34
  resolveTargetWorktree,
34
35
  getMainWorkBranch,
35
- ensureOnMainWorkBranch,
36
- guardMainWorkBranch,
37
36
  } from '../utils/index.js';
38
37
  import type { WorktreeResolveMessages } from '../utils/index.js';
39
38
 
@@ -136,17 +135,10 @@ function cleanupWorktreeAndBranch(worktreePath: string, branchName: string): voi
136
135
  * @param {MergeOptions} options - 命令选项
137
136
  */
138
137
  async function handleMerge(options: MergeOptions): Promise<void> {
139
- runPreChecks({ mainWorktree: true, headExists: true });
140
-
141
- await guardMainWorkBranch();
142
-
143
- await guardMainWorkBranch();
138
+ await runPreChecks(PRE_CHECK_MERGE);
144
139
 
145
140
  const mainWorktreePath = getGitTopLevel();
146
141
 
147
- // 确保当前在主工作分支上
148
- await ensureOnMainWorkBranch(mainWorktreePath);
149
-
150
142
  logger.info(`merge 命令执行,分支: ${options.branch ?? '(未指定)'},提交信息: ${options.message ?? '(未提供)'}`);
151
143
 
152
144
  // 解析目标 worktree(精确匹配 / 模糊匹配 / 交互选择)
@@ -55,7 +55,7 @@ export function registerRemoveCommand(program: Command): void {
55
55
  * @param {RemoveOptions} options - 命令选项
56
56
  */
57
57
  async function handleRemove(options: RemoveOptions): Promise<void> {
58
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
58
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
59
59
 
60
60
  const projectName = getProjectName();
61
61
  logger.info(`remove 命令执行,项目: ${projectName}`);
@@ -30,7 +30,7 @@ export function registerResetCommand(program: Command): void {
30
30
  * 执行 reset 命令:重置主 worktree 工作区和暂存区
31
31
  */
32
32
  async function handleReset(): Promise<void> {
33
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
33
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
34
34
 
35
35
  const mainWorktreePath = getGitTopLevel();
36
36
  logger.info('reset 命令执行');
@@ -3,9 +3,9 @@ import { logger } from '../logger/index.js';
3
3
  import { MESSAGES } from '../constants/index.js';
4
4
  import type { ResumeOptions } from '../types/index.js';
5
5
  import type { WorktreeInfo } from '../types/index.js';
6
+ import { PRE_CHECK_RESUME } from '../constants/index.js';
6
7
  import {
7
8
  runPreChecks,
8
- validateClaudeCodeInstalled,
9
9
  getProjectWorktrees,
10
10
  launchInteractiveClaude,
11
11
  launchInteractiveClaudeInNewTerminal,
@@ -47,8 +47,7 @@ export function registerResumeCommand(program: Command): void {
47
47
  * @param {ResumeOptions} options - 命令选项
48
48
  */
49
49
  async function handleResume(options: ResumeOptions): Promise<void> {
50
- runPreChecks({ mainWorktree: true, headExists: true });
51
- validateClaudeCodeInstalled();
50
+ await runPreChecks(PRE_CHECK_RESUME);
52
51
 
53
52
  logger.info(`resume 命令执行,分支过滤: ${options.branch ?? '(无)'}`);
54
53
  const worktrees = getProjectWorktrees();