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,318 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ConfigError,
|
|
4
|
+
EnvLoader
|
|
5
|
+
} from "./chunk-2ZD3YTVM.js";
|
|
6
|
+
import {
|
|
7
|
+
CONFIG_DIR_NAME,
|
|
8
|
+
CONFIG_FILE_NAME,
|
|
9
|
+
HISTORY_DIR_NAME,
|
|
10
|
+
PLUGINS_DIR_NAME
|
|
11
|
+
} from "./chunk-T2OUKQOX.js";
|
|
12
|
+
|
|
13
|
+
// src/config/config-manager.ts
|
|
14
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
import { homedir } from "os";
|
|
17
|
+
|
|
18
|
+
// src/config/schema.ts
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
var CustomModelSchema = z.object({
|
|
21
|
+
id: z.string(),
|
|
22
|
+
displayName: z.string().optional(),
|
|
23
|
+
contextWindow: z.number().optional()
|
|
24
|
+
});
|
|
25
|
+
var CustomProviderConfigSchema = z.object({
|
|
26
|
+
id: z.string(),
|
|
27
|
+
// 唯一 ID,不能与内置 provider 重名
|
|
28
|
+
displayName: z.string(),
|
|
29
|
+
// 显示名称
|
|
30
|
+
apiKey: z.string().optional(),
|
|
31
|
+
// 可选:直接在此写 key(也可通过 apiKeys 字段或环境变量提供)
|
|
32
|
+
baseUrl: z.string(),
|
|
33
|
+
// OpenAI 兼容 API 的 base URL(必填)
|
|
34
|
+
defaultModel: z.string(),
|
|
35
|
+
// 默认使用的模型 ID
|
|
36
|
+
models: z.array(CustomModelSchema).default([]),
|
|
37
|
+
timeout: z.number().optional()
|
|
38
|
+
// 请求超时(ms),覆盖全局默认值
|
|
39
|
+
});
|
|
40
|
+
var ModelParamsSchema = z.object({
|
|
41
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
42
|
+
maxTokens: z.number().int().positive().optional(),
|
|
43
|
+
timeout: z.number().int().positive().optional(),
|
|
44
|
+
/** 是否启用深度思考(thinking)模式,Claude Sonnet/Opus、GLM-5 等 */
|
|
45
|
+
thinking: z.boolean().optional(),
|
|
46
|
+
/** thinking 模式的 token 预算(最小 1024,仅 Claude Extended Thinking 使用) */
|
|
47
|
+
thinkingBudget: z.number().int().min(1024).optional()
|
|
48
|
+
});
|
|
49
|
+
var UserProfileSchema = z.object({
|
|
50
|
+
/** 真实姓名(如 "Jin Zhengdong") */
|
|
51
|
+
name: z.string().optional(),
|
|
52
|
+
/** 昵称/称呼偏好(如 "东叔"),AI 会以此称呼你 */
|
|
53
|
+
nickname: z.string().optional(),
|
|
54
|
+
/** 职业角色(如 "Full-stack developer"、"Data scientist") */
|
|
55
|
+
role: z.string().optional(),
|
|
56
|
+
/** 个人简介 / 专长描述(1-3 句话) */
|
|
57
|
+
bio: z.string().optional(),
|
|
58
|
+
/** 兴趣领域或技术栈(如 ["TypeScript", "Rust", "AI/ML"]) */
|
|
59
|
+
interests: z.array(z.string()).default([]),
|
|
60
|
+
/** 语言偏好(如 "zh-CN"、"en"),AI 将据此选择交流语言 */
|
|
61
|
+
locale: z.string().optional(),
|
|
62
|
+
/** 自定义人设补充(自由格式,直接注入 system prompt) */
|
|
63
|
+
extra: z.string().optional()
|
|
64
|
+
}).default({});
|
|
65
|
+
var ConfigSchema = z.object({
|
|
66
|
+
version: z.string().default("1.0.0"),
|
|
67
|
+
// 用户身份档案 — 跨 Provider 的 "灵魂"
|
|
68
|
+
userProfile: UserProfileSchema,
|
|
69
|
+
defaultProvider: z.string().default("claude"),
|
|
70
|
+
// 每个 provider 的默认模型(key 为 provider ID)
|
|
71
|
+
defaultModels: z.record(z.string()).default({}),
|
|
72
|
+
// API Keys:放宽为任意 record,支持自定义 provider ID
|
|
73
|
+
apiKeys: z.record(z.string()).default({}),
|
|
74
|
+
customBaseUrls: z.record(z.string()).default({}),
|
|
75
|
+
// Per-provider timeout in ms (e.g. { deepseek: 60000 })
|
|
76
|
+
// ⚠️ Timeout 优先级说明(L5):
|
|
77
|
+
// 1. modelParams[modelId].timeout — 最高优先级,精确到具体模型(如 deepseek-reasoner 需要更长超时)
|
|
78
|
+
// 2. timeouts[providerId] — Provider 级默认,覆盖内置默认值
|
|
79
|
+
// 3. 内置 Provider 的硬编码默认值 — 最低兜底(如 OpenAI 兼容系列默认 60000ms)
|
|
80
|
+
// 实现位置:src/providers/registry.ts initialize(),将 timeouts[id] 注入 provider
|
|
81
|
+
timeouts: z.record(z.number()).default({}),
|
|
82
|
+
// HTTP/HTTPS 代理地址(Node.js 不自动使用系统代理,需显式配置)
|
|
83
|
+
// 例:http://127.0.0.1:10809
|
|
84
|
+
// 也可通过环境变量 HTTPS_PROXY / HTTP_PROXY 覆盖
|
|
85
|
+
proxy: z.string().optional(),
|
|
86
|
+
// 自定义 Provider 列表(OpenAI 兼容接口,无需改代码)
|
|
87
|
+
customProviders: z.array(CustomProviderConfigSchema).default([]),
|
|
88
|
+
// 按模型 ID 存储的推理参数(key 为模型 ID,如 "deepseek-chat")
|
|
89
|
+
modelParams: z.record(ModelParamsSchema).default({}),
|
|
90
|
+
ui: z.object({
|
|
91
|
+
streaming: z.boolean().default(true),
|
|
92
|
+
markdownRendering: z.boolean().default(true),
|
|
93
|
+
showTokenCount: z.boolean().default(true),
|
|
94
|
+
/** 桌面通知阈值(毫秒):AI 任务耗时超过此值时发送系统通知。0 = 禁用。默认 10000 (10s) */
|
|
95
|
+
notificationThreshold: z.number().default(1e4),
|
|
96
|
+
/** 终端输出折行宽度。0 = 自动(使用终端宽度),>0 = 固定列宽。默认 0 */
|
|
97
|
+
wordWrap: z.number().int().min(0).default(0),
|
|
98
|
+
/** 颜色主题:'dark'(默认)| 'light' | 'custom' */
|
|
99
|
+
theme: z.enum(["dark", "light", "custom"]).default("dark"),
|
|
100
|
+
/** 自定义颜色覆盖(仅在 theme='custom' 时生效)。值为 chalk 颜色名或 '#hex',支持 'bold.cyan' 组合 */
|
|
101
|
+
colors: z.object({
|
|
102
|
+
prompt: z.string().optional(),
|
|
103
|
+
info: z.string().optional(),
|
|
104
|
+
warning: z.string().optional(),
|
|
105
|
+
error: z.string().optional(),
|
|
106
|
+
success: z.string().optional(),
|
|
107
|
+
dim: z.string().optional(),
|
|
108
|
+
accent: z.string().optional(),
|
|
109
|
+
toolCall: z.string().optional(),
|
|
110
|
+
toolResult: z.string().optional(),
|
|
111
|
+
heading: z.string().optional()
|
|
112
|
+
}).optional()
|
|
113
|
+
}).default({}),
|
|
114
|
+
session: z.object({
|
|
115
|
+
autoSave: z.boolean().default(true),
|
|
116
|
+
maxHistoryDays: z.number().default(30),
|
|
117
|
+
systemPrompt: z.string().optional()
|
|
118
|
+
}).default({}),
|
|
119
|
+
// 项目上下文文件配置
|
|
120
|
+
// 启动时自动读取并注入 system prompt,类似 Claude Code 的 CLAUDE.md 机制
|
|
121
|
+
// 默认按顺序查找:AICLI.md → CLAUDE.md → .aicli/context.md
|
|
122
|
+
// 设为 false 可禁用此功能
|
|
123
|
+
contextFile: z.union([z.string(), z.literal(false)]).default("auto"),
|
|
124
|
+
// Google Custom Search API 的 Search Engine ID (cx 参数)
|
|
125
|
+
// API Key 通过 apiKeys['google-search'] 或 AICLI_API_KEY_GOOGLESEARCH 环境变量配置
|
|
126
|
+
// CX 也可通过 AICLI_GOOGLE_CX 环境变量覆盖
|
|
127
|
+
googleSearchEngineId: z.string().optional(),
|
|
128
|
+
// MCP (Model Context Protocol) 服务器配置
|
|
129
|
+
// 声明外部 MCP 服务器,启动时自动连接、发现工具并注册
|
|
130
|
+
// 配置格式兼容 Claude Desktop(command + args + env)
|
|
131
|
+
// 示例:{ "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path"] } }
|
|
132
|
+
mcpServers: z.record(z.object({
|
|
133
|
+
command: z.string(),
|
|
134
|
+
args: z.array(z.string()).default([]),
|
|
135
|
+
env: z.record(z.string()).optional(),
|
|
136
|
+
timeout: z.number().default(3e4)
|
|
137
|
+
})).default({}),
|
|
138
|
+
// 工具执行钩子(shell 命令,模板变量:{tool} {dangerLevel} {args} {status})
|
|
139
|
+
hooks: z.object({
|
|
140
|
+
preToolExecution: z.string().optional(),
|
|
141
|
+
postToolExecution: z.string().optional()
|
|
142
|
+
}).optional(),
|
|
143
|
+
// 工具权限规则(按顺序匹配第一个生效)
|
|
144
|
+
permissionRules: z.array(z.object({
|
|
145
|
+
tool: z.string(),
|
|
146
|
+
action: z.enum(["auto-approve", "deny", "confirm"]),
|
|
147
|
+
when: z.object({
|
|
148
|
+
dangerLevel: z.enum(["safe", "write", "destructive"]).optional(),
|
|
149
|
+
pathPattern: z.string().optional()
|
|
150
|
+
}).optional()
|
|
151
|
+
})).default([]),
|
|
152
|
+
// 无规则匹配时的默认权限动作
|
|
153
|
+
defaultPermission: z.enum(["auto-approve", "deny", "confirm"]).default("confirm"),
|
|
154
|
+
// 自动上下文压缩开关
|
|
155
|
+
// 当对话估算 token 数超过模型 contextWindow 的 80% 时,自动触发 compact 压缩旧消息
|
|
156
|
+
// 默认开启。设为 false 则仅在手动 /compact 时压缩
|
|
157
|
+
autoCompact: z.boolean().default(true),
|
|
158
|
+
// Agentic 工具调用循环单次对话最大轮次(默认 200)。
|
|
159
|
+
// 超过此值后 AI 被强制停止调用工具并生成总结。
|
|
160
|
+
// 建议范围:25(保守)~ 1000(宽松,接近 Claude Code)。
|
|
161
|
+
// CLI `--max-tool-rounds <n>` 可覆盖此值。
|
|
162
|
+
maxToolRounds: z.number().int().min(1).max(1e4).default(200),
|
|
163
|
+
// Auto-pause checkpoint:Agentic 循环每隔多少轮暂停一次,让用户确认方向或中途介入。
|
|
164
|
+
// 默认 50;设为 0 禁用(完全自动执行到完成或 maxToolRounds 用尽)。
|
|
165
|
+
// CLI 与 Web UI 行为一致:CLI 用 readline question 提示,Web UI 弹出对话框。
|
|
166
|
+
autoPauseInterval: z.number().int().min(0).max(1e4).default(50),
|
|
167
|
+
// 单次工具输出(如 read_file、bash、grep_files)返回给 AI 的最大字符数上限。
|
|
168
|
+
// 默认 500_000 (~500K chars ≈ 6000-8000 行代码)。
|
|
169
|
+
// 实际上限还会受模型 contextWindow 动态约束(取 contextWindow/4 作为下限)。
|
|
170
|
+
// 设置为 0 或未配置时使用默认值;不建议设为小于 12_000 或大于模型 contextWindow/2。
|
|
171
|
+
maxToolOutputChars: z.number().int().min(0).default(5e5),
|
|
172
|
+
// 月度成本预算(USD)。
|
|
173
|
+
// 设置后,每次 AI 回复后会跟踪成本,接近或超过预算时在 /status 和 /cost 中显示警告。
|
|
174
|
+
// 默认 0 = 不限制。例:50 表示每月最多花 $50。
|
|
175
|
+
monthlyBudget: z.number().min(0).default(0),
|
|
176
|
+
// 插件加载开关(安全控制)
|
|
177
|
+
// 默认 false:不自动加载 ~/.aicli/plugins/ 中的插件文件。
|
|
178
|
+
// 插件以完整 Node.js 权限在主进程中执行(可读写文件、访问网络、执行命令),
|
|
179
|
+
// 必须确认插件来源可信后,再设为 true 启用。
|
|
180
|
+
// 可通过 /config 命令或直接编辑 ~/.aicli/config.json 开启。
|
|
181
|
+
allowPlugins: z.boolean().default(false),
|
|
182
|
+
// 智能模型路由(v0.4.68+)
|
|
183
|
+
// 按用户每轮输入的内容/标签/长度动态选择模型,在同一 provider 内切换,
|
|
184
|
+
// 例:短问题走 haiku(省钱),planning 走 opus(质量)。
|
|
185
|
+
// enabled=false 时永远返回当前模型。rules 按顺序匹配,首个命中的规则生效。
|
|
186
|
+
// 每个 rule 的 match 必须至少有一个条件(tag/contains/maxLength/minLength)。
|
|
187
|
+
// 详见 src/core/model-router.ts。
|
|
188
|
+
routing: z.object({
|
|
189
|
+
enabled: z.boolean().default(false),
|
|
190
|
+
rules: z.array(z.object({
|
|
191
|
+
match: z.object({
|
|
192
|
+
contains: z.array(z.string()).optional(),
|
|
193
|
+
maxLength: z.number().int().positive().optional(),
|
|
194
|
+
minLength: z.number().int().positive().optional(),
|
|
195
|
+
tag: z.string().optional()
|
|
196
|
+
}),
|
|
197
|
+
model: z.string(),
|
|
198
|
+
name: z.string().optional()
|
|
199
|
+
})).default([]),
|
|
200
|
+
fallback: z.string().optional()
|
|
201
|
+
}).default({ enabled: false, rules: [] })
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// src/config/config-manager.ts
|
|
205
|
+
var ConfigManager = class {
|
|
206
|
+
configDir;
|
|
207
|
+
configPath;
|
|
208
|
+
config;
|
|
209
|
+
constructor(configDir) {
|
|
210
|
+
this.configDir = configDir ?? join(homedir(), CONFIG_DIR_NAME);
|
|
211
|
+
this.configPath = join(this.configDir, CONFIG_FILE_NAME);
|
|
212
|
+
this.config = this.load();
|
|
213
|
+
}
|
|
214
|
+
load() {
|
|
215
|
+
if (!existsSync(this.configPath)) {
|
|
216
|
+
return ConfigSchema.parse({});
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
const raw = JSON.parse(readFileSync(this.configPath, "utf-8"));
|
|
220
|
+
return ConfigSchema.parse(raw);
|
|
221
|
+
} catch (err) {
|
|
222
|
+
throw new ConfigError(
|
|
223
|
+
`Config file at ${this.configPath} is invalid. Delete it and run 'ai-cli config' to recreate.
|
|
224
|
+
${err}`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
save() {
|
|
229
|
+
mkdirSync(this.configDir, { recursive: true });
|
|
230
|
+
writeFileSync(this.configPath, JSON.stringify(this.config, null, 2), "utf-8");
|
|
231
|
+
}
|
|
232
|
+
getApiKey(providerId) {
|
|
233
|
+
const envKey = EnvLoader.getApiKey(providerId);
|
|
234
|
+
if (envKey) return envKey;
|
|
235
|
+
return this.config.apiKeys[providerId];
|
|
236
|
+
}
|
|
237
|
+
setApiKey(providerId, key) {
|
|
238
|
+
this.config.apiKeys[providerId] = key;
|
|
239
|
+
this.save();
|
|
240
|
+
}
|
|
241
|
+
get(key) {
|
|
242
|
+
return this.config[key];
|
|
243
|
+
}
|
|
244
|
+
set(key, value) {
|
|
245
|
+
this.config[key] = value;
|
|
246
|
+
this.save();
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* 仅修改内存中的配置值,不持久化到磁盘。
|
|
250
|
+
* 用于 CLI 命令行参数覆盖(-p, -m, --no-stream),使其只对当前进程生效。
|
|
251
|
+
*/
|
|
252
|
+
setTransient(key, value) {
|
|
253
|
+
this.config[key] = value;
|
|
254
|
+
}
|
|
255
|
+
isFirstRun() {
|
|
256
|
+
return !existsSync(this.configPath);
|
|
257
|
+
}
|
|
258
|
+
getConfigDir() {
|
|
259
|
+
return this.configDir;
|
|
260
|
+
}
|
|
261
|
+
getHistoryDir() {
|
|
262
|
+
return join(this.configDir, HISTORY_DIR_NAME);
|
|
263
|
+
}
|
|
264
|
+
getPluginsDir() {
|
|
265
|
+
return join(this.configDir, PLUGINS_DIR_NAME);
|
|
266
|
+
}
|
|
267
|
+
getDefaultProvider() {
|
|
268
|
+
return EnvLoader.getDefaultProvider() ?? this.config.defaultProvider;
|
|
269
|
+
}
|
|
270
|
+
/** 点分路径读取配置值,如 `ui.theme` → config.ui.theme */
|
|
271
|
+
getByPath(path) {
|
|
272
|
+
const keys = path.split(".");
|
|
273
|
+
let current = this.config;
|
|
274
|
+
for (const key of keys) {
|
|
275
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
276
|
+
current = current[key];
|
|
277
|
+
}
|
|
278
|
+
return current;
|
|
279
|
+
}
|
|
280
|
+
/** 点分路径写入配置值,自动类型转换(boolean/number/string)并持久化 */
|
|
281
|
+
setByPath(path, rawValue) {
|
|
282
|
+
const keys = path.split(".");
|
|
283
|
+
if (keys.length === 0) return;
|
|
284
|
+
let value = rawValue;
|
|
285
|
+
if (rawValue === "true") value = true;
|
|
286
|
+
else if (rawValue === "false") value = false;
|
|
287
|
+
else if (rawValue !== "" && !isNaN(Number(rawValue))) value = Number(rawValue);
|
|
288
|
+
const draft = JSON.parse(JSON.stringify(this.config));
|
|
289
|
+
let current = draft;
|
|
290
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
291
|
+
const key = keys[i];
|
|
292
|
+
if (current[key] == null || typeof current[key] !== "object") {
|
|
293
|
+
current[key] = {};
|
|
294
|
+
}
|
|
295
|
+
current = current[key];
|
|
296
|
+
}
|
|
297
|
+
current[keys[keys.length - 1]] = value;
|
|
298
|
+
const result = ConfigSchema.safeParse(draft);
|
|
299
|
+
if (!result.success) {
|
|
300
|
+
const firstErr = result.error.errors[0];
|
|
301
|
+
throw new ConfigError(`Invalid config value for "${path}": ${firstErr?.message ?? "validation failed"}`);
|
|
302
|
+
}
|
|
303
|
+
this.config = result.data;
|
|
304
|
+
this.save();
|
|
305
|
+
}
|
|
306
|
+
/** 获取完整配置对象的格式化 JSON 字符串(用于 /config show 等展示) */
|
|
307
|
+
toFormattedJSON() {
|
|
308
|
+
return JSON.stringify(this.config, null, 2);
|
|
309
|
+
}
|
|
310
|
+
/** 获取完整配置对象(JSON 兼容的原始对象) */
|
|
311
|
+
toJSON() {
|
|
312
|
+
return structuredClone(this.config);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
export {
|
|
317
|
+
ConfigManager
|
|
318
|
+
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/constants.ts
|
|
4
|
+
var VERSION = "0.4.73";
|
|
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
|
+
};
|