clawt 2.9.1 → 2.10.1
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/.claude/agent-memory/docs-sync-updater/MEMORY.md +14 -10
- package/.claude/agents/docs-sync-updater.md +11 -0
- package/README.md +63 -268
- package/dist/index.js +420 -103
- package/dist/postinstall.js +242 -0
- package/docs/spec.md +162 -14
- package/package.json +1 -1
- package/src/commands/remove.ts +21 -28
- package/src/commands/status.ts +327 -0
- package/src/constants/index.ts +1 -1
- package/src/constants/messages/common.ts +41 -0
- package/src/constants/messages/config.ts +5 -0
- package/src/constants/messages/create.ts +5 -0
- package/src/constants/messages/index.ts +29 -0
- package/src/constants/messages/merge.ts +42 -0
- package/src/constants/messages/remove.ts +15 -0
- package/src/constants/messages/reset.ts +7 -0
- package/src/constants/messages/resume.ts +12 -0
- package/src/constants/messages/run.ts +16 -0
- package/src/constants/messages/status.ts +25 -0
- package/src/constants/messages/sync.ts +24 -0
- package/src/constants/messages/validate.ts +25 -0
- package/src/constants/messages.ts +22 -0
- package/src/index.ts +2 -0
- package/src/types/command.ts +6 -0
- package/src/types/index.ts +2 -1
- package/src/types/status.ts +49 -0
- package/src/utils/git.ts +16 -0
- package/src/utils/index.ts +4 -3
- package/src/utils/validate-snapshot.ts +17 -0
- package/src/utils/worktree-matcher.ts +92 -0
- package/tests/unit/commands/config.test.ts +110 -0
- package/tests/unit/commands/create.test.ts +115 -0
- package/tests/unit/commands/list.test.ts +118 -0
- package/tests/unit/commands/merge.test.ts +323 -0
- package/tests/unit/commands/remove.test.ts +240 -0
- package/tests/unit/commands/reset.test.ts +124 -0
- package/tests/unit/commands/resume.test.ts +91 -0
- package/tests/unit/commands/run.test.ts +207 -0
- package/tests/unit/commands/status.test.ts +214 -0
- package/tests/unit/commands/sync.test.ts +208 -0
- package/tests/unit/commands/validate.test.ts +382 -0
- package/tests/unit/constants/messages.test.ts +1 -1
- package/tests/unit/utils/config.test.ts +21 -1
- package/tests/unit/utils/formatter.test.ts +44 -1
- package/tests/unit/utils/git.test.ts +44 -0
- package/tests/unit/utils/validate-snapshot.test.ts +25 -0
- package/tests/unit/utils/worktree-matcher.test.ts +81 -5
package/dist/index.js
CHANGED
|
@@ -18,8 +18,8 @@ var VALIDATE_SNAPSHOTS_DIR = join(CLAWT_HOME, "validate-snapshots");
|
|
|
18
18
|
// src/constants/branch.ts
|
|
19
19
|
var INVALID_BRANCH_CHARS = /[\/\\.\s~:*?[\]^]+/g;
|
|
20
20
|
|
|
21
|
-
// src/constants/messages.ts
|
|
22
|
-
var
|
|
21
|
+
// src/constants/messages/common.ts
|
|
22
|
+
var COMMON_MESSAGES = {
|
|
23
23
|
/** 不在主 worktree 根目录 */
|
|
24
24
|
NOT_MAIN_WORKTREE: "\u8BF7\u5728\u4E3B worktree \u7684\u6839\u76EE\u5F55\u4E0B\u6267\u884C clawt",
|
|
25
25
|
/** Git 未安装 */
|
|
@@ -28,8 +28,6 @@ var MESSAGES = {
|
|
|
28
28
|
CLAUDE_NOT_INSTALLED: "Claude Code CLI \u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u5B89\u88C5\uFF1Anpm install -g @anthropic-ai/claude-code",
|
|
29
29
|
/** 分支已存在 */
|
|
30
30
|
BRANCH_EXISTS: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u65E0\u6CD5\u521B\u5EFA`,
|
|
31
|
-
/** 分支已存在时提示使用 resume */
|
|
32
|
-
BRANCH_EXISTS_USE_RESUME: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u4F7F\u7528 clawt resume -b ${name} \u6062\u590D\u4F1A\u8BDD`,
|
|
33
31
|
/** 分支名清理后为空 */
|
|
34
32
|
BRANCH_NAME_EMPTY: (original) => `\u5206\u652F\u540D "${original}" \u4E2D\u4E0D\u5305\u542B\u5408\u6CD5\u5B57\u7B26\uFF0C\u65E0\u6CD5\u521B\u5EFA\u5206\u652F`,
|
|
35
33
|
/** 分支名被转换 */
|
|
@@ -46,9 +44,44 @@ var MESSAGES = {
|
|
|
46
44
|
MAIN_WORKTREE_DIRTY: "\u4E3B worktree \u6709\u672A\u63D0\u4EA4\u7684\u66F4\u6539\uFF0C\u8BF7\u5148\u5904\u7406",
|
|
47
45
|
/** 目标 worktree 无更改 */
|
|
48
46
|
TARGET_WORKTREE_CLEAN: "\u8BE5 worktree \u7684\u5206\u652F\u4E0A\u6CA1\u6709\u4EFB\u4F55\u66F4\u6539\uFF0C\u65E0\u9700\u9A8C\u8BC1",
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
/** 用户取消破坏性操作 */
|
|
48
|
+
DESTRUCTIVE_OP_CANCELLED: "\u5DF2\u53D6\u6D88\u64CD\u4F5C",
|
|
49
|
+
/** 请提供提交信息 */
|
|
50
|
+
COMMIT_MESSAGE_REQUIRED: "\u8BF7\u63D0\u4F9B\u63D0\u4EA4\u4FE1\u606F\uFF08-m \u53C2\u6570\uFF09",
|
|
51
|
+
/** 配置文件损坏,已重新生成默认配置 */
|
|
52
|
+
CONFIG_CORRUPTED: "\u914D\u7F6E\u6587\u4EF6\u635F\u574F\u6216\u65E0\u6CD5\u89E3\u6790\uFF0C\u5DF2\u91CD\u65B0\u751F\u6210\u9ED8\u8BA4\u914D\u7F6E",
|
|
53
|
+
/** worktree 状态获取失败 */
|
|
54
|
+
WORKTREE_STATUS_UNAVAILABLE: "(\u72B6\u6001\u4E0D\u53EF\u7528)",
|
|
55
|
+
/** 分隔线 */
|
|
56
|
+
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",
|
|
57
|
+
/** 粗分隔线 */
|
|
58
|
+
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"
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/constants/messages/run.ts
|
|
62
|
+
var RUN_MESSAGES = {
|
|
63
|
+
/** 分支已存在时提示使用 resume */
|
|
64
|
+
BRANCH_EXISTS_USE_RESUME: (name) => `\u5206\u652F ${name} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u4F7F\u7528 clawt resume -b ${name} \u6062\u590D\u4F1A\u8BDD`,
|
|
65
|
+
/** 检测到用户中断 */
|
|
66
|
+
INTERRUPTED: "\u68C0\u6D4B\u5230\u9000\u51FA\u6307\u4EE4\uFF0C\u5DF2\u505C\u6B62 Claude Code \u4EFB\u52A1",
|
|
67
|
+
/** 中断后自动清理完成 */
|
|
68
|
+
INTERRUPT_AUTO_CLEANED: (count) => `\u2713 \u5DF2\u81EA\u52A8\u6E05\u7406 ${count} \u4E2A worktree \u548C\u5BF9\u5E94\u5206\u652F`,
|
|
69
|
+
/** 中断后手动确认清理 */
|
|
70
|
+
INTERRUPT_CONFIRM_CLEANUP: "\u662F\u5426\u79FB\u9664\u521A\u521A\u521B\u5EFA\u7684 worktree \u548C\u5BF9\u5E94\u5206\u652F\uFF1F",
|
|
71
|
+
/** 中断后清理完成 */
|
|
72
|
+
INTERRUPT_CLEANED: (count) => `\u2713 \u5DF2\u6E05\u7406 ${count} \u4E2A worktree \u548C\u5BF9\u5E94\u5206\u652F`,
|
|
73
|
+
/** 中断后保留 worktree */
|
|
74
|
+
INTERRUPT_KEPT: "\u5DF2\u4FDD\u7559 worktree\uFF0C\u53EF\u7A0D\u540E\u4F7F\u7528 clawt remove \u624B\u52A8\u6E05\u7406"
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// src/constants/messages/create.ts
|
|
78
|
+
var CREATE_MESSAGES = {
|
|
79
|
+
/** 创建数量参数无效 */
|
|
80
|
+
INVALID_COUNT: (value) => `\u65E0\u6548\u7684\u521B\u5EFA\u6570\u91CF: "${value}"\uFF0C\u8BF7\u8F93\u5165\u6B63\u6574\u6570`
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// src/constants/messages/merge.ts
|
|
84
|
+
var MERGE_MESSAGES = {
|
|
52
85
|
/** merge 成功 */
|
|
53
86
|
MERGE_SUCCESS: (branch, message, pushed) => `\u2713 \u5206\u652F ${branch} \u5DF2\u6210\u529F\u5408\u5E76\u5230\u5F53\u524D\u5206\u652F
|
|
54
87
|
\u63D0\u4EA4\u4FE1\u606F: ${message}${pushed ? "\n \u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\u4ED3\u5E93" : ""}`,
|
|
@@ -58,57 +91,12 @@ var MESSAGES = {
|
|
|
58
91
|
MERGE_CONFLICT: "\u5408\u5E76\u5B58\u5728\u51B2\u7A81\uFF0C\u8BF7\u624B\u52A8\u5904\u7406\uFF1A\n \u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue",
|
|
59
92
|
/** merge 后清理 worktree 和分支成功 */
|
|
60
93
|
WORKTREE_CLEANED: (branch) => `\u2713 \u5DF2\u6E05\u7406 worktree \u548C\u5206\u652F: ${branch}`,
|
|
61
|
-
/** 请提供提交信息 */
|
|
62
|
-
COMMIT_MESSAGE_REQUIRED: "\u8BF7\u63D0\u4F9B\u63D0\u4EA4\u4FE1\u606F\uFF08-m \u53C2\u6570\uFF09",
|
|
63
94
|
/** 目标 worktree 有未提交修改但未指定 -m */
|
|
64
95
|
TARGET_WORKTREE_DIRTY_NO_MESSAGE: "\u76EE\u6807 worktree \u6709\u672A\u63D0\u4EA4\u7684\u4FEE\u6539\uFF0C\u8BF7\u901A\u8FC7 -m \u53C2\u6570\u63D0\u4F9B\u63D0\u4EA4\u4FE1\u606F",
|
|
65
96
|
/** 目标 worktree 既干净又无本地提交 */
|
|
66
97
|
TARGET_WORKTREE_NO_CHANGES: "\u76EE\u6807 worktree \u6CA1\u6709\u4EFB\u4F55\u53EF\u5408\u5E76\u7684\u53D8\u66F4\uFF08\u5DE5\u4F5C\u533A\u5E72\u51C0\u4E14\u65E0\u672C\u5730\u63D0\u4EA4\uFF09",
|
|
67
|
-
/** 检测到用户中断 */
|
|
68
|
-
INTERRUPTED: "\u68C0\u6D4B\u5230\u9000\u51FA\u6307\u4EE4\uFF0C\u5DF2\u505C\u6B62 Claude Code \u4EFB\u52A1",
|
|
69
|
-
/** 中断后自动清理完成 */
|
|
70
|
-
INTERRUPT_AUTO_CLEANED: (count) => `\u2713 \u5DF2\u81EA\u52A8\u6E05\u7406 ${count} \u4E2A worktree \u548C\u5BF9\u5E94\u5206\u652F`,
|
|
71
|
-
/** 中断后手动确认清理 */
|
|
72
|
-
INTERRUPT_CONFIRM_CLEANUP: "\u662F\u5426\u79FB\u9664\u521A\u521A\u521B\u5EFA\u7684 worktree \u548C\u5BF9\u5E94\u5206\u652F\uFF1F",
|
|
73
|
-
/** 中断后清理完成 */
|
|
74
|
-
INTERRUPT_CLEANED: (count) => `\u2713 \u5DF2\u6E05\u7406 ${count} \u4E2A worktree \u548C\u5BF9\u5E94\u5206\u652F`,
|
|
75
|
-
/** 中断后保留 worktree */
|
|
76
|
-
INTERRUPT_KEPT: "\u5DF2\u4FDD\u7559 worktree\uFF0C\u53EF\u7A0D\u540E\u4F7F\u7528 clawt remove \u624B\u52A8\u6E05\u7406",
|
|
77
|
-
/** 配置文件损坏,已重新生成默认配置 */
|
|
78
|
-
CONFIG_CORRUPTED: "\u914D\u7F6E\u6587\u4EF6\u635F\u574F\u6216\u65E0\u6CD5\u89E3\u6790\uFF0C\u5DF2\u91CD\u65B0\u751F\u6210\u9ED8\u8BA4\u914D\u7F6E",
|
|
79
|
-
/** 配置已恢复为默认值 */
|
|
80
|
-
CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C",
|
|
81
|
-
/** 分隔线 */
|
|
82
|
-
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",
|
|
83
|
-
/** 粗分隔线 */
|
|
84
|
-
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",
|
|
85
|
-
/** 创建数量参数无效 */
|
|
86
|
-
INVALID_COUNT: (value) => `\u65E0\u6548\u7684\u521B\u5EFA\u6570\u91CF: "${value}"\uFF0C\u8BF7\u8F93\u5165\u6B63\u6574\u6570`,
|
|
87
|
-
/** worktree 状态获取失败 */
|
|
88
|
-
WORKTREE_STATUS_UNAVAILABLE: "(\u72B6\u6001\u4E0D\u53EF\u7528)",
|
|
89
|
-
/** 增量 validate 成功提示 */
|
|
90
|
-
INCREMENTAL_VALIDATE_SUCCESS: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u6700\u65B0\u53D8\u66F4\u5E94\u7528\u5230\u4E3B worktree\uFF08\u589E\u91CF\u6A21\u5F0F\uFF09
|
|
91
|
-
\u6682\u5B58\u533A = \u4E0A\u6B21\u5FEB\u7167\uFF0C\u5DE5\u4F5C\u76EE\u5F55 = \u6700\u65B0\u53D8\u66F4`,
|
|
92
|
-
/** 增量 validate 降级为全量模式提示 */
|
|
93
|
-
INCREMENTAL_VALIDATE_FALLBACK: "\u589E\u91CF\u5BF9\u6BD4\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u5168\u91CF\u6A21\u5F0F",
|
|
94
|
-
/** validate 状态已清理 */
|
|
95
|
-
VALIDATE_CLEANED: (branch) => `\u2713 \u5206\u652F ${branch} \u7684 validate \u72B6\u6001\u5DF2\u6E05\u7406`,
|
|
96
98
|
/** merge 命令检测到 validate 状态的提示 */
|
|
97
99
|
MERGE_VALIDATE_STATE_HINT: (branch) => `\u4E3B worktree \u53EF\u80FD\u5B58\u5728 validate \u6B8B\u7559\u72B6\u6001\uFF0C\u53EF\u5148\u6267\u884C clawt validate -b ${branch} --clean \u6E05\u7406`,
|
|
98
|
-
/** sync 自动保存未提交变更 */
|
|
99
|
-
SYNC_AUTO_COMMITTED: (branch) => `\u5DF2\u81EA\u52A8\u4FDD\u5B58 ${branch} \u5206\u652F\u7684\u672A\u63D0\u4EA4\u53D8\u66F4`,
|
|
100
|
-
/** sync 开始合并 */
|
|
101
|
-
SYNC_MERGING: (targetBranch, mainBranch) => `\u6B63\u5728\u5C06 ${mainBranch} \u5408\u5E76\u5230 ${targetBranch} ...`,
|
|
102
|
-
/** sync 成功 */
|
|
103
|
-
SYNC_SUCCESS: (targetBranch, mainBranch) => `\u2713 \u5DF2\u5C06 ${mainBranch} \u7684\u6700\u65B0\u4EE3\u7801\u540C\u6B65\u5230 ${targetBranch}`,
|
|
104
|
-
/** sync 冲突 */
|
|
105
|
-
SYNC_CONFLICT: (worktreePath) => `\u5408\u5E76\u5B58\u5728\u51B2\u7A81\uFF0C\u8BF7\u8FDB\u5165\u76EE\u6807 worktree \u624B\u52A8\u89E3\u51B3\uFF1A
|
|
106
|
-
cd ${worktreePath}
|
|
107
|
-
\u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue
|
|
108
|
-
clawt validate -b <branch> \u9A8C\u8BC1\u53D8\u66F4`,
|
|
109
|
-
/** validate patch apply 失败,提示用户同步主分支 */
|
|
110
|
-
VALIDATE_PATCH_APPLY_FAILED: (branch) => `\u53D8\u66F4\u8FC1\u79FB\u5931\u8D25\uFF1A\u76EE\u6807\u5206\u652F\u4E0E\u4E3B\u5206\u652F\u5DEE\u5F02\u8FC7\u5927
|
|
111
|
-
\u8BF7\u5148\u6267\u884C clawt sync -b ${branch} \u540C\u6B65\u4E3B\u5206\u652F\u540E\u91CD\u8BD5`,
|
|
112
100
|
/** merge 检测到 auto-save 提交,提示用户是否压缩 */
|
|
113
101
|
MERGE_SQUASH_PROMPT: "\u68C0\u6D4B\u5230 sync \u4EA7\u751F\u7684\u4E34\u65F6\u63D0\u4EA4\uFF0C\u662F\u5426\u5C06\u6240\u6709\u63D0\u4EA4\u538B\u7F29\u4E3A\u4E00\u4E2A\uFF1F\n \u538B\u7F29\u540E\u53D8\u66F4\u5C06\u4FDD\u7559\u5728\u76EE\u6807worktree\u7684\u6682\u5B58\u533A\uFF0C\u9700\u8981\u91CD\u65B0\u63D0\u4EA4\uFF08\u53EF\u4F7F\u7528 Claude Code Cli\u6216\u5176\u4ED6\u5DE5\u5177\u751F\u6210\u63D0\u4EA4\u4FE1\u606F\uFF09",
|
|
114
102
|
/** squash 完成且通过 -m 直接提交后的提示 */
|
|
@@ -118,25 +106,37 @@ var MESSAGES = {
|
|
|
118
106
|
\u8BF7\u5728\u76EE\u6807 worktree \u4E2D\u63D0\u4EA4\u540E\u91CD\u65B0\u6267\u884C merge\uFF1A
|
|
119
107
|
cd ${worktreePath}
|
|
120
108
|
\u63D0\u4EA4\u5B8C\u6210\u540E\u6267\u884C\uFF1Aclawt merge -b ${branch}`,
|
|
121
|
-
/**
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
|
|
129
|
-
${failures.map((f) => ` \u2717 ${f.path}: ${f.error}`).join("\n")}`,
|
|
130
|
-
/** resume 无可用 worktree */
|
|
131
|
-
RESUME_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
|
|
132
|
-
/** resume 模糊匹配无结果,列出可用分支 */
|
|
133
|
-
RESUME_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
|
|
109
|
+
/** merge 后 pull 冲突 */
|
|
110
|
+
PULL_CONFLICT: "\u81EA\u52A8 pull \u65F6\u53D1\u751F\u51B2\u7A81\uFF0Cmerge \u5DF2\u5B8C\u6210\u4F46\u8FDC\u7A0B\u540C\u6B65\u5931\u8D25\n \u8BF7\u624B\u52A8\u89E3\u51B3\u51B2\u7A81\uFF1A\n \u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git commit\n \u7136\u540E\u6267\u884C git push \u63A8\u9001\u5230\u8FDC\u7A0B",
|
|
111
|
+
/** push 失败 */
|
|
112
|
+
PUSH_FAILED: "\u81EA\u52A8 push \u5931\u8D25\uFF0Cmerge \u548C pull \u5DF2\u5B8C\u6210\n \u8BF7\u624B\u52A8\u6267\u884C git push",
|
|
113
|
+
/** merge 无可用 worktree */
|
|
114
|
+
MERGE_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
|
|
115
|
+
/** merge 模糊匹配无结果,列出可用分支 */
|
|
116
|
+
MERGE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
|
|
134
117
|
\u53EF\u7528\u5206\u652F\uFF1A
|
|
135
118
|
${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
136
|
-
/**
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
|
|
119
|
+
/** merge 交互选择提示 */
|
|
120
|
+
MERGE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u5408\u5E76\u7684\u5206\u652F",
|
|
121
|
+
/** merge 模糊匹配到多个结果提示 */
|
|
122
|
+
MERGE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// src/constants/messages/validate.ts
|
|
126
|
+
var VALIDATE_MESSAGES = {
|
|
127
|
+
/** validate 成功 */
|
|
128
|
+
VALIDATE_SUCCESS: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u53D8\u66F4\u5E94\u7528\u5230\u4E3B worktree
|
|
129
|
+
\u53EF\u4EE5\u5F00\u59CB\u9A8C\u8BC1\u4E86`,
|
|
130
|
+
/** 增量 validate 成功提示 */
|
|
131
|
+
INCREMENTAL_VALIDATE_SUCCESS: (branch) => `\u2713 \u5DF2\u5C06\u5206\u652F ${branch} \u7684\u6700\u65B0\u53D8\u66F4\u5E94\u7528\u5230\u4E3B worktree\uFF08\u589E\u91CF\u6A21\u5F0F\uFF09
|
|
132
|
+
\u6682\u5B58\u533A = \u4E0A\u6B21\u5FEB\u7167\uFF0C\u5DE5\u4F5C\u76EE\u5F55 = \u6700\u65B0\u53D8\u66F4`,
|
|
133
|
+
/** 增量 validate 降级为全量模式提示 */
|
|
134
|
+
INCREMENTAL_VALIDATE_FALLBACK: "\u589E\u91CF\u5BF9\u6BD4\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u5168\u91CF\u6A21\u5F0F",
|
|
135
|
+
/** validate 状态已清理 */
|
|
136
|
+
VALIDATE_CLEANED: (branch) => `\u2713 \u5206\u652F ${branch} \u7684 validate \u72B6\u6001\u5DF2\u6E05\u7406`,
|
|
137
|
+
/** validate patch apply 失败,提示用户同步主分支 */
|
|
138
|
+
VALIDATE_PATCH_APPLY_FAILED: (branch) => `\u53D8\u66F4\u8FC1\u79FB\u5931\u8D25\uFF1A\u76EE\u6807\u5206\u652F\u4E0E\u4E3B\u5206\u652F\u5DEE\u5F02\u8FC7\u5927
|
|
139
|
+
\u8BF7\u5148\u6267\u884C clawt sync -b ${branch} \u540C\u6B65\u4E3B\u5206\u652F\u540E\u91CD\u8BD5`,
|
|
140
140
|
/** validate 无可用 worktree */
|
|
141
141
|
VALIDATE_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
|
|
142
142
|
/** validate 模糊匹配无结果,列出可用分支 */
|
|
@@ -146,17 +146,22 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
|
146
146
|
/** validate 交互选择提示 */
|
|
147
147
|
VALIDATE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u9A8C\u8BC1\u7684\u5206\u652F",
|
|
148
148
|
/** validate 模糊匹配到多个结果提示 */
|
|
149
|
-
VALIDATE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
|
|
149
|
+
VALIDATE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// src/constants/messages/sync.ts
|
|
153
|
+
var SYNC_MESSAGES = {
|
|
154
|
+
/** sync 自动保存未提交变更 */
|
|
155
|
+
SYNC_AUTO_COMMITTED: (branch) => `\u5DF2\u81EA\u52A8\u4FDD\u5B58 ${branch} \u5206\u652F\u7684\u672A\u63D0\u4EA4\u53D8\u66F4`,
|
|
156
|
+
/** sync 开始合并 */
|
|
157
|
+
SYNC_MERGING: (targetBranch, mainBranch) => `\u6B63\u5728\u5C06 ${mainBranch} \u5408\u5E76\u5230 ${targetBranch} ...`,
|
|
158
|
+
/** sync 成功 */
|
|
159
|
+
SYNC_SUCCESS: (targetBranch, mainBranch) => `\u2713 \u5DF2\u5C06 ${mainBranch} \u7684\u6700\u65B0\u4EE3\u7801\u540C\u6B65\u5230 ${targetBranch}`,
|
|
160
|
+
/** sync 冲突 */
|
|
161
|
+
SYNC_CONFLICT: (worktreePath) => `\u5408\u5E76\u5B58\u5728\u51B2\u7A81\uFF0C\u8BF7\u8FDB\u5165\u76EE\u6807 worktree \u624B\u52A8\u89E3\u51B3\uFF1A
|
|
162
|
+
cd ${worktreePath}
|
|
163
|
+
\u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue
|
|
164
|
+
clawt validate -b <branch> \u9A8C\u8BC1\u53D8\u66F4`,
|
|
160
165
|
/** sync 无可用 worktree */
|
|
161
166
|
SYNC_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
|
|
162
167
|
/** sync 模糊匹配无结果,列出可用分支 */
|
|
@@ -166,11 +171,93 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
|
166
171
|
/** sync 交互选择提示 */
|
|
167
172
|
SYNC_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u540C\u6B65\u7684\u5206\u652F",
|
|
168
173
|
/** sync 模糊匹配到多个结果提示 */
|
|
169
|
-
SYNC_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
SYNC_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/constants/messages/resume.ts
|
|
178
|
+
var RESUME_MESSAGES = {
|
|
179
|
+
/** resume 无可用 worktree */
|
|
180
|
+
RESUME_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u8BF7\u5148\u901A\u8FC7 clawt run \u6216 clawt create \u521B\u5EFA",
|
|
181
|
+
/** resume 模糊匹配无结果,列出可用分支 */
|
|
182
|
+
RESUME_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
|
|
183
|
+
\u53EF\u7528\u5206\u652F\uFF1A
|
|
184
|
+
${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
185
|
+
/** resume 交互选择提示 */
|
|
186
|
+
RESUME_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u6062\u590D\u7684\u5206\u652F",
|
|
187
|
+
/** resume 模糊匹配到多个结果提示 */
|
|
188
|
+
RESUME_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/constants/messages/remove.ts
|
|
192
|
+
var REMOVE_MESSAGES = {
|
|
193
|
+
/** remove 无可用 worktree */
|
|
194
|
+
REMOVE_NO_WORKTREES: "\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u53EF\u7528\u7684 worktree\uFF0C\u65E0\u9700\u79FB\u9664",
|
|
195
|
+
/** remove 多选交互提示 */
|
|
196
|
+
REMOVE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u79FB\u9664\u7684\u5206\u652F\uFF08\u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
|
|
197
|
+
/** remove 模糊匹配到多个结果提示 */
|
|
198
|
+
REMOVE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\u8981\u79FB\u9664\u7684\uFF08\u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09\uFF1A`,
|
|
199
|
+
/** remove 模糊匹配无结果,列出可用分支 */
|
|
200
|
+
REMOVE_NO_MATCH: (name, branches) => `\u672A\u627E\u5230\u4E0E "${name}" \u5339\u914D\u7684\u5206\u652F
|
|
201
|
+
\u53EF\u7528\u5206\u652F\uFF1A
|
|
202
|
+
${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
203
|
+
/** 批量移除部分失败 */
|
|
204
|
+
REMOVE_PARTIAL_FAILURE: (failures) => `\u4EE5\u4E0B worktree \u79FB\u9664\u5931\u8D25\uFF1A
|
|
205
|
+
${failures.map((f) => ` \u2717 ${f.path}: ${f.error}`).join("\n")}`
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/constants/messages/reset.ts
|
|
209
|
+
var RESET_MESSAGES = {
|
|
210
|
+
/** reset 成功 */
|
|
211
|
+
RESET_SUCCESS: "\u2713 \u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u91CD\u7F6E",
|
|
212
|
+
/** reset 时工作区和暂存区已干净 */
|
|
213
|
+
RESET_ALREADY_CLEAN: "\u4E3B worktree \u5DE5\u4F5C\u533A\u548C\u6682\u5B58\u533A\u5DF2\u662F\u5E72\u51C0\u72B6\u6001\uFF0C\u65E0\u9700\u91CD\u7F6E"
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// src/constants/messages/config.ts
|
|
217
|
+
var CONFIG_CMD_MESSAGES = {
|
|
218
|
+
/** 配置已恢复为默认值 */
|
|
219
|
+
CONFIG_RESET_SUCCESS: "\u2713 \u914D\u7F6E\u5DF2\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C"
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/constants/messages/status.ts
|
|
223
|
+
var STATUS_MESSAGES = {
|
|
224
|
+
/** status 命令标题 */
|
|
225
|
+
STATUS_TITLE: (projectName) => `\u9879\u76EE\u72B6\u6001\u603B\u89C8: ${projectName}`,
|
|
226
|
+
/** status 主 worktree 区块标题 */
|
|
227
|
+
STATUS_MAIN_SECTION: "\u4E3B Worktree",
|
|
228
|
+
/** status worktrees 区块标题 */
|
|
229
|
+
STATUS_WORKTREES_SECTION: "Worktree \u5217\u8868",
|
|
230
|
+
/** status 快照区块标题 */
|
|
231
|
+
STATUS_SNAPSHOTS_SECTION: "\u672A\u6E05\u7406\u7684 Validate \u5FEB\u7167",
|
|
232
|
+
/** status 无 worktree */
|
|
233
|
+
STATUS_NO_WORKTREES: "(\u65E0\u6D3B\u8DC3 worktree)",
|
|
234
|
+
/** status 无未清理快照 */
|
|
235
|
+
STATUS_NO_SNAPSHOTS: "(\u65E0\u672A\u6E05\u7406\u7684\u5FEB\u7167)",
|
|
236
|
+
/** status 变更状态:已提交 */
|
|
237
|
+
STATUS_CHANGE_COMMITTED: "\u5DF2\u63D0\u4EA4",
|
|
238
|
+
/** status 变更状态:未提交修改 */
|
|
239
|
+
STATUS_CHANGE_UNCOMMITTED: "\u672A\u63D0\u4EA4\u4FEE\u6539",
|
|
240
|
+
/** status 变更状态:合并冲突 */
|
|
241
|
+
STATUS_CHANGE_CONFLICT: "\u5408\u5E76\u51B2\u7A81",
|
|
242
|
+
/** status 变更状态:无变更 */
|
|
243
|
+
STATUS_CHANGE_CLEAN: "\u65E0\u53D8\u66F4",
|
|
244
|
+
/** status 快照对应 worktree 已不存在 */
|
|
245
|
+
STATUS_SNAPSHOT_ORPHANED: "(\u5BF9\u5E94 worktree \u5DF2\u4E0D\u5B58\u5728)"
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// src/constants/messages/index.ts
|
|
249
|
+
var MESSAGES = {
|
|
250
|
+
...COMMON_MESSAGES,
|
|
251
|
+
...RUN_MESSAGES,
|
|
252
|
+
...CREATE_MESSAGES,
|
|
253
|
+
...MERGE_MESSAGES,
|
|
254
|
+
...VALIDATE_MESSAGES,
|
|
255
|
+
...SYNC_MESSAGES,
|
|
256
|
+
...RESUME_MESSAGES,
|
|
257
|
+
...REMOVE_MESSAGES,
|
|
258
|
+
...RESET_MESSAGES,
|
|
259
|
+
...CONFIG_CMD_MESSAGES,
|
|
260
|
+
...STATUS_MESSAGES
|
|
174
261
|
};
|
|
175
262
|
|
|
176
263
|
// src/constants/exitCodes.ts
|
|
@@ -420,6 +507,14 @@ function getCommitCountAhead(branchName, cwd) {
|
|
|
420
507
|
const output = execCommand(`git rev-list --count HEAD..${branchName}`, { cwd });
|
|
421
508
|
return parseInt(output, 10) || 0;
|
|
422
509
|
}
|
|
510
|
+
function getCommitCountBehind(branchName, cwd) {
|
|
511
|
+
try {
|
|
512
|
+
const output = execCommand(`git rev-list --count ${branchName}..HEAD`, { cwd });
|
|
513
|
+
return parseInt(output, 10) || 0;
|
|
514
|
+
} catch {
|
|
515
|
+
return 0;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
423
518
|
function parseShortStat(output) {
|
|
424
519
|
let insertions = 0;
|
|
425
520
|
let deletions = 0;
|
|
@@ -798,6 +893,14 @@ function removeSnapshot(projectName, branchName) {
|
|
|
798
893
|
logger.info(`\u5DF2\u5220\u9664 validate \u5FEB\u7167: ${headPath}`);
|
|
799
894
|
}
|
|
800
895
|
}
|
|
896
|
+
function getProjectSnapshotBranches(projectName) {
|
|
897
|
+
const projectDir = join3(VALIDATE_SNAPSHOTS_DIR, projectName);
|
|
898
|
+
if (!existsSync5(projectDir)) {
|
|
899
|
+
return [];
|
|
900
|
+
}
|
|
901
|
+
const files = readdirSync3(projectDir);
|
|
902
|
+
return files.filter((f) => f.endsWith(".tree")).map((f) => f.replace(/\.tree$/, ""));
|
|
903
|
+
}
|
|
801
904
|
function removeProjectSnapshots(projectName) {
|
|
802
905
|
const projectDir = join3(VALIDATE_SNAPSHOTS_DIR, projectName);
|
|
803
906
|
if (!existsSync5(projectDir)) {
|
|
@@ -833,6 +936,44 @@ async function promptSelectBranch(worktrees, message) {
|
|
|
833
936
|
}).run();
|
|
834
937
|
return worktrees.find((wt) => wt.branch === selectedBranch);
|
|
835
938
|
}
|
|
939
|
+
async function promptMultiSelectBranches(worktrees, message) {
|
|
940
|
+
const selectedBranches = await new Enquirer2.MultiSelect({
|
|
941
|
+
message,
|
|
942
|
+
choices: worktrees.map((wt) => ({
|
|
943
|
+
name: wt.branch,
|
|
944
|
+
message: wt.branch
|
|
945
|
+
})),
|
|
946
|
+
// 使用空心圆/实心圆作为选中指示符
|
|
947
|
+
symbols: {
|
|
948
|
+
indicator: { on: "\u25CF", off: "\u25CB" }
|
|
949
|
+
}
|
|
950
|
+
}).run();
|
|
951
|
+
return worktrees.filter((wt) => selectedBranches.includes(wt.branch));
|
|
952
|
+
}
|
|
953
|
+
async function resolveTargetWorktrees(worktrees, messages, branchName) {
|
|
954
|
+
if (worktrees.length === 0) {
|
|
955
|
+
throw new ClawtError(messages.noWorktrees);
|
|
956
|
+
}
|
|
957
|
+
if (!branchName) {
|
|
958
|
+
if (worktrees.length === 1) {
|
|
959
|
+
return [worktrees[0]];
|
|
960
|
+
}
|
|
961
|
+
return promptMultiSelectBranches(worktrees, messages.selectBranch);
|
|
962
|
+
}
|
|
963
|
+
const exactMatch = findExactMatch(worktrees, branchName);
|
|
964
|
+
if (exactMatch) {
|
|
965
|
+
return [exactMatch];
|
|
966
|
+
}
|
|
967
|
+
const fuzzyMatches = findFuzzyMatches(worktrees, branchName);
|
|
968
|
+
if (fuzzyMatches.length === 1) {
|
|
969
|
+
return [fuzzyMatches[0]];
|
|
970
|
+
}
|
|
971
|
+
if (fuzzyMatches.length > 1) {
|
|
972
|
+
return promptMultiSelectBranches(fuzzyMatches, messages.multipleMatches(branchName));
|
|
973
|
+
}
|
|
974
|
+
const allBranches = worktrees.map((wt) => wt.branch);
|
|
975
|
+
throw new ClawtError(messages.noMatch(branchName, allBranches));
|
|
976
|
+
}
|
|
836
977
|
async function resolveTargetWorktree(worktrees, messages, branchName) {
|
|
837
978
|
if (worktrees.length === 0) {
|
|
838
979
|
throw new ClawtError(messages.noWorktrees);
|
|
@@ -937,32 +1078,28 @@ function handleCreate(options) {
|
|
|
937
1078
|
}
|
|
938
1079
|
|
|
939
1080
|
// src/commands/remove.ts
|
|
1081
|
+
var REMOVE_RESOLVE_MESSAGES = {
|
|
1082
|
+
noWorktrees: MESSAGES.REMOVE_NO_WORKTREES,
|
|
1083
|
+
selectBranch: MESSAGES.REMOVE_SELECT_BRANCH,
|
|
1084
|
+
multipleMatches: MESSAGES.REMOVE_MULTIPLE_MATCHES,
|
|
1085
|
+
noMatch: MESSAGES.REMOVE_NO_MATCH
|
|
1086
|
+
};
|
|
940
1087
|
function registerRemoveCommand(program2) {
|
|
941
|
-
program2.command("remove").description("\u79FB\u9664 worktree\uFF08\u652F\u6301\u5355\u4E2A/\u6279\u91CF/\u5168\u90E8\uFF09").option("--all", "\u79FB\u9664\u5F53\u524D\u9879\u76EE\u4E0B\u6240\u6709 worktree").option("-b, --branch <branchName>", "\u6307\u5B9A\u5206\u652F\u540D\uFF08\
|
|
1088
|
+
program2.command("remove").description("\u79FB\u9664 worktree\uFF08\u652F\u6301\u5355\u4E2A/\u6279\u91CF/\u5168\u90E8\uFF09").option("--all", "\u79FB\u9664\u5F53\u524D\u9879\u76EE\u4E0B\u6240\u6709 worktree").option("-b, --branch <branchName>", "\u6307\u5B9A\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\uFF09").action(async (options) => {
|
|
942
1089
|
await handleRemove(options);
|
|
943
1090
|
});
|
|
944
1091
|
}
|
|
945
|
-
function resolveWorktreesToRemove(options) {
|
|
946
|
-
const allWorktrees = getProjectWorktrees();
|
|
947
|
-
if (options.all) {
|
|
948
|
-
return allWorktrees;
|
|
949
|
-
}
|
|
950
|
-
if (!options.branch) {
|
|
951
|
-
throw new ClawtError("\u8BF7\u6307\u5B9A --all \u6216 -b <branchName> \u53C2\u6570");
|
|
952
|
-
}
|
|
953
|
-
const matched = allWorktrees.filter(
|
|
954
|
-
(wt) => wt.branch === options.branch || wt.branch.startsWith(`${options.branch}-`)
|
|
955
|
-
);
|
|
956
|
-
if (matched.length === 0) {
|
|
957
|
-
throw new ClawtError(MESSAGES.WORKTREE_NOT_FOUND(options.branch));
|
|
958
|
-
}
|
|
959
|
-
return matched;
|
|
960
|
-
}
|
|
961
1092
|
async function handleRemove(options) {
|
|
962
1093
|
validateMainWorktree();
|
|
963
1094
|
const projectName = getProjectName();
|
|
964
1095
|
logger.info(`remove \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${projectName}`);
|
|
965
|
-
const
|
|
1096
|
+
const allWorktrees = getProjectWorktrees();
|
|
1097
|
+
let worktreesToRemove;
|
|
1098
|
+
if (options.all) {
|
|
1099
|
+
worktreesToRemove = allWorktrees;
|
|
1100
|
+
} else {
|
|
1101
|
+
worktreesToRemove = await resolveTargetWorktrees(allWorktrees, REMOVE_RESOLVE_MESSAGES, options.branch);
|
|
1102
|
+
}
|
|
966
1103
|
if (worktreesToRemove.length === 0) {
|
|
967
1104
|
printInfo(MESSAGES.NO_WORKTREES);
|
|
968
1105
|
return;
|
|
@@ -1632,6 +1769,185 @@ async function handleReset() {
|
|
|
1632
1769
|
}
|
|
1633
1770
|
}
|
|
1634
1771
|
|
|
1772
|
+
// src/commands/status.ts
|
|
1773
|
+
import chalk5 from "chalk";
|
|
1774
|
+
function registerStatusCommand(program2) {
|
|
1775
|
+
program2.command("status").description("\u663E\u793A\u9879\u76EE\u5168\u5C40\u72B6\u6001\u603B\u89C8").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
|
|
1776
|
+
handleStatus(options);
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
function handleStatus(options) {
|
|
1780
|
+
validateMainWorktree();
|
|
1781
|
+
const statusResult = collectStatus();
|
|
1782
|
+
logger.info(`status \u547D\u4EE4\u6267\u884C\uFF0C\u9879\u76EE: ${statusResult.main.projectName}\uFF0C\u5171 ${statusResult.totalWorktrees} \u4E2A worktree`);
|
|
1783
|
+
if (options.json) {
|
|
1784
|
+
printStatusAsJson(statusResult);
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
printStatusAsText(statusResult);
|
|
1788
|
+
}
|
|
1789
|
+
function collectStatus() {
|
|
1790
|
+
const projectName = getProjectName();
|
|
1791
|
+
const currentBranch = getCurrentBranch();
|
|
1792
|
+
const isClean = isWorkingDirClean();
|
|
1793
|
+
const main = {
|
|
1794
|
+
branch: currentBranch,
|
|
1795
|
+
isClean,
|
|
1796
|
+
projectName
|
|
1797
|
+
};
|
|
1798
|
+
const worktrees = getProjectWorktrees();
|
|
1799
|
+
const worktreeStatuses = worktrees.map((wt) => collectWorktreeDetailedStatus(wt, projectName));
|
|
1800
|
+
const snapshots = collectSnapshots(projectName, worktrees);
|
|
1801
|
+
return {
|
|
1802
|
+
main,
|
|
1803
|
+
worktrees: worktreeStatuses,
|
|
1804
|
+
snapshots,
|
|
1805
|
+
totalWorktrees: worktrees.length
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
function collectWorktreeDetailedStatus(worktree, projectName) {
|
|
1809
|
+
const changeStatus = detectChangeStatus(worktree);
|
|
1810
|
+
const { commitsAhead, commitsBehind } = countCommitDivergence(worktree.branch);
|
|
1811
|
+
const { insertions, deletions } = countDiffStat(worktree.path);
|
|
1812
|
+
return {
|
|
1813
|
+
path: worktree.path,
|
|
1814
|
+
branch: worktree.branch,
|
|
1815
|
+
changeStatus,
|
|
1816
|
+
commitsAhead,
|
|
1817
|
+
commitsBehind,
|
|
1818
|
+
hasSnapshot: hasSnapshot(projectName, worktree.branch),
|
|
1819
|
+
insertions,
|
|
1820
|
+
deletions
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
function detectChangeStatus(worktree) {
|
|
1824
|
+
try {
|
|
1825
|
+
if (hasMergeConflict(worktree.path)) {
|
|
1826
|
+
return "conflict";
|
|
1827
|
+
}
|
|
1828
|
+
if (!isWorkingDirClean(worktree.path)) {
|
|
1829
|
+
return "uncommitted";
|
|
1830
|
+
}
|
|
1831
|
+
if (hasLocalCommits(worktree.branch)) {
|
|
1832
|
+
return "committed";
|
|
1833
|
+
}
|
|
1834
|
+
return "clean";
|
|
1835
|
+
} catch {
|
|
1836
|
+
return "clean";
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
function countCommitDivergence(branchName) {
|
|
1840
|
+
try {
|
|
1841
|
+
return {
|
|
1842
|
+
commitsAhead: getCommitCountAhead(branchName),
|
|
1843
|
+
commitsBehind: getCommitCountBehind(branchName)
|
|
1844
|
+
};
|
|
1845
|
+
} catch {
|
|
1846
|
+
return { commitsAhead: 0, commitsBehind: 0 };
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
function countDiffStat(worktreePath) {
|
|
1850
|
+
try {
|
|
1851
|
+
return getDiffStat(worktreePath);
|
|
1852
|
+
} catch {
|
|
1853
|
+
return { insertions: 0, deletions: 0 };
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
function collectSnapshots(projectName, worktrees) {
|
|
1857
|
+
const snapshotBranches = getProjectSnapshotBranches(projectName);
|
|
1858
|
+
const worktreeBranchSet = new Set(worktrees.map((wt) => wt.branch));
|
|
1859
|
+
return snapshotBranches.map((branch) => ({
|
|
1860
|
+
branch,
|
|
1861
|
+
worktreeExists: worktreeBranchSet.has(branch)
|
|
1862
|
+
}));
|
|
1863
|
+
}
|
|
1864
|
+
function printStatusAsJson(result) {
|
|
1865
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1866
|
+
}
|
|
1867
|
+
function printStatusAsText(result) {
|
|
1868
|
+
printDoubleSeparator();
|
|
1869
|
+
printInfo(` ${chalk5.bold.cyan(MESSAGES.STATUS_TITLE(result.main.projectName))}`);
|
|
1870
|
+
printDoubleSeparator();
|
|
1871
|
+
printInfo("");
|
|
1872
|
+
printMainSection(result.main);
|
|
1873
|
+
printSeparator();
|
|
1874
|
+
printInfo("");
|
|
1875
|
+
printWorktreesSection(result.worktrees, result.totalWorktrees);
|
|
1876
|
+
printSeparator();
|
|
1877
|
+
printInfo("");
|
|
1878
|
+
printSnapshotsSection(result.snapshots);
|
|
1879
|
+
printDoubleSeparator();
|
|
1880
|
+
}
|
|
1881
|
+
function printMainSection(main) {
|
|
1882
|
+
printInfo(` ${chalk5.bold("\u25C6")} ${chalk5.bold(MESSAGES.STATUS_MAIN_SECTION)}`);
|
|
1883
|
+
printInfo(` \u5206\u652F: ${chalk5.bold(main.branch)}`);
|
|
1884
|
+
if (main.isClean) {
|
|
1885
|
+
printInfo(` \u72B6\u6001: ${chalk5.green("\u2713 \u5E72\u51C0")}`);
|
|
1886
|
+
} else {
|
|
1887
|
+
printInfo(` \u72B6\u6001: ${chalk5.yellow("\u2717 \u6709\u672A\u63D0\u4EA4\u4FEE\u6539")}`);
|
|
1888
|
+
}
|
|
1889
|
+
printInfo("");
|
|
1890
|
+
}
|
|
1891
|
+
function printWorktreesSection(worktrees, total) {
|
|
1892
|
+
printInfo(` ${chalk5.bold("\u25C6")} ${chalk5.bold(MESSAGES.STATUS_WORKTREES_SECTION)} (${total} \u4E2A)`);
|
|
1893
|
+
printInfo("");
|
|
1894
|
+
if (worktrees.length === 0) {
|
|
1895
|
+
printInfo(` ${MESSAGES.STATUS_NO_WORKTREES}`);
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
for (const wt of worktrees) {
|
|
1899
|
+
printWorktreeItem(wt);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
function printWorktreeItem(wt) {
|
|
1903
|
+
const statusLabel = formatChangeStatusLabel(wt.changeStatus);
|
|
1904
|
+
printInfo(` ${chalk5.bold("\u25CF")} ${chalk5.bold(wt.branch)} [${statusLabel}]`);
|
|
1905
|
+
const parts = [];
|
|
1906
|
+
if (wt.insertions > 0 || wt.deletions > 0) {
|
|
1907
|
+
parts.push(`${chalk5.green(`+${wt.insertions}`)} ${chalk5.red(`-${wt.deletions}`)}`);
|
|
1908
|
+
}
|
|
1909
|
+
if (wt.commitsAhead > 0) {
|
|
1910
|
+
parts.push(chalk5.yellow(`${wt.commitsAhead} \u4E2A\u672C\u5730\u63D0\u4EA4`));
|
|
1911
|
+
}
|
|
1912
|
+
if (wt.commitsBehind > 0) {
|
|
1913
|
+
parts.push(chalk5.yellow(`\u843D\u540E\u4E3B\u5206\u652F ${wt.commitsBehind} \u4E2A\u63D0\u4EA4`));
|
|
1914
|
+
} else {
|
|
1915
|
+
parts.push(chalk5.green("\u4E0E\u4E3B\u5206\u652F\u540C\u6B65"));
|
|
1916
|
+
}
|
|
1917
|
+
printInfo(` ${parts.join(" ")}`);
|
|
1918
|
+
if (wt.hasSnapshot) {
|
|
1919
|
+
printInfo(` ${chalk5.blue("\u6709 validate \u5FEB\u7167")}`);
|
|
1920
|
+
}
|
|
1921
|
+
printInfo("");
|
|
1922
|
+
}
|
|
1923
|
+
function formatChangeStatusLabel(status) {
|
|
1924
|
+
switch (status) {
|
|
1925
|
+
case "committed":
|
|
1926
|
+
return chalk5.green(MESSAGES.STATUS_CHANGE_COMMITTED);
|
|
1927
|
+
case "uncommitted":
|
|
1928
|
+
return chalk5.yellow(MESSAGES.STATUS_CHANGE_UNCOMMITTED);
|
|
1929
|
+
case "conflict":
|
|
1930
|
+
return chalk5.red(MESSAGES.STATUS_CHANGE_CONFLICT);
|
|
1931
|
+
case "clean":
|
|
1932
|
+
return chalk5.gray(MESSAGES.STATUS_CHANGE_CLEAN);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
function printSnapshotsSection(snapshots) {
|
|
1936
|
+
printInfo(` ${chalk5.bold("\u25C6")} ${chalk5.bold(MESSAGES.STATUS_SNAPSHOTS_SECTION)} (${snapshots.length} \u4E2A)`);
|
|
1937
|
+
printInfo("");
|
|
1938
|
+
if (snapshots.length === 0) {
|
|
1939
|
+
printInfo(` ${MESSAGES.STATUS_NO_SNAPSHOTS}`);
|
|
1940
|
+
printInfo("");
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
for (const snap of snapshots) {
|
|
1944
|
+
const orphanLabel = snap.worktreeExists ? "" : ` ${chalk5.yellow(MESSAGES.STATUS_SNAPSHOT_ORPHANED)}`;
|
|
1945
|
+
const icon = snap.worktreeExists ? chalk5.blue("\u25CF") : chalk5.yellow("\u26A0");
|
|
1946
|
+
printInfo(` ${icon} ${snap.branch}${orphanLabel}`);
|
|
1947
|
+
}
|
|
1948
|
+
printInfo("");
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1635
1951
|
// src/index.ts
|
|
1636
1952
|
var require2 = createRequire(import.meta.url);
|
|
1637
1953
|
var { version } = require2("../package.json");
|
|
@@ -1653,6 +1969,7 @@ registerMergeCommand(program);
|
|
|
1653
1969
|
registerConfigCommand(program);
|
|
1654
1970
|
registerSyncCommand(program);
|
|
1655
1971
|
registerResetCommand(program);
|
|
1972
|
+
registerStatusCommand(program);
|
|
1656
1973
|
process.on("uncaughtException", (error) => {
|
|
1657
1974
|
if (error instanceof ClawtError) {
|
|
1658
1975
|
printError(error.message);
|