yuangs 5.53.0 → 5.55.0
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/commands/handleAIChat.js +20 -4
- package/dist/commands/handleAIChat.js.map +1 -1
- package/dist/utils/syntaxHandler.d.ts +1 -1
- package/dist/utils/syntaxHandler.js +260 -151
- package/dist/utils/syntaxHandler.js.map +1 -1
- package/package.json +1 -1
- package/dist/core/modelRouter/policies/BalancedPolicy.d.ts +0 -8
- package/dist/core/modelRouter/policies/BalancedPolicy.js +0 -74
- package/dist/core/modelRouter/policies/CostSavingPolicy.d.ts +0 -8
- package/dist/core/modelRouter/policies/CostSavingPolicy.js +0 -35
- package/dist/core/modelRouter/policies/LatencyCriticalPolicy.d.ts +0 -8
- package/dist/core/modelRouter/policies/LatencyCriticalPolicy.js +0 -46
- package/dist/core/modelRouter/policies/QualityFirstPolicy.d.ts +0 -8
- package/dist/core/modelRouter/policies/QualityFirstPolicy.js +0 -55
- package/dist/legacy/governance/GovernanceEngine.d.ts +0 -20
- package/dist/legacy/governance/GovernanceEngine.js +0 -95
- package/dist/legacy/governance/GovernedAction.d.ts +0 -107
- package/dist/legacy/governance/GovernedAction.js +0 -9
- package/dist/legacy/governance/actions/CodeChangeAction.d.ts +0 -28
- package/dist/legacy/governance/actions/CodeChangeAction.js +0 -139
- package/dist/legacy/governance/capability/token.d.ts +0 -45
- package/dist/legacy/governance/capability/token.js +0 -113
- package/dist/legacy/governance/commands/diffEdit.d.ts +0 -2
- package/dist/legacy/governance/commands/diffEdit.js +0 -245
- package/dist/legacy/governance/execution/sandbox.d.ts +0 -12
- package/dist/legacy/governance/execution/sandbox.js +0 -76
- package/dist/legacy/governance/fsm/stateMachine.d.ts +0 -40
- package/dist/legacy/governance/fsm/stateMachine.js +0 -93
- package/dist/legacy/governance/index.d.ts +0 -9
- package/dist/legacy/governance/index.js +0 -26
- package/dist/legacy/governance/review/diffParser.d.ts +0 -12
- package/dist/legacy/governance/review/diffParser.js +0 -61
- package/dist/legacy/governance/review/render.d.ts +0 -5
- package/dist/legacy/governance/review/render.js +0 -58
- package/dist/legacy/governance/storage/store.d.ts +0 -16
- package/dist/legacy/governance/storage/store.js +0 -110
|
@@ -24,54 +24,123 @@ const CONTEXT_MAX_TOKENS = 100000;
|
|
|
24
24
|
async function handleSpecialSyntax(input, stdinData) {
|
|
25
25
|
const trimmed = input.trim();
|
|
26
26
|
// 处理 @ 文件引用语法
|
|
27
|
-
if (trimmed.startsWith(
|
|
27
|
+
if (trimmed.startsWith("@")) {
|
|
28
28
|
// 如果是 @ 开头的语法,跳转到独立的处理器
|
|
29
29
|
return await handleAtSyntax(trimmed, stdinData);
|
|
30
30
|
}
|
|
31
31
|
// 处理 # 目录引用语法
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
// 支持两种格式:
|
|
33
|
+
// 1. `#folder\n问题` (换行分隔,旧格式)
|
|
34
|
+
// 2. `#folder 问题` (空格分隔,自然输入格式)
|
|
35
|
+
if (trimmed.startsWith("#")) {
|
|
36
|
+
// 先尝试换行分隔格式(优先级更高,避免目录名含空格时误判)
|
|
37
|
+
const newlineDirMatch = trimmed.match(/^#\s*(.+?)\s*\n(.*)$/s);
|
|
38
|
+
if (newlineDirMatch) {
|
|
39
|
+
const dirPath = newlineDirMatch[1].trim();
|
|
40
|
+
const question = newlineDirMatch[2].trim() ||
|
|
41
|
+
(stdinData ? `分析以下目录内容:\n\n${stdinData}` : undefined);
|
|
42
|
+
const hasQuestion = !!question || !!stdinData;
|
|
38
43
|
const res = await handleDirectoryReference(dirPath, question);
|
|
39
44
|
return {
|
|
40
45
|
...res,
|
|
41
46
|
isPureReference: !hasQuestion,
|
|
42
|
-
type:
|
|
47
|
+
type: "directory",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// 空格分隔格式:取 # 后的内容,按第一个空白切分为「路径」和「问题」
|
|
51
|
+
// 策略:第一个空格前视为目录路径,之后视为问题
|
|
52
|
+
const afterHash = trimmed.slice(1).trim(); // 去掉 # 前缀
|
|
53
|
+
const spaceIdx = afterHash.search(/\s/); // 第一个空白符位置
|
|
54
|
+
let candidatePath;
|
|
55
|
+
let inlineQuestion;
|
|
56
|
+
if (spaceIdx === -1) {
|
|
57
|
+
// 没有空格:整体视为目录路径,无问题
|
|
58
|
+
candidatePath = afterHash;
|
|
59
|
+
inlineQuestion = undefined;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// 有空格:切分为路径 + 问题
|
|
63
|
+
candidatePath = afterHash.slice(0, spaceIdx).trim();
|
|
64
|
+
inlineQuestion = afterHash.slice(spaceIdx).trim() || undefined;
|
|
65
|
+
}
|
|
66
|
+
if (!candidatePath) {
|
|
67
|
+
return { processed: false };
|
|
68
|
+
}
|
|
69
|
+
// 检查目录路径是否真实存在于磁盘
|
|
70
|
+
const candidateFullPath = path_1.default.resolve(candidatePath);
|
|
71
|
+
let diskExists = false;
|
|
72
|
+
try {
|
|
73
|
+
diskExists = fs_1.default.statSync(candidateFullPath).isDirectory();
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
diskExists = false;
|
|
77
|
+
}
|
|
78
|
+
if (diskExists) {
|
|
79
|
+
// ✅ 目录存在:candidatePath 是合法目录,inlineQuestion 是可选问题
|
|
80
|
+
const question = inlineQuestion ||
|
|
81
|
+
(stdinData ? `分析以下目录内容:\n\n${stdinData}` : undefined);
|
|
82
|
+
const hasQuestion = !!question || !!stdinData;
|
|
83
|
+
const res = await handleDirectoryReference(candidatePath, question);
|
|
84
|
+
return {
|
|
85
|
+
...res,
|
|
86
|
+
isPureReference: !hasQuestion,
|
|
87
|
+
type: "directory",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
else if (!inlineQuestion) {
|
|
91
|
+
// 目录不存在,且无内联问题:整体直接传给 handleDirectoryReference(路径可能含空格)
|
|
92
|
+
const res = await handleDirectoryReference(candidatePath, stdinData ? `分析以下目录内容:\n\n${stdinData}` : undefined);
|
|
93
|
+
return {
|
|
94
|
+
...res,
|
|
95
|
+
isPureReference: !stdinData,
|
|
96
|
+
type: "directory",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// 目录不存在,且有内联问题:给出明确错误提示
|
|
101
|
+
return {
|
|
102
|
+
processed: true,
|
|
103
|
+
result: `错误: 目录 "${candidatePath}" 不存在或不是一个目录\n💡 提示: 使用格式 #目录路径 问题,例如: #src 解释这个文件夹的作用`,
|
|
104
|
+
error: true,
|
|
105
|
+
isPureReference: false,
|
|
106
|
+
type: "directory",
|
|
43
107
|
};
|
|
44
108
|
}
|
|
45
109
|
}
|
|
46
110
|
// 处理 :ls 命令
|
|
47
|
-
if (trimmed ===
|
|
111
|
+
if (trimmed === ":ls") {
|
|
48
112
|
const res = await handleListContext();
|
|
49
|
-
return { ...res, type:
|
|
113
|
+
return { ...res, type: "management" };
|
|
50
114
|
}
|
|
51
115
|
// 场景 5.1: :exec 原子执行
|
|
52
|
-
if (trimmed.startsWith(
|
|
116
|
+
if (trimmed.startsWith(":exec ")) {
|
|
53
117
|
const command = trimmed.slice(6).trim();
|
|
54
118
|
const res = await handleAtomicExec(command);
|
|
55
|
-
return { ...res, type:
|
|
119
|
+
return { ...res, type: "command" };
|
|
56
120
|
}
|
|
57
121
|
// 处理 :cat [index] 命令 (支持 :cat index:start-end)
|
|
58
|
-
if (trimmed ===
|
|
122
|
+
if (trimmed === ":cat" || trimmed.startsWith(":cat ")) {
|
|
59
123
|
const spec = trimmed.slice(4).trim();
|
|
60
124
|
if (!spec) {
|
|
61
125
|
const res = await handleCatContext(null);
|
|
62
|
-
return { ...res, type:
|
|
126
|
+
return { ...res, type: "management" };
|
|
63
127
|
}
|
|
64
128
|
const parsed = parseCatSpec(spec);
|
|
65
129
|
if (parsed.error) {
|
|
66
|
-
return {
|
|
130
|
+
return {
|
|
131
|
+
processed: true,
|
|
132
|
+
result: parsed.error,
|
|
133
|
+
error: true,
|
|
134
|
+
type: "management",
|
|
135
|
+
};
|
|
67
136
|
}
|
|
68
137
|
const res = await handleCatContext(parsed.index, parsed.startLine, parsed.endLine);
|
|
69
|
-
return { ...res, type:
|
|
138
|
+
return { ...res, type: "management" };
|
|
70
139
|
}
|
|
71
140
|
// 处理 :clear 命令
|
|
72
|
-
if (trimmed ===
|
|
141
|
+
if (trimmed === ":clear") {
|
|
73
142
|
const res = await handleClearContext();
|
|
74
|
-
return { ...res, type:
|
|
143
|
+
return { ...res, type: "management" };
|
|
75
144
|
}
|
|
76
145
|
// 如果不是特殊语法,返回未处理
|
|
77
146
|
return { processed: false };
|
|
@@ -86,13 +155,23 @@ function parseCatSpec(spec) {
|
|
|
86
155
|
}
|
|
87
156
|
const match = spec.match(/^(\d+)(?::(\d+)(?:-(\d+))?)?$/);
|
|
88
157
|
if (!match) {
|
|
89
|
-
return {
|
|
158
|
+
return {
|
|
159
|
+
index: null,
|
|
160
|
+
startLine: null,
|
|
161
|
+
endLine: null,
|
|
162
|
+
error: `错误: 无效的索引格式 "${spec}"。请使用 :cat index 或 :cat index:start-end (例如 :cat 1:10-20)`,
|
|
163
|
+
};
|
|
90
164
|
}
|
|
91
165
|
const index = parseInt(match[1]);
|
|
92
166
|
const startLine = match[2] ? parseInt(match[2]) : null;
|
|
93
167
|
const endLine = match[3] ? parseInt(match[3]) : null;
|
|
94
168
|
if (isNaN(index)) {
|
|
95
|
-
return {
|
|
169
|
+
return {
|
|
170
|
+
index: null,
|
|
171
|
+
startLine: null,
|
|
172
|
+
endLine: null,
|
|
173
|
+
error: `错误: 索引 "${match[1]}" 不是有效的数字`,
|
|
174
|
+
};
|
|
96
175
|
}
|
|
97
176
|
return { index, startLine, endLine };
|
|
98
177
|
}
|
|
@@ -108,9 +187,9 @@ function parseCatSpec(spec) {
|
|
|
108
187
|
function tokenizeWithQuotes(input) {
|
|
109
188
|
const tokens = [];
|
|
110
189
|
const isQuoted = [];
|
|
111
|
-
let current =
|
|
190
|
+
let current = "";
|
|
112
191
|
let inQuotes = false;
|
|
113
|
-
let quoteChar =
|
|
192
|
+
let quoteChar = "";
|
|
114
193
|
let escaped = false;
|
|
115
194
|
for (let i = 0; i < input.length; i++) {
|
|
116
195
|
const char = input[i];
|
|
@@ -119,7 +198,7 @@ function tokenizeWithQuotes(input) {
|
|
|
119
198
|
escaped = false;
|
|
120
199
|
continue;
|
|
121
200
|
}
|
|
122
|
-
if (char ===
|
|
201
|
+
if (char === "\\") {
|
|
123
202
|
escaped = true;
|
|
124
203
|
continue;
|
|
125
204
|
}
|
|
@@ -131,13 +210,13 @@ function tokenizeWithQuotes(input) {
|
|
|
131
210
|
inQuotes = false;
|
|
132
211
|
tokens.push(current);
|
|
133
212
|
isQuoted.push(true);
|
|
134
|
-
current =
|
|
213
|
+
current = "";
|
|
135
214
|
}
|
|
136
|
-
else if (!inQuotes && (char ===
|
|
215
|
+
else if (!inQuotes && (char === "," || char === "," || char === " ")) {
|
|
137
216
|
if (current) {
|
|
138
217
|
tokens.push(current.trim());
|
|
139
218
|
isQuoted.push(false);
|
|
140
|
-
current =
|
|
219
|
+
current = "";
|
|
141
220
|
}
|
|
142
221
|
}
|
|
143
222
|
else {
|
|
@@ -175,26 +254,27 @@ async function handleAtSyntax(trimmed, stdinData) {
|
|
|
175
254
|
const startLine = lineRangeMatch[2] ? parseInt(lineRangeMatch[2]) : null;
|
|
176
255
|
const endLine = lineRangeMatch[3] ? parseInt(lineRangeMatch[3]) : null;
|
|
177
256
|
const alias = lineRangeMatch[4];
|
|
178
|
-
let question = lineRangeMatch[5]?.trim() ||
|
|
257
|
+
let question = lineRangeMatch[5]?.trim() ||
|
|
258
|
+
(stdinData ? `分析以下内容:\n\n${stdinData}` : undefined);
|
|
179
259
|
const { filePaths, extraQuestion } = await resolveFilePathsAndQuestion(rawPart);
|
|
180
260
|
if (extraQuestion) {
|
|
181
261
|
question = question ? `${extraQuestion}\n\n${question}` : extraQuestion;
|
|
182
262
|
}
|
|
183
263
|
const hasQuestion = !!question || !!stdinData;
|
|
184
264
|
if (filePaths.length > 1) {
|
|
185
|
-
let warningPrefix =
|
|
265
|
+
let warningPrefix = "";
|
|
186
266
|
if (alias) {
|
|
187
|
-
warningPrefix += chalk_1.default.yellow(
|
|
267
|
+
warningPrefix += chalk_1.default.yellow("⚠️ 警告: 别名 (alias) 仅支持单个文件引用,当前多个文件引用将忽略别名。\n");
|
|
188
268
|
}
|
|
189
269
|
if (startLine !== null) {
|
|
190
|
-
warningPrefix += chalk_1.default.yellow(
|
|
270
|
+
warningPrefix += chalk_1.default.yellow("⚠️ 警告: 行号范围仅支持单个文件引用,当前多个文件引用将忽略行号范围。\n");
|
|
191
271
|
}
|
|
192
272
|
const res = await handleMultipleFileReferences(filePaths, question, !hasQuestion);
|
|
193
273
|
return {
|
|
194
274
|
...res,
|
|
195
275
|
result: warningPrefix + res.result,
|
|
196
276
|
isPureReference: !hasQuestion,
|
|
197
|
-
type:
|
|
277
|
+
type: "file",
|
|
198
278
|
};
|
|
199
279
|
}
|
|
200
280
|
else if (filePaths.length === 1) {
|
|
@@ -202,14 +282,14 @@ async function handleAtSyntax(trimmed, stdinData) {
|
|
|
202
282
|
return {
|
|
203
283
|
...res,
|
|
204
284
|
isPureReference: !hasQuestion,
|
|
205
|
-
type:
|
|
285
|
+
type: "file",
|
|
206
286
|
};
|
|
207
287
|
}
|
|
208
288
|
else {
|
|
209
289
|
return {
|
|
210
290
|
processed: true,
|
|
211
291
|
result: `错误: 未找到有效的文件或序号引用 "${rawPart}"`,
|
|
212
|
-
error: true
|
|
292
|
+
error: true,
|
|
213
293
|
};
|
|
214
294
|
}
|
|
215
295
|
}
|
|
@@ -253,7 +333,9 @@ async function resolveFilePathsAndQuestion(input) {
|
|
|
253
333
|
if (quoted)
|
|
254
334
|
continue;
|
|
255
335
|
const isRange = /^\d+-\d+$/.test(token);
|
|
256
|
-
const isIndex = !isNaN(parseInt(token)) &&
|
|
336
|
+
const isIndex = !isNaN(parseInt(token)) &&
|
|
337
|
+
parseInt(token) > 0 &&
|
|
338
|
+
parseInt(token) <= persisted.length;
|
|
257
339
|
// 【智能边界识别】即便没有空格,如果 token 开头是序号但后面跟着非数字(如 @1分析),也要切分
|
|
258
340
|
// 或者当前的 token 本身就不可识别为路径/索引
|
|
259
341
|
if (!existsOnDisk) {
|
|
@@ -278,7 +360,7 @@ async function resolveFilePathsAndQuestion(input) {
|
|
|
278
360
|
if (questionStartIndex !== -1) {
|
|
279
361
|
pathTokens = tokens.slice(0, questionStartIndex);
|
|
280
362
|
pathStats = stats.slice(0, questionStartIndex);
|
|
281
|
-
extraQuestion = tokens.slice(questionStartIndex).join(
|
|
363
|
+
extraQuestion = tokens.slice(questionStartIndex).join(" ");
|
|
282
364
|
}
|
|
283
365
|
// 4. 解析确定的路径部分
|
|
284
366
|
for (let i = 0; i < pathTokens.length; i++) {
|
|
@@ -311,7 +393,7 @@ async function resolveFilePathsAndQuestion(input) {
|
|
|
311
393
|
}
|
|
312
394
|
return {
|
|
313
395
|
filePaths: [...new Set(filePaths)],
|
|
314
|
-
extraQuestion
|
|
396
|
+
extraQuestion,
|
|
315
397
|
};
|
|
316
398
|
}
|
|
317
399
|
/**
|
|
@@ -329,7 +411,7 @@ async function handleMultipleFileReferences(filePaths, question, isPureReference
|
|
|
329
411
|
const fullPath = path_1.default.resolve(filePath);
|
|
330
412
|
try {
|
|
331
413
|
await fs_1.default.promises.access(fullPath, fs_1.default.constants.F_OK);
|
|
332
|
-
const content = await fs_1.default.promises.readFile(fullPath,
|
|
414
|
+
const content = await fs_1.default.promises.readFile(fullPath, "utf-8");
|
|
333
415
|
return { filePath, content, success: true };
|
|
334
416
|
}
|
|
335
417
|
catch (e) {
|
|
@@ -341,9 +423,9 @@ async function handleMultipleFileReferences(filePaths, question, isPureReference
|
|
|
341
423
|
if (res.success && res.content !== undefined) {
|
|
342
424
|
contentMap.set(res.filePath, res.content);
|
|
343
425
|
contextBuffer.add({
|
|
344
|
-
type:
|
|
426
|
+
type: "file",
|
|
345
427
|
path: res.filePath,
|
|
346
|
-
content: res.content
|
|
428
|
+
content: res.content,
|
|
347
429
|
});
|
|
348
430
|
addedFiles.push(res.filePath);
|
|
349
431
|
}
|
|
@@ -351,22 +433,22 @@ async function handleMultipleFileReferences(filePaths, question, isPureReference
|
|
|
351
433
|
warningList.push(`警告: 跳过 "${res.filePath}": ${res.error}`);
|
|
352
434
|
}
|
|
353
435
|
}
|
|
354
|
-
const warnings = warningList.length > 0 ? warningList.join(
|
|
436
|
+
const warnings = warningList.length > 0 ? warningList.join("\n") + "\n" : "";
|
|
355
437
|
if (addedFiles.length === 0) {
|
|
356
438
|
return {
|
|
357
439
|
processed: true,
|
|
358
|
-
result: warnings ||
|
|
359
|
-
error: true
|
|
440
|
+
result: warnings || "❌ 未找到任何有效的文件引用",
|
|
441
|
+
error: true,
|
|
360
442
|
};
|
|
361
443
|
}
|
|
362
444
|
await (0, contextStorage_1.saveContext)(contextBuffer.export());
|
|
363
445
|
if (isPureReference) {
|
|
364
446
|
return {
|
|
365
447
|
processed: true,
|
|
366
|
-
result: `${warnings}✅ 已将 ${addedFiles.length} 个文件加入上下文:\n${addedFiles.map(f => ` • ${f}`).join(
|
|
448
|
+
result: `${warnings}✅ 已将 ${addedFiles.length} 个文件加入上下文:\n${addedFiles.map((f) => ` • ${f}`).join("\n")}`,
|
|
367
449
|
};
|
|
368
450
|
}
|
|
369
|
-
const prompt = (0, fileReader_1.buildPromptWithFileContent)(`引用了 ${addedFiles.length} 个文件`, addedFiles, contentMap, question ||
|
|
451
|
+
const prompt = (0, fileReader_1.buildPromptWithFileContent)(`引用了 ${addedFiles.length} 个文件`, addedFiles, contentMap, question || "请分析以上文件");
|
|
370
452
|
return { processed: true, result: warnings + prompt };
|
|
371
453
|
}
|
|
372
454
|
async function handleFileReference(filePath, startLine = null, endLine = null, question, alias, isPureReference = false) {
|
|
@@ -375,15 +457,15 @@ async function handleFileReference(filePath, startLine = null, endLine = null, q
|
|
|
375
457
|
await fs_1.default.promises.access(fullPath, fs_1.default.constants.F_OK);
|
|
376
458
|
const stats = await fs_1.default.promises.stat(fullPath);
|
|
377
459
|
if (!stats.isFile())
|
|
378
|
-
throw new Error(
|
|
379
|
-
let content = await fs_1.default.promises.readFile(fullPath,
|
|
460
|
+
throw new Error("不是一个文件");
|
|
461
|
+
let content = await fs_1.default.promises.readFile(fullPath, "utf-8");
|
|
380
462
|
// 如果指定了行号范围,则提取相应行
|
|
381
463
|
if (startLine !== null) {
|
|
382
|
-
const lines = content.split(
|
|
464
|
+
const lines = content.split("\n");
|
|
383
465
|
if (startLine < 1 || startLine > lines.length) {
|
|
384
466
|
return {
|
|
385
467
|
processed: true,
|
|
386
|
-
result: `错误: 起始行号 ${startLine} 超出文件范围 (文件共有 ${lines.length} 行)
|
|
468
|
+
result: `错误: 起始行号 ${startLine} 超出文件范围 (文件共有 ${lines.length} 行)`,
|
|
387
469
|
};
|
|
388
470
|
}
|
|
389
471
|
const startIdx = startLine - 1;
|
|
@@ -391,10 +473,10 @@ async function handleFileReference(filePath, startLine = null, endLine = null, q
|
|
|
391
473
|
if (endLine && (endLine < startLine || endLine > lines.length)) {
|
|
392
474
|
return {
|
|
393
475
|
processed: true,
|
|
394
|
-
result: `错误: 结束行号 ${endLine} 超出有效范围 (应在 ${startLine}-${lines.length} 之间)
|
|
476
|
+
result: `错误: 结束行号 ${endLine} 超出有效范围 (应在 ${startLine}-${lines.length} 之间)`,
|
|
395
477
|
};
|
|
396
478
|
}
|
|
397
|
-
content = lines.slice(startIdx, endIdx).join(
|
|
479
|
+
content = lines.slice(startIdx, endIdx).join("\n");
|
|
398
480
|
}
|
|
399
481
|
const contentMap = new Map();
|
|
400
482
|
contentMap.set(filePath, content);
|
|
@@ -403,23 +485,26 @@ async function handleFileReference(filePath, startLine = null, endLine = null, q
|
|
|
403
485
|
const persisted = await (0, contextStorage_1.loadContext)();
|
|
404
486
|
contextBuffer.import(persisted);
|
|
405
487
|
contextBuffer.add({
|
|
406
|
-
type:
|
|
407
|
-
path: filePath +
|
|
488
|
+
type: "file",
|
|
489
|
+
path: filePath +
|
|
490
|
+
(startLine !== null
|
|
491
|
+
? `:${startLine}${endLine ? `-${endLine}` : ""}`
|
|
492
|
+
: ""),
|
|
408
493
|
content: content,
|
|
409
|
-
alias: alias
|
|
494
|
+
alias: alias,
|
|
410
495
|
});
|
|
411
496
|
await (0, contextStorage_1.saveContext)(contextBuffer.export());
|
|
412
497
|
if (isPureReference) {
|
|
413
498
|
return { processed: true, result: `✅ 已将文件 ${filePath} 加入上下文` };
|
|
414
499
|
}
|
|
415
|
-
const prompt = (0, fileReader_1.buildPromptWithFileContent)(`文件: ${filePath}${startLine !== null ? `:${startLine}${endLine ? `-${endLine}` :
|
|
500
|
+
const prompt = (0, fileReader_1.buildPromptWithFileContent)(`文件: ${filePath}${startLine !== null ? `:${startLine}${endLine ? `-${endLine}` : ""}` : ""}`, [filePath], contentMap, question || `请分析文件: ${filePath}`);
|
|
416
501
|
return { processed: true, result: prompt };
|
|
417
502
|
}
|
|
418
503
|
catch (error) {
|
|
419
504
|
return {
|
|
420
505
|
processed: true,
|
|
421
506
|
result: `错误: 无法处理文件 "${filePath}": ${error.message}`,
|
|
422
|
-
error: true
|
|
507
|
+
error: true,
|
|
423
508
|
};
|
|
424
509
|
}
|
|
425
510
|
}
|
|
@@ -428,22 +513,28 @@ async function handleDirectoryReference(dirPath, question) {
|
|
|
428
513
|
if (!fs_1.default.existsSync(fullPath) || !fs_1.default.statSync(fullPath).isDirectory()) {
|
|
429
514
|
return {
|
|
430
515
|
processed: true,
|
|
431
|
-
result: `错误: 目录 "${dirPath}"
|
|
516
|
+
result: `错误: 目录 "${dirPath}" 不存在或不是一个目录`,
|
|
432
517
|
};
|
|
433
518
|
}
|
|
434
519
|
try {
|
|
435
|
-
const findCommand = process.platform ===
|
|
520
|
+
const findCommand = process.platform === "darwin" || process.platform === "linux"
|
|
436
521
|
? `find "${fullPath}" -type f`
|
|
437
522
|
: `dir /s /b "${fullPath}"`;
|
|
438
523
|
const { stdout } = await execAsync(findCommand);
|
|
439
|
-
const filePaths = stdout
|
|
524
|
+
const filePaths = stdout
|
|
525
|
+
.trim()
|
|
526
|
+
.split("\n")
|
|
527
|
+
.filter((f) => f);
|
|
440
528
|
if (filePaths.length === 0) {
|
|
441
529
|
return {
|
|
442
530
|
processed: true,
|
|
443
|
-
result: `目录 "${dirPath}"
|
|
531
|
+
result: `目录 "${dirPath}" 下没有文件`,
|
|
444
532
|
};
|
|
445
533
|
}
|
|
446
|
-
const contentMap = await (0, fileReader_1.readFilesContent)(filePaths, {
|
|
534
|
+
const contentMap = await (0, fileReader_1.readFilesContent)(filePaths, {
|
|
535
|
+
showProgress: true,
|
|
536
|
+
concurrency: 5,
|
|
537
|
+
});
|
|
447
538
|
// 持久化到上下文
|
|
448
539
|
const contextBuffer = new contextBuffer_1.ContextBuffer();
|
|
449
540
|
const persisted = await (0, contextStorage_1.loadContext)();
|
|
@@ -458,9 +549,9 @@ async function handleDirectoryReference(dirPath, question) {
|
|
|
458
549
|
continue;
|
|
459
550
|
}
|
|
460
551
|
contextBuffer.add({
|
|
461
|
-
type:
|
|
552
|
+
type: "file",
|
|
462
553
|
path: filePath,
|
|
463
|
-
content: content
|
|
554
|
+
content: content,
|
|
464
555
|
});
|
|
465
556
|
successfullyAddedCount++;
|
|
466
557
|
}
|
|
@@ -468,21 +559,21 @@ async function handleDirectoryReference(dirPath, question) {
|
|
|
468
559
|
return {
|
|
469
560
|
processed: true,
|
|
470
561
|
result: `错误: 目录 "${dirPath}" 中的文件都太大,无法加入上下文`,
|
|
471
|
-
error: true
|
|
562
|
+
error: true,
|
|
472
563
|
};
|
|
473
564
|
}
|
|
474
565
|
await (0, contextStorage_1.saveContext)(contextBuffer.export());
|
|
475
566
|
return {
|
|
476
567
|
processed: true,
|
|
477
568
|
result: `已成功加入 ${successfullyAddedCount} 个文件到上下文 (共找到 ${filePaths.length} 个文件)`,
|
|
478
|
-
itemCount: successfullyAddedCount
|
|
569
|
+
itemCount: successfullyAddedCount,
|
|
479
570
|
};
|
|
480
571
|
}
|
|
481
572
|
catch (error) {
|
|
482
573
|
return {
|
|
483
574
|
processed: true,
|
|
484
575
|
result: `错误: 读取目录失败: ${error}`,
|
|
485
|
-
error: true
|
|
576
|
+
error: true,
|
|
486
577
|
};
|
|
487
578
|
}
|
|
488
579
|
}
|
|
@@ -491,12 +582,12 @@ async function handleImmediateExec(filePath) {
|
|
|
491
582
|
if (!fs_1.default.existsSync(fullPath)) {
|
|
492
583
|
return {
|
|
493
584
|
processed: true,
|
|
494
|
-
result: `错误: 文件 "${filePath}"
|
|
585
|
+
result: `错误: 文件 "${filePath}" 不存在`,
|
|
495
586
|
};
|
|
496
587
|
}
|
|
497
588
|
try {
|
|
498
589
|
// 1. 读取脚本内容
|
|
499
|
-
const content = fs_1.default.readFileSync(fullPath,
|
|
590
|
+
const content = fs_1.default.readFileSync(fullPath, "utf-8");
|
|
500
591
|
console.log(chalk_1.default.gray(`正在执行 ${filePath} 并捕获输出...`));
|
|
501
592
|
// 2. 执行脚本
|
|
502
593
|
// 注意:这里使用 execAsync 捕获输出
|
|
@@ -523,10 +614,10 @@ ${stderr}
|
|
|
523
614
|
const persisted = await (0, contextStorage_1.loadContext)();
|
|
524
615
|
contextBuffer.import(persisted);
|
|
525
616
|
contextBuffer.add({
|
|
526
|
-
type:
|
|
617
|
+
type: "file",
|
|
527
618
|
path: `${filePath} (Runtime Log)`,
|
|
528
619
|
content: combinedContext,
|
|
529
|
-
summary:
|
|
620
|
+
summary: "包含脚本源码和执行后的输出日志",
|
|
530
621
|
});
|
|
531
622
|
await (0, contextStorage_1.saveContext)(contextBuffer.export());
|
|
532
623
|
// 返回给 AI 的 Prompt
|
|
@@ -543,26 +634,26 @@ async function handleAtomicExec(command) {
|
|
|
543
634
|
console.log(chalk_1.default.cyan(`\n⚡️ [Atomic Exec] 执行命令: ${command}\n`));
|
|
544
635
|
try {
|
|
545
636
|
// 对于原子执行,我们希望用户能实时看到输出,所以用 inherit
|
|
546
|
-
const { spawn } = require(
|
|
637
|
+
const { spawn } = require("child_process");
|
|
547
638
|
const child = spawn(command, {
|
|
548
639
|
shell: true,
|
|
549
|
-
stdio:
|
|
640
|
+
stdio: "inherit",
|
|
550
641
|
});
|
|
551
642
|
await new Promise((resolve, reject) => {
|
|
552
|
-
child.on(
|
|
643
|
+
child.on("close", (code) => {
|
|
553
644
|
if (code === 0)
|
|
554
645
|
resolve();
|
|
555
646
|
else
|
|
556
647
|
reject(new Error(`Exit code ${code}`));
|
|
557
648
|
});
|
|
558
|
-
child.on(
|
|
649
|
+
child.on("error", reject);
|
|
559
650
|
});
|
|
560
651
|
// 原子执行不将结果传给 AI,直接返回空结果表示处理完成
|
|
561
|
-
return { processed: true, result:
|
|
652
|
+
return { processed: true, result: "" };
|
|
562
653
|
}
|
|
563
654
|
catch (error) {
|
|
564
655
|
console.error(chalk_1.default.red(`执行失败: ${error}`));
|
|
565
|
-
return { processed: true, result:
|
|
656
|
+
return { processed: true, result: "" };
|
|
566
657
|
}
|
|
567
658
|
}
|
|
568
659
|
async function handleListContext() {
|
|
@@ -571,13 +662,13 @@ async function handleListContext() {
|
|
|
571
662
|
const contextBuffer = new contextBuffer_1.ContextBuffer();
|
|
572
663
|
contextBuffer.import(persisted);
|
|
573
664
|
if (contextBuffer.isEmpty()) {
|
|
574
|
-
return { processed: true, result:
|
|
665
|
+
return { processed: true, result: "当前没有上下文" };
|
|
575
666
|
}
|
|
576
667
|
const list = contextBuffer.list();
|
|
577
668
|
// 格式化时间显示
|
|
578
669
|
const formatAge = (ageMin) => {
|
|
579
670
|
if (ageMin < 1)
|
|
580
|
-
return
|
|
671
|
+
return "刚刚";
|
|
581
672
|
if (ageMin < 60)
|
|
582
673
|
return `${ageMin}分钟前`;
|
|
583
674
|
const hours = Math.floor(ageMin / 60);
|
|
@@ -590,12 +681,12 @@ async function handleListContext() {
|
|
|
590
681
|
const formatImportance = (importance) => {
|
|
591
682
|
const value = parseFloat(importance);
|
|
592
683
|
if (value >= 0.8)
|
|
593
|
-
return chalk_1.default.red(
|
|
684
|
+
return chalk_1.default.red("★★★");
|
|
594
685
|
if (value >= 0.6)
|
|
595
|
-
return chalk_1.default.yellow(
|
|
686
|
+
return chalk_1.default.yellow("★★☆");
|
|
596
687
|
if (value >= 0.4)
|
|
597
|
-
return chalk_1.default.green(
|
|
598
|
-
return chalk_1.default.gray(
|
|
688
|
+
return chalk_1.default.green("★☆☆");
|
|
689
|
+
return chalk_1.default.gray("☆☆☆");
|
|
599
690
|
};
|
|
600
691
|
// 列宽常量定义
|
|
601
692
|
const IMPORTANCE_WIDTH = 6; // "重要度"文本宽度
|
|
@@ -605,29 +696,29 @@ async function handleListContext() {
|
|
|
605
696
|
const MAX_PATH_DISPLAY_WIDTH = 40;
|
|
606
697
|
// 计算动态列宽
|
|
607
698
|
const maxIndexWidth = Math.max(String(list.length).length, 1);
|
|
608
|
-
const maxTypeWidth = Math.max(...list.map(item => item.type.length), 4);
|
|
609
|
-
const pathColWidth = Math.min(Math.max(...list.map(item => item.path.length), 4), MAX_PATH_DISPLAY_WIDTH);
|
|
699
|
+
const maxTypeWidth = Math.max(...list.map((item) => item.type.length), 4);
|
|
700
|
+
const pathColWidth = Math.min(Math.max(...list.map((item) => item.path.length), 4), MAX_PATH_DISPLAY_WIDTH);
|
|
610
701
|
// 构建表格边框
|
|
611
|
-
const header = `┌${
|
|
612
|
-
const separator = `├${
|
|
613
|
-
const footer = `└${
|
|
702
|
+
const header = `┌${"─".repeat(maxIndexWidth + 2)}┬${"─".repeat(PINNED_WIDTH + 2)}┬${"─".repeat(maxTypeWidth + 2)}┬${"─".repeat(pathColWidth + 2)}┬${"─".repeat(IMPORTANCE_WIDTH + 2)}┬${"─".repeat(AGE_WIDTH + 2)}┬${"─".repeat(TOKENS_WIDTH + 2)}┐`;
|
|
703
|
+
const separator = `├${"─".repeat(maxIndexWidth + 2)}┼${"─".repeat(PINNED_WIDTH + 2)}┼${"─".repeat(maxTypeWidth + 2)}┼${"─".repeat(pathColWidth + 2)}┼${"─".repeat(IMPORTANCE_WIDTH + 2)}┼${"─".repeat(AGE_WIDTH + 2)}┼${"─".repeat(TOKENS_WIDTH + 2)}┤`;
|
|
704
|
+
const footer = `└${"─".repeat(maxIndexWidth + 2)}┴${"─".repeat(PINNED_WIDTH + 2)}┴${"─".repeat(maxTypeWidth + 2)}┴${"─".repeat(pathColWidth + 2)}┴${"─".repeat(IMPORTANCE_WIDTH + 2)}┴${"─".repeat(AGE_WIDTH + 2)}┴${"─".repeat(TOKENS_WIDTH + 2)}┘`;
|
|
614
705
|
// 表头
|
|
615
|
-
const headerRow = `│ ${chalk_1.default.bold(
|
|
616
|
-
let result = chalk_1.default.cyan.bold(
|
|
617
|
-
result += chalk_1.default.blue.dim(header) +
|
|
618
|
-
result += headerRow +
|
|
619
|
-
result += chalk_1.default.blue.dim(separator) +
|
|
706
|
+
const headerRow = `│ ${chalk_1.default.bold("#".padEnd(maxIndexWidth))} │ ${chalk_1.default.bold("📌".padEnd(PINNED_WIDTH))} │ ${chalk_1.default.bold("Type".padEnd(maxTypeWidth))} │ ${chalk_1.default.bold("Path".padEnd(pathColWidth))} │ ${chalk_1.default.bold("重要度")} │ ${chalk_1.default.bold("添加时间".padEnd(AGE_WIDTH))} │ ${chalk_1.default.bold("Tokens".padEnd(TOKENS_WIDTH))} │`;
|
|
707
|
+
let result = chalk_1.default.cyan.bold("📋 当前上下文列表\n\n");
|
|
708
|
+
result += chalk_1.default.blue.dim(header) + "\n";
|
|
709
|
+
result += headerRow + "\n";
|
|
710
|
+
result += chalk_1.default.blue.dim(separator) + "\n";
|
|
620
711
|
// 行内虚线分隔符 (使用更清晰的蓝色和更饱满的字符)
|
|
621
|
-
const rowSeparator = `├${
|
|
712
|
+
const rowSeparator = `├${"┈".repeat(maxIndexWidth + 2)}┼${"┈".repeat(PINNED_WIDTH + 2)}┼${"┈".repeat(maxTypeWidth + 2)}┼${"┈".repeat(pathColWidth + 2)}┼${"┈".repeat(IMPORTANCE_WIDTH + 2)}┼${"┈".repeat(AGE_WIDTH + 2)}┼${"┈".repeat(TOKENS_WIDTH + 2)}┤`;
|
|
622
713
|
// 数据行
|
|
623
714
|
list.forEach((item, index) => {
|
|
624
715
|
const indexStr = String(index + 1).padEnd(maxIndexWidth);
|
|
625
|
-
const pinnedStr = (item.pinned ?
|
|
716
|
+
const pinnedStr = (item.pinned ? "📌" : " ").padEnd(PINNED_WIDTH);
|
|
626
717
|
const typeStr = item.type.padEnd(maxTypeWidth);
|
|
627
718
|
// 路径截断处理
|
|
628
719
|
let pathStr = item.path;
|
|
629
720
|
if (pathStr.length > MAX_PATH_DISPLAY_WIDTH) {
|
|
630
|
-
pathStr =
|
|
721
|
+
pathStr = "..." + pathStr.slice(-(MAX_PATH_DISPLAY_WIDTH - 3));
|
|
631
722
|
}
|
|
632
723
|
pathStr = pathStr.padEnd(pathColWidth);
|
|
633
724
|
const importanceStr = formatImportance(item.importance);
|
|
@@ -635,28 +726,28 @@ async function handleListContext() {
|
|
|
635
726
|
const tokensStr = String(item.tokens).padStart(TOKENS_WIDTH);
|
|
636
727
|
// 根据类型着色
|
|
637
728
|
let typeColor = chalk_1.default.cyan;
|
|
638
|
-
if (item.type ===
|
|
729
|
+
if (item.type === "memory")
|
|
639
730
|
typeColor = chalk_1.default.magenta;
|
|
640
|
-
if (item.type ===
|
|
731
|
+
if (item.type === "antipattern")
|
|
641
732
|
typeColor = chalk_1.default.red;
|
|
642
733
|
result += `│ ${chalk_1.default.yellow(indexStr)} │ ${pinnedStr} │ ${typeColor(typeStr)} │ ${chalk_1.default.white(pathStr)} │ ${importanceStr} │ ${chalk_1.default.gray(ageStr)} │ ${chalk_1.default.green(tokensStr)} │\n`;
|
|
643
734
|
// 如果不是最后一行,添加虚线分隔符
|
|
644
735
|
if (index < list.length - 1) {
|
|
645
|
-
result += chalk_1.default.blue.dim(rowSeparator) +
|
|
736
|
+
result += chalk_1.default.blue.dim(rowSeparator) + "\n";
|
|
646
737
|
}
|
|
647
738
|
});
|
|
648
739
|
result += chalk_1.default.blue.dim(footer);
|
|
649
740
|
// 统计信息(单行)
|
|
650
741
|
const totalTokens = list.reduce((sum, item) => sum + item.tokens, 0);
|
|
651
|
-
const pinnedCount = list.filter(item => item.pinned).length;
|
|
652
|
-
const memoryCount = list.filter(item => item.type ===
|
|
653
|
-
result += `\n\n${chalk_1.default.cyan(
|
|
742
|
+
const pinnedCount = list.filter((item) => item.pinned).length;
|
|
743
|
+
const memoryCount = list.filter((item) => item.type === "memory").length;
|
|
744
|
+
result += `\n\n${chalk_1.default.cyan("📊")} ${chalk_1.default.gray("总计:")} ${chalk_1.default.yellow(list.length)} ${chalk_1.default.gray("|")} ${chalk_1.default.gray("固定:")} ${chalk_1.default.yellow(pinnedCount)} ${chalk_1.default.gray("|")} ${chalk_1.default.gray("记忆:")} ${chalk_1.default.magenta(memoryCount)} ${chalk_1.default.gray("|")} ${chalk_1.default.gray("Token:")} ${chalk_1.default.green(totalTokens.toLocaleString())}`;
|
|
654
745
|
return { processed: true, result };
|
|
655
746
|
}
|
|
656
747
|
catch (error) {
|
|
657
748
|
return {
|
|
658
749
|
processed: true,
|
|
659
|
-
result: `读取上下文失败: ${error}
|
|
750
|
+
result: `读取上下文失败: ${error}`,
|
|
660
751
|
};
|
|
661
752
|
}
|
|
662
753
|
}
|
|
@@ -666,21 +757,24 @@ async function handleCatContext(index, startLine = null, endLine = null) {
|
|
|
666
757
|
const contextBuffer = new contextBuffer_1.ContextBuffer();
|
|
667
758
|
contextBuffer.import(persisted);
|
|
668
759
|
if (contextBuffer.isEmpty()) {
|
|
669
|
-
return { processed: true, result:
|
|
760
|
+
return { processed: true, result: "当前没有上下文" };
|
|
670
761
|
}
|
|
671
762
|
const items = contextBuffer.export();
|
|
672
763
|
if (index !== null) {
|
|
673
764
|
// 查看指定索引
|
|
674
765
|
if (index < 1 || index > items.length) {
|
|
675
|
-
return {
|
|
766
|
+
return {
|
|
767
|
+
processed: true,
|
|
768
|
+
result: `错误: 索引 ${index} 超出范围 (共有 ${items.length} 个项目)`,
|
|
769
|
+
};
|
|
676
770
|
}
|
|
677
771
|
const item = items[index - 1];
|
|
678
|
-
let content = item.content ||
|
|
772
|
+
let content = item.content || "(无内容)";
|
|
679
773
|
// 获取语言提示 (使用增强的识别逻辑)
|
|
680
774
|
const lang = getLanguageByPath(item.path);
|
|
681
775
|
// 行号切片
|
|
682
776
|
if (startLine !== null) {
|
|
683
|
-
const lines = content.split(
|
|
777
|
+
const lines = content.split("\n");
|
|
684
778
|
// 边界校验:起始行号归一化 (不允许小于 1)
|
|
685
779
|
const clampedStart = Math.max(1, startLine);
|
|
686
780
|
const startIdx = clampedStart - 1;
|
|
@@ -688,45 +782,53 @@ async function handleCatContext(index, startLine = null, endLine = null) {
|
|
|
688
782
|
let endIdx = lines.length;
|
|
689
783
|
if (endLine !== null) {
|
|
690
784
|
if (endLine < clampedStart) {
|
|
691
|
-
return {
|
|
785
|
+
return {
|
|
786
|
+
processed: true,
|
|
787
|
+
result: `错误: 结束行号 ${endLine} 不能小于起始行号 ${clampedStart}`,
|
|
788
|
+
};
|
|
692
789
|
}
|
|
693
790
|
endIdx = Math.min(endLine, lines.length);
|
|
694
791
|
}
|
|
695
792
|
if (startIdx >= lines.length) {
|
|
696
|
-
return {
|
|
793
|
+
return {
|
|
794
|
+
processed: true,
|
|
795
|
+
result: `错误: 起始行号 ${startLine} 超出范围 (该文件共有 ${lines.length} 行)`,
|
|
796
|
+
};
|
|
697
797
|
}
|
|
698
|
-
content = lines.slice(startIdx, endIdx).join(
|
|
699
|
-
const rangeLabel = endLine
|
|
798
|
+
content = lines.slice(startIdx, endIdx).join("\n");
|
|
799
|
+
const rangeLabel = endLine
|
|
800
|
+
? `${clampedStart}-${endIdx}`
|
|
801
|
+
: `${clampedStart}-末尾`;
|
|
700
802
|
// 渲染高亮内容
|
|
701
803
|
const highlighted = (0, renderer_1.renderMarkdown)(`\`\`\`${lang}\n${content}\n\`\`\``);
|
|
702
804
|
return {
|
|
703
805
|
processed: true,
|
|
704
|
-
result: `${chalk_1.default.blue.bold(`--- [${index}] ${item.type}: ${item.path} (第 ${rangeLabel} 行) ---`)}\n${highlighted}\n${chalk_1.default.blue.bold(
|
|
806
|
+
result: `${chalk_1.default.blue.bold(`--- [${index}] ${item.type}: ${item.path} (第 ${rangeLabel} 行) ---`)}\n${highlighted}\n${chalk_1.default.blue.bold("--- End ---")}`,
|
|
705
807
|
};
|
|
706
808
|
}
|
|
707
809
|
// 渲染完整内容的高亮
|
|
708
810
|
const highlighted = (0, renderer_1.renderMarkdown)(`\`\`\`${lang}\n${content}\n\`\`\``);
|
|
709
811
|
return {
|
|
710
812
|
processed: true,
|
|
711
|
-
result: `${chalk_1.default.blue.bold(`--- [${index}] ${item.type}: ${item.path} ---`)}\n${highlighted}\n${chalk_1.default.blue.bold(
|
|
813
|
+
result: `${chalk_1.default.blue.bold(`--- [${index}] ${item.type}: ${item.path} ---`)}\n${highlighted}\n${chalk_1.default.blue.bold("--- End ---")}`,
|
|
712
814
|
};
|
|
713
815
|
}
|
|
714
816
|
else {
|
|
715
817
|
// 查看全部 (也要高亮每一个)
|
|
716
|
-
let result = chalk_1.default.cyan.bold(
|
|
818
|
+
let result = chalk_1.default.cyan.bold("=== 当前完整上下文内容 ===\n\n");
|
|
717
819
|
items.forEach((item, i) => {
|
|
718
820
|
const lang = getLanguageByPath(item.path);
|
|
719
|
-
const highlighted = (0, renderer_1.renderMarkdown)(`\`\`\`${lang}\n${item.content ||
|
|
821
|
+
const highlighted = (0, renderer_1.renderMarkdown)(`\`\`\`${lang}\n${item.content || "(空)"}\n\`\`\``);
|
|
720
822
|
result += `${chalk_1.default.blue.bold(`--- [${i + 1}] ${item.type}: ${item.path} ---`)}\n${highlighted}\n\n`;
|
|
721
823
|
});
|
|
722
|
-
result += chalk_1.default.cyan.bold(
|
|
824
|
+
result += chalk_1.default.cyan.bold("==========================");
|
|
723
825
|
return { processed: true, result };
|
|
724
826
|
}
|
|
725
827
|
}
|
|
726
828
|
catch (error) {
|
|
727
829
|
return {
|
|
728
830
|
processed: true,
|
|
729
|
-
result: `读取上下文失败: ${error}
|
|
831
|
+
result: `读取上下文失败: ${error}`,
|
|
730
832
|
};
|
|
731
833
|
}
|
|
732
834
|
}
|
|
@@ -734,12 +836,12 @@ async function handleClearContext() {
|
|
|
734
836
|
try {
|
|
735
837
|
// 清除持久化存储
|
|
736
838
|
await (0, contextStorage_1.saveContext)([]);
|
|
737
|
-
return { processed: true, result:
|
|
839
|
+
return { processed: true, result: "上下文已清空(含持久化)" };
|
|
738
840
|
}
|
|
739
841
|
catch (error) {
|
|
740
842
|
return {
|
|
741
843
|
processed: true,
|
|
742
|
-
result: `清除上下文失败: ${error}
|
|
844
|
+
result: `清除上下文失败: ${error}`,
|
|
743
845
|
};
|
|
744
846
|
}
|
|
745
847
|
}
|
|
@@ -747,21 +849,28 @@ async function handleFileAndCommand(filePath, command) {
|
|
|
747
849
|
try {
|
|
748
850
|
const fullPath = path_1.default.resolve(filePath);
|
|
749
851
|
if (!fs_1.default.existsSync(fullPath)) {
|
|
750
|
-
return {
|
|
852
|
+
return {
|
|
853
|
+
processed: true,
|
|
854
|
+
result: `错误: 文件 "${filePath}" 不存在`,
|
|
855
|
+
isPureReference: true,
|
|
856
|
+
type: "file",
|
|
857
|
+
};
|
|
751
858
|
}
|
|
752
|
-
const content = await fs_1.default.promises.readFile(fullPath,
|
|
859
|
+
const content = await fs_1.default.promises.readFile(fullPath, "utf-8");
|
|
753
860
|
const contextBuffer = new contextBuffer_1.ContextBuffer();
|
|
754
861
|
const persisted = await (0, contextStorage_1.loadContext)();
|
|
755
862
|
contextBuffer.import(persisted);
|
|
756
863
|
contextBuffer.add({
|
|
757
|
-
type:
|
|
864
|
+
type: "file",
|
|
758
865
|
path: filePath,
|
|
759
|
-
content: content
|
|
866
|
+
content: content,
|
|
760
867
|
});
|
|
761
868
|
await (0, contextStorage_1.saveContext)(contextBuffer.export());
|
|
762
869
|
console.log(chalk_1.default.green(`✓ 已将文件 "${filePath}" 加入上下文`));
|
|
763
870
|
console.log(chalk_1.default.cyan(`⚡️ 正在执行: ${command}\n`));
|
|
764
|
-
const { stdout, stderr } = await execAsync(command, {
|
|
871
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
872
|
+
cwd: path_1.default.dirname(fullPath),
|
|
873
|
+
});
|
|
765
874
|
if (stdout)
|
|
766
875
|
console.log(stdout);
|
|
767
876
|
if (stderr)
|
|
@@ -770,7 +879,7 @@ async function handleFileAndCommand(filePath, command) {
|
|
|
770
879
|
processed: true,
|
|
771
880
|
result: `命令执行完成`,
|
|
772
881
|
isPureReference: true,
|
|
773
|
-
type:
|
|
882
|
+
type: "command",
|
|
774
883
|
};
|
|
775
884
|
}
|
|
776
885
|
catch (error) {
|
|
@@ -778,7 +887,7 @@ async function handleFileAndCommand(filePath, command) {
|
|
|
778
887
|
processed: true,
|
|
779
888
|
result: `错误: 执行失败: ${error}`,
|
|
780
889
|
isPureReference: true,
|
|
781
|
-
type:
|
|
890
|
+
type: "command",
|
|
782
891
|
};
|
|
783
892
|
}
|
|
784
893
|
}
|
|
@@ -788,34 +897,34 @@ async function handleFileAndCommand(filePath, command) {
|
|
|
788
897
|
function getLanguageByPath(filePath) {
|
|
789
898
|
const ext = path_1.default.extname(filePath).toLowerCase().slice(1);
|
|
790
899
|
if (!ext)
|
|
791
|
-
return
|
|
900
|
+
return "text";
|
|
792
901
|
const langMap = {
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
902
|
+
ts: "typescript",
|
|
903
|
+
js: "javascript",
|
|
904
|
+
tsx: "typescript",
|
|
905
|
+
jsx: "javascript",
|
|
906
|
+
py: "python",
|
|
907
|
+
rb: "ruby",
|
|
908
|
+
sh: "bash",
|
|
909
|
+
zsh: "bash",
|
|
910
|
+
yml: "yaml",
|
|
911
|
+
yaml: "yaml",
|
|
912
|
+
md: "markdown",
|
|
913
|
+
json: "json",
|
|
914
|
+
rs: "rust",
|
|
915
|
+
go: "go",
|
|
916
|
+
c: "c",
|
|
917
|
+
cpp: "cpp",
|
|
918
|
+
h: "cpp",
|
|
919
|
+
java: "java",
|
|
920
|
+
kt: "kotlin",
|
|
921
|
+
css: "css",
|
|
922
|
+
scss: "scss",
|
|
923
|
+
html: "html",
|
|
924
|
+
sql: "sql",
|
|
925
|
+
vue: "html",
|
|
926
|
+
makefile: "makefile",
|
|
927
|
+
dockerfile: "dockerfile",
|
|
819
928
|
};
|
|
820
929
|
return langMap[ext] || ext;
|
|
821
930
|
}
|