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-chunk-7FWBSPCT.mjs";
92
+ } from "./token-goat-chunk-V465YKOR.mjs";
91
93
  import {
92
94
  registerReset
93
95
  } from "./token-goat-chunk-AO2QD2AG.mjs";
@@ -159,7 +161,12 @@ function decodeZipEntry(entries, entryPath) {
159
161
  async function parseOoxmlPart(xmlText2) {
160
162
  const fxp = await loadXmlParser();
161
163
  if (!fxp) throw new Error("fast-xml-parser is not installed; run `npm install fast-xml-parser` to enable this command");
162
- const parser = new fxp.XMLParser({ ignoreAttributes: false, preserveOrder: false, trimValues: false });
164
+ const parser = new fxp.XMLParser({
165
+ ignoreAttributes: false,
166
+ preserveOrder: false,
167
+ trimValues: false,
168
+ parseTagValue: false
169
+ });
163
170
  return parser.parse(xmlText2);
164
171
  }
165
172
  function pushTextValue(runs, val) {
@@ -205,7 +212,7 @@ function collectElements(node, tag) {
205
212
  const obj = n;
206
213
  for (const [key, val] of Object.entries(obj)) {
207
214
  if (key === tag) {
208
- if (Array.isArray(val)) out.push(...val);
215
+ if (Array.isArray(val)) pushAll(out, val);
209
216
  else out.push(val);
210
217
  } else if (val !== null && typeof val === "object") {
211
218
  walk(val);
@@ -2288,28 +2295,257 @@ function formatCsvProfile(profiles) {
2288
2295
  }).join("\n\n");
2289
2296
  }
2290
2297
 
2291
- // src/xlsx_extract.ts
2292
- var loadExcelJs = createLazyModuleLoader(async () => {
2293
- const mod = await import("exceljs");
2294
- return mod.default ?? mod;
2295
- }, "xlsx reading disabled (exceljs package unavailable)");
2296
- async function requireExcelJs() {
2297
- const mod = await loadExcelJs();
2298
- if (!mod) throw new Error("exceljs is not installed; run `npm install exceljs` to enable this command");
2299
- return mod;
2298
+ // src/xlsx_reader.ts
2299
+ var EMPTY_CELL = Object.freeze({ value: null, text: "" });
2300
+ function asArray(val) {
2301
+ if (val === void 0 || val === null) return [];
2302
+ if (Array.isArray(val)) return val.filter((v) => v !== null && typeof v === "object");
2303
+ if (typeof val === "object") return [val];
2304
+ return [];
2305
+ }
2306
+ function textOf(node) {
2307
+ if (node === void 0 || node === null) return "";
2308
+ if (typeof node === "string") return node;
2309
+ if (typeof node === "number" || typeof node === "boolean") return String(node);
2310
+ if (Array.isArray(node)) return node.map(textOf).join("");
2311
+ const t = node["#text"];
2312
+ return t === void 0 ? "" : String(t);
2313
+ }
2314
+ function attr(node, name) {
2315
+ if (node === void 0) return void 0;
2316
+ const v = node[`@_${name}`];
2317
+ return v === void 0 || v === null ? void 0 : String(v);
2318
+ }
2319
+ function isTruthyAttr(v) {
2320
+ return v === "1" || v === "true";
2321
+ }
2322
+ function refToColumn(ref2) {
2323
+ let n = 0;
2324
+ for (const ch of ref2) {
2325
+ const code = ch.toUpperCase().charCodeAt(0);
2326
+ if (code < 65 || code > 90) break;
2327
+ n = n * 26 + (code - 64);
2328
+ }
2329
+ return n;
2300
2330
  }
2301
- async function loadWorkbook(filePath) {
2302
- const ExcelJS = await requireExcelJs();
2303
- const wb = new ExcelJS.Workbook();
2304
- try {
2305
- await wb.xlsx.readFile(filePath);
2306
- } catch (err) {
2307
- const msg = extractErrorMessage(err);
2308
- if (msg.startsWith("File not found:")) throw err;
2309
- throw new Error(`not a valid .xlsx file: ${filePath}`, { cause: err });
2331
+ function refToRow(ref2) {
2332
+ const m = /(\d+)\s*$/.exec(ref2);
2333
+ return m ? parseInt(m[1], 10) : 0;
2334
+ }
2335
+ var BUILTIN_DATE_FORMAT_IDS = /* @__PURE__ */ new Set([14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 46, 47]);
2336
+ function formatCodeIsDate(formatCode) {
2337
+ const stripped = formatCode.replace(/"[^"]*"/g, "").replace(/\[[^\]]*\]/g, "").replace(/\\./g, "");
2338
+ return /[ymdhs]/i.test(stripped);
2339
+ }
2340
+ function serialToDate(serial, date1904) {
2341
+ if (date1904) return new Date(Date.UTC(1904, 0, 1) + Math.round(serial * 864e5));
2342
+ const adjusted = serial >= 61 ? serial - 1 : serial;
2343
+ return new Date(Date.UTC(1899, 11, 31) + Math.round(adjusted * 864e5));
2344
+ }
2345
+ function numberToText(n) {
2346
+ return String(n);
2347
+ }
2348
+ function parseStyles(xml, parsed) {
2349
+ if (xml === null || parsed === null || typeof parsed !== "object") return { dateStyles: [] };
2350
+ const sheet = parsed["styleSheet"];
2351
+ if (sheet === void 0 || sheet === null || typeof sheet !== "object") return { dateStyles: [] };
2352
+ const root = sheet;
2353
+ const customDateFormats = /* @__PURE__ */ new Set();
2354
+ for (const fmt of asArray(root["numFmts"]?.["numFmt"])) {
2355
+ const id = Number(attr(fmt, "numFmtId"));
2356
+ const code = attr(fmt, "formatCode");
2357
+ if (Number.isFinite(id) && code !== void 0 && formatCodeIsDate(code)) customDateFormats.add(id);
2358
+ }
2359
+ const dateStyles = [];
2360
+ for (const xf of asArray(root["cellXfs"]?.["xf"])) {
2361
+ const id = Number(attr(xf, "numFmtId") ?? "0");
2362
+ dateStyles.push(Number.isFinite(id) && (BUILTIN_DATE_FORMAT_IDS.has(id) || customDateFormats.has(id)));
2363
+ }
2364
+ return { dateStyles };
2365
+ }
2366
+ function parseSharedStrings(parsed) {
2367
+ if (parsed === null || typeof parsed !== "object") return [];
2368
+ const sst = parsed["sst"];
2369
+ if (sst === void 0 || sst === null || typeof sst !== "object") return [];
2370
+ return asArray(sst["si"]).map((si) => {
2371
+ if (si["t"] !== void 0) return textOf(si["t"]);
2372
+ const runs = asArray(si["r"]);
2373
+ if (runs.length > 0) return runs.map((r) => textOf(r["t"])).join("");
2374
+ return "";
2375
+ });
2376
+ }
2377
+ function parseWorkbookRels(parsed) {
2378
+ const out = /* @__PURE__ */ new Map();
2379
+ if (parsed === null || typeof parsed !== "object") return out;
2380
+ const rels = parsed["Relationships"];
2381
+ if (rels === void 0 || rels === null || typeof rels !== "object") return out;
2382
+ for (const rel of asArray(rels["Relationship"])) {
2383
+ const id = attr(rel, "Id");
2384
+ const target = attr(rel, "Target");
2385
+ if (id === void 0 || target === void 0) continue;
2386
+ const normalized = target.startsWith("/") ? target.slice(1) : `xl/${target.replace(/^\.\//, "")}`;
2387
+ out.set(id, normalized);
2388
+ }
2389
+ return out;
2390
+ }
2391
+ function buildCell(c, shared, styles, date1904) {
2392
+ const type = attr(c, "t") ?? "n";
2393
+ const styleIdx = Number(attr(c, "s") ?? "-1");
2394
+ const isDateStyle = Number.isInteger(styleIdx) && styleIdx >= 0 && styles.dateStyles[styleIdx] === true;
2395
+ const hasV = c["v"] !== void 0;
2396
+ const hasIs = c["is"] !== void 0;
2397
+ const fNode = c["f"];
2398
+ const formula = fNode === void 0 ? "" : textOf(fNode);
2399
+ if (!hasV && !hasIs && formula === "") return null;
2400
+ let raw;
2401
+ let text;
2402
+ if (type === "s") {
2403
+ const idx = Number(textOf(c["v"]));
2404
+ const s = Number.isInteger(idx) ? shared[idx] ?? "" : "";
2405
+ raw = s;
2406
+ text = s;
2407
+ } else if (type === "inlineStr") {
2408
+ const isNode = c["is"];
2409
+ const s = isNode !== void 0 && isNode !== null && typeof isNode === "object" ? (() => {
2410
+ const node = isNode;
2411
+ if (node["t"] !== void 0) return textOf(node["t"]);
2412
+ return asArray(node["r"]).map((r) => textOf(r["t"])).join("");
2413
+ })() : textOf(isNode);
2414
+ raw = s;
2415
+ text = s;
2416
+ } else if (type === "str") {
2417
+ const s = textOf(c["v"]);
2418
+ raw = s;
2419
+ text = s;
2420
+ } else if (type === "b") {
2421
+ const b = textOf(c["v"]).trim() === "1";
2422
+ raw = b;
2423
+ text = b ? "TRUE" : "FALSE";
2424
+ } else if (type === "e") {
2425
+ const e = textOf(c["v"]);
2426
+ raw = { error: e };
2427
+ text = e;
2428
+ } else {
2429
+ const n = Number(textOf(c["v"]));
2430
+ if (!Number.isFinite(n)) {
2431
+ raw = null;
2432
+ text = "";
2433
+ } else if (isDateStyle) {
2434
+ const d = serialToDate(n, date1904);
2435
+ raw = d;
2436
+ text = d.toISOString();
2437
+ } else {
2438
+ raw = n;
2439
+ text = numberToText(n);
2440
+ }
2441
+ }
2442
+ if (formula !== "") return { value: { formula, result: raw }, text, formula };
2443
+ return { value: raw, text };
2444
+ }
2445
+ function parseSheet(parsed, shared, styles, date1904) {
2446
+ const cells = /* @__PURE__ */ new Map();
2447
+ let rowCount = 0;
2448
+ let columnCount = 0;
2449
+ let populatedRows = 0;
2450
+ if (parsed === null || typeof parsed !== "object") return { cells, rowCount, columnCount, populatedRows };
2451
+ const ws = parsed["worksheet"];
2452
+ if (ws === void 0 || ws === null || typeof ws !== "object") return { cells, rowCount, columnCount, populatedRows };
2453
+ const sheetData = ws["sheetData"];
2454
+ if (sheetData === void 0 || sheetData === null || typeof sheetData !== "object") {
2455
+ return { cells, rowCount, columnCount, populatedRows };
2456
+ }
2457
+ let fallbackRow = 0;
2458
+ for (const row of asArray(sheetData["row"])) {
2459
+ const declaredRow = Number(attr(row, "r"));
2460
+ const rowIdx = Number.isInteger(declaredRow) && declaredRow > 0 ? declaredRow : fallbackRow + 1;
2461
+ fallbackRow = rowIdx;
2462
+ let fallbackCol = 0;
2463
+ const rowCells = /* @__PURE__ */ new Map();
2464
+ for (const c of asArray(row["c"])) {
2465
+ const ref2 = attr(c, "r");
2466
+ const declaredCol = ref2 === void 0 ? 0 : refToColumn(ref2);
2467
+ const colIdx = declaredCol > 0 ? declaredCol : fallbackCol + 1;
2468
+ fallbackCol = colIdx;
2469
+ const cell = buildCell(c, shared, styles, date1904);
2470
+ if (cell === null) continue;
2471
+ rowCells.set(colIdx, cell);
2472
+ if (colIdx > columnCount) columnCount = colIdx;
2473
+ }
2474
+ if (rowCells.size === 0) continue;
2475
+ cells.set(rowIdx, rowCells);
2476
+ populatedRows++;
2477
+ if (rowIdx > rowCount) rowCount = rowIdx;
2478
+ }
2479
+ return { cells, rowCount, columnCount, populatedRows };
2480
+ }
2481
+ function makeWorksheet(name, data) {
2482
+ function getRow(r) {
2483
+ const rowCells = data.cells.get(r);
2484
+ return {
2485
+ get values() {
2486
+ const out = [];
2487
+ if (rowCells !== void 0) for (const [col, cell] of rowCells) out[col] = cell.value;
2488
+ return out;
2489
+ },
2490
+ eachCell(opts, cb) {
2491
+ if (opts.includeEmpty) {
2492
+ let maxCol = 0;
2493
+ if (rowCells !== void 0) {
2494
+ for (const c of rowCells.keys()) if (c > maxCol) maxCol = c;
2495
+ }
2496
+ for (let c = 1; c <= maxCol; c++) cb(rowCells?.get(c) ?? EMPTY_CELL, c);
2497
+ return;
2498
+ }
2499
+ if (rowCells === void 0) return;
2500
+ for (const col of [...rowCells.keys()].sort((a, b) => a - b)) cb(rowCells.get(col), col);
2501
+ }
2502
+ };
2503
+ }
2504
+ return {
2505
+ name,
2506
+ rowCount: data.rowCount,
2507
+ columnCount: data.columnCount,
2508
+ actualRowCount: data.populatedRows,
2509
+ getRow,
2510
+ getCell(addr) {
2511
+ return data.cells.get(refToRow(addr))?.get(refToColumn(addr)) ?? EMPTY_CELL;
2512
+ }
2513
+ };
2514
+ }
2515
+ async function readXlsxWorkbook(filePath) {
2516
+ const entries = await readOoxmlZip(filePath, ".xlsx");
2517
+ const workbookXml = decodeZipEntry(entries, "xl/workbook.xml");
2518
+ if (workbookXml === null) throw new Error(`not a valid .xlsx file: ${filePath}`);
2519
+ const workbookRoot = await parseOoxmlPart(workbookXml);
2520
+ const wbNode = workbookRoot?.["workbook"];
2521
+ if (wbNode === void 0 || wbNode === null || typeof wbNode !== "object") {
2522
+ throw new Error(`not a valid .xlsx file: ${filePath}`);
2523
+ }
2524
+ const wb = wbNode;
2525
+ const date1904 = isTruthyAttr(attr(wb["workbookPr"], "date1904"));
2526
+ const relsXml = decodeZipEntry(entries, "xl/_rels/workbook.xml.rels");
2527
+ const rels = parseWorkbookRels(relsXml === null ? null : await parseOoxmlPart(relsXml));
2528
+ const sharedXml = decodeZipEntry(entries, "xl/sharedStrings.xml");
2529
+ const shared = sharedXml === null ? [] : parseSharedStrings(await parseOoxmlPart(sharedXml));
2530
+ const stylesXml = decodeZipEntry(entries, "xl/styles.xml");
2531
+ const styles = parseStyles(stylesXml, stylesXml === null ? null : await parseOoxmlPart(stylesXml));
2532
+ const worksheets = [];
2533
+ for (const sheet of asArray(wb["sheets"]?.["sheet"])) {
2534
+ const name = attr(sheet, "name") ?? "";
2535
+ const rid = attr(sheet, "r:id") ?? attr(sheet, "relationshipId");
2536
+ const partPath = rid === void 0 ? void 0 : rels.get(rid);
2537
+ const sheetXml = partPath === void 0 ? null : decodeZipEntry(entries, partPath);
2538
+ const data = sheetXml === null ? { cells: /* @__PURE__ */ new Map(), rowCount: 0, columnCount: 0, populatedRows: 0 } : parseSheet(await parseOoxmlPart(sheetXml), shared, styles, date1904);
2539
+ worksheets.push(makeWorksheet(name, data));
2310
2540
  }
2311
- return wb;
2541
+ return {
2542
+ worksheets,
2543
+ getWorksheet: (name) => worksheets.find((ws) => ws.name === name)
2544
+ };
2312
2545
  }
2546
+
2547
+ // src/xlsx_extract.ts
2548
+ var loadWorkbook = readXlsxWorkbook;
2313
2549
  function requireSheet(wb, sheetName) {
2314
2550
  const ws = wb.getWorksheet(sheetName);
2315
2551
  if (ws === void 0) {
@@ -3067,7 +3303,7 @@ function logHintEmission(category, sessionId, correlator, compensateSelfResolve
3067
3303
  } catch {
3068
3304
  }
3069
3305
  }
3070
- var TOKEN_GOAT_INVOCATION_RE = /(?:^|[\s;&|])token-goat(?=[\s]|$)/;
3306
+ var TOKEN_GOAT_INVOCATION_RE = /(?:^|[\s;&|])(?:token-goat|tg)(?=[\s]|$)/;
3071
3307
  function commandMentionsCorrelator(command, correlator) {
3072
3308
  let idx = command.indexOf(correlator);
3073
3309
  while (idx !== -1) {
@@ -5529,6 +5765,7 @@ function uninstallVscode(opts = {}) {
5529
5765
  }
5530
5766
 
5531
5767
  // src/session_store.ts
5768
+ import { createHash as createHash2 } from "node:crypto";
5532
5769
  import * as fs13 from "node:fs";
5533
5770
  import * as path10 from "node:path";
5534
5771
 
@@ -5755,9 +5992,22 @@ function sweepCacheRoots(extraRoots = []) {
5755
5992
  var MAX_FILES = 500;
5756
5993
  var SESSIONS_SUBDIR = "sessions";
5757
5994
  var AGENT_SALT_MARKER = sanitizeIdForFilename(":agent:");
5995
+ var AGENT_SALT_SEPARATOR = ":agent:";
5996
+ var AGENT_DIGEST_CHARS = 12;
5997
+ var SALTED_SESSION_MAX = 64 - AGENT_SALT_MARKER.length - AGENT_DIGEST_CHARS;
5998
+ function saltedStemPrefix(sessionId) {
5999
+ return `${sanitizeIdForFilename(sessionId, SALTED_SESSION_MAX)}${AGENT_SALT_MARKER}`;
6000
+ }
6001
+ function sessionFileStem(sessionId) {
6002
+ const sep = sessionId.indexOf(AGENT_SALT_SEPARATOR);
6003
+ if (sep < 0) return sanitizeIdForFilename(sessionId, 64);
6004
+ const agentId = sessionId.slice(sep + AGENT_SALT_SEPARATOR.length);
6005
+ const digest = createHash2("sha256").update(agentId).digest("hex").slice(0, AGENT_DIGEST_CHARS);
6006
+ return `${saltedStemPrefix(sessionId.slice(0, sep))}${digest}`;
6007
+ }
5758
6008
  function sessionPath(sessionId) {
5759
6009
  if (!sessionId) return null;
5760
- const safe = sanitizeIdForFilename(sessionId, 64);
6010
+ const safe = sessionFileStem(sessionId);
5761
6011
  if (!safe) return null;
5762
6012
  const dir = path10.join(tokenGoatHome(), SESSIONS_SUBDIR);
5763
6013
  const candidate = path10.join(dir, `${safe}.json`);
@@ -6009,9 +6259,8 @@ function readSessionStateFile(sessionId) {
6009
6259
  }
6010
6260
  function listSiblingSessionStates(sessionId) {
6011
6261
  if (!sessionId) return [];
6012
- const safeSessionId = sanitizeIdForFilename(sessionId);
6013
- if (!safeSessionId) return [];
6014
- const prefix = `${safeSessionId}${AGENT_SALT_MARKER}`;
6262
+ const prefix = saltedStemPrefix(sessionId);
6263
+ if (prefix === AGENT_SALT_MARKER) return [];
6015
6264
  const dir = path10.join(tokenGoatHome(), SESSIONS_SUBDIR);
6016
6265
  const out = [];
6017
6266
  try {
@@ -6075,7 +6324,7 @@ function scanForInjectionPatterns(text) {
6075
6324
  var UNTRUSTED_WEB_TAG = "untrusted-web-content";
6076
6325
  function neutralizeFenceMarkers(text, tag) {
6077
6326
  const escapedTag = tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6078
- const marker = new RegExp(`<\\s*/?\\s*${escapedTag}(?:\\s[^>]*)?\\s*/?\\s*>`, "gi");
6327
+ const marker = new RegExp(`<\\s*/?\\s*${escapedTag}(?=[\\s/>])[^>]*>`, "gi");
6079
6328
  return text.replace(marker, (m) => m.replace(/</g, "&lt;").replace(/>/g, "&gt;"));
6080
6329
  }
6081
6330
  function fenceUntrustedContent(text, matchedPatternNames, tag = UNTRUSTED_WEB_TAG) {
@@ -6673,7 +6922,7 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
6673
6922
  }
6674
6923
 
6675
6924
  // src/image_shrink.ts
6676
- import { createHash as createHash2 } from "node:crypto";
6925
+ import { createHash as createHash3 } from "node:crypto";
6677
6926
  import * as fs16 from "node:fs";
6678
6927
  import * as path12 from "node:path";
6679
6928
 
@@ -6875,7 +7124,7 @@ function imageShrinkCacheDir() {
6875
7124
  return path12.join(tokenGoatHome(), "image_shrink_cache");
6876
7125
  }
6877
7126
  function shrinkCacheKey(originalPath, size, mtimeMs, quality) {
6878
- return createHash2("sha256").update(`${originalPath}:${size}:${mtimeMs}:${quality}`).digest("hex").slice(0, 16);
7127
+ return createHash3("sha256").update(`${originalPath}:${size}:${mtimeMs}:${quality}`).digest("hex").slice(0, 16);
6879
7128
  }
6880
7129
  function findCachedShrink(originalPath, size, mtimeMs, quality) {
6881
7130
  const key = shrinkCacheKey(originalPath, size, mtimeMs, quality);
@@ -6924,18 +7173,18 @@ function pruneShrinkCache() {
6924
7173
  }
6925
7174
  async function finalizeShrinkResult(result, filePath) {
6926
7175
  const basename12 = path12.basename(filePath);
7176
+ const shrinkSaved = result.originalBytes - result.shrunkBytes;
7177
+ recordStat("image_shrink", shrinkSaved, Math.round(shrinkSaved / 4), void 0, basename12);
6927
7178
  if (loadConfig().image_shrink.ocr_enabled) {
6928
7179
  const ocr = await ocrImage(result.data);
6929
7180
  if (ocr !== null && isTextHeavy(ocr, loadConfig().image_shrink.ocr_min_confidence)) {
6930
7181
  const textBytes = Buffer.byteLength(ocr.text, "utf8");
6931
- const saved2 = Math.max(0, result.shrunkBytes - textBytes);
6932
- recordStat("image_ocr", saved2, Math.round(saved2 / 4), void 0, basename12);
7182
+ const saved = Math.max(0, result.shrunkBytes - textBytes);
7183
+ recordStat("image_ocr", saved, Math.round(saved / 4), void 0, basename12);
6933
7184
  return contextOutput(formatOcrSummary(ocr, basename12, result.originalBytes));
6934
7185
  }
6935
7186
  }
6936
- const saved = result.originalBytes - result.shrunkBytes;
6937
7187
  const { summary, dataUrl } = formatShrinkSummary(result, basename12);
6938
- recordStat("image_shrink", saved, Math.round(saved / 4), void 0, basename12);
6939
7188
  return contextOutput(`${summary}
6940
7189
  ${dataUrl}`);
6941
7190
  }
@@ -7158,6 +7407,12 @@ async function embedTexts(texts, modelName = DEFAULT_MODEL) {
7158
7407
  `Dimension mismatch: model returned ${vec.length}-dim vector, expected ${expectedDim}`
7159
7408
  );
7160
7409
  }
7410
+ const badIndex = vec.findIndex((component) => !Number.isFinite(component));
7411
+ if (badIndex !== -1) {
7412
+ throw new Error(
7413
+ `Non-finite embedding component at index ${badIndex}: model returned ${String(vec[badIndex])}`
7414
+ );
7415
+ }
7161
7416
  vecs.push(vec);
7162
7417
  }
7163
7418
  } finally {
@@ -7168,6 +7423,11 @@ function packVec(vec) {
7168
7423
  const view = new Float32Array(vec.length);
7169
7424
  for (const [i, val] of vec.entries()) {
7170
7425
  view[i] = val;
7426
+ if (!Number.isFinite(view[i])) {
7427
+ throw new Error(
7428
+ `Non-finite embedding component at index ${i}: ${String(val)} is not representable as a 32-bit float`
7429
+ );
7430
+ }
7171
7431
  }
7172
7432
  return Buffer.from(view.buffer);
7173
7433
  }
@@ -7180,7 +7440,8 @@ function splitRangeIntoChunks(filePath, lines2, rangeStart, rangeEnd, chunkSize,
7180
7440
  const line = lines2[lineNo - 1] ?? "";
7181
7441
  const lineWithNewline = line + "\n";
7182
7442
  if (currentChunk.length + lineWithNewline.length > chunkSize && currentChunk.length > 0) {
7183
- const currentChunkTooSmall = currentChunk.trim().length < MIN_CHUNK_CHARS;
7443
+ const trimmedLength = currentChunk.trim().length;
7444
+ const currentChunkTooSmall = trimmedLength < MIN_CHUNK_CHARS;
7184
7445
  if (!currentChunkTooSmall) {
7185
7446
  chunks.push({
7186
7447
  filePath,
@@ -7190,12 +7451,21 @@ function splitRangeIntoChunks(filePath, lines2, rangeStart, rangeEnd, chunkSize,
7190
7451
  kind
7191
7452
  });
7192
7453
  }
7193
- const overlapLines = Math.ceil(overlap / 40);
7194
- const computedOverlapStart = Math.max(rangeStart, currentLine - overlapLines);
7195
- const overlapStart = currentChunkTooSmall ? Math.min(computedOverlapStart, startLine) : computedOverlapStart;
7196
- const overlapText = lines2.slice(overlapStart - 1, currentLine - 1).join("\n");
7197
- currentChunk = overlapText + "\n";
7198
- startLine = overlapStart;
7454
+ let overlapChars = 0;
7455
+ let computedOverlapStart = currentLine;
7456
+ while (computedOverlapStart > rangeStart) {
7457
+ const candidateChars = (lines2[computedOverlapStart - 2] ?? "").length + 1;
7458
+ if (overlapChars + candidateChars > overlap) break;
7459
+ overlapChars += candidateChars;
7460
+ computedOverlapStart--;
7461
+ }
7462
+ const droppedChunkHadContent = currentChunkTooSmall && trimmedLength > 0;
7463
+ const overlapStart = droppedChunkHadContent ? Math.min(computedOverlapStart, startLine) : computedOverlapStart;
7464
+ if (overlapStart !== startLine) {
7465
+ const overlapText = lines2.slice(overlapStart - 1, currentLine - 1).join("\n");
7466
+ currentChunk = overlapText + "\n";
7467
+ startLine = overlapStart;
7468
+ }
7199
7469
  }
7200
7470
  currentChunk += lineWithNewline;
7201
7471
  currentLine++;
@@ -7344,7 +7614,7 @@ async function upsertChunks(db, chunks) {
7344
7614
  insertChunkVector(vectorInsertStmt, chunkResult.lastInsertRowid, embedding);
7345
7615
  }
7346
7616
  });
7347
- tx();
7617
+ tx.immediate();
7348
7618
  return "embedded";
7349
7619
  }
7350
7620
  var BACKFILL_MULTIPLIER = 3;
@@ -7368,7 +7638,7 @@ function fetchScopedHits(db, queryVec, k, maxDistance, rootDir) {
7368
7638
  if (!row) {
7369
7639
  continue;
7370
7640
  }
7371
- if (row.distance <= maxDistance) {
7641
+ if (typeof row.distance === "number" && Number.isFinite(row.distance) && row.distance <= maxDistance) {
7372
7642
  const chunk = scopeParams !== void 0 ? chunkStmt.get(row.rowid, ...scopeParams) : chunkStmt.get(row.rowid);
7373
7643
  if (chunk) {
7374
7644
  hits.push({
@@ -8638,9 +8908,9 @@ var MONITORING_COMMAND_PATTERNS = [
8638
8908
  recallHint: "--tail 50"
8639
8909
  },
8640
8910
  // token-goat section/outline/symbol repeat calls — output is stable until the file changes
8641
- { pattern: /^token-goat\s+section\s+["'][^"']+["']/, recallHint: "" },
8642
- { pattern: /^token-goat\s+outline\s+\S+/, recallHint: "" },
8643
- { pattern: /^token-goat\s+symbol\s+\S+/, recallHint: "" }
8911
+ { pattern: /^(?:token-goat|tg)\s+section\s+["'][^"']+["']/, recallHint: "" },
8912
+ { pattern: /^(?:token-goat|tg)\s+outline\s+\S+/, recallHint: "" },
8913
+ { pattern: /^(?:token-goat|tg)\s+symbol\s+\S+/, recallHint: "" }
8644
8914
  ];
8645
8915
  function isPsMultilineSystemQuery(cmd) {
8646
8916
  if (!/^(?:powershell(?:\.exe)?|pwsh(?:\.exe)?)\s+/i.test(cmd)) return false;
@@ -12166,7 +12436,7 @@ function extractLua(content, filePath) {
12166
12436
  symbols.push(makeLineSymbol(filePath, baseName, "function", lineNum, stripped.slice(0, 200)));
12167
12437
  }
12168
12438
  if (!lineClosesItself(stripped)) {
12169
- funcStack.push({ name: baseName, endKeywordNeeded: true, isBlock: false });
12439
+ funcStack.push({ name: baseName, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
12170
12440
  }
12171
12441
  continue;
12172
12442
  }
@@ -12180,7 +12450,7 @@ function extractLua(content, filePath) {
12180
12450
  symbols.push(makeLineSymbol(filePath, fname, "function", lineNum, stripped.slice(0, 200)));
12181
12451
  }
12182
12452
  if (!lineClosesItself(stripped)) {
12183
- funcStack.push({ name: fname, endKeywordNeeded: true, isBlock: false });
12453
+ funcStack.push({ name: fname, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
12184
12454
  }
12185
12455
  continue;
12186
12456
  }
@@ -12195,7 +12465,7 @@ function extractLua(content, filePath) {
12195
12465
  symbols.push(makeLineSymbol(filePath, baseName, "function", lineNum, stripped.slice(0, 200)));
12196
12466
  }
12197
12467
  if (!lineClosesItself(stripped)) {
12198
- funcStack.push({ name: baseName, endKeywordNeeded: true, isBlock: false });
12468
+ funcStack.push({ name: baseName, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
12199
12469
  }
12200
12470
  continue;
12201
12471
  }
@@ -12216,7 +12486,13 @@ function extractLua(content, filePath) {
12216
12486
  if (/^(?:\bend\b[\s),;}]*)+$/.test(stripped)) {
12217
12487
  const popCount = (stripped.match(/\bend\b/g) ?? []).length;
12218
12488
  for (let k = 0; k < popCount && funcStack.length > 0; k++) {
12219
- funcStack.pop();
12489
+ const popped = funcStack.pop();
12490
+ if (popped !== void 0 && popped.symbolIndex !== void 0) {
12491
+ const open = symbols[popped.symbolIndex];
12492
+ if (open !== void 0 && lineNum > open.lineStart) {
12493
+ symbols[popped.symbolIndex] = { ...open, lineEnd: lineNum, body: lines2.slice(open.lineStart - 1, lineNum).join("\n") };
12494
+ }
12495
+ }
12220
12496
  }
12221
12497
  }
12222
12498
  }
@@ -12258,14 +12534,14 @@ function extractElixir(content, filePath) {
12258
12534
  if (modM) {
12259
12535
  const modName = modM[1] ?? "";
12260
12536
  symbols.push(makeLineSymbol(filePath, modName, "class", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
12261
- moduleStack.push({ name: modName, endKeywordNeeded: true, isBlock: false });
12537
+ moduleStack.push({ name: modName, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
12262
12538
  continue;
12263
12539
  }
12264
12540
  const protoM = PROTOCOL_RE.exec(stripped);
12265
12541
  if (protoM) {
12266
12542
  const protoName = protoM[1] ?? "";
12267
12543
  symbols.push(makeLineSymbol(filePath, protoName, "protocol", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
12268
- moduleStack.push({ name: protoName, endKeywordNeeded: true, isBlock: false });
12544
+ moduleStack.push({ name: protoName, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
12269
12545
  continue;
12270
12546
  }
12271
12547
  const fm = FUNC_RE4.exec(stripped);
@@ -12278,7 +12554,7 @@ function extractElixir(content, filePath) {
12278
12554
  symbols.push(makeLineSymbol(filePath, fname, "function", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
12279
12555
  }
12280
12556
  if (opensDoBlock) {
12281
- moduleStack.push({ name: fname, endKeywordNeeded: true, isBlock: false });
12557
+ moduleStack.push({ name: fname, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
12282
12558
  }
12283
12559
  continue;
12284
12560
  }
@@ -12292,7 +12568,7 @@ function extractElixir(content, filePath) {
12292
12568
  symbols.push(makeLineSymbol(filePath, fname, "function", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
12293
12569
  }
12294
12570
  if (opensDoBlock) {
12295
- moduleStack.push({ name: fname, endKeywordNeeded: true, isBlock: false });
12571
+ moduleStack.push({ name: fname, endKeywordNeeded: true, isBlock: false, symbolIndex: symbols.length - 1 });
12296
12572
  }
12297
12573
  continue;
12298
12574
  }
@@ -12309,7 +12585,13 @@ function extractElixir(content, filePath) {
12309
12585
  }
12310
12586
  if (stripped === "end" || /^end\s/.test(stripped) || /^end$/.test(stripped)) {
12311
12587
  if (moduleStack.length > 0) {
12312
- moduleStack.pop();
12588
+ const popped = moduleStack.pop();
12589
+ if (popped !== void 0 && popped.symbolIndex !== void 0) {
12590
+ const open = symbols[popped.symbolIndex];
12591
+ if (open !== void 0 && lineNum > open.lineStart) {
12592
+ symbols[popped.symbolIndex] = { ...open, lineEnd: lineNum, body: lines2.slice(open.lineStart - 1, lineNum).join("\n") };
12593
+ }
12594
+ }
12313
12595
  }
12314
12596
  }
12315
12597
  }
@@ -12607,7 +12889,7 @@ function bracedBodyEndLine(content, lineIndex, parenIndex, totalLines, fallback)
12607
12889
  j++;
12608
12890
  }
12609
12891
  if (content[j] !== "{") return fallback;
12610
- return findMatchingBraceEndLine(content, j, totalLines, lineIndex, "#");
12892
+ return findMatchingBraceEndLine(content, j, totalLines, lineIndex, "#", { backtickQuote: true });
12611
12893
  }
12612
12894
  function callEndLine(content, lineIndex, parenIndex, fallback) {
12613
12895
  const close = matchingParenIndex(content, parenIndex);
@@ -13036,25 +13318,53 @@ function stripBashComment(line) {
13036
13318
  }
13037
13319
  return line;
13038
13320
  }
13039
- function findHeredocOpeners(line) {
13321
+ function maskArithmeticSpans(line, carryDepth) {
13322
+ const chars = line.split("");
13323
+ let depth = carryDepth;
13324
+ for (let i = 0; i < chars.length; i++) {
13325
+ if (depth > 0) {
13326
+ if (chars[i] === "(") depth++;
13327
+ else if (chars[i] === ")") depth--;
13328
+ chars[i] = " ";
13329
+ continue;
13330
+ }
13331
+ const isDollar = chars[i] === "$" && chars[i + 1] === "(" && chars[i + 2] === "(";
13332
+ const isBare = chars[i] === "(" && chars[i + 1] === "(";
13333
+ if (!isDollar && !isBare) continue;
13334
+ if (isInsideStringLiteral(line, i)) continue;
13335
+ const open = isDollar ? i + 1 : i;
13336
+ for (let j = open; j < chars.length; j++) {
13337
+ if (chars[j] === "(") depth++;
13338
+ else if (chars[j] === ")") depth--;
13339
+ chars[j] = " ";
13340
+ i = j;
13341
+ if (depth === 0) break;
13342
+ }
13343
+ }
13344
+ return { masked: chars.join(""), depth };
13345
+ }
13346
+ function findHeredocOpeners(line, carryDepth) {
13040
13347
  const terminators = [];
13348
+ const { masked, depth } = maskArithmeticSpans(line, carryDepth);
13041
13349
  HEREDOC_RE.lastIndex = 0;
13042
13350
  let m;
13043
- while ((m = HEREDOC_RE.exec(line)) !== null) {
13351
+ while ((m = HEREDOC_RE.exec(masked)) !== null) {
13044
13352
  if (isInsideStringLiteral(line, m.index)) continue;
13045
13353
  const terminator = m[2] ?? "";
13046
13354
  if (terminator) terminators.push(terminator);
13047
13355
  }
13048
- return terminators;
13356
+ return { terminators, depth };
13049
13357
  }
13050
13358
  function extractBash(content, filePath) {
13051
13359
  const symbols = [];
13052
13360
  const lines2 = content.split(/\r?\n/);
13053
13361
  const heredocs = [];
13362
+ let arithmeticDepth = 0;
13054
13363
  let braceDepth = 0;
13055
13364
  let inFunction = false;
13056
13365
  let functionBraceDepth = 0;
13057
13366
  let awaitingFunctionBrace = false;
13367
+ let openFunctionIndex = null;
13058
13368
  for (let i = 0; i < lines2.length; i++) {
13059
13369
  const rawLine = lines2[i] ?? "";
13060
13370
  const lineNum = i + 1;
@@ -13064,7 +13374,9 @@ function extractBash(content, filePath) {
13064
13374
  }
13065
13375
  const noComment = stripBashComment(rawLine);
13066
13376
  const stripped = noComment.trim();
13067
- heredocs.push(...findHeredocOpeners(noComment));
13377
+ const opened = findHeredocOpeners(noComment, arithmeticDepth);
13378
+ arithmeticDepth = opened.depth;
13379
+ heredocs.push(...opened.terminators);
13068
13380
  if (!stripped) continue;
13069
13381
  if (!inFunction && !awaitingFunctionBrace && braceDepth === 0) {
13070
13382
  const kwMatch = FUNC_KEYWORD_RE.exec(stripped);
@@ -13072,7 +13384,9 @@ function extractBash(content, filePath) {
13072
13384
  const funcMatch = kwMatch ?? posixMatch;
13073
13385
  if (funcMatch) {
13074
13386
  const fname = funcMatch[1] ?? "";
13387
+ let pushedIndex = null;
13075
13388
  if (fname && symbols.length < MAX_SYMBOLS4) {
13389
+ pushedIndex = symbols.length;
13076
13390
  symbols.push(makeLineSymbol(filePath, fname, "function", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
13077
13391
  }
13078
13392
  if (fname) {
@@ -13084,9 +13398,11 @@ function extractBash(content, filePath) {
13084
13398
  } else if (openCount > closeCount) {
13085
13399
  inFunction = true;
13086
13400
  functionBraceDepth = braceDepth;
13401
+ openFunctionIndex = pushedIndex;
13087
13402
  }
13088
13403
  } else {
13089
13404
  awaitingFunctionBrace = true;
13405
+ openFunctionIndex = pushedIndex;
13090
13406
  }
13091
13407
  }
13092
13408
  } else {
@@ -13107,6 +13423,13 @@ function extractBash(content, filePath) {
13107
13423
  braceDepth += (braceLine.match(/\{/g) ?? []).length - (braceLine.match(/\}/g) ?? []).length;
13108
13424
  if (inFunction && braceDepth <= functionBraceDepth) {
13109
13425
  inFunction = false;
13426
+ if (openFunctionIndex !== null) {
13427
+ const open = symbols[openFunctionIndex];
13428
+ if (open !== void 0 && lineNum > open.lineStart) {
13429
+ symbols[openFunctionIndex] = { ...open, lineEnd: lineNum, body: lines2.slice(open.lineStart - 1, lineNum).join("\n") };
13430
+ }
13431
+ openFunctionIndex = null;
13432
+ }
13110
13433
  }
13111
13434
  }
13112
13435
  return symbols;
@@ -13691,7 +14014,7 @@ var TRIGGER_RE2 = new RegExp(
13691
14014
  var RETURN_TYPE = "(?:[A-Za-z_][A-Za-z0-9_.<>?,\\[\\] ]*[ \\t]+)";
13692
14015
  var STATEMENT_KEYWORD_GUARD = "(?!(?:return|throw|new|yield|else|do|try|finally|break|continue)\\b)";
13693
14016
  var METHOD_RE3 = new RegExp(
13694
- `^[ \\t]*(?:@${IDENT3}(?:\\([^\\n)]*\\))?[ \\t]+)*(?:(?:${MODIFIER}[ \\t]+)+(${RETURN_TYPE})?|(?:${MODIFIER}[ \\t]+)*${STATEMENT_KEYWORD_GUARD}(${RETURN_TYPE}))(${IDENT3})[ \\t]*\\([^;{}]*\\)[ \\t\\r\\n]*(?:\\{|;)`,
14017
+ `^[ \\t]*(?:@${IDENT3}(?:\\([^\\n)]*\\))?[ \\t]+)*(?=[^\\n]*\\()(?:(?:${MODIFIER}[ \\t]+)+(${RETURN_TYPE})?|(?:${MODIFIER}[ \\t]+)*${STATEMENT_KEYWORD_GUARD}(${RETURN_TYPE}))(${IDENT3})[ \\t]*\\([^;{}]*\\)[ \\t\\r\\n]*(?:\\{|;)`,
13695
14018
  "gm"
13696
14019
  );
13697
14020
  var CONTROL_NAMES = /* @__PURE__ */ new Set([
@@ -14451,21 +14774,22 @@ function maskSpans(content, spans) {
14451
14774
  }
14452
14775
  return chars.join("");
14453
14776
  }
14454
- function componentSymbol(filePath, name, kind, totalLines) {
14455
- return { filePath, name, kind, lineStart: 1, lineEnd: totalLines, body: "", docstring: "", parent: "" };
14777
+ function componentSymbols(filePath, name, kind, totalLines) {
14778
+ if (totalLines < 1) return [];
14779
+ return [{ filePath, name, kind, lineStart: 1, lineEnd: totalLines, body: "", docstring: "", parent: "" }];
14456
14780
  }
14457
14781
  function extractVue(content, filePath) {
14458
14782
  const totalLines = countContentLines(content);
14459
14783
  const lineIndex = buildLineIndex(content);
14460
14784
  const name = componentName(filePath);
14461
- const symbols = [componentSymbol(filePath, name, "vue_component", totalLines)];
14785
+ const symbols = componentSymbols(filePath, name, "vue_component", totalLines);
14462
14786
  const refs = [];
14463
14787
  for (const block of extractTagBlocks(content, lineIndex, "script")) {
14464
- symbols.push(...extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
14788
+ pushAll(symbols, extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
14465
14789
  }
14466
14790
  for (const block of extractTagBlocks(content, lineIndex, "template")) {
14467
14791
  const markup = stripXmlComments(block.content);
14468
- refs.push(...extractComponentRefs(markup, filePath, block.contentStartLine, true));
14792
+ pushAll(refs, extractComponentRefs(markup, filePath, block.contentStartLine, true));
14469
14793
  }
14470
14794
  return finalize(symbols, refs);
14471
14795
  }
@@ -14473,11 +14797,11 @@ function extractSvelte(content, filePath) {
14473
14797
  const totalLines = countContentLines(content);
14474
14798
  const lineIndex = buildLineIndex(content);
14475
14799
  const name = componentName(filePath);
14476
- const symbols = [componentSymbol(filePath, name, "svelte_component", totalLines)];
14800
+ const symbols = componentSymbols(filePath, name, "svelte_component", totalLines);
14477
14801
  const refs = [];
14478
14802
  const scriptBlocks = extractTagBlocks(content, lineIndex, "script");
14479
14803
  for (const block of scriptBlocks) {
14480
- symbols.push(...extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
14804
+ pushAll(symbols, extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
14481
14805
  }
14482
14806
  const styleBlocks = extractTagBlocks(content, lineIndex, "style");
14483
14807
  const spans = [...scriptBlocks, ...styleBlocks].map((b) => [
@@ -14485,7 +14809,7 @@ function extractSvelte(content, filePath) {
14485
14809
  b.matchEnd
14486
14810
  ]);
14487
14811
  const markup = stripXmlComments(maskSpans(content, spans));
14488
- refs.push(...extractComponentRefs(markup, filePath, 1, true));
14812
+ pushAll(refs, extractComponentRefs(markup, filePath, 1, true));
14489
14813
  return finalize(symbols, refs);
14490
14814
  }
14491
14815
  function detectAstroFrontmatter(content) {
@@ -14506,7 +14830,7 @@ function extractAstro(content, filePath) {
14506
14830
  const totalLines = countContentLines(content);
14507
14831
  const lineIndex = buildLineIndex(content);
14508
14832
  const name = componentName(filePath);
14509
- const symbols = [componentSymbol(filePath, name, "astro_component", totalLines)];
14833
+ const symbols = componentSymbols(filePath, name, "astro_component", totalLines);
14510
14834
  const refs = [];
14511
14835
  const lines2 = content.split("\n");
14512
14836
  const fm = detectAstroFrontmatter(content);
@@ -14514,7 +14838,7 @@ function extractAstro(content, filePath) {
14514
14838
  if (fm) {
14515
14839
  const frontmatterContent = lines2.slice(fm.openLine + 1, fm.closeLine).join("\n");
14516
14840
  const contentStartLine = fm.openLine + 2;
14517
- symbols.push(...extractTopLevelDeclarations(frontmatterContent, filePath, contentStartLine));
14841
+ pushAll(symbols, extractTopLevelDeclarations(frontmatterContent, filePath, contentStartLine));
14518
14842
  const fenceStartOffset = lineIndex[fm.openLine] ?? 0;
14519
14843
  const fenceEndOffset = lineIndex[fm.closeLine + 1] ?? content.length;
14520
14844
  spans.push([fenceStartOffset, fenceEndOffset]);
@@ -14522,7 +14846,7 @@ function extractAstro(content, filePath) {
14522
14846
  const styleBlocks = extractTagBlocks(content, lineIndex, "style");
14523
14847
  for (const block of styleBlocks) spans.push([block.matchStart, block.matchEnd]);
14524
14848
  const markup = stripXmlComments(maskSpans(content, spans));
14525
- refs.push(...extractComponentRefs(markup, filePath, 1, false));
14849
+ pushAll(refs, extractComponentRefs(markup, filePath, 1, false));
14526
14850
  return finalize(symbols, refs);
14527
14851
  }
14528
14852
 
@@ -16267,8 +16591,8 @@ var NO_TREE_SITTER_EXTRACTORS = {
16267
16591
  toml: extractTomlSymbols,
16268
16592
  css: extractCssSymbols,
16269
16593
  dockerfile: extractDockerfileSymbols,
16270
- csharp: (content, filePath) => extractCsharp(content, filePath).symbols,
16271
- php: (content, filePath) => extractPhp(content, filePath).symbols,
16594
+ csharp: (content, filePath) => assignBraceBlockSpans(extractCsharp(content, filePath).symbols, content, "//"),
16595
+ php: (content, filePath) => assignBraceBlockSpans(extractPhp(content, filePath).symbols, content, "//"),
16272
16596
  html: (content, filePath) => {
16273
16597
  const r = extractHtml(content, filePath);
16274
16598
  return [...r.symbols, ...sectionsToHeadingSymbols(r.sections, filePath)];
@@ -16277,13 +16601,13 @@ var NO_TREE_SITTER_EXTRACTORS = {
16277
16601
  const r = extractLiquid(content, filePath);
16278
16602
  return [...r.symbols, ...sectionsToHeadingSymbols(r.sections, filePath)];
16279
16603
  },
16280
- kotlin: (content, filePath) => extractKotlin(content, filePath).symbols,
16281
- swift: (content, filePath) => extractSwift(content, filePath).symbols,
16282
- scala: (content, filePath) => extractScala(content, filePath).symbols,
16604
+ kotlin: (content, filePath) => assignBraceBlockSpans(extractKotlin(content, filePath).symbols, content, "//"),
16605
+ swift: (content, filePath) => assignBraceBlockSpans(extractSwift(content, filePath).symbols, content, "//"),
16606
+ scala: (content, filePath) => assignBraceBlockSpans(extractScala(content, filePath).symbols, content, "//"),
16283
16607
  lua: (content, filePath) => extractLua(content, filePath).symbols,
16284
16608
  elixir: (content, filePath) => extractElixir(content, filePath).symbols,
16285
- dart: (content, filePath) => extractDart(content, filePath).symbols,
16286
- zig: (content, filePath) => extractZig(content, filePath).symbols,
16609
+ dart: (content, filePath) => assignBraceBlockSpans(extractDart(content, filePath).symbols, content, "//"),
16610
+ zig: (content, filePath) => assignBraceBlockSpans(extractZig(content, filePath).symbols, content, "//"),
16287
16611
  r: (content, filePath) => extractR(content, filePath).symbols,
16288
16612
  graphql: (content, filePath) => extractGraphql(content, filePath).symbols,
16289
16613
  sql: extractSql,
@@ -16291,7 +16615,7 @@ var NO_TREE_SITTER_EXTRACTORS = {
16291
16615
  makefile: extractMakefile,
16292
16616
  proto: (content, filePath) => extractProto(content, filePath).symbols,
16293
16617
  terraform: extractTerraform,
16294
- powershell: (content, filePath) => extractPowershell(content, filePath).symbols,
16618
+ powershell: (content, filePath) => assignBraceBlockSpans(extractPowershell(content, filePath).symbols, content, "#"),
16295
16619
  apex: (content, filePath) => extractApex(content, filePath).symbols,
16296
16620
  salesforce_metadata: (content, filePath) => extractSalesforceMetadata(content, filePath).symbols,
16297
16621
  env_file: extractEnv,
@@ -16372,7 +16696,7 @@ function writeParseResult(filePath, content, result, dbPath) {
16372
16696
  insRef.run(r.filePath, r.name, r.line, r.col, r.context);
16373
16697
  }
16374
16698
  });
16375
- writeAll();
16699
+ writeAll.immediate();
16376
16700
  }
16377
16701
  function indexFileSync(filePath, dbPath = globalDbPath(), preReadBytes) {
16378
16702
  const ixCfg = loadConfig().indexing;
@@ -16430,7 +16754,23 @@ function indexedPathSpellingIsStale(storedPath, absPath) {
16430
16754
  } catch {
16431
16755
  return false;
16432
16756
  }
16433
- return real !== stored && foldPath(real) === foldPath(stored);
16757
+ if (real === stored) return false;
16758
+ if (foldPath(real) !== foldPath(stored)) return false;
16759
+ const storedSegments = stored.split("/");
16760
+ const realSegments = real.split("/");
16761
+ if (storedSegments.length !== realSegments.length) return real !== stored;
16762
+ const storedBase = storedSegments[storedSegments.length - 1];
16763
+ const realBase = realSegments[realSegments.length - 1];
16764
+ if (storedBase !== realBase) return true;
16765
+ for (let i = storedSegments.length - 2; i >= 0; i--) {
16766
+ if (storedSegments[i] !== realSegments[i]) {
16767
+ if (storedSegments.slice(0, i + 1).join("/") === candidate.split("/").slice(0, i + 1).join("/")) {
16768
+ continue;
16769
+ }
16770
+ return true;
16771
+ }
16772
+ }
16773
+ return false;
16434
16774
  }
16435
16775
  function isEmbedFresh(storedEmbedSha, sha, embeddingsEnabled, depsAvailable) {
16436
16776
  if (storedEmbedSha === void 0) return false;
@@ -16536,11 +16876,13 @@ function removeFileFromIndex(db, filePath) {
16536
16876
  deleteFileRows(db, filePath);
16537
16877
  deleteFileEmbeddings(db, filePath);
16538
16878
  });
16539
- tx();
16879
+ tx.immediate();
16540
16880
  }
16541
16881
  function isTooShallowToPrune(rootPrefix) {
16542
- const segments = rootPrefix.split("/").filter((s) => s.length > 0 && !/^[a-z]:$/i.test(s));
16882
+ const normalized = normalizePath(rootPrefix);
16883
+ const segments = normalized.split("/").filter((s) => s.length > 0 && !/^[a-z]:$/i.test(s));
16543
16884
  if (segments.length === 2 && segments[0]?.toLowerCase() === "mnt" && /^[a-z]$/i.test(segments[1] ?? "")) return true;
16885
+ if (normalized.startsWith("//") && segments.length <= 2) return true;
16544
16886
  return segments.length === 0;
16545
16887
  }
16546
16888
  function foldedBounds(rootPrefix) {
@@ -16565,7 +16907,8 @@ function findDeletablePaths(rootPrefix, dbPath) {
16565
16907
  for (const p of foldedPathsUnderRoot(rootPrefix, dbPath)) {
16566
16908
  let stillExists;
16567
16909
  try {
16568
- stillExists = fs27.statSync(p, { throwIfNoEntry: false }) !== void 0;
16910
+ const st = fs27.statSync(p, { throwIfNoEntry: false });
16911
+ stillExists = st !== void 0 && st.isFile();
16569
16912
  } catch {
16570
16913
  continue;
16571
16914
  }
@@ -16588,14 +16931,23 @@ function removeFilesBestEffort(db, paths) {
16588
16931
  return removed;
16589
16932
  }
16590
16933
  function removeDeletedFilesBestEffort(db, paths) {
16591
- const stillGone = paths.filter((p) => {
16934
+ const removed = [];
16935
+ for (const p of paths) {
16936
+ let gone;
16592
16937
  try {
16593
- return fs27.statSync(p, { throwIfNoEntry: false }) === void 0;
16938
+ const st = fs27.statSync(p, { throwIfNoEntry: false });
16939
+ gone = st === void 0 || !st.isFile();
16594
16940
  } catch {
16595
- return false;
16941
+ continue;
16596
16942
  }
16597
- });
16598
- return removeFilesBestEffort(db, stillGone);
16943
+ if (!gone) continue;
16944
+ try {
16945
+ removeFileFromIndex(db, p);
16946
+ removed.push(p);
16947
+ } catch {
16948
+ }
16949
+ }
16950
+ return removed;
16599
16951
  }
16600
16952
  function pruneDeletedFiles(rootPrefix, dbPath = globalDbPath()) {
16601
16953
  if (isTooShallowToPrune(rootPrefix)) return 0;
@@ -16673,7 +17025,7 @@ function sweepKnownRoots(dbPath = globalDbPath(), opts) {
16673
17025
  if (isTooShallowToPrune(root)) continue;
16674
17026
  let reachable;
16675
17027
  try {
16676
- reachable = fs27.existsSync(root);
17028
+ reachable = fs27.statSync(root, { throwIfNoEntry: false })?.isDirectory() === true;
16677
17029
  } catch {
16678
17030
  reachable = false;
16679
17031
  }
@@ -16802,7 +17154,7 @@ function bumpRetryCount(dbPath, absPath) {
16802
17154
  db.prepare("INSERT INTO files (path, retry_count) VALUES (?, 1)").run(normalized);
16803
17155
  return 1;
16804
17156
  });
16805
- return tx();
17157
+ return tx.immediate();
16806
17158
  }
16807
17159
  function clearRetryCount(dbPath, absPath) {
16808
17160
  try {
@@ -16922,14 +17274,21 @@ var INDEX_FAILED = /* @__PURE__ */ Symbol("indexFailed");
16922
17274
  var inFlightEmbeddings = /* @__PURE__ */ new Map();
16923
17275
  var activeEmbedSlots = 0;
16924
17276
  var embedSlotWaiters = [];
16925
- function releaseEmbedSlot() {
16926
- activeEmbedSlots -= 1;
16927
- const next = embedSlotWaiters.shift();
16928
- if (next) next();
17277
+ var embedSlotEpoch = 0;
17278
+ function makeReleaseEmbedSlot() {
17279
+ const epoch = embedSlotEpoch;
17280
+ return () => {
17281
+ if (epoch !== embedSlotEpoch) return;
17282
+ activeEmbedSlots -= 1;
17283
+ const next = embedSlotWaiters.shift();
17284
+ if (next) next();
17285
+ };
16929
17286
  }
16930
17287
  registerReset(() => {
17288
+ embedSlotEpoch += 1;
16931
17289
  activeEmbedSlots = 0;
16932
17290
  embedSlotWaiters.length = 0;
17291
+ inFlightEmbeddings.clear();
16933
17292
  });
16934
17293
  function embedFileSerialized(absPath, dbPath, sha) {
16935
17294
  const key = foldPath(absPath);
@@ -16943,7 +17302,8 @@ function embedFileSerialized(absPath, dbPath, sha) {
16943
17302
  const limit = loadConfig().worker.max_pool_workers ?? 4;
16944
17303
  const dispatchEmbed = () => {
16945
17304
  const result = indexFileEmbeddings(absPath, dbPath, sha, onEmbedError);
16946
- result.then(releaseEmbedSlot, releaseEmbedSlot);
17305
+ const release = makeReleaseEmbedSlot();
17306
+ result.then(release, release);
16947
17307
  return result;
16948
17308
  };
16949
17309
  const runEmbed = () => {
@@ -17360,8 +17720,12 @@ var KNOWN_ROOTS_SWEEP_INTERVAL_MS = 24 * 60 * 60 * 1e3;
17360
17720
  async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
17361
17721
  let lastSnapshotCleanupMs = 0;
17362
17722
  let lastKnownRootsSweepMs = 0;
17723
+ let ownedPidFile = false;
17363
17724
  while (!shouldStop()) {
17364
17725
  if (!fs28.existsSync(dir)) break;
17726
+ const pidOwner = readPidFile(dir);
17727
+ if (pidOwner === process.pid) ownedPidFile = true;
17728
+ else if (ownedPidFile && pidOwner !== null) break;
17365
17729
  try {
17366
17730
  drainOnce(dir);
17367
17731
  } catch {