token-goat 2.6.34 → 2.6.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +4 -2
  2. package/SECURITY.md +14 -11
  3. package/dist/{token-goat-chunk-L7QLEVO7.mjs → token-goat-chunk-65BKISIS.mjs} +16 -14
  4. package/dist/{token-goat-chunk-I6EQH27G.mjs → token-goat-chunk-CNDOJ3ZP.mjs} +2 -2
  5. package/dist/{token-goat-chunk-FRMEOIRU.mjs → token-goat-chunk-FRTBMRP7.mjs} +208 -13
  6. package/dist/{token-goat-chunk-IYYQOPY4.mjs → token-goat-chunk-IYTVE6KN.mjs} +29 -10
  7. package/dist/{token-goat-chunk-GXBPEQR2.mjs → token-goat-chunk-KYFJC37X.mjs} +21 -14
  8. package/dist/{token-goat-chunk-CKRATWCJ.mjs → token-goat-chunk-LN6OUHTV.mjs} +5 -5
  9. package/dist/{token-goat-chunk-WA2ER6TZ.mjs → token-goat-chunk-MGOUYAA2.mjs} +1 -1
  10. package/dist/{token-goat-chunk-CN7GUPX6.mjs → token-goat-chunk-UFOVM7ZN.mjs} +461 -97
  11. package/dist/{token-goat-chunk-7FWBSPCT.mjs → token-goat-chunk-V465YKOR.mjs} +198 -21
  12. package/dist/{token-goat-chunk-OA3GFKP2.mjs → token-goat-chunk-VYMGEVZS.mjs} +16 -4
  13. package/dist/{token-goat-hook-chunk-2ISZ4AZR.mjs → token-goat-hook-chunk-3ZDBWJDF.mjs} +1 -1
  14. package/dist/{token-goat-hook-chunk-6C5RN3CM.mjs → token-goat-hook-chunk-5UH54CW6.mjs} +16 -4
  15. package/dist/{token-goat-hook-chunk-OQML5EMA.mjs → token-goat-hook-chunk-6ODM3MP7.mjs} +16 -14
  16. package/dist/{token-goat-hook-chunk-6QTWMSRO.mjs → token-goat-hook-chunk-A77A26A7.mjs} +461 -97
  17. package/dist/{token-goat-hook-chunk-BU5OFDGQ.mjs → token-goat-hook-chunk-BDR6C6IE.mjs} +208 -13
  18. package/dist/{token-goat-hook-chunk-I5VM5PO5.mjs → token-goat-hook-chunk-C6GIABOX.mjs} +21 -14
  19. package/dist/{token-goat-hook-chunk-QTZ6YUMQ.mjs → token-goat-hook-chunk-E257IGSN.mjs} +2 -2
  20. package/dist/{token-goat-hook-chunk-KGLMNOGD.mjs → token-goat-hook-chunk-MW5HPEGD.mjs} +29 -10
  21. package/dist/{token-goat-hook-chunk-ATEGFQAU.mjs → token-goat-hook-chunk-QSCYNJ2B.mjs} +5 -5
  22. package/dist/{token-goat-hook-chunk-CNLMPHXT.mjs → token-goat-hook-chunk-Y2WHX2P3.mjs} +198 -21
  23. package/dist/token-goat-hook.mjs +5 -5
  24. package/dist/token-goat.core.mjs +5 -5
  25. package/package.json +13 -11
  26. package/scripts/install-git-hooks.mjs +55 -0
@@ -5,6 +5,7 @@ import {
5
5
  PER_FILE_COUNTERFACTUAL_CEILING,
6
6
  SOURCE_HINT,
7
7
  SYMBOL_BODY_CHAR_CAP,
8
+ assignBraceBlockSpans,
8
9
  assignFlatEndLines,
9
10
  atomicWriteBytes,
10
11
  atomicWriteText,
@@ -55,6 +56,7 @@ import {
55
56
  parse,
56
57
  precedingDocComment,
57
58
  propagateEndLinesToSymbols,
59
+ pushAll,
58
60
  recordStat,
59
61
  redactIfDotenv,
60
62
  redactSecrets,
@@ -87,7 +89,7 @@ import {
87
89
  withFileLock,
88
90
  writeIfDifferent,
89
91
  writeJsonSettings
90
- } from "./token-goat-hook-chunk-CNLMPHXT.mjs";
92
+ } from "./token-goat-hook-chunk-Y2WHX2P3.mjs";
91
93
  import {
92
94
  registerReset
93
95
  } from "./token-goat-hook-chunk-BUOCULAM.mjs";
@@ -2474,6 +2476,7 @@ registerReset(() => {
2474
2476
  });
2475
2477
 
2476
2478
  // src/session_store.ts
2479
+ import { createHash as createHash2 } from "node:crypto";
2477
2480
  import * as fs9 from "node:fs";
2478
2481
  import * as path8 from "node:path";
2479
2482
 
@@ -2700,9 +2703,22 @@ function sweepCacheRoots(extraRoots = []) {
2700
2703
  var MAX_FILES = 500;
2701
2704
  var SESSIONS_SUBDIR = "sessions";
2702
2705
  var AGENT_SALT_MARKER = sanitizeIdForFilename(":agent:");
2706
+ var AGENT_SALT_SEPARATOR = ":agent:";
2707
+ var AGENT_DIGEST_CHARS = 12;
2708
+ var SALTED_SESSION_MAX = 64 - AGENT_SALT_MARKER.length - AGENT_DIGEST_CHARS;
2709
+ function saltedStemPrefix(sessionId) {
2710
+ return `${sanitizeIdForFilename(sessionId, SALTED_SESSION_MAX)}${AGENT_SALT_MARKER}`;
2711
+ }
2712
+ function sessionFileStem(sessionId) {
2713
+ const sep = sessionId.indexOf(AGENT_SALT_SEPARATOR);
2714
+ if (sep < 0) return sanitizeIdForFilename(sessionId, 64);
2715
+ const agentId = sessionId.slice(sep + AGENT_SALT_SEPARATOR.length);
2716
+ const digest = createHash2("sha256").update(agentId).digest("hex").slice(0, AGENT_DIGEST_CHARS);
2717
+ return `${saltedStemPrefix(sessionId.slice(0, sep))}${digest}`;
2718
+ }
2703
2719
  function sessionPath(sessionId) {
2704
2720
  if (!sessionId) return null;
2705
- const safe = sanitizeIdForFilename(sessionId, 64);
2721
+ const safe = sessionFileStem(sessionId);
2706
2722
  if (!safe) return null;
2707
2723
  const dir = path8.join(tokenGoatHome(), SESSIONS_SUBDIR);
2708
2724
  const candidate = path8.join(dir, `${safe}.json`);
@@ -2954,9 +2970,8 @@ function readSessionStateFile(sessionId) {
2954
2970
  }
2955
2971
  function listSiblingSessionStates(sessionId) {
2956
2972
  if (!sessionId) return [];
2957
- const safeSessionId = sanitizeIdForFilename(sessionId);
2958
- if (!safeSessionId) return [];
2959
- const prefix = `${safeSessionId}${AGENT_SALT_MARKER}`;
2973
+ const prefix = saltedStemPrefix(sessionId);
2974
+ if (prefix === AGENT_SALT_MARKER) return [];
2960
2975
  const dir = path8.join(tokenGoatHome(), SESSIONS_SUBDIR);
2961
2976
  const out = [];
2962
2977
  try {
@@ -3235,7 +3250,7 @@ function logHintEmission(category, sessionId, correlator, compensateSelfResolve
3235
3250
  } catch {
3236
3251
  }
3237
3252
  }
3238
- var TOKEN_GOAT_INVOCATION_RE = /(?:^|[\s;&|])token-goat(?=[\s]|$)/;
3253
+ var TOKEN_GOAT_INVOCATION_RE = /(?:^|[\s;&|])(?:token-goat|tg)(?=[\s]|$)/;
3239
3254
  function commandMentionsCorrelator(command, correlator) {
3240
3255
  let idx = command.indexOf(correlator);
3241
3256
  while (idx !== -1) {
@@ -3442,7 +3457,7 @@ function scanForInjectionPatterns(text) {
3442
3457
  var UNTRUSTED_WEB_TAG = "untrusted-web-content";
3443
3458
  function neutralizeFenceMarkers(text, tag) {
3444
3459
  const escapedTag = tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3445
- const marker = new RegExp(`<\\s*/?\\s*${escapedTag}(?:\\s[^>]*)?\\s*/?\\s*>`, "gi");
3460
+ const marker = new RegExp(`<\\s*/?\\s*${escapedTag}(?=[\\s/>])[^>]*>`, "gi");
3446
3461
  return text.replace(marker, (m) => m.replace(/</g, "&lt;").replace(/>/g, "&gt;"));
3447
3462
  }
3448
3463
  function fenceUntrustedContent(text, matchedPatternNames, tag = UNTRUSTED_WEB_TAG) {
@@ -4040,7 +4055,7 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
4040
4055
  }
4041
4056
 
4042
4057
  // src/image_shrink.ts
4043
- import { createHash as createHash2 } from "node:crypto";
4058
+ import { createHash as createHash3 } from "node:crypto";
4044
4059
  import * as fs12 from "node:fs";
4045
4060
  import * as path10 from "node:path";
4046
4061
 
@@ -4258,7 +4273,7 @@ function imageShrinkCacheDir() {
4258
4273
  return path10.join(tokenGoatHome(), "image_shrink_cache");
4259
4274
  }
4260
4275
  function shrinkCacheKey(originalPath, size, mtimeMs, quality) {
4261
- return createHash2("sha256").update(`${originalPath}:${size}:${mtimeMs}:${quality}`).digest("hex").slice(0, 16);
4276
+ return createHash3("sha256").update(`${originalPath}:${size}:${mtimeMs}:${quality}`).digest("hex").slice(0, 16);
4262
4277
  }
4263
4278
  function findCachedShrink(originalPath, size, mtimeMs, quality) {
4264
4279
  const key = shrinkCacheKey(originalPath, size, mtimeMs, quality);
@@ -4307,18 +4322,18 @@ function pruneShrinkCache() {
4307
4322
  }
4308
4323
  async function finalizeShrinkResult(result, filePath) {
4309
4324
  const basename12 = path10.basename(filePath);
4325
+ const shrinkSaved = result.originalBytes - result.shrunkBytes;
4326
+ recordStat("image_shrink", shrinkSaved, Math.round(shrinkSaved / 4), void 0, basename12);
4310
4327
  if (loadConfig().image_shrink.ocr_enabled) {
4311
4328
  const ocr = await ocrImage(result.data);
4312
4329
  if (ocr !== null && isTextHeavy(ocr, loadConfig().image_shrink.ocr_min_confidence)) {
4313
4330
  const textBytes = Buffer.byteLength(ocr.text, "utf8");
4314
- const saved2 = Math.max(0, result.shrunkBytes - textBytes);
4315
- recordStat("image_ocr", saved2, Math.round(saved2 / 4), void 0, basename12);
4331
+ const saved = Math.max(0, result.shrunkBytes - textBytes);
4332
+ recordStat("image_ocr", saved, Math.round(saved / 4), void 0, basename12);
4316
4333
  return contextOutput(formatOcrSummary(ocr, basename12, result.originalBytes));
4317
4334
  }
4318
4335
  }
4319
- const saved = result.originalBytes - result.shrunkBytes;
4320
4336
  const { summary, dataUrl } = formatShrinkSummary(result, basename12);
4321
- recordStat("image_shrink", saved, Math.round(saved / 4), void 0, basename12);
4322
4337
  return contextOutput(`${summary}
4323
4338
  ${dataUrl}`);
4324
4339
  }
@@ -4562,6 +4577,12 @@ async function embedTexts(texts, modelName = DEFAULT_MODEL) {
4562
4577
  `Dimension mismatch: model returned ${vec.length}-dim vector, expected ${expectedDim}`
4563
4578
  );
4564
4579
  }
4580
+ const badIndex = vec.findIndex((component) => !Number.isFinite(component));
4581
+ if (badIndex !== -1) {
4582
+ throw new Error(
4583
+ `Non-finite embedding component at index ${badIndex}: model returned ${String(vec[badIndex])}`
4584
+ );
4585
+ }
4565
4586
  vecs.push(vec);
4566
4587
  }
4567
4588
  } finally {
@@ -4572,6 +4593,11 @@ function packVec(vec) {
4572
4593
  const view = new Float32Array(vec.length);
4573
4594
  for (const [i, val] of vec.entries()) {
4574
4595
  view[i] = val;
4596
+ if (!Number.isFinite(view[i])) {
4597
+ throw new Error(
4598
+ `Non-finite embedding component at index ${i}: ${String(val)} is not representable as a 32-bit float`
4599
+ );
4600
+ }
4575
4601
  }
4576
4602
  return Buffer.from(view.buffer);
4577
4603
  }
@@ -4584,7 +4610,8 @@ function splitRangeIntoChunks(filePath, lines2, rangeStart, rangeEnd, chunkSize,
4584
4610
  const line = lines2[lineNo - 1] ?? "";
4585
4611
  const lineWithNewline = line + "\n";
4586
4612
  if (currentChunk.length + lineWithNewline.length > chunkSize && currentChunk.length > 0) {
4587
- const currentChunkTooSmall = currentChunk.trim().length < MIN_CHUNK_CHARS;
4613
+ const trimmedLength = currentChunk.trim().length;
4614
+ const currentChunkTooSmall = trimmedLength < MIN_CHUNK_CHARS;
4588
4615
  if (!currentChunkTooSmall) {
4589
4616
  chunks.push({
4590
4617
  filePath,
@@ -4594,12 +4621,21 @@ function splitRangeIntoChunks(filePath, lines2, rangeStart, rangeEnd, chunkSize,
4594
4621
  kind
4595
4622
  });
4596
4623
  }
4597
- const overlapLines = Math.ceil(overlap / 40);
4598
- const computedOverlapStart = Math.max(rangeStart, currentLine - overlapLines);
4599
- const overlapStart = currentChunkTooSmall ? Math.min(computedOverlapStart, startLine) : computedOverlapStart;
4600
- const overlapText = lines2.slice(overlapStart - 1, currentLine - 1).join("\n");
4601
- currentChunk = overlapText + "\n";
4602
- startLine = overlapStart;
4624
+ let overlapChars = 0;
4625
+ let computedOverlapStart = currentLine;
4626
+ while (computedOverlapStart > rangeStart) {
4627
+ const candidateChars = (lines2[computedOverlapStart - 2] ?? "").length + 1;
4628
+ if (overlapChars + candidateChars > overlap) break;
4629
+ overlapChars += candidateChars;
4630
+ computedOverlapStart--;
4631
+ }
4632
+ const droppedChunkHadContent = currentChunkTooSmall && trimmedLength > 0;
4633
+ const overlapStart = droppedChunkHadContent ? Math.min(computedOverlapStart, startLine) : computedOverlapStart;
4634
+ if (overlapStart !== startLine) {
4635
+ const overlapText = lines2.slice(overlapStart - 1, currentLine - 1).join("\n");
4636
+ currentChunk = overlapText + "\n";
4637
+ startLine = overlapStart;
4638
+ }
4603
4639
  }
4604
4640
  currentChunk += lineWithNewline;
4605
4641
  currentLine++;
@@ -4748,7 +4784,7 @@ async function upsertChunks(db, chunks) {
4748
4784
  insertChunkVector(vectorInsertStmt, chunkResult.lastInsertRowid, embedding);
4749
4785
  }
4750
4786
  });
4751
- tx();
4787
+ tx.immediate();
4752
4788
  return "embedded";
4753
4789
  }
4754
4790
  var BACKFILL_MULTIPLIER = 3;
@@ -4772,7 +4808,7 @@ function fetchScopedHits(db, queryVec, k, maxDistance, rootDir) {
4772
4808
  if (!row) {
4773
4809
  continue;
4774
4810
  }
4775
- if (row.distance <= maxDistance) {
4811
+ if (typeof row.distance === "number" && Number.isFinite(row.distance) && row.distance <= maxDistance) {
4776
4812
  const chunk = scopeParams !== void 0 ? chunkStmt.get(row.rowid, ...scopeParams) : chunkStmt.get(row.rowid);
4777
4813
  if (chunk) {
4778
4814
  hits.push({
@@ -6042,9 +6078,9 @@ var MONITORING_COMMAND_PATTERNS = [
6042
6078
  recallHint: "--tail 50"
6043
6079
  },
6044
6080
  // token-goat section/outline/symbol repeat calls — output is stable until the file changes
6045
- { pattern: /^token-goat\s+section\s+["'][^"']+["']/, recallHint: "" },
6046
- { pattern: /^token-goat\s+outline\s+\S+/, recallHint: "" },
6047
- { pattern: /^token-goat\s+symbol\s+\S+/, recallHint: "" }
6081
+ { pattern: /^(?:token-goat|tg)\s+section\s+["'][^"']+["']/, recallHint: "" },
6082
+ { pattern: /^(?:token-goat|tg)\s+outline\s+\S+/, recallHint: "" },
6083
+ { pattern: /^(?:token-goat|tg)\s+symbol\s+\S+/, recallHint: "" }
6048
6084
  ];
6049
6085
  function isPsMultilineSystemQuery(cmd) {
6050
6086
  if (!/^(?:powershell(?:\.exe)?|pwsh(?:\.exe)?)\s+/i.test(cmd)) return false;
@@ -9340,7 +9376,12 @@ function decodeZipEntry(entries, entryPath) {
9340
9376
  async function parseOoxmlPart(xmlText2) {
9341
9377
  const fxp = await loadXmlParser();
9342
9378
  if (!fxp) throw new Error("fast-xml-parser is not installed; run `npm install fast-xml-parser` to enable this command");
9343
- const parser = new fxp.XMLParser({ ignoreAttributes: false, preserveOrder: false, trimValues: false });
9379
+ const parser = new fxp.XMLParser({
9380
+ ignoreAttributes: false,
9381
+ preserveOrder: false,
9382
+ trimValues: false,
9383
+ parseTagValue: false
9384
+ });
9344
9385
  return parser.parse(xmlText2);
9345
9386
  }
9346
9387
  function pushTextValue(runs, val) {
@@ -9386,7 +9427,7 @@ function collectElements(node, tag) {
9386
9427
  const obj = n;
9387
9428
  for (const [key, val] of Object.entries(obj)) {
9388
9429
  if (key === tag) {
9389
- if (Array.isArray(val)) out.push(...val);
9430
+ if (Array.isArray(val)) pushAll(out, val);
9390
9431
  else out.push(val);
9391
9432
  } else if (val !== null && typeof val === "object") {
9392
9433
  walk(val);
@@ -9765,28 +9806,257 @@ function formatCsvProfile(profiles) {
9765
9806
  }).join("\n\n");
9766
9807
  }
9767
9808
 
9768
- // src/xlsx_extract.ts
9769
- var loadExcelJs = createLazyModuleLoader(async () => {
9770
- const mod = await import("exceljs");
9771
- return mod.default ?? mod;
9772
- }, "xlsx reading disabled (exceljs package unavailable)");
9773
- async function requireExcelJs() {
9774
- const mod = await loadExcelJs();
9775
- if (!mod) throw new Error("exceljs is not installed; run `npm install exceljs` to enable this command");
9776
- return mod;
9809
+ // src/xlsx_reader.ts
9810
+ var EMPTY_CELL = Object.freeze({ value: null, text: "" });
9811
+ function asArray(val) {
9812
+ if (val === void 0 || val === null) return [];
9813
+ if (Array.isArray(val)) return val.filter((v) => v !== null && typeof v === "object");
9814
+ if (typeof val === "object") return [val];
9815
+ return [];
9816
+ }
9817
+ function textOf(node) {
9818
+ if (node === void 0 || node === null) return "";
9819
+ if (typeof node === "string") return node;
9820
+ if (typeof node === "number" || typeof node === "boolean") return String(node);
9821
+ if (Array.isArray(node)) return node.map(textOf).join("");
9822
+ const t = node["#text"];
9823
+ return t === void 0 ? "" : String(t);
9824
+ }
9825
+ function attr(node, name) {
9826
+ if (node === void 0) return void 0;
9827
+ const v = node[`@_${name}`];
9828
+ return v === void 0 || v === null ? void 0 : String(v);
9829
+ }
9830
+ function isTruthyAttr(v) {
9831
+ return v === "1" || v === "true";
9832
+ }
9833
+ function refToColumn(ref2) {
9834
+ let n = 0;
9835
+ for (const ch of ref2) {
9836
+ const code = ch.toUpperCase().charCodeAt(0);
9837
+ if (code < 65 || code > 90) break;
9838
+ n = n * 26 + (code - 64);
9839
+ }
9840
+ return n;
9777
9841
  }
9778
- async function loadWorkbook(filePath) {
9779
- const ExcelJS = await requireExcelJs();
9780
- const wb = new ExcelJS.Workbook();
9781
- try {
9782
- await wb.xlsx.readFile(filePath);
9783
- } catch (err) {
9784
- const msg = extractErrorMessage(err);
9785
- if (msg.startsWith("File not found:")) throw err;
9786
- throw new Error(`not a valid .xlsx file: ${filePath}`, { cause: err });
9842
+ function refToRow(ref2) {
9843
+ const m = /(\d+)\s*$/.exec(ref2);
9844
+ return m ? parseInt(m[1], 10) : 0;
9845
+ }
9846
+ var BUILTIN_DATE_FORMAT_IDS = /* @__PURE__ */ new Set([14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 46, 47]);
9847
+ function formatCodeIsDate(formatCode) {
9848
+ const stripped = formatCode.replace(/"[^"]*"/g, "").replace(/\[[^\]]*\]/g, "").replace(/\\./g, "");
9849
+ return /[ymdhs]/i.test(stripped);
9850
+ }
9851
+ function serialToDate(serial, date1904) {
9852
+ if (date1904) return new Date(Date.UTC(1904, 0, 1) + Math.round(serial * 864e5));
9853
+ const adjusted = serial >= 61 ? serial - 1 : serial;
9854
+ return new Date(Date.UTC(1899, 11, 31) + Math.round(adjusted * 864e5));
9855
+ }
9856
+ function numberToText(n) {
9857
+ return String(n);
9858
+ }
9859
+ function parseStyles(xml, parsed) {
9860
+ if (xml === null || parsed === null || typeof parsed !== "object") return { dateStyles: [] };
9861
+ const sheet = parsed["styleSheet"];
9862
+ if (sheet === void 0 || sheet === null || typeof sheet !== "object") return { dateStyles: [] };
9863
+ const root = sheet;
9864
+ const customDateFormats = /* @__PURE__ */ new Set();
9865
+ for (const fmt of asArray(root["numFmts"]?.["numFmt"])) {
9866
+ const id = Number(attr(fmt, "numFmtId"));
9867
+ const code = attr(fmt, "formatCode");
9868
+ if (Number.isFinite(id) && code !== void 0 && formatCodeIsDate(code)) customDateFormats.add(id);
9869
+ }
9870
+ const dateStyles = [];
9871
+ for (const xf of asArray(root["cellXfs"]?.["xf"])) {
9872
+ const id = Number(attr(xf, "numFmtId") ?? "0");
9873
+ dateStyles.push(Number.isFinite(id) && (BUILTIN_DATE_FORMAT_IDS.has(id) || customDateFormats.has(id)));
9874
+ }
9875
+ return { dateStyles };
9876
+ }
9877
+ function parseSharedStrings(parsed) {
9878
+ if (parsed === null || typeof parsed !== "object") return [];
9879
+ const sst = parsed["sst"];
9880
+ if (sst === void 0 || sst === null || typeof sst !== "object") return [];
9881
+ return asArray(sst["si"]).map((si) => {
9882
+ if (si["t"] !== void 0) return textOf(si["t"]);
9883
+ const runs = asArray(si["r"]);
9884
+ if (runs.length > 0) return runs.map((r) => textOf(r["t"])).join("");
9885
+ return "";
9886
+ });
9887
+ }
9888
+ function parseWorkbookRels(parsed) {
9889
+ const out = /* @__PURE__ */ new Map();
9890
+ if (parsed === null || typeof parsed !== "object") return out;
9891
+ const rels = parsed["Relationships"];
9892
+ if (rels === void 0 || rels === null || typeof rels !== "object") return out;
9893
+ for (const rel of asArray(rels["Relationship"])) {
9894
+ const id = attr(rel, "Id");
9895
+ const target = attr(rel, "Target");
9896
+ if (id === void 0 || target === void 0) continue;
9897
+ const normalized = target.startsWith("/") ? target.slice(1) : `xl/${target.replace(/^\.\//, "")}`;
9898
+ out.set(id, normalized);
9899
+ }
9900
+ return out;
9901
+ }
9902
+ function buildCell(c, shared, styles, date1904) {
9903
+ const type = attr(c, "t") ?? "n";
9904
+ const styleIdx = Number(attr(c, "s") ?? "-1");
9905
+ const isDateStyle = Number.isInteger(styleIdx) && styleIdx >= 0 && styles.dateStyles[styleIdx] === true;
9906
+ const hasV = c["v"] !== void 0;
9907
+ const hasIs = c["is"] !== void 0;
9908
+ const fNode = c["f"];
9909
+ const formula = fNode === void 0 ? "" : textOf(fNode);
9910
+ if (!hasV && !hasIs && formula === "") return null;
9911
+ let raw;
9912
+ let text;
9913
+ if (type === "s") {
9914
+ const idx = Number(textOf(c["v"]));
9915
+ const s = Number.isInteger(idx) ? shared[idx] ?? "" : "";
9916
+ raw = s;
9917
+ text = s;
9918
+ } else if (type === "inlineStr") {
9919
+ const isNode = c["is"];
9920
+ const s = isNode !== void 0 && isNode !== null && typeof isNode === "object" ? (() => {
9921
+ const node = isNode;
9922
+ if (node["t"] !== void 0) return textOf(node["t"]);
9923
+ return asArray(node["r"]).map((r) => textOf(r["t"])).join("");
9924
+ })() : textOf(isNode);
9925
+ raw = s;
9926
+ text = s;
9927
+ } else if (type === "str") {
9928
+ const s = textOf(c["v"]);
9929
+ raw = s;
9930
+ text = s;
9931
+ } else if (type === "b") {
9932
+ const b = textOf(c["v"]).trim() === "1";
9933
+ raw = b;
9934
+ text = b ? "TRUE" : "FALSE";
9935
+ } else if (type === "e") {
9936
+ const e = textOf(c["v"]);
9937
+ raw = { error: e };
9938
+ text = e;
9939
+ } else {
9940
+ const n = Number(textOf(c["v"]));
9941
+ if (!Number.isFinite(n)) {
9942
+ raw = null;
9943
+ text = "";
9944
+ } else if (isDateStyle) {
9945
+ const d = serialToDate(n, date1904);
9946
+ raw = d;
9947
+ text = d.toISOString();
9948
+ } else {
9949
+ raw = n;
9950
+ text = numberToText(n);
9951
+ }
9952
+ }
9953
+ if (formula !== "") return { value: { formula, result: raw }, text, formula };
9954
+ return { value: raw, text };
9955
+ }
9956
+ function parseSheet(parsed, shared, styles, date1904) {
9957
+ const cells = /* @__PURE__ */ new Map();
9958
+ let rowCount = 0;
9959
+ let columnCount = 0;
9960
+ let populatedRows = 0;
9961
+ if (parsed === null || typeof parsed !== "object") return { cells, rowCount, columnCount, populatedRows };
9962
+ const ws = parsed["worksheet"];
9963
+ if (ws === void 0 || ws === null || typeof ws !== "object") return { cells, rowCount, columnCount, populatedRows };
9964
+ const sheetData = ws["sheetData"];
9965
+ if (sheetData === void 0 || sheetData === null || typeof sheetData !== "object") {
9966
+ return { cells, rowCount, columnCount, populatedRows };
9967
+ }
9968
+ let fallbackRow = 0;
9969
+ for (const row of asArray(sheetData["row"])) {
9970
+ const declaredRow = Number(attr(row, "r"));
9971
+ const rowIdx = Number.isInteger(declaredRow) && declaredRow > 0 ? declaredRow : fallbackRow + 1;
9972
+ fallbackRow = rowIdx;
9973
+ let fallbackCol = 0;
9974
+ const rowCells = /* @__PURE__ */ new Map();
9975
+ for (const c of asArray(row["c"])) {
9976
+ const ref2 = attr(c, "r");
9977
+ const declaredCol = ref2 === void 0 ? 0 : refToColumn(ref2);
9978
+ const colIdx = declaredCol > 0 ? declaredCol : fallbackCol + 1;
9979
+ fallbackCol = colIdx;
9980
+ const cell = buildCell(c, shared, styles, date1904);
9981
+ if (cell === null) continue;
9982
+ rowCells.set(colIdx, cell);
9983
+ if (colIdx > columnCount) columnCount = colIdx;
9984
+ }
9985
+ if (rowCells.size === 0) continue;
9986
+ cells.set(rowIdx, rowCells);
9987
+ populatedRows++;
9988
+ if (rowIdx > rowCount) rowCount = rowIdx;
9989
+ }
9990
+ return { cells, rowCount, columnCount, populatedRows };
9991
+ }
9992
+ function makeWorksheet(name, data) {
9993
+ function getRow(r) {
9994
+ const rowCells = data.cells.get(r);
9995
+ return {
9996
+ get values() {
9997
+ const out = [];
9998
+ if (rowCells !== void 0) for (const [col, cell] of rowCells) out[col] = cell.value;
9999
+ return out;
10000
+ },
10001
+ eachCell(opts, cb) {
10002
+ if (opts.includeEmpty) {
10003
+ let maxCol = 0;
10004
+ if (rowCells !== void 0) {
10005
+ for (const c of rowCells.keys()) if (c > maxCol) maxCol = c;
10006
+ }
10007
+ for (let c = 1; c <= maxCol; c++) cb(rowCells?.get(c) ?? EMPTY_CELL, c);
10008
+ return;
10009
+ }
10010
+ if (rowCells === void 0) return;
10011
+ for (const col of [...rowCells.keys()].sort((a, b) => a - b)) cb(rowCells.get(col), col);
10012
+ }
10013
+ };
10014
+ }
10015
+ return {
10016
+ name,
10017
+ rowCount: data.rowCount,
10018
+ columnCount: data.columnCount,
10019
+ actualRowCount: data.populatedRows,
10020
+ getRow,
10021
+ getCell(addr) {
10022
+ return data.cells.get(refToRow(addr))?.get(refToColumn(addr)) ?? EMPTY_CELL;
10023
+ }
10024
+ };
10025
+ }
10026
+ async function readXlsxWorkbook(filePath) {
10027
+ const entries = await readOoxmlZip(filePath, ".xlsx");
10028
+ const workbookXml = decodeZipEntry(entries, "xl/workbook.xml");
10029
+ if (workbookXml === null) throw new Error(`not a valid .xlsx file: ${filePath}`);
10030
+ const workbookRoot = await parseOoxmlPart(workbookXml);
10031
+ const wbNode = workbookRoot?.["workbook"];
10032
+ if (wbNode === void 0 || wbNode === null || typeof wbNode !== "object") {
10033
+ throw new Error(`not a valid .xlsx file: ${filePath}`);
10034
+ }
10035
+ const wb = wbNode;
10036
+ const date1904 = isTruthyAttr(attr(wb["workbookPr"], "date1904"));
10037
+ const relsXml = decodeZipEntry(entries, "xl/_rels/workbook.xml.rels");
10038
+ const rels = parseWorkbookRels(relsXml === null ? null : await parseOoxmlPart(relsXml));
10039
+ const sharedXml = decodeZipEntry(entries, "xl/sharedStrings.xml");
10040
+ const shared = sharedXml === null ? [] : parseSharedStrings(await parseOoxmlPart(sharedXml));
10041
+ const stylesXml = decodeZipEntry(entries, "xl/styles.xml");
10042
+ const styles = parseStyles(stylesXml, stylesXml === null ? null : await parseOoxmlPart(stylesXml));
10043
+ const worksheets = [];
10044
+ for (const sheet of asArray(wb["sheets"]?.["sheet"])) {
10045
+ const name = attr(sheet, "name") ?? "";
10046
+ const rid = attr(sheet, "r:id") ?? attr(sheet, "relationshipId");
10047
+ const partPath = rid === void 0 ? void 0 : rels.get(rid);
10048
+ const sheetXml = partPath === void 0 ? null : decodeZipEntry(entries, partPath);
10049
+ const data = sheetXml === null ? { cells: /* @__PURE__ */ new Map(), rowCount: 0, columnCount: 0, populatedRows: 0 } : parseSheet(await parseOoxmlPart(sheetXml), shared, styles, date1904);
10050
+ worksheets.push(makeWorksheet(name, data));
9787
10051
  }
9788
- return wb;
10052
+ return {
10053
+ worksheets,
10054
+ getWorksheet: (name) => worksheets.find((ws) => ws.name === name)
10055
+ };
9789
10056
  }
10057
+
10058
+ // src/xlsx_extract.ts
10059
+ var loadWorkbook = readXlsxWorkbook;
9790
10060
  function requireSheet(wb, sheetName) {
9791
10061
  const ws = wb.getWorksheet(sheetName);
9792
10062
  if (ws === void 0) {
@@ -11361,7 +11631,7 @@ function extractLua(content, filePath) {
11361
11631
  symbols.push(makeLineSymbol(filePath, baseName, "function", lineNum, stripped.slice(0, 200)));
11362
11632
  }
11363
11633
  if (!lineClosesItself(stripped)) {
11364
- funcStack.push({ name: baseName, endKeywordNeeded: true, isBlock: false });
11634
+ funcStack.push({ name: baseName, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
11365
11635
  }
11366
11636
  continue;
11367
11637
  }
@@ -11375,7 +11645,7 @@ function extractLua(content, filePath) {
11375
11645
  symbols.push(makeLineSymbol(filePath, fname, "function", lineNum, stripped.slice(0, 200)));
11376
11646
  }
11377
11647
  if (!lineClosesItself(stripped)) {
11378
- funcStack.push({ name: fname, endKeywordNeeded: true, isBlock: false });
11648
+ funcStack.push({ name: fname, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
11379
11649
  }
11380
11650
  continue;
11381
11651
  }
@@ -11390,7 +11660,7 @@ function extractLua(content, filePath) {
11390
11660
  symbols.push(makeLineSymbol(filePath, baseName, "function", lineNum, stripped.slice(0, 200)));
11391
11661
  }
11392
11662
  if (!lineClosesItself(stripped)) {
11393
- funcStack.push({ name: baseName, endKeywordNeeded: true, isBlock: false });
11663
+ funcStack.push({ name: baseName, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
11394
11664
  }
11395
11665
  continue;
11396
11666
  }
@@ -11411,7 +11681,13 @@ function extractLua(content, filePath) {
11411
11681
  if (/^(?:\bend\b[\s),;}]*)+$/.test(stripped)) {
11412
11682
  const popCount = (stripped.match(/\bend\b/g) ?? []).length;
11413
11683
  for (let k = 0; k < popCount && funcStack.length > 0; k++) {
11414
- funcStack.pop();
11684
+ const popped = funcStack.pop();
11685
+ if (popped !== void 0 && popped.symbolIndex !== void 0) {
11686
+ const open = symbols[popped.symbolIndex];
11687
+ if (open !== void 0 && lineNum > open.lineStart) {
11688
+ symbols[popped.symbolIndex] = { ...open, lineEnd: lineNum, body: lines2.slice(open.lineStart - 1, lineNum).join("\n") };
11689
+ }
11690
+ }
11415
11691
  }
11416
11692
  }
11417
11693
  }
@@ -11453,14 +11729,14 @@ function extractElixir(content, filePath) {
11453
11729
  if (modM) {
11454
11730
  const modName = modM[1] ?? "";
11455
11731
  symbols.push(makeLineSymbol(filePath, modName, "class", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
11456
- moduleStack.push({ name: modName, endKeywordNeeded: true, isBlock: false });
11732
+ moduleStack.push({ name: modName, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
11457
11733
  continue;
11458
11734
  }
11459
11735
  const protoM = PROTOCOL_RE.exec(stripped);
11460
11736
  if (protoM) {
11461
11737
  const protoName = protoM[1] ?? "";
11462
11738
  symbols.push(makeLineSymbol(filePath, protoName, "protocol", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
11463
- moduleStack.push({ name: protoName, endKeywordNeeded: true, isBlock: false });
11739
+ moduleStack.push({ name: protoName, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
11464
11740
  continue;
11465
11741
  }
11466
11742
  const fm = FUNC_RE4.exec(stripped);
@@ -11473,7 +11749,7 @@ function extractElixir(content, filePath) {
11473
11749
  symbols.push(makeLineSymbol(filePath, fname, "function", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
11474
11750
  }
11475
11751
  if (opensDoBlock) {
11476
- moduleStack.push({ name: fname, endKeywordNeeded: true, isBlock: false });
11752
+ moduleStack.push({ name: fname, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
11477
11753
  }
11478
11754
  continue;
11479
11755
  }
@@ -11487,7 +11763,7 @@ function extractElixir(content, filePath) {
11487
11763
  symbols.push(makeLineSymbol(filePath, fname, "function", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
11488
11764
  }
11489
11765
  if (opensDoBlock) {
11490
- moduleStack.push({ name: fname, endKeywordNeeded: true, isBlock: false });
11766
+ moduleStack.push({ name: fname, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
11491
11767
  }
11492
11768
  continue;
11493
11769
  }
@@ -11504,7 +11780,13 @@ function extractElixir(content, filePath) {
11504
11780
  }
11505
11781
  if (stripped === "end" || /^end\s/.test(stripped) || /^end$/.test(stripped)) {
11506
11782
  if (moduleStack.length > 0) {
11507
- moduleStack.pop();
11783
+ const popped = moduleStack.pop();
11784
+ if (popped !== void 0 && popped.symbolIndex !== void 0) {
11785
+ const open = symbols[popped.symbolIndex];
11786
+ if (open !== void 0 && lineNum > open.lineStart) {
11787
+ symbols[popped.symbolIndex] = { ...open, lineEnd: lineNum, body: lines2.slice(open.lineStart - 1, lineNum).join("\n") };
11788
+ }
11789
+ }
11508
11790
  }
11509
11791
  }
11510
11792
  }
@@ -11802,7 +12084,7 @@ function bracedBodyEndLine(content, lineIndex, parenIndex, totalLines, fallback)
11802
12084
  j++;
11803
12085
  }
11804
12086
  if (content[j] !== "{") return fallback;
11805
- return findMatchingBraceEndLine(content, j, totalLines, lineIndex, "#");
12087
+ return findMatchingBraceEndLine(content, j, totalLines, lineIndex, "#", { backtickQuote: true });
11806
12088
  }
11807
12089
  function callEndLine(content, lineIndex, parenIndex, fallback) {
11808
12090
  const close = matchingParenIndex(content, parenIndex);
@@ -12231,25 +12513,53 @@ function stripBashComment(line) {
12231
12513
  }
12232
12514
  return line;
12233
12515
  }
12234
- function findHeredocOpeners(line) {
12516
+ function maskArithmeticSpans(line, carryDepth) {
12517
+ const chars = line.split("");
12518
+ let depth = carryDepth;
12519
+ for (let i = 0; i < chars.length; i++) {
12520
+ if (depth > 0) {
12521
+ if (chars[i] === "(") depth++;
12522
+ else if (chars[i] === ")") depth--;
12523
+ chars[i] = " ";
12524
+ continue;
12525
+ }
12526
+ const isDollar = chars[i] === "$" && chars[i + 1] === "(" && chars[i + 2] === "(";
12527
+ const isBare = chars[i] === "(" && chars[i + 1] === "(";
12528
+ if (!isDollar && !isBare) continue;
12529
+ if (isInsideStringLiteral(line, i)) continue;
12530
+ const open = isDollar ? i + 1 : i;
12531
+ for (let j = open; j < chars.length; j++) {
12532
+ if (chars[j] === "(") depth++;
12533
+ else if (chars[j] === ")") depth--;
12534
+ chars[j] = " ";
12535
+ i = j;
12536
+ if (depth === 0) break;
12537
+ }
12538
+ }
12539
+ return { masked: chars.join(""), depth };
12540
+ }
12541
+ function findHeredocOpeners(line, carryDepth) {
12235
12542
  const terminators = [];
12543
+ const { masked, depth } = maskArithmeticSpans(line, carryDepth);
12236
12544
  HEREDOC_RE.lastIndex = 0;
12237
12545
  let m;
12238
- while ((m = HEREDOC_RE.exec(line)) !== null) {
12546
+ while ((m = HEREDOC_RE.exec(masked)) !== null) {
12239
12547
  if (isInsideStringLiteral(line, m.index)) continue;
12240
12548
  const terminator = m[2] ?? "";
12241
12549
  if (terminator) terminators.push(terminator);
12242
12550
  }
12243
- return terminators;
12551
+ return { terminators, depth };
12244
12552
  }
12245
12553
  function extractBash(content, filePath) {
12246
12554
  const symbols = [];
12247
12555
  const lines2 = content.split(/\r?\n/);
12248
12556
  const heredocs = [];
12557
+ let arithmeticDepth = 0;
12249
12558
  let braceDepth = 0;
12250
12559
  let inFunction = false;
12251
12560
  let functionBraceDepth = 0;
12252
12561
  let awaitingFunctionBrace = false;
12562
+ let openFunctionIndex = null;
12253
12563
  for (let i = 0; i < lines2.length; i++) {
12254
12564
  const rawLine = lines2[i] ?? "";
12255
12565
  const lineNum = i + 1;
@@ -12259,7 +12569,9 @@ function extractBash(content, filePath) {
12259
12569
  }
12260
12570
  const noComment = stripBashComment(rawLine);
12261
12571
  const stripped = noComment.trim();
12262
- heredocs.push(...findHeredocOpeners(noComment));
12572
+ const opened = findHeredocOpeners(noComment, arithmeticDepth);
12573
+ arithmeticDepth = opened.depth;
12574
+ heredocs.push(...opened.terminators);
12263
12575
  if (!stripped) continue;
12264
12576
  if (!inFunction && !awaitingFunctionBrace && braceDepth === 0) {
12265
12577
  const kwMatch = FUNC_KEYWORD_RE.exec(stripped);
@@ -12267,7 +12579,9 @@ function extractBash(content, filePath) {
12267
12579
  const funcMatch = kwMatch ?? posixMatch;
12268
12580
  if (funcMatch) {
12269
12581
  const fname = funcMatch[1] ?? "";
12582
+ let pushedIndex = null;
12270
12583
  if (fname && symbols.length < MAX_SYMBOLS4) {
12584
+ pushedIndex = symbols.length;
12271
12585
  symbols.push(makeLineSymbol(filePath, fname, "function", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
12272
12586
  }
12273
12587
  if (fname) {
@@ -12279,9 +12593,11 @@ function extractBash(content, filePath) {
12279
12593
  } else if (openCount > closeCount) {
12280
12594
  inFunction = true;
12281
12595
  functionBraceDepth = braceDepth;
12596
+ openFunctionIndex = pushedIndex;
12282
12597
  }
12283
12598
  } else {
12284
12599
  awaitingFunctionBrace = true;
12600
+ openFunctionIndex = pushedIndex;
12285
12601
  }
12286
12602
  }
12287
12603
  } else {
@@ -12302,6 +12618,13 @@ function extractBash(content, filePath) {
12302
12618
  braceDepth += (braceLine.match(/\{/g) ?? []).length - (braceLine.match(/\}/g) ?? []).length;
12303
12619
  if (inFunction && braceDepth <= functionBraceDepth) {
12304
12620
  inFunction = false;
12621
+ if (openFunctionIndex !== null) {
12622
+ const open = symbols[openFunctionIndex];
12623
+ if (open !== void 0 && lineNum > open.lineStart) {
12624
+ symbols[openFunctionIndex] = { ...open, lineEnd: lineNum, body: lines2.slice(open.lineStart - 1, lineNum).join("\n") };
12625
+ }
12626
+ openFunctionIndex = null;
12627
+ }
12305
12628
  }
12306
12629
  }
12307
12630
  return symbols;
@@ -12886,7 +13209,7 @@ var TRIGGER_RE2 = new RegExp(
12886
13209
  var RETURN_TYPE = "(?:[A-Za-z_][A-Za-z0-9_.<>?,\\[\\] ]*[ \\t]+)";
12887
13210
  var STATEMENT_KEYWORD_GUARD = "(?!(?:return|throw|new|yield|else|do|try|finally|break|continue)\\b)";
12888
13211
  var METHOD_RE3 = new RegExp(
12889
- `^[ \\t]*(?:@${IDENT3}(?:\\([^\\n)]*\\))?[ \\t]+)*(?:(?:${MODIFIER}[ \\t]+)+(${RETURN_TYPE})?|(?:${MODIFIER}[ \\t]+)*${STATEMENT_KEYWORD_GUARD}(${RETURN_TYPE}))(${IDENT3})[ \\t]*\\([^;{}]*\\)[ \\t\\r\\n]*(?:\\{|;)`,
13212
+ `^[ \\t]*(?:@${IDENT3}(?:\\([^\\n)]*\\))?[ \\t]+)*(?=[^\\n]*\\()(?:(?:${MODIFIER}[ \\t]+)+(${RETURN_TYPE})?|(?:${MODIFIER}[ \\t]+)*${STATEMENT_KEYWORD_GUARD}(${RETURN_TYPE}))(${IDENT3})[ \\t]*\\([^;{}]*\\)[ \\t\\r\\n]*(?:\\{|;)`,
12890
13213
  "gm"
12891
13214
  );
12892
13215
  var CONTROL_NAMES = /* @__PURE__ */ new Set([
@@ -13646,21 +13969,22 @@ function maskSpans(content, spans) {
13646
13969
  }
13647
13970
  return chars.join("");
13648
13971
  }
13649
- function componentSymbol(filePath, name, kind, totalLines) {
13650
- return { filePath, name, kind, lineStart: 1, lineEnd: totalLines, body: "", docstring: "", parent: "" };
13972
+ function componentSymbols(filePath, name, kind, totalLines) {
13973
+ if (totalLines < 1) return [];
13974
+ return [{ filePath, name, kind, lineStart: 1, lineEnd: totalLines, body: "", docstring: "", parent: "" }];
13651
13975
  }
13652
13976
  function extractVue(content, filePath) {
13653
13977
  const totalLines = countContentLines(content);
13654
13978
  const lineIndex = buildLineIndex(content);
13655
13979
  const name = componentName(filePath);
13656
- const symbols = [componentSymbol(filePath, name, "vue_component", totalLines)];
13980
+ const symbols = componentSymbols(filePath, name, "vue_component", totalLines);
13657
13981
  const refs = [];
13658
13982
  for (const block of extractTagBlocks(content, lineIndex, "script")) {
13659
- symbols.push(...extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
13983
+ pushAll(symbols, extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
13660
13984
  }
13661
13985
  for (const block of extractTagBlocks(content, lineIndex, "template")) {
13662
13986
  const markup = stripXmlComments(block.content);
13663
- refs.push(...extractComponentRefs(markup, filePath, block.contentStartLine, true));
13987
+ pushAll(refs, extractComponentRefs(markup, filePath, block.contentStartLine, true));
13664
13988
  }
13665
13989
  return finalize(symbols, refs);
13666
13990
  }
@@ -13668,11 +13992,11 @@ function extractSvelte(content, filePath) {
13668
13992
  const totalLines = countContentLines(content);
13669
13993
  const lineIndex = buildLineIndex(content);
13670
13994
  const name = componentName(filePath);
13671
- const symbols = [componentSymbol(filePath, name, "svelte_component", totalLines)];
13995
+ const symbols = componentSymbols(filePath, name, "svelte_component", totalLines);
13672
13996
  const refs = [];
13673
13997
  const scriptBlocks = extractTagBlocks(content, lineIndex, "script");
13674
13998
  for (const block of scriptBlocks) {
13675
- symbols.push(...extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
13999
+ pushAll(symbols, extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
13676
14000
  }
13677
14001
  const styleBlocks = extractTagBlocks(content, lineIndex, "style");
13678
14002
  const spans = [...scriptBlocks, ...styleBlocks].map((b) => [
@@ -13680,7 +14004,7 @@ function extractSvelte(content, filePath) {
13680
14004
  b.matchEnd
13681
14005
  ]);
13682
14006
  const markup = stripXmlComments(maskSpans(content, spans));
13683
- refs.push(...extractComponentRefs(markup, filePath, 1, true));
14007
+ pushAll(refs, extractComponentRefs(markup, filePath, 1, true));
13684
14008
  return finalize(symbols, refs);
13685
14009
  }
13686
14010
  function detectAstroFrontmatter(content) {
@@ -13701,7 +14025,7 @@ function extractAstro(content, filePath) {
13701
14025
  const totalLines = countContentLines(content);
13702
14026
  const lineIndex = buildLineIndex(content);
13703
14027
  const name = componentName(filePath);
13704
- const symbols = [componentSymbol(filePath, name, "astro_component", totalLines)];
14028
+ const symbols = componentSymbols(filePath, name, "astro_component", totalLines);
13705
14029
  const refs = [];
13706
14030
  const lines2 = content.split("\n");
13707
14031
  const fm = detectAstroFrontmatter(content);
@@ -13709,7 +14033,7 @@ function extractAstro(content, filePath) {
13709
14033
  if (fm) {
13710
14034
  const frontmatterContent = lines2.slice(fm.openLine + 1, fm.closeLine).join("\n");
13711
14035
  const contentStartLine = fm.openLine + 2;
13712
- symbols.push(...extractTopLevelDeclarations(frontmatterContent, filePath, contentStartLine));
14036
+ pushAll(symbols, extractTopLevelDeclarations(frontmatterContent, filePath, contentStartLine));
13713
14037
  const fenceStartOffset = lineIndex[fm.openLine] ?? 0;
13714
14038
  const fenceEndOffset = lineIndex[fm.closeLine + 1] ?? content.length;
13715
14039
  spans.push([fenceStartOffset, fenceEndOffset]);
@@ -13717,7 +14041,7 @@ function extractAstro(content, filePath) {
13717
14041
  const styleBlocks = extractTagBlocks(content, lineIndex, "style");
13718
14042
  for (const block of styleBlocks) spans.push([block.matchStart, block.matchEnd]);
13719
14043
  const markup = stripXmlComments(maskSpans(content, spans));
13720
- refs.push(...extractComponentRefs(markup, filePath, 1, false));
14044
+ pushAll(refs, extractComponentRefs(markup, filePath, 1, false));
13721
14045
  return finalize(symbols, refs);
13722
14046
  }
13723
14047
 
@@ -15462,8 +15786,8 @@ var NO_TREE_SITTER_EXTRACTORS = {
15462
15786
  toml: extractTomlSymbols,
15463
15787
  css: extractCssSymbols,
15464
15788
  dockerfile: extractDockerfileSymbols,
15465
- csharp: (content, filePath) => extractCsharp(content, filePath).symbols,
15466
- php: (content, filePath) => extractPhp(content, filePath).symbols,
15789
+ csharp: (content, filePath) => assignBraceBlockSpans(extractCsharp(content, filePath).symbols, content, "//"),
15790
+ php: (content, filePath) => assignBraceBlockSpans(extractPhp(content, filePath).symbols, content, "//"),
15467
15791
  html: (content, filePath) => {
15468
15792
  const r = extractHtml(content, filePath);
15469
15793
  return [...r.symbols, ...sectionsToHeadingSymbols(r.sections, filePath)];
@@ -15472,13 +15796,13 @@ var NO_TREE_SITTER_EXTRACTORS = {
15472
15796
  const r = extractLiquid(content, filePath);
15473
15797
  return [...r.symbols, ...sectionsToHeadingSymbols(r.sections, filePath)];
15474
15798
  },
15475
- kotlin: (content, filePath) => extractKotlin(content, filePath).symbols,
15476
- swift: (content, filePath) => extractSwift(content, filePath).symbols,
15477
- scala: (content, filePath) => extractScala(content, filePath).symbols,
15799
+ kotlin: (content, filePath) => assignBraceBlockSpans(extractKotlin(content, filePath).symbols, content, "//"),
15800
+ swift: (content, filePath) => assignBraceBlockSpans(extractSwift(content, filePath).symbols, content, "//"),
15801
+ scala: (content, filePath) => assignBraceBlockSpans(extractScala(content, filePath).symbols, content, "//"),
15478
15802
  lua: (content, filePath) => extractLua(content, filePath).symbols,
15479
15803
  elixir: (content, filePath) => extractElixir(content, filePath).symbols,
15480
- dart: (content, filePath) => extractDart(content, filePath).symbols,
15481
- zig: (content, filePath) => extractZig(content, filePath).symbols,
15804
+ dart: (content, filePath) => assignBraceBlockSpans(extractDart(content, filePath).symbols, content, "//"),
15805
+ zig: (content, filePath) => assignBraceBlockSpans(extractZig(content, filePath).symbols, content, "//"),
15482
15806
  r: (content, filePath) => extractR(content, filePath).symbols,
15483
15807
  graphql: (content, filePath) => extractGraphql(content, filePath).symbols,
15484
15808
  sql: extractSql,
@@ -15486,7 +15810,7 @@ var NO_TREE_SITTER_EXTRACTORS = {
15486
15810
  makefile: extractMakefile,
15487
15811
  proto: (content, filePath) => extractProto(content, filePath).symbols,
15488
15812
  terraform: extractTerraform,
15489
- powershell: (content, filePath) => extractPowershell(content, filePath).symbols,
15813
+ powershell: (content, filePath) => assignBraceBlockSpans(extractPowershell(content, filePath).symbols, content, "#"),
15490
15814
  apex: (content, filePath) => extractApex(content, filePath).symbols,
15491
15815
  salesforce_metadata: (content, filePath) => extractSalesforceMetadata(content, filePath).symbols,
15492
15816
  env_file: extractEnv,
@@ -15567,7 +15891,7 @@ function writeParseResult(filePath, content, result, dbPath) {
15567
15891
  insRef.run(r.filePath, r.name, r.line, r.col, r.context);
15568
15892
  }
15569
15893
  });
15570
- writeAll();
15894
+ writeAll.immediate();
15571
15895
  }
15572
15896
  function indexFileSync(filePath, dbPath = globalDbPath(), preReadBytes) {
15573
15897
  const ixCfg = loadConfig().indexing;
@@ -15625,7 +15949,23 @@ function indexedPathSpellingIsStale(storedPath, absPath) {
15625
15949
  } catch {
15626
15950
  return false;
15627
15951
  }
15628
- return real !== stored && foldPath(real) === foldPath(stored);
15952
+ if (real === stored) return false;
15953
+ if (foldPath(real) !== foldPath(stored)) return false;
15954
+ const storedSegments = stored.split("/");
15955
+ const realSegments = real.split("/");
15956
+ if (storedSegments.length !== realSegments.length) return real !== stored;
15957
+ const storedBase = storedSegments[storedSegments.length - 1];
15958
+ const realBase = realSegments[realSegments.length - 1];
15959
+ if (storedBase !== realBase) return true;
15960
+ for (let i = storedSegments.length - 2; i >= 0; i--) {
15961
+ if (storedSegments[i] !== realSegments[i]) {
15962
+ if (storedSegments.slice(0, i + 1).join("/") === candidate.split("/").slice(0, i + 1).join("/")) {
15963
+ continue;
15964
+ }
15965
+ return true;
15966
+ }
15967
+ }
15968
+ return false;
15629
15969
  }
15630
15970
  function isEmbedFresh(storedEmbedSha, sha, embeddingsEnabled, depsAvailable) {
15631
15971
  if (storedEmbedSha === void 0) return false;
@@ -15731,11 +16071,13 @@ function removeFileFromIndex(db, filePath) {
15731
16071
  deleteFileRows(db, filePath);
15732
16072
  deleteFileEmbeddings(db, filePath);
15733
16073
  });
15734
- tx();
16074
+ tx.immediate();
15735
16075
  }
15736
16076
  function isTooShallowToPrune(rootPrefix) {
15737
- const segments = rootPrefix.split("/").filter((s) => s.length > 0 && !/^[a-z]:$/i.test(s));
16077
+ const normalized = normalizePath(rootPrefix);
16078
+ const segments = normalized.split("/").filter((s) => s.length > 0 && !/^[a-z]:$/i.test(s));
15738
16079
  if (segments.length === 2 && segments[0]?.toLowerCase() === "mnt" && /^[a-z]$/i.test(segments[1] ?? "")) return true;
16080
+ if (normalized.startsWith("//") && segments.length <= 2) return true;
15739
16081
  return segments.length === 0;
15740
16082
  }
15741
16083
  function foldedBounds(rootPrefix) {
@@ -15760,7 +16102,8 @@ function findDeletablePaths(rootPrefix, dbPath) {
15760
16102
  for (const p of foldedPathsUnderRoot(rootPrefix, dbPath)) {
15761
16103
  let stillExists;
15762
16104
  try {
15763
- stillExists = fs22.statSync(p, { throwIfNoEntry: false }) !== void 0;
16105
+ const st = fs22.statSync(p, { throwIfNoEntry: false });
16106
+ stillExists = st !== void 0 && st.isFile();
15764
16107
  } catch {
15765
16108
  continue;
15766
16109
  }
@@ -15783,14 +16126,23 @@ function removeFilesBestEffort(db, paths) {
15783
16126
  return removed;
15784
16127
  }
15785
16128
  function removeDeletedFilesBestEffort(db, paths) {
15786
- const stillGone = paths.filter((p) => {
16129
+ const removed = [];
16130
+ for (const p of paths) {
16131
+ let gone;
15787
16132
  try {
15788
- return fs22.statSync(p, { throwIfNoEntry: false }) === void 0;
16133
+ const st = fs22.statSync(p, { throwIfNoEntry: false });
16134
+ gone = st === void 0 || !st.isFile();
15789
16135
  } catch {
15790
- return false;
16136
+ continue;
15791
16137
  }
15792
- });
15793
- return removeFilesBestEffort(db, stillGone);
16138
+ if (!gone) continue;
16139
+ try {
16140
+ removeFileFromIndex(db, p);
16141
+ removed.push(p);
16142
+ } catch {
16143
+ }
16144
+ }
16145
+ return removed;
15794
16146
  }
15795
16147
  function pruneDeletedFiles(rootPrefix, dbPath = globalDbPath()) {
15796
16148
  if (isTooShallowToPrune(rootPrefix)) return 0;
@@ -15868,7 +16220,7 @@ function sweepKnownRoots(dbPath = globalDbPath(), opts) {
15868
16220
  if (isTooShallowToPrune(root)) continue;
15869
16221
  let reachable;
15870
16222
  try {
15871
- reachable = fs22.existsSync(root);
16223
+ reachable = fs22.statSync(root, { throwIfNoEntry: false })?.isDirectory() === true;
15872
16224
  } catch {
15873
16225
  reachable = false;
15874
16226
  }
@@ -15997,7 +16349,7 @@ function bumpRetryCount(dbPath, absPath) {
15997
16349
  db.prepare("INSERT INTO files (path, retry_count) VALUES (?, 1)").run(normalized);
15998
16350
  return 1;
15999
16351
  });
16000
- return tx();
16352
+ return tx.immediate();
16001
16353
  }
16002
16354
  function clearRetryCount(dbPath, absPath) {
16003
16355
  try {
@@ -16117,14 +16469,21 @@ var INDEX_FAILED = /* @__PURE__ */ Symbol("indexFailed");
16117
16469
  var inFlightEmbeddings = /* @__PURE__ */ new Map();
16118
16470
  var activeEmbedSlots = 0;
16119
16471
  var embedSlotWaiters = [];
16120
- function releaseEmbedSlot() {
16121
- activeEmbedSlots -= 1;
16122
- const next = embedSlotWaiters.shift();
16123
- if (next) next();
16472
+ var embedSlotEpoch = 0;
16473
+ function makeReleaseEmbedSlot() {
16474
+ const epoch = embedSlotEpoch;
16475
+ return () => {
16476
+ if (epoch !== embedSlotEpoch) return;
16477
+ activeEmbedSlots -= 1;
16478
+ const next = embedSlotWaiters.shift();
16479
+ if (next) next();
16480
+ };
16124
16481
  }
16125
16482
  registerReset(() => {
16483
+ embedSlotEpoch += 1;
16126
16484
  activeEmbedSlots = 0;
16127
16485
  embedSlotWaiters.length = 0;
16486
+ inFlightEmbeddings.clear();
16128
16487
  });
16129
16488
  function embedFileSerialized(absPath, dbPath, sha) {
16130
16489
  const key = foldPath(absPath);
@@ -16138,7 +16497,8 @@ function embedFileSerialized(absPath, dbPath, sha) {
16138
16497
  const limit = loadConfig().worker.max_pool_workers ?? 4;
16139
16498
  const dispatchEmbed = () => {
16140
16499
  const result = indexFileEmbeddings(absPath, dbPath, sha, onEmbedError);
16141
- result.then(releaseEmbedSlot, releaseEmbedSlot);
16500
+ const release = makeReleaseEmbedSlot();
16501
+ result.then(release, release);
16142
16502
  return result;
16143
16503
  };
16144
16504
  const runEmbed = () => {
@@ -16555,8 +16915,12 @@ var KNOWN_ROOTS_SWEEP_INTERVAL_MS = 24 * 60 * 60 * 1e3;
16555
16915
  async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
16556
16916
  let lastSnapshotCleanupMs = 0;
16557
16917
  let lastKnownRootsSweepMs = 0;
16918
+ let ownedPidFile = false;
16558
16919
  while (!shouldStop()) {
16559
16920
  if (!fs23.existsSync(dir)) break;
16921
+ const pidOwner = readPidFile(dir);
16922
+ if (pidOwner === process.pid) ownedPidFile = true;
16923
+ else if (ownedPidFile && pidOwner !== null) break;
16560
16924
  try {
16561
16925
  drainOnce(dir);
16562
16926
  } catch {