@cortexkit/aft-opencode 0.38.0 → 0.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7652,10 +7652,10 @@ var require_stringify = __commonJS((exports, module) => {
7652
7652
  replacer = null;
7653
7653
  indent = EMPTY;
7654
7654
  };
7655
- var join9 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
7655
+ var join10 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
7656
7656
  var join_content = (inside, value, gap) => {
7657
7657
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
7658
- return join9(comment, inside, gap);
7658
+ return join10(comment, inside, gap);
7659
7659
  };
7660
7660
  var stringify_string = (holder, key, value) => {
7661
7661
  const raw = get_raw_string_literal(holder, key);
@@ -7677,13 +7677,13 @@ var require_stringify = __commonJS((exports, module) => {
7677
7677
  if (i !== 0) {
7678
7678
  inside += COMMA;
7679
7679
  }
7680
- const before = join9(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
7680
+ const before = join10(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
7681
7681
  inside += before || LF + deeper_gap;
7682
7682
  inside += stringify(i, value, deeper_gap) || STR_NULL;
7683
7683
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
7684
7684
  after_comma = process_comments(value, AFTER(i), deeper_gap);
7685
7685
  }
7686
- inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
7686
+ inside += join10(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
7687
7687
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
7688
7688
  };
7689
7689
  var object_stringify = (value, gap) => {
@@ -7704,13 +7704,13 @@ var require_stringify = __commonJS((exports, module) => {
7704
7704
  inside += COMMA;
7705
7705
  }
7706
7706
  first = false;
7707
- const before = join9(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
7707
+ const before = join10(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
7708
7708
  inside += before || LF + deeper_gap;
7709
7709
  inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
7710
7710
  after_comma = process_comments(value, AFTER(key), deeper_gap);
7711
7711
  };
7712
7712
  keys.forEach(iteratee);
7713
- inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
7713
+ inside += join10(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
7714
7714
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
7715
7715
  };
7716
7716
  function stringify(key, holder, gap) {
@@ -11593,7 +11593,45 @@ function error(message, meta) {
11593
11593
  console.error(`[aft-bridge] ERROR: ${message}`);
11594
11594
  }
11595
11595
  }
11596
+ // ../aft-bridge/dist/bash-format.js
11597
+ function appendPipeStripNote(output, note) {
11598
+ return note ? `${output}
11599
+
11600
+ ${note}` : output;
11601
+ }
11602
+ function formatSeconds(ms) {
11603
+ return `${Number((ms / 1000).toFixed(1))}s`;
11604
+ }
11605
+ function formatForegroundResult(data) {
11606
+ const output = data.output_preview ?? "";
11607
+ const outputPath = data.output_path;
11608
+ const truncated = data.output_truncated === true;
11609
+ const status = data.status;
11610
+ const exit = data.exit_code;
11611
+ let rendered = output;
11612
+ if (truncated && outputPath) {
11613
+ rendered += `
11614
+ [output truncated; full output at ${outputPath}]`;
11615
+ }
11616
+ if (status === "timed_out") {
11617
+ rendered += `
11618
+ [command timed out]`;
11619
+ }
11620
+ if (typeof exit === "number" && exit !== 0) {
11621
+ rendered += `
11622
+ [exit code: ${exit}]`;
11623
+ }
11624
+ return rendered;
11625
+ }
11626
+ function isTerminalStatus(status) {
11627
+ return status === "completed" || status === "failed" || status === "killed" || status === "timed_out";
11628
+ }
11629
+ function sleep(ms) {
11630
+ return new Promise((resolve) => setTimeout(resolve, ms));
11631
+ }
11596
11632
  // ../aft-bridge/dist/bash-hints.js
11633
+ import * as os from "node:os";
11634
+ import * as path from "node:path";
11597
11635
  var CONFLICT_HINT = `
11598
11636
 
11599
11637
  [Hint] Use aft_conflicts to see all conflict regions across files in a single call.`;
@@ -11607,48 +11645,175 @@ function maybeAppendConflictsHint(output) {
11607
11645
  return output;
11608
11646
  return output + CONFLICT_HINT;
11609
11647
  }
11610
- function commandLeadsWithCodeSearch(command) {
11611
- const trimmed = command.trim();
11612
- if (!trimmed)
11613
- return false;
11614
- const afterCd = peelLeadingCdAnd(trimmed);
11615
- if (afterCd === null)
11616
- return false;
11617
- const firstStage = firstPipelineStage(afterCd);
11618
- if (firstStage === null)
11648
+ function commandInvokesCodeSearch(command) {
11649
+ const statements = splitTopLevelStatements(command);
11650
+ if (statements === null)
11619
11651
  return false;
11620
- const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0));
11621
- if (firstToken === null)
11622
- return false;
11623
- return firstToken.token === "grep" || firstToken.token === "rg";
11652
+ for (const statement of statements) {
11653
+ const firstStage = firstPipelineStage(statement);
11654
+ if (firstStage === null)
11655
+ continue;
11656
+ const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0));
11657
+ if (firstToken === null)
11658
+ continue;
11659
+ if (firstToken.token === "grep" || firstToken.token === "rg")
11660
+ return true;
11661
+ }
11662
+ return false;
11624
11663
  }
11625
- function maybeAppendGrepSearchHint(output, command, aftSearchRegistered) {
11664
+ function maybeAppendGrepSearchHint(output, command, aftSearchRegistered, projectRoot) {
11626
11665
  if (output === "")
11627
11666
  return output;
11628
- if (!commandLeadsWithCodeSearch(command))
11667
+ if (!commandInvokesCodeSearch(command))
11629
11668
  return output;
11630
11669
  if (output.includes(GREP_SEARCH_HINT_PREFIX))
11631
11670
  return output;
11671
+ if (shouldSuppressGrepSearchHint(command, projectRoot))
11672
+ return output;
11632
11673
  const hint = aftSearchRegistered ? GREP_SEARCH_AFT_SEARCH_HINT : GREP_SEARCH_GREP_HINT;
11633
11674
  return `${output}
11634
11675
 
11635
11676
  ${hint}`;
11636
11677
  }
11637
- function peelLeadingCdAnd(command) {
11638
- const first = readShellToken(command, skipSpaces(command, 0));
11639
- if (first === null)
11640
- return null;
11641
- if (first.token !== "cd")
11642
- return command;
11643
- const dir = readShellToken(command, skipSpaces(command, first.end));
11644
- if (dir === null)
11678
+ function shouldSuppressGrepSearchHint(command, projectRoot) {
11679
+ const root = projectRoot?.trim();
11680
+ if (!root)
11681
+ return false;
11682
+ const statements = splitTopLevelStatements(command);
11683
+ if (statements === null)
11684
+ return false;
11685
+ let sawCodeSearchStatement = false;
11686
+ for (const statement of statements) {
11687
+ const firstStage = firstPipelineStage(statement);
11688
+ if (firstStage === null)
11689
+ continue;
11690
+ const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0));
11691
+ if (firstToken === null)
11692
+ continue;
11693
+ if (firstToken.token !== "grep" && firstToken.token !== "rg")
11694
+ continue;
11695
+ sawCodeSearchStatement = true;
11696
+ const operands = collectPathOperands(firstStage, firstToken.end);
11697
+ if (operands.length === 0)
11698
+ return false;
11699
+ for (const operand of operands) {
11700
+ if (isPathInsideProject(root, operand))
11701
+ return false;
11702
+ }
11703
+ }
11704
+ return sawCodeSearchStatement;
11705
+ }
11706
+ function collectPathOperands(firstStage, startAfterCommand) {
11707
+ const operands = [];
11708
+ let index = skipSpaces(firstStage, startAfterCommand);
11709
+ while (index < firstStage.length) {
11710
+ const tokenResult = readShellToken(firstStage, index);
11711
+ if (tokenResult === null)
11712
+ break;
11713
+ const { token, end } = tokenResult;
11714
+ if (end <= index)
11715
+ break;
11716
+ index = skipSpaces(firstStage, end);
11717
+ if (token.startsWith("-"))
11718
+ continue;
11719
+ if (looksLikePathOperand(token))
11720
+ operands.push(token);
11721
+ }
11722
+ return operands;
11723
+ }
11724
+ function looksLikePathOperand(token) {
11725
+ return token.includes("/") || token.startsWith("~") || token.startsWith("./") || token.startsWith("../");
11726
+ }
11727
+ function expandTilde(target) {
11728
+ if (!target.startsWith("~"))
11729
+ return target;
11730
+ if (target === "~" || target.startsWith("~/")) {
11731
+ return path.join(os.homedir(), target.slice(1));
11732
+ }
11733
+ return target;
11734
+ }
11735
+ function resolvePathOperand(projectRoot, operand) {
11736
+ const expanded = expandTilde(operand);
11737
+ return path.isAbsolute(expanded) ? path.resolve(expanded) : path.resolve(projectRoot, expanded);
11738
+ }
11739
+ function isPathInsideProject(projectRoot, operand) {
11740
+ const root = path.resolve(projectRoot);
11741
+ const resolved = resolvePathOperand(root, operand);
11742
+ const rel = path.relative(root, resolved);
11743
+ return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
11744
+ }
11745
+ function splitTopLevelStatements(command) {
11746
+ const statements = [];
11747
+ let start = 0;
11748
+ let quote = "none";
11749
+ let escaped = false;
11750
+ let inBacktick = false;
11751
+ let parenDepth = 0;
11752
+ for (let index = 0;index < command.length; index++) {
11753
+ const ch = command[index];
11754
+ if (escaped) {
11755
+ escaped = false;
11756
+ continue;
11757
+ }
11758
+ if (quote === "single") {
11759
+ if (ch === "'")
11760
+ quote = "none";
11761
+ continue;
11762
+ }
11763
+ if (quote === "double") {
11764
+ if (ch === "\\")
11765
+ escaped = true;
11766
+ else if (ch === '"')
11767
+ quote = "none";
11768
+ continue;
11769
+ }
11770
+ if (inBacktick) {
11771
+ if (ch === "`")
11772
+ inBacktick = false;
11773
+ continue;
11774
+ }
11775
+ if (ch === "\\") {
11776
+ escaped = true;
11777
+ continue;
11778
+ }
11779
+ if (ch === "'") {
11780
+ quote = "single";
11781
+ continue;
11782
+ }
11783
+ if (ch === '"') {
11784
+ quote = "double";
11785
+ continue;
11786
+ }
11787
+ if (ch === "`") {
11788
+ inBacktick = true;
11789
+ continue;
11790
+ }
11791
+ if (ch === "(") {
11792
+ parenDepth++;
11793
+ continue;
11794
+ }
11795
+ if (ch === ")") {
11796
+ if (parenDepth > 0)
11797
+ parenDepth--;
11798
+ continue;
11799
+ }
11800
+ if (parenDepth > 0)
11801
+ continue;
11802
+ const next = command[index + 1];
11803
+ if (ch === "&" && next === "&" || ch === "|" && next === "|") {
11804
+ statements.push(command.slice(start, index));
11805
+ index++;
11806
+ start = index + 1;
11807
+ } else if (ch === ";" || ch === `
11808
+ ` || ch === "&") {
11809
+ statements.push(command.slice(start, index));
11810
+ start = index + 1;
11811
+ }
11812
+ }
11813
+ if (quote !== "none" || inBacktick || escaped)
11645
11814
  return null;
11646
- if (!dir.token)
11647
- return command;
11648
- const afterDir = skipSpaces(command, dir.end);
11649
- if (!command.startsWith("&&", afterDir))
11650
- return command;
11651
- return command.slice(afterDir + 2).trim();
11815
+ statements.push(command.slice(start));
11816
+ return statements;
11652
11817
  }
11653
11818
  function firstPipelineStage(command) {
11654
11819
  let quote = "none";
@@ -11756,8 +11921,8 @@ function resolveBashKillTimeout(modelTimeout, foregroundWaitMs) {
11756
11921
  }
11757
11922
  // ../aft-bridge/dist/bridge.js
11758
11923
  import { spawn } from "node:child_process";
11759
- import { homedir } from "node:os";
11760
- import { join } from "node:path";
11924
+ import { homedir as homedir2 } from "node:os";
11925
+ import { join as join2 } from "node:path";
11761
11926
  import { StringDecoder } from "node:string_decoder";
11762
11927
 
11763
11928
  // ../aft-bridge/dist/command-timeouts.js
@@ -12135,7 +12300,7 @@ class BinaryBridge {
12135
12300
  `;
12136
12301
  const keepBridgeOnTimeout = options?.keepBridgeOnTimeout === true;
12137
12302
  let requestSentAt = Date.now();
12138
- const response = await new Promise((resolve, reject) => {
12303
+ const response = await new Promise((resolve2, reject) => {
12139
12304
  const timer = setTimeout(() => {
12140
12305
  const entry = this.pending.get(id);
12141
12306
  if (!entry)
@@ -12170,7 +12335,7 @@ class BinaryBridge {
12170
12335
  entry.reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
12171
12336
  this.handleTimeout(requestSessionId);
12172
12337
  }, effectiveTimeoutMs);
12173
- this.pending.set(id, { resolve, reject, timer, onProgress: options?.onProgress, command });
12338
+ this.pending.set(id, { resolve: resolve2, reject, timer, onProgress: options?.onProgress, command });
12174
12339
  if (!this.process?.stdin?.writable) {
12175
12340
  this.pending.delete(id);
12176
12341
  clearTimeout(timer);
@@ -12268,15 +12433,15 @@ class BinaryBridge {
12268
12433
  if (this.process) {
12269
12434
  const proc = this.process;
12270
12435
  this.process = null;
12271
- return new Promise((resolve) => {
12436
+ return new Promise((resolve2) => {
12272
12437
  const forceKillTimer = setTimeout(() => {
12273
12438
  proc.kill("SIGKILL");
12274
- resolve();
12439
+ resolve2();
12275
12440
  }, 5000);
12276
12441
  proc.once("exit", () => {
12277
12442
  clearTimeout(forceKillTimer);
12278
12443
  this.logVia("Process exited during shutdown");
12279
- resolve();
12444
+ resolve2();
12280
12445
  });
12281
12446
  proc.kill("SIGTERM");
12282
12447
  });
@@ -12321,15 +12486,15 @@ class BinaryBridge {
12321
12486
  return;
12322
12487
  const proc = this.process;
12323
12488
  this.process = null;
12324
- await new Promise((resolve) => {
12489
+ await new Promise((resolve2) => {
12325
12490
  const forceKillTimer = setTimeout(() => {
12326
12491
  proc.kill("SIGKILL");
12327
- resolve();
12492
+ resolve2();
12328
12493
  }, 5000);
12329
12494
  proc.once("exit", () => {
12330
12495
  clearTimeout(forceKillTimer);
12331
12496
  this.logVia("Process exited during coordinated binary replacement");
12332
- resolve();
12497
+ resolve2();
12333
12498
  });
12334
12499
  proc.kill("SIGTERM");
12335
12500
  });
@@ -12356,7 +12521,7 @@ class BinaryBridge {
12356
12521
  })();
12357
12522
  const useFastembedBackend = semanticBackend === undefined || semanticBackend === "fastembed" || semanticBackend === "";
12358
12523
  const ortDir = typeof this.configOverrides._ort_dylib_dir === "string" && useFastembedBackend ? this.configOverrides._ort_dylib_dir : null;
12359
- const ortLibraryPath = ortDir == null ? null : join(ortDir, process.platform === "win32" ? "onnxruntime.dll" : process.platform === "darwin" ? "libonnxruntime.dylib" : "libonnxruntime.so");
12524
+ const ortLibraryPath = ortDir == null ? null : join2(ortDir, process.platform === "win32" ? "onnxruntime.dll" : process.platform === "darwin" ? "libonnxruntime.dylib" : "libonnxruntime.so");
12360
12525
  const envPath = process.platform === "win32" && ortDir ? `${ortDir};${process.env.PATH ?? ""}` : process.env.PATH;
12361
12526
  const env = {
12362
12527
  ...process.env,
@@ -12364,7 +12529,7 @@ class BinaryBridge {
12364
12529
  };
12365
12530
  this.logVia(`bridge.spawnProcess: useFastembedBackend=${useFastembedBackend}, ` + `parentORT=${process.env.ORT_DYLIB_PATH ?? "(unset)"}, ` + `ortLibraryPath=${ortLibraryPath ?? "(none)"}`);
12366
12531
  if (useFastembedBackend) {
12367
- env.FASTEMBED_CACHE_DIR = process.env.FASTEMBED_CACHE_DIR || (typeof this.configOverrides.storage_dir === "string" ? join(this.configOverrides.storage_dir, "semantic", "models") : join(homedir() || "", ".cache", "fastembed"));
12532
+ env.FASTEMBED_CACHE_DIR = process.env.FASTEMBED_CACHE_DIR || (typeof this.configOverrides.storage_dir === "string" ? join2(this.configOverrides.storage_dir, "semantic", "models") : join2(homedir2() || "", ".cache", "fastembed"));
12368
12533
  if (process.env.ORT_DYLIB_PATH) {
12369
12534
  this.logVia(`ORT_DYLIB_PATH inherited from parent env: ${process.env.ORT_DYLIB_PATH}`);
12370
12535
  } else if (ortLibraryPath) {
@@ -12706,12 +12871,23 @@ function coerceStringArray(value) {
12706
12871
  }
12707
12872
  return [];
12708
12873
  }
12874
+ function isEmptyParam(value) {
12875
+ if (value === undefined || value === null)
12876
+ return true;
12877
+ if (typeof value === "string")
12878
+ return value.length === 0;
12879
+ if (Array.isArray(value))
12880
+ return value.length === 0;
12881
+ if (typeof value === "object")
12882
+ return Object.keys(value).length === 0;
12883
+ return false;
12884
+ }
12709
12885
  // ../aft-bridge/dist/downloader.js
12710
12886
  import { spawnSync } from "node:child_process";
12711
12887
  import { createHash, randomUUID } from "node:crypto";
12712
12888
  import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
12713
- import { homedir as homedir2 } from "node:os";
12714
- import { join as join2 } from "node:path";
12889
+ import { homedir as homedir3 } from "node:os";
12890
+ import { join as join3 } from "node:path";
12715
12891
  import { Readable } from "node:stream";
12716
12892
  import { pipeline } from "node:stream/promises";
12717
12893
 
@@ -12768,11 +12944,11 @@ function isExpectedCachedBinary(binaryPath, tag) {
12768
12944
  function getCacheDir() {
12769
12945
  if (process.platform === "win32") {
12770
12946
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
12771
- const base2 = localAppData || join2(homedir2(), "AppData", "Local");
12772
- return join2(base2, "aft", "bin");
12947
+ const base2 = localAppData || join3(homedir3(), "AppData", "Local");
12948
+ return join3(base2, "aft", "bin");
12773
12949
  }
12774
- const base = process.env.XDG_CACHE_HOME || join2(homedir2(), ".cache");
12775
- return join2(base, "aft", "bin");
12950
+ const base = process.env.XDG_CACHE_HOME || join3(homedir3(), ".cache");
12951
+ return join3(base, "aft", "bin");
12776
12952
  }
12777
12953
  function getBinaryName() {
12778
12954
  return process.platform === "win32" ? "aft.exe" : "aft";
@@ -12780,7 +12956,7 @@ function getBinaryName() {
12780
12956
  function getCachedBinaryPath(version) {
12781
12957
  if (!version)
12782
12958
  return null;
12783
- const binaryPath = join2(getCacheDir(), version, getBinaryName());
12959
+ const binaryPath = join3(getCacheDir(), version, getBinaryName());
12784
12960
  return existsSync(binaryPath) ? binaryPath : null;
12785
12961
  }
12786
12962
  async function downloadBinary(version) {
@@ -12797,16 +12973,16 @@ async function downloadBinary(version) {
12797
12973
  return null;
12798
12974
  }
12799
12975
  const tag = rawTag.startsWith("v") ? rawTag : `v${rawTag}`;
12800
- const versionedCacheDir = join2(getCacheDir(), tag);
12976
+ const versionedCacheDir = join3(getCacheDir(), tag);
12801
12977
  const binaryName = getBinaryName();
12802
- const binaryPath = join2(versionedCacheDir, binaryName);
12978
+ const binaryPath = join3(versionedCacheDir, binaryName);
12803
12979
  if (existsSync(binaryPath) && isExpectedCachedBinary(binaryPath, tag)) {
12804
12980
  return binaryPath;
12805
12981
  }
12806
12982
  const downloadUrl = `https://github.com/${REPO}/releases/download/${tag}/${assetName}`;
12807
12983
  const checksumUrl = `https://github.com/${REPO}/releases/download/${tag}/checksums.sha256`;
12808
12984
  log(`Downloading AFT binary (${tag}) for ${platformKey}...`);
12809
- const lockPath = join2(versionedCacheDir, ".download.lock");
12985
+ const lockPath = join3(versionedCacheDir, ".download.lock");
12810
12986
  let releaseLock = null;
12811
12987
  let binaryController = null;
12812
12988
  let checksumController = null;
@@ -12958,7 +13134,7 @@ async function acquireDownloadLock(lockPath) {
12958
13134
  if (Date.now() - startedAt > DOWNLOAD_LOCK_TIMEOUT_MS) {
12959
13135
  throw new Error(`Timed out waiting for download lock: ${lockPath}`);
12960
13136
  }
12961
- await new Promise((resolve) => setTimeout(resolve, 100));
13137
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
12962
13138
  }
12963
13139
  }
12964
13140
  }
@@ -13044,18 +13220,18 @@ function stripJsoncSymbols(value) {
13044
13220
  // ../aft-bridge/dist/migration.js
13045
13221
  import { spawnSync as spawnSync2 } from "node:child_process";
13046
13222
  import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
13047
- import { homedir as homedir4, tmpdir } from "node:os";
13048
- import { dirname as dirname2, join as join5 } from "node:path";
13223
+ import { homedir as homedir5, tmpdir } from "node:os";
13224
+ import { dirname as dirname2, join as join6 } from "node:path";
13049
13225
 
13050
13226
  // ../aft-bridge/dist/paths.js
13051
13227
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync } from "node:fs";
13052
- import { dirname, join as join3 } from "node:path";
13228
+ import { dirname, join as join4 } from "node:path";
13053
13229
  function resolveHarnessStoragePath(storageRoot, harness, ...segments) {
13054
- return join3(storageRoot, harness, ...segments);
13230
+ return join4(storageRoot, harness, ...segments);
13055
13231
  }
13056
13232
  function repairRootScopedStorageFile(storageRoot, harness, fileName) {
13057
13233
  const harnessPath = resolveHarnessStoragePath(storageRoot, harness, fileName);
13058
- const rootPath = join3(storageRoot, fileName);
13234
+ const rootPath = join4(storageRoot, fileName);
13059
13235
  if (existsSync2(harnessPath) || !existsSync2(rootPath))
13060
13236
  return harnessPath;
13061
13237
  try {
@@ -13101,8 +13277,8 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
13101
13277
  import { execSync } from "node:child_process";
13102
13278
  import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, openSync as openSync2, readSync, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
13103
13279
  import { createRequire as createRequire2 } from "node:module";
13104
- import { homedir as homedir3 } from "node:os";
13105
- import { join as join4 } from "node:path";
13280
+ import { homedir as homedir4 } from "node:os";
13281
+ import { join as join5 } from "node:path";
13106
13282
  var ensureBinaryForResolver = ensureBinary;
13107
13283
  function copyToVersionedCache(npmBinaryPath, knownVersion) {
13108
13284
  try {
@@ -13111,9 +13287,9 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
13111
13287
  return null;
13112
13288
  const tag = version.startsWith("v") ? version : `v${version}`;
13113
13289
  const cacheDir = getCacheDir();
13114
- const versionedDir = join4(cacheDir, tag);
13290
+ const versionedDir = join5(cacheDir, tag);
13115
13291
  const ext = process.platform === "win32" ? ".exe" : "";
13116
- const cachedPath = join4(versionedDir, `aft${ext}`);
13292
+ const cachedPath = join5(versionedDir, `aft${ext}`);
13117
13293
  if (existsSync3(cachedPath)) {
13118
13294
  const cachedVersion = readBinaryVersion(cachedPath);
13119
13295
  if (cachedVersion === version)
@@ -13143,18 +13319,18 @@ function normalizeBareVersion(version) {
13143
13319
  return version.startsWith("v") ? version.slice(1) : version;
13144
13320
  }
13145
13321
  function homeDirFromEnv(env) {
13146
- return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir3();
13322
+ return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir4();
13147
13323
  }
13148
13324
  function cacheDirFromEnv(env) {
13149
13325
  if (process.platform === "win32") {
13150
- const base2 = env.LOCALAPPDATA || env.APPDATA || join4(homeDirFromEnv(env), "AppData", "Local");
13151
- return join4(base2, "aft", "bin");
13326
+ const base2 = env.LOCALAPPDATA || env.APPDATA || join5(homeDirFromEnv(env), "AppData", "Local");
13327
+ return join5(base2, "aft", "bin");
13152
13328
  }
13153
- const base = env.XDG_CACHE_HOME || join4(homeDirFromEnv(env), ".cache");
13154
- return join4(base, "aft", "bin");
13329
+ const base = env.XDG_CACHE_HOME || join5(homeDirFromEnv(env), ".cache");
13330
+ return join5(base, "aft", "bin");
13155
13331
  }
13156
13332
  function cachedBinaryPathFromEnv(version, env, ext) {
13157
- const binaryPath = join4(cacheDirFromEnv(env), version, `aft${ext}`);
13333
+ const binaryPath = join5(cacheDirFromEnv(env), version, `aft${ext}`);
13158
13334
  return existsSync3(binaryPath) ? binaryPath : null;
13159
13335
  }
13160
13336
  function isExpectedCachedBinary2(binaryPath, expectedVersion) {
@@ -13273,7 +13449,7 @@ function findBinarySync(expectedVersion) {
13273
13449
  return usable;
13274
13450
  }
13275
13451
  } catch {}
13276
- const cargoPath = join4(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
13452
+ const cargoPath = join5(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
13277
13453
  if (existsSync3(cargoPath)) {
13278
13454
  const usable = probeBinaryCandidate(cargoPath, "cargo", expectedVersion);
13279
13455
  if (usable)
@@ -13319,22 +13495,22 @@ function dataHome() {
13319
13495
  if (process.env.XDG_DATA_HOME)
13320
13496
  return process.env.XDG_DATA_HOME;
13321
13497
  if (process.platform === "win32") {
13322
- return process.env.LOCALAPPDATA || process.env.APPDATA || join5(homeDir(), "AppData", "Local");
13498
+ return process.env.LOCALAPPDATA || process.env.APPDATA || join6(homeDir(), "AppData", "Local");
13323
13499
  }
13324
- return join5(homeDir(), ".local", "share");
13500
+ return join6(homeDir(), ".local", "share");
13325
13501
  }
13326
13502
  function homeDir() {
13327
13503
  if (process.platform === "win32")
13328
- return process.env.USERPROFILE || process.env.HOME || homedir4();
13329
- return process.env.HOME || homedir4();
13504
+ return process.env.USERPROFILE || process.env.HOME || homedir5();
13505
+ return process.env.HOME || homedir5();
13330
13506
  }
13331
13507
  function resolveLegacyStorageRoot(harness) {
13332
13508
  if (harness === "pi")
13333
- return join5(homeDir(), ".pi", "agent", "aft");
13334
- return join5(dataHome(), "opencode", "storage", "plugin", "aft");
13509
+ return join6(homeDir(), ".pi", "agent", "aft");
13510
+ return join6(dataHome(), "opencode", "storage", "plugin", "aft");
13335
13511
  }
13336
13512
  function resolveCortexKitStorageRoot() {
13337
- return join5(dataHome(), "cortexkit", "aft");
13513
+ return join6(dataHome(), "cortexkit", "aft");
13338
13514
  }
13339
13515
  function tail(value) {
13340
13516
  if (!value)
@@ -13348,12 +13524,12 @@ function spawnErrorLabel(error2) {
13348
13524
  return [code, error2.message].filter(Boolean).join(": ");
13349
13525
  }
13350
13526
  function migrationLogPath(newRoot, harness, logger) {
13351
- const desired = join5(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
13527
+ const desired = join6(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
13352
13528
  try {
13353
13529
  mkdirSync4(dirname2(desired), { recursive: true });
13354
13530
  return desired;
13355
13531
  } catch (err) {
13356
- const fallback = join5(tmpdir(), `aft-migration-${harness}-${Date.now()}.jsonl`);
13532
+ const fallback = join6(tmpdir(), `aft-migration-${harness}-${Date.now()}.jsonl`);
13357
13533
  logger?.warn?.(`Failed to create AFT migration log directory ${dirname2(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
13358
13534
  return fallback;
13359
13535
  }
@@ -13416,13 +13592,13 @@ async function ensureStorageMigrated(opts) {
13416
13592
  }
13417
13593
  // ../aft-bridge/dist/npm-resolver.js
13418
13594
  import { readdirSync, statSync as statSync2 } from "node:fs";
13419
- import { homedir as homedir5 } from "node:os";
13420
- import { delimiter, dirname as dirname3, isAbsolute, join as join6 } from "node:path";
13595
+ import { homedir as homedir6 } from "node:os";
13596
+ import { delimiter, dirname as dirname3, isAbsolute as isAbsolute2, join as join7 } from "node:path";
13421
13597
  function defaultDeps() {
13422
13598
  return {
13423
13599
  platform: process.platform,
13424
13600
  env: process.env,
13425
- home: homedir5(),
13601
+ home: homedir6(),
13426
13602
  execPath: process.execPath
13427
13603
  };
13428
13604
  }
@@ -13441,16 +13617,16 @@ function npmFromPath(deps) {
13441
13617
  const raw = deps.env.PATH ?? deps.env.Path ?? "";
13442
13618
  for (const entry of raw.split(delimiter)) {
13443
13619
  const dir = entry.trim().replace(/^"|"$/g, "");
13444
- if (!dir || !isAbsolute(dir))
13620
+ if (!dir || !isAbsolute2(dir))
13445
13621
  continue;
13446
- if (isFile(join6(dir, name)))
13622
+ if (isFile(join7(dir, name)))
13447
13623
  return dir;
13448
13624
  }
13449
13625
  return null;
13450
13626
  }
13451
13627
  function npmAdjacentToNode(deps) {
13452
13628
  const dir = dirname3(deps.execPath);
13453
- return isFile(join6(dir, npmBinaryName(deps.platform))) ? dir : null;
13629
+ return isFile(join7(dir, npmBinaryName(deps.platform))) ? dir : null;
13454
13630
  }
13455
13631
  function highestVersionedNodeBin(installsDir, name) {
13456
13632
  let entries;
@@ -13459,8 +13635,8 @@ function highestVersionedNodeBin(installsDir, name) {
13459
13635
  } catch {
13460
13636
  return null;
13461
13637
  }
13462
- const candidates = entries.filter((v) => isFile(join6(installsDir, v, "bin", name))).sort((a, b) => compareVersionsDesc(a, b));
13463
- return candidates.length > 0 ? join6(installsDir, candidates[0], "bin") : null;
13638
+ const candidates = entries.filter((v) => isFile(join7(installsDir, v, "bin", name))).sort((a, b) => compareVersionsDesc(a, b));
13639
+ return candidates.length > 0 ? join7(installsDir, candidates[0], "bin") : null;
13464
13640
  }
13465
13641
  function compareVersionsDesc(a, b) {
13466
13642
  const pa = a.replace(/^v/, "").split(".").map((n) => Number.parseInt(n, 10));
@@ -13485,22 +13661,22 @@ function wellKnownNpmDirs(deps) {
13485
13661
  const programFiles = env.ProgramFiles || "C:\\Program Files";
13486
13662
  const appData = env.APPDATA;
13487
13663
  const localAppData = env.LOCALAPPDATA;
13488
- push(join6(programFiles, "nodejs"));
13664
+ push(join7(programFiles, "nodejs"));
13489
13665
  if (appData)
13490
- push(join6(appData, "npm"));
13666
+ push(join7(appData, "npm"));
13491
13667
  if (localAppData)
13492
- push(join6(localAppData, "Volta", "bin"));
13668
+ push(join7(localAppData, "Volta", "bin"));
13493
13669
  if (env.NVM_SYMLINK)
13494
13670
  push(env.NVM_SYMLINK);
13495
13671
  } else {
13496
13672
  if (env.NVM_BIN)
13497
13673
  push(env.NVM_BIN);
13498
- push(highestVersionedNodeBin(join6(home, ".nvm", "versions", "node"), name));
13499
- push(highestVersionedNodeBin(join6(home, ".local", "share", "mise", "installs", "node"), name));
13500
- push(highestVersionedNodeBin(join6(home, ".asdf", "installs", "nodejs"), name));
13501
- push(join6(home, ".volta", "bin"));
13502
- push(join6(home, ".asdf", "shims"));
13503
- const systemDirs = deps.systemNpmDirs ?? (platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : ["/usr/local/bin", "/usr/bin", join6(home, ".local", "bin")]);
13674
+ push(highestVersionedNodeBin(join7(home, ".nvm", "versions", "node"), name));
13675
+ push(highestVersionedNodeBin(join7(home, ".local", "share", "mise", "installs", "node"), name));
13676
+ push(highestVersionedNodeBin(join7(home, ".asdf", "installs", "nodejs"), name));
13677
+ push(join7(home, ".volta", "bin"));
13678
+ push(join7(home, ".asdf", "shims"));
13679
+ const systemDirs = deps.systemNpmDirs ?? (platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : ["/usr/local/bin", "/usr/bin", join7(home, ".local", "bin")]);
13504
13680
  for (const dir of systemDirs)
13505
13681
  push(dir);
13506
13682
  }
@@ -13510,12 +13686,12 @@ function resolveNpm(deps = defaultDeps()) {
13510
13686
  const name = npmBinaryName(deps.platform);
13511
13687
  const onPath = npmFromPath(deps);
13512
13688
  if (onPath)
13513
- return { command: join6(onPath, name), binDir: onPath };
13689
+ return { command: join7(onPath, name), binDir: onPath };
13514
13690
  const adjacent = npmAdjacentToNode(deps);
13515
13691
  if (adjacent)
13516
- return { command: join6(adjacent, name), binDir: adjacent };
13692
+ return { command: join7(adjacent, name), binDir: adjacent };
13517
13693
  for (const dir of wellKnownNpmDirs(deps)) {
13518
- const candidate = join6(dir, name);
13694
+ const candidate = join7(dir, name);
13519
13695
  if (isFile(candidate))
13520
13696
  return { command: candidate, binDir: dir };
13521
13697
  }
@@ -13535,7 +13711,7 @@ function isNpmAvailable(deps = defaultDeps()) {
13535
13711
  import { execFileSync } from "node:child_process";
13536
13712
  import { createHash as createHash2 } from "node:crypto";
13537
13713
  import { chmodSync as chmodSync3, closeSync as closeSync3, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync5, openSync as openSync3, readdirSync as readdirSync2, readFileSync as readFileSync3, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync3, symlinkSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
13538
- import { basename, dirname as dirname4, isAbsolute as isAbsolute2, join as join7, relative, resolve, win32 } from "node:path";
13714
+ import { basename, dirname as dirname4, isAbsolute as isAbsolute3, join as join8, relative as relative2, resolve as resolve2, win32 } from "node:path";
13539
13715
  import { Readable as Readable2 } from "node:stream";
13540
13716
  import { pipeline as pipeline2 } from "node:stream/promises";
13541
13717
  var ORT_VERSION = "1.24.4";
@@ -13601,10 +13777,10 @@ function getManualInstallHint() {
13601
13777
  }
13602
13778
  async function ensureOnnxRuntime(storageDir) {
13603
13779
  const info = getPlatformInfo();
13604
- const ortVersionDir = join7(storageDir, "onnxruntime", ORT_VERSION);
13780
+ const ortVersionDir = join8(storageDir, "onnxruntime", ORT_VERSION);
13605
13781
  const libName = info?.libName ?? "libonnxruntime.dylib";
13606
13782
  const resolvedOrtDir = resolveCachedOnnxRuntimeDir(ortVersionDir, libName);
13607
- const libPath = join7(resolvedOrtDir, libName);
13783
+ const libPath = join8(resolvedOrtDir, libName);
13608
13784
  if (existsSync5(libPath)) {
13609
13785
  const meta = readOnnxInstalledMeta(ortVersionDir);
13610
13786
  if (meta?.sha256) {
@@ -13634,9 +13810,9 @@ async function ensureOnnxRuntime(storageDir) {
13634
13810
  warn(`ONNX Runtime auto-download not available for ${process.platform}/${process.arch}. Install manually: ${getManualInstallHint()}`);
13635
13811
  return null;
13636
13812
  }
13637
- const onnxBaseDir = join7(storageDir, "onnxruntime");
13813
+ const onnxBaseDir = join8(storageDir, "onnxruntime");
13638
13814
  mkdirSync5(onnxBaseDir, { recursive: true });
13639
- const lockPath = join7(onnxBaseDir, ONNX_LOCK_FILE);
13815
+ const lockPath = join8(onnxBaseDir, ONNX_LOCK_FILE);
13640
13816
  cleanupAbandonedStagingDirs(onnxBaseDir);
13641
13817
  if (!acquireLock(lockPath)) {
13642
13818
  warn(`ONNX Runtime install already in progress in another process (lock: ${lockPath}). Skipping.`);
@@ -13655,7 +13831,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
13655
13831
  for (const entry of entries) {
13656
13832
  if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
13657
13833
  continue;
13658
- const stagingDir = join7(onnxBaseDir, entry);
13834
+ const stagingDir = join8(onnxBaseDir, entry);
13659
13835
  const parts = entry.split(".");
13660
13836
  const pidStr = parts[parts.length - 2];
13661
13837
  const pid = pidStr ? Number.parseInt(pidStr, 10) : NaN;
@@ -13692,7 +13868,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
13692
13868
  }
13693
13869
  function cleanupIncompleteTargetIfUnowned(ortDir) {
13694
13870
  try {
13695
- if (existsSync5(ortDir) && !existsSync5(join7(ortDir, ONNX_INSTALLED_META_FILE))) {
13871
+ if (existsSync5(ortDir) && !existsSync5(join8(ortDir, ONNX_INSTALLED_META_FILE))) {
13696
13872
  log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
13697
13873
  rmSync2(ortDir, { recursive: true, force: true });
13698
13874
  }
@@ -13720,8 +13896,8 @@ function parseOnnxVersionFromDirectoryPath(value) {
13720
13896
  return null;
13721
13897
  }
13722
13898
  function isPathInsideRoot(root, candidate) {
13723
- const rel = relative(root, candidate);
13724
- return rel === "" || !rel.startsWith("../") && !rel.startsWith("..\\") && rel !== ".." && !isAbsolute2(rel) && !win32.isAbsolute(rel);
13899
+ const rel = relative2(root, candidate);
13900
+ return rel === "" || !rel.startsWith("../") && !rel.startsWith("..\\") && rel !== ".." && !isAbsolute3(rel) && !win32.isAbsolute(rel);
13725
13901
  }
13726
13902
  function detectOnnxVersion(libDir, libName) {
13727
13903
  try {
@@ -13736,7 +13912,7 @@ function detectOnnxVersion(libDir, libName) {
13736
13912
  if (version)
13737
13913
  return version;
13738
13914
  }
13739
- const base = join7(libDir, libName);
13915
+ const base = join8(libDir, libName);
13740
13916
  if (existsSync5(base)) {
13741
13917
  try {
13742
13918
  const real = realpathSync(base);
@@ -13772,7 +13948,7 @@ function pathEntriesForPlatform() {
13772
13948
  return pathEnvValue().split(delimiter2).map((entry) => entry.trim().replace(/^"|"$/g, "")).filter((entry) => {
13773
13949
  if (!entry || entry === "." || entry.includes("\x00"))
13774
13950
  return false;
13775
- return isAbsolute2(entry) || win32.isAbsolute(entry);
13951
+ return isAbsolute3(entry) || win32.isAbsolute(entry);
13776
13952
  });
13777
13953
  }
13778
13954
  function directoryContainsLibrary(dir, libName) {
@@ -13788,10 +13964,10 @@ function directoryContainsLibrary(dir, libName) {
13788
13964
  }
13789
13965
  }
13790
13966
  function resolveCachedOnnxRuntimeDir(ortVersionDir, libName) {
13791
- if (existsSync5(join7(ortVersionDir, libName)))
13967
+ if (existsSync5(join8(ortVersionDir, libName)))
13792
13968
  return ortVersionDir;
13793
- const libSubdir = join7(ortVersionDir, "lib");
13794
- if (existsSync5(join7(libSubdir, libName)))
13969
+ const libSubdir = join8(ortVersionDir, "lib");
13970
+ if (existsSync5(join8(libSubdir, libName)))
13795
13971
  return libSubdir;
13796
13972
  return ortVersionDir;
13797
13973
  }
@@ -13806,12 +13982,12 @@ function findSystemOnnxRuntime(libName) {
13806
13982
  } else if (process.platform === "win32") {
13807
13983
  const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
13808
13984
  const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
13809
- searchPaths.push(join7(programFiles, "onnxruntime", "lib"), join7(programFiles, "Microsoft ONNX Runtime", "lib"), join7(programFiles, "Microsoft Machine Learning", "lib"), join7(programFilesX86, "onnxruntime", "lib"), ...(() => {
13985
+ searchPaths.push(join8(programFiles, "onnxruntime", "lib"), join8(programFiles, "Microsoft ONNX Runtime", "lib"), join8(programFiles, "Microsoft Machine Learning", "lib"), join8(programFilesX86, "onnxruntime", "lib"), ...(() => {
13810
13986
  const nugetPaths = [];
13811
13987
  const userProfile = process.env.USERPROFILE ?? "";
13812
13988
  if (!userProfile)
13813
13989
  return nugetPaths;
13814
- const nugetPackageDir = join7(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
13990
+ const nugetPackageDir = join8(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
13815
13991
  if (!existsSync5(nugetPackageDir))
13816
13992
  return nugetPaths;
13817
13993
  try {
@@ -13820,7 +13996,7 @@ function findSystemOnnxRuntime(libName) {
13820
13996
  continue;
13821
13997
  if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
13822
13998
  continue;
13823
- nugetPaths.push(join7(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join7(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
13999
+ nugetPaths.push(join8(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join8(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
13824
14000
  }
13825
14001
  } catch (err) {
13826
14002
  warn(`Failed to scan NuGet ONNX Runtime cache ${nugetPackageDir}: ${err instanceof Error ? err.message : String(err)}`);
@@ -13832,7 +14008,7 @@ function findSystemOnnxRuntime(libName) {
13832
14008
  const normalizeCase = process.platform === "win32" || process.platform === "darwin";
13833
14009
  const seen = new Set;
13834
14010
  const uniquePaths = searchPaths.filter((p) => {
13835
- let key = resolve(p).replace(/[/\\]+$/, "");
14011
+ let key = resolve2(p).replace(/[/\\]+$/, "");
13836
14012
  if (normalizeCase)
13837
14013
  key = key.toLowerCase();
13838
14014
  if (seen.has(key))
@@ -13842,7 +14018,7 @@ function findSystemOnnxRuntime(libName) {
13842
14018
  });
13843
14019
  const unknownVersionPaths = [];
13844
14020
  for (const dir of uniquePaths) {
13845
- const libPath = join7(dir, libName);
14021
+ const libPath = join8(dir, libName);
13846
14022
  if (process.platform === "win32") {
13847
14023
  if (!directoryContainsLibrary(dir, libName))
13848
14024
  continue;
@@ -13908,19 +14084,19 @@ function validateExtractedTree(stagingRoot) {
13908
14084
  const walk = (dir) => {
13909
14085
  const entries = readdirSync2(dir);
13910
14086
  for (const entry of entries) {
13911
- const fullPath = join7(dir, entry);
14087
+ const fullPath = join8(dir, entry);
13912
14088
  const lst = lstatSync(fullPath);
13913
14089
  if (lst.isSymbolicLink()) {
13914
14090
  const linkTarget = readlinkSync(fullPath);
13915
- const resolvedTarget = resolve(dirname4(fullPath), linkTarget);
13916
- const rel2 = relative(realRoot, resolvedTarget);
13917
- if (rel2.startsWith("..") || isAbsolute2(rel2) || win32.isAbsolute(rel2)) {
14091
+ const resolvedTarget = resolve2(dirname4(fullPath), linkTarget);
14092
+ const rel2 = relative2(realRoot, resolvedTarget);
14093
+ if (rel2.startsWith("..") || isAbsolute3(rel2) || win32.isAbsolute(rel2)) {
13918
14094
  throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
13919
14095
  }
13920
14096
  continue;
13921
14097
  }
13922
- const rel = relative(realRoot, fullPath);
13923
- if (rel.startsWith("..") || isAbsolute2(rel) || win32.isAbsolute(rel)) {
14098
+ const rel = relative2(realRoot, fullPath);
14099
+ if (rel.startsWith("..") || isAbsolute3(rel) || win32.isAbsolute(rel)) {
13924
14100
  throw new Error(`extracted entry ${fullPath} escapes staging root`);
13925
14101
  }
13926
14102
  if (lst.isDirectory()) {
@@ -13943,7 +14119,7 @@ async function downloadOnnxRuntime(info, targetDir) {
13943
14119
  const tmpDir = `${targetDir}.tmp.${process.pid}.${Date.now().toString(36)}`;
13944
14120
  try {
13945
14121
  mkdirSync5(tmpDir, { recursive: true });
13946
- const archivePath = join7(tmpDir, `onnxruntime.${info.archiveType}`);
14122
+ const archivePath = join8(tmpDir, `onnxruntime.${info.archiveType}`);
13947
14123
  await downloadFileWithCap(url, archivePath);
13948
14124
  const archiveSha256 = sha256File(archivePath);
13949
14125
  log(`ONNX Runtime archive sha256=${archiveSha256}`);
@@ -13959,7 +14135,7 @@ async function downloadOnnxRuntime(info, targetDir) {
13959
14135
  unlinkSync3(archivePath);
13960
14136
  } catch {}
13961
14137
  validateExtractedTree(tmpDir);
13962
- const extractedDir = join7(tmpDir, info.assetName, "lib");
14138
+ const extractedDir = join8(tmpDir, info.assetName, "lib");
13963
14139
  if (!existsSync5(extractedDir)) {
13964
14140
  throw new Error(`Expected directory not found: ${extractedDir}`);
13965
14141
  }
@@ -13968,7 +14144,7 @@ async function downloadOnnxRuntime(info, targetDir) {
13968
14144
  const realFiles = [];
13969
14145
  const symlinks = [];
13970
14146
  for (const libFile of libFiles) {
13971
- const src = join7(extractedDir, libFile);
14147
+ const src = join8(extractedDir, libFile);
13972
14148
  try {
13973
14149
  const stat = lstatSync(src);
13974
14150
  log(`ORT extract: ${libFile} — isSymlink=${stat.isSymbolicLink()}, isFile=${stat.isFile()}, size=${stat.size}`);
@@ -13983,7 +14159,7 @@ async function downloadOnnxRuntime(info, targetDir) {
13983
14159
  }
13984
14160
  }
13985
14161
  copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks);
13986
- const libPath = join7(targetDir, info.libName);
14162
+ const libPath = join8(targetDir, info.libName);
13987
14163
  let libHash = null;
13988
14164
  try {
13989
14165
  libHash = sha256File(libPath);
@@ -14008,8 +14184,8 @@ async function downloadOnnxRuntime(info, targetDir) {
14008
14184
  function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, copyFile = copyFileSync3) {
14009
14185
  const requiredLibs = new Set([info.libName]);
14010
14186
  for (const libFile of realFiles) {
14011
- const src = join7(extractedDir, libFile);
14012
- const dst = join7(targetDir, libFile);
14187
+ const src = join8(extractedDir, libFile);
14188
+ const dst = join8(targetDir, libFile);
14013
14189
  try {
14014
14190
  copyFile(src, dst);
14015
14191
  if (process.platform !== "win32") {
@@ -14025,12 +14201,12 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
14025
14201
  }
14026
14202
  const targetRoot = realpathSync(targetDir);
14027
14203
  for (const link of symlinks) {
14028
- const dst = join7(targetDir, link.name);
14204
+ const dst = join8(targetDir, link.name);
14029
14205
  try {
14030
14206
  unlinkSync3(dst);
14031
14207
  } catch {}
14032
- const dstForContainment = join7(targetRoot, link.name);
14033
- const resolvedTarget = resolve(dirname4(dstForContainment), link.target);
14208
+ const dstForContainment = join8(targetRoot, link.name);
14209
+ const resolvedTarget = resolve2(dirname4(dstForContainment), link.target);
14034
14210
  if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
14035
14211
  const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
14036
14212
  if (requiredLibs.has(link.name)) {
@@ -14050,7 +14226,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
14050
14226
  log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
14051
14227
  }
14052
14228
  }
14053
- const requiredPath = join7(targetDir, info.libName);
14229
+ const requiredPath = join8(targetDir, info.libName);
14054
14230
  if (!existsSync5(requiredPath)) {
14055
14231
  rmSync2(targetDir, { recursive: true, force: true });
14056
14232
  throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
@@ -14077,17 +14253,17 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
14077
14253
  ...sha256 ? { sha256 } : {},
14078
14254
  archiveSha256
14079
14255
  };
14080
- writeFileSync2(join7(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
14256
+ writeFileSync2(join8(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
14081
14257
  } catch (err) {
14082
14258
  log(`[onnx] failed to write installed-meta in ${installDir}: ${err}`);
14083
14259
  }
14084
14260
  }
14085
14261
  function readOnnxInstalledMeta(installDir) {
14086
- const path = join7(installDir, ONNX_INSTALLED_META_FILE);
14262
+ const path2 = join8(installDir, ONNX_INSTALLED_META_FILE);
14087
14263
  try {
14088
- if (!statSync3(path).isFile())
14264
+ if (!statSync3(path2).isFile())
14089
14265
  return null;
14090
- const raw = readFileSync3(path, "utf8");
14266
+ const raw = readFileSync3(path2, "utf8");
14091
14267
  const parsed = JSON.parse(raw);
14092
14268
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
14093
14269
  return null;
@@ -14101,9 +14277,9 @@ function readOnnxInstalledMeta(installDir) {
14101
14277
  return null;
14102
14278
  }
14103
14279
  }
14104
- function sha256File(path) {
14280
+ function sha256File(path2) {
14105
14281
  const hash = createHash2("sha256");
14106
- hash.update(readFileSync3(path));
14282
+ hash.update(readFileSync3(path2));
14107
14283
  return hash.digest("hex");
14108
14284
  }
14109
14285
  function acquireLock(lockPath) {
@@ -14939,6 +15115,91 @@ function normalizeKey(projectRoot) {
14939
15115
  return stripped;
14940
15116
  }
14941
15117
  }
15118
+ // ../aft-bridge/dist/tool-format.js
15119
+ function asPlainObject(value) {
15120
+ if (!value || typeof value !== "object" || Array.isArray(value))
15121
+ return;
15122
+ return value;
15123
+ }
15124
+ function candidateLocation(candidate) {
15125
+ const file = typeof candidate.file === "string" && candidate.file.length > 0 ? candidate.file : undefined;
15126
+ if (!file)
15127
+ return;
15128
+ const line = typeof candidate.line === "number" && Number.isFinite(candidate.line) ? candidate.line : undefined;
15129
+ return line === undefined ? file : `${file}:${line}`;
15130
+ }
15131
+ function stringifyData(data) {
15132
+ if (data === undefined)
15133
+ return;
15134
+ try {
15135
+ return JSON.stringify(data, null, 2);
15136
+ } catch {
15137
+ return String(data);
15138
+ }
15139
+ }
15140
+ function formatBridgeErrorMessage(command, response, params = {}) {
15141
+ const code = typeof response.code === "string" && response.code.length > 0 ? response.code : undefined;
15142
+ const message = typeof response.message === "string" && response.message.length > 0 ? response.message : `${command} failed`;
15143
+ const data = asPlainObject(response.data);
15144
+ const rawCandidates = Array.isArray(response.candidates) ? response.candidates : Array.isArray(data?.candidates) ? data.candidates : undefined;
15145
+ const rawSymbol = typeof response.symbol === "string" && response.symbol.length > 0 ? response.symbol : typeof data?.symbol === "string" && data.symbol.length > 0 ? data.symbol : undefined;
15146
+ if (code === "ambiguous_target" || code === "target_symbol_not_in_file") {
15147
+ const candidates = (rawCandidates ?? []).map(asPlainObject).filter((candidate) => candidate !== undefined).map(candidateLocation).filter((candidate) => candidate !== undefined);
15148
+ if (candidates.length > 0) {
15149
+ const symbol = typeof params.toSymbol === "string" && params.toSymbol.length > 0 ? params.toSymbol : rawSymbol;
15150
+ const target = symbol ? `multiple symbols named "${symbol}"` : message.replace(/[.!?]+$/, "");
15151
+ const action = code === "ambiguous_target" ? "Pass toFile to disambiguate" : "Try one of these files for toFile";
15152
+ return `${command}: ${code} — ${target}. ${action}:
15153
+ ${candidates.map((candidate) => ` - ${candidate}`).join(`
15154
+ `)}`;
15155
+ }
15156
+ }
15157
+ if (!code)
15158
+ return message;
15159
+ const lines = [`${command}: ${code} — ${message}`];
15160
+ const extras = collectStructuredExtras(response);
15161
+ if (extras)
15162
+ lines.push(`data: ${extras}`);
15163
+ return lines.join(`
15164
+ `);
15165
+ }
15166
+ function collectStructuredExtras(response) {
15167
+ const reserved = new Set([
15168
+ "id",
15169
+ "success",
15170
+ "code",
15171
+ "message",
15172
+ "data",
15173
+ "status_bar",
15174
+ "bg_completions"
15175
+ ]);
15176
+ const extras = {};
15177
+ for (const [key, value] of Object.entries(response)) {
15178
+ if (reserved.has(key))
15179
+ continue;
15180
+ extras[key] = value;
15181
+ }
15182
+ if (Object.keys(extras).length === 0) {
15183
+ return stringifyData(response.data);
15184
+ }
15185
+ if (response.data !== undefined)
15186
+ extras.data = response.data;
15187
+ return stringifyData(extras);
15188
+ }
15189
+ function formatReadFooter(agentSpecifiedRange, data, options) {
15190
+ if (agentSpecifiedRange)
15191
+ return "";
15192
+ if (!data.truncated)
15193
+ return "";
15194
+ const startLine = data.start_line;
15195
+ const endLine = data.end_line;
15196
+ const totalLines = data.total_lines;
15197
+ if (startLine === undefined || endLine === undefined || totalLines === undefined) {
15198
+ return "";
15199
+ }
15200
+ return `
15201
+ (Showing lines ${startLine}-${endLine} of ${totalLines}. Use ${options.rangeHint} to read other sections.)`;
15202
+ }
14942
15203
  // ../aft-bridge/dist/zoom-format.js
14943
15204
  function formatZoomText(targetLabel, response) {
14944
15205
  const range = response.range;
@@ -15019,16 +15280,39 @@ function formatZoomMultiTargetResult(entries) {
15019
15280
 
15020
15281
  `) };
15021
15282
  }
15283
+ function isRustZoomBatchEnvelope(response) {
15284
+ if (!Array.isArray(response.symbols) || response.symbols.length === 0) {
15285
+ return false;
15286
+ }
15287
+ return response.symbols.every((entry) => {
15288
+ if (!entry || typeof entry !== "object")
15289
+ return false;
15290
+ const row = entry;
15291
+ return typeof row.name === "string" && row.response !== undefined && row.response !== null;
15292
+ });
15293
+ }
15294
+ function unwrapRustZoomBatchEnvelope(response) {
15295
+ if (!isRustZoomBatchEnvelope(response)) {
15296
+ return null;
15297
+ }
15298
+ const names = [];
15299
+ const responses = [];
15300
+ for (const entry of response.symbols) {
15301
+ names.push(entry.name);
15302
+ responses.push(entry.response);
15303
+ }
15304
+ return { names, responses };
15305
+ }
15022
15306
  // src/bg-notifications.ts
15023
15307
  import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
15024
15308
 
15025
15309
  // src/logger.ts
15026
15310
  import * as fs from "node:fs";
15027
- import * as os from "node:os";
15028
- import * as path from "node:path";
15311
+ import * as os2 from "node:os";
15312
+ import * as path2 from "node:path";
15029
15313
  var TAG = "[aft-plugin]";
15030
15314
  var isTestEnv = process.env.BUN_TEST === "1" || false;
15031
- var logFile = path.join(os.tmpdir(), isTestEnv ? "aft-plugin-test.log" : "aft-plugin.log");
15315
+ var logFile = path2.join(os2.tmpdir(), isTestEnv ? "aft-plugin-test.log" : "aft-plugin.log");
15032
15316
  var useStderr = process.env.AFT_LOG_STDERR === "1";
15033
15317
  var buffer = [];
15034
15318
  var flushTimer = null;
@@ -15972,8 +16256,8 @@ function formatDuration(completion) {
15972
16256
 
15973
16257
  // src/config.ts
15974
16258
  import { existsSync as existsSync6, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
15975
- import { homedir as homedir6 } from "node:os";
15976
- import { join as join9 } from "node:path";
16259
+ import { homedir as homedir7 } from "node:os";
16260
+ import { join as join10 } from "node:path";
15977
16261
  var import_comment_json = __toESM(require_src2(), 1);
15978
16262
 
15979
16263
  // ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
@@ -16741,10 +17025,10 @@ function mergeDefs(...defs) {
16741
17025
  function cloneDef(schema) {
16742
17026
  return mergeDefs(schema._zod.def);
16743
17027
  }
16744
- function getElementAtPath(obj, path2) {
16745
- if (!path2)
17028
+ function getElementAtPath(obj, path3) {
17029
+ if (!path3)
16746
17030
  return obj;
16747
- return path2.reduce((acc, key) => acc?.[key], obj);
17031
+ return path3.reduce((acc, key) => acc?.[key], obj);
16748
17032
  }
16749
17033
  function promiseAllObject(promisesObj) {
16750
17034
  const keys = Object.keys(promisesObj);
@@ -17125,11 +17409,11 @@ function aborted(x, startIndex = 0) {
17125
17409
  }
17126
17410
  return false;
17127
17411
  }
17128
- function prefixIssues(path2, issues) {
17412
+ function prefixIssues(path3, issues) {
17129
17413
  return issues.map((iss) => {
17130
17414
  var _a;
17131
17415
  (_a = iss).path ?? (_a.path = []);
17132
- iss.path.unshift(path2);
17416
+ iss.path.unshift(path3);
17133
17417
  return iss;
17134
17418
  });
17135
17419
  }
@@ -17312,7 +17596,7 @@ function formatError(error3, mapper = (issue2) => issue2.message) {
17312
17596
  }
17313
17597
  function treeifyError(error3, mapper = (issue2) => issue2.message) {
17314
17598
  const result = { errors: [] };
17315
- const processError = (error4, path2 = []) => {
17599
+ const processError = (error4, path3 = []) => {
17316
17600
  var _a, _b;
17317
17601
  for (const issue2 of error4.issues) {
17318
17602
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -17322,7 +17606,7 @@ function treeifyError(error3, mapper = (issue2) => issue2.message) {
17322
17606
  } else if (issue2.code === "invalid_element") {
17323
17607
  processError({ issues: issue2.issues }, issue2.path);
17324
17608
  } else {
17325
- const fullpath = [...path2, ...issue2.path];
17609
+ const fullpath = [...path3, ...issue2.path];
17326
17610
  if (fullpath.length === 0) {
17327
17611
  result.errors.push(mapper(issue2));
17328
17612
  continue;
@@ -17354,8 +17638,8 @@ function treeifyError(error3, mapper = (issue2) => issue2.message) {
17354
17638
  }
17355
17639
  function toDotPath(_path) {
17356
17640
  const segs = [];
17357
- const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
17358
- for (const seg of path2) {
17641
+ const path3 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
17642
+ for (const seg of path3) {
17359
17643
  if (typeof seg === "number")
17360
17644
  segs.push(`[${seg}]`);
17361
17645
  else if (typeof seg === "symbol")
@@ -29102,13 +29386,13 @@ function resolveRef(ref, ctx) {
29102
29386
  if (!ref.startsWith("#")) {
29103
29387
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
29104
29388
  }
29105
- const path2 = ref.slice(1).split("/").filter(Boolean);
29106
- if (path2.length === 0) {
29389
+ const path3 = ref.slice(1).split("/").filter(Boolean);
29390
+ if (path3.length === 0) {
29107
29391
  return ctx.rootSchema;
29108
29392
  }
29109
29393
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
29110
- if (path2[0] === defsKey) {
29111
- const key = path2[1];
29394
+ if (path3[0] === defsKey) {
29395
+ const key = path3[1];
29112
29396
  if (!key || !ctx.defs[key]) {
29113
29397
  throw new Error(`Reference not found: ${ref}`);
29114
29398
  }
@@ -29806,9 +30090,9 @@ function extractCommentsForPreservation(content) {
29806
30090
  }
29807
30091
  return comments;
29808
30092
  }
29809
- function ensureRecordAtPath(root, path2) {
30093
+ function ensureRecordAtPath(root, path3) {
29810
30094
  let current = root;
29811
- for (const segment of path2) {
30095
+ for (const segment of path3) {
29812
30096
  const existing = current[segment];
29813
30097
  if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
29814
30098
  current[segment] = {};
@@ -29817,9 +30101,9 @@ function ensureRecordAtPath(root, path2) {
29817
30101
  }
29818
30102
  return current;
29819
30103
  }
29820
- function hasPath(root, path2) {
30104
+ function hasPath(root, path3) {
29821
30105
  let current = root;
29822
- for (const segment of path2) {
30106
+ for (const segment of path3) {
29823
30107
  if (!current || typeof current !== "object" || Array.isArray(current))
29824
30108
  return false;
29825
30109
  const record2 = current;
@@ -29829,9 +30113,9 @@ function hasPath(root, path2) {
29829
30113
  }
29830
30114
  return true;
29831
30115
  }
29832
- function setPath(root, path2, value) {
29833
- const parent = ensureRecordAtPath(root, path2.slice(0, -1));
29834
- parent[path2[path2.length - 1]] = value;
30116
+ function setPath(root, path3, value) {
30117
+ const parent = ensureRecordAtPath(root, path3.slice(0, -1));
30118
+ parent[path3[path3.length - 1]] = value;
29835
30119
  }
29836
30120
  function migrateRawConfig(rawConfig, configPath, logger) {
29837
30121
  const oldKeys = [];
@@ -30168,17 +30452,17 @@ function getOpenCodeConfigDir() {
30168
30452
  if (envDir) {
30169
30453
  return envDir;
30170
30454
  }
30171
- const xdgConfig = process.env.XDG_CONFIG_HOME || join9(homedir6(), ".config");
30172
- return join9(xdgConfig, "opencode");
30455
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join10(homedir7(), ".config");
30456
+ return join10(xdgConfig, "opencode");
30173
30457
  }
30174
30458
  function loadAftConfig(projectDirectory) {
30175
30459
  const configDir = getOpenCodeConfigDir();
30176
- const userBasePath = join9(configDir, "aft");
30460
+ const userBasePath = join10(configDir, "aft");
30177
30461
  migrateAftConfigFile(`${userBasePath}.jsonc`);
30178
30462
  migrateAftConfigFile(`${userBasePath}.json`);
30179
30463
  const userDetected = detectConfigFile(userBasePath);
30180
30464
  const userConfigPath = userDetected.format !== "none" ? userDetected.path : `${userBasePath}.json`;
30181
- const projectBasePath = join9(projectDirectory, ".opencode", "aft");
30465
+ const projectBasePath = join10(projectDirectory, ".opencode", "aft");
30182
30466
  migrateAftConfigFile(`${projectBasePath}.jsonc`);
30183
30467
  migrateAftConfigFile(`${projectBasePath}.json`);
30184
30468
  const projectDetected = detectConfigFile(projectBasePath);
@@ -30204,8 +30488,8 @@ function loadAftConfig(projectDirectory) {
30204
30488
 
30205
30489
  // src/notifications.ts
30206
30490
  import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
30207
- import { homedir as homedir7, platform } from "node:os";
30208
- import { join as join10 } from "node:path";
30491
+ import { homedir as homedir8, platform } from "node:os";
30492
+ import { join as join11 } from "node:path";
30209
30493
  function isTuiMode() {
30210
30494
  return process.env.OPENCODE_CLIENT === "cli";
30211
30495
  }
@@ -30225,18 +30509,18 @@ var FEATURE_MARKER = `${AFT_MARKER} New in`;
30225
30509
  var WARNING_MARKER = `${AFT_MARKER} ⚠️`;
30226
30510
  var STATUS_MARKER = `${AFT_MARKER} ✅`;
30227
30511
  function getDesktopStatePath() {
30228
- const os2 = platform();
30229
- const home = homedir7();
30230
- if (os2 === "darwin") {
30231
- return join10(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
30512
+ const os3 = platform();
30513
+ const home = homedir8();
30514
+ if (os3 === "darwin") {
30515
+ return join11(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
30232
30516
  }
30233
- if (os2 === "linux") {
30234
- const xdgConfig = process.env.XDG_CONFIG_HOME || join10(home, ".config");
30235
- return join10(xdgConfig, "ai.opencode.desktop", "opencode.global.dat");
30517
+ if (os3 === "linux") {
30518
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join11(home, ".config");
30519
+ return join11(xdgConfig, "ai.opencode.desktop", "opencode.global.dat");
30236
30520
  }
30237
- if (os2 === "win32") {
30238
- const appData = process.env.APPDATA || join10(home, "AppData", "Roaming");
30239
- return join10(appData, "ai.opencode.desktop", "opencode.global.dat");
30521
+ if (os3 === "win32") {
30522
+ const appData = process.env.APPDATA || join11(home, "AppData", "Roaming");
30523
+ return join11(appData, "ai.opencode.desktop", "opencode.global.dat");
30240
30524
  }
30241
30525
  return null;
30242
30526
  }
@@ -30706,37 +30990,37 @@ import { dirname as dirname7 } from "node:path";
30706
30990
  import { spawn as spawn2 } from "node:child_process";
30707
30991
  import { cpSync, existsSync as existsSync9, mkdtempSync, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "node:fs";
30708
30992
  import { tmpdir as tmpdir3 } from "node:os";
30709
- import { basename as basename2, dirname as dirname6, join as join13 } from "node:path";
30993
+ import { basename as basename2, dirname as dirname6, join as join14 } from "node:path";
30710
30994
  var import_comment_json3 = __toESM(require_src2(), 1);
30711
30995
 
30712
30996
  // src/hooks/auto-update-checker/checker.ts
30713
30997
  var import_comment_json2 = __toESM(require_src2(), 1);
30714
30998
  import { existsSync as existsSync8, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
30715
- import { homedir as homedir9 } from "node:os";
30716
- import { dirname as dirname5, isAbsolute as isAbsolute3, join as join12, resolve as resolve2 } from "node:path";
30999
+ import { homedir as homedir10 } from "node:os";
31000
+ import { dirname as dirname5, isAbsolute as isAbsolute4, join as join13, resolve as resolve3 } from "node:path";
30717
31001
  import { fileURLToPath } from "node:url";
30718
31002
 
30719
31003
  // src/hooks/auto-update-checker/constants.ts
30720
- import { homedir as homedir8, platform as platform2 } from "node:os";
30721
- import { join as join11 } from "node:path";
31004
+ import { homedir as homedir9, platform as platform2 } from "node:os";
31005
+ import { join as join12 } from "node:path";
30722
31006
  var PACKAGE_NAME = "@cortexkit/aft-opencode";
30723
31007
  var NPM_REGISTRY_URL = "https://registry.npmjs.org";
30724
31008
  var NPM_FETCH_TIMEOUT = 1e4;
30725
31009
  function getOpenCodeCacheRoot() {
30726
31010
  if (platform2() === "win32") {
30727
- return join11(process.env.LOCALAPPDATA ?? homedir8(), "opencode");
31011
+ return join12(process.env.LOCALAPPDATA ?? homedir9(), "opencode");
30728
31012
  }
30729
- return join11(homedir8(), ".cache", "opencode");
31013
+ return join12(homedir9(), ".cache", "opencode");
30730
31014
  }
30731
31015
  function getOpenCodeConfigRoot() {
30732
31016
  if (platform2() === "win32") {
30733
- return join11(process.env.APPDATA ?? join11(homedir8(), "AppData", "Roaming"), "opencode");
31017
+ return join12(process.env.APPDATA ?? join12(homedir9(), "AppData", "Roaming"), "opencode");
30734
31018
  }
30735
- return join11(process.env.XDG_CONFIG_HOME ?? join11(homedir8(), ".config"), "opencode");
31019
+ return join12(process.env.XDG_CONFIG_HOME ?? join12(homedir9(), ".config"), "opencode");
30736
31020
  }
30737
- var CACHE_DIR = join11(getOpenCodeCacheRoot(), "packages");
30738
- var USER_OPENCODE_CONFIG = join11(getOpenCodeConfigRoot(), "opencode.json");
30739
- var USER_OPENCODE_CONFIG_JSONC = join11(getOpenCodeConfigRoot(), "opencode.jsonc");
31021
+ var CACHE_DIR = join12(getOpenCodeCacheRoot(), "packages");
31022
+ var USER_OPENCODE_CONFIG = join12(getOpenCodeConfigRoot(), "opencode.json");
31023
+ var USER_OPENCODE_CONFIG_JSONC = join12(getOpenCodeConfigRoot(), "opencode.jsonc");
30740
31024
 
30741
31025
  // src/hooks/auto-update-checker/types.ts
30742
31026
  var NpmPackageEnvelopeSchema = exports_external.object({
@@ -30794,8 +31078,8 @@ function extractChannel(version2) {
30794
31078
  }
30795
31079
  function getConfigPaths(directory) {
30796
31080
  return [
30797
- join12(directory, ".opencode", "opencode.json"),
30798
- join12(directory, ".opencode", "opencode.jsonc"),
31081
+ join13(directory, ".opencode", "opencode.json"),
31082
+ join13(directory, ".opencode", "opencode.jsonc"),
30799
31083
  USER_OPENCODE_CONFIG,
30800
31084
  USER_OPENCODE_CONFIG_JSONC
30801
31085
  ];
@@ -30808,9 +31092,9 @@ function resolvePathPluginSpec(spec, configPath) {
30808
31092
  return spec.replace(/^file:\/\//, "");
30809
31093
  }
30810
31094
  }
30811
- if (isAbsolute3(spec) || /^[A-Za-z]:[\\/]/.test(spec))
31095
+ if (isAbsolute4(spec) || /^[A-Za-z]:[\\/]/.test(spec))
30812
31096
  return spec;
30813
- return resolve2(dirname5(configPath), spec);
31097
+ return resolve3(dirname5(configPath), spec);
30814
31098
  }
30815
31099
  function getLocalDevPath(directory) {
30816
31100
  for (const configPath of getConfigPaths(directory)) {
@@ -30822,7 +31106,7 @@ function getLocalDevPath(directory) {
30822
31106
  for (const entry of plugins) {
30823
31107
  if (entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))
30824
31108
  continue;
30825
- if (entry.startsWith("file://") || entry.startsWith(".") || isAbsolute3(entry)) {
31109
+ if (entry.startsWith("file://") || entry.startsWith(".") || isAbsolute4(entry)) {
30826
31110
  const localPath = resolvePathPluginSpec(entry, configPath);
30827
31111
  const pkgPath = findPackageJsonUp(localPath);
30828
31112
  if (!pkgPath)
@@ -30841,7 +31125,7 @@ function findPackageJsonUp(startPath) {
30841
31125
  const stat = statSync4(startPath);
30842
31126
  let dir = stat.isDirectory() ? startPath : dirname5(startPath);
30843
31127
  for (let i = 0;i < 10; i++) {
30844
- const pkgPath = join12(dir, "package.json");
31128
+ const pkgPath = join13(dir, "package.json");
30845
31129
  if (existsSync8(pkgPath)) {
30846
31130
  try {
30847
31131
  const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync6(pkgPath, "utf-8")));
@@ -30902,7 +31186,7 @@ function findPluginEntry(directory) {
30902
31186
  }
30903
31187
  var cachedPackageVersion = null;
30904
31188
  function getSpecCachePackageJsonPath(spec) {
30905
- return join12(CACHE_DIR, spec, "node_modules", PACKAGE_NAME, "package.json");
31189
+ return join13(CACHE_DIR, spec, "node_modules", PACKAGE_NAME, "package.json");
30906
31190
  }
30907
31191
  function getCachedVersion(spec) {
30908
31192
  if (!spec && cachedPackageVersion)
@@ -30911,7 +31195,7 @@ function getCachedVersion(spec) {
30911
31195
  getCurrentRuntimePackageJsonPath(),
30912
31196
  spec ? getSpecCachePackageJsonPath(spec) : null,
30913
31197
  getSpecCachePackageJsonPath(`${PACKAGE_NAME}@latest`),
30914
- join12(homedir9(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
31198
+ join13(homedir10(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
30915
31199
  ].filter(isString);
30916
31200
  for (const packageJsonPath of candidates) {
30917
31201
  try {
@@ -30959,10 +31243,10 @@ async function getLatestVersion(channel = "latest", options = {}) {
30959
31243
  // src/hooks/auto-update-checker/cache.ts
30960
31244
  var pendingSnapshots = new Map;
30961
31245
  function createAutoUpdateSnapshot(installDir, packageJsonPath, packageName) {
30962
- const packageDir = join13(installDir, "node_modules", packageName);
30963
- const lockfilePath = join13(installDir, "package-lock.json");
30964
- const tempDir = mkdtempSync(join13(tmpdir3(), "aft-auto-update-"));
30965
- const stagedPackageDir = existsSync9(packageDir) ? join13(tempDir, "package") : null;
31246
+ const packageDir = join14(installDir, "node_modules", packageName);
31247
+ const lockfilePath = join14(installDir, "package-lock.json");
31248
+ const tempDir = mkdtempSync(join14(tmpdir3(), "aft-auto-update-"));
31249
+ const stagedPackageDir = existsSync9(packageDir) ? join14(tempDir, "package") : null;
30966
31250
  if (stagedPackageDir)
30967
31251
  cpSync(packageDir, stagedPackageDir, { recursive: true });
30968
31252
  return {
@@ -31003,7 +31287,7 @@ function stripPackageNameFromPath(pathValue, packageName) {
31003
31287
  return current;
31004
31288
  }
31005
31289
  function removeFromPackageLock(installDir, packageName) {
31006
- const lockPath = join13(installDir, "package-lock.json");
31290
+ const lockPath = join14(installDir, "package-lock.json");
31007
31291
  if (!existsSync9(lockPath))
31008
31292
  return false;
31009
31293
  try {
@@ -31052,7 +31336,7 @@ function ensureDependencyVersion(packageJsonPath, packageName, version2) {
31052
31336
  }
31053
31337
  }
31054
31338
  function removeInstalledPackage(installDir, packageName) {
31055
- const packageDir = join13(installDir, "node_modules", packageName);
31339
+ const packageDir = join14(installDir, "node_modules", packageName);
31056
31340
  if (!existsSync9(packageDir))
31057
31341
  return false;
31058
31342
  rmSync3(packageDir, { recursive: true, force: true });
@@ -31065,13 +31349,13 @@ function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackage
31065
31349
  const nodeModulesDir = stripPackageNameFromPath(packageDir, PACKAGE_NAME);
31066
31350
  if (nodeModulesDir && basename2(nodeModulesDir) === "node_modules") {
31067
31351
  const installDir = dirname6(nodeModulesDir);
31068
- const packageJsonPath = join13(installDir, "package.json");
31352
+ const packageJsonPath = join14(installDir, "package.json");
31069
31353
  if (existsSync9(packageJsonPath))
31070
31354
  return { installDir, packageJsonPath };
31071
31355
  }
31072
31356
  return null;
31073
31357
  }
31074
- const legacyPackageJsonPath = join13(dirname6(CACHE_DIR), "package.json");
31358
+ const legacyPackageJsonPath = join14(dirname6(CACHE_DIR), "package.json");
31075
31359
  if (existsSync9(legacyPackageJsonPath)) {
31076
31360
  return { installDir: dirname6(CACHE_DIR), packageJsonPath: legacyPackageJsonPath };
31077
31361
  }
@@ -31402,7 +31686,7 @@ import {
31402
31686
  statSync as statSync6,
31403
31687
  writeFileSync as writeFileSync8
31404
31688
  } from "node:fs";
31405
- import { join as join16 } from "node:path";
31689
+ import { join as join17 } from "node:path";
31406
31690
 
31407
31691
  // src/lsp-cache.ts
31408
31692
  import {
@@ -31414,36 +31698,36 @@ import {
31414
31698
  unlinkSync as unlinkSync5,
31415
31699
  writeFileSync as writeFileSync7
31416
31700
  } from "node:fs";
31417
- import { homedir as homedir10 } from "node:os";
31418
- import { join as join14 } from "node:path";
31701
+ import { homedir as homedir11 } from "node:os";
31702
+ import { join as join15 } from "node:path";
31419
31703
  function aftCacheBase() {
31420
31704
  const override = process.env.AFT_CACHE_DIR;
31421
31705
  if (override && override.length > 0)
31422
31706
  return override;
31423
31707
  if (process.platform === "win32") {
31424
31708
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
31425
- const base2 = localAppData || join14(homedir10(), "AppData", "Local");
31426
- return join14(base2, "aft");
31709
+ const base2 = localAppData || join15(homedir11(), "AppData", "Local");
31710
+ return join15(base2, "aft");
31427
31711
  }
31428
- const base = process.env.XDG_CACHE_HOME || join14(homedir10(), ".cache");
31429
- return join14(base, "aft");
31712
+ const base = process.env.XDG_CACHE_HOME || join15(homedir11(), ".cache");
31713
+ return join15(base, "aft");
31430
31714
  }
31431
31715
  function lspCacheRoot() {
31432
- return join14(aftCacheBase(), "lsp-packages");
31716
+ return join15(aftCacheBase(), "lsp-packages");
31433
31717
  }
31434
31718
  function lspPackageDir(npmPackage) {
31435
- return join14(lspCacheRoot(), encodeURIComponent(npmPackage));
31719
+ return join15(lspCacheRoot(), encodeURIComponent(npmPackage));
31436
31720
  }
31437
31721
  function lspBinaryPath(npmPackage, binary) {
31438
- return join14(lspPackageDir(npmPackage), "node_modules", ".bin", binary);
31722
+ return join15(lspPackageDir(npmPackage), "node_modules", ".bin", binary);
31439
31723
  }
31440
31724
  function lspBinDir(npmPackage) {
31441
- return join14(lspPackageDir(npmPackage), "node_modules", ".bin");
31725
+ return join15(lspPackageDir(npmPackage), "node_modules", ".bin");
31442
31726
  }
31443
31727
  function isInstalled(npmPackage, binary) {
31444
31728
  for (const candidate of lspBinaryCandidates(binary)) {
31445
31729
  try {
31446
- if (statSync5(join14(lspBinDir(npmPackage), candidate)).isFile())
31730
+ if (statSync5(join15(lspBinDir(npmPackage), candidate)).isFile())
31447
31731
  return true;
31448
31732
  } catch {}
31449
31733
  }
@@ -31463,17 +31747,17 @@ function writeInstalledMetaIn(installDir, version2, sha256) {
31463
31747
  installedAt: new Date().toISOString(),
31464
31748
  ...sha256 ? { sha256 } : {}
31465
31749
  };
31466
- writeFileSync7(join14(installDir, INSTALLED_META_FILE), JSON.stringify(meta3), "utf8");
31750
+ writeFileSync7(join15(installDir, INSTALLED_META_FILE), JSON.stringify(meta3), "utf8");
31467
31751
  } catch (err) {
31468
31752
  log2(`[lsp-cache] failed to write installed-meta in ${installDir}: ${err}`);
31469
31753
  }
31470
31754
  }
31471
31755
  function readInstalledMetaIn(installDir) {
31472
- const path2 = join14(installDir, INSTALLED_META_FILE);
31756
+ const path3 = join15(installDir, INSTALLED_META_FILE);
31473
31757
  try {
31474
- if (!statSync5(path2).isFile())
31758
+ if (!statSync5(path3).isFile())
31475
31759
  return null;
31476
- const raw = readFileSync9(path2, "utf8");
31760
+ const raw = readFileSync9(path3, "utf8");
31477
31761
  const parsed = JSON.parse(raw);
31478
31762
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
31479
31763
  return null;
@@ -31493,7 +31777,7 @@ function readInstalledMeta(packageKey) {
31493
31777
  return readInstalledMetaIn(lspPackageDir(packageKey));
31494
31778
  }
31495
31779
  function lockPath(npmPackage) {
31496
- return join14(lspPackageDir(npmPackage), ".aft-installing");
31780
+ return join15(lspPackageDir(npmPackage), ".aft-installing");
31497
31781
  }
31498
31782
  var STALE_LOCK_MS2 = 30 * 60 * 1000;
31499
31783
  function acquireInstallLock(lockKey) {
@@ -31600,7 +31884,7 @@ async function withInstallLock(lockKey, task) {
31600
31884
  }
31601
31885
  var VERSION_CHECK_FILE = ".aft-version-check";
31602
31886
  function readVersionCheck(npmPackage) {
31603
- const file2 = join14(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
31887
+ const file2 = join15(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
31604
31888
  try {
31605
31889
  const raw = readFileSync9(file2, "utf8");
31606
31890
  const parsed = JSON.parse(raw);
@@ -31617,7 +31901,7 @@ function readVersionCheck(npmPackage) {
31617
31901
  }
31618
31902
  function writeVersionCheck(npmPackage, latest) {
31619
31903
  mkdirSync7(lspPackageDir(npmPackage), { recursive: true });
31620
- const file2 = join14(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
31904
+ const file2 = join15(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
31621
31905
  const record2 = {
31622
31906
  last_checked: new Date().toISOString(),
31623
31907
  latest_eligible: latest
@@ -31780,7 +32064,7 @@ var NPM_LSP_TABLE = [
31780
32064
 
31781
32065
  // src/lsp-project-relevance.ts
31782
32066
  import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync10 } from "node:fs";
31783
- import { join as join15 } from "node:path";
32067
+ import { join as join16 } from "node:path";
31784
32068
  var MAX_WALK_DIRS = 200;
31785
32069
  var MAX_WALK_DEPTH = 4;
31786
32070
  var NOISE_DIRS = new Set([
@@ -31797,7 +32081,7 @@ function hasRootMarker(projectRoot, rootMarkers) {
31797
32081
  if (!rootMarkers)
31798
32082
  return false;
31799
32083
  for (const marker of rootMarkers) {
31800
- if (existsSync11(join15(projectRoot, marker)))
32084
+ if (existsSync11(join16(projectRoot, marker)))
31801
32085
  return true;
31802
32086
  }
31803
32087
  return false;
@@ -31821,7 +32105,7 @@ function hasPackageJsonDep(projectRoot, depNames) {
31821
32105
  }
31822
32106
  function readPackageJson(projectRoot) {
31823
32107
  try {
31824
- const raw = readFileSync10(join15(projectRoot, "package.json"), "utf8");
32108
+ const raw = readFileSync10(join16(projectRoot, "package.json"), "utf8");
31825
32109
  const parsed = JSON.parse(raw);
31826
32110
  if (typeof parsed !== "object" || parsed === null)
31827
32111
  return null;
@@ -31851,7 +32135,7 @@ function relevantExtensionsInProject(projectRoot, extToServer) {
31851
32135
  for (const entry of entries) {
31852
32136
  if (entry.isDirectory()) {
31853
32137
  if (current.depth < MAX_WALK_DEPTH && !NOISE_DIRS.has(entry.name.toLowerCase())) {
31854
- queue.push({ dir: join15(current.dir, entry.name), depth: current.depth + 1 });
32138
+ queue.push({ dir: join16(current.dir, entry.name), depth: current.depth + 1 });
31855
32139
  }
31856
32140
  continue;
31857
32141
  }
@@ -31978,7 +32262,7 @@ async function resolveTargetVersion(spec, config2, fetchImpl = fetch) {
31978
32262
  }
31979
32263
  function ensureInstallAnchor(cwd) {
31980
32264
  try {
31981
- const stub = join16(cwd, "package.json");
32265
+ const stub = join17(cwd, "package.json");
31982
32266
  if (!existsSync12(stub)) {
31983
32267
  writeFileSync8(stub, `${JSON.stringify({ name: "aft-lsp-cache", version: "0.0.0", private: true })}
31984
32268
  `);
@@ -31988,18 +32272,18 @@ function ensureInstallAnchor(cwd) {
31988
32272
  }
31989
32273
  }
31990
32274
  function runInstall(spec, version2, cwd, signal) {
31991
- return new Promise((resolve3) => {
32275
+ return new Promise((resolve4) => {
31992
32276
  const target = `${spec.npm}@${version2}`;
31993
32277
  log2(`[lsp] installing ${target} to ${cwd}`);
31994
32278
  if (signal?.aborted) {
31995
32279
  warn2(`[lsp] install ${target} aborted before spawn`);
31996
- resolve3(false);
32280
+ resolve4(false);
31997
32281
  return;
31998
32282
  }
31999
32283
  const npm = resolveNpm();
32000
32284
  if (!npm) {
32001
32285
  warn2(`[lsp] npm not found on PATH or known locations; cannot install ${target}`);
32002
- resolve3(false);
32286
+ resolve4(false);
32003
32287
  return;
32004
32288
  }
32005
32289
  ensureInstallAnchor(cwd);
@@ -32022,7 +32306,7 @@ function runInstall(spec, version2, cwd, signal) {
32022
32306
  return;
32023
32307
  settled = true;
32024
32308
  cleanup();
32025
- resolve3(ok);
32309
+ resolve4(ok);
32026
32310
  };
32027
32311
  const onAbort = () => {
32028
32312
  warn2(`[lsp] install ${target} aborted during shutdown`);
@@ -32123,7 +32407,7 @@ function cachedPackageDir(npmPackage) {
32123
32407
  return lspBinDir(npmPackage).replace(/[\\/]node_modules[\\/]\.bin[\\/]?$/, "");
32124
32408
  }
32125
32409
  function hashInstalledBinary(spec) {
32126
- return new Promise((resolve3, reject) => {
32410
+ return new Promise((resolve4, reject) => {
32127
32411
  const candidates = process.platform === "win32" ? [
32128
32412
  lspBinaryPath(spec.npm, spec.binary),
32129
32413
  lspBinaryPath(spec.npm, `${spec.binary}.cmd`),
@@ -32147,7 +32431,7 @@ function hashInstalledBinary(spec) {
32147
32431
  const stream = createReadStream(pathToHash);
32148
32432
  stream.on("error", reject);
32149
32433
  stream.on("data", (chunk) => hash2.update(chunk));
32150
- stream.on("end", () => resolve3(hash2.digest("hex")));
32434
+ stream.on("end", () => resolve4(hash2.digest("hex")));
32151
32435
  });
32152
32436
  }
32153
32437
  function installedBinaryPath(spec) {
@@ -32165,15 +32449,15 @@ function installedBinaryPath(spec) {
32165
32449
  }
32166
32450
  return null;
32167
32451
  }
32168
- function sha256OfFileSync(path2) {
32169
- return createHash4("sha256").update(readFileSync11(path2)).digest("hex");
32452
+ function sha256OfFileSync(path3) {
32453
+ return createHash4("sha256").update(readFileSync11(path3)).digest("hex");
32170
32454
  }
32171
32455
  function quarantineCachedNpmInstall(spec, reason) {
32172
32456
  const packageDir = lspPackageDir(spec.npm);
32173
- const dest = join16(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
32457
+ const dest = join17(packageDir, "..", ".quarantine", encodeURIComponent(spec.npm), `${Date.now()}`);
32174
32458
  warn2(`[lsp] tofu_mismatch ${spec.npm}: ${reason}; quarantining ${packageDir} -> ${dest}`);
32175
32459
  try {
32176
- mkdirSync8(join16(dest, ".."), { recursive: true });
32460
+ mkdirSync8(join17(dest, ".."), { recursive: true });
32177
32461
  rmSync5(dest, { recursive: true, force: true });
32178
32462
  renameSync6(packageDir, dest);
32179
32463
  } catch (err) {
@@ -32269,7 +32553,7 @@ import {
32269
32553
  unlinkSync as unlinkSync6,
32270
32554
  writeFileSync as writeFileSync9
32271
32555
  } from "node:fs";
32272
- import { dirname as dirname8, join as join17, relative as relative2, resolve as resolve3 } from "node:path";
32556
+ import { dirname as dirname8, join as join18, relative as relative3, resolve as resolve4 } from "node:path";
32273
32557
  import { Readable as Readable3 } from "node:stream";
32274
32558
  import { pipeline as pipeline3 } from "node:stream/promises";
32275
32559
 
@@ -32359,26 +32643,26 @@ function detectHostPlatform() {
32359
32643
 
32360
32644
  // src/lsp-github-install.ts
32361
32645
  function ghCacheRoot() {
32362
- return join17(aftCacheBase(), "lsp-binaries");
32646
+ return join18(aftCacheBase(), "lsp-binaries");
32363
32647
  }
32364
32648
  function ghPackageDir(spec) {
32365
- return join17(ghCacheRoot(), spec.id);
32649
+ return join18(ghCacheRoot(), spec.id);
32366
32650
  }
32367
32651
  function ghBinDir(spec) {
32368
- return join17(ghPackageDir(spec), "bin");
32652
+ return join18(ghPackageDir(spec), "bin");
32369
32653
  }
32370
32654
  function ghExtractDir(spec) {
32371
- return join17(ghPackageDir(spec), "extracted");
32655
+ return join18(ghPackageDir(spec), "extracted");
32372
32656
  }
32373
32657
  var INSTALLED_META_FILE2 = ".aft-installed";
32374
32658
  function ghBinaryPath(spec, platform3) {
32375
32659
  const ext = platform3 === "win32" ? ".exe" : "";
32376
- return join17(ghBinDir(spec), `${spec.binary}${ext}`);
32660
+ return join18(ghBinDir(spec), `${spec.binary}${ext}`);
32377
32661
  }
32378
32662
  function isGithubInstalled(spec, platform3) {
32379
32663
  for (const candidate of ghBinaryCandidates(spec, platform3)) {
32380
32664
  try {
32381
- if (statSync7(join17(ghBinDir(spec), candidate)).isFile())
32665
+ if (statSync7(join18(ghBinDir(spec), candidate)).isFile())
32382
32666
  return true;
32383
32667
  } catch {}
32384
32668
  }
@@ -32391,10 +32675,10 @@ function ghBinaryCandidates(spec, platform3) {
32391
32675
  }
32392
32676
  function readGithubInstalledMetaIn(installDir) {
32393
32677
  try {
32394
- const path2 = join17(installDir, INSTALLED_META_FILE2);
32395
- if (!statSync7(path2).isFile())
32678
+ const path3 = join18(installDir, INSTALLED_META_FILE2);
32679
+ if (!statSync7(path3).isFile())
32396
32680
  return null;
32397
- const parsed = JSON.parse(readFileSync12(path2, "utf8"));
32681
+ const parsed = JSON.parse(readFileSync12(path3, "utf8"));
32398
32682
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
32399
32683
  return null;
32400
32684
  return {
@@ -32418,24 +32702,24 @@ function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveS
32418
32702
  binarySha256,
32419
32703
  ...archiveSha256 ? { archiveSha256 } : {}
32420
32704
  };
32421
- writeFileSync9(join17(installDir, INSTALLED_META_FILE2), JSON.stringify(meta3), "utf8");
32705
+ writeFileSync9(join18(installDir, INSTALLED_META_FILE2), JSON.stringify(meta3), "utf8");
32422
32706
  } catch (err) {
32423
32707
  warn2(`[lsp] failed to write github installed metadata in ${installDir}: ${err}`);
32424
32708
  }
32425
32709
  }
32426
32710
  var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
32427
32711
  var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
32428
- function sha256OfFile(path2) {
32429
- return new Promise((resolve4, reject) => {
32712
+ function sha256OfFile(path3) {
32713
+ return new Promise((resolve5, reject) => {
32430
32714
  const hash2 = createHash5("sha256");
32431
- const stream = createReadStream2(path2);
32715
+ const stream = createReadStream2(path3);
32432
32716
  stream.on("error", reject);
32433
32717
  stream.on("data", (chunk) => hash2.update(chunk));
32434
- stream.on("end", () => resolve4(hash2.digest("hex")));
32718
+ stream.on("end", () => resolve5(hash2.digest("hex")));
32435
32719
  });
32436
32720
  }
32437
- function sha256OfFileSync2(path2) {
32438
- return createHash5("sha256").update(readFileSync12(path2)).digest("hex");
32721
+ function sha256OfFileSync2(path3) {
32722
+ return createHash5("sha256").update(readFileSync12(path3)).digest("hex");
32439
32723
  }
32440
32724
  async function fetchReleaseByTag(githubRepo, tag, fetchImpl, signal) {
32441
32725
  const candidates = [];
@@ -32667,7 +32951,7 @@ function validateExtraction(stagingRoot) {
32667
32951
  throw new Error(`failed to read staging dir ${dir}: ${err}`);
32668
32952
  }
32669
32953
  for (const entry of entries) {
32670
- const full = join17(dir, entry);
32954
+ const full = join18(dir, entry);
32671
32955
  let lst;
32672
32956
  try {
32673
32957
  lst = lstatSync2(full);
@@ -32679,7 +32963,7 @@ function validateExtraction(stagingRoot) {
32679
32963
  try {
32680
32964
  target = readlinkSync2(full);
32681
32965
  } catch {}
32682
- throw new Error(`archive contains symlink ${relative2(realStagingRoot, full)} → ${target}; rejecting (zip-slip defense)`);
32966
+ throw new Error(`archive contains symlink ${relative3(realStagingRoot, full)} → ${target}; rejecting (zip-slip defense)`);
32683
32967
  }
32684
32968
  let realFull;
32685
32969
  try {
@@ -32687,8 +32971,8 @@ function validateExtraction(stagingRoot) {
32687
32971
  } catch (err) {
32688
32972
  throw new Error(`failed to realpath ${full}: ${err}`);
32689
32973
  }
32690
- const rel = relative2(realStagingRoot, realFull);
32691
- if (rel.startsWith("..") || resolve3(realStagingRoot, rel) !== realFull) {
32974
+ const rel = relative3(realStagingRoot, realFull);
32975
+ if (rel.startsWith("..") || resolve4(realStagingRoot, rel) !== realFull) {
32692
32976
  throw new Error(`archive entry escapes staging root: ${full} → ${realFull} (zip-slip defense)`);
32693
32977
  }
32694
32978
  if (lst.isDirectory()) {
@@ -32755,7 +33039,7 @@ function extractArchiveSafely(archivePath, destDir, archiveType) {
32755
33039
  }
32756
33040
  function quarantineCachedGithubInstall(spec, reason) {
32757
33041
  const packageDir = ghPackageDir(spec);
32758
- const dest = join17(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
33042
+ const dest = join18(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
32759
33043
  warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
32760
33044
  try {
32761
33045
  mkdirSync9(dirname8(dest), { recursive: true });
@@ -32768,7 +33052,7 @@ function quarantineCachedGithubInstall(spec, reason) {
32768
33052
  function validateCachedGithubInstall(spec, platform3) {
32769
33053
  const packageDir = ghPackageDir(spec);
32770
33054
  const meta3 = readGithubInstalledMetaIn(packageDir);
32771
- const binaryPath = ghBinaryCandidates(spec, platform3).map((candidate) => join17(ghBinDir(spec), candidate)).find((candidate) => {
33055
+ const binaryPath = ghBinaryCandidates(spec, platform3).map((candidate) => join18(ghBinDir(spec), candidate)).find((candidate) => {
32772
33056
  try {
32773
33057
  return statSync7(candidate).isFile();
32774
33058
  } catch {
@@ -32846,7 +33130,7 @@ async function downloadAndInstall(spec, tag, assets, platform3, arch, fetchImpl,
32846
33130
  }
32847
33131
  const pkgDir = ghPackageDir(spec);
32848
33132
  const extractDir = ghExtractDir(spec);
32849
- const archivePath = join17(pkgDir, expected.name);
33133
+ const archivePath = join18(pkgDir, expected.name);
32850
33134
  log2(`[lsp] downloading ${spec.id} ${tag} → ${matchingAsset.url}`);
32851
33135
  try {
32852
33136
  await downloadFile(matchingAsset.url, archivePath, fetchImpl, matchingAsset.size, signal);
@@ -32886,7 +33170,7 @@ async function downloadAndInstall(spec, tag, assets, platform3, arch, fetchImpl,
32886
33170
  unlinkSync6(archivePath);
32887
33171
  } catch {}
32888
33172
  }
32889
- const innerBinaryPath = join17(extractDir, spec.binaryPathInArchive(platform3, arch, version2));
33173
+ const innerBinaryPath = join18(extractDir, spec.binaryPathInArchive(platform3, arch, version2));
32890
33174
  if (!existsSync13(innerBinaryPath)) {
32891
33175
  error2(`[lsp] ${spec.id}: extracted binary not found at ${innerBinaryPath}`);
32892
33176
  return null;
@@ -33181,7 +33465,7 @@ function renderScreen(state, rows = state.rows, cols = state.cols) {
33181
33465
  `);
33182
33466
  }
33183
33467
  function writeTerminal(terminal, data) {
33184
- return new Promise((resolve4) => terminal.write(data, resolve4));
33468
+ return new Promise((resolve5) => terminal.write(data, resolve5));
33185
33469
  }
33186
33470
 
33187
33471
  // src/shared/rpc-server.ts
@@ -33196,18 +33480,18 @@ import {
33196
33480
  writeFileSync as writeFileSync10
33197
33481
  } from "node:fs";
33198
33482
  import { createServer } from "node:http";
33199
- import { dirname as dirname9, join as join19 } from "node:path";
33483
+ import { dirname as dirname9, join as join20 } from "node:path";
33200
33484
 
33201
33485
  // src/shared/rpc-utils.ts
33202
33486
  import { createHash as createHash6 } from "node:crypto";
33203
- import { join as join18 } from "node:path";
33487
+ import { join as join19 } from "node:path";
33204
33488
  function projectHash(directory) {
33205
33489
  const normalized = directory.replace(/\/+$/, "");
33206
33490
  return createHash6("sha256").update(normalized).digest("hex").slice(0, 16);
33207
33491
  }
33208
33492
  function rpcPortFileDir(storageDir, directory) {
33209
33493
  const hash2 = projectHash(directory);
33210
- return join18(storageDir, "rpc", hash2, "ports");
33494
+ return join19(storageDir, "rpc", hash2, "ports");
33211
33495
  }
33212
33496
  function isPidAlive(pid) {
33213
33497
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
@@ -33260,13 +33544,13 @@ class AftRpcServer {
33260
33544
  constructor(storageDir, directory) {
33261
33545
  this.portsDir = rpcPortFileDir(storageDir, directory);
33262
33546
  this.instanceId = randomBytes2(8).toString("hex");
33263
- this.portFilePath = join19(this.portsDir, `${this.instanceId}.json`);
33547
+ this.portFilePath = join20(this.portsDir, `${this.instanceId}.json`);
33264
33548
  }
33265
33549
  handle(method, handler) {
33266
33550
  this.handlers.set(method, handler);
33267
33551
  }
33268
33552
  async start() {
33269
- return new Promise((resolve4, reject) => {
33553
+ return new Promise((resolve5, reject) => {
33270
33554
  const server = createServer((req, res) => this.dispatch(req, res));
33271
33555
  server.on("error", (err) => {
33272
33556
  warn2(`RPC server error: ${err.message}`);
@@ -33299,7 +33583,7 @@ class AftRpcServer {
33299
33583
  } catch (err) {
33300
33584
  warn2(`Failed to write RPC port file: ${err}`);
33301
33585
  }
33302
- resolve4(this.port);
33586
+ resolve5(this.port);
33303
33587
  });
33304
33588
  server.unref();
33305
33589
  });
@@ -33314,7 +33598,7 @@ class AftRpcServer {
33314
33598
  for (const entry of entries) {
33315
33599
  if (!entry.endsWith(".json"))
33316
33600
  continue;
33317
- const filePath = join19(this.portsDir, entry);
33601
+ const filePath = join20(this.portsDir, entry);
33318
33602
  if (filePath === this.portFilePath)
33319
33603
  continue;
33320
33604
  try {
@@ -33719,20 +34003,20 @@ function formatStatusMarkdown(status) {
33719
34003
  // src/shared/tui-config.ts
33720
34004
  var import_comment_json4 = __toESM(require_src2(), 1);
33721
34005
  import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "node:fs";
33722
- import { dirname as dirname10, join as join21 } from "node:path";
34006
+ import { dirname as dirname10, join as join22 } from "node:path";
33723
34007
 
33724
34008
  // src/shared/opencode-config-dir.ts
33725
- import { homedir as homedir11 } from "node:os";
33726
- import { join as join20, resolve as resolve4 } from "node:path";
34009
+ import { homedir as homedir12 } from "node:os";
34010
+ import { join as join21, resolve as resolve5 } from "node:path";
33727
34011
  function getCliConfigDir() {
33728
34012
  const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
33729
34013
  if (envConfigDir) {
33730
- return resolve4(envConfigDir);
34014
+ return resolve5(envConfigDir);
33731
34015
  }
33732
34016
  if (process.platform === "win32") {
33733
- return join20(homedir11(), ".config", "opencode");
34017
+ return join21(homedir12(), ".config", "opencode");
33734
34018
  }
33735
- return join20(process.env.XDG_CONFIG_HOME || join20(homedir11(), ".config"), "opencode");
34019
+ return join21(process.env.XDG_CONFIG_HOME || join21(homedir12(), ".config"), "opencode");
33736
34020
  }
33737
34021
  function getOpenCodeConfigDir2(_options) {
33738
34022
  return getCliConfigDir();
@@ -33741,10 +34025,10 @@ function getOpenCodeConfigPaths(options) {
33741
34025
  const configDir = getOpenCodeConfigDir2(options);
33742
34026
  return {
33743
34027
  configDir,
33744
- configJson: join20(configDir, "opencode.json"),
33745
- configJsonc: join20(configDir, "opencode.jsonc"),
33746
- packageJson: join20(configDir, "package.json"),
33747
- omoConfig: join20(configDir, "magic-context.jsonc")
34028
+ configJson: join21(configDir, "opencode.json"),
34029
+ configJsonc: join21(configDir, "opencode.jsonc"),
34030
+ packageJson: join21(configDir, "package.json"),
34031
+ omoConfig: join21(configDir, "magic-context.jsonc")
33748
34032
  };
33749
34033
  }
33750
34034
 
@@ -33753,8 +34037,8 @@ var PLUGIN_NAME = "@cortexkit/aft-opencode";
33753
34037
  var PLUGIN_ENTRY = `${PLUGIN_NAME}@latest`;
33754
34038
  function resolveTuiConfigPath() {
33755
34039
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
33756
- const jsoncPath = join21(configDir, "tui.jsonc");
33757
- const jsonPath = join21(configDir, "tui.json");
34040
+ const jsoncPath = join22(configDir, "tui.jsonc");
34041
+ const jsonPath = join22(configDir, "tui.json");
33758
34042
  if (existsSync15(jsoncPath))
33759
34043
  return jsoncPath;
33760
34044
  if (existsSync15(jsonPath))
@@ -33843,8 +34127,8 @@ function installProcessHandlers() {
33843
34127
  }
33844
34128
  signalShutdownStarted = true;
33845
34129
  process.exitCode = SIGNAL_EXIT_CODES[sig];
33846
- const timeout = new Promise((resolve5) => {
33847
- setTimeout(resolve5, SIGNAL_CLEANUP_TIMEOUT_MS);
34130
+ const timeout = new Promise((resolve6) => {
34131
+ setTimeout(resolve6, SIGNAL_CLEANUP_TIMEOUT_MS);
33848
34132
  });
33849
34133
  Promise.race([runCleanups(sig), timeout]).finally(() => {
33850
34134
  process.exit(SIGNAL_EXIT_CODES[sig]);
@@ -33956,99 +34240,18 @@ import { tool as tool3 } from "@opencode-ai/plugin";
33956
34240
 
33957
34241
  // src/tools/_shared.ts
33958
34242
  import * as fs3 from "node:fs";
33959
- import * as os2 from "node:os";
33960
- import * as path2 from "node:path";
34243
+ import * as os3 from "node:os";
34244
+ import * as path3 from "node:path";
33961
34245
  import { tool as tool2 } from "@opencode-ai/plugin";
33962
34246
  var z2 = tool2.schema;
33963
34247
  var optionalInt = (min, max) => z2.number().int().min(min).max(max).optional();
33964
- function isEmptyParam(value) {
33965
- if (value === undefined || value === null)
33966
- return true;
33967
- if (typeof value === "string")
33968
- return value.length === 0;
33969
- if (Array.isArray(value))
33970
- return value.length === 0;
33971
- if (typeof value === "object")
33972
- return Object.keys(value).length === 0;
33973
- return false;
33974
- }
33975
34248
  var BASH_TRANSPORT_TIMEOUT_MS = 30000;
33976
- function asPlainObject(value) {
33977
- if (!value || typeof value !== "object" || Array.isArray(value))
33978
- return;
33979
- return value;
33980
- }
33981
- function candidateLocation(candidate) {
33982
- const file2 = typeof candidate.file === "string" && candidate.file.length > 0 ? candidate.file : undefined;
33983
- if (!file2)
33984
- return;
33985
- const line = typeof candidate.line === "number" && Number.isFinite(candidate.line) ? candidate.line : undefined;
33986
- return line === undefined ? file2 : `${file2}:${line}`;
33987
- }
33988
- function stringifyData(data) {
33989
- if (data === undefined)
33990
- return;
33991
- try {
33992
- return JSON.stringify(data, null, 2);
33993
- } catch {
33994
- return String(data);
33995
- }
33996
- }
33997
- function formatBridgeErrorMessage(command, response, params = {}) {
33998
- const code = typeof response.code === "string" && response.code.length > 0 ? response.code : undefined;
33999
- const message = typeof response.message === "string" && response.message.length > 0 ? response.message : `${command} failed`;
34000
- const data = asPlainObject(response.data);
34001
- const rawCandidates = Array.isArray(response.candidates) ? response.candidates : Array.isArray(data?.candidates) ? data.candidates : undefined;
34002
- const rawSymbol = typeof response.symbol === "string" && response.symbol.length > 0 ? response.symbol : typeof data?.symbol === "string" && data.symbol.length > 0 ? data.symbol : undefined;
34003
- if (code === "ambiguous_target" || code === "target_symbol_not_in_file") {
34004
- const candidates = (rawCandidates ?? []).map(asPlainObject).filter((candidate) => candidate !== undefined).map(candidateLocation).filter((candidate) => candidate !== undefined);
34005
- if (candidates.length > 0) {
34006
- const symbol2 = typeof params.toSymbol === "string" && params.toSymbol.length > 0 ? params.toSymbol : rawSymbol;
34007
- const target = symbol2 ? `multiple symbols named "${symbol2}"` : message.replace(/[.!?]+$/, "");
34008
- const action = code === "ambiguous_target" ? "Pass toFile to disambiguate" : "Try one of these files for toFile";
34009
- return `${command}: ${code} — ${target}. ${action}:
34010
- ${candidates.map((candidate) => ` - ${candidate}`).join(`
34011
- `)}`;
34012
- }
34013
- }
34014
- if (!code)
34015
- return message;
34016
- const lines = [`${command}: ${code} — ${message}`];
34017
- const extras = collectStructuredExtras(response);
34018
- if (extras)
34019
- lines.push(`data: ${extras}`);
34020
- return lines.join(`
34021
- `);
34022
- }
34023
- function collectStructuredExtras(response) {
34024
- const reserved = new Set([
34025
- "id",
34026
- "success",
34027
- "code",
34028
- "message",
34029
- "data",
34030
- "status_bar",
34031
- "bg_completions"
34032
- ]);
34033
- const extras = {};
34034
- for (const [key, value] of Object.entries(response)) {
34035
- if (reserved.has(key))
34036
- continue;
34037
- extras[key] = value;
34038
- }
34039
- if (Object.keys(extras).length === 0) {
34040
- return stringifyData(response.data);
34041
- }
34042
- if (response.data !== undefined)
34043
- extras.data = response.data;
34044
- return stringifyData(extras);
34045
- }
34046
34249
  function canonicalizeDirectory(dir) {
34047
34250
  const trimmed = dir.replace(/[/\\]+$/, "");
34048
34251
  try {
34049
34252
  return fs3.realpathSync(trimmed);
34050
34253
  } catch {
34051
- return path2.resolve(trimmed);
34254
+ return path3.resolve(trimmed);
34052
34255
  }
34053
34256
  }
34054
34257
  function projectRootFor(runtime) {
@@ -34065,19 +34268,19 @@ async function resolveProjectRoot(ctx, runtime) {
34065
34268
  }
34066
34269
  return projectRootFor(runtime);
34067
34270
  }
34068
- function expandTilde(input) {
34271
+ function expandTilde2(input) {
34069
34272
  if (!input || !input.startsWith("~"))
34070
34273
  return input;
34071
34274
  if (input === "~")
34072
- return os2.homedir();
34073
- if (input.startsWith("~/") || input.startsWith(`~${path2.sep}`)) {
34074
- return path2.resolve(os2.homedir(), input.slice(2));
34275
+ return os3.homedir();
34276
+ if (input.startsWith("~/") || input.startsWith(`~${path3.sep}`)) {
34277
+ return path3.resolve(os3.homedir(), input.slice(2));
34075
34278
  }
34076
34279
  return input;
34077
34280
  }
34078
34281
  function resolvePathFromProjectRoot(projectRoot, target) {
34079
- const expanded = expandTilde(target);
34080
- return path2.isAbsolute(expanded) ? expanded : path2.resolve(projectRoot, expanded);
34282
+ const expanded = expandTilde2(target);
34283
+ return path3.isAbsolute(expanded) ? expanded : path3.resolve(projectRoot, expanded);
34081
34284
  }
34082
34285
  async function resolvePathArg(ctx, runtime, target) {
34083
34286
  return resolvePathFromProjectRoot(await resolveProjectRoot(ctx, runtime), target);
@@ -34119,19 +34322,19 @@ async function callBashBridge(ctx, runtime, command, params = {}, options) {
34119
34322
 
34120
34323
  // src/tools/permissions.ts
34121
34324
  import * as fs4 from "node:fs";
34122
- import * as path3 from "node:path";
34325
+ import * as path4 from "node:path";
34123
34326
  var UNSUPPORTED_ASK_HOST = "AFT requires OpenCode 1.15.5 or newer for permission asks; please upgrade OpenCode";
34124
34327
  async function runAsk(maybe) {
34125
34328
  await maybe;
34126
34329
  }
34127
34330
  function resolveAbsolutePath(context, target) {
34128
- return path3.isAbsolute(target) ? target : path3.resolve(projectRootFor(context), target);
34331
+ return path4.isAbsolute(target) ? target : path4.resolve(projectRootFor(context), target);
34129
34332
  }
34130
34333
  function resolveRelativePattern(context, target) {
34131
- return path3.relative(projectRootFor(context), resolveAbsolutePath(context, target)) || ".";
34334
+ return path4.relative(projectRootFor(context), resolveAbsolutePath(context, target)) || ".";
34132
34335
  }
34133
34336
  function resolveRelativePatternFromAbsolute(context, absolutePath) {
34134
- return path3.relative(projectRootFor(context), absolutePath) || ".";
34337
+ return path4.relative(projectRootFor(context), absolutePath) || ".";
34135
34338
  }
34136
34339
  function resolveRelativePatterns(context, targets) {
34137
34340
  const seen = new Set;
@@ -34171,8 +34374,8 @@ async function askEditPermission(context, patterns, metadata = {}) {
34171
34374
  function containsPath(parent, child) {
34172
34375
  if (!parent)
34173
34376
  return false;
34174
- const rel = path3.relative(parent, child);
34175
- return rel === "" || !rel.startsWith("..") && !path3.isAbsolute(rel);
34377
+ const rel = path4.relative(parent, child);
34378
+ return rel === "" || !rel.startsWith("..") && !path4.isAbsolute(rel);
34176
34379
  }
34177
34380
  function windowsPath(p) {
34178
34381
  if (process.platform !== "win32")
@@ -34182,7 +34385,7 @@ function windowsPath(p) {
34182
34385
  function normalizePath(p) {
34183
34386
  if (process.platform !== "win32")
34184
34387
  return p;
34185
- const resolved = path3.resolve(windowsPath(p));
34388
+ const resolved = path4.resolve(windowsPath(p));
34186
34389
  try {
34187
34390
  return fs4.realpathSync.native(resolved);
34188
34391
  } catch {
@@ -34198,7 +34401,7 @@ function normalizePathPattern(p) {
34198
34401
  if (!match)
34199
34402
  return normalizePath(p);
34200
34403
  const dir = /^[A-Za-z]:$/.test(match[1]) ? `${match[1]}\\` : match[1];
34201
- return path3.join(normalizePath(dir), match[2]);
34404
+ return path4.join(normalizePath(dir), match[2]);
34202
34405
  }
34203
34406
  async function assertExternalDirectoryPermission(context, target, options) {
34204
34407
  if (!target)
@@ -34217,8 +34420,8 @@ async function assertExternalDirectoryPermission(context, target, options) {
34217
34420
  return;
34218
34421
  }
34219
34422
  const kind = options?.kind ?? "file";
34220
- const parentDir = kind === "directory" ? absoluteTarget : path3.dirname(absoluteTarget);
34221
- const rawGlob = process.platform === "win32" ? normalizePathPattern(path3.join(parentDir, "*")) : path3.join(parentDir, "*").replaceAll("\\", "/");
34423
+ const parentDir = kind === "directory" ? absoluteTarget : path4.dirname(absoluteTarget);
34424
+ const rawGlob = process.platform === "win32" ? normalizePathPattern(path4.join(parentDir, "*")) : path4.join(parentDir, "*").replaceAll("\\", "/");
34222
34425
  try {
34223
34426
  await runAsk(context.ask({
34224
34427
  permission: "external_directory",
@@ -34292,6 +34495,20 @@ function extractHint(response) {
34292
34495
  const hint = response.hint;
34293
34496
  return typeof hint === "string" && hint.length > 0 ? hint : null;
34294
34497
  }
34498
+ function appendSkippedFiles(output, skippedFiles) {
34499
+ if (!skippedFiles || skippedFiles.length === 0)
34500
+ return output;
34501
+ const lines = skippedFiles.map((skipped) => {
34502
+ const file2 = skipped.file ?? "unknown";
34503
+ const reason = skipped.reason ?? "unknown reason";
34504
+ return ` ${file2}: ${reason}`;
34505
+ });
34506
+ return `${output}
34507
+
34508
+ Incomplete: skipped ${skippedFiles.length} file(s)
34509
+ ${lines.join(`
34510
+ `)}`;
34511
+ }
34295
34512
  async function resolveAstPaths(ctx, context, paths) {
34296
34513
  if (isEmptyParam(paths) || !Array.isArray(paths))
34297
34514
  return;
@@ -34316,17 +34533,18 @@ var SUPPORTED_LANGS = [
34316
34533
  "python",
34317
34534
  "rust",
34318
34535
  "go",
34319
- "pascal"
34536
+ "pascal",
34537
+ "r"
34320
34538
  ];
34321
34539
  function astTools(ctx) {
34322
34540
  const searchTool = {
34323
- description: `Search code patterns across filesystem using AST-aware matching. Supports 7 languages.
34541
+ description: `Search code patterns across filesystem using AST-aware matching. Supports 8 languages.
34324
34542
 
34325
34543
  ` + `Use meta-variables: $VAR matches a single AST node, $$$ matches multiple nodes (variadic).
34326
34544
  ` + `IMPORTANT: Patterns must be complete AST nodes (valid code fragments).
34327
34545
  ` + `For functions, include params and body: 'export async function $NAME($$$) { $$$ }' not just 'export async function $NAME'.
34328
34546
 
34329
- ` + "Examples: pattern='console.log($MSG)' lang='typescript', pattern='async function $NAME($$$) { $$$ }' lang='javascript', pattern='def $FUNC($$$): $$$' lang='python', pattern='Writeln($MSG);' lang='pascal'",
34547
+ ` + "Examples: pattern='console.log($MSG)' lang='typescript', pattern='async function $NAME($$$) { $$$ }' lang='javascript', pattern='def $FUNC($$$): $$$' lang='python', pattern='Writeln($MSG);' lang='pascal', pattern='$X <- $Y' lang='r'",
34330
34548
  args: {
34331
34549
  pattern: z3.string().describe("AST pattern with meta-variables ($VAR, $$$). Must be complete AST node."),
34332
34550
  lang: z3.enum(SUPPORTED_LANGS).describe("Target language"),
@@ -34407,6 +34625,9 @@ ${hint}`;
34407
34625
  }
34408
34626
  }
34409
34627
  }
34628
+ if (data.complete === false || (data.skipped_files?.length ?? 0) > 0) {
34629
+ output = appendSkippedFiles(output, data.skipped_files);
34630
+ }
34410
34631
  showOutputToUser(context, output);
34411
34632
  return output;
34412
34633
  }
@@ -34750,7 +34971,7 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
34750
34971
  throw new Error(status.message ?? "bash_status failed");
34751
34972
  }
34752
34973
  if (isTerminalStatus(status.status)) {
34753
- const rendered2 = maybeAppendGrepSearchHint(appendPipeStripNote(formatForegroundResult(status), pipeStrip.note), command, aftSearchRegistered);
34974
+ const rendered2 = maybeAppendGrepSearchHint(appendPipeStripNote(formatForegroundResult(status), pipeStrip.note), command, aftSearchRegistered, projectRootFor(context));
34754
34975
  const metadataPayload2 = foregroundMetadata(description, status, rendered2);
34755
34976
  metadata?.(metadataPayload2);
34756
34977
  return { output: rendered2, title: uiTitle, metadata: metadataPayload2 };
@@ -34814,11 +35035,6 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
34814
35035
  }
34815
35036
  };
34816
35037
  }
34817
- function appendPipeStripNote(output, note) {
34818
- return note ? `${output}
34819
-
34820
- ${note}` : output;
34821
- }
34822
35038
  function createBashStatusTool(ctx) {
34823
35039
  return {
34824
35040
  description: "Read-only snapshot of a background or PTY bash task's current state and output. Returns immediately. Never waits. Use bash_watch to block on or register for pattern matches and exit events.",
@@ -34923,9 +35139,6 @@ function ptyCacheKey(runtime, taskId) {
34923
35139
  function preview(output) {
34924
35140
  return output.length <= METADATA_PREVIEW_LIMIT ? output : output.slice(-METADATA_PREVIEW_LIMIT);
34925
35141
  }
34926
- function isTerminalStatus(status) {
34927
- return status === "completed" || status === "failed" || status === "killed" || status === "timed_out";
34928
- }
34929
35142
  function subagentGuidance(taskId) {
34930
35143
  return `
34931
35144
 
@@ -34941,30 +35154,6 @@ function formatPromotionMessage(taskId, timeout, waitWindowMs) {
34941
35154
  const waited = timeout !== undefined ? Math.min(timeout, waitWindowMs) : waitWindowMs;
34942
35155
  return `Foreground bash didn't finish within ${formatSeconds(waited)} and was promoted to background: ${taskId}. A completion reminder will be delivered automatically; use bash_status({ taskId: "${taskId}" }) to inspect output or bash_kill({ taskId: "${taskId}" }) to terminate.`;
34943
35156
  }
34944
- function formatSeconds(ms) {
34945
- return `${Number((ms / 1000).toFixed(1))}s`;
34946
- }
34947
- function formatForegroundResult(data) {
34948
- const output = data.output_preview ?? "";
34949
- const outputPath = data.output_path;
34950
- const truncated = data.output_truncated === true;
34951
- const status = data.status;
34952
- const exit = data.exit_code;
34953
- let rendered = output;
34954
- if (truncated && outputPath) {
34955
- rendered += `
34956
- [output truncated; full output at ${outputPath}]`;
34957
- }
34958
- if (status === "timed_out") {
34959
- rendered += `
34960
- [command timed out]`;
34961
- }
34962
- if (typeof exit === "number" && exit !== 0) {
34963
- rendered += `
34964
- [exit code: ${exit}]`;
34965
- }
34966
- return rendered;
34967
- }
34968
35157
  function foregroundMetadata(description, data, rendered) {
34969
35158
  const outputPath = data.output_path;
34970
35159
  return {
@@ -34975,9 +35164,6 @@ function foregroundMetadata(description, data, rendered) {
34975
35164
  ...outputPath ? { outputPath } : {}
34976
35165
  };
34977
35166
  }
34978
- function sleep(ms) {
34979
- return new Promise((resolve7) => setTimeout(resolve7, ms));
34980
- }
34981
35167
  function getCallID(ctx) {
34982
35168
  const c = ctx;
34983
35169
  return c.callID ?? c.callId ?? c.call_id;
@@ -35000,7 +35186,7 @@ function conflictTools(ctx) {
35000
35186
  execute: async (args, context) => {
35001
35187
  const params = {};
35002
35188
  if (!isEmptyParam(args?.path)) {
35003
- const expanded = expandTilde(String(args.path));
35189
+ const expanded = expandTilde2(String(args.path));
35004
35190
  const projectRoot = await resolveProjectRoot(ctx, context);
35005
35191
  const resolved = resolvePathFromProjectRoot(projectRoot, expanded);
35006
35192
  const denied = await assertExternalDirectoryPermission(context, resolved, {
@@ -35022,7 +35208,7 @@ function conflictTools(ctx) {
35022
35208
 
35023
35209
  // src/tools/hoisted.ts
35024
35210
  import * as fs6 from "node:fs";
35025
- import * as path4 from "node:path";
35211
+ import * as path5 from "node:path";
35026
35212
  import { tool as tool8 } from "@opencode-ai/plugin";
35027
35213
 
35028
35214
  // src/patch-parser.ts
@@ -35462,13 +35648,16 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
35462
35648
  let scanText = "";
35463
35649
  let scanBaseOffset = 0;
35464
35650
  const bridgeOptions = {};
35651
+ if (waitFor?.kind === "regex") {
35652
+ await validateWaitRegex(ctx, runtime, waitFor);
35653
+ }
35465
35654
  clearSyncWatchAbort(runtime.sessionID);
35466
35655
  markTaskWaiting(runtime.sessionID, taskId);
35467
35656
  let sawTerminal = false;
35468
35657
  try {
35469
35658
  while (true) {
35470
35659
  const data = await bashStatusSnapshot2(ctx, runtime, taskId, outputMode, bridgeOptions);
35471
- const terminal = isTerminalStatus2(data.status);
35660
+ const terminal = isTerminalStatus(data.status);
35472
35661
  if (waitFor) {
35473
35662
  const scan = await readNewTaskOutput(runtime, taskId, data, spillCursor);
35474
35663
  if (scan) {
@@ -35476,7 +35665,12 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
35476
35665
  if (scanText.length === 0)
35477
35666
  scanBaseOffset = scan.baseOffset;
35478
35667
  scanText += scan.text;
35479
- const match = findWaitMatch(scanText, waitFor);
35668
+ if (waitFor.kind === "regex") {
35669
+ const trimmed = trimWaitScanBuffer(scanText, scanBaseOffset, waitFor);
35670
+ scanText = trimmed.text;
35671
+ scanBaseOffset = trimmed.baseOffset;
35672
+ }
35673
+ const match = await findWaitMatch(ctx, runtime, scanText, waitFor);
35480
35674
  if (match) {
35481
35675
  if (terminal) {
35482
35676
  sawTerminal = true;
@@ -35487,12 +35681,14 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
35487
35681
  reason: "matched",
35488
35682
  elapsed_ms: Date.now() - startedAt,
35489
35683
  match: match.text,
35490
- match_offset: scanBaseOffset + Buffer.byteLength(scanText.slice(0, match.index), "utf8")
35684
+ match_offset: scanBaseOffset + match.byteOffset
35491
35685
  });
35492
35686
  }
35493
- const trimmed = trimWaitScanBuffer(scanText, scanBaseOffset, waitFor);
35494
- scanText = trimmed.text;
35495
- scanBaseOffset = trimmed.baseOffset;
35687
+ if (waitFor.kind === "substring") {
35688
+ const trimmed = trimWaitScanBuffer(scanText, scanBaseOffset, waitFor);
35689
+ scanText = trimmed.text;
35690
+ scanBaseOffset = trimmed.baseOffset;
35691
+ }
35496
35692
  }
35497
35693
  }
35498
35694
  if (terminal) {
@@ -35507,7 +35703,7 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
35507
35703
  if (Date.now() >= deadline) {
35508
35704
  return withWaited(data, { reason: "timeout", elapsed_ms: Date.now() - startedAt });
35509
35705
  }
35510
- await sleep2(Math.min(BASH_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
35706
+ await sleep(Math.min(BASH_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
35511
35707
  }
35512
35708
  } finally {
35513
35709
  if (!sawTerminal)
@@ -35573,20 +35769,41 @@ function parseWaitPattern(value) {
35573
35769
  if (typeof value === "string")
35574
35770
  return { kind: "substring", value };
35575
35771
  if (isRegexWaitObject(value))
35576
- return { kind: "regex", value: new RegExp(value.regex), source: value.regex };
35772
+ return { kind: "regex", source: value.regex };
35577
35773
  return;
35578
35774
  }
35579
35775
  function isRegexWaitObject(value) {
35580
35776
  return typeof value === "object" && value !== null && "regex" in value && typeof value.regex === "string";
35581
35777
  }
35582
- function findWaitMatch(text, pattern) {
35778
+ async function validateWaitRegex(ctx, runtime, pattern) {
35779
+ await matchRegexWithBridge(ctx, runtime, pattern.source, "");
35780
+ }
35781
+ async function findWaitMatch(ctx, runtime, text, pattern) {
35583
35782
  if (pattern.kind === "substring") {
35584
35783
  const index = text.indexOf(pattern.value);
35585
- return index >= 0 ? { text: pattern.value, index } : undefined;
35784
+ return index >= 0 ? { text: pattern.value, byteOffset: Buffer.byteLength(text.slice(0, index), "utf8") } : undefined;
35586
35785
  }
35587
- pattern.value.lastIndex = 0;
35588
- const match = pattern.value.exec(text);
35589
- return match ? { text: match[0], index: match.index } : undefined;
35786
+ return await matchRegexWithBridge(ctx, runtime, pattern.source, text);
35787
+ }
35788
+ async function matchRegexWithBridge(ctx, runtime, pattern, text) {
35789
+ const result = await callBashBridge(ctx, runtime, "bash_regex_match", { pattern, text });
35790
+ if (result.success === false) {
35791
+ const code = String(result.code ?? "invalid_request");
35792
+ const message = String(result.message ?? "bash_regex_match failed");
35793
+ if (code === "invalid_regex")
35794
+ throw new Error(`invalid_request: invalid_regex: ${message}`);
35795
+ throw new Error(`${code}: ${message}`);
35796
+ }
35797
+ if (result.matched !== true)
35798
+ return;
35799
+ return {
35800
+ text: typeof result.match_text === "string" ? result.match_text : "",
35801
+ byteOffset: coerceMatchOffset(result.match_offset)
35802
+ };
35803
+ }
35804
+ function coerceMatchOffset(value) {
35805
+ const offset = typeof value === "number" ? value : Number(value ?? 0);
35806
+ return Number.isFinite(offset) && offset >= 0 ? offset : 0;
35590
35807
  }
35591
35808
  function trimWaitScanBuffer(text, baseOffset, pattern) {
35592
35809
  const keepFrom = pattern.kind === "substring" ? substringKeepStart(text, pattern.value) : regexKeepStart(text, REGEX_WAIT_SCAN_WINDOW_BYTES);
@@ -35619,18 +35836,12 @@ function regexKeepStart(text, maxBytes) {
35619
35836
  function withWaited(data, waited) {
35620
35837
  return { ...data, waited };
35621
35838
  }
35622
- function isTerminalStatus2(status) {
35623
- return status === "completed" || status === "failed" || status === "killed" || status === "timed_out";
35624
- }
35625
35839
  function ptyCacheKey2(runtime, taskId) {
35626
35840
  return `${projectRootFor(runtime)}::${runtime.sessionID ?? "__default__"}::${taskId}`;
35627
35841
  }
35628
35842
  function watchPtyCacheKey(runtime, taskId) {
35629
35843
  return `${ptyCacheKey2(runtime, taskId)}::watch`;
35630
35844
  }
35631
- function sleep2(ms) {
35632
- return new Promise((resolve7) => setTimeout(resolve7, ms));
35633
- }
35634
35845
 
35635
35846
  // src/tools/bash_write.ts
35636
35847
  import { tool as tool7 } from "@opencode-ai/plugin";
@@ -35665,21 +35876,10 @@ function createBashWriteTool(ctx) {
35665
35876
 
35666
35877
  // src/tools/hoisted.ts
35667
35878
  function relativeToWorktree(fp, worktree) {
35668
- return path4.relative(worktree, fp);
35879
+ return path5.relative(worktree, fp);
35669
35880
  }
35670
- function formatReadFooter(agentSpecifiedRange, data) {
35671
- if (agentSpecifiedRange)
35672
- return "";
35673
- if (!data.truncated)
35674
- return "";
35675
- const startLine = data.start_line;
35676
- const endLine = data.end_line;
35677
- const totalLines = data.total_lines;
35678
- if (startLine === undefined || endLine === undefined || totalLines === undefined) {
35679
- return "";
35680
- }
35681
- return `
35682
- (Showing lines ${startLine}-${endLine} of ${totalLines}. Use startLine/endLine to read other sections.)`;
35881
+ function formatReadFooter2(agentSpecifiedRange, data) {
35882
+ return formatReadFooter(agentSpecifiedRange, data, { rangeHint: "startLine/endLine" });
35683
35883
  }
35684
35884
  function buildUnifiedDiff(fp, before, after) {
35685
35885
  const beforeLines = before.split(`
@@ -35872,6 +36072,91 @@ var z8 = tool8.schema;
35872
36072
  function diagnosticsOnEditDefault(ctx) {
35873
36073
  return ctx.config.lsp?.diagnostics_on_edit ?? false;
35874
36074
  }
36075
+ async function readCurrentFileForPreview(filePath) {
36076
+ try {
36077
+ return await fs6.promises.readFile(filePath, "utf-8");
36078
+ } catch (error50) {
36079
+ if (error50 && typeof error50 === "object" && "code" in error50 && error50.code === "ENOENT") {
36080
+ return "";
36081
+ }
36082
+ throw error50;
36083
+ }
36084
+ }
36085
+ function previewDiffFromResponse(data, filePath) {
36086
+ const previewDiff = data.preview_diff;
36087
+ if (typeof previewDiff === "string")
36088
+ return previewDiff;
36089
+ const diff = data.diff;
36090
+ if (typeof diff?.before === "string" && typeof diff.after === "string") {
36091
+ return buildUnifiedDiff(filePath, diff.before, diff.after);
36092
+ }
36093
+ return "";
36094
+ }
36095
+ function virtualPatchContent(virtualFiles, filePath) {
36096
+ return virtualFiles.has(filePath) ? virtualFiles.get(filePath) ?? null : undefined;
36097
+ }
36098
+ async function readPatchPreviewContent(virtualFiles, filePath, action, patchPath) {
36099
+ const virtualContent = virtualPatchContent(virtualFiles, filePath);
36100
+ if (virtualContent !== undefined) {
36101
+ if (virtualContent === null) {
36102
+ throw new Error(`Failed to ${action} ${patchPath}: file not found: ${filePath}`);
36103
+ }
36104
+ return virtualContent;
36105
+ }
36106
+ try {
36107
+ return await fs6.promises.readFile(filePath, "utf-8");
36108
+ } catch (error50) {
36109
+ throw new Error(`Failed to ${action} ${patchPath}: ${formatError2(error50)}`);
36110
+ }
36111
+ }
36112
+ async function buildApplyPatchPreview(hunks, projectRoot) {
36113
+ const virtualFiles = new Map;
36114
+ const patches = [];
36115
+ let firstFilePath = "";
36116
+ for (const hunk of hunks) {
36117
+ const filePath = resolvePathFromProjectRoot(projectRoot, hunk.path);
36118
+ if (!firstFilePath)
36119
+ firstFilePath = filePath;
36120
+ switch (hunk.type) {
36121
+ case "add": {
36122
+ const virtualContent = virtualPatchContent(virtualFiles, filePath);
36123
+ const exists = virtualContent !== undefined ? virtualContent !== null : fs6.existsSync(filePath);
36124
+ if (exists) {
36125
+ throw new Error(`Failed to create ${hunk.path}: file already exists. Use *** Update File: to modify, or *** Delete File: first if you want to replace it entirely.`);
36126
+ }
36127
+ const after = hunk.contents.endsWith(`
36128
+ `) ? hunk.contents : `${hunk.contents}
36129
+ `;
36130
+ patches.push(buildUnifiedDiff(filePath, "", after));
36131
+ virtualFiles.set(filePath, after);
36132
+ break;
36133
+ }
36134
+ case "delete": {
36135
+ const before = await readPatchPreviewContent(virtualFiles, filePath, "delete", hunk.path);
36136
+ patches.push(buildUnifiedDiff(filePath, before, ""));
36137
+ virtualFiles.set(filePath, null);
36138
+ break;
36139
+ }
36140
+ case "update": {
36141
+ const before = await readPatchPreviewContent(virtualFiles, filePath, "update", hunk.path);
36142
+ let after;
36143
+ try {
36144
+ after = applyUpdateChunks(before, filePath, hunk.chunks);
36145
+ } catch (error50) {
36146
+ throw new Error(`Failed to update ${hunk.path}: ${formatError2(error50)}`);
36147
+ }
36148
+ const targetPath = hunk.move_path ? resolvePathFromProjectRoot(projectRoot, hunk.move_path) : filePath;
36149
+ patches.push(buildUnifiedDiff(targetPath, before, after));
36150
+ if (hunk.move_path)
36151
+ virtualFiles.set(filePath, null);
36152
+ virtualFiles.set(targetPath, after);
36153
+ break;
36154
+ }
36155
+ }
36156
+ }
36157
+ return { filepath: firstFilePath || projectRoot, diff: patches.join(`
36158
+ `) };
36159
+ }
35875
36160
  var READ_DESCRIPTION = `Read file contents or list directory entries.
35876
36161
 
35877
36162
  Use either startLine/endLine OR offset/limit to read a section of a file.
@@ -35921,7 +36206,7 @@ function createReadTool(ctx) {
35921
36206
  return permissionDeniedResponse(error50.message);
35922
36207
  return permissionDeniedResponse("Permission denied.");
35923
36208
  }
35924
- const ext = path4.extname(filePath).toLowerCase();
36209
+ const ext = path5.extname(filePath).toLowerCase();
35925
36210
  const mimeMap = {
35926
36211
  ".png": "image/png",
35927
36212
  ".jpg": "image/jpeg",
@@ -35950,7 +36235,7 @@ function createReadTool(ctx) {
35950
36235
  const msg = `${label} read successfully`;
35951
36236
  return {
35952
36237
  output: `${msg} (${ext.slice(1).toUpperCase()}, ${sizeStr}). File: ${filePath}`,
35953
- title: path4.relative(projectRoot, filePath),
36238
+ title: path5.relative(projectRoot, filePath),
35954
36239
  metadata: {
35955
36240
  preview: msg,
35956
36241
  filepath: filePath,
@@ -35992,7 +36277,7 @@ function createReadTool(ctx) {
35992
36277
  }
35993
36278
  let output = data.content;
35994
36279
  const agentSpecifiedRange = args.startLine !== undefined || args.endLine !== undefined || args.offset !== undefined || args.limit !== undefined;
35995
- const footer = formatReadFooter(agentSpecifiedRange, data);
36280
+ const footer = formatReadFooter2(agentSpecifiedRange, data);
35996
36281
  if (footer)
35997
36282
  output += footer;
35998
36283
  return { output, title: dp, metadata: { title: dp } };
@@ -36014,18 +36299,20 @@ function createWriteTool(ctx, editToolName = "edit") {
36014
36299
  const content = args.content;
36015
36300
  const projectRoot = await resolveProjectRoot(ctx, context);
36016
36301
  const filePath = resolvePathFromProjectRoot(projectRoot, file2);
36017
- const relPath = path4.relative(projectRoot, filePath);
36302
+ const relPath = path5.relative(projectRoot, filePath);
36018
36303
  {
36019
- const denial = await assertExternalDirectoryPermission(context, filePath);
36020
- if (denial)
36021
- return permissionDeniedResponse(denial);
36022
- }
36023
- await runAsk(context.ask({
36024
- permission: "edit",
36025
- patterns: [relPath],
36026
- always: ["*"],
36027
- metadata: { filepath: filePath }
36028
- }));
36304
+ const denial2 = await assertExternalDirectoryPermission(context, filePath);
36305
+ if (denial2)
36306
+ return permissionDeniedResponse(denial2);
36307
+ }
36308
+ const currentContent = await readCurrentFileForPreview(filePath);
36309
+ const previewDiff = buildUnifiedDiff(filePath, currentContent, content);
36310
+ const denial = await askEditPermission(context, [relPath], {
36311
+ filepath: filePath,
36312
+ diff: previewDiff
36313
+ });
36314
+ if (denial)
36315
+ return permissionDeniedResponse(denial);
36029
36316
  const data = await callBridge(ctx, context, "write", {
36030
36317
  file: filePath,
36031
36318
  content,
@@ -36163,18 +36450,12 @@ function createEditTool(ctx, writeToolName = "write") {
36163
36450
  throw new Error("'filePath' parameter is required");
36164
36451
  const projectRoot = await resolveProjectRoot(ctx, context);
36165
36452
  const filePath = resolvePathFromProjectRoot(projectRoot, file2);
36166
- const relPath = path4.relative(projectRoot, filePath);
36453
+ const relPath = path5.relative(projectRoot, filePath);
36167
36454
  {
36168
- const denial = await assertExternalDirectoryPermission(context, filePath);
36169
- if (denial)
36170
- return permissionDeniedResponse(denial);
36455
+ const denial2 = await assertExternalDirectoryPermission(context, filePath);
36456
+ if (denial2)
36457
+ return permissionDeniedResponse(denial2);
36171
36458
  }
36172
- await runAsk(context.ask({
36173
- permission: "edit",
36174
- patterns: [relPath],
36175
- always: ["*"],
36176
- metadata: { filepath: filePath }
36177
- }));
36178
36459
  const params = { file: filePath };
36179
36460
  let command;
36180
36461
  if (typeof args.appendContent === "string") {
@@ -36217,6 +36498,21 @@ function createEditTool(ctx, writeToolName = "write") {
36217
36498
  const hint = typeof args.content === "string" ? ` To write the whole file, use the '${writeToolName}' tool. To edit existing content, provide 'oldString' (and optionally 'newString'), 'symbol' + 'content', or an 'edits' array.` : " Provide 'oldString' (+ optional 'newString'), 'symbol' + 'content', or an 'edits' array.";
36218
36499
  throw new Error(`edit: no edit mode resolved from arguments.${hint}`);
36219
36500
  }
36501
+ const previewData = await callBridge(ctx, context, command, {
36502
+ ...params,
36503
+ preview: true,
36504
+ include_diff_content: true
36505
+ });
36506
+ if (previewData.success === false) {
36507
+ throw new Error(previewData.message || "edit preview failed");
36508
+ }
36509
+ const previewDiff = previewDiffFromResponse(previewData, filePath);
36510
+ const denial = await askEditPermission(context, [relPath], {
36511
+ filepath: filePath,
36512
+ diff: previewDiff
36513
+ });
36514
+ if (denial)
36515
+ return permissionDeniedResponse(denial);
36220
36516
  params.diagnostics = diagnosticsOnEditDefault(ctx);
36221
36517
  params.include_diff_content = true;
36222
36518
  const data = await callBridge(ctx, context, command, params);
@@ -36357,22 +36653,34 @@ function createApplyPatchTool(ctx) {
36357
36653
  }
36358
36654
  const projectRoot = await resolveProjectRoot(ctx, context);
36359
36655
  const affectedAbs = new Set;
36656
+ const initiallyExistsAbs = new Map;
36657
+ const rememberAffectedPath = (abs) => {
36658
+ affectedAbs.add(abs);
36659
+ if (!initiallyExistsAbs.has(abs)) {
36660
+ initiallyExistsAbs.set(abs, fs6.existsSync(abs));
36661
+ }
36662
+ };
36663
+ for (const h of hunks) {
36664
+ const srcAbs = resolvePathFromProjectRoot(projectRoot, h.path);
36665
+ rememberAffectedPath(srcAbs);
36666
+ if (h.type === "update" && h.move_path) {
36667
+ rememberAffectedPath(resolvePathFromProjectRoot(projectRoot, h.move_path));
36668
+ }
36669
+ }
36360
36670
  const newlyCreatedAbs = new Set;
36361
36671
  for (const h of hunks) {
36362
36672
  const srcAbs = resolvePathFromProjectRoot(projectRoot, h.path);
36363
- affectedAbs.add(srcAbs);
36364
- if (h.type === "add") {
36673
+ if (h.type === "add" && initiallyExistsAbs.get(srcAbs) === false) {
36365
36674
  newlyCreatedAbs.add(srcAbs);
36366
36675
  }
36367
36676
  if (h.type === "update" && h.move_path) {
36368
36677
  const dstAbs = resolvePathFromProjectRoot(projectRoot, h.move_path);
36369
- affectedAbs.add(dstAbs);
36370
- if (!fs6.existsSync(dstAbs)) {
36678
+ if (initiallyExistsAbs.get(dstAbs) === false) {
36371
36679
  newlyCreatedAbs.add(dstAbs);
36372
36680
  }
36373
36681
  }
36374
36682
  }
36375
- const relPaths = Array.from(affectedAbs).map((abs) => path4.relative(projectRoot, abs));
36683
+ const relPaths = Array.from(affectedAbs).map((abs) => path5.relative(projectRoot, abs));
36376
36684
  const multiFileWritePaths = Array.from(affectedAbs);
36377
36685
  {
36378
36686
  const asked = new Set;
@@ -36380,17 +36688,18 @@ function createApplyPatchTool(ctx) {
36380
36688
  if (asked.has(filePath))
36381
36689
  continue;
36382
36690
  asked.add(filePath);
36383
- const denial = await assertExternalDirectoryPermission(context, filePath);
36384
- if (denial)
36385
- return permissionDeniedResponse(denial);
36691
+ const denial2 = await assertExternalDirectoryPermission(context, filePath);
36692
+ if (denial2)
36693
+ return permissionDeniedResponse(denial2);
36386
36694
  }
36387
36695
  }
36388
- await runAsk(context.ask({
36389
- permission: "edit",
36390
- patterns: relPaths,
36391
- always: ["*"],
36392
- metadata: {}
36393
- }));
36696
+ const preview2 = await buildApplyPatchPreview(hunks, projectRoot);
36697
+ const denial = await askEditPermission(context, relPaths, {
36698
+ filepath: preview2.filepath,
36699
+ diff: preview2.diff
36700
+ });
36701
+ if (denial)
36702
+ return permissionDeniedResponse(denial);
36394
36703
  const checkpointPaths = Array.from(affectedAbs).filter((abs) => !newlyCreatedAbs.has(abs));
36395
36704
  const checkpointName = `apply_patch_${Date.now()}`;
36396
36705
  let checkpointCreated = false;
@@ -36405,15 +36714,15 @@ function createApplyPatchTool(ctx) {
36405
36714
  }
36406
36715
  const results = [];
36407
36716
  const failures = [];
36408
- const perFileDiffs = [];
36409
- for (const hunk of hunks) {
36717
+ const appliedHunkResults = [];
36718
+ for (const [hunkIndex, hunk] of hunks.entries()) {
36410
36719
  const filePath = resolvePathFromProjectRoot(projectRoot, hunk.path);
36411
36720
  switch (hunk.type) {
36412
36721
  case "add": {
36413
36722
  if (fs6.existsSync(filePath)) {
36414
36723
  const msg = `Failed to create ${hunk.path}: file already exists. Use *** Update File: to modify, or *** Delete File: first if you want to replace it entirely.`;
36415
36724
  results.push(msg);
36416
- failures.push(hunk.path);
36725
+ failures.push({ index: hunkIndex, path: hunk.path });
36417
36726
  break;
36418
36727
  }
36419
36728
  try {
@@ -36435,10 +36744,13 @@ function createApplyPatchTool(ctx) {
36435
36744
  throw new Error("produced invalid syntax (rolled back)");
36436
36745
  }
36437
36746
  const wrDiff = writeResult.diff;
36438
- perFileDiffs.push({
36747
+ appliedHunkResults.push({
36748
+ index: hunkIndex,
36749
+ hunk,
36439
36750
  filePath,
36751
+ displayPath: filePath,
36440
36752
  before: "",
36441
- after: hunk.contents,
36753
+ after: content,
36442
36754
  additions: wrDiff?.additions ?? lineCount(content),
36443
36755
  deletions: wrDiff?.deletions ?? 0
36444
36756
  });
@@ -36446,7 +36758,7 @@ function createApplyPatchTool(ctx) {
36446
36758
  } catch (e) {
36447
36759
  const msg = `Failed to create ${hunk.path}: ${e instanceof Error ? e.message : e}`;
36448
36760
  results.push(msg);
36449
- failures.push(hunk.path);
36761
+ failures.push({ index: hunkIndex, path: hunk.path });
36450
36762
  const filePath2 = resolvePathFromProjectRoot(projectRoot, hunk.path);
36451
36763
  if (fs6.existsSync(filePath2)) {
36452
36764
  try {
@@ -36465,8 +36777,11 @@ function createApplyPatchTool(ctx) {
36465
36777
  if (deleteResult.success === false) {
36466
36778
  throw new Error(deleteResult.message ?? "delete failed");
36467
36779
  }
36468
- perFileDiffs.push({
36780
+ appliedHunkResults.push({
36781
+ index: hunkIndex,
36782
+ hunk,
36469
36783
  filePath,
36784
+ displayPath: filePath,
36470
36785
  before,
36471
36786
  after: "",
36472
36787
  additions: 0,
@@ -36475,7 +36790,7 @@ function createApplyPatchTool(ctx) {
36475
36790
  results.push(`Deleted ${hunk.path}`);
36476
36791
  } catch (e) {
36477
36792
  results.push(`Failed to delete ${hunk.path}: ${e instanceof Error ? e.message : e}`);
36478
- failures.push(hunk.path);
36793
+ failures.push({ index: hunkIndex, path: hunk.path });
36479
36794
  }
36480
36795
  break;
36481
36796
  }
@@ -36502,7 +36817,7 @@ function createApplyPatchTool(ctx) {
36502
36817
  if (diags && diags.length > 0) {
36503
36818
  const errors3 = diags.filter((d) => d.severity === "error");
36504
36819
  if (errors3.length > 0) {
36505
- const relPath = path4.relative(projectRoot, targetPath);
36820
+ const relPath = path5.relative(projectRoot, targetPath);
36506
36821
  const diagLines = errors3.map((d) => ` Line ${d.line}: ${d.message}`).join(`
36507
36822
  `);
36508
36823
  results.push(`
@@ -36516,13 +36831,17 @@ ${diagLines}`);
36516
36831
  additions: wrDiff.additions,
36517
36832
  deletions: wrDiff.deletions
36518
36833
  };
36519
- perFileDiffs.push({
36834
+ const appliedHunkResult = {
36835
+ index: hunkIndex,
36836
+ hunk,
36520
36837
  filePath,
36838
+ displayPath: targetPath,
36839
+ ...hunk.move_path ? { movePath: targetPath } : {},
36521
36840
  before: original,
36522
36841
  after: newContent,
36523
36842
  additions,
36524
36843
  deletions
36525
- });
36844
+ };
36526
36845
  if (hunk.move_path) {
36527
36846
  try {
36528
36847
  const deleteResult = await callBridge(ctx, context, "delete_file", {
@@ -36555,13 +36874,15 @@ ${diagLines}`);
36555
36874
  }
36556
36875
  throw new Error(`source delete failed after writing move destination; restored pre-patch checkpoint ${checkpointName}: ${formatError2(deleteError)}`);
36557
36876
  }
36877
+ appliedHunkResults.push(appliedHunkResult);
36558
36878
  results.push(`Updated and moved ${hunk.path} → ${hunk.move_path}`);
36559
36879
  } else {
36880
+ appliedHunkResults.push(appliedHunkResult);
36560
36881
  results.push(`Updated ${hunk.path}`);
36561
36882
  }
36562
36883
  } catch (e) {
36563
36884
  results.push(`Failed to update ${hunk.path}: ${e instanceof Error ? e.message : e}`);
36564
- failures.push(hunk.path);
36885
+ failures.push({ index: hunkIndex, path: hunk.path });
36565
36886
  break;
36566
36887
  }
36567
36888
  break;
@@ -36570,7 +36891,8 @@ ${diagLines}`);
36570
36891
  }
36571
36892
  if (failures.length > 0) {
36572
36893
  const partial2 = failures.length < hunks.length;
36573
- const summary = partial2 ? `Patch partially applied — ${hunks.length - failures.length} of ${hunks.length} hunk(s) succeeded. Failed: ${failures.join(", ")}. Successful changes are kept; use \`aft_safety\` to revert if you want to abort.` : `Patch failed — none of the ${hunks.length} hunk(s) applied: ${failures.join(", ")}.`;
36894
+ const failedList = failures.map((failure) => failure.path).join(", ");
36895
+ const summary = partial2 ? `Patch partially applied — ${hunks.length - failures.length} of ${hunks.length} hunk(s) succeeded. Failed: ${failedList}. Successful changes are kept; use \`aft_safety\` to revert if you want to abort.` : `Patch failed — none of the ${hunks.length} hunk(s) applied: ${failedList}.`;
36574
36896
  results.push(summary);
36575
36897
  if (!partial2) {
36576
36898
  throw new Error(results.join(`
@@ -36578,28 +36900,56 @@ ${diagLines}`);
36578
36900
  }
36579
36901
  }
36580
36902
  {
36581
- const diffByPath = new Map(perFileDiffs.map((d) => [d.filePath, d]));
36582
- const failedPaths = new Set(failures);
36583
- const appliedHunks = hunks.filter((h) => !failedPaths.has(h.path));
36584
- const files = appliedHunks.map((h) => {
36585
- const filePath = resolvePathFromProjectRoot(projectRoot, h.path);
36586
- const rawMovePath = h.type === "update" ? h.move_path : undefined;
36587
- const movePath = rawMovePath ? resolvePathFromProjectRoot(projectRoot, rawMovePath) : undefined;
36588
- const displayPath = movePath ?? filePath;
36589
- const relPath = path4.relative(projectRoot, displayPath);
36590
- const diffEntry = diffByPath.get(filePath);
36591
- const patch = diffEntry ? buildUnifiedDiff(displayPath, diffEntry.before, diffEntry.after) : "";
36592
- const additions = diffEntry?.additions ?? 0;
36593
- const deletions = diffEntry?.deletions ?? 0;
36594
- const uiType = h.type === "update" && rawMovePath ? "move" : h.type;
36903
+ const diffByReportKey = new Map;
36904
+ for (const applied of appliedHunkResults) {
36905
+ const reportKey = applied.movePath ? `${applied.filePath}\x00${applied.displayPath}` : applied.filePath;
36906
+ const existing = diffByReportKey.get(reportKey);
36907
+ if (!existing) {
36908
+ diffByReportKey.set(reportKey, {
36909
+ filePath: applied.filePath,
36910
+ displayPath: applied.displayPath,
36911
+ ...applied.movePath ? { movePath: applied.movePath } : {},
36912
+ lastHunk: applied.hunk,
36913
+ before: applied.before,
36914
+ after: applied.after,
36915
+ additions: applied.additions,
36916
+ deletions: applied.deletions,
36917
+ hunkCount: 1
36918
+ });
36919
+ continue;
36920
+ }
36921
+ existing.displayPath = applied.displayPath;
36922
+ existing.movePath = applied.movePath ?? existing.movePath;
36923
+ existing.lastHunk = applied.hunk;
36924
+ existing.after = applied.after;
36925
+ existing.hunkCount += 1;
36926
+ const netCounts = countDiffLines(existing.before, existing.after);
36927
+ existing.additions = netCounts.additions;
36928
+ existing.deletions = netCounts.deletions;
36929
+ }
36930
+ const files = Array.from(diffByReportKey.values()).map((entry) => {
36931
+ const relPath = path5.relative(projectRoot, entry.displayPath);
36932
+ const patch = buildUnifiedDiff(entry.displayPath, entry.before, entry.after);
36933
+ let uiType;
36934
+ if (entry.movePath) {
36935
+ uiType = "move";
36936
+ } else if (entry.hunkCount === 1) {
36937
+ uiType = entry.lastHunk.type;
36938
+ } else if (entry.before.length === 0 && entry.after.length > 0) {
36939
+ uiType = "add";
36940
+ } else if (entry.before.length > 0 && entry.after.length === 0) {
36941
+ uiType = "delete";
36942
+ } else {
36943
+ uiType = "update";
36944
+ }
36595
36945
  return {
36596
- filePath,
36946
+ filePath: entry.filePath,
36597
36947
  relativePath: relPath,
36598
36948
  type: uiType,
36599
36949
  patch,
36600
- additions,
36601
- deletions,
36602
- ...movePath ? { movePath } : {}
36950
+ additions: entry.additions,
36951
+ deletions: entry.deletions,
36952
+ ...entry.movePath ? { movePath: entry.movePath } : {}
36603
36953
  };
36604
36954
  });
36605
36955
  const fileList = files.map((f) => {
@@ -36607,7 +36957,7 @@ ${diagLines}`);
36607
36957
  return `${prefix} ${f.relativePath}`;
36608
36958
  }).join(`
36609
36959
  `);
36610
- const title = failures.length > 0 ? `Partially applied (${files.length} of ${hunks.length}). Updated:
36960
+ const title = failures.length > 0 ? `Partially applied (${hunks.length - failures.length} of ${hunks.length}). Updated:
36611
36961
  ${fileList}` : `Success. Updated the following files:
36612
36962
  ${fileList}`;
36613
36963
  const diffText = files.map((f) => f.patch).filter(Boolean).join(`
@@ -36760,18 +37110,20 @@ function aftPrefixedTools(ctx) {
36760
37110
  const file2 = normalizedArgs.filePath;
36761
37111
  const projectRoot = await resolveProjectRoot(ctx, context);
36762
37112
  const filePath = resolvePathFromProjectRoot(projectRoot, file2);
36763
- const relPath = path4.relative(projectRoot, filePath);
37113
+ const relPath = path5.relative(projectRoot, filePath);
36764
37114
  {
36765
- const denial = await assertExternalDirectoryPermission(context, filePath);
36766
- if (denial)
36767
- return permissionDeniedResponse(denial);
37115
+ const denial2 = await assertExternalDirectoryPermission(context, filePath);
37116
+ if (denial2)
37117
+ return permissionDeniedResponse(denial2);
36768
37118
  }
36769
- await runAsk(context.ask({
36770
- permission: "edit",
36771
- patterns: [relPath],
36772
- always: ["*"],
36773
- metadata: { filepath: filePath }
36774
- }));
37119
+ const currentContent = await readCurrentFileForPreview(filePath);
37120
+ const previewDiff = buildUnifiedDiff(filePath, currentContent, normalizedArgs.content);
37121
+ const denial = await askEditPermission(context, [relPath], {
37122
+ filepath: filePath,
37123
+ diff: previewDiff
37124
+ });
37125
+ if (denial)
37126
+ return permissionDeniedResponse(denial);
36775
37127
  const writeParams = {
36776
37128
  file: filePath,
36777
37129
  content: normalizedArgs.content,
@@ -37155,15 +37507,15 @@ function buildZoomTitle(args) {
37155
37507
  }
37156
37508
  return `${args.targets.filePath}#${args.targets.symbol}`;
37157
37509
  }
37158
- const path5 = args.filePath ?? args.url ?? "";
37510
+ const path6 = args.filePath ?? args.url ?? "";
37159
37511
  if (typeof args.symbols === "string")
37160
- return path5 ? `${path5}#${args.symbols}` : args.symbols;
37512
+ return path6 ? `${path6}#${args.symbols}` : args.symbols;
37161
37513
  if (Array.isArray(args.symbols) && args.symbols.length > 0) {
37162
37514
  if (args.symbols.length === 1)
37163
- return path5 ? `${path5}#${args.symbols[0]}` : args.symbols[0];
37164
- return path5 ? `${path5} (${args.symbols.length} symbols)` : `${args.symbols.length} symbols`;
37515
+ return path6 ? `${path6}#${args.symbols[0]}` : args.symbols[0];
37516
+ return path6 ? `${path6} (${args.symbols.length} symbols)` : `${args.symbols.length} symbols`;
37165
37517
  }
37166
- return path5 || "(no target)";
37518
+ return path6 || "(no target)";
37167
37519
  }
37168
37520
  function readingTools(ctx) {
37169
37521
  return {
@@ -37396,6 +37748,11 @@ function readingTools(ctx) {
37396
37748
  }));
37397
37749
  if (symbolsArray.length === 1) {
37398
37750
  const response = results[0] ?? { success: false, message: "missing zoom response" };
37751
+ const rustBatch = unwrapRustZoomBatchEnvelope(response);
37752
+ if (rustBatch) {
37753
+ const batch = formatZoomBatchResult(targetLabel, rustBatch.names, rustBatch.responses);
37754
+ return withMeta(batch.text);
37755
+ }
37399
37756
  if (response.success === false) {
37400
37757
  throw new Error(response.message || "zoom failed");
37401
37758
  }
@@ -37664,11 +38021,11 @@ function refactoringTools(ctx) {
37664
38021
  }
37665
38022
 
37666
38023
  // src/tools/safety.ts
37667
- import * as path5 from "node:path";
38024
+ import * as path6 from "node:path";
37668
38025
  import { tool as tool14 } from "@opencode-ai/plugin";
37669
38026
  var z14 = tool14.schema;
37670
38027
  function responsePaths(response) {
37671
- return Array.isArray(response.paths) ? response.paths.filter((path6) => typeof path6 === "string" && path6.length > 0) : [];
38028
+ return Array.isArray(response.paths) ? response.paths.filter((path7) => typeof path7 === "string" && path7.length > 0) : [];
37672
38029
  }
37673
38030
  function bridgeErrorMessage(response, fallback) {
37674
38031
  return typeof response.message === "string" && response.message.length > 0 ? response.message : fallback;
@@ -37745,9 +38102,9 @@ function safetyTools(ctx) {
37745
38102
  for (const rawFile of checkpointFiles) {
37746
38103
  if (typeof rawFile !== "string")
37747
38104
  continue;
37748
- const file2 = expandTilde(rawFile);
37749
- const abs = path5.isAbsolute(file2) ? file2 : path5.resolve(projectRoot, file2);
37750
- const parent = path5.dirname(abs);
38105
+ const file2 = expandTilde2(rawFile);
38106
+ const abs = path6.isAbsolute(file2) ? file2 : path6.resolve(projectRoot, file2);
38107
+ const parent = path6.dirname(abs);
37751
38108
  if (uniqueParents.has(parent))
37752
38109
  continue;
37753
38110
  uniqueParents.add(parent);
@@ -37785,8 +38142,8 @@ function safetyTools(ctx) {
37785
38142
  const params = {};
37786
38143
  if (args.name !== undefined)
37787
38144
  params.name = args.name;
37788
- const payloadFiles = coerceStringArray(args.files).map(expandTilde);
37789
- const filePathArg = typeof args.filePath === "string" ? expandTilde(args.filePath) : undefined;
38145
+ const payloadFiles = coerceStringArray(args.files).map(expandTilde2);
38146
+ const filePathArg = typeof args.filePath === "string" ? expandTilde2(args.filePath) : undefined;
37790
38147
  if (op === "checkpoint") {
37791
38148
  if (payloadFiles.length > 0) {
37792
38149
  params.files = payloadFiles;
@@ -37811,7 +38168,7 @@ function safetyTools(ctx) {
37811
38168
 
37812
38169
  // src/tools/search.ts
37813
38170
  import * as fs7 from "node:fs";
37814
- import * as path6 from "node:path";
38171
+ import * as path7 from "node:path";
37815
38172
  function arg2(schema) {
37816
38173
  return schema;
37817
38174
  }
@@ -37843,7 +38200,7 @@ function normalizeGlob(pattern) {
37843
38200
  return pattern;
37844
38201
  }
37845
38202
  function absoluteSearchPath(projectRoot, target) {
37846
- return resolvePathFromProjectRoot(projectRoot, expandTilde(target));
38203
+ return resolvePathFromProjectRoot(projectRoot, expandTilde2(target));
37847
38204
  }
37848
38205
  function searchPathExists(projectRoot, target) {
37849
38206
  return fs7.existsSync(absoluteSearchPath(projectRoot, target));
@@ -37920,7 +38277,7 @@ function searchTools(ctx) {
37920
38277
  const projectRoot = await resolveProjectRoot(ctx, context);
37921
38278
  const pattern = String(args.pattern);
37922
38279
  const includeArg = args.include ? String(args.include) : undefined;
37923
- const pathArg = args.path ? expandTilde(String(args.path)) : undefined;
38280
+ const pathArg = args.path ? expandTilde2(String(args.path)) : undefined;
37924
38281
  const bridgePath = pathArg ? absoluteSearchPath(projectRoot, pathArg) : undefined;
37925
38282
  const grepDenied = await askGrepPermission(context, pattern, {
37926
38283
  path: bridgePath,
@@ -37958,8 +38315,8 @@ function searchTools(ctx) {
37958
38315
  },
37959
38316
  execute: async (args, context) => {
37960
38317
  const projectRoot = await resolveProjectRoot(ctx, context);
37961
- let globPattern = expandTilde(String(args.pattern));
37962
- let globPath = args.path ? expandTilde(String(args.path)) : undefined;
38318
+ let globPattern = expandTilde2(String(args.pattern));
38319
+ let globPath = args.path ? expandTilde2(String(args.path)) : undefined;
37963
38320
  if (!globPath && globPattern.startsWith("/")) {
37964
38321
  const metaIdx = globPattern.search(/[*?{}[\]]/);
37965
38322
  if (metaIdx > 0) {
@@ -37969,8 +38326,8 @@ function searchTools(ctx) {
37969
38326
  globPattern = `**/${globPattern.slice(lastSlash + 1)}`;
37970
38327
  }
37971
38328
  } else if (metaIdx === -1) {
37972
- globPath = path6.dirname(globPattern);
37973
- globPattern = path6.basename(globPattern);
38329
+ globPath = path7.dirname(globPattern);
38330
+ globPattern = path7.basename(globPattern);
37974
38331
  }
37975
38332
  }
37976
38333
  const globDenied = await askGlobPermission(context, globPattern, { path: globPath });
@@ -38218,12 +38575,12 @@ var PLUGIN_VERSION = (() => {
38218
38575
  return "0.0.0";
38219
38576
  }
38220
38577
  })();
38221
- var ANNOUNCEMENT_VERSION = "0.38.0";
38578
+ var ANNOUNCEMENT_VERSION = "0.39.0";
38222
38579
  var ANNOUNCEMENT_FEATURES = [
38223
- "Code Health you can trust: dead code and unused exports are back in the sidebar and status displays, rebuilt on a real TS/JS module-graph engine (oxc) barrel re-exports, entry points, and dynamic imports resolve correctly now.",
38224
- "Tools stay responsive during builds: filesystem events are processed off the request thread, so a cargo/webpack compile no longer queues your tool calls behind an event flood.",
38225
- "Pascal support in outline, zoom, and the AST tools.",
38226
- "Sidebar reliability: the status panel reconnects after heavy host load instead of going dark, and transient environment errors (schema fetch timeouts) no longer count as code errors."
38580
+ "Accurate Rust dead code: constructors and associated functions reached through receiver-type inference are no longer mis-reported as dead.",
38581
+ "Code Health scans reparse only the file you changed instead of the whole project on every edit — a single-file edit dropped from ~3s of scan work to ~130ms on this repo, byte-identical results.",
38582
+ "R language support in outline, zoom, and the AST tools.",
38583
+ "Edit approval prompts now show the pending diff instead of 'No diff provided', so you can review a change before approving it."
38227
38584
  ];
38228
38585
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
38229
38586
  var plugin = async (input) => initializePluginForDirectory(input);
@@ -38335,13 +38692,13 @@ ${lines}
38335
38692
  const upgradePromise = (async () => {
38336
38693
  warn2(`WARNING: aft binary v${binaryVersion} is older than plugin v${minVersion}. ` + "Some features may not work. Attempting to download a compatible binary...");
38337
38694
  try {
38338
- const path7 = await ensureBinary(`v${minVersion}`);
38339
- if (!path7) {
38695
+ const path8 = await ensureBinary(`v${minVersion}`);
38696
+ if (!path8) {
38340
38697
  warn2(`Could not find or download v${minVersion}. Continuing with v${binaryVersion}.`);
38341
38698
  return null;
38342
38699
  }
38343
- log2(`Found/downloaded compatible binary at ${path7}. Replacing running bridges...`);
38344
- const replaced = await pool.replaceBinary(path7);
38700
+ log2(`Found/downloaded compatible binary at ${path8}. Replacing running bridges...`);
38701
+ const replaced = await pool.replaceBinary(path8);
38345
38702
  log2("Binary replaced successfully. New bridges will use the updated binary.");
38346
38703
  return replaced;
38347
38704
  } catch (err) {