jinzd-ai-cli 0.4.233 → 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 (37) hide show
  1. package/dist/{auth-FSTAKSYF.js → auth-PE3Z5OHS.js} +1 -1
  2. package/dist/{batch-WWNPYQ6G.js → batch-Y7MHXVR7.js} +2 -2
  3. package/dist/{chat-index-O56HDGFI.js → chat-index-7HXBWQFH.js} +2 -2
  4. package/dist/{chat-index-WSI7ULRQ.js → chat-index-WYXYD7YP.js} +1 -1
  5. package/dist/{chunk-4KM53GLG.js → chunk-2QQKS56X.js} +1 -1
  6. package/dist/{chunk-NVUCDUXE.js → chunk-5MYPIQ3Y.js} +11 -5
  7. package/dist/{chunk-NXYXFRPA.js → chunk-7D5EJBHO.js} +3 -3
  8. package/dist/{chunk-5PKNKJYP.js → chunk-BFTTW5LS.js} +1 -1
  9. package/dist/{chunk-FQSAK75P.js → chunk-CDOQNIVV.js} +3 -3
  10. package/dist/{chunk-7KS3RGZ7.js → chunk-DHNYGJYB.js} +2 -2
  11. package/dist/{chunk-HX67JAK6.js → chunk-FYJGVFXL.js} +87 -54
  12. package/dist/{chunk-ACTO5C6K.js → chunk-GDXKFWFL.js} +1 -1
  13. package/dist/{chunk-EZGZOK6E.js → chunk-GFDRC2JZ.js} +1 -1
  14. package/dist/{chunk-4OAV7QMB.js → chunk-HHYMSEWS.js} +1 -1
  15. package/dist/{chunk-ZWU7FWHO.js → chunk-JMP3LJC2.js} +38 -91
  16. package/dist/chunk-KCEO2XJ4.js +420 -0
  17. package/dist/{chunk-M4FW2NPU.js → chunk-Q5GVANMH.js} +1 -1
  18. package/dist/{chunk-JVKAL5Q3.js → chunk-RYQBBEMM.js} +33 -3
  19. package/dist/{chunk-LF3OUJHK.js → chunk-UO3XWZFE.js} +302 -4
  20. package/dist/{chunk-TJK7AU65.js → chunk-WXFQ6LYN.js} +1 -1
  21. package/dist/{ci-V5V3JSJC.js → ci-OAFNH3MA.js} +4 -4
  22. package/dist/{ci-format-XS7LKBO5.js → ci-format-4LSXJNND.js} +2 -2
  23. package/dist/{constants-UROQJXLY.js → constants-ERIAS7GV.js} +1 -1
  24. package/dist/{doctor-cli-BY3FIV2B.js → doctor-cli-VFSVWZU6.js} +4 -4
  25. package/dist/electron-server.js +235 -224
  26. package/dist/{hub-L2QCRI5M.js → hub-X4EUYRIN.js} +2 -2
  27. package/dist/index.js +23 -23
  28. package/dist/{persist-I3JI27OK.js → persist-AMH5SQ4W.js} +3 -3
  29. package/dist/{pr-IJRUPR5C.js → pr-2CZN5JLE.js} +4 -4
  30. package/dist/{run-tests-FLCVJ6TK.js → run-tests-DXRJDPFF.js} +2 -2
  31. package/dist/{run-tests-NNXBS4EL.js → run-tests-YSRET52H.js} +1 -1
  32. package/dist/{server-W2X3C6GJ.js → server-5ZAXMKRI.js} +21 -20
  33. package/dist/{server-E6LZB72O.js → server-FGJGXHZD.js} +6 -6
  34. package/dist/{task-orchestrator-W55VKYTC.js → task-orchestrator-QMERCD77.js} +6 -6
  35. package/dist/{usage-MZCDTJWB.js → usage-633DUDNT.js} +2 -2
  36. package/package.json +2 -4
  37. package/dist/chunk-B5TYJO7V.js +0 -123
@@ -38,7 +38,7 @@ import {
38
38
  VERSION,
39
39
  buildUserIdentityPrompt,
40
40
  runTestsTool
41
- } from "./chunk-4OAV7QMB.js";
41
+ } from "./chunk-HHYMSEWS.js";
42
42
  import {
43
43
  hasSemanticIndex,
44
44
  semanticSearch
@@ -49,12 +49,18 @@ import {
49
49
  import "./chunk-IEQAE3QG.js";
50
50
  import {
51
51
  DEFAULT_PATTERNS,
52
+ listFromIndex,
52
53
  loadChatIndex,
54
+ readSessionMeta,
53
55
  redactJson,
54
56
  redactString,
57
+ removeSessionFromIndex,
58
+ safeDate,
55
59
  scanString,
56
- searchChatMemory
57
- } from "./chunk-LF3OUJHK.js";
60
+ searchCandidates,
61
+ searchChatMemory,
62
+ updateSessionInIndex
63
+ } from "./chunk-UO3XWZFE.js";
58
64
  import "./chunk-JV5N65KN.js";
59
65
  import {
60
66
  atomicWriteFileSync
@@ -64,8 +70,8 @@ import {
64
70
  import express from "express";
65
71
  import { createServer } from "http";
66
72
  import { WebSocketServer } from "ws";
67
- import { join as join28, dirname as dirname11, resolve as resolve13, relative as relative4, sep as sep3 } from "path";
68
- 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";
69
75
  import { networkInterfaces } from "os";
70
76
 
71
77
  // src/config/config-manager.ts
@@ -1902,12 +1908,12 @@ function findPhantomClaims(content, extraMessages) {
1902
1908
  const claimed = extractClaimedFilePaths(content);
1903
1909
  if (claimed.length === 0) return [];
1904
1910
  const normalize = (p) => p.replace(/\\/g, "/").toLowerCase().replace(/^\.\//, "");
1905
- const basename7 = (p) => {
1911
+ const basename8 = (p) => {
1906
1912
  const parts = normalize(p).split("/");
1907
1913
  return parts[parts.length - 1] ?? "";
1908
1914
  };
1909
1915
  const written = extractWrittenFilePaths(extraMessages).map(normalize);
1910
- const writtenBases = new Set(written.map(basename7));
1916
+ const writtenBases = new Set(written.map(basename8));
1911
1917
  const writtenFull = new Set(written);
1912
1918
  const bashText = extractBashCommands(extraMessages).map((c) => c.replace(/\\/g, "/").toLowerCase()).join("\n");
1913
1919
  return claimed.filter((raw) => {
@@ -1916,8 +1922,8 @@ function findPhantomClaims(content, extraMessages) {
1916
1922
  for (const w of writtenFull) {
1917
1923
  if (w.endsWith("/" + norm2) || norm2.endsWith("/" + w)) return false;
1918
1924
  }
1919
- if (writtenBases.has(basename7(norm2))) return false;
1920
- const base = basename7(norm2);
1925
+ if (writtenBases.has(basename8(norm2))) return false;
1926
+ const base = basename8(norm2);
1921
1927
  if (base && bashText.includes(base)) return false;
1922
1928
  return true;
1923
1929
  });
@@ -3947,7 +3953,7 @@ var ProviderRegistry = class {
3947
3953
  };
3948
3954
 
3949
3955
  // src/session/session-manager.ts
3950
- import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, unlinkSync as unlinkSync2, openSync, readSync, closeSync, statSync } from "fs";
3956
+ import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, unlinkSync as unlinkSync2, statSync } from "fs";
3951
3957
  import { join as join3 } from "path";
3952
3958
  import { v4 as uuidv4 } from "uuid";
3953
3959
 
@@ -4483,19 +4489,6 @@ var Session = class _Session {
4483
4489
  };
4484
4490
 
4485
4491
  // src/session/session-manager.ts
4486
- function safeDate(value) {
4487
- const d = new Date(value);
4488
- return isNaN(d.getTime()) ? /* @__PURE__ */ new Date(0) : d;
4489
- }
4490
- function extractJsonField(header, field) {
4491
- const re = new RegExp(`"${field}"\\s*:\\s*"([^"]*)"`, "i");
4492
- const m = header.match(re);
4493
- return m ? m[1] : void 0;
4494
- }
4495
- function extractJsonNumberField(header, field) {
4496
- const m = header.match(new RegExp(`"${field}"\\s*:\\s*(\\d+)`));
4497
- return m ? Number(m[1]) : void 0;
4498
- }
4499
4492
  var SessionManager = class {
4500
4493
  _current = null;
4501
4494
  historyDir;
@@ -4553,6 +4546,18 @@ var SessionManager = class {
4553
4546
  this.lastRedactionHits = hits.length;
4554
4547
  atomicWriteFileSync(filePath, JSON.stringify(payload, null, 2));
4555
4548
  this._current.dirty = false;
4549
+ try {
4550
+ updateSessionInIndex(this.historyDir, {
4551
+ id: this._current.id,
4552
+ provider: this._current.provider,
4553
+ model: this._current.model,
4554
+ title: this._current.title,
4555
+ messageCount: this._current.messages.length,
4556
+ created: this._current.created,
4557
+ updated: this._current.updated
4558
+ }, filePath);
4559
+ } catch {
4560
+ }
4556
4561
  }
4557
4562
  loadSession(id) {
4558
4563
  const filePath = join3(this.historyDir, `${id}.json`);
@@ -4571,12 +4576,16 @@ var SessionManager = class {
4571
4576
  }
4572
4577
  listSessions(opts) {
4573
4578
  if (!existsSync3(this.historyDir)) return [];
4579
+ try {
4580
+ return listFromIndex(this.historyDir, opts);
4581
+ } catch {
4582
+ }
4574
4583
  const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json"));
4575
4584
  const metas = [];
4576
4585
  for (const file of files) {
4577
4586
  try {
4578
4587
  const filePath = join3(this.historyDir, file);
4579
- const meta = this.readSessionMeta(filePath);
4588
+ const meta = readSessionMeta(filePath);
4580
4589
  if (!meta) continue;
4581
4590
  if (opts?.includeFileSize) {
4582
4591
  try {
@@ -4594,78 +4603,6 @@ var SessionManager = class {
4594
4603
  }
4595
4604
  return metas.sort((a, b) => b.updated.getTime() - a.updated.getTime());
4596
4605
  }
4597
- /**
4598
- * P1-B: Read only the first ~1KB of a session file to extract metadata fields.
4599
- * Session JSON format puts id/provider/model/created/updated/title before the
4600
- * large "messages" array, so a small header read suffices for metadata extraction.
4601
- * Falls back to full file read if header parsing fails.
4602
- */
4603
- readSessionMeta(filePath) {
4604
- const HEADER_SIZE = 1024;
4605
- let header;
4606
- try {
4607
- const fd = openSync(filePath, "r");
4608
- const buf = Buffer.alloc(HEADER_SIZE);
4609
- const bytesRead = readSync(fd, buf, 0, HEADER_SIZE, 0);
4610
- closeSync(fd);
4611
- header = buf.toString("utf-8", 0, bytesRead);
4612
- } catch {
4613
- return null;
4614
- }
4615
- const id = extractJsonField(header, "id");
4616
- const provider = extractJsonField(header, "provider");
4617
- const model = extractJsonField(header, "model");
4618
- const created = extractJsonField(header, "created");
4619
- const updated = extractJsonField(header, "updated");
4620
- const title = extractJsonField(header, "title");
4621
- if (id && provider && model) {
4622
- let messageCount = extractJsonNumberField(header, "messageCount");
4623
- if (messageCount === void 0) {
4624
- messageCount = 0;
4625
- try {
4626
- const full = readFileSync3(filePath, "utf-8");
4627
- const matches = full.match(/"role"\s*:/g);
4628
- messageCount = matches ? matches.length : 0;
4629
- } catch {
4630
- }
4631
- }
4632
- let branchCount;
4633
- let activeBranch;
4634
- try {
4635
- const brMatch = header.match(/"branches"\s*:\s*\[/);
4636
- if (brMatch) {
4637
- const brIds = header.match(/"id"\s*:\s*"[^"]*"/g);
4638
- const parentMatches = header.match(/"parentBranchId"/g);
4639
- branchCount = parentMatches ? parentMatches.length + 1 : 1;
4640
- }
4641
- activeBranch = extractJsonField(header, "activeBranchId");
4642
- } catch {
4643
- }
4644
- return {
4645
- id,
4646
- provider,
4647
- model,
4648
- messageCount,
4649
- created: safeDate(created),
4650
- updated: safeDate(updated),
4651
- title: title || void 0,
4652
- branchCount,
4653
- activeBranch
4654
- };
4655
- }
4656
- const data = JSON.parse(readFileSync3(filePath, "utf-8"));
4657
- return {
4658
- id: data.id,
4659
- provider: data.provider,
4660
- model: data.model,
4661
- messageCount: data.messages?.length ?? 0,
4662
- created: safeDate(data.created),
4663
- updated: safeDate(data.updated),
4664
- title: data.title,
4665
- branchCount: data.branches?.length ?? 1,
4666
- activeBranch: data.activeBranchId
4667
- };
4668
- }
4669
4606
  deleteSession(id) {
4670
4607
  const filePath = join3(this.historyDir, `${id}.json`);
4671
4608
  if (!existsSync3(filePath)) return false;
@@ -4674,6 +4611,10 @@ var SessionManager = class {
4674
4611
  if (this._current && this._current.id === id) {
4675
4612
  this._current = null;
4676
4613
  }
4614
+ try {
4615
+ removeSessionFromIndex(this.historyDir, id);
4616
+ } catch {
4617
+ }
4677
4618
  return true;
4678
4619
  } catch {
4679
4620
  return false;
@@ -4707,9 +4648,15 @@ var SessionManager = class {
4707
4648
  searchMessages(query, maxResults = 20) {
4708
4649
  if (!existsSync3(this.historyDir)) return [];
4709
4650
  const q = query.toLowerCase();
4710
- const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) => join3(this.historyDir, f));
4651
+ const candidateIds = searchCandidates(this.historyDir, q);
4652
+ let filesToSearch;
4653
+ if (candidateIds !== null) {
4654
+ filesToSearch = candidateIds.map((id) => join3(this.historyDir, `${id}.json`)).filter((fp) => existsSync3(fp));
4655
+ } else {
4656
+ filesToSearch = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) => join3(this.historyDir, f));
4657
+ }
4711
4658
  const results = [];
4712
- for (const filePath of files) {
4659
+ for (const filePath of filesToSearch) {
4713
4660
  if (results.length >= maxResults) break;
4714
4661
  try {
4715
4662
  const data = JSON.parse(readFileSync3(filePath, "utf-8"));
@@ -5990,11 +5937,11 @@ ${content}`;
5990
5937
 
5991
5938
  // src/tools/builtin/write-file.ts
5992
5939
  import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
5993
- import { dirname as dirname4 } from "path";
5940
+ import { dirname as dirname5 } from "path";
5994
5941
 
5995
5942
  // src/tools/executor.ts
5996
5943
  import chalk3 from "chalk";
5997
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
5944
+ import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
5998
5945
  import { tmpdir } from "os";
5999
5946
 
6000
5947
  // src/core/readline-internal.ts
@@ -6363,7 +6310,8 @@ function runHook(template, vars) {
6363
6310
  }
6364
6311
 
6365
6312
  // src/tools/permissions.ts
6366
- 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";
6367
6315
  function checkPermission(toolName, args, dangerLevel, rules, defaultAction = "confirm") {
6368
6316
  for (const rule of rules) {
6369
6317
  if (rule.tool !== "*" && rule.tool !== toolName) continue;
@@ -6408,10 +6356,28 @@ var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
6408
6356
  function normalizePathForPermission(value) {
6409
6357
  return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
6410
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
+ }
6411
6374
  function isInsidePath(root, target) {
6412
6375
  const absoluteTarget = isAbsolute(target) ? target : resolve3(root, target);
6413
- const r = normalizePathForPermission(resolve3(root));
6414
- 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);
6415
6381
  return t === r || t.startsWith(`${r}/`);
6416
6382
  }
6417
6383
  function extractPathArg(toolName, args) {
@@ -6463,19 +6429,31 @@ function profileHardDecision(profileName, toolName, args, dangerLevel, ctx) {
6463
6429
  return null;
6464
6430
  }
6465
6431
  if (profileName === "workspace-write") {
6466
- 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
+ }
6467
6435
  if (dangerLevel === "write" && isExplicitFileWriteTool(toolName)) {
6468
6436
  const target = extractPathArg(toolName, args);
6469
- 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
+ }
6470
6440
  const allowedRoots = getAllowedWriteRoots(ctx);
6471
6441
  if (allowedRoots.length === 0 || !allowedRoots.some((root) => isInsidePath(root, target))) {
6472
- 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 };
6473
6443
  }
6474
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
+ }
6475
6453
  return null;
6476
6454
  }
6477
6455
  if (profileName === "danger-full-access") {
6478
- 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 };
6479
6457
  return null;
6480
6458
  }
6481
6459
  return null;
@@ -6487,7 +6465,8 @@ function checkPermissionWithProfile(toolName, args, dangerLevel, rules, defaultA
6487
6465
  const combinedRules = [...getProfileRules(profileContext), ...rules];
6488
6466
  const profileDefault = profileName === "legacy" ? defaultAction : getProfileDefaultAction(profileName, profileContext);
6489
6467
  const action = checkPermission(toolName, args, dangerLevel, combinedRules, profileDefault);
6490
- if (hard?.action === "confirm" && action === "auto-approve") return hard;
6468
+ if (action === "deny") return { action, profileName };
6469
+ if (hard?.action === "confirm") return hard;
6491
6470
  return { action, profileName };
6492
6471
  }
6493
6472
  function formatPermissionProfileWarning(profileName) {
@@ -6745,8 +6724,8 @@ var theme = new Proxy(DARK_THEME, {
6745
6724
  });
6746
6725
 
6747
6726
  // src/diagnostics/tool-stats.ts
6748
- import { existsSync as existsSync8, readFileSync as readFileSync7, mkdirSync as mkdirSync5 } from "fs";
6749
- 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";
6750
6729
  import { homedir as homedir3 } from "os";
6751
6730
  var STATS_FILE_NAME = "tool-stats.json";
6752
6731
  var STATS_VERSION = 1;
@@ -6761,7 +6740,7 @@ function statsFilePath() {
6761
6740
  }
6762
6741
  function load() {
6763
6742
  const path3 = statsFilePath();
6764
- if (!existsSync8(path3)) {
6743
+ if (!existsSync9(path3)) {
6765
6744
  return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
6766
6745
  }
6767
6746
  try {
@@ -6823,7 +6802,7 @@ function flush() {
6823
6802
  if (!dirty || state === null) return;
6824
6803
  const path3 = statsFilePath();
6825
6804
  try {
6826
- mkdirSync5(dirname3(path3), { recursive: true });
6805
+ mkdirSync5(dirname4(path3), { recursive: true });
6827
6806
  atomicWriteFileSync(path3, JSON.stringify(state, null, 2));
6828
6807
  dirty = false;
6829
6808
  pendingWrites = 0;
@@ -6847,7 +6826,7 @@ function getTopUsedTools(limit = 5) {
6847
6826
  }
6848
6827
 
6849
6828
  // src/tools/action-classifier.ts
6850
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
6829
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
6851
6830
  import { isAbsolute as isAbsolute2, join as join6, resolve as resolve4 } from "path";
6852
6831
  var RECENT_DENIED_LIMIT = 50;
6853
6832
  var recentlyDenied = [];
@@ -6890,7 +6869,7 @@ function splitShellWords(command) {
6890
6869
  }
6891
6870
  function readPackageManifest(root) {
6892
6871
  const p = join6(root, "package.json");
6893
- if (!existsSync9(p)) return null;
6872
+ if (!existsSync10(p)) return null;
6894
6873
  try {
6895
6874
  return JSON.parse(readFileSync8(p, "utf-8"));
6896
6875
  } catch {
@@ -6909,7 +6888,7 @@ function manifestHasDependency(root, name) {
6909
6888
  function lockfileMentions(root, name) {
6910
6889
  for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
6911
6890
  const p = join6(root, file);
6912
- if (!existsSync9(p)) continue;
6891
+ if (!existsSync10(p)) continue;
6913
6892
  try {
6914
6893
  if (readFileSync8(p, "utf-8").includes(name)) return true;
6915
6894
  } catch {
@@ -7185,14 +7164,15 @@ var ToolExecutor = class {
7185
7164
  return { callId: call.id, content: message, isError: true };
7186
7165
  }
7187
7166
  }
7188
- if (networkPermission?.action === "confirm" && dangerLevel === "safe") {
7167
+ const requiresHardConfirmation = permission.action === "confirm" && permission.hardBoundary === true;
7168
+ if ((networkPermission?.action === "confirm" || requiresHardConfirmation) && dangerLevel === "safe") {
7189
7169
  this.printToolCall(call);
7190
7170
  toolCallAlreadyPrinted = true;
7191
7171
  const confirmed = await this.confirm(call, "write");
7192
7172
  if (!confirmed) {
7193
7173
  return {
7194
7174
  callId: call.id,
7195
- 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.`,
7196
7176
  isError: true
7197
7177
  };
7198
7178
  }
@@ -7210,7 +7190,7 @@ var ToolExecutor = class {
7210
7190
  isError: true
7211
7191
  };
7212
7192
  }
7213
- if (classification.decision === "auto-approve") {
7193
+ if (classification.decision === "auto-approve" && !requiresHardConfirmation) {
7214
7194
  this.printToolCall(call);
7215
7195
  if (dangerLevel === "write") this.printDiffPreview(call);
7216
7196
  console.log(theme.warning(` \u26A1 Auto-approved (/auto: ${classification.ruleId})`));
@@ -7231,7 +7211,7 @@ var ToolExecutor = class {
7231
7211
  }
7232
7212
  }
7233
7213
  }
7234
- if (this.sessionAutoApprove && dangerLevel === "write") {
7214
+ if (this.sessionAutoApprove && dangerLevel === "write" && !requiresHardConfirmation) {
7235
7215
  this.printToolCall(call);
7236
7216
  if (dangerLevel === "write") this.printDiffPreview(call);
7237
7217
  console.log(theme.warning(" \u26A1 Auto-approved (session /yolo mode)"));
@@ -7475,7 +7455,7 @@ var ToolExecutor = class {
7475
7455
  const filePath = String(call.arguments["path"] ?? "");
7476
7456
  const newContent = String(call.arguments["content"] ?? "");
7477
7457
  if (!filePath) return;
7478
- if (existsSync10(filePath)) {
7458
+ if (existsSync11(filePath)) {
7479
7459
  let oldContent;
7480
7460
  try {
7481
7461
  oldContent = readFileSync9(filePath, "utf-8");
@@ -7501,7 +7481,7 @@ var ToolExecutor = class {
7501
7481
  }
7502
7482
  } else if (call.name === "edit_file") {
7503
7483
  const filePath = String(call.arguments["path"] ?? "");
7504
- if (!filePath || !existsSync10(filePath)) return;
7484
+ if (!filePath || !existsSync11(filePath)) return;
7505
7485
  const oldStr = call.arguments["old_str"];
7506
7486
  const newStr = call.arguments["new_str"];
7507
7487
  if (oldStr !== void 0) {
@@ -7620,7 +7600,7 @@ var ToolExecutor = class {
7620
7600
  };
7621
7601
 
7622
7602
  // src/tools/sensitive-paths.ts
7623
- 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";
7624
7604
  import { homedir as homedir4 } from "os";
7625
7605
  var home = homedir4();
7626
7606
  function norm(p) {
@@ -7639,7 +7619,7 @@ function homeRel(p) {
7639
7619
  function classifyWritePath(path3) {
7640
7620
  if (!path3) return { sensitive: false };
7641
7621
  const abs = norm(path3);
7642
- const base = basename2(abs);
7622
+ const base = basename3(abs);
7643
7623
  const rel = homeRel(path3);
7644
7624
  if (rel) {
7645
7625
  const shellRc = /* @__PURE__ */ new Set([
@@ -7763,7 +7743,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
7763
7743
  }
7764
7744
  undoStack.push(filePath, `write_file${appendMode ? " (append)" : ""}: ${filePath}`);
7765
7745
  fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
7766
- mkdirSync6(dirname4(filePath), { recursive: true });
7746
+ mkdirSync6(dirname5(filePath), { recursive: true });
7767
7747
  if (appendMode) {
7768
7748
  appendFileSync(filePath, content, encoding);
7769
7749
  } else {
@@ -7783,7 +7763,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
7783
7763
  };
7784
7764
 
7785
7765
  // src/tools/builtin/edit-file.ts
7786
- import { readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
7766
+ import { readFileSync as readFileSync10, existsSync as existsSync12 } from "fs";
7787
7767
 
7788
7768
  // src/tools/builtin/patch-apply.ts
7789
7769
  function parseUnifiedDiff(patch) {
@@ -8144,7 +8124,7 @@ Note: Path can be absolute or relative to cwd.`,
8144
8124
  const filePath = String(args["path"] ?? args["file"] ?? "");
8145
8125
  const encoding = args["encoding"] ?? "utf-8";
8146
8126
  if (!filePath) throw new ToolError("edit_file", "path is required");
8147
- 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}`);
8148
8128
  const verdict = classifyWritePath(filePath);
8149
8129
  if (verdict.sensitive && subAgentGuard.active) {
8150
8130
  throw new ToolError(
@@ -8310,8 +8290,8 @@ function truncatePreview(str, maxLen = 80) {
8310
8290
  }
8311
8291
 
8312
8292
  // src/tools/builtin/list-dir.ts
8313
- import { readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync12 } from "fs";
8314
- 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";
8315
8295
  var listDirTool = {
8316
8296
  definition: {
8317
8297
  name: "list_dir",
@@ -8333,8 +8313,8 @@ var listDirTool = {
8333
8313
  async execute(args) {
8334
8314
  const dirPath = String(args["path"] ?? process.cwd());
8335
8315
  const recursive = Boolean(args["recursive"] ?? false);
8336
- if (!existsSync12(dirPath)) {
8337
- const targetName = basename3(dirPath).toLowerCase();
8316
+ if (!existsSync13(dirPath)) {
8317
+ const targetName = basename4(dirPath).toLowerCase();
8338
8318
  const cwd = process.cwd();
8339
8319
  const suggestions = [];
8340
8320
  try {
@@ -8411,7 +8391,7 @@ function formatSize(bytes) {
8411
8391
  }
8412
8392
 
8413
8393
  // src/tools/builtin/grep-files.ts
8414
- 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";
8415
8395
  import { readFile } from "fs/promises";
8416
8396
  import { join as join8, relative } from "path";
8417
8397
  var grepFilesTool = {
@@ -8464,7 +8444,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
8464
8444
  const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
8465
8445
  const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
8466
8446
  if (!pattern) throw new ToolError("grep_files", "pattern is required");
8467
- 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}`);
8468
8448
  const MAX_PATTERN_LENGTH = 1e3;
8469
8449
  if (pattern.length > MAX_PATTERN_LENGTH) {
8470
8450
  throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
@@ -8648,8 +8628,8 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
8648
8628
  }
8649
8629
 
8650
8630
  // src/tools/builtin/glob-files.ts
8651
- import { readdirSync as readdirSync6, statSync as statSync6, existsSync as existsSync14 } from "fs";
8652
- 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";
8653
8633
  var globFilesTool = {
8654
8634
  definition: {
8655
8635
  name: "glob_files",
@@ -8682,7 +8662,7 @@ Results sorted by most recent modification time. Automatically skips node_module
8682
8662
  const rootPath = String(args["path"] ?? process.cwd());
8683
8663
  const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
8684
8664
  if (!pattern) throw new ToolError("glob_files", "pattern is required");
8685
- 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}`);
8686
8666
  const regex = globToRegex(pattern);
8687
8667
  const matches = [];
8688
8668
  collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
@@ -8765,7 +8745,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
8765
8745
  collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
8766
8746
  } else if (entry.isFile()) {
8767
8747
  const relPath = relative2(rootPath, fullPath).replace(/\\/g, "/");
8768
- if (regex.test(relPath) || regex.test(basename4(relPath))) {
8748
+ if (regex.test(relPath) || regex.test(basename5(relPath))) {
8769
8749
  try {
8770
8750
  const stat = statSync6(fullPath);
8771
8751
  results.push({ relPath, absPath: fullPath, mtime: stat.mtimeMs });
@@ -9500,7 +9480,7 @@ ${preamble}`;
9500
9480
 
9501
9481
  // src/tools/builtin/save-last-response.ts
9502
9482
  import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync4, rmdirSync as rmdirSync2 } from "fs";
9503
- import { dirname as dirname5 } from "path";
9483
+ import { dirname as dirname6 } from "path";
9504
9484
  var lastResponseStore = { content: "" };
9505
9485
  function cleanupRejectedTeeFile(filePath) {
9506
9486
  try {
@@ -9508,7 +9488,7 @@ function cleanupRejectedTeeFile(filePath) {
9508
9488
  } catch {
9509
9489
  }
9510
9490
  try {
9511
- rmdirSync2(dirname5(filePath));
9491
+ rmdirSync2(dirname6(filePath));
9512
9492
  } catch {
9513
9493
  }
9514
9494
  }
@@ -9549,7 +9529,7 @@ Any of these triggers means use save_last_response, NOT write_file:
9549
9529
  throw new ToolError("save_last_response", "No content to save: AI has not produced any response yet, or the last response was empty.");
9550
9530
  }
9551
9531
  undoStack.push(filePath, `save_last_response: ${filePath}`);
9552
- mkdirSync7(dirname5(filePath), { recursive: true });
9532
+ mkdirSync7(dirname6(filePath), { recursive: true });
9553
9533
  atomicWriteFileSync(filePath, content);
9554
9534
  const lines = content.split("\n").length;
9555
9535
  return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
@@ -9561,13 +9541,13 @@ import { join as join12 } from "path";
9561
9541
  import { homedir as homedir5 } from "os";
9562
9542
 
9563
9543
  // src/memory/persistent-memory.ts
9564
- 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";
9565
9545
  import { randomUUID, createHash as createHash2 } from "crypto";
9566
- 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";
9567
9547
 
9568
9548
  // src/tools/git-context.ts
9569
9549
  import { execSync as execSync2 } from "child_process";
9570
- import { existsSync as existsSync15 } from "fs";
9550
+ import { existsSync as existsSync16 } from "fs";
9571
9551
  import { join as join10 } from "path";
9572
9552
  function runGit(cmd, cwd) {
9573
9553
  try {
@@ -9585,7 +9565,7 @@ function getGitRoot(cwd = process.cwd()) {
9585
9565
  return runGit("rev-parse --show-toplevel", cwd);
9586
9566
  }
9587
9567
  function getGitContext(cwd = process.cwd()) {
9588
- if (!existsSync15(join10(cwd, ".git"))) {
9568
+ if (!existsSync16(join10(cwd, ".git"))) {
9589
9569
  const result = runGit("rev-parse --git-dir", cwd);
9590
9570
  if (!result) return null;
9591
9571
  }
@@ -9696,7 +9676,7 @@ function normalizeEntry(value) {
9696
9676
  }
9697
9677
  function legacyEntriesFromMarkdown(configDir) {
9698
9678
  const file = memoryMarkdownPath(configDir);
9699
- if (!existsSync16(file)) return [];
9679
+ if (!existsSync17(file)) return [];
9700
9680
  const content = readFileSync12(file, "utf-8").trim();
9701
9681
  if (!content) return [];
9702
9682
  const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
@@ -9713,7 +9693,7 @@ ${chunk}`).digest("hex").slice(0, 12);
9713
9693
  }
9714
9694
  function loadMemoryEntries(configDir) {
9715
9695
  const file = memoryStorePath(configDir);
9716
- if (!existsSync16(file)) return legacyEntriesFromMarkdown(configDir);
9696
+ if (!existsSync17(file)) return legacyEntriesFromMarkdown(configDir);
9717
9697
  const text = readFileSync12(file, "utf-8").trim();
9718
9698
  if (!text) return [];
9719
9699
  const entries = [];
@@ -10161,13 +10141,13 @@ function formatResults2(query, data, _requested) {
10161
10141
  }
10162
10142
 
10163
10143
  // src/agents/agent-config.ts
10164
- 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";
10165
10145
  import { join as join14 } from "path";
10166
10146
  import { homedir as homedir6 } from "os";
10167
10147
 
10168
10148
  // src/plugins/plugin-manager.ts
10169
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync13, cpSync, rmSync, statSync as statSync7 } from "fs";
10170
- 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";
10171
10151
  import { createHash as createHash3 } from "crypto";
10172
10152
  import { z as z2 } from "zod";
10173
10153
  var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
@@ -10237,7 +10217,7 @@ function statePath(configDir) {
10237
10217
  }
10238
10218
  function loadState(configDir) {
10239
10219
  const file = statePath(configDir);
10240
- if (!existsSync17(file)) return { version: 1, plugins: [] };
10220
+ if (!existsSync18(file)) return { version: 1, plugins: [] };
10241
10221
  try {
10242
10222
  const parsed = JSON.parse(readFileSync13(file, "utf-8"));
10243
10223
  return { version: 1, plugins: Array.isArray(parsed.plugins) ? parsed.plugins : [] };
@@ -10257,10 +10237,10 @@ function readPluginManifest(manifestPath) {
10257
10237
  }
10258
10238
  function findPluginSourceDir(inputPath) {
10259
10239
  const abs = resolve7(inputPath);
10260
- const direct = statSync7(abs).isDirectory() ? abs : dirname7(abs);
10261
- if (existsSync17(pluginManifestPath(direct))) return direct;
10240
+ const direct = statSync7(abs).isDirectory() ? abs : dirname8(abs);
10241
+ if (existsSync18(pluginManifestPath(direct))) return direct;
10262
10242
  const nested = join13(direct, ".aicli-plugin");
10263
- if (existsSync17(join13(nested, "plugin.json"))) return direct;
10243
+ if (existsSync18(join13(nested, "plugin.json"))) return direct;
10264
10244
  throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
10265
10245
  }
10266
10246
  function installPlugin(configDir, inputPath) {
@@ -10268,7 +10248,7 @@ function installPlugin(configDir, inputPath) {
10268
10248
  const manifest = readPluginManifest(pluginManifestPath(sourceDir));
10269
10249
  const targetDir = join13(pluginRoot(configDir), manifest.name);
10270
10250
  mkdirSync9(pluginRoot(configDir), { recursive: true });
10271
- if (existsSync17(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10251
+ if (existsSync18(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10272
10252
  cpSync(sourceDir, targetDir, { recursive: true });
10273
10253
  const manifestPath = pluginManifestPath(targetDir);
10274
10254
  const hash = hashPluginManifest(manifestPath);
@@ -10326,7 +10306,7 @@ function getInstalledPlugin(configDir, name) {
10326
10306
  }
10327
10307
  function listInstalledPlugins(configDir) {
10328
10308
  const root = pluginRoot(configDir);
10329
- if (!existsSync17(root)) return [];
10309
+ if (!existsSync18(root)) return [];
10330
10310
  const state2 = loadState(configDir);
10331
10311
  const out = [];
10332
10312
  for (const entry of readdirSync7(root)) {
@@ -10337,7 +10317,7 @@ function listInstalledPlugins(configDir) {
10337
10317
  continue;
10338
10318
  }
10339
10319
  const manifestPath = pluginManifestPath(dir);
10340
- if (!existsSync17(manifestPath)) continue;
10320
+ if (!existsSync18(manifestPath)) continue;
10341
10321
  try {
10342
10322
  const manifest = readPluginManifest(manifestPath);
10343
10323
  const hash = hashPluginManifest(manifestPath);
@@ -10377,7 +10357,7 @@ function uniqueDirs(plugin, rels) {
10377
10357
  for (const rel of rels) {
10378
10358
  const abs = resolve7(plugin.dir, rel);
10379
10359
  if (!abs.startsWith(resolve7(plugin.dir))) continue;
10380
- if (existsSync17(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname7(abs));
10360
+ if (existsSync18(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname8(abs));
10381
10361
  }
10382
10362
  return [...dirs];
10383
10363
  }
@@ -10540,7 +10520,7 @@ function parseAgentConfig(raw, source, path3) {
10540
10520
  return cfg;
10541
10521
  }
10542
10522
  function loadAgentDir(dir, source) {
10543
- if (!existsSync18(dir)) return [];
10523
+ if (!existsSync19(dir)) return [];
10544
10524
  const out = [];
10545
10525
  for (const file of readdirSync8(dir)) {
10546
10526
  if (!file.endsWith(".json")) continue;
@@ -11272,13 +11252,13 @@ var taskStopTool = {
11272
11252
 
11273
11253
  // src/tools/builtin/git-tools.ts
11274
11254
  import { execFileSync as execFileSync2 } from "child_process";
11275
- import { existsSync as existsSync19 } from "fs";
11255
+ import { existsSync as existsSync20 } from "fs";
11276
11256
  import { join as join15 } from "path";
11277
11257
  function assertGitRepo(cwd) {
11278
11258
  let dir = cwd;
11279
11259
  const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
11280
11260
  while (dir && dir !== root) {
11281
- if (existsSync19(join15(dir, ".git"))) return;
11261
+ if (existsSync20(join15(dir, ".git"))) return;
11282
11262
  const parent = join15(dir, "..");
11283
11263
  if (parent === dir) break;
11284
11264
  dir = parent;
@@ -11544,7 +11524,7 @@ ${commitOutput.trim()}`;
11544
11524
  };
11545
11525
 
11546
11526
  // src/tools/builtin/notebook-edit.ts
11547
- import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
11527
+ import { readFileSync as readFileSync15, existsSync as existsSync21 } from "fs";
11548
11528
  import { writeFile } from "fs/promises";
11549
11529
  import { resolve as resolve8, extname as extname2 } from "path";
11550
11530
  var notebookEditTool = {
@@ -11599,7 +11579,7 @@ var notebookEditTool = {
11599
11579
  if (extname2(absPath).toLowerCase() !== ".ipynb") {
11600
11580
  throw new ToolError("notebook_edit", "path must point to a .ipynb file");
11601
11581
  }
11602
- if (!existsSync20(absPath)) {
11582
+ if (!existsSync21(absPath)) {
11603
11583
  throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
11604
11584
  }
11605
11585
  const raw = readFileSync15(absPath, "utf-8");
@@ -12019,7 +11999,7 @@ function estimateToolDefinitionTokens(def) {
12019
11999
 
12020
12000
  // src/tools/registry.ts
12021
12001
  import { pathToFileURL } from "url";
12022
- 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";
12023
12003
  import { join as join16 } from "path";
12024
12004
  var ToolRegistry = class {
12025
12005
  tools = /* @__PURE__ */ new Map();
@@ -12181,7 +12161,7 @@ var ToolRegistry = class {
12181
12161
  * Returns the number of successfully loaded plugins.
12182
12162
  */
12183
12163
  async loadPlugins(pluginsDir, allowPlugins = false) {
12184
- if (!existsSync21(pluginsDir)) {
12164
+ if (!existsSync22(pluginsDir)) {
12185
12165
  try {
12186
12166
  mkdirSync10(pluginsDir, { recursive: true });
12187
12167
  } catch {
@@ -12758,12 +12738,12 @@ var McpManager = class {
12758
12738
  };
12759
12739
 
12760
12740
  // src/skills/manager.ts
12761
- 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";
12762
12742
  import { join as join17 } from "path";
12763
12743
 
12764
12744
  // src/skills/types.ts
12765
12745
  import { readFileSync as readFileSync16 } from "fs";
12766
- import { basename as basename6 } from "path";
12746
+ import { basename as basename7 } from "path";
12767
12747
  function parseSimpleYaml(yaml) {
12768
12748
  const result = {};
12769
12749
  for (const line of yaml.split("\n")) {
@@ -12792,7 +12772,7 @@ function parseSkillFile(filePath) {
12792
12772
  if (!frontmatterMatch) {
12793
12773
  return {
12794
12774
  meta: {
12795
- name: basename6(filePath, ".md"),
12775
+ name: basename7(filePath, ".md"),
12796
12776
  description: ""
12797
12777
  },
12798
12778
  content: raw.trim(),
@@ -12803,7 +12783,7 @@ function parseSkillFile(filePath) {
12803
12783
  const parsed = parseSimpleYaml(yaml);
12804
12784
  return {
12805
12785
  meta: {
12806
- name: parsed["name"] ?? basename6(filePath, ".md"),
12786
+ name: parsed["name"] ?? basename7(filePath, ".md"),
12807
12787
  description: parsed["description"] ?? "",
12808
12788
  tools: parsed["tools"] ? parseYamlArray(parsed["tools"]) : void 0
12809
12789
  },
@@ -12832,7 +12812,7 @@ var SkillManager = class {
12832
12812
  return this.skills.size;
12833
12813
  }
12834
12814
  loadSkillDir(dir, createIfMissing) {
12835
- if (!existsSync22(dir)) {
12815
+ if (!existsSync23(dir)) {
12836
12816
  if (createIfMissing) {
12837
12817
  try {
12838
12818
  mkdirSync11(dir, { recursive: true });
@@ -12856,7 +12836,7 @@ var SkillManager = class {
12856
12836
  try {
12857
12837
  if (statSync8(fullPath).isDirectory()) {
12858
12838
  const skillMd = join17(fullPath, "SKILL.md");
12859
- if (existsSync22(skillMd)) filePath = skillMd;
12839
+ if (existsSync23(skillMd)) filePath = skillMd;
12860
12840
  else continue;
12861
12841
  } else {
12862
12842
  continue;
@@ -12915,7 +12895,7 @@ var SkillManager = class {
12915
12895
  // src/web/tool-executor-web.ts
12916
12896
  import { randomUUID as randomUUID3 } from "crypto";
12917
12897
  import { tmpdir as tmpdir2 } from "os";
12918
- import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
12898
+ import { existsSync as existsSync24, readFileSync as readFileSync17 } from "fs";
12919
12899
  var ToolExecutorWeb = class _ToolExecutorWeb {
12920
12900
  constructor(registry, ws) {
12921
12901
  this.registry = registry;
@@ -13054,7 +13034,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13054
13034
  if (call.name === "write_file") {
13055
13035
  const filePath = String(call.arguments["path"] ?? "");
13056
13036
  const newContent = String(call.arguments["content"] ?? "");
13057
- if (filePath && existsSync23(filePath)) {
13037
+ if (filePath && existsSync24(filePath)) {
13058
13038
  try {
13059
13039
  const old = readFileSync17(filePath, "utf-8");
13060
13040
  return renderDiff(old, newContent, { filePath });
@@ -13212,12 +13192,13 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13212
13192
  return { callId: call.id, content: message, isError: true };
13213
13193
  }
13214
13194
  }
13215
- if (networkPermission?.action === "confirm" && dangerLevel === "safe") {
13195
+ const requiresHardConfirmation = permission.action === "confirm" && permission.hardBoundary === true;
13196
+ if ((networkPermission?.action === "confirm" || requiresHardConfirmation) && dangerLevel === "safe") {
13216
13197
  this.sendToolCallStart(call);
13217
13198
  toolCallStarted = true;
13218
13199
  const confirmed = await this.confirm(call, "write");
13219
13200
  if (!confirmed) {
13220
- 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.`;
13221
13202
  this.sendToolCallResult(call, rejectionMsg, true);
13222
13203
  return { callId: call.id, content: rejectionMsg, isError: true };
13223
13204
  }
@@ -13234,7 +13215,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13234
13215
  this.sendToolCallResult(call, rejectionMsg, true);
13235
13216
  return { callId: call.id, content: rejectionMsg, isError: true };
13236
13217
  }
13237
- if (classification.decision === "auto-approve") {
13218
+ if (classification.decision === "auto-approve" && !requiresHardConfirmation) {
13238
13219
  this.send({ type: "info", message: `\u26A1 Auto-approved (/auto: ${classification.ruleId})` });
13239
13220
  try {
13240
13221
  const rawContent = await runTool(tool, call.arguments, call.name);
@@ -13252,7 +13233,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13252
13233
  }
13253
13234
  }
13254
13235
  }
13255
- if (this.sessionAutoApprove && dangerLevel === "write") {
13236
+ if (this.sessionAutoApprove && dangerLevel === "write" && !requiresHardConfirmation) {
13256
13237
  try {
13257
13238
  const rawContent = await runTool(tool, call.arguments, call.name);
13258
13239
  const content = truncateOutput(rawContent, call.name);
@@ -13359,7 +13340,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13359
13340
  };
13360
13341
 
13361
13342
  // src/repl/dev-state.ts
13362
- 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";
13363
13344
  import { join as join18 } from "path";
13364
13345
  import { homedir as homedir7 } from "os";
13365
13346
  function getDevStatePath() {
@@ -13367,7 +13348,7 @@ function getDevStatePath() {
13367
13348
  }
13368
13349
  function loadDevState() {
13369
13350
  const path3 = getDevStatePath();
13370
- if (!existsSync24(path3)) return null;
13351
+ if (!existsSync25(path3)) return null;
13371
13352
  const content = readFileSync18(path3, "utf-8").trim();
13372
13353
  return content || null;
13373
13354
  }
@@ -13625,7 +13606,7 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
13625
13606
  }
13626
13607
 
13627
13608
  // src/core/context-files.ts
13628
- import { existsSync as existsSync25, readFileSync as readFileSync19 } from "fs";
13609
+ import { existsSync as existsSync26, readFileSync as readFileSync19 } from "fs";
13629
13610
  import { join as join19, relative as relative3, resolve as resolve9 } from "path";
13630
13611
  function uniqueNonEmpty(values) {
13631
13612
  const seen = /* @__PURE__ */ new Set();
@@ -13704,7 +13685,7 @@ function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
13704
13685
  const skipped = [];
13705
13686
  for (const candidate of candidates) {
13706
13687
  const filePath = join19(dir, candidate);
13707
- if (!existsSync25(filePath)) continue;
13688
+ if (!existsSync26(filePath)) continue;
13708
13689
  const result = readContextFile(level, filePath, cwd, maxBytes);
13709
13690
  if (result.skipped) skipped.push(result.skipped);
13710
13691
  if (result.layer) return { layer: result.layer, skipped };
@@ -13731,7 +13712,7 @@ function loadContextFiles(options) {
13731
13712
  });
13732
13713
  return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
13733
13714
  }
13734
- if (!existsSync25(filePath)) {
13715
+ if (!existsSync26(filePath)) {
13735
13716
  skipped.push({
13736
13717
  level: "single",
13737
13718
  filePath,
@@ -13783,10 +13764,10 @@ function loadContextFiles(options) {
13783
13764
 
13784
13765
  // src/web/session-handler.ts
13785
13766
  import { mkdirSync as mkdirSync16, createWriteStream as createWriteStream3 } from "fs";
13786
- import { join as join26, dirname as dirname10 } from "path";
13767
+ import { join as join26, dirname as dirname11 } from "path";
13787
13768
 
13788
13769
  // src/repl/commands/project-init.ts
13789
- 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";
13790
13771
  import { join as join20 } from "path";
13791
13772
  var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
13792
13773
  "node_modules",
@@ -13870,7 +13851,7 @@ function scanProject(cwd) {
13870
13851
  configFiles: [],
13871
13852
  directoryStructure: ""
13872
13853
  };
13873
- const check = (file) => existsSync26(join20(cwd, file));
13854
+ const check = (file) => existsSync27(join20(cwd, file));
13874
13855
  const configCandidates = [
13875
13856
  "package.json",
13876
13857
  "tsconfig.json",
@@ -14672,8 +14653,8 @@ async function handlePlan(args, ctx) {
14672
14653
  }
14673
14654
 
14674
14655
  // src/web/commands/_shared.ts
14675
- import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync3, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync12, createWriteStream } from "fs";
14676
- 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";
14677
14658
 
14678
14659
  // src/cli/review-prompts.ts
14679
14660
  function buildReviewPrompt(diff, gitContextStr, detailed) {
@@ -14745,13 +14726,13 @@ function readGitDiff(options = {}) {
14745
14726
  }
14746
14727
 
14747
14728
  // src/diagnostics/doctor-report.ts
14748
- import { existsSync as existsSync28, statSync as statSync11 } from "fs";
14729
+ import { existsSync as existsSync29, statSync as statSync11 } from "fs";
14749
14730
  import { join as join23 } from "path";
14750
14731
  import { arch as arch2, platform as platform7, release as release2 } from "os";
14751
14732
 
14752
14733
  // src/diagnostics/crash-log.ts
14753
14734
  import {
14754
- existsSync as existsSync27,
14735
+ existsSync as existsSync28,
14755
14736
  mkdirSync as mkdirSync13,
14756
14737
  readdirSync as readdirSync12,
14757
14738
  readFileSync as readFileSync21,
@@ -14772,7 +14753,7 @@ function truncate(s, n) {
14772
14753
  }
14773
14754
  function listRecentCrashes(limit = 10, configDir) {
14774
14755
  const dir = getLogsDir(configDir);
14775
- if (!existsSync27(dir)) return [];
14756
+ if (!existsSync28(dir)) return [];
14776
14757
  const files = [];
14777
14758
  let names;
14778
14759
  try {
@@ -14805,7 +14786,7 @@ function listRecentCrashes(limit = 10, configDir) {
14805
14786
  }
14806
14787
  function getConfigDirUsage(configDir) {
14807
14788
  const base = configDir ?? join22(homedir8(), CONFIG_DIR_NAME);
14808
- if (!existsSync27(base)) return { totalBytes: 0, entries: [] };
14789
+ if (!existsSync28(base)) return { totalBytes: 0, entries: [] };
14809
14790
  const entries = [];
14810
14791
  let total = 0;
14811
14792
  let names;
@@ -14840,7 +14821,7 @@ function dirSize(path3) {
14840
14821
 
14841
14822
  // src/diagnostics/doctor-report.ts
14842
14823
  function checkFile(label, path3) {
14843
- const exists = existsSync28(path3);
14824
+ const exists = existsSync29(path3);
14844
14825
  let sizeBytes = null;
14845
14826
  if (exists) {
14846
14827
  try {
@@ -14938,7 +14919,7 @@ function buildDoctorReport(options) {
14938
14919
  mcp: {
14939
14920
  enabled: config.get("mcpEnabled"),
14940
14921
  configured: Object.keys(mcpServers).length,
14941
- projectConfigExists: existsSync28(join23(projectRoot, MCP_PROJECT_CONFIG_NAME)),
14922
+ projectConfigExists: existsSync29(join23(projectRoot, MCP_PROJECT_CONFIG_NAME)),
14942
14923
  statuses: options.mcpStatuses ?? []
14943
14924
  },
14944
14925
  recentCrashes: listRecentCrashes(5, configDir),
@@ -15808,7 +15789,7 @@ ${activated.meta.description || ""}` });
15808
15789
  async function handleCommands(_args, ctx) {
15809
15790
  const configDir = ctx.config.getConfigDir();
15810
15791
  const commandsDir = join24(configDir, CUSTOM_COMMANDS_DIR_NAME);
15811
- if (!existsSync29(commandsDir)) {
15792
+ if (!existsSync30(commandsDir)) {
15812
15793
  ctx.send({ type: "info", message: `No custom commands directory.
15813
15794
  Create: ${commandsDir}/ with .md files.` });
15814
15795
  return;
@@ -15908,7 +15889,7 @@ async function handleSecurityReview(args, ctx) {
15908
15889
  async function handleTest(args, ctx) {
15909
15890
  ctx.send({ type: "info", message: "\u{1F9EA} Running tests..." });
15910
15891
  try {
15911
- const { executeTests } = await import("./run-tests-NNXBS4EL.js");
15892
+ const { executeTests } = await import("./run-tests-YSRET52H.js");
15912
15893
  const argStr = args.join(" ").trim();
15913
15894
  let testArgs = {};
15914
15895
  if (argStr) {
@@ -15938,7 +15919,7 @@ async function handleDiff(args, ctx) {
15938
15919
  let modifiedFiles = 0;
15939
15920
  const diffLines2 = [];
15940
15921
  for (const [filePath, { earliest }] of fileMap) {
15941
- const currentContent = existsSync29(filePath) ? (() => {
15922
+ const currentContent = existsSync30(filePath) ? (() => {
15942
15923
  try {
15943
15924
  return readFileSync22(filePath, "utf-8");
15944
15925
  } catch {
@@ -15977,7 +15958,7 @@ async function handleInit(args, ctx) {
15977
15958
  const cwd = process.cwd();
15978
15959
  const targetPath = join24(cwd, "AICLI.md");
15979
15960
  const force = args.includes("--force");
15980
- if (existsSync29(targetPath) && !force) {
15961
+ if (existsSync30(targetPath) && !force) {
15981
15962
  ctx.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
15982
15963
  Use /init --force to overwrite.` });
15983
15964
  return;
@@ -16161,7 +16142,7 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
16161
16142
  return;
16162
16143
  }
16163
16144
  const dirPath = resolve10(sub);
16164
- if (!existsSync29(dirPath)) {
16145
+ if (!existsSync30(dirPath)) {
16165
16146
  ctx.send({ type: "error", message: `Directory not found: ${dirPath}` });
16166
16147
  return;
16167
16148
  }
@@ -17236,7 +17217,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
17236
17217
  this.send({ type: "response_done", content, usage });
17237
17218
  if (pendingTeeSave && isCleanDocumentBody(content)) {
17238
17219
  try {
17239
- mkdirSync16(dirname10(pendingTeeSave), { recursive: true });
17220
+ mkdirSync16(dirname11(pendingTeeSave), { recursive: true });
17240
17221
  const bodyToSave = stripOuterCodeFence(content);
17241
17222
  atomicWriteFileSync(pendingTeeSave, bodyToSave);
17242
17223
  undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
@@ -17412,7 +17393,7 @@ ${summaryContent}`,
17412
17393
  let isError = false;
17413
17394
  let summary;
17414
17395
  try {
17415
- mkdirSync16(dirname10(saveToFile), { recursive: true });
17396
+ mkdirSync16(dirname11(saveToFile), { recursive: true });
17416
17397
  fileStream = createWriteStream3(saveToFile);
17417
17398
  const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
17418
17399
  const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
@@ -17771,7 +17752,7 @@ ${entry.content}`;
17771
17752
  return;
17772
17753
  }
17773
17754
  try {
17774
- const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-WSI7ULRQ.js");
17755
+ const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-WYXYD7YP.js");
17775
17756
  const loaded = loadChatIndex2();
17776
17757
  if (!loaded || loaded.idx.chunks.length === 0) {
17777
17758
  this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
@@ -17807,7 +17788,7 @@ ${entry.content}`;
17807
17788
  }
17808
17789
  async handleMemoryStatus() {
17809
17790
  try {
17810
- const { getChatIndexStatus } = await import("./chat-index-WSI7ULRQ.js");
17791
+ const { getChatIndexStatus } = await import("./chat-index-WYXYD7YP.js");
17811
17792
  const s = getChatIndexStatus();
17812
17793
  this.send({
17813
17794
  type: "memory_status",
@@ -17832,7 +17813,7 @@ ${entry.content}`;
17832
17813
  type: "info",
17833
17814
  message: full ? "\u{1F9E0} Rebuilding chat memory index (this may take a while on first run \u2014 ~117 MB embedder)." : "\u{1F9E0} Refreshing chat memory index (incremental)\u2026"
17834
17815
  });
17835
- const { buildChatIndex } = await import("./chat-index-WSI7ULRQ.js");
17816
+ const { buildChatIndex } = await import("./chat-index-WYXYD7YP.js");
17836
17817
  const stats = await buildChatIndex({
17837
17818
  full,
17838
17819
  onProgress: (p) => {
@@ -17987,8 +17968,8 @@ async function setupProxy(configProxy) {
17987
17968
  }
17988
17969
 
17989
17970
  // src/web/auth.ts
17990
- import { existsSync as existsSync32, readFileSync as readFileSync25, writeFileSync as writeFileSync6, mkdirSync as mkdirSync17, readdirSync as readdirSync16, copyFileSync } from "fs";
17991
- 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";
17992
17973
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
17993
17974
  var USERS_FILE = "users.json";
17994
17975
  var TOKEN_EXPIRY_HOURS = 24;
@@ -18186,7 +18167,7 @@ var AuthManager = class {
18186
18167
  if (err) return err;
18187
18168
  const userDir = this.getUserDataDir(username);
18188
18169
  const globalConfig = join27(this.baseDir, "config.json");
18189
- if (existsSync32(globalConfig)) {
18170
+ if (existsSync33(globalConfig)) {
18190
18171
  try {
18191
18172
  const content = readFileSync25(globalConfig, "utf-8");
18192
18173
  writeFileSync6(join27(userDir, "config.json"), content, "utf-8");
@@ -18194,7 +18175,7 @@ var AuthManager = class {
18194
18175
  }
18195
18176
  }
18196
18177
  const globalMemory = join27(this.baseDir, "memory.md");
18197
- if (existsSync32(globalMemory)) {
18178
+ if (existsSync33(globalMemory)) {
18198
18179
  try {
18199
18180
  const content = readFileSync25(globalMemory, "utf-8");
18200
18181
  writeFileSync6(join27(userDir, "memory.md"), content, "utf-8");
@@ -18202,7 +18183,7 @@ var AuthManager = class {
18202
18183
  }
18203
18184
  }
18204
18185
  const globalHistory = join27(this.baseDir, "history");
18205
- if (existsSync32(globalHistory)) {
18186
+ if (existsSync33(globalHistory)) {
18206
18187
  try {
18207
18188
  const files = readdirSync16(globalHistory).filter((f) => f.endsWith(".json"));
18208
18189
  const userHistory = join27(userDir, "history");
@@ -18219,10 +18200,19 @@ var AuthManager = class {
18219
18200
  }
18220
18201
  // ── Private methods ────────────────────────────────────────────
18221
18202
  loadOrCreate() {
18222
- if (existsSync32(this.usersFile)) {
18203
+ if (existsSync33(this.usersFile)) {
18223
18204
  try {
18224
- return JSON.parse(readFileSync25(this.usersFile, "utf-8"));
18225
- } 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
+ );
18226
18216
  }
18227
18217
  }
18228
18218
  const db = {
@@ -18233,6 +18223,27 @@ var AuthManager = class {
18233
18223
  this.saveDB(db);
18234
18224
  return db;
18235
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
+ }
18236
18247
  save() {
18237
18248
  this.saveDB(this.db);
18238
18249
  }
@@ -18269,7 +18280,7 @@ function getModuleDir() {
18269
18280
  if (typeof import.meta?.url === "string") {
18270
18281
  const url = new URL(import.meta.url);
18271
18282
  const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
18272
- return dirname11(filePath);
18283
+ return dirname12(filePath);
18273
18284
  }
18274
18285
  } catch {
18275
18286
  }
@@ -18342,7 +18353,7 @@ async function startWebServer(options = {}) {
18342
18353
  }
18343
18354
  let skillManager = null;
18344
18355
  const skillsDir = join28(config.getConfigDir(), SKILLS_DIR_NAME);
18345
- if (existsSync33(skillsDir)) {
18356
+ if (existsSync34(skillsDir)) {
18346
18357
  skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
18347
18358
  skillManager.loadSkills(pluginAssets.skillDirs);
18348
18359
  const count = skillManager.listSkills().length;
@@ -18414,13 +18425,13 @@ async function startWebServer(options = {}) {
18414
18425
  };
18415
18426
  const moduleDir = getModuleDir();
18416
18427
  let clientDir = join28(moduleDir, "web", "client");
18417
- if (!existsSync33(clientDir)) {
18428
+ if (!existsSync34(clientDir)) {
18418
18429
  clientDir = join28(moduleDir, "client");
18419
18430
  }
18420
- if (!existsSync33(clientDir)) {
18431
+ if (!existsSync34(clientDir)) {
18421
18432
  clientDir = join28(moduleDir, "..", "..", "src", "web", "client");
18422
18433
  }
18423
- if (!existsSync33(clientDir)) {
18434
+ if (!existsSync34(clientDir)) {
18424
18435
  clientDir = join28(process.cwd(), "src", "web", "client");
18425
18436
  }
18426
18437
  console.log(` Static files: ${clientDir}`);
@@ -18493,8 +18504,8 @@ async function startWebServer(options = {}) {
18493
18504
  const prefix = req.query.prefix || "";
18494
18505
  const targetDir = join28(cwd, prefix);
18495
18506
  try {
18496
- const canonicalTarget = realpathSync(resolve13(targetDir));
18497
- const canonicalCwd = realpathSync(resolve13(cwd));
18507
+ const canonicalTarget = realpathSync2(resolve13(targetDir));
18508
+ const canonicalCwd = realpathSync2(resolve13(cwd));
18498
18509
  if (!canonicalTarget.startsWith(canonicalCwd + sep3) && canonicalTarget !== canonicalCwd) {
18499
18510
  res.json({ files: [] });
18500
18511
  return;
@@ -18508,7 +18519,7 @@ async function startWebServer(options = {}) {
18508
18519
  const entries = readdirSync17(targetDir, { withFileTypes: true });
18509
18520
  const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
18510
18521
  name: e.name,
18511
- path: relative4(cwd, join28(targetDir, e.name)).replace(/\\/g, "/"),
18522
+ path: relative5(cwd, join28(targetDir, e.name)).replace(/\\/g, "/"),
18512
18523
  isDir: e.isDirectory()
18513
18524
  }));
18514
18525
  res.json({ files });
@@ -18549,13 +18560,13 @@ async function startWebServer(options = {}) {
18549
18560
  const authUser = req._authUser;
18550
18561
  const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
18551
18562
  const filePath = join28(histDir, `${id}.json`);
18552
- if (!existsSync33(filePath)) {
18563
+ if (!existsSync34(filePath)) {
18553
18564
  res.status(404).json({ error: "Session not found" });
18554
18565
  return;
18555
18566
  }
18556
18567
  try {
18557
- const canonicalFile = realpathSync(filePath);
18558
- const canonicalDir = realpathSync(histDir);
18568
+ const canonicalFile = realpathSync2(filePath);
18569
+ const canonicalDir = realpathSync2(histDir);
18559
18570
  if (!canonicalFile.startsWith(canonicalDir + sep3)) {
18560
18571
  res.status(404).json({ error: "Session not found" });
18561
18572
  return;
@@ -18579,8 +18590,8 @@ async function startWebServer(options = {}) {
18579
18590
  const cwd = process.cwd();
18580
18591
  const fullPath = resolve13(join28(cwd, filePath));
18581
18592
  try {
18582
- const canonicalFull = realpathSync(fullPath);
18583
- const canonicalCwd = realpathSync(resolve13(cwd));
18593
+ const canonicalFull = realpathSync2(fullPath);
18594
+ const canonicalCwd = realpathSync2(resolve13(cwd));
18584
18595
  if (!canonicalFull.startsWith(canonicalCwd + sep3) && canonicalFull !== canonicalCwd) {
18585
18596
  res.json({ error: "Access denied" });
18586
18597
  return;
@@ -18851,14 +18862,14 @@ function resolveProjectMcpPath() {
18851
18862
  const gitRoot = getGitRoot(cwd);
18852
18863
  const projectRoot = gitRoot ?? cwd;
18853
18864
  const configPath = join28(projectRoot, MCP_PROJECT_CONFIG_NAME);
18854
- return existsSync33(configPath) ? configPath : null;
18865
+ return existsSync34(configPath) ? configPath : null;
18855
18866
  }
18856
18867
  function loadProjectMcpConfig() {
18857
18868
  const cwd = process.cwd();
18858
18869
  const gitRoot = getGitRoot(cwd);
18859
18870
  const projectRoot = gitRoot ?? cwd;
18860
18871
  const configPath = join28(projectRoot, MCP_PROJECT_CONFIG_NAME);
18861
- if (!existsSync33(configPath)) return null;
18872
+ if (!existsSync34(configPath)) return null;
18862
18873
  try {
18863
18874
  const raw = JSON.parse(readFileSync26(configPath, "utf-8"));
18864
18875
  return raw.mcpServers ?? raw;