opencode-codebase-index 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -496,7 +496,7 @@ var require_ignore = __commonJS({
496
496
  // path matching.
497
497
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
498
498
  // @returns {TestResult} true if a file is ignored
499
- test(path25, checkUnignored, mode) {
499
+ test(path26, checkUnignored, mode) {
500
500
  let ignored = false;
501
501
  let unignored = false;
502
502
  let matchedRule;
@@ -505,7 +505,7 @@ var require_ignore = __commonJS({
505
505
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
506
506
  return;
507
507
  }
508
- const matched = rule[mode].test(path25);
508
+ const matched = rule[mode].test(path26);
509
509
  if (!matched) {
510
510
  return;
511
511
  }
@@ -526,17 +526,17 @@ var require_ignore = __commonJS({
526
526
  var throwError = (message, Ctor) => {
527
527
  throw new Ctor(message);
528
528
  };
529
- var checkPath = (path25, originalPath, doThrow) => {
530
- if (!isString(path25)) {
529
+ var checkPath = (path26, originalPath, doThrow) => {
530
+ if (!isString(path26)) {
531
531
  return doThrow(
532
532
  `path must be a string, but got \`${originalPath}\``,
533
533
  TypeError
534
534
  );
535
535
  }
536
- if (!path25) {
536
+ if (!path26) {
537
537
  return doThrow(`path must not be empty`, TypeError);
538
538
  }
539
- if (checkPath.isNotRelative(path25)) {
539
+ if (checkPath.isNotRelative(path26)) {
540
540
  const r = "`path.relative()`d";
541
541
  return doThrow(
542
542
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -545,7 +545,7 @@ var require_ignore = __commonJS({
545
545
  }
546
546
  return true;
547
547
  };
548
- var isNotRelative = (path25) => REGEX_TEST_INVALID_PATH.test(path25);
548
+ var isNotRelative = (path26) => REGEX_TEST_INVALID_PATH.test(path26);
549
549
  checkPath.isNotRelative = isNotRelative;
550
550
  checkPath.convert = (p) => p;
551
551
  var Ignore2 = class {
@@ -575,19 +575,19 @@ var require_ignore = __commonJS({
575
575
  }
576
576
  // @returns {TestResult}
577
577
  _test(originalPath, cache, checkUnignored, slices) {
578
- const path25 = originalPath && checkPath.convert(originalPath);
578
+ const path26 = originalPath && checkPath.convert(originalPath);
579
579
  checkPath(
580
- path25,
580
+ path26,
581
581
  originalPath,
582
582
  this._strictPathCheck ? throwError : RETURN_FALSE
583
583
  );
584
- return this._t(path25, cache, checkUnignored, slices);
584
+ return this._t(path26, cache, checkUnignored, slices);
585
585
  }
586
- checkIgnore(path25) {
587
- if (!REGEX_TEST_TRAILING_SLASH.test(path25)) {
588
- return this.test(path25);
586
+ checkIgnore(path26) {
587
+ if (!REGEX_TEST_TRAILING_SLASH.test(path26)) {
588
+ return this.test(path26);
589
589
  }
590
- const slices = path25.split(SLASH2).filter(Boolean);
590
+ const slices = path26.split(SLASH2).filter(Boolean);
591
591
  slices.pop();
592
592
  if (slices.length) {
593
593
  const parent = this._t(
@@ -600,18 +600,18 @@ var require_ignore = __commonJS({
600
600
  return parent;
601
601
  }
602
602
  }
603
- return this._rules.test(path25, false, MODE_CHECK_IGNORE);
603
+ return this._rules.test(path26, false, MODE_CHECK_IGNORE);
604
604
  }
605
- _t(path25, cache, checkUnignored, slices) {
606
- if (path25 in cache) {
607
- return cache[path25];
605
+ _t(path26, cache, checkUnignored, slices) {
606
+ if (path26 in cache) {
607
+ return cache[path26];
608
608
  }
609
609
  if (!slices) {
610
- slices = path25.split(SLASH2).filter(Boolean);
610
+ slices = path26.split(SLASH2).filter(Boolean);
611
611
  }
612
612
  slices.pop();
613
613
  if (!slices.length) {
614
- return cache[path25] = this._rules.test(path25, checkUnignored, MODE_IGNORE);
614
+ return cache[path26] = this._rules.test(path26, checkUnignored, MODE_IGNORE);
615
615
  }
616
616
  const parent = this._t(
617
617
  slices.join(SLASH2) + SLASH2,
@@ -619,29 +619,29 @@ var require_ignore = __commonJS({
619
619
  checkUnignored,
620
620
  slices
621
621
  );
622
- return cache[path25] = parent.ignored ? parent : this._rules.test(path25, checkUnignored, MODE_IGNORE);
622
+ return cache[path26] = parent.ignored ? parent : this._rules.test(path26, checkUnignored, MODE_IGNORE);
623
623
  }
624
- ignores(path25) {
625
- return this._test(path25, this._ignoreCache, false).ignored;
624
+ ignores(path26) {
625
+ return this._test(path26, this._ignoreCache, false).ignored;
626
626
  }
627
627
  createFilter() {
628
- return (path25) => !this.ignores(path25);
628
+ return (path26) => !this.ignores(path26);
629
629
  }
630
630
  filter(paths) {
631
631
  return makeArray(paths).filter(this.createFilter());
632
632
  }
633
633
  // @returns {TestResult}
634
- test(path25) {
635
- return this._test(path25, this._testCache, true);
634
+ test(path26) {
635
+ return this._test(path26, this._testCache, true);
636
636
  }
637
637
  };
638
638
  var factory = (options) => new Ignore2(options);
639
- var isPathValid = (path25) => checkPath(path25 && checkPath.convert(path25), path25, RETURN_FALSE);
639
+ var isPathValid = (path26) => checkPath(path26 && checkPath.convert(path26), path26, RETURN_FALSE);
640
640
  var setupWindows = () => {
641
641
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
642
642
  checkPath.convert = makePosix;
643
643
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
644
- checkPath.isNotRelative = (path25) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path25) || isNotRelative(path25);
644
+ checkPath.isNotRelative = (path26) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path26) || isNotRelative(path26);
645
645
  };
646
646
  if (
647
647
  // Detect `process` so that it can run in browsers.
@@ -667,7 +667,7 @@ module.exports = __toCommonJS(cli_exports);
667
667
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
668
668
  var import_fs15 = require("fs");
669
669
  var os5 = __toESM(require("os"), 1);
670
- var path24 = __toESM(require("path"), 1);
670
+ var path25 = __toESM(require("path"), 1);
671
671
  var import_url2 = require("url");
672
672
 
673
673
  // src/config/constants.ts
@@ -800,7 +800,8 @@ function getDefaultIndexingConfig() {
800
800
  requireProjectMarker: true,
801
801
  maxDepth: 5,
802
802
  maxFilesPerDirectory: 100,
803
- fallbackToTextOnMaxChunks: true
803
+ fallbackToTextOnMaxChunks: true,
804
+ gitBlame: { enabled: false }
804
805
  };
805
806
  }
806
807
  function getDefaultSearchConfig() {
@@ -924,7 +925,10 @@ function parseConfig(raw) {
924
925
  requireProjectMarker: typeof rawIndexing.requireProjectMarker === "boolean" ? rawIndexing.requireProjectMarker : defaultIndexing.requireProjectMarker,
925
926
  maxDepth: typeof rawIndexing.maxDepth === "number" ? rawIndexing.maxDepth < -1 ? -1 : rawIndexing.maxDepth : defaultIndexing.maxDepth,
926
927
  maxFilesPerDirectory: typeof rawIndexing.maxFilesPerDirectory === "number" ? Math.max(1, rawIndexing.maxFilesPerDirectory) : defaultIndexing.maxFilesPerDirectory,
927
- fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks
928
+ fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks,
929
+ gitBlame: {
930
+ enabled: rawIndexing.gitBlame && typeof rawIndexing.gitBlame === "object" && typeof rawIndexing.gitBlame.enabled === "boolean" ? rawIndexing.gitBlame.enabled : defaultIndexing.gitBlame.enabled
931
+ }
928
932
  };
929
933
  const rawSearch = input.search && typeof input.search === "object" ? input.search : {};
930
934
  const search = {
@@ -1109,9 +1113,9 @@ var import_fs = require("fs");
1109
1113
  var path = __toESM(require("path"), 1);
1110
1114
 
1111
1115
  // src/eval/report-formatters.ts
1112
- function assertFiniteNumber(value, path25) {
1116
+ function assertFiniteNumber(value, path26) {
1113
1117
  if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
1114
- throw new Error(`${path25} must be a finite number`);
1118
+ throw new Error(`${path26} must be a finite number`);
1115
1119
  }
1116
1120
  return value;
1117
1121
  }
@@ -1300,15 +1304,15 @@ function buildPerQueryArtifact(perQuery) {
1300
1304
 
1301
1305
  // src/eval/runner.ts
1302
1306
  var import_fs10 = require("fs");
1303
- var path13 = __toESM(require("path"), 1);
1307
+ var path14 = __toESM(require("path"), 1);
1304
1308
  var import_perf_hooks2 = require("perf_hooks");
1305
1309
 
1306
1310
  // src/indexer/index.ts
1307
1311
  var import_fs7 = require("fs");
1308
- var path10 = __toESM(require("path"), 1);
1312
+ var path11 = __toESM(require("path"), 1);
1309
1313
  var import_perf_hooks = require("perf_hooks");
1310
- var import_child_process2 = require("child_process");
1311
- var import_util2 = require("util");
1314
+ var import_child_process3 = require("child_process");
1315
+ var import_util3 = require("util");
1312
1316
 
1313
1317
  // node_modules/eventemitter3/index.mjs
1314
1318
  var import_index = __toESM(require_eventemitter3(), 1);
@@ -4839,8 +4843,8 @@ function normalizeFiles(rawFiles, projectRoot) {
4839
4843
  const trimmed = raw.trim();
4840
4844
  if (!trimmed) continue;
4841
4845
  const absolute = path9.resolve(projectRoot, trimmed);
4842
- const relative10 = path9.relative(projectRoot, absolute);
4843
- const cleaned = relative10.startsWith("./") ? relative10.slice(2) : relative10;
4846
+ const relative11 = path9.relative(projectRoot, absolute);
4847
+ const cleaned = relative11.startsWith("./") ? relative11.slice(2) : relative11;
4844
4848
  if (!seen.has(cleaned)) {
4845
4849
  seen.add(cleaned);
4846
4850
  result.push(cleaned);
@@ -4849,8 +4853,61 @@ function normalizeFiles(rawFiles, projectRoot) {
4849
4853
  return result;
4850
4854
  }
4851
4855
 
4856
+ // src/indexer/git-blame.ts
4857
+ var import_child_process2 = require("child_process");
4858
+ var path10 = __toESM(require("path"), 1);
4859
+ var import_util2 = require("util");
4860
+ var execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
4861
+ function parseGitBlamePorcelain(output) {
4862
+ const commits = /* @__PURE__ */ new Map();
4863
+ let current;
4864
+ for (const line of output.split("\n")) {
4865
+ if (/^[0-9a-f]{40} /.test(line)) {
4866
+ const sha = line.slice(0, 40);
4867
+ current = commits.get(sha) ?? {
4868
+ sha,
4869
+ author: "",
4870
+ authorEmail: "",
4871
+ committedAt: 0,
4872
+ summary: "",
4873
+ lines: 0
4874
+ };
4875
+ commits.set(sha, current);
4876
+ continue;
4877
+ }
4878
+ if (!current) {
4879
+ continue;
4880
+ }
4881
+ if (line.startsWith("author ")) {
4882
+ current.author = line.slice("author ".length);
4883
+ } else if (line.startsWith("author-mail ")) {
4884
+ current.authorEmail = line.slice("author-mail ".length).replace(/^<|>$/g, "");
4885
+ } else if (line.startsWith("author-time ")) {
4886
+ current.committedAt = Number.parseInt(line.slice("author-time ".length), 10);
4887
+ } else if (line.startsWith("summary ")) {
4888
+ current.summary = line.slice("summary ".length);
4889
+ } else if (line.startsWith(" ")) {
4890
+ current.lines += 1;
4891
+ }
4892
+ }
4893
+ return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
4894
+ }
4895
+ async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
4896
+ const relativePath = path10.relative(projectRoot, filePath);
4897
+ try {
4898
+ const { stdout } = await execFileAsync2(
4899
+ "git",
4900
+ ["blame", "--line-porcelain", "-L", `${startLine},${endLine}`, "--", relativePath],
4901
+ { cwd: projectRoot, timeout: 3e4 }
4902
+ );
4903
+ return parseGitBlamePorcelain(stdout);
4904
+ } catch {
4905
+ return void 0;
4906
+ }
4907
+ }
4908
+
4852
4909
  // src/indexer/index.ts
4853
- var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
4910
+ var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab", "bash"]);
4854
4911
  var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
4855
4912
  var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
4856
4913
  "function_declaration",
@@ -4930,6 +4987,45 @@ function isSqliteCorruptionError(error) {
4930
4987
  return message.includes("database disk image is malformed") || message.includes("file is not a database") || message.includes("database schema is corrupt") || message.includes("sqlite_corrupt");
4931
4988
  }
4932
4989
  var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
4990
+ function metadataFromBlame(blame) {
4991
+ if (!blame) {
4992
+ return {};
4993
+ }
4994
+ return {
4995
+ blameSha: blame.sha,
4996
+ blameAuthor: blame.author,
4997
+ blameAuthorEmail: blame.authorEmail,
4998
+ blameCommittedAt: blame.committedAt,
4999
+ blameSummary: blame.summary
5000
+ };
5001
+ }
5002
+ function blameFromChunkData(chunk) {
5003
+ if (!chunk?.blameSha || !chunk.blameAuthor || !chunk.blameAuthorEmail || chunk.blameCommittedAt === void 0 || !chunk.blameSummary) {
5004
+ return void 0;
5005
+ }
5006
+ return {
5007
+ sha: chunk.blameSha,
5008
+ author: chunk.blameAuthor,
5009
+ authorEmail: chunk.blameAuthorEmail,
5010
+ committedAt: chunk.blameCommittedAt,
5011
+ summary: chunk.blameSummary
5012
+ };
5013
+ }
5014
+ function blameFromMetadata(metadata) {
5015
+ if (!metadata.blameSha || !metadata.blameAuthor || !metadata.blameAuthorEmail || metadata.blameCommittedAt === void 0 || !metadata.blameSummary) {
5016
+ return void 0;
5017
+ }
5018
+ return {
5019
+ sha: metadata.blameSha,
5020
+ author: metadata.blameAuthor,
5021
+ authorEmail: metadata.blameAuthorEmail,
5022
+ committedAt: metadata.blameCommittedAt,
5023
+ summary: metadata.blameSummary
5024
+ };
5025
+ }
5026
+ function hasBlameMetadata(metadata) {
5027
+ return blameFromMetadata(metadata) !== void 0;
5028
+ }
4933
5029
  var INDEX_METADATA_VERSION = "1";
4934
5030
  var EMBEDDING_STRATEGY_VERSION = "2";
4935
5031
  var RANKING_TOKEN_CACHE_LIMIT = 4096;
@@ -5088,9 +5184,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
5088
5184
  return true;
5089
5185
  }
5090
5186
  function isPathWithinRoot(filePath, rootPath) {
5091
- const normalizedFilePath = path10.resolve(filePath);
5092
- const normalizedRoot = path10.resolve(rootPath);
5093
- return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path10.sep}`);
5187
+ const normalizedFilePath = path11.resolve(filePath);
5188
+ const normalizedRoot = path11.resolve(rootPath);
5189
+ return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path11.sep}`);
5094
5190
  }
5095
5191
  var rankingQueryTokenCache = /* @__PURE__ */ new Map();
5096
5192
  var rankingNameTokenCache = /* @__PURE__ */ new Map();
@@ -5849,7 +5945,8 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
5849
5945
  chunkType,
5850
5946
  name: chunk.name ?? void 0,
5851
5947
  language: chunk.language,
5852
- hash: chunk.contentHash
5948
+ hash: chunk.contentHash,
5949
+ ...metadataFromBlame(blameFromChunkData(chunk))
5853
5950
  };
5854
5951
  const baselineScore = existing?.score ?? 0.5;
5855
5952
  candidateUnion.set(chunk.chunkId, {
@@ -5933,7 +6030,8 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
5933
6030
  chunkType,
5934
6031
  name: chunk.name ?? void 0,
5935
6032
  language: chunk.language,
5936
- hash: chunk.contentHash
6033
+ hash: chunk.contentHash,
6034
+ ...metadataFromBlame(blameFromChunkData(chunk))
5937
6035
  }
5938
6036
  });
5939
6037
  }
@@ -6102,6 +6200,21 @@ function matchesSearchFilters(candidate, options, minScore) {
6102
6200
  if (options?.chunkType && candidate.metadata.chunkType !== options.chunkType) {
6103
6201
  return false;
6104
6202
  }
6203
+ if (options?.blameAuthor) {
6204
+ const author = options.blameAuthor.toLowerCase();
6205
+ const candidateAuthor = candidate.metadata.blameAuthor?.toLowerCase();
6206
+ const candidateEmail = candidate.metadata.blameAuthorEmail?.toLowerCase();
6207
+ if (candidateAuthor !== author && candidateEmail !== author) return false;
6208
+ }
6209
+ if (options?.blameSha && !candidate.metadata.blameSha?.toLowerCase().startsWith(options.blameSha.toLowerCase())) {
6210
+ return false;
6211
+ }
6212
+ if (options?.blameSince) {
6213
+ const sinceMs = Date.parse(options.blameSince);
6214
+ if (Number.isNaN(sinceMs)) return false;
6215
+ const committedAt = candidate.metadata.blameCommittedAt;
6216
+ if (committedAt === void 0 || committedAt < Math.floor(sinceMs / 1e3)) return false;
6217
+ }
6105
6218
  return true;
6106
6219
  }
6107
6220
  function unionCandidates(semanticCandidates, keywordCandidates) {
@@ -6145,9 +6258,9 @@ var Indexer = class {
6145
6258
  this.config = config;
6146
6259
  this.host = host;
6147
6260
  this.indexPath = this.getIndexPath();
6148
- this.fileHashCachePath = path10.join(this.indexPath, "file-hashes.json");
6149
- this.failedBatchesPath = path10.join(this.indexPath, "failed-batches.json");
6150
- this.indexingLockPath = path10.join(this.indexPath, "indexing.lock");
6261
+ this.fileHashCachePath = path11.join(this.indexPath, "file-hashes.json");
6262
+ this.failedBatchesPath = path11.join(this.indexPath, "failed-batches.json");
6263
+ this.indexingLockPath = path11.join(this.indexPath, "indexing.lock");
6151
6264
  this.logger = initializeLogger(config.debug);
6152
6265
  }
6153
6266
  getIndexPath() {
@@ -6179,14 +6292,14 @@ var Indexer = class {
6179
6292
  }
6180
6293
  atomicWriteSync(targetPath, data) {
6181
6294
  const tempPath = `${targetPath}.tmp`;
6182
- (0, import_fs7.mkdirSync)(path10.dirname(targetPath), { recursive: true });
6295
+ (0, import_fs7.mkdirSync)(path11.dirname(targetPath), { recursive: true });
6183
6296
  (0, import_fs7.writeFileSync)(tempPath, data);
6184
6297
  (0, import_fs7.renameSync)(tempPath, targetPath);
6185
6298
  }
6186
6299
  getScopedRoots() {
6187
- const roots = /* @__PURE__ */ new Set([path10.resolve(this.projectRoot)]);
6300
+ const roots = /* @__PURE__ */ new Set([path11.resolve(this.projectRoot)]);
6188
6301
  for (const kbRoot of this.config.knowledgeBases) {
6189
- roots.add(path10.resolve(this.projectRoot, kbRoot));
6302
+ roots.add(path11.resolve(this.projectRoot, kbRoot));
6190
6303
  }
6191
6304
  return Array.from(roots);
6192
6305
  }
@@ -6195,29 +6308,29 @@ var Indexer = class {
6195
6308
  if (this.config.scope !== "global") {
6196
6309
  return branchName;
6197
6310
  }
6198
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6311
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6199
6312
  return `${projectHash}:${branchName}`;
6200
6313
  }
6201
6314
  getBranchCatalogKeyFor(branchName) {
6202
6315
  if (this.config.scope !== "global") {
6203
6316
  return branchName;
6204
6317
  }
6205
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6318
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6206
6319
  return `${projectHash}:${branchName}`;
6207
6320
  }
6208
6321
  getLegacyBranchCatalogKey() {
6209
6322
  return this.currentBranch || "default";
6210
6323
  }
6211
6324
  getLegacyMigrationMetadataKey() {
6212
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6325
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6213
6326
  return `index.globalBranchMigration.${projectHash}`;
6214
6327
  }
6215
6328
  getProjectEmbeddingStrategyMetadataKey() {
6216
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6329
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6217
6330
  return `index.embeddingStrategyVersion.${projectHash}`;
6218
6331
  }
6219
6332
  getProjectForceReembedMetadataKey() {
6220
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6333
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6221
6334
  return `index.forceReembed.${projectHash}`;
6222
6335
  }
6223
6336
  hasProjectForceReembedPending() {
@@ -6311,7 +6424,7 @@ var Indexer = class {
6311
6424
  if (!this.database) {
6312
6425
  return { chunkIds, symbolIds };
6313
6426
  }
6314
- const projectRootPath = path10.resolve(this.projectRoot);
6427
+ const projectRootPath = path11.resolve(this.projectRoot);
6315
6428
  const projectLocalFilePaths = /* @__PURE__ */ new Set([
6316
6429
  ...Array.from(this.fileHashCache.keys()).filter(
6317
6430
  (filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
@@ -6334,7 +6447,7 @@ var Indexer = class {
6334
6447
  if (this.config.scope !== "global") {
6335
6448
  return this.getBranchCatalogCleanupKeys();
6336
6449
  }
6337
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6450
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6338
6451
  const keys = /* @__PURE__ */ new Set();
6339
6452
  const projectChunkIdSet = new Set(projectChunkIds);
6340
6453
  const projectSymbolIdSet = new Set(projectSymbolIds);
@@ -6418,7 +6531,7 @@ var Indexer = class {
6418
6531
  if (!this.database || this.config.scope !== "global") {
6419
6532
  return false;
6420
6533
  }
6421
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6534
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6422
6535
  const roots = this.getScopedRoots();
6423
6536
  const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
6424
6537
  return this.database.getAllBranches().some(
@@ -6452,7 +6565,7 @@ var Indexer = class {
6452
6565
  ...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
6453
6566
  ...scopedEntries.map(({ metadata }) => metadata.filePath)
6454
6567
  ]);
6455
- const projectRootPath = path10.resolve(this.projectRoot);
6568
+ const projectRootPath = path11.resolve(this.projectRoot);
6456
6569
  const projectLocalFilePaths = new Set(
6457
6570
  Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
6458
6571
  );
@@ -6812,13 +6925,13 @@ var Indexer = class {
6812
6925
  }
6813
6926
  await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
6814
6927
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
6815
- const storePath = path10.join(this.indexPath, "vectors");
6928
+ const storePath = path11.join(this.indexPath, "vectors");
6816
6929
  this.store = new VectorStore(storePath, dimensions);
6817
- const indexFilePath = path10.join(this.indexPath, "vectors.usearch");
6930
+ const indexFilePath = path11.join(this.indexPath, "vectors.usearch");
6818
6931
  if ((0, import_fs7.existsSync)(indexFilePath)) {
6819
6932
  this.store.load();
6820
6933
  }
6821
- const invertedIndexPath = path10.join(this.indexPath, "inverted-index.json");
6934
+ const invertedIndexPath = path11.join(this.indexPath, "inverted-index.json");
6822
6935
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
6823
6936
  try {
6824
6937
  this.invertedIndex.load();
@@ -6828,7 +6941,7 @@ var Indexer = class {
6828
6941
  }
6829
6942
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
6830
6943
  }
6831
- const dbPath = path10.join(this.indexPath, "codebase.db");
6944
+ const dbPath = path11.join(this.indexPath, "codebase.db");
6832
6945
  let dbIsNew = !(0, import_fs7.existsSync)(dbPath);
6833
6946
  try {
6834
6947
  this.database = new Database(dbPath);
@@ -6909,7 +7022,7 @@ var Indexer = class {
6909
7022
  if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
6910
7023
  return {
6911
7024
  resetCorruptedIndex: true,
6912
- warning: this.getCorruptedIndexWarning(path10.join(this.indexPath, "codebase.db"))
7025
+ warning: this.getCorruptedIndexWarning(path11.join(this.indexPath, "codebase.db"))
6913
7026
  };
6914
7027
  }
6915
7028
  throw error;
@@ -6924,7 +7037,7 @@ var Indexer = class {
6924
7037
  return;
6925
7038
  }
6926
7039
  const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
6927
- const storeBasePath = path10.join(this.indexPath, "vectors");
7040
+ const storeBasePath = path11.join(this.indexPath, "vectors");
6928
7041
  const storeIndexPath = `${storeBasePath}.usearch`;
6929
7042
  const storeMetadataPath = `${storeBasePath}.meta.json`;
6930
7043
  const backupIndexPath = `${storeIndexPath}.bak`;
@@ -7009,7 +7122,7 @@ var Indexer = class {
7009
7122
  if (!isSqliteCorruptionError(error)) {
7010
7123
  return false;
7011
7124
  }
7012
- const dbPath = path10.join(this.indexPath, "codebase.db");
7125
+ const dbPath = path11.join(this.indexPath, "codebase.db");
7013
7126
  const warning = this.getCorruptedIndexWarning(dbPath);
7014
7127
  const errorMessage = getErrorMessage2(error);
7015
7128
  if (this.config.scope === "global") {
@@ -7032,15 +7145,15 @@ var Indexer = class {
7032
7145
  this.indexCompatibility = null;
7033
7146
  this.fileHashCache.clear();
7034
7147
  const resetPaths = [
7035
- path10.join(this.indexPath, "codebase.db"),
7036
- path10.join(this.indexPath, "codebase.db-shm"),
7037
- path10.join(this.indexPath, "codebase.db-wal"),
7038
- path10.join(this.indexPath, "vectors.usearch"),
7039
- path10.join(this.indexPath, "inverted-index.json"),
7040
- path10.join(this.indexPath, "file-hashes.json"),
7041
- path10.join(this.indexPath, "failed-batches.json"),
7042
- path10.join(this.indexPath, "indexing.lock"),
7043
- path10.join(this.indexPath, "vectors")
7148
+ path11.join(this.indexPath, "codebase.db"),
7149
+ path11.join(this.indexPath, "codebase.db-shm"),
7150
+ path11.join(this.indexPath, "codebase.db-wal"),
7151
+ path11.join(this.indexPath, "vectors.usearch"),
7152
+ path11.join(this.indexPath, "inverted-index.json"),
7153
+ path11.join(this.indexPath, "file-hashes.json"),
7154
+ path11.join(this.indexPath, "failed-batches.json"),
7155
+ path11.join(this.indexPath, "indexing.lock"),
7156
+ path11.join(this.indexPath, "vectors")
7044
7157
  ];
7045
7158
  await Promise.all(resetPaths.map(async (targetPath) => {
7046
7159
  try {
@@ -7277,6 +7390,7 @@ var Indexer = class {
7277
7390
  this.logger.debug("Parsed changed files", { parsedCount: parsedFiles.length, parseMs: parseMs.toFixed(2) });
7278
7391
  const existingChunks = /* @__PURE__ */ new Map();
7279
7392
  const existingChunksByFile = /* @__PURE__ */ new Map();
7393
+ const existingMetadataById = /* @__PURE__ */ new Map();
7280
7394
  for (const { key, metadata } of store.getAllMetadata()) {
7281
7395
  if (scopedRoots && !this.isFileInCurrentScope(metadata.filePath, scopedRoots)) {
7282
7396
  continue;
@@ -7285,6 +7399,7 @@ var Indexer = class {
7285
7399
  continue;
7286
7400
  }
7287
7401
  existingChunks.set(key, metadata.hash);
7402
+ existingMetadataById.set(key, metadata);
7288
7403
  const fileChunks = existingChunksByFile.get(metadata.filePath) || /* @__PURE__ */ new Set();
7289
7404
  fileChunks.add(key);
7290
7405
  existingChunksByFile.set(metadata.filePath, fileChunks);
@@ -7292,6 +7407,8 @@ var Indexer = class {
7292
7407
  const currentChunkIds = /* @__PURE__ */ new Set();
7293
7408
  const currentFilePaths = /* @__PURE__ */ new Set();
7294
7409
  const pendingChunks = [];
7410
+ const gitBlameEnabled = this.config.indexing.gitBlame.enabled && isGitRepo(this.projectRoot);
7411
+ let backfilledBlameMetadata = false;
7295
7412
  for (const filePath of unchangedFilePaths) {
7296
7413
  currentFilePaths.add(filePath);
7297
7414
  const fileChunks = existingChunksByFile.get(filePath);
@@ -7302,10 +7419,52 @@ var Indexer = class {
7302
7419
  }
7303
7420
  }
7304
7421
  const chunkDataBatch = [];
7422
+ if (gitBlameEnabled) {
7423
+ const backfillItems = [];
7424
+ for (const chunkId of currentChunkIds) {
7425
+ const metadata = existingMetadataById.get(chunkId);
7426
+ if (!metadata || hasBlameMetadata(metadata)) {
7427
+ continue;
7428
+ }
7429
+ const chunk = database.getChunk(chunkId);
7430
+ if (!chunk) {
7431
+ continue;
7432
+ }
7433
+ const blame = await getChunkGitBlame(this.projectRoot, chunk.filePath, chunk.startLine, chunk.endLine);
7434
+ const blameMetadata = metadataFromBlame(blame);
7435
+ if (!blameMetadata.blameSha) {
7436
+ continue;
7437
+ }
7438
+ chunkDataBatch.push({
7439
+ ...chunk,
7440
+ blameSha: blameMetadata.blameSha,
7441
+ blameAuthor: blameMetadata.blameAuthor,
7442
+ blameAuthorEmail: blameMetadata.blameAuthorEmail,
7443
+ blameCommittedAt: blameMetadata.blameCommittedAt,
7444
+ blameSummary: blameMetadata.blameSummary
7445
+ });
7446
+ const embeddingBuffer = database.getEmbedding(chunk.contentHash);
7447
+ if (!embeddingBuffer) {
7448
+ continue;
7449
+ }
7450
+ backfillItems.push({
7451
+ id: chunkId,
7452
+ vector: Array.from(bufferToFloat32Array(embeddingBuffer)),
7453
+ metadata: {
7454
+ ...metadata,
7455
+ ...blameMetadata
7456
+ }
7457
+ });
7458
+ }
7459
+ if (backfillItems.length > 0) {
7460
+ store.addBatch(backfillItems);
7461
+ backfilledBlameMetadata = true;
7462
+ }
7463
+ }
7305
7464
  for (const parsed of parsedFiles) {
7306
7465
  currentFilePaths.add(parsed.path);
7307
7466
  if (parsed.chunks.length === 0) {
7308
- const relativePath = path10.relative(this.projectRoot, parsed.path);
7467
+ const relativePath = path11.relative(this.projectRoot, parsed.path);
7309
7468
  stats.parseFailures.push(relativePath);
7310
7469
  }
7311
7470
  let fileChunkCount = 0;
@@ -7326,6 +7485,10 @@ var Indexer = class {
7326
7485
  }
7327
7486
  const id = generateChunkId(parsed.path, chunk);
7328
7487
  const contentHash = generateChunkHash(chunk);
7488
+ const existingContentHash = existingChunks.get(id);
7489
+ const existingChunk = gitBlameEnabled ? database.getChunk(id) : null;
7490
+ const blame = gitBlameEnabled && existingContentHash !== contentHash ? await getChunkGitBlame(this.projectRoot, parsed.path, chunk.startLine, chunk.endLine) : blameFromChunkData(existingChunk);
7491
+ const blameMetadata = metadataFromBlame(blame);
7329
7492
  currentChunkIds.add(id);
7330
7493
  chunkDataBatch.push({
7331
7494
  chunkId: id,
@@ -7335,9 +7498,14 @@ var Indexer = class {
7335
7498
  endLine: chunk.endLine,
7336
7499
  nodeType: chunk.chunkType,
7337
7500
  name: chunk.name,
7338
- language: chunk.language
7501
+ language: chunk.language,
7502
+ blameSha: blameMetadata.blameSha,
7503
+ blameAuthor: blameMetadata.blameAuthor,
7504
+ blameAuthorEmail: blameMetadata.blameAuthorEmail,
7505
+ blameCommittedAt: blameMetadata.blameCommittedAt,
7506
+ blameSummary: blameMetadata.blameSummary
7339
7507
  });
7340
- if (existingChunks.get(id) === contentHash) {
7508
+ if (existingContentHash === contentHash) {
7341
7509
  fileChunkCount++;
7342
7510
  continue;
7343
7511
  }
@@ -7352,7 +7520,8 @@ var Indexer = class {
7352
7520
  chunkType: chunk.chunkType,
7353
7521
  name: chunk.name,
7354
7522
  language: chunk.language,
7355
- hash: contentHash
7523
+ hash: contentHash,
7524
+ ...blameMetadata
7356
7525
  };
7357
7526
  pendingChunks.push({
7358
7527
  id,
@@ -7495,6 +7664,9 @@ var Indexer = class {
7495
7664
  database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
7496
7665
  database.clearBranchSymbols(branchCatalogKey);
7497
7666
  database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
7667
+ if (backfilledBlameMetadata) {
7668
+ store.save();
7669
+ }
7498
7670
  if (scopedRoots) {
7499
7671
  this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
7500
7672
  this.clearScopedFailedBatches(scopedRoots);
@@ -8058,7 +8230,8 @@ var Indexer = class {
8058
8230
  content,
8059
8231
  score: r.score,
8060
8232
  chunkType: r.metadata.chunkType,
8061
- name: r.metadata.name
8233
+ name: r.metadata.name,
8234
+ blame: blameFromMetadata(r.metadata)
8062
8235
  };
8063
8236
  })
8064
8237
  );
@@ -8150,12 +8323,12 @@ var Indexer = class {
8150
8323
  this.indexCompatibility = compatibility;
8151
8324
  return;
8152
8325
  }
8153
- const localProjectIndexPaths = [path10.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
8326
+ const localProjectIndexPaths = [path11.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
8154
8327
  if (this.host !== "opencode") {
8155
- localProjectIndexPaths.push(path10.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
8328
+ localProjectIndexPaths.push(path11.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
8156
8329
  }
8157
8330
  const isLocalProjectIndex = localProjectIndexPaths.some(
8158
- (localPath) => path10.resolve(this.indexPath) === path10.resolve(localPath)
8331
+ (localPath) => path11.resolve(this.indexPath) === path11.resolve(localPath)
8159
8332
  );
8160
8333
  if (!isLocalProjectIndex) {
8161
8334
  throw new Error(
@@ -8245,7 +8418,7 @@ var Indexer = class {
8245
8418
  gcOrphanSymbols: 0,
8246
8419
  gcOrphanCallEdges: 0,
8247
8420
  resetCorruptedIndex: true,
8248
- warning: this.getCorruptedIndexWarning(path10.join(this.indexPath, "codebase.db"))
8421
+ warning: this.getCorruptedIndexWarning(path11.join(this.indexPath, "codebase.db"))
8249
8422
  };
8250
8423
  }
8251
8424
  this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
@@ -8565,7 +8738,8 @@ var Indexer = class {
8565
8738
  content,
8566
8739
  score: r.score,
8567
8740
  chunkType: r.metadata.chunkType,
8568
- name: r.metadata.name
8741
+ name: r.metadata.name,
8742
+ blame: blameFromMetadata(r.metadata)
8569
8743
  };
8570
8744
  })
8571
8745
  );
@@ -8602,9 +8776,9 @@ var Indexer = class {
8602
8776
  const { database } = await this.ensureInitialized();
8603
8777
  let shortest = [];
8604
8778
  for (const branchKey of this.getBranchCatalogKeys()) {
8605
- const path25 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
8606
- if (path25.length > 0 && (shortest.length === 0 || path25.length < shortest.length)) {
8607
- shortest = path25;
8779
+ const path26 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
8780
+ if (path26.length > 0 && (shortest.length === 0 || path26.length < shortest.length)) {
8781
+ shortest = path26;
8608
8782
  }
8609
8783
  }
8610
8784
  return shortest;
@@ -8636,7 +8810,7 @@ var Indexer = class {
8636
8810
  }
8637
8811
  async getPrImpact(opts) {
8638
8812
  const { database } = await this.ensureInitialized();
8639
- const execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
8813
+ const execFileAsync3 = (0, import_util3.promisify)(import_child_process3.execFile);
8640
8814
  const changedFilesResult = await getChangedFiles({
8641
8815
  pr: opts.pr,
8642
8816
  branch: opts.branch,
@@ -8658,7 +8832,7 @@ var Indexer = class {
8658
8832
  "Run index_codebase first to build the call graph and symbol index for this branch."
8659
8833
  );
8660
8834
  }
8661
- const absoluteChangedFiles = changedFiles.map((f) => path10.resolve(this.projectRoot, f));
8835
+ const absoluteChangedFiles = changedFiles.map((f) => path11.resolve(this.projectRoot, f));
8662
8836
  const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
8663
8837
  const directIds = directSymbols.map((s) => s.id);
8664
8838
  const direction = opts.direction ?? "both";
@@ -8720,7 +8894,7 @@ var Indexer = class {
8720
8894
  if (opts.checkConflicts) {
8721
8895
  conflictingPRs = [];
8722
8896
  try {
8723
- const { stdout } = await execFileAsync2(
8897
+ const { stdout } = await execFileAsync3(
8724
8898
  "gh",
8725
8899
  ["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "10000"],
8726
8900
  { cwd: this.projectRoot, timeout: 3e4 }
@@ -8741,7 +8915,7 @@ var Indexer = class {
8741
8915
  projectRoot: this.projectRoot,
8742
8916
  baseBranch: this.baseBranch
8743
8917
  });
8744
- const otherAbsolute = otherChanged.files.map((f) => path10.resolve(this.projectRoot, f));
8918
+ const otherAbsolute = otherChanged.files.map((f) => path11.resolve(this.projectRoot, f));
8745
8919
  const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
8746
8920
  const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
8747
8921
  const otherLabels = /* @__PURE__ */ new Set();
@@ -8804,12 +8978,12 @@ var Indexer = class {
8804
8978
  if (meta.filePath) filePaths.add(meta.filePath);
8805
8979
  }
8806
8980
  const directory = options?.directory?.replace(/\/$/, "");
8807
- const absoluteDirectoryFilter = directory ? path10.resolve(this.projectRoot, directory) : void 0;
8981
+ const absoluteDirectoryFilter = directory ? path11.resolve(this.projectRoot, directory) : void 0;
8808
8982
  for (const filePath of filePaths) {
8809
8983
  if (directory) {
8810
- const absoluteFilePath = path10.resolve(filePath);
8984
+ const absoluteFilePath = path11.resolve(filePath);
8811
8985
  const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
8812
- const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path10.sep));
8986
+ const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path11.sep));
8813
8987
  if (!matchesRelative && !matchesProjectRelative) {
8814
8988
  continue;
8815
8989
  }
@@ -9089,13 +9263,13 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
9089
9263
  // src/eval/runner-config.ts
9090
9264
  var import_fs8 = require("fs");
9091
9265
  var os4 = __toESM(require("os"), 1);
9092
- var path12 = __toESM(require("path"), 1);
9266
+ var path13 = __toESM(require("path"), 1);
9093
9267
 
9094
9268
  // src/config/rebase.ts
9095
- var path11 = __toESM(require("path"), 1);
9269
+ var path12 = __toESM(require("path"), 1);
9096
9270
  function isWithinRoot(rootDir, targetPath) {
9097
- const relativePath = path11.relative(rootDir, targetPath);
9098
- return relativePath === "" || !relativePath.startsWith("..") && !path11.isAbsolute(relativePath);
9271
+ const relativePath = path12.relative(rootDir, targetPath);
9272
+ return relativePath === "" || !relativePath.startsWith("..") && !path12.isAbsolute(relativePath);
9099
9273
  }
9100
9274
  function rebasePathEntries(values, fromDir, toDir) {
9101
9275
  if (!Array.isArray(values)) {
@@ -9103,10 +9277,10 @@ function rebasePathEntries(values, fromDir, toDir) {
9103
9277
  }
9104
9278
  return values.filter((value) => typeof value === "string").map((value) => {
9105
9279
  const trimmed = value.trim();
9106
- if (!trimmed || path11.isAbsolute(trimmed)) {
9280
+ if (!trimmed || path12.isAbsolute(trimmed)) {
9107
9281
  return trimmed;
9108
9282
  }
9109
- return normalizePathSeparators(path11.normalize(path11.relative(toDir, path11.resolve(fromDir, trimmed))));
9283
+ return normalizePathSeparators(path12.normalize(path12.relative(toDir, path12.resolve(fromDir, trimmed))));
9110
9284
  }).filter(Boolean);
9111
9285
  }
9112
9286
  function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
@@ -9118,17 +9292,17 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
9118
9292
  if (!trimmed) {
9119
9293
  return trimmed;
9120
9294
  }
9121
- if (path11.isAbsolute(trimmed)) {
9295
+ if (path12.isAbsolute(trimmed)) {
9122
9296
  if (isWithinRoot(sourceRoot, trimmed)) {
9123
- return normalizePathSeparators(path11.normalize(path11.relative(sourceRoot, trimmed) || "."));
9297
+ return normalizePathSeparators(path12.normalize(path12.relative(sourceRoot, trimmed) || "."));
9124
9298
  }
9125
- return path11.normalize(trimmed);
9299
+ return path12.normalize(trimmed);
9126
9300
  }
9127
- const resolvedFromSource = path11.resolve(sourceRoot, trimmed);
9301
+ const resolvedFromSource = path12.resolve(sourceRoot, trimmed);
9128
9302
  if (isWithinRoot(sourceRoot, resolvedFromSource)) {
9129
- return normalizePathSeparators(path11.normalize(trimmed));
9303
+ return normalizePathSeparators(path12.normalize(trimmed));
9130
9304
  }
9131
- return normalizePathSeparators(path11.normalize(path11.relative(targetRoot, resolvedFromSource)));
9305
+ return normalizePathSeparators(path12.normalize(path12.relative(targetRoot, resolvedFromSource)));
9132
9306
  }).filter(Boolean);
9133
9307
  }
9134
9308
 
@@ -9176,20 +9350,20 @@ function parseJsonConfigFile(filePath) {
9176
9350
  }
9177
9351
  }
9178
9352
  function toAbsolute(projectRoot, maybeRelative) {
9179
- return path12.isAbsolute(maybeRelative) ? maybeRelative : path12.join(projectRoot, maybeRelative);
9353
+ return path13.isAbsolute(maybeRelative) ? maybeRelative : path13.join(projectRoot, maybeRelative);
9180
9354
  }
9181
9355
  function isProjectScopedConfigPath(configPath) {
9182
- return path12.basename(configPath) === "codebase-index.json" && path12.basename(path12.dirname(configPath)) === ".opencode";
9356
+ return path13.basename(configPath) === "codebase-index.json" && path13.basename(path13.dirname(configPath)) === ".opencode";
9183
9357
  }
9184
9358
  function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfigPath) {
9185
9359
  const config = rawConfig && typeof rawConfig === "object" ? { ...rawConfig } : {};
9186
9360
  const rebaseEntries = (values) => isProjectScopedConfigPath(resolvedConfigPath) ? resolveInheritedKnowledgeBaseEntries(
9187
9361
  values,
9188
- path12.dirname(path12.dirname(resolvedConfigPath)),
9362
+ path13.dirname(path13.dirname(resolvedConfigPath)),
9189
9363
  projectRoot
9190
9364
  ) : rebasePathEntries(
9191
9365
  values,
9192
- path12.dirname(resolvedConfigPath),
9366
+ path13.dirname(resolvedConfigPath),
9193
9367
  projectRoot
9194
9368
  );
9195
9369
  if (Array.isArray(config.knowledgeBases)) {
@@ -9217,7 +9391,7 @@ function loadRawConfig(projectRoot, configPath) {
9217
9391
  projectConfig
9218
9392
  );
9219
9393
  }
9220
- const globalConfig = path12.join(os4.homedir(), ".config", "opencode", "codebase-index.json");
9394
+ const globalConfig = path13.join(os4.homedir(), ".config", "opencode", "codebase-index.json");
9221
9395
  if ((0, import_fs8.existsSync)(globalConfig)) {
9222
9396
  return parseJsonConfigFile(globalConfig);
9223
9397
  }
@@ -9227,10 +9401,10 @@ function getIndexRootPath(projectRoot, scope) {
9227
9401
  return scope === "global" ? getGlobalIndexPath() : resolveProjectIndexPath(projectRoot, scope);
9228
9402
  }
9229
9403
  function getLocalProjectIndexRoot(projectRoot) {
9230
- return path12.join(projectRoot, ".opencode", "index");
9404
+ return path13.join(projectRoot, ".opencode", "index");
9231
9405
  }
9232
9406
  function getLocalProjectConfigPath(projectRoot) {
9233
- return path12.join(projectRoot, ".opencode", "codebase-index.json");
9407
+ return path13.join(projectRoot, ".opencode", "codebase-index.json");
9234
9408
  }
9235
9409
  function clearIndexRoot(projectRoot, scope) {
9236
9410
  const indexRoot = scope === "global" ? getIndexRootPath(projectRoot, scope) : getLocalProjectIndexRoot(projectRoot);
@@ -9252,7 +9426,7 @@ function ensureLocalEvalProjectConfig(projectRoot, configPath) {
9252
9426
  projectRoot,
9253
9427
  resolvedConfigPath
9254
9428
  );
9255
- (0, import_fs8.mkdirSync)(path12.dirname(localConfigPath), { recursive: true });
9429
+ (0, import_fs8.mkdirSync)(path13.dirname(localConfigPath), { recursive: true });
9256
9430
  (0, import_fs8.writeFileSync)(localConfigPath, JSON.stringify(sourceConfig, null, 2), "utf-8");
9257
9431
  return localConfigPath;
9258
9432
  }
@@ -9302,23 +9476,23 @@ function isRecord2(value) {
9302
9476
  function isStringArray3(value) {
9303
9477
  return Array.isArray(value) && value.every((item) => typeof item === "string");
9304
9478
  }
9305
- function asPositiveNumber(value, path25) {
9479
+ function asPositiveNumber(value, path26) {
9306
9480
  if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
9307
- throw new Error(`${path25} must be a non-negative number`);
9481
+ throw new Error(`${path26} must be a non-negative number`);
9308
9482
  }
9309
9483
  return value;
9310
9484
  }
9311
- function parseQueryType(value, path25) {
9485
+ function parseQueryType(value, path26) {
9312
9486
  if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy") {
9313
9487
  return value;
9314
9488
  }
9315
9489
  throw new Error(
9316
- `${path25} must be one of: definition, implementation-intent, similarity, keyword-heavy`
9490
+ `${path26} must be one of: definition, implementation-intent, similarity, keyword-heavy`
9317
9491
  );
9318
9492
  }
9319
- function parseExpected(input, path25) {
9493
+ function parseExpected(input, path26) {
9320
9494
  if (!isRecord2(input)) {
9321
- throw new Error(`${path25} must be an object`);
9495
+ throw new Error(`${path26} must be an object`);
9322
9496
  }
9323
9497
  const filePathRaw = input.filePath;
9324
9498
  const acceptableFilesRaw = input.acceptableFiles;
@@ -9327,16 +9501,16 @@ function parseExpected(input, path25) {
9327
9501
  const filePath = typeof filePathRaw === "string" ? filePathRaw : void 0;
9328
9502
  const acceptableFiles = isStringArray3(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
9329
9503
  if (!filePath && (!acceptableFiles || acceptableFiles.length === 0)) {
9330
- throw new Error(`${path25} must include either expected.filePath or expected.acceptableFiles`);
9504
+ throw new Error(`${path26} must include either expected.filePath or expected.acceptableFiles`);
9331
9505
  }
9332
9506
  if (acceptableFilesRaw !== void 0 && !isStringArray3(acceptableFilesRaw)) {
9333
- throw new Error(`${path25}.acceptableFiles must be an array of strings`);
9507
+ throw new Error(`${path26}.acceptableFiles must be an array of strings`);
9334
9508
  }
9335
9509
  if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
9336
- throw new Error(`${path25}.symbol must be a string when provided`);
9510
+ throw new Error(`${path26}.symbol must be a string when provided`);
9337
9511
  }
9338
9512
  if (branchRaw !== void 0 && typeof branchRaw !== "string") {
9339
- throw new Error(`${path25}.branch must be a string when provided`);
9513
+ throw new Error(`${path26}.branch must be a string when provided`);
9340
9514
  }
9341
9515
  return {
9342
9516
  filePath,
@@ -9346,25 +9520,25 @@ function parseExpected(input, path25) {
9346
9520
  };
9347
9521
  }
9348
9522
  function parseQuery(input, index) {
9349
- const path25 = `queries[${index}]`;
9523
+ const path26 = `queries[${index}]`;
9350
9524
  if (!isRecord2(input)) {
9351
- throw new Error(`${path25} must be an object`);
9525
+ throw new Error(`${path26} must be an object`);
9352
9526
  }
9353
9527
  const id = input.id;
9354
9528
  const query = input.query;
9355
9529
  const queryType = input.queryType;
9356
9530
  const expected = input.expected;
9357
9531
  if (typeof id !== "string" || id.trim().length === 0) {
9358
- throw new Error(`${path25}.id must be a non-empty string`);
9532
+ throw new Error(`${path26}.id must be a non-empty string`);
9359
9533
  }
9360
9534
  if (typeof query !== "string" || query.trim().length === 0) {
9361
- throw new Error(`${path25}.query must be a non-empty string`);
9535
+ throw new Error(`${path26}.query must be a non-empty string`);
9362
9536
  }
9363
9537
  return {
9364
9538
  id,
9365
9539
  query,
9366
- queryType: parseQueryType(queryType, `${path25}.queryType`),
9367
- expected: parseExpected(expected, `${path25}.expected`)
9540
+ queryType: parseQueryType(queryType, `${path26}.queryType`),
9541
+ expected: parseExpected(expected, `${path26}.expected`)
9368
9542
  };
9369
9543
  }
9370
9544
  function parseGoldenDataset(raw, sourceLabel) {
@@ -9547,13 +9721,13 @@ async function runEvaluation(options) {
9547
9721
  };
9548
9722
  const outputDir = createRunDirectory(toAbsolute(options.projectRoot, options.outputRoot));
9549
9723
  const perQueryArtifact = buildPerQueryArtifact(perQuery);
9550
- writeJson(path13.join(outputDir, "summary.json"), summary);
9551
- writeJson(path13.join(outputDir, "per-query.json"), perQueryArtifact);
9724
+ writeJson(path14.join(outputDir, "summary.json"), summary);
9725
+ writeJson(path14.join(outputDir, "per-query.json"), perQueryArtifact);
9552
9726
  let comparison;
9553
9727
  if (againstPath) {
9554
9728
  const baseline = loadSummary(againstPath);
9555
9729
  comparison = compareSummaries(summary, baseline, againstPath);
9556
- writeJson(path13.join(outputDir, "compare.json"), comparison);
9730
+ writeJson(path14.join(outputDir, "compare.json"), comparison);
9557
9731
  }
9558
9732
  let gate;
9559
9733
  if (options.ciMode) {
@@ -9566,7 +9740,7 @@ async function runEvaluation(options) {
9566
9740
  if ((0, import_fs10.existsSync)(resolvedBaseline)) {
9567
9741
  const baselineSummary = loadSummary(resolvedBaseline);
9568
9742
  comparison = compareSummaries(summary, baselineSummary, resolvedBaseline);
9569
- writeJson(path13.join(outputDir, "compare.json"), comparison);
9743
+ writeJson(path14.join(outputDir, "compare.json"), comparison);
9570
9744
  } else if (budget.failOnMissingBaseline) {
9571
9745
  throw new Error(
9572
9746
  `Budget baseline is missing: ${resolvedBaseline}. Set failOnMissingBaseline=false to allow CI run without baseline.`
@@ -9576,7 +9750,7 @@ async function runEvaluation(options) {
9576
9750
  gate = evaluateBudgetGate(budget, summary, comparison);
9577
9751
  }
9578
9752
  const markdown = createSummaryMarkdown(summary, comparison, gate);
9579
- writeText(path13.join(outputDir, "summary.md"), markdown);
9753
+ writeText(path14.join(outputDir, "summary.md"), markdown);
9580
9754
  return { outputDir, summary, perQuery, comparison, gate };
9581
9755
  } finally {
9582
9756
  await indexer.close();
@@ -9634,23 +9808,23 @@ async function runSweep(options, sweep) {
9634
9808
  bestByMrrAt10,
9635
9809
  bestByP95Latency
9636
9810
  };
9637
- writeJson(path13.join(outputDir, "compare.json"), aggregate);
9811
+ writeJson(path14.join(outputDir, "compare.json"), aggregate);
9638
9812
  const md = createSummaryMarkdown(
9639
9813
  bestByHitAt5?.summary ?? runs[0].summary,
9640
9814
  bestByHitAt5?.comparison,
9641
9815
  void 0,
9642
9816
  aggregate
9643
9817
  );
9644
- writeText(path13.join(outputDir, "summary.md"), md);
9645
- writeJson(path13.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
9818
+ writeText(path14.join(outputDir, "summary.md"), md);
9819
+ writeJson(path14.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
9646
9820
  return { outputDir, aggregate };
9647
9821
  }
9648
9822
 
9649
9823
  // src/eval/cli.ts
9650
- var path15 = __toESM(require("path"), 1);
9824
+ var path16 = __toESM(require("path"), 1);
9651
9825
 
9652
9826
  // src/eval/cli-parser.ts
9653
- var path14 = __toESM(require("path"), 1);
9827
+ var path15 = __toESM(require("path"), 1);
9654
9828
  function printUsage() {
9655
9829
  console.log(`
9656
9830
  Usage:
@@ -9722,12 +9896,12 @@ function parseEvalArgs(argv, cwd) {
9722
9896
  const arg = argv[i];
9723
9897
  const next = argv[i + 1];
9724
9898
  if (arg === "--project" && next) {
9725
- parsed.projectRoot = path14.resolve(cwd, next);
9899
+ parsed.projectRoot = path15.resolve(cwd, next);
9726
9900
  i += 1;
9727
9901
  continue;
9728
9902
  }
9729
9903
  if (arg === "--config" && next) {
9730
- parsed.configPath = path14.resolve(cwd, next);
9904
+ parsed.configPath = path15.resolve(cwd, next);
9731
9905
  i += 1;
9732
9906
  continue;
9733
9907
  }
@@ -9923,22 +10097,22 @@ async function handleEvalCommand(args, cwd) {
9923
10097
  if (!parsed.againstPath.endsWith(".json")) {
9924
10098
  throw new Error("eval diff --against must point to a summary JSON file");
9925
10099
  }
9926
- const currentSummary = loadSummary(path15.resolve(parsed.projectRoot, currentPath), {
10100
+ const currentSummary = loadSummary(path16.resolve(parsed.projectRoot, currentPath), {
9927
10101
  allowLegacyDiversityMetrics: true
9928
10102
  });
9929
- const baselineSummary = loadSummary(path15.resolve(parsed.projectRoot, parsed.againstPath), {
10103
+ const baselineSummary = loadSummary(path16.resolve(parsed.projectRoot, parsed.againstPath), {
9930
10104
  allowLegacyDiversityMetrics: true
9931
10105
  });
9932
10106
  const comparison = compareSummaries(
9933
10107
  currentSummary,
9934
10108
  baselineSummary,
9935
- path15.resolve(parsed.projectRoot, parsed.againstPath)
10109
+ path16.resolve(parsed.projectRoot, parsed.againstPath)
9936
10110
  );
9937
- const outputDir = createRunDirectory(path15.resolve(parsed.projectRoot, parsed.outputRoot));
10111
+ const outputDir = createRunDirectory(path16.resolve(parsed.projectRoot, parsed.outputRoot));
9938
10112
  const summaryMd = createSummaryMarkdown(currentSummary, comparison);
9939
- writeJson(path15.join(outputDir, "compare.json"), comparison);
9940
- writeText(path15.join(outputDir, "summary.md"), summaryMd);
9941
- writeJson(path15.join(outputDir, "summary.json"), currentSummary);
10113
+ writeJson(path16.join(outputDir, "compare.json"), comparison);
10114
+ writeText(path16.join(outputDir, "summary.md"), summaryMd);
10115
+ writeJson(path16.join(outputDir, "summary.json"), currentSummary);
9942
10116
  console.log(`Eval diff complete. Artifacts: ${outputDir}`);
9943
10117
  return 0;
9944
10118
  }
@@ -10183,6 +10357,17 @@ function calculatePercentage(progress) {
10183
10357
  if (progress.phase === "storing") return 95;
10184
10358
  return 0;
10185
10359
  }
10360
+ function formatCodebasePeek(results) {
10361
+ if (results.length === 0) {
10362
+ return "No matching code found. Try a different query or run index_codebase first.";
10363
+ }
10364
+ const formatted = results.map((r, idx) => {
10365
+ const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
10366
+ const name = r.name ? `"${r.name}"` : "(anonymous)";
10367
+ return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})${formatBlame(r)}`;
10368
+ });
10369
+ return formatted.join("\n");
10370
+ }
10186
10371
  function formatHealthCheck(result) {
10187
10372
  if (result.resetCorruptedIndex) {
10188
10373
  return result.warning ?? "Detected a corrupted local index and reset it. Run index_codebase to rebuild search data.";
@@ -10211,16 +10396,68 @@ function formatHealthCheck(result) {
10211
10396
  }
10212
10397
  return lines.join("\n");
10213
10398
  }
10399
+ function formatCallGraphCallers(name, callers, relationshipType) {
10400
+ if (callers.length === 0) {
10401
+ return `No callers found for "${name}"${relationshipType ? ` with type ${relationshipType}` : ""}. It may not be called by any tracked function, or the index needs updating.`;
10402
+ }
10403
+ const formatted = callers.map((edge, index) => {
10404
+ const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
10405
+ return `[${index + 1}] \u2190 from ${edge.fromSymbolName ?? "<unknown>"} in ${edge.fromSymbolFilePath ?? "<unknown file>"} [${edge.fromSymbolId}] (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? " [resolved]" : " [unresolved]"}`;
10406
+ });
10407
+ return `"${name}" is called by ${callers.length} function(s):
10408
+
10409
+ ${formatted.join("\n")}`;
10410
+ }
10411
+ function formatCallGraphCallees(symbolId, callees, relationshipType) {
10412
+ if (callees.length === 0) {
10413
+ return `No callees found for symbol ${symbolId}${relationshipType ? ` with type ${relationshipType}` : ""}. The function may not call any other tracked functions.`;
10414
+ }
10415
+ return callees.map((edge, index) => {
10416
+ const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
10417
+ return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
10418
+ }).join("\n");
10419
+ }
10420
+ function formatCallGraphPath(from, to, path26) {
10421
+ if (path26.length === 0) {
10422
+ return `No path found between "${from}" and "${to}". They may be in disconnected components, or the call graph index needs updating.`;
10423
+ }
10424
+ const formatted = path26.map((hop, index) => {
10425
+ const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
10426
+ const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
10427
+ return `${prefix} ${hop.symbolName}${location}`;
10428
+ });
10429
+ return `Path (${path26.length} hops):
10430
+ ${formatted.join("\n")}`;
10431
+ }
10214
10432
  function formatResultHeader(result, index) {
10215
10433
  return result.name ? `[${index + 1}] ${result.chunkType} "${result.name}" in ${result.filePath}:${result.startLine}-${result.endLine}` : `[${index + 1}] ${result.chunkType} in ${result.filePath}:${result.startLine}-${result.endLine}`;
10216
10434
  }
10435
+ function formatBlame(result) {
10436
+ if (!result.blame) {
10437
+ return "";
10438
+ }
10439
+ const date = new Date(result.blame.committedAt * 1e3).toISOString().slice(0, 10);
10440
+ return `
10441
+ ${result.blame.sha.slice(0, 7)} | ${result.blame.author} | ${date} | ${result.blame.summary}`;
10442
+ }
10217
10443
  function formatDefinitionLookup(results, query) {
10218
10444
  if (results.length === 0) {
10219
10445
  return `No definition found for "${query}". Try codebase_search for broader discovery, or verify the symbol name.`;
10220
10446
  }
10221
10447
  const formatted = results.map((r, idx) => {
10222
10448
  const header = formatResultHeader(r, idx);
10223
- return `${header} (score: ${r.score.toFixed(2)})
10449
+ return `${header} (score: ${r.score.toFixed(2)})${formatBlame(r)}
10450
+ \`\`\`
10451
+ ${truncateContent(r.content)}
10452
+ \`\`\``;
10453
+ });
10454
+ return formatted.join("\n\n");
10455
+ }
10456
+ function formatSearchResults(results, scoreFormat = "similarity") {
10457
+ const formatted = results.map((r, idx) => {
10458
+ const header = formatResultHeader(r, idx);
10459
+ const scoreLabel = scoreFormat === "similarity" ? `(similarity: ${(r.score * 100).toFixed(1)}%)` : `(score: ${r.score.toFixed(2)})`;
10460
+ return `${header} ${scoreLabel}${formatBlame(r)}
10224
10461
  \`\`\`
10225
10462
  ${truncateContent(r.content)}
10226
10463
  \`\`\``;
@@ -10281,11 +10518,11 @@ function formatPrImpact(result) {
10281
10518
 
10282
10519
  // src/tools/operations.ts
10283
10520
  var import_fs13 = require("fs");
10284
- var path19 = __toESM(require("path"), 1);
10521
+ var path20 = __toESM(require("path"), 1);
10285
10522
 
10286
10523
  // src/config/merger.ts
10287
10524
  var import_fs11 = require("fs");
10288
- var path16 = __toESM(require("path"), 1);
10525
+ var path17 = __toESM(require("path"), 1);
10289
10526
  var PROJECT_OVERRIDE_KEYS = [
10290
10527
  "embeddingProvider",
10291
10528
  "customProvider",
@@ -10318,8 +10555,8 @@ function mergeUniqueStringArray(values) {
10318
10555
  return [...new Set(values.map((value) => String(value).trim()))];
10319
10556
  }
10320
10557
  function normalizeKnowledgeBasePath(value) {
10321
- let normalized = path16.normalize(String(value).trim());
10322
- const root = path16.parse(normalized).root;
10558
+ let normalized = path17.normalize(String(value).trim());
10559
+ const root = path17.parse(normalized).root;
10323
10560
  while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
10324
10561
  normalized = normalized.slice(0, -1);
10325
10562
  }
@@ -10372,7 +10609,7 @@ function loadConfigFile(filePath) {
10372
10609
  }
10373
10610
  function materializeLocalProjectConfig(projectRoot, config, host = "opencode") {
10374
10611
  const localConfigPath = resolveWritableProjectConfigPath(projectRoot, host);
10375
- (0, import_fs11.mkdirSync)(path16.dirname(localConfigPath), { recursive: true });
10612
+ (0, import_fs11.mkdirSync)(path17.dirname(localConfigPath), { recursive: true });
10376
10613
  (0, import_fs11.writeFileSync)(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
10377
10614
  return localConfigPath;
10378
10615
  }
@@ -10383,7 +10620,7 @@ function loadProjectConfigLayer(projectRoot, host = "opencode") {
10383
10620
  return {};
10384
10621
  }
10385
10622
  const normalizedConfig = { ...projectConfig };
10386
- const projectConfigBaseDir = path16.dirname(path16.dirname(projectConfigPath));
10623
+ const projectConfigBaseDir = path17.dirname(path17.dirname(projectConfigPath));
10387
10624
  if (Array.isArray(normalizedConfig.knowledgeBases)) {
10388
10625
  normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
10389
10626
  normalizedConfig.knowledgeBases,
@@ -10447,19 +10684,19 @@ function loadMergedConfig(projectRoot, host = "opencode") {
10447
10684
  }
10448
10685
 
10449
10686
  // src/tools/knowledge-base-paths.ts
10450
- var path17 = __toESM(require("path"), 1);
10687
+ var path18 = __toESM(require("path"), 1);
10451
10688
  function resolveConfigPathValue(value, baseDir) {
10452
10689
  const trimmed = value.trim();
10453
10690
  if (!trimmed) {
10454
10691
  return trimmed;
10455
10692
  }
10456
- const absolutePath = path17.isAbsolute(trimmed) ? trimmed : path17.resolve(baseDir, trimmed);
10457
- return path17.normalize(absolutePath);
10693
+ const absolutePath = path18.isAbsolute(trimmed) ? trimmed : path18.resolve(baseDir, trimmed);
10694
+ return path18.normalize(absolutePath);
10458
10695
  }
10459
10696
 
10460
10697
  // src/tools/config-state.ts
10461
10698
  var import_fs12 = require("fs");
10462
- var path18 = __toESM(require("path"), 1);
10699
+ var path19 = __toESM(require("path"), 1);
10463
10700
  function normalizeKnowledgeBasePaths(config, projectRoot) {
10464
10701
  const normalized = { ...config };
10465
10702
  if (Array.isArray(normalized.knowledgeBases)) {
@@ -10527,7 +10764,7 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
10527
10764
  }
10528
10765
  function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
10529
10766
  const root = getProjectRoot(projectRoot, host);
10530
- const localIndexPath = path19.join(root, getHostProjectIndexRelativePath(host));
10767
+ const localIndexPath = path20.join(root, getHostProjectIndexRelativePath(host));
10531
10768
  if ((0, import_fs13.existsSync)(localIndexPath)) {
10532
10769
  return false;
10533
10770
  }
@@ -10542,7 +10779,10 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
10542
10779
  chunkType: options.chunkType,
10543
10780
  contextLines: options.contextLines,
10544
10781
  metadataOnly: options.metadataOnly,
10545
- definitionIntent: options.definitionIntent
10782
+ definitionIntent: options.definitionIntent,
10783
+ blameAuthor: options.blameAuthor,
10784
+ blameSha: options.blameSha,
10785
+ blameSince: options.blameSince
10546
10786
  });
10547
10787
  }
10548
10788
  async function findSimilarCode(projectRoot, host, code, options = {}) {
@@ -10684,13 +10924,6 @@ async function getIndexLogs(projectRoot, host, args) {
10684
10924
  }
10685
10925
 
10686
10926
  // src/mcp-server/shared.ts
10687
- var MAX_CONTENT_LINES2 = 30;
10688
- function truncateContent2(content) {
10689
- const lines = content.split("\n");
10690
- if (lines.length <= MAX_CONTENT_LINES2) return content;
10691
- return lines.slice(0, MAX_CONTENT_LINES2).join("\n") + `
10692
- // ... (${lines.length - MAX_CONTENT_LINES2} more lines)`;
10693
- }
10694
10927
  var CHUNK_TYPE_ENUM = [
10695
10928
  "function",
10696
10929
  "class",
@@ -10716,7 +10949,10 @@ function registerMcpTools(server, runtime) {
10716
10949
  fileType: import_zod2.z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10717
10950
  directory: import_zod2.z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10718
10951
  chunkType: import_zod2.z.enum(CHUNK_TYPE_ENUM).optional().describe("Filter by code chunk type"),
10719
- contextLines: import_zod2.z.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
10952
+ contextLines: import_zod2.z.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
10953
+ blameAuthor: import_zod2.z.string().optional().describe("Filter by git blame author name or email"),
10954
+ blameSha: import_zod2.z.string().optional().describe("Filter by git blame commit SHA or prefix"),
10955
+ blameSince: import_zod2.z.string().optional().describe("Filter to chunks last changed on or after this date")
10720
10956
  },
10721
10957
  async (args) => {
10722
10958
  const results = await searchCodebase(runtime.projectRoot, runtime.host, args.query, {
@@ -10724,21 +10960,17 @@ function registerMcpTools(server, runtime) {
10724
10960
  fileType: args.fileType,
10725
10961
  directory: args.directory,
10726
10962
  chunkType: args.chunkType,
10727
- contextLines: args.contextLines
10963
+ contextLines: args.contextLines,
10964
+ blameAuthor: args.blameAuthor,
10965
+ blameSha: args.blameSha,
10966
+ blameSince: args.blameSince
10728
10967
  });
10729
10968
  if (results.length === 0) {
10730
10969
  return { content: [{ type: "text", text: "No matching code found. Try a different query or run index_codebase first." }] };
10731
10970
  }
10732
- const formatted = results.map((r, idx) => {
10733
- const header = r.name ? `[${idx + 1}] ${r.chunkType} "${r.name}" in ${r.filePath}:${r.startLine}-${r.endLine}` : `[${idx + 1}] ${r.chunkType} in ${r.filePath}:${r.startLine}-${r.endLine}`;
10734
- return `${header} (score: ${r.score.toFixed(2)})
10735
- \`\`\`
10736
- ${truncateContent2(r.content)}
10737
- \`\`\``;
10738
- });
10739
10971
  return { content: [{ type: "text", text: `Found ${results.length} results for "${args.query}":
10740
10972
 
10741
- ${formatted.join("\n\n")}` }] };
10973
+ ${formatSearchResults(results, "score")}` }] };
10742
10974
  }
10743
10975
  );
10744
10976
  server.tool(
@@ -10749,7 +10981,10 @@ ${formatted.join("\n\n")}` }] };
10749
10981
  limit: import_zod2.z.number().optional().default(10).describe("Maximum number of results to return"),
10750
10982
  fileType: import_zod2.z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10751
10983
  directory: import_zod2.z.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10752
- chunkType: import_zod2.z.enum(CHUNK_TYPE_ENUM).optional().describe("Filter by code chunk type")
10984
+ chunkType: import_zod2.z.enum(CHUNK_TYPE_ENUM).optional().describe("Filter by code chunk type"),
10985
+ blameAuthor: import_zod2.z.string().optional().describe("Filter by git blame author name or email"),
10986
+ blameSha: import_zod2.z.string().optional().describe("Filter by git blame commit SHA or prefix"),
10987
+ blameSince: import_zod2.z.string().optional().describe("Filter to chunks last changed on or after this date")
10753
10988
  },
10754
10989
  async (args) => {
10755
10990
  const results = await searchCodebase(runtime.projectRoot, runtime.host, args.query, {
@@ -10757,19 +10992,17 @@ ${formatted.join("\n\n")}` }] };
10757
10992
  fileType: args.fileType,
10758
10993
  directory: args.directory,
10759
10994
  chunkType: args.chunkType,
10760
- metadataOnly: true
10995
+ metadataOnly: true,
10996
+ blameAuthor: args.blameAuthor,
10997
+ blameSha: args.blameSha,
10998
+ blameSince: args.blameSince
10761
10999
  });
10762
11000
  if (results.length === 0) {
10763
11001
  return { content: [{ type: "text", text: "No matching code found. Try a different query or run index_codebase first." }] };
10764
11002
  }
10765
- const formatted = results.map((r, idx) => {
10766
- const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
10767
- const name = r.name ? `"${r.name}"` : "(anonymous)";
10768
- return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})`;
10769
- });
10770
11003
  return { content: [{ type: "text", text: `Found ${results.length} locations for "${args.query}":
10771
11004
 
10772
- ${formatted.join("\n")}
11005
+ ${formatCodebasePeek(results)}
10773
11006
 
10774
11007
  Use Read tool to examine specific files.` }] };
10775
11008
  }
@@ -10850,16 +11083,9 @@ Use Read tool to examine specific files.` }] };
10850
11083
  if (results.length === 0) {
10851
11084
  return { content: [{ type: "text", text: "No similar code found. Try a different snippet or run index_codebase first." }] };
10852
11085
  }
10853
- const formatted = results.map((r, idx) => {
10854
- const header = r.name ? `[${idx + 1}] ${r.chunkType} "${r.name}" in ${r.filePath}:${r.startLine}-${r.endLine}` : `[${idx + 1}] ${r.chunkType} in ${r.filePath}:${r.startLine}-${r.endLine}`;
10855
- return `${header} (similarity: ${(r.score * 100).toFixed(1)}%)
10856
- \`\`\`
10857
- ${truncateContent2(r.content)}
10858
- \`\`\``;
10859
- });
10860
11086
  return { content: [{ type: "text", text: `Found ${results.length} similar code blocks:
10861
11087
 
10862
- ${formatted.join("\n\n")}` }] };
11088
+ ${formatSearchResults(results)}` }] };
10863
11089
  }
10864
11090
  );
10865
11091
  server.tool(
@@ -10895,28 +11121,10 @@ ${formatted.join("\n\n")}` }] };
10895
11121
  return { content: [{ type: "text", text: "Error: 'symbolId' is required when direction is 'callees'." }] };
10896
11122
  }
10897
11123
  const { callees } = await getCallGraphData(runtime.projectRoot, runtime.host, args);
10898
- if (callees.length === 0) {
10899
- return { content: [{ type: "text", text: `No callees found for symbol ${args.symbolId}${args.relationshipType ? ` with type ${args.relationshipType}` : ""}.` }] };
10900
- }
10901
- const formatted2 = callees.map((e, i) => {
10902
- const conf = e.confidence !== "Direct" ? ` [${e.confidence.toLowerCase()}]` : "";
10903
- return `[${i + 1}] \u2192 ${e.targetName} (${e.callType})${conf} at line ${e.line}${e.isResolved ? ` [resolved: ${e.toSymbolId}]` : " [unresolved]"}`;
10904
- });
10905
- return { content: [{ type: "text", text: `Callees (${callees.length}):
10906
-
10907
- ${formatted2.join("\n")}` }] };
11124
+ return { content: [{ type: "text", text: formatCallGraphCallees(args.symbolId, callees, args.relationshipType) }] };
10908
11125
  }
10909
11126
  const { callers } = await getCallGraphData(runtime.projectRoot, runtime.host, args);
10910
- if (callers.length === 0) {
10911
- return { content: [{ type: "text", text: `No callers found for "${args.name}"${args.relationshipType ? ` with type ${args.relationshipType}` : ""}.` }] };
10912
- }
10913
- const formatted = callers.map((e, i) => {
10914
- const conf = e.confidence !== "Direct" ? ` [${e.confidence.toLowerCase()}]` : "";
10915
- return `[${i + 1}] \u2190 from ${e.fromSymbolName ?? "<unknown>"} in ${e.fromSymbolFilePath ?? "<unknown file>"} [${e.fromSymbolId}] (${e.callType})${conf} at line ${e.line}${e.isResolved ? " [resolved]" : " [unresolved]"}`;
10916
- });
10917
- return { content: [{ type: "text", text: `"${args.name}" is called by ${callers.length} function(s):
10918
-
10919
- ${formatted.join("\n")}` }] };
11127
+ return { content: [{ type: "text", text: formatCallGraphCallers(args.name, callers, args.relationshipType) }] };
10920
11128
  }
10921
11129
  );
10922
11130
  server.tool(
@@ -10928,17 +11136,8 @@ ${formatted.join("\n")}` }] };
10928
11136
  maxDepth: import_zod2.z.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
10929
11137
  },
10930
11138
  async (args) => {
10931
- const path25 = await getCallGraphPath(runtime.projectRoot, runtime.host, args.from, args.to, args.maxDepth);
10932
- if (path25.length === 0) {
10933
- return { content: [{ type: "text", text: `No path found between "${args.from}" and "${args.to}". They may be in disconnected components, or the call graph index needs updating.` }] };
10934
- }
10935
- const formatted = path25.map((hop, i) => {
10936
- const prefix = i === 0 ? "[start]" : `--${hop.callType}-->`;
10937
- const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
10938
- return `${prefix} ${hop.symbolName}${location}`;
10939
- });
10940
- return { content: [{ type: "text", text: `Path (${path25.length} hops):
10941
- ${formatted.join("\n")}` }] };
11139
+ const path26 = await getCallGraphPath(runtime.projectRoot, runtime.host, args.from, args.to, args.maxDepth);
11140
+ return { content: [{ type: "text", text: formatCallGraphPath(args.from, args.to, path26) }] };
10942
11141
  }
10943
11142
  );
10944
11143
  server.tool(
@@ -11098,7 +11297,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
11098
11297
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
11099
11298
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
11100
11299
  if (wantBigintFsStats) {
11101
- this._stat = (path25) => statMethod(path25, { bigint: true });
11300
+ this._stat = (path26) => statMethod(path26, { bigint: true });
11102
11301
  } else {
11103
11302
  this._stat = statMethod;
11104
11303
  }
@@ -11123,8 +11322,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
11123
11322
  const par = this.parent;
11124
11323
  const fil = par && par.files;
11125
11324
  if (fil && fil.length > 0) {
11126
- const { path: path25, depth } = par;
11127
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path25));
11325
+ const { path: path26, depth } = par;
11326
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path26));
11128
11327
  const awaited = await Promise.all(slice);
11129
11328
  for (const entry of awaited) {
11130
11329
  if (!entry)
@@ -11164,20 +11363,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
11164
11363
  this.reading = false;
11165
11364
  }
11166
11365
  }
11167
- async _exploreDir(path25, depth) {
11366
+ async _exploreDir(path26, depth) {
11168
11367
  let files;
11169
11368
  try {
11170
- files = await (0, import_promises.readdir)(path25, this._rdOptions);
11369
+ files = await (0, import_promises.readdir)(path26, this._rdOptions);
11171
11370
  } catch (error) {
11172
11371
  this._onError(error);
11173
11372
  }
11174
- return { files, depth, path: path25 };
11373
+ return { files, depth, path: path26 };
11175
11374
  }
11176
- async _formatEntry(dirent, path25) {
11375
+ async _formatEntry(dirent, path26) {
11177
11376
  let entry;
11178
11377
  const basename5 = this._isDirent ? dirent.name : dirent;
11179
11378
  try {
11180
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path25, basename5));
11379
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path26, basename5));
11181
11380
  entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename5 };
11182
11381
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
11183
11382
  } catch (err) {
@@ -11577,16 +11776,16 @@ var delFromSet = (main2, prop, item) => {
11577
11776
  };
11578
11777
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
11579
11778
  var FsWatchInstances = /* @__PURE__ */ new Map();
11580
- function createFsWatchInstance(path25, options, listener, errHandler, emitRaw) {
11779
+ function createFsWatchInstance(path26, options, listener, errHandler, emitRaw) {
11581
11780
  const handleEvent = (rawEvent, evPath) => {
11582
- listener(path25);
11583
- emitRaw(rawEvent, evPath, { watchedPath: path25 });
11584
- if (evPath && path25 !== evPath) {
11585
- fsWatchBroadcast(sp.resolve(path25, evPath), KEY_LISTENERS, sp.join(path25, evPath));
11781
+ listener(path26);
11782
+ emitRaw(rawEvent, evPath, { watchedPath: path26 });
11783
+ if (evPath && path26 !== evPath) {
11784
+ fsWatchBroadcast(sp.resolve(path26, evPath), KEY_LISTENERS, sp.join(path26, evPath));
11586
11785
  }
11587
11786
  };
11588
11787
  try {
11589
- return (0, import_node_fs.watch)(path25, {
11788
+ return (0, import_node_fs.watch)(path26, {
11590
11789
  persistent: options.persistent
11591
11790
  }, handleEvent);
11592
11791
  } catch (error) {
@@ -11602,12 +11801,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
11602
11801
  listener(val1, val2, val3);
11603
11802
  });
11604
11803
  };
11605
- var setFsWatchListener = (path25, fullPath, options, handlers) => {
11804
+ var setFsWatchListener = (path26, fullPath, options, handlers) => {
11606
11805
  const { listener, errHandler, rawEmitter } = handlers;
11607
11806
  let cont = FsWatchInstances.get(fullPath);
11608
11807
  let watcher;
11609
11808
  if (!options.persistent) {
11610
- watcher = createFsWatchInstance(path25, options, listener, errHandler, rawEmitter);
11809
+ watcher = createFsWatchInstance(path26, options, listener, errHandler, rawEmitter);
11611
11810
  if (!watcher)
11612
11811
  return;
11613
11812
  return watcher.close.bind(watcher);
@@ -11618,7 +11817,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
11618
11817
  addAndConvert(cont, KEY_RAW, rawEmitter);
11619
11818
  } else {
11620
11819
  watcher = createFsWatchInstance(
11621
- path25,
11820
+ path26,
11622
11821
  options,
11623
11822
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
11624
11823
  errHandler,
@@ -11633,7 +11832,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
11633
11832
  cont.watcherUnusable = true;
11634
11833
  if (isWindows && error.code === "EPERM") {
11635
11834
  try {
11636
- const fd = await (0, import_promises2.open)(path25, "r");
11835
+ const fd = await (0, import_promises2.open)(path26, "r");
11637
11836
  await fd.close();
11638
11837
  broadcastErr(error);
11639
11838
  } catch (err) {
@@ -11664,7 +11863,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
11664
11863
  };
11665
11864
  };
11666
11865
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
11667
- var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
11866
+ var setFsWatchFileListener = (path26, fullPath, options, handlers) => {
11668
11867
  const { listener, rawEmitter } = handlers;
11669
11868
  let cont = FsWatchFileInstances.get(fullPath);
11670
11869
  const copts = cont && cont.options;
@@ -11686,7 +11885,7 @@ var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
11686
11885
  });
11687
11886
  const currmtime = curr.mtimeMs;
11688
11887
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
11689
- foreach(cont.listeners, (listener2) => listener2(path25, curr));
11888
+ foreach(cont.listeners, (listener2) => listener2(path26, curr));
11690
11889
  }
11691
11890
  })
11692
11891
  };
@@ -11716,13 +11915,13 @@ var NodeFsHandler = class {
11716
11915
  * @param listener on fs change
11717
11916
  * @returns closer for the watcher instance
11718
11917
  */
11719
- _watchWithNodeFs(path25, listener) {
11918
+ _watchWithNodeFs(path26, listener) {
11720
11919
  const opts = this.fsw.options;
11721
- const directory = sp.dirname(path25);
11722
- const basename5 = sp.basename(path25);
11920
+ const directory = sp.dirname(path26);
11921
+ const basename5 = sp.basename(path26);
11723
11922
  const parent = this.fsw._getWatchedDir(directory);
11724
11923
  parent.add(basename5);
11725
- const absolutePath = sp.resolve(path25);
11924
+ const absolutePath = sp.resolve(path26);
11726
11925
  const options = {
11727
11926
  persistent: opts.persistent
11728
11927
  };
@@ -11732,12 +11931,12 @@ var NodeFsHandler = class {
11732
11931
  if (opts.usePolling) {
11733
11932
  const enableBin = opts.interval !== opts.binaryInterval;
11734
11933
  options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
11735
- closer = setFsWatchFileListener(path25, absolutePath, options, {
11934
+ closer = setFsWatchFileListener(path26, absolutePath, options, {
11736
11935
  listener,
11737
11936
  rawEmitter: this.fsw._emitRaw
11738
11937
  });
11739
11938
  } else {
11740
- closer = setFsWatchListener(path25, absolutePath, options, {
11939
+ closer = setFsWatchListener(path26, absolutePath, options, {
11741
11940
  listener,
11742
11941
  errHandler: this._boundHandleError,
11743
11942
  rawEmitter: this.fsw._emitRaw
@@ -11759,7 +11958,7 @@ var NodeFsHandler = class {
11759
11958
  let prevStats = stats;
11760
11959
  if (parent.has(basename5))
11761
11960
  return;
11762
- const listener = async (path25, newStats) => {
11961
+ const listener = async (path26, newStats) => {
11763
11962
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
11764
11963
  return;
11765
11964
  if (!newStats || newStats.mtimeMs === 0) {
@@ -11773,11 +11972,11 @@ var NodeFsHandler = class {
11773
11972
  this.fsw._emit(EV.CHANGE, file, newStats2);
11774
11973
  }
11775
11974
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
11776
- this.fsw._closeFile(path25);
11975
+ this.fsw._closeFile(path26);
11777
11976
  prevStats = newStats2;
11778
11977
  const closer2 = this._watchWithNodeFs(file, listener);
11779
11978
  if (closer2)
11780
- this.fsw._addPathCloser(path25, closer2);
11979
+ this.fsw._addPathCloser(path26, closer2);
11781
11980
  } else {
11782
11981
  prevStats = newStats2;
11783
11982
  }
@@ -11809,7 +12008,7 @@ var NodeFsHandler = class {
11809
12008
  * @param item basename of this item
11810
12009
  * @returns true if no more processing is needed for this entry.
11811
12010
  */
11812
- async _handleSymlink(entry, directory, path25, item) {
12011
+ async _handleSymlink(entry, directory, path26, item) {
11813
12012
  if (this.fsw.closed) {
11814
12013
  return;
11815
12014
  }
@@ -11819,7 +12018,7 @@ var NodeFsHandler = class {
11819
12018
  this.fsw._incrReadyCount();
11820
12019
  let linkPath;
11821
12020
  try {
11822
- linkPath = await (0, import_promises2.realpath)(path25);
12021
+ linkPath = await (0, import_promises2.realpath)(path26);
11823
12022
  } catch (e) {
11824
12023
  this.fsw._emitReady();
11825
12024
  return true;
@@ -11829,12 +12028,12 @@ var NodeFsHandler = class {
11829
12028
  if (dir.has(item)) {
11830
12029
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
11831
12030
  this.fsw._symlinkPaths.set(full, linkPath);
11832
- this.fsw._emit(EV.CHANGE, path25, entry.stats);
12031
+ this.fsw._emit(EV.CHANGE, path26, entry.stats);
11833
12032
  }
11834
12033
  } else {
11835
12034
  dir.add(item);
11836
12035
  this.fsw._symlinkPaths.set(full, linkPath);
11837
- this.fsw._emit(EV.ADD, path25, entry.stats);
12036
+ this.fsw._emit(EV.ADD, path26, entry.stats);
11838
12037
  }
11839
12038
  this.fsw._emitReady();
11840
12039
  return true;
@@ -11864,9 +12063,9 @@ var NodeFsHandler = class {
11864
12063
  return;
11865
12064
  }
11866
12065
  const item = entry.path;
11867
- let path25 = sp.join(directory, item);
12066
+ let path26 = sp.join(directory, item);
11868
12067
  current.add(item);
11869
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path25, item)) {
12068
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path26, item)) {
11870
12069
  return;
11871
12070
  }
11872
12071
  if (this.fsw.closed) {
@@ -11875,8 +12074,8 @@ var NodeFsHandler = class {
11875
12074
  }
11876
12075
  if (item === target || !target && !previous.has(item)) {
11877
12076
  this.fsw._incrReadyCount();
11878
- path25 = sp.join(dir, sp.relative(dir, path25));
11879
- this._addToNodeFs(path25, initialAdd, wh, depth + 1);
12077
+ path26 = sp.join(dir, sp.relative(dir, path26));
12078
+ this._addToNodeFs(path26, initialAdd, wh, depth + 1);
11880
12079
  }
11881
12080
  }).on(EV.ERROR, this._boundHandleError);
11882
12081
  return new Promise((resolve15, reject) => {
@@ -11945,13 +12144,13 @@ var NodeFsHandler = class {
11945
12144
  * @param depth Child path actually targeted for watch
11946
12145
  * @param target Child path actually targeted for watch
11947
12146
  */
11948
- async _addToNodeFs(path25, initialAdd, priorWh, depth, target) {
12147
+ async _addToNodeFs(path26, initialAdd, priorWh, depth, target) {
11949
12148
  const ready = this.fsw._emitReady;
11950
- if (this.fsw._isIgnored(path25) || this.fsw.closed) {
12149
+ if (this.fsw._isIgnored(path26) || this.fsw.closed) {
11951
12150
  ready();
11952
12151
  return false;
11953
12152
  }
11954
- const wh = this.fsw._getWatchHelpers(path25);
12153
+ const wh = this.fsw._getWatchHelpers(path26);
11955
12154
  if (priorWh) {
11956
12155
  wh.filterPath = (entry) => priorWh.filterPath(entry);
11957
12156
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -11967,8 +12166,8 @@ var NodeFsHandler = class {
11967
12166
  const follow = this.fsw.options.followSymlinks;
11968
12167
  let closer;
11969
12168
  if (stats.isDirectory()) {
11970
- const absPath = sp.resolve(path25);
11971
- const targetPath = follow ? await (0, import_promises2.realpath)(path25) : path25;
12169
+ const absPath = sp.resolve(path26);
12170
+ const targetPath = follow ? await (0, import_promises2.realpath)(path26) : path26;
11972
12171
  if (this.fsw.closed)
11973
12172
  return;
11974
12173
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -11978,29 +12177,29 @@ var NodeFsHandler = class {
11978
12177
  this.fsw._symlinkPaths.set(absPath, targetPath);
11979
12178
  }
11980
12179
  } else if (stats.isSymbolicLink()) {
11981
- const targetPath = follow ? await (0, import_promises2.realpath)(path25) : path25;
12180
+ const targetPath = follow ? await (0, import_promises2.realpath)(path26) : path26;
11982
12181
  if (this.fsw.closed)
11983
12182
  return;
11984
12183
  const parent = sp.dirname(wh.watchPath);
11985
12184
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
11986
12185
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
11987
- closer = await this._handleDir(parent, stats, initialAdd, depth, path25, wh, targetPath);
12186
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path26, wh, targetPath);
11988
12187
  if (this.fsw.closed)
11989
12188
  return;
11990
12189
  if (targetPath !== void 0) {
11991
- this.fsw._symlinkPaths.set(sp.resolve(path25), targetPath);
12190
+ this.fsw._symlinkPaths.set(sp.resolve(path26), targetPath);
11992
12191
  }
11993
12192
  } else {
11994
12193
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
11995
12194
  }
11996
12195
  ready();
11997
12196
  if (closer)
11998
- this.fsw._addPathCloser(path25, closer);
12197
+ this.fsw._addPathCloser(path26, closer);
11999
12198
  return false;
12000
12199
  } catch (error) {
12001
12200
  if (this.fsw._handleError(error)) {
12002
12201
  ready();
12003
- return path25;
12202
+ return path26;
12004
12203
  }
12005
12204
  }
12006
12205
  }
@@ -12032,35 +12231,35 @@ function createPattern(matcher) {
12032
12231
  if (matcher.path === string)
12033
12232
  return true;
12034
12233
  if (matcher.recursive) {
12035
- const relative10 = sp2.relative(matcher.path, string);
12036
- if (!relative10) {
12234
+ const relative11 = sp2.relative(matcher.path, string);
12235
+ if (!relative11) {
12037
12236
  return false;
12038
12237
  }
12039
- return !relative10.startsWith("..") && !sp2.isAbsolute(relative10);
12238
+ return !relative11.startsWith("..") && !sp2.isAbsolute(relative11);
12040
12239
  }
12041
12240
  return false;
12042
12241
  };
12043
12242
  }
12044
12243
  return () => false;
12045
12244
  }
12046
- function normalizePath2(path25) {
12047
- if (typeof path25 !== "string")
12245
+ function normalizePath2(path26) {
12246
+ if (typeof path26 !== "string")
12048
12247
  throw new Error("string expected");
12049
- path25 = sp2.normalize(path25);
12050
- path25 = path25.replace(/\\/g, "/");
12248
+ path26 = sp2.normalize(path26);
12249
+ path26 = path26.replace(/\\/g, "/");
12051
12250
  let prepend = false;
12052
- if (path25.startsWith("//"))
12251
+ if (path26.startsWith("//"))
12053
12252
  prepend = true;
12054
- path25 = path25.replace(DOUBLE_SLASH_RE, "/");
12253
+ path26 = path26.replace(DOUBLE_SLASH_RE, "/");
12055
12254
  if (prepend)
12056
- path25 = "/" + path25;
12057
- return path25;
12255
+ path26 = "/" + path26;
12256
+ return path26;
12058
12257
  }
12059
12258
  function matchPatterns(patterns, testString, stats) {
12060
- const path25 = normalizePath2(testString);
12259
+ const path26 = normalizePath2(testString);
12061
12260
  for (let index = 0; index < patterns.length; index++) {
12062
12261
  const pattern = patterns[index];
12063
- if (pattern(path25, stats)) {
12262
+ if (pattern(path26, stats)) {
12064
12263
  return true;
12065
12264
  }
12066
12265
  }
@@ -12098,19 +12297,19 @@ var toUnix = (string) => {
12098
12297
  }
12099
12298
  return str;
12100
12299
  };
12101
- var normalizePathToUnix = (path25) => toUnix(sp2.normalize(toUnix(path25)));
12102
- var normalizeIgnored = (cwd = "") => (path25) => {
12103
- if (typeof path25 === "string") {
12104
- return normalizePathToUnix(sp2.isAbsolute(path25) ? path25 : sp2.join(cwd, path25));
12300
+ var normalizePathToUnix = (path26) => toUnix(sp2.normalize(toUnix(path26)));
12301
+ var normalizeIgnored = (cwd = "") => (path26) => {
12302
+ if (typeof path26 === "string") {
12303
+ return normalizePathToUnix(sp2.isAbsolute(path26) ? path26 : sp2.join(cwd, path26));
12105
12304
  } else {
12106
- return path25;
12305
+ return path26;
12107
12306
  }
12108
12307
  };
12109
- var getAbsolutePath = (path25, cwd) => {
12110
- if (sp2.isAbsolute(path25)) {
12111
- return path25;
12308
+ var getAbsolutePath = (path26, cwd) => {
12309
+ if (sp2.isAbsolute(path26)) {
12310
+ return path26;
12112
12311
  }
12113
- return sp2.join(cwd, path25);
12312
+ return sp2.join(cwd, path26);
12114
12313
  };
12115
12314
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
12116
12315
  var DirEntry = class {
@@ -12175,10 +12374,10 @@ var WatchHelper = class {
12175
12374
  dirParts;
12176
12375
  followSymlinks;
12177
12376
  statMethod;
12178
- constructor(path25, follow, fsw) {
12377
+ constructor(path26, follow, fsw) {
12179
12378
  this.fsw = fsw;
12180
- const watchPath = path25;
12181
- this.path = path25 = path25.replace(REPLACER_RE, "");
12379
+ const watchPath = path26;
12380
+ this.path = path26 = path26.replace(REPLACER_RE, "");
12182
12381
  this.watchPath = watchPath;
12183
12382
  this.fullWatchPath = sp2.resolve(watchPath);
12184
12383
  this.dirParts = [];
@@ -12318,20 +12517,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
12318
12517
  this._closePromise = void 0;
12319
12518
  let paths = unifyPaths(paths_);
12320
12519
  if (cwd) {
12321
- paths = paths.map((path25) => {
12322
- const absPath = getAbsolutePath(path25, cwd);
12520
+ paths = paths.map((path26) => {
12521
+ const absPath = getAbsolutePath(path26, cwd);
12323
12522
  return absPath;
12324
12523
  });
12325
12524
  }
12326
- paths.forEach((path25) => {
12327
- this._removeIgnoredPath(path25);
12525
+ paths.forEach((path26) => {
12526
+ this._removeIgnoredPath(path26);
12328
12527
  });
12329
12528
  this._userIgnored = void 0;
12330
12529
  if (!this._readyCount)
12331
12530
  this._readyCount = 0;
12332
12531
  this._readyCount += paths.length;
12333
- Promise.all(paths.map(async (path25) => {
12334
- const res = await this._nodeFsHandler._addToNodeFs(path25, !_internal, void 0, 0, _origAdd);
12532
+ Promise.all(paths.map(async (path26) => {
12533
+ const res = await this._nodeFsHandler._addToNodeFs(path26, !_internal, void 0, 0, _origAdd);
12335
12534
  if (res)
12336
12535
  this._emitReady();
12337
12536
  return res;
@@ -12353,17 +12552,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
12353
12552
  return this;
12354
12553
  const paths = unifyPaths(paths_);
12355
12554
  const { cwd } = this.options;
12356
- paths.forEach((path25) => {
12357
- if (!sp2.isAbsolute(path25) && !this._closers.has(path25)) {
12555
+ paths.forEach((path26) => {
12556
+ if (!sp2.isAbsolute(path26) && !this._closers.has(path26)) {
12358
12557
  if (cwd)
12359
- path25 = sp2.join(cwd, path25);
12360
- path25 = sp2.resolve(path25);
12558
+ path26 = sp2.join(cwd, path26);
12559
+ path26 = sp2.resolve(path26);
12361
12560
  }
12362
- this._closePath(path25);
12363
- this._addIgnoredPath(path25);
12364
- if (this._watched.has(path25)) {
12561
+ this._closePath(path26);
12562
+ this._addIgnoredPath(path26);
12563
+ if (this._watched.has(path26)) {
12365
12564
  this._addIgnoredPath({
12366
- path: path25,
12565
+ path: path26,
12367
12566
  recursive: true
12368
12567
  });
12369
12568
  }
@@ -12427,38 +12626,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
12427
12626
  * @param stats arguments to be passed with event
12428
12627
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
12429
12628
  */
12430
- async _emit(event, path25, stats) {
12629
+ async _emit(event, path26, stats) {
12431
12630
  if (this.closed)
12432
12631
  return;
12433
12632
  const opts = this.options;
12434
12633
  if (isWindows)
12435
- path25 = sp2.normalize(path25);
12634
+ path26 = sp2.normalize(path26);
12436
12635
  if (opts.cwd)
12437
- path25 = sp2.relative(opts.cwd, path25);
12438
- const args = [path25];
12636
+ path26 = sp2.relative(opts.cwd, path26);
12637
+ const args = [path26];
12439
12638
  if (stats != null)
12440
12639
  args.push(stats);
12441
12640
  const awf = opts.awaitWriteFinish;
12442
12641
  let pw;
12443
- if (awf && (pw = this._pendingWrites.get(path25))) {
12642
+ if (awf && (pw = this._pendingWrites.get(path26))) {
12444
12643
  pw.lastChange = /* @__PURE__ */ new Date();
12445
12644
  return this;
12446
12645
  }
12447
12646
  if (opts.atomic) {
12448
12647
  if (event === EVENTS.UNLINK) {
12449
- this._pendingUnlinks.set(path25, [event, ...args]);
12648
+ this._pendingUnlinks.set(path26, [event, ...args]);
12450
12649
  setTimeout(() => {
12451
- this._pendingUnlinks.forEach((entry, path26) => {
12650
+ this._pendingUnlinks.forEach((entry, path27) => {
12452
12651
  this.emit(...entry);
12453
12652
  this.emit(EVENTS.ALL, ...entry);
12454
- this._pendingUnlinks.delete(path26);
12653
+ this._pendingUnlinks.delete(path27);
12455
12654
  });
12456
12655
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
12457
12656
  return this;
12458
12657
  }
12459
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path25)) {
12658
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path26)) {
12460
12659
  event = EVENTS.CHANGE;
12461
- this._pendingUnlinks.delete(path25);
12660
+ this._pendingUnlinks.delete(path26);
12462
12661
  }
12463
12662
  }
12464
12663
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -12476,16 +12675,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
12476
12675
  this.emitWithAll(event, args);
12477
12676
  }
12478
12677
  };
12479
- this._awaitWriteFinish(path25, awf.stabilityThreshold, event, awfEmit);
12678
+ this._awaitWriteFinish(path26, awf.stabilityThreshold, event, awfEmit);
12480
12679
  return this;
12481
12680
  }
12482
12681
  if (event === EVENTS.CHANGE) {
12483
- const isThrottled = !this._throttle(EVENTS.CHANGE, path25, 50);
12682
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path26, 50);
12484
12683
  if (isThrottled)
12485
12684
  return this;
12486
12685
  }
12487
12686
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
12488
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path25) : path25;
12687
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path26) : path26;
12489
12688
  let stats2;
12490
12689
  try {
12491
12690
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -12516,23 +12715,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
12516
12715
  * @param timeout duration of time to suppress duplicate actions
12517
12716
  * @returns tracking object or false if action should be suppressed
12518
12717
  */
12519
- _throttle(actionType, path25, timeout) {
12718
+ _throttle(actionType, path26, timeout) {
12520
12719
  if (!this._throttled.has(actionType)) {
12521
12720
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
12522
12721
  }
12523
12722
  const action = this._throttled.get(actionType);
12524
12723
  if (!action)
12525
12724
  throw new Error("invalid throttle");
12526
- const actionPath = action.get(path25);
12725
+ const actionPath = action.get(path26);
12527
12726
  if (actionPath) {
12528
12727
  actionPath.count++;
12529
12728
  return false;
12530
12729
  }
12531
12730
  let timeoutObject;
12532
12731
  const clear = () => {
12533
- const item = action.get(path25);
12732
+ const item = action.get(path26);
12534
12733
  const count = item ? item.count : 0;
12535
- action.delete(path25);
12734
+ action.delete(path26);
12536
12735
  clearTimeout(timeoutObject);
12537
12736
  if (item)
12538
12737
  clearTimeout(item.timeoutObject);
@@ -12540,7 +12739,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
12540
12739
  };
12541
12740
  timeoutObject = setTimeout(clear, timeout);
12542
12741
  const thr = { timeoutObject, clear, count: 0 };
12543
- action.set(path25, thr);
12742
+ action.set(path26, thr);
12544
12743
  return thr;
12545
12744
  }
12546
12745
  _incrReadyCount() {
@@ -12554,44 +12753,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
12554
12753
  * @param event
12555
12754
  * @param awfEmit Callback to be called when ready for event to be emitted.
12556
12755
  */
12557
- _awaitWriteFinish(path25, threshold, event, awfEmit) {
12756
+ _awaitWriteFinish(path26, threshold, event, awfEmit) {
12558
12757
  const awf = this.options.awaitWriteFinish;
12559
12758
  if (typeof awf !== "object")
12560
12759
  return;
12561
12760
  const pollInterval = awf.pollInterval;
12562
12761
  let timeoutHandler;
12563
- let fullPath = path25;
12564
- if (this.options.cwd && !sp2.isAbsolute(path25)) {
12565
- fullPath = sp2.join(this.options.cwd, path25);
12762
+ let fullPath = path26;
12763
+ if (this.options.cwd && !sp2.isAbsolute(path26)) {
12764
+ fullPath = sp2.join(this.options.cwd, path26);
12566
12765
  }
12567
12766
  const now = /* @__PURE__ */ new Date();
12568
12767
  const writes = this._pendingWrites;
12569
12768
  function awaitWriteFinishFn(prevStat) {
12570
12769
  (0, import_node_fs2.stat)(fullPath, (err, curStat) => {
12571
- if (err || !writes.has(path25)) {
12770
+ if (err || !writes.has(path26)) {
12572
12771
  if (err && err.code !== "ENOENT")
12573
12772
  awfEmit(err);
12574
12773
  return;
12575
12774
  }
12576
12775
  const now2 = Number(/* @__PURE__ */ new Date());
12577
12776
  if (prevStat && curStat.size !== prevStat.size) {
12578
- writes.get(path25).lastChange = now2;
12777
+ writes.get(path26).lastChange = now2;
12579
12778
  }
12580
- const pw = writes.get(path25);
12779
+ const pw = writes.get(path26);
12581
12780
  const df = now2 - pw.lastChange;
12582
12781
  if (df >= threshold) {
12583
- writes.delete(path25);
12782
+ writes.delete(path26);
12584
12783
  awfEmit(void 0, curStat);
12585
12784
  } else {
12586
12785
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
12587
12786
  }
12588
12787
  });
12589
12788
  }
12590
- if (!writes.has(path25)) {
12591
- writes.set(path25, {
12789
+ if (!writes.has(path26)) {
12790
+ writes.set(path26, {
12592
12791
  lastChange: now,
12593
12792
  cancelWait: () => {
12594
- writes.delete(path25);
12793
+ writes.delete(path26);
12595
12794
  clearTimeout(timeoutHandler);
12596
12795
  return event;
12597
12796
  }
@@ -12602,8 +12801,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
12602
12801
  /**
12603
12802
  * Determines whether user has asked to ignore this path.
12604
12803
  */
12605
- _isIgnored(path25, stats) {
12606
- if (this.options.atomic && DOT_RE.test(path25))
12804
+ _isIgnored(path26, stats) {
12805
+ if (this.options.atomic && DOT_RE.test(path26))
12607
12806
  return true;
12608
12807
  if (!this._userIgnored) {
12609
12808
  const { cwd } = this.options;
@@ -12613,17 +12812,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
12613
12812
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
12614
12813
  this._userIgnored = anymatch(list, void 0);
12615
12814
  }
12616
- return this._userIgnored(path25, stats);
12815
+ return this._userIgnored(path26, stats);
12617
12816
  }
12618
- _isntIgnored(path25, stat4) {
12619
- return !this._isIgnored(path25, stat4);
12817
+ _isntIgnored(path26, stat4) {
12818
+ return !this._isIgnored(path26, stat4);
12620
12819
  }
12621
12820
  /**
12622
12821
  * Provides a set of common helpers and properties relating to symlink handling.
12623
12822
  * @param path file or directory pattern being watched
12624
12823
  */
12625
- _getWatchHelpers(path25) {
12626
- return new WatchHelper(path25, this.options.followSymlinks, this);
12824
+ _getWatchHelpers(path26) {
12825
+ return new WatchHelper(path26, this.options.followSymlinks, this);
12627
12826
  }
12628
12827
  // Directory helpers
12629
12828
  // -----------------
@@ -12655,63 +12854,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
12655
12854
  * @param item base path of item/directory
12656
12855
  */
12657
12856
  _remove(directory, item, isDirectory) {
12658
- const path25 = sp2.join(directory, item);
12659
- const fullPath = sp2.resolve(path25);
12660
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path25) || this._watched.has(fullPath);
12661
- if (!this._throttle("remove", path25, 100))
12857
+ const path26 = sp2.join(directory, item);
12858
+ const fullPath = sp2.resolve(path26);
12859
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path26) || this._watched.has(fullPath);
12860
+ if (!this._throttle("remove", path26, 100))
12662
12861
  return;
12663
12862
  if (!isDirectory && this._watched.size === 1) {
12664
12863
  this.add(directory, item, true);
12665
12864
  }
12666
- const wp = this._getWatchedDir(path25);
12865
+ const wp = this._getWatchedDir(path26);
12667
12866
  const nestedDirectoryChildren = wp.getChildren();
12668
- nestedDirectoryChildren.forEach((nested) => this._remove(path25, nested));
12867
+ nestedDirectoryChildren.forEach((nested) => this._remove(path26, nested));
12669
12868
  const parent = this._getWatchedDir(directory);
12670
12869
  const wasTracked = parent.has(item);
12671
12870
  parent.remove(item);
12672
12871
  if (this._symlinkPaths.has(fullPath)) {
12673
12872
  this._symlinkPaths.delete(fullPath);
12674
12873
  }
12675
- let relPath = path25;
12874
+ let relPath = path26;
12676
12875
  if (this.options.cwd)
12677
- relPath = sp2.relative(this.options.cwd, path25);
12876
+ relPath = sp2.relative(this.options.cwd, path26);
12678
12877
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
12679
12878
  const event = this._pendingWrites.get(relPath).cancelWait();
12680
12879
  if (event === EVENTS.ADD)
12681
12880
  return;
12682
12881
  }
12683
- this._watched.delete(path25);
12882
+ this._watched.delete(path26);
12684
12883
  this._watched.delete(fullPath);
12685
12884
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
12686
- if (wasTracked && !this._isIgnored(path25))
12687
- this._emit(eventName, path25);
12688
- this._closePath(path25);
12885
+ if (wasTracked && !this._isIgnored(path26))
12886
+ this._emit(eventName, path26);
12887
+ this._closePath(path26);
12689
12888
  }
12690
12889
  /**
12691
12890
  * Closes all watchers for a path
12692
12891
  */
12693
- _closePath(path25) {
12694
- this._closeFile(path25);
12695
- const dir = sp2.dirname(path25);
12696
- this._getWatchedDir(dir).remove(sp2.basename(path25));
12892
+ _closePath(path26) {
12893
+ this._closeFile(path26);
12894
+ const dir = sp2.dirname(path26);
12895
+ this._getWatchedDir(dir).remove(sp2.basename(path26));
12697
12896
  }
12698
12897
  /**
12699
12898
  * Closes only file-specific watchers
12700
12899
  */
12701
- _closeFile(path25) {
12702
- const closers = this._closers.get(path25);
12900
+ _closeFile(path26) {
12901
+ const closers = this._closers.get(path26);
12703
12902
  if (!closers)
12704
12903
  return;
12705
12904
  closers.forEach((closer) => closer());
12706
- this._closers.delete(path25);
12905
+ this._closers.delete(path26);
12707
12906
  }
12708
- _addPathCloser(path25, closer) {
12907
+ _addPathCloser(path26, closer) {
12709
12908
  if (!closer)
12710
12909
  return;
12711
- let list = this._closers.get(path25);
12910
+ let list = this._closers.get(path26);
12712
12911
  if (!list) {
12713
12912
  list = [];
12714
- this._closers.set(path25, list);
12913
+ this._closers.set(path26, list);
12715
12914
  }
12716
12915
  list.push(closer);
12717
12916
  }
@@ -12741,7 +12940,7 @@ function watch(paths, options = {}) {
12741
12940
  var chokidar_default = { watch, FSWatcher };
12742
12941
 
12743
12942
  // src/watcher/file-watcher.ts
12744
- var path20 = __toESM(require("path"), 1);
12943
+ var path21 = __toESM(require("path"), 1);
12745
12944
  var FileWatcher = class {
12746
12945
  watcher = null;
12747
12946
  projectRoot;
@@ -12767,15 +12966,15 @@ var FileWatcher = class {
12767
12966
  const watchTargets = this.configPath ? [this.projectRoot, this.configPath] : this.projectRoot;
12768
12967
  this.watcher = chokidar_default.watch(watchTargets, {
12769
12968
  ignored: (filePath) => {
12770
- const relativePath = path20.relative(this.projectRoot, filePath);
12969
+ const relativePath = path21.relative(this.projectRoot, filePath);
12771
12970
  if (!relativePath) return false;
12772
12971
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
12773
12972
  return false;
12774
12973
  }
12775
- if (hasFilteredPathSegment(relativePath, path20.sep)) {
12974
+ if (hasFilteredPathSegment(relativePath, path21.sep)) {
12776
12975
  return true;
12777
12976
  }
12778
- if (isRestrictedDirectory(relativePath, path20.sep)) {
12977
+ if (isRestrictedDirectory(relativePath, path21.sep)) {
12779
12978
  return true;
12780
12979
  }
12781
12980
  if (ignoreFilter.ignores(relativePath)) {
@@ -12821,24 +13020,24 @@ var FileWatcher = class {
12821
13020
  this.scheduleFlush();
12822
13021
  }
12823
13022
  isProjectConfigPath(filePath) {
12824
- const relativePath = path20.relative(this.projectRoot, filePath);
12825
- const normalizedRelativePath = path20.normalize(relativePath);
13023
+ const relativePath = path21.relative(this.projectRoot, filePath);
13024
+ const normalizedRelativePath = path21.normalize(relativePath);
12826
13025
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
12827
13026
  }
12828
13027
  isProjectConfigPathOrAncestor(relativePath) {
12829
- const normalizedRelativePath = path20.normalize(relativePath);
13028
+ const normalizedRelativePath = path21.normalize(relativePath);
12830
13029
  return this.getProjectConfigRelativePaths().some(
12831
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path20.sep}`)
13030
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path21.sep}`)
12832
13031
  );
12833
13032
  }
12834
13033
  getProjectConfigRelativePaths() {
12835
13034
  if (this.configPath) {
12836
- return [path20.normalize(path20.relative(this.projectRoot, this.configPath))];
13035
+ return [path21.normalize(path21.relative(this.projectRoot, this.configPath))];
12837
13036
  }
12838
13037
  return [
12839
13038
  resolveProjectConfigPath(this.projectRoot, this.host),
12840
13039
  resolveWritableProjectConfigPath(this.projectRoot, this.host)
12841
- ].map((configPath) => path20.normalize(path20.relative(this.projectRoot, configPath)));
13040
+ ].map((configPath) => path21.normalize(path21.relative(this.projectRoot, configPath)));
12842
13041
  }
12843
13042
  scheduleFlush() {
12844
13043
  if (this.debounceTimer) {
@@ -12853,7 +13052,7 @@ var FileWatcher = class {
12853
13052
  return;
12854
13053
  }
12855
13054
  const changes = Array.from(this.pendingChanges.entries()).map(
12856
- ([path25, type]) => ({ path: path25, type })
13055
+ ([path26, type]) => ({ path: path26, type })
12857
13056
  );
12858
13057
  this.pendingChanges.clear();
12859
13058
  try {
@@ -12880,7 +13079,7 @@ var FileWatcher = class {
12880
13079
  };
12881
13080
 
12882
13081
  // src/watcher/git-head-watcher.ts
12883
- var path21 = __toESM(require("path"), 1);
13082
+ var path22 = __toESM(require("path"), 1);
12884
13083
  var GitHeadWatcher = class {
12885
13084
  watcher = null;
12886
13085
  projectRoot;
@@ -12902,7 +13101,7 @@ var GitHeadWatcher = class {
12902
13101
  this.onBranchChange = handler;
12903
13102
  this.currentBranch = getCurrentBranch(this.projectRoot);
12904
13103
  const headPath = getHeadPath(this.projectRoot);
12905
- const refsPath = path21.join(this.projectRoot, ".git", "refs", "heads");
13104
+ const refsPath = path22.join(this.projectRoot, ".git", "refs", "heads");
12906
13105
  this.watcher = chokidar_default.watch([headPath, refsPath], {
12907
13106
  persistent: true,
12908
13107
  ignoreInitial: true,
@@ -13045,8 +13244,8 @@ function getConfigPaths(projectRoot, host, options) {
13045
13244
  }
13046
13245
 
13047
13246
  // src/tools/visualize/activity.ts
13048
- var import_child_process3 = require("child_process");
13049
- var path22 = __toESM(require("path"), 1);
13247
+ var import_child_process4 = require("child_process");
13248
+ var path23 = __toESM(require("path"), 1);
13050
13249
  function attachRecentActivity(data, projectRoot) {
13051
13250
  const activity = readGitActivity(projectRoot);
13052
13251
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -13057,7 +13256,7 @@ function attachRecentActivity(data, projectRoot) {
13057
13256
  }
13058
13257
  function readGitActivity(projectRoot) {
13059
13258
  try {
13060
- const output = (0, import_child_process3.execFileSync)(
13259
+ const output = (0, import_child_process4.execFileSync)(
13061
13260
  "git",
13062
13261
  ["-C", projectRoot, "log", "--since=90.days", "--numstat", "--date=short", "--pretty=format:__COMMIT__%x09%h%x09%ad%x09%s"],
13063
13262
  { encoding: "utf8", maxBuffer: 8 * 1024 * 1024, stdio: ["ignore", "pipe", "ignore"] }
@@ -13208,7 +13407,7 @@ function normalizePath3(filePath) {
13208
13407
  return filePath.replace(/\\/g, "/");
13209
13408
  }
13210
13409
  function toGitRelativePath(projectRoot, filePath) {
13211
- const relativePath = path22.isAbsolute(filePath) ? path22.relative(projectRoot, filePath) : filePath;
13410
+ const relativePath = path23.isAbsolute(filePath) ? path23.relative(projectRoot, filePath) : filePath;
13212
13411
  return normalizePath3(relativePath);
13213
13412
  }
13214
13413
 
@@ -13466,7 +13665,7 @@ render();
13466
13665
  }
13467
13666
 
13468
13667
  // src/tools/visualize/transform.ts
13469
- var path23 = __toESM(require("path"), 1);
13668
+ var path24 = __toESM(require("path"), 1);
13470
13669
 
13471
13670
  // src/tools/visualize/modules.ts
13472
13671
  var MAX_MODULES = 18;
@@ -13599,8 +13798,8 @@ function compactModules(prefixToNodes) {
13599
13798
  function deriveModules(nodes) {
13600
13799
  const initial = /* @__PURE__ */ new Map();
13601
13800
  for (const node of nodes) {
13602
- const relative10 = stripToProjectRelative(node.filePath);
13603
- const prefix = modulePrefixFromRelativePath(relative10);
13801
+ const relative11 = stripToProjectRelative(node.filePath);
13802
+ const prefix = modulePrefixFromRelativePath(relative11);
13604
13803
  if (!initial.has(prefix)) initial.set(prefix, []);
13605
13804
  initial.get(prefix)?.push(node);
13606
13805
  }
@@ -13726,7 +13925,7 @@ function transformForVisualization(symbols, edges, options = {}) {
13726
13925
  filePath: s.filePath,
13727
13926
  kind: s.kind,
13728
13927
  line: s.startLine,
13729
- directory: path23.dirname(s.filePath),
13928
+ directory: path24.dirname(s.filePath),
13730
13929
  moduleId: "",
13731
13930
  moduleLabel: ""
13732
13931
  }));
@@ -13755,9 +13954,9 @@ function parseArgs(argv) {
13755
13954
  let host = "opencode";
13756
13955
  for (let i = 2; i < argv.length; i++) {
13757
13956
  if (argv[i] === "--project" && argv[i + 1]) {
13758
- project = path24.resolve(argv[++i]);
13957
+ project = path25.resolve(argv[++i]);
13759
13958
  } else if (argv[i] === "--config" && argv[i + 1]) {
13760
- config = path24.resolve(argv[++i]);
13959
+ config = path25.resolve(argv[++i]);
13761
13960
  } else if (argv[i] === "--host" && argv[i + 1]) {
13762
13961
  host = parseHostMode(argv[++i]);
13763
13962
  } else if (argv[i] === "--host") {
@@ -13780,7 +13979,7 @@ function parseVisualizeArgs(argv, cwd) {
13780
13979
  for (let i = 0; i < argv.length; i++) {
13781
13980
  const arg = argv[i];
13782
13981
  if (arg === "--project" && argv[i + 1]) {
13783
- project = path24.resolve(argv[++i]);
13982
+ project = path25.resolve(argv[++i]);
13784
13983
  } else if (arg === "--max" && argv[i + 1]) {
13785
13984
  maxNodes = Number(argv[++i]);
13786
13985
  } else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
@@ -13817,7 +14016,7 @@ async function handleVisualizeCommand(argv, cwd) {
13817
14016
  console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
13818
14017
  return 1;
13819
14018
  }
13820
- const outputPath = path24.join(os5.tmpdir(), `call-graph-${Date.now()}.html`);
14019
+ const outputPath = path25.join(os5.tmpdir(), `call-graph-${Date.now()}.html`);
13821
14020
  (0, import_fs15.writeFileSync)(outputPath, generateVisualizationHtml(vizData), "utf-8");
13822
14021
  console.log(`Temporal call graph visualization generated: ${outputPath}`);
13823
14022
  console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
@@ -13847,7 +14046,7 @@ async function main() {
13847
14046
  const transport = new import_stdio.StdioServerTransport();
13848
14047
  await server.connect(transport);
13849
14048
  let watcher = null;
13850
- const isHomeDir = path24.resolve(args.project) === path24.resolve(os5.homedir());
14049
+ const isHomeDir = path25.resolve(args.project) === path25.resolve(os5.homedir());
13851
14050
  const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
13852
14051
  if (config.indexing.autoIndex && isValidProject) {
13853
14052
  const indexer = getIndexerForProject(args.project, args.host);