token-goat 2.6.33 → 2.6.34

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 (24) hide show
  1. package/README.md +4 -0
  2. package/dist/{token-goat-chunk-WFRR2DBG.mjs → token-goat-chunk-7FWBSPCT.mjs} +446 -339
  3. package/dist/{token-goat-chunk-HPWPUBAK.mjs → token-goat-chunk-CKRATWCJ.mjs} +5 -5
  4. package/dist/{token-goat-chunk-TDYTBCT4.mjs → token-goat-chunk-CN7GUPX6.mjs} +328 -86
  5. package/dist/{token-goat-chunk-L6FVTDPD.mjs → token-goat-chunk-FRMEOIRU.mjs} +361 -23
  6. package/dist/{token-goat-chunk-RPOXYJWK.mjs → token-goat-chunk-GXBPEQR2.mjs} +70 -25
  7. package/dist/{token-goat-chunk-77CX3MCN.mjs → token-goat-chunk-I6EQH27G.mjs} +2 -2
  8. package/dist/{token-goat-chunk-FN6WLFIX.mjs → token-goat-chunk-IYYQOPY4.mjs} +801 -51
  9. package/dist/{token-goat-chunk-72R7M5H7.mjs → token-goat-chunk-L7QLEVO7.mjs} +3 -3
  10. package/dist/{token-goat-chunk-OHQ7RBLL.mjs → token-goat-chunk-OA3GFKP2.mjs} +2 -2
  11. package/dist/{token-goat-hook-chunk-HXHFLKEL.mjs → token-goat-chunk-WA2ER6TZ.mjs} +7 -2
  12. package/dist/{token-goat-chunk-RQQ4SY2P.mjs → token-goat-hook-chunk-2ISZ4AZR.mjs} +7 -2
  13. package/dist/{token-goat-hook-chunk-ZT47M3IA.mjs → token-goat-hook-chunk-6C5RN3CM.mjs} +2 -2
  14. package/dist/{token-goat-hook-chunk-UWV4MTYI.mjs → token-goat-hook-chunk-6QTWMSRO.mjs} +328 -86
  15. package/dist/{token-goat-hook-chunk-XBOWDNJB.mjs → token-goat-hook-chunk-ATEGFQAU.mjs} +5 -5
  16. package/dist/{token-goat-hook-chunk-QD2DX2U7.mjs → token-goat-hook-chunk-BU5OFDGQ.mjs} +361 -23
  17. package/dist/{token-goat-hook-chunk-E6AIPKNF.mjs → token-goat-hook-chunk-CNLMPHXT.mjs} +442 -335
  18. package/dist/{token-goat-hook-chunk-WBBHV6TZ.mjs → token-goat-hook-chunk-I5VM5PO5.mjs} +70 -25
  19. package/dist/{token-goat-hook-chunk-VDXQXYJK.mjs → token-goat-hook-chunk-KGLMNOGD.mjs} +801 -51
  20. package/dist/{token-goat-hook-chunk-EKGDLI4D.mjs → token-goat-hook-chunk-OQML5EMA.mjs} +3 -3
  21. package/dist/{token-goat-hook-chunk-26DLRODR.mjs → token-goat-hook-chunk-QTZ6YUMQ.mjs} +2 -2
  22. package/dist/token-goat-hook.mjs +5 -5
  23. package/dist/token-goat.core.mjs +5 -5
  24. package/package.json +8 -8
@@ -10,15 +10,20 @@ import {
10
10
  atomicWriteText,
11
11
  backupFile,
12
12
  buildLineIndex,
13
+ countContentLines,
13
14
  countNoun,
14
15
  dataDir,
16
+ decodeSource,
15
17
  detectHarness,
16
18
  detectLanguage,
19
+ displaySafePath,
20
+ displaySafeText,
17
21
  ensureDirSync,
18
22
  escapeRegExp,
19
23
  extractEnv,
20
24
  extractErrorMessage,
21
25
  extractIni,
26
+ fileIsAbsent,
22
27
  findHtmlHeadingMatches,
23
28
  findMatchingBraceEndLine,
24
29
  findProject,
@@ -82,7 +87,7 @@ import {
82
87
  withFileLock,
83
88
  writeIfDifferent,
84
89
  writeJsonSettings
85
- } from "./token-goat-hook-chunk-E6AIPKNF.mjs";
90
+ } from "./token-goat-hook-chunk-CNLMPHXT.mjs";
86
91
  import {
87
92
  registerReset
88
93
  } from "./token-goat-hook-chunk-BUOCULAM.mjs";
@@ -336,12 +341,12 @@ function buildGuidanceBody(fallbackToolClause, opts = {}) {
336
341
  '- searching for a *concept* rather than a literal string \u2192 `semantic "description"`',
337
342
  "- re-reading output you already captured \u2192 `bash-output`/`web-output`/`mcp-output` by ID",
338
343
  "- a directory listing or recursive wildcard walk to orient in an unfamiliar repo \u2192 `map --compact`",
339
- "- pulling one value or subtree out of a JSON/YAML file (manifest, lockfile, spec, config) \u2192 `json-query file 'a.b.c'` / `yaml-query file 'a.b.c'`",
344
+ "- pulling one value or subtree out of a JSON/YAML/XML file (manifest, lockfile, spec, config) \u2192 `json-query file 'a.b.c'` / `yaml-query file 'a.b.c'` / `xml-query file 'a.b.c'`",
340
345
  "- opening an image to check its dimensions, format, or size \u2192 `image-meta file`",
341
346
  "- opening a screenshot, diagram, or scan to read the text in it \u2192 `image-text file`",
342
347
  "- opening a PDF or Office document \u2192 inspect its format first, then read a narrow slice: PDF `pdf-meta`/`pdf-outline` then `pdf-extract`; Word `docx-outline` then `docx-text`; PowerPoint `pptx-outline` then `pptx-slide`/`pptx-notes`; Excel `xlsx-sheets` then `xlsx-head`/`xlsx-range`/`xlsx-query`",
343
348
  "",
344
- 'Commands: `symbol NAME`, `read "file::symbol"`, `brief "file::symbol"`, `section "file::Heading"`, `semantic "description"`, `outline file`/`skeleton file`, `map --compact`, `refs file::symbol --callers`, `changed --symbol`, `config-get file KEY`, `json-query file \'a.b.c\'`/`yaml-query`, `json-outline file`/`yaml-outline`, `bash-output`/`web-output`/`mcp-output`, ' + (gdrive ? "`gdrive-sections <file-id>`, " : "") + "`image-meta file`/`image-text file`, `pdf-meta`/`pdf-outline`/`pdf-extract`, `docx-outline`/`docx-text`, `pptx-outline`/`pptx-slide`/`pptx-notes`/`pptx-text`, `xlsx-sheets`/`xlsx-head`/`xlsx-range`/`xlsx-query`.",
349
+ 'Commands: `symbol NAME`, `read "file::symbol"`, `brief "file::symbol"`, `section "file::Heading"`, `semantic "description"`, `outline file`/`skeleton file`, `map --compact`, `refs file::symbol --callers`, `changed --symbol`, `config-get file KEY`, `json-query file \'a.b.c\'`/`yaml-query`/`xml-query`, `json-outline file`/`yaml-outline`/`xml-outline`, `bash-output`/`web-output`/`mcp-output`, ' + (gdrive ? "`gdrive-sections <file-id>`, " : "") + "`image-meta file`/`image-text file`, `pdf-meta`/`pdf-outline`/`pdf-extract`, `docx-outline`/`docx-text`, `pptx-outline`/`pptx-slide`/`pptx-notes`/`pptx-text`, `xlsx-sheets`/`xlsx-head`/`xlsx-range`/`xlsx-query`.",
345
350
  "",
346
351
  "Sub-agent briefs must carry this gate verbatim: a sub-agent inherits none of this context and its reads spend the same token budget.",
347
352
  "",
@@ -3211,10 +3216,11 @@ function logHintEmission(category, sessionId, correlator, compensateSelfResolve
3211
3216
  try {
3212
3217
  const db = getDb(globalDbPath());
3213
3218
  const resolved = correlator === null ? 1 : 0;
3219
+ const actedOn = resolved === 1 && isSuppressionCategory(category) ? 1 : 0;
3214
3220
  const window = ACTED_ON_WINDOW + (compensateSelfResolve ? 1 : 0);
3215
3221
  db.prepare(
3216
3222
  `INSERT INTO hint_emissions (category, session_id, harness, correlator, emitted_at, resolved, acted_on, calls_remaining, bytes_emitted)
3217
- VALUES (@category, @sessionId, @harness, @correlator, @emittedAt, @resolved, 0, @callsRemaining, @bytesEmitted)`
3223
+ VALUES (@category, @sessionId, @harness, @correlator, @emittedAt, @resolved, @actedOn, @callsRemaining, @bytesEmitted)`
3218
3224
  ).run({
3219
3225
  category,
3220
3226
  sessionId,
@@ -3222,6 +3228,7 @@ function logHintEmission(category, sessionId, correlator, compensateSelfResolve
3222
3228
  correlator,
3223
3229
  emittedAt: Date.now(),
3224
3230
  resolved,
3231
+ actedOn,
3225
3232
  callsRemaining: correlator === null ? 0 : window,
3226
3233
  bytesEmitted
3227
3234
  });
@@ -3241,6 +3248,30 @@ function commandMentionsCorrelator(command, correlator) {
3241
3248
  }
3242
3249
  return false;
3243
3250
  }
3251
+ var SUPPRESSION_HINT_CATEGORIES = /* @__PURE__ */ new Set([
3252
+ "read_reread_dedup",
3253
+ "edit_reread_suggest"
3254
+ ]);
3255
+ function isSuppressionCategory(category) {
3256
+ return SUPPRESSION_HINT_CATEGORIES.has(category);
3257
+ }
3258
+ var EVENT_PATH_KEYS = ["file_path", "filePath", "notebook_path", "path"];
3259
+ function eventTargetText(event) {
3260
+ if (event.toolName === "Bash") {
3261
+ const c = event.toolInput["command"];
3262
+ return typeof c === "string" ? c : "";
3263
+ }
3264
+ for (const key of EVENT_PATH_KEYS) {
3265
+ const v = event.toolInput[key];
3266
+ if (typeof v === "string" && v !== "") return v;
3267
+ }
3268
+ return "";
3269
+ }
3270
+ function isDefiance(correlator, target) {
3271
+ if (target === "") return false;
3272
+ if (TOKEN_GOAT_INVOCATION_RE.test(target)) return false;
3273
+ return commandMentionsCorrelator(target, correlator);
3274
+ }
3244
3275
  function isActedOn(category, correlator, command) {
3245
3276
  if (!TOKEN_GOAT_INVOCATION_RE.test(command)) return false;
3246
3277
  if (category === "bash_recall") {
@@ -3252,10 +3283,29 @@ function resolvePendingHintsForEvent(event) {
3252
3283
  try {
3253
3284
  const db = getDb(globalDbPath());
3254
3285
  const command = event.toolName === "Bash" && typeof event.toolInput["command"] === "string" ? event.toolInput["command"] : "";
3286
+ const target = eventTargetText(event);
3255
3287
  const pending = db.prepare(`SELECT id, category, correlator, calls_remaining FROM hint_emissions WHERE session_id = ? AND resolved = 0`).all(event.sessionId);
3256
3288
  for (const row of pending) {
3257
3289
  if (!isHintCategory(row.category) || row.correlator === null) {
3258
- db.prepare(`UPDATE hint_emissions SET resolved = 1 WHERE id = ?`).run(row.id);
3290
+ const unobservable = isHintCategory(row.category) && isSuppressionCategory(row.category) ? 1 : 0;
3291
+ db.prepare(`UPDATE hint_emissions SET acted_on = ?, resolved = 1 WHERE id = ?`).run(unobservable, row.id);
3292
+ continue;
3293
+ }
3294
+ if (isSuppressionCategory(row.category)) {
3295
+ if (isDefiance(row.correlator, target)) {
3296
+ db.prepare(`UPDATE hint_emissions SET acted_on = 0, resolved = 1 WHERE id = ?`).run(row.id);
3297
+ continue;
3298
+ }
3299
+ if (command !== "" && isActedOn(row.category, row.correlator, command)) {
3300
+ db.prepare(`UPDATE hint_emissions SET acted_on = 1, resolved = 1 WHERE id = ?`).run(row.id);
3301
+ continue;
3302
+ }
3303
+ const left = row.calls_remaining - 1;
3304
+ if (left <= 0) {
3305
+ db.prepare(`UPDATE hint_emissions SET acted_on = 1, resolved = 1 WHERE id = ?`).run(row.id);
3306
+ } else {
3307
+ db.prepare(`UPDATE hint_emissions SET calls_remaining = ? WHERE id = ?`).run(left, row.id);
3308
+ }
3259
3309
  continue;
3260
3310
  }
3261
3311
  if (command !== "" && isActedOn(row.category, row.correlator, command)) {
@@ -3313,15 +3363,18 @@ function manualMarks(category) {
3313
3363
  }
3314
3364
  }
3315
3365
  function getHintStatsSummary() {
3366
+ const probeThresholds = loadConfig().hints.backoff_thresholds.filter((t) => t > 0);
3316
3367
  return HINT_CATEGORIES.map((category) => {
3317
3368
  const { emitted, actedOn, bytesEmitted, legacyEmissions } = categoryStats(category);
3318
3369
  const marks = manualMarks(category);
3370
+ const suppressed = shouldSuppress(category, "");
3319
3371
  return {
3320
3372
  category,
3321
3373
  emitted,
3322
3374
  actedOn: actedOn ?? 0,
3323
3375
  efficacyPct: emitted === 0 ? null : Math.round(1e3 * (actedOn ?? 0) / emitted) / 10,
3324
- suppressed: shouldSuppress(category, ""),
3376
+ suppressed,
3377
+ suppressionPermanent: suppressed && probeThresholds.length === 0,
3325
3378
  manualEffective: marks.effective,
3326
3379
  manualIneffective: marks.ineffective,
3327
3380
  bytesEmitted,
@@ -4057,7 +4110,7 @@ async function ocrImage(input) {
4057
4110
  const entryPath = resolveTesseractEntry();
4058
4111
  if (entryPath === null) return null;
4059
4112
  if (ocrBlockedOffline()) return null;
4060
- return new Promise((resolve9) => {
4113
+ return new Promise((resolve10) => {
4061
4114
  let settled = false;
4062
4115
  let child;
4063
4116
  try {
@@ -4066,7 +4119,7 @@ async function ocrImage(input) {
4066
4119
  });
4067
4120
  } catch {
4068
4121
  _ocrUnavailableThisProcess = true;
4069
- resolve9(null);
4122
+ resolve10(null);
4070
4123
  return;
4071
4124
  }
4072
4125
  const chunks = [];
@@ -4079,7 +4132,7 @@ async function ocrImage(input) {
4079
4132
  child.kill();
4080
4133
  } catch {
4081
4134
  }
4082
- resolve9(result);
4135
+ resolve10(result);
4083
4136
  };
4084
4137
  const timer = setTimeout(() => finish(null, true), _ocrTimeoutMs);
4085
4138
  child.stdout?.on("data", (c) => chunks.push(c));
@@ -4204,11 +4257,11 @@ function statInfo(absPath) {
4204
4257
  function imageShrinkCacheDir() {
4205
4258
  return path10.join(tokenGoatHome(), "image_shrink_cache");
4206
4259
  }
4207
- function shrinkCacheKey(originalPath, size, mtimeMs) {
4208
- return createHash2("sha256").update(`${originalPath}:${size}:${mtimeMs}`).digest("hex").slice(0, 16);
4260
+ function shrinkCacheKey(originalPath, size, mtimeMs, quality) {
4261
+ return createHash2("sha256").update(`${originalPath}:${size}:${mtimeMs}:${quality}`).digest("hex").slice(0, 16);
4209
4262
  }
4210
- function findCachedShrink(originalPath, size, mtimeMs) {
4211
- const key = shrinkCacheKey(originalPath, size, mtimeMs);
4263
+ function findCachedShrink(originalPath, size, mtimeMs, quality) {
4264
+ const key = shrinkCacheKey(originalPath, size, mtimeMs, quality);
4212
4265
  const dir = imageShrinkCacheDir();
4213
4266
  const candidates = [
4214
4267
  { ext: ".webp", format: "webp" },
@@ -4220,11 +4273,11 @@ function findCachedShrink(originalPath, size, mtimeMs) {
4220
4273
  }
4221
4274
  return null;
4222
4275
  }
4223
- function writeCachedShrink(originalPath, result, mtimeMs) {
4276
+ function writeCachedShrink(originalPath, result, mtimeMs, quality) {
4224
4277
  try {
4225
4278
  const dir = imageShrinkCacheDir();
4226
4279
  ensureDirSync(dir);
4227
- const key = shrinkCacheKey(originalPath, result.originalBytes, mtimeMs);
4280
+ const key = shrinkCacheKey(originalPath, result.originalBytes, mtimeMs, quality);
4228
4281
  const ext = result.format === "jpeg" ? ".jpg" : ".webp";
4229
4282
  atomicWriteBytes(path10.join(dir, `token-goat-shrink-${key}${ext}`), result.data);
4230
4283
  } catch {
@@ -4278,7 +4331,8 @@ async function preReadImageHandler(event) {
4278
4331
  const stat2 = statInfo(filePath);
4279
4332
  if (stat2 === null) return passOutput();
4280
4333
  const size = stat2.size;
4281
- const cached = findCachedShrink(filePath, stat2.size, stat2.mtimeMs);
4334
+ const quality = loadConfig().image_shrink.jpeg_quality;
4335
+ const cached = findCachedShrink(filePath, stat2.size, stat2.mtimeMs, quality);
4282
4336
  if (cached !== null) {
4283
4337
  let cachedData;
4284
4338
  try {
@@ -4331,12 +4385,12 @@ async function preReadImageHandler(event) {
4331
4385
  return passOutput();
4332
4386
  }
4333
4387
  }
4334
- const result = await shrinkImage(input, { sizeThresholdBytes: 0 });
4388
+ const result = await shrinkImage(input, { quality, sizeThresholdBytes: 0 });
4335
4389
  if (result === null) {
4336
4390
  recordStat("image_shrink_skipped");
4337
4391
  return passOutput();
4338
4392
  }
4339
- writeCachedShrink(filePath, result, stat2.mtimeMs);
4393
+ writeCachedShrink(filePath, result, stat2.mtimeMs, quality);
4340
4394
  return finalizeShrinkResult(result, filePath);
4341
4395
  }
4342
4396
  registerHook("pre_tool_use", preReadImageHandler, { toolName: "Read" });
@@ -4398,7 +4452,7 @@ registerReset(() => {
4398
4452
  PIPELINE_RETRY_DELAY_MS = DEFAULT_PIPELINE_RETRY_DELAY_MS;
4399
4453
  });
4400
4454
  function sleep(ms) {
4401
- return new Promise((resolve9) => setTimeout(resolve9, ms));
4455
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
4402
4456
  }
4403
4457
  async function buildExtractorWithRetry(pipelineFn, modelName) {
4404
4458
  let lastError;
@@ -4948,6 +5002,7 @@ function trimToBudget(text, budgetTokens, command) {
4948
5002
  return text;
4949
5003
  }
4950
5004
  const lines2 = text.split("\n");
5005
+ if (lines2.length > 1 && lines2[lines2.length - 1] === "") lines2.pop();
4951
5006
  const totalLines = lines2.length;
4952
5007
  const bodyBudget = Math.max(1, budgetTokens - markerMarginTokens);
4953
5008
  const charBudget = bodyBudget * 3;
@@ -6231,7 +6286,7 @@ var ResizeableBuffer = class {
6231
6286
  }
6232
6287
  toString(encoding) {
6233
6288
  if (encoding) {
6234
- return this.buf.slice(0, this.length).toString(encoding);
6289
+ return this.buf.toString(encoding, 0, this.length);
6235
6290
  } else {
6236
6291
  return Uint8Array.prototype.slice.call(this.buf.slice(0, this.length));
6237
6292
  }
@@ -7483,14 +7538,19 @@ var transform = function(original_options = {}) {
7483
7538
  const obj = {};
7484
7539
  for (let i = 0, l = record.length; i < l; i++) {
7485
7540
  if (columns[i] === void 0 || columns[i].disabled) continue;
7486
- if (group_columns_by_name === true && obj[columns[i].name] !== void 0) {
7541
+ if (group_columns_by_name === true && Object.hasOwn(obj, columns[i].name)) {
7487
7542
  if (Array.isArray(obj[columns[i].name])) {
7488
7543
  obj[columns[i].name] = obj[columns[i].name].concat(record[i]);
7489
7544
  } else {
7490
7545
  obj[columns[i].name] = [obj[columns[i].name], record[i]];
7491
7546
  }
7492
7547
  } else {
7493
- obj[columns[i].name] = record[i];
7548
+ Object.defineProperty(obj, columns[i].name, {
7549
+ value: record[i],
7550
+ enumerable: true,
7551
+ writable: true,
7552
+ configurable: true
7553
+ });
7494
7554
  }
7495
7555
  }
7496
7556
  if (raw === true || info2 === true) {
@@ -7917,6 +7977,7 @@ ${recall}`
7917
7977
  if (htmlResult.shouldBlock) return htmlResult;
7918
7978
  }
7919
7979
  const lines2 = content.split("\n");
7980
+ if (lines2.length > 1 && lines2[lines2.length - 1] === "") lines2.pop();
7920
7981
  const preview = [
7921
7982
  "--- first 5 lines ---",
7922
7983
  ...lines2.slice(0, 5),
@@ -8532,7 +8593,8 @@ function isSessionArtifactFile(filePath) {
8532
8593
  if (/[/\\]tool-results[/\\][a-z0-9]+\.txt$/i.test(filePath)) return true;
8533
8594
  return false;
8534
8595
  }
8535
- function sessionArtifactRecall(filePath) {
8596
+ function sessionArtifactRecall(rawPath) {
8597
+ const filePath = displaySafePath(rawPath);
8536
8598
  return 'Use `token-goat bash-output --file "' + filePath + '" --tail 50` (or `--grep PATTERN`) to read a slice instead of the full file.';
8537
8599
  }
8538
8600
  function readIntToolInput(event, key) {
@@ -8599,7 +8661,8 @@ function estimateRequestedSlice(event, absPath) {
8599
8661
  if (scan.trustworthy) return { kind: "bytes", bytes: scan.bytes };
8600
8662
  return { kind: "unbounded" };
8601
8663
  }
8602
- function describeSliceAdvice(slice, absPath) {
8664
+ function describeSliceAdvice(slice, rawAbsPath) {
8665
+ const absPath = displaySafePath(rawAbsPath);
8603
8666
  if (slice.kind === "nearSingleLine") {
8604
8667
  return BYTE_RANGE_ADVICE(absPath);
8605
8668
  }
@@ -8758,6 +8821,7 @@ function preReadHandlerInner(event) {
8758
8821
  }
8759
8822
  if (filePath === void 0) return passOutput();
8760
8823
  const normalized = normalizePath(filePath);
8824
+ const shown = displaySafePath(normalized);
8761
8825
  if (isNodeModulesPath(normalized)) {
8762
8826
  return denyOutput(
8763
8827
  "node_modules is typically noise; use npm ls, npm outdated, or npm audit instead for dependency info. To force access, use: token-goat read node_modules/package/file.js::symbol-name or token-goat section node_modules/package/file.js::heading"
@@ -8781,7 +8845,7 @@ function preReadHandlerInner(event) {
8781
8845
  const basename12 = path16.basename(normalized);
8782
8846
  if (isLockFile(basename12)) {
8783
8847
  return denyOutput(
8784
- 'Lock files are rarely useful to read in full. Use `token-goat section "' + normalized + '::<section>"` to extract a specific dependency, or read the relevant manifest instead.'
8848
+ 'Lock files are rarely useful to read in full. Use `token-goat section "' + shown + '::<section>"` to extract a specific dependency, or read the relevant manifest instead.'
8785
8849
  );
8786
8850
  }
8787
8851
  if (normalized.toLowerCase().endsWith(".tsbuildinfo")) {
@@ -8805,13 +8869,13 @@ function preReadHandlerInner(event) {
8805
8869
  if (isTsConfigFile(basename12) && wasFileReadThisSession(normalized)) {
8806
8870
  recordActualRead(event, normalized);
8807
8871
  return quietContextOutput(
8808
- "Already read " + basename12 + '. Use `token-goat section "' + normalized + '::compilerOptions"` to extract compiler options, or `token-goat config-get ' + normalized + " compilerOptions.target` for a single value."
8872
+ "Already read " + basename12 + '. Use `token-goat section "' + shown + '::compilerOptions"` to extract compiler options, or `token-goat config-get ' + shown + " compilerOptions.target` for a single value."
8809
8873
  );
8810
8874
  }
8811
8875
  if (isManifestFile(basename12) && wasFileReadThisSession(normalized)) {
8812
8876
  recordActualRead(event, normalized);
8813
8877
  return quietContextOutput(
8814
- "You've already read " + basename12 + '. Use `token-goat section "' + normalized + '::<field>"` or `token-goat config-get ' + normalized + " <key>` to extract just the value you need."
8878
+ "You've already read " + basename12 + '. Use `token-goat section "' + shown + '::<field>"` or `token-goat config-get ' + shown + " <key>` to extract just the value you need."
8815
8879
  );
8816
8880
  }
8817
8881
  const skillName = detectSkillFile(normalized);
@@ -8840,7 +8904,7 @@ function preReadHandlerInner(event) {
8840
8904
  const savedBytes = Math.max(0, fullSize - compactBody.length);
8841
8905
  recordStat("session_hint", savedBytes, Math.round(savedBytes / 4));
8842
8906
  return denyOutput(
8843
- "Serving the extractive compact sidecar in place of the full file (source unchanged since the last `compact-doc` build):\n\n" + fenceUntrustedFileContent(compactBody) + '\n\nUse `token-goat compact-doc "' + normalized + '" --force` to rebuild it, or `token-goat compact-doc "' + normalized + '" --show` to view it directly. ' + editAnywayHint(normalized)
8907
+ "Serving the extractive compact sidecar in place of the full file (source unchanged since the last `compact-doc` build):\n\n" + fenceUntrustedFileContent(compactBody) + '\n\nUse `token-goat compact-doc "' + shown + '" --force` to rebuild it, or `token-goat compact-doc "' + shown + '" --show` to view it directly. ' + editAnywayHint(normalized)
8844
8908
  );
8845
8909
  }
8846
8910
  }
@@ -8881,7 +8945,7 @@ function preReadHandlerInner(event) {
8881
8945
  const hintText = formatHeadingTree(headings, normalized);
8882
8946
  const headingTextsLower = new Set(headings.map((h) => h.text.trim().toLowerCase()));
8883
8947
  const wellKnown = getWellKnownSections(basename12).filter((s) => headingTextsLower.has(s.trim().toLowerCase()));
8884
- const wellKnownText = wellKnown.length > 0 ? "\nQuick access: " + wellKnown.map((s) => 'token-goat section "' + normalized + "::" + s + '"').join(" | ") : "";
8948
+ const wellKnownText = wellKnown.length > 0 ? "\nQuick access: " + wellKnown.map((s) => 'token-goat section "' + shown + "::" + s + '"').join(" | ") : "";
8885
8949
  const changelogExtra = basename12.toLowerCase() === "changelog.md" ? extractChangelogVersionHint(fileContent, normalized) : "";
8886
8950
  let message = fenceUntrustedFileContent(hintText + changelogExtra) + wellKnownText;
8887
8951
  const slice = estimateRequestedSlice(event, normalized);
@@ -8905,7 +8969,7 @@ function preReadHandlerInner(event) {
8905
8969
  recordStat("session_hint", 0, 0);
8906
8970
  const isMainMemory = basename12.toLowerCase() === "memory.md";
8907
8971
  return denyOutput(
8908
- isMainMemory ? "MEMORY.md was read this session. Its content is in the compact manifest as 'session memory'." : normalized + ' was already read this session. Memory files rarely change mid-session. Use `token-goat section "' + normalized + '::SectionHeading"` to extract one section.'
8972
+ isMainMemory ? "MEMORY.md was read this session. Its content is in the compact manifest as 'session memory'." : shown + ' was already read this session. Memory files rarely change mid-session. Use `token-goat section "' + shown + '::SectionHeading"` to extract one section.'
8909
8973
  );
8910
8974
  }
8911
8975
  if (/^\.improve-state-.*\.json$/.test(basename12) && wasFileReadThisSession(normalized)) {
@@ -8919,7 +8983,7 @@ function preReadHandlerInner(event) {
8919
8983
  recordActualRead(event, normalized);
8920
8984
  recordStat("session_hint", 0, 0);
8921
8985
  return denyOutput(
8922
- normalized + " was already read this session. Environment files rarely change mid-session. Use `token-goat config-get " + normalized + " KEY_NAME` to extract a specific variable."
8986
+ shown + " was already read this session. Environment files rarely change mid-session. Use `token-goat config-get " + shown + " KEY_NAME` to extract a specific variable."
8923
8987
  );
8924
8988
  }
8925
8989
  if (isSessionArtifactFile(normalized)) {
@@ -8950,7 +9014,7 @@ function preReadHandlerInner(event) {
8950
9014
  recordActualRead(event, normalized);
8951
9015
  recordStat("session_hint", 0, 0);
8952
9016
  return denyOutput(
8953
- normalized + " was already read this session. " + sessionArtifactRecall(normalized)
9017
+ shown + " was already read this session. " + sessionArtifactRecall(normalized)
8954
9018
  );
8955
9019
  }
8956
9020
  {
@@ -9009,7 +9073,7 @@ function preReadHandlerInner(event) {
9009
9073
  if (scanCrossSessionManifests(project.root, project.hash, normalized, ttlSecs)) {
9010
9074
  recordActualRead(event, normalized);
9011
9075
  return quietContextOutput(
9012
- "This file may have already been read by another agent/session working in this project recently. If you are a subagent continuing shared work, consider whether you already have this content from context, or use `token-goat read " + normalized + "::SymbolName` for a narrower slice instead of a full re-read."
9076
+ "This file may have already been read by another agent/session working in this project recently. If you are a subagent continuing shared work, consider whether you already have this content from context, or use `token-goat read " + shown + "::SymbolName` for a narrower slice instead of a full re-read."
9013
9077
  );
9014
9078
  }
9015
9079
  }
@@ -9041,7 +9105,7 @@ function preReadHandlerInner(event) {
9041
9105
  if (/\.(md|mdx|markdown|rst)$/i.test(basename12)) {
9042
9106
  recordStat("session_hint", rereadCredit, Math.round(rereadCredit / 4));
9043
9107
  return denyOutput(
9044
- 'Markdown file already read this session. Use `token-goat section "' + normalized + '::HeadingName"` to read one section. ' + editAnywayHint(normalized)
9108
+ 'Markdown file already read this session. Use `token-goat section "' + shown + '::HeadingName"` to read one section. ' + editAnywayHint(normalized)
9045
9109
  );
9046
9110
  }
9047
9111
  const isSourceExt = isSourceExtension(basename12);
@@ -9049,22 +9113,22 @@ function preReadHandlerInner(event) {
9049
9113
  recordStat("read_count_deny", rereadCredit, Math.round(rereadCredit / 4));
9050
9114
  recordStat("session_hint", rereadCredit, Math.round(rereadCredit / 4));
9051
9115
  return denyOutput(
9052
- "Read this file " + reads + ' times already \u2014 use `token-goat read "' + normalized + '::Symbol"`, `token-goat skeleton ' + normalized + "`, or `token-goat outline " + normalized + "` to pull just the part you need. " + editAnywayHint(normalized)
9116
+ "Read this file " + reads + ' times already \u2014 use `token-goat read "' + shown + '::Symbol"`, `token-goat skeleton ' + shown + "`, or `token-goat outline " + shown + "` to pull just the part you need. " + editAnywayHint(normalized)
9053
9117
  );
9054
9118
  }
9055
9119
  }
9056
- const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + normalized + '::SectionName"` to read one section.' : "Use token-goat read/section/symbol to re-read surgically.";
9120
+ const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + shown + '::SectionName"` to read one section.' : "Use token-goat read/section/symbol to re-read surgically.";
9057
9121
  if (config2.hints.reread_deny && !protectedRead && (rereadBytes >= config2.hints.reread_deny_min_bytes || reads >= 2)) {
9058
9122
  recordStat("session_hint", rereadCredit, Math.round(rereadCredit / 4));
9059
9123
  return denyOutput(
9060
- normalized + " was already read this session (" + reads + " " + plural + "). " + hint + " " + editAnywayHint(normalized)
9124
+ shown + " was already read this session (" + reads + " " + plural + "). " + hint + " " + editAnywayHint(normalized)
9061
9125
  );
9062
9126
  }
9063
9127
  if (!isWithinQuietHours(config2.hints.quiet_hours)) {
9064
9128
  recordStat("session_hint", 0, 0);
9065
9129
  }
9066
9130
  return quietContextOutput(
9067
- "Note: " + normalized + " was already read this session (" + reads + " " + plural + "). " + hint
9131
+ "Note: " + shown + " was already read this session (" + reads + " " + plural + "). " + hint
9068
9132
  );
9069
9133
  }
9070
9134
  const size = statSize(normalized);
@@ -9077,12 +9141,12 @@ function preReadHandlerInner(event) {
9077
9141
  }
9078
9142
  const kb = toKB(size);
9079
9143
  const config2 = loadConfig();
9080
- const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + normalized + '::SectionName"` to read one section.' : "Consider token-goat skeleton or token-goat section.";
9144
+ const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + shown + '::SectionName"` to read one section.' : "Consider token-goat skeleton or token-goat section.";
9081
9145
  if (gateSize >= largeFileDenyBytes()) {
9082
9146
  const denyCredit = Math.min(size, PER_FILE_COUNTERFACTUAL_CEILING);
9083
9147
  recordStat("session_hint", denyCredit, Math.round(denyCredit / 4));
9084
9148
  return denyOutput(
9085
- normalized + " is very large (" + kb + "KB). " + hint + " " + describeSliceAdvice(slice, normalized) + " " + editAnywayHint(normalized)
9149
+ shown + " is very large (" + kb + "KB). " + hint + " " + describeSliceAdvice(slice, normalized) + " " + editAnywayHint(normalized)
9086
9150
  );
9087
9151
  }
9088
9152
  recordActualRead(event, normalized);
@@ -9096,7 +9160,7 @@ function preReadHandlerInner(event) {
9096
9160
  recordStat("session_hint", 0, 0);
9097
9161
  }
9098
9162
  return quietContextOutput(
9099
- "Note: " + normalized + " is large (" + kb + "KB). " + hint + contextPressureAdvisorySuffix()
9163
+ "Note: " + shown + " is large (" + kb + "KB). " + hint + contextPressureAdvisorySuffix()
9100
9164
  );
9101
9165
  }
9102
9166
  const fileTypeExt = path16.extname(normalized).slice(1).toLowerCase();
@@ -9144,16 +9208,19 @@ function estimateTruncatedLineCount(normalized) {
9144
9208
  }
9145
9209
  return Infinity;
9146
9210
  }
9147
- function editAnywayHint(normalized) {
9211
+ function editAnywayHint(rawPath) {
9212
+ const normalized = displaySafePath(rawPath);
9148
9213
  return 'To edit it anyway, use `token-goat replace "' + normalized + '" --old-b64 <base64> --new-b64 <base64>` (preferred \u2014 no temp files needed) or `--old-from <oldfile> --new-from <newfile>` for a snippet edit, or `token-goat write-file "' + normalized + "\" --b64 <base64>` (or `--from <newfile>`) to rewrite the whole file \u2014 Read/Edit's own precondition can't be satisfied after this deny.";
9149
9214
  }
9150
- function truncatedReadDenyMessage(normalized) {
9215
+ function truncatedReadDenyMessage(rawPath) {
9216
+ const normalized = displaySafePath(rawPath);
9151
9217
  return 'File was truncated on last read (>33K tokens). Use `token-goat skeleton "' + normalized + '"` for structure or `token-goat read "' + normalized + '::SymbolName"` for one function. ' + editAnywayHint(normalized);
9152
9218
  }
9153
9219
  function postReadHandlerInner(event) {
9154
9220
  const filePath = getFilePath(event);
9155
9221
  if (filePath === void 0) return passOutput();
9156
9222
  const normalized = normalizePath(filePath);
9223
+ const shown = displaySafePath(normalized);
9157
9224
  const respText = extractReadOutput(event.raw);
9158
9225
  if (respText.includes("[Truncated:") || respText.includes("Truncated: PARTIAL view")) {
9159
9226
  markFileTruncated(normalized);
@@ -9174,7 +9241,7 @@ function postReadHandlerInner(event) {
9174
9241
  try {
9175
9242
  const cwd = getCwd(event) ?? process.cwd();
9176
9243
  const project = findProject(cwd) ?? makeProjectAt(cwd);
9177
- const source = fs18.readFileSync(normalized, "utf8");
9244
+ const source = decodeSource(fs18.readFileSync(normalized));
9178
9245
  recordEvidence({ projectRoot: project.root, source: normalized, representation: "file", text: source });
9179
9246
  } catch {
9180
9247
  }
@@ -9210,7 +9277,7 @@ function postReadHandlerInner(event) {
9210
9277
  if (lineCount >= minLines && meetsSavingsFloor(sz)) {
9211
9278
  recordStat("session_hint", 0, 0);
9212
9279
  return quietContextOutput(
9213
- normalized + " is " + lineCount + ' lines. Use `token-goat skeleton "' + normalized + '"` or `token-goat outline "' + normalized + '"` for structural navigation instead of a future full re-read.'
9280
+ shown + " is " + lineCount + ' lines. Use `token-goat skeleton "' + shown + '"` or `token-goat outline "' + shown + '"` for structural navigation instead of a future full re-read.'
9214
9281
  );
9215
9282
  }
9216
9283
  }
@@ -9688,9 +9755,12 @@ function profileCsv(content, opts = {}) {
9688
9755
  }
9689
9756
  function formatCsvProfile(profiles) {
9690
9757
  return profiles.map((p) => {
9691
- const lines2 = [`${p.name} (${p.inferredType})`, ` nulls: ${p.nullCount} distinct: ${p.distinctCount}`];
9692
- if (p.min !== void 0) lines2.push(` range: ${p.min} .. ${p.max}`);
9693
- if (p.topValues !== void 0) lines2.push(` values: ${p.topValues.map((t) => `${t.value} (${t.count})`).join(", ")}`);
9758
+ const lines2 = [
9759
+ `${displaySafeText(p.name)} (${p.inferredType})`,
9760
+ ` nulls: ${p.nullCount} distinct: ${p.distinctCount}`
9761
+ ];
9762
+ if (p.min !== void 0) lines2.push(` range: ${displaySafeText(p.min)} .. ${displaySafeText(p.max ?? "")}`);
9763
+ if (p.topValues !== void 0) lines2.push(` values: ${p.topValues.map((t) => `${displaySafeText(t.value)} (${t.count})`).join(", ")}`);
9694
9764
  return lines2.join("\n");
9695
9765
  }).join("\n\n");
9696
9766
  }
@@ -10607,7 +10677,7 @@ function extractHtml(content, filePath) {
10607
10677
  }
10608
10678
  }
10609
10679
  sections.sort((a, b) => a.line - b.line);
10610
- const totalLines = code.split("\n").length;
10680
+ const totalLines = countContentLines(code);
10611
10681
  assignFlatEndLines(sections, totalLines);
10612
10682
  const seenId = /* @__PURE__ */ new Set();
10613
10683
  const seenClass = /* @__PURE__ */ new Set();
@@ -10710,7 +10780,7 @@ function extractLiquid(content, filePath, relPath) {
10710
10780
  const stem = path18.basename(resolvedRel, path18.extname(resolvedRel));
10711
10781
  symbols.push({ filePath, name: stem, kind: "liquid_section_file", lineStart: 1, lineEnd: 1, body: "", docstring: "", parent: "" });
10712
10782
  }
10713
- const totalLines = content.split("\n").length;
10783
+ const totalLines = countContentLines(content);
10714
10784
  for (const hm of findHtmlHeadingMatches(content)) {
10715
10785
  if (hm.heading) {
10716
10786
  const line = offsetToLine(lineIndex, hm.offset);
@@ -11687,12 +11757,64 @@ function extractZig(content, filePath) {
11687
11757
 
11688
11758
  // src/languages/r.ts
11689
11759
  var FUNC_ASSIGN_RE = /^([A-Za-z_][A-Za-z0-9_.]*)\s*(?:<-|=)\s*(?:function|\\)\s*\(/;
11690
- var SETCLASS_RE = /setClass\s*\(\s*["']([A-Za-z_][A-Za-z0-9_.]*)/;
11691
- var SETMETHOD_RE = /setMethod\s*\(\s*["']([A-Za-z_][A-Za-z0-9_.]*)/;
11760
+ var SETCLASS_RE = /^(?:[A-Za-z_][A-Za-z0-9_.]*\s*(?:<-|=)\s*)?setClass\s*\(\s*["']([A-Za-z_][A-Za-z0-9_.]*)/;
11761
+ var SETMETHOD_RE = /^(?:[A-Za-z_][A-Za-z0-9_.]*\s*(?:<-|=)\s*)?setMethod\s*\(\s*["']([A-Za-z_][A-Za-z0-9_.]*)/;
11762
+ function matchingParenIndex(content, openIndex) {
11763
+ let depth = 0;
11764
+ let quote = null;
11765
+ for (let i = openIndex; i < content.length; i++) {
11766
+ const ch = content[i];
11767
+ if (quote !== null) {
11768
+ if (ch === "\\") {
11769
+ i++;
11770
+ continue;
11771
+ }
11772
+ if (ch === quote) quote = null;
11773
+ continue;
11774
+ }
11775
+ if (ch === "#") {
11776
+ while (i < content.length && content[i] !== "\n") i++;
11777
+ continue;
11778
+ }
11779
+ if (ch === '"' || ch === "'" || ch === "`") {
11780
+ quote = ch;
11781
+ continue;
11782
+ }
11783
+ if (ch === "(") depth++;
11784
+ else if (ch === ")") {
11785
+ depth--;
11786
+ if (depth === 0) return i;
11787
+ }
11788
+ }
11789
+ return null;
11790
+ }
11791
+ function bracedBodyEndLine(content, lineIndex, parenIndex, totalLines, fallback) {
11792
+ const close = matchingParenIndex(content, parenIndex);
11793
+ if (close === null) return fallback;
11794
+ let j = close + 1;
11795
+ while (j < content.length) {
11796
+ const ch = content[j];
11797
+ if (ch === "#") {
11798
+ while (j < content.length && content[j] !== "\n") j++;
11799
+ continue;
11800
+ }
11801
+ if (ch !== " " && ch !== " " && ch !== "\r" && ch !== "\n") break;
11802
+ j++;
11803
+ }
11804
+ if (content[j] !== "{") return fallback;
11805
+ return findMatchingBraceEndLine(content, j, totalLines, lineIndex, "#");
11806
+ }
11807
+ function callEndLine(content, lineIndex, parenIndex, fallback) {
11808
+ const close = matchingParenIndex(content, parenIndex);
11809
+ return close === null ? fallback : offsetToLine(lineIndex, close);
11810
+ }
11692
11811
  function extractR(content, filePath) {
11693
11812
  const symbols = [];
11694
11813
  const imports = [];
11695
11814
  const lines2 = content.split(/\r?\n/);
11815
+ const lineIndex = buildLineIndex(content);
11816
+ const totalLines = countContentLines(content);
11817
+ const spanBody = (startLine, endLine) => lines2.slice(startLine - 1, endLine).join("\n");
11696
11818
  for (let i = 0; i < lines2.length; i++) {
11697
11819
  const rawLine = lines2[i] ?? "";
11698
11820
  const lineNum = i + 1;
@@ -11705,17 +11827,21 @@ function extractR(content, filePath) {
11705
11827
  if (!isIndented) {
11706
11828
  const fm = FUNC_ASSIGN_RE.exec(stripped);
11707
11829
  if (fm) {
11708
- symbols.push(makeLineSymbol(filePath, fm[1] ?? "", "function", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
11830
+ const parenIndex = (lineIndex[i] ?? 0) + fm[0].length - 1;
11831
+ const endLine = bracedBodyEndLine(content, lineIndex, parenIndex, totalLines, lineNum);
11832
+ symbols.push(makeSpanSymbol(filePath, fm[1] ?? "", "function", { startLine: lineNum, endLine, body: spanBody(lineNum, endLine) }, void 0, lines2, "hash"));
11709
11833
  continue;
11710
11834
  }
11711
11835
  const cm = SETCLASS_RE.exec(stripped);
11712
11836
  if (cm) {
11713
- symbols.push(makeLineSymbol(filePath, cm[1] ?? "", "class", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
11837
+ const endLine = callEndLine(content, lineIndex, (lineIndex[i] ?? 0) + (cm.index + cm[0].indexOf("(")), lineNum);
11838
+ symbols.push(makeSpanSymbol(filePath, cm[1] ?? "", "class", { startLine: lineNum, endLine, body: spanBody(lineNum, endLine) }, void 0, lines2, "hash"));
11714
11839
  continue;
11715
11840
  }
11716
11841
  const mm = SETMETHOD_RE.exec(stripped);
11717
11842
  if (mm) {
11718
- symbols.push(makeLineSymbol(filePath, mm[1] ?? "", "function", lineNum, stripped.slice(0, 200), void 0, lines2, "hash"));
11843
+ const endLine = callEndLine(content, lineIndex, (lineIndex[i] ?? 0) + (mm.index + mm[0].indexOf("(")), lineNum);
11844
+ symbols.push(makeSpanSymbol(filePath, mm[1] ?? "", "function", { startLine: lineNum, endLine, body: spanBody(lineNum, endLine) }, void 0, lines2, "hash"));
11719
11845
  }
11720
11846
  }
11721
11847
  }
@@ -11782,7 +11908,7 @@ function extractGraphql(content, filePath) {
11782
11908
  }
11783
11909
  }
11784
11910
  const stripped = stripHashComments(descriptionsStripped);
11785
- const totalLines = content.split("\n").length;
11911
+ const totalLines = countContentLines(content);
11786
11912
  const lineIndex = buildLineIndex(stripped);
11787
11913
  for (const m of stripped.matchAll(TYPE_RE)) {
11788
11914
  const keyword = m.groups?.["keyword"] ?? "";
@@ -12058,7 +12184,7 @@ function extractSql(content, filePath) {
12058
12184
  const sections = [];
12059
12185
  const seen = /* @__PURE__ */ new Set();
12060
12186
  const emit2 = makeSymbolEmitter(symbols, sections, seen, filePath, MAX_SYMBOLS3, MAX_HEADING_LEN2);
12061
- const totalLines = content.split("\n").length;
12187
+ const totalLines = countContentLines(content);
12062
12188
  const lineIndex = buildLineIndex(content);
12063
12189
  const noStrings = stripSqlStringLiterals(content);
12064
12190
  const singleLineEndLines = /* @__PURE__ */ new Map();
@@ -12260,7 +12386,7 @@ function extractMakefile(content, filePath) {
12260
12386
  const seen = /* @__PURE__ */ new Set();
12261
12387
  const emit2 = makeSymbolEmitter(symbols, sections, seen, filePath, MAX_SYMBOLS5, MAX_HEADING_LEN3);
12262
12388
  const stripped = stripComments(content);
12263
- const totalLines = content.split("\n").length;
12389
+ const totalLines = countContentLines(content);
12264
12390
  const lineIndex = buildLineIndex(stripped);
12265
12391
  const { noContinuation: strippedNoContinuation, forTargets: strippedForTargets } = maskContinuationAndDefines(stripped);
12266
12392
  for (const m of strippedForTargets.matchAll(TARGET_RE)) {
@@ -12340,7 +12466,7 @@ function extractProto(content, filePath) {
12340
12466
  const imports = [];
12341
12467
  const emit2 = makeSymbolEmitter(symbols, sections, seen, filePath, MAX_SYMBOLS6, MAX_HEADING_LEN4);
12342
12468
  const stripped = stripComments2(content);
12343
- const totalLines = content.split("\n").length;
12469
+ const totalLines = countContentLines(content);
12344
12470
  const lineIndex = buildLineIndex(stripped);
12345
12471
  const blockEndLines = /* @__PURE__ */ new Map();
12346
12472
  for (const m of stripped.matchAll(IMPORT_RE4)) {
@@ -12473,7 +12599,7 @@ function extractTerraform(content, filePath) {
12473
12599
  const seen = /* @__PURE__ */ new Set();
12474
12600
  const emit2 = makeSymbolEmitter(symbols, sections, seen, filePath, MAX_SYMBOLS7, MAX_HEADING_LEN5);
12475
12601
  const stripped = stripComments3(maskHeredocs(content));
12476
- const totalLines = content.split("\n").length;
12602
+ const totalLines = countContentLines(content);
12477
12603
  const lineIndex = buildLineIndex(stripped);
12478
12604
  const blockEndLines = /* @__PURE__ */ new Map();
12479
12605
  const matches = [
@@ -13271,8 +13397,8 @@ function extractLwcJavaScript(content, filePath) {
13271
13397
  const sourceLines = lines(content);
13272
13398
  const bundle = bundleName(filePath);
13273
13399
  const symbols = [
13274
- symbol(filePath, bundle, "lwc_bundle", 1, sourceLines.length),
13275
- symbol(filePath, lwcTagAlias(bundle), "lwc_component_alias", 1, sourceLines.length)
13400
+ symbol(filePath, bundle, "lwc_bundle", 1, countContentLines(content)),
13401
+ symbol(filePath, lwcTagAlias(bundle), "lwc_component_alias", 1, countContentLines(content))
13276
13402
  ];
13277
13403
  const refs = [];
13278
13404
  const commentFree = stripJsComments(content);
@@ -13370,10 +13496,9 @@ function attributeRefs(refs, content, filePath, attribute, split = false) {
13370
13496
  function extractSalesforceMarkup(content, filePath) {
13371
13497
  const normalized = filePath.replaceAll("\\", "/");
13372
13498
  const extension = path20.posix.extname(normalized).toLowerCase();
13373
- const sourceLines = lines(content);
13374
13499
  const kind = MARKUP_KIND[extension] ?? "salesforce_markup";
13375
13500
  const symbols = [
13376
- symbol(filePath, markupArtifactName(normalized, extension), kind, 1, sourceLines.length)
13501
+ symbol(filePath, markupArtifactName(normalized, extension), kind, 1, countContentLines(content))
13377
13502
  ];
13378
13503
  const refs = [];
13379
13504
  const isAura = [".cmp", ".app", ".evt", ".intf", ".design", ".auradoc", ".tokens"].includes(extension);
@@ -13525,7 +13650,7 @@ function componentSymbol(filePath, name, kind, totalLines) {
13525
13650
  return { filePath, name, kind, lineStart: 1, lineEnd: totalLines, body: "", docstring: "", parent: "" };
13526
13651
  }
13527
13652
  function extractVue(content, filePath) {
13528
- const totalLines = content.split("\n").length;
13653
+ const totalLines = countContentLines(content);
13529
13654
  const lineIndex = buildLineIndex(content);
13530
13655
  const name = componentName(filePath);
13531
13656
  const symbols = [componentSymbol(filePath, name, "vue_component", totalLines)];
@@ -13540,7 +13665,7 @@ function extractVue(content, filePath) {
13540
13665
  return finalize(symbols, refs);
13541
13666
  }
13542
13667
  function extractSvelte(content, filePath) {
13543
- const totalLines = content.split("\n").length;
13668
+ const totalLines = countContentLines(content);
13544
13669
  const lineIndex = buildLineIndex(content);
13545
13670
  const name = componentName(filePath);
13546
13671
  const symbols = [componentSymbol(filePath, name, "svelte_component", totalLines)];
@@ -13573,7 +13698,7 @@ function detectAstroFrontmatter(content) {
13573
13698
  return null;
13574
13699
  }
13575
13700
  function extractAstro(content, filePath) {
13576
- const totalLines = content.split("\n").length;
13701
+ const totalLines = countContentLines(content);
13577
13702
  const lineIndex = buildLineIndex(content);
13578
13703
  const name = componentName(filePath);
13579
13704
  const symbols = [componentSymbol(filePath, name, "astro_component", totalLines)];
@@ -13788,6 +13913,12 @@ var TSJS_KIND_BY_TYPE = /* @__PURE__ */ new Map([
13788
13913
  ["method_signature", "method"],
13789
13914
  ["property_signature", "var"],
13790
13915
  ["abstract_method_signature", "method"],
13916
+ // `declare function f(): void` parses as a `function_signature` inside an
13917
+ // ambient_declaration -- a distinct node type from `function_declaration`, which always
13918
+ // carries a body. Without an entry here every ambient function in a .d.ts file was
13919
+ // missing from the index entirely, the same container-drop shape as the module entries
13920
+ // below rather than a wrong span.
13921
+ ["function_signature", "function"],
13791
13922
  // `namespace Foo { ... }` (and the legacy `module Foo { ... }` synonym) parses as
13792
13923
  // `internal_module`; `declare module "some-string" { ... }` (an ambient module declaration,
13793
13924
  // common in .d.ts files) parses as `module` -- a distinct node type from either. Neither had a
@@ -13836,15 +13967,41 @@ function nodeName(node) {
13836
13967
  if (named !== null) return named.text;
13837
13968
  return null;
13838
13969
  }
13970
+ var SPEC_DECLARATION_OWNER = /* @__PURE__ */ new Map([
13971
+ ["variable_declarator", /* @__PURE__ */ new Set(["lexical_declaration", "variable_declaration"])],
13972
+ ["var_spec", /* @__PURE__ */ new Set(["var_declaration"])],
13973
+ ["const_spec", /* @__PURE__ */ new Set(["const_declaration"])],
13974
+ ["type_spec", /* @__PURE__ */ new Set(["type_declaration"])]
13975
+ ]);
13976
+ var PREFIX_WRAPPER_TYPES = /* @__PURE__ */ new Set(["export_statement", "ambient_declaration"]);
13977
+ function widenToDeclaration(node) {
13978
+ let widened = node;
13979
+ const owner = SPEC_DECLARATION_OWNER.get(widened.type);
13980
+ if (owner !== void 0) {
13981
+ const decl = widened.parent;
13982
+ if (decl === null || !owner.has(decl.type)) return widened;
13983
+ let specs = 0;
13984
+ for (const c of decl.namedChildren) if (c.type === widened.type) specs++;
13985
+ if (specs !== 1) return widened;
13986
+ if (decl.startPosition.row !== widened.startPosition.row) return widened;
13987
+ widened = decl;
13988
+ }
13989
+ for (; ; ) {
13990
+ const parent = widened.parent;
13991
+ if (parent === null || !PREFIX_WRAPPER_TYPES.has(parent.type)) return widened;
13992
+ widened = parent;
13993
+ }
13994
+ }
13839
13995
  function makeSymbol(filePath, name, kind, node, lines2, style) {
13840
- const lineStart = node.startPosition.row + 1;
13996
+ const ranged = widenToDeclaration(node);
13997
+ const lineStart = ranged.startPosition.row + 1;
13841
13998
  return {
13842
13999
  filePath,
13843
14000
  name,
13844
14001
  kind,
13845
14002
  lineStart,
13846
- lineEnd: node.endPosition.row + 1,
13847
- body: node.text,
14003
+ lineEnd: ranged.endPosition.row + 1,
14004
+ body: ranged.text,
13848
14005
  docstring: lines2 !== void 0 && style !== void 0 ? precedingDocComment(lines2, lineStart, style) : "",
13849
14006
  parent: ""
13850
14007
  };
@@ -13883,13 +14040,23 @@ function extractTsJsSymbols(root, filePath, lines2) {
13883
14040
  out.push(makeSymbol(filePath, name, kind, node, lines2, "c"));
13884
14041
  } else {
13885
14042
  const lineStart = decorators[0].startPosition.row + 1;
14043
+ const decoratedEnd = widenToDeclaration(node).endPosition.row + 1;
13886
14044
  out.push({
13887
14045
  filePath,
13888
14046
  name,
13889
14047
  kind,
13890
14048
  lineStart,
13891
- lineEnd: node.endPosition.row + 1,
13892
- body: [...decorators, node].map((n) => n.text).join("\n"),
14049
+ lineEnd: decoratedEnd,
14050
+ // Read the body off the file rather than gluing the decorator and the node
14051
+ // together with a newline: `@dec export class X {}` has no newline between
14052
+ // them, and the glued form both invents one and drops the `export` that sits
14053
+ // between the two nodes. A decorated declaration yields one symbol, so taking
14054
+ // its whole span cannot fan out.
14055
+ // The trailing replace keeps a convention the rest of the index follows: a
14056
+ // tree-sitter node never carries the indentation of its own first line,
14057
+ // because it starts at the first real character. Reading the span off the
14058
+ // file would otherwise make decorated symbols the one shape that does.
14059
+ body: lines2.slice(lineStart - 1, decoratedEnd).join("\n").replace(/^[ \t]+/, ""),
13893
14060
  docstring: precedingDocComment(lines2, lineStart, "c"),
13894
14061
  parent: ""
13895
14062
  });
@@ -13906,8 +14073,11 @@ function extractTsJsSymbols(root, filePath, lines2) {
13906
14073
  const isFn = value !== null && (value.type === "arrow_function" || value.type === "function_expression" || value.type === "function");
13907
14074
  out.push(makeSymbol(filePath, name.text, isFn ? "function" : "variable", child, lines2, "c"));
13908
14075
  } else {
13909
- for (const bound of collectPatternBindings(name)) {
13910
- out.push(makeSymbol(filePath, bound, "variable", child, lines2, "c"));
14076
+ const bindings = collectPatternBindings(name);
14077
+ const elideBodies = bindings.length > 1 && bindings.length * child.text.length > MAX_SYMBOL_BODY_CHARS;
14078
+ for (const bound of bindings) {
14079
+ const sym = makeSymbol(filePath, bound, "variable", child, lines2, "c");
14080
+ out.push(elideBodies ? { ...sym, body: "" } : sym);
13911
14081
  }
13912
14082
  }
13913
14083
  }
@@ -15419,7 +15589,7 @@ function indexFileSync(filePath, dbPath = globalDbPath(), preReadBytes) {
15419
15589
  throw err;
15420
15590
  }
15421
15591
  }
15422
- const content = raw.toString("utf8");
15592
+ const content = decodeSource(raw);
15423
15593
  const { symbols, refs } = parseContent(content, filePath, language);
15424
15594
  writeParseResult(filePath, raw, { symbols, refs, language, duration: 0 }, dbPath);
15425
15595
  }
@@ -15444,6 +15614,19 @@ var UNAVAILABLE_EMBED_SHA_PREFIX = "unavailable:";
15444
15614
  function unavailableEmbedSha(sha) {
15445
15615
  return UNAVAILABLE_EMBED_SHA_PREFIX + sha;
15446
15616
  }
15617
+ function indexedPathSpellingIsStale(storedPath, absPath) {
15618
+ if (!isCaseInsensitiveFs()) return false;
15619
+ const stored = normalizePath(storedPath);
15620
+ const candidate = normalizePath(path22.resolve(absPath));
15621
+ if (foldPath(stored) !== foldPath(candidate)) return false;
15622
+ let real;
15623
+ try {
15624
+ real = normalizePath(fs21.realpathSync.native(absPath));
15625
+ } catch {
15626
+ return false;
15627
+ }
15628
+ return real !== stored && foldPath(real) === foldPath(stored);
15629
+ }
15447
15630
  function isEmbedFresh(storedEmbedSha, sha, embeddingsEnabled, depsAvailable) {
15448
15631
  if (storedEmbedSha === void 0) return false;
15449
15632
  if (!embeddingsEnabled) return storedEmbedSha === disabledEmbedSha(sha);
@@ -15488,7 +15671,7 @@ async function indexFileEmbeddings(filePath, dbPath = globalDbPath(), sha, onErr
15488
15671
  }
15489
15672
  let content;
15490
15673
  try {
15491
- content = await fs21.promises.readFile(filePath, "utf8");
15674
+ content = decodeSource(await fs21.promises.readFile(filePath));
15492
15675
  } catch {
15493
15676
  return;
15494
15677
  }
@@ -15626,6 +15809,41 @@ function pruneSystemTempFiles(dbPath = globalDbPath()) {
15626
15809
  const db = getDb(dbPath);
15627
15810
  return removeFilesBestEffort(db, findSystemTempFiles(dbPath));
15628
15811
  }
15812
+ function findOrphanedChunkPaths(dbPath = globalDbPath()) {
15813
+ return orphanedChunkGroups(getDb(dbPath)).map((g) => g.representative);
15814
+ }
15815
+ function orphanedChunkGroups(db) {
15816
+ const known = new Set(
15817
+ db.prepare("SELECT DISTINCT path FROM files").all().map(
15818
+ (r) => foldPath(normalizePath(r.path))
15819
+ )
15820
+ );
15821
+ const rows = db.prepare("SELECT DISTINCT file_path FROM chunks").all();
15822
+ const byFolded = /* @__PURE__ */ new Map();
15823
+ for (const { file_path: raw } of rows) {
15824
+ const folded = foldPath(normalizePath(raw));
15825
+ if (known.has(folded)) continue;
15826
+ const group = byFolded.get(folded);
15827
+ if (group === void 0) byFolded.set(folded, { representative: raw, spellings: [raw] });
15828
+ else group.spellings.push(raw);
15829
+ }
15830
+ return [...byFolded.values()];
15831
+ }
15832
+ function pruneOrphanedChunks(dbPath = globalDbPath()) {
15833
+ const db = getDb(dbPath);
15834
+ const removed = [];
15835
+ const run = db.transaction(() => {
15836
+ for (const group of orphanedChunkGroups(db)) {
15837
+ try {
15838
+ for (const spelling of group.spellings) deleteFileEmbeddings(db, spelling);
15839
+ removed.push(group.representative);
15840
+ } catch {
15841
+ }
15842
+ }
15843
+ });
15844
+ run.immediate();
15845
+ return removed;
15846
+ }
15629
15847
  function recordKnownRoot(filePath, dbPath = globalDbPath()) {
15630
15848
  const project = findProject(path23.dirname(filePath));
15631
15849
  if (project === null || isTooShallowToPrune(project.root)) return;
@@ -15680,7 +15898,8 @@ function sweepKnownRoots(dbPath = globalDbPath(), opts) {
15680
15898
  prunedRows += removeDeletedFilesBestEffort(db, deletable).length;
15681
15899
  prunedRoots.push(root);
15682
15900
  }
15683
- return { prunedRows, prunedRoots, flaggedRoots };
15901
+ const prunedOrphanChunkPaths = pruneOrphanedChunks(dbPath);
15902
+ return { prunedRows, prunedRoots, flaggedRoots, prunedOrphanChunkPaths };
15684
15903
  }
15685
15904
  var KNOWN_ROOT_RECORD_MIN_INTERVAL_MS = 60 * 60 * 1e3;
15686
15905
  function knownRootRecordMarkerPath(dir, filePath) {
@@ -15821,11 +16040,25 @@ function pidFileIsWithinStartupGrace(dir) {
15821
16040
  return false;
15822
16041
  }
15823
16042
  }
16043
+ var ENCODED_LINE_MARKER = "!";
16044
+ function encodeDirtyQueueLine(absPath) {
16045
+ const needsEncoding = /[\r\n]/.test(absPath) || absPath !== absPath.trim();
16046
+ return needsEncoding ? ENCODED_LINE_MARKER + JSON.stringify(absPath) : absPath;
16047
+ }
16048
+ function decodeDirtyQueueLine(line) {
16049
+ if (!line.startsWith(ENCODED_LINE_MARKER)) return line;
16050
+ try {
16051
+ const decoded = JSON.parse(line.slice(ENCODED_LINE_MARKER.length));
16052
+ return typeof decoded === "string" ? decoded : line;
16053
+ } catch {
16054
+ return line;
16055
+ }
16056
+ }
15824
16057
  function parseDirtyQueueLines(raw) {
15825
16058
  const seen = /* @__PURE__ */ new Set();
15826
16059
  const out = [];
15827
16060
  for (const line of raw.split("\n")) {
15828
- const trimmed = line.trim();
16061
+ const trimmed = decodeDirtyQueueLine(line.trim());
15829
16062
  if (trimmed === "") continue;
15830
16063
  const normalized = normalizePath(trimmed);
15831
16064
  const dedupeKey = foldPath(normalized);
@@ -15913,10 +16146,10 @@ function embedFileSerialized(absPath, dbPath, sha) {
15913
16146
  activeEmbedSlots += 1;
15914
16147
  return dispatchEmbed();
15915
16148
  }
15916
- return new Promise((resolve9) => {
16149
+ return new Promise((resolve10) => {
15917
16150
  embedSlotWaiters.push(() => {
15918
16151
  activeEmbedSlots += 1;
15919
- resolve9(dispatchEmbed());
16152
+ resolve10(dispatchEmbed());
15920
16153
  });
15921
16154
  });
15922
16155
  };
@@ -15970,7 +16203,7 @@ function appendToDirtyQueue(dir, absPath) {
15970
16203
  if (existing.length > 0 && !existing.endsWith("\n")) leadingNewline = "\n";
15971
16204
  } catch {
15972
16205
  }
15973
- fs23.appendFileSync(queuePath, `${leadingNewline}${absPath}
16206
+ fs23.appendFileSync(queuePath, `${leadingNewline}${encodeDirtyQueueLine(absPath)}
15974
16207
  `);
15975
16208
  } catch {
15976
16209
  }
@@ -15988,7 +16221,7 @@ function makeIndexer(dbPath) {
15988
16221
  return true;
15989
16222
  }
15990
16223
  const entry = getFileEntry(absPath, dbPath);
15991
- const parseUnchanged = entry?.sha === sha;
16224
+ const parseUnchanged = entry?.sha === sha && !(entry !== null && indexedPathSpellingIsStale(entry.filePath, absPath));
15992
16225
  if (!parseUnchanged) {
15993
16226
  indexFileSync(absPath, dbPath);
15994
16227
  }
@@ -16021,7 +16254,7 @@ function processDirtyBatch(paths, index = makeIndexer(globalDbPath()), remove =
16021
16254
  if (!p) continue;
16022
16255
  writeDrainHeartbeat(dir);
16023
16256
  if (isUnderBlockedRoot(p, blockedRoots)) continue;
16024
- if (!fs23.existsSync(p)) {
16257
+ if (fileIsAbsent(p)) {
16025
16258
  remove(p);
16026
16259
  continue;
16027
16260
  }
@@ -16253,6 +16486,12 @@ function claimWorkerPidFile(dir, pid) {
16253
16486
  if (existingPid !== null && pidAlive(existingPid) && (hasFreshWorkerHeartbeat(dir, existingPid) || pidFileIsWithinStartupGrace(dir))) {
16254
16487
  return false;
16255
16488
  }
16489
+ if (existingPid !== null && pidAlive(existingPid) && existingPid !== pid && existingPid !== process.pid) {
16490
+ try {
16491
+ process.kill(existingPid, "SIGTERM");
16492
+ } catch {
16493
+ }
16494
+ }
16256
16495
  try {
16257
16496
  fs23.rmSync(pidPath, { force: true });
16258
16497
  } catch {
@@ -16352,7 +16591,7 @@ async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
16352
16591
  lastKnownRootsSweepMs = Date.now();
16353
16592
  }
16354
16593
  if (shouldStop()) break;
16355
- await new Promise((resolve9) => setTimeout(resolve9, pollIntervalMs));
16594
+ await new Promise((resolve10) => setTimeout(resolve10, pollIntervalMs));
16356
16595
  }
16357
16596
  }
16358
16597
  function runDetachedWorkerDaemon() {
@@ -16391,7 +16630,7 @@ function appendDirtyPath(normalizedPath2) {
16391
16630
  if (existing.length > 0 && !existing.endsWith("\n")) leadingNewline = "\n";
16392
16631
  } catch {
16393
16632
  }
16394
- fs24.appendFileSync(queuePath, `${leadingNewline}${normalizedPath2}
16633
+ fs24.appendFileSync(queuePath, `${leadingNewline}${encodeDirtyQueueLine(normalizedPath2)}
16395
16634
  `);
16396
16635
  }
16397
16636
  function enqueueDirtyPathSafe(filePath, opts) {
@@ -16482,7 +16721,7 @@ import * as readline from "node:readline";
16482
16721
  async function defaultConfirm(question) {
16483
16722
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
16484
16723
  try {
16485
- const answer = await new Promise((resolve9) => rl.question(question, resolve9));
16724
+ const answer = await new Promise((resolve10) => rl.question(question, resolve10));
16486
16725
  return /^y(es)?$/i.test(answer.trim());
16487
16726
  } finally {
16488
16727
  rl.close();
@@ -17533,6 +17772,7 @@ export {
17533
17772
  tomlBracketDelta,
17534
17773
  isParseSkipEligible,
17535
17774
  indexFileSync,
17775
+ indexedPathSpellingIsStale,
17536
17776
  isEmbedFresh,
17537
17777
  indexFileEmbeddings,
17538
17778
  removeFileFromIndex,
@@ -17540,6 +17780,8 @@ export {
17540
17780
  pruneBlockedRoot,
17541
17781
  findSystemTempFiles,
17542
17782
  pruneSystemTempFiles,
17783
+ findOrphanedChunkPaths,
17784
+ pruneOrphanedChunks,
17543
17785
  recordKnownRootThrottled,
17544
17786
  WORKER_HEARTBEAT_STALE_MS,
17545
17787
  dirtyQueuePathFor,