@zjex/git-workflow 0.2.17 → 0.2.19
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 +636 -158
- package/dist/index.js +266 -108
- package/package.json +1 -1
- package/src/ai-service.ts +5 -44
- package/src/commands/commit.ts +26 -23
- package/src/commands/help.ts +8 -1
- package/src/commands/init.ts +99 -47
- package/src/commands/update.ts +122 -0
- package/src/config.ts +46 -11
- package/src/index.ts +8 -0
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
|
@@ -33,7 +33,14 @@ Tag 命令:
|
|
|
33
33
|
gw r 同上 (别名)
|
|
34
34
|
|
|
35
35
|
配置命令:
|
|
36
|
-
gw init 初始化配置文件
|
|
36
|
+
gw init 初始化配置文件
|
|
37
|
+
• 全局配置: ~/.gwrc.json (所有项目生效)
|
|
38
|
+
• 项目配置: .gwrc.json (仅当前项目)
|
|
39
|
+
运行时可选择配置范围
|
|
40
|
+
|
|
41
|
+
更新命令:
|
|
42
|
+
gw update 检查并更新到最新版本
|
|
43
|
+
gw upt 同上 (别名)
|
|
37
44
|
|
|
38
45
|
Stash 命令:
|
|
39
46
|
gw stash 交互式管理 stash
|
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(
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import boxen from "boxen";
|
|
4
|
+
import { colors } from "../utils.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 获取 npm 上的最新版本
|
|
8
|
+
*/
|
|
9
|
+
async function getLatestVersion(packageName: string): Promise<string | null> {
|
|
10
|
+
try {
|
|
11
|
+
const result = execSync(`npm view ${packageName} version`, {
|
|
12
|
+
encoding: "utf-8",
|
|
13
|
+
timeout: 3000,
|
|
14
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
15
|
+
});
|
|
16
|
+
return result.trim();
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 手动更新命令
|
|
24
|
+
*/
|
|
25
|
+
export async function update(currentVersion: string): Promise<void> {
|
|
26
|
+
const packageName = "@zjex/git-workflow";
|
|
27
|
+
|
|
28
|
+
console.log("");
|
|
29
|
+
console.log(colors.bold("🔍 检查更新..."));
|
|
30
|
+
console.log("");
|
|
31
|
+
|
|
32
|
+
const spinner = ora("正在获取最新版本信息...").start();
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const latestVersion = await getLatestVersion(packageName);
|
|
36
|
+
|
|
37
|
+
if (!latestVersion) {
|
|
38
|
+
spinner.fail("无法获取最新版本信息");
|
|
39
|
+
console.log(colors.dim(" 请检查网络连接后重试"));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
spinner.stop();
|
|
44
|
+
|
|
45
|
+
if (latestVersion === currentVersion) {
|
|
46
|
+
console.log(
|
|
47
|
+
boxen(
|
|
48
|
+
[
|
|
49
|
+
colors.bold("✅ 已是最新版本"),
|
|
50
|
+
"",
|
|
51
|
+
`当前版本: ${colors.green(currentVersion)}`,
|
|
52
|
+
].join("\n"),
|
|
53
|
+
{
|
|
54
|
+
padding: 1,
|
|
55
|
+
margin: { top: 0, bottom: 1, left: 2, right: 2 },
|
|
56
|
+
borderStyle: "round",
|
|
57
|
+
borderColor: "green",
|
|
58
|
+
align: "left",
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 有新版本
|
|
66
|
+
console.log(
|
|
67
|
+
boxen(
|
|
68
|
+
[
|
|
69
|
+
colors.bold("🎉 发现新版本!"),
|
|
70
|
+
"",
|
|
71
|
+
`${colors.dim(currentVersion)} → ${colors.green(
|
|
72
|
+
colors.bold(latestVersion)
|
|
73
|
+
)}`,
|
|
74
|
+
].join("\n"),
|
|
75
|
+
{
|
|
76
|
+
padding: 1,
|
|
77
|
+
margin: { top: 0, bottom: 1, left: 2, right: 2 },
|
|
78
|
+
borderStyle: "round",
|
|
79
|
+
borderColor: "yellow",
|
|
80
|
+
align: "left",
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// 开始更新
|
|
86
|
+
const updateSpinner = ora("正在更新...").start();
|
|
87
|
+
|
|
88
|
+
execSync(`npm install -g ${packageName}@latest`, {
|
|
89
|
+
encoding: "utf-8",
|
|
90
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
updateSpinner.succeed(colors.green("更新成功!"));
|
|
94
|
+
console.log("");
|
|
95
|
+
console.log(
|
|
96
|
+
boxen(
|
|
97
|
+
[
|
|
98
|
+
colors.bold("✨ 更新完成!"),
|
|
99
|
+
"",
|
|
100
|
+
colors.dim("请重新打开终端使用新版本"),
|
|
101
|
+
].join("\n"),
|
|
102
|
+
{
|
|
103
|
+
padding: 1,
|
|
104
|
+
margin: { top: 0, bottom: 1, left: 2, right: 2 },
|
|
105
|
+
borderStyle: "round",
|
|
106
|
+
borderColor: "green",
|
|
107
|
+
align: "left",
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// 更新成功后退出
|
|
113
|
+
process.exit(0);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
spinner.fail(colors.red("更新失败"));
|
|
116
|
+
console.log("");
|
|
117
|
+
console.log(colors.dim(" 你可以手动运行以下命令更新:"));
|
|
118
|
+
console.log(colors.cyan(` npm install -g ${packageName}@latest`));
|
|
119
|
+
console.log("");
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
}
|
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
|
@@ -12,6 +12,7 @@ import { stash } from "./commands/stash.js";
|
|
|
12
12
|
import { commit } from "./commands/commit.js";
|
|
13
13
|
import { showHelp } from "./commands/help.js";
|
|
14
14
|
import { checkForUpdates } from "./update-notifier.js";
|
|
15
|
+
import { update } from "./commands/update.js";
|
|
15
16
|
|
|
16
17
|
// 捕获 Ctrl+C 退出,静默处理
|
|
17
18
|
process.on("uncaughtException", (err) => {
|
|
@@ -282,6 +283,13 @@ cli
|
|
|
282
283
|
return commit();
|
|
283
284
|
});
|
|
284
285
|
|
|
286
|
+
cli
|
|
287
|
+
.command("update", "检查并更新到最新版本")
|
|
288
|
+
.alias("upt")
|
|
289
|
+
.action(async () => {
|
|
290
|
+
return update(version);
|
|
291
|
+
});
|
|
292
|
+
|
|
285
293
|
cli.help((sections) => {
|
|
286
294
|
sections.push({
|
|
287
295
|
body: showHelp(),
|