jinzd-ai-cli 0.4.71 → 0.4.73
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/dist/chunk-2ZD3YTVM.js +114 -0
- package/dist/{chunk-6QRHSSJB.js → chunk-76KBSQHA.js} +1 -1
- package/dist/{chunk-UHZ6YANH.js → chunk-H65WPFDO.js} +164 -114
- package/dist/{chunk-6OKAZIY7.js → chunk-HAOCJWW2.js} +3 -152
- package/dist/chunk-LR7IV4SK.js +318 -0
- package/dist/chunk-T2OUKQOX.js +155 -0
- package/dist/{chunk-IZK6GNT4.js → chunk-TIGB5ADX.js} +47 -354
- package/dist/{hub-DUCBBK3Y.js → hub-AFXKRJ5D.js} +1 -1
- package/dist/index.js +56 -7
- package/dist/{run-tests-CKMD75Y2.js → run-tests-5BUD2CL4.js} +1 -1
- package/dist/{run-tests-A42NM2XQ.js → run-tests-6JUSVL4W.js} +2 -1
- package/dist/{server-BOAYC5O3.js → server-QQGBLS42.js} +9 -5
- package/dist/{task-orchestrator-WX5NZZJR.js → task-orchestrator-M7Y32LNH.js} +4 -2
- package/package.json +1 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/config/env-loader.ts
|
|
4
|
+
var ENV_KEY_MAP = {
|
|
5
|
+
claude: "AICLI_API_KEY_CLAUDE",
|
|
6
|
+
gemini: "AICLI_API_KEY_GEMINI",
|
|
7
|
+
deepseek: "AICLI_API_KEY_DEEPSEEK",
|
|
8
|
+
zhipu: "AICLI_API_KEY_ZHIPU",
|
|
9
|
+
kimi: "AICLI_API_KEY_KIMI",
|
|
10
|
+
openai: "AICLI_API_KEY_OPENAI",
|
|
11
|
+
openrouter: "AICLI_API_KEY_OPENROUTER",
|
|
12
|
+
"google-search": "AICLI_API_KEY_GOOGLESEARCH",
|
|
13
|
+
ollama: "AICLI_API_KEY_OLLAMA"
|
|
14
|
+
};
|
|
15
|
+
var EnvLoader = class {
|
|
16
|
+
/**
|
|
17
|
+
* 读取指定 provider 的 API Key 环境变量。
|
|
18
|
+
* 优先级:固定映射(如 AICLI_API_KEY_CLAUDE)> 动态格式(AICLI_API_KEY_<ID大写>)
|
|
19
|
+
* 自定义 provider 示例:id="siliconflow" → 读取 AICLI_API_KEY_SILICONFLOW
|
|
20
|
+
*/
|
|
21
|
+
static getApiKey(providerId) {
|
|
22
|
+
const fixedEnvVar = ENV_KEY_MAP[providerId];
|
|
23
|
+
const dynamicEnvVar = `AICLI_API_KEY_${providerId.toUpperCase().replace(/-/g, "_")}`;
|
|
24
|
+
if (fixedEnvVar && fixedEnvVar !== dynamicEnvVar) {
|
|
25
|
+
const fixedVal = process.env[fixedEnvVar];
|
|
26
|
+
const dynVal = process.env[dynamicEnvVar];
|
|
27
|
+
if (fixedVal && dynVal && fixedVal !== dynVal) {
|
|
28
|
+
process.stderr.write(`[warn] env var collision: ${fixedEnvVar} and ${dynamicEnvVar} have different values for provider "${providerId}". Using ${fixedEnvVar}.
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (fixedEnvVar) {
|
|
33
|
+
const val = process.env[fixedEnvVar];
|
|
34
|
+
if (val) return val;
|
|
35
|
+
}
|
|
36
|
+
return process.env[dynamicEnvVar] || void 0;
|
|
37
|
+
}
|
|
38
|
+
static getDefaultProvider() {
|
|
39
|
+
return process.env["AICLI_PROVIDER"] || void 0;
|
|
40
|
+
}
|
|
41
|
+
static isStreamingDisabled() {
|
|
42
|
+
return process.env["AICLI_NO_STREAM"] === "1";
|
|
43
|
+
}
|
|
44
|
+
/** Google Custom Search Engine ID (cx) 环境变量 */
|
|
45
|
+
static getGoogleSearchEngineId() {
|
|
46
|
+
return process.env["AICLI_GOOGLE_CX"] || void 0;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// src/core/errors.ts
|
|
51
|
+
var AiCliError = class extends Error {
|
|
52
|
+
constructor(message, options) {
|
|
53
|
+
super(message, options);
|
|
54
|
+
this.name = "AiCliError";
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var ProviderError = class extends AiCliError {
|
|
58
|
+
constructor(providerId, message, cause) {
|
|
59
|
+
super(`[${providerId}] ${message}`, cause !== void 0 ? { cause } : void 0);
|
|
60
|
+
this.providerId = providerId;
|
|
61
|
+
this.name = "ProviderError";
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var AuthError = class extends ProviderError {
|
|
65
|
+
constructor(providerId) {
|
|
66
|
+
super(providerId, "Invalid or missing API key. Run: ai-cli config");
|
|
67
|
+
this.name = "AuthError";
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var RateLimitError = class extends ProviderError {
|
|
71
|
+
constructor(providerId) {
|
|
72
|
+
super(providerId, "Rate limit exceeded. Please wait before trying again.");
|
|
73
|
+
this.name = "RateLimitError";
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var ConfigError = class extends AiCliError {
|
|
77
|
+
constructor(message) {
|
|
78
|
+
super(message);
|
|
79
|
+
this.name = "ConfigError";
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var ProviderNotFoundError = class extends AiCliError {
|
|
83
|
+
constructor(providerId) {
|
|
84
|
+
super(
|
|
85
|
+
`Provider '${providerId}' is not configured. Run: ai-cli config`
|
|
86
|
+
);
|
|
87
|
+
this.name = "ProviderNotFoundError";
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var ToolError = class extends AiCliError {
|
|
91
|
+
constructor(toolName, message, cause) {
|
|
92
|
+
super(`[${toolName}] ${message}`, cause !== void 0 ? { cause } : void 0);
|
|
93
|
+
this.toolName = toolName;
|
|
94
|
+
this.name = "ToolError";
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
var NetworkError = class extends AiCliError {
|
|
98
|
+
constructor(message, statusCode, cause) {
|
|
99
|
+
super(message, cause !== void 0 ? { cause } : void 0);
|
|
100
|
+
this.statusCode = statusCode;
|
|
101
|
+
this.name = "NetworkError";
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
EnvLoader,
|
|
107
|
+
ProviderError,
|
|
108
|
+
AuthError,
|
|
109
|
+
RateLimitError,
|
|
110
|
+
ConfigError,
|
|
111
|
+
ProviderNotFoundError,
|
|
112
|
+
ToolError,
|
|
113
|
+
NetworkError
|
|
114
|
+
};
|
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
import {
|
|
3
3
|
fileCheckpoints
|
|
4
4
|
} from "./chunk-4BKXL7SM.js";
|
|
5
|
+
import {
|
|
6
|
+
runTestsTool
|
|
7
|
+
} from "./chunk-HAOCJWW2.js";
|
|
8
|
+
import {
|
|
9
|
+
EnvLoader,
|
|
10
|
+
NetworkError,
|
|
11
|
+
ToolError
|
|
12
|
+
} from "./chunk-2ZD3YTVM.js";
|
|
5
13
|
import {
|
|
6
14
|
CONFIG_DIR_NAME,
|
|
7
15
|
DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
|
|
8
16
|
MEMORY_FILE_NAME,
|
|
9
17
|
SUBAGENT_ALLOWED_TOOLS,
|
|
10
18
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
11
|
-
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
12
|
-
|
|
13
|
-
} from "./chunk-6OKAZIY7.js";
|
|
19
|
+
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
20
|
+
} from "./chunk-T2OUKQOX.js";
|
|
14
21
|
|
|
15
22
|
// src/tools/builtin/bash.ts
|
|
16
23
|
import { execSync } from "child_process";
|
|
@@ -133,61 +140,6 @@ var UndoStack = class {
|
|
|
133
140
|
};
|
|
134
141
|
var undoStack = new UndoStack();
|
|
135
142
|
|
|
136
|
-
// src/core/errors.ts
|
|
137
|
-
var AiCliError = class extends Error {
|
|
138
|
-
constructor(message, options) {
|
|
139
|
-
super(message, options);
|
|
140
|
-
this.name = "AiCliError";
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
var ProviderError = class extends AiCliError {
|
|
144
|
-
constructor(providerId, message, cause) {
|
|
145
|
-
super(`[${providerId}] ${message}`, cause !== void 0 ? { cause } : void 0);
|
|
146
|
-
this.providerId = providerId;
|
|
147
|
-
this.name = "ProviderError";
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
var AuthError = class extends ProviderError {
|
|
151
|
-
constructor(providerId) {
|
|
152
|
-
super(providerId, "Invalid or missing API key. Run: ai-cli config");
|
|
153
|
-
this.name = "AuthError";
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
var RateLimitError = class extends ProviderError {
|
|
157
|
-
constructor(providerId) {
|
|
158
|
-
super(providerId, "Rate limit exceeded. Please wait before trying again.");
|
|
159
|
-
this.name = "RateLimitError";
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
var ConfigError = class extends AiCliError {
|
|
163
|
-
constructor(message) {
|
|
164
|
-
super(message);
|
|
165
|
-
this.name = "ConfigError";
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
var ProviderNotFoundError = class extends AiCliError {
|
|
169
|
-
constructor(providerId) {
|
|
170
|
-
super(
|
|
171
|
-
`Provider '${providerId}' is not configured. Run: ai-cli config`
|
|
172
|
-
);
|
|
173
|
-
this.name = "ProviderNotFoundError";
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
var ToolError = class extends AiCliError {
|
|
177
|
-
constructor(toolName, message, cause) {
|
|
178
|
-
super(`[${toolName}] ${message}`, cause !== void 0 ? { cause } : void 0);
|
|
179
|
-
this.toolName = toolName;
|
|
180
|
-
this.name = "ToolError";
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
var NetworkError = class extends AiCliError {
|
|
184
|
-
constructor(message, statusCode, cause) {
|
|
185
|
-
super(message, cause !== void 0 ? { cause } : void 0);
|
|
186
|
-
this.statusCode = statusCode;
|
|
187
|
-
this.name = "NetworkError";
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
|
|
191
143
|
// src/tools/builtin/bash.ts
|
|
192
144
|
var IS_WINDOWS = platform() === "win32";
|
|
193
145
|
var SHELL = IS_WINDOWS ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
|
|
@@ -1783,6 +1735,118 @@ Important: For long content (over 500 lines or 3000 chars), you MUST split into
|
|
|
1783
1735
|
|
|
1784
1736
|
// src/tools/builtin/edit-file.ts
|
|
1785
1737
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
|
|
1738
|
+
|
|
1739
|
+
// src/tools/builtin/patch-apply.ts
|
|
1740
|
+
function parseUnifiedDiff(patch) {
|
|
1741
|
+
const lines = patch.split("\n");
|
|
1742
|
+
const hunks = [];
|
|
1743
|
+
let current = null;
|
|
1744
|
+
const headerRe = /^@@\s+-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s+@@/;
|
|
1745
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1746
|
+
const line = lines[i];
|
|
1747
|
+
const m = line.match(headerRe);
|
|
1748
|
+
if (m) {
|
|
1749
|
+
if (current) hunks.push(current);
|
|
1750
|
+
current = {
|
|
1751
|
+
oldStart: parseInt(m[1], 10) || 1,
|
|
1752
|
+
oldLines: [],
|
|
1753
|
+
newLines: [],
|
|
1754
|
+
header: line
|
|
1755
|
+
};
|
|
1756
|
+
continue;
|
|
1757
|
+
}
|
|
1758
|
+
if (!current) continue;
|
|
1759
|
+
if (line.startsWith("---") || line.startsWith("+++") || line.startsWith("diff ") || line.startsWith("index ")) {
|
|
1760
|
+
hunks.push(current);
|
|
1761
|
+
current = null;
|
|
1762
|
+
continue;
|
|
1763
|
+
}
|
|
1764
|
+
if (line.startsWith("\\")) continue;
|
|
1765
|
+
const tag = line[0];
|
|
1766
|
+
const body = line.slice(1);
|
|
1767
|
+
if (tag === " " || tag === void 0) {
|
|
1768
|
+
current.oldLines.push(body ?? "");
|
|
1769
|
+
current.newLines.push(body ?? "");
|
|
1770
|
+
} else if (tag === "-") {
|
|
1771
|
+
current.oldLines.push(body);
|
|
1772
|
+
} else if (tag === "+") {
|
|
1773
|
+
current.newLines.push(body);
|
|
1774
|
+
} else {
|
|
1775
|
+
current.oldLines.push(line);
|
|
1776
|
+
current.newLines.push(line);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
if (current) hunks.push(current);
|
|
1780
|
+
return hunks;
|
|
1781
|
+
}
|
|
1782
|
+
function locate(haystack, needle, hintLine1Based, searchWindow = 200) {
|
|
1783
|
+
if (needle.length === 0) return { index: Math.max(0, hintLine1Based - 1), mode: "exact" };
|
|
1784
|
+
const hint = Math.max(0, hintLine1Based - 1);
|
|
1785
|
+
const matchExactAt = (start) => {
|
|
1786
|
+
if (start < 0 || start + needle.length > haystack.length) return false;
|
|
1787
|
+
for (let j = 0; j < needle.length; j++) {
|
|
1788
|
+
if (haystack[start + j] !== needle[j]) return false;
|
|
1789
|
+
}
|
|
1790
|
+
return true;
|
|
1791
|
+
};
|
|
1792
|
+
const matchWsAt = (start) => {
|
|
1793
|
+
if (start < 0 || start + needle.length > haystack.length) return false;
|
|
1794
|
+
for (let j = 0; j < needle.length; j++) {
|
|
1795
|
+
if ((haystack[start + j] ?? "").trim() !== needle[j].trim()) return false;
|
|
1796
|
+
}
|
|
1797
|
+
return true;
|
|
1798
|
+
};
|
|
1799
|
+
if (matchExactAt(hint)) return { index: hint, mode: "exact" };
|
|
1800
|
+
for (let d = 1; d <= searchWindow; d++) {
|
|
1801
|
+
if (matchExactAt(hint - d)) return { index: hint - d, mode: "fuzzy" };
|
|
1802
|
+
if (matchExactAt(hint + d)) return { index: hint + d, mode: "fuzzy" };
|
|
1803
|
+
}
|
|
1804
|
+
for (let d = 0; d <= searchWindow; d++) {
|
|
1805
|
+
if (matchWsAt(hint - d)) return { index: hint - d, mode: "ws" };
|
|
1806
|
+
if (d > 0 && matchWsAt(hint + d)) return { index: hint + d, mode: "ws" };
|
|
1807
|
+
}
|
|
1808
|
+
for (let i = 0; i <= haystack.length - needle.length; i++) {
|
|
1809
|
+
if (matchWsAt(i)) return { index: i, mode: "ws-global" };
|
|
1810
|
+
}
|
|
1811
|
+
return null;
|
|
1812
|
+
}
|
|
1813
|
+
function applyUnifiedPatch(content, hunks, options = {}) {
|
|
1814
|
+
const stopOnError = options.stopOnError !== false;
|
|
1815
|
+
const lines = content.split("\n");
|
|
1816
|
+
let working = lines.slice();
|
|
1817
|
+
let offset = 0;
|
|
1818
|
+
const reports = [];
|
|
1819
|
+
let appliedCount = 0;
|
|
1820
|
+
for (let i = 0; i < hunks.length; i++) {
|
|
1821
|
+
const h = hunks[i];
|
|
1822
|
+
const hint = h.oldStart + offset;
|
|
1823
|
+
const loc = locate(working, h.oldLines, hint);
|
|
1824
|
+
if (!loc) {
|
|
1825
|
+
reports.push({
|
|
1826
|
+
index: i,
|
|
1827
|
+
ok: false,
|
|
1828
|
+
detail: `Hunk @${h.header} \u2014 context not found near line ${h.oldStart} (${h.oldLines.length} old lines).`
|
|
1829
|
+
});
|
|
1830
|
+
if (stopOnError) {
|
|
1831
|
+
return { ok: false, content: void 0, reports, appliedCount };
|
|
1832
|
+
}
|
|
1833
|
+
continue;
|
|
1834
|
+
}
|
|
1835
|
+
const before = working.slice(0, loc.index);
|
|
1836
|
+
const after = working.slice(loc.index + h.oldLines.length);
|
|
1837
|
+
working = before.concat(h.newLines, after);
|
|
1838
|
+
offset += h.newLines.length - h.oldLines.length;
|
|
1839
|
+
appliedCount++;
|
|
1840
|
+
reports.push({
|
|
1841
|
+
index: i,
|
|
1842
|
+
ok: true,
|
|
1843
|
+
detail: `Applied at line ${loc.index + 1} (${loc.mode}${loc.index + 1 === hint ? "" : `, drift ${loc.index + 1 - hint}`}): -${h.oldLines.length} +${h.newLines.length}`
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
return { ok: true, content: working.join("\n"), reports, appliedCount };
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// src/tools/builtin/edit-file.ts
|
|
1786
1850
|
function similarityScore(a, b) {
|
|
1787
1851
|
if (a === b) return 1;
|
|
1788
1852
|
if (a.length < 2 || b.length < 2) return 0;
|
|
@@ -1948,11 +2012,12 @@ function parseEditsArg(raw) {
|
|
|
1948
2012
|
var editFileTool = {
|
|
1949
2013
|
definition: {
|
|
1950
2014
|
name: "edit_file",
|
|
1951
|
-
description: `Precisely edit file contents.
|
|
2015
|
+
description: `Precisely edit file contents. Five modes:
|
|
1952
2016
|
1. String replace (most common): Provide old_str and new_str to replace an exact match. old_str must appear exactly once (unless replace_all is true).
|
|
1953
2017
|
2. Line insert: Provide insert_after_line (1-based) and insert_content.
|
|
1954
2018
|
3. Line delete: Provide delete_from_line and delete_to_line (inclusive).
|
|
1955
2019
|
4. Batch edits: Provide edits=[{old_str, new_str, ignore_whitespace?, replace_all?}, ...] to apply MULTIPLE edits in ONE call \u2014 saves tool rounds/tokens when refactoring a file. Edits are applied sequentially in-memory; by default any failure rolls back ALL edits (set stop_on_error=false to apply successful ones and report failures).
|
|
2020
|
+
5. Unified diff patch: Provide patch=<unified-diff string> with one or more '@@ -a,b +c,d @@' hunks. Use this for MANY scattered small changes in a LARGE file \u2014 most compact format (context lines are shared between old/new). Supports line-number drift (\xB1200 lines) and whitespace-tolerant fallback. Format each hunk as standard unified diff: ' ' context / '-' remove / '+' add. File headers (---/+++) are optional and ignored.
|
|
1956
2021
|
Optional ignore_whitespace: true ignores indentation differences during matching.
|
|
1957
2022
|
Optional replace_all: true replaces ALL occurrences of old_str.
|
|
1958
2023
|
Note: Path can be absolute or relative to cwd.`,
|
|
@@ -2009,7 +2074,12 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
2009
2074
|
},
|
|
2010
2075
|
stop_on_error: {
|
|
2011
2076
|
type: "boolean",
|
|
2012
|
-
description: "[Batch mode] If true (default), any failing edit rolls back the whole
|
|
2077
|
+
description: "[Batch mode / Patch mode] If true (default), any failing edit/hunk rolls back the whole operation and writes nothing. If false, successful edits are written and failed ones are reported.",
|
|
2078
|
+
required: false
|
|
2079
|
+
},
|
|
2080
|
+
patch: {
|
|
2081
|
+
type: "string",
|
|
2082
|
+
description: '[Patch mode] A unified-diff string with one or more "@@ -oldStart,oldLines +newStart,newLines @@" hunks. Most compact for many scattered small changes. Tolerates line-number drift (\xB1200 lines) and whitespace differences.',
|
|
2013
2083
|
required: false
|
|
2014
2084
|
},
|
|
2015
2085
|
encoding: {
|
|
@@ -2027,6 +2097,39 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
2027
2097
|
if (!filePath) throw new ToolError("edit_file", "path is required");
|
|
2028
2098
|
if (!existsSync5(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
|
|
2029
2099
|
const original = readFileSync4(filePath, encoding);
|
|
2100
|
+
if (args["patch"] !== void 0) {
|
|
2101
|
+
const patchText = String(args["patch"] ?? "");
|
|
2102
|
+
const stopOnError = args["stop_on_error"] !== false;
|
|
2103
|
+
if (!patchText.trim()) {
|
|
2104
|
+
throw new ToolError("edit_file", "patch is empty");
|
|
2105
|
+
}
|
|
2106
|
+
const hunks = parseUnifiedDiff(patchText);
|
|
2107
|
+
if (hunks.length === 0) {
|
|
2108
|
+
throw new ToolError(
|
|
2109
|
+
"edit_file",
|
|
2110
|
+
'patch contained no hunks. Expected unified diff format with "@@ -a,b +c,d @@" headers.'
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
const res = applyUnifiedPatch(original, hunks, { stopOnError });
|
|
2114
|
+
const lines = [];
|
|
2115
|
+
if (!res.ok) {
|
|
2116
|
+
lines.push(`ERROR: Patch aborted \u2014 ${res.appliedCount}/${hunks.length} hunks applied, then a hunk failed. No changes written (stop_on_error=true).`);
|
|
2117
|
+
} else if (res.appliedCount < hunks.length) {
|
|
2118
|
+
lines.push(`Partial success: ${res.appliedCount}/${hunks.length} hunks applied to ${filePath} (stop_on_error=false).`);
|
|
2119
|
+
} else {
|
|
2120
|
+
lines.push(`Successfully applied ${res.appliedCount}/${hunks.length} hunk(s) to ${filePath}.`);
|
|
2121
|
+
}
|
|
2122
|
+
lines.push("");
|
|
2123
|
+
for (const r of res.reports) {
|
|
2124
|
+
lines.push(r.ok ? ` \u2713 #${r.index + 1}: ${r.detail}` : ` \u2717 #${r.index + 1}: ${r.detail}`);
|
|
2125
|
+
}
|
|
2126
|
+
if (res.ok && res.appliedCount > 0 && res.content !== void 0) {
|
|
2127
|
+
undoStack.push(filePath, `edit_file (patch ${res.appliedCount}/${hunks.length}): ${filePath}`);
|
|
2128
|
+
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
2129
|
+
writeFileSync3(filePath, res.content, encoding);
|
|
2130
|
+
}
|
|
2131
|
+
return lines.join("\n");
|
|
2132
|
+
}
|
|
2030
2133
|
if (args["edits"] !== void 0) {
|
|
2031
2134
|
const edits = parseEditsArg(args["edits"]);
|
|
2032
2135
|
const stopOnError = args["stop_on_error"] !== false;
|
|
@@ -2140,7 +2243,7 @@ Please read the file first and use exact text.`;
|
|
|
2140
2243
|
}
|
|
2141
2244
|
throw new ToolError(
|
|
2142
2245
|
"edit_file",
|
|
2143
|
-
"No operation specified. Provide one of: (old_str + new_str), (insert_after_line + insert_content), (delete_from_line + delete_to_line),
|
|
2246
|
+
"No operation specified. Provide one of: (old_str + new_str), (insert_after_line + insert_content), (delete_from_line + delete_to_line), edits=[...], or patch=<unified diff>."
|
|
2144
2247
|
);
|
|
2145
2248
|
}
|
|
2146
2249
|
};
|
|
@@ -3188,53 +3291,6 @@ function renderTodoList(todos) {
|
|
|
3188
3291
|
console.log();
|
|
3189
3292
|
}
|
|
3190
3293
|
|
|
3191
|
-
// src/config/env-loader.ts
|
|
3192
|
-
var ENV_KEY_MAP = {
|
|
3193
|
-
claude: "AICLI_API_KEY_CLAUDE",
|
|
3194
|
-
gemini: "AICLI_API_KEY_GEMINI",
|
|
3195
|
-
deepseek: "AICLI_API_KEY_DEEPSEEK",
|
|
3196
|
-
zhipu: "AICLI_API_KEY_ZHIPU",
|
|
3197
|
-
kimi: "AICLI_API_KEY_KIMI",
|
|
3198
|
-
openai: "AICLI_API_KEY_OPENAI",
|
|
3199
|
-
openrouter: "AICLI_API_KEY_OPENROUTER",
|
|
3200
|
-
"google-search": "AICLI_API_KEY_GOOGLESEARCH",
|
|
3201
|
-
ollama: "AICLI_API_KEY_OLLAMA"
|
|
3202
|
-
};
|
|
3203
|
-
var EnvLoader = class {
|
|
3204
|
-
/**
|
|
3205
|
-
* 读取指定 provider 的 API Key 环境变量。
|
|
3206
|
-
* 优先级:固定映射(如 AICLI_API_KEY_CLAUDE)> 动态格式(AICLI_API_KEY_<ID大写>)
|
|
3207
|
-
* 自定义 provider 示例:id="siliconflow" → 读取 AICLI_API_KEY_SILICONFLOW
|
|
3208
|
-
*/
|
|
3209
|
-
static getApiKey(providerId) {
|
|
3210
|
-
const fixedEnvVar = ENV_KEY_MAP[providerId];
|
|
3211
|
-
const dynamicEnvVar = `AICLI_API_KEY_${providerId.toUpperCase().replace(/-/g, "_")}`;
|
|
3212
|
-
if (fixedEnvVar && fixedEnvVar !== dynamicEnvVar) {
|
|
3213
|
-
const fixedVal = process.env[fixedEnvVar];
|
|
3214
|
-
const dynVal = process.env[dynamicEnvVar];
|
|
3215
|
-
if (fixedVal && dynVal && fixedVal !== dynVal) {
|
|
3216
|
-
process.stderr.write(`[warn] env var collision: ${fixedEnvVar} and ${dynamicEnvVar} have different values for provider "${providerId}". Using ${fixedEnvVar}.
|
|
3217
|
-
`);
|
|
3218
|
-
}
|
|
3219
|
-
}
|
|
3220
|
-
if (fixedEnvVar) {
|
|
3221
|
-
const val = process.env[fixedEnvVar];
|
|
3222
|
-
if (val) return val;
|
|
3223
|
-
}
|
|
3224
|
-
return process.env[dynamicEnvVar] || void 0;
|
|
3225
|
-
}
|
|
3226
|
-
static getDefaultProvider() {
|
|
3227
|
-
return process.env["AICLI_PROVIDER"] || void 0;
|
|
3228
|
-
}
|
|
3229
|
-
static isStreamingDisabled() {
|
|
3230
|
-
return process.env["AICLI_NO_STREAM"] === "1";
|
|
3231
|
-
}
|
|
3232
|
-
/** Google Custom Search Engine ID (cx) 环境变量 */
|
|
3233
|
-
static getGoogleSearchEngineId() {
|
|
3234
|
-
return process.env["AICLI_GOOGLE_CX"] || void 0;
|
|
3235
|
-
}
|
|
3236
|
-
};
|
|
3237
|
-
|
|
3238
3294
|
// src/tools/builtin/google-search.ts
|
|
3239
3295
|
var GOOGLE_SEARCH_API = "https://www.googleapis.com/customsearch/v1";
|
|
3240
3296
|
var REQUEST_TIMEOUT_MS = 15e3;
|
|
@@ -4463,12 +4519,6 @@ var ToolRegistry = class {
|
|
|
4463
4519
|
};
|
|
4464
4520
|
|
|
4465
4521
|
export {
|
|
4466
|
-
EnvLoader,
|
|
4467
|
-
ProviderError,
|
|
4468
|
-
AuthError,
|
|
4469
|
-
RateLimitError,
|
|
4470
|
-
ConfigError,
|
|
4471
|
-
ProviderNotFoundError,
|
|
4472
4522
|
getDangerLevel,
|
|
4473
4523
|
schemaToJsonSchema,
|
|
4474
4524
|
initTheme,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
TEST_TIMEOUT
|
|
4
|
+
} from "./chunk-T2OUKQOX.js";
|
|
2
5
|
|
|
3
6
|
// src/tools/builtin/run-tests.ts
|
|
4
7
|
import { execSync } from "child_process";
|
|
@@ -6,126 +9,6 @@ import { existsSync, readFileSync, readdirSync } from "fs";
|
|
|
6
9
|
import { join } from "path";
|
|
7
10
|
import { platform } from "os";
|
|
8
11
|
import chalk from "chalk";
|
|
9
|
-
|
|
10
|
-
// src/core/constants.ts
|
|
11
|
-
var VERSION = "0.4.71";
|
|
12
|
-
var APP_NAME = "ai-cli";
|
|
13
|
-
var CONFIG_DIR_NAME = ".aicli";
|
|
14
|
-
var CONFIG_FILE_NAME = "config.json";
|
|
15
|
-
var HISTORY_DIR_NAME = "history";
|
|
16
|
-
var PLUGINS_DIR_NAME = "plugins";
|
|
17
|
-
var SKILLS_DIR_NAME = "skills";
|
|
18
|
-
var CUSTOM_COMMANDS_DIR_NAME = "commands";
|
|
19
|
-
var CONTEXT_FILE_CANDIDATES = ["AICLI.md", "CLAUDE.md"];
|
|
20
|
-
var MEMORY_FILE_NAME = "memory.md";
|
|
21
|
-
var MEMORY_MAX_CHARS = 1e4;
|
|
22
|
-
var DEV_STATE_FILE_NAME = "dev-state.md";
|
|
23
|
-
var DEFAULT_MAX_TOKENS = 8192;
|
|
24
|
-
var DEFAULT_MAX_TOOL_ROUNDS = 200;
|
|
25
|
-
var DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP = 5e5;
|
|
26
|
-
var MCP_TOOL_PREFIX = "mcp__";
|
|
27
|
-
var MCP_PROJECT_CONFIG_NAME = ".mcp.json";
|
|
28
|
-
var MCP_CONNECT_TIMEOUT = 3e4;
|
|
29
|
-
var MCP_CALL_TIMEOUT = 6e4;
|
|
30
|
-
var MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
31
|
-
var PLAN_MODE_READONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
32
|
-
"read_file",
|
|
33
|
-
"list_dir",
|
|
34
|
-
"grep_files",
|
|
35
|
-
"glob_files",
|
|
36
|
-
"web_fetch",
|
|
37
|
-
"google_search",
|
|
38
|
-
"ask_user",
|
|
39
|
-
// 允许:可向用户澄清需求
|
|
40
|
-
"write_todos"
|
|
41
|
-
// 允许:可输出任务列表作为实施计划
|
|
42
|
-
]);
|
|
43
|
-
var PLAN_MODE_SYSTEM_ADDON = `# \u{1F50D} Plan Mode \u2014 Read-Only Planning Mode
|
|
44
|
-
|
|
45
|
-
You are currently in read-only planning (Plan) mode.
|
|
46
|
-
|
|
47
|
-
**Allowed tools**: read_file \xB7 list_dir \xB7 grep_files \xB7 glob_files \xB7 web_fetch \xB7 google_search \xB7 ask_user \xB7 write_todos
|
|
48
|
-
**Disabled tools**: bash \xB7 write_file \xB7 edit_file \xB7 run_interactive \xB7 save_last_response \xB7 save_memory and all MCP tools
|
|
49
|
-
|
|
50
|
-
**Your task**:
|
|
51
|
-
1. Use read-only tools to thoroughly analyze the codebase, file structure, and existing implementation
|
|
52
|
-
2. Use ask_user to clarify any ambiguous requirements with the user
|
|
53
|
-
3. Develop a detailed implementation plan (you may use write_todos to present the task list), including:
|
|
54
|
-
- List of files to be modified or created
|
|
55
|
-
- Specific changes for each file
|
|
56
|
-
- Execution order and dependencies
|
|
57
|
-
- Potential risks and considerations
|
|
58
|
-
|
|
59
|
-
**CRITICAL RULES**:
|
|
60
|
-
- Do NOT attempt to call bash, write_file, edit_file, or any disabled tool \u2014 they will fail silently.
|
|
61
|
-
- Do NOT write shell commands, SQL queries, or code in your text response as a substitute for tool calls \u2014 the user's system will misinterpret this as a pseudo-tool-call error.
|
|
62
|
-
- If the user asks you to run commands, test connections, or modify files, respond with: "This requires execution tools. Please type \`/plan execute\` to switch to execute mode, then I can perform these operations."
|
|
63
|
-
- Do NOT call write_todos repeatedly with the same content \u2014 call it once, then give a text response.
|
|
64
|
-
- Focus your analysis on reading files and producing actionable plans.
|
|
65
|
-
|
|
66
|
-
Once planning is complete, clearly inform the user: type \`/plan execute\` to begin executing the plan, or \`/plan exit\` to discard it.`;
|
|
67
|
-
var SUBAGENT_DEFAULT_MAX_ROUNDS = 15;
|
|
68
|
-
var SUBAGENT_MAX_ROUNDS_LIMIT = 30;
|
|
69
|
-
var SUBAGENT_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
|
|
70
|
-
"bash",
|
|
71
|
-
"read_file",
|
|
72
|
-
"write_file",
|
|
73
|
-
"edit_file",
|
|
74
|
-
"list_dir",
|
|
75
|
-
"grep_files",
|
|
76
|
-
"glob_files",
|
|
77
|
-
"run_interactive",
|
|
78
|
-
"web_fetch",
|
|
79
|
-
"google_search",
|
|
80
|
-
"write_todos",
|
|
81
|
-
"run_tests"
|
|
82
|
-
]);
|
|
83
|
-
var CONTEXT_PRESSURE_THRESHOLD = 0.8;
|
|
84
|
-
var TEST_TIMEOUT = 3e5;
|
|
85
|
-
var AGENTIC_BEHAVIOR_GUIDELINE = `# Important Behavioral Guidelines
|
|
86
|
-
|
|
87
|
-
**Respond appropriately to the user's intent \u2014 do NOT over-react**:
|
|
88
|
-
- For **greetings and casual chat** (e.g., "hello", "hi", "hey", "\u4F60\u597D", "what's up"): respond naturally with a friendly greeting. Do NOT use any tools. Do NOT explore directories, read files, or start any project work. Just chat.
|
|
89
|
-
- When the user asks you to "read", "understand", "review", "analyze", "examine", or "look at" files or a project, your task is only to **read and summarize**, then wait for the user's next instruction. Do not automatically start executing tasks described in the project.
|
|
90
|
-
- Only begin using write/execute tools when the user **explicitly requests** an action (e.g., "generate", "create", "modify", "run", "start", etc.).
|
|
91
|
-
- Project context files (CLAUDE.md, AICLI.md) provide background information about the project. They are NOT instructions to start working. Only use them as reference when the user asks a project-related question or task.
|
|
92
|
-
- If you are unsure about the user's intent, use the ask_user tool to confirm with the user, rather than assuming and executing on your own.
|
|
93
|
-
- **Do NOT abuse ask_user for redundant confirmations**: When the user has already given a clear, explicit instruction (e.g., "write lesson 142", "generate file X", "create the report"), execute it immediately. Do NOT ask "are you sure?" or request details that can be found in project documents. Repeatedly asking the user to confirm wastes their time and is extremely frustrating. Only use ask_user when critical information is genuinely missing and cannot be inferred from context files.`;
|
|
94
|
-
function buildUserIdentityPrompt(profile) {
|
|
95
|
-
const lines = [];
|
|
96
|
-
const displayName = profile.nickname || profile.name;
|
|
97
|
-
if (displayName) {
|
|
98
|
-
lines.push(`The user's name is **${profile.name || displayName}**${profile.nickname && profile.name ? ` (prefers to be called **${profile.nickname}**)` : ""}.`);
|
|
99
|
-
}
|
|
100
|
-
if (profile.role) {
|
|
101
|
-
lines.push(`Role: ${profile.role}.`);
|
|
102
|
-
}
|
|
103
|
-
if (profile.bio) {
|
|
104
|
-
lines.push(`About: ${profile.bio}`);
|
|
105
|
-
}
|
|
106
|
-
if (profile.interests && profile.interests.length > 0) {
|
|
107
|
-
lines.push(`Interests & expertise: ${profile.interests.join(", ")}.`);
|
|
108
|
-
}
|
|
109
|
-
if (profile.locale) {
|
|
110
|
-
lines.push(`Preferred language: ${profile.locale}. Please respond in this language unless the user explicitly uses another language.`);
|
|
111
|
-
}
|
|
112
|
-
if (profile.extra) {
|
|
113
|
-
lines.push(`
|
|
114
|
-
${profile.extra}`);
|
|
115
|
-
}
|
|
116
|
-
if (lines.length === 0) return null;
|
|
117
|
-
return `# Who You're Talking To
|
|
118
|
-
|
|
119
|
-
${lines.join("\n")}
|
|
120
|
-
|
|
121
|
-
Address the user personally and adapt your communication style to their background. This identity persists across all conversations and all AI providers.`;
|
|
122
|
-
}
|
|
123
|
-
var AUTHOR = "Jin Zhengdong";
|
|
124
|
-
var AUTHOR_EMAIL = "zhengdong.jin@gmail.com";
|
|
125
|
-
var DESCRIPTION = "Cross-platform REPL-style AI conversation tool with multi-provider and agentic tool calling support";
|
|
126
|
-
var REPO_URL = "https://github.com/jinzhengdong/ai-cli";
|
|
127
|
-
|
|
128
|
-
// src/tools/builtin/run-tests.ts
|
|
129
12
|
var IS_WINDOWS = platform() === "win32";
|
|
130
13
|
function detectNodeTestFramework(cwd, pkg) {
|
|
131
14
|
const devDeps = pkg.devDependencies ?? {};
|
|
@@ -480,38 +363,6 @@ var runTestsTool = {
|
|
|
480
363
|
};
|
|
481
364
|
|
|
482
365
|
export {
|
|
483
|
-
VERSION,
|
|
484
|
-
APP_NAME,
|
|
485
|
-
CONFIG_DIR_NAME,
|
|
486
|
-
CONFIG_FILE_NAME,
|
|
487
|
-
HISTORY_DIR_NAME,
|
|
488
|
-
PLUGINS_DIR_NAME,
|
|
489
|
-
SKILLS_DIR_NAME,
|
|
490
|
-
CUSTOM_COMMANDS_DIR_NAME,
|
|
491
|
-
CONTEXT_FILE_CANDIDATES,
|
|
492
|
-
MEMORY_FILE_NAME,
|
|
493
|
-
MEMORY_MAX_CHARS,
|
|
494
|
-
DEV_STATE_FILE_NAME,
|
|
495
|
-
DEFAULT_MAX_TOKENS,
|
|
496
|
-
DEFAULT_MAX_TOOL_ROUNDS,
|
|
497
|
-
DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
|
|
498
|
-
MCP_TOOL_PREFIX,
|
|
499
|
-
MCP_PROJECT_CONFIG_NAME,
|
|
500
|
-
MCP_CONNECT_TIMEOUT,
|
|
501
|
-
MCP_CALL_TIMEOUT,
|
|
502
|
-
MCP_PROTOCOL_VERSION,
|
|
503
|
-
PLAN_MODE_READONLY_TOOLS,
|
|
504
|
-
PLAN_MODE_SYSTEM_ADDON,
|
|
505
|
-
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
506
|
-
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
507
|
-
SUBAGENT_ALLOWED_TOOLS,
|
|
508
|
-
CONTEXT_PRESSURE_THRESHOLD,
|
|
509
|
-
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
510
|
-
buildUserIdentityPrompt,
|
|
511
|
-
AUTHOR,
|
|
512
|
-
AUTHOR_EMAIL,
|
|
513
|
-
DESCRIPTION,
|
|
514
|
-
REPO_URL,
|
|
515
366
|
executeTests,
|
|
516
367
|
runTestsTool
|
|
517
368
|
};
|