jinzd-ai-cli 0.4.260 → 0.4.262

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.
Files changed (39) hide show
  1. package/README.md +10 -8
  2. package/README.zh-CN.md +11 -9
  3. package/dist/{batch-LBMJWMMO.js → batch-2B7UUX27.js} +2 -2
  4. package/dist/{chunk-U2HN3EW6.js → chunk-25HFWCM3.js} +19 -7
  5. package/dist/{chunk-2BGNEVS5.js → chunk-2XPJRRBB.js} +4 -4
  6. package/dist/{chunk-AAWV4BP4.js → chunk-A6JLBNAI.js} +2 -2
  7. package/dist/{chunk-XTA3LSPQ.js → chunk-BS44XEZ4.js} +1 -1
  8. package/dist/{chunk-NEYNE6UE.js → chunk-D6DHS5GL.js} +11 -9
  9. package/dist/{chunk-63M2ELJS.js → chunk-FWBSGBB7.js} +4 -4
  10. package/dist/{chunk-HBR33J6J.js → chunk-HPDI4F5F.js} +1 -1
  11. package/dist/{chunk-Y7UZSFGS.js → chunk-JEJHNVA2.js} +1 -1
  12. package/dist/{chunk-CLJKNKUN.js → chunk-JFXKQPKY.js} +1 -1
  13. package/dist/{chunk-3TE7ZWO5.js → chunk-KMS3LBO3.js} +1 -1
  14. package/dist/{chunk-KRENSXPC.js → chunk-KWYJFLXY.js} +1 -1
  15. package/dist/{chunk-K6WMQBUP.js → chunk-MTIS7H6V.js} +1 -1
  16. package/dist/{chunk-VJP2GLW5.js → chunk-WAAFA32V.js} +1 -1
  17. package/dist/{chunk-FCPYMBTA.js → chunk-XZNRZGG2.js} +57 -25
  18. package/dist/{chunk-26C6YZ3A.js → chunk-ZQZEBPAQ.js} +1 -1
  19. package/dist/{ci-PSA7F4EI.js → ci-G6OZDH4H.js} +6 -6
  20. package/dist/{ci-format-WAHEZZMY.js → ci-format-BRS7G5X5.js} +2 -2
  21. package/dist/{constants-C55LGUR7.js → constants-24AD5JQV.js} +1 -1
  22. package/dist/{doctor-cli-YANDJBHC.js → doctor-cli-WWZRFGQJ.js} +6 -6
  23. package/dist/electron-server.js +87 -42
  24. package/dist/{hub-CBOC3TT3.js → hub-2DYZQVEN.js} +1 -1
  25. package/dist/index.js +90 -41
  26. package/dist/{persistent-memory-DUFOZTDV.js → persistent-memory-DFSZZSYP.js} +2 -2
  27. package/dist/{persistent-memory-KEYVKD54.js → persistent-memory-LJT37XVO.js} +2 -2
  28. package/dist/{pr-T6KCK5DT.js → pr-OOIQPCBX.js} +6 -6
  29. package/dist/{run-tests-K43DLTA3.js → run-tests-ICTVJSJN.js} +2 -2
  30. package/dist/{run-tests-S2RLPMCR.js → run-tests-W4TG4OQV.js} +2 -2
  31. package/dist/{server-GTRCCOXN.js → server-GTVYIHBG.js} +6 -6
  32. package/dist/{server-SV3TV5BJ.js → server-IXUVVPTV.js} +14 -14
  33. package/dist/{task-orchestrator-DW5AWW7J.js → task-orchestrator-6MHSDOFR.js} +6 -6
  34. package/dist/{usage-BP442V2Q.js → usage-23I3ZJZO.js} +2 -2
  35. package/dist/web/client/actions.js +12 -7
  36. package/dist/web/client/app.js +4 -36
  37. package/dist/web/client/sidebar-tabs.js +48 -0
  38. package/dist/web/client/sw.js +1 -1
  39. package/package.json +1 -1
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  <!-- AICLI:DOCS_DEFAULT_WEB_PORT=3000 -->
6
6
  <!-- AICLI:DOCS_BUILTIN_TOOL_COUNT=30 -->
7
7
  <!-- AICLI:DOCS_REPL_COMMAND_COUNT=48 -->
8
+ <!-- AICLI:DOCS_BUILTIN_PROVIDER_COUNT=10 -->
8
9
  <!-- AICLI:DOCS_MEMORY_SOURCE=memory.jsonl -->
9
10
 
10
11
  > A cross-platform AI coding assistant — CLI, Web UI, and Desktop App — with multi-provider support and agentic tool calling
@@ -12,7 +13,6 @@
12
13
  [![npm version](https://img.shields.io/npm/v/jinzd-ai-cli)](https://www.npmjs.com/package/jinzd-ai-cli)
13
14
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
14
15
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20.18.1-brightgreen)](https://nodejs.org)
15
- [![Tests](https://img.shields.io/badge/tests-1639%20passing-brightgreen)]()
16
16
  [![GitHub Release](https://img.shields.io/github/v/release/jinzhengdong/ai-cli)](https://github.com/jinzhengdong/ai-cli/releases)
17
17
  [![CI](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml)
18
18
 
@@ -131,13 +131,14 @@ aicli user delete x # Delete user
131
131
  |----------|--------|-------------|
132
132
  | **Claude** | Opus 4, Sonnet 4, Haiku 4 | [console.anthropic.com](https://console.anthropic.com) |
133
133
  | **Gemini** | 2.5 Pro, 2.5 Flash | [aistudio.google.com](https://aistudio.google.com) |
134
- | **DeepSeek** | DeepSeek-Chat (V3), Reasoner (R1) | [platform.deepseek.com](https://platform.deepseek.com) |
134
+ | **DeepSeek** | deepseek-v4-flash (default), deepseek-v4-pro | [platform.deepseek.com](https://platform.deepseek.com) |
135
135
  | **OpenAI** | GPT-5.4, GPT-5, GPT-4.1, o3, o4-mini | [platform.openai.com](https://platform.openai.com) |
136
136
  | **OpenRouter** | 300+ models (Claude, GPT, Gemini, Llama, Qwen, Mistral...) | [openrouter.ai](https://openrouter.ai) |
137
137
  | **Zhipu** | GLM-4, GLM-5 | [open.bigmodel.cn](https://open.bigmodel.cn) |
138
138
  | **Kimi** | Kimi K2.6, K2.5, K2 Thinking | [platform.moonshot.cn](https://platform.moonshot.cn) |
139
- | **Qwen** | qwen-plus, qwen-max, qwen-turbo, qwen-coder-plus | [bailian.console.aliyun.com](https://bailian.console.aliyun.com) |
140
- | **Ollama** | Any locally installed model (Llama, Qwen, Gemma, Mistral...) | No API key — [ollama.com](https://ollama.com) |
139
+ | **Qwen** | qwen-plus, qwen-max, qwen-turbo, qwen-coder-plus | [bailian.console.aliyun.com](https://bailian.console.aliyun.com) |
140
+ | **MiniMax** | MiniMax-M3 (default), M2.7, M2.5, M2.1, M2 | [platform.minimaxi.com](https://platform.minimaxi.com) |
141
+ | **Ollama** | Any locally installed model (Llama, Qwen, Gemma, Mistral...) | No API key — [ollama.com](https://ollama.com) |
141
142
 
142
143
  Any OpenAI-compatible API can also be used via `customBaseUrls` in config.
143
144
 
@@ -462,8 +463,9 @@ Environment variables take precedence over config file values:
462
463
  | `AICLI_API_KEY_OPENROUTER` | OpenRouter API Key |
463
464
  | `AICLI_API_KEY_ZHIPU` | Zhipu API Key |
464
465
  | `AICLI_API_KEY_KIMI` | Kimi API Key |
465
- | `AICLI_API_KEY_QWEN` | Qwen / Alibaba Cloud API Key |
466
- | `AICLI_PROVIDER` | Default provider ID |
466
+ | `AICLI_API_KEY_QWEN` | Qwen / Alibaba Cloud API Key |
467
+ | `AICLI_API_KEY_MINIMAX` | MiniMax API Key |
468
+ | `AICLI_PROVIDER` | Default provider ID |
467
469
  | `AICLI_NO_STREAM` | Set to `1` to disable streaming |
468
470
  | `HTTPS_PROXY` / `HTTP_PROXY` | Proxy URL |
469
471
 
@@ -517,13 +519,13 @@ The Web UI (`aicli web`) provides a full-featured browser interface:
517
519
  ## Testing
518
520
 
519
521
  ```bash
520
- npm test # Offline regression suite: 91 files, 1639 passed, 2 skipped
522
+ npm test # Offline regression suite
521
523
  npm run test:providers # Real-provider smoke tests (network/API-key dependent)
522
524
  npm run test:e2e-web # Playwright browser smoke; may need browser-process permission in sandboxes
523
525
  npm run test:watch # Watch mode
524
526
  ```
525
527
 
526
- 79 offline test files covering: authentication, sessions, tool types & danger levels, permissions, output truncation, diff rendering, edit-file similarity, error hierarchy, config management, env loading, provider registry, web-fetch, grep-files, hub renderer, hub discussion, hub presets, dev-state, token estimator, tool registry budget, parallel tool execution, cost tracker, session tool history.
528
+ Offline tests cover authentication, sessions, tool types and danger levels, permissions, output truncation, diff rendering, edit-file similarity, error hierarchy, config management, env loading, provider registry, web-fetch, grep-files, Hub, dev-state, token estimation, tool registry budgets, parallel tool execution, cost tracking, and session tool history.
527
529
 
528
530
  ## Releasing (Version Bump → npm Publish → GitHub Push)
529
531
 
package/README.zh-CN.md CHANGED
@@ -5,6 +5,7 @@
5
5
  <!-- AICLI:DOCS_DEFAULT_WEB_PORT=3000 -->
6
6
  <!-- AICLI:DOCS_BUILTIN_TOOL_COUNT=30 -->
7
7
  <!-- AICLI:DOCS_REPL_COMMAND_COUNT=48 -->
8
+ <!-- AICLI:DOCS_BUILTIN_PROVIDER_COUNT=10 -->
8
9
  <!-- AICLI:DOCS_MEMORY_SOURCE=memory.jsonl -->
9
10
 
10
11
  > 跨平台 AI 编程助手 — CLI 终端、Web 界面、桌面应用三合一,支持 10 大 Provider(含本地 Ollama)与 Agentic 工具调用
@@ -12,7 +13,6 @@
12
13
  [![npm version](https://img.shields.io/npm/v/jinzd-ai-cli)](https://www.npmjs.com/package/jinzd-ai-cli)
13
14
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
14
15
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20.18.1-brightgreen)](https://nodejs.org)
15
- [![Tests](https://img.shields.io/badge/tests-1097%20passing-brightgreen)]()
16
16
  [![GitHub Release](https://img.shields.io/github/v/release/jinzhengdong/ai-cli)](https://github.com/jinzhengdong/ai-cli/releases)
17
17
  [![CI](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml)
18
18
 
@@ -119,13 +119,14 @@ aicli user delete x # 删除用户
119
119
  |----------|------|-------------|
120
120
  | **Claude** | Opus 4, Sonnet 4, Haiku 4 | [console.anthropic.com](https://console.anthropic.com) |
121
121
  | **Gemini** | 2.5 Pro, 2.5 Flash | [aistudio.google.com](https://aistudio.google.com) |
122
- | **DeepSeek** | DeepSeek-Chat (V3), Reasoner (R1) | [platform.deepseek.com](https://platform.deepseek.com) |
122
+ | **DeepSeek** | deepseek-v4-flash(默认), deepseek-v4-pro | [platform.deepseek.com](https://platform.deepseek.com) |
123
123
  | **OpenAI** | GPT-5.4, GPT-5, GPT-4.1, o3, o4-mini | [platform.openai.com](https://platform.openai.com) |
124
124
  | **OpenRouter** | 300+ 模型(Claude/GPT/Gemini/Llama/Qwen/Mistral...) | [openrouter.ai](https://openrouter.ai) |
125
- | **智谱清言** | GLM-4, GLM-5 | [open.bigmodel.cn](https://open.bigmodel.cn) |
125
+ | **Zhipu(智谱)** | GLM-4, GLM-5 | [open.bigmodel.cn](https://open.bigmodel.cn) |
126
126
  | **Kimi** | Kimi K2.6, K2.5, K2 Thinking | [platform.moonshot.cn](https://platform.moonshot.cn) |
127
- | **Qwen** | qwen-plus, qwen-max, qwen-turbo, qwen-coder-plus | [bailian.console.aliyun.com](https://bailian.console.aliyun.com) |
128
- | **Ollama** | 任意本地模型(LlamaQwenGemmaMistral...) | 无需 API Key — [ollama.com](https://ollama.com) |
127
+ | **Qwen** | qwen-plus, qwen-max, qwen-turbo, qwen-coder-plus | [bailian.console.aliyun.com](https://bailian.console.aliyun.com) |
128
+ | **MiniMax** | MiniMax-M3(默认)、M2.7M2.5M2.1M2 | [platform.minimaxi.com](https://platform.minimaxi.com) |
129
+ | **Ollama** | 任意本地模型(Llama、Qwen、Gemma、Mistral...) | 无需 API Key — [ollama.com](https://ollama.com) |
129
130
 
130
131
  也可通过 `customBaseUrls` 配置接入任意 OpenAI 兼容 API。
131
132
 
@@ -420,8 +421,9 @@ Auto Mode 不会被项目配置静默开启。用 `/permissions recently-denied`
420
421
  | `AICLI_API_KEY_OPENROUTER` | OpenRouter API Key |
421
422
  | `AICLI_API_KEY_ZHIPU` | 智谱 API Key |
422
423
  | `AICLI_API_KEY_KIMI` | Kimi API Key |
423
- | `AICLI_API_KEY_QWEN` | Qwen / Alibaba Cloud API Key |
424
- | `AICLI_PROVIDER` | 默认 Provider |
424
+ | `AICLI_API_KEY_QWEN` | Qwen / Alibaba Cloud API Key |
425
+ | `AICLI_API_KEY_MINIMAX` | MiniMax API Key |
426
+ | `AICLI_PROVIDER` | 默认 Provider |
425
427
  | `AICLI_NO_STREAM` | 设为 `1` 禁用流式输出 |
426
428
  | `HTTPS_PROXY` / `HTTP_PROXY` | 代理地址 |
427
429
 
@@ -475,11 +477,11 @@ Web UI(`aicli web`)提供功能完备的浏览器界面:
475
477
  ## 测试
476
478
 
477
479
  ```bash
478
- npm test # 运行全部 515 个测试(30 suites)
480
+ npm test # 运行离线回归测试
479
481
  npm run test:watch # 监听模式
480
482
  ```
481
483
 
482
- 26 个测试套件覆盖:认证、会话、工具类型与危险级别、权限、输出截断、diff 渲染、edit-file 相似度、错误层级、配置管理、环境变量、Provider 注册、web-fetch、grep-files、Hub 渲染、Hub 讨论、Hub 预设、开发状态、Token 估算、工具注册表预算、并行工具执行、费用追踪、会话工具历史。
484
+ 离线测试覆盖:认证、会话、工具类型与危险级别、权限、输出截断、diff 渲染、edit-file 相似度、错误层级、配置管理、环境变量、Provider 注册、web-fetch、grep-files、Hub、开发状态、Token 估算、工具注册表预算、并行工具执行、费用追踪和会话工具历史。
483
485
 
484
486
  ## 发布(版本提升 → npm 发布 → 推送 GitHub)
485
487
 
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-K6WMQBUP.js";
4
+ } from "./chunk-MTIS7H6V.js";
5
5
  import "./chunk-TU3L3PW7.js";
6
- import "./chunk-KRENSXPC.js";
6
+ import "./chunk-KWYJFLXY.js";
7
7
  import {
8
8
  atomicWriteFileSync
9
9
  } from "./chunk-IW3Q7AE5.js";
@@ -3,7 +3,7 @@ import {
3
3
  detectsHallucinatedFileOp,
4
4
  parseToolCallArguments,
5
5
  schemaToJsonSchema
6
- } from "./chunk-FCPYMBTA.js";
6
+ } from "./chunk-XZNRZGG2.js";
7
7
  import {
8
8
  AuthError,
9
9
  ProviderError,
@@ -2116,6 +2116,18 @@ var BUILT_IN_PROVIDERS = [
2116
2116
  OpenRouterProvider,
2117
2117
  OllamaProvider
2118
2118
  ];
2119
+ function listBuiltInProviderMetadata() {
2120
+ return BUILT_IN_PROVIDERS.map((ProviderClass) => {
2121
+ const { info } = new ProviderClass();
2122
+ return {
2123
+ id: info.id,
2124
+ displayName: info.displayName,
2125
+ defaultModel: info.defaultModel,
2126
+ apiKeyEnvVar: info.apiKeyEnvVar,
2127
+ requiresApiKey: info.requiresApiKey
2128
+ };
2129
+ });
2130
+ }
2119
2131
  var ProviderRegistry = class {
2120
2132
  providers = /* @__PURE__ */ new Map();
2121
2133
  customConfigs = [];
@@ -2210,13 +2222,12 @@ var ProviderRegistry = class {
2210
2222
  return [...this.providers.values()];
2211
2223
  }
2212
2224
  listAll() {
2213
- const builtIn = BUILT_IN_PROVIDERS.map((P) => {
2214
- const p = new P();
2225
+ const builtIn = listBuiltInProviderMetadata().map((info) => {
2215
2226
  return {
2216
- id: p.info.id,
2217
- displayName: p.info.displayName,
2218
- configured: this.providers.has(p.info.id),
2219
- defaultModel: p.info.defaultModel,
2227
+ id: info.id,
2228
+ displayName: info.displayName,
2229
+ configured: this.providers.has(info.id),
2230
+ defaultModel: info.defaultModel,
2220
2231
  isCustom: false
2221
2232
  };
2222
2233
  });
@@ -2235,5 +2246,6 @@ var ProviderRegistry = class {
2235
2246
  };
2236
2247
 
2237
2248
  export {
2249
+ listBuiltInProviderMetadata,
2238
2250
  ProviderRegistry
2239
2251
  };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  evaluateCiGate,
4
4
  normalizeCiThresholds
5
- } from "./chunk-VJP2GLW5.js";
5
+ } from "./chunk-WAAFA32V.js";
6
6
  import {
7
7
  buildReviewPrompt,
8
8
  buildSecurityReviewPrompt,
@@ -10,13 +10,13 @@ import {
10
10
  } from "./chunk-HLWUDRBO.js";
11
11
  import {
12
12
  ProviderRegistry
13
- } from "./chunk-U2HN3EW6.js";
13
+ } from "./chunk-25HFWCM3.js";
14
14
  import {
15
15
  ConfigManager
16
- } from "./chunk-K6WMQBUP.js";
16
+ } from "./chunk-MTIS7H6V.js";
17
17
  import {
18
18
  VERSION
19
- } from "./chunk-KRENSXPC.js";
19
+ } from "./chunk-KWYJFLXY.js";
20
20
 
21
21
  // src/cli/ci.ts
22
22
  import { execFileSync } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  truncateForPersist
4
- } from "./chunk-NEYNE6UE.js";
4
+ } from "./chunk-D6DHS5GL.js";
5
5
  import {
6
6
  APP_NAME,
7
7
  CONFIG_DIR_NAME,
@@ -11,7 +11,7 @@ import {
11
11
  MCP_PROTOCOL_VERSION,
12
12
  MCP_TOOL_PREFIX,
13
13
  VERSION
14
- } from "./chunk-KRENSXPC.js";
14
+ } from "./chunk-KWYJFLXY.js";
15
15
  import {
16
16
  atomicWriteFileSync
17
17
  } from "./chunk-IW3Q7AE5.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TEST_TIMEOUT
3
- } from "./chunk-26C6YZ3A.js";
3
+ } from "./chunk-ZQZEBPAQ.js";
4
4
 
5
5
  // src/tools/builtin/run-tests.ts
6
6
  import { execSync, spawnSync } from "child_process";
@@ -7,7 +7,7 @@ import {
7
7
  searchPersistentMemories,
8
8
  touchMemoryReferences,
9
9
  updateMemoryEntry
10
- } from "./chunk-3TE7ZWO5.js";
10
+ } from "./chunk-KMS3LBO3.js";
11
11
  import {
12
12
  indexProject
13
13
  } from "./chunk-GLSR2NR3.js";
@@ -23,13 +23,13 @@ import {
23
23
  runHook,
24
24
  runLifecycleHooks,
25
25
  runTool
26
- } from "./chunk-Y7UZSFGS.js";
26
+ } from "./chunk-JEJHNVA2.js";
27
27
  import {
28
28
  buildTruncatedArgumentsError,
29
29
  getDangerLevel,
30
30
  isFileWriteTool,
31
31
  runLeanAgentLoop
32
- } from "./chunk-FCPYMBTA.js";
32
+ } from "./chunk-XZNRZGG2.js";
33
33
  import {
34
34
  EnvLoader,
35
35
  NetworkError,
@@ -40,14 +40,14 @@ import {
40
40
  } from "./chunk-4BKXL7SM.js";
41
41
  import {
42
42
  runTestsTool
43
- } from "./chunk-HBR33J6J.js";
43
+ } from "./chunk-HPDI4F5F.js";
44
44
  import {
45
45
  CONFIG_DIR_NAME,
46
46
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
47
47
  SUBAGENT_ALLOWED_TOOLS,
48
48
  SUBAGENT_DEFAULT_MAX_ROUNDS,
49
49
  SUBAGENT_MAX_ROUNDS_LIMIT
50
- } from "./chunk-KRENSXPC.js";
50
+ } from "./chunk-KWYJFLXY.js";
51
51
  import {
52
52
  loadChatIndex,
53
53
  searchChatMemory
@@ -169,9 +169,9 @@ function buildWrappedCommand(command, id, isWindows) {
169
169
  if (!isWindows) {
170
170
  return `echo "${markerStart}"; ${command}; echo "${markerEnd}:$?"`;
171
171
  }
172
- const inner = `${WIN_UTF8_PREAMBLE} Write-Output "${markerStart}"; ${command}; $__ok = $?; $__ec = $LASTEXITCODE; $__code = if ($__ok) { 0 } elseif ($null -ne $__ec -and $__ec -ne 0) { $__ec } else { 1 }; Write-Output "${markerEnd}:$__code"`;
173
- const b64 = Buffer.from(inner, "utf-8").toString("base64");
174
- return `Invoke-Expression ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${b64}')))`;
172
+ const b64 = Buffer.from(command, "utf-8").toString("base64");
173
+ const decoded = `[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${b64}'))`;
174
+ return `${WIN_UTF8_PREAMBLE} Write-Output "${markerStart}"; $global:LASTEXITCODE = 0; Invoke-Expression (${decoded}); $__ok = $?; $__ec = $LASTEXITCODE; $__code = if ($null -ne $__ec -and $__ec -ne 0) { $__ec } elseif ($__ok) { 0 } else { 1 }; Write-Output "${markerEnd}:$__code"`;
175
175
  }
176
176
  async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
177
177
  let entry = pool.get(sessionKey);
@@ -3477,7 +3477,9 @@ Note: Path can be absolute or relative to cwd.`,
3477
3477
  }
3478
3478
  const lines = [];
3479
3479
  if (anyFailed && stopOnError) {
3480
- lines.push(`ERROR: Batch edit aborted \u2014 ${appliedCount}/${edits.length} applied, then edit #${appliedCount} failed. No changes written (stop_on_error=true).`);
3480
+ const failedIdx = reports.find((r) => !r.ok)?.index;
3481
+ const failedLabel = failedIdx != null ? `#${failedIdx + 1}` : "an edit";
3482
+ lines.push(`ERROR: Batch edit aborted \u2014 ${appliedCount}/${edits.length} applied, then edit ${failedLabel} failed. No changes written (stop_on_error=true).`);
3481
3483
  } else if (anyFailed) {
3482
3484
  lines.push(`Partial success: ${appliedCount}/${edits.length} edits applied to ${filePath} (stop_on_error=false).`);
3483
3485
  } else {
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ProviderRegistry
4
- } from "./chunk-U2HN3EW6.js";
4
+ } from "./chunk-25HFWCM3.js";
5
5
  import {
6
6
  ConfigManager
7
- } from "./chunk-K6WMQBUP.js";
7
+ } from "./chunk-MTIS7H6V.js";
8
8
  import {
9
9
  getActivePluginAssets,
10
10
  getPendingHookTrust,
@@ -14,7 +14,7 @@ import {
14
14
  listHooks,
15
15
  listInstalledPlugins,
16
16
  resetStats
17
- } from "./chunk-Y7UZSFGS.js";
17
+ } from "./chunk-JEJHNVA2.js";
18
18
  import {
19
19
  getGitRoot
20
20
  } from "./chunk-HOSJZMQS.js";
@@ -26,7 +26,7 @@ import {
26
26
  MCP_PROJECT_CONFIG_NAME,
27
27
  MEMORY_FILE_NAME,
28
28
  VERSION
29
- } from "./chunk-KRENSXPC.js";
29
+ } from "./chunk-KWYJFLXY.js";
30
30
 
31
31
  // src/diagnostics/doctor-report.ts
32
32
  import { existsSync as existsSync3, statSync as statSync2 } from "fs";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-KRENSXPC.js";
4
+ } from "./chunk-KWYJFLXY.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync, spawnSync } from "child_process";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  CONFIG_DIR_NAME,
4
4
  PLUGINS_DIR_NAME
5
- } from "./chunk-KRENSXPC.js";
5
+ } from "./chunk-KWYJFLXY.js";
6
6
  import {
7
7
  atomicWriteFileSync
8
8
  } from "./chunk-IW3Q7AE5.js";
@@ -6,7 +6,7 @@ import {
6
6
  MEMORY_FILE_NAME,
7
7
  MEMORY_MAX_CHARS,
8
8
  MEMORY_STORE_FILE_NAME
9
- } from "./chunk-26C6YZ3A.js";
9
+ } from "./chunk-ZQZEBPAQ.js";
10
10
  import {
11
11
  atomicWriteFileSync
12
12
  } from "./chunk-6BUTA5VW.js";
@@ -6,7 +6,7 @@ import {
6
6
  MEMORY_FILE_NAME,
7
7
  MEMORY_MAX_CHARS,
8
8
  MEMORY_STORE_FILE_NAME
9
- } from "./chunk-KRENSXPC.js";
9
+ } from "./chunk-KWYJFLXY.js";
10
10
  import {
11
11
  DEFAULT_PATTERNS,
12
12
  redactString
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.260";
4
+ var VERSION = "0.4.262";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-KRENSXPC.js";
11
+ } from "./chunk-KWYJFLXY.js";
12
12
  import {
13
13
  atomicWriteFileSync
14
14
  } from "./chunk-IW3Q7AE5.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  VERSION
4
- } from "./chunk-KRENSXPC.js";
4
+ } from "./chunk-KWYJFLXY.js";
5
5
 
6
6
  // src/cli/ci-format.ts
7
7
  var DEFAULT_CI_THRESHOLDS = {
@@ -188,6 +188,52 @@ function buildWriteRoundReminder(writtenCount) {
188
188
  You have successfully called write_file ${writtenCount} time(s) so far in this turn. If there are more files to write, call write_file NOW for the next file. Do NOT produce a text summary until ALL files have been written via tool calls.`;
189
189
  }
190
190
  var HALLUCINATION_CORRECTION_MESSAGE = "You did NOT actually call the write_file tool \u2014 the file was NOT created! Please immediately use the write_file tool via the function calling API to perform the actual file write. Do NOT describe file content in text \u2014 you MUST invoke write_file through the tool_calls mechanism.";
191
+ var ACTION_WORD_RE = /(?:已[生保写创]|saved|written|created|完成.*(?:写入|保存|创建|生成)|输出|file\s+(?:saved|written|created))/i;
192
+ var CLAUSE_BOUNDARY_RE = /[。.!?!?;;,,]/;
193
+ var LIST_LINE_RE = /^\s*(?:[-*+]|\d+[.)]|\|)/;
194
+ function hasWriteIntentNear(content, pathStart, pathEnd) {
195
+ const lineStart = content.lastIndexOf("\n", pathStart - 1) + 1;
196
+ const lineEndRaw = content.indexOf("\n", pathEnd);
197
+ const lineEnd = lineEndRaw === -1 ? content.length : lineEndRaw;
198
+ const currentLine = content.slice(lineStart, lineEnd);
199
+ const prevLineStart = lineStart === 0 ? 0 : content.lastIndexOf("\n", lineStart - 2) + 1;
200
+ const prevLine = lineStart === 0 ? "" : content.slice(prevLineStart, lineStart - 1);
201
+ const introColon = /[::]\s*$/.test(prevLine);
202
+ if (LIST_LINE_RE.test(currentLine) || introColon) {
203
+ let pos = pathStart;
204
+ let linesBack = 0;
205
+ while (linesBack < 9 && pos > 0) {
206
+ pos--;
207
+ if (content[pos] === "\n") linesBack++;
208
+ }
209
+ const windowStart = pos === 0 ? 0 : pos + 1;
210
+ return ACTION_WORD_RE.test(content.slice(windowStart, lineEnd));
211
+ }
212
+ const boundaryBefore = (from) => {
213
+ for (let i = from - 1; i >= lineStart; i--) {
214
+ if (CLAUSE_BOUNDARY_RE.test(content[i] ?? "")) return i + 1;
215
+ }
216
+ return lineStart;
217
+ };
218
+ let clauseEnd = lineEnd;
219
+ for (let i = pathEnd; i < lineEnd; i++) {
220
+ if (CLAUSE_BOUNDARY_RE.test(content[i] ?? "")) {
221
+ clauseEnd = i + 1;
222
+ break;
223
+ }
224
+ }
225
+ const isBareClause = (text2) => text2.replace(/`[^`\n]*`/g, "").replace(content.slice(pathStart, pathEnd), "").replace(/[\s'"“”‘’()()、,,。;;::+\-和与及]/g, "").length <= 2;
226
+ let start = boundaryBefore(pathStart);
227
+ let text = content.slice(start, clauseEnd);
228
+ if (ACTION_WORD_RE.test(text)) return true;
229
+ for (let step = 0; step < 6 && start > lineStart && isBareClause(text); step++) {
230
+ const prevEnd = start - 1;
231
+ start = boundaryBefore(prevEnd);
232
+ text = content.slice(start, prevEnd);
233
+ if (ACTION_WORD_RE.test(text)) return true;
234
+ }
235
+ return false;
236
+ }
191
237
  function extractClaimedFilePaths(content) {
192
238
  const paths = /* @__PURE__ */ new Set();
193
239
  const add = (p) => {
@@ -195,36 +241,19 @@ function extractClaimedFilePaths(content) {
195
241
  if (trimmed && /\.\w{1,6}$/.test(trimmed)) paths.add(trimmed);
196
242
  };
197
243
  let m;
198
- const actionLineRe = /(?:已[生保写创]|saved|written|created|完成.*(?:写入|保存|创建|生成)|输出|file\s+(?:saved|written|created))/i;
199
244
  const backtickRe = /`([^`\n]+?\.\w{1,6})`/g;
200
245
  while ((m = backtickRe.exec(content)) !== null) {
201
- let pos = m.index;
202
- let linesBack = 0;
203
- while (linesBack < 9 && pos > 0) {
204
- pos--;
205
- if (content[pos] === "\n") linesBack++;
206
- }
207
- const windowStart = pos === 0 ? 0 : pos + 1;
208
- const lineEndIdx = content.indexOf("\n", m.index + m[0].length);
209
- const window = content.slice(windowStart, lineEndIdx === -1 ? void 0 : lineEndIdx);
210
- if (actionLineRe.test(window)) add(m[1]);
246
+ const pathStart = m.index + m[0].indexOf(m[1]);
247
+ if (hasWriteIntentNear(content, pathStart, pathStart + m[1].length)) add(m[1]);
211
248
  }
212
- const zhRe = /(?:已保存(?:到)?|已写入(?:到)?|已创建|已生成|文件路径[::]|保存为|写入到)\s*[`'”””]?([^\s`'”””,,。\n]+?\.\w{1,6})/g;
249
+ const zhRe = /(?:已保存(?:到)?|已写入(?:到)?|已创建|已生成|文件路径[::]|保存为|写入到)\s*[`'”””]?([^\s`'”””,,。\n]+\.\w{1,6})/g;
213
250
  while ((m = zhRe.exec(content)) !== null) add(m[1]);
214
- const enRe = /(?:saved|written|created)\s+(?:to|as|at)\s+[`'”]?([^\s`'”\n,]+?\.\w{1,6})/gi;
251
+ const enRe = /(?:saved|written|created)\s+(?:to|as|at)\s+[`'”]?([^\s`'”\n,]+\.\w{1,6})/gi;
215
252
  while ((m = enRe.exec(content)) !== null) add(m[1]);
216
253
  const checkRe = /✅[^\n`]*?[`'”]?([^\s`'”\n,,。]+?\.\w{1,6})/g;
217
254
  while ((m = checkRe.exec(content)) !== null) {
218
- let pos = m.index;
219
- let linesBack = 0;
220
- while (linesBack < 9 && pos > 0) {
221
- pos--;
222
- if (content[pos] === "\n") linesBack++;
223
- }
224
- const windowStart = pos === 0 ? 0 : pos + 1;
225
- const lineEndIdx = content.indexOf("\n", m.index + m[0].length);
226
- const window = content.slice(windowStart, lineEndIdx === -1 ? void 0 : lineEndIdx);
227
- if (actionLineRe.test(window)) add(m[1]);
255
+ const pathStart = m.index + m[0].lastIndexOf(m[1]);
256
+ if (hasWriteIntentNear(content, pathStart, pathStart + m[1].length)) add(m[1]);
228
257
  }
229
258
  return Array.from(paths);
230
259
  }
@@ -337,7 +366,7 @@ function findPhantomClaims(content, extraMessages) {
337
366
  }
338
367
  function buildPhantomCorrectionMessage(phantoms) {
339
368
  const list = phantoms.map((p) => ` - ${p}`).join("\n");
340
- return "You claimed to have written the following file(s), but no matching write_file tool call was actually made in this turn:\n" + list + '\n\nEach of these files does NOT exist on disk. You MUST now invoke write_file (via the function calling API) for every missing file listed above. Do NOT output another "completion summary" until the tool calls have actually been made.';
369
+ return 'Your reply mentions the following file(s) alongside a "written / saved / created" claim, but no matching write_file or edit_file tool call was made in this turn:\n' + list + "\n\nIf you did intend to create or modify them, invoke write_file / edit_file NOW \u2014 they do not exist on disk yet, and you must not report the task as done until the tool calls return.\n\nIf you were only REFERENCING them (files you merely read, cited as examples, or named as prior art), then this was a false alarm: do NOT create them, do NOT overwrite anything. Simply restate your answer and say explicitly which of the listed paths you only read or cited. Creating a file that the user never asked for is worse than the false alarm.";
341
370
  }
342
371
  var DSML_PIPE_CLASS = "[|\\uFF5C\\u2502\\u2503\\u01C0]";
343
372
  var PSEUDO_TOOL_CALL_PATTERNS = [
@@ -972,6 +1001,8 @@ async function runAgentLoop(host) {
972
1001
  const freeRounds = new FreeRoundTracker();
973
1002
  const roundToolHistory = [];
974
1003
  const hasWriteTools = host.toolDefs.some((t) => t.name === "write_file" || t.name === "edit_file");
1004
+ let hallucinationRetries = 0;
1005
+ const MAX_HALLUCINATION_RETRIES = 2;
975
1006
  for (let round = 0; round < maxToolRounds; round++) {
976
1007
  if (host.signal?.aborted) return { reason: "aborted", usage };
977
1008
  host.onRoundStart?.(round, maxToolRounds);
@@ -1009,7 +1040,8 @@ async function runAgentLoop(host) {
1009
1040
  const phantomPaths = (coarseHallucination || alreadyWrote) && !host.planMode && hasWriteTools && content ? findPhantomClaims(content, extraMessages) : [];
1010
1041
  const bashRanThisTurn = extractBashCommands(extraMessages).length > 0;
1011
1042
  const coarseShouldFire = coarseHallucination && !bashRanThisTurn;
1012
- if ((phantomPaths.length > 0 || coarseShouldFire) && round < maxToolRounds - 1) {
1043
+ if ((phantomPaths.length > 0 || coarseShouldFire) && round < maxToolRounds - 1 && hallucinationRetries < MAX_HALLUCINATION_RETRIES) {
1044
+ hallucinationRetries++;
1013
1045
  host.onHallucinationRetry?.({ phantomPaths, round, alreadyRendered });
1014
1046
  const correctionMsg = phantomPaths.length > 0 ? buildPhantomCorrectionMessage(phantomPaths) : HALLUCINATION_CORRECTION_MESSAGE;
1015
1047
  const reasoningField = outcome.reasoningContent ? { reasoning_content: outcome.reasoningContent } : host.providerId === "deepseek" ? { reasoning_content: "" } : {};
@@ -1,5 +1,5 @@
1
1
  // src/core/constants.ts
2
- var VERSION = "0.4.260";
2
+ var VERSION = "0.4.262";
3
3
  var APP_NAME = "ai-cli";
4
4
  var CONFIG_DIR_NAME = ".aicli";
5
5
  var CONFIG_FILE_NAME = "config.json";
@@ -3,14 +3,14 @@ import {
3
3
  CI_COMMENT_MARKER,
4
4
  countSeverity,
5
5
  runCi
6
- } from "./chunk-2BGNEVS5.js";
7
- import "./chunk-VJP2GLW5.js";
6
+ } from "./chunk-2XPJRRBB.js";
7
+ import "./chunk-WAAFA32V.js";
8
8
  import "./chunk-HLWUDRBO.js";
9
- import "./chunk-U2HN3EW6.js";
10
- import "./chunk-K6WMQBUP.js";
11
- import "./chunk-FCPYMBTA.js";
9
+ import "./chunk-25HFWCM3.js";
10
+ import "./chunk-MTIS7H6V.js";
11
+ import "./chunk-XZNRZGG2.js";
12
12
  import "./chunk-TU3L3PW7.js";
13
- import "./chunk-KRENSXPC.js";
13
+ import "./chunk-KWYJFLXY.js";
14
14
  import "./chunk-IW3Q7AE5.js";
15
15
  export {
16
16
  CI_COMMENT_MARKER,
@@ -6,8 +6,8 @@ import {
6
6
  formatCiResult,
7
7
  formatCiSarif,
8
8
  normalizeCiThresholds
9
- } from "./chunk-VJP2GLW5.js";
10
- import "./chunk-KRENSXPC.js";
9
+ } from "./chunk-WAAFA32V.js";
10
+ import "./chunk-KWYJFLXY.js";
11
11
  export {
12
12
  DEFAULT_CI_THRESHOLDS,
13
13
  detectCiGateSignals,
@@ -38,7 +38,7 @@ import {
38
38
  TEST_TIMEOUT,
39
39
  VERSION,
40
40
  buildUserIdentityPrompt
41
- } from "./chunk-KRENSXPC.js";
41
+ } from "./chunk-KWYJFLXY.js";
42
42
  export {
43
43
  AGENTIC_BEHAVIOR_GUIDELINE,
44
44
  APP_NAME,
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  formatDoctorReport,
4
4
  runDoctorCli
5
- } from "./chunk-63M2ELJS.js";
6
- import "./chunk-U2HN3EW6.js";
7
- import "./chunk-K6WMQBUP.js";
8
- import "./chunk-Y7UZSFGS.js";
9
- import "./chunk-FCPYMBTA.js";
5
+ } from "./chunk-FWBSGBB7.js";
6
+ import "./chunk-25HFWCM3.js";
7
+ import "./chunk-MTIS7H6V.js";
8
+ import "./chunk-JEJHNVA2.js";
9
+ import "./chunk-XZNRZGG2.js";
10
10
  import "./chunk-TU3L3PW7.js";
11
11
  import "./chunk-HOSJZMQS.js";
12
- import "./chunk-KRENSXPC.js";
12
+ import "./chunk-KWYJFLXY.js";
13
13
  import "./chunk-IW3Q7AE5.js";
14
14
  export {
15
15
  formatDoctorReport,