jinzd-ai-cli 0.4.234 → 0.4.236

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 (29) hide show
  1. package/README.md +5 -5
  2. package/dist/{auth-FSTAKSYF.js → auth-PE3Z5OHS.js} +1 -1
  3. package/dist/{batch-T3FRX4B6.js → batch-HZI7QAE7.js} +2 -2
  4. package/dist/{chunk-AFT6VFMI.js → chunk-7VAE3I3D.js} +1 -1
  5. package/dist/{chunk-3KTSJ2EL.js → chunk-CGQARJMP.js} +1 -1
  6. package/dist/{chunk-ETJ5X6V7.js → chunk-MFWAL5S2.js} +1 -1
  7. package/dist/{chunk-6MJMHHRK.js → chunk-O4QYY6SF.js} +3 -3
  8. package/dist/{chunk-5DTEEWYC.js → chunk-QDP3NUPX.js} +3 -3
  9. package/dist/{chunk-JVKAL5Q3.js → chunk-RYQBBEMM.js} +33 -3
  10. package/dist/{chunk-E5RLXMSU.js → chunk-SBSWH2EG.js} +89 -56
  11. package/dist/{chunk-TAXIG7HH.js → chunk-SCFGLRB3.js} +1 -1
  12. package/dist/{chunk-II6RZRTF.js → chunk-T6EIBUKW.js} +115 -18
  13. package/dist/{chunk-5VPRZMZB.js → chunk-TT7YURG3.js} +3 -3
  14. package/dist/{chunk-L5PNAQ6O.js → chunk-VBAW4ZPY.js} +1 -1
  15. package/dist/{ci-GJ53GPLA.js → ci-FYLCHRN3.js} +4 -4
  16. package/dist/{ci-format-AXE4QCI6.js → ci-format-TH2ZTFKU.js} +2 -2
  17. package/dist/{constants-DC4EXFSB.js → constants-BF6YNKRW.js} +1 -1
  18. package/dist/{doctor-cli-HKREND6Q.js → doctor-cli-B6GE4BYJ.js} +4 -4
  19. package/dist/electron-server.js +312 -151
  20. package/dist/{hub-LTTKRGZC.js → hub-QBQCKEPE.js} +1 -1
  21. package/dist/index.js +2439 -2437
  22. package/dist/{pr-4VODJVRV.js → pr-KV2WK7SU.js} +4 -4
  23. package/dist/{run-tests-QR2PYR2P.js → run-tests-DI6WEYHX.js} +1 -1
  24. package/dist/{run-tests-G5BNTMWQ.js → run-tests-MEHC73SQ.js} +2 -2
  25. package/dist/{server-EO6VQHKL.js → server-ETIGAZX5.js} +4 -4
  26. package/dist/{server-64WZVZZN.js → server-KZAP5JN3.js} +34 -33
  27. package/dist/{task-orchestrator-4INCZMLT.js → task-orchestrator-WS724BUX.js} +4 -4
  28. package/dist/{usage-T3HFMNNV.js → usage-DHZR5PG7.js} +2 -2
  29. package/package.json +2 -4
@@ -38,7 +38,7 @@ import {
38
38
  VERSION,
39
39
  buildUserIdentityPrompt,
40
40
  runTestsTool
41
- } from "./chunk-ETJ5X6V7.js";
41
+ } from "./chunk-MFWAL5S2.js";
42
42
  import {
43
43
  hasSemanticIndex,
44
44
  semanticSearch
@@ -70,8 +70,8 @@ import {
70
70
  import express from "express";
71
71
  import { createServer } from "http";
72
72
  import { WebSocketServer } from "ws";
73
- import { join as join28, dirname as dirname11, resolve as resolve13, relative as relative4, sep as sep3 } from "path";
74
- import { existsSync as existsSync33, readFileSync as readFileSync26, readdirSync as readdirSync17, statSync as statSync15, realpathSync } from "fs";
73
+ import { join as join28, dirname as dirname11, resolve as resolve13, relative as relative6, sep as sep4 } from "path";
74
+ import { existsSync as existsSync33, readFileSync as readFileSync25, readdirSync as readdirSync16, statSync as statSync14, realpathSync as realpathSync3 } from "fs";
75
75
  import { networkInterfaces } from "os";
76
76
 
77
77
  // src/config/config-manager.ts
@@ -1908,12 +1908,12 @@ function findPhantomClaims(content, extraMessages) {
1908
1908
  const claimed = extractClaimedFilePaths(content);
1909
1909
  if (claimed.length === 0) return [];
1910
1910
  const normalize = (p) => p.replace(/\\/g, "/").toLowerCase().replace(/^\.\//, "");
1911
- const basename7 = (p) => {
1911
+ const basename8 = (p) => {
1912
1912
  const parts = normalize(p).split("/");
1913
1913
  return parts[parts.length - 1] ?? "";
1914
1914
  };
1915
1915
  const written = extractWrittenFilePaths(extraMessages).map(normalize);
1916
- const writtenBases = new Set(written.map(basename7));
1916
+ const writtenBases = new Set(written.map(basename8));
1917
1917
  const writtenFull = new Set(written);
1918
1918
  const bashText = extractBashCommands(extraMessages).map((c) => c.replace(/\\/g, "/").toLowerCase()).join("\n");
1919
1919
  return claimed.filter((raw) => {
@@ -1922,8 +1922,8 @@ function findPhantomClaims(content, extraMessages) {
1922
1922
  for (const w of writtenFull) {
1923
1923
  if (w.endsWith("/" + norm2) || norm2.endsWith("/" + w)) return false;
1924
1924
  }
1925
- if (writtenBases.has(basename7(norm2))) return false;
1926
- const base = basename7(norm2);
1925
+ if (writtenBases.has(basename8(norm2))) return false;
1926
+ const base = basename8(norm2);
1927
1927
  if (base && bashText.includes(base)) return false;
1928
1928
  return true;
1929
1929
  });
@@ -5937,11 +5937,11 @@ ${content}`;
5937
5937
 
5938
5938
  // src/tools/builtin/write-file.ts
5939
5939
  import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
5940
- import { dirname as dirname4 } from "path";
5940
+ import { dirname as dirname5 } from "path";
5941
5941
 
5942
5942
  // src/tools/executor.ts
5943
5943
  import chalk3 from "chalk";
5944
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
5944
+ import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
5945
5945
  import { tmpdir } from "os";
5946
5946
 
5947
5947
  // src/core/readline-internal.ts
@@ -6310,7 +6310,8 @@ function runHook(template, vars) {
6310
6310
  }
6311
6311
 
6312
6312
  // src/tools/permissions.ts
6313
- import { isAbsolute, resolve as resolve3 } from "path";
6313
+ import { existsSync as existsSync8, realpathSync } from "fs";
6314
+ import { basename as basename2, dirname as dirname3, isAbsolute, resolve as resolve3 } from "path";
6314
6315
  function checkPermission(toolName, args, dangerLevel, rules, defaultAction = "confirm") {
6315
6316
  for (const rule of rules) {
6316
6317
  if (rule.tool !== "*" && rule.tool !== toolName) continue;
@@ -6355,10 +6356,28 @@ var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
6355
6356
  function normalizePathForPermission(value) {
6356
6357
  return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
6357
6358
  }
6359
+ function canonicalizeWithNearestParent(value) {
6360
+ const missing = [];
6361
+ let cursor = resolve3(value);
6362
+ while (!existsSync8(cursor)) {
6363
+ const parent = dirname3(cursor);
6364
+ if (parent === cursor) return null;
6365
+ missing.unshift(basename2(cursor));
6366
+ cursor = parent;
6367
+ }
6368
+ try {
6369
+ return resolve3(realpathSync(cursor), ...missing);
6370
+ } catch {
6371
+ return null;
6372
+ }
6373
+ }
6358
6374
  function isInsidePath(root, target) {
6359
6375
  const absoluteTarget = isAbsolute(target) ? target : resolve3(root, target);
6360
- const r = normalizePathForPermission(resolve3(root));
6361
- const t = normalizePathForPermission(absoluteTarget);
6376
+ const canonicalRoot = canonicalizeWithNearestParent(root);
6377
+ const canonicalTarget = canonicalizeWithNearestParent(absoluteTarget);
6378
+ if (!canonicalRoot || !canonicalTarget) return false;
6379
+ const r = normalizePathForPermission(canonicalRoot);
6380
+ const t = normalizePathForPermission(canonicalTarget);
6362
6381
  return t === r || t.startsWith(`${r}/`);
6363
6382
  }
6364
6383
  function extractPathArg(toolName, args) {
@@ -6410,19 +6429,31 @@ function profileHardDecision(profileName, toolName, args, dangerLevel, ctx) {
6410
6429
  return null;
6411
6430
  }
6412
6431
  if (profileName === "workspace-write") {
6413
- if (dangerLevel === "destructive") return { action: "confirm", reason: "destructive tools always require confirmation", profileName };
6432
+ if (dangerLevel === "destructive") {
6433
+ return { action: "confirm", reason: "destructive tools always require confirmation", profileName, hardBoundary: true };
6434
+ }
6414
6435
  if (dangerLevel === "write" && isExplicitFileWriteTool(toolName)) {
6415
6436
  const target = extractPathArg(toolName, args);
6416
- if (!target) return { action: "confirm", reason: "file write target is not explicit", profileName };
6437
+ if (!target) {
6438
+ return { action: "confirm", reason: "file write target is not explicit", profileName, hardBoundary: true };
6439
+ }
6417
6440
  const allowedRoots = getAllowedWriteRoots(ctx);
6418
6441
  if (allowedRoots.length === 0 || !allowedRoots.some((root) => isInsidePath(root, target))) {
6419
- return { action: "deny", reason: "file write target is outside workspace/temp roots", profileName };
6442
+ return { action: "deny", reason: "file write target is outside workspace/temp roots", profileName, hardBoundary: true };
6420
6443
  }
6421
6444
  }
6445
+ if (toolName === "bash" || toolName === "run_interactive" || toolName === "task_create") {
6446
+ return {
6447
+ action: "confirm",
6448
+ reason: "shell/process writes cannot be proven to stay inside workspace/temp roots",
6449
+ profileName,
6450
+ hardBoundary: true
6451
+ };
6452
+ }
6422
6453
  return null;
6423
6454
  }
6424
6455
  if (profileName === "danger-full-access") {
6425
- if (dangerLevel === "destructive") return { action: "confirm", reason: "destructive tools still require confirmation", profileName };
6456
+ if (dangerLevel === "destructive") return { action: "confirm", reason: "destructive tools still require confirmation", profileName, hardBoundary: true };
6426
6457
  return null;
6427
6458
  }
6428
6459
  return null;
@@ -6434,7 +6465,8 @@ function checkPermissionWithProfile(toolName, args, dangerLevel, rules, defaultA
6434
6465
  const combinedRules = [...getProfileRules(profileContext), ...rules];
6435
6466
  const profileDefault = profileName === "legacy" ? defaultAction : getProfileDefaultAction(profileName, profileContext);
6436
6467
  const action = checkPermission(toolName, args, dangerLevel, combinedRules, profileDefault);
6437
- if (hard?.action === "confirm" && action === "auto-approve") return hard;
6468
+ if (action === "deny") return { action, profileName };
6469
+ if (hard?.action === "confirm") return hard;
6438
6470
  return { action, profileName };
6439
6471
  }
6440
6472
  function formatPermissionProfileWarning(profileName) {
@@ -6692,8 +6724,8 @@ var theme = new Proxy(DARK_THEME, {
6692
6724
  });
6693
6725
 
6694
6726
  // src/diagnostics/tool-stats.ts
6695
- import { existsSync as existsSync8, readFileSync as readFileSync7, mkdirSync as mkdirSync5 } from "fs";
6696
- import { join as join5, dirname as dirname3 } from "path";
6727
+ import { existsSync as existsSync9, readFileSync as readFileSync7, mkdirSync as mkdirSync5 } from "fs";
6728
+ import { join as join5, dirname as dirname4 } from "path";
6697
6729
  import { homedir as homedir3 } from "os";
6698
6730
  var STATS_FILE_NAME = "tool-stats.json";
6699
6731
  var STATS_VERSION = 1;
@@ -6708,7 +6740,7 @@ function statsFilePath() {
6708
6740
  }
6709
6741
  function load() {
6710
6742
  const path3 = statsFilePath();
6711
- if (!existsSync8(path3)) {
6743
+ if (!existsSync9(path3)) {
6712
6744
  return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
6713
6745
  }
6714
6746
  try {
@@ -6770,7 +6802,7 @@ function flush() {
6770
6802
  if (!dirty || state === null) return;
6771
6803
  const path3 = statsFilePath();
6772
6804
  try {
6773
- mkdirSync5(dirname3(path3), { recursive: true });
6805
+ mkdirSync5(dirname4(path3), { recursive: true });
6774
6806
  atomicWriteFileSync(path3, JSON.stringify(state, null, 2));
6775
6807
  dirty = false;
6776
6808
  pendingWrites = 0;
@@ -6794,7 +6826,7 @@ function getTopUsedTools(limit = 5) {
6794
6826
  }
6795
6827
 
6796
6828
  // src/tools/action-classifier.ts
6797
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
6829
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
6798
6830
  import { isAbsolute as isAbsolute2, join as join6, resolve as resolve4 } from "path";
6799
6831
  var RECENT_DENIED_LIMIT = 50;
6800
6832
  var recentlyDenied = [];
@@ -6837,7 +6869,7 @@ function splitShellWords(command) {
6837
6869
  }
6838
6870
  function readPackageManifest(root) {
6839
6871
  const p = join6(root, "package.json");
6840
- if (!existsSync9(p)) return null;
6872
+ if (!existsSync10(p)) return null;
6841
6873
  try {
6842
6874
  return JSON.parse(readFileSync8(p, "utf-8"));
6843
6875
  } catch {
@@ -6856,7 +6888,7 @@ function manifestHasDependency(root, name) {
6856
6888
  function lockfileMentions(root, name) {
6857
6889
  for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
6858
6890
  const p = join6(root, file);
6859
- if (!existsSync9(p)) continue;
6891
+ if (!existsSync10(p)) continue;
6860
6892
  try {
6861
6893
  if (readFileSync8(p, "utf-8").includes(name)) return true;
6862
6894
  } catch {
@@ -7132,14 +7164,15 @@ var ToolExecutor = class {
7132
7164
  return { callId: call.id, content: message, isError: true };
7133
7165
  }
7134
7166
  }
7135
- if (networkPermission?.action === "confirm" && dangerLevel === "safe") {
7167
+ const requiresHardConfirmation = permission.action === "confirm" && permission.hardBoundary === true;
7168
+ if ((networkPermission?.action === "confirm" || requiresHardConfirmation) && dangerLevel === "safe") {
7136
7169
  this.printToolCall(call);
7137
7170
  toolCallAlreadyPrinted = true;
7138
7171
  const confirmed = await this.confirm(call, "write");
7139
7172
  if (!confirmed) {
7140
7173
  return {
7141
7174
  callId: call.id,
7142
- content: `[User cancelled] The user declined network access for ${call.name}. Do not retry without asking.`,
7175
+ content: `[User cancelled] The user declined permission for ${call.name}. Do not retry without asking.`,
7143
7176
  isError: true
7144
7177
  };
7145
7178
  }
@@ -7157,7 +7190,7 @@ var ToolExecutor = class {
7157
7190
  isError: true
7158
7191
  };
7159
7192
  }
7160
- if (classification.decision === "auto-approve") {
7193
+ if (classification.decision === "auto-approve" && !requiresHardConfirmation) {
7161
7194
  this.printToolCall(call);
7162
7195
  if (dangerLevel === "write") this.printDiffPreview(call);
7163
7196
  console.log(theme.warning(` \u26A1 Auto-approved (/auto: ${classification.ruleId})`));
@@ -7178,7 +7211,7 @@ var ToolExecutor = class {
7178
7211
  }
7179
7212
  }
7180
7213
  }
7181
- if (this.sessionAutoApprove && dangerLevel === "write") {
7214
+ if (this.sessionAutoApprove && dangerLevel === "write" && !requiresHardConfirmation) {
7182
7215
  this.printToolCall(call);
7183
7216
  if (dangerLevel === "write") this.printDiffPreview(call);
7184
7217
  console.log(theme.warning(" \u26A1 Auto-approved (session /yolo mode)"));
@@ -7422,7 +7455,7 @@ var ToolExecutor = class {
7422
7455
  const filePath = String(call.arguments["path"] ?? "");
7423
7456
  const newContent = String(call.arguments["content"] ?? "");
7424
7457
  if (!filePath) return;
7425
- if (existsSync10(filePath)) {
7458
+ if (existsSync11(filePath)) {
7426
7459
  let oldContent;
7427
7460
  try {
7428
7461
  oldContent = readFileSync9(filePath, "utf-8");
@@ -7448,7 +7481,7 @@ var ToolExecutor = class {
7448
7481
  }
7449
7482
  } else if (call.name === "edit_file") {
7450
7483
  const filePath = String(call.arguments["path"] ?? "");
7451
- if (!filePath || !existsSync10(filePath)) return;
7484
+ if (!filePath || !existsSync11(filePath)) return;
7452
7485
  const oldStr = call.arguments["old_str"];
7453
7486
  const newStr = call.arguments["new_str"];
7454
7487
  if (oldStr !== void 0) {
@@ -7567,7 +7600,7 @@ var ToolExecutor = class {
7567
7600
  };
7568
7601
 
7569
7602
  // src/tools/sensitive-paths.ts
7570
- import { resolve as resolve5, sep as sep2, basename as basename2 } from "path";
7603
+ import { resolve as resolve5, sep as sep2, basename as basename3 } from "path";
7571
7604
  import { homedir as homedir4 } from "os";
7572
7605
  var home = homedir4();
7573
7606
  function norm(p) {
@@ -7586,7 +7619,7 @@ function homeRel(p) {
7586
7619
  function classifyWritePath(path3) {
7587
7620
  if (!path3) return { sensitive: false };
7588
7621
  const abs = norm(path3);
7589
- const base = basename2(abs);
7622
+ const base = basename3(abs);
7590
7623
  const rel = homeRel(path3);
7591
7624
  if (rel) {
7592
7625
  const shellRc = /* @__PURE__ */ new Set([
@@ -7710,7 +7743,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
7710
7743
  }
7711
7744
  undoStack.push(filePath, `write_file${appendMode ? " (append)" : ""}: ${filePath}`);
7712
7745
  fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
7713
- mkdirSync6(dirname4(filePath), { recursive: true });
7746
+ mkdirSync6(dirname5(filePath), { recursive: true });
7714
7747
  if (appendMode) {
7715
7748
  appendFileSync(filePath, content, encoding);
7716
7749
  } else {
@@ -7730,7 +7763,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
7730
7763
  };
7731
7764
 
7732
7765
  // src/tools/builtin/edit-file.ts
7733
- import { readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
7766
+ import { readFileSync as readFileSync10, existsSync as existsSync12 } from "fs";
7734
7767
 
7735
7768
  // src/tools/builtin/patch-apply.ts
7736
7769
  function parseUnifiedDiff(patch) {
@@ -8091,7 +8124,7 @@ Note: Path can be absolute or relative to cwd.`,
8091
8124
  const filePath = String(args["path"] ?? args["file"] ?? "");
8092
8125
  const encoding = args["encoding"] ?? "utf-8";
8093
8126
  if (!filePath) throw new ToolError("edit_file", "path is required");
8094
- if (!existsSync11(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
8127
+ if (!existsSync12(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
8095
8128
  const verdict = classifyWritePath(filePath);
8096
8129
  if (verdict.sensitive && subAgentGuard.active) {
8097
8130
  throw new ToolError(
@@ -8257,8 +8290,8 @@ function truncatePreview(str, maxLen = 80) {
8257
8290
  }
8258
8291
 
8259
8292
  // src/tools/builtin/list-dir.ts
8260
- import { readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync12 } from "fs";
8261
- import { join as join7, basename as basename3 } from "path";
8293
+ import { readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync13 } from "fs";
8294
+ import { join as join7, basename as basename4 } from "path";
8262
8295
  var listDirTool = {
8263
8296
  definition: {
8264
8297
  name: "list_dir",
@@ -8280,8 +8313,8 @@ var listDirTool = {
8280
8313
  async execute(args) {
8281
8314
  const dirPath = String(args["path"] ?? process.cwd());
8282
8315
  const recursive = Boolean(args["recursive"] ?? false);
8283
- if (!existsSync12(dirPath)) {
8284
- const targetName = basename3(dirPath).toLowerCase();
8316
+ if (!existsSync13(dirPath)) {
8317
+ const targetName = basename4(dirPath).toLowerCase();
8285
8318
  const cwd = process.cwd();
8286
8319
  const suggestions = [];
8287
8320
  try {
@@ -8358,7 +8391,7 @@ function formatSize(bytes) {
8358
8391
  }
8359
8392
 
8360
8393
  // src/tools/builtin/grep-files.ts
8361
- import { readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync5, existsSync as existsSync13 } from "fs";
8394
+ import { readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync5, existsSync as existsSync14 } from "fs";
8362
8395
  import { readFile } from "fs/promises";
8363
8396
  import { join as join8, relative } from "path";
8364
8397
  var grepFilesTool = {
@@ -8411,7 +8444,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
8411
8444
  const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
8412
8445
  const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
8413
8446
  if (!pattern) throw new ToolError("grep_files", "pattern is required");
8414
- if (!existsSync13(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
8447
+ if (!existsSync14(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
8415
8448
  const MAX_PATTERN_LENGTH = 1e3;
8416
8449
  if (pattern.length > MAX_PATTERN_LENGTH) {
8417
8450
  throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
@@ -8595,8 +8628,8 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
8595
8628
  }
8596
8629
 
8597
8630
  // src/tools/builtin/glob-files.ts
8598
- import { readdirSync as readdirSync6, statSync as statSync6, existsSync as existsSync14 } from "fs";
8599
- import { join as join9, relative as relative2, basename as basename4 } from "path";
8631
+ import { readdirSync as readdirSync6, statSync as statSync6, existsSync as existsSync15 } from "fs";
8632
+ import { join as join9, relative as relative2, basename as basename5 } from "path";
8600
8633
  var globFilesTool = {
8601
8634
  definition: {
8602
8635
  name: "glob_files",
@@ -8629,7 +8662,7 @@ Results sorted by most recent modification time. Automatically skips node_module
8629
8662
  const rootPath = String(args["path"] ?? process.cwd());
8630
8663
  const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
8631
8664
  if (!pattern) throw new ToolError("glob_files", "pattern is required");
8632
- if (!existsSync14(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
8665
+ if (!existsSync15(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
8633
8666
  const regex = globToRegex(pattern);
8634
8667
  const matches = [];
8635
8668
  collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
@@ -8712,7 +8745,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
8712
8745
  collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
8713
8746
  } else if (entry.isFile()) {
8714
8747
  const relPath = relative2(rootPath, fullPath).replace(/\\/g, "/");
8715
- if (regex.test(relPath) || regex.test(basename4(relPath))) {
8748
+ if (regex.test(relPath) || regex.test(basename5(relPath))) {
8716
8749
  try {
8717
8750
  const stat = statSync6(fullPath);
8718
8751
  results.push({ relPath, absPath: fullPath, mtime: stat.mtimeMs });
@@ -9447,7 +9480,7 @@ ${preamble}`;
9447
9480
 
9448
9481
  // src/tools/builtin/save-last-response.ts
9449
9482
  import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync4, rmdirSync as rmdirSync2 } from "fs";
9450
- import { dirname as dirname5 } from "path";
9483
+ import { dirname as dirname6 } from "path";
9451
9484
  var lastResponseStore = { content: "" };
9452
9485
  function cleanupRejectedTeeFile(filePath) {
9453
9486
  try {
@@ -9455,7 +9488,7 @@ function cleanupRejectedTeeFile(filePath) {
9455
9488
  } catch {
9456
9489
  }
9457
9490
  try {
9458
- rmdirSync2(dirname5(filePath));
9491
+ rmdirSync2(dirname6(filePath));
9459
9492
  } catch {
9460
9493
  }
9461
9494
  }
@@ -9496,7 +9529,7 @@ Any of these triggers means use save_last_response, NOT write_file:
9496
9529
  throw new ToolError("save_last_response", "No content to save: AI has not produced any response yet, or the last response was empty.");
9497
9530
  }
9498
9531
  undoStack.push(filePath, `save_last_response: ${filePath}`);
9499
- mkdirSync7(dirname5(filePath), { recursive: true });
9532
+ mkdirSync7(dirname6(filePath), { recursive: true });
9500
9533
  atomicWriteFileSync(filePath, content);
9501
9534
  const lines = content.split("\n").length;
9502
9535
  return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
@@ -9508,13 +9541,13 @@ import { join as join12 } from "path";
9508
9541
  import { homedir as homedir5 } from "os";
9509
9542
 
9510
9543
  // src/memory/persistent-memory.ts
9511
- import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync12 } from "fs";
9544
+ import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync12 } from "fs";
9512
9545
  import { randomUUID, createHash as createHash2 } from "crypto";
9513
- import { dirname as dirname6, join as join11, resolve as resolve6 } from "path";
9546
+ import { dirname as dirname7, join as join11, resolve as resolve6 } from "path";
9514
9547
 
9515
9548
  // src/tools/git-context.ts
9516
9549
  import { execSync as execSync2 } from "child_process";
9517
- import { existsSync as existsSync15 } from "fs";
9550
+ import { existsSync as existsSync16 } from "fs";
9518
9551
  import { join as join10 } from "path";
9519
9552
  function runGit(cmd, cwd) {
9520
9553
  try {
@@ -9532,7 +9565,7 @@ function getGitRoot(cwd = process.cwd()) {
9532
9565
  return runGit("rev-parse --show-toplevel", cwd);
9533
9566
  }
9534
9567
  function getGitContext(cwd = process.cwd()) {
9535
- if (!existsSync15(join10(cwd, ".git"))) {
9568
+ if (!existsSync16(join10(cwd, ".git"))) {
9536
9569
  const result = runGit("rev-parse --git-dir", cwd);
9537
9570
  if (!result) return null;
9538
9571
  }
@@ -9643,7 +9676,7 @@ function normalizeEntry(value) {
9643
9676
  }
9644
9677
  function legacyEntriesFromMarkdown(configDir) {
9645
9678
  const file = memoryMarkdownPath(configDir);
9646
- if (!existsSync16(file)) return [];
9679
+ if (!existsSync17(file)) return [];
9647
9680
  const content = readFileSync12(file, "utf-8").trim();
9648
9681
  if (!content) return [];
9649
9682
  const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
@@ -9660,7 +9693,7 @@ ${chunk}`).digest("hex").slice(0, 12);
9660
9693
  }
9661
9694
  function loadMemoryEntries(configDir) {
9662
9695
  const file = memoryStorePath(configDir);
9663
- if (!existsSync16(file)) return legacyEntriesFromMarkdown(configDir);
9696
+ if (!existsSync17(file)) return legacyEntriesFromMarkdown(configDir);
9664
9697
  const text = readFileSync12(file, "utf-8").trim();
9665
9698
  if (!text) return [];
9666
9699
  const entries = [];
@@ -10108,13 +10141,13 @@ function formatResults2(query, data, _requested) {
10108
10141
  }
10109
10142
 
10110
10143
  // src/agents/agent-config.ts
10111
- import { existsSync as existsSync18, readdirSync as readdirSync8, readFileSync as readFileSync14 } from "fs";
10144
+ import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync14 } from "fs";
10112
10145
  import { join as join14 } from "path";
10113
10146
  import { homedir as homedir6 } from "os";
10114
10147
 
10115
10148
  // src/plugins/plugin-manager.ts
10116
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync13, cpSync, rmSync, statSync as statSync7 } from "fs";
10117
- import { basename as basename5, dirname as dirname7, join as join13, resolve as resolve7 } from "path";
10149
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync13, realpathSync as realpathSync2, cpSync, rmSync, statSync as statSync7 } from "fs";
10150
+ import { basename as basename6, dirname as dirname8, join as join13, relative as relative3, resolve as resolve7, sep as sep3 } from "path";
10118
10151
  import { createHash as createHash3 } from "crypto";
10119
10152
  import { z as z2 } from "zod";
10120
10153
  var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
@@ -10184,30 +10217,127 @@ function statePath(configDir) {
10184
10217
  }
10185
10218
  function loadState(configDir) {
10186
10219
  const file = statePath(configDir);
10187
- if (!existsSync17(file)) return { version: 1, plugins: [] };
10220
+ if (!existsSync18(file)) return { version: 2, plugins: [] };
10188
10221
  try {
10189
10222
  const parsed = JSON.parse(readFileSync13(file, "utf-8"));
10190
- return { version: 1, plugins: Array.isArray(parsed.plugins) ? parsed.plugins : [] };
10223
+ const rawPlugins = Array.isArray(parsed.plugins) ? parsed.plugins : [];
10224
+ return { version: 2, plugins: rawPlugins.map((p) => migrateEntry(p)) };
10191
10225
  } catch {
10192
- return { version: 1, plugins: [] };
10226
+ return { version: 2, plugins: [] };
10193
10227
  }
10194
10228
  }
10229
+ function migrateEntry(entry) {
10230
+ const name = String(entry.name ?? "");
10231
+ const contentHash = typeof entry.contentHash === "string" ? entry.contentHash : void 0;
10232
+ const legacyHash = typeof entry.hash === "string" ? entry.hash : void 0;
10233
+ const trusted = contentHash ? entry.trusted === true : false;
10234
+ return {
10235
+ name,
10236
+ enabled: entry.enabled === true,
10237
+ trusted,
10238
+ contentHash: contentHash ?? legacyHash,
10239
+ installedAt: String(entry.installedAt ?? (/* @__PURE__ */ new Date(0)).toISOString()),
10240
+ trustedAt: trusted && entry.trustedAt ? String(entry.trustedAt) : void 0,
10241
+ enabledAt: entry.enabledAt ? String(entry.enabledAt) : void 0
10242
+ };
10243
+ }
10195
10244
  function saveState(configDir, state2) {
10196
10245
  mkdirSync9(pluginRoot(configDir), { recursive: true });
10197
10246
  atomicWriteFileSync(statePath(configDir), JSON.stringify(state2, null, 2));
10198
10247
  }
10199
- function hashPluginManifest(manifestPath) {
10200
- return createHash3("sha256").update(readFileSync13(manifestPath)).digest("hex");
10248
+ function normalizeForHash(buf) {
10249
+ const str = buf.toString("utf-8").replaceAll("\r\n", "\n");
10250
+ return Buffer.from(str, "utf-8");
10251
+ }
10252
+ function extractCommandPaths(command, pluginDir) {
10253
+ const resolvedDir = resolve7(pluginDir);
10254
+ const refs = [];
10255
+ for (const m of command.matchAll(/\$PLUGIN_DIR[\\/]([^"'\s]+)/g)) {
10256
+ refs.push(m[1]);
10257
+ }
10258
+ for (const m of command.matchAll(/\$\{pluginDir\}[\\/]([^"'\s}]+)/g)) {
10259
+ refs.push(m[1]);
10260
+ }
10261
+ for (const m of command.matchAll(/(?<=["'\s])(\.?[.\\/][^"'\s]+)/g)) {
10262
+ refs.push(m[0]);
10263
+ }
10264
+ const seen = /* @__PURE__ */ new Set();
10265
+ const valid = [];
10266
+ for (const ref of refs) {
10267
+ if (seen.has(ref)) continue;
10268
+ seen.add(ref);
10269
+ try {
10270
+ const abs = resolve7(resolvedDir, ref);
10271
+ if (!abs.startsWith(resolvedDir + sep3)) continue;
10272
+ if (existsSync18(abs)) {
10273
+ const real = realpathSync2(abs);
10274
+ if (!real.startsWith(resolvedDir + sep3)) continue;
10275
+ valid.push(relative3(resolvedDir, real));
10276
+ }
10277
+ } catch {
10278
+ }
10279
+ }
10280
+ return valid;
10281
+ }
10282
+ function hashPluginContent(pluginDir, manifest) {
10283
+ const resolvedDir = resolve7(pluginDir);
10284
+ const assetRels = /* @__PURE__ */ new Set(
10285
+ [...manifest.skills, ...manifest.commands, ...manifest.agents]
10286
+ );
10287
+ for (const raw of Object.values(manifest.hooks?.events ?? {})) {
10288
+ const items = Array.isArray(raw) ? raw : [raw];
10289
+ for (const item of items) {
10290
+ const cmd = typeof item === "string" ? item : item.command;
10291
+ if (cmd) {
10292
+ for (const p of extractCommandPaths(cmd, pluginDir)) assetRels.add(p);
10293
+ }
10294
+ }
10295
+ }
10296
+ for (const server of Object.values(manifest.mcpServers ?? {})) {
10297
+ for (const p of extractCommandPaths(server.command, pluginDir)) assetRels.add(p);
10298
+ for (const arg of server.args ?? []) {
10299
+ for (const p of extractCommandPaths(arg, pluginDir)) assetRels.add(p);
10300
+ }
10301
+ }
10302
+ const sorted = [...assetRels].sort();
10303
+ const hasher = createHash3("sha256");
10304
+ const manifestPath = join13(resolvedDir, MANIFEST_RELATIVE);
10305
+ hasher.update(normalizeForHash(readFileSync13(manifestPath)));
10306
+ for (const rel of sorted) {
10307
+ const abs = resolve7(resolvedDir, rel);
10308
+ if (!abs.startsWith(resolvedDir + sep3)) {
10309
+ throw new Error(`plugin asset escapes plugin dir: ${rel}`);
10310
+ }
10311
+ let realPath;
10312
+ try {
10313
+ realPath = realpathSync2(abs);
10314
+ } catch {
10315
+ continue;
10316
+ }
10317
+ try {
10318
+ if (statSync7(realPath).isDirectory()) continue;
10319
+ } catch {
10320
+ continue;
10321
+ }
10322
+ if (!realPath.startsWith(resolvedDir + sep3)) {
10323
+ throw new Error(`plugin asset symlink escapes plugin dir: ${rel} \u2192 ${realPath}`);
10324
+ }
10325
+ hasher.update(normalizeForHash(readFileSync13(abs)));
10326
+ }
10327
+ return hasher.digest("hex");
10328
+ }
10329
+ function effectiveHash(entry) {
10330
+ return entry.contentHash ?? entry.hash ?? "";
10201
10331
  }
10202
10332
  function readPluginManifest(manifestPath) {
10203
10333
  return PluginManifestSchema.parse(JSON.parse(readFileSync13(manifestPath, "utf-8")));
10204
10334
  }
10205
10335
  function findPluginSourceDir(inputPath) {
10206
10336
  const abs = resolve7(inputPath);
10207
- const direct = statSync7(abs).isDirectory() ? abs : dirname7(abs);
10208
- if (existsSync17(pluginManifestPath(direct))) return direct;
10337
+ const direct = statSync7(abs).isDirectory() ? abs : dirname8(abs);
10338
+ if (existsSync18(pluginManifestPath(direct))) return direct;
10209
10339
  const nested = join13(direct, ".aicli-plugin");
10210
- if (existsSync17(join13(nested, "plugin.json"))) return direct;
10340
+ if (existsSync18(join13(nested, "plugin.json"))) return direct;
10211
10341
  throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
10212
10342
  }
10213
10343
  function installPlugin(configDir, inputPath) {
@@ -10215,22 +10345,22 @@ function installPlugin(configDir, inputPath) {
10215
10345
  const manifest = readPluginManifest(pluginManifestPath(sourceDir));
10216
10346
  const targetDir = join13(pluginRoot(configDir), manifest.name);
10217
10347
  mkdirSync9(pluginRoot(configDir), { recursive: true });
10218
- if (existsSync17(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10348
+ if (existsSync18(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10219
10349
  cpSync(sourceDir, targetDir, { recursive: true });
10220
10350
  const manifestPath = pluginManifestPath(targetDir);
10221
- const hash = hashPluginManifest(manifestPath);
10351
+ const contentHash = hashPluginContent(targetDir, manifest);
10222
10352
  const state2 = loadState(configDir);
10223
10353
  const previous = state2.plugins.find((p) => p.name === manifest.name);
10224
10354
  const next = {
10225
10355
  name: manifest.name,
10226
10356
  enabled: previous?.enabled ?? false,
10227
10357
  trusted: false,
10228
- hash,
10358
+ contentHash,
10229
10359
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
10230
10360
  };
10231
10361
  state2.plugins = [...state2.plugins.filter((p) => p.name !== manifest.name), next];
10232
10362
  saveState(configDir, state2);
10233
- return { name: manifest.name, dir: targetDir, manifestPath, manifest, hash, enabled: next.enabled, trusted: false, valid: true };
10363
+ return { name: manifest.name, dir: targetDir, manifestPath, manifest, hash: contentHash, enabled: next.enabled, trusted: false, valid: true };
10234
10364
  }
10235
10365
  function setPluginEnabled(configDir, name, enabled) {
10236
10366
  const plugin = getInstalledPlugin(configDir, name);
@@ -10240,10 +10370,10 @@ function setPluginEnabled(configDir, name, enabled) {
10240
10370
  name: plugin.name,
10241
10371
  enabled: false,
10242
10372
  trusted: false,
10243
- hash: plugin.hash,
10373
+ contentHash: plugin.hash,
10244
10374
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
10245
10375
  };
10246
- const next = { ...current, enabled, hash: plugin.hash, enabledAt: enabled ? (/* @__PURE__ */ new Date()).toISOString() : current.enabledAt };
10376
+ const next = { ...current, enabled, contentHash: plugin.hash, enabledAt: enabled ? (/* @__PURE__ */ new Date()).toISOString() : current.enabledAt };
10247
10377
  state2.plugins = [...state2.plugins.filter((p) => p.name !== plugin.name), next];
10248
10378
  saveState(configDir, state2);
10249
10379
  return next;
@@ -10256,10 +10386,10 @@ function trustPlugin(configDir, name) {
10256
10386
  name: plugin.name,
10257
10387
  enabled: false,
10258
10388
  trusted: false,
10259
- hash: plugin.hash,
10389
+ contentHash: plugin.hash,
10260
10390
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
10261
10391
  };
10262
- const next = { ...current, trusted: true, hash: plugin.hash, trustedAt: (/* @__PURE__ */ new Date()).toISOString() };
10392
+ const next = { ...current, trusted: true, contentHash: plugin.hash, trustedAt: (/* @__PURE__ */ new Date()).toISOString() };
10263
10393
  state2.plugins = [...state2.plugins.filter((p) => p.name !== plugin.name), next];
10264
10394
  saveState(configDir, state2);
10265
10395
  return next;
@@ -10273,7 +10403,7 @@ function getInstalledPlugin(configDir, name) {
10273
10403
  }
10274
10404
  function listInstalledPlugins(configDir) {
10275
10405
  const root = pluginRoot(configDir);
10276
- if (!existsSync17(root)) return [];
10406
+ if (!existsSync18(root)) return [];
10277
10407
  const state2 = loadState(configDir);
10278
10408
  const out = [];
10279
10409
  for (const entry of readdirSync7(root)) {
@@ -10284,18 +10414,18 @@ function listInstalledPlugins(configDir) {
10284
10414
  continue;
10285
10415
  }
10286
10416
  const manifestPath = pluginManifestPath(dir);
10287
- if (!existsSync17(manifestPath)) continue;
10417
+ if (!existsSync18(manifestPath)) continue;
10288
10418
  try {
10289
10419
  const manifest = readPluginManifest(manifestPath);
10290
- const hash = hashPluginManifest(manifestPath);
10420
+ const contentHash = hashPluginContent(dir, manifest);
10291
10421
  const st = state2.plugins.find((p) => p.name === manifest.name);
10292
- const trusted = st?.trusted === true && st.hash === hash;
10422
+ const trusted = st?.trusted === true && effectiveHash(st) === contentHash;
10293
10423
  out.push({
10294
10424
  name: manifest.name,
10295
10425
  dir,
10296
10426
  manifestPath,
10297
10427
  manifest,
10298
- hash,
10428
+ hash: contentHash,
10299
10429
  enabled: st?.enabled === true,
10300
10430
  trusted,
10301
10431
  valid: true
@@ -10324,7 +10454,7 @@ function uniqueDirs(plugin, rels) {
10324
10454
  for (const rel of rels) {
10325
10455
  const abs = resolve7(plugin.dir, rel);
10326
10456
  if (!abs.startsWith(resolve7(plugin.dir))) continue;
10327
- if (existsSync17(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname7(abs));
10457
+ if (existsSync18(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname8(abs));
10328
10458
  }
10329
10459
  return [...dirs];
10330
10460
  }
@@ -10487,7 +10617,7 @@ function parseAgentConfig(raw, source, path3) {
10487
10617
  return cfg;
10488
10618
  }
10489
10619
  function loadAgentDir(dir, source) {
10490
- if (!existsSync18(dir)) return [];
10620
+ if (!existsSync19(dir)) return [];
10491
10621
  const out = [];
10492
10622
  for (const file of readdirSync8(dir)) {
10493
10623
  if (!file.endsWith(".json")) continue;
@@ -11219,13 +11349,13 @@ var taskStopTool = {
11219
11349
 
11220
11350
  // src/tools/builtin/git-tools.ts
11221
11351
  import { execFileSync as execFileSync2 } from "child_process";
11222
- import { existsSync as existsSync19 } from "fs";
11352
+ import { existsSync as existsSync20 } from "fs";
11223
11353
  import { join as join15 } from "path";
11224
11354
  function assertGitRepo(cwd) {
11225
11355
  let dir = cwd;
11226
11356
  const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
11227
11357
  while (dir && dir !== root) {
11228
- if (existsSync19(join15(dir, ".git"))) return;
11358
+ if (existsSync20(join15(dir, ".git"))) return;
11229
11359
  const parent = join15(dir, "..");
11230
11360
  if (parent === dir) break;
11231
11361
  dir = parent;
@@ -11491,7 +11621,7 @@ ${commitOutput.trim()}`;
11491
11621
  };
11492
11622
 
11493
11623
  // src/tools/builtin/notebook-edit.ts
11494
- import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
11624
+ import { readFileSync as readFileSync15, existsSync as existsSync21 } from "fs";
11495
11625
  import { writeFile } from "fs/promises";
11496
11626
  import { resolve as resolve8, extname as extname2 } from "path";
11497
11627
  var notebookEditTool = {
@@ -11546,7 +11676,7 @@ var notebookEditTool = {
11546
11676
  if (extname2(absPath).toLowerCase() !== ".ipynb") {
11547
11677
  throw new ToolError("notebook_edit", "path must point to a .ipynb file");
11548
11678
  }
11549
- if (!existsSync20(absPath)) {
11679
+ if (!existsSync21(absPath)) {
11550
11680
  throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
11551
11681
  }
11552
11682
  const raw = readFileSync15(absPath, "utf-8");
@@ -11966,7 +12096,7 @@ function estimateToolDefinitionTokens(def) {
11966
12096
 
11967
12097
  // src/tools/registry.ts
11968
12098
  import { pathToFileURL } from "url";
11969
- import { existsSync as existsSync21, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
12099
+ import { existsSync as existsSync22, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
11970
12100
  import { join as join16 } from "path";
11971
12101
  var ToolRegistry = class {
11972
12102
  tools = /* @__PURE__ */ new Map();
@@ -12128,7 +12258,7 @@ var ToolRegistry = class {
12128
12258
  * Returns the number of successfully loaded plugins.
12129
12259
  */
12130
12260
  async loadPlugins(pluginsDir, allowPlugins = false) {
12131
- if (!existsSync21(pluginsDir)) {
12261
+ if (!existsSync22(pluginsDir)) {
12132
12262
  try {
12133
12263
  mkdirSync10(pluginsDir, { recursive: true });
12134
12264
  } catch {
@@ -12705,12 +12835,12 @@ var McpManager = class {
12705
12835
  };
12706
12836
 
12707
12837
  // src/skills/manager.ts
12708
- import { existsSync as existsSync22, readdirSync as readdirSync10, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
12838
+ import { existsSync as existsSync23, readdirSync as readdirSync10, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
12709
12839
  import { join as join17 } from "path";
12710
12840
 
12711
12841
  // src/skills/types.ts
12712
12842
  import { readFileSync as readFileSync16 } from "fs";
12713
- import { basename as basename6 } from "path";
12843
+ import { basename as basename7 } from "path";
12714
12844
  function parseSimpleYaml(yaml) {
12715
12845
  const result = {};
12716
12846
  for (const line of yaml.split("\n")) {
@@ -12739,7 +12869,7 @@ function parseSkillFile(filePath) {
12739
12869
  if (!frontmatterMatch) {
12740
12870
  return {
12741
12871
  meta: {
12742
- name: basename6(filePath, ".md"),
12872
+ name: basename7(filePath, ".md"),
12743
12873
  description: ""
12744
12874
  },
12745
12875
  content: raw.trim(),
@@ -12750,7 +12880,7 @@ function parseSkillFile(filePath) {
12750
12880
  const parsed = parseSimpleYaml(yaml);
12751
12881
  return {
12752
12882
  meta: {
12753
- name: parsed["name"] ?? basename6(filePath, ".md"),
12883
+ name: parsed["name"] ?? basename7(filePath, ".md"),
12754
12884
  description: parsed["description"] ?? "",
12755
12885
  tools: parsed["tools"] ? parseYamlArray(parsed["tools"]) : void 0
12756
12886
  },
@@ -12779,7 +12909,7 @@ var SkillManager = class {
12779
12909
  return this.skills.size;
12780
12910
  }
12781
12911
  loadSkillDir(dir, createIfMissing) {
12782
- if (!existsSync22(dir)) {
12912
+ if (!existsSync23(dir)) {
12783
12913
  if (createIfMissing) {
12784
12914
  try {
12785
12915
  mkdirSync11(dir, { recursive: true });
@@ -12803,7 +12933,7 @@ var SkillManager = class {
12803
12933
  try {
12804
12934
  if (statSync8(fullPath).isDirectory()) {
12805
12935
  const skillMd = join17(fullPath, "SKILL.md");
12806
- if (existsSync22(skillMd)) filePath = skillMd;
12936
+ if (existsSync23(skillMd)) filePath = skillMd;
12807
12937
  else continue;
12808
12938
  } else {
12809
12939
  continue;
@@ -12862,7 +12992,7 @@ var SkillManager = class {
12862
12992
  // src/web/tool-executor-web.ts
12863
12993
  import { randomUUID as randomUUID3 } from "crypto";
12864
12994
  import { tmpdir as tmpdir2 } from "os";
12865
- import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
12995
+ import { existsSync as existsSync24, readFileSync as readFileSync17 } from "fs";
12866
12996
  var ToolExecutorWeb = class _ToolExecutorWeb {
12867
12997
  constructor(registry, ws) {
12868
12998
  this.registry = registry;
@@ -13001,7 +13131,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13001
13131
  if (call.name === "write_file") {
13002
13132
  const filePath = String(call.arguments["path"] ?? "");
13003
13133
  const newContent = String(call.arguments["content"] ?? "");
13004
- if (filePath && existsSync23(filePath)) {
13134
+ if (filePath && existsSync24(filePath)) {
13005
13135
  try {
13006
13136
  const old = readFileSync17(filePath, "utf-8");
13007
13137
  return renderDiff(old, newContent, { filePath });
@@ -13159,12 +13289,13 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13159
13289
  return { callId: call.id, content: message, isError: true };
13160
13290
  }
13161
13291
  }
13162
- if (networkPermission?.action === "confirm" && dangerLevel === "safe") {
13292
+ const requiresHardConfirmation = permission.action === "confirm" && permission.hardBoundary === true;
13293
+ if ((networkPermission?.action === "confirm" || requiresHardConfirmation) && dangerLevel === "safe") {
13163
13294
  this.sendToolCallStart(call);
13164
13295
  toolCallStarted = true;
13165
13296
  const confirmed = await this.confirm(call, "write");
13166
13297
  if (!confirmed) {
13167
- const rejectionMsg = `[User cancelled] The user declined network access for ${call.name}. Do not retry without asking.`;
13298
+ const rejectionMsg = `[User cancelled] The user declined permission for ${call.name}. Do not retry without asking.`;
13168
13299
  this.sendToolCallResult(call, rejectionMsg, true);
13169
13300
  return { callId: call.id, content: rejectionMsg, isError: true };
13170
13301
  }
@@ -13181,7 +13312,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13181
13312
  this.sendToolCallResult(call, rejectionMsg, true);
13182
13313
  return { callId: call.id, content: rejectionMsg, isError: true };
13183
13314
  }
13184
- if (classification.decision === "auto-approve") {
13315
+ if (classification.decision === "auto-approve" && !requiresHardConfirmation) {
13185
13316
  this.send({ type: "info", message: `\u26A1 Auto-approved (/auto: ${classification.ruleId})` });
13186
13317
  try {
13187
13318
  const rawContent = await runTool(tool, call.arguments, call.name);
@@ -13199,7 +13330,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13199
13330
  }
13200
13331
  }
13201
13332
  }
13202
- if (this.sessionAutoApprove && dangerLevel === "write") {
13333
+ if (this.sessionAutoApprove && dangerLevel === "write" && !requiresHardConfirmation) {
13203
13334
  try {
13204
13335
  const rawContent = await runTool(tool, call.arguments, call.name);
13205
13336
  const content = truncateOutput(rawContent, call.name);
@@ -13306,7 +13437,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13306
13437
  };
13307
13438
 
13308
13439
  // src/repl/dev-state.ts
13309
- import { existsSync as existsSync24, readFileSync as readFileSync18, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "fs";
13440
+ import { existsSync as existsSync25, readFileSync as readFileSync18, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "fs";
13310
13441
  import { join as join18 } from "path";
13311
13442
  import { homedir as homedir7 } from "os";
13312
13443
  function getDevStatePath() {
@@ -13314,7 +13445,7 @@ function getDevStatePath() {
13314
13445
  }
13315
13446
  function loadDevState() {
13316
13447
  const path3 = getDevStatePath();
13317
- if (!existsSync24(path3)) return null;
13448
+ if (!existsSync25(path3)) return null;
13318
13449
  const content = readFileSync18(path3, "utf-8").trim();
13319
13450
  return content || null;
13320
13451
  }
@@ -13572,8 +13703,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
13572
13703
  }
13573
13704
 
13574
13705
  // src/core/context-files.ts
13575
- import { existsSync as existsSync25, readFileSync as readFileSync19 } from "fs";
13576
- import { join as join19, relative as relative3, resolve as resolve9 } from "path";
13706
+ import { existsSync as existsSync26, readFileSync as readFileSync19 } from "fs";
13707
+ import { join as join19, relative as relative4, resolve as resolve9 } from "path";
13577
13708
  function uniqueNonEmpty(values) {
13578
13709
  const seen = /* @__PURE__ */ new Set();
13579
13710
  const out = [];
@@ -13589,14 +13720,14 @@ function buildContextFileCandidates(fallbackFilenames = []) {
13589
13720
  return uniqueNonEmpty([...CONTEXT_FILE_CANDIDATES, ...fallbackFilenames]);
13590
13721
  }
13591
13722
  function displayPath(filePath, cwd) {
13592
- const rel = relative3(cwd, filePath);
13723
+ const rel = relative4(cwd, filePath);
13593
13724
  if (rel && !rel.startsWith("..") && !rel.startsWith("/") && !rel.startsWith("\\")) {
13594
13725
  return rel;
13595
13726
  }
13596
13727
  return filePath;
13597
13728
  }
13598
13729
  function isInsideOrEqual(parent, child) {
13599
- const rel = relative3(resolve9(parent), resolve9(child));
13730
+ const rel = relative4(resolve9(parent), resolve9(child));
13600
13731
  return rel === "" || !!rel && !rel.startsWith("..") && !rel.startsWith("/") && !rel.startsWith("\\");
13601
13732
  }
13602
13733
  function readContextFile(level, filePath, cwd, maxBytes) {
@@ -13651,7 +13782,7 @@ function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
13651
13782
  const skipped = [];
13652
13783
  for (const candidate of candidates) {
13653
13784
  const filePath = join19(dir, candidate);
13654
- if (!existsSync25(filePath)) continue;
13785
+ if (!existsSync26(filePath)) continue;
13655
13786
  const result = readContextFile(level, filePath, cwd, maxBytes);
13656
13787
  if (result.skipped) skipped.push(result.skipped);
13657
13788
  if (result.layer) return { layer: result.layer, skipped };
@@ -13678,7 +13809,7 @@ function loadContextFiles(options) {
13678
13809
  });
13679
13810
  return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
13680
13811
  }
13681
- if (!existsSync25(filePath)) {
13812
+ if (!existsSync26(filePath)) {
13682
13813
  skipped.push({
13683
13814
  level: "single",
13684
13815
  filePath,
@@ -13729,11 +13860,11 @@ function loadContextFiles(options) {
13729
13860
  }
13730
13861
 
13731
13862
  // src/web/session-handler.ts
13732
- import { mkdirSync as mkdirSync16, createWriteStream as createWriteStream3 } from "fs";
13863
+ import { mkdirSync as mkdirSync15, createWriteStream as createWriteStream2 } from "fs";
13733
13864
  import { join as join26, dirname as dirname10 } from "path";
13734
13865
 
13735
13866
  // src/repl/commands/project-init.ts
13736
- import { existsSync as existsSync26, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
13867
+ import { existsSync as existsSync27, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
13737
13868
  import { join as join20 } from "path";
13738
13869
  var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
13739
13870
  "node_modules",
@@ -13817,7 +13948,7 @@ function scanProject(cwd) {
13817
13948
  configFiles: [],
13818
13949
  directoryStructure: ""
13819
13950
  };
13820
- const check = (file) => existsSync26(join20(cwd, file));
13951
+ const check = (file) => existsSync27(join20(cwd, file));
13821
13952
  const configCandidates = [
13822
13953
  "package.json",
13823
13954
  "tsconfig.json",
@@ -14619,8 +14750,8 @@ async function handlePlan(args, ctx) {
14619
14750
  }
14620
14751
 
14621
14752
  // src/web/commands/_shared.ts
14622
- import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync3, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync12, createWriteStream } from "fs";
14623
- import { join as join24, resolve as resolve10, dirname as dirname8 } from "path";
14753
+ import { existsSync as existsSync30, readFileSync as readFileSync22, writeFileSync as writeFileSync3, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync12, createWriteStream } from "fs";
14754
+ import { join as join24, resolve as resolve10, dirname as dirname9 } from "path";
14624
14755
 
14625
14756
  // src/cli/review-prompts.ts
14626
14757
  function buildReviewPrompt(diff, gitContextStr, detailed) {
@@ -14692,13 +14823,13 @@ function readGitDiff(options = {}) {
14692
14823
  }
14693
14824
 
14694
14825
  // src/diagnostics/doctor-report.ts
14695
- import { existsSync as existsSync28, statSync as statSync11 } from "fs";
14826
+ import { existsSync as existsSync29, statSync as statSync11 } from "fs";
14696
14827
  import { join as join23 } from "path";
14697
14828
  import { arch as arch2, platform as platform7, release as release2 } from "os";
14698
14829
 
14699
14830
  // src/diagnostics/crash-log.ts
14700
14831
  import {
14701
- existsSync as existsSync27,
14832
+ existsSync as existsSync28,
14702
14833
  mkdirSync as mkdirSync13,
14703
14834
  readdirSync as readdirSync12,
14704
14835
  readFileSync as readFileSync21,
@@ -14719,7 +14850,7 @@ function truncate(s, n) {
14719
14850
  }
14720
14851
  function listRecentCrashes(limit = 10, configDir) {
14721
14852
  const dir = getLogsDir(configDir);
14722
- if (!existsSync27(dir)) return [];
14853
+ if (!existsSync28(dir)) return [];
14723
14854
  const files = [];
14724
14855
  let names;
14725
14856
  try {
@@ -14752,7 +14883,7 @@ function listRecentCrashes(limit = 10, configDir) {
14752
14883
  }
14753
14884
  function getConfigDirUsage(configDir) {
14754
14885
  const base = configDir ?? join22(homedir8(), CONFIG_DIR_NAME);
14755
- if (!existsSync27(base)) return { totalBytes: 0, entries: [] };
14886
+ if (!existsSync28(base)) return { totalBytes: 0, entries: [] };
14756
14887
  const entries = [];
14757
14888
  let total = 0;
14758
14889
  let names;
@@ -14787,7 +14918,7 @@ function dirSize(path3) {
14787
14918
 
14788
14919
  // src/diagnostics/doctor-report.ts
14789
14920
  function checkFile(label, path3) {
14790
- const exists = existsSync28(path3);
14921
+ const exists = existsSync29(path3);
14791
14922
  let sizeBytes = null;
14792
14923
  if (exists) {
14793
14924
  try {
@@ -14885,7 +15016,7 @@ function buildDoctorReport(options) {
14885
15016
  mcp: {
14886
15017
  enabled: config.get("mcpEnabled"),
14887
15018
  configured: Object.keys(mcpServers).length,
14888
- projectConfigExists: existsSync28(join23(projectRoot, MCP_PROJECT_CONFIG_NAME)),
15019
+ projectConfigExists: existsSync29(join23(projectRoot, MCP_PROJECT_CONFIG_NAME)),
14889
15020
  statuses: options.mcpStatuses ?? []
14890
15021
  },
14891
15022
  recentCrashes: listRecentCrashes(5, configDir),
@@ -15755,7 +15886,7 @@ ${activated.meta.description || ""}` });
15755
15886
  async function handleCommands(_args, ctx) {
15756
15887
  const configDir = ctx.config.getConfigDir();
15757
15888
  const commandsDir = join24(configDir, CUSTOM_COMMANDS_DIR_NAME);
15758
- if (!existsSync29(commandsDir)) {
15889
+ if (!existsSync30(commandsDir)) {
15759
15890
  ctx.send({ type: "info", message: `No custom commands directory.
15760
15891
  Create: ${commandsDir}/ with .md files.` });
15761
15892
  return;
@@ -15855,7 +15986,7 @@ async function handleSecurityReview(args, ctx) {
15855
15986
  async function handleTest(args, ctx) {
15856
15987
  ctx.send({ type: "info", message: "\u{1F9EA} Running tests..." });
15857
15988
  try {
15858
- const { executeTests } = await import("./run-tests-QR2PYR2P.js");
15989
+ const { executeTests } = await import("./run-tests-DI6WEYHX.js");
15859
15990
  const argStr = args.join(" ").trim();
15860
15991
  let testArgs = {};
15861
15992
  if (argStr) {
@@ -15885,7 +16016,7 @@ async function handleDiff(args, ctx) {
15885
16016
  let modifiedFiles = 0;
15886
16017
  const diffLines2 = [];
15887
16018
  for (const [filePath, { earliest }] of fileMap) {
15888
- const currentContent = existsSync29(filePath) ? (() => {
16019
+ const currentContent = existsSync30(filePath) ? (() => {
15889
16020
  try {
15890
16021
  return readFileSync22(filePath, "utf-8");
15891
16022
  } catch {
@@ -15924,7 +16055,7 @@ async function handleInit(args, ctx) {
15924
16055
  const cwd = process.cwd();
15925
16056
  const targetPath = join24(cwd, "AICLI.md");
15926
16057
  const force = args.includes("--force");
15927
- if (existsSync29(targetPath) && !force) {
16058
+ if (existsSync30(targetPath) && !force) {
15928
16059
  ctx.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
15929
16060
  Use /init --force to overwrite.` });
15930
16061
  return;
@@ -16108,7 +16239,7 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
16108
16239
  return;
16109
16240
  }
16110
16241
  const dirPath = resolve10(sub);
16111
- if (!existsSync29(dirPath)) {
16242
+ if (!existsSync30(dirPath)) {
16112
16243
  ctx.send({ type: "error", message: `Directory not found: ${dirPath}` });
16113
16244
  return;
16114
16245
  }
@@ -17183,7 +17314,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
17183
17314
  this.send({ type: "response_done", content, usage });
17184
17315
  if (pendingTeeSave && isCleanDocumentBody(content)) {
17185
17316
  try {
17186
- mkdirSync16(dirname10(pendingTeeSave), { recursive: true });
17317
+ mkdirSync15(dirname10(pendingTeeSave), { recursive: true });
17187
17318
  const bodyToSave = stripOuterCodeFence(content);
17188
17319
  atomicWriteFileSync(pendingTeeSave, bodyToSave);
17189
17320
  undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
@@ -17359,8 +17490,8 @@ ${summaryContent}`,
17359
17490
  let isError = false;
17360
17491
  let summary;
17361
17492
  try {
17362
- mkdirSync16(dirname10(saveToFile), { recursive: true });
17363
- fileStream = createWriteStream3(saveToFile);
17493
+ mkdirSync15(dirname10(saveToFile), { recursive: true });
17494
+ fileStream = createWriteStream2(saveToFile);
17364
17495
  const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
17365
17496
  const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
17366
17497
  const chatRequest = {
@@ -17934,8 +18065,8 @@ async function setupProxy(configProxy) {
17934
18065
  }
17935
18066
 
17936
18067
  // src/web/auth.ts
17937
- import { existsSync as existsSync32, readFileSync as readFileSync25, writeFileSync as writeFileSync6, mkdirSync as mkdirSync17, readdirSync as readdirSync16, copyFileSync } from "fs";
17938
- import { join as join27 } from "path";
18068
+ import { existsSync as existsSync32, readFileSync as readFileSync24, writeFileSync as writeFileSync5, mkdirSync as mkdirSync16, readdirSync as readdirSync15, copyFileSync } from "fs";
18069
+ import { isAbsolute as isAbsolute3, join as join27, relative as relative5 } from "path";
17939
18070
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
17940
18071
  var USERS_FILE = "users.json";
17941
18072
  var TOKEN_EXPIRY_HOURS = 24;
@@ -17981,7 +18112,7 @@ var AuthManager = class {
17981
18112
  const passwordHash = this.hashPassword(password, salt);
17982
18113
  const dataDir = join27(USERS_DIR, username);
17983
18114
  const fullDataDir = join27(this.baseDir, dataDir);
17984
- mkdirSync17(join27(fullDataDir, "history"), { recursive: true });
18115
+ mkdirSync16(join27(fullDataDir, "history"), { recursive: true });
17985
18116
  const user = {
17986
18117
  username,
17987
18118
  passwordHash,
@@ -18135,23 +18266,23 @@ var AuthManager = class {
18135
18266
  const globalConfig = join27(this.baseDir, "config.json");
18136
18267
  if (existsSync32(globalConfig)) {
18137
18268
  try {
18138
- const content = readFileSync25(globalConfig, "utf-8");
18139
- writeFileSync6(join27(userDir, "config.json"), content, "utf-8");
18269
+ const content = readFileSync24(globalConfig, "utf-8");
18270
+ writeFileSync5(join27(userDir, "config.json"), content, "utf-8");
18140
18271
  } catch {
18141
18272
  }
18142
18273
  }
18143
18274
  const globalMemory = join27(this.baseDir, "memory.md");
18144
18275
  if (existsSync32(globalMemory)) {
18145
18276
  try {
18146
- const content = readFileSync25(globalMemory, "utf-8");
18147
- writeFileSync6(join27(userDir, "memory.md"), content, "utf-8");
18277
+ const content = readFileSync24(globalMemory, "utf-8");
18278
+ writeFileSync5(join27(userDir, "memory.md"), content, "utf-8");
18148
18279
  } catch {
18149
18280
  }
18150
18281
  }
18151
18282
  const globalHistory = join27(this.baseDir, "history");
18152
18283
  if (existsSync32(globalHistory)) {
18153
18284
  try {
18154
- const files = readdirSync16(globalHistory).filter((f) => f.endsWith(".json"));
18285
+ const files = readdirSync15(globalHistory).filter((f) => f.endsWith(".json"));
18155
18286
  const userHistory = join27(userDir, "history");
18156
18287
  for (const f of files) {
18157
18288
  try {
@@ -18168,8 +18299,17 @@ var AuthManager = class {
18168
18299
  loadOrCreate() {
18169
18300
  if (existsSync32(this.usersFile)) {
18170
18301
  try {
18171
- return JSON.parse(readFileSync25(this.usersFile, "utf-8"));
18172
- } catch {
18302
+ const parsed = JSON.parse(readFileSync24(this.usersFile, "utf-8"));
18303
+ if (!this.isValidUsersDB(parsed)) {
18304
+ throw new Error("database structure is invalid");
18305
+ }
18306
+ return parsed;
18307
+ } catch (err) {
18308
+ const detail = err instanceof Error ? err.message : String(err);
18309
+ throw new Error(
18310
+ `Authentication database is unreadable or corrupt: ${this.usersFile} (${detail}). Refusing to start without authentication; restore or remove the file explicitly.`,
18311
+ { cause: err }
18312
+ );
18173
18313
  }
18174
18314
  }
18175
18315
  const db = {
@@ -18180,11 +18320,32 @@ var AuthManager = class {
18180
18320
  this.saveDB(db);
18181
18321
  return db;
18182
18322
  }
18323
+ isValidUsersDB(value) {
18324
+ if (!value || typeof value !== "object") return false;
18325
+ const db = value;
18326
+ if (db.version !== 1 || typeof db.secret !== "string" || db.secret.length < 32 || !Array.isArray(db.users)) {
18327
+ return false;
18328
+ }
18329
+ const usernames = /* @__PURE__ */ new Set();
18330
+ for (const user of db.users) {
18331
+ if (!user || typeof user !== "object") return false;
18332
+ const record = user;
18333
+ if (typeof record.username !== "string" || !/^[a-z0-9_-]{2,32}$/.test(record.username) || usernames.has(record.username) || typeof record.passwordHash !== "string" || record.passwordHash.length === 0 || typeof record.salt !== "string" || record.salt.length === 0 || typeof record.createdAt !== "string" || typeof record.dataDir !== "string" || record.dataDir.length === 0 || isAbsolute3(record.dataDir)) {
18334
+ return false;
18335
+ }
18336
+ const dataDirFromBase = relative5(this.baseDir, join27(this.baseDir, record.dataDir));
18337
+ if (dataDirFromBase.startsWith("..") || isAbsolute3(dataDirFromBase)) return false;
18338
+ if (record.hashVersion !== void 0 && record.hashVersion !== 1 && record.hashVersion !== 2) return false;
18339
+ if (record.tokensRevokedBefore !== void 0 && !Number.isFinite(record.tokensRevokedBefore)) return false;
18340
+ usernames.add(record.username);
18341
+ }
18342
+ return true;
18343
+ }
18183
18344
  save() {
18184
18345
  this.saveDB(this.db);
18185
18346
  }
18186
18347
  saveDB(db) {
18187
- mkdirSync17(this.baseDir, { recursive: true });
18348
+ mkdirSync16(this.baseDir, { recursive: true });
18188
18349
  atomicWriteFileSync(this.usersFile, JSON.stringify(db, null, 2));
18189
18350
  }
18190
18351
  /** Legacy hash — kept only for migrating old users (v0.2.x) */
@@ -18440,9 +18601,9 @@ async function startWebServer(options = {}) {
18440
18601
  const prefix = req.query.prefix || "";
18441
18602
  const targetDir = join28(cwd, prefix);
18442
18603
  try {
18443
- const canonicalTarget = realpathSync(resolve13(targetDir));
18444
- const canonicalCwd = realpathSync(resolve13(cwd));
18445
- if (!canonicalTarget.startsWith(canonicalCwd + sep3) && canonicalTarget !== canonicalCwd) {
18604
+ const canonicalTarget = realpathSync3(resolve13(targetDir));
18605
+ const canonicalCwd = realpathSync3(resolve13(cwd));
18606
+ if (!canonicalTarget.startsWith(canonicalCwd + sep4) && canonicalTarget !== canonicalCwd) {
18446
18607
  res.json({ files: [] });
18447
18608
  return;
18448
18609
  }
@@ -18452,10 +18613,10 @@ async function startWebServer(options = {}) {
18452
18613
  }
18453
18614
  try {
18454
18615
  const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "dist-cjs", "release", "__pycache__", ".next", ".nuxt", "coverage", ".cache"]);
18455
- const entries = readdirSync17(targetDir, { withFileTypes: true });
18616
+ const entries = readdirSync16(targetDir, { withFileTypes: true });
18456
18617
  const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
18457
18618
  name: e.name,
18458
- path: relative4(cwd, join28(targetDir, e.name)).replace(/\\/g, "/"),
18619
+ path: relative6(cwd, join28(targetDir, e.name)).replace(/\\/g, "/"),
18459
18620
  isDir: e.isDirectory()
18460
18621
  }));
18461
18622
  res.json({ files });
@@ -18501,9 +18662,9 @@ async function startWebServer(options = {}) {
18501
18662
  return;
18502
18663
  }
18503
18664
  try {
18504
- const canonicalFile = realpathSync(filePath);
18505
- const canonicalDir = realpathSync(histDir);
18506
- if (!canonicalFile.startsWith(canonicalDir + sep3)) {
18665
+ const canonicalFile = realpathSync3(filePath);
18666
+ const canonicalDir = realpathSync3(histDir);
18667
+ if (!canonicalFile.startsWith(canonicalDir + sep4)) {
18507
18668
  res.status(404).json({ error: "Session not found" });
18508
18669
  return;
18509
18670
  }
@@ -18511,7 +18672,7 @@ async function startWebServer(options = {}) {
18511
18672
  res.status(404).json({ error: "Session not found" });
18512
18673
  return;
18513
18674
  }
18514
- const data = JSON.parse(readFileSync26(filePath, "utf-8"));
18675
+ const data = JSON.parse(readFileSync25(filePath, "utf-8"));
18515
18676
  res.json({ session: data });
18516
18677
  } catch (err) {
18517
18678
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
@@ -18526,9 +18687,9 @@ async function startWebServer(options = {}) {
18526
18687
  const cwd = process.cwd();
18527
18688
  const fullPath = resolve13(join28(cwd, filePath));
18528
18689
  try {
18529
- const canonicalFull = realpathSync(fullPath);
18530
- const canonicalCwd = realpathSync(resolve13(cwd));
18531
- if (!canonicalFull.startsWith(canonicalCwd + sep3) && canonicalFull !== canonicalCwd) {
18690
+ const canonicalFull = realpathSync3(fullPath);
18691
+ const canonicalCwd = realpathSync3(resolve13(cwd));
18692
+ if (!canonicalFull.startsWith(canonicalCwd + sep4) && canonicalFull !== canonicalCwd) {
18532
18693
  res.json({ error: "Access denied" });
18533
18694
  return;
18534
18695
  }
@@ -18537,12 +18698,12 @@ async function startWebServer(options = {}) {
18537
18698
  return;
18538
18699
  }
18539
18700
  try {
18540
- const stat = statSync15(fullPath);
18701
+ const stat = statSync14(fullPath);
18541
18702
  if (stat.size > 512 * 1024) {
18542
18703
  res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
18543
18704
  return;
18544
18705
  }
18545
- const content = readFileSync26(fullPath, "utf-8");
18706
+ const content = readFileSync25(fullPath, "utf-8");
18546
18707
  res.json({ content, size: stat.size });
18547
18708
  } catch {
18548
18709
  res.json({ error: "Cannot read file" });
@@ -18807,7 +18968,7 @@ function loadProjectMcpConfig() {
18807
18968
  const configPath = join28(projectRoot, MCP_PROJECT_CONFIG_NAME);
18808
18969
  if (!existsSync33(configPath)) return null;
18809
18970
  try {
18810
- const raw = JSON.parse(readFileSync26(configPath, "utf-8"));
18971
+ const raw = JSON.parse(readFileSync25(configPath, "utf-8"));
18811
18972
  return raw.mcpServers ?? raw;
18812
18973
  } catch {
18813
18974
  return null;