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