zen-gitsync 2.12.2 → 2.12.3
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/LICENSE +190 -21
- package/index.js +25 -11
- package/package.json +2 -2
- package/scripts/convert-colors-to-vars.cjs +286 -272
- package/scripts/convert-fontsize-to-vars.cjs +221 -207
- package/scripts/convert-spacing-to-vars.cjs +256 -242
- package/scripts/convert-to-standard-vars.cjs +282 -268
- package/scripts/release.js +599 -585
- package/src/config.js +350 -336
- package/src/gitCommit.js +455 -440
- package/src/ui/public/assets/EditorView-CbqSI9nw.css +1 -0
- package/src/ui/public/assets/{EditorView-bnJmBq-i.js → EditorView-GS5cmh99.js} +2 -2
- package/src/ui/public/assets/SourceMapView-DyMK80hS.css +1 -0
- package/src/ui/public/assets/{SourceMapView-Rz5SD0A0.js → SourceMapView-_YRtzmZZ.js} +3 -3
- package/src/ui/public/assets/{index-bOs5P8fz.css → index-ML5Y-5lO.css} +1 -1
- package/src/ui/public/assets/{index-Bo3tntQh.js → index-yky0Sd13.js} +11 -11
- package/src/ui/public/index.html +2 -2
- package/src/ui/server/index.js +410 -396
- package/src/ui/server/middleware/requestLogger.js +51 -37
- package/src/ui/server/routes/branchStatus.js +101 -87
- package/src/ui/server/routes/code.js +110 -96
- package/src/ui/server/routes/codeAnalysis.js +995 -981
- package/src/ui/server/routes/config.js +1172 -1158
- package/src/ui/server/routes/exec.js +272 -258
- package/src/ui/server/routes/fileOpen.js +279 -265
- package/src/ui/server/routes/fs.js +701 -687
- package/src/ui/server/routes/git/diff.js +352 -338
- package/src/ui/server/routes/git/diffUtils.js +128 -114
- package/src/ui/server/routes/git/stash.js +552 -538
- package/src/ui/server/routes/git/tags.js +172 -158
- package/src/ui/server/routes/git.js +190 -176
- package/src/ui/server/routes/gitOps.js +1179 -1165
- package/src/ui/server/routes/instances.js +14 -0
- package/src/ui/server/routes/npm.js +1023 -1009
- package/src/ui/server/routes/process.js +82 -68
- package/src/ui/server/routes/status.js +67 -53
- package/src/ui/server/routes/terminal.js +319 -305
- package/src/ui/server/socket/registerUiSocketHandlers.js +226 -212
- package/src/ui/server/utils/createSavePortToFile.js +46 -32
- package/src/ui/server/utils/instanceRegistry.js +14 -0
- package/src/ui/server/utils/pathGuard.js +14 -0
- package/src/ui/server/utils/pathGuard.test.js +14 -0
- package/src/ui/server/utils/randomStartPort.js +14 -0
- package/src/ui/server/utils/startServerOnAvailablePort.js +101 -87
- package/src/utils/index.js +14 -0
- package/src/ui/public/assets/EditorView-CHBjgiZc.css +0 -1
- package/src/ui/public/assets/SourceMapView-DhQX0K7t.css +0 -1
package/src/config.js
CHANGED
|
@@ -1,336 +1,350 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
console.log(chalk.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
async function
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
list = list.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
async function
|
|
305
|
-
if (
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
1
|
+
// Copyright 2026 xz333221
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
//
|
|
15
|
+
import { promises as fs } from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import os from 'os';
|
|
18
|
+
import chalk from 'chalk';
|
|
19
|
+
import { execSync } from 'child_process';
|
|
20
|
+
|
|
21
|
+
const configPath = path.join(os.homedir(), '.git-commit-tool.json');
|
|
22
|
+
|
|
23
|
+
// 默认配置
|
|
24
|
+
const defaultConfig = {
|
|
25
|
+
defaultCommitMessage: "submit",
|
|
26
|
+
descriptionTemplates: [], // 添加描述模板数组
|
|
27
|
+
scopeTemplates: [],
|
|
28
|
+
messageTemplates: [],
|
|
29
|
+
commandTemplates: [
|
|
30
|
+
'echo "{{cmd}}"',
|
|
31
|
+
'npm run dev',
|
|
32
|
+
'npm run build',
|
|
33
|
+
'git status',
|
|
34
|
+
'git add .',
|
|
35
|
+
'git commit -m "{{message}}" --no-verify',
|
|
36
|
+
'git push',
|
|
37
|
+
],
|
|
38
|
+
lockedFiles: [], // 添加锁定文件数组
|
|
39
|
+
customCommands: [], // 添加自定义命令数组
|
|
40
|
+
orchestrations: [],
|
|
41
|
+
startupItems: [], // 添加项目启动项数组
|
|
42
|
+
startupAutoRun: false, // 添加启动项自动执行开关
|
|
43
|
+
afterQuickPushAction: {
|
|
44
|
+
enabled: false,
|
|
45
|
+
type: 'command',
|
|
46
|
+
refId: ''
|
|
47
|
+
},
|
|
48
|
+
currentDirectory: '',
|
|
49
|
+
// 提交设置
|
|
50
|
+
isStandardCommit: true,
|
|
51
|
+
skipHooks: false,
|
|
52
|
+
autoQuickPushOnEnter: false,
|
|
53
|
+
autoSetDefaultMessage: false,
|
|
54
|
+
autoClosePushModal: false,
|
|
55
|
+
pullBeforePush: true,
|
|
56
|
+
// 通用设置
|
|
57
|
+
theme: 'light', // 主题: light | dark | auto
|
|
58
|
+
locale: 'zh-CN', // 语言: zh-CN | en-US
|
|
59
|
+
// AI 模型配置
|
|
60
|
+
models: [],
|
|
61
|
+
// UI 状态(跨项目共享,存到顶层 ui 对象)
|
|
62
|
+
// 之前散落在 localStorage,因随机端口启动而失效,迁到文件持久化
|
|
63
|
+
ui: {
|
|
64
|
+
layout: { leftRatio: 0.25, midRatio: 0.375, rightRatio: 0.375, topRatio: 0.5 },
|
|
65
|
+
fileListViewMode: 'list', // 'list' | 'tree'
|
|
66
|
+
fileDiffSplitPercent: 35, // 15-85
|
|
67
|
+
commandConsole: {
|
|
68
|
+
expanded: true,
|
|
69
|
+
useTerminal: true,
|
|
70
|
+
showTerminalSessions: true,
|
|
71
|
+
splitPercent: 25, // 15-85
|
|
72
|
+
},
|
|
73
|
+
editorAutoSave: false,
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// 规范化项目路径作为配置键
|
|
78
|
+
function normalizeProjectPath(p) {
|
|
79
|
+
const resolved = path.resolve(p);
|
|
80
|
+
return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 获取当前项目的唯一键(优先使用 Git 根目录)
|
|
84
|
+
function getCurrentProjectKey() {
|
|
85
|
+
try {
|
|
86
|
+
const gitRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
87
|
+
if (gitRoot) return normalizeProjectPath(gitRoot);
|
|
88
|
+
} catch (_) {
|
|
89
|
+
// 非 Git 项目或 git 不可用,降级到 CWD
|
|
90
|
+
}
|
|
91
|
+
return normalizeProjectPath(process.cwd());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 从磁盘读取原始配置对象
|
|
95
|
+
async function readRawConfigFile() {
|
|
96
|
+
const state = await safeLoadRaw();
|
|
97
|
+
if (!state.ok) {
|
|
98
|
+
throw state.error;
|
|
99
|
+
}
|
|
100
|
+
return state.obj;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 将原始配置对象写回磁盘
|
|
104
|
+
async function writeRawConfigFile(obj) {
|
|
105
|
+
await fs.writeFile(configPath, JSON.stringify(obj, null, 2), 'utf-8');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function backupConfigFileIfExists() {
|
|
109
|
+
try {
|
|
110
|
+
await fs.access(configPath);
|
|
111
|
+
} catch (_) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
await fs.copyFile(configPath, `${configPath}.bak`);
|
|
117
|
+
} catch (_) {
|
|
118
|
+
// 备份失败不阻断主流程
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 更安全的读取,区分“文件不存在”和“解析失败”
|
|
123
|
+
async function safeLoadRaw() {
|
|
124
|
+
try {
|
|
125
|
+
const data = await fs.readFile(configPath, 'utf-8');
|
|
126
|
+
return { ok: true, obj: JSON.parse(data), existed: true };
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (err && err.code === 'ENOENT') {
|
|
129
|
+
// 文件不存在:当作空对象,但可继续写入
|
|
130
|
+
return { ok: true, obj: {}, existed: false };
|
|
131
|
+
}
|
|
132
|
+
// 其他错误(例如解析失败):不写入,提示用户
|
|
133
|
+
return { ok: false, error: err };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 异步读取配置文件
|
|
138
|
+
async function loadConfig() {
|
|
139
|
+
const key = getCurrentProjectKey();
|
|
140
|
+
let raw = null;
|
|
141
|
+
try {
|
|
142
|
+
raw = await readRawConfigFile();
|
|
143
|
+
} catch (err) {
|
|
144
|
+
const msg = err?.message ? String(err.message) : String(err);
|
|
145
|
+
throw new Error(`系统配置文件JSON格式错误,请修复后重试。\n文件: ${configPath}\n原因: ${msg}`);
|
|
146
|
+
}
|
|
147
|
+
// 兼容旧版(全局扁平结构)
|
|
148
|
+
if (raw && !raw.projects) {
|
|
149
|
+
return { ...defaultConfig, ...raw };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 新版结构:{ projects: { [key]: projectConfig }, theme?, locale?, ui?, recentDirectories? }
|
|
153
|
+
const projectConfig = raw?.projects?.[key];
|
|
154
|
+
// 合并:默认配置 + 项目配置 + 全局通用设置(theme, locale, models, ui)
|
|
155
|
+
// models / ui 是全局配置(跨项目共享),始终取顶层,不使用项目级的(防止旧数据覆盖)
|
|
156
|
+
return {
|
|
157
|
+
...defaultConfig,
|
|
158
|
+
...(projectConfig || {}),
|
|
159
|
+
theme: raw?.theme ?? defaultConfig.theme,
|
|
160
|
+
locale: raw?.locale ?? defaultConfig.locale,
|
|
161
|
+
models: raw?.models ?? defaultConfig.models,
|
|
162
|
+
ui: raw?.ui ?? defaultConfig.ui
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 异步保存配置
|
|
167
|
+
async function saveConfig(config) {
|
|
168
|
+
if(!config || typeof config !== 'object' || Array.isArray(config)){
|
|
169
|
+
console.warn(chalk.yellow('⚠️ 配置文件为空,已取消写入以避免覆盖。'));
|
|
170
|
+
console.warn(chalk.gray(`文件: ${configPath}`));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (Object.keys(config).length === 0) {
|
|
174
|
+
console.warn(chalk.yellow('⚠️ 配置文件为空,已取消写入以避免覆盖。'));
|
|
175
|
+
console.warn(chalk.gray(`文件: ${configPath}`));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const key = getCurrentProjectKey();
|
|
179
|
+
const state = await safeLoadRaw();
|
|
180
|
+
if (!state.ok) {
|
|
181
|
+
console.warn(chalk.yellow('⚠️ 解析配置文件失败,已取消写入以避免覆盖。'));
|
|
182
|
+
console.warn(chalk.gray(`文件: ${configPath}`));
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const raw = state.obj; // 保留现有顶层键
|
|
187
|
+
|
|
188
|
+
// 确保 projects 容器存在
|
|
189
|
+
if (!raw.projects || typeof raw.projects !== 'object') {
|
|
190
|
+
raw.projects = {};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 分离全局设置和项目设置
|
|
194
|
+
// models / ui 也是全局配置(跨项目共享),和 theme/locale 一样存到顶层
|
|
195
|
+
const { theme, locale, models, ui, ...projectConfig } = config;
|
|
196
|
+
|
|
197
|
+
// 保存全局设置到根级别
|
|
198
|
+
if (theme !== undefined) {
|
|
199
|
+
raw.theme = theme;
|
|
200
|
+
}
|
|
201
|
+
if (locale !== undefined) {
|
|
202
|
+
raw.locale = locale;
|
|
203
|
+
}
|
|
204
|
+
if (models !== undefined) {
|
|
205
|
+
raw.models = models;
|
|
206
|
+
}
|
|
207
|
+
if (ui !== undefined) {
|
|
208
|
+
raw.ui = ui;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 写入当前项目配置(在 defaultConfig 基础上合并,但不清空顶层其它键)
|
|
212
|
+
const existingProjectConfig = (raw.projects[key] && typeof raw.projects[key] === 'object') ? raw.projects[key] : {};
|
|
213
|
+
raw.projects[key] = { ...defaultConfig, ...existingProjectConfig, ...projectConfig };
|
|
214
|
+
await backupConfigFileIfExists();
|
|
215
|
+
await writeRawConfigFile(raw);
|
|
216
|
+
}
|
|
217
|
+
// 文件锁定管理函数
|
|
218
|
+
async function lockFile(filePath) {
|
|
219
|
+
const config = await loadConfig();
|
|
220
|
+
const normalizedPath = path.normalize(filePath);
|
|
221
|
+
|
|
222
|
+
if (!config.lockedFiles.includes(normalizedPath)) {
|
|
223
|
+
config.lockedFiles.push(normalizedPath);
|
|
224
|
+
await saveConfig(config);
|
|
225
|
+
console.log(chalk.green(`✓ 文件已锁定: "${normalizedPath}"`));
|
|
226
|
+
return true;
|
|
227
|
+
} else {
|
|
228
|
+
console.log(chalk.yellow(`⚠️ 文件已经被锁定: "${normalizedPath}"`));
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async function unlockFile(filePath) {
|
|
234
|
+
const config = await loadConfig();
|
|
235
|
+
const normalizedPath = path.normalize(filePath);
|
|
236
|
+
const index = config.lockedFiles.indexOf(normalizedPath);
|
|
237
|
+
|
|
238
|
+
if (index > -1) {
|
|
239
|
+
config.lockedFiles.splice(index, 1);
|
|
240
|
+
await saveConfig(config);
|
|
241
|
+
console.log(chalk.green(`✓ 文件已解锁: "${normalizedPath}"`));
|
|
242
|
+
return true;
|
|
243
|
+
} else {
|
|
244
|
+
console.log(chalk.yellow(`⚠️ 文件未被锁定: "${normalizedPath}"`));
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function isFileLocked(filePath) {
|
|
250
|
+
const config = await loadConfig();
|
|
251
|
+
const normalizedPath = path.normalize(filePath);
|
|
252
|
+
return config.lockedFiles.includes(normalizedPath);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function listLockedFiles() {
|
|
256
|
+
const config = await loadConfig();
|
|
257
|
+
if (config.lockedFiles.length === 0) {
|
|
258
|
+
console.log(chalk.blue('📝 当前没有锁定的文件'));
|
|
259
|
+
} else {
|
|
260
|
+
console.log(chalk.blue('🔒 已锁定的文件:'));
|
|
261
|
+
config.lockedFiles.forEach((file, index) => {
|
|
262
|
+
console.log(chalk.gray(` ${index + 1}. ${file}`));
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
return config.lockedFiles;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function getLockedFiles() {
|
|
269
|
+
const config = await loadConfig();
|
|
270
|
+
return config.lockedFiles || [];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 全局“最近访问的目录”管理(保存在原始配置的顶层)
|
|
274
|
+
const MAX_RECENT_DIRS = 12;
|
|
275
|
+
|
|
276
|
+
async function getRecentDirectories() {
|
|
277
|
+
const raw = await readRawConfigFile();
|
|
278
|
+
const list = raw?.recentDirectories;
|
|
279
|
+
return Array.isArray(list) ? list : [];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function saveRecentDirectory(dirPath) {
|
|
283
|
+
if (!dirPath || typeof dirPath !== 'string') return;
|
|
284
|
+
const state = await safeLoadRaw();
|
|
285
|
+
if (!state.ok) {
|
|
286
|
+
console.warn(chalk.yellow('⚠️ 解析配置文件失败,已取消写入最近目录以避免覆盖。'));
|
|
287
|
+
console.warn(chalk.gray(`文件: ${configPath}`));
|
|
288
|
+
return [];
|
|
289
|
+
}
|
|
290
|
+
const raw = state.obj; // 保留现有顶层键
|
|
291
|
+
let list = Array.isArray(raw.recentDirectories) ? raw.recentDirectories.slice() : [];
|
|
292
|
+
|
|
293
|
+
// 规范化:Windows 下统一为小写,去掉重复,保持最新在前
|
|
294
|
+
const normalized = normalizeProjectPath(dirPath);
|
|
295
|
+
list = list.filter(p => normalizeProjectPath(p) !== normalized);
|
|
296
|
+
list.unshift(dirPath);
|
|
297
|
+
if (list.length > MAX_RECENT_DIRS) list = list.slice(0, MAX_RECENT_DIRS);
|
|
298
|
+
|
|
299
|
+
raw.recentDirectories = list;
|
|
300
|
+
await writeRawConfigFile(raw);
|
|
301
|
+
return list;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function removeRecentDirectory(dirPath) {
|
|
305
|
+
if (!dirPath || typeof dirPath !== 'string') return;
|
|
306
|
+
const state = await safeLoadRaw();
|
|
307
|
+
if (!state.ok) return [];
|
|
308
|
+
const raw = state.obj;
|
|
309
|
+
let list = Array.isArray(raw.recentDirectories) ? raw.recentDirectories.slice() : [];
|
|
310
|
+
const normalized = normalizeProjectPath(dirPath);
|
|
311
|
+
list = list.filter(p => normalizeProjectPath(p) !== normalized);
|
|
312
|
+
raw.recentDirectories = list;
|
|
313
|
+
await writeRawConfigFile(raw);
|
|
314
|
+
return list;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 添加配置管理函数
|
|
318
|
+
async function handleConfigCommands() {
|
|
319
|
+
if (process.argv.includes('get-config')) {
|
|
320
|
+
const currentConfig = await loadConfig();
|
|
321
|
+
console.log('Current configuration:');
|
|
322
|
+
console.log(currentConfig);
|
|
323
|
+
process.exit();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const setMsgArg = process.argv.find(arg => arg.startsWith('--set-default-message='));
|
|
327
|
+
if (setMsgArg) {
|
|
328
|
+
const newMessage = setMsgArg.split('=')[1];
|
|
329
|
+
const currentConfig = await loadConfig();
|
|
330
|
+
currentConfig.defaultCommitMessage = newMessage;
|
|
331
|
+
await saveConfig(currentConfig);
|
|
332
|
+
console.log(chalk.green(`✓ 默认提交信息已设置为: "${newMessage}"`));
|
|
333
|
+
process.exit();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
export default {
|
|
337
|
+
loadConfig,
|
|
338
|
+
saveConfig,
|
|
339
|
+
handleConfigCommands,
|
|
340
|
+
lockFile,
|
|
341
|
+
unlockFile,
|
|
342
|
+
isFileLocked,
|
|
343
|
+
listLockedFiles,
|
|
344
|
+
getLockedFiles,
|
|
345
|
+
getRecentDirectories,
|
|
346
|
+
saveRecentDirectory,
|
|
347
|
+
removeRecentDirectory,
|
|
348
|
+
readRawConfigFile,
|
|
349
|
+
writeRawConfigFile
|
|
350
|
+
};
|