jinzd-ai-cli 0.4.72 → 0.4.74
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/README.md +28 -3
- package/README.zh-CN.md +28 -3
- package/dist/{chunk-OKNKH5D7.js → chunk-265H5S5H.js} +1 -1
- package/dist/chunk-2ZD3YTVM.js +114 -0
- package/dist/chunk-BT2TCINO.js +155 -0
- package/dist/{chunk-XH65H3BT.js → chunk-D5ZDVEJJ.js} +10 -111
- package/dist/{chunk-73UI5AH7.js → chunk-FKVJRBPO.js} +3 -152
- package/dist/{chunk-57HHY5ZX.js → chunk-PLJUAA3J.js} +242 -368
- package/dist/chunk-VG3MFZYG.js +318 -0
- package/dist/{hub-NQADIYTS.js → hub-ABCHM2OR.js} +1 -1
- package/dist/index.js +182 -7
- package/dist/{run-tests-X2P5BOWV.js → run-tests-FMEFXUGO.js} +1 -1
- package/dist/{run-tests-4565WOUK.js → run-tests-HBLD2R6B.js} +2 -1
- package/dist/{server-Y4CT5IJJ.js → server-NMMRIWT2.js} +106 -5
- package/dist/{task-orchestrator-R33SWTHO.js → task-orchestrator-24UUKJW5.js} +4 -2
- package/dist/web/client/app.js +32 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/jinzd-ai-cli)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[](https://nodejs.org)
|
|
10
|
-
[]()
|
|
11
11
|
[](https://github.com/jinzhengdong/ai-cli/releases)
|
|
12
12
|
[](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml)
|
|
13
13
|
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
- **8 Built-in Providers** — Claude, Gemini, DeepSeek, OpenAI, Zhipu GLM, Kimi, OpenRouter (300+ models), **Ollama** (local models, no API key needed)
|
|
25
25
|
- **3 Interfaces** — Terminal CLI, browser Web UI (`aicli web`), Electron desktop app
|
|
26
26
|
- **Agentic Tool Calling** — AI autonomously runs shell commands, reads/writes files, searches code, fetches web, runs tests (default 200 rounds, configurable up to 10000 via `config.maxToolRounds` or `--max-tool-rounds`)
|
|
27
|
+
- **Prompt Caching** *(v0.4.70+)* — System prompt split into stable/volatile halves so Claude caches the stable part with `cache_control: ephemeral`; cached tokens bill at ~10% of the input price
|
|
28
|
+
- **Unified-Diff Patch Edits** *(v0.4.72+)* — `edit_file` accepts standard `@@ -a,b +c,d @@` hunks for the most compact way to apply many scattered small changes to a large file (±200-line drift tolerance + whitespace fallback)
|
|
29
|
+
- **Anthropic Batches API** *(v0.4.73+)* — `aicli batch submit/list/status/results/cancel` for 50%-off, 24-hour async processing — ideal for offline analysis and bulk evals
|
|
30
|
+
- **Web UI Session Replay** *(v0.4.71+)* — 🎬 button on every saved session opens a timeline replay: every message, tool call, reasoning, and cache-aware token usage at a glance
|
|
31
|
+
- **Conversation Branching** *(v0.4.74+)* — `/branch list/new/switch/delete/rename` inside the REPL, plus a 🌿 "fork here" button on every replay step — explore alternate directions without losing the original thread
|
|
27
32
|
- **Streaming Tool Use** — Real-time streaming of AI reasoning and tool calls as they happen
|
|
28
33
|
- **Sub-Agents** — Delegate complex subtasks to isolated child agents with independent tool loops
|
|
29
34
|
- **Extended Thinking** — Claude deep reasoning mode with `/think` toggle
|
|
@@ -34,7 +39,7 @@
|
|
|
34
39
|
- **PWA Support** — Install Web UI as a desktop/mobile app, accessible over LAN
|
|
35
40
|
- **Hierarchical Context** — 3-layer context files (global / project / subdirectory) auto-injected
|
|
36
41
|
- **Headless Mode** — `ai-cli -p "prompt"` for CI/CD pipelines and scripting
|
|
37
|
-
- **40 REPL Commands** — Session management, checkpointing, code review, security review, rewind, scaffolding, and more
|
|
42
|
+
- **40 REPL Commands** — Session management, checkpointing, code review, security review, rewind, scaffolding, cross-session history search, and more
|
|
38
43
|
- **GitHub Actions CI/CD** — Automated testing on Node 20/22 + npm publish on release tags
|
|
39
44
|
- **Cross-Platform** — Windows, macOS, Linux
|
|
40
45
|
|
|
@@ -188,7 +193,8 @@ AI autonomously invokes these 24 tools during conversations:
|
|
|
188
193
|
| `/compact` | Compress conversation history |
|
|
189
194
|
| `/session` | Session management (new / list / load) |
|
|
190
195
|
| `/checkpoint` | Save/restore conversation checkpoints |
|
|
191
|
-
| `/fork` |
|
|
196
|
+
| `/fork` | Fork the current session into a new session file |
|
|
197
|
+
| `/branch` | Create/switch/delete branches *within* the current session (B2) |
|
|
192
198
|
| `/search <keyword>` | Full-text search across all sessions |
|
|
193
199
|
| `/skill` | Manage agent skill packs |
|
|
194
200
|
| `/mcp` | View MCP server status and tools |
|
|
@@ -227,8 +233,27 @@ Subcommands:
|
|
|
227
233
|
aicli providers List all providers and status
|
|
228
234
|
aicli sessions List recent sessions
|
|
229
235
|
aicli user <action> Manage Web UI users
|
|
236
|
+
aicli batch <action> Anthropic Batches API (submit | list | status | results | cancel)
|
|
230
237
|
```
|
|
231
238
|
|
|
239
|
+
### Batch Mode (Anthropic Message Batches)
|
|
240
|
+
|
|
241
|
+
For offline analysis, bulk evals, or any workload where latency is flexible, use the Batches API for **50% off** tokens with a 24-hour processing window.
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# 1. Prepare a JSONL file (one request per line):
|
|
245
|
+
# {"customId":"req-1","messages":[{"role":"user","content":"..."}],"maxTokens":1024}
|
|
246
|
+
aicli batch submit prompts.jsonl # validate + submit + track locally
|
|
247
|
+
aicli batch submit --dry-run prompts.jsonl # parse only, no network
|
|
248
|
+
|
|
249
|
+
aicli batch list # live status of recent batches
|
|
250
|
+
aicli batch status <id> # detailed status + request counts
|
|
251
|
+
aicli batch results <id> out.jsonl # download results (stdout if no path)
|
|
252
|
+
aicli batch cancel <id> # cancel an in-progress batch
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Local tracking file: `~/.aicli/batches.json` (last 200 submissions). Requires `AICLI_API_KEY_CLAUDE` or a Claude API key configured via `aicli config`.
|
|
256
|
+
|
|
232
257
|
### Headless Mode
|
|
233
258
|
|
|
234
259
|
```bash
|
package/README.zh-CN.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/jinzd-ai-cli)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[](https://nodejs.org)
|
|
10
|
-
[]()
|
|
11
11
|
[](https://github.com/jinzhengdong/ai-cli/releases)
|
|
12
12
|
[](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml)
|
|
13
13
|
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
- **8 大内置 Provider** — Claude、Gemini、DeepSeek、OpenAI、智谱 GLM、Kimi、OpenRouter(300+ 模型)、**Ollama**(本地模型,无需 API Key)
|
|
17
17
|
- **三种使用方式** — 终端 CLI、浏览器 Web UI(`aicli web`)、Electron 桌面应用
|
|
18
18
|
- **Agentic 工具调用** — AI 自主执行 bash 命令、读写文件、搜索代码、抓取网页、运行测试(默认 200 轮,可通过 `config.maxToolRounds` 或 `--max-tool-rounds` 调整,上限 10000)
|
|
19
|
+
- **Prompt Caching**(v0.4.70+)— system prompt 拆分稳定/易变两段,Claude 对稳定段启用 `cache_control: ephemeral`,命中时按 10% 计费
|
|
20
|
+
- **Unified Diff Patch 编辑**(v0.4.72+)— `edit_file` 支持标准 `@@ -a,b +c,d @@` hunk,大文件多处小改最省 token;容忍 ±200 行漂移与空白差异
|
|
21
|
+
- **Anthropic Batches API**(v0.4.73+)— `aicli batch submit/list/status/results/cancel` 包 Message Batches(50% 折扣 + 24 小时窗口),适合离线分析和批量 eval
|
|
22
|
+
- **Web UI 会话回放**(v0.4.71+)— 会话列表每项 🎬 按钮打开时间轴回放:消息、工具调用、推理内容、cache-aware token 用量一目了然
|
|
23
|
+
- **对话分支**(v0.4.74+)— REPL 内 `/branch list/new/switch/delete/rename`,Web UI 回放面板每条消息旁 🌿 "fork here" 按钮,任意位置开辟新分支探索不同方向,原对话保持不变
|
|
19
24
|
- **流式工具调用** — 实时流式展示 AI 推理过程和工具调用
|
|
20
25
|
- **子代理系统** — 将复杂子任务委派给独立子代理执行
|
|
21
26
|
- **深度推理** — Claude Extended Thinking,`/think` 一键切换
|
|
@@ -180,7 +185,8 @@ AI 在对话中可自主调用 24 个工具:
|
|
|
180
185
|
| `/compact` | 压缩对话历史 |
|
|
181
186
|
| `/session` | 会话管理(new / list / load) |
|
|
182
187
|
| `/checkpoint` | 保存/恢复会话检查点 |
|
|
183
|
-
| `/fork` |
|
|
188
|
+
| `/fork` | 复制整个会话为新的 session 文件 |
|
|
189
|
+
| `/branch` | 在当前 session **内部**创建/切换/删除分支(B2)|
|
|
184
190
|
| `/search <关键词>` | 跨会话全文搜索 |
|
|
185
191
|
| `/skill` | 管理 Agent 技能包 |
|
|
186
192
|
| `/mcp` | 查看 MCP 服务器状态 |
|
|
@@ -219,8 +225,27 @@ aicli [选项]
|
|
|
219
225
|
aicli providers 列出所有 Provider
|
|
220
226
|
aicli sessions 列出最近会话
|
|
221
227
|
aicli user <操作> 管理 Web UI 用户
|
|
228
|
+
aicli batch <操作> Anthropic Batches API(submit | list | status | results | cancel)
|
|
222
229
|
```
|
|
223
230
|
|
|
231
|
+
### 批处理模式(Anthropic Message Batches)
|
|
232
|
+
|
|
233
|
+
离线分析、批量 eval 等对延迟不敏感的场景,使用 Batches API 可享 **50% 折扣** + 24 小时处理窗口。
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
# 1. 准备 JSONL 输入(每行一个请求):
|
|
237
|
+
# {"customId":"req-1","messages":[{"role":"user","content":"..."}],"maxTokens":1024}
|
|
238
|
+
aicli batch submit prompts.jsonl # 校验 + 提交 + 本地追踪
|
|
239
|
+
aicli batch submit --dry-run prompts.jsonl # 只解析不提交
|
|
240
|
+
|
|
241
|
+
aicli batch list # 实时查看已追踪批次状态
|
|
242
|
+
aicli batch status <id> # 单批详情 + 请求计数分解
|
|
243
|
+
aicli batch results <id> out.jsonl # 下载结果(省略文件名则输出到 stdout)
|
|
244
|
+
aicli batch cancel <id> # 取消进行中的批次
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
本地追踪文件:`~/.aicli/batches.json`(保留最近 200 次提交)。需配置 `AICLI_API_KEY_CLAUDE` 或通过 `aicli config` 设置 Claude API Key。
|
|
248
|
+
|
|
224
249
|
### 无头模式
|
|
225
250
|
|
|
226
251
|
```bash
|
|
@@ -391,7 +416,7 @@ Web UI(`aicli web`)提供功能完备的浏览器界面:
|
|
|
391
416
|
## 测试
|
|
392
417
|
|
|
393
418
|
```bash
|
|
394
|
-
npm test # 运行全部
|
|
419
|
+
npm test # 运行全部 515 个测试(30 suites)
|
|
395
420
|
npm run test:watch # 监听模式
|
|
396
421
|
```
|
|
397
422
|
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/constants.ts
|
|
4
|
+
var VERSION = "0.4.74";
|
|
5
|
+
var APP_NAME = "ai-cli";
|
|
6
|
+
var CONFIG_DIR_NAME = ".aicli";
|
|
7
|
+
var CONFIG_FILE_NAME = "config.json";
|
|
8
|
+
var HISTORY_DIR_NAME = "history";
|
|
9
|
+
var PLUGINS_DIR_NAME = "plugins";
|
|
10
|
+
var SKILLS_DIR_NAME = "skills";
|
|
11
|
+
var CUSTOM_COMMANDS_DIR_NAME = "commands";
|
|
12
|
+
var CONTEXT_FILE_CANDIDATES = ["AICLI.md", "CLAUDE.md"];
|
|
13
|
+
var MEMORY_FILE_NAME = "memory.md";
|
|
14
|
+
var MEMORY_MAX_CHARS = 1e4;
|
|
15
|
+
var DEV_STATE_FILE_NAME = "dev-state.md";
|
|
16
|
+
var DEFAULT_MAX_TOKENS = 8192;
|
|
17
|
+
var DEFAULT_MAX_TOOL_ROUNDS = 200;
|
|
18
|
+
var DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP = 5e5;
|
|
19
|
+
var MCP_TOOL_PREFIX = "mcp__";
|
|
20
|
+
var MCP_PROJECT_CONFIG_NAME = ".mcp.json";
|
|
21
|
+
var MCP_CONNECT_TIMEOUT = 3e4;
|
|
22
|
+
var MCP_CALL_TIMEOUT = 6e4;
|
|
23
|
+
var MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
24
|
+
var PLAN_MODE_READONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
25
|
+
"read_file",
|
|
26
|
+
"list_dir",
|
|
27
|
+
"grep_files",
|
|
28
|
+
"glob_files",
|
|
29
|
+
"web_fetch",
|
|
30
|
+
"google_search",
|
|
31
|
+
"ask_user",
|
|
32
|
+
// 允许:可向用户澄清需求
|
|
33
|
+
"write_todos"
|
|
34
|
+
// 允许:可输出任务列表作为实施计划
|
|
35
|
+
]);
|
|
36
|
+
var PLAN_MODE_SYSTEM_ADDON = `# \u{1F50D} Plan Mode \u2014 Read-Only Planning Mode
|
|
37
|
+
|
|
38
|
+
You are currently in read-only planning (Plan) mode.
|
|
39
|
+
|
|
40
|
+
**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
|
|
41
|
+
**Disabled tools**: bash \xB7 write_file \xB7 edit_file \xB7 run_interactive \xB7 save_last_response \xB7 save_memory and all MCP tools
|
|
42
|
+
|
|
43
|
+
**Your task**:
|
|
44
|
+
1. Use read-only tools to thoroughly analyze the codebase, file structure, and existing implementation
|
|
45
|
+
2. Use ask_user to clarify any ambiguous requirements with the user
|
|
46
|
+
3. Develop a detailed implementation plan (you may use write_todos to present the task list), including:
|
|
47
|
+
- List of files to be modified or created
|
|
48
|
+
- Specific changes for each file
|
|
49
|
+
- Execution order and dependencies
|
|
50
|
+
- Potential risks and considerations
|
|
51
|
+
|
|
52
|
+
**CRITICAL RULES**:
|
|
53
|
+
- Do NOT attempt to call bash, write_file, edit_file, or any disabled tool \u2014 they will fail silently.
|
|
54
|
+
- 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.
|
|
55
|
+
- 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."
|
|
56
|
+
- Do NOT call write_todos repeatedly with the same content \u2014 call it once, then give a text response.
|
|
57
|
+
- Focus your analysis on reading files and producing actionable plans.
|
|
58
|
+
|
|
59
|
+
Once planning is complete, clearly inform the user: type \`/plan execute\` to begin executing the plan, or \`/plan exit\` to discard it.`;
|
|
60
|
+
var SUBAGENT_DEFAULT_MAX_ROUNDS = 15;
|
|
61
|
+
var SUBAGENT_MAX_ROUNDS_LIMIT = 30;
|
|
62
|
+
var SUBAGENT_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
|
|
63
|
+
"bash",
|
|
64
|
+
"read_file",
|
|
65
|
+
"write_file",
|
|
66
|
+
"edit_file",
|
|
67
|
+
"list_dir",
|
|
68
|
+
"grep_files",
|
|
69
|
+
"glob_files",
|
|
70
|
+
"run_interactive",
|
|
71
|
+
"web_fetch",
|
|
72
|
+
"google_search",
|
|
73
|
+
"write_todos",
|
|
74
|
+
"run_tests"
|
|
75
|
+
]);
|
|
76
|
+
var CONTEXT_PRESSURE_THRESHOLD = 0.8;
|
|
77
|
+
var TEST_TIMEOUT = 3e5;
|
|
78
|
+
var AGENTIC_BEHAVIOR_GUIDELINE = `# Important Behavioral Guidelines
|
|
79
|
+
|
|
80
|
+
**Respond appropriately to the user's intent \u2014 do NOT over-react**:
|
|
81
|
+
- 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.
|
|
82
|
+
- 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.
|
|
83
|
+
- Only begin using write/execute tools when the user **explicitly requests** an action (e.g., "generate", "create", "modify", "run", "start", etc.).
|
|
84
|
+
- 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.
|
|
85
|
+
- 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.
|
|
86
|
+
- **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.`;
|
|
87
|
+
function buildUserIdentityPrompt(profile) {
|
|
88
|
+
const lines = [];
|
|
89
|
+
const displayName = profile.nickname || profile.name;
|
|
90
|
+
if (displayName) {
|
|
91
|
+
lines.push(`The user's name is **${profile.name || displayName}**${profile.nickname && profile.name ? ` (prefers to be called **${profile.nickname}**)` : ""}.`);
|
|
92
|
+
}
|
|
93
|
+
if (profile.role) {
|
|
94
|
+
lines.push(`Role: ${profile.role}.`);
|
|
95
|
+
}
|
|
96
|
+
if (profile.bio) {
|
|
97
|
+
lines.push(`About: ${profile.bio}`);
|
|
98
|
+
}
|
|
99
|
+
if (profile.interests && profile.interests.length > 0) {
|
|
100
|
+
lines.push(`Interests & expertise: ${profile.interests.join(", ")}.`);
|
|
101
|
+
}
|
|
102
|
+
if (profile.locale) {
|
|
103
|
+
lines.push(`Preferred language: ${profile.locale}. Please respond in this language unless the user explicitly uses another language.`);
|
|
104
|
+
}
|
|
105
|
+
if (profile.extra) {
|
|
106
|
+
lines.push(`
|
|
107
|
+
${profile.extra}`);
|
|
108
|
+
}
|
|
109
|
+
if (lines.length === 0) return null;
|
|
110
|
+
return `# Who You're Talking To
|
|
111
|
+
|
|
112
|
+
${lines.join("\n")}
|
|
113
|
+
|
|
114
|
+
Address the user personally and adapt your communication style to their background. This identity persists across all conversations and all AI providers.`;
|
|
115
|
+
}
|
|
116
|
+
var AUTHOR = "Jin Zhengdong";
|
|
117
|
+
var AUTHOR_EMAIL = "zhengdong.jin@gmail.com";
|
|
118
|
+
var DESCRIPTION = "Cross-platform REPL-style AI conversation tool with multi-provider and agentic tool calling support";
|
|
119
|
+
var REPO_URL = "https://github.com/jinzhengdong/ai-cli";
|
|
120
|
+
|
|
121
|
+
export {
|
|
122
|
+
VERSION,
|
|
123
|
+
APP_NAME,
|
|
124
|
+
CONFIG_DIR_NAME,
|
|
125
|
+
CONFIG_FILE_NAME,
|
|
126
|
+
HISTORY_DIR_NAME,
|
|
127
|
+
PLUGINS_DIR_NAME,
|
|
128
|
+
SKILLS_DIR_NAME,
|
|
129
|
+
CUSTOM_COMMANDS_DIR_NAME,
|
|
130
|
+
CONTEXT_FILE_CANDIDATES,
|
|
131
|
+
MEMORY_FILE_NAME,
|
|
132
|
+
MEMORY_MAX_CHARS,
|
|
133
|
+
DEV_STATE_FILE_NAME,
|
|
134
|
+
DEFAULT_MAX_TOKENS,
|
|
135
|
+
DEFAULT_MAX_TOOL_ROUNDS,
|
|
136
|
+
DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
|
|
137
|
+
MCP_TOOL_PREFIX,
|
|
138
|
+
MCP_PROJECT_CONFIG_NAME,
|
|
139
|
+
MCP_CONNECT_TIMEOUT,
|
|
140
|
+
MCP_CALL_TIMEOUT,
|
|
141
|
+
MCP_PROTOCOL_VERSION,
|
|
142
|
+
PLAN_MODE_READONLY_TOOLS,
|
|
143
|
+
PLAN_MODE_SYSTEM_ADDON,
|
|
144
|
+
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
145
|
+
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
146
|
+
SUBAGENT_ALLOWED_TOOLS,
|
|
147
|
+
CONTEXT_PRESSURE_THRESHOLD,
|
|
148
|
+
TEST_TIMEOUT,
|
|
149
|
+
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
150
|
+
buildUserIdentityPrompt,
|
|
151
|
+
AUTHOR,
|
|
152
|
+
AUTHOR_EMAIL,
|
|
153
|
+
DESCRIPTION,
|
|
154
|
+
REPO_URL
|
|
155
|
+
};
|
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
import {
|
|
3
3
|
fileCheckpoints
|
|
4
4
|
} from "./chunk-4BKXL7SM.js";
|
|
5
|
+
import {
|
|
6
|
+
runTestsTool
|
|
7
|
+
} from "./chunk-FKVJRBPO.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-73UI5AH7.js";
|
|
19
|
+
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
20
|
+
} from "./chunk-BT2TCINO.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";
|
|
@@ -3339,53 +3291,6 @@ function renderTodoList(todos) {
|
|
|
3339
3291
|
console.log();
|
|
3340
3292
|
}
|
|
3341
3293
|
|
|
3342
|
-
// src/config/env-loader.ts
|
|
3343
|
-
var ENV_KEY_MAP = {
|
|
3344
|
-
claude: "AICLI_API_KEY_CLAUDE",
|
|
3345
|
-
gemini: "AICLI_API_KEY_GEMINI",
|
|
3346
|
-
deepseek: "AICLI_API_KEY_DEEPSEEK",
|
|
3347
|
-
zhipu: "AICLI_API_KEY_ZHIPU",
|
|
3348
|
-
kimi: "AICLI_API_KEY_KIMI",
|
|
3349
|
-
openai: "AICLI_API_KEY_OPENAI",
|
|
3350
|
-
openrouter: "AICLI_API_KEY_OPENROUTER",
|
|
3351
|
-
"google-search": "AICLI_API_KEY_GOOGLESEARCH",
|
|
3352
|
-
ollama: "AICLI_API_KEY_OLLAMA"
|
|
3353
|
-
};
|
|
3354
|
-
var EnvLoader = class {
|
|
3355
|
-
/**
|
|
3356
|
-
* 读取指定 provider 的 API Key 环境变量。
|
|
3357
|
-
* 优先级:固定映射(如 AICLI_API_KEY_CLAUDE)> 动态格式(AICLI_API_KEY_<ID大写>)
|
|
3358
|
-
* 自定义 provider 示例:id="siliconflow" → 读取 AICLI_API_KEY_SILICONFLOW
|
|
3359
|
-
*/
|
|
3360
|
-
static getApiKey(providerId) {
|
|
3361
|
-
const fixedEnvVar = ENV_KEY_MAP[providerId];
|
|
3362
|
-
const dynamicEnvVar = `AICLI_API_KEY_${providerId.toUpperCase().replace(/-/g, "_")}`;
|
|
3363
|
-
if (fixedEnvVar && fixedEnvVar !== dynamicEnvVar) {
|
|
3364
|
-
const fixedVal = process.env[fixedEnvVar];
|
|
3365
|
-
const dynVal = process.env[dynamicEnvVar];
|
|
3366
|
-
if (fixedVal && dynVal && fixedVal !== dynVal) {
|
|
3367
|
-
process.stderr.write(`[warn] env var collision: ${fixedEnvVar} and ${dynamicEnvVar} have different values for provider "${providerId}". Using ${fixedEnvVar}.
|
|
3368
|
-
`);
|
|
3369
|
-
}
|
|
3370
|
-
}
|
|
3371
|
-
if (fixedEnvVar) {
|
|
3372
|
-
const val = process.env[fixedEnvVar];
|
|
3373
|
-
if (val) return val;
|
|
3374
|
-
}
|
|
3375
|
-
return process.env[dynamicEnvVar] || void 0;
|
|
3376
|
-
}
|
|
3377
|
-
static getDefaultProvider() {
|
|
3378
|
-
return process.env["AICLI_PROVIDER"] || void 0;
|
|
3379
|
-
}
|
|
3380
|
-
static isStreamingDisabled() {
|
|
3381
|
-
return process.env["AICLI_NO_STREAM"] === "1";
|
|
3382
|
-
}
|
|
3383
|
-
/** Google Custom Search Engine ID (cx) 环境变量 */
|
|
3384
|
-
static getGoogleSearchEngineId() {
|
|
3385
|
-
return process.env["AICLI_GOOGLE_CX"] || void 0;
|
|
3386
|
-
}
|
|
3387
|
-
};
|
|
3388
|
-
|
|
3389
3294
|
// src/tools/builtin/google-search.ts
|
|
3390
3295
|
var GOOGLE_SEARCH_API = "https://www.googleapis.com/customsearch/v1";
|
|
3391
3296
|
var REQUEST_TIMEOUT_MS = 15e3;
|
|
@@ -4614,12 +4519,6 @@ var ToolRegistry = class {
|
|
|
4614
4519
|
};
|
|
4615
4520
|
|
|
4616
4521
|
export {
|
|
4617
|
-
EnvLoader,
|
|
4618
|
-
ProviderError,
|
|
4619
|
-
AuthError,
|
|
4620
|
-
RateLimitError,
|
|
4621
|
-
ConfigError,
|
|
4622
|
-
ProviderNotFoundError,
|
|
4623
4522
|
getDangerLevel,
|
|
4624
4523
|
schemaToJsonSchema,
|
|
4625
4524
|
initTheme,
|