clawt 3.4.1 → 3.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -13
- package/dist/index.js +141 -56
- package/dist/postinstall.js +15 -2
- package/package.json +1 -1
- package/src/commands/cover-validate.ts +2 -4
- package/src/commands/create.ts +5 -2
- package/src/commands/home.ts +3 -4
- package/src/commands/init.ts +7 -3
- package/src/commands/list.ts +2 -2
- package/src/commands/merge.ts +7 -2
- package/src/commands/remove.ts +2 -4
- package/src/commands/reset.ts +2 -4
- package/src/commands/resume.ts +2 -2
- package/src/commands/run.ts +4 -2
- package/src/commands/status.ts +24 -3
- package/src/commands/sync.ts +4 -4
- package/src/commands/validate.ts +3 -7
- package/src/constants/interactive-panel.ts +2 -2
- package/src/constants/messages/common.ts +8 -0
- package/src/constants/messages/index.ts +2 -2
- package/src/constants/messages/interactive-panel.ts +27 -0
- package/src/constants/messages/status.ts +9 -0
- package/src/types/status.ts +4 -0
- package/src/utils/index.ts +2 -2
- package/src/utils/interactive-panel-render.ts +28 -2
- package/src/utils/project-config.ts +39 -2
- package/src/utils/validation.ts +50 -0
- package/tests/unit/commands/cover-validate.test.ts +3 -1
- package/tests/unit/commands/create.test.ts +7 -5
- package/tests/unit/commands/init.test.ts +4 -1
- package/tests/unit/commands/list.test.ts +5 -5
- package/tests/unit/commands/merge.test.ts +3 -1
- package/tests/unit/commands/remove.test.ts +5 -3
- package/tests/unit/commands/reset.test.ts +3 -1
- package/tests/unit/commands/resume.test.ts +5 -5
- package/tests/unit/commands/run.test.ts +3 -1
- package/tests/unit/commands/status.test.ts +7 -1
- package/tests/unit/commands/sync.test.ts +3 -1
- package/tests/unit/commands/validate.test.ts +3 -1
- package/tests/unit/utils/validation.test.ts +43 -1
package/README.md
CHANGED
|
@@ -17,24 +17,33 @@ npm i -g clawt
|
|
|
17
17
|
## 快速开始
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
# 1. 在项目根目录(包含 .git
|
|
20
|
+
# 1. 在项目根目录(包含 .git 的目录)下初始化,确认主工作分支
|
|
21
21
|
clawt init
|
|
22
22
|
|
|
23
|
-
# 2.
|
|
24
|
-
clawt run -b
|
|
25
|
-
clawt run -b
|
|
26
|
-
clawt run -b
|
|
23
|
+
# 2. 并行执行任务,每个任务在独立的 worktree 中运行
|
|
24
|
+
clawt run -b feat-login
|
|
25
|
+
clawt run -b feat-search
|
|
26
|
+
clawt run -b fix-bug
|
|
27
27
|
|
|
28
|
-
# 3.
|
|
29
|
-
clawt status
|
|
30
|
-
|
|
31
|
-
# 4. 验证某个分支的变更(在主 worktree 中测试)
|
|
32
|
-
clawt validate -b branch-1
|
|
33
|
-
|
|
34
|
-
# 5. 确认无误后合并到主分支
|
|
35
|
-
clawt merge -b branch-1 -m "feat: 实现xxx功能"
|
|
28
|
+
# 3. 打开交互式面板,实时查看所有任务状态,一站式完成后续操作
|
|
29
|
+
clawt status -i
|
|
36
30
|
```
|
|
37
31
|
|
|
32
|
+
`clawt status -i` 提供实时刷新的 TUI 面板,用方向键选中 worktree 后可直接按快捷键操作:
|
|
33
|
+
|
|
34
|
+
| 快捷键 | 操作 | 等同命令 |
|
|
35
|
+
| ------ | ---- | -------- |
|
|
36
|
+
| `v` | 验证分支变更 | `clawt validate -b <branch>` |
|
|
37
|
+
| `m` | 合并到主分支 | `clawt merge -b <branch>` |
|
|
38
|
+
| `r` | 恢复 Claude Code 会话 | `clawt resume -b <branch>` |
|
|
39
|
+
| `s` | 同步主分支代码 | `clawt sync -b <branch>` |
|
|
40
|
+
| `d` | 删除 worktree | `clawt remove -b <branch>` |
|
|
41
|
+
| `q` | 退出面板 | — |
|
|
42
|
+
|
|
43
|
+
示例:
|
|
44
|
+

|
|
45
|
+
> 所有操作也可通过独立命令执行,详见下方「命令一览」。
|
|
46
|
+
|
|
38
47
|
## 命令一览
|
|
39
48
|
|
|
40
49
|
> 除 `config`、`alias`、`projects`、`completion` 外,其余命令需在**主 worktree 的仓库根目录**下执行。`-b` 参数支持模糊匹配。大部分操作命令(`run`、`create`、`validate`、`merge` 等)需要先执行 `clawt init`。
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,8 @@ var COMMON_MESSAGES = {
|
|
|
30
30
|
GIT_NOT_INSTALLED: "Git \u672A\u5B89\u88C5\u6216\u4E0D\u5728 PATH \u4E2D\uFF0C\u8BF7\u5148\u5B89\u88C5 Git",
|
|
31
31
|
/** Claude Code CLI 未安装 */
|
|
32
32
|
CLAUDE_NOT_INSTALLED: "Claude Code CLI \u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u5B89\u88C5\uFF1Anpm install -g @anthropic-ai/claude-code",
|
|
33
|
+
/** HEAD 不存在(仓库无任何 commit) */
|
|
34
|
+
HEAD_NOT_FOUND: "\u5F53\u524D\u4ED3\u5E93\u5C1A\u672A\u521B\u5EFA\u4EFB\u4F55\u63D0\u4EA4\uFF0C\u8BF7\u5148\u6267\u884C git commit \u521B\u5EFA\u9996\u6B21\u63D0\u4EA4\u540E\u518D\u4F7F\u7528 clawt",
|
|
33
35
|
/** 分支已存在 */
|
|
34
36
|
BRANCH_EXISTS: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u65E0\u6CD5\u521B\u5EFA`,
|
|
35
37
|
/** 分支名清理后为空 */
|
|
@@ -59,7 +61,11 @@ var COMMON_MESSAGES = {
|
|
|
59
61
|
/** 分隔线 */
|
|
60
62
|
SEPARATOR: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
61
63
|
/** 粗分隔线 */
|
|
62
|
-
DOUBLE_SEPARATOR: "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
|
|
64
|
+
DOUBLE_SEPARATOR: "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
65
|
+
/** 守卫检测:配置的主工作分支已不存在 */
|
|
66
|
+
GUARD_BRANCH_NOT_EXISTS: (branchName) => `\u914D\u7F6E\u7684\u4E3B\u5DE5\u4F5C\u5206\u652F ${branchName} \u5DF2\u4E0D\u5B58\u5728\uFF0C\u8BF7\u6267\u884C clawt init \u91CD\u65B0\u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F`,
|
|
67
|
+
/** 守卫检测:当前分支与配置的主工作分支不一致 */
|
|
68
|
+
GUARD_BRANCH_MISMATCH: (configuredBranch, currentBranch) => `\u5F53\u524D\u5206\u652F ${currentBranch} \u4E0E\u914D\u7F6E\u7684\u4E3B\u5DE5\u4F5C\u5206\u652F ${configuredBranch} \u4E0D\u4E00\u81F4\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u6267\u884C clawt init`
|
|
63
69
|
};
|
|
64
70
|
|
|
65
71
|
// src/constants/messages/run.ts
|
|
@@ -371,7 +377,13 @@ var STATUS_MESSAGES = {
|
|
|
371
377
|
/** status 上次验证时间标签 */
|
|
372
378
|
STATUS_LAST_VALIDATED: (relativeTime) => `\u4E0A\u6B21\u9A8C\u8BC1: ${relativeTime}`,
|
|
373
379
|
/** status 未验证警示 */
|
|
374
|
-
STATUS_NOT_VALIDATED: "\u2717 \u672A\u9A8C\u8BC1"
|
|
380
|
+
STATUS_NOT_VALIDATED: "\u2717 \u672A\u9A8C\u8BC1",
|
|
381
|
+
/** status 配置的主工作分支(正常状态) */
|
|
382
|
+
STATUS_CONFIGURED_BRANCH: (branchName) => `\u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}`,
|
|
383
|
+
/** status 配置的主工作分支已不存在 */
|
|
384
|
+
STATUS_CONFIGURED_BRANCH_DELETED: (branchName) => `\u2717 \u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}\uFF08\u5DF2\u4E0D\u5B58\u5728\uFF0C\u8BF7\u6267\u884C clawt init \u91CD\u65B0\u8BBE\u7F6E\uFF09`,
|
|
385
|
+
/** status 当前分支与配置的主工作分支不一致 */
|
|
386
|
+
STATUS_CONFIGURED_BRANCH_MISMATCH: (branchName) => `\u26A0 \u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}\uFF08\u5F53\u524D\u5206\u652F\u4E0D\u4E00\u81F4\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u6267\u884C clawt init\uFF09`
|
|
375
387
|
};
|
|
376
388
|
|
|
377
389
|
// src/constants/messages/alias.ts
|
|
@@ -529,7 +541,7 @@ var PANEL_SHORTCUT_KEYS = {
|
|
|
529
541
|
QUIT: "q"
|
|
530
542
|
};
|
|
531
543
|
var PANEL_DATE_SEPARATOR_PREFIX = "\u2550\u2550\u2550\u2550";
|
|
532
|
-
var PANEL_FIXED_ROWS =
|
|
544
|
+
var PANEL_FIXED_ROWS = 5;
|
|
533
545
|
|
|
534
546
|
// src/constants/messages/interactive-panel.ts
|
|
535
547
|
var SHORTCUT_LABELS = {
|
|
@@ -556,6 +568,10 @@ var PANEL_NO_WORKTREES = "(\u65E0\u6D3B\u8DC3 worktree)";
|
|
|
556
568
|
var PANEL_PRESS_ENTER_TO_RETURN = chalk.gray("\n\u6309 Enter \u8FD4\u56DE\u9762\u677F...");
|
|
557
569
|
var PANEL_NOT_TTY = "\u4EA4\u4E92\u5F0F\u9762\u677F\u9700\u8981 TTY \u7EC8\u7AEF\u73AF\u5883\uFF0C\u8BF7\u76F4\u63A5\u5728\u7EC8\u7AEF\u4E2D\u8FD0\u884C clawt status -i";
|
|
558
570
|
var PANEL_TITLE = (projectName) => chalk.bold.cyan(`\u9879\u76EE\u72B6\u6001\u603B\u89C8: ${projectName}`);
|
|
571
|
+
var PANEL_CONFIGURED_BRANCH = (branchName) => chalk.gray(`\u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}`);
|
|
572
|
+
var PANEL_CONFIGURED_BRANCH_DELETED = (branchName) => chalk.red(`\u2717 \u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}\uFF08\u5DF2\u4E0D\u5B58\u5728\uFF09`);
|
|
573
|
+
var PANEL_CONFIGURED_BRANCH_MISMATCH = (branchName) => chalk.yellow(`\u26A0 \u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}\uFF08\u4E0D\u4E00\u81F4\uFF09`);
|
|
574
|
+
var PANEL_NOT_INITIALIZED = chalk.gray("\u672A\u521D\u59CB\u5316\uFF08\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F\uFF09");
|
|
559
575
|
|
|
560
576
|
// src/constants/messages/index.ts
|
|
561
577
|
var MESSAGES = {
|
|
@@ -1215,31 +1231,9 @@ function validateBranchesNotExist(branchNames) {
|
|
|
1215
1231
|
}
|
|
1216
1232
|
}
|
|
1217
1233
|
|
|
1218
|
-
// src/utils/
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
const gitCommonDir = getGitCommonDir();
|
|
1222
|
-
if (gitCommonDir !== ".git") {
|
|
1223
|
-
throw new ClawtError(MESSAGES.NOT_MAIN_WORKTREE);
|
|
1224
|
-
}
|
|
1225
|
-
} catch (error) {
|
|
1226
|
-
if (error instanceof ClawtError) {
|
|
1227
|
-
throw error;
|
|
1228
|
-
}
|
|
1229
|
-
throw new ClawtError(MESSAGES.NOT_MAIN_WORKTREE);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
function validateClaudeCodeInstalled() {
|
|
1233
|
-
try {
|
|
1234
|
-
execCommand("claude --version");
|
|
1235
|
-
} catch {
|
|
1236
|
-
throw new ClawtError(MESSAGES.CLAUDE_NOT_INSTALLED);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
// src/utils/worktree.ts
|
|
1241
|
-
import { join as join4 } from "path";
|
|
1242
|
-
import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
|
|
1234
|
+
// src/utils/project-config.ts
|
|
1235
|
+
import { existsSync as existsSync3, readFileSync, writeFileSync } from "fs";
|
|
1236
|
+
import { join as join3 } from "path";
|
|
1243
1237
|
|
|
1244
1238
|
// src/utils/fs.ts
|
|
1245
1239
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmdirSync, statSync } from "fs";
|
|
@@ -1280,12 +1274,7 @@ function calculateDirSize(dirPath) {
|
|
|
1280
1274
|
return totalSize;
|
|
1281
1275
|
}
|
|
1282
1276
|
|
|
1283
|
-
// src/utils/validate-branch.ts
|
|
1284
|
-
import Enquirer from "enquirer";
|
|
1285
|
-
|
|
1286
1277
|
// src/utils/project-config.ts
|
|
1287
|
-
import { existsSync as existsSync3, readFileSync, writeFileSync } from "fs";
|
|
1288
|
-
import { join as join3 } from "path";
|
|
1289
1278
|
function getProjectConfigPath(projectName) {
|
|
1290
1279
|
return join3(PROJECTS_CONFIG_DIR, projectName, "config.json");
|
|
1291
1280
|
}
|
|
@@ -1325,12 +1314,80 @@ function getMainWorkBranch() {
|
|
|
1325
1314
|
const config2 = requireProjectConfig();
|
|
1326
1315
|
return config2.clawtMainWorkBranch;
|
|
1327
1316
|
}
|
|
1317
|
+
function guardMainWorkBranchExists(cwd) {
|
|
1318
|
+
const config2 = requireProjectConfig();
|
|
1319
|
+
const mainBranch = config2.clawtMainWorkBranch;
|
|
1320
|
+
if (!checkBranchExists(mainBranch, cwd)) {
|
|
1321
|
+
throw new ClawtError(MESSAGES.GUARD_BRANCH_NOT_EXISTS(mainBranch));
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
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
|
+
}
|
|
1328
1337
|
function getValidateRunCommand() {
|
|
1329
1338
|
const config2 = loadProjectConfig();
|
|
1330
1339
|
return config2?.validateRunCommand || void 0;
|
|
1331
1340
|
}
|
|
1332
1341
|
|
|
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
|
+
|
|
1333
1389
|
// src/utils/validate-branch.ts
|
|
1390
|
+
import Enquirer from "enquirer";
|
|
1334
1391
|
function getValidateBranchName(branchName) {
|
|
1335
1392
|
return `${VALIDATE_BRANCH_PREFIX}${branchName}`;
|
|
1336
1393
|
}
|
|
@@ -3178,6 +3235,7 @@ function buildSeparatorWithHint(cols, hint) {
|
|
|
3178
3235
|
function buildPanelFrame(statusResult, selectedIndex, scrollOffset, rows, cols, countdown) {
|
|
3179
3236
|
const lines = [];
|
|
3180
3237
|
lines.push(PANEL_TITLE(statusResult.main.projectName));
|
|
3238
|
+
lines.push(renderConfiguredBranchLine(statusResult.main));
|
|
3181
3239
|
lines.push(renderSnapshotSummary(statusResult.snapshots.total, statusResult.snapshots.orphaned));
|
|
3182
3240
|
const visibleRows = calculateVisibleRows(rows);
|
|
3183
3241
|
if (statusResult.worktrees.length === 0) {
|
|
@@ -3301,6 +3359,18 @@ function formatChangeStatusLabel(status) {
|
|
|
3301
3359
|
return chalk9.gray(MESSAGES.STATUS_CHANGE_CLEAN);
|
|
3302
3360
|
}
|
|
3303
3361
|
}
|
|
3362
|
+
function renderConfiguredBranchLine(main2) {
|
|
3363
|
+
if (main2.configuredMainBranch === null) {
|
|
3364
|
+
return PANEL_NOT_INITIALIZED;
|
|
3365
|
+
}
|
|
3366
|
+
if (main2.configuredBranchExists === false) {
|
|
3367
|
+
return PANEL_CONFIGURED_BRANCH_DELETED(main2.configuredMainBranch);
|
|
3368
|
+
}
|
|
3369
|
+
if (main2.branch !== main2.configuredMainBranch && !main2.branch.startsWith(VALIDATE_BRANCH_PREFIX)) {
|
|
3370
|
+
return PANEL_CONFIGURED_BRANCH_MISMATCH(main2.configuredMainBranch);
|
|
3371
|
+
}
|
|
3372
|
+
return PANEL_CONFIGURED_BRANCH(main2.configuredMainBranch);
|
|
3373
|
+
}
|
|
3304
3374
|
function renderSnapshotSummary(total, orphaned) {
|
|
3305
3375
|
return PANEL_SNAPSHOT_SUMMARY(total, orphaned);
|
|
3306
3376
|
}
|
|
@@ -3748,7 +3818,7 @@ function registerListCommand(program2) {
|
|
|
3748
3818
|
});
|
|
3749
3819
|
}
|
|
3750
3820
|
function handleList(options) {
|
|
3751
|
-
|
|
3821
|
+
runPreChecks({ mainWorktree: true });
|
|
3752
3822
|
const projectName = getProjectName();
|
|
3753
3823
|
const worktrees = getProjectWorktrees();
|
|
3754
3824
|
logger.info(`list \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}\uFF0C\u5171 ${worktrees.length} \u4E2A worktree`);
|
|
@@ -3798,7 +3868,8 @@ function registerCreateCommand(program2) {
|
|
|
3798
3868
|
});
|
|
3799
3869
|
}
|
|
3800
3870
|
async function handleCreate(options) {
|
|
3801
|
-
|
|
3871
|
+
runPreChecks({ mainWorktree: true, headExists: true });
|
|
3872
|
+
await guardMainWorkBranch();
|
|
3802
3873
|
await ensureOnMainWorkBranch();
|
|
3803
3874
|
const count = Number(options.number);
|
|
3804
3875
|
if (!Number.isInteger(count) || count <= 0) {
|
|
@@ -3833,8 +3904,7 @@ function registerRemoveCommand(program2) {
|
|
|
3833
3904
|
});
|
|
3834
3905
|
}
|
|
3835
3906
|
async function handleRemove(options) {
|
|
3836
|
-
|
|
3837
|
-
requireProjectConfig();
|
|
3907
|
+
runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
|
|
3838
3908
|
const projectName = getProjectName();
|
|
3839
3909
|
logger.info(`remove \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}`);
|
|
3840
3910
|
const allWorktrees = getProjectWorktrees();
|
|
@@ -3940,8 +4010,9 @@ function handleDryRunFromFile(options) {
|
|
|
3940
4010
|
printDryRunPreview(branchNames, tasks, concurrency);
|
|
3941
4011
|
}
|
|
3942
4012
|
async function handleRun(options) {
|
|
3943
|
-
|
|
4013
|
+
runPreChecks({ mainWorktree: true, headExists: true });
|
|
3944
4014
|
if (!options.dryRun) {
|
|
4015
|
+
await guardMainWorkBranch();
|
|
3945
4016
|
await ensureOnMainWorkBranch();
|
|
3946
4017
|
}
|
|
3947
4018
|
if (options.file && options.tasks) {
|
|
@@ -4004,7 +4075,7 @@ function registerResumeCommand(program2) {
|
|
|
4004
4075
|
});
|
|
4005
4076
|
}
|
|
4006
4077
|
async function handleResume(options) {
|
|
4007
|
-
|
|
4078
|
+
runPreChecks({ mainWorktree: true, headExists: true });
|
|
4008
4079
|
validateClaudeCodeInstalled();
|
|
4009
4080
|
logger.info(`resume \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F\u8FC7\u6EE4: ${options.branch ?? "(\u65E0)"}`);
|
|
4010
4081
|
const worktrees = getProjectWorktrees();
|
|
@@ -4105,8 +4176,8 @@ async function executeSyncForBranch(targetWorktreePath, branch) {
|
|
|
4105
4176
|
return { success: true, hasConflict: false };
|
|
4106
4177
|
}
|
|
4107
4178
|
async function handleSync(options) {
|
|
4108
|
-
|
|
4109
|
-
|
|
4179
|
+
runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
|
|
4180
|
+
await guardMainWorkBranch();
|
|
4110
4181
|
logger.info(`sync \u547D\u4EE4\u6267\u884C\uFF0C\u5206\u652F: ${options.branch ?? "(\u672A\u6307\u5B9A)"}`);
|
|
4111
4182
|
const worktrees = getProjectWorktrees();
|
|
4112
4183
|
const worktree = await resolveTargetWorktree(worktrees, SYNC_RESOLVE_MESSAGES, options.branch);
|
|
@@ -4139,8 +4210,7 @@ async function handlePatchApplyFailure(targetWorktreePath, branchName) {
|
|
|
4139
4210
|
const syncResult = await executeSyncForBranch(targetWorktreePath, branchName);
|
|
4140
4211
|
}
|
|
4141
4212
|
async function handleValidateClean(options) {
|
|
4142
|
-
|
|
4143
|
-
requireProjectConfig();
|
|
4213
|
+
runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
|
|
4144
4214
|
const projectName = getProjectName();
|
|
4145
4215
|
const mainWorktreePath = getGitTopLevel();
|
|
4146
4216
|
const worktrees = getProjectWorktrees();
|
|
@@ -4225,8 +4295,7 @@ async function handleValidate(options) {
|
|
|
4225
4295
|
await handleValidateClean(options);
|
|
4226
4296
|
return;
|
|
4227
4297
|
}
|
|
4228
|
-
|
|
4229
|
-
requireProjectConfig();
|
|
4298
|
+
runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
|
|
4230
4299
|
const projectName = getProjectName();
|
|
4231
4300
|
const mainWorktreePath = getGitTopLevel();
|
|
4232
4301
|
const worktrees = getProjectWorktrees();
|
|
@@ -4291,8 +4360,7 @@ function computeIncrementalPatch(snapshotTreeHash, mainWorktreePath) {
|
|
|
4291
4360
|
return { patch, currentTreeHash };
|
|
4292
4361
|
}
|
|
4293
4362
|
async function handleCoverValidate() {
|
|
4294
|
-
|
|
4295
|
-
requireProjectConfig();
|
|
4363
|
+
runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
|
|
4296
4364
|
const projectName = getProjectName();
|
|
4297
4365
|
const mainWorktreePath = getGitTopLevel();
|
|
4298
4366
|
const currentBranch = getCurrentBranch(mainWorktreePath);
|
|
@@ -4371,7 +4439,9 @@ function cleanupWorktreeAndBranch(worktreePath, branchName) {
|
|
|
4371
4439
|
printSuccess(MESSAGES.WORKTREE_CLEANED(branchName));
|
|
4372
4440
|
}
|
|
4373
4441
|
async function handleMerge(options) {
|
|
4374
|
-
|
|
4442
|
+
runPreChecks({ mainWorktree: true, headExists: true });
|
|
4443
|
+
await guardMainWorkBranch();
|
|
4444
|
+
await guardMainWorkBranch();
|
|
4375
4445
|
const mainWorktreePath = getGitTopLevel();
|
|
4376
4446
|
await ensureOnMainWorkBranch(mainWorktreePath);
|
|
4377
4447
|
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)"}`);
|
|
@@ -4532,8 +4602,7 @@ function registerResetCommand(program2) {
|
|
|
4532
4602
|
});
|
|
4533
4603
|
}
|
|
4534
4604
|
async function handleReset() {
|
|
4535
|
-
|
|
4536
|
-
requireProjectConfig();
|
|
4605
|
+
runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
|
|
4537
4606
|
const mainWorktreePath = getGitTopLevel();
|
|
4538
4607
|
logger.info("reset \u547D\u4EE4\u6267\u884C");
|
|
4539
4608
|
if (!isWorkingDirClean(mainWorktreePath)) {
|
|
@@ -4563,7 +4632,7 @@ function registerStatusCommand(program2) {
|
|
|
4563
4632
|
});
|
|
4564
4633
|
}
|
|
4565
4634
|
async function handleStatus(options) {
|
|
4566
|
-
|
|
4635
|
+
runPreChecks({ mainWorktree: true, headExists: true });
|
|
4567
4636
|
if (options.interactive) {
|
|
4568
4637
|
const panel = new InteractivePanel(collectStatus);
|
|
4569
4638
|
await panel.start();
|
|
@@ -4581,10 +4650,15 @@ function collectStatus() {
|
|
|
4581
4650
|
const projectName = getProjectName();
|
|
4582
4651
|
const currentBranch = getCurrentBranch();
|
|
4583
4652
|
const isClean = isWorkingDirClean();
|
|
4653
|
+
const projectConfig = loadProjectConfig();
|
|
4654
|
+
const configuredMainBranch = projectConfig?.clawtMainWorkBranch || null;
|
|
4655
|
+
const configuredBranchExists = configuredMainBranch ? checkBranchExists(configuredMainBranch) : null;
|
|
4584
4656
|
const main2 = {
|
|
4585
4657
|
branch: currentBranch,
|
|
4586
4658
|
isClean,
|
|
4587
|
-
projectName
|
|
4659
|
+
projectName,
|
|
4660
|
+
configuredMainBranch,
|
|
4661
|
+
configuredBranchExists
|
|
4588
4662
|
};
|
|
4589
4663
|
const worktrees = getProjectWorktrees();
|
|
4590
4664
|
const worktreeStatuses = worktrees.map((wt) => collectWorktreeDetailedStatus(wt, projectName));
|
|
@@ -4694,6 +4768,15 @@ function printMainSection(main2) {
|
|
|
4694
4768
|
} else {
|
|
4695
4769
|
printInfo(` \u72B6\u6001: ${chalk11.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
|
|
4696
4770
|
}
|
|
4771
|
+
if (main2.configuredMainBranch !== null) {
|
|
4772
|
+
if (main2.configuredBranchExists === false) {
|
|
4773
|
+
printInfo(` ${chalk11.red(MESSAGES.STATUS_CONFIGURED_BRANCH_DELETED(main2.configuredMainBranch))}`);
|
|
4774
|
+
} else if (main2.branch !== main2.configuredMainBranch && !main2.branch.startsWith(VALIDATE_BRANCH_PREFIX)) {
|
|
4775
|
+
printInfo(` ${chalk11.yellow(MESSAGES.STATUS_CONFIGURED_BRANCH_MISMATCH(main2.configuredMainBranch))}`);
|
|
4776
|
+
} else {
|
|
4777
|
+
printInfo(` ${chalk11.gray(MESSAGES.STATUS_CONFIGURED_BRANCH(main2.configuredMainBranch))}`);
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4697
4780
|
printInfo("");
|
|
4698
4781
|
}
|
|
4699
4782
|
function printWorktreesSection(worktrees, total) {
|
|
@@ -5229,7 +5312,7 @@ function registerInitCommand(program2) {
|
|
|
5229
5312
|
);
|
|
5230
5313
|
}
|
|
5231
5314
|
async function handleInitShow() {
|
|
5232
|
-
|
|
5315
|
+
runPreChecks({ mainWorktree: true, projectConfig: true });
|
|
5233
5316
|
const config2 = requireProjectConfig();
|
|
5234
5317
|
logger.info("init show \u547D\u4EE4\u6267\u884C\uFF0C\u8FDB\u5165\u4EA4\u4E92\u5F0F\u9879\u76EE\u914D\u7F6E");
|
|
5235
5318
|
const { key, newValue } = await interactiveConfigEditor(
|
|
@@ -5242,8 +5325,11 @@ async function handleInitShow() {
|
|
|
5242
5325
|
printSuccess(MESSAGES.INIT_SET_SUCCESS(key, String(newValue)));
|
|
5243
5326
|
}
|
|
5244
5327
|
async function handleInit(options) {
|
|
5245
|
-
|
|
5328
|
+
runPreChecks({ mainWorktree: true });
|
|
5246
5329
|
const existingConfig = loadProjectConfig();
|
|
5330
|
+
if (!options.branch) {
|
|
5331
|
+
validateHeadExists();
|
|
5332
|
+
}
|
|
5247
5333
|
const branchName = options.branch || getCurrentBranch();
|
|
5248
5334
|
logger.info(`init \u547D\u4EE4\u6267\u884C\uFF0C\u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}`);
|
|
5249
5335
|
saveProjectConfig({ clawtMainWorkBranch: branchName });
|
|
@@ -5261,8 +5347,7 @@ function registerHomeCommand(program2) {
|
|
|
5261
5347
|
});
|
|
5262
5348
|
}
|
|
5263
5349
|
async function handleHome() {
|
|
5264
|
-
|
|
5265
|
-
requireProjectConfig();
|
|
5350
|
+
runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true, branchExists: true });
|
|
5266
5351
|
const mainBranch = getMainWorkBranch();
|
|
5267
5352
|
const currentBranch = getCurrentBranch();
|
|
5268
5353
|
if (currentBranch === mainBranch) {
|
package/dist/postinstall.js
CHANGED
|
@@ -21,6 +21,8 @@ var COMMON_MESSAGES = {
|
|
|
21
21
|
GIT_NOT_INSTALLED: "Git \u672A\u5B89\u88C5\u6216\u4E0D\u5728 PATH \u4E2D\uFF0C\u8BF7\u5148\u5B89\u88C5 Git",
|
|
22
22
|
/** Claude Code CLI 未安装 */
|
|
23
23
|
CLAUDE_NOT_INSTALLED: "Claude Code CLI \u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u5B89\u88C5\uFF1Anpm install -g @anthropic-ai/claude-code",
|
|
24
|
+
/** HEAD 不存在(仓库无任何 commit) */
|
|
25
|
+
HEAD_NOT_FOUND: "\u5F53\u524D\u4ED3\u5E93\u5C1A\u672A\u521B\u5EFA\u4EFB\u4F55\u63D0\u4EA4\uFF0C\u8BF7\u5148\u6267\u884C git commit \u521B\u5EFA\u9996\u6B21\u63D0\u4EA4\u540E\u518D\u4F7F\u7528 clawt",
|
|
24
26
|
/** 分支已存在 */
|
|
25
27
|
BRANCH_EXISTS: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u65E0\u6CD5\u521B\u5EFA`,
|
|
26
28
|
/** 分支名清理后为空 */
|
|
@@ -50,7 +52,11 @@ var COMMON_MESSAGES = {
|
|
|
50
52
|
/** 分隔线 */
|
|
51
53
|
SEPARATOR: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
52
54
|
/** 粗分隔线 */
|
|
53
|
-
DOUBLE_SEPARATOR: "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
|
|
55
|
+
DOUBLE_SEPARATOR: "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
56
|
+
/** 守卫检测:配置的主工作分支已不存在 */
|
|
57
|
+
GUARD_BRANCH_NOT_EXISTS: (branchName) => `\u914D\u7F6E\u7684\u4E3B\u5DE5\u4F5C\u5206\u652F ${branchName} \u5DF2\u4E0D\u5B58\u5728\uFF0C\u8BF7\u6267\u884C clawt init \u91CD\u65B0\u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F`,
|
|
58
|
+
/** 守卫检测:当前分支与配置的主工作分支不一致 */
|
|
59
|
+
GUARD_BRANCH_MISMATCH: (configuredBranch, currentBranch) => `\u5F53\u524D\u5206\u652F ${currentBranch} \u4E0E\u914D\u7F6E\u7684\u4E3B\u5DE5\u4F5C\u5206\u652F ${configuredBranch} \u4E0D\u4E00\u81F4\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u6267\u884C clawt init`
|
|
54
60
|
};
|
|
55
61
|
|
|
56
62
|
// src/constants/messages/run.ts
|
|
@@ -361,7 +367,13 @@ var STATUS_MESSAGES = {
|
|
|
361
367
|
/** status 上次验证时间标签 */
|
|
362
368
|
STATUS_LAST_VALIDATED: (relativeTime) => `\u4E0A\u6B21\u9A8C\u8BC1: ${relativeTime}`,
|
|
363
369
|
/** status 未验证警示 */
|
|
364
|
-
STATUS_NOT_VALIDATED: "\u2717 \u672A\u9A8C\u8BC1"
|
|
370
|
+
STATUS_NOT_VALIDATED: "\u2717 \u672A\u9A8C\u8BC1",
|
|
371
|
+
/** status 配置的主工作分支(正常状态) */
|
|
372
|
+
STATUS_CONFIGURED_BRANCH: (branchName) => `\u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}`,
|
|
373
|
+
/** status 配置的主工作分支已不存在 */
|
|
374
|
+
STATUS_CONFIGURED_BRANCH_DELETED: (branchName) => `\u2717 \u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}\uFF08\u5DF2\u4E0D\u5B58\u5728\uFF0C\u8BF7\u6267\u884C clawt init \u91CD\u65B0\u8BBE\u7F6E\uFF09`,
|
|
375
|
+
/** status 当前分支与配置的主工作分支不一致 */
|
|
376
|
+
STATUS_CONFIGURED_BRANCH_MISMATCH: (branchName) => `\u26A0 \u4E3B\u5DE5\u4F5C\u5206\u652F: ${branchName}\uFF08\u5F53\u524D\u5206\u652F\u4E0D\u4E00\u81F4\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u6267\u884C clawt init\uFF09`
|
|
365
377
|
};
|
|
366
378
|
|
|
367
379
|
// src/constants/messages/alias.ts
|
|
@@ -513,6 +525,7 @@ var PANEL_FOOTER_SHORTCUTS = Object.entries(SHORTCUT_LABELS).map(([key, label])
|
|
|
513
525
|
var PANEL_OVERFLOW_DOWN_HINT = chalk.gray("\u2193 \u66F4\u591A worktree...");
|
|
514
526
|
var PANEL_OVERFLOW_UP_HINT = chalk.gray("\u2191 \u66F4\u591A worktree...");
|
|
515
527
|
var PANEL_PRESS_ENTER_TO_RETURN = chalk.gray("\n\u6309 Enter \u8FD4\u56DE\u9762\u677F...");
|
|
528
|
+
var PANEL_NOT_INITIALIZED = chalk.gray("\u672A\u521D\u59CB\u5316\uFF08\u6267\u884C clawt init \u8BBE\u7F6E\u4E3B\u5DE5\u4F5C\u5206\u652F\uFF09");
|
|
516
529
|
|
|
517
530
|
// src/constants/messages/index.ts
|
|
518
531
|
var MESSAGES = {
|
package/package.json
CHANGED
|
@@ -3,8 +3,7 @@ import { logger } from '../logger/index.js';
|
|
|
3
3
|
import { ClawtError } from '../errors/index.js';
|
|
4
4
|
import { MESSAGES, VALIDATE_BRANCH_PREFIX } from '../constants/index.js';
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
requireProjectConfig,
|
|
6
|
+
runPreChecks,
|
|
8
7
|
getProjectName,
|
|
9
8
|
getGitTopLevel,
|
|
10
9
|
getCurrentBranch,
|
|
@@ -98,8 +97,7 @@ export function computeIncrementalPatch(snapshotTreeHash: string, mainWorktreePa
|
|
|
98
97
|
*/
|
|
99
98
|
async function handleCoverValidate(): Promise<void> {
|
|
100
99
|
// 步骤 1:前置校验
|
|
101
|
-
|
|
102
|
-
requireProjectConfig();
|
|
100
|
+
runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true });
|
|
103
101
|
const projectName = getProjectName();
|
|
104
102
|
const mainWorktreePath = getGitTopLevel();
|
|
105
103
|
const currentBranch = getCurrentBranch(mainWorktreePath);
|
package/src/commands/create.ts
CHANGED
|
@@ -4,13 +4,14 @@ import { ClawtError } from '../errors/index.js';
|
|
|
4
4
|
import { logger } from '../logger/index.js';
|
|
5
5
|
import type { CreateOptions } from '../types/index.js';
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
runPreChecks,
|
|
8
8
|
createWorktrees,
|
|
9
9
|
ensureOnMainWorkBranch,
|
|
10
10
|
getValidateBranchName,
|
|
11
11
|
printSuccess,
|
|
12
12
|
printInfo,
|
|
13
13
|
printSeparator,
|
|
14
|
+
guardMainWorkBranch,
|
|
14
15
|
} from '../utils/index.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -33,7 +34,9 @@ export function registerCreateCommand(program: Command): void {
|
|
|
33
34
|
* @param {CreateOptions} options - 命令选项
|
|
34
35
|
*/
|
|
35
36
|
async function handleCreate(options: CreateOptions): Promise<void> {
|
|
36
|
-
|
|
37
|
+
runPreChecks({ mainWorktree: true, headExists: true });
|
|
38
|
+
|
|
39
|
+
await guardMainWorkBranch();
|
|
37
40
|
|
|
38
41
|
await ensureOnMainWorkBranch();
|
|
39
42
|
|
package/src/commands/home.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
2
|
import { MESSAGES } from '../constants/index.js';
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
requireProjectConfig,
|
|
4
|
+
runPreChecks,
|
|
6
5
|
ensureOnMainWorkBranch,
|
|
7
6
|
getCurrentBranch,
|
|
8
7
|
getMainWorkBranch,
|
|
9
8
|
printSuccess,
|
|
10
9
|
printInfo,
|
|
10
|
+
guardMainWorkBranchExists,
|
|
11
11
|
} from '../utils/index.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -27,8 +27,7 @@ export function registerHomeCommand(program: Command): void {
|
|
|
27
27
|
* 执行 home 命令:切换回主工作分支
|
|
28
28
|
*/
|
|
29
29
|
async function handleHome(): Promise<void> {
|
|
30
|
-
|
|
31
|
-
requireProjectConfig();
|
|
30
|
+
runPreChecks({ mainWorktree: true, headExists: true, projectConfig: true, branchExists: true });
|
|
32
31
|
|
|
33
32
|
const mainBranch = getMainWorkBranch();
|
|
34
33
|
const currentBranch = getCurrentBranch();
|
package/src/commands/init.ts
CHANGED
|
@@ -4,7 +4,8 @@ import { logger } from '../logger/index.js';
|
|
|
4
4
|
import { MESSAGES, PROJECT_CONFIG_DEFINITIONS } from '../constants/index.js';
|
|
5
5
|
import type { InitOptions, ProjectConfig } from '../types/index.js';
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
runPreChecks,
|
|
8
|
+
validateHeadExists,
|
|
8
9
|
getCurrentBranch,
|
|
9
10
|
loadProjectConfig,
|
|
10
11
|
saveProjectConfig,
|
|
@@ -40,7 +41,7 @@ export function registerInitCommand(program: Command): void {
|
|
|
40
41
|
* 处理 init show 子命令:交互式面板展示和修改项目配置
|
|
41
42
|
*/
|
|
42
43
|
async function handleInitShow(): Promise<void> {
|
|
43
|
-
|
|
44
|
+
runPreChecks({ mainWorktree: true, projectConfig: true });
|
|
44
45
|
const config = requireProjectConfig();
|
|
45
46
|
|
|
46
47
|
logger.info('init show 命令执行,进入交互式项目配置');
|
|
@@ -66,11 +67,14 @@ async function handleInitShow(): Promise<void> {
|
|
|
66
67
|
* @param {InitOptions} options - 命令选项
|
|
67
68
|
*/
|
|
68
69
|
async function handleInit(options: InitOptions): Promise<void> {
|
|
69
|
-
|
|
70
|
+
runPreChecks({ mainWorktree: true });
|
|
70
71
|
|
|
71
72
|
const existingConfig = loadProjectConfig();
|
|
72
73
|
|
|
73
74
|
// 确定分支名:优先使用 -b 参数,否则使用当前分支
|
|
75
|
+
if (!options.branch) {
|
|
76
|
+
validateHeadExists();
|
|
77
|
+
}
|
|
74
78
|
const branchName = options.branch || getCurrentBranch();
|
|
75
79
|
|
|
76
80
|
logger.info(`init 命令执行,主工作分支: ${branchName}`);
|
package/src/commands/list.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { MESSAGES } from '../constants/index.js';
|
|
|
4
4
|
import { logger } from '../logger/index.js';
|
|
5
5
|
import type { ListOptions } from '../types/index.js';
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
runPreChecks,
|
|
8
8
|
getProjectName,
|
|
9
9
|
getProjectWorktrees,
|
|
10
10
|
getWorktreeStatus,
|
|
@@ -33,7 +33,7 @@ export function registerListCommand(program: Command): void {
|
|
|
33
33
|
* @param {ListOptions} options - 命令选项
|
|
34
34
|
*/
|
|
35
35
|
function handleList(options: ListOptions): void {
|
|
36
|
-
|
|
36
|
+
runPreChecks({ mainWorktree: true });
|
|
37
37
|
|
|
38
38
|
const projectName = getProjectName();
|
|
39
39
|
const worktrees = getProjectWorktrees();
|
package/src/commands/merge.ts
CHANGED
|
@@ -4,7 +4,7 @@ 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
6
|
import {
|
|
7
|
-
|
|
7
|
+
runPreChecks,
|
|
8
8
|
getProjectName,
|
|
9
9
|
getGitTopLevel,
|
|
10
10
|
getProjectWorktrees,
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
resolveTargetWorktree,
|
|
34
34
|
getMainWorkBranch,
|
|
35
35
|
ensureOnMainWorkBranch,
|
|
36
|
+
guardMainWorkBranch,
|
|
36
37
|
} from '../utils/index.js';
|
|
37
38
|
import type { WorktreeResolveMessages } from '../utils/index.js';
|
|
38
39
|
|
|
@@ -135,7 +136,11 @@ function cleanupWorktreeAndBranch(worktreePath: string, branchName: string): voi
|
|
|
135
136
|
* @param {MergeOptions} options - 命令选项
|
|
136
137
|
*/
|
|
137
138
|
async function handleMerge(options: MergeOptions): Promise<void> {
|
|
138
|
-
|
|
139
|
+
runPreChecks({ mainWorktree: true, headExists: true });
|
|
140
|
+
|
|
141
|
+
await guardMainWorkBranch();
|
|
142
|
+
|
|
143
|
+
await guardMainWorkBranch();
|
|
139
144
|
|
|
140
145
|
const mainWorktreePath = getGitTopLevel();
|
|
141
146
|
|