clawt 3.4.3 → 3.4.5

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,71 +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 runPreChecks(options) {
1371
- if (options.mainWorktree) {
1372
- validateMainWorktree();
1373
- }
1374
- if (options.headExists) {
1375
- validateHeadExists();
1376
- }
1377
- if (options.projectConfig) {
1378
- requireProjectConfig();
1379
- }
1380
- if (options.branchExists) {
1381
- guardMainWorkBranchExists();
1382
- }
1383
- }
1384
-
1385
- // src/utils/worktree.ts
1386
- import { join as join4 } from "path";
1387
- import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
1388
-
1389
1347
  // src/utils/validate-branch.ts
1390
1348
  import Enquirer from "enquirer";
1391
1349
  function getValidateBranchName(branchName) {
@@ -1476,6 +1434,11 @@ async function ensureOnMainWorkBranch(cwd) {
1476
1434
  gitCheckout(mainBranch, cwd);
1477
1435
  return;
1478
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
+ }
1479
1442
  logger.info(`\u5F53\u524D\u5728\u5206\u652F ${currentBranch} \u4E0A\uFF0C\u9700\u5207\u6362\u5230\u4E3B\u5DE5\u4F5C\u5206\u652F ${mainBranch}`);
1480
1443
  if (!isWorkingDirClean(cwd)) {
1481
1444
  await handleDirtyWorkingDir(cwd);
@@ -1483,7 +1446,66 @@ async function ensureOnMainWorkBranch(cwd) {
1483
1446
  gitCheckout(mainBranch, cwd);
1484
1447
  }
1485
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
+
1486
1506
  // src/utils/worktree.ts
1507
+ import { join as join4 } from "path";
1508
+ import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
1487
1509
  function getProjectWorktreeDir() {
1488
1510
  const projectName = getProjectName();
1489
1511
  return join4(WORKTREES_DIR, projectName);
@@ -1963,6 +1985,14 @@ function getWorktreeCreatedDate(dirPath) {
1963
1985
  return null;
1964
1986
  }
1965
1987
  }
1988
+ function getWorktreeCreatedTime(dirPath) {
1989
+ try {
1990
+ const stat = statSync3(dirPath);
1991
+ return stat.birthtime.toISOString();
1992
+ } catch {
1993
+ return null;
1994
+ }
1995
+ }
1966
1996
  function formatRelativeDate(dateStr) {
1967
1997
  const today = formatLocalDate(/* @__PURE__ */ new Date());
1968
1998
  const todayMs = new Date(today).getTime();
@@ -3580,6 +3610,10 @@ var InteractivePanel = class {
3580
3610
  this.executeOperation(() => this.handleSync());
3581
3611
  return;
3582
3612
  }
3613
+ if (key === PANEL_SHORTCUT_KEYS.COVER) {
3614
+ this.executeOperation(() => this.handleCover());
3615
+ return;
3616
+ }
3583
3617
  }
3584
3618
  /**
3585
3619
  * 向上导航,选中显示顺序中的上一个 worktree
@@ -3792,6 +3826,13 @@ var InteractivePanel = class {
3792
3826
  const branch = this.getSelectedBranch();
3793
3827
  runCommandInherited(`clawt sync -b ${branch}`);
3794
3828
  }
3829
+ /**
3830
+ * 执行覆盖操作
3831
+ * cover 命令从主 worktree 当前所在的验证分支名自动推导目标分支
3832
+ */
3833
+ handleCover() {
3834
+ runCommandInherited("clawt cover");
3835
+ }
3795
3836
  /**
3796
3837
  * 等待用户按回车键
3797
3838
  * @returns {Promise<void>} 用户按回车时 resolve
@@ -3813,12 +3854,12 @@ var InteractivePanel = class {
3813
3854
  // src/commands/list.ts
3814
3855
  import chalk10 from "chalk";
3815
3856
  function registerListCommand(program2) {
3816
- 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) => {
3817
- 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);
3818
3859
  });
3819
3860
  }
3820
- function handleList(options) {
3821
- runPreChecks({ mainWorktree: true });
3861
+ async function handleList(options) {
3862
+ await runPreChecks({ requireMainWorktree: true });
3822
3863
  const projectName = getProjectName();
3823
3864
  const worktrees = getProjectWorktrees();
3824
3865
  logger.info(`list \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}\uFF0C\u5171 ${worktrees.length} \u4E2A worktree`);
@@ -3868,9 +3909,7 @@ function registerCreateCommand(program2) {
3868
3909
  });
3869
3910
  }
3870
3911
  async function handleCreate(options) {
3871
- runPreChecks({ mainWorktree: true, headExists: true });
3872
- await guardMainWorkBranch();
3873
- await ensureOnMainWorkBranch();
3912
+ await runPreChecks(PRE_CHECK_CREATE);
3874
3913
  const count = Number(options.number);
3875
3914
  if (!Number.isInteger(count) || count <= 0) {
3876
3915
  throw new ClawtError(
@@ -3904,7 +3943,7 @@ function registerRemoveCommand(program2) {
3904
3943
  });
3905
3944
  }
3906
3945
  async function handleRemove(options) {
3907
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
3946
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
3908
3947
  const projectName = getProjectName();
3909
3948
  logger.info(`remove \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}`);
3910
3949
  const allWorktrees = getProjectWorktrees();
@@ -4010,11 +4049,8 @@ function handleDryRunFromFile(options) {
4010
4049
  printDryRunPreview(branchNames, tasks, concurrency);
4011
4050
  }
4012
4051
  async function handleRun(options) {
4013
- runPreChecks({ mainWorktree: true, headExists: true });
4014
- if (!options.dryRun) {
4015
- await guardMainWorkBranch();
4016
- await ensureOnMainWorkBranch();
4017
- }
4052
+ const preChecks = options.dryRun ? PRE_CHECK_DRY_RUN : PRE_CHECK_RUN;
4053
+ await runPreChecks(preChecks);
4018
4054
  if (options.file && options.tasks) {
4019
4055
  throw new ClawtError(MESSAGES.FILE_AND_TASKS_CONFLICT);
4020
4056
  }
@@ -4075,8 +4111,7 @@ function registerResumeCommand(program2) {
4075
4111
  });
4076
4112
  }
4077
4113
  async function handleResume(options) {
4078
- runPreChecks({ mainWorktree: true, headExists: true });
4079
- validateClaudeCodeInstalled();
4114
+ await runPreChecks(PRE_CHECK_RESUME);
4080
4115
  logger.info(`resume \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F\u8FC7\u6EE4: ${options.branch ?? "(\u65E0)"}`);
4081
4116
  const worktrees = getProjectWorktrees();
4082
4117
  let targetWorktrees;
@@ -4176,8 +4211,7 @@ async function executeSyncForBranch(targetWorktreePath, branch) {
4176
4211
  return { success: true, hasConflict: false };
4177
4212
  }
4178
4213
  async function handleSync(options) {
4179
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
4180
- await guardMainWorkBranch();
4214
+ await runPreChecks(PRE_CHECK_SYNC);
4181
4215
  logger.info(`sync \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch ?? "(\u672A\u6307\u5B9A)"}`);
4182
4216
  const worktrees = getProjectWorktrees();
4183
4217
  const worktree = await resolveTargetWorktree(worktrees, SYNC_RESOLVE_MESSAGES, options.branch);
@@ -4210,7 +4244,7 @@ async function handlePatchApplyFailure(targetWorktreePath, branchName) {
4210
4244
  const syncResult = await executeSyncForBranch(targetWorktreePath, branchName);
4211
4245
  }
4212
4246
  async function handleValidateClean(options) {
4213
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
4247
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
4214
4248
  const projectName = getProjectName();
4215
4249
  const mainWorktreePath = getGitTopLevel();
4216
4250
  const worktrees = getProjectWorktrees();
@@ -4295,7 +4329,7 @@ async function handleValidate(options) {
4295
4329
  await handleValidateClean(options);
4296
4330
  return;
4297
4331
  }
4298
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
4332
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
4299
4333
  const projectName = getProjectName();
4300
4334
  const mainWorktreePath = getGitTopLevel();
4301
4335
  const worktrees = getProjectWorktrees();
@@ -4360,7 +4394,7 @@ function computeIncrementalPatch(snapshotTreeHash, mainWorktreePath) {
4360
4394
  return { patch, currentTreeHash };
4361
4395
  }
4362
4396
  async function handleCoverValidate() {
4363
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
4397
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
4364
4398
  const projectName = getProjectName();
4365
4399
  const mainWorktreePath = getGitTopLevel();
4366
4400
  const currentBranch = getCurrentBranch(mainWorktreePath);
@@ -4439,11 +4473,8 @@ function cleanupWorktreeAndBranch(worktreePath, branchName) {
4439
4473
  printSuccess(MESSAGES.WORKTREE_CLEANED(branchName));
4440
4474
  }
4441
4475
  async function handleMerge(options) {
4442
- runPreChecks({ mainWorktree: true, headExists: true });
4443
- await guardMainWorkBranch();
4444
- await guardMainWorkBranch();
4476
+ await runPreChecks(PRE_CHECK_MERGE);
4445
4477
  const mainWorktreePath = getGitTopLevel();
4446
- await ensureOnMainWorkBranch(mainWorktreePath);
4447
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)"}`);
4448
4479
  const worktrees = getProjectWorktrees();
4449
4480
  const worktree = await resolveTargetWorktree(worktrees, MERGE_RESOLVE_MESSAGES, options.branch);
@@ -4602,7 +4633,7 @@ function registerResetCommand(program2) {
4602
4633
  });
4603
4634
  }
4604
4635
  async function handleReset() {
4605
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
4636
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true });
4606
4637
  const mainWorktreePath = getGitTopLevel();
4607
4638
  logger.info("reset \u547D\u4EE4\u6267\u884C");
4608
4639
  if (!isWorkingDirClean(mainWorktreePath)) {
@@ -4632,7 +4663,7 @@ function registerStatusCommand(program2) {
4632
4663
  });
4633
4664
  }
4634
4665
  async function handleStatus(options) {
4635
- runPreChecks({ mainWorktree: true, headExists: true });
4666
+ await runPreChecks({ requireMainWorktree: true, requireHead: true });
4636
4667
  if (options.interactive) {
4637
4668
  const panel = new InteractivePanel(collectStatus);
4638
4669
  await panel.start();
@@ -4674,7 +4705,7 @@ function collectWorktreeDetailedStatus(worktree, projectName) {
4674
4705
  const changeStatus = detectChangeStatus(worktree);
4675
4706
  const { commitsAhead, commitsBehind } = countCommitDivergence(worktree.branch);
4676
4707
  const { insertions, deletions } = countDiffStat(worktree.path);
4677
- const createdAt = resolveBranchCreatedAt(worktree.branch);
4708
+ const createdAt = getWorktreeCreatedTime(worktree.path);
4678
4709
  return {
4679
4710
  path: worktree.path,
4680
4711
  branch: worktree.branch,
@@ -4720,13 +4751,6 @@ function countDiffStat(worktreePath) {
4720
4751
  return { insertions: 0, deletions: 0 };
4721
4752
  }
4722
4753
  }
4723
- function resolveBranchCreatedAt(branchName) {
4724
- try {
4725
- return getBranchCreatedAt(branchName);
4726
- } catch {
4727
- return null;
4728
- }
4729
- }
4730
4754
  function resolveSnapshotTime(projectName, branchName) {
4731
4755
  try {
4732
4756
  return getSnapshotModifiedTime(projectName, branchName);
@@ -5312,7 +5336,7 @@ function registerInitCommand(program2) {
5312
5336
  );
5313
5337
  }
5314
5338
  async function handleInitShow() {
5315
- runPreChecks({ mainWorktree: true, projectConfig: true });
5339
+ await runPreChecks({ requireMainWorktree: true, requireProjectConfig: true });
5316
5340
  const config2 = requireProjectConfig();
5317
5341
  logger.info("init show \u547D\u4EE4\u6267\u884C\uFF0C\u8FDB\u5165\u4EA4\u4E92\u5F0F\u9879\u76EE\u914D\u7F6E");
5318
5342
  const { key, newValue } = await interactiveConfigEditor(
@@ -5325,7 +5349,7 @@ async function handleInitShow() {
5325
5349
  printSuccess(MESSAGES.INIT_SET_SUCCESS(key, String(newValue)));
5326
5350
  }
5327
5351
  async function handleInit(options) {
5328
- runPreChecks({ mainWorktree: true });
5352
+ await runPreChecks({ requireMainWorktree: true });
5329
5353
  const existingConfig = loadProjectConfig();
5330
5354
  if (!options.branch) {
5331
5355
  validateHeadExists();
@@ -5347,7 +5371,7 @@ function registerHomeCommand(program2) {
5347
5371
  });
5348
5372
  }
5349
5373
  async function handleHome() {
5350
- runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true, branchExists: true });
5374
+ await runPreChecks({ requireMainWorktree: true, requireHead: true, requireProjectConfig: true, requireMainBranchExists: true });
5351
5375
  const mainBranch = getMainWorkBranch();
5352
5376
  const currentBranch = getCurrentBranch();
5353
5377
  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.3",
3
+ "version": "3.4.5",
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,15 +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
10
  getValidateBranchName,
11
11
  printSuccess,
12
12
  printInfo,
13
13
  printSeparator,
14
- guardMainWorkBranch,
15
14
  } from '../utils/index.js';
16
15
 
17
16
  /**
@@ -34,11 +33,7 @@ export function registerCreateCommand(program: Command): void {
34
33
  * @param {CreateOptions} options - 命令选项
35
34
  */
36
35
  async function handleCreate(options: CreateOptions): Promise<void> {
37
- runPreChecks({ mainWorktree: true, headExists: true });
38
-
39
- await guardMainWorkBranch();
40
-
41
- await ensureOnMainWorkBranch();
36
+ await runPreChecks(PRE_CHECK_CREATE);
42
37
 
43
38
  const count = Number(options.number);
44
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
 
@@ -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();
@@ -3,6 +3,7 @@ import { logger } from '../logger/index.js';
3
3
  import { ClawtError } from '../errors/index.js';
4
4
  import { MESSAGES } from '../constants/index.js';
5
5
  import type { RunOptions, WorktreeInfo } from '../types/index.js';
6
+ import { PRE_CHECK_DRY_RUN, PRE_CHECK_RUN } from '../constants/index.js';
6
7
  import {
7
8
  runPreChecks,
8
9
  validateClaudeCodeInstalled,
@@ -19,8 +20,6 @@ import {
19
20
  parseTasksFromOptions,
20
21
  executeBatchTasks,
21
22
  printDryRunPreview,
22
- ensureOnMainWorkBranch,
23
- guardMainWorkBranch,
24
23
  } from '../utils/index.js';
25
24
 
26
25
  /**
@@ -120,13 +119,8 @@ function handleDryRunFromFile(options: RunOptions): void {
120
119
  * @param {RunOptions} options - 命令选项
121
120
  */
122
121
  async function handleRun(options: RunOptions): Promise<void> {
123
- runPreChecks({ mainWorktree: true, headExists: true });
124
-
125
- // dry-run 模式跳过项目配置前置校验
126
- if (!options.dryRun) {
127
- await guardMainWorkBranch();
128
- await ensureOnMainWorkBranch();
129
- }
122
+ const preChecks = options.dryRun ? PRE_CHECK_DRY_RUN : PRE_CHECK_RUN;
123
+ await runPreChecks(preChecks);
130
124
 
131
125
  // 互斥校验:--file 和 --tasks 不能同时使用
132
126
  if (options.file && options.tasks) {