jinzd-ai-cli 0.4.193 → 0.4.194

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.
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-VQT27CZK.js";
4
+ } from "./chunk-ZVMXIRBH.js";
5
5
  import "./chunk-TZQHYZKT.js";
6
- import "./chunk-P3PTUSP4.js";
6
+ import "./chunk-ZEHHAE3B.js";
7
7
  import {
8
8
  atomicWriteFileSync
9
9
  } from "./chunk-IW3Q7AE5.js";
@@ -3,7 +3,7 @@ import {
3
3
  detectsHallucinatedFileOp,
4
4
  repairToolCallArguments,
5
5
  schemaToJsonSchema
6
- } from "./chunk-JOR572WG.js";
6
+ } from "./chunk-ZRG2FPC6.js";
7
7
  import {
8
8
  AuthError,
9
9
  ProviderError,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-P3PTUSP4.js";
4
+ } from "./chunk-ZEHHAE3B.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
  VERSION
5
- } from "./chunk-P3PTUSP4.js";
5
+ } from "./chunk-ZEHHAE3B.js";
6
6
 
7
7
  // src/diagnostics/crash-log.ts
8
8
  import {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  truncateForPersist
4
- } from "./chunk-SI5EO3NJ.js";
4
+ } from "./chunk-ZJX6ZU5D.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-P3PTUSP4.js";
14
+ } from "./chunk-ZEHHAE3B.js";
15
15
 
16
16
  // src/mcp/client.ts
17
17
  import { spawn } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CONFIG_DIR_NAME
4
- } from "./chunk-P3PTUSP4.js";
4
+ } from "./chunk-ZEHHAE3B.js";
5
5
  import {
6
6
  atomicWriteFileSync
7
7
  } from "./chunk-IW3Q7AE5.js";
@@ -6,7 +6,7 @@ import { platform } from "os";
6
6
  import chalk from "chalk";
7
7
 
8
8
  // src/core/constants.ts
9
- var VERSION = "0.4.193";
9
+ var VERSION = "0.4.194";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.193";
4
+ var VERSION = "0.4.194";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -5,15 +5,15 @@ import {
5
5
  } from "./chunk-T2NL5ZIA.js";
6
6
  import {
7
7
  runTestsTool
8
- } from "./chunk-7PX3ZX4G.js";
8
+ } from "./chunk-3KCEPI4E.js";
9
9
  import {
10
10
  runTool
11
- } from "./chunk-SHI5UFUH.js";
11
+ } from "./chunk-CKBBAXCA.js";
12
12
  import {
13
13
  getDangerLevel,
14
14
  isFileWriteTool,
15
15
  runLeanAgentLoop
16
- } from "./chunk-JOR572WG.js";
16
+ } from "./chunk-ZRG2FPC6.js";
17
17
  import {
18
18
  EnvLoader,
19
19
  NetworkError,
@@ -26,7 +26,7 @@ import {
26
26
  SUBAGENT_ALLOWED_TOOLS,
27
27
  SUBAGENT_DEFAULT_MAX_ROUNDS,
28
28
  SUBAGENT_MAX_ROUNDS_LIMIT
29
- } from "./chunk-P3PTUSP4.js";
29
+ } from "./chunk-ZEHHAE3B.js";
30
30
  import {
31
31
  fileCheckpoints
32
32
  } from "./chunk-4BKXL7SM.js";
@@ -536,6 +536,12 @@ This fresh stream has NO tools \u2014 output is captured verbatim. STOP emitting
536
536
  summary: `File saved: ${saveToFile} (${lines} lines, ${bytes} bytes)`
537
537
  };
538
538
  }
539
+ function buildDeferredSaveInstruction(saveToFile) {
540
+ return `[save_last_response \u65E0\u6CD5\u81EA\u52A8\u751F\u6210\u6587\u6863]
541
+ content-only \u6D41\u4EA7\u51FA\u7684\u662F\u5DE5\u5177\u8C03\u7528\u6807\u8BB0 / \u5185\u5FC3\u72EC\u767D\u800C\u975E\u6587\u6863\u6B63\u6587\uFF0C${saveToFile} \u672A\u4FDD\u5B58\u3002
542
+
543
+ \u4E0D\u8981\u518D\u8C03\u7528 save_last_response\u3002\u8BF7\u76F4\u63A5\u7528**\u666E\u901A\u6587\u672C\u56DE\u590D**\u628A\u5B8C\u6574\u6587\u6863\u5199\u51FA\u6765\uFF1A\u7EAF markdown\uFF0C\u7B2C\u4E00\u4E2A\u5B57\u7B26\u5C31\u662F\u9876\u7EA7\u6807\u9898\uFF08\u5982 "# \u5BA1\u8BA1\u62A5\u544A"\uFF09\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5DE5\u5177\u8C03\u7528\u3001\u4E0D\u8981\u524D\u8A00\u94FA\u57AB\u3002\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u628A\u4F60\u8FD9\u6761\u56DE\u590D\u4FDD\u5B58\u5230 ${saveToFile}\u3002`;
544
+ }
539
545
  function teeStreamErrorSummary(saveToFile, errMsg) {
540
546
  return `[save_last_response failed] streaming was interrupted: ${errMsg}. ${saveToFile} (partial) was deleted. Retry \u2014 and consider producing a more compact output (split very large reports across multiple save_last_response calls if the previous attempt timed out).`;
541
547
  }
@@ -1046,7 +1052,9 @@ export {
1046
1052
  stripToolCallReminder,
1047
1053
  TEE_FINAL_USER_NUDGE,
1048
1054
  CONTENT_ONLY_STREAM_REMINDER,
1055
+ isCleanDocumentBody,
1049
1056
  evaluateTeeContent,
1057
+ buildDeferredSaveInstruction,
1050
1058
  teeStreamErrorSummary,
1051
1059
  ThinkTagFilter,
1052
1060
  repairToolCallArguments,
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-P3PTUSP4.js";
11
+ } from "./chunk-ZEHHAE3B.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -6,15 +6,15 @@ import {
6
6
  } from "./chunk-HLWUDRBO.js";
7
7
  import {
8
8
  ProviderRegistry
9
- } from "./chunk-BGXQGCEO.js";
10
- import "./chunk-JOR572WG.js";
9
+ } from "./chunk-2BLBXGOE.js";
10
+ import "./chunk-ZRG2FPC6.js";
11
11
  import {
12
12
  ConfigManager
13
- } from "./chunk-VQT27CZK.js";
13
+ } from "./chunk-ZVMXIRBH.js";
14
14
  import "./chunk-TZQHYZKT.js";
15
15
  import {
16
16
  VERSION
17
- } from "./chunk-P3PTUSP4.js";
17
+ } from "./chunk-ZEHHAE3B.js";
18
18
 
19
19
  // src/cli/ci.ts
20
20
  import { execFileSync, execSync } from "child_process";
@@ -36,7 +36,7 @@ import {
36
36
  TEST_TIMEOUT,
37
37
  VERSION,
38
38
  buildUserIdentityPrompt
39
- } from "./chunk-P3PTUSP4.js";
39
+ } from "./chunk-ZEHHAE3B.js";
40
40
  export {
41
41
  AGENTIC_BEHAVIOR_GUIDELINE,
42
42
  APP_NAME,
@@ -2,26 +2,26 @@
2
2
  import {
3
3
  getConfigDirUsage,
4
4
  listRecentCrashes
5
- } from "./chunk-PO3ZY3PN.js";
5
+ } from "./chunk-5IVUINDO.js";
6
6
  import {
7
7
  ProviderRegistry
8
- } from "./chunk-BGXQGCEO.js";
8
+ } from "./chunk-2BLBXGOE.js";
9
9
  import {
10
10
  getStatsSnapshot,
11
11
  getTopFailingTools,
12
12
  getTopUsedTools,
13
13
  resetStats
14
- } from "./chunk-SHI5UFUH.js";
15
- import "./chunk-JOR572WG.js";
14
+ } from "./chunk-CKBBAXCA.js";
15
+ import "./chunk-ZRG2FPC6.js";
16
16
  import {
17
17
  ConfigManager
18
- } from "./chunk-VQT27CZK.js";
18
+ } from "./chunk-ZVMXIRBH.js";
19
19
  import "./chunk-TZQHYZKT.js";
20
20
  import {
21
21
  DEV_STATE_FILE_NAME,
22
22
  MEMORY_FILE_NAME,
23
23
  VERSION
24
- } from "./chunk-P3PTUSP4.js";
24
+ } from "./chunk-ZEHHAE3B.js";
25
25
  import "./chunk-IW3Q7AE5.js";
26
26
 
27
27
  // src/diagnostics/doctor-cli.ts
@@ -36,7 +36,7 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-IFETB4PY.js";
39
+ } from "./chunk-LTBUGDLE.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -2027,6 +2027,12 @@ This fresh stream has NO tools \u2014 output is captured verbatim. STOP emitting
2027
2027
  summary: `File saved: ${saveToFile} (${lines} lines, ${bytes} bytes)`
2028
2028
  };
2029
2029
  }
2030
+ function buildDeferredSaveInstruction(saveToFile) {
2031
+ return `[save_last_response \u65E0\u6CD5\u81EA\u52A8\u751F\u6210\u6587\u6863]
2032
+ content-only \u6D41\u4EA7\u51FA\u7684\u662F\u5DE5\u5177\u8C03\u7528\u6807\u8BB0 / \u5185\u5FC3\u72EC\u767D\u800C\u975E\u6587\u6863\u6B63\u6587\uFF0C${saveToFile} \u672A\u4FDD\u5B58\u3002
2033
+
2034
+ \u4E0D\u8981\u518D\u8C03\u7528 save_last_response\u3002\u8BF7\u76F4\u63A5\u7528**\u666E\u901A\u6587\u672C\u56DE\u590D**\u628A\u5B8C\u6574\u6587\u6863\u5199\u51FA\u6765\uFF1A\u7EAF markdown\uFF0C\u7B2C\u4E00\u4E2A\u5B57\u7B26\u5C31\u662F\u9876\u7EA7\u6807\u9898\uFF08\u5982 "# \u5BA1\u8BA1\u62A5\u544A"\uFF09\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5DE5\u5177\u8C03\u7528\u3001\u4E0D\u8981\u524D\u8A00\u94FA\u57AB\u3002\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u628A\u4F60\u8FD9\u6761\u56DE\u590D\u4FDD\u5B58\u5230 ${saveToFile}\u3002`;
2035
+ }
2030
2036
  function teeStreamErrorSummary(saveToFile, errMsg) {
2031
2037
  return `[save_last_response failed] streaming was interrupted: ${errMsg}. ${saveToFile} (partial) was deleted. Retry \u2014 and consider producing a more compact output (split very large reports across multiple save_last_response calls if the previous attempt timed out).`;
2032
2038
  }
@@ -12760,6 +12766,7 @@ ${mcpBudgetNote}` : "");
12760
12766
  const supportsStreamingTools = typeof provider.chatWithToolsStream === "function";
12761
12767
  const ac = new AbortController();
12762
12768
  this.abortController = ac;
12769
+ let pendingTeeSave = null;
12763
12770
  try {
12764
12771
  const loopResult = await runAgentLoop({
12765
12772
  maxToolRounds,
@@ -12903,6 +12910,18 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
12903
12910
  },
12904
12911
  onFinalContent: (content, { reasoningContent }) => {
12905
12912
  this.send({ type: "response_done", content, usage });
12913
+ if (pendingTeeSave && isCleanDocumentBody(content)) {
12914
+ try {
12915
+ mkdirSync10(dirname5(pendingTeeSave), { recursive: true });
12916
+ writeFileSync7(pendingTeeSave, content, "utf-8");
12917
+ undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
12918
+ const lines = content.split("\n").length;
12919
+ this.send({ type: "info", message: `\u2705 Saved (from reply): ${pendingTeeSave} (${lines} lines, ${content.length} chars)` });
12920
+ } catch (saveErr) {
12921
+ this.send({ type: "info", message: `\u2717 Could not save to ${pendingTeeSave}: ${saveErr.message ?? saveErr}` });
12922
+ }
12923
+ pendingTeeSave = null;
12924
+ }
12906
12925
  session.addMessage({
12907
12926
  role: "assistant",
12908
12927
  content,
@@ -12960,6 +12979,15 @@ ${summaryContent}`,
12960
12979
  // 与 REPL 不同:Web 端 tee 成功后继续 agentic 循环(返回 'continue'),
12961
12980
  // 让模型基于工具结果给出最终文本。
12962
12981
  runSaveLastResponseTee: async ({ toolCalls, call, saveToFile, extraMessages, reasoningContent }) => {
12982
+ if (pendingTeeSave === saveToFile) {
12983
+ const results = toolCalls.map((tc) => ({
12984
+ callId: tc.id,
12985
+ content: tc.id === call.id ? buildDeferredSaveInstruction(saveToFile) : "[skipped: write the document as plain text, not via tools]",
12986
+ isError: tc.id === call.id
12987
+ }));
12988
+ extraMessages.push(...provider.buildToolResultMessages(toolCalls, results, reasoningContent));
12989
+ return "continue";
12990
+ }
12963
12991
  const teeResult = await this.runSaveLastResponseTee(
12964
12992
  provider,
12965
12993
  call,
@@ -12972,6 +13000,7 @@ ${summaryContent}`,
12972
13000
  ac,
12973
13001
  usage
12974
13002
  );
13003
+ if (teeResult.deferred) pendingTeeSave = saveToFile;
12975
13004
  const teeToolResults = toolCalls.map((tc) => {
12976
13005
  if (tc.id === call.id) {
12977
13006
  return {
@@ -13028,6 +13057,7 @@ ${summaryContent}`,
13028
13057
  * 场景极少。
13029
13058
  */
13030
13059
  async runSaveLastResponseTee(provider, call, saveToFile, apiMessages, extraMessages, systemPrompt, systemPromptVolatile, modelParams, ac, roundUsage) {
13060
+ let deferred = false;
13031
13061
  this.send({
13032
13062
  type: "tool_call_start",
13033
13063
  callId: call.id,
@@ -13080,7 +13110,8 @@ ${summaryContent}`,
13080
13110
  if (verdict.kind === "reject") {
13081
13111
  cleanupRejectedTeeFile(saveToFile);
13082
13112
  isError = true;
13083
- summary = verdict.summary;
13113
+ deferred = true;
13114
+ summary = buildDeferredSaveInstruction(saveToFile);
13084
13115
  fullContent = "";
13085
13116
  } else {
13086
13117
  if (verdict.kind === "salvaged" || verdict.kind === "fallback") {
@@ -13115,7 +13146,7 @@ ${summaryContent}`,
13115
13146
  content: summary,
13116
13147
  isError
13117
13148
  });
13118
- return { content: fullContent, summary, isError };
13149
+ return { content: fullContent, summary, isError, deferred };
13119
13150
  }
13120
13151
  /**
13121
13152
  * Consume streaming tool call events and forward to client.
@@ -14149,7 +14180,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
14149
14180
  case "test": {
14150
14181
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
14151
14182
  try {
14152
- const { executeTests } = await import("./run-tests-4QWMA4ZF.js");
14183
+ const { executeTests } = await import("./run-tests-RFK53W3D.js");
14153
14184
  const argStr = args.join(" ").trim();
14154
14185
  let testArgs = {};
14155
14186
  if (argStr) {
@@ -154,7 +154,7 @@ ${content}`);
154
154
  }
155
155
  }
156
156
  async function runTaskMode(config, providers, configManager, topic) {
157
- const { TaskOrchestrator } = await import("./task-orchestrator-RI63ZZNN.js");
157
+ const { TaskOrchestrator } = await import("./task-orchestrator-GJBPVYFW.js");
158
158
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
159
159
  let interrupted = false;
160
160
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  saveDevState,
16
16
  sessionHasMeaningfulContent,
17
17
  setupProxy
18
- } from "./chunk-MI23I43Y.js";
18
+ } from "./chunk-6VMXFFO3.js";
19
19
  import {
20
20
  ToolExecutor,
21
21
  ToolRegistry,
@@ -35,10 +35,10 @@ import {
35
35
  spawnAgentContext,
36
36
  theme,
37
37
  undoStack
38
- } from "./chunk-SI5EO3NJ.js";
38
+ } from "./chunk-ZJX6ZU5D.js";
39
39
  import "./chunk-T2NL5ZIA.js";
40
40
  import "./chunk-BXP6YZ2P.js";
41
- import "./chunk-7PX3ZX4G.js";
41
+ import "./chunk-3KCEPI4E.js";
42
42
  import {
43
43
  SessionManager,
44
44
  getContentText
@@ -55,34 +55,36 @@ import {
55
55
  getConfigDirUsage,
56
56
  listRecentCrashes,
57
57
  writeCrashLog
58
- } from "./chunk-PO3ZY3PN.js";
58
+ } from "./chunk-5IVUINDO.js";
59
59
  import {
60
60
  ProviderRegistry
61
- } from "./chunk-BGXQGCEO.js";
61
+ } from "./chunk-2BLBXGOE.js";
62
62
  import {
63
63
  getStatsSnapshot,
64
64
  getTopFailingTools,
65
65
  getTopUsedTools,
66
66
  installFlushOnExit
67
- } from "./chunk-SHI5UFUH.js";
67
+ } from "./chunk-CKBBAXCA.js";
68
68
  import {
69
69
  CONTENT_ONLY_STREAM_REMINDER,
70
70
  TEE_FINAL_USER_NUDGE,
71
71
  TOOL_CALL_REMINDER,
72
72
  ThinkTagFilter,
73
73
  accumulateUsage,
74
+ buildDeferredSaveInstruction,
74
75
  buildRoundBudgetHint,
75
76
  buildWriteRoundReminder,
76
77
  consumeToolCallStream,
77
78
  evaluateTeeContent,
78
79
  extractWrittenFilePaths,
80
+ isCleanDocumentBody,
79
81
  runAgentLoop,
80
82
  stripToolCallReminder,
81
83
  teeStreamErrorSummary
82
- } from "./chunk-JOR572WG.js";
84
+ } from "./chunk-ZRG2FPC6.js";
83
85
  import {
84
86
  ConfigManager
85
- } from "./chunk-VQT27CZK.js";
87
+ } from "./chunk-ZVMXIRBH.js";
86
88
  import {
87
89
  AuthError,
88
90
  ProviderError,
@@ -109,7 +111,7 @@ import {
109
111
  SKILLS_DIR_NAME,
110
112
  VERSION,
111
113
  buildUserIdentityPrompt
112
- } from "./chunk-P3PTUSP4.js";
114
+ } from "./chunk-ZEHHAE3B.js";
113
115
  import {
114
116
  formatGitContextForPrompt,
115
117
  getGitContext,
@@ -138,7 +140,7 @@ import { program } from "commander";
138
140
 
139
141
  // src/repl/repl.ts
140
142
  import * as readline from "readline";
141
- import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
143
+ import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync4 } from "fs";
142
144
  import { join as join4, resolve as resolve2, extname as extname2, dirname as dirname3, basename as basename2 } from "path";
143
145
  import chalk4 from "chalk";
144
146
 
@@ -1824,7 +1826,7 @@ No tools match "${filter}".
1824
1826
  const { join: join5 } = await import("path");
1825
1827
  const { existsSync: existsSync5 } = await import("fs");
1826
1828
  const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
1827
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-LEU54VQQ.js");
1829
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-CDVYNTU7.js");
1828
1830
  const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
1829
1831
  const cwd = process.cwd();
1830
1832
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2885,7 +2887,7 @@ ${hint}` : "")
2885
2887
  usage: "/test [command|filter]",
2886
2888
  async execute(args, ctx) {
2887
2889
  try {
2888
- const { executeTests } = await import("./run-tests-IL4342SV.js");
2890
+ const { executeTests } = await import("./run-tests-A2I3WZ2H.js");
2889
2891
  const argStr = args.join(" ").trim();
2890
2892
  let testArgs = {};
2891
2893
  if (argStr) {
@@ -6395,6 +6397,7 @@ ${mcpBudgetNote}` : "");
6395
6397
  const supportsStreamingTools = useStreaming && typeof provider.chatWithToolsStream === "function";
6396
6398
  let lastToolCallSignature = "";
6397
6399
  let repeatedToolCallCount = 0;
6400
+ let pendingTeeSave = null;
6398
6401
  this.setupInterjectionListener();
6399
6402
  try {
6400
6403
  const loopResult = await runAgentLoop({
@@ -6633,6 +6636,28 @@ ${decision.displayMessage}
6633
6636
  process.stdout.write("\n\n");
6634
6637
  }
6635
6638
  lastResponseStore.content = content;
6639
+ if (pendingTeeSave && isCleanDocumentBody(content)) {
6640
+ try {
6641
+ mkdirSync4(dirname3(pendingTeeSave), { recursive: true });
6642
+ writeFileSync2(pendingTeeSave, content, "utf-8");
6643
+ undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
6644
+ const lines = content.split("\n").length;
6645
+ process.stdout.write(theme.success(
6646
+ `
6647
+ \u2705 Saved (from your reply): ${pendingTeeSave} (${lines} lines, ${content.length} chars)
6648
+
6649
+ `
6650
+ ));
6651
+ } catch (saveErr) {
6652
+ process.stdout.write(theme.error(
6653
+ `
6654
+ \u2717 Could not save to ${pendingTeeSave}: ${saveErr.message ?? saveErr}
6655
+
6656
+ `
6657
+ ));
6658
+ }
6659
+ pendingTeeSave = null;
6660
+ }
6636
6661
  session.addMessage({
6637
6662
  role: "assistant",
6638
6663
  content,
@@ -6782,6 +6807,15 @@ Tip: You can continue the conversation by asking the AI to proceed.`
6782
6807
  // 'continue' 让模型重试
6783
6808
  runSaveLastResponseTee: async ({ toolCalls, saveToFile, extraMessages, reasoningContent }) => {
6784
6809
  spinner.stop();
6810
+ if (pendingTeeSave === saveToFile) {
6811
+ const results = toolCalls.map((tc) => ({
6812
+ callId: tc.id,
6813
+ content: tc.name === "save_last_response" ? buildDeferredSaveInstruction(saveToFile) : `[skipped: write the document as plain text, not via tools]`,
6814
+ isError: tc.name === "save_last_response"
6815
+ }));
6816
+ extraMessages.push(...provider.buildToolResultMessages(toolCalls, results, reasoningContent));
6817
+ return "continue";
6818
+ }
6785
6819
  const teeAc = this.setupStreamInterrupt();
6786
6820
  try {
6787
6821
  const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
@@ -6836,16 +6870,17 @@ Tip: You can continue the conversation by asking the AI to proceed.`
6836
6870
  if (verdict.kind === "reject") {
6837
6871
  cleanupRejectedTeeFile(saveToFile);
6838
6872
  const label = verdict.reason === "meta-narration" ? "meta-narration / leaked reasoning, not document body" : "pseudo-tool-call markup with no usable document body";
6839
- process.stdout.write(theme.error(
6873
+ pendingTeeSave = saveToFile;
6874
+ process.stdout.write(theme.warning(
6840
6875
  `
6841
- \u2717 Rejected save: response was ${label} (matched: ${verdict.matched})
6842
- ${saveToFile} was deleted; asking model to retry.
6876
+ \u26A0 Rejected save: response was ${label} (matched: ${verdict.matched})
6877
+ ${saveToFile} was deleted. Asking the model to write the document as plain text instead (will auto-save).
6843
6878
 
6844
6879
  `
6845
6880
  ));
6846
6881
  const errorResults = toolCalls.map((tc) => ({
6847
6882
  callId: tc.id,
6848
- content: tc.name === "save_last_response" ? verdict.summary : `[skipped: save_last_response was rejected and other parallel calls are abandoned]`,
6883
+ content: tc.name === "save_last_response" ? buildDeferredSaveInstruction(saveToFile) : `[skipped: save_last_response was rejected and other parallel calls are abandoned]`,
6849
6884
  isError: tc.name === "save_last_response"
6850
6885
  }));
6851
6886
  extraMessages.push(...provider.buildToolResultMessages(toolCalls, errorResults, reasoningContent));
@@ -7209,7 +7244,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7209
7244
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7210
7245
  process.exit(1);
7211
7246
  }
7212
- const { startWebServer } = await import("./server-KE7X3BHW.js");
7247
+ const { startWebServer } = await import("./server-6NLQUZKD.js");
7213
7248
  await startWebServer({ port, host: options.host });
7214
7249
  });
7215
7250
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
@@ -7376,16 +7411,16 @@ program.command("sessions").description("List recent conversation sessions").opt
7376
7411
  console.log(footer + "\n");
7377
7412
  });
7378
7413
  program.command("usage").description("Show token + cost usage grouped by provider/model (cross-session)").option("--days <n>", "Only the last N days (inclusive of today)").option("--month <ym>", "Only a specific month, format YYYY-MM (e.g. 2026-06)").option("--json", "Output as JSON (for scripting)").action(async (options) => {
7379
- const { runUsageCli } = await import("./usage-CG7PJ3AB.js");
7414
+ const { runUsageCli } = await import("./usage-7DVISDK2.js");
7380
7415
  await runUsageCli(options);
7381
7416
  });
7382
7417
  program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
7383
- const { runDoctorCli } = await import("./doctor-cli-4RKSIH7N.js");
7418
+ const { runDoctorCli } = await import("./doctor-cli-Q3QFVEWQ.js");
7384
7419
  await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
7385
7420
  });
7386
7421
  program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
7387
7422
  try {
7388
- const batch = await import("./batch-M6367YB4.js");
7423
+ const batch = await import("./batch-XGARS57W.js");
7389
7424
  switch (action) {
7390
7425
  case "submit":
7391
7426
  if (!arg) {
@@ -7428,7 +7463,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7428
7463
  }
7429
7464
  });
7430
7465
  program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
7431
- const { startMcpServer } = await import("./server-Y7TCCIGA.js");
7466
+ const { startMcpServer } = await import("./server-RULXWZD4.js");
7432
7467
  await startMcpServer({
7433
7468
  allowDestructive: !!options.allowDestructive,
7434
7469
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7437,7 +7472,7 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
7437
7472
  });
7438
7473
  });
7439
7474
  program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number; diff fetched via `gh pr diff <num>`", (v) => parseInt(v, 10)).option("--base <ref>", "Base ref for `git diff <ref>...HEAD` (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (options) => {
7440
- const { runCi } = await import("./ci-4MJ4EGKQ.js");
7475
+ const { runCi } = await import("./ci-4TYLP7HF.js");
7441
7476
  const result = await runCi({
7442
7477
  pr: options.pr,
7443
7478
  base: options.base,
@@ -7583,7 +7618,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7583
7618
  }),
7584
7619
  config.get("customProviders")
7585
7620
  );
7586
- const { startHub } = await import("./hub-XMUI5HMK.js");
7621
+ const { startHub } = await import("./hub-UORFAHQ3.js");
7587
7622
  await startHub(
7588
7623
  {
7589
7624
  topic: topic ?? "",
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-7PX3ZX4G.js";
6
- import "./chunk-P3PTUSP4.js";
5
+ } from "./chunk-3KCEPI4E.js";
6
+ import "./chunk-ZEHHAE3B.js";
7
7
  export {
8
8
  executeTests,
9
9
  runTestsTool
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-IFETB4PY.js";
4
+ } from "./chunk-LTBUGDLE.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -19,7 +19,7 @@ import {
19
19
  loadDevState,
20
20
  persistToolRound,
21
21
  setupProxy
22
- } from "./chunk-MI23I43Y.js";
22
+ } from "./chunk-6VMXFFO3.js";
23
23
  import {
24
24
  ToolExecutor,
25
25
  ToolRegistry,
@@ -38,10 +38,10 @@ import {
38
38
  spawnAgentContext,
39
39
  truncateOutput,
40
40
  undoStack
41
- } from "./chunk-SI5EO3NJ.js";
41
+ } from "./chunk-ZJX6ZU5D.js";
42
42
  import "./chunk-T2NL5ZIA.js";
43
43
  import "./chunk-BXP6YZ2P.js";
44
- import "./chunk-7PX3ZX4G.js";
44
+ import "./chunk-3KCEPI4E.js";
45
45
  import {
46
46
  SessionManager,
47
47
  getContentText
@@ -52,25 +52,27 @@ import {
52
52
  } from "./chunk-V37XOYOE.js";
53
53
  import {
54
54
  ProviderRegistry
55
- } from "./chunk-BGXQGCEO.js";
55
+ } from "./chunk-2BLBXGOE.js";
56
56
  import {
57
57
  runTool
58
- } from "./chunk-SHI5UFUH.js";
58
+ } from "./chunk-CKBBAXCA.js";
59
59
  import {
60
60
  CONTENT_ONLY_STREAM_REMINDER,
61
61
  TEE_FINAL_USER_NUDGE,
62
62
  TOOL_CALL_REMINDER,
63
+ buildDeferredSaveInstruction,
63
64
  buildRoundBudgetHint,
64
65
  consumeToolCallStream,
65
66
  evaluateTeeContent,
66
67
  getDangerLevel,
68
+ isCleanDocumentBody,
67
69
  runAgentLoop,
68
70
  stripToolCallReminder,
69
71
  teeStreamErrorSummary
70
- } from "./chunk-JOR572WG.js";
72
+ } from "./chunk-ZRG2FPC6.js";
71
73
  import {
72
74
  ConfigManager
73
- } from "./chunk-VQT27CZK.js";
75
+ } from "./chunk-ZVMXIRBH.js";
74
76
  import "./chunk-TZQHYZKT.js";
75
77
  import {
76
78
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -90,7 +92,7 @@ import {
90
92
  SKILLS_DIR_NAME,
91
93
  VERSION,
92
94
  buildUserIdentityPrompt
93
- } from "./chunk-P3PTUSP4.js";
95
+ } from "./chunk-ZEHHAE3B.js";
94
96
  import {
95
97
  formatGitContextForPrompt,
96
98
  getGitContext,
@@ -1023,6 +1025,7 @@ ${mcpBudgetNote}` : "");
1023
1025
  const supportsStreamingTools = typeof provider.chatWithToolsStream === "function";
1024
1026
  const ac = new AbortController();
1025
1027
  this.abortController = ac;
1028
+ let pendingTeeSave = null;
1026
1029
  try {
1027
1030
  const loopResult = await runAgentLoop({
1028
1031
  maxToolRounds,
@@ -1166,6 +1169,18 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
1166
1169
  },
1167
1170
  onFinalContent: (content, { reasoningContent }) => {
1168
1171
  this.send({ type: "response_done", content, usage });
1172
+ if (pendingTeeSave && isCleanDocumentBody(content)) {
1173
+ try {
1174
+ mkdirSync(dirname(pendingTeeSave), { recursive: true });
1175
+ writeFileSync(pendingTeeSave, content, "utf-8");
1176
+ undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
1177
+ const lines = content.split("\n").length;
1178
+ this.send({ type: "info", message: `\u2705 Saved (from reply): ${pendingTeeSave} (${lines} lines, ${content.length} chars)` });
1179
+ } catch (saveErr) {
1180
+ this.send({ type: "info", message: `\u2717 Could not save to ${pendingTeeSave}: ${saveErr.message ?? saveErr}` });
1181
+ }
1182
+ pendingTeeSave = null;
1183
+ }
1169
1184
  session.addMessage({
1170
1185
  role: "assistant",
1171
1186
  content,
@@ -1223,6 +1238,15 @@ ${summaryContent}`,
1223
1238
  // 与 REPL 不同:Web 端 tee 成功后继续 agentic 循环(返回 'continue'),
1224
1239
  // 让模型基于工具结果给出最终文本。
1225
1240
  runSaveLastResponseTee: async ({ toolCalls, call, saveToFile, extraMessages, reasoningContent }) => {
1241
+ if (pendingTeeSave === saveToFile) {
1242
+ const results = toolCalls.map((tc) => ({
1243
+ callId: tc.id,
1244
+ content: tc.id === call.id ? buildDeferredSaveInstruction(saveToFile) : "[skipped: write the document as plain text, not via tools]",
1245
+ isError: tc.id === call.id
1246
+ }));
1247
+ extraMessages.push(...provider.buildToolResultMessages(toolCalls, results, reasoningContent));
1248
+ return "continue";
1249
+ }
1226
1250
  const teeResult = await this.runSaveLastResponseTee(
1227
1251
  provider,
1228
1252
  call,
@@ -1235,6 +1259,7 @@ ${summaryContent}`,
1235
1259
  ac,
1236
1260
  usage
1237
1261
  );
1262
+ if (teeResult.deferred) pendingTeeSave = saveToFile;
1238
1263
  const teeToolResults = toolCalls.map((tc) => {
1239
1264
  if (tc.id === call.id) {
1240
1265
  return {
@@ -1291,6 +1316,7 @@ ${summaryContent}`,
1291
1316
  * 场景极少。
1292
1317
  */
1293
1318
  async runSaveLastResponseTee(provider, call, saveToFile, apiMessages, extraMessages, systemPrompt, systemPromptVolatile, modelParams, ac, roundUsage) {
1319
+ let deferred = false;
1294
1320
  this.send({
1295
1321
  type: "tool_call_start",
1296
1322
  callId: call.id,
@@ -1343,7 +1369,8 @@ ${summaryContent}`,
1343
1369
  if (verdict.kind === "reject") {
1344
1370
  cleanupRejectedTeeFile(saveToFile);
1345
1371
  isError = true;
1346
- summary = verdict.summary;
1372
+ deferred = true;
1373
+ summary = buildDeferredSaveInstruction(saveToFile);
1347
1374
  fullContent = "";
1348
1375
  } else {
1349
1376
  if (verdict.kind === "salvaged" || verdict.kind === "fallback") {
@@ -1378,7 +1405,7 @@ ${summaryContent}`,
1378
1405
  content: summary,
1379
1406
  isError
1380
1407
  });
1381
- return { content: fullContent, summary, isError };
1408
+ return { content: fullContent, summary, isError, deferred };
1382
1409
  }
1383
1410
  /**
1384
1411
  * Consume streaming tool call events and forward to client.
@@ -2412,7 +2439,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2412
2439
  case "test": {
2413
2440
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2414
2441
  try {
2415
- const { executeTests } = await import("./run-tests-IL4342SV.js");
2442
+ const { executeTests } = await import("./run-tests-A2I3WZ2H.js");
2416
2443
  const argStr = args.join(" ").trim();
2417
2444
  let testArgs = {};
2418
2445
  if (argStr) {
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ToolRegistry
4
- } from "./chunk-SI5EO3NJ.js";
4
+ } from "./chunk-ZJX6ZU5D.js";
5
5
  import "./chunk-T2NL5ZIA.js";
6
6
  import "./chunk-BXP6YZ2P.js";
7
- import "./chunk-7PX3ZX4G.js";
7
+ import "./chunk-3KCEPI4E.js";
8
8
  import {
9
9
  runTool
10
- } from "./chunk-SHI5UFUH.js";
10
+ } from "./chunk-CKBBAXCA.js";
11
11
  import {
12
12
  getDangerLevel,
13
13
  schemaToJsonSchema
14
- } from "./chunk-JOR572WG.js";
14
+ } from "./chunk-ZRG2FPC6.js";
15
15
  import "./chunk-TZQHYZKT.js";
16
16
  import {
17
17
  VERSION
18
- } from "./chunk-P3PTUSP4.js";
18
+ } from "./chunk-ZEHHAE3B.js";
19
19
  import "./chunk-4BKXL7SM.js";
20
20
  import "./chunk-TB4W4Y4T.js";
21
21
  import "./chunk-KHYD3WXE.js";
@@ -3,21 +3,21 @@ import {
3
3
  ToolRegistry,
4
4
  googleSearchContext,
5
5
  truncateOutput
6
- } from "./chunk-SI5EO3NJ.js";
6
+ } from "./chunk-ZJX6ZU5D.js";
7
7
  import "./chunk-T2NL5ZIA.js";
8
8
  import "./chunk-BXP6YZ2P.js";
9
- import "./chunk-7PX3ZX4G.js";
9
+ import "./chunk-3KCEPI4E.js";
10
10
  import {
11
11
  runTool
12
- } from "./chunk-SHI5UFUH.js";
12
+ } from "./chunk-CKBBAXCA.js";
13
13
  import {
14
14
  getDangerLevel,
15
15
  runLeanAgentLoop
16
- } from "./chunk-JOR572WG.js";
16
+ } from "./chunk-ZRG2FPC6.js";
17
17
  import "./chunk-TZQHYZKT.js";
18
18
  import {
19
19
  SUBAGENT_ALLOWED_TOOLS
20
- } from "./chunk-P3PTUSP4.js";
20
+ } from "./chunk-ZEHHAE3B.js";
21
21
  import "./chunk-4BKXL7SM.js";
22
22
  import "./chunk-TB4W4Y4T.js";
23
23
  import "./chunk-KHYD3WXE.js";
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-V37XOYOE.js";
9
9
  import {
10
10
  ConfigManager
11
- } from "./chunk-VQT27CZK.js";
11
+ } from "./chunk-ZVMXIRBH.js";
12
12
  import "./chunk-TZQHYZKT.js";
13
- import "./chunk-P3PTUSP4.js";
13
+ import "./chunk-ZEHHAE3B.js";
14
14
  import "./chunk-IW3Q7AE5.js";
15
15
 
16
16
  // src/cli/usage.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.193",
3
+ "version": "0.4.194",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",