jinzd-ai-cli 0.4.218 → 0.4.220
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 +102 -99
- package/README.zh-CN.md +40 -38
- package/dist/{batch-7V7OTMUP.js → batch-QBSKTLTO.js} +3 -3
- package/dist/{chunk-TZQHYZKT.js → chunk-5ULLIOVC.js} +1 -0
- package/dist/{chunk-P4VBLXKS.js → chunk-5WBNXYDJ.js} +2 -2
- package/dist/chunk-ALQN7VUJ.js +604 -0
- package/dist/{chunk-C2Z42DI5.js → chunk-CAUHLHDR.js} +1 -1
- package/dist/{chunk-GX3HSGJX.js → chunk-IZW5LMI6.js} +166 -638
- package/dist/chunk-P3LKFA54.js +639 -0
- package/dist/{chunk-5CA2TJ5F.js → chunk-P4DYAGUI.js} +1 -1
- package/dist/{chunk-L4UREAID.js → chunk-P4PTXCHY.js} +6 -5
- package/dist/{chunk-QMXC327F.js → chunk-PUBCJF7E.js} +213 -175
- package/dist/{chunk-NTCB7CMT.js → chunk-QZYNRCPX.js} +1 -1
- package/dist/{chunk-H2UIHGHH.js → chunk-VHX7KZDY.js} +29 -189
- package/dist/{chunk-VGFTM3XT.js → chunk-Y25MSSUP.js} +1 -1
- package/dist/{ci-L6GH2WVC.js → ci-5SNXGM3M.js} +6 -6
- package/dist/{ci-format-WW7454AY.js → ci-format-57QQTHQE.js} +2 -2
- package/dist/{constants-NCTFSHDU.js → constants-ML5KFEOH.js} +1 -1
- package/dist/doctor-cli-ZRH3XZ3R.js +17 -0
- package/dist/electron-server.js +836 -470
- package/dist/{hub-CDL6T7CP.js → hub-IPLMJ6TJ.js} +1 -1
- package/dist/index.js +125 -194
- package/dist/{pr-D6PEKEGK.js → pr-GJYDAPK4.js} +8 -7
- package/dist/{run-tests-NXVVKAK2.js → run-tests-GK5AIAV2.js} +1 -1
- package/dist/{run-tests-SWU2XEV7.js → run-tests-KLQAI5CX.js} +2 -2
- package/dist/{server-WUT7VYTD.js → server-5FZDALHH.js} +10 -10
- package/dist/{server-LHYSS6CK.js → server-RU36PRSH.js} +74 -83
- package/dist/{task-orchestrator-C5AA2BI5.js → task-orchestrator-JMHPVNUO.js} +10 -10
- package/dist/{usage-6ZUUJBI2.js → usage-IRMRCCKN.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-MWKE2TNS.js +0 -129
- package/dist/chunk-OUC75QCF.js +0 -166
- package/dist/doctor-cli-EWMFBP5Q.js +0 -226
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
indexProject
|
|
4
|
+
} from "./chunk-K3CF65QH.js";
|
|
2
5
|
import {
|
|
3
6
|
hasSemanticIndex,
|
|
4
7
|
semanticSearch
|
|
5
8
|
} from "./chunk-T2NL5ZIA.js";
|
|
6
9
|
import {
|
|
7
10
|
runTestsTool
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-Y25MSSUP.js";
|
|
12
|
+
import {
|
|
13
|
+
fileCheckpoints
|
|
14
|
+
} from "./chunk-4BKXL7SM.js";
|
|
9
15
|
import {
|
|
16
|
+
loadChatIndex,
|
|
17
|
+
searchChatMemory
|
|
18
|
+
} from "./chunk-TB4W4Y4T.js";
|
|
19
|
+
import {
|
|
20
|
+
DEFAULT_PATTERNS,
|
|
21
|
+
redactString
|
|
22
|
+
} from "./chunk-SLSWPBK3.js";
|
|
23
|
+
import {
|
|
24
|
+
getActivePluginAssets,
|
|
25
|
+
runHook,
|
|
26
|
+
runLifecycleHooks,
|
|
10
27
|
runTool
|
|
11
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-ALQN7VUJ.js";
|
|
12
29
|
import {
|
|
13
30
|
getDangerLevel,
|
|
14
31
|
isFileWriteTool,
|
|
@@ -18,35 +35,20 @@ import {
|
|
|
18
35
|
EnvLoader,
|
|
19
36
|
NetworkError,
|
|
20
37
|
ToolError
|
|
21
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-5ULLIOVC.js";
|
|
22
39
|
import {
|
|
23
40
|
CONFIG_DIR_NAME,
|
|
24
41
|
DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
|
|
25
42
|
MEMORY_FILE_NAME,
|
|
26
43
|
MEMORY_MAX_CHARS,
|
|
27
44
|
MEMORY_STORE_FILE_NAME,
|
|
28
|
-
PLUGINS_DIR_NAME,
|
|
29
45
|
SUBAGENT_ALLOWED_TOOLS,
|
|
30
46
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
31
47
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
32
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-QZYNRCPX.js";
|
|
33
49
|
import {
|
|
34
50
|
getGitRoot
|
|
35
51
|
} from "./chunk-HOSJZMQS.js";
|
|
36
|
-
import {
|
|
37
|
-
fileCheckpoints
|
|
38
|
-
} from "./chunk-4BKXL7SM.js";
|
|
39
|
-
import {
|
|
40
|
-
loadChatIndex,
|
|
41
|
-
searchChatMemory
|
|
42
|
-
} from "./chunk-TB4W4Y4T.js";
|
|
43
|
-
import {
|
|
44
|
-
DEFAULT_PATTERNS,
|
|
45
|
-
redactString
|
|
46
|
-
} from "./chunk-SLSWPBK3.js";
|
|
47
|
-
import {
|
|
48
|
-
indexProject
|
|
49
|
-
} from "./chunk-K3CF65QH.js";
|
|
50
52
|
import {
|
|
51
53
|
loadIndex
|
|
52
54
|
} from "./chunk-CKH4KQ4E.js";
|
|
@@ -54,295 +56,14 @@ import {
|
|
|
54
56
|
atomicWriteFileSync
|
|
55
57
|
} from "./chunk-IW3Q7AE5.js";
|
|
56
58
|
|
|
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
|
-
|
|
338
59
|
// src/tools/builtin/bash.ts
|
|
339
60
|
import { spawn } from "child_process";
|
|
340
|
-
import { existsSync as
|
|
61
|
+
import { existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
341
62
|
import { platform as platform2 } from "os";
|
|
342
|
-
import { resolve
|
|
63
|
+
import { resolve } from "path";
|
|
343
64
|
|
|
344
65
|
// src/tools/undo-stack.ts
|
|
345
|
-
import { readFileSync
|
|
66
|
+
import { readFileSync, writeFileSync, unlinkSync, rmdirSync, existsSync } from "fs";
|
|
346
67
|
var MAX_UNDO_DEPTH = 20;
|
|
347
68
|
var UndoStack = class {
|
|
348
69
|
stack = [];
|
|
@@ -353,9 +74,9 @@ var UndoStack = class {
|
|
|
353
74
|
*/
|
|
354
75
|
push(filePath, description) {
|
|
355
76
|
let previousContent = null;
|
|
356
|
-
if (
|
|
77
|
+
if (existsSync(filePath)) {
|
|
357
78
|
try {
|
|
358
|
-
previousContent =
|
|
79
|
+
previousContent = readFileSync(filePath, "utf-8");
|
|
359
80
|
} catch {
|
|
360
81
|
return;
|
|
361
82
|
}
|
|
@@ -409,7 +130,7 @@ var UndoStack = class {
|
|
|
409
130
|
try {
|
|
410
131
|
if (entry.previousContent === null) {
|
|
411
132
|
if (entry.isDirectory) {
|
|
412
|
-
if (
|
|
133
|
+
if (existsSync(entry.filePath)) {
|
|
413
134
|
try {
|
|
414
135
|
rmdirSync(entry.filePath);
|
|
415
136
|
return { entry, result: `Removed newly created directory: ${entry.filePath}` };
|
|
@@ -419,7 +140,7 @@ var UndoStack = class {
|
|
|
419
140
|
}
|
|
420
141
|
return { entry, result: `Directory already removed: ${entry.filePath}` };
|
|
421
142
|
} else {
|
|
422
|
-
if (
|
|
143
|
+
if (existsSync(entry.filePath)) {
|
|
423
144
|
unlinkSync(entry.filePath);
|
|
424
145
|
}
|
|
425
146
|
return { entry, result: `Deleted newly created file: ${entry.filePath}` };
|
|
@@ -578,7 +299,7 @@ Important rules:
|
|
|
578
299
|
throw new ToolError("bash", blockingHint);
|
|
579
300
|
}
|
|
580
301
|
let currentCwd = getCwd();
|
|
581
|
-
if (!
|
|
302
|
+
if (!existsSync2(currentCwd)) {
|
|
582
303
|
const fallback = process.cwd();
|
|
583
304
|
process.stderr.write(
|
|
584
305
|
`[bash] Previous cwd "${currentCwd}" no longer exists, reset to "${fallback}"
|
|
@@ -589,8 +310,8 @@ Important rules:
|
|
|
589
310
|
}
|
|
590
311
|
let effectiveCwd = currentCwd;
|
|
591
312
|
if (cwdArg) {
|
|
592
|
-
const resolved =
|
|
593
|
-
if (!
|
|
313
|
+
const resolved = resolve(currentCwd, cwdArg);
|
|
314
|
+
if (!existsSync2(resolved)) {
|
|
594
315
|
throw new ToolError(
|
|
595
316
|
"bash",
|
|
596
317
|
`cwd directory does not exist: "${resolved}". Create it first (e.g. mkdir -p "${resolved}") before specifying it as cwd.`
|
|
@@ -610,7 +331,7 @@ Important rules:
|
|
|
610
331
|
const parsedTargets = parseCreationTargets(command, effectiveCwd);
|
|
611
332
|
const parsedTargetsBefore = /* @__PURE__ */ new Map();
|
|
612
333
|
for (const t of parsedTargets) {
|
|
613
|
-
parsedTargetsBefore.set(t,
|
|
334
|
+
parsedTargetsBefore.set(t, existsSync2(t));
|
|
614
335
|
}
|
|
615
336
|
try {
|
|
616
337
|
const { stdout, stderr, status, signal, timedOut, aborted } = await runShell(
|
|
@@ -790,7 +511,7 @@ Then poll readiness with SHORT bash calls (these DO exit), e.g.:
|
|
|
790
511
|
}
|
|
791
512
|
function snapshotDir(dir) {
|
|
792
513
|
try {
|
|
793
|
-
return new Set(
|
|
514
|
+
return new Set(readdirSync(dir).map((name) => resolve(dir, name)));
|
|
794
515
|
} catch {
|
|
795
516
|
return /* @__PURE__ */ new Set();
|
|
796
517
|
}
|
|
@@ -798,25 +519,25 @@ function snapshotDir(dir) {
|
|
|
798
519
|
function parseCreationTargets(command, cwd) {
|
|
799
520
|
const targets = [];
|
|
800
521
|
for (const m of command.matchAll(/(?:echo|cat|printf)\s+[^>]*>\s*(['"]?)([^\s;&|'"]+)\1/g)) {
|
|
801
|
-
if (m[2]) targets.push(
|
|
522
|
+
if (m[2]) targets.push(resolve(cwd, m[2]));
|
|
802
523
|
}
|
|
803
524
|
for (const m of command.matchAll(/\btouch\s+((?:['"]?[^\s;&|'"]+['"]?\s*)+)/g)) {
|
|
804
525
|
for (const f of m[1].trim().split(/\s+/)) {
|
|
805
526
|
const clean = f.replace(/^['"]|['"]$/g, "");
|
|
806
|
-
if (clean && !clean.startsWith("-")) targets.push(
|
|
527
|
+
if (clean && !clean.startsWith("-")) targets.push(resolve(cwd, clean));
|
|
807
528
|
}
|
|
808
529
|
}
|
|
809
530
|
for (const m of command.matchAll(/\bmkdir\s+(?:-\w+\s+)*((?:['"]?[^\s;&|'"]+['"]?\s*)+)/g)) {
|
|
810
531
|
for (const d of m[1].trim().split(/\s+/)) {
|
|
811
532
|
const clean = d.replace(/^['"]|['"]$/g, "");
|
|
812
|
-
if (clean && !clean.startsWith("-")) targets.push(
|
|
533
|
+
if (clean && !clean.startsWith("-")) targets.push(resolve(cwd, clean));
|
|
813
534
|
}
|
|
814
535
|
}
|
|
815
536
|
for (const m of command.matchAll(/\bcp\s+(?:-\w+\s+)*['"]?[^\s;&|'"]+['"]?\s+(['"]?)([^\s;&|'"]+)\1/g)) {
|
|
816
|
-
if (m[2]) targets.push(
|
|
537
|
+
if (m[2]) targets.push(resolve(cwd, m[2]));
|
|
817
538
|
}
|
|
818
539
|
for (const m of command.matchAll(/\bNew-Item\s+(?:-(?:Path|ItemType)\s+\w+\s+)*['"]?([^\s;&|'"]+)['"]?/gi)) {
|
|
819
|
-
if (m[1] && !m[1].startsWith("-")) targets.push(
|
|
540
|
+
if (m[1] && !m[1].startsWith("-")) targets.push(resolve(cwd, m[1]));
|
|
820
541
|
}
|
|
821
542
|
return [...new Set(targets)];
|
|
822
543
|
}
|
|
@@ -826,7 +547,7 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
|
|
|
826
547
|
for (const absPath of afterSnapshot) {
|
|
827
548
|
if (!beforeSnapshot.has(absPath)) {
|
|
828
549
|
try {
|
|
829
|
-
const st =
|
|
550
|
+
const st = statSync(absPath);
|
|
830
551
|
if (st.isDirectory()) {
|
|
831
552
|
undoStack.pushNewDir(absPath, `bash (new dir): ${absPath}`);
|
|
832
553
|
} else {
|
|
@@ -838,9 +559,9 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
|
|
|
838
559
|
}
|
|
839
560
|
}
|
|
840
561
|
for (const [target, existedBefore] of parsedTargetsBefore) {
|
|
841
|
-
if (!existedBefore && !tracked.has(target) &&
|
|
562
|
+
if (!existedBefore && !tracked.has(target) && existsSync2(target)) {
|
|
842
563
|
try {
|
|
843
|
-
const st =
|
|
564
|
+
const st = statSync(target);
|
|
844
565
|
if (st.isDirectory()) {
|
|
845
566
|
undoStack.pushNewDir(target, `bash (new dir): ${target}`);
|
|
846
567
|
} else {
|
|
@@ -950,8 +671,8 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
950
671
|
const target = lastMatch?.[2];
|
|
951
672
|
if (!target || target.startsWith("$") || target === "~") return;
|
|
952
673
|
try {
|
|
953
|
-
const newDir =
|
|
954
|
-
if (
|
|
674
|
+
const newDir = resolve(baseCwd, target);
|
|
675
|
+
if (existsSync2(newDir)) {
|
|
955
676
|
setCwd(newDir);
|
|
956
677
|
}
|
|
957
678
|
} catch {
|
|
@@ -959,9 +680,9 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
959
680
|
}
|
|
960
681
|
|
|
961
682
|
// src/tools/builtin/read-file.ts
|
|
962
|
-
import { readFileSync as
|
|
683
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
|
|
963
684
|
import { execFileSync } from "child_process";
|
|
964
|
-
import { extname, resolve as
|
|
685
|
+
import { extname, resolve as resolve2, basename, sep, dirname } from "path";
|
|
965
686
|
import { homedir } from "os";
|
|
966
687
|
|
|
967
688
|
// src/tools/builtin/notebook-utils.ts
|
|
@@ -1071,7 +792,7 @@ var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
|
1071
792
|
function getHardRefusal(normalizedPath) {
|
|
1072
793
|
const home2 = homedir();
|
|
1073
794
|
const p = normalizedPath.toLowerCase();
|
|
1074
|
-
const base =
|
|
795
|
+
const base = basename(normalizedPath).toLowerCase();
|
|
1075
796
|
if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "config.json") {
|
|
1076
797
|
return `[Refused] Reading ${normalizedPath} is blocked because it contains all of your provider API keys.
|
|
1077
798
|
If you genuinely need to inspect or edit it, open it manually outside ai-cli (e.g. via your editor).
|
|
@@ -1085,7 +806,7 @@ For programmatic access, use \`aicli config\` or \`aicli config get <key>\` whic
|
|
|
1085
806
|
function getSensitiveWarning(normalizedPath) {
|
|
1086
807
|
const home2 = homedir();
|
|
1087
808
|
const p = normalizedPath.toLowerCase();
|
|
1088
|
-
const base =
|
|
809
|
+
const base = basename(normalizedPath).toLowerCase();
|
|
1089
810
|
if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "config.json") {
|
|
1090
811
|
return "[\u26A0 Security Warning: This file contains API keys. Be careful not to share this content.]\n\n";
|
|
1091
812
|
}
|
|
@@ -1148,19 +869,19 @@ function isBinaryBuffer(buf) {
|
|
|
1148
869
|
return nullCount / sample.length > 0.1;
|
|
1149
870
|
}
|
|
1150
871
|
function findSimilarFiles(filePath) {
|
|
1151
|
-
const targetName =
|
|
872
|
+
const targetName = basename(filePath).toLowerCase();
|
|
1152
873
|
if (!targetName) return [];
|
|
1153
874
|
const cwd = process.cwd();
|
|
1154
875
|
const candidates = [];
|
|
1155
876
|
try {
|
|
1156
|
-
for (const entry of
|
|
877
|
+
for (const entry of readdirSync2(cwd, { withFileTypes: true })) {
|
|
1157
878
|
if (entry.isFile() && entry.name.toLowerCase() === targetName) {
|
|
1158
879
|
candidates.push(entry.name);
|
|
1159
880
|
}
|
|
1160
881
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
1161
882
|
try {
|
|
1162
|
-
const subDir =
|
|
1163
|
-
for (const sub of
|
|
883
|
+
const subDir = resolve2(cwd, entry.name);
|
|
884
|
+
for (const sub of readdirSync2(subDir, { withFileTypes: true })) {
|
|
1164
885
|
if (sub.isFile() && sub.name.toLowerCase() === targetName) {
|
|
1165
886
|
candidates.push(`${entry.name}/${sub.name}`);
|
|
1166
887
|
}
|
|
@@ -1233,10 +954,10 @@ var readFileTool = {
|
|
|
1233
954
|
const offsetLine = typeof rawOffset === "number" && Number.isFinite(rawOffset) && rawOffset > 0 ? Math.floor(rawOffset) : void 0;
|
|
1234
955
|
const limitLines = typeof rawLimit === "number" && Number.isFinite(rawLimit) && rawLimit > 0 ? Math.floor(rawLimit) : void 0;
|
|
1235
956
|
if (!filePath) throw new ToolError("read_file", "path is required");
|
|
1236
|
-
const normalizedPath =
|
|
957
|
+
const normalizedPath = resolve2(filePath);
|
|
1237
958
|
const refusal = getHardRefusal(normalizedPath);
|
|
1238
959
|
if (refusal) return refusal;
|
|
1239
|
-
if (!
|
|
960
|
+
if (!existsSync3(normalizedPath)) {
|
|
1240
961
|
const suggestions = findSimilarFiles(filePath);
|
|
1241
962
|
if (suggestions.length > 0) {
|
|
1242
963
|
throw new ToolError(
|
|
@@ -1255,7 +976,7 @@ Current working directory: ${process.cwd()}
|
|
|
1255
976
|
Please use list_dir to verify the file path and retry.`
|
|
1256
977
|
);
|
|
1257
978
|
}
|
|
1258
|
-
const { size } =
|
|
979
|
+
const { size } = statSync2(normalizedPath);
|
|
1259
980
|
if (size > MAX_FILE_BYTES) {
|
|
1260
981
|
const mb = (size / 1024 / 1024).toFixed(1);
|
|
1261
982
|
return `[File too large: ${filePath} (${mb} MB)]
|
|
@@ -1275,13 +996,13 @@ Use the bash tool to read in segments, e.g.:
|
|
|
1275
996
|
|
|
1276
997
|
${pdfText}`;
|
|
1277
998
|
}
|
|
1278
|
-
const dir =
|
|
1279
|
-
const nameNoExt =
|
|
1280
|
-
const textAlts = [".md", ".txt", ".html"].map((e) =>
|
|
999
|
+
const dir = dirname(normalizedPath);
|
|
1000
|
+
const nameNoExt = basename(normalizedPath, ext);
|
|
1001
|
+
const textAlts = [".md", ".txt", ".html"].map((e) => resolve2(dir, nameNoExt + e)).filter(existsSync3);
|
|
1281
1002
|
if (textAlts.length > 0) {
|
|
1282
1003
|
return `[PDF file: ${filePath}]
|
|
1283
1004
|
Cannot extract text from this PDF, but found alternative text versions:
|
|
1284
|
-
` + textAlts.map((p) => ` \u2192 ${
|
|
1005
|
+
` + textAlts.map((p) => ` \u2192 ${basename(p)}`).join("\n") + `
|
|
1285
1006
|
Please use read_file to read the above files.`;
|
|
1286
1007
|
}
|
|
1287
1008
|
return `[PDF file: ${filePath}]
|
|
@@ -1290,7 +1011,7 @@ Suggestion: read existing text versions (.md / .txt) in the project, or install
|
|
|
1290
1011
|
}
|
|
1291
1012
|
if (ext === ".ipynb") {
|
|
1292
1013
|
try {
|
|
1293
|
-
const raw =
|
|
1014
|
+
const raw = readFileSync2(normalizedPath, "utf-8");
|
|
1294
1015
|
const nb = parseNotebook(raw);
|
|
1295
1016
|
return renderNotebookAsText(nb, filePath);
|
|
1296
1017
|
} catch (err) {
|
|
@@ -1303,7 +1024,7 @@ ${err.message}`;
|
|
|
1303
1024
|
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.`;
|
|
1304
1025
|
}
|
|
1305
1026
|
const { readFile: readFile2 } = await import("fs/promises");
|
|
1306
|
-
const buf = size > 1048576 ? await readFile2(normalizedPath) :
|
|
1027
|
+
const buf = size > 1048576 ? await readFile2(normalizedPath) : readFileSync2(normalizedPath);
|
|
1307
1028
|
if (encoding === "base64") {
|
|
1308
1029
|
return `[File: ${filePath} | base64]
|
|
1309
1030
|
|
|
@@ -1341,12 +1062,12 @@ ${content}`;
|
|
|
1341
1062
|
};
|
|
1342
1063
|
|
|
1343
1064
|
// src/tools/builtin/write-file.ts
|
|
1344
|
-
import { appendFileSync, mkdirSync
|
|
1345
|
-
import { dirname as
|
|
1065
|
+
import { appendFileSync, mkdirSync } from "fs";
|
|
1066
|
+
import { dirname as dirname2 } from "path";
|
|
1346
1067
|
|
|
1347
1068
|
// src/tools/executor.ts
|
|
1348
1069
|
import chalk3 from "chalk";
|
|
1349
|
-
import { existsSync as
|
|
1070
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
1350
1071
|
import { tmpdir } from "os";
|
|
1351
1072
|
|
|
1352
1073
|
// src/core/readline-internal.ts
|
|
@@ -1552,186 +1273,8 @@ function simpleDiff(oldLines, newLines) {
|
|
|
1552
1273
|
return result;
|
|
1553
1274
|
}
|
|
1554
1275
|
|
|
1555
|
-
// src/tools/hooks.ts
|
|
1556
|
-
import { execSync } from "child_process";
|
|
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";
|
|
1560
|
-
var TRUST_FILE = "hooks-trust.json";
|
|
1561
|
-
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
1562
|
-
function asArray(entry) {
|
|
1563
|
-
if (!entry) return [];
|
|
1564
|
-
const raw = Array.isArray(entry) ? entry : [entry];
|
|
1565
|
-
return raw.map((item) => typeof item === "string" ? { command: item } : item);
|
|
1566
|
-
}
|
|
1567
|
-
function hookTrustPath(configDir) {
|
|
1568
|
-
return join2(configDir, TRUST_FILE);
|
|
1569
|
-
}
|
|
1570
|
-
function loadHookTrustStore(configDir) {
|
|
1571
|
-
const file = hookTrustPath(configDir);
|
|
1572
|
-
if (!existsSync5(file)) return { records: [] };
|
|
1573
|
-
try {
|
|
1574
|
-
const parsed = JSON.parse(readFileSync4(file, "utf-8"));
|
|
1575
|
-
return { records: Array.isArray(parsed.records) ? parsed.records : [] };
|
|
1576
|
-
} catch {
|
|
1577
|
-
return { records: [] };
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
function saveHookTrustStore(configDir, store) {
|
|
1581
|
-
mkdirSync2(configDir, { recursive: true });
|
|
1582
|
-
atomicWriteFileSync(hookTrustPath(configDir), JSON.stringify(store, null, 2));
|
|
1583
|
-
}
|
|
1584
|
-
function hashHook(event, command) {
|
|
1585
|
-
return createHash2("sha256").update(`${event}
|
|
1586
|
-
${command}`, "utf-8").digest("hex");
|
|
1587
|
-
}
|
|
1588
|
-
function isTrusted(source, event, hash, store) {
|
|
1589
|
-
if (source === "user" || source === "managed") return true;
|
|
1590
|
-
return store.records.some((r) => r.source === source && r.event === event && r.hash === hash);
|
|
1591
|
-
}
|
|
1592
|
-
function listHooks(config, configDir) {
|
|
1593
|
-
if (!config || config.enabled === false) return [];
|
|
1594
|
-
const store = loadHookTrustStore(configDir);
|
|
1595
|
-
const out = [];
|
|
1596
|
-
const push = (event, hook, index) => {
|
|
1597
|
-
if (!hook.command) return;
|
|
1598
|
-
const source = hook.source ?? "user";
|
|
1599
|
-
const hash = hashHook(event, hook.command);
|
|
1600
|
-
out.push({
|
|
1601
|
-
id: `${event}:${index}`,
|
|
1602
|
-
event,
|
|
1603
|
-
command: hook.command,
|
|
1604
|
-
source,
|
|
1605
|
-
hash,
|
|
1606
|
-
trusted: isTrusted(source, event, hash, store),
|
|
1607
|
-
disabled: hook.disabled === true,
|
|
1608
|
-
description: hook.description
|
|
1609
|
-
});
|
|
1610
|
-
};
|
|
1611
|
-
if (config.preToolExecution) push("PreToolUse", { command: config.preToolExecution }, 0);
|
|
1612
|
-
if (config.postToolExecution) push("PostToolUse", { command: config.postToolExecution }, 0);
|
|
1613
|
-
for (const [event, entry] of Object.entries(config.events ?? {})) {
|
|
1614
|
-
asArray(entry).forEach((hook, i) => push(event, hook, i));
|
|
1615
|
-
}
|
|
1616
|
-
return out;
|
|
1617
|
-
}
|
|
1618
|
-
function trustHook(configDir, hook) {
|
|
1619
|
-
const store = loadHookTrustStore(configDir);
|
|
1620
|
-
const next = store.records.filter((r) => !(r.source === hook.source && r.event === hook.event && r.id === hook.id));
|
|
1621
|
-
next.push({ id: hook.id, event: hook.event, source: hook.source, hash: hook.hash, trustedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1622
|
-
saveHookTrustStore(configDir, { records: next });
|
|
1623
|
-
}
|
|
1624
|
-
function untrustHook(configDir, hook) {
|
|
1625
|
-
const store = loadHookTrustStore(configDir);
|
|
1626
|
-
saveHookTrustStore(configDir, {
|
|
1627
|
-
records: store.records.filter((r) => !(r.source === hook.source && r.event === hook.event && r.hash === hook.hash))
|
|
1628
|
-
});
|
|
1629
|
-
}
|
|
1630
|
-
function getPendingHookTrust(config, configDir) {
|
|
1631
|
-
return listHooks(config, configDir).filter((h) => h.source === "project" && !h.disabled && !h.trusted);
|
|
1632
|
-
}
|
|
1633
|
-
function normalizeDecision(value, hook) {
|
|
1634
|
-
if (!value || typeof value !== "object") return null;
|
|
1635
|
-
const obj = value;
|
|
1636
|
-
const action = obj.action;
|
|
1637
|
-
if (action !== "allow" && action !== "deny" && action !== "ask") return null;
|
|
1638
|
-
return {
|
|
1639
|
-
action,
|
|
1640
|
-
reason: typeof obj.reason === "string" ? obj.reason : void 0,
|
|
1641
|
-
prompt: typeof obj.prompt === "string" ? obj.prompt : void 0,
|
|
1642
|
-
contextAppend: typeof obj.contextAppend === "string" ? obj.contextAppend : void 0,
|
|
1643
|
-
warning: typeof obj.warning === "string" ? obj.warning : void 0,
|
|
1644
|
-
warnings: Array.isArray(obj.warnings) ? obj.warnings.filter((x) => typeof x === "string") : void 0,
|
|
1645
|
-
hook
|
|
1646
|
-
};
|
|
1647
|
-
}
|
|
1648
|
-
function runStructuredHook(hook, descriptor, payload) {
|
|
1649
|
-
if (hook.disabled === true) return null;
|
|
1650
|
-
const timeout = Math.max(100, Math.min(hook.timeoutMs ?? DEFAULT_TIMEOUT_MS, 3e4));
|
|
1651
|
-
try {
|
|
1652
|
-
const stdout = execSync(hook.command, {
|
|
1653
|
-
timeout,
|
|
1654
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1655
|
-
encoding: "utf-8",
|
|
1656
|
-
env: {
|
|
1657
|
-
...process.env,
|
|
1658
|
-
AICLI_HOOK_EVENT: payload.event,
|
|
1659
|
-
AICLI_HOOK_EVENT_JSON: JSON.stringify(payload)
|
|
1660
|
-
}
|
|
1661
|
-
}).trim();
|
|
1662
|
-
if (!stdout) return null;
|
|
1663
|
-
return normalizeDecision(JSON.parse(stdout), descriptor);
|
|
1664
|
-
} catch (err) {
|
|
1665
|
-
process.stderr.write(`\u26A0 Hook failed: ${hook.command.slice(0, 100)}
|
|
1666
|
-
`);
|
|
1667
|
-
if (hook.required) {
|
|
1668
|
-
return { action: "deny", reason: err instanceof Error ? err.message : String(err), hook: descriptor };
|
|
1669
|
-
}
|
|
1670
|
-
return null;
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
function runLifecycleHooks(config, event, payload, opts) {
|
|
1674
|
-
if (!config || config.enabled === false) return [];
|
|
1675
|
-
const options = typeof opts === "string" ? { configDir: opts } : opts;
|
|
1676
|
-
const store = loadHookTrustStore(options.configDir);
|
|
1677
|
-
const hooks = asArray(config.events?.[event]);
|
|
1678
|
-
const decisions = [];
|
|
1679
|
-
for (let i = 0; i < hooks.length; i++) {
|
|
1680
|
-
const hook = hooks[i];
|
|
1681
|
-
if (!hook.command || hook.disabled) continue;
|
|
1682
|
-
const source = hook.source ?? "user";
|
|
1683
|
-
const hash = hashHook(event, hook.command);
|
|
1684
|
-
const descriptor = {
|
|
1685
|
-
id: `${event}:${i}`,
|
|
1686
|
-
event,
|
|
1687
|
-
command: hook.command,
|
|
1688
|
-
source,
|
|
1689
|
-
hash,
|
|
1690
|
-
trusted: isTrusted(source, event, hash, store),
|
|
1691
|
-
disabled: false,
|
|
1692
|
-
description: hook.description
|
|
1693
|
-
};
|
|
1694
|
-
if (!descriptor.trusted) {
|
|
1695
|
-
options.onSummary?.(`hook skipped: ${event} project hook requires trust (${hash.slice(0, 12)})`);
|
|
1696
|
-
continue;
|
|
1697
|
-
}
|
|
1698
|
-
const decision = runStructuredHook(hook, descriptor, { event, ...payload });
|
|
1699
|
-
if (decision) {
|
|
1700
|
-
decisions.push(decision);
|
|
1701
|
-
options.onSummary?.(`hook ${event}: ${decision.action}${decision.reason ? ` (${decision.reason})` : ""}`);
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1704
|
-
return decisions;
|
|
1705
|
-
}
|
|
1706
|
-
function rewriteTemplate(template, isWindows) {
|
|
1707
|
-
const ref = (name) => isWindows ? `%${name}%` : `$${name}`;
|
|
1708
|
-
return template.replace(/\{tool\}/g, ref("AICLI_HOOK_TOOL")).replace(/\{dangerLevel\}/g, ref("AICLI_HOOK_DANGER_LEVEL")).replace(/\{args\}/g, ref("AICLI_HOOK_ARGS")).replace(/\{status\}/g, ref("AICLI_HOOK_STATUS"));
|
|
1709
|
-
}
|
|
1710
|
-
function runHook(template, vars) {
|
|
1711
|
-
if (!template) return;
|
|
1712
|
-
const isWindows = process.platform === "win32";
|
|
1713
|
-
const cmd = rewriteTemplate(template, isWindows);
|
|
1714
|
-
try {
|
|
1715
|
-
execSync(cmd, {
|
|
1716
|
-
timeout: DEFAULT_TIMEOUT_MS,
|
|
1717
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1718
|
-
encoding: "utf-8",
|
|
1719
|
-
env: {
|
|
1720
|
-
...process.env,
|
|
1721
|
-
AICLI_HOOK_TOOL: vars.tool,
|
|
1722
|
-
AICLI_HOOK_DANGER_LEVEL: vars.dangerLevel ?? "",
|
|
1723
|
-
AICLI_HOOK_ARGS: vars.args ?? "",
|
|
1724
|
-
AICLI_HOOK_STATUS: vars.status ?? ""
|
|
1725
|
-
}
|
|
1726
|
-
});
|
|
1727
|
-
} catch {
|
|
1728
|
-
process.stderr.write(`\u26A0 Hook failed: ${cmd.slice(0, 100)}
|
|
1729
|
-
`);
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
1276
|
// src/tools/permissions.ts
|
|
1734
|
-
import { isAbsolute, resolve as
|
|
1277
|
+
import { isAbsolute, resolve as resolve3 } from "path";
|
|
1735
1278
|
function checkPermission(toolName, args, dangerLevel, rules, defaultAction = "confirm") {
|
|
1736
1279
|
for (const rule of rules) {
|
|
1737
1280
|
if (rule.tool !== "*" && rule.tool !== toolName) continue;
|
|
@@ -1777,8 +1320,8 @@ function normalizePathForPermission(value) {
|
|
|
1777
1320
|
return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
1778
1321
|
}
|
|
1779
1322
|
function isInsidePath(root, target) {
|
|
1780
|
-
const absoluteTarget = isAbsolute(target) ? target :
|
|
1781
|
-
const r = normalizePathForPermission(
|
|
1323
|
+
const absoluteTarget = isAbsolute(target) ? target : resolve3(root, target);
|
|
1324
|
+
const r = normalizePathForPermission(resolve3(root));
|
|
1782
1325
|
const t = normalizePathForPermission(absoluteTarget);
|
|
1783
1326
|
return t === r || t.startsWith(`${r}/`);
|
|
1784
1327
|
}
|
|
@@ -2152,8 +1695,8 @@ var theme = new Proxy(DARK_THEME, {
|
|
|
2152
1695
|
});
|
|
2153
1696
|
|
|
2154
1697
|
// src/tools/action-classifier.ts
|
|
2155
|
-
import { existsSync as
|
|
2156
|
-
import { isAbsolute as isAbsolute2, join
|
|
1698
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
1699
|
+
import { isAbsolute as isAbsolute2, join, resolve as resolve4 } from "path";
|
|
2157
1700
|
var RECENT_DENIED_LIMIT = 50;
|
|
2158
1701
|
var recentlyDenied = [];
|
|
2159
1702
|
function recordRecentlyDeniedAutoAction(call, classification) {
|
|
@@ -2176,8 +1719,8 @@ function normalizePath(value) {
|
|
|
2176
1719
|
return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
2177
1720
|
}
|
|
2178
1721
|
function isInside(root, target) {
|
|
2179
|
-
const absoluteRoot =
|
|
2180
|
-
const absoluteTarget = isAbsolute2(target) ? target :
|
|
1722
|
+
const absoluteRoot = resolve4(root);
|
|
1723
|
+
const absoluteTarget = isAbsolute2(target) ? target : resolve4(root, target);
|
|
2181
1724
|
const base = normalizePath(absoluteRoot);
|
|
2182
1725
|
const t = normalizePath(absoluteTarget);
|
|
2183
1726
|
return t === base || t.startsWith(base + "/");
|
|
@@ -2194,10 +1737,10 @@ function splitShellWords(command) {
|
|
|
2194
1737
|
return out.filter(Boolean);
|
|
2195
1738
|
}
|
|
2196
1739
|
function readPackageManifest(root) {
|
|
2197
|
-
const p =
|
|
2198
|
-
if (!
|
|
1740
|
+
const p = join(root, "package.json");
|
|
1741
|
+
if (!existsSync4(p)) return null;
|
|
2199
1742
|
try {
|
|
2200
|
-
return JSON.parse(
|
|
1743
|
+
return JSON.parse(readFileSync3(p, "utf-8"));
|
|
2201
1744
|
} catch {
|
|
2202
1745
|
return null;
|
|
2203
1746
|
}
|
|
@@ -2213,10 +1756,10 @@ function manifestHasDependency(root, name) {
|
|
|
2213
1756
|
}
|
|
2214
1757
|
function lockfileMentions(root, name) {
|
|
2215
1758
|
for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
|
|
2216
|
-
const p =
|
|
2217
|
-
if (!
|
|
1759
|
+
const p = join(root, file);
|
|
1760
|
+
if (!existsSync4(p)) continue;
|
|
2218
1761
|
try {
|
|
2219
|
-
if (
|
|
1762
|
+
if (readFileSync3(p, "utf-8").includes(name)) return true;
|
|
2220
1763
|
} catch {
|
|
2221
1764
|
}
|
|
2222
1765
|
}
|
|
@@ -2703,7 +2246,7 @@ var ToolExecutor = class {
|
|
|
2703
2246
|
rl.resume();
|
|
2704
2247
|
process.stdout.write(prompt);
|
|
2705
2248
|
this.confirming = true;
|
|
2706
|
-
return new Promise((
|
|
2249
|
+
return new Promise((resolve8) => {
|
|
2707
2250
|
let completed = false;
|
|
2708
2251
|
const cleanup = (result) => {
|
|
2709
2252
|
if (completed) return;
|
|
@@ -2713,7 +2256,7 @@ var ToolExecutor = class {
|
|
|
2713
2256
|
rl.pause();
|
|
2714
2257
|
rlAny.output = savedOutput;
|
|
2715
2258
|
this.confirming = false;
|
|
2716
|
-
|
|
2259
|
+
resolve8(result);
|
|
2717
2260
|
};
|
|
2718
2261
|
const onLine = (line) => {
|
|
2719
2262
|
const trimmed = line.trim();
|
|
@@ -2780,10 +2323,10 @@ var ToolExecutor = class {
|
|
|
2780
2323
|
const filePath = String(call.arguments["path"] ?? "");
|
|
2781
2324
|
const newContent = String(call.arguments["content"] ?? "");
|
|
2782
2325
|
if (!filePath) return;
|
|
2783
|
-
if (
|
|
2326
|
+
if (existsSync5(filePath)) {
|
|
2784
2327
|
let oldContent;
|
|
2785
2328
|
try {
|
|
2786
|
-
oldContent =
|
|
2329
|
+
oldContent = readFileSync4(filePath, "utf-8");
|
|
2787
2330
|
} catch {
|
|
2788
2331
|
return;
|
|
2789
2332
|
}
|
|
@@ -2806,7 +2349,7 @@ var ToolExecutor = class {
|
|
|
2806
2349
|
}
|
|
2807
2350
|
} else if (call.name === "edit_file") {
|
|
2808
2351
|
const filePath = String(call.arguments["path"] ?? "");
|
|
2809
|
-
if (!filePath || !
|
|
2352
|
+
if (!filePath || !existsSync5(filePath)) return;
|
|
2810
2353
|
const oldStr = call.arguments["old_str"];
|
|
2811
2354
|
const newStr = call.arguments["new_str"];
|
|
2812
2355
|
if (oldStr !== void 0) {
|
|
@@ -2833,7 +2376,7 @@ var ToolExecutor = class {
|
|
|
2833
2376
|
const to = Number(call.arguments["delete_to_line"] ?? from);
|
|
2834
2377
|
let fileContent;
|
|
2835
2378
|
try {
|
|
2836
|
-
fileContent =
|
|
2379
|
+
fileContent = readFileSync4(filePath, "utf-8");
|
|
2837
2380
|
} catch {
|
|
2838
2381
|
return;
|
|
2839
2382
|
}
|
|
@@ -2887,7 +2430,7 @@ var ToolExecutor = class {
|
|
|
2887
2430
|
rl.resume();
|
|
2888
2431
|
process.stdout.write(color("Proceed? [y/N] (type y + Enter to confirm) "));
|
|
2889
2432
|
this.confirming = true;
|
|
2890
|
-
return new Promise((
|
|
2433
|
+
return new Promise((resolve8) => {
|
|
2891
2434
|
let completed = false;
|
|
2892
2435
|
const cleanup = (answer) => {
|
|
2893
2436
|
if (completed) return;
|
|
@@ -2897,7 +2440,7 @@ var ToolExecutor = class {
|
|
|
2897
2440
|
rl.pause();
|
|
2898
2441
|
rlAny.output = savedOutput;
|
|
2899
2442
|
this.confirming = false;
|
|
2900
|
-
|
|
2443
|
+
resolve8(answer === "y");
|
|
2901
2444
|
};
|
|
2902
2445
|
const onLine = (line) => {
|
|
2903
2446
|
const trimmed = line.trim();
|
|
@@ -2925,11 +2468,11 @@ var ToolExecutor = class {
|
|
|
2925
2468
|
};
|
|
2926
2469
|
|
|
2927
2470
|
// src/tools/sensitive-paths.ts
|
|
2928
|
-
import { resolve as
|
|
2471
|
+
import { resolve as resolve5, sep as sep2, basename as basename2 } from "path";
|
|
2929
2472
|
import { homedir as homedir2 } from "os";
|
|
2930
2473
|
var home = homedir2();
|
|
2931
2474
|
function norm(p) {
|
|
2932
|
-
const abs =
|
|
2475
|
+
const abs = resolve5(p);
|
|
2933
2476
|
return process.platform === "win32" ? abs.toLowerCase() : abs;
|
|
2934
2477
|
}
|
|
2935
2478
|
function homeRel(p) {
|
|
@@ -2944,7 +2487,7 @@ function homeRel(p) {
|
|
|
2944
2487
|
function classifyWritePath(path3) {
|
|
2945
2488
|
if (!path3) return { sensitive: false };
|
|
2946
2489
|
const abs = norm(path3);
|
|
2947
|
-
const base =
|
|
2490
|
+
const base = basename2(abs);
|
|
2948
2491
|
const rel = homeRel(path3);
|
|
2949
2492
|
if (rel) {
|
|
2950
2493
|
const shellRc = /* @__PURE__ */ new Set([
|
|
@@ -3068,7 +2611,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
3068
2611
|
}
|
|
3069
2612
|
undoStack.push(filePath, `write_file${appendMode ? " (append)" : ""}: ${filePath}`);
|
|
3070
2613
|
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
3071
|
-
|
|
2614
|
+
mkdirSync(dirname2(filePath), { recursive: true });
|
|
3072
2615
|
if (appendMode) {
|
|
3073
2616
|
appendFileSync(filePath, content, encoding);
|
|
3074
2617
|
} else {
|
|
@@ -3088,7 +2631,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
3088
2631
|
};
|
|
3089
2632
|
|
|
3090
2633
|
// src/tools/builtin/edit-file.ts
|
|
3091
|
-
import { readFileSync as
|
|
2634
|
+
import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
|
|
3092
2635
|
|
|
3093
2636
|
// src/tools/builtin/patch-apply.ts
|
|
3094
2637
|
function parseUnifiedDiff(patch) {
|
|
@@ -3449,7 +2992,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
3449
2992
|
const filePath = String(args["path"] ?? "");
|
|
3450
2993
|
const encoding = args["encoding"] ?? "utf-8";
|
|
3451
2994
|
if (!filePath) throw new ToolError("edit_file", "path is required");
|
|
3452
|
-
if (!
|
|
2995
|
+
if (!existsSync6(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
|
|
3453
2996
|
const verdict = classifyWritePath(filePath);
|
|
3454
2997
|
if (verdict.sensitive && subAgentGuard.active) {
|
|
3455
2998
|
throw new ToolError(
|
|
@@ -3457,7 +3000,7 @@ Note: Path can be absolute or relative to cwd.`,
|
|
|
3457
3000
|
`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.`
|
|
3458
3001
|
);
|
|
3459
3002
|
}
|
|
3460
|
-
const original =
|
|
3003
|
+
const original = readFileSync5(filePath, encoding);
|
|
3461
3004
|
if (args["patch"] !== void 0) {
|
|
3462
3005
|
const patchText = String(args["patch"] ?? "");
|
|
3463
3006
|
const stopOnError = args["stop_on_error"] !== false;
|
|
@@ -3615,8 +3158,8 @@ function truncatePreview(str, maxLen = 80) {
|
|
|
3615
3158
|
}
|
|
3616
3159
|
|
|
3617
3160
|
// src/tools/builtin/list-dir.ts
|
|
3618
|
-
import { readdirSync as
|
|
3619
|
-
import { join as
|
|
3161
|
+
import { readdirSync as readdirSync3, statSync as statSync3, existsSync as existsSync7 } from "fs";
|
|
3162
|
+
import { join as join2, basename as basename3 } from "path";
|
|
3620
3163
|
var listDirTool = {
|
|
3621
3164
|
definition: {
|
|
3622
3165
|
name: "list_dir",
|
|
@@ -3638,12 +3181,12 @@ var listDirTool = {
|
|
|
3638
3181
|
async execute(args) {
|
|
3639
3182
|
const dirPath = String(args["path"] ?? process.cwd());
|
|
3640
3183
|
const recursive = Boolean(args["recursive"] ?? false);
|
|
3641
|
-
if (!
|
|
3642
|
-
const targetName =
|
|
3184
|
+
if (!existsSync7(dirPath)) {
|
|
3185
|
+
const targetName = basename3(dirPath).toLowerCase();
|
|
3643
3186
|
const cwd = process.cwd();
|
|
3644
3187
|
const suggestions = [];
|
|
3645
3188
|
try {
|
|
3646
|
-
for (const entry of
|
|
3189
|
+
for (const entry of readdirSync3(cwd, { withFileTypes: true })) {
|
|
3647
3190
|
if (entry.isDirectory() && entry.name.toLowerCase() === targetName) {
|
|
3648
3191
|
suggestions.push(entry.name);
|
|
3649
3192
|
}
|
|
@@ -3676,7 +3219,7 @@ Please use list_dir (without path) to see the current directory structure first.
|
|
|
3676
3219
|
function listRecursive(basePath, indent, recursive, lines) {
|
|
3677
3220
|
let entries;
|
|
3678
3221
|
try {
|
|
3679
|
-
entries =
|
|
3222
|
+
entries = readdirSync3(basePath, { withFileTypes: true });
|
|
3680
3223
|
} catch {
|
|
3681
3224
|
lines.push(`${indent}(permission denied)`);
|
|
3682
3225
|
return;
|
|
@@ -3696,11 +3239,11 @@ function listRecursive(basePath, indent, recursive, lines) {
|
|
|
3696
3239
|
if (entry.isDirectory()) {
|
|
3697
3240
|
lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
|
|
3698
3241
|
if (recursive) {
|
|
3699
|
-
listRecursive(
|
|
3242
|
+
listRecursive(join2(basePath, entry.name), indent + " ", true, lines);
|
|
3700
3243
|
}
|
|
3701
3244
|
} else {
|
|
3702
3245
|
try {
|
|
3703
|
-
const stat =
|
|
3246
|
+
const stat = statSync3(join2(basePath, entry.name));
|
|
3704
3247
|
const size = formatSize(stat.size);
|
|
3705
3248
|
lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
|
|
3706
3249
|
} catch {
|
|
@@ -3716,9 +3259,9 @@ function formatSize(bytes) {
|
|
|
3716
3259
|
}
|
|
3717
3260
|
|
|
3718
3261
|
// src/tools/builtin/grep-files.ts
|
|
3719
|
-
import { readdirSync as
|
|
3262
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync4, existsSync as existsSync8 } from "fs";
|
|
3720
3263
|
import { readFile } from "fs/promises";
|
|
3721
|
-
import { join as
|
|
3264
|
+
import { join as join3, relative } from "path";
|
|
3722
3265
|
var grepFilesTool = {
|
|
3723
3266
|
definition: {
|
|
3724
3267
|
name: "grep_files",
|
|
@@ -3769,7 +3312,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
3769
3312
|
const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
|
|
3770
3313
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
|
|
3771
3314
|
if (!pattern) throw new ToolError("grep_files", "pattern is required");
|
|
3772
|
-
if (!
|
|
3315
|
+
if (!existsSync8(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
|
|
3773
3316
|
const MAX_PATTERN_LENGTH = 1e3;
|
|
3774
3317
|
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
3775
3318
|
throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
|
|
@@ -3782,7 +3325,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
|
|
|
3782
3325
|
regex = new RegExp(escaped, ignoreCase ? "gi" : "g");
|
|
3783
3326
|
}
|
|
3784
3327
|
const results = [];
|
|
3785
|
-
const stat =
|
|
3328
|
+
const stat = statSync4(rootPath);
|
|
3786
3329
|
if (stat.isFile()) {
|
|
3787
3330
|
searchInFile(rootPath, rootPath, regex, contextLines, maxResults, results);
|
|
3788
3331
|
} else {
|
|
@@ -3855,7 +3398,7 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
|
|
|
3855
3398
|
if (paths.length >= maxFiles) return;
|
|
3856
3399
|
let entries;
|
|
3857
3400
|
try {
|
|
3858
|
-
entries =
|
|
3401
|
+
entries = readdirSync4(dirPath, { withFileTypes: true });
|
|
3859
3402
|
} catch {
|
|
3860
3403
|
return;
|
|
3861
3404
|
}
|
|
@@ -3863,13 +3406,13 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
|
|
|
3863
3406
|
if (paths.length >= maxFiles) return;
|
|
3864
3407
|
if (entry.isDirectory()) {
|
|
3865
3408
|
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
3866
|
-
walk(
|
|
3409
|
+
walk(join3(dirPath, entry.name));
|
|
3867
3410
|
} else if (entry.isFile()) {
|
|
3868
3411
|
if (isBinary(entry.name)) continue;
|
|
3869
3412
|
if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
|
|
3870
|
-
const fullPath =
|
|
3413
|
+
const fullPath = join3(dirPath, entry.name);
|
|
3871
3414
|
try {
|
|
3872
|
-
if (
|
|
3415
|
+
if (statSync4(fullPath).size > 1e6) continue;
|
|
3873
3416
|
} catch {
|
|
3874
3417
|
continue;
|
|
3875
3418
|
}
|
|
@@ -3915,14 +3458,14 @@ async function searchInFileAsync(fullPath, displayPath, regex, contextLines, max
|
|
|
3915
3458
|
}
|
|
3916
3459
|
function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, results) {
|
|
3917
3460
|
try {
|
|
3918
|
-
const stat =
|
|
3461
|
+
const stat = statSync4(fullPath);
|
|
3919
3462
|
if (stat.size > 1e6) return;
|
|
3920
3463
|
} catch {
|
|
3921
3464
|
return;
|
|
3922
3465
|
}
|
|
3923
3466
|
let content;
|
|
3924
3467
|
try {
|
|
3925
|
-
content =
|
|
3468
|
+
content = readFileSync6(fullPath, "utf-8");
|
|
3926
3469
|
} catch {
|
|
3927
3470
|
return;
|
|
3928
3471
|
}
|
|
@@ -3953,8 +3496,8 @@ function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, re
|
|
|
3953
3496
|
}
|
|
3954
3497
|
|
|
3955
3498
|
// src/tools/builtin/glob-files.ts
|
|
3956
|
-
import { readdirSync as
|
|
3957
|
-
import { join as
|
|
3499
|
+
import { readdirSync as readdirSync5, statSync as statSync5, existsSync as existsSync9 } from "fs";
|
|
3500
|
+
import { join as join4, relative as relative2, basename as basename4 } from "path";
|
|
3958
3501
|
var globFilesTool = {
|
|
3959
3502
|
definition: {
|
|
3960
3503
|
name: "glob_files",
|
|
@@ -3987,7 +3530,7 @@ Results sorted by most recent modification time. Automatically skips node_module
|
|
|
3987
3530
|
const rootPath = String(args["path"] ?? process.cwd());
|
|
3988
3531
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
|
|
3989
3532
|
if (!pattern) throw new ToolError("glob_files", "pattern is required");
|
|
3990
|
-
if (!
|
|
3533
|
+
if (!existsSync9(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
|
|
3991
3534
|
const regex = globToRegex(pattern);
|
|
3992
3535
|
const matches = [];
|
|
3993
3536
|
collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
|
|
@@ -4058,21 +3601,21 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
4058
3601
|
if (results.length >= maxResults) return;
|
|
4059
3602
|
let entries;
|
|
4060
3603
|
try {
|
|
4061
|
-
entries =
|
|
3604
|
+
entries = readdirSync5(dirPath, { withFileTypes: true });
|
|
4062
3605
|
} catch {
|
|
4063
3606
|
return;
|
|
4064
3607
|
}
|
|
4065
3608
|
for (const entry of entries) {
|
|
4066
3609
|
if (results.length >= maxResults) break;
|
|
4067
|
-
const fullPath =
|
|
3610
|
+
const fullPath = join4(dirPath, entry.name);
|
|
4068
3611
|
if (entry.isDirectory()) {
|
|
4069
3612
|
if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
4070
3613
|
collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
|
|
4071
3614
|
} else if (entry.isFile()) {
|
|
4072
3615
|
const relPath = relative2(rootPath, fullPath).replace(/\\/g, "/");
|
|
4073
|
-
if (regex.test(relPath) || regex.test(
|
|
3616
|
+
if (regex.test(relPath) || regex.test(basename4(relPath))) {
|
|
4074
3617
|
try {
|
|
4075
|
-
const stat =
|
|
3618
|
+
const stat = statSync5(fullPath);
|
|
4076
3619
|
results.push({ relPath, absPath: fullPath, mtime: stat.mtimeMs });
|
|
4077
3620
|
} catch {
|
|
4078
3621
|
results.push({ relPath, absPath: fullPath, mtime: 0 });
|
|
@@ -4146,7 +3689,7 @@ var runInteractiveTool = {
|
|
|
4146
3689
|
PYTHONDONTWRITEBYTECODE: "1"
|
|
4147
3690
|
};
|
|
4148
3691
|
const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
|
|
4149
|
-
return new Promise((
|
|
3692
|
+
return new Promise((resolve8) => {
|
|
4150
3693
|
const child = spawn2(executable, cmdArgs.map(String), {
|
|
4151
3694
|
cwd: process.cwd(),
|
|
4152
3695
|
env,
|
|
@@ -4179,22 +3722,22 @@ var runInteractiveTool = {
|
|
|
4179
3722
|
setTimeout(writeNextLine, 400);
|
|
4180
3723
|
const timer = setTimeout(() => {
|
|
4181
3724
|
child.kill();
|
|
4182
|
-
|
|
3725
|
+
resolve8(`${prefixWarnings}[Timeout after ${timeout}ms]
|
|
4183
3726
|
${buildOutput(stdout, stderr)}`);
|
|
4184
3727
|
}, timeout);
|
|
4185
3728
|
child.on("close", (code) => {
|
|
4186
3729
|
clearTimeout(timer);
|
|
4187
3730
|
const output = buildOutput(stdout, stderr);
|
|
4188
3731
|
if (code !== 0 && code !== null) {
|
|
4189
|
-
|
|
3732
|
+
resolve8(`${prefixWarnings}Exit code ${code}:
|
|
4190
3733
|
${output}`);
|
|
4191
3734
|
} else {
|
|
4192
|
-
|
|
3735
|
+
resolve8(`${prefixWarnings}${output || "(no output)"}`);
|
|
4193
3736
|
}
|
|
4194
3737
|
});
|
|
4195
3738
|
child.on("error", (err) => {
|
|
4196
3739
|
clearTimeout(timer);
|
|
4197
|
-
|
|
3740
|
+
resolve8(
|
|
4198
3741
|
`${prefixWarnings}Failed to start process "${executable}": ${err.message}
|
|
4199
3742
|
Hint: On Windows, use the full path to the executable, e.g.:
|
|
4200
3743
|
C:\\Users\\Jinzd\\anaconda3\\envs\\python312\\python.exe`
|
|
@@ -4804,8 +4347,8 @@ ${preamble}`;
|
|
|
4804
4347
|
}
|
|
4805
4348
|
|
|
4806
4349
|
// src/tools/builtin/save-last-response.ts
|
|
4807
|
-
import { mkdirSync as
|
|
4808
|
-
import { dirname as
|
|
4350
|
+
import { mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, rmdirSync as rmdirSync2 } from "fs";
|
|
4351
|
+
import { dirname as dirname3 } from "path";
|
|
4809
4352
|
var lastResponseStore = { content: "" };
|
|
4810
4353
|
function cleanupRejectedTeeFile(filePath) {
|
|
4811
4354
|
try {
|
|
@@ -4813,7 +4356,7 @@ function cleanupRejectedTeeFile(filePath) {
|
|
|
4813
4356
|
} catch {
|
|
4814
4357
|
}
|
|
4815
4358
|
try {
|
|
4816
|
-
rmdirSync2(
|
|
4359
|
+
rmdirSync2(dirname3(filePath));
|
|
4817
4360
|
} catch {
|
|
4818
4361
|
}
|
|
4819
4362
|
}
|
|
@@ -4854,7 +4397,7 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
4854
4397
|
throw new ToolError("save_last_response", "No content to save: AI has not produced any response yet, or the last response was empty.");
|
|
4855
4398
|
}
|
|
4856
4399
|
undoStack.push(filePath, `save_last_response: ${filePath}`);
|
|
4857
|
-
|
|
4400
|
+
mkdirSync2(dirname3(filePath), { recursive: true });
|
|
4858
4401
|
atomicWriteFileSync(filePath, content);
|
|
4859
4402
|
const lines = content.split("\n").length;
|
|
4860
4403
|
return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
|
|
@@ -4862,21 +4405,21 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
4862
4405
|
};
|
|
4863
4406
|
|
|
4864
4407
|
// src/tools/builtin/save-memory.ts
|
|
4865
|
-
import { join as
|
|
4408
|
+
import { join as join6 } from "path";
|
|
4866
4409
|
import { homedir as homedir3 } from "os";
|
|
4867
4410
|
|
|
4868
4411
|
// src/memory/persistent-memory.ts
|
|
4869
|
-
import { existsSync as
|
|
4870
|
-
import { randomUUID, createHash
|
|
4871
|
-
import { dirname as
|
|
4412
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync7 } from "fs";
|
|
4413
|
+
import { randomUUID, createHash } from "crypto";
|
|
4414
|
+
import { dirname as dirname4, join as join5, resolve as resolve6 } from "path";
|
|
4872
4415
|
function memoryStorePath(configDir) {
|
|
4873
|
-
return
|
|
4416
|
+
return join5(configDir, MEMORY_STORE_FILE_NAME);
|
|
4874
4417
|
}
|
|
4875
4418
|
function memoryMarkdownPath(configDir) {
|
|
4876
|
-
return
|
|
4419
|
+
return join5(configDir, MEMORY_FILE_NAME);
|
|
4877
4420
|
}
|
|
4878
4421
|
function getProjectKey(cwd = process.cwd()) {
|
|
4879
|
-
return
|
|
4422
|
+
return resolve6(getGitRoot(cwd) ?? cwd);
|
|
4880
4423
|
}
|
|
4881
4424
|
function nowIso() {
|
|
4882
4425
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4909,8 +4452,8 @@ function normalizeEntry(value) {
|
|
|
4909
4452
|
}
|
|
4910
4453
|
function legacyEntriesFromMarkdown(configDir) {
|
|
4911
4454
|
const file = memoryMarkdownPath(configDir);
|
|
4912
|
-
if (!
|
|
4913
|
-
const content =
|
|
4455
|
+
if (!existsSync10(file)) return [];
|
|
4456
|
+
const content = readFileSync7(file, "utf-8").trim();
|
|
4914
4457
|
if (!content) return [];
|
|
4915
4458
|
const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
|
|
4916
4459
|
const chunks = parts.length > 1 ? parts : [content];
|
|
@@ -4919,15 +4462,15 @@ function legacyEntriesFromMarkdown(configDir) {
|
|
|
4919
4462
|
const body = (match ? match[2] : chunk).trim();
|
|
4920
4463
|
const parsed = match ? Date.parse(match[1].replace(" ", "T")) : NaN;
|
|
4921
4464
|
const createdAt = Number.isFinite(parsed) ? new Date(parsed).toISOString() : nowIso();
|
|
4922
|
-
const id = "legacy-" +
|
|
4465
|
+
const id = "legacy-" + createHash("sha1").update(`${index}
|
|
4923
4466
|
${chunk}`).digest("hex").slice(0, 12);
|
|
4924
4467
|
return normalizeEntry({ id, content: body, scope: "personal", createdAt, updatedAt: createdAt, sensitivity: "low", approved: true });
|
|
4925
4468
|
}).filter((entry) => Boolean(entry));
|
|
4926
4469
|
}
|
|
4927
4470
|
function loadMemoryEntries(configDir) {
|
|
4928
4471
|
const file = memoryStorePath(configDir);
|
|
4929
|
-
if (!
|
|
4930
|
-
const text =
|
|
4472
|
+
if (!existsSync10(file)) return legacyEntriesFromMarkdown(configDir);
|
|
4473
|
+
const text = readFileSync7(file, "utf-8").trim();
|
|
4931
4474
|
if (!text) return [];
|
|
4932
4475
|
const entries = [];
|
|
4933
4476
|
for (const line of text.split("\n")) {
|
|
@@ -4942,7 +4485,7 @@ function loadMemoryEntries(configDir) {
|
|
|
4942
4485
|
return entries;
|
|
4943
4486
|
}
|
|
4944
4487
|
function saveMemoryEntries(configDir, entries) {
|
|
4945
|
-
|
|
4488
|
+
mkdirSync3(configDir, { recursive: true });
|
|
4946
4489
|
const jsonl = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
4947
4490
|
atomicWriteFileSync(memoryStorePath(configDir), jsonl ? jsonl + "\n" : "");
|
|
4948
4491
|
syncLegacyMarkdown(configDir, entries);
|
|
@@ -5017,7 +4560,7 @@ function findUnique(entries, idPrefix) {
|
|
|
5017
4560
|
return matches[0];
|
|
5018
4561
|
}
|
|
5019
4562
|
function syncLegacyMarkdown(configDir, entries = loadMemoryEntries(configDir)) {
|
|
5020
|
-
|
|
4563
|
+
mkdirSync3(dirname4(memoryMarkdownPath(configDir)), { recursive: true });
|
|
5021
4564
|
const active = entries.filter((entry) => entry.approved && !isMemoryExpired(entry));
|
|
5022
4565
|
const markdown = active.map((entry) => {
|
|
5023
4566
|
const date = entry.createdAt.replace("T", " ").slice(0, 19);
|
|
@@ -5060,7 +4603,7 @@ ${entry.content}`;
|
|
|
5060
4603
|
|
|
5061
4604
|
// src/tools/builtin/save-memory.ts
|
|
5062
4605
|
function getConfigDir() {
|
|
5063
|
-
return
|
|
4606
|
+
return join6(homedir3(), CONFIG_DIR_NAME);
|
|
5064
4607
|
}
|
|
5065
4608
|
function parseScope(value) {
|
|
5066
4609
|
return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
|
|
@@ -5152,7 +4695,7 @@ function promptUser(rl, question) {
|
|
|
5152
4695
|
console.log();
|
|
5153
4696
|
console.log(chalk4.cyan("\u2753 ") + chalk4.bold(question));
|
|
5154
4697
|
process.stdout.write(chalk4.cyan("> "));
|
|
5155
|
-
return new Promise((
|
|
4698
|
+
return new Promise((resolve8) => {
|
|
5156
4699
|
let completed = false;
|
|
5157
4700
|
const cleanup = (answer) => {
|
|
5158
4701
|
if (completed) return;
|
|
@@ -5162,7 +4705,7 @@ function promptUser(rl, question) {
|
|
|
5162
4705
|
rl.pause();
|
|
5163
4706
|
rlAny.output = savedOutput;
|
|
5164
4707
|
askUserContext.prompting = false;
|
|
5165
|
-
|
|
4708
|
+
resolve8(answer);
|
|
5166
4709
|
};
|
|
5167
4710
|
const onLine = (line) => {
|
|
5168
4711
|
cleanup(line);
|
|
@@ -5394,8 +4937,8 @@ function formatResults2(query, data, _requested) {
|
|
|
5394
4937
|
}
|
|
5395
4938
|
|
|
5396
4939
|
// src/agents/agent-config.ts
|
|
5397
|
-
import { existsSync as
|
|
5398
|
-
import { join as
|
|
4940
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync8 } from "fs";
|
|
4941
|
+
import { join as join7 } from "path";
|
|
5399
4942
|
import { homedir as homedir4 } from "os";
|
|
5400
4943
|
var READ_ONLY_TOOLS2 = /* @__PURE__ */ new Set([
|
|
5401
4944
|
"read_file",
|
|
@@ -5490,13 +5033,13 @@ function parseAgentConfig(raw, source, path3) {
|
|
|
5490
5033
|
return cfg;
|
|
5491
5034
|
}
|
|
5492
5035
|
function loadAgentDir(dir, source) {
|
|
5493
|
-
if (!
|
|
5036
|
+
if (!existsSync11(dir)) return [];
|
|
5494
5037
|
const out = [];
|
|
5495
|
-
for (const file of
|
|
5038
|
+
for (const file of readdirSync6(dir)) {
|
|
5496
5039
|
if (!file.endsWith(".json")) continue;
|
|
5497
|
-
const path3 =
|
|
5040
|
+
const path3 = join7(dir, file);
|
|
5498
5041
|
try {
|
|
5499
|
-
const parsed = JSON.parse(
|
|
5042
|
+
const parsed = JSON.parse(readFileSync8(path3, "utf-8"));
|
|
5500
5043
|
const cfg = parseAgentConfig(parsed, source, path3);
|
|
5501
5044
|
if (cfg) out.push(cfg);
|
|
5502
5045
|
} catch {
|
|
@@ -5506,8 +5049,8 @@ function loadAgentDir(dir, source) {
|
|
|
5506
5049
|
}
|
|
5507
5050
|
function getAgentDirs(configDir, cwd = process.cwd()) {
|
|
5508
5051
|
return {
|
|
5509
|
-
user:
|
|
5510
|
-
project:
|
|
5052
|
+
user: join7(configDir ?? join7(homedir4(), CONFIG_DIR_NAME), "agents"),
|
|
5053
|
+
project: join7(cwd, CONFIG_DIR_NAME, "agents")
|
|
5511
5054
|
};
|
|
5512
5055
|
}
|
|
5513
5056
|
function listAgentConfigs(configDir, cwd = process.cwd()) {
|
|
@@ -6222,14 +5765,14 @@ var taskStopTool = {
|
|
|
6222
5765
|
|
|
6223
5766
|
// src/tools/builtin/git-tools.ts
|
|
6224
5767
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
6225
|
-
import { existsSync as
|
|
6226
|
-
import { join as
|
|
5768
|
+
import { existsSync as existsSync12 } from "fs";
|
|
5769
|
+
import { join as join8 } from "path";
|
|
6227
5770
|
function assertGitRepo(cwd) {
|
|
6228
5771
|
let dir = cwd;
|
|
6229
5772
|
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
6230
5773
|
while (dir && dir !== root) {
|
|
6231
|
-
if (
|
|
6232
|
-
const parent =
|
|
5774
|
+
if (existsSync12(join8(dir, ".git"))) return;
|
|
5775
|
+
const parent = join8(dir, "..");
|
|
6233
5776
|
if (parent === dir) break;
|
|
6234
5777
|
dir = parent;
|
|
6235
5778
|
}
|
|
@@ -6494,9 +6037,9 @@ ${commitOutput.trim()}`;
|
|
|
6494
6037
|
};
|
|
6495
6038
|
|
|
6496
6039
|
// src/tools/builtin/notebook-edit.ts
|
|
6497
|
-
import { readFileSync as
|
|
6040
|
+
import { readFileSync as readFileSync9, existsSync as existsSync13 } from "fs";
|
|
6498
6041
|
import { writeFile } from "fs/promises";
|
|
6499
|
-
import { resolve as
|
|
6042
|
+
import { resolve as resolve7, extname as extname2 } from "path";
|
|
6500
6043
|
var notebookEditTool = {
|
|
6501
6044
|
definition: {
|
|
6502
6045
|
name: "notebook_edit",
|
|
@@ -6545,14 +6088,14 @@ var notebookEditTool = {
|
|
|
6545
6088
|
if (!Number.isInteger(cellIndexRaw) || cellIndexRaw < 1) {
|
|
6546
6089
|
throw new ToolError("notebook_edit", "cell_index must be a positive integer (1-based)");
|
|
6547
6090
|
}
|
|
6548
|
-
const absPath =
|
|
6091
|
+
const absPath = resolve7(filePath);
|
|
6549
6092
|
if (extname2(absPath).toLowerCase() !== ".ipynb") {
|
|
6550
6093
|
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
6551
6094
|
}
|
|
6552
|
-
if (!
|
|
6095
|
+
if (!existsSync13(absPath)) {
|
|
6553
6096
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
6554
6097
|
}
|
|
6555
|
-
const raw =
|
|
6098
|
+
const raw = readFileSync9(absPath, "utf-8");
|
|
6556
6099
|
const nb = parseNotebook(raw);
|
|
6557
6100
|
const cellIdx0 = cellIndexRaw - 1;
|
|
6558
6101
|
undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
|
|
@@ -6969,8 +6512,8 @@ function estimateToolDefinitionTokens(def) {
|
|
|
6969
6512
|
|
|
6970
6513
|
// src/tools/registry.ts
|
|
6971
6514
|
import { pathToFileURL } from "url";
|
|
6972
|
-
import { existsSync as
|
|
6973
|
-
import { join as
|
|
6515
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync4, readdirSync as readdirSync7 } from "fs";
|
|
6516
|
+
import { join as join9 } from "path";
|
|
6974
6517
|
var ToolRegistry = class {
|
|
6975
6518
|
tools = /* @__PURE__ */ new Map();
|
|
6976
6519
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -7131,16 +6674,16 @@ var ToolRegistry = class {
|
|
|
7131
6674
|
* Returns the number of successfully loaded plugins.
|
|
7132
6675
|
*/
|
|
7133
6676
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
7134
|
-
if (!
|
|
6677
|
+
if (!existsSync14(pluginsDir)) {
|
|
7135
6678
|
try {
|
|
7136
|
-
|
|
6679
|
+
mkdirSync4(pluginsDir, { recursive: true });
|
|
7137
6680
|
} catch {
|
|
7138
6681
|
}
|
|
7139
6682
|
return 0;
|
|
7140
6683
|
}
|
|
7141
6684
|
let files;
|
|
7142
6685
|
try {
|
|
7143
|
-
files =
|
|
6686
|
+
files = readdirSync7(pluginsDir).filter((f) => f.endsWith(".js"));
|
|
7144
6687
|
} catch {
|
|
7145
6688
|
return 0;
|
|
7146
6689
|
}
|
|
@@ -7157,12 +6700,12 @@ var ToolRegistry = class {
|
|
|
7157
6700
|
process.stderr.write(
|
|
7158
6701
|
`
|
|
7159
6702
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
7160
|
-
` + files.map((f) => ` + ${
|
|
6703
|
+
` + files.map((f) => ` + ${join9(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
7161
6704
|
);
|
|
7162
6705
|
let loaded = 0;
|
|
7163
6706
|
for (const file of files) {
|
|
7164
6707
|
try {
|
|
7165
|
-
const fileUrl = pathToFileURL(
|
|
6708
|
+
const fileUrl = pathToFileURL(join9(pluginsDir, file)).href;
|
|
7166
6709
|
const mod = await import(fileUrl);
|
|
7167
6710
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
7168
6711
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -7194,25 +6737,10 @@ export {
|
|
|
7194
6737
|
theme,
|
|
7195
6738
|
undoStack,
|
|
7196
6739
|
renderDiff,
|
|
7197
|
-
listHooks,
|
|
7198
|
-
trustHook,
|
|
7199
|
-
untrustHook,
|
|
7200
|
-
getPendingHookTrust,
|
|
7201
|
-
runLifecycleHooks,
|
|
7202
|
-
runHook,
|
|
7203
6740
|
recordRecentlyDeniedAutoAction,
|
|
7204
6741
|
getRecentlyDeniedAutoActions,
|
|
7205
6742
|
clearRecentlyDeniedAutoActions,
|
|
7206
6743
|
defaultActionClassifier,
|
|
7207
|
-
pluginRoot,
|
|
7208
|
-
installPlugin,
|
|
7209
|
-
setPluginEnabled,
|
|
7210
|
-
trustPlugin,
|
|
7211
|
-
getInstalledPlugin,
|
|
7212
|
-
listInstalledPlugins,
|
|
7213
|
-
mergePluginHooks,
|
|
7214
|
-
getActivePluginAssets,
|
|
7215
|
-
describePlugin,
|
|
7216
6744
|
memoryStorePath,
|
|
7217
6745
|
memoryMarkdownPath,
|
|
7218
6746
|
isMemoryExpired,
|