clawt 3.5.0 → 3.5.2
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 +278 -33
- package/dist/postinstall.js +38 -2
- package/package.json +1 -1
- package/src/commands/merge.ts +20 -5
- package/src/constants/ai-prompts.ts +14 -0
- package/src/constants/config.ts +9 -0
- package/src/constants/index.ts +1 -0
- package/src/constants/messages/merge.ts +15 -0
- package/src/constants/messages/validate.ts +12 -0
- package/src/types/command.ts +2 -0
- package/src/types/config.ts +4 -0
- package/src/utils/clipboard.ts +52 -0
- package/src/utils/conflict-resolver.ts +170 -0
- package/src/utils/git-core.ts +49 -1
- package/src/utils/index.ts +9 -3
- package/src/utils/shell.ts +96 -0
- package/src/utils/validate-runner.ts +63 -16
- package/tests/unit/commands/merge.test.ts +59 -3
- package/tests/unit/utils/conflict-resolver.test.ts +250 -0
- package/src/constants/messages.ts +0 -179
package/dist/index.js
CHANGED
|
@@ -173,7 +173,22 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
|
173
173
|
/** merge 交互选择提示 */
|
|
174
174
|
MERGE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u5408\u5E76\u7684\u5206\u652F",
|
|
175
175
|
/** merge 模糊匹配到多个结果提示 */
|
|
176
|
-
MERGE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A
|
|
176
|
+
MERGE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`,
|
|
177
|
+
/** 询问是否使用 AI 辅助解决冲突 */
|
|
178
|
+
MERGE_CONFLICT_ASK_AI: "\u68C0\u6D4B\u5230\u5408\u5E76\u51B2\u7A81\uFF0C\u662F\u5426\u4F7F\u7528 Claude Code \u81EA\u52A8\u89E3\u51B3\uFF1F",
|
|
179
|
+
/** AI 冲突解决开始 */
|
|
180
|
+
MERGE_CONFLICT_AI_START: (fileCount) => `\u6B63\u5728\u4F7F\u7528 Claude Code \u5206\u6790\u5E76\u89E3\u51B3 ${fileCount} \u4E2A\u51B2\u7A81\u6587\u4EF6...`,
|
|
181
|
+
/** AI 冲突解决成功 */
|
|
182
|
+
MERGE_CONFLICT_AI_SUCCESS: "\u2713 Claude Code \u5DF2\u6210\u529F\u89E3\u51B3\u6240\u6709\u51B2\u7A81",
|
|
183
|
+
/** AI 冲突解决后仍有未解决的冲突 */
|
|
184
|
+
MERGE_CONFLICT_AI_PARTIAL: (remaining) => `Claude Code \u5DF2\u5904\u7406\u51B2\u7A81\u6587\u4EF6\uFF0C\u4F46\u4ECD\u6709 ${remaining} \u4E2A\u6587\u4EF6\u5B58\u5728\u51B2\u7A81
|
|
185
|
+
\u8BF7\u624B\u52A8\u5904\u7406\u5269\u4F59\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue`,
|
|
186
|
+
/** AI 冲突解决失败 */
|
|
187
|
+
MERGE_CONFLICT_AI_FAILED: (errorMsg) => `Claude Code \u89E3\u51B3\u51B2\u7A81\u5931\u8D25: ${errorMsg}
|
|
188
|
+
\u8BF7\u624B\u52A8\u5904\u7406\uFF1A
|
|
189
|
+
\u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue`,
|
|
190
|
+
/** --auto 模式下的冲突手动解决(配置为 manual) */
|
|
191
|
+
MERGE_CONFLICT_MANUAL: "\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"
|
|
177
192
|
};
|
|
178
193
|
|
|
179
194
|
// src/constants/messages/validate.ts
|
|
@@ -235,7 +250,19 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
|
235
250
|
VALIDATE_BRANCH_NOT_FOUND: (validateBranch, branch) => `\u9A8C\u8BC1\u5206\u652F ${validateBranch} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C clawt create \u6216 clawt run \u521B\u5EFA\u5206\u652F ${branch}`,
|
|
236
251
|
/** validate 成功(含验证分支信息) */
|
|
237
252
|
VALIDATE_SUCCESS_WITH_BRANCH: (branch, validateBranch) => `\u2713 \u5DF2\u5207\u6362\u5230\u9A8C\u8BC1\u5206\u652F ${validateBranch} \u5E76\u5E94\u7528\u5206\u652F ${branch} \u7684\u53D8\u66F4
|
|
238
|
-
\u53EF\u4EE5\u5F00\u59CB\u9A8C\u8BC1\u4E86
|
|
253
|
+
\u53EF\u4EE5\u5F00\u59CB\u9A8C\u8BC1\u4E86`,
|
|
254
|
+
/** 错误信息已复制到剪贴板提示 */
|
|
255
|
+
VALIDATE_RUN_ERROR_COPIED: "\u2702 \u9519\u8BEF\u4FE1\u606F\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F",
|
|
256
|
+
/** 剪贴板复制失败提示 */
|
|
257
|
+
VALIDATE_RUN_ERROR_COPY_FAILED: "\u26A0 \u9519\u8BEF\u4FE1\u606F\u590D\u5236\u5230\u526A\u8D34\u677F\u5931\u8D25",
|
|
258
|
+
/** 单命令(含 && 链)剪贴板错误格式 */
|
|
259
|
+
VALIDATE_CLIPBOARD_SINGLE_ERROR: (command, stderr) => `${command} \u6307\u4EE4\u6267\u884C\u51FA\u9519\uFF0C\u9519\u8BEF\u4FE1\u606F\uFF1A
|
|
260
|
+
${stderr}`,
|
|
261
|
+
/** 并行命令中单个命令的剪贴板错误格式 */
|
|
262
|
+
VALIDATE_CLIPBOARD_PARALLEL_ERROR: (command, stderr) => `${command} \u6307\u4EE4\u6267\u884C\u51FA\u9519\uFF0C\u9519\u8BEF\u4FE1\u606F\uFF1A
|
|
263
|
+
${stderr}`,
|
|
264
|
+
/** 多个错误之间的分隔符 */
|
|
265
|
+
VALIDATE_CLIPBOARD_SEPARATOR: "\n\n---\n\n"
|
|
239
266
|
};
|
|
240
267
|
|
|
241
268
|
// src/constants/messages/sync.ts
|
|
@@ -667,6 +694,15 @@ var CONFIG_DEFINITIONS = {
|
|
|
667
694
|
autoUpdate: {
|
|
668
695
|
defaultValue: true,
|
|
669
696
|
description: "\u662F\u5426\u542F\u7528\u81EA\u52A8\u66F4\u65B0\u68C0\u67E5\uFF08\u6BCF 24 \u5C0F\u65F6\u68C0\u67E5\u4E00\u6B21 npm registry\uFF09"
|
|
697
|
+
},
|
|
698
|
+
conflictResolveMode: {
|
|
699
|
+
defaultValue: "ask",
|
|
700
|
+
description: "merge \u51B2\u7A81\u65F6\u7684\u89E3\u51B3\u6A21\u5F0F\uFF1Aask\uFF08\u8BE2\u95EE\u662F\u5426\u4F7F\u7528 AI\uFF09\u3001auto\uFF08\u81EA\u52A8 AI \u89E3\u51B3\uFF09\u3001manual\uFF08\u624B\u52A8\u89E3\u51B3\uFF09",
|
|
701
|
+
allowedValues: ["ask", "auto", "manual"]
|
|
702
|
+
},
|
|
703
|
+
conflictResolveTimeoutMs: {
|
|
704
|
+
defaultValue: 3e5,
|
|
705
|
+
description: "Claude Code \u51B2\u7A81\u89E3\u51B3\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 300000\uFF085 \u5206\u949F\uFF09"
|
|
670
706
|
}
|
|
671
707
|
};
|
|
672
708
|
function deriveDefaultConfig(definitions) {
|
|
@@ -768,6 +804,21 @@ var GROUP_SEPARATOR_LABEL = (dateLabel, relativeTime) => `\u2550\u2550\u2550\u25
|
|
|
768
804
|
var UNKNOWN_DATE_GROUP = "\u672A\u77E5\u65E5\u671F";
|
|
769
805
|
var UNKNOWN_DATE_SEPARATOR_LABEL = `\u2550\u2550\u2550\u2550 ${chalk2.bold.hex("#FF8C00")("\u672A\u77E5\u65E5\u671F")} \u2550\u2550\u2550\u2550`;
|
|
770
806
|
|
|
807
|
+
// src/constants/ai-prompts.ts
|
|
808
|
+
var CONFLICT_RESOLVE_PROMPT = `\u4F60\u662F\u4E00\u4E2A Git \u5408\u5E76\u51B2\u7A81\u89E3\u51B3\u4E13\u5BB6\u3002\u5F53\u524D\u4ED3\u5E93\u5904\u4E8E\u5408\u5E76\u51B2\u7A81\u72B6\u6001\u3002
|
|
809
|
+
|
|
810
|
+
## \u4EFB\u52A1
|
|
811
|
+
|
|
812
|
+
1. \u901A\u8FC7 git status \u548C git diff \u7B49\u547D\u4EE4\uFF0C\u81EA\u884C\u67E5\u770B\u5F53\u524D\u4ED3\u5E93\u7684\u51B2\u7A81\u6587\u4EF6\u5217\u8868\u53CA\u51B2\u7A81\u5185\u5BB9
|
|
813
|
+
2. \u901A\u8FC7 git log \u7B49\u547D\u4EE4\uFF0C\u5206\u6790\u4E24\u4E2A\u5206\u652F\u5404\u81EA\u7684\u53D8\u66F4\u610F\u56FE
|
|
814
|
+
3. \u76F4\u63A5\u7F16\u8F91\u6BCF\u4E2A\u51B2\u7A81\u6587\u4EF6\uFF0C\u79FB\u9664\u6240\u6709\u51B2\u7A81\u6807\u8BB0\uFF08<<<<<<<\u3001=======\u3001>>>>>>>\uFF09
|
|
815
|
+
4. \u4FDD\u7559\u53CC\u65B9\u6709\u610F\u4E49\u7684\u53D8\u66F4\uFF0C\u5408\u7406\u5408\u5E76\u4EE3\u7801\u903B\u8F91
|
|
816
|
+
5. \u5982\u679C\u4E24\u4E2A\u5206\u652F\u4FEE\u6539\u4E86\u540C\u4E00\u6BB5\u4EE3\u7801\u4F46\u610F\u56FE\u4E0D\u540C\uFF0C\u4F18\u5148\u4FDD\u8BC1\u4EE3\u7801\u7684\u6B63\u786E\u6027\u548C\u5B8C\u6574\u6027
|
|
817
|
+
6. \u89E3\u51B3\u51B2\u7A81\u540E\uFF0C\u786E\u4FDD\u4EE3\u7801\u8BED\u6CD5\u6B63\u786E\u3001\u903B\u8F91\u5B8C\u6574
|
|
818
|
+
7. \u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u6CE8\u91CA\u8BF4\u660E\u4F60\u505A\u4E86\u4EC0\u4E48\u4FEE\u6539\uFF0C\u53EA\u9700\u8981\u4FEE\u6539\u6587\u4EF6\u5185\u5BB9
|
|
819
|
+
|
|
820
|
+
\u8BF7\u76F4\u63A5\u5F00\u59CB\u3002`;
|
|
821
|
+
|
|
771
822
|
// src/constants/pre-checks.ts
|
|
772
823
|
var PRE_CHECK_CREATE = {
|
|
773
824
|
requireMainWorktree: true,
|
|
@@ -940,29 +991,86 @@ function parseParallelCommands(commandString) {
|
|
|
940
991
|
const parts = escaped.split("&");
|
|
941
992
|
return parts.map((part) => part.replace(new RegExp(placeholder, "g"), "&&").trim()).filter((part) => part.length > 0);
|
|
942
993
|
}
|
|
943
|
-
function
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
994
|
+
function spawnWithStderrCapture(command, options) {
|
|
995
|
+
return new Promise((resolve4) => {
|
|
996
|
+
const child = spawn(command, {
|
|
997
|
+
cwd: options?.cwd,
|
|
998
|
+
stdio: ["inherit", "inherit", "pipe"],
|
|
999
|
+
shell: true
|
|
1000
|
+
});
|
|
1001
|
+
const stderrChunks = [];
|
|
1002
|
+
child.stderr?.on("data", (chunk) => {
|
|
1003
|
+
process.stderr.write(chunk);
|
|
1004
|
+
stderrChunks.push(chunk);
|
|
1005
|
+
});
|
|
1006
|
+
child.on("error", (err) => {
|
|
1007
|
+
resolve4({
|
|
1008
|
+
exitCode: 1,
|
|
1009
|
+
error: err.message,
|
|
1010
|
+
stderr: Buffer.concat(stderrChunks).toString("utf-8")
|
|
954
1011
|
});
|
|
955
|
-
|
|
956
|
-
|
|
1012
|
+
});
|
|
1013
|
+
child.on("close", (code) => {
|
|
1014
|
+
resolve4({
|
|
1015
|
+
exitCode: code ?? 1,
|
|
1016
|
+
stderr: Buffer.concat(stderrChunks).toString("utf-8")
|
|
957
1017
|
});
|
|
958
1018
|
});
|
|
959
1019
|
});
|
|
1020
|
+
}
|
|
1021
|
+
function runCommandWithStderrCapture(command, options) {
|
|
1022
|
+
logger.debug(`\u6267\u884C\u547D\u4EE4(stderr\u6355\u83B7): ${command}${options?.cwd ? ` (cwd: ${options.cwd})` : ""}`);
|
|
1023
|
+
return spawnWithStderrCapture(command, options);
|
|
1024
|
+
}
|
|
1025
|
+
function runParallelCommandsWithStderrCapture(commands, options) {
|
|
1026
|
+
const promises = commands.map(async (command) => {
|
|
1027
|
+
logger.debug(`\u5E76\u884C\u542F\u52A8\u547D\u4EE4(stderr\u6355\u83B7): ${command}${options?.cwd ? ` (cwd: ${options.cwd})` : ""}`);
|
|
1028
|
+
const result = await spawnWithStderrCapture(command, options);
|
|
1029
|
+
return { command, ...result };
|
|
1030
|
+
});
|
|
960
1031
|
return Promise.all(promises);
|
|
961
1032
|
}
|
|
962
1033
|
|
|
1034
|
+
// src/utils/clipboard.ts
|
|
1035
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
1036
|
+
function getClipboardCommand() {
|
|
1037
|
+
switch (process.platform) {
|
|
1038
|
+
case "darwin":
|
|
1039
|
+
return { command: "pbcopy", args: [] };
|
|
1040
|
+
case "linux":
|
|
1041
|
+
return { command: "xclip", args: ["-selection", "clipboard"] };
|
|
1042
|
+
case "win32":
|
|
1043
|
+
return { command: "clip", args: [] };
|
|
1044
|
+
default:
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
function copyToClipboard(text) {
|
|
1049
|
+
try {
|
|
1050
|
+
const clipboardCmd = getClipboardCommand();
|
|
1051
|
+
if (!clipboardCmd) {
|
|
1052
|
+
logger.debug(`\u4E0D\u652F\u6301\u7684\u5E73\u53F0: ${process.platform}\uFF0C\u8DF3\u8FC7\u526A\u8D34\u677F\u590D\u5236`);
|
|
1053
|
+
return false;
|
|
1054
|
+
}
|
|
1055
|
+
const result = spawnSync2(clipboardCmd.command, clipboardCmd.args, {
|
|
1056
|
+
input: text,
|
|
1057
|
+
encoding: "utf-8",
|
|
1058
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1059
|
+
});
|
|
1060
|
+
if (result.status !== 0) {
|
|
1061
|
+
logger.debug(`\u526A\u8D34\u677F\u547D\u4EE4\u6267\u884C\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${result.status}`);
|
|
1062
|
+
return false;
|
|
1063
|
+
}
|
|
1064
|
+
return true;
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
logger.debug(`\u526A\u8D34\u677F\u590D\u5236\u5F02\u5E38: ${error.message}`);
|
|
1067
|
+
return false;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
963
1071
|
// src/utils/git-core.ts
|
|
964
1072
|
import { basename } from "path";
|
|
965
|
-
import { execSync as execSync2 } from "child_process";
|
|
1073
|
+
import { execSync as execSync2, execFileSync as execFileSync2 } from "child_process";
|
|
966
1074
|
function getGitCommonDir(cwd) {
|
|
967
1075
|
return execCommand("git rev-parse --git-common-dir", { cwd });
|
|
968
1076
|
}
|
|
@@ -1085,6 +1193,24 @@ function gitApplyCachedCheck(patchContent, cwd) {
|
|
|
1085
1193
|
return false;
|
|
1086
1194
|
}
|
|
1087
1195
|
}
|
|
1196
|
+
function getConflictFiles(cwd) {
|
|
1197
|
+
const status = getStatusPorcelain(cwd);
|
|
1198
|
+
if (!status) return [];
|
|
1199
|
+
return status.split("\n").filter((line) => /^(UU|AA|DD|DU|UD|AU|UA)/.test(line)).map((line) => line.slice(3));
|
|
1200
|
+
}
|
|
1201
|
+
function gitAddFiles(files, cwd) {
|
|
1202
|
+
if (files.length === 0) return;
|
|
1203
|
+
const args = ["add", "--", ...files];
|
|
1204
|
+
logger.debug(`\u6267\u884C\u547D\u4EE4: git ${args.join(" ")}${cwd ? ` (cwd: ${cwd})` : ""}`);
|
|
1205
|
+
execFileSync2("git", args, {
|
|
1206
|
+
cwd,
|
|
1207
|
+
encoding: "utf-8",
|
|
1208
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
function gitMergeContinue(cwd) {
|
|
1212
|
+
execCommand("GIT_EDITOR=true git merge --continue", { cwd });
|
|
1213
|
+
}
|
|
1088
1214
|
|
|
1089
1215
|
// src/utils/git-branch.ts
|
|
1090
1216
|
function checkBranchExists(branchName, cwd) {
|
|
@@ -1700,12 +1826,12 @@ function parseConcurrency(optionValue, configValue) {
|
|
|
1700
1826
|
import Enquirer2 from "enquirer";
|
|
1701
1827
|
|
|
1702
1828
|
// src/utils/claude.ts
|
|
1703
|
-
import { spawnSync as
|
|
1829
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
1704
1830
|
import { existsSync as existsSync7, readdirSync as readdirSync3 } from "fs";
|
|
1705
1831
|
import { join as join5 } from "path";
|
|
1706
1832
|
|
|
1707
1833
|
// src/utils/terminal.ts
|
|
1708
|
-
import { execFileSync as
|
|
1834
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
1709
1835
|
import { existsSync as existsSync6 } from "fs";
|
|
1710
1836
|
function isITerm2Installed() {
|
|
1711
1837
|
return existsSync6(ITERM2_APP_PATH);
|
|
@@ -1764,7 +1890,7 @@ function openCommandInNewTerminalTab(command, tabTitle) {
|
|
|
1764
1890
|
logger.debug(`\u6253\u5F00\u7EC8\u7AEF Tab [${terminalApp}]: ${tabTitle}`);
|
|
1765
1891
|
logger.debug(`\u6267\u884C\u547D\u4EE4: ${command}`);
|
|
1766
1892
|
try {
|
|
1767
|
-
|
|
1893
|
+
execFileSync3("osascript", ["-e", script], {
|
|
1768
1894
|
encoding: "utf-8",
|
|
1769
1895
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1770
1896
|
});
|
|
@@ -1809,7 +1935,7 @@ function launchInteractiveClaude(worktree, options = {}) {
|
|
|
1809
1935
|
printInfo(` \u6A21\u5F0F: ${hasPreviousSession ? "\u7EE7\u7EED\u4E0A\u6B21\u5BF9\u8BDD" : "\u65B0\u5BF9\u8BDD"}`);
|
|
1810
1936
|
}
|
|
1811
1937
|
printInfo("");
|
|
1812
|
-
const result =
|
|
1938
|
+
const result = spawnSync3(cmd, args, {
|
|
1813
1939
|
cwd: worktree.path,
|
|
1814
1940
|
stdio: "inherit"
|
|
1815
1941
|
});
|
|
@@ -3170,39 +3296,66 @@ async function checkForUpdates(currentVersion) {
|
|
|
3170
3296
|
}
|
|
3171
3297
|
|
|
3172
3298
|
// src/utils/validate-runner.ts
|
|
3173
|
-
function
|
|
3299
|
+
function handleErrorClipboard(clipboardContent) {
|
|
3300
|
+
const success = copyToClipboard(clipboardContent);
|
|
3301
|
+
if (success) {
|
|
3302
|
+
printInfo(MESSAGES.VALIDATE_RUN_ERROR_COPIED);
|
|
3303
|
+
} else {
|
|
3304
|
+
printWarning(MESSAGES.VALIDATE_RUN_ERROR_COPY_FAILED);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
function buildSingleErrorClipboard(command, stderr, exitCode) {
|
|
3308
|
+
if (stderr.trim()) {
|
|
3309
|
+
return MESSAGES.VALIDATE_CLIPBOARD_SINGLE_ERROR(command, stderr.trim());
|
|
3310
|
+
}
|
|
3311
|
+
return `${command} \u6307\u4EE4\u6267\u884C\u51FA\u9519\uFF0C\u9000\u51FA\u7801: ${exitCode}`;
|
|
3312
|
+
}
|
|
3313
|
+
async function executeSingleCommand(command, mainWorktreePath) {
|
|
3174
3314
|
printInfo(MESSAGES.VALIDATE_RUN_START(command));
|
|
3175
3315
|
printSeparator();
|
|
3176
|
-
const result =
|
|
3316
|
+
const result = await runCommandWithStderrCapture(command, { cwd: mainWorktreePath });
|
|
3177
3317
|
printSeparator();
|
|
3178
3318
|
if (result.error) {
|
|
3179
|
-
printError(MESSAGES.VALIDATE_RUN_ERROR(command, result.error
|
|
3319
|
+
printError(MESSAGES.VALIDATE_RUN_ERROR(command, result.error));
|
|
3320
|
+
const clipboardContent = MESSAGES.VALIDATE_CLIPBOARD_SINGLE_ERROR(command, result.error);
|
|
3321
|
+
handleErrorClipboard(clipboardContent);
|
|
3180
3322
|
return;
|
|
3181
3323
|
}
|
|
3182
|
-
|
|
3183
|
-
if (exitCode === 0) {
|
|
3324
|
+
if (result.exitCode === 0) {
|
|
3184
3325
|
printSuccess(MESSAGES.VALIDATE_RUN_SUCCESS(command));
|
|
3185
3326
|
} else {
|
|
3186
|
-
printError(MESSAGES.VALIDATE_RUN_FAILED(command, exitCode));
|
|
3327
|
+
printError(MESSAGES.VALIDATE_RUN_FAILED(command, result.exitCode));
|
|
3328
|
+
const clipboardContent = buildSingleErrorClipboard(command, result.stderr, result.exitCode);
|
|
3329
|
+
handleErrorClipboard(clipboardContent);
|
|
3187
3330
|
}
|
|
3188
3331
|
}
|
|
3189
3332
|
function reportParallelResults(results) {
|
|
3190
3333
|
printSeparator();
|
|
3191
3334
|
const successCount = results.filter((r) => r.exitCode === 0 && !r.error).length;
|
|
3192
3335
|
const failedCount = results.length - successCount;
|
|
3336
|
+
const errorClipboardParts = [];
|
|
3193
3337
|
for (const result of results) {
|
|
3194
3338
|
if (result.error) {
|
|
3195
3339
|
printError(MESSAGES.VALIDATE_PARALLEL_CMD_ERROR(result.command, result.error));
|
|
3340
|
+
errorClipboardParts.push(
|
|
3341
|
+
MESSAGES.VALIDATE_CLIPBOARD_PARALLEL_ERROR(result.command, result.error)
|
|
3342
|
+
);
|
|
3196
3343
|
} else if (result.exitCode === 0) {
|
|
3197
3344
|
printSuccess(MESSAGES.VALIDATE_PARALLEL_CMD_SUCCESS(result.command));
|
|
3198
3345
|
} else {
|
|
3199
3346
|
printError(MESSAGES.VALIDATE_PARALLEL_CMD_FAILED(result.command, result.exitCode));
|
|
3347
|
+
const errorContent = result.stderr.trim() ? result.stderr.trim() : `\u9000\u51FA\u7801: ${result.exitCode}`;
|
|
3348
|
+
errorClipboardParts.push(
|
|
3349
|
+
MESSAGES.VALIDATE_CLIPBOARD_PARALLEL_ERROR(result.command, errorContent)
|
|
3350
|
+
);
|
|
3200
3351
|
}
|
|
3201
3352
|
}
|
|
3202
3353
|
if (failedCount === 0) {
|
|
3203
3354
|
printSuccess(MESSAGES.VALIDATE_PARALLEL_RUN_ALL_SUCCESS(results.length));
|
|
3204
3355
|
} else {
|
|
3205
3356
|
printError(MESSAGES.VALIDATE_PARALLEL_RUN_SUMMARY(successCount, failedCount));
|
|
3357
|
+
const clipboardContent = errorClipboardParts.join(MESSAGES.VALIDATE_CLIPBOARD_SEPARATOR);
|
|
3358
|
+
handleErrorClipboard(clipboardContent);
|
|
3206
3359
|
}
|
|
3207
3360
|
}
|
|
3208
3361
|
async function executeParallelCommands(commands, mainWorktreePath) {
|
|
@@ -3211,14 +3364,14 @@ async function executeParallelCommands(commands, mainWorktreePath) {
|
|
|
3211
3364
|
printInfo(MESSAGES.VALIDATE_PARALLEL_CMD_START(i + 1, commands.length, commands[i]));
|
|
3212
3365
|
}
|
|
3213
3366
|
printSeparator();
|
|
3214
|
-
const results = await
|
|
3367
|
+
const results = await runParallelCommandsWithStderrCapture(commands, { cwd: mainWorktreePath });
|
|
3215
3368
|
reportParallelResults(results);
|
|
3216
3369
|
}
|
|
3217
3370
|
async function executeRunCommand(command, mainWorktreePath) {
|
|
3218
3371
|
printInfo("");
|
|
3219
3372
|
const commands = parseParallelCommands(command);
|
|
3220
3373
|
if (commands.length <= 1) {
|
|
3221
|
-
executeSingleCommand(commands[0] || command, mainWorktreePath);
|
|
3374
|
+
await executeSingleCommand(commands[0] || command, mainWorktreePath);
|
|
3222
3375
|
} else {
|
|
3223
3376
|
await executeParallelCommands(commands, mainWorktreePath);
|
|
3224
3377
|
}
|
|
@@ -3948,6 +4101,89 @@ var InteractivePanel = class {
|
|
|
3948
4101
|
}
|
|
3949
4102
|
};
|
|
3950
4103
|
|
|
4104
|
+
// src/utils/conflict-resolver.ts
|
|
4105
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
4106
|
+
var DEFAULT_CONFLICT_RESOLVE_TIMEOUT_MS = 3e5;
|
|
4107
|
+
function buildConflictResolvePrompt() {
|
|
4108
|
+
return CONFLICT_RESOLVE_PROMPT;
|
|
4109
|
+
}
|
|
4110
|
+
function getConflictResolveTimeout() {
|
|
4111
|
+
const configValue = getConfigValue("conflictResolveTimeoutMs");
|
|
4112
|
+
if (typeof configValue === "number" && configValue > 0) {
|
|
4113
|
+
return configValue;
|
|
4114
|
+
}
|
|
4115
|
+
return DEFAULT_CONFLICT_RESOLVE_TIMEOUT_MS;
|
|
4116
|
+
}
|
|
4117
|
+
function invokeClaudeForConflictResolve(prompt, cwd) {
|
|
4118
|
+
const args = ["-p", prompt, "--permission-mode", "bypassPermissions"];
|
|
4119
|
+
logger.info(`\u8C03\u7528 Claude Code \u89E3\u51B3\u51B2\u7A81\uFF0C\u547D\u4EE4: claude -p "..." --permission-mode bypassPermissions`);
|
|
4120
|
+
try {
|
|
4121
|
+
const output = execFileSync4("claude", args, {
|
|
4122
|
+
cwd,
|
|
4123
|
+
encoding: "utf-8",
|
|
4124
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4125
|
+
timeout: getConflictResolveTimeout()
|
|
4126
|
+
});
|
|
4127
|
+
return output;
|
|
4128
|
+
} catch (error) {
|
|
4129
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
4130
|
+
logger.error(`Claude Code \u51B2\u7A81\u89E3\u51B3\u5931\u8D25: ${errMsg}`);
|
|
4131
|
+
throw new ClawtError(MESSAGES.MERGE_CONFLICT_AI_FAILED(errMsg));
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
function resolveConflictsWithAI(currentBranch, incomingBranch, cwd) {
|
|
4135
|
+
const conflictFiles = getConflictFiles(cwd);
|
|
4136
|
+
if (conflictFiles.length === 0) {
|
|
4137
|
+
return true;
|
|
4138
|
+
}
|
|
4139
|
+
printInfo(MESSAGES.MERGE_CONFLICT_AI_START(conflictFiles.length));
|
|
4140
|
+
const prompt = buildConflictResolvePrompt();
|
|
4141
|
+
try {
|
|
4142
|
+
invokeClaudeForConflictResolve(prompt, cwd);
|
|
4143
|
+
} catch (error) {
|
|
4144
|
+
const errMsg = error instanceof ClawtError ? error.message : String(error);
|
|
4145
|
+
printWarning(errMsg);
|
|
4146
|
+
return false;
|
|
4147
|
+
}
|
|
4148
|
+
const remainingConflicts = getConflictFiles(cwd);
|
|
4149
|
+
if (remainingConflicts.length === 0) {
|
|
4150
|
+
gitAddFiles(conflictFiles, cwd);
|
|
4151
|
+
gitMergeContinue(cwd);
|
|
4152
|
+
printSuccess(MESSAGES.MERGE_CONFLICT_AI_SUCCESS);
|
|
4153
|
+
return true;
|
|
4154
|
+
}
|
|
4155
|
+
const resolvedFiles = conflictFiles.filter((f) => !remainingConflicts.includes(f));
|
|
4156
|
+
if (resolvedFiles.length > 0) {
|
|
4157
|
+
gitAddFiles(resolvedFiles, cwd);
|
|
4158
|
+
}
|
|
4159
|
+
printWarning(MESSAGES.MERGE_CONFLICT_AI_PARTIAL(remainingConflicts.length));
|
|
4160
|
+
return false;
|
|
4161
|
+
}
|
|
4162
|
+
function determineConflictResolveMode(autoFlag) {
|
|
4163
|
+
if (autoFlag === true) {
|
|
4164
|
+
return "auto";
|
|
4165
|
+
}
|
|
4166
|
+
const configMode = getConfigValue("conflictResolveMode");
|
|
4167
|
+
if (configMode === "auto" || configMode === "manual") {
|
|
4168
|
+
return configMode;
|
|
4169
|
+
}
|
|
4170
|
+
return "ask";
|
|
4171
|
+
}
|
|
4172
|
+
async function handleMergeConflict(currentBranch, incomingBranch, cwd, autoFlag) {
|
|
4173
|
+
const mode = determineConflictResolveMode(autoFlag);
|
|
4174
|
+
if (mode === "manual") {
|
|
4175
|
+
throw new ClawtError(MESSAGES.MERGE_CONFLICT_MANUAL);
|
|
4176
|
+
}
|
|
4177
|
+
if (mode === "auto") {
|
|
4178
|
+
return resolveConflictsWithAI(currentBranch, incomingBranch, cwd);
|
|
4179
|
+
}
|
|
4180
|
+
const shouldUseAI = await confirmAction(MESSAGES.MERGE_CONFLICT_ASK_AI);
|
|
4181
|
+
if (!shouldUseAI) {
|
|
4182
|
+
throw new ClawtError(MESSAGES.MERGE_CONFLICT_MANUAL);
|
|
4183
|
+
}
|
|
4184
|
+
return resolveConflictsWithAI(currentBranch, incomingBranch, cwd);
|
|
4185
|
+
}
|
|
4186
|
+
|
|
3951
4187
|
// src/commands/list.ts
|
|
3952
4188
|
import chalk10 from "chalk";
|
|
3953
4189
|
function registerListCommand(program2) {
|
|
@@ -4527,7 +4763,7 @@ async function handleCoverValidate() {
|
|
|
4527
4763
|
|
|
4528
4764
|
// src/commands/merge.ts
|
|
4529
4765
|
function registerMergeCommand(program2) {
|
|
4530
|
-
program2.command("merge").description("\u5408\u5E76\u67D0\u4E2A\u5DF2\u9A8C\u8BC1\u7684 worktree \u5206\u652F\u5230\u4E3B worktree").option("-b, --branch <branchName>", "\u8981\u5408\u5E76\u7684\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\u4F9B\u9009\u62E9\uFF09").option("-m, --message <commitMessage>", "\u63D0\u4EA4\u4FE1\u606F\uFF08\u76EE\u6807 worktree \u5DE5\u4F5C\u533A\u6709\u4FEE\u6539\u65F6\u5FC5\u586B\uFF09").action(async (options) => {
|
|
4766
|
+
program2.command("merge").description("\u5408\u5E76\u67D0\u4E2A\u5DF2\u9A8C\u8BC1\u7684 worktree \u5206\u652F\u5230\u4E3B worktree").option("-b, --branch <branchName>", "\u8981\u5408\u5E76\u7684\u5206\u652F\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF0C\u4E0D\u4F20\u5219\u5217\u51FA\u6240\u6709\u5206\u652F\u4F9B\u9009\u62E9\uFF09").option("-m, --message <commitMessage>", "\u63D0\u4EA4\u4FE1\u606F\uFF08\u76EE\u6807 worktree \u5DE5\u4F5C\u533A\u6709\u4FEE\u6539\u65F6\u5FC5\u586B\uFF09").option("--auto", "\u9047\u5230\u51B2\u7A81\u76F4\u63A5\u8C03\u7528 AI \u89E3\u51B3\uFF0C\u4E0D\u518D\u8BE2\u95EE").action(async (options) => {
|
|
4531
4767
|
await handleMerge(options);
|
|
4532
4768
|
});
|
|
4533
4769
|
}
|
|
@@ -4599,16 +4835,25 @@ async function handleMerge(options) {
|
|
|
4599
4835
|
throw new ClawtError(MESSAGES.TARGET_WORKTREE_NO_CHANGES);
|
|
4600
4836
|
}
|
|
4601
4837
|
}
|
|
4838
|
+
let mergeHadConflict = false;
|
|
4602
4839
|
try {
|
|
4603
4840
|
gitMerge(branch, mainWorktreePath);
|
|
4604
4841
|
} catch (error) {
|
|
4605
4842
|
if (hasMergeConflict(mainWorktreePath)) {
|
|
4606
|
-
|
|
4843
|
+
mergeHadConflict = true;
|
|
4844
|
+
} else {
|
|
4845
|
+
throw error;
|
|
4607
4846
|
}
|
|
4608
|
-
throw error;
|
|
4609
4847
|
}
|
|
4610
|
-
if (hasMergeConflict(mainWorktreePath)) {
|
|
4611
|
-
|
|
4848
|
+
if (!mergeHadConflict && hasMergeConflict(mainWorktreePath)) {
|
|
4849
|
+
mergeHadConflict = true;
|
|
4850
|
+
}
|
|
4851
|
+
if (mergeHadConflict) {
|
|
4852
|
+
const currentBranch = getCurrentBranch(mainWorktreePath);
|
|
4853
|
+
const resolved = await handleMergeConflict(currentBranch, branch, mainWorktreePath, options.auto);
|
|
4854
|
+
if (!resolved) {
|
|
4855
|
+
return;
|
|
4856
|
+
}
|
|
4612
4857
|
}
|
|
4613
4858
|
const autoPullPush = getConfigValue("autoPullPush");
|
|
4614
4859
|
if (autoPullPush) {
|
package/dist/postinstall.js
CHANGED
|
@@ -164,7 +164,22 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
|
164
164
|
/** merge 交互选择提示 */
|
|
165
165
|
MERGE_SELECT_BRANCH: "\u8BF7\u9009\u62E9\u8981\u5408\u5E76\u7684\u5206\u652F",
|
|
166
166
|
/** merge 模糊匹配到多个结果提示 */
|
|
167
|
-
MERGE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A
|
|
167
|
+
MERGE_MULTIPLE_MATCHES: (name) => `"${name}" \u5339\u914D\u5230\u591A\u4E2A\u5206\u652F\uFF0C\u8BF7\u9009\u62E9\uFF1A`,
|
|
168
|
+
/** 询问是否使用 AI 辅助解决冲突 */
|
|
169
|
+
MERGE_CONFLICT_ASK_AI: "\u68C0\u6D4B\u5230\u5408\u5E76\u51B2\u7A81\uFF0C\u662F\u5426\u4F7F\u7528 Claude Code \u81EA\u52A8\u89E3\u51B3\uFF1F",
|
|
170
|
+
/** AI 冲突解决开始 */
|
|
171
|
+
MERGE_CONFLICT_AI_START: (fileCount) => `\u6B63\u5728\u4F7F\u7528 Claude Code \u5206\u6790\u5E76\u89E3\u51B3 ${fileCount} \u4E2A\u51B2\u7A81\u6587\u4EF6...`,
|
|
172
|
+
/** AI 冲突解决成功 */
|
|
173
|
+
MERGE_CONFLICT_AI_SUCCESS: "\u2713 Claude Code \u5DF2\u6210\u529F\u89E3\u51B3\u6240\u6709\u51B2\u7A81",
|
|
174
|
+
/** AI 冲突解决后仍有未解决的冲突 */
|
|
175
|
+
MERGE_CONFLICT_AI_PARTIAL: (remaining) => `Claude Code \u5DF2\u5904\u7406\u51B2\u7A81\u6587\u4EF6\uFF0C\u4F46\u4ECD\u6709 ${remaining} \u4E2A\u6587\u4EF6\u5B58\u5728\u51B2\u7A81
|
|
176
|
+
\u8BF7\u624B\u52A8\u5904\u7406\u5269\u4F59\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue`,
|
|
177
|
+
/** AI 冲突解决失败 */
|
|
178
|
+
MERGE_CONFLICT_AI_FAILED: (errorMsg) => `Claude Code \u89E3\u51B3\u51B2\u7A81\u5931\u8D25: ${errorMsg}
|
|
179
|
+
\u8BF7\u624B\u52A8\u5904\u7406\uFF1A
|
|
180
|
+
\u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add . && git merge --continue`,
|
|
181
|
+
/** --auto 模式下的冲突手动解决(配置为 manual) */
|
|
182
|
+
MERGE_CONFLICT_MANUAL: "\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"
|
|
168
183
|
};
|
|
169
184
|
|
|
170
185
|
// src/constants/messages/validate.ts
|
|
@@ -226,7 +241,19 @@ ${branches.map((b) => ` - ${b}`).join("\n")}`,
|
|
|
226
241
|
VALIDATE_BRANCH_NOT_FOUND: (validateBranch, branch) => `\u9A8C\u8BC1\u5206\u652F ${validateBranch} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C clawt create \u6216 clawt run \u521B\u5EFA\u5206\u652F ${branch}`,
|
|
227
242
|
/** validate 成功(含验证分支信息) */
|
|
228
243
|
VALIDATE_SUCCESS_WITH_BRANCH: (branch, validateBranch) => `\u2713 \u5DF2\u5207\u6362\u5230\u9A8C\u8BC1\u5206\u652F ${validateBranch} \u5E76\u5E94\u7528\u5206\u652F ${branch} \u7684\u53D8\u66F4
|
|
229
|
-
\u53EF\u4EE5\u5F00\u59CB\u9A8C\u8BC1\u4E86
|
|
244
|
+
\u53EF\u4EE5\u5F00\u59CB\u9A8C\u8BC1\u4E86`,
|
|
245
|
+
/** 错误信息已复制到剪贴板提示 */
|
|
246
|
+
VALIDATE_RUN_ERROR_COPIED: "\u2702 \u9519\u8BEF\u4FE1\u606F\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F",
|
|
247
|
+
/** 剪贴板复制失败提示 */
|
|
248
|
+
VALIDATE_RUN_ERROR_COPY_FAILED: "\u26A0 \u9519\u8BEF\u4FE1\u606F\u590D\u5236\u5230\u526A\u8D34\u677F\u5931\u8D25",
|
|
249
|
+
/** 单命令(含 && 链)剪贴板错误格式 */
|
|
250
|
+
VALIDATE_CLIPBOARD_SINGLE_ERROR: (command, stderr) => `${command} \u6307\u4EE4\u6267\u884C\u51FA\u9519\uFF0C\u9519\u8BEF\u4FE1\u606F\uFF1A
|
|
251
|
+
${stderr}`,
|
|
252
|
+
/** 并行命令中单个命令的剪贴板错误格式 */
|
|
253
|
+
VALIDATE_CLIPBOARD_PARALLEL_ERROR: (command, stderr) => `${command} \u6307\u4EE4\u6267\u884C\u51FA\u9519\uFF0C\u9519\u8BEF\u4FE1\u606F\uFF1A
|
|
254
|
+
${stderr}`,
|
|
255
|
+
/** 多个错误之间的分隔符 */
|
|
256
|
+
VALIDATE_CLIPBOARD_SEPARATOR: "\n\n---\n\n"
|
|
230
257
|
};
|
|
231
258
|
|
|
232
259
|
// src/constants/messages/sync.ts
|
|
@@ -603,6 +630,15 @@ var CONFIG_DEFINITIONS = {
|
|
|
603
630
|
autoUpdate: {
|
|
604
631
|
defaultValue: true,
|
|
605
632
|
description: "\u662F\u5426\u542F\u7528\u81EA\u52A8\u66F4\u65B0\u68C0\u67E5\uFF08\u6BCF 24 \u5C0F\u65F6\u68C0\u67E5\u4E00\u6B21 npm registry\uFF09"
|
|
633
|
+
},
|
|
634
|
+
conflictResolveMode: {
|
|
635
|
+
defaultValue: "ask",
|
|
636
|
+
description: "merge \u51B2\u7A81\u65F6\u7684\u89E3\u51B3\u6A21\u5F0F\uFF1Aask\uFF08\u8BE2\u95EE\u662F\u5426\u4F7F\u7528 AI\uFF09\u3001auto\uFF08\u81EA\u52A8 AI \u89E3\u51B3\uFF09\u3001manual\uFF08\u624B\u52A8\u89E3\u51B3\uFF09",
|
|
637
|
+
allowedValues: ["ask", "auto", "manual"]
|
|
638
|
+
},
|
|
639
|
+
conflictResolveTimeoutMs: {
|
|
640
|
+
defaultValue: 3e5,
|
|
641
|
+
description: "Claude Code \u51B2\u7A81\u89E3\u51B3\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 300000\uFF085 \u5206\u949F\uFF09"
|
|
606
642
|
}
|
|
607
643
|
};
|
|
608
644
|
function deriveDefaultConfig(definitions) {
|
package/package.json
CHANGED
package/src/commands/merge.ts
CHANGED
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
gitCheckout,
|
|
34
34
|
resolveTargetWorktree,
|
|
35
35
|
getMainWorkBranch,
|
|
36
|
+
getCurrentBranch,
|
|
37
|
+
handleMergeConflict,
|
|
36
38
|
} from '../utils/index.js';
|
|
37
39
|
import type { WorktreeResolveMessages } from '../utils/index.js';
|
|
38
40
|
|
|
@@ -46,6 +48,7 @@ export function registerMergeCommand(program: Command): void {
|
|
|
46
48
|
.description('合并某个已验证的 worktree 分支到主 worktree')
|
|
47
49
|
.option('-b, --branch <branchName>', '要合并的分支名(支持模糊匹配,不传则列出所有分支供选择)')
|
|
48
50
|
.option('-m, --message <commitMessage>', '提交信息(目标 worktree 工作区有修改时必填)')
|
|
51
|
+
.option('--auto', '遇到冲突直接调用 AI 解决,不再询问')
|
|
49
52
|
.action(async (options: MergeOptions) => {
|
|
50
53
|
await handleMerge(options);
|
|
51
54
|
});
|
|
@@ -182,19 +185,31 @@ async function handleMerge(options: MergeOptions): Promise<void> {
|
|
|
182
185
|
}
|
|
183
186
|
|
|
184
187
|
// 步骤 5:回到主 worktree 进行合并
|
|
188
|
+
let mergeHadConflict = false;
|
|
185
189
|
try {
|
|
186
190
|
gitMerge(branch, mainWorktreePath);
|
|
187
191
|
} catch (error) {
|
|
188
192
|
// 检查是否有冲突
|
|
189
193
|
if (hasMergeConflict(mainWorktreePath)) {
|
|
190
|
-
|
|
194
|
+
mergeHadConflict = true;
|
|
195
|
+
} else {
|
|
196
|
+
throw error;
|
|
191
197
|
}
|
|
192
|
-
throw error;
|
|
193
198
|
}
|
|
194
199
|
|
|
195
|
-
// 步骤
|
|
196
|
-
if (hasMergeConflict(mainWorktreePath)) {
|
|
197
|
-
|
|
200
|
+
// 步骤 5.5:冲突检测(二次确认)
|
|
201
|
+
if (!mergeHadConflict && hasMergeConflict(mainWorktreePath)) {
|
|
202
|
+
mergeHadConflict = true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 步骤 5.6:如果有冲突,尝试 AI 辅助解决
|
|
206
|
+
if (mergeHadConflict) {
|
|
207
|
+
const currentBranch = getCurrentBranch(mainWorktreePath);
|
|
208
|
+
const resolved = await handleMergeConflict(currentBranch, branch, mainWorktreePath, options.auto);
|
|
209
|
+
if (!resolved) {
|
|
210
|
+
// AI 未能完全解决冲突,流程中止(handleMergeConflict 内部已输出提示)
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
198
213
|
}
|
|
199
214
|
|
|
200
215
|
// 步骤 7:根据配置决定是否自动 pull 和 push
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Claude Code 冲突解决指令性 prompt */
|
|
2
|
+
export const CONFLICT_RESOLVE_PROMPT = `你是一个 Git 合并冲突解决专家。当前仓库处于合并冲突状态。
|
|
3
|
+
|
|
4
|
+
## 任务
|
|
5
|
+
|
|
6
|
+
1. 通过 git status 和 git diff 等命令,自行查看当前仓库的冲突文件列表及冲突内容
|
|
7
|
+
2. 通过 git log 等命令,分析两个分支各自的变更意图
|
|
8
|
+
3. 直接编辑每个冲突文件,移除所有冲突标记(<<<<<<<、=======、>>>>>>>)
|
|
9
|
+
4. 保留双方有意义的变更,合理合并代码逻辑
|
|
10
|
+
5. 如果两个分支修改了同一段代码但意图不同,优先保证代码的正确性和完整性
|
|
11
|
+
6. 解决冲突后,确保代码语法正确、逻辑完整
|
|
12
|
+
7. 不要添加任何注释说明你做了什么修改,只需要修改文件内容
|
|
13
|
+
|
|
14
|
+
请直接开始。`;
|
package/src/constants/config.ts
CHANGED
|
@@ -47,6 +47,15 @@ export const CONFIG_DEFINITIONS: ConfigDefinitions = {
|
|
|
47
47
|
defaultValue: true,
|
|
48
48
|
description: '是否启用自动更新检查(每 24 小时检查一次 npm registry)',
|
|
49
49
|
},
|
|
50
|
+
conflictResolveMode: {
|
|
51
|
+
defaultValue: 'ask',
|
|
52
|
+
description: 'merge 冲突时的解决模式:ask(询问是否使用 AI)、auto(自动 AI 解决)、manual(手动解决)',
|
|
53
|
+
allowedValues: ['ask', 'auto', 'manual'] as const,
|
|
54
|
+
},
|
|
55
|
+
conflictResolveTimeoutMs: {
|
|
56
|
+
defaultValue: 300000,
|
|
57
|
+
description: 'Claude Code 冲突解决超时时间(毫秒),默认 300000(5 分钟)',
|
|
58
|
+
},
|
|
50
59
|
};
|
|
51
60
|
|
|
52
61
|
/**
|
package/src/constants/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ export {
|
|
|
31
31
|
CURSOR_HOME,
|
|
32
32
|
} from './progress.js';
|
|
33
33
|
export { SELECT_ALL_NAME, SELECT_ALL_LABEL, GROUP_SELECT_ALL_PREFIX, GROUP_SELECT_ALL_LABEL, GROUP_SEPARATOR_LABEL, UNKNOWN_DATE_GROUP, UNKNOWN_DATE_SEPARATOR_LABEL } from './prompt.js';
|
|
34
|
+
export { CONFLICT_RESOLVE_PROMPT } from './ai-prompts.js';
|
|
34
35
|
export {
|
|
35
36
|
PANEL_REFRESH_INTERVAL_MS,
|
|
36
37
|
PANEL_COUNTDOWN_INTERVAL_MS,
|
|
@@ -40,4 +40,19 @@ export const MERGE_MESSAGES = {
|
|
|
40
40
|
MERGE_SELECT_BRANCH: '请选择要合并的分支',
|
|
41
41
|
/** merge 模糊匹配到多个结果提示 */
|
|
42
42
|
MERGE_MULTIPLE_MATCHES: (name: string) => `"${name}" 匹配到多个分支,请选择:`,
|
|
43
|
+
/** 询问是否使用 AI 辅助解决冲突 */
|
|
44
|
+
MERGE_CONFLICT_ASK_AI: '检测到合并冲突,是否使用 Claude Code 自动解决?',
|
|
45
|
+
/** AI 冲突解决开始 */
|
|
46
|
+
MERGE_CONFLICT_AI_START: (fileCount: number) =>
|
|
47
|
+
`正在使用 Claude Code 分析并解决 ${fileCount} 个冲突文件...`,
|
|
48
|
+
/** AI 冲突解决成功 */
|
|
49
|
+
MERGE_CONFLICT_AI_SUCCESS: '✓ Claude Code 已成功解决所有冲突',
|
|
50
|
+
/** AI 冲突解决后仍有未解决的冲突 */
|
|
51
|
+
MERGE_CONFLICT_AI_PARTIAL: (remaining: number) =>
|
|
52
|
+
`Claude Code 已处理冲突文件,但仍有 ${remaining} 个文件存在冲突\n 请手动处理剩余冲突后执行 git add . && git merge --continue`,
|
|
53
|
+
/** AI 冲突解决失败 */
|
|
54
|
+
MERGE_CONFLICT_AI_FAILED: (errorMsg: string) =>
|
|
55
|
+
`Claude Code 解决冲突失败: ${errorMsg}\n 请手动处理:\n 解决冲突后执行 git add . && git merge --continue`,
|
|
56
|
+
/** --auto 模式下的冲突手动解决(配置为 manual) */
|
|
57
|
+
MERGE_CONFLICT_MANUAL: '合并存在冲突,请手动处理:\n 解决冲突后执行 git add . && git merge --continue',
|
|
43
58
|
} as const;
|
|
@@ -71,4 +71,16 @@ export const VALIDATE_MESSAGES = {
|
|
|
71
71
|
/** validate 成功(含验证分支信息) */
|
|
72
72
|
VALIDATE_SUCCESS_WITH_BRANCH: (branch: string, validateBranch: string) =>
|
|
73
73
|
`✓ 已切换到验证分支 ${validateBranch} 并应用分支 ${branch} 的变更\n 可以开始验证了`,
|
|
74
|
+
/** 错误信息已复制到剪贴板提示 */
|
|
75
|
+
VALIDATE_RUN_ERROR_COPIED: '✂ 错误信息已复制到剪贴板',
|
|
76
|
+
/** 剪贴板复制失败提示 */
|
|
77
|
+
VALIDATE_RUN_ERROR_COPY_FAILED: '⚠ 错误信息复制到剪贴板失败',
|
|
78
|
+
/** 单命令(含 && 链)剪贴板错误格式 */
|
|
79
|
+
VALIDATE_CLIPBOARD_SINGLE_ERROR: (command: string, stderr: string) =>
|
|
80
|
+
`${command} 指令执行出错,错误信息:\n${stderr}`,
|
|
81
|
+
/** 并行命令中单个命令的剪贴板错误格式 */
|
|
82
|
+
VALIDATE_CLIPBOARD_PARALLEL_ERROR: (command: string, stderr: string) =>
|
|
83
|
+
`${command} 指令执行出错,错误信息:\n${stderr}`,
|
|
84
|
+
/** 多个错误之间的分隔符 */
|
|
85
|
+
VALIDATE_CLIPBOARD_SEPARATOR: '\n\n---\n\n',
|
|
74
86
|
} as const;
|