jinzd-ai-cli 0.4.234 → 0.4.235

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 (28) hide show
  1. package/dist/{auth-FSTAKSYF.js → auth-PE3Z5OHS.js} +1 -1
  2. package/dist/{batch-T3FRX4B6.js → batch-Y7MHXVR7.js} +2 -2
  3. package/dist/{chunk-5DTEEWYC.js → chunk-7D5EJBHO.js} +3 -3
  4. package/dist/{chunk-L5PNAQ6O.js → chunk-BFTTW5LS.js} +1 -1
  5. package/dist/{chunk-6MJMHHRK.js → chunk-CDOQNIVV.js} +3 -3
  6. package/dist/{chunk-5VPRZMZB.js → chunk-DHNYGJYB.js} +2 -2
  7. package/dist/{chunk-E5RLXMSU.js → chunk-FYJGVFXL.js} +85 -52
  8. package/dist/{chunk-3KTSJ2EL.js → chunk-GDXKFWFL.js} +1 -1
  9. package/dist/{chunk-II6RZRTF.js → chunk-GFDRC2JZ.js} +1 -1
  10. package/dist/{chunk-ETJ5X6V7.js → chunk-HHYMSEWS.js} +1 -1
  11. package/dist/{chunk-AFT6VFMI.js → chunk-Q5GVANMH.js} +1 -1
  12. package/dist/{chunk-JVKAL5Q3.js → chunk-RYQBBEMM.js} +33 -3
  13. package/dist/{chunk-TAXIG7HH.js → chunk-WXFQ6LYN.js} +1 -1
  14. package/dist/{ci-GJ53GPLA.js → ci-OAFNH3MA.js} +4 -4
  15. package/dist/{ci-format-AXE4QCI6.js → ci-format-4LSXJNND.js} +2 -2
  16. package/dist/{constants-DC4EXFSB.js → constants-ERIAS7GV.js} +1 -1
  17. package/dist/{doctor-cli-HKREND6Q.js → doctor-cli-VFSVWZU6.js} +4 -4
  18. package/dist/electron-server.js +194 -130
  19. package/dist/{hub-LTTKRGZC.js → hub-X4EUYRIN.js} +1 -1
  20. package/dist/index.js +19 -19
  21. package/dist/{pr-4VODJVRV.js → pr-2CZN5JLE.js} +4 -4
  22. package/dist/{run-tests-G5BNTMWQ.js → run-tests-DXRJDPFF.js} +2 -2
  23. package/dist/{run-tests-QR2PYR2P.js → run-tests-YSRET52H.js} +1 -1
  24. package/dist/{server-64WZVZZN.js → server-5ZAXMKRI.js} +14 -13
  25. package/dist/{server-EO6VQHKL.js → server-FGJGXHZD.js} +4 -4
  26. package/dist/{task-orchestrator-4INCZMLT.js → task-orchestrator-QMERCD77.js} +4 -4
  27. package/dist/{usage-T3HFMNNV.js → usage-633DUDNT.js} +2 -2
  28. 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-HHYMSEWS.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 dirname12, resolve as resolve13, relative as relative5, sep as sep3 } from "path";
74
+ import { existsSync as existsSync34, readFileSync as readFileSync26, readdirSync as readdirSync17, statSync as statSync15, realpathSync as realpathSync2 } 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, cpSync, rmSync, statSync as statSync7 } from "fs";
10150
+ import { basename as basename6, dirname as dirname8, join as join13, resolve as resolve7 } 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,7 +10217,7 @@ 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: 1, plugins: [] };
10188
10221
  try {
10189
10222
  const parsed = JSON.parse(readFileSync13(file, "utf-8"));
10190
10223
  return { version: 1, plugins: Array.isArray(parsed.plugins) ? parsed.plugins : [] };
@@ -10204,10 +10237,10 @@ function readPluginManifest(manifestPath) {
10204
10237
  }
10205
10238
  function findPluginSourceDir(inputPath) {
10206
10239
  const abs = resolve7(inputPath);
10207
- const direct = statSync7(abs).isDirectory() ? abs : dirname7(abs);
10208
- if (existsSync17(pluginManifestPath(direct))) return direct;
10240
+ const direct = statSync7(abs).isDirectory() ? abs : dirname8(abs);
10241
+ if (existsSync18(pluginManifestPath(direct))) return direct;
10209
10242
  const nested = join13(direct, ".aicli-plugin");
10210
- if (existsSync17(join13(nested, "plugin.json"))) return direct;
10243
+ if (existsSync18(join13(nested, "plugin.json"))) return direct;
10211
10244
  throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
10212
10245
  }
10213
10246
  function installPlugin(configDir, inputPath) {
@@ -10215,7 +10248,7 @@ function installPlugin(configDir, inputPath) {
10215
10248
  const manifest = readPluginManifest(pluginManifestPath(sourceDir));
10216
10249
  const targetDir = join13(pluginRoot(configDir), manifest.name);
10217
10250
  mkdirSync9(pluginRoot(configDir), { recursive: true });
10218
- if (existsSync17(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10251
+ if (existsSync18(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10219
10252
  cpSync(sourceDir, targetDir, { recursive: true });
10220
10253
  const manifestPath = pluginManifestPath(targetDir);
10221
10254
  const hash = hashPluginManifest(manifestPath);
@@ -10273,7 +10306,7 @@ function getInstalledPlugin(configDir, name) {
10273
10306
  }
10274
10307
  function listInstalledPlugins(configDir) {
10275
10308
  const root = pluginRoot(configDir);
10276
- if (!existsSync17(root)) return [];
10309
+ if (!existsSync18(root)) return [];
10277
10310
  const state2 = loadState(configDir);
10278
10311
  const out = [];
10279
10312
  for (const entry of readdirSync7(root)) {
@@ -10284,7 +10317,7 @@ function listInstalledPlugins(configDir) {
10284
10317
  continue;
10285
10318
  }
10286
10319
  const manifestPath = pluginManifestPath(dir);
10287
- if (!existsSync17(manifestPath)) continue;
10320
+ if (!existsSync18(manifestPath)) continue;
10288
10321
  try {
10289
10322
  const manifest = readPluginManifest(manifestPath);
10290
10323
  const hash = hashPluginManifest(manifestPath);
@@ -10324,7 +10357,7 @@ function uniqueDirs(plugin, rels) {
10324
10357
  for (const rel of rels) {
10325
10358
  const abs = resolve7(plugin.dir, rel);
10326
10359
  if (!abs.startsWith(resolve7(plugin.dir))) continue;
10327
- if (existsSync17(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname7(abs));
10360
+ if (existsSync18(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname8(abs));
10328
10361
  }
10329
10362
  return [...dirs];
10330
10363
  }
@@ -10487,7 +10520,7 @@ function parseAgentConfig(raw, source, path3) {
10487
10520
  return cfg;
10488
10521
  }
10489
10522
  function loadAgentDir(dir, source) {
10490
- if (!existsSync18(dir)) return [];
10523
+ if (!existsSync19(dir)) return [];
10491
10524
  const out = [];
10492
10525
  for (const file of readdirSync8(dir)) {
10493
10526
  if (!file.endsWith(".json")) continue;
@@ -11219,13 +11252,13 @@ var taskStopTool = {
11219
11252
 
11220
11253
  // src/tools/builtin/git-tools.ts
11221
11254
  import { execFileSync as execFileSync2 } from "child_process";
11222
- import { existsSync as existsSync19 } from "fs";
11255
+ import { existsSync as existsSync20 } from "fs";
11223
11256
  import { join as join15 } from "path";
11224
11257
  function assertGitRepo(cwd) {
11225
11258
  let dir = cwd;
11226
11259
  const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
11227
11260
  while (dir && dir !== root) {
11228
- if (existsSync19(join15(dir, ".git"))) return;
11261
+ if (existsSync20(join15(dir, ".git"))) return;
11229
11262
  const parent = join15(dir, "..");
11230
11263
  if (parent === dir) break;
11231
11264
  dir = parent;
@@ -11491,7 +11524,7 @@ ${commitOutput.trim()}`;
11491
11524
  };
11492
11525
 
11493
11526
  // src/tools/builtin/notebook-edit.ts
11494
- import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
11527
+ import { readFileSync as readFileSync15, existsSync as existsSync21 } from "fs";
11495
11528
  import { writeFile } from "fs/promises";
11496
11529
  import { resolve as resolve8, extname as extname2 } from "path";
11497
11530
  var notebookEditTool = {
@@ -11546,7 +11579,7 @@ var notebookEditTool = {
11546
11579
  if (extname2(absPath).toLowerCase() !== ".ipynb") {
11547
11580
  throw new ToolError("notebook_edit", "path must point to a .ipynb file");
11548
11581
  }
11549
- if (!existsSync20(absPath)) {
11582
+ if (!existsSync21(absPath)) {
11550
11583
  throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
11551
11584
  }
11552
11585
  const raw = readFileSync15(absPath, "utf-8");
@@ -11966,7 +11999,7 @@ function estimateToolDefinitionTokens(def) {
11966
11999
 
11967
12000
  // src/tools/registry.ts
11968
12001
  import { pathToFileURL } from "url";
11969
- import { existsSync as existsSync21, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
12002
+ import { existsSync as existsSync22, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
11970
12003
  import { join as join16 } from "path";
11971
12004
  var ToolRegistry = class {
11972
12005
  tools = /* @__PURE__ */ new Map();
@@ -12128,7 +12161,7 @@ var ToolRegistry = class {
12128
12161
  * Returns the number of successfully loaded plugins.
12129
12162
  */
12130
12163
  async loadPlugins(pluginsDir, allowPlugins = false) {
12131
- if (!existsSync21(pluginsDir)) {
12164
+ if (!existsSync22(pluginsDir)) {
12132
12165
  try {
12133
12166
  mkdirSync10(pluginsDir, { recursive: true });
12134
12167
  } catch {
@@ -12705,12 +12738,12 @@ var McpManager = class {
12705
12738
  };
12706
12739
 
12707
12740
  // src/skills/manager.ts
12708
- import { existsSync as existsSync22, readdirSync as readdirSync10, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
12741
+ import { existsSync as existsSync23, readdirSync as readdirSync10, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
12709
12742
  import { join as join17 } from "path";
12710
12743
 
12711
12744
  // src/skills/types.ts
12712
12745
  import { readFileSync as readFileSync16 } from "fs";
12713
- import { basename as basename6 } from "path";
12746
+ import { basename as basename7 } from "path";
12714
12747
  function parseSimpleYaml(yaml) {
12715
12748
  const result = {};
12716
12749
  for (const line of yaml.split("\n")) {
@@ -12739,7 +12772,7 @@ function parseSkillFile(filePath) {
12739
12772
  if (!frontmatterMatch) {
12740
12773
  return {
12741
12774
  meta: {
12742
- name: basename6(filePath, ".md"),
12775
+ name: basename7(filePath, ".md"),
12743
12776
  description: ""
12744
12777
  },
12745
12778
  content: raw.trim(),
@@ -12750,7 +12783,7 @@ function parseSkillFile(filePath) {
12750
12783
  const parsed = parseSimpleYaml(yaml);
12751
12784
  return {
12752
12785
  meta: {
12753
- name: parsed["name"] ?? basename6(filePath, ".md"),
12786
+ name: parsed["name"] ?? basename7(filePath, ".md"),
12754
12787
  description: parsed["description"] ?? "",
12755
12788
  tools: parsed["tools"] ? parseYamlArray(parsed["tools"]) : void 0
12756
12789
  },
@@ -12779,7 +12812,7 @@ var SkillManager = class {
12779
12812
  return this.skills.size;
12780
12813
  }
12781
12814
  loadSkillDir(dir, createIfMissing) {
12782
- if (!existsSync22(dir)) {
12815
+ if (!existsSync23(dir)) {
12783
12816
  if (createIfMissing) {
12784
12817
  try {
12785
12818
  mkdirSync11(dir, { recursive: true });
@@ -12803,7 +12836,7 @@ var SkillManager = class {
12803
12836
  try {
12804
12837
  if (statSync8(fullPath).isDirectory()) {
12805
12838
  const skillMd = join17(fullPath, "SKILL.md");
12806
- if (existsSync22(skillMd)) filePath = skillMd;
12839
+ if (existsSync23(skillMd)) filePath = skillMd;
12807
12840
  else continue;
12808
12841
  } else {
12809
12842
  continue;
@@ -12862,7 +12895,7 @@ var SkillManager = class {
12862
12895
  // src/web/tool-executor-web.ts
12863
12896
  import { randomUUID as randomUUID3 } from "crypto";
12864
12897
  import { tmpdir as tmpdir2 } from "os";
12865
- import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
12898
+ import { existsSync as existsSync24, readFileSync as readFileSync17 } from "fs";
12866
12899
  var ToolExecutorWeb = class _ToolExecutorWeb {
12867
12900
  constructor(registry, ws) {
12868
12901
  this.registry = registry;
@@ -13001,7 +13034,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13001
13034
  if (call.name === "write_file") {
13002
13035
  const filePath = String(call.arguments["path"] ?? "");
13003
13036
  const newContent = String(call.arguments["content"] ?? "");
13004
- if (filePath && existsSync23(filePath)) {
13037
+ if (filePath && existsSync24(filePath)) {
13005
13038
  try {
13006
13039
  const old = readFileSync17(filePath, "utf-8");
13007
13040
  return renderDiff(old, newContent, { filePath });
@@ -13159,12 +13192,13 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13159
13192
  return { callId: call.id, content: message, isError: true };
13160
13193
  }
13161
13194
  }
13162
- if (networkPermission?.action === "confirm" && dangerLevel === "safe") {
13195
+ const requiresHardConfirmation = permission.action === "confirm" && permission.hardBoundary === true;
13196
+ if ((networkPermission?.action === "confirm" || requiresHardConfirmation) && dangerLevel === "safe") {
13163
13197
  this.sendToolCallStart(call);
13164
13198
  toolCallStarted = true;
13165
13199
  const confirmed = await this.confirm(call, "write");
13166
13200
  if (!confirmed) {
13167
- const rejectionMsg = `[User cancelled] The user declined network access for ${call.name}. Do not retry without asking.`;
13201
+ const rejectionMsg = `[User cancelled] The user declined permission for ${call.name}. Do not retry without asking.`;
13168
13202
  this.sendToolCallResult(call, rejectionMsg, true);
13169
13203
  return { callId: call.id, content: rejectionMsg, isError: true };
13170
13204
  }
@@ -13181,7 +13215,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13181
13215
  this.sendToolCallResult(call, rejectionMsg, true);
13182
13216
  return { callId: call.id, content: rejectionMsg, isError: true };
13183
13217
  }
13184
- if (classification.decision === "auto-approve") {
13218
+ if (classification.decision === "auto-approve" && !requiresHardConfirmation) {
13185
13219
  this.send({ type: "info", message: `\u26A1 Auto-approved (/auto: ${classification.ruleId})` });
13186
13220
  try {
13187
13221
  const rawContent = await runTool(tool, call.arguments, call.name);
@@ -13199,7 +13233,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13199
13233
  }
13200
13234
  }
13201
13235
  }
13202
- if (this.sessionAutoApprove && dangerLevel === "write") {
13236
+ if (this.sessionAutoApprove && dangerLevel === "write" && !requiresHardConfirmation) {
13203
13237
  try {
13204
13238
  const rawContent = await runTool(tool, call.arguments, call.name);
13205
13239
  const content = truncateOutput(rawContent, call.name);
@@ -13306,7 +13340,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13306
13340
  };
13307
13341
 
13308
13342
  // src/repl/dev-state.ts
13309
- import { existsSync as existsSync24, readFileSync as readFileSync18, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "fs";
13343
+ import { existsSync as existsSync25, readFileSync as readFileSync18, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "fs";
13310
13344
  import { join as join18 } from "path";
13311
13345
  import { homedir as homedir7 } from "os";
13312
13346
  function getDevStatePath() {
@@ -13314,7 +13348,7 @@ function getDevStatePath() {
13314
13348
  }
13315
13349
  function loadDevState() {
13316
13350
  const path3 = getDevStatePath();
13317
- if (!existsSync24(path3)) return null;
13351
+ if (!existsSync25(path3)) return null;
13318
13352
  const content = readFileSync18(path3, "utf-8").trim();
13319
13353
  return content || null;
13320
13354
  }
@@ -13572,7 +13606,7 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
13572
13606
  }
13573
13607
 
13574
13608
  // src/core/context-files.ts
13575
- import { existsSync as existsSync25, readFileSync as readFileSync19 } from "fs";
13609
+ import { existsSync as existsSync26, readFileSync as readFileSync19 } from "fs";
13576
13610
  import { join as join19, relative as relative3, resolve as resolve9 } from "path";
13577
13611
  function uniqueNonEmpty(values) {
13578
13612
  const seen = /* @__PURE__ */ new Set();
@@ -13651,7 +13685,7 @@ function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
13651
13685
  const skipped = [];
13652
13686
  for (const candidate of candidates) {
13653
13687
  const filePath = join19(dir, candidate);
13654
- if (!existsSync25(filePath)) continue;
13688
+ if (!existsSync26(filePath)) continue;
13655
13689
  const result = readContextFile(level, filePath, cwd, maxBytes);
13656
13690
  if (result.skipped) skipped.push(result.skipped);
13657
13691
  if (result.layer) return { layer: result.layer, skipped };
@@ -13678,7 +13712,7 @@ function loadContextFiles(options) {
13678
13712
  });
13679
13713
  return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
13680
13714
  }
13681
- if (!existsSync25(filePath)) {
13715
+ if (!existsSync26(filePath)) {
13682
13716
  skipped.push({
13683
13717
  level: "single",
13684
13718
  filePath,
@@ -13730,10 +13764,10 @@ function loadContextFiles(options) {
13730
13764
 
13731
13765
  // src/web/session-handler.ts
13732
13766
  import { mkdirSync as mkdirSync16, createWriteStream as createWriteStream3 } from "fs";
13733
- import { join as join26, dirname as dirname10 } from "path";
13767
+ import { join as join26, dirname as dirname11 } from "path";
13734
13768
 
13735
13769
  // src/repl/commands/project-init.ts
13736
- import { existsSync as existsSync26, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
13770
+ import { existsSync as existsSync27, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
13737
13771
  import { join as join20 } from "path";
13738
13772
  var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
13739
13773
  "node_modules",
@@ -13817,7 +13851,7 @@ function scanProject(cwd) {
13817
13851
  configFiles: [],
13818
13852
  directoryStructure: ""
13819
13853
  };
13820
- const check = (file) => existsSync26(join20(cwd, file));
13854
+ const check = (file) => existsSync27(join20(cwd, file));
13821
13855
  const configCandidates = [
13822
13856
  "package.json",
13823
13857
  "tsconfig.json",
@@ -14619,8 +14653,8 @@ async function handlePlan(args, ctx) {
14619
14653
  }
14620
14654
 
14621
14655
  // 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";
14656
+ import { existsSync as existsSync30, readFileSync as readFileSync22, writeFileSync as writeFileSync3, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync12, createWriteStream } from "fs";
14657
+ import { join as join24, resolve as resolve10, dirname as dirname9 } from "path";
14624
14658
 
14625
14659
  // src/cli/review-prompts.ts
14626
14660
  function buildReviewPrompt(diff, gitContextStr, detailed) {
@@ -14692,13 +14726,13 @@ function readGitDiff(options = {}) {
14692
14726
  }
14693
14727
 
14694
14728
  // src/diagnostics/doctor-report.ts
14695
- import { existsSync as existsSync28, statSync as statSync11 } from "fs";
14729
+ import { existsSync as existsSync29, statSync as statSync11 } from "fs";
14696
14730
  import { join as join23 } from "path";
14697
14731
  import { arch as arch2, platform as platform7, release as release2 } from "os";
14698
14732
 
14699
14733
  // src/diagnostics/crash-log.ts
14700
14734
  import {
14701
- existsSync as existsSync27,
14735
+ existsSync as existsSync28,
14702
14736
  mkdirSync as mkdirSync13,
14703
14737
  readdirSync as readdirSync12,
14704
14738
  readFileSync as readFileSync21,
@@ -14719,7 +14753,7 @@ function truncate(s, n) {
14719
14753
  }
14720
14754
  function listRecentCrashes(limit = 10, configDir) {
14721
14755
  const dir = getLogsDir(configDir);
14722
- if (!existsSync27(dir)) return [];
14756
+ if (!existsSync28(dir)) return [];
14723
14757
  const files = [];
14724
14758
  let names;
14725
14759
  try {
@@ -14752,7 +14786,7 @@ function listRecentCrashes(limit = 10, configDir) {
14752
14786
  }
14753
14787
  function getConfigDirUsage(configDir) {
14754
14788
  const base = configDir ?? join22(homedir8(), CONFIG_DIR_NAME);
14755
- if (!existsSync27(base)) return { totalBytes: 0, entries: [] };
14789
+ if (!existsSync28(base)) return { totalBytes: 0, entries: [] };
14756
14790
  const entries = [];
14757
14791
  let total = 0;
14758
14792
  let names;
@@ -14787,7 +14821,7 @@ function dirSize(path3) {
14787
14821
 
14788
14822
  // src/diagnostics/doctor-report.ts
14789
14823
  function checkFile(label, path3) {
14790
- const exists = existsSync28(path3);
14824
+ const exists = existsSync29(path3);
14791
14825
  let sizeBytes = null;
14792
14826
  if (exists) {
14793
14827
  try {
@@ -14885,7 +14919,7 @@ function buildDoctorReport(options) {
14885
14919
  mcp: {
14886
14920
  enabled: config.get("mcpEnabled"),
14887
14921
  configured: Object.keys(mcpServers).length,
14888
- projectConfigExists: existsSync28(join23(projectRoot, MCP_PROJECT_CONFIG_NAME)),
14922
+ projectConfigExists: existsSync29(join23(projectRoot, MCP_PROJECT_CONFIG_NAME)),
14889
14923
  statuses: options.mcpStatuses ?? []
14890
14924
  },
14891
14925
  recentCrashes: listRecentCrashes(5, configDir),
@@ -15755,7 +15789,7 @@ ${activated.meta.description || ""}` });
15755
15789
  async function handleCommands(_args, ctx) {
15756
15790
  const configDir = ctx.config.getConfigDir();
15757
15791
  const commandsDir = join24(configDir, CUSTOM_COMMANDS_DIR_NAME);
15758
- if (!existsSync29(commandsDir)) {
15792
+ if (!existsSync30(commandsDir)) {
15759
15793
  ctx.send({ type: "info", message: `No custom commands directory.
15760
15794
  Create: ${commandsDir}/ with .md files.` });
15761
15795
  return;
@@ -15855,7 +15889,7 @@ async function handleSecurityReview(args, ctx) {
15855
15889
  async function handleTest(args, ctx) {
15856
15890
  ctx.send({ type: "info", message: "\u{1F9EA} Running tests..." });
15857
15891
  try {
15858
- const { executeTests } = await import("./run-tests-QR2PYR2P.js");
15892
+ const { executeTests } = await import("./run-tests-YSRET52H.js");
15859
15893
  const argStr = args.join(" ").trim();
15860
15894
  let testArgs = {};
15861
15895
  if (argStr) {
@@ -15885,7 +15919,7 @@ async function handleDiff(args, ctx) {
15885
15919
  let modifiedFiles = 0;
15886
15920
  const diffLines2 = [];
15887
15921
  for (const [filePath, { earliest }] of fileMap) {
15888
- const currentContent = existsSync29(filePath) ? (() => {
15922
+ const currentContent = existsSync30(filePath) ? (() => {
15889
15923
  try {
15890
15924
  return readFileSync22(filePath, "utf-8");
15891
15925
  } catch {
@@ -15924,7 +15958,7 @@ async function handleInit(args, ctx) {
15924
15958
  const cwd = process.cwd();
15925
15959
  const targetPath = join24(cwd, "AICLI.md");
15926
15960
  const force = args.includes("--force");
15927
- if (existsSync29(targetPath) && !force) {
15961
+ if (existsSync30(targetPath) && !force) {
15928
15962
  ctx.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
15929
15963
  Use /init --force to overwrite.` });
15930
15964
  return;
@@ -16108,7 +16142,7 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
16108
16142
  return;
16109
16143
  }
16110
16144
  const dirPath = resolve10(sub);
16111
- if (!existsSync29(dirPath)) {
16145
+ if (!existsSync30(dirPath)) {
16112
16146
  ctx.send({ type: "error", message: `Directory not found: ${dirPath}` });
16113
16147
  return;
16114
16148
  }
@@ -17183,7 +17217,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
17183
17217
  this.send({ type: "response_done", content, usage });
17184
17218
  if (pendingTeeSave && isCleanDocumentBody(content)) {
17185
17219
  try {
17186
- mkdirSync16(dirname10(pendingTeeSave), { recursive: true });
17220
+ mkdirSync16(dirname11(pendingTeeSave), { recursive: true });
17187
17221
  const bodyToSave = stripOuterCodeFence(content);
17188
17222
  atomicWriteFileSync(pendingTeeSave, bodyToSave);
17189
17223
  undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
@@ -17359,7 +17393,7 @@ ${summaryContent}`,
17359
17393
  let isError = false;
17360
17394
  let summary;
17361
17395
  try {
17362
- mkdirSync16(dirname10(saveToFile), { recursive: true });
17396
+ mkdirSync16(dirname11(saveToFile), { recursive: true });
17363
17397
  fileStream = createWriteStream3(saveToFile);
17364
17398
  const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
17365
17399
  const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
@@ -17934,8 +17968,8 @@ async function setupProxy(configProxy) {
17934
17968
  }
17935
17969
 
17936
17970
  // 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";
17971
+ import { existsSync as existsSync33, readFileSync as readFileSync25, writeFileSync as writeFileSync6, mkdirSync as mkdirSync17, readdirSync as readdirSync16, copyFileSync } from "fs";
17972
+ import { isAbsolute as isAbsolute3, join as join27, relative as relative4 } from "path";
17939
17973
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
17940
17974
  var USERS_FILE = "users.json";
17941
17975
  var TOKEN_EXPIRY_HOURS = 24;
@@ -18133,7 +18167,7 @@ var AuthManager = class {
18133
18167
  if (err) return err;
18134
18168
  const userDir = this.getUserDataDir(username);
18135
18169
  const globalConfig = join27(this.baseDir, "config.json");
18136
- if (existsSync32(globalConfig)) {
18170
+ if (existsSync33(globalConfig)) {
18137
18171
  try {
18138
18172
  const content = readFileSync25(globalConfig, "utf-8");
18139
18173
  writeFileSync6(join27(userDir, "config.json"), content, "utf-8");
@@ -18141,7 +18175,7 @@ var AuthManager = class {
18141
18175
  }
18142
18176
  }
18143
18177
  const globalMemory = join27(this.baseDir, "memory.md");
18144
- if (existsSync32(globalMemory)) {
18178
+ if (existsSync33(globalMemory)) {
18145
18179
  try {
18146
18180
  const content = readFileSync25(globalMemory, "utf-8");
18147
18181
  writeFileSync6(join27(userDir, "memory.md"), content, "utf-8");
@@ -18149,7 +18183,7 @@ var AuthManager = class {
18149
18183
  }
18150
18184
  }
18151
18185
  const globalHistory = join27(this.baseDir, "history");
18152
- if (existsSync32(globalHistory)) {
18186
+ if (existsSync33(globalHistory)) {
18153
18187
  try {
18154
18188
  const files = readdirSync16(globalHistory).filter((f) => f.endsWith(".json"));
18155
18189
  const userHistory = join27(userDir, "history");
@@ -18166,10 +18200,19 @@ var AuthManager = class {
18166
18200
  }
18167
18201
  // ── Private methods ────────────────────────────────────────────
18168
18202
  loadOrCreate() {
18169
- if (existsSync32(this.usersFile)) {
18203
+ if (existsSync33(this.usersFile)) {
18170
18204
  try {
18171
- return JSON.parse(readFileSync25(this.usersFile, "utf-8"));
18172
- } catch {
18205
+ const parsed = JSON.parse(readFileSync25(this.usersFile, "utf-8"));
18206
+ if (!this.isValidUsersDB(parsed)) {
18207
+ throw new Error("database structure is invalid");
18208
+ }
18209
+ return parsed;
18210
+ } catch (err) {
18211
+ const detail = err instanceof Error ? err.message : String(err);
18212
+ throw new Error(
18213
+ `Authentication database is unreadable or corrupt: ${this.usersFile} (${detail}). Refusing to start without authentication; restore or remove the file explicitly.`,
18214
+ { cause: err }
18215
+ );
18173
18216
  }
18174
18217
  }
18175
18218
  const db = {
@@ -18180,6 +18223,27 @@ var AuthManager = class {
18180
18223
  this.saveDB(db);
18181
18224
  return db;
18182
18225
  }
18226
+ isValidUsersDB(value) {
18227
+ if (!value || typeof value !== "object") return false;
18228
+ const db = value;
18229
+ if (db.version !== 1 || typeof db.secret !== "string" || db.secret.length < 32 || !Array.isArray(db.users)) {
18230
+ return false;
18231
+ }
18232
+ const usernames = /* @__PURE__ */ new Set();
18233
+ for (const user of db.users) {
18234
+ if (!user || typeof user !== "object") return false;
18235
+ const record = user;
18236
+ 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)) {
18237
+ return false;
18238
+ }
18239
+ const dataDirFromBase = relative4(this.baseDir, join27(this.baseDir, record.dataDir));
18240
+ if (dataDirFromBase.startsWith("..") || isAbsolute3(dataDirFromBase)) return false;
18241
+ if (record.hashVersion !== void 0 && record.hashVersion !== 1 && record.hashVersion !== 2) return false;
18242
+ if (record.tokensRevokedBefore !== void 0 && !Number.isFinite(record.tokensRevokedBefore)) return false;
18243
+ usernames.add(record.username);
18244
+ }
18245
+ return true;
18246
+ }
18183
18247
  save() {
18184
18248
  this.saveDB(this.db);
18185
18249
  }
@@ -18216,7 +18280,7 @@ function getModuleDir() {
18216
18280
  if (typeof import.meta?.url === "string") {
18217
18281
  const url = new URL(import.meta.url);
18218
18282
  const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
18219
- return dirname11(filePath);
18283
+ return dirname12(filePath);
18220
18284
  }
18221
18285
  } catch {
18222
18286
  }
@@ -18289,7 +18353,7 @@ async function startWebServer(options = {}) {
18289
18353
  }
18290
18354
  let skillManager = null;
18291
18355
  const skillsDir = join28(config.getConfigDir(), SKILLS_DIR_NAME);
18292
- if (existsSync33(skillsDir)) {
18356
+ if (existsSync34(skillsDir)) {
18293
18357
  skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
18294
18358
  skillManager.loadSkills(pluginAssets.skillDirs);
18295
18359
  const count = skillManager.listSkills().length;
@@ -18361,13 +18425,13 @@ async function startWebServer(options = {}) {
18361
18425
  };
18362
18426
  const moduleDir = getModuleDir();
18363
18427
  let clientDir = join28(moduleDir, "web", "client");
18364
- if (!existsSync33(clientDir)) {
18428
+ if (!existsSync34(clientDir)) {
18365
18429
  clientDir = join28(moduleDir, "client");
18366
18430
  }
18367
- if (!existsSync33(clientDir)) {
18431
+ if (!existsSync34(clientDir)) {
18368
18432
  clientDir = join28(moduleDir, "..", "..", "src", "web", "client");
18369
18433
  }
18370
- if (!existsSync33(clientDir)) {
18434
+ if (!existsSync34(clientDir)) {
18371
18435
  clientDir = join28(process.cwd(), "src", "web", "client");
18372
18436
  }
18373
18437
  console.log(` Static files: ${clientDir}`);
@@ -18440,8 +18504,8 @@ async function startWebServer(options = {}) {
18440
18504
  const prefix = req.query.prefix || "";
18441
18505
  const targetDir = join28(cwd, prefix);
18442
18506
  try {
18443
- const canonicalTarget = realpathSync(resolve13(targetDir));
18444
- const canonicalCwd = realpathSync(resolve13(cwd));
18507
+ const canonicalTarget = realpathSync2(resolve13(targetDir));
18508
+ const canonicalCwd = realpathSync2(resolve13(cwd));
18445
18509
  if (!canonicalTarget.startsWith(canonicalCwd + sep3) && canonicalTarget !== canonicalCwd) {
18446
18510
  res.json({ files: [] });
18447
18511
  return;
@@ -18455,7 +18519,7 @@ async function startWebServer(options = {}) {
18455
18519
  const entries = readdirSync17(targetDir, { withFileTypes: true });
18456
18520
  const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
18457
18521
  name: e.name,
18458
- path: relative4(cwd, join28(targetDir, e.name)).replace(/\\/g, "/"),
18522
+ path: relative5(cwd, join28(targetDir, e.name)).replace(/\\/g, "/"),
18459
18523
  isDir: e.isDirectory()
18460
18524
  }));
18461
18525
  res.json({ files });
@@ -18496,13 +18560,13 @@ async function startWebServer(options = {}) {
18496
18560
  const authUser = req._authUser;
18497
18561
  const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
18498
18562
  const filePath = join28(histDir, `${id}.json`);
18499
- if (!existsSync33(filePath)) {
18563
+ if (!existsSync34(filePath)) {
18500
18564
  res.status(404).json({ error: "Session not found" });
18501
18565
  return;
18502
18566
  }
18503
18567
  try {
18504
- const canonicalFile = realpathSync(filePath);
18505
- const canonicalDir = realpathSync(histDir);
18568
+ const canonicalFile = realpathSync2(filePath);
18569
+ const canonicalDir = realpathSync2(histDir);
18506
18570
  if (!canonicalFile.startsWith(canonicalDir + sep3)) {
18507
18571
  res.status(404).json({ error: "Session not found" });
18508
18572
  return;
@@ -18526,8 +18590,8 @@ async function startWebServer(options = {}) {
18526
18590
  const cwd = process.cwd();
18527
18591
  const fullPath = resolve13(join28(cwd, filePath));
18528
18592
  try {
18529
- const canonicalFull = realpathSync(fullPath);
18530
- const canonicalCwd = realpathSync(resolve13(cwd));
18593
+ const canonicalFull = realpathSync2(fullPath);
18594
+ const canonicalCwd = realpathSync2(resolve13(cwd));
18531
18595
  if (!canonicalFull.startsWith(canonicalCwd + sep3) && canonicalFull !== canonicalCwd) {
18532
18596
  res.json({ error: "Access denied" });
18533
18597
  return;
@@ -18798,14 +18862,14 @@ function resolveProjectMcpPath() {
18798
18862
  const gitRoot = getGitRoot(cwd);
18799
18863
  const projectRoot = gitRoot ?? cwd;
18800
18864
  const configPath = join28(projectRoot, MCP_PROJECT_CONFIG_NAME);
18801
- return existsSync33(configPath) ? configPath : null;
18865
+ return existsSync34(configPath) ? configPath : null;
18802
18866
  }
18803
18867
  function loadProjectMcpConfig() {
18804
18868
  const cwd = process.cwd();
18805
18869
  const gitRoot = getGitRoot(cwd);
18806
18870
  const projectRoot = gitRoot ?? cwd;
18807
18871
  const configPath = join28(projectRoot, MCP_PROJECT_CONFIG_NAME);
18808
- if (!existsSync33(configPath)) return null;
18872
+ if (!existsSync34(configPath)) return null;
18809
18873
  try {
18810
18874
  const raw = JSON.parse(readFileSync26(configPath, "utf-8"));
18811
18875
  return raw.mcpServers ?? raw;