jinzd-ai-cli 0.1.50 → 0.1.52
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/CLAUDE.md
CHANGED
|
@@ -32,7 +32,7 @@ src/
|
|
|
32
32
|
│ ├── claude.ts # Anthropic SDK provider
|
|
33
33
|
│ ├── gemini.ts # Google Generative AI provider(role: assistant → model)
|
|
34
34
|
│ ├── openai.ts # OpenAI provider(GPT-5.4/5/4.1/4o/o3/o4-mini)
|
|
35
|
-
│ ├── deepseek.ts / zhipu.ts / kimi.ts # 继承 OpenAICompatibleProvider
|
|
35
|
+
│ ├── deepseek.ts / zhipu.ts / kimi.ts # 继承 OpenAICompatibleProvider;deepseek/kimi 覆写 chatWithTools 实现虚假声明检测
|
|
36
36
|
├── config/
|
|
37
37
|
│ ├── schema.ts # Zod schema(含 timeouts / customBaseUrls / defaultModels)
|
|
38
38
|
│ ├── config-manager.ts # 读写 ~/.aicli/config.json,三层优先级:env > file > default
|
|
@@ -350,6 +350,59 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
|
|
|
350
350
|
- [x] **web_fetch DNS 解析时 SSRF 防护**(v0.1.25):新增 `resolveAndCheck()` 函数,用 `dns.promises.lookup()` 预解析域名,检查结果 IP 是否为私有地址。初始 URL 和 redirect 目标均校验。
|
|
351
351
|
- [ ] **`persistentCwd` 全局状态**:bash 工具的当前工作目录是模块级全局变量,多 session 并发时可能串扰。现阶段单 session REPL 无影响,GUI 多会话扩展时需重构为 per-session 状态。
|
|
352
352
|
|
|
353
|
+
## 本轮开发完成记录(2026-03-08,v0.1.51 → v0.1.52)
|
|
354
|
+
|
|
355
|
+
### Bug 修复:DeepSeek 虚假完成声明(方案 C)+ 虚假声明检测共享重构
|
|
356
|
+
|
|
357
|
+
**问题**:DeepSeek 在多轮对话中生成多个文件时,偶尔会跳过后续文件的 `write_file` 工具调用,直接在回复文本中声称"✅ 文件已保存",实际上文件并未创建。这与 Kimi 的方案 C 问题(v0.1.41)完全相同——模型虚假声称已完成文件操作。
|
|
358
|
+
|
|
359
|
+
**修复**:三层变更
|
|
360
|
+
|
|
361
|
+
| 文件 | 变更 | 说明 |
|
|
362
|
+
|------|------|------|
|
|
363
|
+
| `src/providers/openai-compatible.ts` | 新增共享代码 | HALLUCINATION_PATTERNS(8 个模式)+ `detectsHallucinatedFileOp()` / `mergeUsage()` 两个 protected 方法 |
|
|
364
|
+
| `src/providers/kimi.ts` | 简化(去重) | 移除私有 HALLUCINATION_PATTERNS、detectsHallucinatedFileOp、mergeUsage,改用父类 protected 方法 |
|
|
365
|
+
| `src/providers/deepseek.ts` | 新增覆写 | 方案 B(system prompt 规范提示)+ 方案 C(虚假声明检测 + 自动重试) |
|
|
366
|
+
|
|
367
|
+
**共享重构 — HALLUCINATION_PATTERNS 提升到基类**:
|
|
368
|
+
|
|
369
|
+
原本 HALLUCINATION_PATTERNS 和检测/合并方法分别在 kimi.ts 中重复定义。本次将共享逻辑提升到 `openai-compatible.ts` 基类:
|
|
370
|
+
- `HALLUCINATION_PATTERNS`:模块级常量,8 个正则模式(比原 Kimi 版增加 `/已保存/`(更宽泛)、`/已创建/`、`/✅.../` 三个新模式)
|
|
371
|
+
- `detectsHallucinatedFileOp(content)`:protected 方法,子类直接调用 `this.detectsHallucinatedFileOp()`
|
|
372
|
+
- `mergeUsage(a, b)`:protected 方法,合并两次 API 调用的 token 用量
|
|
373
|
+
|
|
374
|
+
**DeepSeek 方案 B — system prompt 规范提示**:
|
|
375
|
+
|
|
376
|
+
`DEEPSEEK_TOOL_CALL_REMINDER` 注入 system prompt 末尾,比 Kimi 版更简洁(无 XML 相关条款),新增"如果需要生成多个文件,必须对每个文件分别调用 write_file 工具,不可省略任何一个"条款。
|
|
377
|
+
|
|
378
|
+
**DeepSeek 方案 C — 虚假声明检测 + 自动重试**:
|
|
379
|
+
|
|
380
|
+
与 Kimi 方案 C 逻辑一致:
|
|
381
|
+
1. 检测纯文本响应中的虚假完成声明模式
|
|
382
|
+
2. 注入纠正消息(AI 的虚假回复 + "你刚才没有实际调用 write_file 工具"纠正指令)到 `_extraMessages`
|
|
383
|
+
3. 重新调用 `super.chatWithTools()` 一次(防无限循环)
|
|
384
|
+
4. 合并两次调用的 token 用量
|
|
385
|
+
5. stderr 输出 `[deepseek] ⚠ 检测到虚假完成声明` 警告
|
|
386
|
+
|
|
387
|
+
**与 Kimi 的区别**:DeepSeek 不存在 XML 伪工具调用问题(方案 A),因此 deepseek.ts 仅需方案 B + C 两道防线。
|
|
388
|
+
|
|
389
|
+
### 版本与收尾
|
|
390
|
+
- `src/core/constants.ts`:VERSION `0.1.51` → `0.1.52`
|
|
391
|
+
- `package.json`:version 同步
|
|
392
|
+
- 构建验证:`npm run build` 零错误(ESM + CJS 双产物)
|
|
393
|
+
|
|
394
|
+
### 本轮变更文件汇总
|
|
395
|
+
|
|
396
|
+
| 文件 | 变更类型 | 说明 |
|
|
397
|
+
|------|---------|------|
|
|
398
|
+
| `src/providers/openai-compatible.ts` | 修改 | HALLUCINATION_PATTERNS + detectsHallucinatedFileOp + mergeUsage(共享) |
|
|
399
|
+
| `src/providers/kimi.ts` | 修改 | 移除重复定义,使用父类 protected 方法 |
|
|
400
|
+
| `src/providers/deepseek.ts` | 重写 | 方案 B(system prompt)+ 方案 C(虚假声明检测 + 重试) |
|
|
401
|
+
| `src/core/constants.ts` | 修改 | VERSION 0.1.51 → 0.1.52 |
|
|
402
|
+
| `package.json` | 修改 | version 0.1.51 → 0.1.52 |
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
353
406
|
## 本轮开发完成记录(2026-03-08,v0.1.49 → v0.1.50)
|
|
354
407
|
|
|
355
408
|
### 代码质量:L1 低危修复 — run-tests.ts package.json 细粒度错误处理
|
|
@@ -657,7 +710,7 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
|
|
|
657
710
|
3. **`/diff` 命令**:显示当前 session 内所有文件修改的汇总 diff,便于 AI 操作后快速审查变更
|
|
658
711
|
|
|
659
712
|
#### Tier 2 — 体验增强
|
|
660
|
-
4.
|
|
713
|
+
4. ~~**L1 低危**~~:✅ 已在 v0.1.50 修复(safeReadPackageJson + detectNodeTestFramework)
|
|
661
714
|
5. **IDE 集成**:VS Code 扩展(架构已准备就绪,core/providers/tools 无终端依赖)
|
|
662
715
|
6. **OAuth/浏览器登录**:无需手动填 API Key,打开浏览器完成 OAuth 流程自动保存 token
|
|
663
716
|
7. ~~**`/undo` 增强**~~:✅ 已在 v0.1.48 实现(bash 文件追踪 + /undo list + /undo <n>)
|
|
@@ -736,7 +789,7 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
|
|
|
736
789
|
### 下一步建议
|
|
737
790
|
1. ~~**Extended Thinking**~~:✅ 已在 v0.1.38 实现
|
|
738
791
|
2. ~~**theme 迁移扩展**~~:✅ 已在 v0.1.38 全覆盖迁移
|
|
739
|
-
3.
|
|
792
|
+
3. ~~**L1 低危**~~:✅ 已在 v0.1.50 修复
|
|
740
793
|
4. **IDE 集成**:VS Code 扩展(架构已准备就绪)
|
|
741
794
|
5. **OAuth/浏览器登录**:无需手动填 API Key
|
|
742
795
|
|
|
@@ -826,7 +879,7 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
|
|
|
826
879
|
2. ~~**`--resume <id>` 启动参数**~~:✅ 已在 v0.1.37 实现
|
|
827
880
|
3. ~~**Word wrap 配置**~~:✅ 已在 v0.1.37 实现
|
|
828
881
|
4. ~~**主题/颜色自定义**~~:✅ 已在 v0.1.37 实现
|
|
829
|
-
5.
|
|
882
|
+
5. ~~**L1 低危**~~:✅ 已在 v0.1.50 修复
|
|
830
883
|
|
|
831
884
|
---
|
|
832
885
|
|
|
@@ -900,7 +953,7 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
|
|
|
900
953
|
### 下一步建议
|
|
901
954
|
1. **P0 功能缺口**:并行工具调用、`/add-dir` 命令
|
|
902
955
|
2. **P1 功能缺口**:`/memory` 命令、`/doctor` 健康检查、`/bug` 反馈
|
|
903
|
-
3.
|
|
956
|
+
3. ~~**L1 低危**~~:✅ 已在 v0.1.50 修复
|
|
904
957
|
|
|
905
958
|
---
|
|
906
959
|
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
30
30
|
VERSION,
|
|
31
31
|
runTestsTool
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-KRYABIB4.js";
|
|
33
33
|
|
|
34
34
|
// src/index.ts
|
|
35
35
|
import { program } from "commander";
|
|
@@ -967,6 +967,24 @@ Node.js \u4E0D\u4F1A\u81EA\u52A8\u4F7F\u7528\u7CFB\u7EDF\u4EE3\u7406\u3002\u8BF7
|
|
|
967
967
|
|
|
968
968
|
// src/providers/openai-compatible.ts
|
|
969
969
|
import OpenAI from "openai";
|
|
970
|
+
var HALLUCINATION_PATTERNS = [
|
|
971
|
+
/文件路径[::]\s*`?[^\s`]+/,
|
|
972
|
+
// 文件路径: `path/to/file`
|
|
973
|
+
/已生成[::!!]/,
|
|
974
|
+
// 已生成完成!
|
|
975
|
+
/已保存/,
|
|
976
|
+
// 已保存到 / 文件已保存 / 已保存成功
|
|
977
|
+
/已写入[::!!]/,
|
|
978
|
+
// 已写入!
|
|
979
|
+
/已创建[::!!]/,
|
|
980
|
+
// 已创建!
|
|
981
|
+
/File\s+(?:written|saved|created)/i,
|
|
982
|
+
// File written / saved / created
|
|
983
|
+
/生成完成[!!]/,
|
|
984
|
+
// 生成完成!
|
|
985
|
+
/✅\s*(?:文件|已[生保写创]|第)/
|
|
986
|
+
// ✅ 文件已保存 / ✅ 已保存 / ✅ 第二份已生成
|
|
987
|
+
];
|
|
970
988
|
var OpenAICompatibleProvider = class extends BaseProvider {
|
|
971
989
|
client;
|
|
972
990
|
defaultTimeout = 6e4;
|
|
@@ -1179,6 +1197,25 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
1179
1197
|
async listModels() {
|
|
1180
1198
|
return this.info.models;
|
|
1181
1199
|
}
|
|
1200
|
+
/**
|
|
1201
|
+
* 检测 AI 文本响应中是否存在虚假的文件操作完成声明。
|
|
1202
|
+
* 当 AI 返回纯文本而非 tool_calls,且文本中包含「已保存」「已写入」等声明时,
|
|
1203
|
+
* 说明 AI 跳过了实际工具调用,需要触发重试。
|
|
1204
|
+
* 子类(Kimi / DeepSeek)在 chatWithTools 覆写中使用。
|
|
1205
|
+
*/
|
|
1206
|
+
detectsHallucinatedFileOp(content) {
|
|
1207
|
+
return HALLUCINATION_PATTERNS.some((pattern) => pattern.test(content));
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* 合并两次 API 调用的 token 用量(虚假声明重试时使用)
|
|
1211
|
+
*/
|
|
1212
|
+
mergeUsage(a, b) {
|
|
1213
|
+
if (!a && !b) return void 0;
|
|
1214
|
+
return {
|
|
1215
|
+
inputTokens: (a?.inputTokens ?? 0) + (b?.inputTokens ?? 0),
|
|
1216
|
+
outputTokens: (a?.outputTokens ?? 0) + (b?.outputTokens ?? 0)
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1182
1219
|
wrapError(err) {
|
|
1183
1220
|
if (err instanceof OpenAI.AuthenticationError) {
|
|
1184
1221
|
return new AuthError(this.info.id);
|
|
@@ -1194,6 +1231,13 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
1194
1231
|
};
|
|
1195
1232
|
|
|
1196
1233
|
// src/providers/deepseek.ts
|
|
1234
|
+
var DEEPSEEK_TOOL_CALL_REMINDER = `
|
|
1235
|
+
|
|
1236
|
+
\u3010\u26A0\uFE0F \u5DE5\u5177\u8C03\u7528\u5F3A\u5236\u89C4\u8303\u3011
|
|
1237
|
+
\u5F53\u9700\u8981\u521B\u5EFA\u3001\u5199\u5165\u6216\u4FEE\u6539\u6587\u4EF6\u65F6\uFF0C\u5FC5\u987B\u4E14\u53EA\u80FD\u901A\u8FC7 API \u7684 function calling \u673A\u5236\u8C03\u7528 write_file \u6216 edit_file \u5DE5\u5177\u3002
|
|
1238
|
+
\u7EDD\u5BF9\u4E0D\u5141\u8BB8\u5728\u56DE\u590D\u6587\u672C\u4E2D\u58F0\u79F0"\u6587\u4EF6\u5DF2\u751F\u6210"\u3001"\u6587\u4EF6\u5DF2\u4FDD\u5B58"\u3001"\u5DF2\u5199\u5165"\u7B49\uFF0C\u800C\u4E0D\u5B9E\u9645\u8C03\u7528\u5DE5\u5177\u3002
|
|
1239
|
+
\u4EC5\u5728\u6587\u672C\u4E2D\u63CF\u8FF0\u6587\u4EF6\u5185\u5BB9\u800C\u4E0D\u8C03\u7528\u5DE5\u5177 = \u6587\u4EF6\u4E0D\u5B58\u5728 = \u4EFB\u52A1\u5931\u8D25\u3002
|
|
1240
|
+
\u5982\u679C\u9700\u8981\u751F\u6210\u591A\u4E2A\u6587\u4EF6\uFF0C\u5FC5\u987B\u5BF9\u6BCF\u4E2A\u6587\u4EF6\u5206\u522B\u8C03\u7528 write_file \u5DE5\u5177\uFF0C\u4E0D\u53EF\u7701\u7565\u4EFB\u4F55\u4E00\u4E2A\u3002`;
|
|
1197
1241
|
var DeepSeekProvider = class extends OpenAICompatibleProvider {
|
|
1198
1242
|
defaultBaseUrl = "https://api.deepseek.com/v1";
|
|
1199
1243
|
info = {
|
|
@@ -1218,6 +1262,51 @@ var DeepSeekProvider = class extends OpenAICompatibleProvider {
|
|
|
1218
1262
|
}
|
|
1219
1263
|
]
|
|
1220
1264
|
};
|
|
1265
|
+
/**
|
|
1266
|
+
* 覆写 chatWithTools,叠加两道防线:
|
|
1267
|
+
*
|
|
1268
|
+
* 方案 B(预防):在 system prompt 末尾追加工具调用规范提示,
|
|
1269
|
+
* 指示 DeepSeek 必须使用 API function calling,禁止虚假完成声明。
|
|
1270
|
+
*
|
|
1271
|
+
* 方案 C(兜底):若 DeepSeek 在文本中声称已完成文件操作但未调用任何工具,
|
|
1272
|
+
* 自动检测并注入纠正消息,重新请求一次,强制 DeepSeek 实际调用工具。
|
|
1273
|
+
*/
|
|
1274
|
+
async chatWithTools(request, tools) {
|
|
1275
|
+
const enhancedRequest = {
|
|
1276
|
+
...request,
|
|
1277
|
+
systemPrompt: (request.systemPrompt ?? "") + DEEPSEEK_TOOL_CALL_REMINDER
|
|
1278
|
+
};
|
|
1279
|
+
const result = await super.chatWithTools(enhancedRequest, tools);
|
|
1280
|
+
if ("content" in result && result.content) {
|
|
1281
|
+
const hasWriteTools = tools.some((t) => t.name === "write_file" || t.name === "edit_file");
|
|
1282
|
+
if (hasWriteTools && this.detectsHallucinatedFileOp(result.content)) {
|
|
1283
|
+
process.stderr.write(
|
|
1284
|
+
`[deepseek] \u26A0 \u68C0\u6D4B\u5230\u865A\u5047\u5B8C\u6210\u58F0\u660E\uFF08AI \u58F0\u79F0\u5DF2\u5199\u5165\u6587\u4EF6\u4F46\u672A\u8C03\u7528\u5DE5\u5177\uFF09\uFF0C\u6B63\u5728\u5F3A\u5236\u91CD\u65B0\u8BF7\u6C42...
|
|
1285
|
+
`
|
|
1286
|
+
);
|
|
1287
|
+
const existingExtra = enhancedRequest._extraMessages ?? [];
|
|
1288
|
+
const correctionRequest = {
|
|
1289
|
+
...enhancedRequest,
|
|
1290
|
+
_extraMessages: [
|
|
1291
|
+
...existingExtra,
|
|
1292
|
+
{ role: "assistant", content: result.content },
|
|
1293
|
+
{
|
|
1294
|
+
role: "user",
|
|
1295
|
+
content: "\u4F60\u521A\u624D\u6CA1\u6709\u5B9E\u9645\u8C03\u7528 write_file \u5DE5\u5177\uFF0C\u6587\u4EF6\u5E76\u672A\u88AB\u521B\u5EFA\uFF01\u8BF7\u7ACB\u5373\u4F7F\u7528 write_file \u5DE5\u5177\u7684 function calling API \u6267\u884C\u5B9E\u9645\u7684\u6587\u4EF6\u5199\u5165\u64CD\u4F5C\u3002\u4E0D\u8981\u518D\u7528\u6587\u5B57\u63CF\u8FF0\u6587\u4EF6\u5185\u5BB9\uFF0C\u5FC5\u987B\u901A\u8FC7 API \u7684 tool_calls \u673A\u5236\u8C03\u7528 write_file\u3002"
|
|
1296
|
+
}
|
|
1297
|
+
]
|
|
1298
|
+
};
|
|
1299
|
+
const retryResult = await super.chatWithTools(correctionRequest, tools);
|
|
1300
|
+
if ("toolCalls" in retryResult) {
|
|
1301
|
+
retryResult.usage = this.mergeUsage(result.usage, retryResult.usage);
|
|
1302
|
+
} else if ("content" in retryResult) {
|
|
1303
|
+
retryResult.usage = this.mergeUsage(result.usage, retryResult.usage);
|
|
1304
|
+
}
|
|
1305
|
+
return retryResult;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
return result;
|
|
1309
|
+
}
|
|
1221
1310
|
};
|
|
1222
1311
|
|
|
1223
1312
|
// src/providers/zhipu.ts
|
|
@@ -1306,20 +1395,6 @@ var KIMI_TOOL_CALL_REMINDER = `
|
|
|
1306
1395
|
\u7EDD\u5BF9\u4E0D\u5141\u8BB8\u5728\u56DE\u590D\u4E2D\u58F0\u79F0"\u6587\u4EF6\u5DF2\u751F\u6210"\u3001"\u6587\u4EF6\u5DF2\u4FDD\u5B58"\u3001"\u5DF2\u5199\u5165"\u7B49\uFF0C\u800C\u4E0D\u5B9E\u9645\u8C03\u7528 write_file \u5DE5\u5177\u3002
|
|
1307
1396
|
\u5982\u679C\u7528\u6237\u8981\u6C42\u521B\u5EFA/\u5199\u5165\u6587\u4EF6\uFF0C\u4F60\u5FC5\u987B\u901A\u8FC7 function calling \u8C03\u7528 write_file \u5DE5\u5177\uFF0C\u7CFB\u7EDF\u624D\u4F1A\u6267\u884C\u5199\u5165\u3002
|
|
1308
1397
|
\u4EC5\u5728\u6587\u672C\u4E2D\u63CF\u8FF0\u6587\u4EF6\u5185\u5BB9\u800C\u4E0D\u8C03\u7528\u5DE5\u5177 = \u6587\u4EF6\u4E0D\u5B58\u5728 = \u4EFB\u52A1\u5931\u8D25\u3002`;
|
|
1309
|
-
var HALLUCINATION_PATTERNS = [
|
|
1310
|
-
/文件路径[::]\s*`?[^\s`]+/,
|
|
1311
|
-
// 文件路径: `path/to/file`
|
|
1312
|
-
/已生成[::!!]/,
|
|
1313
|
-
// 已生成完成!
|
|
1314
|
-
/已保存到/,
|
|
1315
|
-
// 已保存到指定路径
|
|
1316
|
-
/已写入[::!!]/,
|
|
1317
|
-
// 已写入!
|
|
1318
|
-
/File\s+(?:written|saved|created)/i,
|
|
1319
|
-
// File written / saved / created
|
|
1320
|
-
/生成完成[!!]/
|
|
1321
|
-
// 生成完成!
|
|
1322
|
-
];
|
|
1323
1398
|
var KimiProvider = class extends OpenAICompatibleProvider {
|
|
1324
1399
|
defaultBaseUrl = "https://api.moonshot.ai/v1";
|
|
1325
1400
|
info = {
|
|
@@ -1449,25 +1524,6 @@ var KimiProvider = class extends OpenAICompatibleProvider {
|
|
|
1449
1524
|
}
|
|
1450
1525
|
return result;
|
|
1451
1526
|
}
|
|
1452
|
-
/**
|
|
1453
|
-
* 方案 C 辅助:检测 AI 文本响应中是否存在虚假的文件操作完成声明。
|
|
1454
|
-
*
|
|
1455
|
-
* 当 AI 回复中包含"文件路径: xxx"、"已生成完成!"等模式时,
|
|
1456
|
-
* 且当前工具列表中包含写文件工具,说明 AI 在虚假声称已完成操作。
|
|
1457
|
-
*/
|
|
1458
|
-
detectsHallucinatedFileOp(content) {
|
|
1459
|
-
return HALLUCINATION_PATTERNS.some((pattern) => pattern.test(content));
|
|
1460
|
-
}
|
|
1461
|
-
/**
|
|
1462
|
-
* 合并两次 API 调用的 token 用量
|
|
1463
|
-
*/
|
|
1464
|
-
mergeUsage(a, b) {
|
|
1465
|
-
if (!a && !b) return void 0;
|
|
1466
|
-
return {
|
|
1467
|
-
inputTokens: (a?.inputTokens ?? 0) + (b?.inputTokens ?? 0),
|
|
1468
|
-
outputTokens: (a?.outputTokens ?? 0) + (b?.outputTokens ?? 0)
|
|
1469
|
-
};
|
|
1470
|
-
}
|
|
1471
1527
|
/**
|
|
1472
1528
|
* 方案 A 核心逻辑:从 Kimi 的文本响应中提取 XML 格式伪工具调用。
|
|
1473
1529
|
*
|
|
@@ -4288,7 +4344,7 @@ ${hint}` : "")
|
|
|
4288
4344
|
description: "Run project tests and show structured report",
|
|
4289
4345
|
usage: "/test [command|filter]",
|
|
4290
4346
|
async execute(args, _ctx) {
|
|
4291
|
-
const { executeTests } = await import("./run-tests-
|
|
4347
|
+
const { executeTests } = await import("./run-tests-MTYDLLII.js");
|
|
4292
4348
|
const argStr = args.join(" ").trim();
|
|
4293
4349
|
let testArgs = {};
|
|
4294
4350
|
if (argStr) {
|
|
@@ -5234,21 +5290,19 @@ ${pdfText}`;
|
|
|
5234
5290
|
const dir = dirname3(normalizedPath);
|
|
5235
5291
|
const nameNoExt = basename2(normalizedPath, ext);
|
|
5236
5292
|
const textAlts = [".md", ".txt", ".html"].map((e) => resolve3(dir, nameNoExt + e)).filter(existsSync8);
|
|
5237
|
-
|
|
5238
|
-
PDF
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
\u8BF7\u4F7F\u7528 read_file \u8BFB\u53D6\u4E0A\u8FF0\u6587\u4EF6\u3002
|
|
5242
|
-
|
|
5243
|
-
|
|
5293
|
+
if (textAlts.length > 0) {
|
|
5294
|
+
return `[PDF file: ${filePath}]
|
|
5295
|
+
\u6B64 PDF \u6587\u4EF6\u5F53\u524D\u73AF\u5883\u65E0\u6CD5\u81EA\u52A8\u63D0\u53D6\u6587\u672C\uFF0C\u4F46\u627E\u5230\u53EF\u66FF\u4EE3\u7684\u6587\u672C\u7248\u672C\uFF1A
|
|
5296
|
+
` + textAlts.map((p) => ` \u2192 ${basename2(p)}`).join("\n") + `
|
|
5297
|
+
\u8BF7\u4F7F\u7528 read_file \u8BFB\u53D6\u4E0A\u8FF0\u6587\u4EF6\u3002`;
|
|
5298
|
+
}
|
|
5299
|
+
return `[PDF file: ${filePath}]
|
|
5300
|
+
\u6B64 PDF \u6587\u4EF6\u5F53\u524D\u73AF\u5883\u65E0\u6CD5\u81EA\u52A8\u63D0\u53D6\u6587\u672C\uFF08\u9700\u5B89\u88C5 pdftotext \u6216 pdfminer.six\uFF09\u3002
|
|
5301
|
+
\u5EFA\u8BAE\uFF1A\u76F4\u63A5\u53C2\u8003\u9879\u76EE\u4E2D\u5DF2\u6709\u7684\u6587\u672C\u7248\u672C\uFF08.md / .txt\uFF09\uFF0C\u6216\u7528 bash \u5B89\u88C5\u63D0\u53D6\u5DE5\u5177\u540E\u91CD\u8BD5\u3002`;
|
|
5244
5302
|
}
|
|
5245
5303
|
if (BINARY_EXTENSIONS.has(ext)) {
|
|
5246
|
-
return `[Binary file: ${filePath}]
|
|
5247
|
-
\u6B64\u6587\u4EF6\u4E3A\u4E8C\u8FDB\u5236\u683C\u5F0F\
|
|
5248
|
-
\u5982\u9700\u4F7F\u7528\u5176\u5185\u5BB9\uFF0C\u8BF7\u8003\u8651\uFF1A
|
|
5249
|
-
1. \u5C06\u6587\u4EF6\u8F6C\u6362\u4E3A\u6587\u672C\u683C\u5F0F\u540E\u518D\u8BFB\u53D6
|
|
5250
|
-
2. \u4F7F\u7528 bash \u5DE5\u5177\u8C03\u7528\u5916\u90E8\u8F6C\u6362\u7A0B\u5E8F\uFF08\u5982 pandoc \u7B49\uFF09
|
|
5251
|
-
3. \u82E5\u6709\u5BF9\u5E94\u7684\u7EAF\u6587\u672C\u7248\u672C\uFF08.md / .txt\uFF09\uFF0C\u8BF7\u76F4\u63A5\u8BFB\u53D6\u90A3\u4E2A\u6587\u4EF6`;
|
|
5304
|
+
return `[Binary file: ${filePath} (${ext})]
|
|
5305
|
+
\u6B64\u6587\u4EF6\u4E3A\u4E8C\u8FDB\u5236\u683C\u5F0F\uFF0C\u4E0D\u652F\u6301\u76F4\u63A5\u6587\u672C\u8BFB\u53D6\u3002\u5982\u9879\u76EE\u4E2D\u6709\u5BF9\u5E94\u7684\u6587\u672C\u7248\u672C\uFF08.md / .txt\uFF09\uFF0C\u8BF7\u76F4\u63A5\u8BFB\u53D6\u3002`;
|
|
5252
5306
|
}
|
|
5253
5307
|
const buf = readFileSync5(normalizedPath);
|
|
5254
5308
|
if (encoding === "base64") {
|