@zjex/git-workflow 0.2.16 → 0.2.18
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 +587 -158
- package/dist/index.js +168 -125
- package/package.json +1 -1
- package/scripts/publish.js +18 -5
- package/src/ai-service.ts +5 -44
- package/src/commands/commit.ts +26 -23
- package/src/commands/help.ts +4 -1
- package/src/commands/init.ts +99 -47
- package/src/config.ts +46 -11
- package/src/index.ts +11 -2
- package/src/update-notifier.ts +4 -19
package/src/ai-service.ts
CHANGED
|
@@ -17,13 +17,6 @@ const AI_PROVIDERS: Record<string, AIProvider> = {
|
|
|
17
17
|
free: true,
|
|
18
18
|
needsKey: true,
|
|
19
19
|
},
|
|
20
|
-
groq: {
|
|
21
|
-
name: "Groq",
|
|
22
|
-
endpoint: "https://api.groq.com/openai/v1/chat/completions",
|
|
23
|
-
defaultModel: "llama-3.1-8b-instant",
|
|
24
|
-
free: true,
|
|
25
|
-
needsKey: true,
|
|
26
|
-
},
|
|
27
20
|
openai: {
|
|
28
21
|
name: "OpenAI",
|
|
29
22
|
endpoint: "https://api.openai.com/v1/chat/completions",
|
|
@@ -78,7 +71,8 @@ function buildPrompt(diff: string, language: string): string {
|
|
|
78
71
|
2. type 必须是以下之一:feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
|
|
79
72
|
3. scope 是可选的,表示影响范围
|
|
80
73
|
4. subject 用中文描述,简洁明了,不超过 50 字
|
|
81
|
-
5.
|
|
74
|
+
5. 只返回一条 commit message,即使有多个文件改动也要总结成一条
|
|
75
|
+
6. 不要有其他解释或多余内容
|
|
82
76
|
|
|
83
77
|
示例:
|
|
84
78
|
- feat(auth): 添加用户登录功能
|
|
@@ -91,7 +85,8 @@ Rules:
|
|
|
91
85
|
2. type must be one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
|
|
92
86
|
3. scope is optional, indicates the affected area
|
|
93
87
|
4. subject should be concise, no more than 50 characters
|
|
94
|
-
5. Return only
|
|
88
|
+
5. Return only ONE commit message, even if multiple files are changed, summarize into one message
|
|
89
|
+
6. No explanations or extra content
|
|
95
90
|
|
|
96
91
|
Examples:
|
|
97
92
|
- feat(auth): add user login functionality
|
|
@@ -137,38 +132,6 @@ async function callGitHubAPI(
|
|
|
137
132
|
return data.choices[0]?.message?.content?.trim() || "";
|
|
138
133
|
}
|
|
139
134
|
|
|
140
|
-
/**
|
|
141
|
-
* 调用 Groq API
|
|
142
|
-
*/
|
|
143
|
-
async function callGroqAPI(
|
|
144
|
-
prompt: string,
|
|
145
|
-
apiKey: string,
|
|
146
|
-
model: string,
|
|
147
|
-
maxTokens: number
|
|
148
|
-
): Promise<string> {
|
|
149
|
-
const response = await fetch(AI_PROVIDERS.groq.endpoint, {
|
|
150
|
-
method: "POST",
|
|
151
|
-
headers: {
|
|
152
|
-
Authorization: `Bearer ${apiKey}`,
|
|
153
|
-
"Content-Type": "application/json",
|
|
154
|
-
},
|
|
155
|
-
body: JSON.stringify({
|
|
156
|
-
model,
|
|
157
|
-
messages: [{ role: "user", content: prompt }],
|
|
158
|
-
max_tokens: maxTokens,
|
|
159
|
-
temperature: 0.3,
|
|
160
|
-
}),
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
if (!response.ok) {
|
|
164
|
-
const error = await response.text();
|
|
165
|
-
throw new Error(`Groq API 错误: ${response.status} ${error}`);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const data = await response.json();
|
|
169
|
-
return data.choices[0]?.message?.content?.trim() || "";
|
|
170
|
-
}
|
|
171
|
-
|
|
172
135
|
/**
|
|
173
136
|
* 调用 OpenAI API
|
|
174
137
|
*/
|
|
@@ -277,7 +240,7 @@ export async function generateAICommitMessage(
|
|
|
277
240
|
config: GwConfig
|
|
278
241
|
): Promise<string> {
|
|
279
242
|
const aiConfig = config.aiCommit || {};
|
|
280
|
-
const provider = aiConfig.provider || "
|
|
243
|
+
const provider = aiConfig.provider || "github";
|
|
281
244
|
const language = aiConfig.language || "zh-CN";
|
|
282
245
|
const maxTokens = aiConfig.maxTokens || 200;
|
|
283
246
|
|
|
@@ -316,8 +279,6 @@ export async function generateAICommitMessage(
|
|
|
316
279
|
switch (provider) {
|
|
317
280
|
case "github":
|
|
318
281
|
return await callGitHubAPI(prompt, apiKey, model, maxTokens);
|
|
319
|
-
case "groq":
|
|
320
|
-
return await callGroqAPI(prompt, apiKey, model, maxTokens);
|
|
321
282
|
case "openai":
|
|
322
283
|
return await callOpenAIAPI(prompt, apiKey, model, maxTokens);
|
|
323
284
|
case "claude":
|
package/src/commands/commit.ts
CHANGED
|
@@ -79,22 +79,8 @@ export async function commit(): Promise<void> {
|
|
|
79
79
|
const config = getConfig();
|
|
80
80
|
let { staged, unstaged } = parseGitStatus();
|
|
81
81
|
|
|
82
|
-
//
|
|
83
|
-
if (
|
|
84
|
-
if (unstaged.length === 0) {
|
|
85
|
-
console.log(colors.yellow("工作区干净,没有需要提交的更改"));
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
console.log(colors.yellow("没有暂存的更改"));
|
|
90
|
-
divider();
|
|
91
|
-
console.log("未暂存的文件:");
|
|
92
|
-
for (const { status, file } of unstaged) {
|
|
93
|
-
console.log(` ${formatFileStatus(status)} ${file}`);
|
|
94
|
-
}
|
|
95
|
-
divider();
|
|
96
|
-
|
|
97
|
-
// 根据配置决定是否自动暂存
|
|
82
|
+
// 如果有未暂存的更改,根据配置决定是否自动暂存
|
|
83
|
+
if (unstaged.length > 0) {
|
|
98
84
|
const autoStage = config.autoStage ?? true;
|
|
99
85
|
|
|
100
86
|
if (autoStage) {
|
|
@@ -105,7 +91,17 @@ export async function commit(): Promise<void> {
|
|
|
105
91
|
// 重新获取状态
|
|
106
92
|
const newStatus = parseGitStatus();
|
|
107
93
|
staged = newStatus.staged;
|
|
108
|
-
|
|
94
|
+
unstaged = newStatus.unstaged;
|
|
95
|
+
} else if (staged.length === 0) {
|
|
96
|
+
// 没有暂存的文件,且不自动暂存,让用户选择
|
|
97
|
+
console.log(colors.yellow("没有暂存的更改"));
|
|
98
|
+
divider();
|
|
99
|
+
console.log("未暂存的文件:");
|
|
100
|
+
for (const { status, file } of unstaged) {
|
|
101
|
+
console.log(` ${formatFileStatus(status)} ${file}`);
|
|
102
|
+
}
|
|
103
|
+
divider();
|
|
104
|
+
|
|
109
105
|
// 让用户选择要暂存的文件
|
|
110
106
|
const filesToStage = await checkbox({
|
|
111
107
|
message: "选择要暂存的文件:",
|
|
@@ -133,14 +129,21 @@ export async function commit(): Promise<void> {
|
|
|
133
129
|
const newStatus = parseGitStatus();
|
|
134
130
|
staged = newStatus.staged;
|
|
135
131
|
}
|
|
136
|
-
} else {
|
|
137
|
-
console.log("已暂存的文件:");
|
|
138
|
-
for (const { status, file } of staged) {
|
|
139
|
-
console.log(` ${formatFileStatus(status)} ${file}`);
|
|
140
|
-
}
|
|
141
|
-
divider();
|
|
142
132
|
}
|
|
143
133
|
|
|
134
|
+
// 没有暂存的更改
|
|
135
|
+
if (staged.length === 0) {
|
|
136
|
+
console.log(colors.yellow("工作区干净,没有需要提交的更改"));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 显示已暂存的文件
|
|
141
|
+
console.log("已暂存的文件:");
|
|
142
|
+
for (const { status, file } of staged) {
|
|
143
|
+
console.log(` ${formatFileStatus(status)} ${file}`);
|
|
144
|
+
}
|
|
145
|
+
divider();
|
|
146
|
+
|
|
144
147
|
// 询问用户选择手动还是 AI 生成
|
|
145
148
|
const aiAvailable = isAICommitAvailable(config);
|
|
146
149
|
let commitMode: "ai" | "manual" = "manual";
|
package/src/commands/help.ts
CHANGED
package/src/commands/init.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { existsSync, writeFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
2
4
|
import { select, input } from "@inquirer/prompts";
|
|
3
5
|
import { colors, theme, divider } from "../utils.js";
|
|
4
6
|
import type { GwConfig } from "../config.js";
|
|
5
7
|
|
|
6
|
-
const CONFIG_FILE = ".gwrc.json";
|
|
7
|
-
|
|
8
8
|
// 默认的 commit emoji 配置
|
|
9
9
|
const DEFAULT_COMMIT_EMOJIS = {
|
|
10
10
|
feat: "✨",
|
|
@@ -21,9 +21,34 @@ const DEFAULT_COMMIT_EMOJIS = {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
export async function init(): Promise<void> {
|
|
24
|
-
|
|
24
|
+
console.log("");
|
|
25
|
+
console.log(colors.bold("⚙️ 初始化 git-workflow 配置"));
|
|
26
|
+
console.log("");
|
|
27
|
+
|
|
28
|
+
// 选择配置范围
|
|
29
|
+
const configScope = await select({
|
|
30
|
+
message: "选择配置范围:",
|
|
31
|
+
choices: [
|
|
32
|
+
{
|
|
33
|
+
name: "全局配置(所有项目生效)",
|
|
34
|
+
value: "global",
|
|
35
|
+
description: "保存到 ~/.gwrc.json,所有项目都会使用此配置",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "项目配置(仅当前项目)",
|
|
39
|
+
value: "project",
|
|
40
|
+
description: "保存到当前目录 .gwrc.json,仅当前项目使用",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
theme,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const isGlobal = configScope === "global";
|
|
47
|
+
const configFile = isGlobal ? join(homedir(), ".gwrc.json") : ".gwrc.json";
|
|
48
|
+
|
|
49
|
+
if (existsSync(configFile)) {
|
|
25
50
|
const overwrite = await select({
|
|
26
|
-
message: `${
|
|
51
|
+
message: `${isGlobal ? "全局" : "项目"}配置文件已存在,是否覆盖?`,
|
|
27
52
|
choices: [
|
|
28
53
|
{ name: "否,取消", value: false },
|
|
29
54
|
{ name: "是,覆盖", value: true },
|
|
@@ -55,14 +80,14 @@ export async function init(): Promise<void> {
|
|
|
55
80
|
default: "feature",
|
|
56
81
|
theme,
|
|
57
82
|
});
|
|
58
|
-
|
|
83
|
+
config.featurePrefix = featurePrefix;
|
|
59
84
|
|
|
60
85
|
const hotfixPrefix = await input({
|
|
61
86
|
message: "Hotfix 分支前缀:",
|
|
62
87
|
default: "hotfix",
|
|
63
88
|
theme,
|
|
64
89
|
});
|
|
65
|
-
|
|
90
|
+
config.hotfixPrefix = hotfixPrefix;
|
|
66
91
|
|
|
67
92
|
divider();
|
|
68
93
|
|
|
@@ -75,21 +100,21 @@ export async function init(): Promise<void> {
|
|
|
75
100
|
],
|
|
76
101
|
theme,
|
|
77
102
|
});
|
|
78
|
-
|
|
103
|
+
config.requireId = requireId;
|
|
79
104
|
|
|
80
105
|
const featureIdLabel = await input({
|
|
81
106
|
message: "Feature 分支 ID 标签:",
|
|
82
107
|
default: "Story ID",
|
|
83
108
|
theme,
|
|
84
109
|
});
|
|
85
|
-
|
|
110
|
+
config.featureIdLabel = featureIdLabel;
|
|
86
111
|
|
|
87
112
|
const hotfixIdLabel = await input({
|
|
88
113
|
message: "Hotfix 分支 ID 标签:",
|
|
89
114
|
default: "Issue ID",
|
|
90
115
|
theme,
|
|
91
116
|
});
|
|
92
|
-
|
|
117
|
+
config.hotfixIdLabel = hotfixIdLabel;
|
|
93
118
|
|
|
94
119
|
divider();
|
|
95
120
|
|
|
@@ -110,8 +135,12 @@ export async function init(): Promise<void> {
|
|
|
110
135
|
],
|
|
111
136
|
theme,
|
|
112
137
|
});
|
|
113
|
-
if (autoPushChoice === "yes")
|
|
114
|
-
|
|
138
|
+
if (autoPushChoice === "yes") {
|
|
139
|
+
config.autoPush = true;
|
|
140
|
+
} else if (autoPushChoice === "no") {
|
|
141
|
+
config.autoPush = false;
|
|
142
|
+
}
|
|
143
|
+
// autoPushChoice === "ask" 时不设置,使用默认行为(每次询问)
|
|
115
144
|
|
|
116
145
|
divider();
|
|
117
146
|
|
|
@@ -124,7 +153,7 @@ export async function init(): Promise<void> {
|
|
|
124
153
|
],
|
|
125
154
|
theme,
|
|
126
155
|
});
|
|
127
|
-
|
|
156
|
+
config.autoStage = autoStage;
|
|
128
157
|
|
|
129
158
|
const useEmoji = await select({
|
|
130
159
|
message: "Commit 时是否使用 emoji?",
|
|
@@ -134,7 +163,7 @@ export async function init(): Promise<void> {
|
|
|
134
163
|
],
|
|
135
164
|
theme,
|
|
136
165
|
});
|
|
137
|
-
|
|
166
|
+
config.useEmoji = useEmoji;
|
|
138
167
|
|
|
139
168
|
// 始终写入默认的 commitEmojis 配置,方便用户修改
|
|
140
169
|
config.commitEmojis = DEFAULT_COMMIT_EMOJIS;
|
|
@@ -164,11 +193,6 @@ export async function init(): Promise<void> {
|
|
|
164
193
|
value: "github",
|
|
165
194
|
description: "使用 GitHub 账号,每天 150 次免费",
|
|
166
195
|
},
|
|
167
|
-
{
|
|
168
|
-
name: "Groq(免费)",
|
|
169
|
-
value: "groq",
|
|
170
|
-
description: "需要注册,每天 14,400 次免费",
|
|
171
|
-
},
|
|
172
196
|
{
|
|
173
197
|
name: "OpenAI(付费)",
|
|
174
198
|
value: "openai",
|
|
@@ -188,28 +212,42 @@ export async function init(): Promise<void> {
|
|
|
188
212
|
theme,
|
|
189
213
|
});
|
|
190
214
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
215
|
+
let apiKey = "";
|
|
216
|
+
|
|
217
|
+
// GitHub Models 需要配置 GitHub Token
|
|
218
|
+
if (aiProvider === "github") {
|
|
219
|
+
console.log("");
|
|
220
|
+
console.log(colors.cyan("💡 如何获取 GitHub Token:"));
|
|
221
|
+
console.log(
|
|
222
|
+
colors.dim(" 1. 访问: https://github.com/settings/tokens/new")
|
|
223
|
+
);
|
|
224
|
+
console.log(colors.dim(" 2. 勾选 'repo' 权限"));
|
|
225
|
+
console.log(colors.dim(" 3. 生成并复制 token"));
|
|
226
|
+
console.log("");
|
|
227
|
+
|
|
228
|
+
apiKey = await input({
|
|
229
|
+
message: "输入你的 GitHub Token:",
|
|
230
|
+
validate: (value) => {
|
|
231
|
+
if (!value.trim()) return "GitHub Token 不能为空";
|
|
232
|
+
return true;
|
|
203
233
|
},
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
})
|
|
234
|
+
theme,
|
|
235
|
+
});
|
|
236
|
+
} else if (aiProvider !== "ollama") {
|
|
237
|
+
// OpenAI 和 Claude 必须配置 API key
|
|
238
|
+
console.log("");
|
|
239
|
+
if (aiProvider === "openai") {
|
|
240
|
+
console.log(colors.cyan("💡 如何获取 OpenAI API Key:"));
|
|
241
|
+
console.log(colors.dim(" 访问: https://platform.openai.com/api-keys"));
|
|
242
|
+
} else {
|
|
243
|
+
console.log(colors.cyan("💡 如何获取 Claude API Key:"));
|
|
244
|
+
console.log(colors.dim(" 访问: https://console.anthropic.com/"));
|
|
245
|
+
}
|
|
246
|
+
console.log("");
|
|
207
247
|
|
|
208
|
-
let apiKey = "";
|
|
209
|
-
if (!useBuiltinKey) {
|
|
210
248
|
apiKey = await input({
|
|
211
249
|
message: `输入你的 ${
|
|
212
|
-
aiProvider === "
|
|
250
|
+
aiProvider === "openai" ? "OpenAI API Key" : "Claude API Key"
|
|
213
251
|
}:`,
|
|
214
252
|
validate: (value) => {
|
|
215
253
|
if (!value.trim()) return "API Key 不能为空";
|
|
@@ -230,12 +268,7 @@ export async function init(): Promise<void> {
|
|
|
230
268
|
|
|
231
269
|
config.aiCommit = {
|
|
232
270
|
enabled: true,
|
|
233
|
-
provider: aiProvider as
|
|
234
|
-
| "github"
|
|
235
|
-
| "groq"
|
|
236
|
-
| "openai"
|
|
237
|
-
| "claude"
|
|
238
|
-
| "ollama",
|
|
271
|
+
provider: aiProvider as "github" | "openai" | "claude" | "ollama",
|
|
239
272
|
apiKey: apiKey || undefined,
|
|
240
273
|
language: language as "zh-CN" | "en-US",
|
|
241
274
|
};
|
|
@@ -243,7 +276,6 @@ export async function init(): Promise<void> {
|
|
|
243
276
|
// 根据提供商设置默认模型
|
|
244
277
|
const defaultModels: Record<string, string> = {
|
|
245
278
|
github: "gpt-4o-mini",
|
|
246
|
-
groq: "llama-3.1-8b-instant",
|
|
247
279
|
openai: "gpt-4o-mini",
|
|
248
280
|
claude: "claude-3-haiku-20240307",
|
|
249
281
|
ollama: "qwen2.5-coder:7b",
|
|
@@ -259,15 +291,35 @@ export async function init(): Promise<void> {
|
|
|
259
291
|
|
|
260
292
|
// 写入配置
|
|
261
293
|
const content = JSON.stringify(config, null, 2);
|
|
262
|
-
writeFileSync(
|
|
294
|
+
writeFileSync(configFile, content + "\n");
|
|
263
295
|
|
|
264
|
-
console.log(colors.green(`✓ 配置已保存到 ${CONFIG_FILE}`));
|
|
265
296
|
console.log(
|
|
266
|
-
colors.
|
|
267
|
-
|
|
297
|
+
colors.green(
|
|
298
|
+
`✓ 配置已保存到 ${
|
|
299
|
+
isGlobal ? "全局配置文件" : "项目配置文件"
|
|
300
|
+
}: ${configFile}`
|
|
268
301
|
)
|
|
269
302
|
);
|
|
270
303
|
|
|
304
|
+
if (isGlobal) {
|
|
305
|
+
console.log("");
|
|
306
|
+
console.log(colors.cyan("💡 提示:"));
|
|
307
|
+
console.log(
|
|
308
|
+
colors.dim(" • 全局配置对所有项目生效,无需在每个项目中重复配置")
|
|
309
|
+
);
|
|
310
|
+
console.log(
|
|
311
|
+
colors.dim(" • 如需为特定项目自定义配置,可在项目中运行 gw init")
|
|
312
|
+
);
|
|
313
|
+
console.log(colors.dim(" • 项目配置会覆盖全局配置"));
|
|
314
|
+
} else {
|
|
315
|
+
console.log("");
|
|
316
|
+
console.log(
|
|
317
|
+
colors.dim(
|
|
318
|
+
"提示: 可以在配置文件中修改 commitEmojis 来自定义各类型的 emoji"
|
|
319
|
+
)
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
271
323
|
if (config.aiCommit?.enabled) {
|
|
272
324
|
console.log(
|
|
273
325
|
colors.dim(
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
3
4
|
import { execOutput } from "./utils.js";
|
|
4
5
|
|
|
5
6
|
export interface GwConfig {
|
|
@@ -39,7 +40,7 @@ export interface GwConfig {
|
|
|
39
40
|
// AI commit 配置
|
|
40
41
|
aiCommit?: {
|
|
41
42
|
enabled?: boolean; // 是否启用 AI commit,默认 true
|
|
42
|
-
provider?: "github" | "
|
|
43
|
+
provider?: "github" | "openai" | "claude" | "ollama"; // AI 提供商,默认 github
|
|
43
44
|
apiKey?: string; // API key,空则使用内置 key
|
|
44
45
|
model?: string; // 模型名称
|
|
45
46
|
language?: "zh-CN" | "en-US"; // 生成语言,默认 zh-CN
|
|
@@ -89,21 +90,55 @@ function findConfigFile(): string | null {
|
|
|
89
90
|
return null;
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
function findGlobalConfigFile(): string | null {
|
|
94
|
+
const globalConfigPath = join(homedir(), ".gwrc.json");
|
|
95
|
+
return existsSync(globalConfigPath) ? globalConfigPath : null;
|
|
96
|
+
}
|
|
97
|
+
|
|
92
98
|
export function loadConfig(): GwConfig {
|
|
93
|
-
|
|
99
|
+
let config = { ...defaultConfig };
|
|
94
100
|
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
// 1. 先加载全局配置
|
|
102
|
+
const globalConfigPath = findGlobalConfigFile();
|
|
103
|
+
if (globalConfigPath) {
|
|
104
|
+
try {
|
|
105
|
+
const content = readFileSync(globalConfigPath, "utf-8");
|
|
106
|
+
const globalConfig = JSON.parse(content) as Partial<GwConfig>;
|
|
107
|
+
// 深度合并 aiCommit 配置
|
|
108
|
+
config = {
|
|
109
|
+
...config,
|
|
110
|
+
...globalConfig,
|
|
111
|
+
aiCommit: {
|
|
112
|
+
...config.aiCommit,
|
|
113
|
+
...globalConfig.aiCommit,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
} catch (e) {
|
|
117
|
+
console.warn(`全局配置文件解析失败: ${globalConfigPath}`);
|
|
118
|
+
}
|
|
97
119
|
}
|
|
98
120
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
// 2. 再加载项目配置(会覆盖全局配置)
|
|
122
|
+
const projectConfigPath = findConfigFile();
|
|
123
|
+
if (projectConfigPath) {
|
|
124
|
+
try {
|
|
125
|
+
const content = readFileSync(projectConfigPath, "utf-8");
|
|
126
|
+
const projectConfig = JSON.parse(content) as Partial<GwConfig>;
|
|
127
|
+
// 深度合并 aiCommit 配置
|
|
128
|
+
config = {
|
|
129
|
+
...config,
|
|
130
|
+
...projectConfig,
|
|
131
|
+
aiCommit: {
|
|
132
|
+
...config.aiCommit,
|
|
133
|
+
...projectConfig.aiCommit,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
} catch (e) {
|
|
137
|
+
console.warn(`项目配置文件解析失败: ${projectConfigPath}`);
|
|
138
|
+
}
|
|
106
139
|
}
|
|
140
|
+
|
|
141
|
+
return config;
|
|
107
142
|
}
|
|
108
143
|
|
|
109
144
|
// 全局配置实例
|
package/src/index.ts
CHANGED
|
@@ -66,7 +66,7 @@ async function mainMenu(): Promise<void> {
|
|
|
66
66
|
╚══════╝ ╚════╝ ╚══════╝╚═╝ ╚═╝
|
|
67
67
|
`)
|
|
68
68
|
);
|
|
69
|
-
console.log(colors.dim(` git-workflow v${version}\n`));
|
|
69
|
+
console.log(colors.dim(` git-workflow v${colors.yellow(version)}\n`));
|
|
70
70
|
|
|
71
71
|
const action = await select({
|
|
72
72
|
message: "选择操作:",
|
|
@@ -287,6 +287,15 @@ cli.help((sections) => {
|
|
|
287
287
|
body: showHelp(),
|
|
288
288
|
});
|
|
289
289
|
});
|
|
290
|
-
|
|
290
|
+
|
|
291
|
+
// 不使用 cac 的 version,手动处理 --version
|
|
292
|
+
cli.option("-v, --version", "显示版本号");
|
|
293
|
+
|
|
294
|
+
// 在 parse 之前检查 --version
|
|
295
|
+
const args = process.argv.slice(2);
|
|
296
|
+
if (args.includes("-v") || args.includes("--version")) {
|
|
297
|
+
console.log(colors.yellow(`v${version}`));
|
|
298
|
+
process.exit(0);
|
|
299
|
+
}
|
|
291
300
|
|
|
292
301
|
cli.parse();
|
package/src/update-notifier.ts
CHANGED
|
@@ -145,19 +145,8 @@ async function performUpdate(packageName: string): Promise<void> {
|
|
|
145
145
|
}).start();
|
|
146
146
|
|
|
147
147
|
try {
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
execSync(`npm uninstall -g ${packageName}`, {
|
|
151
|
-
encoding: "utf-8",
|
|
152
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
153
|
-
});
|
|
154
|
-
spinner.text = "正在安装新版本...";
|
|
155
|
-
} catch {
|
|
156
|
-
// 当前版本不存在,忽略错误
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// 执行安装命令
|
|
160
|
-
execSync(`npm install -g ${packageName}`, {
|
|
148
|
+
// 直接安装最新版本(npm 会自动覆盖旧版本)
|
|
149
|
+
execSync(`npm install -g ${packageName}@latest`, {
|
|
161
150
|
encoding: "utf-8",
|
|
162
151
|
stdio: ["pipe", "pipe", "pipe"],
|
|
163
152
|
});
|
|
@@ -169,11 +158,7 @@ async function performUpdate(packageName: string): Promise<void> {
|
|
|
169
158
|
[
|
|
170
159
|
colors.bold("✨ 更新完成!"),
|
|
171
160
|
"",
|
|
172
|
-
colors.dim("
|
|
173
|
-
"",
|
|
174
|
-
colors.yellow(" hash -r && gw --version"),
|
|
175
|
-
"",
|
|
176
|
-
colors.dim("或者重新打开终端"),
|
|
161
|
+
colors.dim("请重新打开终端使用新版本"),
|
|
177
162
|
].join("\n"),
|
|
178
163
|
{
|
|
179
164
|
padding: 1,
|
|
@@ -191,7 +176,7 @@ async function performUpdate(packageName: string): Promise<void> {
|
|
|
191
176
|
spinner.fail(colors.red("更新失败"));
|
|
192
177
|
console.log("");
|
|
193
178
|
console.log(colors.dim(" 你可以手动运行以下命令更新:"));
|
|
194
|
-
console.log(colors.cyan(` npm install -g ${packageName}`));
|
|
179
|
+
console.log(colors.cyan(` npm install -g ${packageName}@latest`));
|
|
195
180
|
console.log("");
|
|
196
181
|
}
|
|
197
182
|
}
|