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,只声明 defaultBaseUrl info
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. **L1 低危**:`run-tests.ts` `JSON.parse(package.json)` 细粒度错误处理
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. **L1 低危**:`run-tests.ts` `JSON.parse(package.json)` 细粒度错误处理
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. **L1 低危**:`run-tests.ts` `JSON.parse(package.json)` 细粒度错误处理
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. **L1 低危**:`run-tests.ts` `JSON.parse(package.json)` 细粒度错误处理
956
+ 3. ~~**L1 低危**~~:✅ 已在 v0.1.50 修复
904
957
 
905
958
  ---
906
959
 
@@ -8,7 +8,7 @@ import { platform } from "os";
8
8
  import chalk from "chalk";
9
9
 
10
10
  // src/core/constants.ts
11
- var VERSION = "0.1.50";
11
+ var VERSION = "0.1.52";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";
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-64YCGWC5.js";
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-6DPROQJ6.js");
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
- return `[Binary file: ${filePath}]
5238
- PDF \u6587\u672C\u63D0\u53D6\u5931\u8D25\uFF08pdftotext \u548C pdfminer \u5747\u4E0D\u53EF\u7528\uFF09\u3002
5239
- ` + (textAlts.length > 0 ? `\u627E\u5230\u53EF\u66FF\u4EE3\u7684\u6587\u672C\u7248\u672C\uFF1A
5240
- ${textAlts.map((p) => ` \u2192 ${basename2(p)}`).join("\n")}
5241
- \u8BF7\u4F7F\u7528 read_file \u8BFB\u53D6\u4E0A\u8FF0\u6587\u4EF6\u3002` : `\u5982\u9700\u4F7F\u7528\u5176\u5185\u5BB9\uFF0C\u8BF7\u4F7F\u7528 bash \u5DE5\u5177\u5B89\u88C5\u5E76\u8C03\u7528 pdftotext\uFF1A
5242
- pip install pdfminer.six # \u5B89\u88C5 Python PDF \u5E93
5243
- \u6216\u5C06 PDF \u624B\u52A8\u8F6C\u6362\u4E3A .md / .txt \u6587\u4EF6\u540E\u518D\u8BFB\u53D6\u3002`);
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\uFF08${ext}\uFF09\uFF0C\u65E0\u6CD5\u4F5C\u4E3A\u6587\u672C\u8BFB\u53D6\u3002
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") {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-64YCGWC5.js";
5
+ } from "./chunk-KRYABIB4.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.1.50",
3
+ "version": "0.1.52",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",