@unifan/pi-commit-zh 1.0.9 → 1.0.11
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/index.ts +36 -47
- package/package.json +1 -1
- package/src/git.ts +33 -2
package/index.ts
CHANGED
|
@@ -3,37 +3,33 @@ import {
|
|
|
3
3
|
commitWithMsg,
|
|
4
4
|
getChangedFiles,
|
|
5
5
|
getStagedDiff,
|
|
6
|
+
getUnpushedCommits,
|
|
6
7
|
getUnstagedDiff,
|
|
7
8
|
isGitRepo,
|
|
8
9
|
pushCurrentBranch,
|
|
9
10
|
stageAll,
|
|
10
11
|
} from "./src/git.js";
|
|
11
|
-
import { COMMIT_SYSTEM_PROMPT } from "./src/prompt.
|
|
12
|
+
import { COMMIT_SYSTEM_PROMPT } from "./src/prompt.js";
|
|
12
13
|
|
|
13
14
|
interface ParsedCommitArgs {
|
|
14
|
-
yes: boolean;
|
|
15
15
|
stageAll: boolean;
|
|
16
16
|
hint?: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function parseArgs(raw: string): ParsedCommitArgs {
|
|
20
20
|
const tokens = raw.trim().split(/\s+/).filter(Boolean);
|
|
21
|
-
let yes = false;
|
|
22
21
|
let shouldStageAll = false;
|
|
23
22
|
const hintParts: string[] = [];
|
|
24
23
|
|
|
25
24
|
for (const t of tokens) {
|
|
26
|
-
if (t === "-
|
|
27
|
-
yes = true;
|
|
28
|
-
} else if (t === "-a" || t === "--all") {
|
|
25
|
+
if (t === "-a" || t === "--all") {
|
|
29
26
|
shouldStageAll = true;
|
|
30
|
-
} else {
|
|
27
|
+
} else if (t !== "-y" && t !== "--yes") {
|
|
31
28
|
hintParts.push(t);
|
|
32
29
|
}
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
return {
|
|
36
|
-
yes,
|
|
37
33
|
stageAll: shouldStageAll,
|
|
38
34
|
hint: hintParts.join(" ").trim() || undefined,
|
|
39
35
|
};
|
|
@@ -63,6 +59,7 @@ async function generateCommitMessage(
|
|
|
63
59
|
try {
|
|
64
60
|
const provider = ctx.modelRegistry.getProvider(ctx.model.provider);
|
|
65
61
|
if (provider) {
|
|
62
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
66
63
|
const response = await provider
|
|
67
64
|
.streamSimple(
|
|
68
65
|
ctx.model,
|
|
@@ -76,7 +73,7 @@ async function generateCommitMessage(
|
|
|
76
73
|
},
|
|
77
74
|
],
|
|
78
75
|
},
|
|
79
|
-
{ maxTokens: 800 },
|
|
76
|
+
{ apiKey: auth?.apiKey, headers: auth?.headers, maxTokens: 800 },
|
|
80
77
|
)
|
|
81
78
|
.result();
|
|
82
79
|
|
|
@@ -86,16 +83,15 @@ async function generateCommitMessage(
|
|
|
86
83
|
.trim();
|
|
87
84
|
|
|
88
85
|
if (text) {
|
|
89
|
-
// Clean any unexpected code blocks
|
|
90
86
|
return text.replace(/^```[a-zA-Z]*\n?/, "").replace(/\n?```$/, "").trim();
|
|
91
87
|
}
|
|
92
88
|
}
|
|
93
|
-
} catch {
|
|
94
|
-
|
|
89
|
+
} catch (err) {
|
|
90
|
+
console.error("pi-commit streamSimple error:", err);
|
|
95
91
|
}
|
|
96
92
|
}
|
|
97
93
|
|
|
98
|
-
//
|
|
94
|
+
// 智能保底推断
|
|
99
95
|
const firstFile = changedFiles[0] ?? "core";
|
|
100
96
|
const scope = firstFile.split(/[/\\]/)[0] || "core";
|
|
101
97
|
return `chore(${scope}): 更新代码与相关配置\n\n- 更新了 ${changedFiles.length} 个文件`;
|
|
@@ -119,7 +115,28 @@ export default function (pi: ExtensionAPI) {
|
|
|
119
115
|
const changedFiles = await getChangedFiles(ctx.cwd);
|
|
120
116
|
|
|
121
117
|
if (!stagedDiff && !unstagedDiff) {
|
|
122
|
-
|
|
118
|
+
if (andPush) {
|
|
119
|
+
const unpushed = await getUnpushedCommits(ctx.cwd);
|
|
120
|
+
if (unpushed.length > 0) {
|
|
121
|
+
notify(`当前工作区干净,但检测到有 ${unpushed.length} 个未推送提交,正在推送到远端...`, "info");
|
|
122
|
+
const pushRes = await pushCurrentBranch(ctx.cwd, (msg) => notify(msg, "info"));
|
|
123
|
+
if (pushRes.ok) {
|
|
124
|
+
const successMsg = pushRes.autoRebased
|
|
125
|
+
? `🚀 已自动完成变基 (pull --rebase),并成功将 ${unpushed.length} 个本地提交推送至远端!`
|
|
126
|
+
: `🚀 成功将本地未推送的 ${unpushed.length} 个提交推送至远端!`;
|
|
127
|
+
notify(successMsg, "info");
|
|
128
|
+
pi.sendMessage({
|
|
129
|
+
customType: "pi-commit-result",
|
|
130
|
+
content: `### 🚀 远端推送完成\n\n当前工作区无未提交的修改,已成功将本地 **${unpushed.length} 个历史提交** 推送至远端分支:\n\n\`\`\`text\n${unpushed.join("\n")}\n\`\`\`\n\n${successMsg}`,
|
|
131
|
+
display: true,
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
notify(`⚠️ 推送失败: ${pushRes.output}`, "error");
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
notify("当前工作区干净,且所有本地提交均已同步至远端,无需提交和推送。", "info");
|
|
123
140
|
return;
|
|
124
141
|
}
|
|
125
142
|
|
|
@@ -135,40 +152,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
135
152
|
notify("正在深度分析代码改动并生成中文 Commit Message...", "info");
|
|
136
153
|
const commitMessage = await generateCommitMessage(ctx, stagedDiff || unstagedDiff, changedFiles, parsed.hint);
|
|
137
154
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (!parsed.yes && ctx.hasUI) {
|
|
141
|
-
const choice = await ctx.ui.select(
|
|
142
|
-
`✨ AI 生成的提交信息:\n\n${commitMessage}\n\n请选择操作:`,
|
|
143
|
-
[
|
|
144
|
-
{ label: "✅ 立即以此信息提交 (Commit)", value: "commit" },
|
|
145
|
-
{ label: "📝 编辑后再提交 (Edit & Commit)", value: "edit" },
|
|
146
|
-
{ label: "❌ 取消提交 (Cancel)", value: "cancel" },
|
|
147
|
-
],
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
if (!choice || choice === "cancel") {
|
|
151
|
-
notify("已取消本次提交。", "info");
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (choice === "edit") {
|
|
156
|
-
const edited = await ctx.ui.editor("编辑 Commit Message", commitMessage);
|
|
157
|
-
if (!edited || !edited.trim()) {
|
|
158
|
-
notify("提交信息为空,已取消提交。", "warning");
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
finalMessage = edited.trim();
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const commitRes = await commitWithMsg(ctx.cwd, finalMessage);
|
|
155
|
+
const commitRes = await commitWithMsg(ctx.cwd, commitMessage);
|
|
166
156
|
if (!commitRes.ok) {
|
|
167
157
|
notify(`Git 提交失败: ${commitRes.output}`, "error");
|
|
168
158
|
return;
|
|
169
159
|
}
|
|
170
160
|
|
|
171
|
-
const firstLine =
|
|
161
|
+
const firstLine = commitMessage.split("\n")[0];
|
|
172
162
|
notify(`✅ 成功提交: ${firstLine}`, "info");
|
|
173
163
|
|
|
174
164
|
let pushText = "";
|
|
@@ -189,16 +179,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
189
179
|
|
|
190
180
|
pi.sendMessage({
|
|
191
181
|
customType: "pi-commit-result",
|
|
192
|
-
content: `### 📦 Git 提交完成\n\n\`\`\`text\n${
|
|
182
|
+
content: `### 📦 Git 提交完成\n\n\`\`\`text\n${commitMessage}\n\`\`\`${pushText}`,
|
|
193
183
|
display: true,
|
|
194
184
|
});
|
|
195
185
|
};
|
|
196
186
|
|
|
197
187
|
pi.registerCommand("commit", {
|
|
198
|
-
description: "智能 Git 提交助手:自动分析 diff 生成标准中文
|
|
188
|
+
description: "智能 Git 提交助手:自动分析 diff 生成标准中文 Commit 并直接提交 (-a 自动暂存全部修改)",
|
|
199
189
|
getArgumentCompletions: (prefix: string) => {
|
|
200
190
|
const options = [
|
|
201
|
-
{ value: "-y", label: "-y / --yes", description: "直接提交无需二次确认" },
|
|
202
191
|
{ value: "-a", label: "-a / --all", description: "自动暂存全部修改 (git add -A)" },
|
|
203
192
|
];
|
|
204
193
|
const trimmed = prefix.trimStart();
|
|
@@ -211,7 +200,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
211
200
|
});
|
|
212
201
|
|
|
213
202
|
pi.registerCommand("commit-push", {
|
|
214
|
-
description: "智能 Git 提交并推流:生成标准中文 Commit
|
|
203
|
+
description: "智能 Git 提交并推流:生成标准中文 Commit 后自动提交并执行 git push (支持自动变基与未推送提交自动推流)",
|
|
215
204
|
handler: async (args, ctx) => {
|
|
216
205
|
await handleCommitCommand(args, ctx, true);
|
|
217
206
|
},
|
package/package.json
CHANGED
package/src/git.ts
CHANGED
|
@@ -45,6 +45,27 @@ export async function getChangedFiles(cwd: string): Promise<string[]> {
|
|
|
45
45
|
.filter(Boolean);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
export async function getUnpushedCommits(cwd: string): Promise<string[]> {
|
|
49
|
+
const res = await runGit(cwd, ["log", "@{u}..HEAD", "--oneline"]);
|
|
50
|
+
if (res.ok && res.stdout.trim()) {
|
|
51
|
+
return res.stdout.trim().split("\n").filter(Boolean);
|
|
52
|
+
}
|
|
53
|
+
const statusRes = await runGit(cwd, ["status", "--porcelain=v1", "-b"]);
|
|
54
|
+
if (statusRes.ok) {
|
|
55
|
+
const match = statusRes.stdout.match(/\[ahead\s+(\d+)\]/);
|
|
56
|
+
if (match && match[1]) {
|
|
57
|
+
const count = parseInt(match[1], 10);
|
|
58
|
+
if (count > 0) {
|
|
59
|
+
const logRes = await runGit(cwd, ["log", "-n", String(count), "--oneline"]);
|
|
60
|
+
if (logRes.ok && logRes.stdout.trim()) {
|
|
61
|
+
return logRes.stdout.trim().split("\n").filter(Boolean);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
|
|
48
69
|
export async function stageAll(cwd: string): Promise<boolean> {
|
|
49
70
|
const res = await runGit(cwd, ["add", "-A"]);
|
|
50
71
|
return res.ok;
|
|
@@ -59,12 +80,22 @@ export async function pushCurrentBranch(
|
|
|
59
80
|
cwd: string,
|
|
60
81
|
onLog?: (msg: string) => void,
|
|
61
82
|
): Promise<{ ok: boolean; output: string; autoRebased?: boolean }> {
|
|
62
|
-
|
|
83
|
+
let initialPush = await runGit(cwd, ["push"]);
|
|
63
84
|
if (initialPush.ok) {
|
|
64
85
|
return { ok: true, output: initialPush.stdout || "推送成功" };
|
|
65
86
|
}
|
|
66
87
|
|
|
67
|
-
|
|
88
|
+
let pushError = `${initialPush.stderr} ${initialPush.stdout}`.toLowerCase();
|
|
89
|
+
|
|
90
|
+
if (pushError.includes("no upstream branch") || pushError.includes("set-upstream")) {
|
|
91
|
+
if (onLog) onLog("未关联远端分支,正在自动关联并推送 (git push -u origin HEAD)...");
|
|
92
|
+
const setUpstream = await runGit(cwd, ["push", "-u", "origin", "HEAD"]);
|
|
93
|
+
if (setUpstream.ok) {
|
|
94
|
+
return { ok: true, output: setUpstream.stdout || "推送成功" };
|
|
95
|
+
}
|
|
96
|
+
pushError = `${setUpstream.stderr} ${setUpstream.stdout}`.toLowerCase();
|
|
97
|
+
}
|
|
98
|
+
|
|
68
99
|
const needsPull =
|
|
69
100
|
pushError.includes("fetch first") ||
|
|
70
101
|
pushError.includes("non-fast-forward") ||
|