jinzd-ai-cli 0.1.38 → 0.1.40

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
@@ -46,7 +46,7 @@ src/
46
46
  │ ├── dev-state.ts # 开发状态交接(provider/model 切换时快照生成、save/load/clear)
47
47
  │ ├── setup-wizard.ts # @inquirer/prompts 首次运行交互式设置
48
48
  │ └── commands/
49
- │ └── index.ts # CommandRegistry + 32个命令(/help /about /provider /model /clear /compact /plan /session /system /context /status /search /undo /export /copy /cost /init /skill /tools /plugins /mcp /config /checkpoint /review /commands /test /scaffold /add-dir /memory /doctor /bug /exit)
49
+ │ └── index.ts # CommandRegistry + 33个命令(/help /about /provider /model /clear /compact /plan /session /system /context /status /search /undo /export /copy /cost /init /skill /tools /plugins /mcp /config /checkpoint /review /commands /test /scaffold /add-dir /memory /doctor /bug /think /exit)
50
50
  │ ├── custom-commands.ts # CustomCommandManager(~/.aicli/commands/*.md 用户自定义命令)
51
51
  ├── skills/
52
52
  │ ├── types.ts # Skill/SkillMeta 接口、parseSkillFile(YAML frontmatter 解析)
@@ -348,6 +348,163 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
348
348
  - [x] **web_fetch DNS 解析时 SSRF 防护**(v0.1.25):新增 `resolveAndCheck()` 函数,用 `dns.promises.lookup()` 预解析域名,检查结果 IP 是否为私有地址。初始 URL 和 redirect 目标均校验。
349
349
  - [ ] **`persistentCwd` 全局状态**:bash 工具的当前工作目录是模块级全局变量,多 session 并发时可能串扰。现阶段单 session REPL 无影响,GUI 多会话扩展时需重构为 per-session 状态。
350
350
 
351
+ ## 本轮开发完成记录(2026-03-07,v0.1.38 → v0.1.40)
352
+
353
+ ### Bug 修复(3 个使用中发现的问题 + 1 个系统改进)
354
+
355
+ **Bug 1:`/init` 输出路径错误** 🔴
356
+ - **问题**:在 `D:\xgitee\ai-courses\prjs\vocational` 运行 `/init`,AICLI.md 被写到 git 根目录 `D:\xgitee\ai-courses\AICLI.md` 而非 cwd
357
+ - **根因**:`commands/index.ts` 第 1118 行 `const targetDir = gitRoot ?? cwd`,git 仓库内永远写到根目录
358
+ - **修复**:改为 `const targetDir = cwd`,始终写到当前工作目录(子目录层),符合 `/init` 命令语义
359
+
360
+ **Bug 2:工具轮次耗尽后无总结、无法继续** 🟠
361
+ - **问题**:AI 用完 20 轮后只显示 `Error: Reached maximum tool call rounds (20). Stopping.`,无任何已完成工作的总结
362
+ - **修复**:轮次耗尽后,给 AI 最后一次机会生成总结——传入空工具列表 + user 消息要求总结已完成/未完成工作 + 下一步建议。总结内容存入 session,用户可继续对话让 AI 接续
363
+ - **文件**:`src/repl/repl.ts` 轮次耗尽后新增 `summaryExtra` + `chatWithTools(request, [])` 总结生成逻辑
364
+
365
+ **Bug 3:`write_todos` 消耗宝贵工具轮次** 🟡
366
+ - **问题**:用户示例中 3 次 `write_todos` 占用 15% 的工具轮次(3/20),纯进度展示浪费了实际工作容量
367
+ - **修复**:
368
+ - 新增 `FREE_ROUND_TOOLS = new Set(['write_todos'])`:本轮全部工具调用都属于免费工具时,`round--` 回退计数
369
+ - MAX_TOOL_ROUNDS 从 20 → **25**:更充裕的工具调用空间
370
+
371
+ **Bug 4:AI 在 Windows 上反复使用 Unix 命令导致 bash 工具失败** 🟠
372
+ - **问题**:AI 使用 `date +%Y%m%d`、`grep`、`head`、`find | wc -l` 等 Unix 命令,在 Windows PowerShell 上全部失败,每次浪费 1-2 个工具轮次
373
+ - **根因**:system prompt 中未告知 AI 当前操作系统和 shell 环境,AI 默认使用 Unix 命令
374
+ - **修复**:`buildCurrentSystemPrompt()` 中注入操作系统信息 + shell 类型 + 工作目录。Windows 环境下明确提示"不要使用 Unix 命令,应使用 PowerShell 等效命令",列出常见替代(Select-String/Select-Object -First/Get-ChildItem/Get-Date -Format 等)
375
+ - **文件**:`src/repl/repl.ts` `buildCurrentSystemPrompt()` 新增 `envInfo` 段落
376
+
377
+ ### 版本与收尾
378
+ - `src/core/constants.ts`:VERSION `0.1.38` → `0.1.40`
379
+ - `package.json`:version 同步
380
+ - 构建验证:`npm run build` 零错误
381
+
382
+ ### 本轮变更文件汇总
383
+
384
+ | 文件 | 变更类型 | 说明 |
385
+ |------|---------|------|
386
+ | `src/core/constants.ts` | 修改 | VERSION 0.1.38 → 0.1.40 |
387
+ | `src/repl/repl.ts` | 修改 | MAX_TOOL_ROUNDS 20→25 + FREE_ROUND_TOOLS + 轮次耗尽总结 + OS/shell 信息注入 system prompt |
388
+ | `src/repl/commands/index.ts` | 修改 | /init targetDir 从 gitRoot → cwd |
389
+ | `package.json` | 修改 | version 0.1.38 → 0.1.40 |
390
+
391
+ ### 下一步建议
392
+ 1. **`/config set` 快捷配置**:REPL 内直接 `/config set ui.theme light` 无需进入向导
393
+ 2. **流式工具调用(Streaming Tool Use)**:流式解析 tool_use 事件,更早启动工具执行
394
+ 3. **`/diff` 命令**:显示当前 session 内所有文件修改的汇总 diff
395
+
396
+ ---
397
+
398
+ ## 本轮开发完成记录(2026-03-07,v0.1.37 → v0.1.38)
399
+
400
+ ### Tier 1 高价值功能:三项全部实现
401
+
402
+ **Feature 1:Extended Thinking — Claude 深度推理模式**
403
+
404
+ | 文件 | 变更 |
405
+ |------|------|
406
+ | `src/core/types.ts` | ChatRequest 新增 `thinkingBudget?: number` |
407
+ | `src/config/schema.ts` | ModelParamsSchema 新增 `thinkingBudget: z.number().int().min(1024).optional()` |
408
+ | `src/providers/claude.ts` | 完整重写,3 个 API 方法支持 thinking |
409
+ | `src/repl/repl.ts` | `runtimeThinking` 运行时覆盖 + `[THINK]` 提示符标记 + thinkingBudget 传递 |
410
+ | `src/repl/commands/index.ts` | 新增 `/think` 命令(on/off/status/toggle) |
411
+
412
+ - `claude.ts` 核心改动:
413
+ - 新增 `buildThinkingParams(request)` 辅助方法:thinking 启用时 `temperature` 必须为 `undefined`(API 强制 temperature=1),`budget_tokens` 最小 1024 默认 10000
414
+ - 新增 `extractContent(blocks)` 辅助方法:从 `response.content` 提取 ThinkingBlock + TextBlock,thinking 用 `<think>...</think>` 标签包裹(复用 renderer 已有折叠逻辑)
415
+ - `chat()` / `chatStream()` / `chatWithTools()`:三个 API 方法均传入 `thinking` + `temperature` 参数
416
+ - 流式输出:`currentBlockType` 跟踪当前 content block 类型,`content_block_start` 时发 `<think>`,`content_block_stop` 时发 `</think>`,`thinking_delta` 事件直接输出 thinking 文本
417
+ - 工具调用循环:`_rawContent` 属性附着在 `toolCalls` 数组上,保留原始 response.content 块(含 ThinkingBlock/RedactedThinkingBlock)
418
+ - `buildToolResultMessages()`:优先使用 `_rawContent` 构建 assistantContent,保留 thinking/redacted_thinking/tool_use/text 四种块类型传回 API
419
+ - `repl.ts`:
420
+ - `runtimeThinking: boolean | null = null`(null=用配置值,true/false=运行时覆盖)
421
+ - `getModelParams()` 返回 `thinkingBudget` + `runtimeThinking` 优先覆盖 `thinking`
422
+ - 全部 4 处 API 调用新增 `thinkingBudget: modelParams.thinkingBudget`
423
+ - `refreshPrompt()` thinking 启用时显示黄色 `[THINK]` 标记
424
+ - CommandContext 注入 `getThinkingMode` / `setThinkingMode`
425
+ - `/think` 命令:`/think` toggle 切换 | `/think on` 启用 | `/think off` 禁用 | `/think status` 显示状态
426
+
427
+ **Feature 2:theme 迁移全覆盖**
428
+
429
+ | 文件 | chalk→theme 迁移数 | 保留 chalk.white |
430
+ |------|-------------------|-----------------|
431
+ | `src/repl/commands/index.ts` | 116 | 4(中性色) |
432
+ | `src/repl/repl.ts` | — | 1(中性色) |
433
+ | `src/repl/renderer.ts` | — | 4(中性色) |
434
+ | `src/tools/executor.ts` | ~15 | 3(中性色) |
435
+ | `src/repl/setup-wizard.ts` | 26 | 0 |
436
+
437
+ - 迁移映射规则:
438
+ - `chalk.red` → `theme.error`(错误信息)
439
+ - `chalk.yellow` → `theme.warning`(警告)或 `theme.toolCall`(工具标记,视上下文)
440
+ - `chalk.green` → `theme.success`(成功确认)
441
+ - `chalk.cyan` → `theme.accent`(强调 ID/路径/值)
442
+ - `chalk.dim` / `chalk.gray` → `theme.dim`(次要文本)
443
+ - `chalk.bold` / `chalk.bold.cyan` → `theme.heading`(标题)
444
+ - `chalk.magenta` → `theme.toolCall`(工具/技能标记)
445
+ - `chalk.white` → 保持不变(中性内容,不受主题影响)
446
+ - 现在切换 `config.ui.theme` 为 `"light"` 或 `"custom"` 时,全部终端输出(命令/REPL/工具/向导)统一变化
447
+
448
+ **Feature 3:edit_file 工具增强**(`src/tools/builtin/edit-file.ts`)
449
+
450
+ - 新增 `similarityScore(a, b): number`:bigram(2-gram)字符串相似度算法,O(n) 复杂度,返回 0-1 分数
451
+ - 新增 `findSimilarLines(fileContent, searchStr, maxResults=3): string[]`:当 `old_str` 匹配失败时,搜索文件中最相似的行(>50% 阈值),返回 "Line N: <text>" 格式建议
452
+ - 新增 `findWhitespaceTolerant(fileLines, searchLines): { start, count } | null`:滑动窗口逐行 trim 匹配,支持忽略缩进差异
453
+ - 新增 `ignore_whitespace` 可选参数(默认 false):启用时按行 trim 后匹配,保持唯一性检查
454
+ - 增强错误消息格式:
455
+ ```
456
+ ERROR: old_str not found in file.
457
+ File has 167 lines.
458
+ Similar lines found (did you mean?):
459
+ Line 42: const firstIndex = original.indexOf(oldStr);
460
+ Line 88: if (firstIndex === -1) {
461
+ Tip: If it's a whitespace/indentation issue, try setting ignore_whitespace: true
462
+ Please read the file first and use exact text including whitespace/indentation.
463
+ ```
464
+
465
+ ### 版本与收尾
466
+ - `src/core/constants.ts`:VERSION `0.1.37` → `0.1.38`
467
+ - `package.json`:version 同步
468
+ - `src/repl/renderer.ts`:`/about` 命令计数 32 → 33,命令列表新增 `/think`,新增 3 条特性条目(Extended Thinking / theme 全覆盖 / edit_file 智能提示)
469
+ - 构建验证:`npm run build` 零错误(ESM + CJS 双产物)
470
+ - 发布:`npm publish` → `jinzd-ai-cli@0.1.38`
471
+
472
+ ### 本轮变更文件汇总
473
+
474
+ | 文件 | 变更类型 | 说明 |
475
+ |------|---------|------|
476
+ | `src/core/types.ts` | 修改 | ChatRequest 新增 `thinkingBudget` |
477
+ | `src/core/constants.ts` | 修改 | VERSION 0.1.37 → 0.1.38 |
478
+ | `src/config/schema.ts` | 修改 | ModelParamsSchema 新增 `thinkingBudget` |
479
+ | `src/providers/claude.ts` | 重写 | Extended Thinking 完整支持(3 API 方法 + thinking 块处理) |
480
+ | `src/repl/repl.ts` | 修改 | runtimeThinking + thinkingBudget + [THINK] 标记 + theme 迁移 |
481
+ | `src/repl/renderer.ts` | 修改 | /about 更新(33 命令 + 3 特性) |
482
+ | `src/repl/commands/index.ts` | 修改 | /think 命令 + 116 处 theme 迁移 |
483
+ | `src/repl/setup-wizard.ts` | 修改 | 26 处 chalk→theme 全量迁移 |
484
+ | `src/tools/executor.ts` | 修改 | ~15 处 chalk→theme 迁移 |
485
+ | `src/tools/builtin/edit-file.ts` | 重写 | similarityScore + findSimilarLines + ignore_whitespace |
486
+ | `package.json` | 修改 | version 0.1.37 → 0.1.38 |
487
+
488
+ ### 下一步建议
489
+
490
+ #### Tier 1 — 高价值功能
491
+ 1. **`/config set` 快捷配置**:REPL 内直接 `/config set ui.theme light` 无需进入向导,key-path 语法读写 config.json
492
+ 2. **流式工具调用(Streaming Tool Use)**:Claude/OpenAI 均支持流式返回 tool_use 事件,当前等待完整 response 才开始执行,改为流式解析可更早启动工具执行
493
+ 3. **`/diff` 命令**:显示当前 session 内所有文件修改的汇总 diff,便于 AI 操作后快速审查变更
494
+
495
+ #### Tier 2 — 体验增强
496
+ 4. **L1 低危**:`run-tests.ts` 的 `JSON.parse(package.json)` 细粒度错误处理
497
+ 5. **IDE 集成**:VS Code 扩展(架构已准备就绪,core/providers/tools 无终端依赖)
498
+ 6. **OAuth/浏览器登录**:无需手动填 API Key,打开浏览器完成 OAuth 流程自动保存 token
499
+ 7. **`/undo` 增强**:支持 bash 工具创建的文件/目录的撤销(当前仅支持 write_file / edit_file)
500
+ 8. **对话分支(fork)**:`/fork` 从当前对话某个 checkpoint 分叉为新 session,探索不同方案
501
+
502
+ #### Tier 3 — 长远方向
503
+ 9. **Web UI**:基于 EventBus + WebSocket 的浏览器界面,复用 core/providers/tools 层
504
+ 10. **GitHub 仓库迁移**:从 gitee 迁移到 GitHub,npm 包受众更广
505
+
506
+ ---
507
+
351
508
  ## 本轮开发完成记录(2026-03-05,v0.1.36 → v0.1.37)
352
509
 
353
510
  ### P2 四项功能全部实现
@@ -413,8 +570,8 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
413
570
  | `package.json` | 修改 | version 0.1.36 → 0.1.37 |
414
571
 
415
572
  ### 下一步建议
416
- 1. **Extended Thinking**:Claude 3.7 深度推理模式集成
417
- 2. **theme 迁移扩展**:`commands/index.ts` ~115 个 chalk 调用增量迁移为 theme 调用
573
+ 1. ~~**Extended Thinking**~~:✅ 已在 v0.1.38 实现
574
+ 2. ~~**theme 迁移扩展**~~:✅ 已在 v0.1.38 全覆盖迁移
418
575
  3. **L1 低危**:`run-tests.ts` 的 `JSON.parse(package.json)` 细粒度错误处理
419
576
  4. **IDE 集成**:VS Code 扩展(架构已准备就绪)
420
577
  5. **OAuth/浏览器登录**:无需手动填 API Key
@@ -1184,8 +1341,10 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
1184
1341
  - Dev State 开发状态交接
1185
1342
  - Agent Skills 系统
1186
1343
  - 企业级权限规则 + Hooks 系统
1187
- - 主题/颜色自定义系统(dark/light/custom + 10 语义色槽)
1344
+ - 主题/颜色自定义系统(dark/light/custom + 10 语义色槽,全部终端输出已迁移)
1188
1345
  - 项目级 MCP 配置(`.mcp.json` + 全局合并 + 来源追踪)
1346
+ - Extended Thinking 深度推理(`/think` 运行时切换 + thinking 块折叠)
1347
+ - edit_file 智能提示(匹配失败 did you mean + `ignore_whitespace` 容错模式)
1189
1348
 
1190
1349
  ### ❌ 缺失功能路线图(新增)
1191
1350
 
@@ -1204,11 +1363,11 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
1204
1363
  - [x] **`--allowed-tools` / `--blocked-tools` 启动参数**(v0.1.35):启动时白名单/黑名单限制 AI 可用工具
1205
1364
  - [x] **流式 JSON 输出**(v0.1.36):`--output-format streaming-json`,NDJSON 每 chunk 一行 `{"type":"delta","text":"..."}` + 最终 `{"type":"done",...}`
1206
1365
 
1207
- #### P2 — 体验增强(v0.1.37 完成 4 项)
1366
+ #### P2 — 体验增强(v0.1.37 完成 4 项,v0.1.38 完成 Extended Thinking)
1208
1367
  - [x] **项目级 `.mcp.json`**(v0.1.37):项目根目录 `.mcp.json` 自动发现,与全局合并(项目覆盖同名),`/mcp` 显示来源标签
1209
1368
  - [x] **`--resume <id>` 启动参数**(v0.1.37):命令行直接恢复指定会话(前缀匹配),找不到时显示最近 5 个 session
1210
1369
  - [x] **Word wrap 配置**(v0.1.37):`config.ui.wordWrap`,ANSI 转义码感知折行
1211
1370
  - [x] **主题/颜色自定义**(v0.1.37):dark/light/custom 三主题 + 10 语义色槽 + Proxy 全局导出
1212
1371
  - [ ] **IDE 集成**:VS Code / JetBrains 扩展(架构已准备就绪)
1213
1372
  - [ ] **OAuth/浏览器登录**:无需手动填 API Key
1214
- - [ ] **Extended Thinking**:Claude 深度推理模式集成
1373
+ - [x] **Extended Thinking**(v0.1.38):Claude 深度推理模式集成,`/think` 运行时切换,thinking 块折叠显示
@@ -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.38";
11
+ var VERSION = "0.1.40";
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-JLT4LDJH.js";
32
+ } from "./chunk-5XTQNTUG.js";
33
33
 
34
34
  // src/index.ts
35
35
  import { program } from "commander";
@@ -3462,9 +3462,7 @@ ${text}
3462
3462
  usage: "/init [--force]",
3463
3463
  async execute(args, ctx) {
3464
3464
  const cwd = process.cwd();
3465
- const gitRoot = getGitRoot(cwd);
3466
- const targetDir = gitRoot ?? cwd;
3467
- const targetPath = join5(targetDir, "AICLI.md");
3465
+ const targetPath = join5(cwd, "AICLI.md");
3468
3466
  const force = args.includes("--force");
3469
3467
  if (existsSync6(targetPath) && !force) {
3470
3468
  ctx.renderer.printInfo(`AICLI.md already exists at ${targetPath}`);
@@ -3713,7 +3711,7 @@ ${hint}` : "")
3713
3711
  description: "Run project tests and show structured report",
3714
3712
  usage: "/test [command|filter]",
3715
3713
  async execute(args, _ctx) {
3716
- const { executeTests } = await import("./run-tests-E6ZA5BYD.js");
3714
+ const { executeTests } = await import("./run-tests-COTUXAOM.js");
3717
3715
  const argStr = args.join(" ").trim();
3718
3716
  let testArgs = {};
3719
3717
  if (argStr) {
@@ -7918,7 +7916,8 @@ ${content}
7918
7916
  parts.push(...imageParts);
7919
7917
  return { parts, hasImage: imageParts.length > 0, refs };
7920
7918
  }
7921
- var MAX_TOOL_ROUNDS = 20;
7919
+ var MAX_TOOL_ROUNDS = 25;
7920
+ var FREE_ROUND_TOOLS = /* @__PURE__ */ new Set(["write_todos"]);
7922
7921
  function fmtTokens(n) {
7923
7922
  if (n >= 1e6) return `${Math.round(n / 1e5) / 10}M`;
7924
7923
  if (n >= 1e3) return `${Math.round(n / 1024)}K`;
@@ -8377,7 +8376,12 @@ ${projectContext}`);
8377
8376
  const dateStr = `${now.getFullYear()}\u5E74${pad(now.getMonth() + 1)}\u6708${pad(now.getDate())}\u65E5 ${WEEKDAYS[now.getDay()]}`;
8378
8377
  const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
8379
8378
  const dateTimeInfo = `\u5F53\u524D\u65E5\u671F\u65F6\u95F4\uFF1A${dateStr} ${timeStr}`;
8380
- const parts = [dateTimeInfo];
8379
+ const osName = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
8380
+ const shellInfo = process.platform === "win32" ? "bash \u5DE5\u5177\u4F7F\u7528 PowerShell\uFF08\u975E cmd\uFF09\uFF0C\u8BF7\u4F7F\u7528 PowerShell \u547D\u4EE4\u8BED\u6CD5\u3002\u4E0D\u8981\u4F7F\u7528 Unix \u547D\u4EE4\uFF08\u5982 grep\u3001head\u3001find\u3001wc\u3001date +\u683C\u5F0F \u7B49\uFF09\uFF0C\u5E94\u4F7F\u7528 PowerShell \u7B49\u6548\u547D\u4EE4\uFF08\u5982 Select-String\u3001Select-Object -First\u3001Get-ChildItem\u3001Measure-Object\u3001Get-Date -Format \u7B49\uFF09\u3002" : `bash \u5DE5\u5177\u4F7F\u7528 ${process.env.SHELL || "/bin/bash"}\u3002`;
8381
+ const envInfo = `\u64CD\u4F5C\u7CFB\u7EDF\uFF1A${osName}
8382
+ ${shellInfo}
8383
+ \u5DE5\u4F5C\u76EE\u5F55\uFF1A${process.cwd()}`;
8384
+ const parts = [dateTimeInfo + "\n" + envInfo];
8381
8385
  const memory = this.loadMemoryContent();
8382
8386
  if (memory) {
8383
8387
  parts.push(`# Persistent Memory
@@ -9336,15 +9340,74 @@ Session '${this.resumeSessionId}' not found.
9336
9340
  const reasoningContent = "reasoningContent" in result ? result.reasoningContent : void 0;
9337
9341
  const newMsgs = provider.buildToolResultMessages(result.toolCalls, toolResults, reasoningContent);
9338
9342
  extraMessages.push(...newMsgs);
9343
+ const allFree = result.toolCalls.every((tc) => FREE_ROUND_TOOLS.has(tc.name));
9344
+ if (allFree) {
9345
+ round--;
9346
+ }
9339
9347
  const nextRound = round + 2;
9340
9348
  spinner.start(
9341
9349
  nextRound <= MAX_TOOL_ROUNDS ? `Thinking... (round ${nextRound}/${MAX_TOOL_ROUNDS})` : "Thinking..."
9342
9350
  );
9343
9351
  }
9344
9352
  spinner.stop();
9345
- this.renderer.renderError(
9346
- `Reached maximum tool call rounds (${MAX_TOOL_ROUNDS}). Stopping.`
9347
- );
9353
+ try {
9354
+ spinner.start("Generating summary...");
9355
+ const summaryExtra = [
9356
+ ...extraMessages,
9357
+ {
9358
+ role: "user",
9359
+ content: `\u4F60\u5DF2\u7ECF\u7528\u5B8C\u4E86\u5168\u90E8 ${MAX_TOOL_ROUNDS} \u8F6E\u5DE5\u5177\u8C03\u7528\u6B21\u6570\u3002\u8BF7\u4E0D\u8981\u518D\u8C03\u7528\u4EFB\u4F55\u5DE5\u5177\uFF0C\u76F4\u63A5\u7528\u6587\u5B57\u603B\u7ED3\uFF1A
9360
+ 1. \u5230\u76EE\u524D\u4E3A\u6B62\u4F60\u5DF2\u7ECF\u5B8C\u6210\u4E86\u54EA\u4E9B\u5DE5\u4F5C
9361
+ 2. \u8FD8\u6709\u54EA\u4E9B\u4EFB\u52A1\u672A\u5B8C\u6210
9362
+ 3. \u7528\u6237\u63A5\u4E0B\u6765\u53EF\u4EE5\u600E\u4E48\u505A\uFF08\u4F8B\u5982\u91CD\u65B0\u53D1\u9001\u8BF7\u6C42\u8BA9\u4F60\u7EE7\u7EED\uFF09`
9363
+ }
9364
+ ];
9365
+ const summaryResult = await provider.chatWithTools(
9366
+ {
9367
+ messages: apiMessages,
9368
+ model: this.currentModel,
9369
+ systemPrompt,
9370
+ stream: false,
9371
+ temperature: modelParams.temperature,
9372
+ maxTokens: modelParams.maxTokens,
9373
+ timeout: modelParams.timeout,
9374
+ thinking: modelParams.thinking,
9375
+ thinkingBudget: modelParams.thinkingBudget,
9376
+ _extraMessages: summaryExtra
9377
+ },
9378
+ []
9379
+ // 不提供任何工具,强制 AI 返回纯文本
9380
+ );
9381
+ spinner.stop();
9382
+ if ("content" in summaryResult) {
9383
+ this.renderer.renderError(`Reached maximum tool call rounds (${MAX_TOOL_ROUNDS}). Here is a summary:`);
9384
+ this.renderer.renderResponse(summaryResult.content);
9385
+ lastResponseStore.content = summaryResult.content;
9386
+ session.addMessage({ role: "assistant", content: summaryResult.content, timestamp: /* @__PURE__ */ new Date() });
9387
+ if (summaryResult.usage) {
9388
+ roundUsage.inputTokens += summaryResult.usage.inputTokens;
9389
+ roundUsage.outputTokens += summaryResult.usage.outputTokens;
9390
+ }
9391
+ } else {
9392
+ this.renderer.renderError(
9393
+ `Reached maximum tool call rounds (${MAX_TOOL_ROUNDS}). Stopping.
9394
+ Tip: You can continue the conversation by asking the AI to proceed.`
9395
+ );
9396
+ }
9397
+ } catch {
9398
+ this.renderer.renderError(
9399
+ `Reached maximum tool call rounds (${MAX_TOOL_ROUNDS}). Stopping.
9400
+ Tip: You can continue the conversation by asking the AI to proceed.`
9401
+ );
9402
+ }
9403
+ if (roundUsage.inputTokens > 0 || roundUsage.outputTokens > 0) {
9404
+ this.sessionTokenUsage.inputTokens += roundUsage.inputTokens;
9405
+ this.sessionTokenUsage.outputTokens += roundUsage.outputTokens;
9406
+ session.addTokenUsage(roundUsage);
9407
+ if (this.shouldShowTokens()) {
9408
+ this.renderer.renderUsage(roundUsage, this.sessionTokenUsage);
9409
+ }
9410
+ }
9348
9411
  } finally {
9349
9412
  spinner.stop();
9350
9413
  await this.checkContextPressure();
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-JLT4LDJH.js";
5
+ } from "./chunk-5XTQNTUG.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.38",
3
+ "version": "0.1.40",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",