@wrongstack/tools 0.307.0 → 0.308.0

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/builtin.js CHANGED
@@ -1031,8 +1031,8 @@ async function* spawnStream(opts) {
1031
1031
  try {
1032
1032
  for (; ; ) {
1033
1033
  while (queue.length === 0) {
1034
- await new Promise((resolve22) => {
1035
- waiter = resolve22;
1034
+ await new Promise((resolve23) => {
1035
+ waiter = resolve23;
1036
1036
  });
1037
1037
  }
1038
1038
  const chunk = queue.shift();
@@ -1135,9 +1135,9 @@ async function detectPackageManager(cwd, stopAt) {
1135
1135
  return "npm";
1136
1136
  }
1137
1137
  async function detectPackageManagerInDir(dir) {
1138
- const fs42 = await import("node:fs/promises");
1138
+ const fs43 = await import("node:fs/promises");
1139
1139
  try {
1140
- const raw = await fs42.readFile(path3.join(dir, "package.json"), "utf8");
1140
+ const raw = await fs43.readFile(path3.join(dir, "package.json"), "utf8");
1141
1141
  const declared = JSON.parse(raw).packageManager;
1142
1142
  if (typeof declared === "string") {
1143
1143
  const name = declared.split("@")[0] ?? "";
@@ -1156,7 +1156,7 @@ async function detectPackageManagerInDir(dir) {
1156
1156
  ];
1157
1157
  for (const [file, manager] of lockfiles) {
1158
1158
  try {
1159
- await fs42.stat(`${dir}/${file}`);
1159
+ await fs43.stat(`${dir}/${file}`);
1160
1160
  return manager;
1161
1161
  } catch {
1162
1162
  }
@@ -4137,15 +4137,15 @@ async function changedPaths(before, after, beforeSizes, afterSizes) {
4137
4137
  const beforeSet = new Set(before);
4138
4138
  const afterSet = new Set(after);
4139
4139
  const changed = /* @__PURE__ */ new Set();
4140
- for (const path48 of after) {
4141
- if (!beforeSet.has(path48)) changed.add(path48);
4140
+ for (const path49 of after) {
4141
+ if (!beforeSet.has(path49)) changed.add(path49);
4142
4142
  }
4143
- for (const path48 of before) {
4144
- if (!afterSet.has(path48)) changed.add(path48);
4143
+ for (const path49 of before) {
4144
+ if (!afterSet.has(path49)) changed.add(path49);
4145
4145
  }
4146
4146
  if (beforeSizes && afterSizes) {
4147
- for (const path48 of after) {
4148
- if (beforeSizes.get(path48) !== afterSizes.get(path48)) changed.add(path48);
4147
+ for (const path49 of after) {
4148
+ if (beforeSizes.get(path49) !== afterSizes.get(path49)) changed.add(path49);
4149
4149
  }
4150
4150
  }
4151
4151
  return [...changed].sort();
@@ -5640,7 +5640,7 @@ async function syncGoParse(filePath, content, lang) {
5640
5640
  }
5641
5641
  const goBinary = resolveWin32Command("go");
5642
5642
  const goResult = await new Promise(
5643
- (resolve22, reject) => {
5643
+ (resolve23, reject) => {
5644
5644
  let settled = false;
5645
5645
  const proc = spawn5(goBinary, ["run", scriptPath], {
5646
5646
  stdio: ["pipe", "pipe", "pipe"],
@@ -5669,7 +5669,7 @@ async function syncGoParse(filePath, content, lang) {
5669
5669
  if (settled) return;
5670
5670
  settled = true;
5671
5671
  clearTimeout(timer);
5672
- resolve22({ code: code2, stdout: stdout2 });
5672
+ resolve23({ code: code2, stdout: stdout2 });
5673
5673
  });
5674
5674
  }
5675
5675
  );
@@ -6345,7 +6345,7 @@ async function resolvePython() {
6345
6345
  return null;
6346
6346
  }
6347
6347
  function commandIsAvailable(command) {
6348
- return new Promise((resolve22) => {
6348
+ return new Promise((resolve23) => {
6349
6349
  let settled = false;
6350
6350
  const proc = spawn6(command, ["--version"], {
6351
6351
  stdio: "ignore",
@@ -6355,7 +6355,7 @@ function commandIsAvailable(command) {
6355
6355
  if (settled) return;
6356
6356
  settled = true;
6357
6357
  clearTimeout(timer);
6358
- resolve22(available);
6358
+ resolve23(available);
6359
6359
  };
6360
6360
  const timer = setTimeout(() => {
6361
6361
  proc.kill("SIGKILL");
@@ -6367,7 +6367,7 @@ function commandIsAvailable(command) {
6367
6367
  });
6368
6368
  }
6369
6369
  function spawnPyParser(pyBinary, scriptPath, filePath, content) {
6370
- return new Promise((resolve22, reject) => {
6370
+ return new Promise((resolve23, reject) => {
6371
6371
  let settled = false;
6372
6372
  const proc = spawn6(pyBinary, [scriptPath, filePath], {
6373
6373
  stdio: ["pipe", "pipe", "pipe"],
@@ -6396,7 +6396,7 @@ function spawnPyParser(pyBinary, scriptPath, filePath, content) {
6396
6396
  if (settled) return;
6397
6397
  settled = true;
6398
6398
  clearTimeout(timer);
6399
- resolve22({ code, stdout });
6399
+ resolve23({ code, stdout });
6400
6400
  });
6401
6401
  });
6402
6402
  }
@@ -9230,10 +9230,10 @@ var bashTool = {
9230
9230
  queue.push(c);
9231
9231
  }
9232
9232
  };
9233
- const next = () => new Promise((resolve22) => {
9233
+ const next = () => new Promise((resolve23) => {
9234
9234
  const c = queue.shift();
9235
- if (c) resolve22(c);
9236
- else resolveNext = resolve22;
9235
+ if (c) resolve23(c);
9236
+ else resolveNext = resolve23;
9237
9237
  });
9238
9238
  let lastFlush = Date.now();
9239
9239
  const flush = () => {
@@ -9696,11 +9696,11 @@ var BrowserArtifactStore = class {
9696
9696
  };
9697
9697
  async function hashFile(target) {
9698
9698
  const hash = createHash3("sha256");
9699
- await new Promise((resolve22, reject) => {
9699
+ await new Promise((resolve23, reject) => {
9700
9700
  const stream = createReadStream(target);
9701
9701
  stream.on("data", (chunk) => hash.update(chunk));
9702
9702
  stream.once("error", reject);
9703
- stream.once("end", resolve22);
9703
+ stream.once("end", resolve23);
9704
9704
  });
9705
9705
  return hash.digest("hex");
9706
9706
  }
@@ -9850,7 +9850,7 @@ var BrowserNetworkGuardProxy = class {
9850
9850
  async start() {
9851
9851
  if (this.url) return this.url;
9852
9852
  if (this.startPromise) return this.startPromise;
9853
- this.startPromise = new Promise((resolve22, reject) => {
9853
+ this.startPromise = new Promise((resolve23, reject) => {
9854
9854
  const onError = (error) => {
9855
9855
  this.server.off("listening", onListening);
9856
9856
  reject(error);
@@ -9863,7 +9863,7 @@ var BrowserNetworkGuardProxy = class {
9863
9863
  return;
9864
9864
  }
9865
9865
  this.url = `http://127.0.0.1:${address.port}`;
9866
- resolve22(this.url);
9866
+ resolve23(this.url);
9867
9867
  };
9868
9868
  this.server.once("error", onError);
9869
9869
  this.server.once("listening", onListening);
@@ -9878,7 +9878,7 @@ var BrowserNetworkGuardProxy = class {
9878
9878
  for (const socket of this.sockets) socket.destroy();
9879
9879
  this.sockets.clear();
9880
9880
  if (!this.server.listening) return;
9881
- await new Promise((resolve22) => this.server.close(() => resolve22()));
9881
+ await new Promise((resolve23) => this.server.close(() => resolve23()));
9882
9882
  }
9883
9883
  async forwardHttp(request2, response) {
9884
9884
  try {
@@ -10451,7 +10451,7 @@ function pushBounded(target, value, limit) {
10451
10451
  }
10452
10452
  async function abortable(signal, operation, onAbort) {
10453
10453
  signal.throwIfAborted();
10454
- return new Promise((resolve22, reject) => {
10454
+ return new Promise((resolve23, reject) => {
10455
10455
  let settled = false;
10456
10456
  let aborting = false;
10457
10457
  const finish = (fn) => {
@@ -10469,7 +10469,7 @@ async function abortable(signal, operation, onAbort) {
10469
10469
  signal.addEventListener("abort", abort, { once: true });
10470
10470
  operation().then(
10471
10471
  (value) => {
10472
- if (!aborting) finish(() => resolve22(value));
10472
+ if (!aborting) finish(() => resolve23(value));
10473
10473
  },
10474
10474
  (err) => {
10475
10475
  if (!aborting) finish(() => reject(err));
@@ -11725,10 +11725,16 @@ function buildFileGraphNodeState(pkgSyms, localFiles, packageOf) {
11725
11725
  }
11726
11726
  return { fileNodes, symToFile, fileStats, ensureFileNode };
11727
11727
  }
11728
- function buildSymbolGraphNodes(symById, relatedIds, fileFilter, packageOf) {
11728
+ function buildSymbolGraphNodes(symById, relatedIds, localFiles, packageOf) {
11729
+ const local = new Set(
11730
+ [...typeof localFiles === "string" ? [localFiles] : localFiles].map(
11731
+ (file) => file.replace(/\\/g, "/")
11732
+ )
11733
+ );
11734
+ const isLocal = (file) => local.has(file.replace(/\\/g, "/"));
11729
11735
  return [...relatedIds].map((id) => symById.get(id)).filter((symbol) => symbol !== void 0).sort((a, b) => {
11730
- const aExternal = a.file === fileFilter ? 0 : 1;
11731
- const bExternal = b.file === fileFilter ? 0 : 1;
11736
+ const aExternal = isLocal(a.file) ? 0 : 1;
11737
+ const bExternal = isLocal(b.file) ? 0 : 1;
11732
11738
  return aExternal - bExternal || a.file.localeCompare(b.file) || a.line - b.line || a.id - b.id;
11733
11739
  }).map((s) => ({
11734
11740
  id: `sym:${s.id}`,
@@ -11742,7 +11748,7 @@ function buildSymbolGraphNodes(symById, relatedIds, fileFilter, packageOf) {
11742
11748
  line: s.line,
11743
11749
  signature: s.signature,
11744
11750
  scope: s.scope,
11745
- external: s.file !== fileFilter
11751
+ external: !isLocal(s.file)
11746
11752
  }));
11747
11753
  }
11748
11754
  function addWeightedEdge(edgeMap, source, target, callType, weight) {
@@ -11777,6 +11783,56 @@ function materializeWeightedEdges(edgeMap, idPrefix) {
11777
11783
  return edges;
11778
11784
  }
11779
11785
 
11786
+ // src/codebase-index/writer-helpers.ts
11787
+ import { resolveWstackPaths as resolveWstackPaths2 } from "@wrongstack/core/utils";
11788
+ function escapeLike(value) {
11789
+ return value.replace(/[\\%_]/g, (char) => `\\${char}`);
11790
+ }
11791
+ function posixIndexPath(file) {
11792
+ return file.replace(/\\/g, "/").replace(/^\.\//, "");
11793
+ }
11794
+ function indexedFileMatchSql(column = "file") {
11795
+ return `(${column} = ? OR replace(${column}, '\\', '/') = ? OR replace(${column}, '\\', '/') LIKE ? ESCAPE '\\')`;
11796
+ }
11797
+ function indexedFileMatchArgs(file) {
11798
+ const posix4 = posixIndexPath(file.trim());
11799
+ return [file, posix4, `%/${escapeLike(posix4)}`];
11800
+ }
11801
+ function matchesIndexedPackageFilter(storedFile, packageLabel, filter) {
11802
+ if (packageLabel === filter) return true;
11803
+ const posixFile = posixIndexPath(storedFile);
11804
+ const posixFilter = posixIndexPath(filter.trim());
11805
+ if (!posixFilter) return false;
11806
+ return posixFile === posixFilter || posixFile.endsWith(`/${posixFilter}`) || posixFile.includes(`/${posixFilter}/`);
11807
+ }
11808
+ function assignRefsToSymbols(refs, symbols) {
11809
+ if (refs.length === 0 || symbols.length === 0) return [];
11810
+ const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
11811
+ const seen = /* @__PURE__ */ new Set();
11812
+ const assigned = [];
11813
+ for (const ref of refs) {
11814
+ let owner2;
11815
+ for (const symbol of ordered) {
11816
+ if (symbol.line > ref.line) break;
11817
+ owner2 = symbol;
11818
+ }
11819
+ if (!owner2 && ref.callType === "import") owner2 = ordered[0];
11820
+ if (!owner2 || owner2.id <= 0) continue;
11821
+ const key = `${owner2.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
11822
+ if (seen.has(key)) continue;
11823
+ seen.add(key);
11824
+ assigned.push({ ...ref, fromId: owner2.id });
11825
+ }
11826
+ return assigned;
11827
+ }
11828
+ function resolveIndexDir(projectRoot, override) {
11829
+ return override ?? resolveWstackPaths2({ projectRoot }).projectCodebaseIndex;
11830
+ }
11831
+ function codebaseIndexDirOverride(ctx) {
11832
+ const v = ctx.meta?.["codebaseIndexDir"];
11833
+ return typeof v === "string" ? v : void 0;
11834
+ }
11835
+
11780
11836
  // src/codebase-index/writer-ref-mapper.ts
11781
11837
  function mapWriterRefRow(row) {
11782
11838
  return {
@@ -11832,10 +11888,23 @@ function mapCallSiteRow(row) {
11832
11888
  line: row.ref_line
11833
11889
  };
11834
11890
  }
11891
+ function resolveIndexedFiles(stmt, file) {
11892
+ const rows = stmt(
11893
+ `SELECT DISTINCT file FROM symbols WHERE ${indexedFileMatchSql("file")} ORDER BY length(file), file`
11894
+ ).all(...indexedFileMatchArgs(file));
11895
+ return rows.map((row) => row.file);
11896
+ }
11835
11897
  function resolveSymbolIds(stmt, symbolName, file) {
11836
- const baseSql = file ? `SELECT id FROM symbols WHERE name = ? AND file = ? ORDER BY id` : `SELECT id FROM symbols WHERE name = ? ORDER BY id`;
11837
- const args = file ? [symbolName, file] : [symbolName];
11838
- const rows = stmt(baseSql).all(...args);
11898
+ if (!file) {
11899
+ const rows2 = stmt("SELECT id FROM symbols WHERE name = ? ORDER BY id").all(symbolName);
11900
+ return rows2.map((r) => r.id);
11901
+ }
11902
+ const indexedFiles = resolveIndexedFiles(stmt, file);
11903
+ if (indexedFiles.length === 0) return [];
11904
+ const placeholders = indexedFiles.map(() => "?").join(",");
11905
+ const rows = stmt(
11906
+ `SELECT id FROM symbols WHERE name = ? AND file IN (${placeholders}) ORDER BY id`
11907
+ ).all(symbolName, ...indexedFiles);
11839
11908
  return rows.map((r) => r.id);
11840
11909
  }
11841
11910
  function findIncomingCallsByName(stmt, symbolName, file, limit) {
@@ -12157,7 +12226,7 @@ function getFileGraphWithStatement(stmt, packageFilter) {
12157
12226
  const allFiles = stmt("SELECT DISTINCT file FROM symbols").all();
12158
12227
  const packageOf = readPackageLabeller(stmt);
12159
12228
  const langOf = (file) => detectLang(file) ?? "other";
12160
- const pkgFilePaths = allFiles.filter((f) => packageOf(f.file) === packageFilter).map((f) => f.file);
12229
+ const pkgFilePaths = allFiles.filter((f) => matchesIndexedPackageFilter(f.file, packageOf(f.file), packageFilter)).map((f) => f.file);
12161
12230
  const localFiles = new Set(pkgFilePaths);
12162
12231
  if (localFiles.size === 0) return { nodes: [], edges: [] };
12163
12232
  const filePlaceholders = [...localFiles].map(() => "?").join(",");
@@ -12233,9 +12302,12 @@ function getFileGraphWithStatement(stmt, packageFilter) {
12233
12302
  return { nodes: [...fileNodes.values()], edges };
12234
12303
  }
12235
12304
  function getSymbolGraphWithStatement(stmt, fileFilter) {
12305
+ const indexedFiles = resolveIndexedFiles(stmt, fileFilter);
12306
+ if (indexedFiles.length === 0) return { nodes: [], edges: [] };
12307
+ const filePlaceholders = indexedFiles.map(() => "?").join(",");
12236
12308
  const syms = stmt(
12237
- "SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file = ? ORDER BY line, id"
12238
- ).all(fileFilter);
12309
+ `SELECT id, name, kind, lang, file, line, signature, scope FROM symbols WHERE file IN (${filePlaceholders}) ORDER BY line, id`
12310
+ ).all(...indexedFiles);
12239
12311
  if (syms.length === 0) return { nodes: [], edges: [] };
12240
12312
  const symById = new Map(syms.map((symbol) => [symbol.id, symbol]));
12241
12313
  const relatedIds = new Set(syms.map((symbol) => symbol.id));
@@ -12245,16 +12317,16 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
12245
12317
  SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line
12246
12318
  FROM refs r
12247
12319
  JOIN symbols s ON s.id = r.from_id
12248
- WHERE s.file = ?
12320
+ WHERE s.file IN (${filePlaceholders})
12249
12321
  UNION
12250
12322
  SELECT r.from_id, r.to_id, r.to_name, r.call_type, r.line
12251
12323
  FROM refs r
12252
12324
  JOIN symbols s ON s.id = r.to_id
12253
- WHERE s.file = ?
12325
+ WHERE s.file IN (${filePlaceholders})
12254
12326
  )
12255
12327
  WHERE to_id IS NOT NULL
12256
12328
  GROUP BY from_id, to_id, call_type`
12257
- ).all(fileFilter, fileFilter);
12329
+ ).all(...indexedFiles, ...indexedFiles);
12258
12330
  const edgeMap = /* @__PURE__ */ new Map();
12259
12331
  for (const r of refRows) {
12260
12332
  if (r.to_id == null) continue;
@@ -12273,43 +12345,15 @@ function getSymbolGraphWithStatement(stmt, fileFilter) {
12273
12345
  ).all(...missingIds);
12274
12346
  for (const s of extras) symById.set(s.id, s);
12275
12347
  }
12276
- const nodes = buildSymbolGraphNodes(symById, relatedIds, fileFilter, readPackageLabeller(stmt));
12348
+ const nodes = buildSymbolGraphNodes(
12349
+ symById,
12350
+ relatedIds,
12351
+ new Set(syms.map((symbol) => symbol.file)),
12352
+ readPackageLabeller(stmt)
12353
+ );
12277
12354
  return { nodes, edges };
12278
12355
  }
12279
12356
 
12280
- // src/codebase-index/writer-helpers.ts
12281
- import { resolveWstackPaths as resolveWstackPaths2 } from "@wrongstack/core/utils";
12282
- function escapeLike(value) {
12283
- return value.replace(/[\\%_]/g, (char) => `\\${char}`);
12284
- }
12285
- function assignRefsToSymbols(refs, symbols) {
12286
- if (refs.length === 0 || symbols.length === 0) return [];
12287
- const ordered = [...symbols].sort((a, b) => a.line - b.line || a.col - b.col || a.id - b.id);
12288
- const seen = /* @__PURE__ */ new Set();
12289
- const assigned = [];
12290
- for (const ref of refs) {
12291
- let owner2;
12292
- for (const symbol of ordered) {
12293
- if (symbol.line > ref.line) break;
12294
- owner2 = symbol;
12295
- }
12296
- if (!owner2 && ref.callType === "import") owner2 = ordered[0];
12297
- if (!owner2 || owner2.id <= 0) continue;
12298
- const key = `${owner2.id}:${ref.toName}:${ref.callType}:${ref.module ?? ""}`;
12299
- if (seen.has(key)) continue;
12300
- seen.add(key);
12301
- assigned.push({ ...ref, fromId: owner2.id });
12302
- }
12303
- return assigned;
12304
- }
12305
- function resolveIndexDir(projectRoot, override) {
12306
- return override ?? resolveWstackPaths2({ projectRoot }).projectCodebaseIndex;
12307
- }
12308
- function codebaseIndexDirOverride(ctx) {
12309
- const v = ctx.meta?.["codebaseIndexDir"];
12310
- return typeof v === "string" ? v : void 0;
12311
- }
12312
-
12313
12357
  // src/codebase-index/vector-search.ts
12314
12358
  var RRF_K = 60;
12315
12359
  var VECTOR_DIMENSIONS = 384;
@@ -13952,8 +13996,8 @@ function isProjectIndexServerHealth(value) {
13952
13996
  return typeof health.checkedAt === "number" && typeof health.uptimeMs === "number" && typeof memory?.rss === "number" && typeof memory.heapUsed === "number" && typeof memory.heapTotal === "number" && typeof memory.external === "number" && typeof health.clients === "number" && typeof health.activeRequests === "number" && typeof health.activeWrites === "number" && typeof health.queuedWrites === "number" && typeof health.pendingExternalFiles === "number" && typeof health.watchingExternal === "boolean" && typeof activity?.indexing === "boolean" && typeof activity.currentFile === "number" && typeof activity.totalFiles === "number" && typeof activity.generation === "number";
13953
13997
  }
13954
13998
  function delay(ms) {
13955
- return new Promise((resolve22) => {
13956
- const timer = setTimeout(resolve22, ms);
13999
+ return new Promise((resolve23) => {
14000
+ const timer = setTimeout(resolve23, ms);
13957
14001
  timer.unref?.();
13958
14002
  });
13959
14003
  }
@@ -14159,7 +14203,7 @@ var ProjectServerConnection = class {
14159
14203
  return Promise.reject(new Error("codebase-index server connection is not available"));
14160
14204
  }
14161
14205
  const id = this.nextId++;
14162
- return new Promise((resolve22, reject) => {
14206
+ return new Promise((resolve23, reject) => {
14163
14207
  const timer = setTimeout(() => {
14164
14208
  const entry = this.pending.get(id);
14165
14209
  if (!entry) return;
@@ -14182,7 +14226,7 @@ var ProjectServerConnection = class {
14182
14226
  entry.reject(cancellationError(signal));
14183
14227
  } : void 0;
14184
14228
  this.pending.set(id, {
14185
- resolve: resolve22,
14229
+ resolve: resolve23,
14186
14230
  reject,
14187
14231
  timer,
14188
14232
  signal,
@@ -14251,7 +14295,7 @@ var ProjectServerConnection = class {
14251
14295
  this.binaryBuffer = [];
14252
14296
  this.useBinary = false;
14253
14297
  this.textDecoder = null;
14254
- return new Promise((resolve22, reject) => {
14298
+ return new Promise((resolve23, reject) => {
14255
14299
  const socket = net3.createConnection(this.endpoint);
14256
14300
  this.socket = socket;
14257
14301
  const timer = setTimeout(() => {
@@ -14263,7 +14307,7 @@ var ProjectServerConnection = class {
14263
14307
  clearTimeout(timer);
14264
14308
  this.connectResolve = null;
14265
14309
  this.connectReject = null;
14266
- resolve22();
14310
+ resolve23();
14267
14311
  };
14268
14312
  const finishReject = (error) => {
14269
14313
  clearTimeout(timer);
@@ -15277,9 +15321,9 @@ var ParserWorkerPool = class {
15277
15321
  for (let i = 0; i < files.length; i++) {
15278
15322
  chunks[i % workerCount].push(files[i]);
15279
15323
  }
15280
- return new Promise((resolve22, reject) => {
15324
+ return new Promise((resolve23, reject) => {
15281
15325
  this.pending.set(batchId, {
15282
- resolve: resolve22,
15326
+ resolve: resolve23,
15283
15327
  reject,
15284
15328
  accumulated: [],
15285
15329
  expectedWorkers: workerCount,
@@ -15310,10 +15354,10 @@ var ParserWorkerPool = class {
15310
15354
  await Promise.allSettled(
15311
15355
  workers.map(
15312
15356
  (w) => Promise.race([
15313
- new Promise((resolve22) => {
15314
- w.once("exit", () => resolve22());
15357
+ new Promise((resolve23) => {
15358
+ w.once("exit", () => resolve23());
15315
15359
  }),
15316
- new Promise((resolve22) => setTimeout(() => resolve22(), 2e3))
15360
+ new Promise((resolve23) => setTimeout(() => resolve23(), 2e3))
15317
15361
  ]).then(() => {
15318
15362
  if (!w.threadId) return;
15319
15363
  return w.terminate().catch(() => {
@@ -15374,7 +15418,7 @@ function shouldUseParserWorkerPool(candidateFileCount, parseBatchCount) {
15374
15418
  return !isFrugalPerf() && candidateFileCount >= WORKER_POOL_THRESHOLD && parseBatchCount > 1;
15375
15419
  }
15376
15420
  function yieldEventLoop() {
15377
- return new Promise((resolve22) => setImmediate(resolve22));
15421
+ return new Promise((resolve23) => setImmediate(resolve23));
15378
15422
  }
15379
15423
  function throwIfAborted(signal) {
15380
15424
  if (!signal?.aborted) return;
@@ -15406,7 +15450,7 @@ function normalizeComparablePath(value) {
15406
15450
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
15407
15451
  }
15408
15452
  function gitOutput(projectRoot, args) {
15409
- return new Promise((resolve22, reject) => {
15453
+ return new Promise((resolve23, reject) => {
15410
15454
  execFile(
15411
15455
  "git",
15412
15456
  ["-C", projectRoot, ...args],
@@ -15417,7 +15461,7 @@ function gitOutput(projectRoot, args) {
15417
15461
  },
15418
15462
  (error, stdout) => {
15419
15463
  if (error) reject(error);
15420
- else resolve22(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
15464
+ else resolve23(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
15421
15465
  }
15422
15466
  );
15423
15467
  });
@@ -16193,7 +16237,7 @@ function callIndexOp(op, args, opts) {
16193
16237
  opts.signal.reason instanceof Error ? opts.signal.reason : new Error("Indexing cancelled")
16194
16238
  );
16195
16239
  }
16196
- return new Promise((resolve22, reject) => {
16240
+ return new Promise((resolve23, reject) => {
16197
16241
  const id = nextRpcId++;
16198
16242
  const timer = setTimeout(() => {
16199
16243
  pending.delete(id);
@@ -16215,7 +16259,7 @@ function callIndexOp(op, args, opts) {
16215
16259
  pending.set(id, {
16216
16260
  resolve: (v) => {
16217
16261
  cleanup();
16218
- resolve22(v);
16262
+ resolve23(v);
16219
16263
  },
16220
16264
  reject: (e) => {
16221
16265
  cleanup();
@@ -17142,9 +17186,90 @@ var codebaseAstReplaceTool = {
17142
17186
  }
17143
17187
  };
17144
17188
 
17145
- // src/codebase-index/codebase-impact-analysis-tool.ts
17189
+ // src/codebase-index/codebase-invariant-check-tool.ts
17190
+ import * as fs22 from "node:fs/promises";
17146
17191
  import * as path27 from "node:path";
17147
17192
  import { toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
17193
+ init_languages2();
17194
+ var codebaseInvariantCheckTool = {
17195
+ name: "codebase-invariant-check",
17196
+ category: "Inspect",
17197
+ icon: "index",
17198
+ permission: "auto",
17199
+ subjectKey: "file",
17200
+ mutating: false,
17201
+ capabilities: ["fs.read"],
17202
+ description: "Mathematically verify AST backward-compatibility invariants between original code and candidate mutations across TypeScript, Python, Go, Rust, Java, etc. Detects missing exports (INV-001), mandatory parameter additions (INV-002), interface breaking expansions (INV-003), and return type changes (INV-004) with zero hallucinations.",
17203
+ usageHint: "PRE-FLIGHT AST INVARIANT AUDIT:\n\n- Provide `file` and `modifiedCode` (or both `originalCode` and `modifiedCode`).\n- Returns `valid: true` if changes are strictly backward compatible.\n- Returns structured `violations[]` with exact rule IDs, offending symbol names, and fix instructions.",
17204
+ inputSchema: {
17205
+ type: "object",
17206
+ properties: {
17207
+ file: {
17208
+ type: "string",
17209
+ description: "Path to existing source file to compare against candidate modifications."
17210
+ },
17211
+ originalCode: {
17212
+ type: "string",
17213
+ description: "Original source code string (if not reading from an existing file)."
17214
+ },
17215
+ modifiedCode: {
17216
+ type: "string",
17217
+ description: "The candidate modified code string to validate."
17218
+ },
17219
+ lang: {
17220
+ type: "string",
17221
+ description: "Language hint (ts, py, go, rs, etc.). Inferred from file path if omitted."
17222
+ }
17223
+ },
17224
+ required: ["modifiedCode"],
17225
+ additionalProperties: false
17226
+ },
17227
+ async execute(input, ctx) {
17228
+ try {
17229
+ let originalCode = input.originalCode;
17230
+ let lang = input.lang;
17231
+ if (!originalCode && input.file) {
17232
+ const resolved = path27.isAbsolute(input.file) ? input.file : path27.resolve(ctx.projectRoot, input.file);
17233
+ originalCode = await fs22.readFile(resolved, "utf8");
17234
+ if (!lang) {
17235
+ lang = detectLang(resolved) ?? "ts";
17236
+ }
17237
+ }
17238
+ if (originalCode === void 0) {
17239
+ return {
17240
+ valid: false,
17241
+ violations: [],
17242
+ summary: "Neither originalCode nor file was provided for invariant comparison.",
17243
+ error: "Missing originalCode or file path."
17244
+ };
17245
+ }
17246
+ const res = await polyglotInvariantEngine.evaluate({
17247
+ originalCode,
17248
+ modifiedCode: input.modifiedCode,
17249
+ lang,
17250
+ filePath: input.file
17251
+ });
17252
+ const summary = res.valid ? "AST Invariants PASSED. Mutation is 100% backward compatible." : `AST Invariants FAILED with ${res.violations.length} violation(s):
17253
+ ` + res.violations.map((v) => ` - [${v.ruleId}] ${v.symbolName}: ${v.message}`).join("\n");
17254
+ return {
17255
+ valid: res.valid,
17256
+ violations: res.violations,
17257
+ summary
17258
+ };
17259
+ } catch (err) {
17260
+ return {
17261
+ valid: false,
17262
+ violations: [],
17263
+ summary: `Error during invariant evaluation: ${toErrorMessage4(err)}`,
17264
+ error: toErrorMessage4(err)
17265
+ };
17266
+ }
17267
+ }
17268
+ };
17269
+
17270
+ // src/codebase-index/codebase-impact-analysis-tool.ts
17271
+ import * as path28 from "node:path";
17272
+ import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
17148
17273
  var TEST_FILE_REGEX = /(?:test|spec|mock|fixture|bench)[s]?[/\\._]/i;
17149
17274
  var codebaseImpactAnalysisTool = {
17150
17275
  name: "codebase-impact-analysis",
@@ -17181,6 +17306,9 @@ var codebaseImpactAnalysisTool = {
17181
17306
  const limit = 200;
17182
17307
  const transitive = input.transitive ?? true;
17183
17308
  let rawSites = [];
17309
+ let symbolFound = true;
17310
+ let indexUnavailable = false;
17311
+ let indexError;
17184
17312
  try {
17185
17313
  const serviced = await incomingCallsService2({
17186
17314
  projectRoot,
@@ -17191,13 +17319,49 @@ var codebaseImpactAnalysisTool = {
17191
17319
  transitive
17192
17320
  });
17193
17321
  rawSites = serviced.calls;
17194
- } catch {
17322
+ symbolFound = serviced.symbolFound;
17323
+ } catch (err) {
17324
+ symbolFound = false;
17325
+ indexUnavailable = true;
17326
+ indexError = toErrorMessage5(err);
17327
+ }
17328
+ if (indexUnavailable) {
17329
+ const detail = indexError ? ` (${indexError})` : "";
17330
+ const reason = `Index query failed for '${input.symbol}'. Run codebase-index, then retry.${detail}`;
17331
+ return {
17332
+ status: "error",
17333
+ symbol: input.symbol,
17334
+ riskLevel: "low",
17335
+ summary: reason,
17336
+ totalCallSites: 0,
17337
+ affectedProductionFiles: [],
17338
+ affectedTestFiles: [],
17339
+ callSites: [],
17340
+ recommendedActionPlan: [reason],
17341
+ symbolFound: false,
17342
+ error: reason
17343
+ };
17344
+ }
17345
+ if (!symbolFound) {
17346
+ const reason = `Symbol '${input.symbol}' was not found in the index` + (input.file ? ` for file filter '${input.file}'` : "") + ". Use codebase-search to verify the name.";
17347
+ return {
17348
+ status: "ok",
17349
+ symbol: input.symbol,
17350
+ riskLevel: "low",
17351
+ summary: reason,
17352
+ totalCallSites: 0,
17353
+ affectedProductionFiles: [],
17354
+ affectedTestFiles: [],
17355
+ callSites: [],
17356
+ recommendedActionPlan: [reason],
17357
+ symbolFound: false
17358
+ };
17195
17359
  }
17196
17360
  const prodFilesSet = /* @__PURE__ */ new Set();
17197
17361
  const testFilesSet = /* @__PURE__ */ new Set();
17198
17362
  const callSites = [];
17199
17363
  for (const site of rawSites) {
17200
- const relPath = path27.relative(projectRoot, site.symbol.file).replace(/\\/g, "/");
17364
+ const relPath = path28.relative(projectRoot, site.symbol.file).replace(/\\/g, "/");
17201
17365
  const isTest = TEST_FILE_REGEX.test(relPath);
17202
17366
  if (isTest) {
17203
17367
  testFilesSet.add(relPath);
@@ -17247,7 +17411,8 @@ var codebaseImpactAnalysisTool = {
17247
17411
  affectedProductionFiles: prodFiles,
17248
17412
  affectedTestFiles: testFiles,
17249
17413
  callSites,
17250
- recommendedActionPlan
17414
+ recommendedActionPlan,
17415
+ symbolFound: true
17251
17416
  };
17252
17417
  } catch (err) {
17253
17418
  return {
@@ -17260,14 +17425,15 @@ var codebaseImpactAnalysisTool = {
17260
17425
  affectedTestFiles: [],
17261
17426
  callSites: [],
17262
17427
  recommendedActionPlan: [],
17263
- error: toErrorMessage4(err)
17428
+ symbolFound: false,
17429
+ error: toErrorMessage5(err)
17264
17430
  };
17265
17431
  }
17266
17432
  }
17267
17433
  };
17268
17434
 
17269
17435
  // src/codebase-index/codebase-incoming-calls-tool.ts
17270
- import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
17436
+ import { toErrorMessage as toErrorMessage6 } from "@wrongstack/core/utils";
17271
17437
  var codebaseIncomingCallsTool = {
17272
17438
  name: "codebase-incoming-calls",
17273
17439
  category: "Project",
@@ -17340,7 +17506,7 @@ var codebaseIncomingCallsTool = {
17340
17506
  symbol: input.symbol,
17341
17507
  calls: [],
17342
17508
  total: 0,
17343
- indexStatus: `Index query failed: ${toErrorMessage5(err)}. Fall back to grep for this lookup.`
17509
+ indexStatus: `Index query failed: ${toErrorMessage6(err)}. Fall back to grep for this lookup.`
17344
17510
  };
17345
17511
  }
17346
17512
  const { calls, symbolFound, ambiguous, totalMatches } = serviced;
@@ -17395,7 +17561,7 @@ var codebaseIncomingCallsTool = {
17395
17561
  import { ToolValidationError } from "@wrongstack/core/types";
17396
17562
 
17397
17563
  // src/codebase-index/codebase-search-tool.ts
17398
- import { toErrorMessage as toErrorMessage6 } from "@wrongstack/core/utils";
17564
+ import { toErrorMessage as toErrorMessage7 } from "@wrongstack/core/utils";
17399
17565
  var INDEXABLE_LANG_IDS = [
17400
17566
  "ts",
17401
17567
  "tsx",
@@ -17520,7 +17686,7 @@ var codebaseSearchTool = {
17520
17686
  results: [],
17521
17687
  total: 0,
17522
17688
  query: input.query,
17523
- indexStatus: `Index query failed: ${toErrorMessage6(err)}. Fall back to grep/glob for this lookup.`
17689
+ indexStatus: `Index query failed: ${toErrorMessage7(err)}. Fall back to grep/glob for this lookup.`
17524
17690
  };
17525
17691
  }
17526
17692
  const { results, total } = searched;
@@ -17628,7 +17794,7 @@ var codebaseIndexTool = {
17628
17794
  };
17629
17795
 
17630
17796
  // src/codebase-index/codebase-outgoing-calls-tool.ts
17631
- import { toErrorMessage as toErrorMessage7 } from "@wrongstack/core/utils";
17797
+ import { toErrorMessage as toErrorMessage8 } from "@wrongstack/core/utils";
17632
17798
  var codebaseOutgoingCallsTool = {
17633
17799
  name: "codebase-outgoing-calls",
17634
17800
  category: "Project",
@@ -17701,7 +17867,7 @@ var codebaseOutgoingCallsTool = {
17701
17867
  symbol: input.symbol,
17702
17868
  calls: [],
17703
17869
  total: 0,
17704
- indexStatus: `Index query failed: ${toErrorMessage7(err)}. Fall back to grep for this lookup.`
17870
+ indexStatus: `Index query failed: ${toErrorMessage8(err)}. Fall back to grep for this lookup.`
17705
17871
  };
17706
17872
  }
17707
17873
  const { calls, symbolFound, unresolvedCount, totalMatches } = serviced;
@@ -17753,18 +17919,18 @@ var codebaseOutgoingCallsTool = {
17753
17919
  };
17754
17920
 
17755
17921
  // src/codebase-index/codebase-repo-map-tool.ts
17756
- import { toErrorMessage as toErrorMessage8 } from "@wrongstack/core/utils";
17922
+ import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
17757
17923
 
17758
17924
  // src/codebase-index/repo-map.ts
17759
17925
  init_languages2();
17760
- import * as fs23 from "node:fs/promises";
17761
- import * as path29 from "node:path";
17926
+ import * as fs24 from "node:fs/promises";
17927
+ import * as path30 from "node:path";
17762
17928
 
17763
17929
  // src/codebase-index/skeleton-extractor.ts
17764
17930
  init_languages2();
17765
17931
  init_tree_sitter_parser();
17766
- import * as fs22 from "node:fs/promises";
17767
- import * as path28 from "node:path";
17932
+ import * as fs23 from "node:fs/promises";
17933
+ import * as path29 from "node:path";
17768
17934
  var tsModule3 = null;
17769
17935
  async function loadTs3() {
17770
17936
  if (!tsModule3) {
@@ -18092,7 +18258,7 @@ async function extractDirectorySkeleton(opts) {
18092
18258
  if (filePaths.length >= maxFiles) return;
18093
18259
  let entries;
18094
18260
  try {
18095
- entries = await fs22.readdir(dir, { withFileTypes: true });
18261
+ entries = await fs23.readdir(dir, { withFileTypes: true });
18096
18262
  } catch {
18097
18263
  return;
18098
18264
  }
@@ -18101,7 +18267,7 @@ async function extractDirectorySkeleton(opts) {
18101
18267
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build") {
18102
18268
  continue;
18103
18269
  }
18104
- const fullPath = path28.join(dir, entry.name);
18270
+ const fullPath = path29.join(dir, entry.name);
18105
18271
  if (entry.isDirectory()) {
18106
18272
  await scan(fullPath);
18107
18273
  } else if (entry.isFile() && isIndexablePath(fullPath)) {
@@ -18118,7 +18284,7 @@ async function extractDirectorySkeleton(opts) {
18118
18284
  const combinedParts = [];
18119
18285
  for (const fp of filePaths) {
18120
18286
  try {
18121
- const content = await fs22.readFile(fp, "utf8");
18287
+ const content = await fs23.readFile(fp, "utf8");
18122
18288
  const res = await extractFileSkeleton({
18123
18289
  file: fp,
18124
18290
  content,
@@ -18129,7 +18295,7 @@ async function extractDirectorySkeleton(opts) {
18129
18295
  totalSkelChars += res.skeleton.length;
18130
18296
  totalOrigLines += res.stats.originalLines;
18131
18297
  totalSkelLines += res.stats.skeletonLines;
18132
- const rel = path28.relative(opts.dirPath, fp).replace(/\\/g, "/");
18298
+ const rel = path29.relative(opts.dirPath, fp).replace(/\\/g, "/");
18133
18299
  combinedParts.push(
18134
18300
  `// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 ${rel} (${res.lang}) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
18135
18301
  ${res.skeleton}`
@@ -18166,13 +18332,13 @@ async function generateRepoMap(opts) {
18166
18332
  const projectRoot = opts.projectRoot;
18167
18333
  const maxTokens = opts.maxTokens ?? 1200;
18168
18334
  const charBudget = maxTokens * 4;
18169
- const focusSet = new Set((opts.focusFiles ?? []).map((f) => path29.resolve(projectRoot, f)));
18335
+ const focusSet = new Set((opts.focusFiles ?? []).map((f) => path30.resolve(projectRoot, f)));
18170
18336
  const allFiles = [];
18171
18337
  async function scan(dir) {
18172
18338
  if (allFiles.length > 500) return;
18173
18339
  let entries;
18174
18340
  try {
18175
- entries = await fs23.readdir(dir, { withFileTypes: true });
18341
+ entries = await fs24.readdir(dir, { withFileTypes: true });
18176
18342
  } catch {
18177
18343
  return;
18178
18344
  }
@@ -18180,7 +18346,7 @@ async function generateRepoMap(opts) {
18180
18346
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "coverage") {
18181
18347
  continue;
18182
18348
  }
18183
- const fullPath = path29.join(dir, entry.name);
18349
+ const fullPath = path30.join(dir, entry.name);
18184
18350
  if (entry.isDirectory()) {
18185
18351
  await scan(fullPath);
18186
18352
  } else if (entry.isFile() && isIndexablePath(fullPath)) {
@@ -18200,7 +18366,7 @@ async function generateRepoMap(opts) {
18200
18366
  }
18201
18367
  const scoredFiles = [];
18202
18368
  for (const file of allFiles) {
18203
- const relPath = path29.relative(projectRoot, file).replace(/\\/g, "/");
18369
+ const relPath = path30.relative(projectRoot, file).replace(/\\/g, "/");
18204
18370
  let score = 10;
18205
18371
  if (focusSet.has(file)) {
18206
18372
  score += 100;
@@ -18215,7 +18381,7 @@ async function generateRepoMap(opts) {
18215
18381
  for (const candidate of scoredFiles) {
18216
18382
  if (currentChars >= charBudget) break;
18217
18383
  try {
18218
- const content = await fs23.readFile(candidate.file, "utf8");
18384
+ const content = await fs24.readFile(candidate.file, "utf8");
18219
18385
  if (content.length === 0) continue;
18220
18386
  const skeletonResult = await extractFileSkeleton({
18221
18387
  file: candidate.file,
@@ -18298,16 +18464,16 @@ var codebaseRepoMapTool = {
18298
18464
  totalFilesScanned: 0,
18299
18465
  estimatedTokens: 0,
18300
18466
  rankedFiles: [],
18301
- error: toErrorMessage8(err)
18467
+ error: toErrorMessage9(err)
18302
18468
  };
18303
18469
  }
18304
18470
  }
18305
18471
  };
18306
18472
 
18307
18473
  // src/codebase-index/codebase-skeleton-tool.ts
18308
- import * as fs24 from "node:fs/promises";
18309
- import * as path30 from "node:path";
18310
- import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
18474
+ import * as fs25 from "node:fs/promises";
18475
+ import * as path31 from "node:path";
18476
+ import { toErrorMessage as toErrorMessage10 } from "@wrongstack/core/utils";
18311
18477
  var codebaseSkeletonTool = {
18312
18478
  name: "codebase-skeleton",
18313
18479
  category: "Project",
@@ -18346,13 +18512,13 @@ var codebaseSkeletonTool = {
18346
18512
  additionalProperties: false
18347
18513
  },
18348
18514
  async execute(input, ctx) {
18349
- const targetPath = path30.isAbsolute(input.path) ? input.path : path30.resolve(ctx.projectRoot ?? process.cwd(), input.path);
18515
+ const targetPath = path31.isAbsolute(input.path) ? input.path : path31.resolve(ctx.projectRoot ?? process.cwd(), input.path);
18350
18516
  let stat21;
18351
18517
  try {
18352
- stat21 = await fs24.stat(targetPath);
18518
+ stat21 = await fs25.stat(targetPath);
18353
18519
  } catch (err) {
18354
18520
  throw new Error(
18355
- `codebase-skeleton: file or directory not found at "${input.path}": ${toErrorMessage9(err)}`
18521
+ `codebase-skeleton: file or directory not found at "${input.path}": ${toErrorMessage10(err)}`
18356
18522
  );
18357
18523
  }
18358
18524
  const options = {
@@ -18385,7 +18551,7 @@ var codebaseSkeletonTool = {
18385
18551
  files: dirResult.files
18386
18552
  };
18387
18553
  }
18388
- const content = await fs24.readFile(targetPath, "utf8");
18554
+ const content = await fs25.readFile(targetPath, "utf8");
18389
18555
  const fileResult = await extractFileSkeleton({
18390
18556
  file: targetPath,
18391
18557
  content,
@@ -18489,28 +18655,28 @@ var codebaseStatsTool = {
18489
18655
  // src/codebase-index/codebase-targeted-test-tool.ts
18490
18656
  init_spawn_stream();
18491
18657
  init_util();
18492
- import * as fs25 from "node:fs/promises";
18493
- import * as path31 from "node:path";
18494
- import { toErrorMessage as toErrorMessage10 } from "@wrongstack/core/utils";
18658
+ import * as fs26 from "node:fs/promises";
18659
+ import * as path32 from "node:path";
18660
+ import { toErrorMessage as toErrorMessage11 } from "@wrongstack/core/utils";
18495
18661
  var TEST_FILE_REGEX2 = /(?:test|spec|mock|fixture|bench)[s]?[/\\._]/i;
18496
18662
  async function findConventionTestFiles(projectRoot, sourceRelPath) {
18497
- const parsed = path31.parse(sourceRelPath);
18663
+ const parsed = path32.parse(sourceRelPath);
18498
18664
  const candidates = [
18499
- path31.join(parsed.dir, `${parsed.name}.test${parsed.ext}`),
18500
- path31.join(parsed.dir, `${parsed.name}.spec${parsed.ext}`),
18501
- path31.join(parsed.dir, `${parsed.name}_test${parsed.ext}`),
18502
- path31.join(parsed.dir, `test_${parsed.name}${parsed.ext}`),
18503
- path31.join("tests", `${parsed.name}.test${parsed.ext}`),
18504
- path31.join("tests", `${parsed.name}.spec${parsed.ext}`),
18505
- path31.join("test", `${parsed.name}.test${parsed.ext}`),
18506
- path31.join(parsed.dir, "__tests__", `${parsed.name}.test${parsed.ext}`),
18507
- path31.join(parsed.dir, "__tests__", `${parsed.name}${parsed.ext}`)
18665
+ path32.join(parsed.dir, `${parsed.name}.test${parsed.ext}`),
18666
+ path32.join(parsed.dir, `${parsed.name}.spec${parsed.ext}`),
18667
+ path32.join(parsed.dir, `${parsed.name}_test${parsed.ext}`),
18668
+ path32.join(parsed.dir, `test_${parsed.name}${parsed.ext}`),
18669
+ path32.join("tests", `${parsed.name}.test${parsed.ext}`),
18670
+ path32.join("tests", `${parsed.name}.spec${parsed.ext}`),
18671
+ path32.join("test", `${parsed.name}.test${parsed.ext}`),
18672
+ path32.join(parsed.dir, "__tests__", `${parsed.name}.test${parsed.ext}`),
18673
+ path32.join(parsed.dir, "__tests__", `${parsed.name}${parsed.ext}`)
18508
18674
  ];
18509
18675
  const found = [];
18510
18676
  for (const cand of candidates) {
18511
- const absPath = path31.resolve(projectRoot, cand);
18677
+ const absPath = path32.resolve(projectRoot, cand);
18512
18678
  try {
18513
- const st = await fs25.stat(absPath);
18679
+ const st = await fs26.stat(absPath);
18514
18680
  if (st.isFile()) {
18515
18681
  found.push(cand.replace(/\\/g, "/"));
18516
18682
  }
@@ -18567,7 +18733,7 @@ var codebaseTargetedTestTool = {
18567
18733
  transitive: true
18568
18734
  });
18569
18735
  for (const site of serviced.calls) {
18570
- const relPath = path31.relative(projectRoot, site.symbol.file).replace(/\\/g, "/");
18736
+ const relPath = path32.relative(projectRoot, site.symbol.file).replace(/\\/g, "/");
18571
18737
  if (TEST_FILE_REGEX2.test(relPath)) {
18572
18738
  suitesSet.add(relPath);
18573
18739
  }
@@ -18644,7 +18810,7 @@ ${streamRes.stderr}`.trim(),
18644
18810
  failed: 0,
18645
18811
  durationMs: 0,
18646
18812
  output: "",
18647
- error: toErrorMessage10(err)
18813
+ error: toErrorMessage11(err)
18648
18814
  };
18649
18815
  }
18650
18816
  }
@@ -18652,8 +18818,8 @@ ${streamRes.stderr}`.trim(),
18652
18818
 
18653
18819
  // src/codebase-index/dead-code-scan.ts
18654
18820
  init_languages2();
18655
- import * as fs26 from "node:fs";
18656
- import * as path32 from "node:path";
18821
+ import * as fs27 from "node:fs";
18822
+ import * as path33 from "node:path";
18657
18823
  var deadCodeScanTool = {
18658
18824
  name: "dead-code-scan",
18659
18825
  category: "Project",
@@ -18698,25 +18864,25 @@ var deadCodeScanTool = {
18698
18864
  };
18699
18865
  function tryReadJson(filePath) {
18700
18866
  try {
18701
- const raw = fs26.readFileSync(filePath, "utf8");
18867
+ const raw = fs27.readFileSync(filePath, "utf8");
18702
18868
  return JSON.parse(raw);
18703
18869
  } catch {
18704
18870
  return null;
18705
18871
  }
18706
18872
  }
18707
18873
  function resolveAgainst(base, relative19) {
18708
- if (path32.isAbsolute(relative19)) return relative19;
18709
- return path32.resolve(base, relative19);
18874
+ if (path33.isAbsolute(relative19)) return relative19;
18875
+ return path33.resolve(base, relative19);
18710
18876
  }
18711
18877
  function discoverEntryPoints(projectRoot, userEntryPoints) {
18712
18878
  const entries = /* @__PURE__ */ new Set();
18713
18879
  if (userEntryPoints) {
18714
18880
  for (const ep of userEntryPoints) {
18715
18881
  const resolved = resolveAgainst(projectRoot, ep);
18716
- if (fs26.existsSync(resolved)) entries.add(resolved);
18882
+ if (fs27.existsSync(resolved)) entries.add(resolved);
18717
18883
  }
18718
18884
  }
18719
- const rootPkg = tryReadJson(path32.join(projectRoot, "package.json"));
18885
+ const rootPkg = tryReadJson(path33.join(projectRoot, "package.json"));
18720
18886
  if (rootPkg) {
18721
18887
  addPkgJsonEntryPoints(projectRoot, rootPkg, entries);
18722
18888
  }
@@ -18730,45 +18896,45 @@ function discoverEntryPoints(projectRoot, userEntryPoints) {
18730
18896
  workspaces = [];
18731
18897
  }
18732
18898
  for (const wsDir of workspaces) {
18733
- const pkgJsonPath = path32.join(wsDir, "package.json");
18899
+ const pkgJsonPath = path33.join(wsDir, "package.json");
18734
18900
  const pkg = tryReadJson(pkgJsonPath);
18735
18901
  if (pkg) {
18736
18902
  addPkgJsonEntryPoints(wsDir, pkg, entries);
18737
- const convention1 = path32.join(wsDir, "src", "index.ts");
18738
- if (fs26.existsSync(convention1)) entries.add(convention1);
18739
- const convention2 = path32.join(wsDir, "src", "main.ts");
18740
- if (fs26.existsSync(convention2)) entries.add(convention2);
18741
- const convention3 = path32.join(wsDir, "index.ts");
18742
- if (fs26.existsSync(convention3)) entries.add(convention3);
18903
+ const convention1 = path33.join(wsDir, "src", "index.ts");
18904
+ if (fs27.existsSync(convention1)) entries.add(convention1);
18905
+ const convention2 = path33.join(wsDir, "src", "main.ts");
18906
+ if (fs27.existsSync(convention2)) entries.add(convention2);
18907
+ const convention3 = path33.join(wsDir, "index.ts");
18908
+ if (fs27.existsSync(convention3)) entries.add(convention3);
18743
18909
  }
18744
18910
  }
18745
18911
  if (!rootPkg || !workspaces.length) {
18746
18912
  for (const name of ["src/index.ts", "src/main.ts", "index.ts"]) {
18747
- const convention = path32.join(projectRoot, name);
18748
- if (fs26.existsSync(convention)) entries.add(convention);
18913
+ const convention = path33.join(projectRoot, name);
18914
+ if (fs27.existsSync(convention)) entries.add(convention);
18749
18915
  }
18750
18916
  }
18751
18917
  return [...entries];
18752
18918
  }
18753
18919
  var BUILD_OUTPUT_DIRS = ["dist", "out", "build", "release"];
18754
- var BUILD_OUTPUT_DIR_NAMES = BUILD_OUTPUT_DIRS.map((d) => `${path32.sep}${d}${path32.sep}`);
18920
+ var BUILD_OUTPUT_DIR_NAMES = BUILD_OUTPUT_DIRS.map((d) => `${path33.sep}${d}${path33.sep}`);
18755
18921
  function trySourceEquivalent(resolved) {
18756
- resolved = resolved.replace(/[/\\]/g, path32.sep);
18922
+ resolved = resolved.replace(/[/\\]/g, path33.sep);
18757
18923
  for (const marker of BUILD_OUTPUT_DIR_NAMES) {
18758
18924
  const idx = resolved.indexOf(marker);
18759
18925
  if (idx === -1) continue;
18760
- const base = resolved.replace(marker, `${path32.sep}src${path32.sep}`);
18926
+ const base = resolved.replace(marker, `${path33.sep}src${path33.sep}`);
18761
18927
  const candidate = base.replace(/\.(js|mjs|cjs)$/, ".ts");
18762
- if (candidate !== base && fs26.existsSync(candidate)) {
18928
+ if (candidate !== base && fs27.existsSync(candidate)) {
18763
18929
  return candidate;
18764
18930
  }
18765
18931
  const dtsStripped = base.replace(/\.d\.ts$/, "");
18766
18932
  const candidateDts = dtsStripped + ".ts";
18767
- if (candidateDts !== base && candidateDts !== candidate && fs26.existsSync(candidateDts)) {
18933
+ if (candidateDts !== base && candidateDts !== candidate && fs27.existsSync(candidateDts)) {
18768
18934
  return candidateDts;
18769
18935
  }
18770
18936
  const candidateNoExt = base + ".ts";
18771
- if (candidate !== candidateNoExt && candidateNoExt !== candidateDts && fs26.existsSync(candidateNoExt)) {
18937
+ if (candidate !== candidateNoExt && candidateNoExt !== candidateDts && fs27.existsSync(candidateNoExt)) {
18772
18938
  return candidateNoExt;
18773
18939
  }
18774
18940
  }
@@ -18776,9 +18942,9 @@ function trySourceEquivalent(resolved) {
18776
18942
  }
18777
18943
  function tryAddEntryPath(pkgDir, rawPath, entries) {
18778
18944
  const resolved = resolveAgainst(pkgDir, rawPath);
18779
- if (fs26.existsSync(resolved)) entries.add(resolved);
18945
+ if (fs27.existsSync(resolved)) entries.add(resolved);
18780
18946
  const tsResolved = resolved.replace(/\.(js|mjs|cjs)$/, ".ts");
18781
- if (tsResolved !== resolved && fs26.existsSync(tsResolved)) {
18947
+ if (tsResolved !== resolved && fs27.existsSync(tsResolved)) {
18782
18948
  entries.add(tsResolved);
18783
18949
  }
18784
18950
  const srcAlt = trySourceEquivalent(resolved);
@@ -18822,18 +18988,18 @@ function expandGlobPattern(entry, projectRoot) {
18822
18988
  const dirs = [];
18823
18989
  if (entry.includes("*")) {
18824
18990
  const base = entry.replace(/\/\*+$/, "");
18825
- const baseDir = path32.resolve(projectRoot, base);
18991
+ const baseDir = path33.resolve(projectRoot, base);
18826
18992
  try {
18827
- const children = fs26.readdirSync(baseDir, { withFileTypes: true });
18993
+ const children = fs27.readdirSync(baseDir, { withFileTypes: true });
18828
18994
  for (const child of children) {
18829
18995
  if (child.isDirectory()) {
18830
- dirs.push(path32.join(baseDir, child.name));
18996
+ dirs.push(path33.join(baseDir, child.name));
18831
18997
  }
18832
18998
  }
18833
18999
  } catch {
18834
19000
  }
18835
19001
  } else {
18836
- dirs.push(path32.resolve(projectRoot, entry));
19002
+ dirs.push(path33.resolve(projectRoot, entry));
18837
19003
  }
18838
19004
  return dirs;
18839
19005
  }
@@ -18850,10 +19016,10 @@ function extractWorkspaceGlobs(pkg, projectRoot) {
18850
19016
  return dirs;
18851
19017
  }
18852
19018
  function extractPnpmWorkspaceDirs(projectRoot) {
18853
- const yamlPath = path32.join(projectRoot, "pnpm-workspace.yaml");
18854
- if (!fs26.existsSync(yamlPath)) return [];
19019
+ const yamlPath = path33.join(projectRoot, "pnpm-workspace.yaml");
19020
+ if (!fs27.existsSync(yamlPath)) return [];
18855
19021
  try {
18856
- const content = fs26.readFileSync(yamlPath, "utf8");
19022
+ const content = fs27.readFileSync(yamlPath, "utf8");
18857
19023
  const dirs = [];
18858
19024
  let inPackages = false;
18859
19025
  const lines = content.split("\n");
@@ -18887,8 +19053,8 @@ function extractPnpmWorkspaceDirs(projectRoot) {
18887
19053
  }
18888
19054
  function resolveModulePath(importerPath, moduleSpecifier, indexedFiles) {
18889
19055
  if (!moduleSpecifier.startsWith(".")) return [];
18890
- const dir = path32.dirname(importerPath);
18891
- const base = path32.resolve(dir, moduleSpecifier);
19056
+ const dir = path33.dirname(importerPath);
19057
+ const base = path33.resolve(dir, moduleSpecifier);
18892
19058
  const results = [];
18893
19059
  const stripped = base.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, "");
18894
19060
  const skipBase = stripped !== base && /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(base);
@@ -18900,18 +19066,18 @@ function resolveModulePath(importerPath, moduleSpecifier, indexedFiles) {
18900
19066
  if (indexedFiles.has(candidate + ".jsx")) results.push(candidate + ".jsx");
18901
19067
  if (indexedFiles.has(candidate + ".mjs")) results.push(candidate + ".mjs");
18902
19068
  if (indexedFiles.has(candidate + ".cjs")) results.push(candidate + ".cjs");
18903
- if (indexedFiles.has(path32.join(candidate, "index.ts")))
18904
- results.push(path32.join(candidate, "index.ts"));
18905
- if (indexedFiles.has(path32.join(candidate, "index.tsx")))
18906
- results.push(path32.join(candidate, "index.tsx"));
18907
- if (indexedFiles.has(path32.join(candidate, "index.js")))
18908
- results.push(path32.join(candidate, "index.js"));
18909
- if (indexedFiles.has(path32.join(candidate, "index.jsx")))
18910
- results.push(path32.join(candidate, "index.jsx"));
18911
- if (indexedFiles.has(path32.join(candidate, "index.mjs")))
18912
- results.push(path32.join(candidate, "index.mjs"));
18913
- if (indexedFiles.has(path32.join(candidate, "index.cjs")))
18914
- results.push(path32.join(candidate, "index.cjs"));
19069
+ if (indexedFiles.has(path33.join(candidate, "index.ts")))
19070
+ results.push(path33.join(candidate, "index.ts"));
19071
+ if (indexedFiles.has(path33.join(candidate, "index.tsx")))
19072
+ results.push(path33.join(candidate, "index.tsx"));
19073
+ if (indexedFiles.has(path33.join(candidate, "index.js")))
19074
+ results.push(path33.join(candidate, "index.js"));
19075
+ if (indexedFiles.has(path33.join(candidate, "index.jsx")))
19076
+ results.push(path33.join(candidate, "index.jsx"));
19077
+ if (indexedFiles.has(path33.join(candidate, "index.mjs")))
19078
+ results.push(path33.join(candidate, "index.mjs"));
19079
+ if (indexedFiles.has(path33.join(candidate, "index.cjs")))
19080
+ results.push(path33.join(candidate, "index.cjs"));
18915
19081
  }
18916
19082
  return [...new Set(results)];
18917
19083
  }
@@ -18943,7 +19109,7 @@ function runDeadCodeScan(projectRoot, opts = {}) {
18943
19109
  symbolById.set(s.id, s);
18944
19110
  }
18945
19111
  const discoveredFiles = discoverEntryPoints(projectRoot, opts.userEntryPoints);
18946
- const entryFileSet = new Set(discoveredFiles.map((f) => path32.resolve(f)));
19112
+ const entryFileSet = new Set(discoveredFiles.map((f) => path33.resolve(f)));
18947
19113
  const indexedFiles = /* @__PURE__ */ new Set();
18948
19114
  for (const s of allSymbols) indexedFiles.add(s.file);
18949
19115
  for (const fm of store.getAllFileMetas()) indexedFiles.add(fm.file);
@@ -18969,7 +19135,7 @@ function runDeadCodeScan(projectRoot, opts = {}) {
18969
19135
  if (scannedBarrels.has(epFile)) continue;
18970
19136
  scannedBarrels.add(epFile);
18971
19137
  try {
18972
- const content = fs26.readFileSync(epFile, "utf8");
19138
+ const content = fs27.readFileSync(epFile, "utf8");
18973
19139
  const strippedContent = content.replace(/\/\*[\s\S]*?\*\//g, (m) => " ".repeat(m.length)).replace(/\/\/[^\n]*/g, (m) => " ".repeat(m.length));
18974
19140
  const reExportRe = /export\s+(?:(?:type\s+)?\{[\s\S]*?\}\s+from|\*\s+as\s+\w+\s+from|\*\s+from)\s+['"]([^'"]+)['"]/g;
18975
19141
  for (; ; ) {
@@ -19059,7 +19225,7 @@ function runDeadCodeScan(projectRoot, opts = {}) {
19059
19225
  const deadPackages = [];
19060
19226
  const pkgEntries = findPackageEntries(projectRoot);
19061
19227
  for (const [pkgName, pkgDir] of pkgEntries) {
19062
- const pkgFiles = allSymbols.filter((s) => s.file.startsWith(pkgDir + path32.sep));
19228
+ const pkgFiles = allSymbols.filter((s) => s.file.startsWith(pkgDir + path33.sep));
19063
19229
  if (pkgFiles.length === 0) continue;
19064
19230
  const pkgUsed = pkgFiles.filter((s) => alive.has(s.id));
19065
19231
  if (pkgUsed.length === 0) {
@@ -19095,7 +19261,7 @@ function runDeadCodeScan(projectRoot, opts = {}) {
19095
19261
  }
19096
19262
  function findPackageEntries(projectRoot) {
19097
19263
  const pkgMap = /* @__PURE__ */ new Map();
19098
- const rootPkg = tryReadJson(path32.join(projectRoot, "package.json"));
19264
+ const rootPkg = tryReadJson(path33.join(projectRoot, "package.json"));
19099
19265
  if (rootPkg && typeof rootPkg.name === "string") {
19100
19266
  pkgMap.set(rootPkg.name, projectRoot);
19101
19267
  }
@@ -19105,7 +19271,7 @@ function findPackageEntries(projectRoot) {
19105
19271
  wsDirs = extractPnpmWorkspaceDirs(projectRoot);
19106
19272
  }
19107
19273
  for (const wsDir of wsDirs) {
19108
- const wsPkg = tryReadJson(path32.join(wsDir, "package.json"));
19274
+ const wsPkg = tryReadJson(path33.join(wsDir, "package.json"));
19109
19275
  if (wsPkg && typeof wsPkg.name === "string") {
19110
19276
  pkgMap.set(wsPkg.name, wsDir);
19111
19277
  }
@@ -19115,7 +19281,7 @@ function findPackageEntries(projectRoot) {
19115
19281
  }
19116
19282
 
19117
19283
  // src/clarify.ts
19118
- import { toErrorMessage as toErrorMessage11 } from "@wrongstack/core/utils";
19284
+ import { toErrorMessage as toErrorMessage12 } from "@wrongstack/core/utils";
19119
19285
  var clarifyTool = {
19120
19286
  name: "clarify",
19121
19287
  category: "Meta",
@@ -19188,15 +19354,15 @@ var clarifyTool = {
19188
19354
  question: input.question ?? "",
19189
19355
  selectedOptions: [],
19190
19356
  decisionSummary: "",
19191
- error: toErrorMessage11(err)
19357
+ error: toErrorMessage12(err)
19192
19358
  };
19193
19359
  }
19194
19360
  }
19195
19361
  };
19196
19362
 
19197
19363
  // src/design.ts
19198
- import * as fs27 from "node:fs/promises";
19199
- import * as path33 from "node:path";
19364
+ import * as fs28 from "node:fs/promises";
19365
+ import * as path34 from "node:path";
19200
19366
  import {
19201
19367
  applyTokenOverrides,
19202
19368
  getDesignKitLoader,
@@ -19211,17 +19377,17 @@ import {
19211
19377
  setDesignOverrides
19212
19378
  } from "@wrongstack/core/design";
19213
19379
  async function resolveReal(p) {
19214
- const resolved = path33.resolve(p);
19380
+ const resolved = path34.resolve(p);
19215
19381
  let probe = resolved;
19216
19382
  const missing = [];
19217
19383
  for (; ; ) {
19218
19384
  try {
19219
- return path33.resolve(await fs27.realpath(probe), ...missing);
19385
+ return path34.resolve(await fs28.realpath(probe), ...missing);
19220
19386
  } catch (err) {
19221
19387
  if (err.code === "ENOENT") {
19222
- const parent = path33.dirname(probe);
19388
+ const parent = path34.dirname(probe);
19223
19389
  if (parent === probe) return resolved;
19224
- missing.unshift(path33.basename(probe));
19390
+ missing.unshift(path34.basename(probe));
19225
19391
  probe = parent;
19226
19392
  continue;
19227
19393
  }
@@ -19422,16 +19588,16 @@ Run \`design {action:"materialize"}\` to write the tuned tokens to your theme fi
19422
19588
  outPath: input.out
19423
19589
  });
19424
19590
  const root = await resolveReal(ctx.projectRoot);
19425
- const absResolved = path33.isAbsolute(result.path) ? path33.resolve(result.path) : path33.resolve(path33.join(ctx.projectRoot, result.path));
19426
- const absParent = await resolveReal(path33.dirname(absResolved));
19427
- const abs = path33.join(absParent, path33.basename(absResolved));
19428
- const rel = path33.relative(root, abs);
19429
- if (rel.startsWith("..") || path33.isAbsolute(rel)) {
19591
+ const absResolved = path34.isAbsolute(result.path) ? path34.resolve(result.path) : path34.resolve(path34.join(ctx.projectRoot, result.path));
19592
+ const absParent = await resolveReal(path34.dirname(absResolved));
19593
+ const abs = path34.join(absParent, path34.basename(absResolved));
19594
+ const rel = path34.relative(root, abs);
19595
+ if (rel.startsWith("..") || path34.isAbsolute(rel)) {
19430
19596
  throw new Error(`design: materialize path "${result.path}" would escape the project root`);
19431
19597
  }
19432
19598
  let exists = false;
19433
19599
  try {
19434
- await fs27.access(abs);
19600
+ await fs28.access(abs);
19435
19601
  exists = true;
19436
19602
  } catch {
19437
19603
  }
@@ -19448,8 +19614,8 @@ ${result.content}
19448
19614
  \`\`\``
19449
19615
  };
19450
19616
  }
19451
- await fs27.mkdir(path33.dirname(abs), { recursive: true });
19452
- await fs27.writeFile(abs, result.content);
19617
+ await fs28.mkdir(path34.dirname(abs), { recursive: true });
19618
+ await fs28.writeFile(abs, result.content);
19453
19619
  return {
19454
19620
  action,
19455
19621
  kit: active.kit,
@@ -19499,8 +19665,8 @@ Replace off-palette colors with kit tokens (or the materialized CSS vars / token
19499
19665
  init_util();
19500
19666
  import { spawn as spawn7 } from "node:child_process";
19501
19667
  import { statSync as statSync3 } from "node:fs";
19502
- import * as fs28 from "node:fs/promises";
19503
- import * as path34 from "node:path";
19668
+ import * as fs29 from "node:fs/promises";
19669
+ import * as path35 from "node:path";
19504
19670
  import { buildChildEnv as buildChildEnv3 } from "@wrongstack/core/utils";
19505
19671
  import { ToolValidationError as ToolValidationError2 } from "@wrongstack/core/types";
19506
19672
  var MAX_FILE_DUMP_BYTES = 5 * 1024 * 1024;
@@ -19615,18 +19781,18 @@ function findGitDir(cwd) {
19615
19781
  let dir = cwd;
19616
19782
  for (let i = 0; i < 20; i++) {
19617
19783
  try {
19618
- const stat21 = statSync3(path34.join(dir, ".git"));
19784
+ const stat21 = statSync3(path35.join(dir, ".git"));
19619
19785
  if (stat21.isDirectory()) return dir;
19620
19786
  } catch {
19621
19787
  }
19622
- const parent = path34.dirname(dir);
19788
+ const parent = path35.dirname(dir);
19623
19789
  if (parent === dir) break;
19624
19790
  dir = parent;
19625
19791
  }
19626
19792
  return null;
19627
19793
  }
19628
19794
  function runGit(args, cwd, signal) {
19629
- return new Promise((resolve22) => {
19795
+ return new Promise((resolve23) => {
19630
19796
  let stdout = "";
19631
19797
  let stderr = "";
19632
19798
  const child = spawn7("git", args, {
@@ -19642,8 +19808,8 @@ function runGit(args, cwd, signal) {
19642
19808
  child.stderr?.on("data", (c) => {
19643
19809
  stderr += c.toString();
19644
19810
  });
19645
- child.on("close", (code) => resolve22({ stdout, stderr, exitCode: code ?? 0 }));
19646
- child.on("error", (e) => resolve22({ stdout: "", stderr: e.message, exitCode: 1 }));
19811
+ child.on("close", (code) => resolve23({ stdout, stderr, exitCode: code ?? 0 }));
19812
+ child.on("error", (e) => resolve23({ stdout: "", stderr: e.message, exitCode: 1 }));
19647
19813
  });
19648
19814
  }
19649
19815
  async function fileDiff(input, ctx, _signal) {
@@ -19661,7 +19827,7 @@ async function fileDiff(input, ctx, _signal) {
19661
19827
  let truncated = false;
19662
19828
  for (const file of files) {
19663
19829
  const absPath = await safeResolveReal(file, ctx);
19664
- const stat21 = await fs28.stat(absPath).catch(() => null);
19830
+ const stat21 = await fs29.stat(absPath).catch(() => null);
19665
19831
  if (!stat21?.isFile()) continue;
19666
19832
  if (stat21.size > MAX_FILE_DUMP_BYTES) {
19667
19833
  truncated = true;
@@ -19670,7 +19836,7 @@ async function fileDiff(input, ctx, _signal) {
19670
19836
  );
19671
19837
  continue;
19672
19838
  }
19673
- const content = await fs28.readFile(absPath, "utf8");
19839
+ const content = await fs29.readFile(absPath, "utf8");
19674
19840
  const lines = content.split(/\r?\n/);
19675
19841
  results.push(formatWithLineNumbers(file, lines));
19676
19842
  }
@@ -19693,8 +19859,8 @@ ${numbered}`;
19693
19859
 
19694
19860
  // src/document.ts
19695
19861
  init_util();
19696
- import * as fs29 from "node:fs/promises";
19697
- import * as path35 from "node:path";
19862
+ import * as fs30 from "node:fs/promises";
19863
+ import * as path36 from "node:path";
19698
19864
  var documentTool = {
19699
19865
  name: "document",
19700
19866
  category: "Project",
@@ -19742,7 +19908,7 @@ var documentTool = {
19742
19908
  ) : input.path ? [await safeResolveReal(input.path, ctx)] : [];
19743
19909
  for (const absPath of fileList) {
19744
19910
  try {
19745
- const content = await fs29.readFile(absPath, "utf8");
19911
+ const content = await fs30.readFile(absPath, "utf8");
19746
19912
  filesProcessed++;
19747
19913
  const processed = processFile(
19748
19914
  content,
@@ -19780,12 +19946,12 @@ async function resolveFiles(filesInput, cwd, ctx) {
19780
19946
  if (!entry) continue;
19781
19947
  let absPath;
19782
19948
  try {
19783
- absPath = ensureInsideRoot(path35.resolve(cwd, entry), ctx);
19949
+ absPath = ensureInsideRoot(path36.resolve(cwd, entry), ctx);
19784
19950
  } catch {
19785
19951
  continue;
19786
19952
  }
19787
19953
  try {
19788
- const stat21 = await fs29.stat(absPath);
19954
+ const stat21 = await fs30.stat(absPath);
19789
19955
  if (stat21.isFile()) resolved.push(absPath);
19790
19956
  } catch {
19791
19957
  }
@@ -19856,7 +20022,7 @@ function processFile(content, absPath, _style, _overwrite, target) {
19856
20022
  // src/e2e.ts
19857
20023
  init_util();
19858
20024
  import { open, readdir as readdir9 } from "node:fs/promises";
19859
- import * as path36 from "node:path";
20025
+ import * as path37 from "node:path";
19860
20026
  async function readBoundedText(filePath, maxBytes) {
19861
20027
  let handle;
19862
20028
  try {
@@ -19906,8 +20072,8 @@ var MAX_PACKAGE_BYTES = 512 * 1024;
19906
20072
  var MAX_CONFIG_BYTES = 512 * 1024;
19907
20073
  var MAX_SPEC_SAMPLES = 20;
19908
20074
  function relativePath(root, target) {
19909
- const value = path36.relative(root, target) || ".";
19910
- return value.split(path36.sep).join("/");
20075
+ const value = path37.relative(root, target) || ".";
20076
+ return value.split(path37.sep).join("/");
19911
20077
  }
19912
20078
  function escapeRegExp(value) {
19913
20079
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -19979,7 +20145,7 @@ async function scanWorkspace(root, maxDepth, signal) {
19979
20145
  }
19980
20146
  for (const entry of entries) {
19981
20147
  signal.throwIfAborted();
19982
- const absolutePath = path36.join(current.directory, entry.name);
20148
+ const absolutePath = path37.join(current.directory, entry.name);
19983
20149
  if (entry.isFile()) {
19984
20150
  if (entry.name === "package.json") result.packageFiles.push(absolutePath);
19985
20151
  const framework = CONFIG_NAMES.get(entry.name);
@@ -20040,9 +20206,9 @@ async function detectPackageManager3(projectRoot, scanRoot, declared) {
20040
20206
  if (names.has("bun.lock") || names.has("bun.lockb")) return "bun";
20041
20207
  if (names.has("package-lock.json") || names.has("npm-shrinkwrap.json")) return "npm";
20042
20208
  if (directory === scanRoot) break;
20043
- const parent = path36.dirname(directory);
20044
- const relativeParent = path36.relative(scanRoot, parent);
20045
- if (parent === directory || relativeParent.startsWith("..") || path36.isAbsolute(relativeParent)) {
20209
+ const parent = path37.dirname(directory);
20210
+ const relativeParent = path37.relative(scanRoot, parent);
20211
+ if (parent === directory || relativeParent.startsWith("..") || path37.isAbsolute(relativeParent)) {
20046
20212
  break;
20047
20213
  }
20048
20214
  directory = parent;
@@ -20087,7 +20253,7 @@ function isSpec(framework, filename) {
20087
20253
  return /\.(?:spec|test)\.(?:[cm]?[jt]sx?)$/i.test(filename);
20088
20254
  }
20089
20255
  async function collectSpecs(root, framework, testDirectory, signal) {
20090
- const roots = testDirectory ? [path36.resolve(root, testDirectory)] : framework === "cypress" ? [path36.join(root, "cypress", "e2e"), path36.join(root, "cypress", "integration")] : [path36.join(root, "tests")];
20256
+ const roots = testDirectory ? [path37.resolve(root, testDirectory)] : framework === "cypress" ? [path37.join(root, "cypress", "e2e"), path37.join(root, "cypress", "integration")] : [path37.join(root, "tests")];
20091
20257
  const samples = [];
20092
20258
  let count = 0;
20093
20259
  let scanned = 0;
@@ -20106,7 +20272,7 @@ async function collectSpecs(root, framework, testDirectory, signal) {
20106
20272
  }
20107
20273
  for (const entry of entries) {
20108
20274
  signal.throwIfAborted();
20109
- const target = path36.join(directory, entry.name);
20275
+ const target = path37.join(directory, entry.name);
20110
20276
  if (entry.isDirectory() && !SKIP_DIRECTORIES.has(entry.name)) queue.push(target);
20111
20277
  else if (entry.isFile() && isSpec(framework, entry.name)) {
20112
20278
  count += 1;
@@ -20126,7 +20292,7 @@ function nearestPackage(projectRoot, packagesByDirectory, scanRoot) {
20126
20292
  const found = packagesByDirectory.get(directory);
20127
20293
  if (found) return found;
20128
20294
  if (directory === scanRoot) return void 0;
20129
- const parent = path36.dirname(directory);
20295
+ const parent = path37.dirname(directory);
20130
20296
  if (parent === directory || relativePath(scanRoot, parent).startsWith("..")) return void 0;
20131
20297
  directory = parent;
20132
20298
  }
@@ -20137,13 +20303,13 @@ async function discoverE2EProjects(root, options) {
20137
20303
  const packages = (await Promise.all(scan.packageFiles.map(readPackageInfo))).filter(
20138
20304
  (info) => Boolean(info)
20139
20305
  );
20140
- const packagesByDirectory = new Map(packages.map((info) => [path36.dirname(info.path), info]));
20306
+ const packagesByDirectory = new Map(packages.map((info) => [path37.dirname(info.path), info]));
20141
20307
  const candidates = /* @__PURE__ */ new Map();
20142
20308
  for (const config of scan.configs) {
20143
20309
  if (options.framework && options.framework !== "all" && config.framework !== options.framework) {
20144
20310
  continue;
20145
20311
  }
20146
- const projectRoot = path36.dirname(config.absolutePath);
20312
+ const projectRoot = path37.dirname(config.absolutePath);
20147
20313
  candidates.set(`${config.framework}:${projectRoot}`, {
20148
20314
  framework: config.framework,
20149
20315
  root: projectRoot,
@@ -20151,7 +20317,7 @@ async function discoverE2EProjects(root, options) {
20151
20317
  });
20152
20318
  }
20153
20319
  for (const info of packages) {
20154
- const projectRoot = path36.dirname(info.path);
20320
+ const projectRoot = path37.dirname(info.path);
20155
20321
  for (const framework of frameworkFromPackage(info)) {
20156
20322
  if (options.framework && options.framework !== "all" && framework !== options.framework)
20157
20323
  continue;
@@ -20167,10 +20333,10 @@ async function discoverE2EProjects(root, options) {
20167
20333
  const scripts = matchingScripts(info, candidate.framework);
20168
20334
  const manager = await detectPackageManager3(candidate.root, root, info?.packageManager);
20169
20335
  const testDirectory = candidate.framework === "playwright" ? staticString(source, "testDir") : void 0;
20170
- const resolvedTestDirectory = testDirectory ? path36.resolve(candidate.root, testDirectory) : void 0;
20171
- const relativeTestDirectory = resolvedTestDirectory ? path36.relative(root, resolvedTestDirectory) : void 0;
20336
+ const resolvedTestDirectory = testDirectory ? path37.resolve(candidate.root, testDirectory) : void 0;
20337
+ const relativeTestDirectory = resolvedTestDirectory ? path37.relative(root, resolvedTestDirectory) : void 0;
20172
20338
  const unsafeTestDirectory = Boolean(
20173
- relativeTestDirectory && (relativeTestDirectory.startsWith("..") || path36.isAbsolute(relativeTestDirectory))
20339
+ relativeTestDirectory && (relativeTestDirectory.startsWith("..") || path37.isAbsolute(relativeTestDirectory))
20174
20340
  );
20175
20341
  const specs = options.includeSpecs === false || unsafeTestDirectory ? { count: 0, samples: [], truncated: false } : await collectSpecs(candidate.root, candidate.framework, testDirectory, options.signal);
20176
20342
  const warnings = [];
@@ -20270,7 +20436,7 @@ var e2ePlanTool = {
20270
20436
  };
20271
20437
 
20272
20438
  // src/edit.ts
20273
- import * as fs30 from "node:fs/promises";
20439
+ import * as fs31 from "node:fs/promises";
20274
20440
  import {
20275
20441
  atomicWrite as atomicWrite3,
20276
20442
  detectNewlineStyle,
@@ -20494,7 +20660,7 @@ function prefixSimilarity(a, b) {
20494
20660
  }
20495
20661
 
20496
20662
  // src/_syntax-check.ts
20497
- import * as path37 from "node:path";
20663
+ import * as path38 from "node:path";
20498
20664
  var TS_LIKE = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"]);
20499
20665
  var MAX_CHECK_CHARS = 15e5;
20500
20666
  var MAX_ERRORS = 5;
@@ -20518,15 +20684,15 @@ async function checkSyntax(filePath, content, previousContent) {
20518
20684
  return { errors, preExisting };
20519
20685
  }
20520
20686
  function isJsoncFile(filePath) {
20521
- const base = path37.basename(filePath).toLowerCase();
20687
+ const base = path38.basename(filePath).toLowerCase();
20522
20688
  if (base.endsWith(".jsonc")) return true;
20523
20689
  if (/^(tsconfig|jsconfig)([.-].*)?\.json$/.test(base)) return true;
20524
- const dir = path37.basename(path37.dirname(filePath)).toLowerCase();
20690
+ const dir = path38.basename(path38.dirname(filePath)).toLowerCase();
20525
20691
  return dir === ".vscode";
20526
20692
  }
20527
20693
  async function parseErrors(filePath, content) {
20528
20694
  if (content.length > MAX_CHECK_CHARS) return void 0;
20529
- const ext = path37.extname(filePath).toLowerCase();
20695
+ const ext = path38.extname(filePath).toLowerCase();
20530
20696
  if (ext === ".json" || ext === ".jsonc") {
20531
20697
  try {
20532
20698
  JSON.parse(content);
@@ -20553,7 +20719,7 @@ async function parseErrors(filePath, content) {
20553
20719
  ts2.ScriptKind.JSX
20554
20720
  );
20555
20721
  const sourceFile = ts2.createSourceFile(
20556
- path37.basename(filePath),
20722
+ path38.basename(filePath),
20557
20723
  content,
20558
20724
  ts2.ScriptTarget.Latest,
20559
20725
  /* setParentNodes */
@@ -20644,7 +20810,7 @@ var editTool = {
20644
20810
  });
20645
20811
  }
20646
20812
  const absPath = await safeResolveReal(input.path, ctx);
20647
- const stat21 = await fs30.stat(absPath).catch((err) => {
20813
+ const stat21 = await fs31.stat(absPath).catch((err) => {
20648
20814
  if (err.code === "ENOENT") {
20649
20815
  throw new ToolValidationError3({
20650
20816
  message: `edit: file "${input.path}" does not exist. Use \`write\` instead.`,
@@ -20661,8 +20827,8 @@ var editTool = {
20661
20827
  });
20662
20828
  }
20663
20829
  const autoRead = !ctx.hasRead(absPath);
20664
- const original = await fs30.readFile(absPath, "utf8");
20665
- const updated = await fs30.stat(absPath);
20830
+ const original = await fs31.readFile(absPath, "utf8");
20831
+ const updated = await fs31.stat(absPath);
20666
20832
  const mtimeTolerance = process.platform === "win32" ? 2e3 : 1;
20667
20833
  const originalHash = sha256hex(original);
20668
20834
  const lastReadHash = ctx.lastReadHash?.(absPath);
@@ -20770,7 +20936,7 @@ var editTool = {
20770
20936
  enqueueReindex({ projectRoot: ctx.projectRoot, files: [absPath] });
20771
20937
  } catch {
20772
20938
  }
20773
- const written = await fs30.stat(absPath);
20939
+ const written = await fs31.stat(absPath);
20774
20940
  ctx.recordRead(absPath, written.mtimeMs, "write", sha256hex(newFile));
20775
20941
  ctx.session.recordFileChange({
20776
20942
  path: absPath,
@@ -20820,7 +20986,7 @@ import {
20820
20986
  emitProcessOutput as emitProcessOutput3,
20821
20987
  emitProcessStarted as emitProcessStarted3
20822
20988
  } from "@wrongstack/core/observability";
20823
- import { toErrorMessage as toErrorMessage12 } from "@wrongstack/core/utils/error";
20989
+ import { toErrorMessage as toErrorMessage13 } from "@wrongstack/core/utils/error";
20824
20990
 
20825
20991
  // src/_danger-detect.ts
20826
20992
  var argHas = (args, value) => args.includes(value);
@@ -21176,7 +21342,7 @@ init_win32_resolve();
21176
21342
 
21177
21343
  // src/exec-kill-guard.ts
21178
21344
  import * as os8 from "node:os";
21179
- import * as path38 from "node:path";
21345
+ import * as path39 from "node:path";
21180
21346
  var isWin3 = os8.platform() === "win32";
21181
21347
  async function checkExecKillCommand(cmd, args) {
21182
21348
  if (!cmd) return { blocked: false };
@@ -21393,7 +21559,7 @@ async function checkKillTarget(target) {
21393
21559
  reason: `Blocked: kill ${target.signal} '${target.name}' targets a WrongStack process name.`
21394
21560
  };
21395
21561
  }
21396
- const currentImage = path38.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
21562
+ const currentImage = path39.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
21397
21563
  const targetsNodeRuntime = nameLower === "node" || nameLower.startsWith("node");
21398
21564
  if (targetsNodeRuntime && currentImage === "node") {
21399
21565
  return {
@@ -22278,7 +22444,7 @@ var execTool = {
22278
22444
  }
22279
22445
  };
22280
22446
  function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
22281
- return new Promise((resolve22) => {
22447
+ return new Promise((resolve23) => {
22282
22448
  let stdout = "";
22283
22449
  let stderr = "";
22284
22450
  let killed = false;
@@ -22286,7 +22452,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
22286
22452
  const finish = (result) => {
22287
22453
  if (resolvedOnce.value) return;
22288
22454
  resolvedOnce.value = true;
22289
- resolve22(result);
22455
+ resolve23(result);
22290
22456
  };
22291
22457
  const startedAt = Date.now();
22292
22458
  let stdoutBytes = 0;
@@ -22338,7 +22504,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
22338
22504
  command: cmd,
22339
22505
  args,
22340
22506
  stdout: "",
22341
- stderr: `spawn failed: ${toErrorMessage12(err)}`,
22507
+ stderr: `spawn failed: ${toErrorMessage13(err)}`,
22342
22508
  exitCode: 1,
22343
22509
  truncated: false,
22344
22510
  allowed: true,
@@ -22962,10 +23128,10 @@ function parseFormatterCounts(fixer, output) {
22962
23128
  };
22963
23129
  }
22964
23130
  async function detectFixer(cwd) {
22965
- const fs42 = await import("node:fs/promises");
23131
+ const fs43 = await import("node:fs/promises");
22966
23132
  const exists = async (file) => {
22967
23133
  try {
22968
- await fs42.stat(`${cwd}/${file}`);
23134
+ await fs43.stat(`${cwd}/${file}`);
22969
23135
  return true;
22970
23136
  } catch {
22971
23137
  return false;
@@ -22988,7 +23154,7 @@ async function detectFixer(cwd) {
22988
23154
  if (await exists(cfg)) return "prettier";
22989
23155
  }
22990
23156
  try {
22991
- const raw = await fs42.readFile(`${cwd}/package.json`, "utf8");
23157
+ const raw = await fs43.readFile(`${cwd}/package.json`, "utf8");
22992
23158
  const pkg = JSON.parse(raw);
22993
23159
  if (pkg["prettier"] !== void 0) return "prettier";
22994
23160
  } catch {
@@ -23000,7 +23166,7 @@ async function detectFixer(cwd) {
23000
23166
  init_util();
23001
23167
  import { spawn as spawn9 } from "node:child_process";
23002
23168
  import { statSync as statSync4 } from "node:fs";
23003
- import { dirname as dirname13, resolve as resolve18, sep as sep6 } from "node:path";
23169
+ import { dirname as dirname13, resolve as resolve19, sep as sep6 } from "node:path";
23004
23170
  import { assessCommitSafety } from "@wrongstack/core/coordination";
23005
23171
  import { buildChildEnv as buildChildEnv4 } from "@wrongstack/core/utils";
23006
23172
  var TIMEOUT_MS2 = 3e4;
@@ -23161,8 +23327,8 @@ function validateWorktreeInput(input, projectRoot) {
23161
23327
  return reject(`unsafe worktree path: ${input.worktreePath}`);
23162
23328
  }
23163
23329
  if ((input.worktreeAction === "add" || input.worktreeAction === "remove") && input.worktreePath) {
23164
- const root = resolve18(projectRoot);
23165
- const abs = resolve18(root, input.worktreePath);
23330
+ const root = resolve19(projectRoot);
23331
+ const abs = resolve19(root, input.worktreePath);
23166
23332
  if (abs !== root && !abs.startsWith(root + sep6)) {
23167
23333
  return reject(`unsafe worktree path (escapes project root): ${input.worktreePath}`);
23168
23334
  }
@@ -23256,7 +23422,7 @@ function buildArgs(input) {
23256
23422
  }
23257
23423
  }
23258
23424
  function runGit2(args, cwd, signal) {
23259
- return new Promise((resolve22) => {
23425
+ return new Promise((resolve23) => {
23260
23426
  let stdout = "";
23261
23427
  let stderr = "";
23262
23428
  const child = spawn9("git", args, {
@@ -23277,7 +23443,7 @@ function runGit2(args, cwd, signal) {
23277
23443
  }
23278
23444
  });
23279
23445
  child.on("error", (err) => {
23280
- resolve22({
23446
+ resolve23({
23281
23447
  command: args[0],
23282
23448
  stdout: normalizeCommandOutput(stdout),
23283
23449
  stderr: err.message,
@@ -23286,7 +23452,7 @@ function runGit2(args, cwd, signal) {
23286
23452
  });
23287
23453
  });
23288
23454
  child.on("close", (code) => {
23289
- resolve22({
23455
+ resolve23({
23290
23456
  command: args[0],
23291
23457
  stdout: normalizeCommandOutput(stdout),
23292
23458
  stderr: normalizeCommandOutput(stderr),
@@ -23298,8 +23464,8 @@ function runGit2(args, cwd, signal) {
23298
23464
  }
23299
23465
 
23300
23466
  // src/glob.ts
23301
- import * as fs31 from "node:fs/promises";
23302
- import * as path39 from "node:path";
23467
+ import * as fs32 from "node:fs/promises";
23468
+ import * as path40 from "node:path";
23303
23469
  import { compileGlob as compileGlob2, DEFAULT_WALK_IGNORE_DIRS as DEFAULT_WALK_IGNORE_DIRS2 } from "@wrongstack/core/utils";
23304
23470
  import { ToolValidationError as ToolValidationError6 } from "@wrongstack/core/types";
23305
23471
 
@@ -23379,7 +23545,7 @@ var globTool = {
23379
23545
  return;
23380
23546
  }
23381
23547
  try {
23382
- const st = await fs31.stat(full);
23548
+ const st = await fs32.stat(full);
23383
23549
  if (truncated || results.length >= limit) {
23384
23550
  truncated = true;
23385
23551
  return;
@@ -23400,7 +23566,7 @@ var globTool = {
23400
23566
  }
23401
23567
  let entries;
23402
23568
  try {
23403
- entries = await fs31.readdir(dir, { withFileTypes: true });
23569
+ entries = await fs32.readdir(dir, { withFileTypes: true });
23404
23570
  } catch {
23405
23571
  return;
23406
23572
  }
@@ -23410,7 +23576,7 @@ var globTool = {
23410
23576
  const name = e.name;
23411
23577
  if (DEFAULT_IGNORE2.has(name)) continue;
23412
23578
  const rel = relPrefix ? `${relPrefix}/${name}` : name;
23413
- const full = path39.join(dir, name);
23579
+ const full = path40.join(dir, name);
23414
23580
  if (e.isDirectory()) {
23415
23581
  if (isGitIgnored(rel, true)) continue;
23416
23582
  subdirs.push({ full, rel });
@@ -23425,15 +23591,15 @@ var globTool = {
23425
23591
  }
23426
23592
  } else if (e.isSymbolicLink()) {
23427
23593
  try {
23428
- const st = await fs31.stat(full);
23594
+ const st = await fs32.stat(full);
23429
23595
  if (st.isDirectory()) {
23430
23596
  if (isGitIgnored(rel, true)) continue;
23431
- const real = await fs31.realpath(full);
23597
+ const real = await fs32.realpath(full);
23432
23598
  await assertRealInsideRoot(real, ctx);
23433
23599
  subdirs.push({ full, rel });
23434
23600
  } else if (st.isFile()) {
23435
23601
  if (isGitIgnored(rel, false)) continue;
23436
- const real = await fs31.realpath(full);
23602
+ const real = await fs32.realpath(full);
23437
23603
  await assertRealInsideRoot(real, ctx);
23438
23604
  re.lastIndex = 0;
23439
23605
  const relMatch = re.test(rel);
@@ -23463,8 +23629,8 @@ var globTool = {
23463
23629
 
23464
23630
  // src/grep.ts
23465
23631
  import { spawn as spawn10 } from "node:child_process";
23466
- import * as fs32 from "node:fs/promises";
23467
- import * as path40 from "node:path";
23632
+ import * as fs33 from "node:fs/promises";
23633
+ import * as path41 from "node:path";
23468
23634
  import { ToolValidationError as ToolValidationError7 } from "@wrongstack/core/types";
23469
23635
  import {
23470
23636
  buildChildEnv as buildChildEnv5,
@@ -23699,7 +23865,7 @@ var grepTool = {
23699
23865
  };
23700
23866
  var rgAvailabilityCache;
23701
23867
  function detectRg() {
23702
- rgAvailabilityCache ??= new Promise((resolve22) => {
23868
+ rgAvailabilityCache ??= new Promise((resolve23) => {
23703
23869
  try {
23704
23870
  const p = spawn10("rg", ["--version"], {
23705
23871
  env: buildChildEnv5(),
@@ -23707,10 +23873,10 @@ function detectRg() {
23707
23873
  signal: AbortSignal.timeout(1e4),
23708
23874
  windowsHide: true
23709
23875
  });
23710
- p.on("error", () => resolve22(false));
23711
- p.on("close", (code) => resolve22(code === 0));
23876
+ p.on("error", () => resolve23(false));
23877
+ p.on("close", (code) => resolve23(code === 0));
23712
23878
  } catch {
23713
- resolve22(false);
23879
+ resolve23(false);
23714
23880
  }
23715
23881
  });
23716
23882
  return rgAvailabilityCache;
@@ -23727,8 +23893,8 @@ async function* runRgStream(input, base, mode, limit, signal) {
23727
23893
  for (const ignored of DEFAULT_IGNORE3) {
23728
23894
  args.push("--glob", `!${ignored}/**`, "--glob", `!**/${ignored}/**`);
23729
23895
  }
23730
- const gitignorePath = path40.join(base, ".gitignore");
23731
- if (await fs32.access(gitignorePath).then(
23896
+ const gitignorePath = path41.join(base, ".gitignore");
23897
+ if (await fs33.access(gitignorePath).then(
23732
23898
  () => true,
23733
23899
  () => false
23734
23900
  )) {
@@ -23918,9 +24084,9 @@ async function runNative(input, base, mode, limit, signal) {
23918
24084
  if (globRe && !globRe.test(name) && !globRe.test(full)) return;
23919
24085
  if (globRe) globRe.lastIndex = 0;
23920
24086
  try {
23921
- const stat21 = await fs32.stat(full);
24087
+ const stat21 = await fs33.stat(full);
23922
24088
  if (!stat21.isFile() || stat21.size > maxBytes || stopped || signal.aborted) return;
23923
- const file = await fs32.open(full, "r");
24089
+ const file = await fs33.open(full, "r");
23924
24090
  try {
23925
24091
  let bytesReadTotal = 0;
23926
24092
  let lineNumber = 0;
@@ -24005,7 +24171,7 @@ async function runNative(input, base, mode, limit, signal) {
24005
24171
  if (stopped || signal.aborted) return;
24006
24172
  let entries;
24007
24173
  try {
24008
- entries = await fs32.readdir(dir, { withFileTypes: true });
24174
+ entries = await fs33.readdir(dir, { withFileTypes: true });
24009
24175
  } catch {
24010
24176
  return;
24011
24177
  }
@@ -24016,7 +24182,7 @@ async function runNative(input, base, mode, limit, signal) {
24016
24182
  if (DEFAULT_IGNORE3.has(e.name)) continue;
24017
24183
  if (e.isSymbolicLink()) continue;
24018
24184
  const rel = relPrefix ? `${relPrefix}/${e.name}` : e.name;
24019
- const full = path40.join(dir, e.name);
24185
+ const full = path41.join(dir, e.name);
24020
24186
  if (e.isDirectory()) {
24021
24187
  if (isGitIgnored(rel, true)) continue;
24022
24188
  subdirs.push({ full, rel });
@@ -24256,7 +24422,7 @@ function resolveManifestPath(cwd, pkgManager) {
24256
24422
  }
24257
24423
 
24258
24424
  // src/json.ts
24259
- import * as fs33 from "node:fs/promises";
24425
+ import * as fs34 from "node:fs/promises";
24260
24426
  import { deepMerge as deepMergeCore } from "@wrongstack/core/utils";
24261
24427
  init_util();
24262
24428
  var MAX_JSON_FILE_BYTES = 16 * 1024 * 1024;
@@ -24271,11 +24437,11 @@ var JsonFileTooLargeError = class extends Error {
24271
24437
  };
24272
24438
  async function readJsonFileBounded(filePath, ctx) {
24273
24439
  const resolved = await safeResolveReal(filePath, ctx);
24274
- const stat21 = await fs33.stat(resolved);
24440
+ const stat21 = await fs34.stat(resolved);
24275
24441
  if (stat21.size > MAX_JSON_FILE_BYTES) {
24276
24442
  throw new JsonFileTooLargeError(filePath, stat21.size);
24277
24443
  }
24278
- return fs33.readFile(resolved, "utf8");
24444
+ return fs34.readFile(resolved, "utf8");
24279
24445
  }
24280
24446
  var jsonTool = {
24281
24447
  name: "json",
@@ -24752,60 +24918,60 @@ function jmespathSearch(data, query) {
24752
24918
  }
24753
24919
  function validateJsonSchema(data, schema) {
24754
24920
  const errors = [];
24755
- function check(value, s, path48) {
24921
+ function check(value, s, path49) {
24756
24922
  if (s["type"]) {
24757
24923
  const expectedType = s["type"];
24758
24924
  const actualType = Array.isArray(value) ? "array" : value === null ? "null" : typeof value;
24759
24925
  if (expectedType === "integer") {
24760
- if (!Number.isInteger(value)) errors.push(`${path48}: expected integer, got ${actualType}`);
24926
+ if (!Number.isInteger(value)) errors.push(`${path49}: expected integer, got ${actualType}`);
24761
24927
  } else if (expectedType !== actualType) {
24762
- errors.push(`${path48}: expected ${expectedType}, got ${actualType}`);
24928
+ errors.push(`${path49}: expected ${expectedType}, got ${actualType}`);
24763
24929
  }
24764
24930
  }
24765
24931
  if (typeof value === "string" && s["format"] === "uri" && value) {
24766
24932
  try {
24767
24933
  new URL(value);
24768
24934
  } catch {
24769
- errors.push(`${path48}: not a valid URI`);
24935
+ errors.push(`${path49}: not a valid URI`);
24770
24936
  }
24771
24937
  }
24772
24938
  if (typeof value === "string" && s["pattern"]) {
24773
24939
  const compiled = compileUserRegex(s["pattern"], "");
24774
24940
  if (!compiled.ok) {
24775
- errors.push(`${path48}: invalid schema pattern \u2014 ${compiled.reason}`);
24941
+ errors.push(`${path49}: invalid schema pattern \u2014 ${compiled.reason}`);
24776
24942
  } else if (!compiled.regex.test(capSubject(value))) {
24777
- errors.push(`${path48}: does not match pattern ${s["pattern"]}`);
24943
+ errors.push(`${path49}: does not match pattern ${s["pattern"]}`);
24778
24944
  }
24779
24945
  }
24780
24946
  if (typeof value === "string" && s["minLength"] !== void 0 && value.length < s["minLength"]) {
24781
- errors.push(`${path48}: string too short (min ${s["minLength"]})`);
24947
+ errors.push(`${path49}: string too short (min ${s["minLength"]})`);
24782
24948
  }
24783
24949
  if (typeof value === "string" && s["maxLength"] !== void 0 && value.length > s["maxLength"]) {
24784
- errors.push(`${path48}: string too long (max ${s["maxLength"]})`);
24950
+ errors.push(`${path49}: string too long (max ${s["maxLength"]})`);
24785
24951
  }
24786
24952
  if (typeof value === "number" && s["minimum"] !== void 0 && value < s["minimum"]) {
24787
- errors.push(`${path48}: below minimum ${s["minimum"]}`);
24953
+ errors.push(`${path49}: below minimum ${s["minimum"]}`);
24788
24954
  }
24789
24955
  if (typeof value === "number" && s["maximum"] !== void 0 && value > s["maximum"]) {
24790
- errors.push(`${path48}: above maximum ${s["maximum"]}`);
24956
+ errors.push(`${path49}: above maximum ${s["maximum"]}`);
24791
24957
  }
24792
24958
  if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
24793
24959
  for (let i = 0; i < value.length; i++) {
24794
- check(value[i], s["items"], `${path48}[${i}]`);
24960
+ check(value[i], s["items"], `${path49}[${i}]`);
24795
24961
  }
24796
24962
  }
24797
24963
  if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
24798
24964
  const props = s["properties"];
24799
24965
  for (const [k, propSchema] of Object.entries(props)) {
24800
- check(value[k], propSchema, `${path48}.${k}`);
24966
+ check(value[k], propSchema, `${path49}.${k}`);
24801
24967
  }
24802
24968
  }
24803
24969
  }
24804
24970
  check(data, schema, "$");
24805
24971
  return { valid: errors.length === 0, errors };
24806
24972
  }
24807
- function simpleQuery(data, path48) {
24808
- const parts = path48.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
24973
+ function simpleQuery(data, path49) {
24974
+ const parts = path49.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
24809
24975
  let current = data;
24810
24976
  for (const part of parts) {
24811
24977
  if (current === null || current === void 0) return void 0;
@@ -27477,7 +27643,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
27477
27643
  };
27478
27644
  }
27479
27645
  args.push("--timestamps", service);
27480
- return new Promise((resolve22) => {
27646
+ return new Promise((resolve23) => {
27481
27647
  let stdout = "";
27482
27648
  let stderr = "";
27483
27649
  const MAX = 2e5;
@@ -27493,7 +27659,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
27493
27659
  if (settled) return;
27494
27660
  settled = true;
27495
27661
  clearTimeout(timer);
27496
- resolve22(result);
27662
+ resolve23(result);
27497
27663
  };
27498
27664
  const child = spawn11("docker", args, {
27499
27665
  cwd,
@@ -27538,7 +27704,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
27538
27704
  }
27539
27705
  var DOCKER_LOGS_TIMEOUT_MS = 3e3;
27540
27706
  var MAX_TAIL_LINES = 1e5;
27541
- async function fileLogs(path48, lines, filterRe) {
27707
+ async function fileLogs(path49, lines, filterRe) {
27542
27708
  const { createInterface } = await import("node:readline");
27543
27709
  const { createReadStream: createReadStream2 } = await import("node:fs");
27544
27710
  const entries = [];
@@ -27547,7 +27713,7 @@ async function fileLogs(path48, lines, filterRe) {
27547
27713
  let writeIdx = 0;
27548
27714
  let totalLines = 0;
27549
27715
  const rl = createInterface({
27550
- input: createReadStream2(path48),
27716
+ input: createReadStream2(path49),
27551
27717
  crlfDelay: Number.POSITIVE_INFINITY
27552
27718
  });
27553
27719
  for await (const line of rl) {
@@ -27568,7 +27734,7 @@ async function fileLogs(path48, lines, filterRe) {
27568
27734
  if (parsed) entries.push(parsed);
27569
27735
  }
27570
27736
  return {
27571
- source: path48,
27737
+ source: path49,
27572
27738
  entries,
27573
27739
  total: entries.length,
27574
27740
  truncated: totalLines > effLines,
@@ -27710,7 +27876,7 @@ var outdatedTool = {
27710
27876
  }
27711
27877
  };
27712
27878
  function runOutdated(manager, args, cwd, signal) {
27713
- return new Promise((resolve22) => {
27879
+ return new Promise((resolve23) => {
27714
27880
  let stdout = "";
27715
27881
  let stderr = "";
27716
27882
  const MAX = 1e5;
@@ -27735,10 +27901,10 @@ function runOutdated(manager, args, cwd, signal) {
27735
27901
  });
27736
27902
  child.on("close", (code) => {
27737
27903
  const result = parseOutdatedOutput(stdout, code ?? 0);
27738
- resolve22(result);
27904
+ resolve23(result);
27739
27905
  });
27740
27906
  child.on("error", (e) => {
27741
- resolve22({
27907
+ resolve23({
27742
27908
  exit_code: 1,
27743
27909
  packages: [],
27744
27910
  total: 0,
@@ -27798,10 +27964,10 @@ Note: exit code 1 from \`outdated\` means outdated packages were found (expected
27798
27964
  // src/patch.ts
27799
27965
  init_util();
27800
27966
  import { spawn as spawn13 } from "node:child_process";
27801
- import * as fs34 from "node:fs/promises";
27967
+ import * as fs35 from "node:fs/promises";
27802
27968
  import * as os9 from "node:os";
27803
- import * as path41 from "node:path";
27804
- import { buildChildEnv as buildChildEnv8, toErrorMessage as toErrorMessage13 } from "@wrongstack/core/utils";
27969
+ import * as path42 from "node:path";
27970
+ import { buildChildEnv as buildChildEnv8, toErrorMessage as toErrorMessage14 } from "@wrongstack/core/utils";
27805
27971
  var patchTool = {
27806
27972
  name: "patch",
27807
27973
  category: "Filesystem",
@@ -27845,26 +28011,26 @@ var patchTool = {
27845
28011
  try {
27846
28012
  dir = input.directory ? await safeResolveReal(input.directory, ctx) : ctx.cwd;
27847
28013
  } catch (err) {
27848
- return refuse(`patch refused: ${toErrorMessage13(err)}`);
28014
+ return refuse(`patch refused: ${toErrorMessage14(err)}`);
27849
28015
  }
27850
- const realRoot = await fs34.realpath(ctx.projectRoot).catch(() => path41.resolve(ctx.projectRoot));
28016
+ const realRoot = await fs35.realpath(ctx.projectRoot).catch(() => path42.resolve(ctx.projectRoot));
27851
28017
  const targets = extractDiffTargets(input.patch);
27852
28018
  const resolvedTargets = [];
27853
28019
  for (const t of targets) {
27854
28020
  const stripped = stripPathComponents(t.raw, strip);
27855
28021
  if (!stripped) continue;
27856
- if (path41.isAbsolute(stripped)) {
28022
+ if (path42.isAbsolute(stripped)) {
27857
28023
  return refuse(`patch refused: target "${t.raw}" strips to absolute path`);
27858
28024
  }
27859
- const candidate = path41.resolve(dir, stripped);
28025
+ const candidate = path42.resolve(dir, stripped);
27860
28026
  let real;
27861
28027
  try {
27862
28028
  real = await resolveRealInsideRoot(candidate, ctx);
27863
28029
  } catch (err) {
27864
- return refuse(`patch refused: target "${t.raw}" ${toErrorMessage13(err)}`);
28030
+ return refuse(`patch refused: target "${t.raw}" ${toErrorMessage14(err)}`);
27865
28031
  }
27866
- const rel = path41.relative(realRoot, real);
27867
- if (rel.startsWith("..") || path41.isAbsolute(rel)) {
28032
+ const rel = path42.relative(realRoot, real);
28033
+ if (rel.startsWith("..") || path42.isAbsolute(rel)) {
27868
28034
  return refuse(`patch refused: target "${t.raw}" resolves outside project root`);
27869
28035
  }
27870
28036
  resolvedTargets.push({ raw: t.raw, deleted: t.deleted, abs: real });
@@ -27873,17 +28039,17 @@ var patchTool = {
27873
28039
  const beforeExisted = /* @__PURE__ */ new Set();
27874
28040
  if (!dryRun) {
27875
28041
  for (const target of resolvedTargets) {
27876
- const existed = (await fs34.stat(target.abs).catch(() => null))?.isFile() ?? false;
28042
+ const existed = (await fs35.stat(target.abs).catch(() => null))?.isFile() ?? false;
27877
28043
  if (existed) beforeExisted.add(target.abs);
27878
28044
  beforeContents.set(target.abs, await readTextForTracking(target.abs));
27879
28045
  }
27880
28046
  }
27881
- const tmpDir = await fs34.mkdtemp(path41.join(os9.tmpdir(), ".wstack_patch_"));
28047
+ const tmpDir = await fs35.mkdtemp(path42.join(os9.tmpdir(), ".wstack_patch_"));
27882
28048
  try {
27883
- await fs34.chmod(tmpDir, 448).catch(() => {
28049
+ await fs35.chmod(tmpDir, 448).catch(() => {
27884
28050
  });
27885
- const patchFile = path41.join(tmpDir, "in.diff");
27886
- await fs34.writeFile(patchFile, input.patch, { mode: 384 });
28051
+ const patchFile = path42.join(tmpDir, "in.diff");
28052
+ await fs35.writeFile(patchFile, input.patch, { mode: 384 });
27887
28053
  const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
27888
28054
  const result = await runPatch(args, dir, opts.signal, {
27889
28055
  patchFile,
@@ -27895,7 +28061,7 @@ var patchTool = {
27895
28061
  for (const target of resolvedTargets) {
27896
28062
  const abs = target.abs;
27897
28063
  const before = beforeContents.get(abs) ?? null;
27898
- const stat21 = await fs34.stat(abs).catch(() => null);
28064
+ const stat21 = await fs35.stat(abs).catch(() => null);
27899
28065
  if (!stat21?.isFile()) {
27900
28066
  if (beforeExisted.has(abs)) {
27901
28067
  touched.push(abs);
@@ -27928,7 +28094,7 @@ var patchTool = {
27928
28094
  }
27929
28095
  if (result.exitCode !== 0) {
27930
28096
  if (!dryRun) {
27931
- const partial = touched.length > 0 ? ` ${touched.length} file(s) were still modified on disk and have been recorded for rewind: ${touched.map((p) => path41.relative(realRoot, p) || p).join(", ")}.` : "";
28097
+ const partial = touched.length > 0 ? ` ${touched.length} file(s) were still modified on disk and have been recorded for rewind: ${touched.map((p) => path42.relative(realRoot, p) || p).join(", ")}.` : "";
27932
28098
  return {
27933
28099
  applied: touched.length,
27934
28100
  rejected: 1,
@@ -27936,7 +28102,7 @@ var patchTool = {
27936
28102
  // success path (which returns GNU patch's dir-relative names).
27937
28103
  // `touched` entries are realpaths from resolveRealInsideRoot, and
27938
28104
  // realRoot is also a realpath, so path.relative is like-for-like.
27939
- files: touched.map((p) => path41.relative(realRoot, p) || p),
28105
+ files: touched.map((p) => path42.relative(realRoot, p) || p),
27940
28106
  dry_run: dryRun,
27941
28107
  message: `patch failed: ${result.stderr || result.stdout}${partial}`
27942
28108
  };
@@ -27952,7 +28118,7 @@ var patchTool = {
27952
28118
  }
27953
28119
  const patched = result.engine === "git" ? [
27954
28120
  ...new Set(
27955
- resolvedTargets.map((target) => path41.relative(dir, target.abs) || target.abs)
28121
+ resolvedTargets.map((target) => path42.relative(dir, target.abs) || target.abs)
27956
28122
  )
27957
28123
  ] : extractPatchedFiles(result.stdout);
27958
28124
  return {
@@ -27963,7 +28129,7 @@ var patchTool = {
27963
28129
  message: result.stdout || "patch applied"
27964
28130
  };
27965
28131
  } finally {
27966
- await fs34.rm(tmpDir, { recursive: true, force: true }).catch(() => {
28132
+ await fs35.rm(tmpDir, { recursive: true, force: true }).catch(() => {
27967
28133
  });
27968
28134
  }
27969
28135
  }
@@ -27971,9 +28137,9 @@ var patchTool = {
27971
28137
  var MAX_TRACKING_BYTES = 5 * 1024 * 1024;
27972
28138
  async function readTextForTracking(absPath) {
27973
28139
  try {
27974
- const stat21 = await fs34.stat(absPath);
28140
+ const stat21 = await fs35.stat(absPath);
27975
28141
  if (!stat21.isFile() || stat21.size > MAX_TRACKING_BYTES) return null;
27976
- const buf = await fs34.readFile(absPath);
28142
+ const buf = await fs35.readFile(absPath);
27977
28143
  if (buf.includes(0)) return null;
27978
28144
  return buf.toString("utf8");
27979
28145
  } catch {
@@ -28057,7 +28223,7 @@ function runPatch(args, cwd, signal, fallback) {
28057
28223
  });
28058
28224
  }
28059
28225
  function runPatchProcess(command, args, cwd, signal) {
28060
- return new Promise((resolve22) => {
28226
+ return new Promise((resolve23) => {
28061
28227
  let stdout = "";
28062
28228
  let stderr = "";
28063
28229
  const env = { ...buildChildEnv8(), LANG: "C", LC_ALL: "C" };
@@ -28076,11 +28242,11 @@ function runPatchProcess(command, args, cwd, signal) {
28076
28242
  });
28077
28243
  child.on(
28078
28244
  "close",
28079
- (code) => resolve22({ exitCode: code ?? 1, stdout, stderr, unavailable: false })
28245
+ (code) => resolve23({ exitCode: code ?? 1, stdout, stderr, unavailable: false })
28080
28246
  );
28081
28247
  child.on(
28082
28248
  "error",
28083
- (e) => resolve22({
28249
+ (e) => resolve23({
28084
28250
  exitCode: 1,
28085
28251
  stdout: "",
28086
28252
  stderr: e.message,
@@ -28871,9 +29037,9 @@ function mkResult(plan, ok, message, todos) {
28871
29037
 
28872
29038
  // src/read.ts
28873
29039
  init_util();
28874
- import * as fs35 from "node:fs/promises";
29040
+ import * as fs36 from "node:fs/promises";
28875
29041
  import { FsError, ToolValidationError as ToolValidationError8 } from "@wrongstack/core/types";
28876
- import { toErrorMessage as toErrorMessage14 } from "@wrongstack/core/utils";
29042
+ import { toErrorMessage as toErrorMessage15 } from "@wrongstack/core/utils";
28877
29043
  var ADVANCED_MODE_META_KEY = "tools.read.advancedMode";
28878
29044
  var MAX_BYTES2 = 5 * 1024 * 1024;
28879
29045
  var readTool = {
@@ -28931,7 +29097,7 @@ var readTool = {
28931
29097
  const shouldIncludeSymbols = input.includeSymbols === true || input.includeSymbols !== false && ctx.meta[ADVANCED_MODE_META_KEY] === true;
28932
29098
  let stat21;
28933
29099
  try {
28934
- stat21 = await fs35.stat(absPath);
29100
+ stat21 = await fs36.stat(absPath);
28935
29101
  } catch (err) {
28936
29102
  const code = err.code;
28937
29103
  if (code === "ENOENT") {
@@ -28943,7 +29109,7 @@ var readTool = {
28943
29109
  });
28944
29110
  }
28945
29111
  throw new FsError({
28946
- message: `read: failed to stat "${input.path}": ${toErrorMessage14(err)}`,
29112
+ message: `read: failed to stat "${input.path}": ${toErrorMessage15(err)}`,
28947
29113
  code: "FS_READ_FAILED",
28948
29114
  path: absPath,
28949
29115
  context: { errno: code },
@@ -28983,7 +29149,7 @@ var readTool = {
28983
29149
  ...symResult2?.symbols ? { symbols: symResult2.symbols } : {}
28984
29150
  };
28985
29151
  }
28986
- const buf = await fs35.readFile(absPath);
29152
+ const buf = await fs36.readFile(absPath);
28987
29153
  if (isBinaryBuffer(buf)) {
28988
29154
  throw new FsError({
28989
29155
  message: `read: "${input.path}" appears to be binary`,
@@ -29150,8 +29316,8 @@ ${interesting.join("\n")}` : "symbols/imports: (none detected)"
29150
29316
 
29151
29317
  // src/replace.ts
29152
29318
  import { spawn as spawn14 } from "node:child_process";
29153
- import * as fs36 from "node:fs/promises";
29154
- import * as path42 from "node:path";
29319
+ import * as fs37 from "node:fs/promises";
29320
+ import * as path43 from "node:path";
29155
29321
  import { ToolValidationError as ToolValidationError9 } from "@wrongstack/core/types";
29156
29322
  import {
29157
29323
  atomicWrite as atomicWrite4,
@@ -29234,14 +29400,14 @@ var replaceTool = {
29234
29400
  const dryRun = input.dry_run ?? true;
29235
29401
  const filesInput = Array.isArray(input.files) ? input.files.join(",") : input.files;
29236
29402
  const fileList = await resolveFiles2(filesInput, ctx, globRe);
29237
- const realRoot = await fs36.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
29403
+ const realRoot = await fs37.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
29238
29404
  const results = [];
29239
29405
  let totalReplacements = 0;
29240
29406
  let diffBytesUsed = 0;
29241
29407
  let diffsOmitted = 0;
29242
29408
  let diffsTruncated = 0;
29243
29409
  for (const absPath of fileList) {
29244
- const lstat2 = await fs36.lstat(absPath).catch((err) => {
29410
+ const lstat2 = await fs37.lstat(absPath).catch((err) => {
29245
29411
  if (err.code === "ENOENT") return null;
29246
29412
  throw err;
29247
29413
  });
@@ -29249,17 +29415,17 @@ var replaceTool = {
29249
29415
  if (lstat2.isSymbolicLink()) continue;
29250
29416
  let realPath;
29251
29417
  try {
29252
- realPath = await fs36.realpath(absPath);
29418
+ realPath = await fs37.realpath(absPath);
29253
29419
  } catch {
29254
29420
  continue;
29255
29421
  }
29256
- const rel = path42.relative(realRoot, realPath);
29257
- if (rel.startsWith("..") || path42.isAbsolute(rel)) continue;
29258
- const stat21 = await fs36.stat(realPath).catch(() => null);
29422
+ const rel = path43.relative(realRoot, realPath);
29423
+ if (rel.startsWith("..") || path43.isAbsolute(rel)) continue;
29424
+ const stat21 = await fs37.stat(realPath).catch(() => null);
29259
29425
  if (!stat21?.isFile()) continue;
29260
29426
  let content;
29261
29427
  try {
29262
- const buf = await fs36.readFile(realPath);
29428
+ const buf = await fs37.readFile(realPath);
29263
29429
  if (isBinaryBuffer(buf)) continue;
29264
29430
  content = buf.toString("utf8");
29265
29431
  } catch {
@@ -29282,7 +29448,7 @@ var replaceTool = {
29282
29448
  if (!dryRun) {
29283
29449
  const newContent = toStyle2(newContentLf, style);
29284
29450
  await atomicWrite4(realPath, newContent, { mode: stat21.mode & 511 });
29285
- const written = await fs36.stat(realPath).catch(() => null);
29451
+ const written = await fs37.stat(realPath).catch(() => null);
29286
29452
  if (written) {
29287
29453
  ctx.recordRead?.(realPath, written.mtimeMs, "write", sha256hex(newContent));
29288
29454
  }
@@ -29382,8 +29548,8 @@ async function resolveFiles2(filesInput, ctx, extraGlob) {
29382
29548
  const resolved = [];
29383
29549
  for (const p of parts) {
29384
29550
  const absPath = await safeResolveReal(p, ctx);
29385
- if (extraGlob && !passesExtraGlob(extraGlob, path42.basename(absPath), absPath)) continue;
29386
- const stat21 = await fs36.stat(absPath).catch(() => null);
29551
+ if (extraGlob && !passesExtraGlob(extraGlob, path43.basename(absPath), absPath)) continue;
29552
+ const stat21 = await fs37.stat(absPath).catch(() => null);
29387
29553
  if (stat21?.isFile()) {
29388
29554
  resolved.push(absPath);
29389
29555
  }
@@ -29397,7 +29563,7 @@ async function globFiles(pattern, base, extraGlob) {
29397
29563
  const { promise } = spawnRgFind(pattern, base);
29398
29564
  const files = await promise;
29399
29565
  if (extraGlob) {
29400
- return files.filter((f) => passesExtraGlob(extraGlob, path42.basename(f), f));
29566
+ return files.filter((f) => passesExtraGlob(extraGlob, path43.basename(f), f));
29401
29567
  }
29402
29568
  return files;
29403
29569
  } catch {
@@ -29407,17 +29573,17 @@ async function globFiles(pattern, base, extraGlob) {
29407
29573
  }
29408
29574
  var rgAvailabilityCache2;
29409
29575
  function checkRg() {
29410
- rgAvailabilityCache2 ??= new Promise((resolve22) => {
29576
+ rgAvailabilityCache2 ??= new Promise((resolve23) => {
29411
29577
  try {
29412
29578
  const p = spawn14("rg", ["--version"], {
29413
29579
  env: buildChildEnv9(),
29414
29580
  stdio: "ignore",
29415
29581
  windowsHide: true
29416
29582
  });
29417
- p.on("error", () => resolve22(false));
29418
- p.on("close", (code) => resolve22(code === 0));
29583
+ p.on("error", () => resolve23(false));
29584
+ p.on("close", (code) => resolve23(code === 0));
29419
29585
  } catch {
29420
- resolve22(false);
29586
+ resolve23(false);
29421
29587
  }
29422
29588
  });
29423
29589
  return rgAvailabilityCache2;
@@ -29443,10 +29609,10 @@ function spawnRgFind(pattern, base) {
29443
29609
  }
29444
29610
  });
29445
29611
  return {
29446
- promise: new Promise((resolve22, reject) => {
29612
+ promise: new Promise((resolve23, reject) => {
29447
29613
  child.on("error", reject);
29448
29614
  child.on("close", () => {
29449
- resolve22(buf.split("\n").filter(Boolean));
29615
+ resolve23(buf.split("\n").filter(Boolean));
29450
29616
  });
29451
29617
  })
29452
29618
  };
@@ -29457,15 +29623,15 @@ async function globNative(pattern, base, extraGlob) {
29457
29623
  const walk = async (dir) => {
29458
29624
  let entries;
29459
29625
  try {
29460
- entries = await fs36.readdir(dir, { withFileTypes: true });
29626
+ entries = await fs37.readdir(dir, { withFileTypes: true });
29461
29627
  } catch {
29462
29628
  return;
29463
29629
  }
29464
29630
  for (const e of entries) {
29465
29631
  if (DEFAULT_IGNORE4.includes(e.name)) continue;
29466
- const full = path42.join(dir, e.name);
29632
+ const full = path43.join(dir, e.name);
29467
29633
  try {
29468
- const stat21 = await fs36.lstat(full);
29634
+ const stat21 = await fs37.lstat(full);
29469
29635
  if (stat21.isSymbolicLink()) continue;
29470
29636
  } catch {
29471
29637
  continue;
@@ -29489,8 +29655,8 @@ async function globNative(pattern, base, extraGlob) {
29489
29655
 
29490
29656
  // src/scaffold.ts
29491
29657
  init_util();
29492
- import * as fs37 from "node:fs/promises";
29493
- import * as path43 from "node:path";
29658
+ import * as fs38 from "node:fs/promises";
29659
+ import * as path44 from "node:path";
29494
29660
  import { atomicWrite as atomicWrite5 } from "@wrongstack/core/utils";
29495
29661
  var BUILT_IN_TEMPLATES = {
29496
29662
  "npm-package": {
@@ -29641,16 +29807,16 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
29641
29807
  let filesCreated = 0;
29642
29808
  for (const [filePath, content] of Object.entries(templateFiles)) {
29643
29809
  const resolvedPath = substituteVars(filePath, name, vars);
29644
- const joinedPath = path43.join(cwd, resolvedPath);
29645
- const root = path43.resolve(ctx.projectRoot);
29646
- const target = path43.resolve(joinedPath);
29647
- const rel = path43.relative(root, target);
29648
- if (rel.startsWith("..") || path43.isAbsolute(rel)) {
29810
+ const joinedPath = path44.join(cwd, resolvedPath);
29811
+ const root = path44.resolve(ctx.projectRoot);
29812
+ const target = path44.resolve(joinedPath);
29813
+ const rel = path44.relative(root, target);
29814
+ if (rel.startsWith("..") || path44.isAbsolute(rel)) {
29649
29815
  throw new Error(`scaffold: generated path "${resolvedPath}" would escape project root`);
29650
29816
  }
29651
29817
  const fullPath = target;
29652
29818
  if (!dryRun) {
29653
- await fs37.mkdir(path43.dirname(fullPath), { recursive: true });
29819
+ await fs38.mkdir(path44.dirname(fullPath), { recursive: true });
29654
29820
  await atomicWrite5(fullPath, substituteVars(content, name, vars));
29655
29821
  }
29656
29822
  files.push(resolvedPath);
@@ -29683,7 +29849,7 @@ function substituteVars(content, name, vars) {
29683
29849
  // src/search.ts
29684
29850
  import { FetchError as FetchError3, ToolValidationError as ToolValidationError10 } from "@wrongstack/core/types";
29685
29851
  import { expectDefined as expectDefined9 } from "@wrongstack/core/utils";
29686
- import { toErrorMessage as toErrorMessage15 } from "@wrongstack/core/utils";
29852
+ import { toErrorMessage as toErrorMessage16 } from "@wrongstack/core/utils";
29687
29853
  var DEFAULT_NUM = 10;
29688
29854
  var MAX_RESULTS = 50;
29689
29855
  var MAX_SNIPPET_CHARS = 300;
@@ -29893,9 +30059,9 @@ async function duckduckgoSearch(query, signal) {
29893
30059
  return { results: parseDuckDuckGo(html, MAX_RESULTS) };
29894
30060
  } catch (err) {
29895
30061
  console.log(
29896
- JSON.stringify({ level: "debug", event: "search_failed", query, error: toErrorMessage15(err) })
30062
+ JSON.stringify({ level: "debug", event: "search_failed", query, error: toErrorMessage16(err) })
29897
30063
  );
29898
- return { results: [], error: `duckduckgo unreachable: ${toErrorMessage15(err)}` };
30064
+ return { results: [], error: `duckduckgo unreachable: ${toErrorMessage16(err)}` };
29899
30065
  }
29900
30066
  }
29901
30067
  function takeFrom(iter, max) {
@@ -29961,7 +30127,7 @@ async function googleSearch(query, signal) {
29961
30127
  const html = await fetchWithTimeout(url, signal, TIMEOUT_MS3).then((r) => r.text());
29962
30128
  return { results: parseGoogleResults(html, MAX_RESULTS) };
29963
30129
  } catch (err) {
29964
- return { results: [], error: `google unreachable: ${toErrorMessage15(err)}` };
30130
+ return { results: [], error: `google unreachable: ${toErrorMessage16(err)}` };
29965
30131
  }
29966
30132
  }
29967
30133
  function parseGoogleResults(html, num) {
@@ -29998,7 +30164,7 @@ async function bingSearch(query, signal) {
29998
30164
  const html = await fetchWithTimeout(url, signal, TIMEOUT_MS3).then((r) => r.text());
29999
30165
  return { results: parseBingResults(html, MAX_RESULTS) };
30000
30166
  } catch (err) {
30001
- return { results: [], error: `bing unreachable: ${toErrorMessage15(err)}` };
30167
+ return { results: [], error: `bing unreachable: ${toErrorMessage16(err)}` };
30002
30168
  }
30003
30169
  }
30004
30170
  function parseBingResults(html, num) {
@@ -30096,9 +30262,9 @@ function decodeHtmlEntities(text) {
30096
30262
  }
30097
30263
 
30098
30264
  // src/security-ast-scan-tool.ts
30099
- import * as fs38 from "node:fs/promises";
30100
- import * as path44 from "node:path";
30101
- import { toErrorMessage as toErrorMessage16 } from "@wrongstack/core/utils";
30265
+ import * as fs39 from "node:fs/promises";
30266
+ import * as path45 from "node:path";
30267
+ import { toErrorMessage as toErrorMessage17 } from "@wrongstack/core/utils";
30102
30268
  var SECRET_PATTERNS = [
30103
30269
  { name: "AWS Access Key", regex: /\b(AKIA[0-9A-Z]{16})\b/ },
30104
30270
  { name: "GitHub Token", regex: /\b(gh[pousr]_[A-Za-z0-9_]{36,255})\b/ },
@@ -30264,9 +30430,9 @@ var securityAstScanTool = {
30264
30430
  let targetFile = input.file ?? "inline-code.ts";
30265
30431
  let content = input.content;
30266
30432
  if (!content && input.file) {
30267
- const absPath = path44.isAbsolute(input.file) ? input.file : path44.resolve(projectRoot, input.file);
30268
- targetFile = path44.relative(projectRoot, absPath).replace(/\\/g, "/");
30269
- content = await fs38.readFile(absPath, "utf8");
30433
+ const absPath = path45.isAbsolute(input.file) ? input.file : path45.resolve(projectRoot, input.file);
30434
+ targetFile = path45.relative(projectRoot, absPath).replace(/\\/g, "/");
30435
+ content = await fs39.readFile(absPath, "utf8");
30270
30436
  }
30271
30437
  if (!content) {
30272
30438
  return {
@@ -30307,15 +30473,15 @@ var securityAstScanTool = {
30307
30473
  warningCount: 0,
30308
30474
  findings: [],
30309
30475
  summary: "",
30310
- error: toErrorMessage16(err)
30476
+ error: toErrorMessage17(err)
30311
30477
  };
30312
30478
  }
30313
30479
  }
30314
30480
  };
30315
30481
 
30316
30482
  // src/set-working-dir.ts
30317
- import * as fs39 from "node:fs/promises";
30318
- import { toErrorMessage as toErrorMessage17 } from "@wrongstack/core/utils";
30483
+ import * as fs40 from "node:fs/promises";
30484
+ import { toErrorMessage as toErrorMessage18 } from "@wrongstack/core/utils";
30319
30485
  var setWorkingDirTool = {
30320
30486
  name: "set_working_dir",
30321
30487
  category: "Context",
@@ -30349,12 +30515,12 @@ var setWorkingDirTool = {
30349
30515
  } catch (err) {
30350
30516
  return {
30351
30517
  current: ctx.workingDir,
30352
- error: toErrorMessage17(err)
30518
+ error: toErrorMessage18(err)
30353
30519
  };
30354
30520
  }
30355
30521
  let isDirectory = false;
30356
30522
  try {
30357
- isDirectory = (await fs39.stat(resolved)).isDirectory();
30523
+ isDirectory = (await fs40.stat(resolved)).isDirectory();
30358
30524
  } catch {
30359
30525
  isDirectory = false;
30360
30526
  }
@@ -30889,7 +31055,7 @@ ${formatTaskList2(file.tasks)}` : file.tasks.length > 0 ? formatTaskList2(file.t
30889
31055
  init_spawn_stream();
30890
31056
  init_util();
30891
31057
  init_legacy_bridge();
30892
- import * as path45 from "node:path";
31058
+ import * as path46 from "node:path";
30893
31059
  var testTool = {
30894
31060
  name: "test",
30895
31061
  category: "Code Quality",
@@ -30996,7 +31162,7 @@ async function detectRunner(cwd) {
30996
31162
  const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
30997
31163
  for (const f of candidates) {
30998
31164
  try {
30999
- await stat21(path45.join(cwd, f));
31165
+ await stat21(path46.join(cwd, f));
31000
31166
  if (f.includes("vitest")) return "vitest";
31001
31167
  if (f.includes("jest")) return "jest";
31002
31168
  if (f.includes("mocha")) return "mocha";
@@ -31373,8 +31539,8 @@ var toolUseTool = {
31373
31539
 
31374
31540
  // src/tree.ts
31375
31541
  init_util();
31376
- import * as fs40 from "node:fs/promises";
31377
- import * as path46 from "node:path";
31542
+ import * as fs41 from "node:fs/promises";
31543
+ import * as path47 from "node:path";
31378
31544
  import { DEFAULT_WALK_IGNORE_DIRS as DEFAULT_WALK_IGNORE_DIRS4, expectDefined as expectDefined10 } from "@wrongstack/core/utils";
31379
31545
  var DEFAULT_IGNORE5 = /* @__PURE__ */ new Set([
31380
31546
  ...DEFAULT_WALK_IGNORE_DIRS4,
@@ -31537,7 +31703,7 @@ var treeTool = {
31537
31703
  async function walkDir(dir, depth, opts) {
31538
31704
  opts.signal.throwIfAborted();
31539
31705
  if (opts.retention.truncated) return;
31540
- const entries = await fs40.readdir(dir, { withFileTypes: true }).catch(() => []);
31706
+ const entries = await fs41.readdir(dir, { withFileTypes: true }).catch(() => []);
31541
31707
  const filtered = entries.filter((e) => {
31542
31708
  if (!opts.showHidden && e.name.startsWith(".")) return false;
31543
31709
  if (opts.exclude.has(e.name)) return false;
@@ -31579,7 +31745,7 @@ async function walkDir(dir, depth, opts) {
31579
31745
  opts.retention.outputBytes += lineBytes;
31580
31746
  if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
31581
31747
  const childPrefix = opts.prefix + connector;
31582
- await walkDir(path46.join(dir, entry.name), depth + 1, {
31748
+ await walkDir(path47.join(dir, entry.name), depth + 1, {
31583
31749
  ...opts,
31584
31750
  prefix: childPrefix,
31585
31751
  isLast
@@ -31592,7 +31758,7 @@ async function walkDir(dir, depth, opts) {
31592
31758
  init_spawn_stream();
31593
31759
  init_util();
31594
31760
  init_legacy_bridge();
31595
- import * as path47 from "node:path";
31761
+ import * as path48 from "node:path";
31596
31762
  var typecheckTool = {
31597
31763
  name: "typecheck",
31598
31764
  category: "Code Quality",
@@ -31704,8 +31870,8 @@ async function findTsConfig(cwd) {
31704
31870
  const candidates = ["tsconfig.json", "tsconfig.base.json"];
31705
31871
  for (const f of candidates) {
31706
31872
  try {
31707
- const s = await stat21(path47.join(cwd, f));
31708
- if (s.isFile()) return path47.join(cwd, f);
31873
+ const s = await stat21(path48.join(cwd, f));
31874
+ if (s.isFile()) return path48.join(cwd, f);
31709
31875
  } catch {
31710
31876
  }
31711
31877
  }
@@ -31713,7 +31879,7 @@ async function findTsConfig(cwd) {
31713
31879
  }
31714
31880
 
31715
31881
  // src/write.ts
31716
- import * as fs41 from "node:fs/promises";
31882
+ import * as fs42 from "node:fs/promises";
31717
31883
  import { ToolValidationError as ToolValidationError11 } from "@wrongstack/core/types";
31718
31884
  import {
31719
31885
  atomicWrite as atomicWrite6,
@@ -31790,14 +31956,14 @@ async function prepareWrite(input, ctx) {
31790
31956
  let existed = false;
31791
31957
  let prev = "";
31792
31958
  try {
31793
- const stat21 = await fs41.stat(absPath);
31959
+ const stat21 = await fs42.stat(absPath);
31794
31960
  existed = stat21.isFile();
31795
31961
  if (existed) {
31796
31962
  if (!ctx.hasRead(absPath)) {
31797
- prev = await fs41.readFile(absPath, "utf8");
31963
+ prev = await fs42.readFile(absPath, "utf8");
31798
31964
  ctx.recordRead(absPath, stat21.mtimeMs, "write", sha256hex(prev));
31799
31965
  } else {
31800
- prev = await fs41.readFile(absPath, "utf8");
31966
+ prev = await fs42.readFile(absPath, "utf8");
31801
31967
  }
31802
31968
  }
31803
31969
  } catch (err) {
@@ -31818,7 +31984,7 @@ async function finishWrite(input, ctx, prepared, signal) {
31818
31984
  const rawDiff = prepared.existed ? unifiedDiff3(prepared.prev, content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
31819
31985
  + (new file, ${content.split("\n").length} lines)`;
31820
31986
  const { text: diff, truncated: diffTruncated } = truncateDiffPayload(rawDiff, MAX_DIFF_BYTES3);
31821
- const stat21 = await fs41.stat(prepared.absPath);
31987
+ const stat21 = await fs42.stat(prepared.absPath);
31822
31988
  ctx.recordRead(prepared.absPath, stat21.mtimeMs, "write", sha256hex(content));
31823
31989
  ctx.session.recordFileChange({
31824
31990
  path: prepared.absPath,
@@ -31874,6 +32040,7 @@ var TIER1_TOOLS = [
31874
32040
  editTool,
31875
32041
  clarifyTool,
31876
32042
  codebaseAstReplaceTool,
32043
+ codebaseInvariantCheckTool,
31877
32044
  codebaseStatsTool,
31878
32045
  codebaseSearchTool,
31879
32046
  codebaseSkeletonTool,
@@ -31933,6 +32100,7 @@ var builtinTools = [
31933
32100
  editTool,
31934
32101
  clarifyTool,
31935
32102
  codebaseAstReplaceTool,
32103
+ codebaseInvariantCheckTool,
31936
32104
  codebaseStatsTool,
31937
32105
  codebaseSearchTool,
31938
32106
  codebaseSkeletonTool,