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.
Files changed (27) hide show
  1. package/README.md +4 -2
  2. package/dist/{batch-YCOVKTXD.js → batch-7V7OTMUP.js} +2 -2
  3. package/dist/{chunk-WZ3VKLF3.js → chunk-5CA2TJ5F.js} +1 -1
  4. package/dist/{chunk-QAYOI57M.js → chunk-C2Z42DI5.js} +1 -1
  5. package/dist/{chunk-MGBMNCHG.js → chunk-GX3HSGJX.js} +452 -156
  6. package/dist/{chunk-OUAZOE5U.js → chunk-H2UIHGHH.js} +23 -22
  7. package/dist/{chunk-KOPUCJXM.js → chunk-L4UREAID.js} +3 -3
  8. package/dist/{chunk-SNJAOXFT.js → chunk-MWKE2TNS.js} +1 -1
  9. package/dist/{chunk-XJGEQIYS.js → chunk-NTCB7CMT.js} +1 -1
  10. package/dist/{chunk-524WZOKS.js → chunk-OUC75QCF.js} +1 -1
  11. package/dist/{chunk-WKOQ5CYC.js → chunk-P4VBLXKS.js} +1 -1
  12. package/dist/{chunk-VTH7BLXK.js → chunk-VGFTM3XT.js} +1 -1
  13. package/dist/{ci-52RZIYWB.js → ci-L6GH2WVC.js} +4 -4
  14. package/dist/{ci-format-73UXKE65.js → ci-format-WW7454AY.js} +2 -2
  15. package/dist/{constants-DIXAD35W.js → constants-NCTFSHDU.js} +1 -1
  16. package/dist/{doctor-cli-7GOQPULZ.js → doctor-cli-EWMFBP5Q.js} +4 -4
  17. package/dist/electron-server.js +575 -231
  18. package/dist/{hub-GIGBITZN.js → hub-CDL6T7CP.js} +1 -1
  19. package/dist/index.js +148 -46
  20. package/dist/{pr-KPQ5RPKB.js → pr-D6PEKEGK.js} +4 -4
  21. package/dist/{run-tests-K7QR5QN4.js → run-tests-NXVVKAK2.js} +1 -1
  22. package/dist/{run-tests-ZDSA3QES.js → run-tests-SWU2XEV7.js} +2 -2
  23. package/dist/{server-QT3SC2KI.js → server-LHYSS6CK.js} +85 -21
  24. package/dist/{server-22YF3U34.js → server-WUT7VYTD.js} +4 -4
  25. package/dist/{task-orchestrator-BHQQCVTY.js → task-orchestrator-C5AA2BI5.js} +4 -4
  26. package/dist/{usage-WZZFSFLM.js → usage-6ZUUJBI2.js} +2 -2
  27. 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-VTH7BLXK.js";
8
+ } from "./chunk-VGFTM3XT.js";
9
9
  import {
10
10
  runTool
11
- } from "./chunk-SNJAOXFT.js";
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-XJGEQIYS.js";
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 existsSync2, readdirSync, statSync } from "fs";
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 (existsSync(filePath)) {
356
+ if (existsSync2(filePath)) {
75
357
  try {
76
- previousContent = readFileSync(filePath, "utf-8");
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 (existsSync(entry.filePath)) {
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 (existsSync(entry.filePath)) {
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 (!existsSync2(currentCwd)) {
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 = resolve(currentCwd, cwdArg);
311
- if (!existsSync2(resolved)) {
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, existsSync2(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(readdirSync(dir).map((name) => resolve(dir, name)));
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(resolve(cwd, m[2]));
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(resolve(cwd, clean));
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(resolve(cwd, clean));
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(resolve(cwd, m[2]));
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(resolve(cwd, m[1]));
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 = statSync(absPath);
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) && existsSync2(target)) {
841
+ if (!existedBefore && !tracked.has(target) && existsSync3(target)) {
560
842
  try {
561
- const st = statSync(target);
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 = resolve(baseCwd, target);
672
- if (existsSync2(newDir)) {
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 readFileSync2, existsSync as existsSync3, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
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 resolve2, basename, sep, dirname } from "path";
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 = basename(normalizedPath).toLowerCase();
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 = basename(normalizedPath).toLowerCase();
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 = basename(filePath).toLowerCase();
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 readdirSync2(cwd, { withFileTypes: true })) {
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 = resolve2(cwd, entry.name);
881
- for (const sub of readdirSync2(subDir, { withFileTypes: true })) {
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 = resolve2(filePath);
1236
+ const normalizedPath = resolve3(filePath);
955
1237
  const refusal = getHardRefusal(normalizedPath);
956
1238
  if (refusal) return refusal;
957
- if (!existsSync3(normalizedPath)) {
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 } = statSync2(normalizedPath);
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 = dirname(normalizedPath);
997
- const nameNoExt = basename(normalizedPath, ext);
998
- const textAlts = [".md", ".txt", ".html"].map((e) => resolve2(dir, nameNoExt + e)).filter(existsSync3);
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 ${basename(p)}`).join("\n") + `
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 = readFileSync2(normalizedPath, "utf-8");
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) : readFileSync2(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 mkdirSync2 } from "fs";
1063
- import { dirname as dirname2 } from "path";
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 existsSync6, readFileSync as readFileSync5 } from "fs";
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 existsSync4, mkdirSync, readFileSync as readFileSync3 } from "fs";
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 join(configDir, TRUST_FILE);
1568
+ return join2(configDir, TRUST_FILE);
1287
1569
  }
1288
1570
  function loadHookTrustStore(configDir) {
1289
1571
  const file = hookTrustPath(configDir);
1290
- if (!existsSync4(file)) return { records: [] };
1572
+ if (!existsSync5(file)) return { records: [] };
1291
1573
  try {
1292
- const parsed = JSON.parse(readFileSync3(file, "utf-8"));
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
- mkdirSync(configDir, { recursive: true });
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 createHash("sha256").update(`${event}
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 resolve3 } from "path";
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 : resolve3(root, target);
1499
- const r = normalizePathForPermission(resolve3(root));
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 existsSync5, readFileSync as readFileSync4 } from "fs";
1874
- import { isAbsolute as isAbsolute2, join as join2, resolve as resolve4 } from "path";
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 = resolve4(root);
1898
- const absoluteTarget = isAbsolute2(target) ? target : resolve4(root, 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 = join2(root, "package.json");
1916
- if (!existsSync5(p)) return null;
2197
+ const p = join3(root, "package.json");
2198
+ if (!existsSync6(p)) return null;
1917
2199
  try {
1918
- return JSON.parse(readFileSync4(p, "utf-8"));
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 = join2(root, file);
1935
- if (!existsSync5(p)) continue;
2216
+ const p = join3(root, file);
2217
+ if (!existsSync6(p)) continue;
1936
2218
  try {
1937
- if (readFileSync4(p, "utf-8").includes(name)) return true;
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((resolve8) => {
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
- resolve8(result);
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 (existsSync6(filePath)) {
2783
+ if (existsSync7(filePath)) {
2502
2784
  let oldContent;
2503
2785
  try {
2504
- oldContent = readFileSync5(filePath, "utf-8");
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 || !existsSync6(filePath)) return;
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 = readFileSync5(filePath, "utf-8");
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((resolve8) => {
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
- resolve8(answer === "y");
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 resolve5, sep as sep2, basename as basename2 } from "path";
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 = resolve5(p);
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 = basename2(abs);
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
- mkdirSync2(dirname2(filePath), { recursive: true });
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 readFileSync6, existsSync as existsSync7 } from "fs";
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 (!existsSync7(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
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 = readFileSync6(filePath, encoding);
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 readdirSync3, statSync as statSync3, existsSync as existsSync8 } from "fs";
3337
- import { join as join3, basename as basename3 } from "path";
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 (!existsSync8(dirPath)) {
3360
- const targetName = basename3(dirPath).toLowerCase();
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 readdirSync3(cwd, { withFileTypes: true })) {
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 = readdirSync3(basePath, { withFileTypes: true });
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(join3(basePath, entry.name), indent + " ", true, lines);
3699
+ listRecursive(join4(basePath, entry.name), indent + " ", true, lines);
3418
3700
  }
3419
3701
  } else {
3420
3702
  try {
3421
- const stat = statSync3(join3(basePath, entry.name));
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 readdirSync4, readFileSync as readFileSync7, statSync as statSync4, existsSync as existsSync9 } from "fs";
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 join4, relative } from "path";
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 (!existsSync9(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
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 = statSync4(rootPath);
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 = readdirSync4(dirPath, { withFileTypes: true });
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(join4(dirPath, entry.name));
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 = join4(dirPath, entry.name);
3870
+ const fullPath = join5(dirPath, entry.name);
3589
3871
  try {
3590
- if (statSync4(fullPath).size > 1e6) continue;
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 = statSync4(fullPath);
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 = readFileSync7(fullPath, "utf-8");
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 readdirSync5, statSync as statSync5, existsSync as existsSync10 } from "fs";
3675
- import { join as join5, relative as relative2, basename as basename4 } from "path";
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 (!existsSync10(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
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 = readdirSync5(dirPath, { withFileTypes: true });
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 = join5(dirPath, entry.name);
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(basename4(relPath))) {
4073
+ if (regex.test(relPath) || regex.test(basename5(relPath))) {
3792
4074
  try {
3793
- const stat = statSync5(fullPath);
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((resolve8) => {
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
- resolve8(`${prefixWarnings}[Timeout after ${timeout}ms]
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
- resolve8(`${prefixWarnings}Exit code ${code}:
4189
+ resolve9(`${prefixWarnings}Exit code ${code}:
3908
4190
  ${output}`);
3909
4191
  } else {
3910
- resolve8(`${prefixWarnings}${output || "(no output)"}`);
4192
+ resolve9(`${prefixWarnings}${output || "(no output)"}`);
3911
4193
  }
3912
4194
  });
3913
4195
  child.on("error", (err) => {
3914
4196
  clearTimeout(timer);
3915
- resolve8(
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 mkdirSync3, unlinkSync as unlinkSync2, rmdirSync as rmdirSync2 } from "fs";
4526
- import { dirname as dirname3 } from "path";
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(dirname3(filePath));
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
- mkdirSync3(dirname3(filePath), { recursive: true });
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 join7 } from "path";
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 existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync8 } from "fs";
4588
- import { randomUUID, createHash as createHash2 } from "crypto";
4589
- import { dirname as dirname4, join as join6, resolve as resolve6 } from "path";
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 join6(configDir, MEMORY_STORE_FILE_NAME);
4873
+ return join7(configDir, MEMORY_STORE_FILE_NAME);
4592
4874
  }
4593
4875
  function memoryMarkdownPath(configDir) {
4594
- return join6(configDir, MEMORY_FILE_NAME);
4876
+ return join7(configDir, MEMORY_FILE_NAME);
4595
4877
  }
4596
4878
  function getProjectKey(cwd = process.cwd()) {
4597
- return resolve6(getGitRoot(cwd) ?? cwd);
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 (!existsSync11(file)) return [];
4631
- const content = readFileSync8(file, "utf-8").trim();
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-" + createHash2("sha1").update(`${index}
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 (!existsSync11(file)) return legacyEntriesFromMarkdown(configDir);
4648
- const text = readFileSync8(file, "utf-8").trim();
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
- mkdirSync4(configDir, { recursive: true });
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
- mkdirSync4(dirname4(memoryMarkdownPath(configDir)), { recursive: true });
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 join7(homedir3(), CONFIG_DIR_NAME);
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((resolve8) => {
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
- resolve8(answer);
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 existsSync12, readdirSync as readdirSync6, readFileSync as readFileSync9 } from "fs";
5116
- import { join as join8 } from "path";
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 (!existsSync12(dir)) return [];
5493
+ if (!existsSync13(dir)) return [];
5212
5494
  const out = [];
5213
- for (const file of readdirSync6(dir)) {
5495
+ for (const file of readdirSync7(dir)) {
5214
5496
  if (!file.endsWith(".json")) continue;
5215
- const path3 = join8(dir, file);
5497
+ const path3 = join9(dir, file);
5216
5498
  try {
5217
- const parsed = JSON.parse(readFileSync9(path3, "utf-8"));
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: join8(configDir ?? join8(homedir4(), CONFIG_DIR_NAME), "agents"),
5228
- project: join8(cwd, CONFIG_DIR_NAME, "agents")
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 existsSync13 } from "fs";
5939
- import { join as join9 } from "path";
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 (existsSync13(join9(dir, ".git"))) return;
5945
- const parent = join9(dir, "..");
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 readFileSync10, existsSync as existsSync14 } from "fs";
6497
+ import { readFileSync as readFileSync11, existsSync as existsSync15 } from "fs";
6211
6498
  import { writeFile } from "fs/promises";
6212
- import { resolve as resolve7, extname as extname2 } from "path";
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 = resolve7(filePath);
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 (!existsSync14(absPath)) {
6552
+ if (!existsSync15(absPath)) {
6266
6553
  throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
6267
6554
  }
6268
- const raw = readFileSync10(absPath, "utf-8");
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 existsSync15, mkdirSync as mkdirSync5, readdirSync as readdirSync7 } from "fs";
6686
- import { join as join10 } from "path";
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 (!existsSync15(pluginsDir)) {
7134
+ if (!existsSync16(pluginsDir)) {
6848
7135
  try {
6849
- mkdirSync5(pluginsDir, { recursive: true });
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 = readdirSync7(pluginsDir).filter((f) => f.endsWith(".js"));
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) => ` + ${join10(pluginsDir, f)}`).join("\n") + "\n\n"
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(join10(pluginsDir, file)).href;
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,