jinzd-ai-cli 0.4.217 → 0.4.218
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 +4 -2
- package/dist/{batch-YCOVKTXD.js → batch-7V7OTMUP.js} +2 -2
- package/dist/{chunk-WZ3VKLF3.js → chunk-5CA2TJ5F.js} +1 -1
- package/dist/{chunk-QAYOI57M.js → chunk-C2Z42DI5.js} +1 -1
- package/dist/{chunk-MGBMNCHG.js → chunk-GX3HSGJX.js} +452 -156
- package/dist/{chunk-OUAZOE5U.js → chunk-H2UIHGHH.js} +23 -22
- package/dist/{chunk-KOPUCJXM.js → chunk-L4UREAID.js} +3 -3
- package/dist/{chunk-SNJAOXFT.js → chunk-MWKE2TNS.js} +1 -1
- package/dist/{chunk-XJGEQIYS.js → chunk-NTCB7CMT.js} +1 -1
- package/dist/{chunk-524WZOKS.js → chunk-OUC75QCF.js} +1 -1
- package/dist/{chunk-WKOQ5CYC.js → chunk-P4VBLXKS.js} +1 -1
- package/dist/{chunk-VTH7BLXK.js → chunk-VGFTM3XT.js} +1 -1
- package/dist/{ci-52RZIYWB.js → ci-L6GH2WVC.js} +4 -4
- package/dist/{ci-format-73UXKE65.js → ci-format-WW7454AY.js} +2 -2
- package/dist/{constants-DIXAD35W.js → constants-NCTFSHDU.js} +1 -1
- package/dist/{doctor-cli-7GOQPULZ.js → doctor-cli-EWMFBP5Q.js} +4 -4
- package/dist/electron-server.js +575 -231
- package/dist/{hub-GIGBITZN.js → hub-CDL6T7CP.js} +1 -1
- package/dist/index.js +148 -46
- package/dist/{pr-KPQ5RPKB.js → pr-D6PEKEGK.js} +4 -4
- package/dist/{run-tests-K7QR5QN4.js → run-tests-NXVVKAK2.js} +1 -1
- package/dist/{run-tests-ZDSA3QES.js → run-tests-SWU2XEV7.js} +2 -2
- package/dist/{server-QT3SC2KI.js → server-LHYSS6CK.js} +85 -21
- package/dist/{server-22YF3U34.js → server-WUT7VYTD.js} +4 -4
- package/dist/{task-orchestrator-BHQQCVTY.js → task-orchestrator-C5AA2BI5.js} +4 -4
- package/dist/{usage-WZZFSFLM.js → usage-6ZUUJBI2.js} +2 -2
- package/package.json +1 -1
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
} from "./chunk-T2NL5ZIA.js";
|
|
6
6
|
import {
|
|
7
7
|
runTestsTool
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VGFTM3XT.js";
|
|
9
9
|
import {
|
|
10
10
|
runTool
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-MWKE2TNS.js";
|
|
12
12
|
import {
|
|
13
13
|
getDangerLevel,
|
|
14
14
|
isFileWriteTool,
|
|
@@ -25,10 +25,11 @@ import {
|
|
|
25
25
|
MEMORY_FILE_NAME,
|
|
26
26
|
MEMORY_MAX_CHARS,
|
|
27
27
|
MEMORY_STORE_FILE_NAME,
|
|
28
|
+
PLUGINS_DIR_NAME,
|
|
28
29
|
SUBAGENT_ALLOWED_TOOLS,
|
|
29
30
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
30
31
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
31
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-NTCB7CMT.js";
|
|
32
33
|
import {
|
|
33
34
|
getGitRoot
|
|
34
35
|
} from "./chunk-HOSJZMQS.js";
|
|
@@ -53,14 +54,295 @@ import {
|
|
|
53
54
|
atomicWriteFileSync
|
|
54
55
|
} from "./chunk-IW3Q7AE5.js";
|
|
55
56
|
|
|
57
|
+
// src/plugins/plugin-manager.ts
|
|
58
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, cpSync, rmSync, statSync } from "fs";
|
|
59
|
+
import { basename, dirname, join, resolve } from "path";
|
|
60
|
+
import { createHash } from "crypto";
|
|
61
|
+
import { z } from "zod";
|
|
62
|
+
var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
|
|
63
|
+
var STATE_FILE = "plugin-state.json";
|
|
64
|
+
var HookCommandSchema = z.union([
|
|
65
|
+
z.string(),
|
|
66
|
+
z.object({
|
|
67
|
+
command: z.string(),
|
|
68
|
+
source: z.enum(["user", "project", "managed"]).optional(),
|
|
69
|
+
description: z.string().optional(),
|
|
70
|
+
required: z.boolean().optional(),
|
|
71
|
+
timeoutMs: z.number().int().min(100).max(3e4).optional(),
|
|
72
|
+
disabled: z.boolean().optional()
|
|
73
|
+
}),
|
|
74
|
+
z.array(z.union([
|
|
75
|
+
z.string(),
|
|
76
|
+
z.object({
|
|
77
|
+
command: z.string(),
|
|
78
|
+
source: z.enum(["user", "project", "managed"]).optional(),
|
|
79
|
+
description: z.string().optional(),
|
|
80
|
+
required: z.boolean().optional(),
|
|
81
|
+
timeoutMs: z.number().int().min(100).max(3e4).optional(),
|
|
82
|
+
disabled: z.boolean().optional()
|
|
83
|
+
})
|
|
84
|
+
]))
|
|
85
|
+
]);
|
|
86
|
+
var HookEventNameSchema = z.enum([
|
|
87
|
+
"SessionStart",
|
|
88
|
+
"UserPromptSubmit",
|
|
89
|
+
"PreToolUse",
|
|
90
|
+
"PermissionRequest",
|
|
91
|
+
"PostToolUse",
|
|
92
|
+
"PreCompact",
|
|
93
|
+
"PostCompact",
|
|
94
|
+
"Stop",
|
|
95
|
+
"SubagentStart",
|
|
96
|
+
"SubagentStop"
|
|
97
|
+
]);
|
|
98
|
+
var McpServerSchema = z.object({
|
|
99
|
+
command: z.string(),
|
|
100
|
+
args: z.array(z.string()).default([]),
|
|
101
|
+
env: z.record(z.string()).optional(),
|
|
102
|
+
timeout: z.number().default(3e4)
|
|
103
|
+
});
|
|
104
|
+
var PluginManifestSchema = z.object({
|
|
105
|
+
name: z.string().regex(/^[a-zA-Z0-9._-]{1,80}$/),
|
|
106
|
+
version: z.string().min(1),
|
|
107
|
+
description: z.string().optional(),
|
|
108
|
+
skills: z.array(z.string()).default([]),
|
|
109
|
+
hooks: z.object({
|
|
110
|
+
enabled: z.boolean().optional(),
|
|
111
|
+
events: z.record(HookEventNameSchema, HookCommandSchema).default({})
|
|
112
|
+
}).default({ events: {} }),
|
|
113
|
+
commands: z.array(z.string()).default([]),
|
|
114
|
+
mcpServers: z.record(McpServerSchema).default({}),
|
|
115
|
+
agents: z.array(z.string()).default([]),
|
|
116
|
+
permissionHints: z.array(z.string()).default([])
|
|
117
|
+
}).strict();
|
|
118
|
+
function pluginRoot(configDir) {
|
|
119
|
+
return join(configDir, PLUGINS_DIR_NAME);
|
|
120
|
+
}
|
|
121
|
+
function pluginManifestPath(pluginDir) {
|
|
122
|
+
return join(pluginDir, MANIFEST_RELATIVE);
|
|
123
|
+
}
|
|
124
|
+
function statePath(configDir) {
|
|
125
|
+
return join(pluginRoot(configDir), STATE_FILE);
|
|
126
|
+
}
|
|
127
|
+
function loadState(configDir) {
|
|
128
|
+
const file = statePath(configDir);
|
|
129
|
+
if (!existsSync(file)) return { version: 1, plugins: [] };
|
|
130
|
+
try {
|
|
131
|
+
const parsed = JSON.parse(readFileSync(file, "utf-8"));
|
|
132
|
+
return { version: 1, plugins: Array.isArray(parsed.plugins) ? parsed.plugins : [] };
|
|
133
|
+
} catch {
|
|
134
|
+
return { version: 1, plugins: [] };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function saveState(configDir, state) {
|
|
138
|
+
mkdirSync(pluginRoot(configDir), { recursive: true });
|
|
139
|
+
atomicWriteFileSync(statePath(configDir), JSON.stringify(state, null, 2));
|
|
140
|
+
}
|
|
141
|
+
function hashPluginManifest(manifestPath) {
|
|
142
|
+
return createHash("sha256").update(readFileSync(manifestPath)).digest("hex");
|
|
143
|
+
}
|
|
144
|
+
function readPluginManifest(manifestPath) {
|
|
145
|
+
return PluginManifestSchema.parse(JSON.parse(readFileSync(manifestPath, "utf-8")));
|
|
146
|
+
}
|
|
147
|
+
function findPluginSourceDir(inputPath) {
|
|
148
|
+
const abs = resolve(inputPath);
|
|
149
|
+
const direct = statSync(abs).isDirectory() ? abs : dirname(abs);
|
|
150
|
+
if (existsSync(pluginManifestPath(direct))) return direct;
|
|
151
|
+
const nested = join(direct, ".aicli-plugin");
|
|
152
|
+
if (existsSync(join(nested, "plugin.json"))) return direct;
|
|
153
|
+
throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
|
|
154
|
+
}
|
|
155
|
+
function installPlugin(configDir, inputPath) {
|
|
156
|
+
const sourceDir = findPluginSourceDir(inputPath);
|
|
157
|
+
const manifest = readPluginManifest(pluginManifestPath(sourceDir));
|
|
158
|
+
const targetDir = join(pluginRoot(configDir), manifest.name);
|
|
159
|
+
mkdirSync(pluginRoot(configDir), { recursive: true });
|
|
160
|
+
if (existsSync(targetDir)) rmSync(targetDir, { recursive: true, force: true });
|
|
161
|
+
cpSync(sourceDir, targetDir, { recursive: true });
|
|
162
|
+
const manifestPath = pluginManifestPath(targetDir);
|
|
163
|
+
const hash = hashPluginManifest(manifestPath);
|
|
164
|
+
const state = loadState(configDir);
|
|
165
|
+
const previous = state.plugins.find((p) => p.name === manifest.name);
|
|
166
|
+
const next = {
|
|
167
|
+
name: manifest.name,
|
|
168
|
+
enabled: previous?.enabled ?? false,
|
|
169
|
+
trusted: false,
|
|
170
|
+
hash,
|
|
171
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
172
|
+
};
|
|
173
|
+
state.plugins = [...state.plugins.filter((p) => p.name !== manifest.name), next];
|
|
174
|
+
saveState(configDir, state);
|
|
175
|
+
return { name: manifest.name, dir: targetDir, manifestPath, manifest, hash, enabled: next.enabled, trusted: false, valid: true };
|
|
176
|
+
}
|
|
177
|
+
function setPluginEnabled(configDir, name, enabled) {
|
|
178
|
+
const plugin = getInstalledPlugin(configDir, name);
|
|
179
|
+
if (!plugin.valid) throw new Error(plugin.error ?? `invalid plugin: ${name}`);
|
|
180
|
+
const state = loadState(configDir);
|
|
181
|
+
const current = state.plugins.find((p) => p.name === plugin.name) ?? {
|
|
182
|
+
name: plugin.name,
|
|
183
|
+
enabled: false,
|
|
184
|
+
trusted: false,
|
|
185
|
+
hash: plugin.hash,
|
|
186
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
187
|
+
};
|
|
188
|
+
const next = { ...current, enabled, hash: plugin.hash, enabledAt: enabled ? (/* @__PURE__ */ new Date()).toISOString() : current.enabledAt };
|
|
189
|
+
state.plugins = [...state.plugins.filter((p) => p.name !== plugin.name), next];
|
|
190
|
+
saveState(configDir, state);
|
|
191
|
+
return next;
|
|
192
|
+
}
|
|
193
|
+
function trustPlugin(configDir, name) {
|
|
194
|
+
const plugin = getInstalledPlugin(configDir, name);
|
|
195
|
+
if (!plugin.valid) throw new Error(plugin.error ?? `invalid plugin: ${name}`);
|
|
196
|
+
const state = loadState(configDir);
|
|
197
|
+
const current = state.plugins.find((p) => p.name === plugin.name) ?? {
|
|
198
|
+
name: plugin.name,
|
|
199
|
+
enabled: false,
|
|
200
|
+
trusted: false,
|
|
201
|
+
hash: plugin.hash,
|
|
202
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
203
|
+
};
|
|
204
|
+
const next = { ...current, trusted: true, hash: plugin.hash, trustedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
205
|
+
state.plugins = [...state.plugins.filter((p) => p.name !== plugin.name), next];
|
|
206
|
+
saveState(configDir, state);
|
|
207
|
+
return next;
|
|
208
|
+
}
|
|
209
|
+
function getInstalledPlugin(configDir, name) {
|
|
210
|
+
const plugins = listInstalledPlugins(configDir);
|
|
211
|
+
const matches = plugins.filter((p) => p.name === name || p.name.startsWith(name));
|
|
212
|
+
if (matches.length === 0) throw new Error(`plugin not found: ${name}`);
|
|
213
|
+
if (matches.length > 1) throw new Error(`plugin name is ambiguous: ${name}`);
|
|
214
|
+
return matches[0];
|
|
215
|
+
}
|
|
216
|
+
function listInstalledPlugins(configDir) {
|
|
217
|
+
const root = pluginRoot(configDir);
|
|
218
|
+
if (!existsSync(root)) return [];
|
|
219
|
+
const state = loadState(configDir);
|
|
220
|
+
const out = [];
|
|
221
|
+
for (const entry of readdirSync(root)) {
|
|
222
|
+
const dir = join(root, entry);
|
|
223
|
+
try {
|
|
224
|
+
if (!statSync(dir).isDirectory()) continue;
|
|
225
|
+
} catch {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const manifestPath = pluginManifestPath(dir);
|
|
229
|
+
if (!existsSync(manifestPath)) continue;
|
|
230
|
+
try {
|
|
231
|
+
const manifest = readPluginManifest(manifestPath);
|
|
232
|
+
const hash = hashPluginManifest(manifestPath);
|
|
233
|
+
const st = state.plugins.find((p) => p.name === manifest.name);
|
|
234
|
+
const trusted = st?.trusted === true && st.hash === hash;
|
|
235
|
+
out.push({
|
|
236
|
+
name: manifest.name,
|
|
237
|
+
dir,
|
|
238
|
+
manifestPath,
|
|
239
|
+
manifest,
|
|
240
|
+
hash,
|
|
241
|
+
enabled: st?.enabled === true,
|
|
242
|
+
trusted,
|
|
243
|
+
valid: true
|
|
244
|
+
});
|
|
245
|
+
} catch (err) {
|
|
246
|
+
out.push({
|
|
247
|
+
name: entry,
|
|
248
|
+
dir,
|
|
249
|
+
manifestPath,
|
|
250
|
+
manifest: { name: entry, version: "invalid", skills: [], commands: [], agents: [], hooks: { events: {} }, mcpServers: {}, permissionHints: [] },
|
|
251
|
+
hash: "",
|
|
252
|
+
enabled: false,
|
|
253
|
+
trusted: false,
|
|
254
|
+
valid: false,
|
|
255
|
+
error: err instanceof Error ? err.message : String(err)
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
260
|
+
}
|
|
261
|
+
function activePlugins(configDir) {
|
|
262
|
+
return listInstalledPlugins(configDir).filter((p) => p.valid && p.enabled && p.trusted);
|
|
263
|
+
}
|
|
264
|
+
function uniqueDirs(plugin, rels) {
|
|
265
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
266
|
+
for (const rel of rels) {
|
|
267
|
+
const abs = resolve(plugin.dir, rel);
|
|
268
|
+
if (!abs.startsWith(resolve(plugin.dir))) continue;
|
|
269
|
+
if (existsSync(abs)) dirs.add(statSync(abs).isDirectory() ? abs : dirname(abs));
|
|
270
|
+
}
|
|
271
|
+
return [...dirs];
|
|
272
|
+
}
|
|
273
|
+
function prefixCommand(plugin, command) {
|
|
274
|
+
return command.replaceAll("{pluginDir}", plugin.dir).replaceAll("$PLUGIN_DIR", plugin.dir);
|
|
275
|
+
}
|
|
276
|
+
function pluginHooks(plugin) {
|
|
277
|
+
const events = plugin.manifest.hooks?.events ?? {};
|
|
278
|
+
const out = {};
|
|
279
|
+
for (const [event, raw] of Object.entries(events)) {
|
|
280
|
+
const normalize = (item) => {
|
|
281
|
+
if (typeof item === "string") return { command: prefixCommand(plugin, item), source: "project", description: `plugin:${plugin.name}` };
|
|
282
|
+
if (item && typeof item === "object") {
|
|
283
|
+
const obj = item;
|
|
284
|
+
return { ...obj, command: prefixCommand(plugin, String(obj.command ?? "")), source: "project", description: obj.description ?? `plugin:${plugin.name}` };
|
|
285
|
+
}
|
|
286
|
+
return item;
|
|
287
|
+
};
|
|
288
|
+
out[event] = Array.isArray(raw) ? raw.map(normalize) : normalize(raw);
|
|
289
|
+
}
|
|
290
|
+
return Object.keys(out).length > 0 ? { enabled: plugin.manifest.hooks?.enabled ?? true, events: out } : void 0;
|
|
291
|
+
}
|
|
292
|
+
function mergePluginHooks(base, configDir) {
|
|
293
|
+
const plugins = activePlugins(configDir);
|
|
294
|
+
if (plugins.length === 0) return base;
|
|
295
|
+
const merged = { ...base ?? {}, enabled: base?.enabled ?? true, events: { ...base?.events ?? {} } };
|
|
296
|
+
for (const plugin of plugins) {
|
|
297
|
+
const hooks = pluginHooks(plugin);
|
|
298
|
+
if (!hooks?.events) continue;
|
|
299
|
+
for (const [event, entry] of Object.entries(hooks.events)) {
|
|
300
|
+
const key = event;
|
|
301
|
+
const existing = merged.events?.[key];
|
|
302
|
+
const existingItems = existing === void 0 ? [] : Array.isArray(existing) ? existing : [existing];
|
|
303
|
+
const newItems = Array.isArray(entry) ? entry : [entry];
|
|
304
|
+
merged.events[key] = [...existingItems, ...newItems];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return merged;
|
|
308
|
+
}
|
|
309
|
+
function getActivePluginAssets(configDir) {
|
|
310
|
+
const plugins = activePlugins(configDir);
|
|
311
|
+
const mcpServers = {};
|
|
312
|
+
const skillDirs = [];
|
|
313
|
+
const commandDirs = [];
|
|
314
|
+
const agentDirs = [];
|
|
315
|
+
for (const plugin of plugins) {
|
|
316
|
+
skillDirs.push(...uniqueDirs(plugin, plugin.manifest.skills));
|
|
317
|
+
commandDirs.push(...uniqueDirs(plugin, plugin.manifest.commands));
|
|
318
|
+
agentDirs.push(...uniqueDirs(plugin, plugin.manifest.agents));
|
|
319
|
+
for (const [serverName, server] of Object.entries(plugin.manifest.mcpServers)) {
|
|
320
|
+
mcpServers[`plugin_${plugin.name}_${serverName}`] = server;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return { plugins, skillDirs, commandDirs, agentDirs, hooks: mergePluginHooks(void 0, configDir), mcpServers };
|
|
324
|
+
}
|
|
325
|
+
function describePlugin(plugin) {
|
|
326
|
+
const m = plugin.manifest;
|
|
327
|
+
return [
|
|
328
|
+
`${plugin.name}@${m.version}`,
|
|
329
|
+
` status: ${plugin.enabled ? "enabled" : "disabled"} \xB7 ${plugin.trusted ? "trusted" : "untrusted"}${plugin.valid ? "" : " \xB7 invalid"}`,
|
|
330
|
+
` dir: ${plugin.dir}`,
|
|
331
|
+
m.description ? ` description: ${m.description}` : void 0,
|
|
332
|
+
` assets: ${m.skills.length} skill(s), ${m.commands.length} command(s), ${m.agents.length} agent(s), ${Object.keys(m.mcpServers).length} MCP server(s), ${Object.keys(m.hooks?.events ?? {}).length} hook event(s)`,
|
|
333
|
+
m.permissionHints.length ? ` permission hints: ${m.permissionHints.join("; ")}` : void 0,
|
|
334
|
+
plugin.error ? ` error: ${plugin.error}` : void 0
|
|
335
|
+
].filter((line) => Boolean(line));
|
|
336
|
+
}
|
|
337
|
+
|
|
56
338
|
// src/tools/builtin/bash.ts
|
|
57
339
|
import { spawn } from "child_process";
|
|
58
|
-
import { existsSync as
|
|
340
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
59
341
|
import { platform as platform2 } from "os";
|
|
60
|
-
import { resolve } from "path";
|
|
342
|
+
import { resolve as resolve2 } from "path";
|
|
61
343
|
|
|
62
344
|
// src/tools/undo-stack.ts
|
|
63
|
-
import { readFileSync, writeFileSync, unlinkSync, rmdirSync, existsSync } from "fs";
|
|
345
|
+
import { readFileSync as readFileSync2, writeFileSync, unlinkSync, rmdirSync, existsSync as existsSync2 } from "fs";
|
|
64
346
|
var MAX_UNDO_DEPTH = 20;
|
|
65
347
|
var UndoStack = class {
|
|
66
348
|
stack = [];
|
|
@@ -71,9 +353,9 @@ var UndoStack = class {
|
|
|
71
353
|
*/
|
|
72
354
|
push(filePath, description) {
|
|
73
355
|
let previousContent = null;
|
|
74
|
-
if (
|
|
356
|
+
if (existsSync2(filePath)) {
|
|
75
357
|
try {
|
|
76
|
-
previousContent =
|
|
358
|
+
previousContent = readFileSync2(filePath, "utf-8");
|
|
77
359
|
} catch {
|
|
78
360
|
return;
|
|
79
361
|
}
|
|
@@ -127,7 +409,7 @@ var UndoStack = class {
|
|
|
127
409
|
try {
|
|
128
410
|
if (entry.previousContent === null) {
|
|
129
411
|
if (entry.isDirectory) {
|
|
130
|
-
if (
|
|
412
|
+
if (existsSync2(entry.filePath)) {
|
|
131
413
|
try {
|
|
132
414
|
rmdirSync(entry.filePath);
|
|
133
415
|
return { entry, result: `Removed newly created directory: ${entry.filePath}` };
|
|
@@ -137,7 +419,7 @@ var UndoStack = class {
|
|
|
137
419
|
}
|
|
138
420
|
return { entry, result: `Directory already removed: ${entry.filePath}` };
|
|
139
421
|
} else {
|
|
140
|
-
if (
|
|
422
|
+
if (existsSync2(entry.filePath)) {
|
|
141
423
|
unlinkSync(entry.filePath);
|
|
142
424
|
}
|
|
143
425
|
return { entry, result: `Deleted newly created file: ${entry.filePath}` };
|
|
@@ -296,7 +578,7 @@ Important rules:
|
|
|
296
578
|
throw new ToolError("bash", blockingHint);
|
|
297
579
|
}
|
|
298
580
|
let currentCwd = getCwd();
|
|
299
|
-
if (!
|
|
581
|
+
if (!existsSync3(currentCwd)) {
|
|
300
582
|
const fallback = process.cwd();
|
|
301
583
|
process.stderr.write(
|
|
302
584
|
`[bash] Previous cwd "${currentCwd}" no longer exists, reset to "${fallback}"
|
|
@@ -307,8 +589,8 @@ Important rules:
|
|
|
307
589
|
}
|
|
308
590
|
let effectiveCwd = currentCwd;
|
|
309
591
|
if (cwdArg) {
|
|
310
|
-
const resolved =
|
|
311
|
-
if (!
|
|
592
|
+
const resolved = resolve2(currentCwd, cwdArg);
|
|
593
|
+
if (!existsSync3(resolved)) {
|
|
312
594
|
throw new ToolError(
|
|
313
595
|
"bash",
|
|
314
596
|
`cwd directory does not exist: "${resolved}". Create it first (e.g. mkdir -p "${resolved}") before specifying it as cwd.`
|
|
@@ -328,7 +610,7 @@ Important rules:
|
|
|
328
610
|
const parsedTargets = parseCreationTargets(command, effectiveCwd);
|
|
329
611
|
const parsedTargetsBefore = /* @__PURE__ */ new Map();
|
|
330
612
|
for (const t of parsedTargets) {
|
|
331
|
-
parsedTargetsBefore.set(t,
|
|
613
|
+
parsedTargetsBefore.set(t, existsSync3(t));
|
|
332
614
|
}
|
|
333
615
|
try {
|
|
334
616
|
const { stdout, stderr, status, signal, timedOut, aborted } = await runShell(
|
|
@@ -508,7 +790,7 @@ Then poll readiness with SHORT bash calls (these DO exit), e.g.:
|
|
|
508
790
|
}
|
|
509
791
|
function snapshotDir(dir) {
|
|
510
792
|
try {
|
|
511
|
-
return new Set(
|
|
793
|
+
return new Set(readdirSync2(dir).map((name) => resolve2(dir, name)));
|
|
512
794
|
} catch {
|
|
513
795
|
return /* @__PURE__ */ new Set();
|
|
514
796
|
}
|
|
@@ -516,25 +798,25 @@ function snapshotDir(dir) {
|
|
|
516
798
|
function parseCreationTargets(command, cwd) {
|
|
517
799
|
const targets = [];
|
|
518
800
|
for (const m of command.matchAll(/(?:echo|cat|printf)\s+[^>]*>\s*(['"]?)([^\s;&|'"]+)\1/g)) {
|
|
519
|
-
if (m[2]) targets.push(
|
|
801
|
+
if (m[2]) targets.push(resolve2(cwd, m[2]));
|
|
520
802
|
}
|
|
521
803
|
for (const m of command.matchAll(/\btouch\s+((?:['"]?[^\s;&|'"]+['"]?\s*)+)/g)) {
|
|
522
804
|
for (const f of m[1].trim().split(/\s+/)) {
|
|
523
805
|
const clean = f.replace(/^['"]|['"]$/g, "");
|
|
524
|
-
if (clean && !clean.startsWith("-")) targets.push(
|
|
806
|
+
if (clean && !clean.startsWith("-")) targets.push(resolve2(cwd, clean));
|
|
525
807
|
}
|
|
526
808
|
}
|
|
527
809
|
for (const m of command.matchAll(/\bmkdir\s+(?:-\w+\s+)*((?:['"]?[^\s;&|'"]+['"]?\s*)+)/g)) {
|
|
528
810
|
for (const d of m[1].trim().split(/\s+/)) {
|
|
529
811
|
const clean = d.replace(/^['"]|['"]$/g, "");
|
|
530
|
-
if (clean && !clean.startsWith("-")) targets.push(
|
|
812
|
+
if (clean && !clean.startsWith("-")) targets.push(resolve2(cwd, clean));
|
|
531
813
|
}
|
|
532
814
|
}
|
|
533
815
|
for (const m of command.matchAll(/\bcp\s+(?:-\w+\s+)*['"]?[^\s;&|'"]+['"]?\s+(['"]?)([^\s;&|'"]+)\1/g)) {
|
|
534
|
-
if (m[2]) targets.push(
|
|
816
|
+
if (m[2]) targets.push(resolve2(cwd, m[2]));
|
|
535
817
|
}
|
|
536
818
|
for (const m of command.matchAll(/\bNew-Item\s+(?:-(?:Path|ItemType)\s+\w+\s+)*['"]?([^\s;&|'"]+)['"]?/gi)) {
|
|
537
|
-
if (m[1] && !m[1].startsWith("-")) targets.push(
|
|
819
|
+
if (m[1] && !m[1].startsWith("-")) targets.push(resolve2(cwd, m[1]));
|
|
538
820
|
}
|
|
539
821
|
return [...new Set(targets)];
|
|
540
822
|
}
|
|
@@ -544,7 +826,7 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
|
|
|
544
826
|
for (const absPath of afterSnapshot) {
|
|
545
827
|
if (!beforeSnapshot.has(absPath)) {
|
|
546
828
|
try {
|
|
547
|
-
const st =
|
|
829
|
+
const st = statSync2(absPath);
|
|
548
830
|
if (st.isDirectory()) {
|
|
549
831
|
undoStack.pushNewDir(absPath, `bash (new dir): ${absPath}`);
|
|
550
832
|
} else {
|
|
@@ -556,9 +838,9 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
|
|
|
556
838
|
}
|
|
557
839
|
}
|
|
558
840
|
for (const [target, existedBefore] of parsedTargetsBefore) {
|
|
559
|
-
if (!existedBefore && !tracked.has(target) &&
|
|
841
|
+
if (!existedBefore && !tracked.has(target) && existsSync3(target)) {
|
|
560
842
|
try {
|
|
561
|
-
const st =
|
|
843
|
+
const st = statSync2(target);
|
|
562
844
|
if (st.isDirectory()) {
|
|
563
845
|
undoStack.pushNewDir(target, `bash (new dir): ${target}`);
|
|
564
846
|
} else {
|
|
@@ -668,8 +950,8 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
668
950
|
const target = lastMatch?.[2];
|
|
669
951
|
if (!target || target.startsWith("$") || target === "~") return;
|
|
670
952
|
try {
|
|
671
|
-
const newDir =
|
|
672
|
-
if (
|
|
953
|
+
const newDir = resolve2(baseCwd, target);
|
|
954
|
+
if (existsSync3(newDir)) {
|
|
673
955
|
setCwd(newDir);
|
|
674
956
|
}
|
|
675
957
|
} catch {
|
|
@@ -677,9 +959,9 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
677
959
|
}
|
|
678
960
|
|
|
679
961
|
// src/tools/builtin/read-file.ts
|
|
680
|
-
import { readFileSync as
|
|
962
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4, statSync as statSync3, readdirSync as readdirSync3 } from "fs";
|
|
681
963
|
import { execFileSync } from "child_process";
|
|
682
|
-
import { extname, resolve as
|
|
964
|
+
import { extname, resolve as resolve3, basename as basename2, sep, dirname as dirname2 } from "path";
|
|
683
965
|
import { homedir } from "os";
|
|
684
966
|
|
|
685
967
|
// src/tools/builtin/notebook-utils.ts
|
|
@@ -789,7 +1071,7 @@ var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
|
789
1071
|
function getHardRefusal(normalizedPath) {
|
|
790
1072
|
const home2 = homedir();
|
|
791
1073
|
const p = normalizedPath.toLowerCase();
|
|
792
|
-
const base =
|
|
1074
|
+
const base = basename2(normalizedPath).toLowerCase();
|
|
793
1075
|
if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "config.json") {
|
|
794
1076
|
return `[Refused] Reading ${normalizedPath} is blocked because it contains all of your provider API keys.
|
|
795
1077
|
If you genuinely need to inspect or edit it, open it manually outside ai-cli (e.g. via your editor).
|
|
@@ -803,7 +1085,7 @@ For programmatic access, use \`aicli config\` or \`aicli config get <key>\` whic
|
|
|
803
1085
|
function getSensitiveWarning(normalizedPath) {
|
|
804
1086
|
const home2 = homedir();
|
|
805
1087
|
const p = normalizedPath.toLowerCase();
|
|
806
|
-
const base =
|
|
1088
|
+
const base = basename2(normalizedPath).toLowerCase();
|
|
807
1089
|
if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "config.json") {
|
|
808
1090
|
return "[\u26A0 Security Warning: This file contains API keys. Be careful not to share this content.]\n\n";
|
|
809
1091
|
}
|
|
@@ -866,19 +1148,19 @@ function isBinaryBuffer(buf) {
|
|
|
866
1148
|
return nullCount / sample.length > 0.1;
|
|
867
1149
|
}
|
|
868
1150
|
function findSimilarFiles(filePath) {
|
|
869
|
-
const targetName =
|
|
1151
|
+
const targetName = basename2(filePath).toLowerCase();
|
|
870
1152
|
if (!targetName) return [];
|
|
871
1153
|
const cwd = process.cwd();
|
|
872
1154
|
const candidates = [];
|
|
873
1155
|
try {
|
|
874
|
-
for (const entry of
|
|
1156
|
+
for (const entry of readdirSync3(cwd, { withFileTypes: true })) {
|
|
875
1157
|
if (entry.isFile() && entry.name.toLowerCase() === targetName) {
|
|
876
1158
|
candidates.push(entry.name);
|
|
877
1159
|
}
|
|
878
1160
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
879
1161
|
try {
|
|
880
|
-
const subDir =
|
|
881
|
-
for (const sub of
|
|
1162
|
+
const subDir = resolve3(cwd, entry.name);
|
|
1163
|
+
for (const sub of readdirSync3(subDir, { withFileTypes: true })) {
|
|
882
1164
|
if (sub.isFile() && sub.name.toLowerCase() === targetName) {
|
|
883
1165
|
candidates.push(`${entry.name}/${sub.name}`);
|
|
884
1166
|
}
|
|
@@ -951,10 +1233,10 @@ var readFileTool = {
|
|
|
951
1233
|
const offsetLine = typeof rawOffset === "number" && Number.isFinite(rawOffset) && rawOffset > 0 ? Math.floor(rawOffset) : void 0;
|
|
952
1234
|
const limitLines = typeof rawLimit === "number" && Number.isFinite(rawLimit) && rawLimit > 0 ? Math.floor(rawLimit) : void 0;
|
|
953
1235
|
if (!filePath) throw new ToolError("read_file", "path is required");
|
|
954
|
-
const normalizedPath =
|
|
1236
|
+
const normalizedPath = resolve3(filePath);
|
|
955
1237
|
const refusal = getHardRefusal(normalizedPath);
|
|
956
1238
|
if (refusal) return refusal;
|
|
957
|
-
if (!
|
|
1239
|
+
if (!existsSync4(normalizedPath)) {
|
|
958
1240
|
const suggestions = findSimilarFiles(filePath);
|
|
959
1241
|
if (suggestions.length > 0) {
|
|
960
1242
|
throw new ToolError(
|
|
@@ -973,7 +1255,7 @@ Current working directory: ${process.cwd()}
|
|
|
973
1255
|
Please use list_dir to verify the file path and retry.`
|
|
974
1256
|
);
|
|
975
1257
|
}
|
|
976
|
-
const { size } =
|
|
1258
|
+
const { size } = statSync3(normalizedPath);
|
|
977
1259
|
if (size > MAX_FILE_BYTES) {
|
|
978
1260
|
const mb = (size / 1024 / 1024).toFixed(1);
|
|
979
1261
|
return `[File too large: ${filePath} (${mb} MB)]
|
|
@@ -993,13 +1275,13 @@ Use the bash tool to read in segments, e.g.:
|
|
|
993
1275
|
|
|
994
1276
|
${pdfText}`;
|
|
995
1277
|
}
|
|
996
|
-
const dir =
|
|
997
|
-
const nameNoExt =
|
|
998
|
-
const textAlts = [".md", ".txt", ".html"].map((e) =>
|
|
1278
|
+
const dir = dirname2(normalizedPath);
|
|
1279
|
+
const nameNoExt = basename2(normalizedPath, ext);
|
|
1280
|
+
const textAlts = [".md", ".txt", ".html"].map((e) => resolve3(dir, nameNoExt + e)).filter(existsSync4);
|
|
999
1281
|
if (textAlts.length > 0) {
|
|
1000
1282
|
return `[PDF file: ${filePath}]
|
|
1001
1283
|
Cannot extract text from this PDF, but found alternative text versions:
|
|
1002
|
-
` + textAlts.map((p) => ` \u2192 ${
|
|
1284
|
+
` + textAlts.map((p) => ` \u2192 ${basename2(p)}`).join("\n") + `
|
|
1003
1285
|
Please use read_file to read the above files.`;
|
|
1004
1286
|
}
|
|
1005
1287
|
return `[PDF file: ${filePath}]
|
|
@@ -1008,7 +1290,7 @@ Suggestion: read existing text versions (.md / .txt) in the project, or install
|
|
|
1008
1290
|
}
|
|
1009
1291
|
if (ext === ".ipynb") {
|
|
1010
1292
|
try {
|
|
1011
|
-
const raw =
|
|
1293
|
+
const raw = readFileSync3(normalizedPath, "utf-8");
|
|
1012
1294
|
const nb = parseNotebook(raw);
|
|
1013
1295
|
return renderNotebookAsText(nb, filePath);
|
|
1014
1296
|
} catch (err) {
|
|
@@ -1021,7 +1303,7 @@ ${err.message}`;
|
|
|
1021
1303
|
This is a binary file and cannot be read as text. If there is a text version (.md / .txt) in the project, please read that instead.`;
|
|
1022
1304
|
}
|
|
1023
1305
|
const { readFile: readFile2 } = await import("fs/promises");
|
|
1024
|
-
const buf = size > 1048576 ? await readFile2(normalizedPath) :
|
|
1306
|
+
const buf = size > 1048576 ? await readFile2(normalizedPath) : readFileSync3(normalizedPath);
|
|
1025
1307
|
if (encoding === "base64") {
|
|
1026
1308
|
return `[File: ${filePath} | base64]
|
|
1027
1309
|
|
|
@@ -1059,12 +1341,12 @@ ${content}`;
|
|
|
1059
1341
|
};
|
|
1060
1342
|
|
|
1061
1343
|
// src/tools/builtin/write-file.ts
|
|
1062
|
-
import { appendFileSync, mkdirSync as
|
|
1063
|
-
import { dirname as
|
|
1344
|
+
import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
1345
|
+
import { dirname as dirname3 } from "path";
|
|
1064
1346
|
|
|
1065
1347
|
// src/tools/executor.ts
|
|
1066
1348
|
import chalk3 from "chalk";
|
|
1067
|
-
import { existsSync as
|
|
1349
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
1068
1350
|
import { tmpdir } from "os";
|
|
1069
1351
|
|
|
1070
1352
|
// src/core/readline-internal.ts
|
|
@@ -1272,9 +1554,9 @@ function simpleDiff(oldLines, newLines) {
|
|
|
1272
1554
|
|
|
1273
1555
|
// src/tools/hooks.ts
|
|
1274
1556
|
import { execSync } from "child_process";
|
|
1275
|
-
import { createHash } from "crypto";
|
|
1276
|
-
import { existsSync as
|
|
1277
|
-
import { join } from "path";
|
|
1557
|
+
import { createHash as createHash2 } from "crypto";
|
|
1558
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
1559
|
+
import { join as join2 } from "path";
|
|
1278
1560
|
var TRUST_FILE = "hooks-trust.json";
|
|
1279
1561
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
1280
1562
|
function asArray(entry) {
|
|
@@ -1283,24 +1565,24 @@ function asArray(entry) {
|
|
|
1283
1565
|
return raw.map((item) => typeof item === "string" ? { command: item } : item);
|
|
1284
1566
|
}
|
|
1285
1567
|
function hookTrustPath(configDir) {
|
|
1286
|
-
return
|
|
1568
|
+
return join2(configDir, TRUST_FILE);
|
|
1287
1569
|
}
|
|
1288
1570
|
function loadHookTrustStore(configDir) {
|
|
1289
1571
|
const file = hookTrustPath(configDir);
|
|
1290
|
-
if (!
|
|
1572
|
+
if (!existsSync5(file)) return { records: [] };
|
|
1291
1573
|
try {
|
|
1292
|
-
const parsed = JSON.parse(
|
|
1574
|
+
const parsed = JSON.parse(readFileSync4(file, "utf-8"));
|
|
1293
1575
|
return { records: Array.isArray(parsed.records) ? parsed.records : [] };
|
|
1294
1576
|
} catch {
|
|
1295
1577
|
return { records: [] };
|
|
1296
1578
|
}
|
|
1297
1579
|
}
|
|
1298
1580
|
function saveHookTrustStore(configDir, store) {
|
|
1299
|
-
|
|
1581
|
+
mkdirSync2(configDir, { recursive: true });
|
|
1300
1582
|
atomicWriteFileSync(hookTrustPath(configDir), JSON.stringify(store, null, 2));
|
|
1301
1583
|
}
|
|
1302
1584
|
function hashHook(event, command) {
|
|
1303
|
-
return
|
|
1585
|
+
return createHash2("sha256").update(`${event}
|
|
1304
1586
|
${command}`, "utf-8").digest("hex");
|
|
1305
1587
|
}
|
|
1306
1588
|
function isTrusted(source, event, hash, store) {
|
|
@@ -1449,7 +1731,7 @@ function runHook(template, vars) {
|
|
|
1449
1731
|
}
|
|
1450
1732
|
|
|
1451
1733
|
// src/tools/permissions.ts
|
|
1452
|
-
import { isAbsolute, resolve as
|
|
1734
|
+
import { isAbsolute, resolve as resolve4 } from "path";
|
|
1453
1735
|
function checkPermission(toolName, args, dangerLevel, rules, defaultAction = "confirm") {
|
|
1454
1736
|
for (const rule of rules) {
|
|
1455
1737
|
if (rule.tool !== "*" && rule.tool !== toolName) continue;
|
|
@@ -1495,8 +1777,8 @@ function normalizePathForPermission(value) {
|
|
|
1495
1777
|
return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
1496
1778
|
}
|
|
1497
1779
|
function isInsidePath(root, target) {
|
|
1498
|
-
const absoluteTarget = isAbsolute(target) ? target :
|
|
1499
|
-
const r = normalizePathForPermission(
|
|
1780
|
+
const absoluteTarget = isAbsolute(target) ? target : resolve4(root, target);
|
|
1781
|
+
const r = normalizePathForPermission(resolve4(root));
|
|
1500
1782
|
const t = normalizePathForPermission(absoluteTarget);
|
|
1501
1783
|
return t === r || t.startsWith(`${r}/`);
|
|
1502
1784
|
}
|
|
@@ -1870,8 +2152,8 @@ var theme = new Proxy(DARK_THEME, {
|
|
|
1870
2152
|
});
|
|
1871
2153
|
|
|
1872
2154
|
// src/tools/action-classifier.ts
|
|
1873
|
-
import { existsSync as
|
|
1874
|
-
import { isAbsolute as isAbsolute2, join as
|
|
2155
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
2156
|
+
import { isAbsolute as isAbsolute2, join as join3, resolve as resolve5 } from "path";
|
|
1875
2157
|
var RECENT_DENIED_LIMIT = 50;
|
|
1876
2158
|
var recentlyDenied = [];
|
|
1877
2159
|
function recordRecentlyDeniedAutoAction(call, classification) {
|
|
@@ -1894,8 +2176,8 @@ function normalizePath(value) {
|
|
|
1894
2176
|
return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
1895
2177
|
}
|
|
1896
2178
|
function isInside(root, target) {
|
|
1897
|
-
const absoluteRoot =
|
|
1898
|
-
const absoluteTarget = isAbsolute2(target) ? target :
|
|
2179
|
+
const absoluteRoot = resolve5(root);
|
|
2180
|
+
const absoluteTarget = isAbsolute2(target) ? target : resolve5(root, target);
|
|
1899
2181
|
const base = normalizePath(absoluteRoot);
|
|
1900
2182
|
const t = normalizePath(absoluteTarget);
|
|
1901
2183
|
return t === base || t.startsWith(base + "/");
|
|
@@ -1912,10 +2194,10 @@ function splitShellWords(command) {
|
|
|
1912
2194
|
return out.filter(Boolean);
|
|
1913
2195
|
}
|
|
1914
2196
|
function readPackageManifest(root) {
|
|
1915
|
-
const p =
|
|
1916
|
-
if (!
|
|
2197
|
+
const p = join3(root, "package.json");
|
|
2198
|
+
if (!existsSync6(p)) return null;
|
|
1917
2199
|
try {
|
|
1918
|
-
return JSON.parse(
|
|
2200
|
+
return JSON.parse(readFileSync5(p, "utf-8"));
|
|
1919
2201
|
} catch {
|
|
1920
2202
|
return null;
|
|
1921
2203
|
}
|
|
@@ -1931,10 +2213,10 @@ function manifestHasDependency(root, name) {
|
|
|
1931
2213
|
}
|
|
1932
2214
|
function lockfileMentions(root, name) {
|
|
1933
2215
|
for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
|
|
1934
|
-
const p =
|
|
1935
|
-
if (!
|
|
2216
|
+
const p = join3(root, file);
|
|
2217
|
+
if (!existsSync6(p)) continue;
|
|
1936
2218
|
try {
|
|
1937
|
-
if (
|
|
2219
|
+
if (readFileSync5(p, "utf-8").includes(name)) return true;
|
|
1938
2220
|
} catch {
|
|
1939
2221
|
}
|
|
1940
2222
|
}
|
|
@@ -2421,7 +2703,7 @@ var ToolExecutor = class {
|
|
|
2421
2703
|
rl.resume();
|
|
2422
2704
|
process.stdout.write(prompt);
|
|
2423
2705
|
this.confirming = true;
|
|
2424
|
-
return new Promise((
|
|
2706
|
+
return new Promise((resolve9) => {
|
|
2425
2707
|
let completed = false;
|
|
2426
2708
|
const cleanup = (result) => {
|
|
2427
2709
|
if (completed) return;
|
|
@@ -2431,7 +2713,7 @@ var ToolExecutor = class {
|
|
|
2431
2713
|
rl.pause();
|
|
2432
2714
|
rlAny.output = savedOutput;
|
|
2433
2715
|
this.confirming = false;
|
|
2434
|
-
|
|
2716
|
+
resolve9(result);
|
|
2435
2717
|
};
|
|
2436
2718
|
const onLine = (line) => {
|
|
2437
2719
|
const trimmed = line.trim();
|
|
@@ -2498,10 +2780,10 @@ var ToolExecutor = class {
|
|
|
2498
2780
|
const filePath = String(call.arguments["path"] ?? "");
|
|
2499
2781
|
const newContent = String(call.arguments["content"] ?? "");
|
|
2500
2782
|
if (!filePath) return;
|
|
2501
|
-
if (
|
|
2783
|
+
if (existsSync7(filePath)) {
|
|
2502
2784
|
let oldContent;
|
|
2503
2785
|
try {
|
|
2504
|
-
oldContent =
|
|
2786
|
+
oldContent = readFileSync6(filePath, "utf-8");
|
|
2505
2787
|
} catch {
|
|
2506
2788
|
return;
|
|
2507
2789
|
}
|
|
@@ -2524,7 +2806,7 @@ var ToolExecutor = class {
|
|
|
2524
2806
|
}
|
|
2525
2807
|
} else if (call.name === "edit_file") {
|
|
2526
2808
|
const filePath = String(call.arguments["path"] ?? "");
|
|
2527
|
-
if (!filePath || !
|
|
2809
|
+
if (!filePath || !existsSync7(filePath)) return;
|
|
2528
2810
|
const oldStr = call.arguments["old_str"];
|
|
2529
2811
|
const newStr = call.arguments["new_str"];
|
|
2530
2812
|
if (oldStr !== void 0) {
|
|
@@ -2551,7 +2833,7 @@ var ToolExecutor = class {
|
|
|
2551
2833
|
const to = Number(call.arguments["delete_to_line"] ?? from);
|
|
2552
2834
|
let fileContent;
|
|
2553
2835
|
try {
|
|
2554
|
-
fileContent =
|
|
2836
|
+
fileContent = readFileSync6(filePath, "utf-8");
|
|
2555
2837
|
} catch {
|
|
2556
2838
|
return;
|
|
2557
2839
|
}
|
|
@@ -2605,7 +2887,7 @@ var ToolExecutor = class {
|
|
|
2605
2887
|
rl.resume();
|
|
2606
2888
|
process.stdout.write(color("Proceed? [y/N] (type y + Enter to confirm) "));
|
|
2607
2889
|
this.confirming = true;
|
|
2608
|
-
return new Promise((
|
|
2890
|
+
return new Promise((resolve9) => {
|
|
2609
2891
|
let completed = false;
|
|
2610
2892
|
const cleanup = (answer) => {
|
|
2611
2893
|
if (completed) return;
|
|
@@ -2615,7 +2897,7 @@ var ToolExecutor = class {
|
|
|
2615
2897
|
rl.pause();
|
|
2616
2898
|
rlAny.output = savedOutput;
|
|
2617
2899
|
this.confirming = false;
|
|
2618
|
-
|
|
2900
|
+
resolve9(answer === "y");
|
|
2619
2901
|
};
|
|
2620
2902
|
const onLine = (line) => {
|
|
2621
2903
|
const trimmed = line.trim();
|
|
@@ -2643,11 +2925,11 @@ var ToolExecutor = class {
|
|
|
2643
2925
|
};
|
|
2644
2926
|
|
|
2645
2927
|
// src/tools/sensitive-paths.ts
|
|
2646
|
-
import { resolve as
|
|
2928
|
+
import { resolve as resolve6, sep as sep2, basename as basename3 } from "path";
|
|
2647
2929
|
import { homedir as homedir2 } from "os";
|
|
2648
2930
|
var home = homedir2();
|
|
2649
2931
|
function norm(p) {
|
|
2650
|
-
const abs =
|
|
2932
|
+
const abs = resolve6(p);
|
|
2651
2933
|
return process.platform === "win32" ? abs.toLowerCase() : abs;
|
|
2652
2934
|
}
|
|
2653
2935
|
function homeRel(p) {
|
|
@@ -2662,7 +2944,7 @@ function homeRel(p) {
|
|
|
2662
2944
|
function classifyWritePath(path3) {
|
|
2663
2945
|
if (!path3) return { sensitive: false };
|
|
2664
2946
|
const abs = norm(path3);
|
|
2665
|
-
const base =
|
|
2947
|
+
const base = basename3(abs);
|
|
2666
2948
|
const rel = homeRel(path3);
|
|
2667
2949
|
if (rel) {
|
|
2668
2950
|
const shellRc = /* @__PURE__ */ new Set([
|
|
@@ -2786,7 +3068,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
2786
3068
|
}
|
|
2787
3069
|
undoStack.push(filePath, `write_file${appendMode ? " (append)" : ""}: ${filePath}`);
|
|
2788
3070
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
2789
|
-
|
|
3071
|
+
mkdirSync3(dirname3(filePath), { recursive: true });
|
|
2790
3072
|
if (appendMode) {
|
|
2791
3073
|
appendFileSync(filePath, content, encoding);
|
|
2792
3074
|
} else {
|
|
@@ -2806,7 +3088,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
2806
3088
|
};
|
|
2807
3089
|
|
|
2808
3090
|
// src/tools/builtin/edit-file.ts
|
|
2809
|
-
import { readFileSync as
|
|
3091
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
2810
3092
|
|
|
2811
3093
|
// src/tools/builtin/patch-apply.ts
|
|
2812
3094
|
function parseUnifiedDiff(patch) {
|
|
@@ -3167,7 +3449,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
3167
3449
|
const filePath = String(args["path"] ?? "");
|
|
3168
3450
|
const encoding = args["encoding"] ?? "utf-8";
|
|
3169
3451
|
if (!filePath) throw new ToolError("edit_file", "path is required");
|
|
3170
|
-
if (!
|
|
3452
|
+
if (!existsSync8(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
|
|
3171
3453
|
const verdict = classifyWritePath(filePath);
|
|
3172
3454
|
if (verdict.sensitive && subAgentGuard.active) {
|
|
3173
3455
|
throw new ToolError(
|
|
@@ -3175,7 +3457,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
3175
3457
|
`Refused: sub-agents cannot edit sensitive paths \u2014 ${verdict.reason}. If this is genuinely needed, ask the parent agent to perform the edit so the user can approve it.`
|
|
3176
3458
|
);
|
|
3177
3459
|
}
|
|
3178
|
-
const original =
|
|
3460
|
+
const original = readFileSync7(filePath, encoding);
|
|
3179
3461
|
if (args["patch"] !== void 0) {
|
|
3180
3462
|
const patchText = String(args["patch"] ?? "");
|
|
3181
3463
|
const stopOnError = args["stop_on_error"] !== false;
|
|
@@ -3333,8 +3615,8 @@ function truncatePreview(str, maxLen = 80) {
|
|
|
3333
3615
|
}
|
|
3334
3616
|
|
|
3335
3617
|
// src/tools/builtin/list-dir.ts
|
|
3336
|
-
import { readdirSync as
|
|
3337
|
-
import { join as
|
|
3618
|
+
import { readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync9 } from "fs";
|
|
3619
|
+
import { join as join4, basename as basename4 } from "path";
|
|
3338
3620
|
var listDirTool = {
|
|
3339
3621
|
definition: {
|
|
3340
3622
|
name: "list_dir",
|
|
@@ -3356,12 +3638,12 @@ var listDirTool = {
|
|
|
3356
3638
|
async execute(args) {
|
|
3357
3639
|
const dirPath = String(args["path"] ?? process.cwd());
|
|
3358
3640
|
const recursive = Boolean(args["recursive"] ?? false);
|
|
3359
|
-
if (!
|
|
3360
|
-
const targetName =
|
|
3641
|
+
if (!existsSync9(dirPath)) {
|
|
3642
|
+
const targetName = basename4(dirPath).toLowerCase();
|
|
3361
3643
|
const cwd = process.cwd();
|
|
3362
3644
|
const suggestions = [];
|
|
3363
3645
|
try {
|
|
3364
|
-
for (const entry of
|
|
3646
|
+
for (const entry of readdirSync4(cwd, { withFileTypes: true })) {
|
|
3365
3647
|
if (entry.isDirectory() && entry.name.toLowerCase() === targetName) {
|
|
3366
3648
|
suggestions.push(entry.name);
|
|
3367
3649
|
}
|
|
@@ -3394,7 +3676,7 @@ Please use list_dir (without path) to see the current directory structure first.
|
|
|
3394
3676
|
function listRecursive(basePath, indent, recursive, lines) {
|
|
3395
3677
|
let entries;
|
|
3396
3678
|
try {
|
|
3397
|
-
entries =
|
|
3679
|
+
entries = readdirSync4(basePath, { withFileTypes: true });
|
|
3398
3680
|
} catch {
|
|
3399
3681
|
lines.push(`${indent}(permission denied)`);
|
|
3400
3682
|
return;
|
|
@@ -3414,11 +3696,11 @@ function listRecursive(basePath, indent, recursive, lines) {
|
|
|
3414
3696
|
if (entry.isDirectory()) {
|
|
3415
3697
|
lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
|
|
3416
3698
|
if (recursive) {
|
|
3417
|
-
listRecursive(
|
|
3699
|
+
listRecursive(join4(basePath, entry.name), indent + " ", true, lines);
|
|
3418
3700
|
}
|
|
3419
3701
|
} else {
|
|
3420
3702
|
try {
|
|
3421
|
-
const stat =
|
|
3703
|
+
const stat = statSync4(join4(basePath, entry.name));
|
|
3422
3704
|
const size = formatSize(stat.size);
|
|
3423
3705
|
lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
|
|
3424
3706
|
} catch {
|
|
@@ -3434,9 +3716,9 @@ function formatSize(bytes) {
|
|
|
3434
3716
|
}
|
|
3435
3717
|
|
|
3436
3718
|
// src/tools/builtin/grep-files.ts
|
|
3437
|
-
import { readdirSync as
|
|
3719
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync8, statSync as statSync5, existsSync as existsSync10 } from "fs";
|
|
3438
3720
|
import { readFile } from "fs/promises";
|
|
3439
|
-
import { join as
|
|
3721
|
+
import { join as join5, relative } from "path";
|
|
3440
3722
|
var grepFilesTool = {
|
|
3441
3723
|
definition: {
|
|
3442
3724
|
name: "grep_files",
|
|
@@ -3487,7 +3769,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
3487
3769
|
const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
|
|
3488
3770
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
|
|
3489
3771
|
if (!pattern) throw new ToolError("grep_files", "pattern is required");
|
|
3490
|
-
if (!
|
|
3772
|
+
if (!existsSync10(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
|
|
3491
3773
|
const MAX_PATTERN_LENGTH = 1e3;
|
|
3492
3774
|
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
3493
3775
|
throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
|
|
@@ -3500,7 +3782,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
3500
3782
|
regex = new RegExp(escaped, ignoreCase ? "gi" : "g");
|
|
3501
3783
|
}
|
|
3502
3784
|
const results = [];
|
|
3503
|
-
const stat =
|
|
3785
|
+
const stat = statSync5(rootPath);
|
|
3504
3786
|
if (stat.isFile()) {
|
|
3505
3787
|
searchInFile(rootPath, rootPath, regex, contextLines, maxResults, results);
|
|
3506
3788
|
} else {
|
|
@@ -3573,7 +3855,7 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
|
|
|
3573
3855
|
if (paths.length >= maxFiles) return;
|
|
3574
3856
|
let entries;
|
|
3575
3857
|
try {
|
|
3576
|
-
entries =
|
|
3858
|
+
entries = readdirSync5(dirPath, { withFileTypes: true });
|
|
3577
3859
|
} catch {
|
|
3578
3860
|
return;
|
|
3579
3861
|
}
|
|
@@ -3581,13 +3863,13 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
|
|
|
3581
3863
|
if (paths.length >= maxFiles) return;
|
|
3582
3864
|
if (entry.isDirectory()) {
|
|
3583
3865
|
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
3584
|
-
walk(
|
|
3866
|
+
walk(join5(dirPath, entry.name));
|
|
3585
3867
|
} else if (entry.isFile()) {
|
|
3586
3868
|
if (isBinary(entry.name)) continue;
|
|
3587
3869
|
if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
|
|
3588
|
-
const fullPath =
|
|
3870
|
+
const fullPath = join5(dirPath, entry.name);
|
|
3589
3871
|
try {
|
|
3590
|
-
if (
|
|
3872
|
+
if (statSync5(fullPath).size > 1e6) continue;
|
|
3591
3873
|
} catch {
|
|
3592
3874
|
continue;
|
|
3593
3875
|
}
|
|
@@ -3633,14 +3915,14 @@ async function searchInFileAsync(fullPath, displayPath, regex, contextLines, max
|
|
|
3633
3915
|
}
|
|
3634
3916
|
function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, results) {
|
|
3635
3917
|
try {
|
|
3636
|
-
const stat =
|
|
3918
|
+
const stat = statSync5(fullPath);
|
|
3637
3919
|
if (stat.size > 1e6) return;
|
|
3638
3920
|
} catch {
|
|
3639
3921
|
return;
|
|
3640
3922
|
}
|
|
3641
3923
|
let content;
|
|
3642
3924
|
try {
|
|
3643
|
-
content =
|
|
3925
|
+
content = readFileSync8(fullPath, "utf-8");
|
|
3644
3926
|
} catch {
|
|
3645
3927
|
return;
|
|
3646
3928
|
}
|
|
@@ -3671,8 +3953,8 @@ function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, re
|
|
|
3671
3953
|
}
|
|
3672
3954
|
|
|
3673
3955
|
// src/tools/builtin/glob-files.ts
|
|
3674
|
-
import { readdirSync as
|
|
3675
|
-
import { join as
|
|
3956
|
+
import { readdirSync as readdirSync6, statSync as statSync6, existsSync as existsSync11 } from "fs";
|
|
3957
|
+
import { join as join6, relative as relative2, basename as basename5 } from "path";
|
|
3676
3958
|
var globFilesTool = {
|
|
3677
3959
|
definition: {
|
|
3678
3960
|
name: "glob_files",
|
|
@@ -3705,7 +3987,7 @@ Results sorted by most recent modification time. Automatically skips node_module
|
|
|
3705
3987
|
const rootPath = String(args["path"] ?? process.cwd());
|
|
3706
3988
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
|
|
3707
3989
|
if (!pattern) throw new ToolError("glob_files", "pattern is required");
|
|
3708
|
-
if (!
|
|
3990
|
+
if (!existsSync11(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
|
|
3709
3991
|
const regex = globToRegex(pattern);
|
|
3710
3992
|
const matches = [];
|
|
3711
3993
|
collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
|
|
@@ -3776,21 +4058,21 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
3776
4058
|
if (results.length >= maxResults) return;
|
|
3777
4059
|
let entries;
|
|
3778
4060
|
try {
|
|
3779
|
-
entries =
|
|
4061
|
+
entries = readdirSync6(dirPath, { withFileTypes: true });
|
|
3780
4062
|
} catch {
|
|
3781
4063
|
return;
|
|
3782
4064
|
}
|
|
3783
4065
|
for (const entry of entries) {
|
|
3784
4066
|
if (results.length >= maxResults) break;
|
|
3785
|
-
const fullPath =
|
|
4067
|
+
const fullPath = join6(dirPath, entry.name);
|
|
3786
4068
|
if (entry.isDirectory()) {
|
|
3787
4069
|
if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
3788
4070
|
collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
|
|
3789
4071
|
} else if (entry.isFile()) {
|
|
3790
4072
|
const relPath = relative2(rootPath, fullPath).replace(/\\/g, "/");
|
|
3791
|
-
if (regex.test(relPath) || regex.test(
|
|
4073
|
+
if (regex.test(relPath) || regex.test(basename5(relPath))) {
|
|
3792
4074
|
try {
|
|
3793
|
-
const stat =
|
|
4075
|
+
const stat = statSync6(fullPath);
|
|
3794
4076
|
results.push({ relPath, absPath: fullPath, mtime: stat.mtimeMs });
|
|
3795
4077
|
} catch {
|
|
3796
4078
|
results.push({ relPath, absPath: fullPath, mtime: 0 });
|
|
@@ -3864,7 +4146,7 @@ var runInteractiveTool = {
|
|
|
3864
4146
|
PYTHONDONTWRITEBYTECODE: "1"
|
|
3865
4147
|
};
|
|
3866
4148
|
const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
|
|
3867
|
-
return new Promise((
|
|
4149
|
+
return new Promise((resolve9) => {
|
|
3868
4150
|
const child = spawn2(executable, cmdArgs.map(String), {
|
|
3869
4151
|
cwd: process.cwd(),
|
|
3870
4152
|
env,
|
|
@@ -3897,22 +4179,22 @@ var runInteractiveTool = {
|
|
|
3897
4179
|
setTimeout(writeNextLine, 400);
|
|
3898
4180
|
const timer = setTimeout(() => {
|
|
3899
4181
|
child.kill();
|
|
3900
|
-
|
|
4182
|
+
resolve9(`${prefixWarnings}[Timeout after ${timeout}ms]
|
|
3901
4183
|
${buildOutput(stdout, stderr)}`);
|
|
3902
4184
|
}, timeout);
|
|
3903
4185
|
child.on("close", (code) => {
|
|
3904
4186
|
clearTimeout(timer);
|
|
3905
4187
|
const output = buildOutput(stdout, stderr);
|
|
3906
4188
|
if (code !== 0 && code !== null) {
|
|
3907
|
-
|
|
4189
|
+
resolve9(`${prefixWarnings}Exit code ${code}:
|
|
3908
4190
|
${output}`);
|
|
3909
4191
|
} else {
|
|
3910
|
-
|
|
4192
|
+
resolve9(`${prefixWarnings}${output || "(no output)"}`);
|
|
3911
4193
|
}
|
|
3912
4194
|
});
|
|
3913
4195
|
child.on("error", (err) => {
|
|
3914
4196
|
clearTimeout(timer);
|
|
3915
|
-
|
|
4197
|
+
resolve9(
|
|
3916
4198
|
`${prefixWarnings}Failed to start process "${executable}": ${err.message}
|
|
3917
4199
|
Hint: On Windows, use the full path to the executable, e.g.:
|
|
3918
4200
|
C:\\Users\\Jinzd\\anaconda3\\envs\\python312\\python.exe`
|
|
@@ -4522,8 +4804,8 @@ ${preamble}`;
|
|
|
4522
4804
|
}
|
|
4523
4805
|
|
|
4524
4806
|
// src/tools/builtin/save-last-response.ts
|
|
4525
|
-
import { mkdirSync as
|
|
4526
|
-
import { dirname as
|
|
4807
|
+
import { mkdirSync as mkdirSync4, unlinkSync as unlinkSync2, rmdirSync as rmdirSync2 } from "fs";
|
|
4808
|
+
import { dirname as dirname4 } from "path";
|
|
4527
4809
|
var lastResponseStore = { content: "" };
|
|
4528
4810
|
function cleanupRejectedTeeFile(filePath) {
|
|
4529
4811
|
try {
|
|
@@ -4531,7 +4813,7 @@ function cleanupRejectedTeeFile(filePath) {
|
|
|
4531
4813
|
} catch {
|
|
4532
4814
|
}
|
|
4533
4815
|
try {
|
|
4534
|
-
rmdirSync2(
|
|
4816
|
+
rmdirSync2(dirname4(filePath));
|
|
4535
4817
|
} catch {
|
|
4536
4818
|
}
|
|
4537
4819
|
}
|
|
@@ -4572,7 +4854,7 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
4572
4854
|
throw new ToolError("save_last_response", "No content to save: AI has not produced any response yet, or the last response was empty.");
|
|
4573
4855
|
}
|
|
4574
4856
|
undoStack.push(filePath, `save_last_response: ${filePath}`);
|
|
4575
|
-
|
|
4857
|
+
mkdirSync4(dirname4(filePath), { recursive: true });
|
|
4576
4858
|
atomicWriteFileSync(filePath, content);
|
|
4577
4859
|
const lines = content.split("\n").length;
|
|
4578
4860
|
return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
|
|
@@ -4580,21 +4862,21 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
4580
4862
|
};
|
|
4581
4863
|
|
|
4582
4864
|
// src/tools/builtin/save-memory.ts
|
|
4583
|
-
import { join as
|
|
4865
|
+
import { join as join8 } from "path";
|
|
4584
4866
|
import { homedir as homedir3 } from "os";
|
|
4585
4867
|
|
|
4586
4868
|
// src/memory/persistent-memory.ts
|
|
4587
|
-
import { existsSync as
|
|
4588
|
-
import { randomUUID, createHash as
|
|
4589
|
-
import { dirname as
|
|
4869
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync9 } from "fs";
|
|
4870
|
+
import { randomUUID, createHash as createHash3 } from "crypto";
|
|
4871
|
+
import { dirname as dirname5, join as join7, resolve as resolve7 } from "path";
|
|
4590
4872
|
function memoryStorePath(configDir) {
|
|
4591
|
-
return
|
|
4873
|
+
return join7(configDir, MEMORY_STORE_FILE_NAME);
|
|
4592
4874
|
}
|
|
4593
4875
|
function memoryMarkdownPath(configDir) {
|
|
4594
|
-
return
|
|
4876
|
+
return join7(configDir, MEMORY_FILE_NAME);
|
|
4595
4877
|
}
|
|
4596
4878
|
function getProjectKey(cwd = process.cwd()) {
|
|
4597
|
-
return
|
|
4879
|
+
return resolve7(getGitRoot(cwd) ?? cwd);
|
|
4598
4880
|
}
|
|
4599
4881
|
function nowIso() {
|
|
4600
4882
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4627,8 +4909,8 @@ function normalizeEntry(value) {
|
|
|
4627
4909
|
}
|
|
4628
4910
|
function legacyEntriesFromMarkdown(configDir) {
|
|
4629
4911
|
const file = memoryMarkdownPath(configDir);
|
|
4630
|
-
if (!
|
|
4631
|
-
const content =
|
|
4912
|
+
if (!existsSync12(file)) return [];
|
|
4913
|
+
const content = readFileSync9(file, "utf-8").trim();
|
|
4632
4914
|
if (!content) return [];
|
|
4633
4915
|
const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
|
|
4634
4916
|
const chunks = parts.length > 1 ? parts : [content];
|
|
@@ -4637,15 +4919,15 @@ function legacyEntriesFromMarkdown(configDir) {
|
|
|
4637
4919
|
const body = (match ? match[2] : chunk).trim();
|
|
4638
4920
|
const parsed = match ? Date.parse(match[1].replace(" ", "T")) : NaN;
|
|
4639
4921
|
const createdAt = Number.isFinite(parsed) ? new Date(parsed).toISOString() : nowIso();
|
|
4640
|
-
const id = "legacy-" +
|
|
4922
|
+
const id = "legacy-" + createHash3("sha1").update(`${index}
|
|
4641
4923
|
${chunk}`).digest("hex").slice(0, 12);
|
|
4642
4924
|
return normalizeEntry({ id, content: body, scope: "personal", createdAt, updatedAt: createdAt, sensitivity: "low", approved: true });
|
|
4643
4925
|
}).filter((entry) => Boolean(entry));
|
|
4644
4926
|
}
|
|
4645
4927
|
function loadMemoryEntries(configDir) {
|
|
4646
4928
|
const file = memoryStorePath(configDir);
|
|
4647
|
-
if (!
|
|
4648
|
-
const text =
|
|
4929
|
+
if (!existsSync12(file)) return legacyEntriesFromMarkdown(configDir);
|
|
4930
|
+
const text = readFileSync9(file, "utf-8").trim();
|
|
4649
4931
|
if (!text) return [];
|
|
4650
4932
|
const entries = [];
|
|
4651
4933
|
for (const line of text.split("\n")) {
|
|
@@ -4660,7 +4942,7 @@ function loadMemoryEntries(configDir) {
|
|
|
4660
4942
|
return entries;
|
|
4661
4943
|
}
|
|
4662
4944
|
function saveMemoryEntries(configDir, entries) {
|
|
4663
|
-
|
|
4945
|
+
mkdirSync5(configDir, { recursive: true });
|
|
4664
4946
|
const jsonl = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
4665
4947
|
atomicWriteFileSync(memoryStorePath(configDir), jsonl ? jsonl + "\n" : "");
|
|
4666
4948
|
syncLegacyMarkdown(configDir, entries);
|
|
@@ -4735,7 +5017,7 @@ function findUnique(entries, idPrefix) {
|
|
|
4735
5017
|
return matches[0];
|
|
4736
5018
|
}
|
|
4737
5019
|
function syncLegacyMarkdown(configDir, entries = loadMemoryEntries(configDir)) {
|
|
4738
|
-
|
|
5020
|
+
mkdirSync5(dirname5(memoryMarkdownPath(configDir)), { recursive: true });
|
|
4739
5021
|
const active = entries.filter((entry) => entry.approved && !isMemoryExpired(entry));
|
|
4740
5022
|
const markdown = active.map((entry) => {
|
|
4741
5023
|
const date = entry.createdAt.replace("T", " ").slice(0, 19);
|
|
@@ -4778,7 +5060,7 @@ ${entry.content}`;
|
|
|
4778
5060
|
|
|
4779
5061
|
// src/tools/builtin/save-memory.ts
|
|
4780
5062
|
function getConfigDir() {
|
|
4781
|
-
return
|
|
5063
|
+
return join8(homedir3(), CONFIG_DIR_NAME);
|
|
4782
5064
|
}
|
|
4783
5065
|
function parseScope(value) {
|
|
4784
5066
|
return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
|
|
@@ -4870,7 +5152,7 @@ function promptUser(rl, question) {
|
|
|
4870
5152
|
console.log();
|
|
4871
5153
|
console.log(chalk4.cyan("\u2753 ") + chalk4.bold(question));
|
|
4872
5154
|
process.stdout.write(chalk4.cyan("> "));
|
|
4873
|
-
return new Promise((
|
|
5155
|
+
return new Promise((resolve9) => {
|
|
4874
5156
|
let completed = false;
|
|
4875
5157
|
const cleanup = (answer) => {
|
|
4876
5158
|
if (completed) return;
|
|
@@ -4880,7 +5162,7 @@ function promptUser(rl, question) {
|
|
|
4880
5162
|
rl.pause();
|
|
4881
5163
|
rlAny.output = savedOutput;
|
|
4882
5164
|
askUserContext.prompting = false;
|
|
4883
|
-
|
|
5165
|
+
resolve9(answer);
|
|
4884
5166
|
};
|
|
4885
5167
|
const onLine = (line) => {
|
|
4886
5168
|
cleanup(line);
|
|
@@ -5112,8 +5394,8 @@ function formatResults2(query, data, _requested) {
|
|
|
5112
5394
|
}
|
|
5113
5395
|
|
|
5114
5396
|
// src/agents/agent-config.ts
|
|
5115
|
-
import { existsSync as
|
|
5116
|
-
import { join as
|
|
5397
|
+
import { existsSync as existsSync13, readdirSync as readdirSync7, readFileSync as readFileSync10 } from "fs";
|
|
5398
|
+
import { join as join9 } from "path";
|
|
5117
5399
|
import { homedir as homedir4 } from "os";
|
|
5118
5400
|
var READ_ONLY_TOOLS2 = /* @__PURE__ */ new Set([
|
|
5119
5401
|
"read_file",
|
|
@@ -5208,13 +5490,13 @@ function parseAgentConfig(raw, source, path3) {
|
|
|
5208
5490
|
return cfg;
|
|
5209
5491
|
}
|
|
5210
5492
|
function loadAgentDir(dir, source) {
|
|
5211
|
-
if (!
|
|
5493
|
+
if (!existsSync13(dir)) return [];
|
|
5212
5494
|
const out = [];
|
|
5213
|
-
for (const file of
|
|
5495
|
+
for (const file of readdirSync7(dir)) {
|
|
5214
5496
|
if (!file.endsWith(".json")) continue;
|
|
5215
|
-
const path3 =
|
|
5497
|
+
const path3 = join9(dir, file);
|
|
5216
5498
|
try {
|
|
5217
|
-
const parsed = JSON.parse(
|
|
5499
|
+
const parsed = JSON.parse(readFileSync10(path3, "utf-8"));
|
|
5218
5500
|
const cfg = parseAgentConfig(parsed, source, path3);
|
|
5219
5501
|
if (cfg) out.push(cfg);
|
|
5220
5502
|
} catch {
|
|
@@ -5224,8 +5506,8 @@ function loadAgentDir(dir, source) {
|
|
|
5224
5506
|
}
|
|
5225
5507
|
function getAgentDirs(configDir, cwd = process.cwd()) {
|
|
5226
5508
|
return {
|
|
5227
|
-
user:
|
|
5228
|
-
project:
|
|
5509
|
+
user: join9(configDir ?? join9(homedir4(), CONFIG_DIR_NAME), "agents"),
|
|
5510
|
+
project: join9(cwd, CONFIG_DIR_NAME, "agents")
|
|
5229
5511
|
};
|
|
5230
5512
|
}
|
|
5231
5513
|
function listAgentConfigs(configDir, cwd = process.cwd()) {
|
|
@@ -5233,6 +5515,11 @@ function listAgentConfigs(configDir, cwd = process.cwd()) {
|
|
|
5233
5515
|
const byName = /* @__PURE__ */ new Map();
|
|
5234
5516
|
for (const cfg of BUILTIN_AGENTS) byName.set(normalizeName(cfg.name), { ...cfg });
|
|
5235
5517
|
for (const cfg of loadAgentDir(dirs.user, "user")) byName.set(normalizeName(cfg.name), cfg);
|
|
5518
|
+
if (configDir) {
|
|
5519
|
+
for (const dir of getActivePluginAssets(configDir).agentDirs) {
|
|
5520
|
+
for (const cfg of loadAgentDir(dir, "plugin")) byName.set(normalizeName(cfg.name), cfg);
|
|
5521
|
+
}
|
|
5522
|
+
}
|
|
5236
5523
|
for (const cfg of loadAgentDir(dirs.project, "project")) byName.set(normalizeName(cfg.name), cfg);
|
|
5237
5524
|
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
5238
5525
|
}
|
|
@@ -5935,14 +6222,14 @@ var taskStopTool = {
|
|
|
5935
6222
|
|
|
5936
6223
|
// src/tools/builtin/git-tools.ts
|
|
5937
6224
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
5938
|
-
import { existsSync as
|
|
5939
|
-
import { join as
|
|
6225
|
+
import { existsSync as existsSync14 } from "fs";
|
|
6226
|
+
import { join as join10 } from "path";
|
|
5940
6227
|
function assertGitRepo(cwd) {
|
|
5941
6228
|
let dir = cwd;
|
|
5942
6229
|
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
5943
6230
|
while (dir && dir !== root) {
|
|
5944
|
-
if (
|
|
5945
|
-
const parent =
|
|
6231
|
+
if (existsSync14(join10(dir, ".git"))) return;
|
|
6232
|
+
const parent = join10(dir, "..");
|
|
5946
6233
|
if (parent === dir) break;
|
|
5947
6234
|
dir = parent;
|
|
5948
6235
|
}
|
|
@@ -6207,9 +6494,9 @@ ${commitOutput.trim()}`;
|
|
|
6207
6494
|
};
|
|
6208
6495
|
|
|
6209
6496
|
// src/tools/builtin/notebook-edit.ts
|
|
6210
|
-
import { readFileSync as
|
|
6497
|
+
import { readFileSync as readFileSync11, existsSync as existsSync15 } from "fs";
|
|
6211
6498
|
import { writeFile } from "fs/promises";
|
|
6212
|
-
import { resolve as
|
|
6499
|
+
import { resolve as resolve8, extname as extname2 } from "path";
|
|
6213
6500
|
var notebookEditTool = {
|
|
6214
6501
|
definition: {
|
|
6215
6502
|
name: "notebook_edit",
|
|
@@ -6258,14 +6545,14 @@ var notebookEditTool = {
|
|
|
6258
6545
|
if (!Number.isInteger(cellIndexRaw) || cellIndexRaw < 1) {
|
|
6259
6546
|
throw new ToolError("notebook_edit", "cell_index must be a positive integer (1-based)");
|
|
6260
6547
|
}
|
|
6261
|
-
const absPath =
|
|
6548
|
+
const absPath = resolve8(filePath);
|
|
6262
6549
|
if (extname2(absPath).toLowerCase() !== ".ipynb") {
|
|
6263
6550
|
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
6264
6551
|
}
|
|
6265
|
-
if (!
|
|
6552
|
+
if (!existsSync15(absPath)) {
|
|
6266
6553
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
6267
6554
|
}
|
|
6268
|
-
const raw =
|
|
6555
|
+
const raw = readFileSync11(absPath, "utf-8");
|
|
6269
6556
|
const nb = parseNotebook(raw);
|
|
6270
6557
|
const cellIdx0 = cellIndexRaw - 1;
|
|
6271
6558
|
undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
|
|
@@ -6682,8 +6969,8 @@ function estimateToolDefinitionTokens(def) {
|
|
|
6682
6969
|
|
|
6683
6970
|
// src/tools/registry.ts
|
|
6684
6971
|
import { pathToFileURL } from "url";
|
|
6685
|
-
import { existsSync as
|
|
6686
|
-
import { join as
|
|
6972
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync6, readdirSync as readdirSync8 } from "fs";
|
|
6973
|
+
import { join as join11 } from "path";
|
|
6687
6974
|
var ToolRegistry = class {
|
|
6688
6975
|
tools = /* @__PURE__ */ new Map();
|
|
6689
6976
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -6844,16 +7131,16 @@ var ToolRegistry = class {
|
|
|
6844
7131
|
* Returns the number of successfully loaded plugins.
|
|
6845
7132
|
*/
|
|
6846
7133
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
6847
|
-
if (!
|
|
7134
|
+
if (!existsSync16(pluginsDir)) {
|
|
6848
7135
|
try {
|
|
6849
|
-
|
|
7136
|
+
mkdirSync6(pluginsDir, { recursive: true });
|
|
6850
7137
|
} catch {
|
|
6851
7138
|
}
|
|
6852
7139
|
return 0;
|
|
6853
7140
|
}
|
|
6854
7141
|
let files;
|
|
6855
7142
|
try {
|
|
6856
|
-
files =
|
|
7143
|
+
files = readdirSync8(pluginsDir).filter((f) => f.endsWith(".js"));
|
|
6857
7144
|
} catch {
|
|
6858
7145
|
return 0;
|
|
6859
7146
|
}
|
|
@@ -6870,12 +7157,12 @@ var ToolRegistry = class {
|
|
|
6870
7157
|
process.stderr.write(
|
|
6871
7158
|
`
|
|
6872
7159
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
6873
|
-
` + files.map((f) => ` + ${
|
|
7160
|
+
` + files.map((f) => ` + ${join11(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
6874
7161
|
);
|
|
6875
7162
|
let loaded = 0;
|
|
6876
7163
|
for (const file of files) {
|
|
6877
7164
|
try {
|
|
6878
|
-
const fileUrl = pathToFileURL(
|
|
7165
|
+
const fileUrl = pathToFileURL(join11(pluginsDir, file)).href;
|
|
6879
7166
|
const mod = await import(fileUrl);
|
|
6880
7167
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
6881
7168
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -6917,6 +7204,15 @@ export {
|
|
|
6917
7204
|
getRecentlyDeniedAutoActions,
|
|
6918
7205
|
clearRecentlyDeniedAutoActions,
|
|
6919
7206
|
defaultActionClassifier,
|
|
7207
|
+
pluginRoot,
|
|
7208
|
+
installPlugin,
|
|
7209
|
+
setPluginEnabled,
|
|
7210
|
+
trustPlugin,
|
|
7211
|
+
getInstalledPlugin,
|
|
7212
|
+
listInstalledPlugins,
|
|
7213
|
+
mergePluginHooks,
|
|
7214
|
+
getActivePluginAssets,
|
|
7215
|
+
describePlugin,
|
|
6920
7216
|
memoryStorePath,
|
|
6921
7217
|
memoryMarkdownPath,
|
|
6922
7218
|
isMemoryExpired,
|