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.js CHANGED
@@ -491,7 +491,7 @@ var require_ignore = __commonJS({
491
491
  // path matching.
492
492
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
493
493
  // @returns {TestResult} true if a file is ignored
494
- test(path25, checkUnignored, mode) {
494
+ test(path26, checkUnignored, mode) {
495
495
  let ignored = false;
496
496
  let unignored = false;
497
497
  let matchedRule;
@@ -500,7 +500,7 @@ var require_ignore = __commonJS({
500
500
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
501
501
  return;
502
502
  }
503
- const matched = rule[mode].test(path25);
503
+ const matched = rule[mode].test(path26);
504
504
  if (!matched) {
505
505
  return;
506
506
  }
@@ -521,17 +521,17 @@ var require_ignore = __commonJS({
521
521
  var throwError = (message, Ctor) => {
522
522
  throw new Ctor(message);
523
523
  };
524
- var checkPath = (path25, originalPath, doThrow) => {
525
- if (!isString(path25)) {
524
+ var checkPath = (path26, originalPath, doThrow) => {
525
+ if (!isString(path26)) {
526
526
  return doThrow(
527
527
  `path must be a string, but got \`${originalPath}\``,
528
528
  TypeError
529
529
  );
530
530
  }
531
- if (!path25) {
531
+ if (!path26) {
532
532
  return doThrow(`path must not be empty`, TypeError);
533
533
  }
534
- if (checkPath.isNotRelative(path25)) {
534
+ if (checkPath.isNotRelative(path26)) {
535
535
  const r = "`path.relative()`d";
536
536
  return doThrow(
537
537
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -540,7 +540,7 @@ var require_ignore = __commonJS({
540
540
  }
541
541
  return true;
542
542
  };
543
- var isNotRelative = (path25) => REGEX_TEST_INVALID_PATH.test(path25);
543
+ var isNotRelative = (path26) => REGEX_TEST_INVALID_PATH.test(path26);
544
544
  checkPath.isNotRelative = isNotRelative;
545
545
  checkPath.convert = (p) => p;
546
546
  var Ignore2 = class {
@@ -570,19 +570,19 @@ var require_ignore = __commonJS({
570
570
  }
571
571
  // @returns {TestResult}
572
572
  _test(originalPath, cache, checkUnignored, slices) {
573
- const path25 = originalPath && checkPath.convert(originalPath);
573
+ const path26 = originalPath && checkPath.convert(originalPath);
574
574
  checkPath(
575
- path25,
575
+ path26,
576
576
  originalPath,
577
577
  this._strictPathCheck ? throwError : RETURN_FALSE
578
578
  );
579
- return this._t(path25, cache, checkUnignored, slices);
579
+ return this._t(path26, cache, checkUnignored, slices);
580
580
  }
581
- checkIgnore(path25) {
582
- if (!REGEX_TEST_TRAILING_SLASH.test(path25)) {
583
- return this.test(path25);
581
+ checkIgnore(path26) {
582
+ if (!REGEX_TEST_TRAILING_SLASH.test(path26)) {
583
+ return this.test(path26);
584
584
  }
585
- const slices = path25.split(SLASH2).filter(Boolean);
585
+ const slices = path26.split(SLASH2).filter(Boolean);
586
586
  slices.pop();
587
587
  if (slices.length) {
588
588
  const parent = this._t(
@@ -595,18 +595,18 @@ var require_ignore = __commonJS({
595
595
  return parent;
596
596
  }
597
597
  }
598
- return this._rules.test(path25, false, MODE_CHECK_IGNORE);
598
+ return this._rules.test(path26, false, MODE_CHECK_IGNORE);
599
599
  }
600
- _t(path25, cache, checkUnignored, slices) {
601
- if (path25 in cache) {
602
- return cache[path25];
600
+ _t(path26, cache, checkUnignored, slices) {
601
+ if (path26 in cache) {
602
+ return cache[path26];
603
603
  }
604
604
  if (!slices) {
605
- slices = path25.split(SLASH2).filter(Boolean);
605
+ slices = path26.split(SLASH2).filter(Boolean);
606
606
  }
607
607
  slices.pop();
608
608
  if (!slices.length) {
609
- return cache[path25] = this._rules.test(path25, checkUnignored, MODE_IGNORE);
609
+ return cache[path26] = this._rules.test(path26, checkUnignored, MODE_IGNORE);
610
610
  }
611
611
  const parent = this._t(
612
612
  slices.join(SLASH2) + SLASH2,
@@ -614,29 +614,29 @@ var require_ignore = __commonJS({
614
614
  checkUnignored,
615
615
  slices
616
616
  );
617
- return cache[path25] = parent.ignored ? parent : this._rules.test(path25, checkUnignored, MODE_IGNORE);
617
+ return cache[path26] = parent.ignored ? parent : this._rules.test(path26, checkUnignored, MODE_IGNORE);
618
618
  }
619
- ignores(path25) {
620
- return this._test(path25, this._ignoreCache, false).ignored;
619
+ ignores(path26) {
620
+ return this._test(path26, this._ignoreCache, false).ignored;
621
621
  }
622
622
  createFilter() {
623
- return (path25) => !this.ignores(path25);
623
+ return (path26) => !this.ignores(path26);
624
624
  }
625
625
  filter(paths) {
626
626
  return makeArray(paths).filter(this.createFilter());
627
627
  }
628
628
  // @returns {TestResult}
629
- test(path25) {
630
- return this._test(path25, this._testCache, true);
629
+ test(path26) {
630
+ return this._test(path26, this._testCache, true);
631
631
  }
632
632
  };
633
633
  var factory = (options) => new Ignore2(options);
634
- var isPathValid = (path25) => checkPath(path25 && checkPath.convert(path25), path25, RETURN_FALSE);
634
+ var isPathValid = (path26) => checkPath(path26 && checkPath.convert(path26), path26, RETURN_FALSE);
635
635
  var setupWindows = () => {
636
636
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
637
637
  checkPath.convert = makePosix;
638
638
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
639
- checkPath.isNotRelative = (path25) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path25) || isNotRelative(path25);
639
+ checkPath.isNotRelative = (path26) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path26) || isNotRelative(path26);
640
640
  };
641
641
  if (
642
642
  // Detect `process` so that it can run in browsers.
@@ -655,7 +655,7 @@ var require_ignore = __commonJS({
655
655
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
656
656
  import { realpathSync as realpathSync2, writeFileSync as writeFileSync6 } from "fs";
657
657
  import * as os5 from "os";
658
- import * as path24 from "path";
658
+ import * as path25 from "path";
659
659
  import { fileURLToPath as fileURLToPath2 } from "url";
660
660
 
661
661
  // src/config/constants.ts
@@ -788,7 +788,8 @@ function getDefaultIndexingConfig() {
788
788
  requireProjectMarker: true,
789
789
  maxDepth: 5,
790
790
  maxFilesPerDirectory: 100,
791
- fallbackToTextOnMaxChunks: true
791
+ fallbackToTextOnMaxChunks: true,
792
+ gitBlame: { enabled: false }
792
793
  };
793
794
  }
794
795
  function getDefaultSearchConfig() {
@@ -912,7 +913,10 @@ function parseConfig(raw) {
912
913
  requireProjectMarker: typeof rawIndexing.requireProjectMarker === "boolean" ? rawIndexing.requireProjectMarker : defaultIndexing.requireProjectMarker,
913
914
  maxDepth: typeof rawIndexing.maxDepth === "number" ? rawIndexing.maxDepth < -1 ? -1 : rawIndexing.maxDepth : defaultIndexing.maxDepth,
914
915
  maxFilesPerDirectory: typeof rawIndexing.maxFilesPerDirectory === "number" ? Math.max(1, rawIndexing.maxFilesPerDirectory) : defaultIndexing.maxFilesPerDirectory,
915
- fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks
916
+ fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks,
917
+ gitBlame: {
918
+ enabled: rawIndexing.gitBlame && typeof rawIndexing.gitBlame === "object" && typeof rawIndexing.gitBlame.enabled === "boolean" ? rawIndexing.gitBlame.enabled : defaultIndexing.gitBlame.enabled
919
+ }
916
920
  };
917
921
  const rawSearch = input.search && typeof input.search === "object" ? input.search : {};
918
922
  const search = {
@@ -1097,9 +1101,9 @@ import { mkdirSync, readFileSync, writeFileSync } from "fs";
1097
1101
  import * as path from "path";
1098
1102
 
1099
1103
  // src/eval/report-formatters.ts
1100
- function assertFiniteNumber(value, path25) {
1104
+ function assertFiniteNumber(value, path26) {
1101
1105
  if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
1102
- throw new Error(`${path25} must be a finite number`);
1106
+ throw new Error(`${path26} must be a finite number`);
1103
1107
  }
1104
1108
  return value;
1105
1109
  }
@@ -1288,15 +1292,15 @@ function buildPerQueryArtifact(perQuery) {
1288
1292
 
1289
1293
  // src/eval/runner.ts
1290
1294
  import { existsSync as existsSync8 } from "fs";
1291
- import * as path13 from "path";
1295
+ import * as path14 from "path";
1292
1296
  import { performance as performance3 } from "perf_hooks";
1293
1297
 
1294
1298
  // src/indexer/index.ts
1295
1299
  import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync2, renameSync, unlinkSync, mkdirSync as mkdirSync2, promises as fsPromises2 } from "fs";
1296
- import * as path10 from "path";
1300
+ import * as path11 from "path";
1297
1301
  import { performance as performance2 } from "perf_hooks";
1298
- import { execFile as execFile2 } from "child_process";
1299
- import { promisify as promisify2 } from "util";
1302
+ import { execFile as execFile3 } from "child_process";
1303
+ import { promisify as promisify3 } from "util";
1300
1304
 
1301
1305
  // node_modules/eventemitter3/index.mjs
1302
1306
  var import_index = __toESM(require_eventemitter3(), 1);
@@ -4826,8 +4830,8 @@ function normalizeFiles(rawFiles, projectRoot) {
4826
4830
  const trimmed = raw.trim();
4827
4831
  if (!trimmed) continue;
4828
4832
  const absolute = path9.resolve(projectRoot, trimmed);
4829
- const relative10 = path9.relative(projectRoot, absolute);
4830
- const cleaned = relative10.startsWith("./") ? relative10.slice(2) : relative10;
4833
+ const relative11 = path9.relative(projectRoot, absolute);
4834
+ const cleaned = relative11.startsWith("./") ? relative11.slice(2) : relative11;
4831
4835
  if (!seen.has(cleaned)) {
4832
4836
  seen.add(cleaned);
4833
4837
  result.push(cleaned);
@@ -4836,8 +4840,61 @@ function normalizeFiles(rawFiles, projectRoot) {
4836
4840
  return result;
4837
4841
  }
4838
4842
 
4843
+ // src/indexer/git-blame.ts
4844
+ import { execFile as execFile2 } from "child_process";
4845
+ import * as path10 from "path";
4846
+ import { promisify as promisify2 } from "util";
4847
+ var execFileAsync2 = promisify2(execFile2);
4848
+ function parseGitBlamePorcelain(output) {
4849
+ const commits = /* @__PURE__ */ new Map();
4850
+ let current;
4851
+ for (const line of output.split("\n")) {
4852
+ if (/^[0-9a-f]{40} /.test(line)) {
4853
+ const sha = line.slice(0, 40);
4854
+ current = commits.get(sha) ?? {
4855
+ sha,
4856
+ author: "",
4857
+ authorEmail: "",
4858
+ committedAt: 0,
4859
+ summary: "",
4860
+ lines: 0
4861
+ };
4862
+ commits.set(sha, current);
4863
+ continue;
4864
+ }
4865
+ if (!current) {
4866
+ continue;
4867
+ }
4868
+ if (line.startsWith("author ")) {
4869
+ current.author = line.slice("author ".length);
4870
+ } else if (line.startsWith("author-mail ")) {
4871
+ current.authorEmail = line.slice("author-mail ".length).replace(/^<|>$/g, "");
4872
+ } else if (line.startsWith("author-time ")) {
4873
+ current.committedAt = Number.parseInt(line.slice("author-time ".length), 10);
4874
+ } else if (line.startsWith("summary ")) {
4875
+ current.summary = line.slice("summary ".length);
4876
+ } else if (line.startsWith(" ")) {
4877
+ current.lines += 1;
4878
+ }
4879
+ }
4880
+ return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
4881
+ }
4882
+ async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
4883
+ const relativePath = path10.relative(projectRoot, filePath);
4884
+ try {
4885
+ const { stdout } = await execFileAsync2(
4886
+ "git",
4887
+ ["blame", "--line-porcelain", "-L", `${startLine},${endLine}`, "--", relativePath],
4888
+ { cwd: projectRoot, timeout: 3e4 }
4889
+ );
4890
+ return parseGitBlamePorcelain(stdout);
4891
+ } catch {
4892
+ return void 0;
4893
+ }
4894
+ }
4895
+
4839
4896
  // src/indexer/index.ts
4840
- var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
4897
+ var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab", "bash"]);
4841
4898
  var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
4842
4899
  var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
4843
4900
  "function_declaration",
@@ -4917,6 +4974,45 @@ function isSqliteCorruptionError(error) {
4917
4974
  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");
4918
4975
  }
4919
4976
  var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
4977
+ function metadataFromBlame(blame) {
4978
+ if (!blame) {
4979
+ return {};
4980
+ }
4981
+ return {
4982
+ blameSha: blame.sha,
4983
+ blameAuthor: blame.author,
4984
+ blameAuthorEmail: blame.authorEmail,
4985
+ blameCommittedAt: blame.committedAt,
4986
+ blameSummary: blame.summary
4987
+ };
4988
+ }
4989
+ function blameFromChunkData(chunk) {
4990
+ if (!chunk?.blameSha || !chunk.blameAuthor || !chunk.blameAuthorEmail || chunk.blameCommittedAt === void 0 || !chunk.blameSummary) {
4991
+ return void 0;
4992
+ }
4993
+ return {
4994
+ sha: chunk.blameSha,
4995
+ author: chunk.blameAuthor,
4996
+ authorEmail: chunk.blameAuthorEmail,
4997
+ committedAt: chunk.blameCommittedAt,
4998
+ summary: chunk.blameSummary
4999
+ };
5000
+ }
5001
+ function blameFromMetadata(metadata) {
5002
+ if (!metadata.blameSha || !metadata.blameAuthor || !metadata.blameAuthorEmail || metadata.blameCommittedAt === void 0 || !metadata.blameSummary) {
5003
+ return void 0;
5004
+ }
5005
+ return {
5006
+ sha: metadata.blameSha,
5007
+ author: metadata.blameAuthor,
5008
+ authorEmail: metadata.blameAuthorEmail,
5009
+ committedAt: metadata.blameCommittedAt,
5010
+ summary: metadata.blameSummary
5011
+ };
5012
+ }
5013
+ function hasBlameMetadata(metadata) {
5014
+ return blameFromMetadata(metadata) !== void 0;
5015
+ }
4920
5016
  var INDEX_METADATA_VERSION = "1";
4921
5017
  var EMBEDDING_STRATEGY_VERSION = "2";
4922
5018
  var RANKING_TOKEN_CACHE_LIMIT = 4096;
@@ -5075,9 +5171,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
5075
5171
  return true;
5076
5172
  }
5077
5173
  function isPathWithinRoot(filePath, rootPath) {
5078
- const normalizedFilePath = path10.resolve(filePath);
5079
- const normalizedRoot = path10.resolve(rootPath);
5080
- return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path10.sep}`);
5174
+ const normalizedFilePath = path11.resolve(filePath);
5175
+ const normalizedRoot = path11.resolve(rootPath);
5176
+ return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path11.sep}`);
5081
5177
  }
5082
5178
  var rankingQueryTokenCache = /* @__PURE__ */ new Map();
5083
5179
  var rankingNameTokenCache = /* @__PURE__ */ new Map();
@@ -5836,7 +5932,8 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
5836
5932
  chunkType,
5837
5933
  name: chunk.name ?? void 0,
5838
5934
  language: chunk.language,
5839
- hash: chunk.contentHash
5935
+ hash: chunk.contentHash,
5936
+ ...metadataFromBlame(blameFromChunkData(chunk))
5840
5937
  };
5841
5938
  const baselineScore = existing?.score ?? 0.5;
5842
5939
  candidateUnion.set(chunk.chunkId, {
@@ -5920,7 +6017,8 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
5920
6017
  chunkType,
5921
6018
  name: chunk.name ?? void 0,
5922
6019
  language: chunk.language,
5923
- hash: chunk.contentHash
6020
+ hash: chunk.contentHash,
6021
+ ...metadataFromBlame(blameFromChunkData(chunk))
5924
6022
  }
5925
6023
  });
5926
6024
  }
@@ -6089,6 +6187,21 @@ function matchesSearchFilters(candidate, options, minScore) {
6089
6187
  if (options?.chunkType && candidate.metadata.chunkType !== options.chunkType) {
6090
6188
  return false;
6091
6189
  }
6190
+ if (options?.blameAuthor) {
6191
+ const author = options.blameAuthor.toLowerCase();
6192
+ const candidateAuthor = candidate.metadata.blameAuthor?.toLowerCase();
6193
+ const candidateEmail = candidate.metadata.blameAuthorEmail?.toLowerCase();
6194
+ if (candidateAuthor !== author && candidateEmail !== author) return false;
6195
+ }
6196
+ if (options?.blameSha && !candidate.metadata.blameSha?.toLowerCase().startsWith(options.blameSha.toLowerCase())) {
6197
+ return false;
6198
+ }
6199
+ if (options?.blameSince) {
6200
+ const sinceMs = Date.parse(options.blameSince);
6201
+ if (Number.isNaN(sinceMs)) return false;
6202
+ const committedAt = candidate.metadata.blameCommittedAt;
6203
+ if (committedAt === void 0 || committedAt < Math.floor(sinceMs / 1e3)) return false;
6204
+ }
6092
6205
  return true;
6093
6206
  }
6094
6207
  function unionCandidates(semanticCandidates, keywordCandidates) {
@@ -6132,9 +6245,9 @@ var Indexer = class {
6132
6245
  this.config = config;
6133
6246
  this.host = host;
6134
6247
  this.indexPath = this.getIndexPath();
6135
- this.fileHashCachePath = path10.join(this.indexPath, "file-hashes.json");
6136
- this.failedBatchesPath = path10.join(this.indexPath, "failed-batches.json");
6137
- this.indexingLockPath = path10.join(this.indexPath, "indexing.lock");
6248
+ this.fileHashCachePath = path11.join(this.indexPath, "file-hashes.json");
6249
+ this.failedBatchesPath = path11.join(this.indexPath, "failed-batches.json");
6250
+ this.indexingLockPath = path11.join(this.indexPath, "indexing.lock");
6138
6251
  this.logger = initializeLogger(config.debug);
6139
6252
  }
6140
6253
  getIndexPath() {
@@ -6166,14 +6279,14 @@ var Indexer = class {
6166
6279
  }
6167
6280
  atomicWriteSync(targetPath, data) {
6168
6281
  const tempPath = `${targetPath}.tmp`;
6169
- mkdirSync2(path10.dirname(targetPath), { recursive: true });
6282
+ mkdirSync2(path11.dirname(targetPath), { recursive: true });
6170
6283
  writeFileSync2(tempPath, data);
6171
6284
  renameSync(tempPath, targetPath);
6172
6285
  }
6173
6286
  getScopedRoots() {
6174
- const roots = /* @__PURE__ */ new Set([path10.resolve(this.projectRoot)]);
6287
+ const roots = /* @__PURE__ */ new Set([path11.resolve(this.projectRoot)]);
6175
6288
  for (const kbRoot of this.config.knowledgeBases) {
6176
- roots.add(path10.resolve(this.projectRoot, kbRoot));
6289
+ roots.add(path11.resolve(this.projectRoot, kbRoot));
6177
6290
  }
6178
6291
  return Array.from(roots);
6179
6292
  }
@@ -6182,29 +6295,29 @@ var Indexer = class {
6182
6295
  if (this.config.scope !== "global") {
6183
6296
  return branchName;
6184
6297
  }
6185
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6298
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6186
6299
  return `${projectHash}:${branchName}`;
6187
6300
  }
6188
6301
  getBranchCatalogKeyFor(branchName) {
6189
6302
  if (this.config.scope !== "global") {
6190
6303
  return branchName;
6191
6304
  }
6192
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6305
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6193
6306
  return `${projectHash}:${branchName}`;
6194
6307
  }
6195
6308
  getLegacyBranchCatalogKey() {
6196
6309
  return this.currentBranch || "default";
6197
6310
  }
6198
6311
  getLegacyMigrationMetadataKey() {
6199
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6312
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6200
6313
  return `index.globalBranchMigration.${projectHash}`;
6201
6314
  }
6202
6315
  getProjectEmbeddingStrategyMetadataKey() {
6203
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6316
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6204
6317
  return `index.embeddingStrategyVersion.${projectHash}`;
6205
6318
  }
6206
6319
  getProjectForceReembedMetadataKey() {
6207
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6320
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6208
6321
  return `index.forceReembed.${projectHash}`;
6209
6322
  }
6210
6323
  hasProjectForceReembedPending() {
@@ -6298,7 +6411,7 @@ var Indexer = class {
6298
6411
  if (!this.database) {
6299
6412
  return { chunkIds, symbolIds };
6300
6413
  }
6301
- const projectRootPath = path10.resolve(this.projectRoot);
6414
+ const projectRootPath = path11.resolve(this.projectRoot);
6302
6415
  const projectLocalFilePaths = /* @__PURE__ */ new Set([
6303
6416
  ...Array.from(this.fileHashCache.keys()).filter(
6304
6417
  (filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
@@ -6321,7 +6434,7 @@ var Indexer = class {
6321
6434
  if (this.config.scope !== "global") {
6322
6435
  return this.getBranchCatalogCleanupKeys();
6323
6436
  }
6324
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6437
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6325
6438
  const keys = /* @__PURE__ */ new Set();
6326
6439
  const projectChunkIdSet = new Set(projectChunkIds);
6327
6440
  const projectSymbolIdSet = new Set(projectSymbolIds);
@@ -6405,7 +6518,7 @@ var Indexer = class {
6405
6518
  if (!this.database || this.config.scope !== "global") {
6406
6519
  return false;
6407
6520
  }
6408
- const projectHash = hashContent(path10.resolve(this.projectRoot)).slice(0, 16);
6521
+ const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6409
6522
  const roots = this.getScopedRoots();
6410
6523
  const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
6411
6524
  return this.database.getAllBranches().some(
@@ -6439,7 +6552,7 @@ var Indexer = class {
6439
6552
  ...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
6440
6553
  ...scopedEntries.map(({ metadata }) => metadata.filePath)
6441
6554
  ]);
6442
- const projectRootPath = path10.resolve(this.projectRoot);
6555
+ const projectRootPath = path11.resolve(this.projectRoot);
6443
6556
  const projectLocalFilePaths = new Set(
6444
6557
  Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
6445
6558
  );
@@ -6799,13 +6912,13 @@ var Indexer = class {
6799
6912
  }
6800
6913
  await fsPromises2.mkdir(this.indexPath, { recursive: true });
6801
6914
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
6802
- const storePath = path10.join(this.indexPath, "vectors");
6915
+ const storePath = path11.join(this.indexPath, "vectors");
6803
6916
  this.store = new VectorStore(storePath, dimensions);
6804
- const indexFilePath = path10.join(this.indexPath, "vectors.usearch");
6917
+ const indexFilePath = path11.join(this.indexPath, "vectors.usearch");
6805
6918
  if (existsSync6(indexFilePath)) {
6806
6919
  this.store.load();
6807
6920
  }
6808
- const invertedIndexPath = path10.join(this.indexPath, "inverted-index.json");
6921
+ const invertedIndexPath = path11.join(this.indexPath, "inverted-index.json");
6809
6922
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
6810
6923
  try {
6811
6924
  this.invertedIndex.load();
@@ -6815,7 +6928,7 @@ var Indexer = class {
6815
6928
  }
6816
6929
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
6817
6930
  }
6818
- const dbPath = path10.join(this.indexPath, "codebase.db");
6931
+ const dbPath = path11.join(this.indexPath, "codebase.db");
6819
6932
  let dbIsNew = !existsSync6(dbPath);
6820
6933
  try {
6821
6934
  this.database = new Database(dbPath);
@@ -6896,7 +7009,7 @@ var Indexer = class {
6896
7009
  if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
6897
7010
  return {
6898
7011
  resetCorruptedIndex: true,
6899
- warning: this.getCorruptedIndexWarning(path10.join(this.indexPath, "codebase.db"))
7012
+ warning: this.getCorruptedIndexWarning(path11.join(this.indexPath, "codebase.db"))
6900
7013
  };
6901
7014
  }
6902
7015
  throw error;
@@ -6911,7 +7024,7 @@ var Indexer = class {
6911
7024
  return;
6912
7025
  }
6913
7026
  const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
6914
- const storeBasePath = path10.join(this.indexPath, "vectors");
7027
+ const storeBasePath = path11.join(this.indexPath, "vectors");
6915
7028
  const storeIndexPath = `${storeBasePath}.usearch`;
6916
7029
  const storeMetadataPath = `${storeBasePath}.meta.json`;
6917
7030
  const backupIndexPath = `${storeIndexPath}.bak`;
@@ -6996,7 +7109,7 @@ var Indexer = class {
6996
7109
  if (!isSqliteCorruptionError(error)) {
6997
7110
  return false;
6998
7111
  }
6999
- const dbPath = path10.join(this.indexPath, "codebase.db");
7112
+ const dbPath = path11.join(this.indexPath, "codebase.db");
7000
7113
  const warning = this.getCorruptedIndexWarning(dbPath);
7001
7114
  const errorMessage = getErrorMessage2(error);
7002
7115
  if (this.config.scope === "global") {
@@ -7019,15 +7132,15 @@ var Indexer = class {
7019
7132
  this.indexCompatibility = null;
7020
7133
  this.fileHashCache.clear();
7021
7134
  const resetPaths = [
7022
- path10.join(this.indexPath, "codebase.db"),
7023
- path10.join(this.indexPath, "codebase.db-shm"),
7024
- path10.join(this.indexPath, "codebase.db-wal"),
7025
- path10.join(this.indexPath, "vectors.usearch"),
7026
- path10.join(this.indexPath, "inverted-index.json"),
7027
- path10.join(this.indexPath, "file-hashes.json"),
7028
- path10.join(this.indexPath, "failed-batches.json"),
7029
- path10.join(this.indexPath, "indexing.lock"),
7030
- path10.join(this.indexPath, "vectors")
7135
+ path11.join(this.indexPath, "codebase.db"),
7136
+ path11.join(this.indexPath, "codebase.db-shm"),
7137
+ path11.join(this.indexPath, "codebase.db-wal"),
7138
+ path11.join(this.indexPath, "vectors.usearch"),
7139
+ path11.join(this.indexPath, "inverted-index.json"),
7140
+ path11.join(this.indexPath, "file-hashes.json"),
7141
+ path11.join(this.indexPath, "failed-batches.json"),
7142
+ path11.join(this.indexPath, "indexing.lock"),
7143
+ path11.join(this.indexPath, "vectors")
7031
7144
  ];
7032
7145
  await Promise.all(resetPaths.map(async (targetPath) => {
7033
7146
  try {
@@ -7264,6 +7377,7 @@ var Indexer = class {
7264
7377
  this.logger.debug("Parsed changed files", { parsedCount: parsedFiles.length, parseMs: parseMs.toFixed(2) });
7265
7378
  const existingChunks = /* @__PURE__ */ new Map();
7266
7379
  const existingChunksByFile = /* @__PURE__ */ new Map();
7380
+ const existingMetadataById = /* @__PURE__ */ new Map();
7267
7381
  for (const { key, metadata } of store.getAllMetadata()) {
7268
7382
  if (scopedRoots && !this.isFileInCurrentScope(metadata.filePath, scopedRoots)) {
7269
7383
  continue;
@@ -7272,6 +7386,7 @@ var Indexer = class {
7272
7386
  continue;
7273
7387
  }
7274
7388
  existingChunks.set(key, metadata.hash);
7389
+ existingMetadataById.set(key, metadata);
7275
7390
  const fileChunks = existingChunksByFile.get(metadata.filePath) || /* @__PURE__ */ new Set();
7276
7391
  fileChunks.add(key);
7277
7392
  existingChunksByFile.set(metadata.filePath, fileChunks);
@@ -7279,6 +7394,8 @@ var Indexer = class {
7279
7394
  const currentChunkIds = /* @__PURE__ */ new Set();
7280
7395
  const currentFilePaths = /* @__PURE__ */ new Set();
7281
7396
  const pendingChunks = [];
7397
+ const gitBlameEnabled = this.config.indexing.gitBlame.enabled && isGitRepo(this.projectRoot);
7398
+ let backfilledBlameMetadata = false;
7282
7399
  for (const filePath of unchangedFilePaths) {
7283
7400
  currentFilePaths.add(filePath);
7284
7401
  const fileChunks = existingChunksByFile.get(filePath);
@@ -7289,10 +7406,52 @@ var Indexer = class {
7289
7406
  }
7290
7407
  }
7291
7408
  const chunkDataBatch = [];
7409
+ if (gitBlameEnabled) {
7410
+ const backfillItems = [];
7411
+ for (const chunkId of currentChunkIds) {
7412
+ const metadata = existingMetadataById.get(chunkId);
7413
+ if (!metadata || hasBlameMetadata(metadata)) {
7414
+ continue;
7415
+ }
7416
+ const chunk = database.getChunk(chunkId);
7417
+ if (!chunk) {
7418
+ continue;
7419
+ }
7420
+ const blame = await getChunkGitBlame(this.projectRoot, chunk.filePath, chunk.startLine, chunk.endLine);
7421
+ const blameMetadata = metadataFromBlame(blame);
7422
+ if (!blameMetadata.blameSha) {
7423
+ continue;
7424
+ }
7425
+ chunkDataBatch.push({
7426
+ ...chunk,
7427
+ blameSha: blameMetadata.blameSha,
7428
+ blameAuthor: blameMetadata.blameAuthor,
7429
+ blameAuthorEmail: blameMetadata.blameAuthorEmail,
7430
+ blameCommittedAt: blameMetadata.blameCommittedAt,
7431
+ blameSummary: blameMetadata.blameSummary
7432
+ });
7433
+ const embeddingBuffer = database.getEmbedding(chunk.contentHash);
7434
+ if (!embeddingBuffer) {
7435
+ continue;
7436
+ }
7437
+ backfillItems.push({
7438
+ id: chunkId,
7439
+ vector: Array.from(bufferToFloat32Array(embeddingBuffer)),
7440
+ metadata: {
7441
+ ...metadata,
7442
+ ...blameMetadata
7443
+ }
7444
+ });
7445
+ }
7446
+ if (backfillItems.length > 0) {
7447
+ store.addBatch(backfillItems);
7448
+ backfilledBlameMetadata = true;
7449
+ }
7450
+ }
7292
7451
  for (const parsed of parsedFiles) {
7293
7452
  currentFilePaths.add(parsed.path);
7294
7453
  if (parsed.chunks.length === 0) {
7295
- const relativePath = path10.relative(this.projectRoot, parsed.path);
7454
+ const relativePath = path11.relative(this.projectRoot, parsed.path);
7296
7455
  stats.parseFailures.push(relativePath);
7297
7456
  }
7298
7457
  let fileChunkCount = 0;
@@ -7313,6 +7472,10 @@ var Indexer = class {
7313
7472
  }
7314
7473
  const id = generateChunkId(parsed.path, chunk);
7315
7474
  const contentHash = generateChunkHash(chunk);
7475
+ const existingContentHash = existingChunks.get(id);
7476
+ const existingChunk = gitBlameEnabled ? database.getChunk(id) : null;
7477
+ const blame = gitBlameEnabled && existingContentHash !== contentHash ? await getChunkGitBlame(this.projectRoot, parsed.path, chunk.startLine, chunk.endLine) : blameFromChunkData(existingChunk);
7478
+ const blameMetadata = metadataFromBlame(blame);
7316
7479
  currentChunkIds.add(id);
7317
7480
  chunkDataBatch.push({
7318
7481
  chunkId: id,
@@ -7322,9 +7485,14 @@ var Indexer = class {
7322
7485
  endLine: chunk.endLine,
7323
7486
  nodeType: chunk.chunkType,
7324
7487
  name: chunk.name,
7325
- language: chunk.language
7488
+ language: chunk.language,
7489
+ blameSha: blameMetadata.blameSha,
7490
+ blameAuthor: blameMetadata.blameAuthor,
7491
+ blameAuthorEmail: blameMetadata.blameAuthorEmail,
7492
+ blameCommittedAt: blameMetadata.blameCommittedAt,
7493
+ blameSummary: blameMetadata.blameSummary
7326
7494
  });
7327
- if (existingChunks.get(id) === contentHash) {
7495
+ if (existingContentHash === contentHash) {
7328
7496
  fileChunkCount++;
7329
7497
  continue;
7330
7498
  }
@@ -7339,7 +7507,8 @@ var Indexer = class {
7339
7507
  chunkType: chunk.chunkType,
7340
7508
  name: chunk.name,
7341
7509
  language: chunk.language,
7342
- hash: contentHash
7510
+ hash: contentHash,
7511
+ ...blameMetadata
7343
7512
  };
7344
7513
  pendingChunks.push({
7345
7514
  id,
@@ -7482,6 +7651,9 @@ var Indexer = class {
7482
7651
  database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
7483
7652
  database.clearBranchSymbols(branchCatalogKey);
7484
7653
  database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
7654
+ if (backfilledBlameMetadata) {
7655
+ store.save();
7656
+ }
7485
7657
  if (scopedRoots) {
7486
7658
  this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
7487
7659
  this.clearScopedFailedBatches(scopedRoots);
@@ -8045,7 +8217,8 @@ var Indexer = class {
8045
8217
  content,
8046
8218
  score: r.score,
8047
8219
  chunkType: r.metadata.chunkType,
8048
- name: r.metadata.name
8220
+ name: r.metadata.name,
8221
+ blame: blameFromMetadata(r.metadata)
8049
8222
  };
8050
8223
  })
8051
8224
  );
@@ -8137,12 +8310,12 @@ var Indexer = class {
8137
8310
  this.indexCompatibility = compatibility;
8138
8311
  return;
8139
8312
  }
8140
- const localProjectIndexPaths = [path10.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
8313
+ const localProjectIndexPaths = [path11.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
8141
8314
  if (this.host !== "opencode") {
8142
- localProjectIndexPaths.push(path10.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
8315
+ localProjectIndexPaths.push(path11.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
8143
8316
  }
8144
8317
  const isLocalProjectIndex = localProjectIndexPaths.some(
8145
- (localPath) => path10.resolve(this.indexPath) === path10.resolve(localPath)
8318
+ (localPath) => path11.resolve(this.indexPath) === path11.resolve(localPath)
8146
8319
  );
8147
8320
  if (!isLocalProjectIndex) {
8148
8321
  throw new Error(
@@ -8232,7 +8405,7 @@ var Indexer = class {
8232
8405
  gcOrphanSymbols: 0,
8233
8406
  gcOrphanCallEdges: 0,
8234
8407
  resetCorruptedIndex: true,
8235
- warning: this.getCorruptedIndexWarning(path10.join(this.indexPath, "codebase.db"))
8408
+ warning: this.getCorruptedIndexWarning(path11.join(this.indexPath, "codebase.db"))
8236
8409
  };
8237
8410
  }
8238
8411
  this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
@@ -8552,7 +8725,8 @@ var Indexer = class {
8552
8725
  content,
8553
8726
  score: r.score,
8554
8727
  chunkType: r.metadata.chunkType,
8555
- name: r.metadata.name
8728
+ name: r.metadata.name,
8729
+ blame: blameFromMetadata(r.metadata)
8556
8730
  };
8557
8731
  })
8558
8732
  );
@@ -8589,9 +8763,9 @@ var Indexer = class {
8589
8763
  const { database } = await this.ensureInitialized();
8590
8764
  let shortest = [];
8591
8765
  for (const branchKey of this.getBranchCatalogKeys()) {
8592
- const path25 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
8593
- if (path25.length > 0 && (shortest.length === 0 || path25.length < shortest.length)) {
8594
- shortest = path25;
8766
+ const path26 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
8767
+ if (path26.length > 0 && (shortest.length === 0 || path26.length < shortest.length)) {
8768
+ shortest = path26;
8595
8769
  }
8596
8770
  }
8597
8771
  return shortest;
@@ -8623,7 +8797,7 @@ var Indexer = class {
8623
8797
  }
8624
8798
  async getPrImpact(opts) {
8625
8799
  const { database } = await this.ensureInitialized();
8626
- const execFileAsync2 = promisify2(execFile2);
8800
+ const execFileAsync3 = promisify3(execFile3);
8627
8801
  const changedFilesResult = await getChangedFiles({
8628
8802
  pr: opts.pr,
8629
8803
  branch: opts.branch,
@@ -8645,7 +8819,7 @@ var Indexer = class {
8645
8819
  "Run index_codebase first to build the call graph and symbol index for this branch."
8646
8820
  );
8647
8821
  }
8648
- const absoluteChangedFiles = changedFiles.map((f) => path10.resolve(this.projectRoot, f));
8822
+ const absoluteChangedFiles = changedFiles.map((f) => path11.resolve(this.projectRoot, f));
8649
8823
  const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
8650
8824
  const directIds = directSymbols.map((s) => s.id);
8651
8825
  const direction = opts.direction ?? "both";
@@ -8707,7 +8881,7 @@ var Indexer = class {
8707
8881
  if (opts.checkConflicts) {
8708
8882
  conflictingPRs = [];
8709
8883
  try {
8710
- const { stdout } = await execFileAsync2(
8884
+ const { stdout } = await execFileAsync3(
8711
8885
  "gh",
8712
8886
  ["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "10000"],
8713
8887
  { cwd: this.projectRoot, timeout: 3e4 }
@@ -8728,7 +8902,7 @@ var Indexer = class {
8728
8902
  projectRoot: this.projectRoot,
8729
8903
  baseBranch: this.baseBranch
8730
8904
  });
8731
- const otherAbsolute = otherChanged.files.map((f) => path10.resolve(this.projectRoot, f));
8905
+ const otherAbsolute = otherChanged.files.map((f) => path11.resolve(this.projectRoot, f));
8732
8906
  const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
8733
8907
  const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
8734
8908
  const otherLabels = /* @__PURE__ */ new Set();
@@ -8791,12 +8965,12 @@ var Indexer = class {
8791
8965
  if (meta.filePath) filePaths.add(meta.filePath);
8792
8966
  }
8793
8967
  const directory = options?.directory?.replace(/\/$/, "");
8794
- const absoluteDirectoryFilter = directory ? path10.resolve(this.projectRoot, directory) : void 0;
8968
+ const absoluteDirectoryFilter = directory ? path11.resolve(this.projectRoot, directory) : void 0;
8795
8969
  for (const filePath of filePaths) {
8796
8970
  if (directory) {
8797
- const absoluteFilePath = path10.resolve(filePath);
8971
+ const absoluteFilePath = path11.resolve(filePath);
8798
8972
  const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
8799
- const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path10.sep));
8973
+ const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path11.sep));
8800
8974
  if (!matchesRelative && !matchesProjectRelative) {
8801
8975
  continue;
8802
8976
  }
@@ -9076,13 +9250,13 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
9076
9250
  // src/eval/runner-config.ts
9077
9251
  import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync3 } from "fs";
9078
9252
  import * as os4 from "os";
9079
- import * as path12 from "path";
9253
+ import * as path13 from "path";
9080
9254
 
9081
9255
  // src/config/rebase.ts
9082
- import * as path11 from "path";
9256
+ import * as path12 from "path";
9083
9257
  function isWithinRoot(rootDir, targetPath) {
9084
- const relativePath = path11.relative(rootDir, targetPath);
9085
- return relativePath === "" || !relativePath.startsWith("..") && !path11.isAbsolute(relativePath);
9258
+ const relativePath = path12.relative(rootDir, targetPath);
9259
+ return relativePath === "" || !relativePath.startsWith("..") && !path12.isAbsolute(relativePath);
9086
9260
  }
9087
9261
  function rebasePathEntries(values, fromDir, toDir) {
9088
9262
  if (!Array.isArray(values)) {
@@ -9090,10 +9264,10 @@ function rebasePathEntries(values, fromDir, toDir) {
9090
9264
  }
9091
9265
  return values.filter((value) => typeof value === "string").map((value) => {
9092
9266
  const trimmed = value.trim();
9093
- if (!trimmed || path11.isAbsolute(trimmed)) {
9267
+ if (!trimmed || path12.isAbsolute(trimmed)) {
9094
9268
  return trimmed;
9095
9269
  }
9096
- return normalizePathSeparators(path11.normalize(path11.relative(toDir, path11.resolve(fromDir, trimmed))));
9270
+ return normalizePathSeparators(path12.normalize(path12.relative(toDir, path12.resolve(fromDir, trimmed))));
9097
9271
  }).filter(Boolean);
9098
9272
  }
9099
9273
  function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
@@ -9105,17 +9279,17 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
9105
9279
  if (!trimmed) {
9106
9280
  return trimmed;
9107
9281
  }
9108
- if (path11.isAbsolute(trimmed)) {
9282
+ if (path12.isAbsolute(trimmed)) {
9109
9283
  if (isWithinRoot(sourceRoot, trimmed)) {
9110
- return normalizePathSeparators(path11.normalize(path11.relative(sourceRoot, trimmed) || "."));
9284
+ return normalizePathSeparators(path12.normalize(path12.relative(sourceRoot, trimmed) || "."));
9111
9285
  }
9112
- return path11.normalize(trimmed);
9286
+ return path12.normalize(trimmed);
9113
9287
  }
9114
- const resolvedFromSource = path11.resolve(sourceRoot, trimmed);
9288
+ const resolvedFromSource = path12.resolve(sourceRoot, trimmed);
9115
9289
  if (isWithinRoot(sourceRoot, resolvedFromSource)) {
9116
- return normalizePathSeparators(path11.normalize(trimmed));
9290
+ return normalizePathSeparators(path12.normalize(trimmed));
9117
9291
  }
9118
- return normalizePathSeparators(path11.normalize(path11.relative(targetRoot, resolvedFromSource)));
9292
+ return normalizePathSeparators(path12.normalize(path12.relative(targetRoot, resolvedFromSource)));
9119
9293
  }).filter(Boolean);
9120
9294
  }
9121
9295
 
@@ -9163,20 +9337,20 @@ function parseJsonConfigFile(filePath) {
9163
9337
  }
9164
9338
  }
9165
9339
  function toAbsolute(projectRoot, maybeRelative) {
9166
- return path12.isAbsolute(maybeRelative) ? maybeRelative : path12.join(projectRoot, maybeRelative);
9340
+ return path13.isAbsolute(maybeRelative) ? maybeRelative : path13.join(projectRoot, maybeRelative);
9167
9341
  }
9168
9342
  function isProjectScopedConfigPath(configPath) {
9169
- return path12.basename(configPath) === "codebase-index.json" && path12.basename(path12.dirname(configPath)) === ".opencode";
9343
+ return path13.basename(configPath) === "codebase-index.json" && path13.basename(path13.dirname(configPath)) === ".opencode";
9170
9344
  }
9171
9345
  function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfigPath) {
9172
9346
  const config = rawConfig && typeof rawConfig === "object" ? { ...rawConfig } : {};
9173
9347
  const rebaseEntries = (values) => isProjectScopedConfigPath(resolvedConfigPath) ? resolveInheritedKnowledgeBaseEntries(
9174
9348
  values,
9175
- path12.dirname(path12.dirname(resolvedConfigPath)),
9349
+ path13.dirname(path13.dirname(resolvedConfigPath)),
9176
9350
  projectRoot
9177
9351
  ) : rebasePathEntries(
9178
9352
  values,
9179
- path12.dirname(resolvedConfigPath),
9353
+ path13.dirname(resolvedConfigPath),
9180
9354
  projectRoot
9181
9355
  );
9182
9356
  if (Array.isArray(config.knowledgeBases)) {
@@ -9204,7 +9378,7 @@ function loadRawConfig(projectRoot, configPath) {
9204
9378
  projectConfig
9205
9379
  );
9206
9380
  }
9207
- const globalConfig = path12.join(os4.homedir(), ".config", "opencode", "codebase-index.json");
9381
+ const globalConfig = path13.join(os4.homedir(), ".config", "opencode", "codebase-index.json");
9208
9382
  if (existsSync7(globalConfig)) {
9209
9383
  return parseJsonConfigFile(globalConfig);
9210
9384
  }
@@ -9214,10 +9388,10 @@ function getIndexRootPath(projectRoot, scope) {
9214
9388
  return scope === "global" ? getGlobalIndexPath() : resolveProjectIndexPath(projectRoot, scope);
9215
9389
  }
9216
9390
  function getLocalProjectIndexRoot(projectRoot) {
9217
- return path12.join(projectRoot, ".opencode", "index");
9391
+ return path13.join(projectRoot, ".opencode", "index");
9218
9392
  }
9219
9393
  function getLocalProjectConfigPath(projectRoot) {
9220
- return path12.join(projectRoot, ".opencode", "codebase-index.json");
9394
+ return path13.join(projectRoot, ".opencode", "codebase-index.json");
9221
9395
  }
9222
9396
  function clearIndexRoot(projectRoot, scope) {
9223
9397
  const indexRoot = scope === "global" ? getIndexRootPath(projectRoot, scope) : getLocalProjectIndexRoot(projectRoot);
@@ -9239,7 +9413,7 @@ function ensureLocalEvalProjectConfig(projectRoot, configPath) {
9239
9413
  projectRoot,
9240
9414
  resolvedConfigPath
9241
9415
  );
9242
- mkdirSync3(path12.dirname(localConfigPath), { recursive: true });
9416
+ mkdirSync3(path13.dirname(localConfigPath), { recursive: true });
9243
9417
  writeFileSync3(localConfigPath, JSON.stringify(sourceConfig, null, 2), "utf-8");
9244
9418
  return localConfigPath;
9245
9419
  }
@@ -9289,23 +9463,23 @@ function isRecord2(value) {
9289
9463
  function isStringArray3(value) {
9290
9464
  return Array.isArray(value) && value.every((item) => typeof item === "string");
9291
9465
  }
9292
- function asPositiveNumber(value, path25) {
9466
+ function asPositiveNumber(value, path26) {
9293
9467
  if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
9294
- throw new Error(`${path25} must be a non-negative number`);
9468
+ throw new Error(`${path26} must be a non-negative number`);
9295
9469
  }
9296
9470
  return value;
9297
9471
  }
9298
- function parseQueryType(value, path25) {
9472
+ function parseQueryType(value, path26) {
9299
9473
  if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy") {
9300
9474
  return value;
9301
9475
  }
9302
9476
  throw new Error(
9303
- `${path25} must be one of: definition, implementation-intent, similarity, keyword-heavy`
9477
+ `${path26} must be one of: definition, implementation-intent, similarity, keyword-heavy`
9304
9478
  );
9305
9479
  }
9306
- function parseExpected(input, path25) {
9480
+ function parseExpected(input, path26) {
9307
9481
  if (!isRecord2(input)) {
9308
- throw new Error(`${path25} must be an object`);
9482
+ throw new Error(`${path26} must be an object`);
9309
9483
  }
9310
9484
  const filePathRaw = input.filePath;
9311
9485
  const acceptableFilesRaw = input.acceptableFiles;
@@ -9314,16 +9488,16 @@ function parseExpected(input, path25) {
9314
9488
  const filePath = typeof filePathRaw === "string" ? filePathRaw : void 0;
9315
9489
  const acceptableFiles = isStringArray3(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
9316
9490
  if (!filePath && (!acceptableFiles || acceptableFiles.length === 0)) {
9317
- throw new Error(`${path25} must include either expected.filePath or expected.acceptableFiles`);
9491
+ throw new Error(`${path26} must include either expected.filePath or expected.acceptableFiles`);
9318
9492
  }
9319
9493
  if (acceptableFilesRaw !== void 0 && !isStringArray3(acceptableFilesRaw)) {
9320
- throw new Error(`${path25}.acceptableFiles must be an array of strings`);
9494
+ throw new Error(`${path26}.acceptableFiles must be an array of strings`);
9321
9495
  }
9322
9496
  if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
9323
- throw new Error(`${path25}.symbol must be a string when provided`);
9497
+ throw new Error(`${path26}.symbol must be a string when provided`);
9324
9498
  }
9325
9499
  if (branchRaw !== void 0 && typeof branchRaw !== "string") {
9326
- throw new Error(`${path25}.branch must be a string when provided`);
9500
+ throw new Error(`${path26}.branch must be a string when provided`);
9327
9501
  }
9328
9502
  return {
9329
9503
  filePath,
@@ -9333,25 +9507,25 @@ function parseExpected(input, path25) {
9333
9507
  };
9334
9508
  }
9335
9509
  function parseQuery(input, index) {
9336
- const path25 = `queries[${index}]`;
9510
+ const path26 = `queries[${index}]`;
9337
9511
  if (!isRecord2(input)) {
9338
- throw new Error(`${path25} must be an object`);
9512
+ throw new Error(`${path26} must be an object`);
9339
9513
  }
9340
9514
  const id = input.id;
9341
9515
  const query = input.query;
9342
9516
  const queryType = input.queryType;
9343
9517
  const expected = input.expected;
9344
9518
  if (typeof id !== "string" || id.trim().length === 0) {
9345
- throw new Error(`${path25}.id must be a non-empty string`);
9519
+ throw new Error(`${path26}.id must be a non-empty string`);
9346
9520
  }
9347
9521
  if (typeof query !== "string" || query.trim().length === 0) {
9348
- throw new Error(`${path25}.query must be a non-empty string`);
9522
+ throw new Error(`${path26}.query must be a non-empty string`);
9349
9523
  }
9350
9524
  return {
9351
9525
  id,
9352
9526
  query,
9353
- queryType: parseQueryType(queryType, `${path25}.queryType`),
9354
- expected: parseExpected(expected, `${path25}.expected`)
9527
+ queryType: parseQueryType(queryType, `${path26}.queryType`),
9528
+ expected: parseExpected(expected, `${path26}.expected`)
9355
9529
  };
9356
9530
  }
9357
9531
  function parseGoldenDataset(raw, sourceLabel) {
@@ -9534,13 +9708,13 @@ async function runEvaluation(options) {
9534
9708
  };
9535
9709
  const outputDir = createRunDirectory(toAbsolute(options.projectRoot, options.outputRoot));
9536
9710
  const perQueryArtifact = buildPerQueryArtifact(perQuery);
9537
- writeJson(path13.join(outputDir, "summary.json"), summary);
9538
- writeJson(path13.join(outputDir, "per-query.json"), perQueryArtifact);
9711
+ writeJson(path14.join(outputDir, "summary.json"), summary);
9712
+ writeJson(path14.join(outputDir, "per-query.json"), perQueryArtifact);
9539
9713
  let comparison;
9540
9714
  if (againstPath) {
9541
9715
  const baseline = loadSummary(againstPath);
9542
9716
  comparison = compareSummaries(summary, baseline, againstPath);
9543
- writeJson(path13.join(outputDir, "compare.json"), comparison);
9717
+ writeJson(path14.join(outputDir, "compare.json"), comparison);
9544
9718
  }
9545
9719
  let gate;
9546
9720
  if (options.ciMode) {
@@ -9553,7 +9727,7 @@ async function runEvaluation(options) {
9553
9727
  if (existsSync8(resolvedBaseline)) {
9554
9728
  const baselineSummary = loadSummary(resolvedBaseline);
9555
9729
  comparison = compareSummaries(summary, baselineSummary, resolvedBaseline);
9556
- writeJson(path13.join(outputDir, "compare.json"), comparison);
9730
+ writeJson(path14.join(outputDir, "compare.json"), comparison);
9557
9731
  } else if (budget.failOnMissingBaseline) {
9558
9732
  throw new Error(
9559
9733
  `Budget baseline is missing: ${resolvedBaseline}. Set failOnMissingBaseline=false to allow CI run without baseline.`
@@ -9563,7 +9737,7 @@ async function runEvaluation(options) {
9563
9737
  gate = evaluateBudgetGate(budget, summary, comparison);
9564
9738
  }
9565
9739
  const markdown = createSummaryMarkdown(summary, comparison, gate);
9566
- writeText(path13.join(outputDir, "summary.md"), markdown);
9740
+ writeText(path14.join(outputDir, "summary.md"), markdown);
9567
9741
  return { outputDir, summary, perQuery, comparison, gate };
9568
9742
  } finally {
9569
9743
  await indexer.close();
@@ -9621,23 +9795,23 @@ async function runSweep(options, sweep) {
9621
9795
  bestByMrrAt10,
9622
9796
  bestByP95Latency
9623
9797
  };
9624
- writeJson(path13.join(outputDir, "compare.json"), aggregate);
9798
+ writeJson(path14.join(outputDir, "compare.json"), aggregate);
9625
9799
  const md = createSummaryMarkdown(
9626
9800
  bestByHitAt5?.summary ?? runs[0].summary,
9627
9801
  bestByHitAt5?.comparison,
9628
9802
  void 0,
9629
9803
  aggregate
9630
9804
  );
9631
- writeText(path13.join(outputDir, "summary.md"), md);
9632
- writeJson(path13.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
9805
+ writeText(path14.join(outputDir, "summary.md"), md);
9806
+ writeJson(path14.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
9633
9807
  return { outputDir, aggregate };
9634
9808
  }
9635
9809
 
9636
9810
  // src/eval/cli.ts
9637
- import * as path15 from "path";
9811
+ import * as path16 from "path";
9638
9812
 
9639
9813
  // src/eval/cli-parser.ts
9640
- import * as path14 from "path";
9814
+ import * as path15 from "path";
9641
9815
  function printUsage() {
9642
9816
  console.log(`
9643
9817
  Usage:
@@ -9709,12 +9883,12 @@ function parseEvalArgs(argv, cwd) {
9709
9883
  const arg = argv[i];
9710
9884
  const next = argv[i + 1];
9711
9885
  if (arg === "--project" && next) {
9712
- parsed.projectRoot = path14.resolve(cwd, next);
9886
+ parsed.projectRoot = path15.resolve(cwd, next);
9713
9887
  i += 1;
9714
9888
  continue;
9715
9889
  }
9716
9890
  if (arg === "--config" && next) {
9717
- parsed.configPath = path14.resolve(cwd, next);
9891
+ parsed.configPath = path15.resolve(cwd, next);
9718
9892
  i += 1;
9719
9893
  continue;
9720
9894
  }
@@ -9910,22 +10084,22 @@ async function handleEvalCommand(args, cwd) {
9910
10084
  if (!parsed.againstPath.endsWith(".json")) {
9911
10085
  throw new Error("eval diff --against must point to a summary JSON file");
9912
10086
  }
9913
- const currentSummary = loadSummary(path15.resolve(parsed.projectRoot, currentPath), {
10087
+ const currentSummary = loadSummary(path16.resolve(parsed.projectRoot, currentPath), {
9914
10088
  allowLegacyDiversityMetrics: true
9915
10089
  });
9916
- const baselineSummary = loadSummary(path15.resolve(parsed.projectRoot, parsed.againstPath), {
10090
+ const baselineSummary = loadSummary(path16.resolve(parsed.projectRoot, parsed.againstPath), {
9917
10091
  allowLegacyDiversityMetrics: true
9918
10092
  });
9919
10093
  const comparison = compareSummaries(
9920
10094
  currentSummary,
9921
10095
  baselineSummary,
9922
- path15.resolve(parsed.projectRoot, parsed.againstPath)
10096
+ path16.resolve(parsed.projectRoot, parsed.againstPath)
9923
10097
  );
9924
- const outputDir = createRunDirectory(path15.resolve(parsed.projectRoot, parsed.outputRoot));
10098
+ const outputDir = createRunDirectory(path16.resolve(parsed.projectRoot, parsed.outputRoot));
9925
10099
  const summaryMd = createSummaryMarkdown(currentSummary, comparison);
9926
- writeJson(path15.join(outputDir, "compare.json"), comparison);
9927
- writeText(path15.join(outputDir, "summary.md"), summaryMd);
9928
- writeJson(path15.join(outputDir, "summary.json"), currentSummary);
10100
+ writeJson(path16.join(outputDir, "compare.json"), comparison);
10101
+ writeText(path16.join(outputDir, "summary.md"), summaryMd);
10102
+ writeJson(path16.join(outputDir, "summary.json"), currentSummary);
9929
10103
  console.log(`Eval diff complete. Artifacts: ${outputDir}`);
9930
10104
  return 0;
9931
10105
  }
@@ -10170,6 +10344,17 @@ function calculatePercentage(progress) {
10170
10344
  if (progress.phase === "storing") return 95;
10171
10345
  return 0;
10172
10346
  }
10347
+ function formatCodebasePeek(results) {
10348
+ if (results.length === 0) {
10349
+ return "No matching code found. Try a different query or run index_codebase first.";
10350
+ }
10351
+ const formatted = results.map((r, idx) => {
10352
+ const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
10353
+ const name = r.name ? `"${r.name}"` : "(anonymous)";
10354
+ return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})${formatBlame(r)}`;
10355
+ });
10356
+ return formatted.join("\n");
10357
+ }
10173
10358
  function formatHealthCheck(result) {
10174
10359
  if (result.resetCorruptedIndex) {
10175
10360
  return result.warning ?? "Detected a corrupted local index and reset it. Run index_codebase to rebuild search data.";
@@ -10198,16 +10383,68 @@ function formatHealthCheck(result) {
10198
10383
  }
10199
10384
  return lines.join("\n");
10200
10385
  }
10386
+ function formatCallGraphCallers(name, callers, relationshipType) {
10387
+ if (callers.length === 0) {
10388
+ return `No callers found for "${name}"${relationshipType ? ` with type ${relationshipType}` : ""}. It may not be called by any tracked function, or the index needs updating.`;
10389
+ }
10390
+ const formatted = callers.map((edge, index) => {
10391
+ const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
10392
+ 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]"}`;
10393
+ });
10394
+ return `"${name}" is called by ${callers.length} function(s):
10395
+
10396
+ ${formatted.join("\n")}`;
10397
+ }
10398
+ function formatCallGraphCallees(symbolId, callees, relationshipType) {
10399
+ if (callees.length === 0) {
10400
+ return `No callees found for symbol ${symbolId}${relationshipType ? ` with type ${relationshipType}` : ""}. The function may not call any other tracked functions.`;
10401
+ }
10402
+ return callees.map((edge, index) => {
10403
+ const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
10404
+ return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
10405
+ }).join("\n");
10406
+ }
10407
+ function formatCallGraphPath(from, to, path26) {
10408
+ if (path26.length === 0) {
10409
+ return `No path found between "${from}" and "${to}". They may be in disconnected components, or the call graph index needs updating.`;
10410
+ }
10411
+ const formatted = path26.map((hop, index) => {
10412
+ const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
10413
+ const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
10414
+ return `${prefix} ${hop.symbolName}${location}`;
10415
+ });
10416
+ return `Path (${path26.length} hops):
10417
+ ${formatted.join("\n")}`;
10418
+ }
10201
10419
  function formatResultHeader(result, index) {
10202
10420
  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}`;
10203
10421
  }
10422
+ function formatBlame(result) {
10423
+ if (!result.blame) {
10424
+ return "";
10425
+ }
10426
+ const date = new Date(result.blame.committedAt * 1e3).toISOString().slice(0, 10);
10427
+ return `
10428
+ ${result.blame.sha.slice(0, 7)} | ${result.blame.author} | ${date} | ${result.blame.summary}`;
10429
+ }
10204
10430
  function formatDefinitionLookup(results, query) {
10205
10431
  if (results.length === 0) {
10206
10432
  return `No definition found for "${query}". Try codebase_search for broader discovery, or verify the symbol name.`;
10207
10433
  }
10208
10434
  const formatted = results.map((r, idx) => {
10209
10435
  const header = formatResultHeader(r, idx);
10210
- return `${header} (score: ${r.score.toFixed(2)})
10436
+ return `${header} (score: ${r.score.toFixed(2)})${formatBlame(r)}
10437
+ \`\`\`
10438
+ ${truncateContent(r.content)}
10439
+ \`\`\``;
10440
+ });
10441
+ return formatted.join("\n\n");
10442
+ }
10443
+ function formatSearchResults(results, scoreFormat = "similarity") {
10444
+ const formatted = results.map((r, idx) => {
10445
+ const header = formatResultHeader(r, idx);
10446
+ const scoreLabel = scoreFormat === "similarity" ? `(similarity: ${(r.score * 100).toFixed(1)}%)` : `(score: ${r.score.toFixed(2)})`;
10447
+ return `${header} ${scoreLabel}${formatBlame(r)}
10211
10448
  \`\`\`
10212
10449
  ${truncateContent(r.content)}
10213
10450
  \`\`\``;
@@ -10268,11 +10505,11 @@ function formatPrImpact(result) {
10268
10505
 
10269
10506
  // src/tools/operations.ts
10270
10507
  import { existsSync as existsSync11, realpathSync, statSync as statSync3 } from "fs";
10271
- import * as path19 from "path";
10508
+ import * as path20 from "path";
10272
10509
 
10273
10510
  // src/config/merger.ts
10274
10511
  import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
10275
- import * as path16 from "path";
10512
+ import * as path17 from "path";
10276
10513
  var PROJECT_OVERRIDE_KEYS = [
10277
10514
  "embeddingProvider",
10278
10515
  "customProvider",
@@ -10305,8 +10542,8 @@ function mergeUniqueStringArray(values) {
10305
10542
  return [...new Set(values.map((value) => String(value).trim()))];
10306
10543
  }
10307
10544
  function normalizeKnowledgeBasePath(value) {
10308
- let normalized = path16.normalize(String(value).trim());
10309
- const root = path16.parse(normalized).root;
10545
+ let normalized = path17.normalize(String(value).trim());
10546
+ const root = path17.parse(normalized).root;
10310
10547
  while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
10311
10548
  normalized = normalized.slice(0, -1);
10312
10549
  }
@@ -10359,7 +10596,7 @@ function loadConfigFile(filePath) {
10359
10596
  }
10360
10597
  function materializeLocalProjectConfig(projectRoot, config, host = "opencode") {
10361
10598
  const localConfigPath = resolveWritableProjectConfigPath(projectRoot, host);
10362
- mkdirSync4(path16.dirname(localConfigPath), { recursive: true });
10599
+ mkdirSync4(path17.dirname(localConfigPath), { recursive: true });
10363
10600
  writeFileSync4(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
10364
10601
  return localConfigPath;
10365
10602
  }
@@ -10370,7 +10607,7 @@ function loadProjectConfigLayer(projectRoot, host = "opencode") {
10370
10607
  return {};
10371
10608
  }
10372
10609
  const normalizedConfig = { ...projectConfig };
10373
- const projectConfigBaseDir = path16.dirname(path16.dirname(projectConfigPath));
10610
+ const projectConfigBaseDir = path17.dirname(path17.dirname(projectConfigPath));
10374
10611
  if (Array.isArray(normalizedConfig.knowledgeBases)) {
10375
10612
  normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
10376
10613
  normalizedConfig.knowledgeBases,
@@ -10434,19 +10671,19 @@ function loadMergedConfig(projectRoot, host = "opencode") {
10434
10671
  }
10435
10672
 
10436
10673
  // src/tools/knowledge-base-paths.ts
10437
- import * as path17 from "path";
10674
+ import * as path18 from "path";
10438
10675
  function resolveConfigPathValue(value, baseDir) {
10439
10676
  const trimmed = value.trim();
10440
10677
  if (!trimmed) {
10441
10678
  return trimmed;
10442
10679
  }
10443
- const absolutePath = path17.isAbsolute(trimmed) ? trimmed : path17.resolve(baseDir, trimmed);
10444
- return path17.normalize(absolutePath);
10680
+ const absolutePath = path18.isAbsolute(trimmed) ? trimmed : path18.resolve(baseDir, trimmed);
10681
+ return path18.normalize(absolutePath);
10445
10682
  }
10446
10683
 
10447
10684
  // src/tools/config-state.ts
10448
10685
  import { existsSync as existsSync10, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
10449
- import * as path18 from "path";
10686
+ import * as path19 from "path";
10450
10687
  function normalizeKnowledgeBasePaths(config, projectRoot) {
10451
10688
  const normalized = { ...config };
10452
10689
  if (Array.isArray(normalized.knowledgeBases)) {
@@ -10514,7 +10751,7 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
10514
10751
  }
10515
10752
  function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
10516
10753
  const root = getProjectRoot(projectRoot, host);
10517
- const localIndexPath = path19.join(root, getHostProjectIndexRelativePath(host));
10754
+ const localIndexPath = path20.join(root, getHostProjectIndexRelativePath(host));
10518
10755
  if (existsSync11(localIndexPath)) {
10519
10756
  return false;
10520
10757
  }
@@ -10529,7 +10766,10 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
10529
10766
  chunkType: options.chunkType,
10530
10767
  contextLines: options.contextLines,
10531
10768
  metadataOnly: options.metadataOnly,
10532
- definitionIntent: options.definitionIntent
10769
+ definitionIntent: options.definitionIntent,
10770
+ blameAuthor: options.blameAuthor,
10771
+ blameSha: options.blameSha,
10772
+ blameSince: options.blameSince
10533
10773
  });
10534
10774
  }
10535
10775
  async function findSimilarCode(projectRoot, host, code, options = {}) {
@@ -10671,13 +10911,6 @@ async function getIndexLogs(projectRoot, host, args) {
10671
10911
  }
10672
10912
 
10673
10913
  // src/mcp-server/shared.ts
10674
- var MAX_CONTENT_LINES2 = 30;
10675
- function truncateContent2(content) {
10676
- const lines = content.split("\n");
10677
- if (lines.length <= MAX_CONTENT_LINES2) return content;
10678
- return lines.slice(0, MAX_CONTENT_LINES2).join("\n") + `
10679
- // ... (${lines.length - MAX_CONTENT_LINES2} more lines)`;
10680
- }
10681
10914
  var CHUNK_TYPE_ENUM = [
10682
10915
  "function",
10683
10916
  "class",
@@ -10703,7 +10936,10 @@ function registerMcpTools(server, runtime) {
10703
10936
  fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10704
10937
  directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10705
10938
  chunkType: z2.enum(CHUNK_TYPE_ENUM).optional().describe("Filter by code chunk type"),
10706
- contextLines: z2.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
10939
+ contextLines: z2.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
10940
+ blameAuthor: z2.string().optional().describe("Filter by git blame author name or email"),
10941
+ blameSha: z2.string().optional().describe("Filter by git blame commit SHA or prefix"),
10942
+ blameSince: z2.string().optional().describe("Filter to chunks last changed on or after this date")
10707
10943
  },
10708
10944
  async (args) => {
10709
10945
  const results = await searchCodebase(runtime.projectRoot, runtime.host, args.query, {
@@ -10711,21 +10947,17 @@ function registerMcpTools(server, runtime) {
10711
10947
  fileType: args.fileType,
10712
10948
  directory: args.directory,
10713
10949
  chunkType: args.chunkType,
10714
- contextLines: args.contextLines
10950
+ contextLines: args.contextLines,
10951
+ blameAuthor: args.blameAuthor,
10952
+ blameSha: args.blameSha,
10953
+ blameSince: args.blameSince
10715
10954
  });
10716
10955
  if (results.length === 0) {
10717
10956
  return { content: [{ type: "text", text: "No matching code found. Try a different query or run index_codebase first." }] };
10718
10957
  }
10719
- const formatted = results.map((r, idx) => {
10720
- 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}`;
10721
- return `${header} (score: ${r.score.toFixed(2)})
10722
- \`\`\`
10723
- ${truncateContent2(r.content)}
10724
- \`\`\``;
10725
- });
10726
10958
  return { content: [{ type: "text", text: `Found ${results.length} results for "${args.query}":
10727
10959
 
10728
- ${formatted.join("\n\n")}` }] };
10960
+ ${formatSearchResults(results, "score")}` }] };
10729
10961
  }
10730
10962
  );
10731
10963
  server.tool(
@@ -10736,7 +10968,10 @@ ${formatted.join("\n\n")}` }] };
10736
10968
  limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
10737
10969
  fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
10738
10970
  directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
10739
- chunkType: z2.enum(CHUNK_TYPE_ENUM).optional().describe("Filter by code chunk type")
10971
+ chunkType: z2.enum(CHUNK_TYPE_ENUM).optional().describe("Filter by code chunk type"),
10972
+ blameAuthor: z2.string().optional().describe("Filter by git blame author name or email"),
10973
+ blameSha: z2.string().optional().describe("Filter by git blame commit SHA or prefix"),
10974
+ blameSince: z2.string().optional().describe("Filter to chunks last changed on or after this date")
10740
10975
  },
10741
10976
  async (args) => {
10742
10977
  const results = await searchCodebase(runtime.projectRoot, runtime.host, args.query, {
@@ -10744,19 +10979,17 @@ ${formatted.join("\n\n")}` }] };
10744
10979
  fileType: args.fileType,
10745
10980
  directory: args.directory,
10746
10981
  chunkType: args.chunkType,
10747
- metadataOnly: true
10982
+ metadataOnly: true,
10983
+ blameAuthor: args.blameAuthor,
10984
+ blameSha: args.blameSha,
10985
+ blameSince: args.blameSince
10748
10986
  });
10749
10987
  if (results.length === 0) {
10750
10988
  return { content: [{ type: "text", text: "No matching code found. Try a different query or run index_codebase first." }] };
10751
10989
  }
10752
- const formatted = results.map((r, idx) => {
10753
- const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
10754
- const name = r.name ? `"${r.name}"` : "(anonymous)";
10755
- return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})`;
10756
- });
10757
10990
  return { content: [{ type: "text", text: `Found ${results.length} locations for "${args.query}":
10758
10991
 
10759
- ${formatted.join("\n")}
10992
+ ${formatCodebasePeek(results)}
10760
10993
 
10761
10994
  Use Read tool to examine specific files.` }] };
10762
10995
  }
@@ -10837,16 +11070,9 @@ Use Read tool to examine specific files.` }] };
10837
11070
  if (results.length === 0) {
10838
11071
  return { content: [{ type: "text", text: "No similar code found. Try a different snippet or run index_codebase first." }] };
10839
11072
  }
10840
- const formatted = results.map((r, idx) => {
10841
- 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}`;
10842
- return `${header} (similarity: ${(r.score * 100).toFixed(1)}%)
10843
- \`\`\`
10844
- ${truncateContent2(r.content)}
10845
- \`\`\``;
10846
- });
10847
11073
  return { content: [{ type: "text", text: `Found ${results.length} similar code blocks:
10848
11074
 
10849
- ${formatted.join("\n\n")}` }] };
11075
+ ${formatSearchResults(results)}` }] };
10850
11076
  }
10851
11077
  );
10852
11078
  server.tool(
@@ -10882,28 +11108,10 @@ ${formatted.join("\n\n")}` }] };
10882
11108
  return { content: [{ type: "text", text: "Error: 'symbolId' is required when direction is 'callees'." }] };
10883
11109
  }
10884
11110
  const { callees } = await getCallGraphData(runtime.projectRoot, runtime.host, args);
10885
- if (callees.length === 0) {
10886
- return { content: [{ type: "text", text: `No callees found for symbol ${args.symbolId}${args.relationshipType ? ` with type ${args.relationshipType}` : ""}.` }] };
10887
- }
10888
- const formatted2 = callees.map((e, i) => {
10889
- const conf = e.confidence !== "Direct" ? ` [${e.confidence.toLowerCase()}]` : "";
10890
- return `[${i + 1}] \u2192 ${e.targetName} (${e.callType})${conf} at line ${e.line}${e.isResolved ? ` [resolved: ${e.toSymbolId}]` : " [unresolved]"}`;
10891
- });
10892
- return { content: [{ type: "text", text: `Callees (${callees.length}):
10893
-
10894
- ${formatted2.join("\n")}` }] };
11111
+ return { content: [{ type: "text", text: formatCallGraphCallees(args.symbolId, callees, args.relationshipType) }] };
10895
11112
  }
10896
11113
  const { callers } = await getCallGraphData(runtime.projectRoot, runtime.host, args);
10897
- if (callers.length === 0) {
10898
- return { content: [{ type: "text", text: `No callers found for "${args.name}"${args.relationshipType ? ` with type ${args.relationshipType}` : ""}.` }] };
10899
- }
10900
- const formatted = callers.map((e, i) => {
10901
- const conf = e.confidence !== "Direct" ? ` [${e.confidence.toLowerCase()}]` : "";
10902
- 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]"}`;
10903
- });
10904
- return { content: [{ type: "text", text: `"${args.name}" is called by ${callers.length} function(s):
10905
-
10906
- ${formatted.join("\n")}` }] };
11114
+ return { content: [{ type: "text", text: formatCallGraphCallers(args.name, callers, args.relationshipType) }] };
10907
11115
  }
10908
11116
  );
10909
11117
  server.tool(
@@ -10915,17 +11123,8 @@ ${formatted.join("\n")}` }] };
10915
11123
  maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
10916
11124
  },
10917
11125
  async (args) => {
10918
- const path25 = await getCallGraphPath(runtime.projectRoot, runtime.host, args.from, args.to, args.maxDepth);
10919
- if (path25.length === 0) {
10920
- 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.` }] };
10921
- }
10922
- const formatted = path25.map((hop, i) => {
10923
- const prefix = i === 0 ? "[start]" : `--${hop.callType}-->`;
10924
- const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
10925
- return `${prefix} ${hop.symbolName}${location}`;
10926
- });
10927
- return { content: [{ type: "text", text: `Path (${path25.length} hops):
10928
- ${formatted.join("\n")}` }] };
11126
+ const path26 = await getCallGraphPath(runtime.projectRoot, runtime.host, args.from, args.to, args.maxDepth);
11127
+ return { content: [{ type: "text", text: formatCallGraphPath(args.from, args.to, path26) }] };
10929
11128
  }
10930
11129
  );
10931
11130
  server.tool(
@@ -11084,7 +11283,7 @@ var ReaddirpStream = class extends Readable {
11084
11283
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
11085
11284
  const statMethod = opts.lstat ? lstat : stat;
11086
11285
  if (wantBigintFsStats) {
11087
- this._stat = (path25) => statMethod(path25, { bigint: true });
11286
+ this._stat = (path26) => statMethod(path26, { bigint: true });
11088
11287
  } else {
11089
11288
  this._stat = statMethod;
11090
11289
  }
@@ -11109,8 +11308,8 @@ var ReaddirpStream = class extends Readable {
11109
11308
  const par = this.parent;
11110
11309
  const fil = par && par.files;
11111
11310
  if (fil && fil.length > 0) {
11112
- const { path: path25, depth } = par;
11113
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path25));
11311
+ const { path: path26, depth } = par;
11312
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path26));
11114
11313
  const awaited = await Promise.all(slice);
11115
11314
  for (const entry of awaited) {
11116
11315
  if (!entry)
@@ -11150,20 +11349,20 @@ var ReaddirpStream = class extends Readable {
11150
11349
  this.reading = false;
11151
11350
  }
11152
11351
  }
11153
- async _exploreDir(path25, depth) {
11352
+ async _exploreDir(path26, depth) {
11154
11353
  let files;
11155
11354
  try {
11156
- files = await readdir(path25, this._rdOptions);
11355
+ files = await readdir(path26, this._rdOptions);
11157
11356
  } catch (error) {
11158
11357
  this._onError(error);
11159
11358
  }
11160
- return { files, depth, path: path25 };
11359
+ return { files, depth, path: path26 };
11161
11360
  }
11162
- async _formatEntry(dirent, path25) {
11361
+ async _formatEntry(dirent, path26) {
11163
11362
  let entry;
11164
11363
  const basename5 = this._isDirent ? dirent.name : dirent;
11165
11364
  try {
11166
- const fullPath = presolve(pjoin(path25, basename5));
11365
+ const fullPath = presolve(pjoin(path26, basename5));
11167
11366
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
11168
11367
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
11169
11368
  } catch (err) {
@@ -11563,16 +11762,16 @@ var delFromSet = (main2, prop, item) => {
11563
11762
  };
11564
11763
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
11565
11764
  var FsWatchInstances = /* @__PURE__ */ new Map();
11566
- function createFsWatchInstance(path25, options, listener, errHandler, emitRaw) {
11765
+ function createFsWatchInstance(path26, options, listener, errHandler, emitRaw) {
11567
11766
  const handleEvent = (rawEvent, evPath) => {
11568
- listener(path25);
11569
- emitRaw(rawEvent, evPath, { watchedPath: path25 });
11570
- if (evPath && path25 !== evPath) {
11571
- fsWatchBroadcast(sp.resolve(path25, evPath), KEY_LISTENERS, sp.join(path25, evPath));
11767
+ listener(path26);
11768
+ emitRaw(rawEvent, evPath, { watchedPath: path26 });
11769
+ if (evPath && path26 !== evPath) {
11770
+ fsWatchBroadcast(sp.resolve(path26, evPath), KEY_LISTENERS, sp.join(path26, evPath));
11572
11771
  }
11573
11772
  };
11574
11773
  try {
11575
- return fs_watch(path25, {
11774
+ return fs_watch(path26, {
11576
11775
  persistent: options.persistent
11577
11776
  }, handleEvent);
11578
11777
  } catch (error) {
@@ -11588,12 +11787,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
11588
11787
  listener(val1, val2, val3);
11589
11788
  });
11590
11789
  };
11591
- var setFsWatchListener = (path25, fullPath, options, handlers) => {
11790
+ var setFsWatchListener = (path26, fullPath, options, handlers) => {
11592
11791
  const { listener, errHandler, rawEmitter } = handlers;
11593
11792
  let cont = FsWatchInstances.get(fullPath);
11594
11793
  let watcher;
11595
11794
  if (!options.persistent) {
11596
- watcher = createFsWatchInstance(path25, options, listener, errHandler, rawEmitter);
11795
+ watcher = createFsWatchInstance(path26, options, listener, errHandler, rawEmitter);
11597
11796
  if (!watcher)
11598
11797
  return;
11599
11798
  return watcher.close.bind(watcher);
@@ -11604,7 +11803,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
11604
11803
  addAndConvert(cont, KEY_RAW, rawEmitter);
11605
11804
  } else {
11606
11805
  watcher = createFsWatchInstance(
11607
- path25,
11806
+ path26,
11608
11807
  options,
11609
11808
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
11610
11809
  errHandler,
@@ -11619,7 +11818,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
11619
11818
  cont.watcherUnusable = true;
11620
11819
  if (isWindows && error.code === "EPERM") {
11621
11820
  try {
11622
- const fd = await open(path25, "r");
11821
+ const fd = await open(path26, "r");
11623
11822
  await fd.close();
11624
11823
  broadcastErr(error);
11625
11824
  } catch (err) {
@@ -11650,7 +11849,7 @@ var setFsWatchListener = (path25, fullPath, options, handlers) => {
11650
11849
  };
11651
11850
  };
11652
11851
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
11653
- var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
11852
+ var setFsWatchFileListener = (path26, fullPath, options, handlers) => {
11654
11853
  const { listener, rawEmitter } = handlers;
11655
11854
  let cont = FsWatchFileInstances.get(fullPath);
11656
11855
  const copts = cont && cont.options;
@@ -11672,7 +11871,7 @@ var setFsWatchFileListener = (path25, fullPath, options, handlers) => {
11672
11871
  });
11673
11872
  const currmtime = curr.mtimeMs;
11674
11873
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
11675
- foreach(cont.listeners, (listener2) => listener2(path25, curr));
11874
+ foreach(cont.listeners, (listener2) => listener2(path26, curr));
11676
11875
  }
11677
11876
  })
11678
11877
  };
@@ -11702,13 +11901,13 @@ var NodeFsHandler = class {
11702
11901
  * @param listener on fs change
11703
11902
  * @returns closer for the watcher instance
11704
11903
  */
11705
- _watchWithNodeFs(path25, listener) {
11904
+ _watchWithNodeFs(path26, listener) {
11706
11905
  const opts = this.fsw.options;
11707
- const directory = sp.dirname(path25);
11708
- const basename5 = sp.basename(path25);
11906
+ const directory = sp.dirname(path26);
11907
+ const basename5 = sp.basename(path26);
11709
11908
  const parent = this.fsw._getWatchedDir(directory);
11710
11909
  parent.add(basename5);
11711
- const absolutePath = sp.resolve(path25);
11910
+ const absolutePath = sp.resolve(path26);
11712
11911
  const options = {
11713
11912
  persistent: opts.persistent
11714
11913
  };
@@ -11718,12 +11917,12 @@ var NodeFsHandler = class {
11718
11917
  if (opts.usePolling) {
11719
11918
  const enableBin = opts.interval !== opts.binaryInterval;
11720
11919
  options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
11721
- closer = setFsWatchFileListener(path25, absolutePath, options, {
11920
+ closer = setFsWatchFileListener(path26, absolutePath, options, {
11722
11921
  listener,
11723
11922
  rawEmitter: this.fsw._emitRaw
11724
11923
  });
11725
11924
  } else {
11726
- closer = setFsWatchListener(path25, absolutePath, options, {
11925
+ closer = setFsWatchListener(path26, absolutePath, options, {
11727
11926
  listener,
11728
11927
  errHandler: this._boundHandleError,
11729
11928
  rawEmitter: this.fsw._emitRaw
@@ -11745,7 +11944,7 @@ var NodeFsHandler = class {
11745
11944
  let prevStats = stats;
11746
11945
  if (parent.has(basename5))
11747
11946
  return;
11748
- const listener = async (path25, newStats) => {
11947
+ const listener = async (path26, newStats) => {
11749
11948
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
11750
11949
  return;
11751
11950
  if (!newStats || newStats.mtimeMs === 0) {
@@ -11759,11 +11958,11 @@ var NodeFsHandler = class {
11759
11958
  this.fsw._emit(EV.CHANGE, file, newStats2);
11760
11959
  }
11761
11960
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
11762
- this.fsw._closeFile(path25);
11961
+ this.fsw._closeFile(path26);
11763
11962
  prevStats = newStats2;
11764
11963
  const closer2 = this._watchWithNodeFs(file, listener);
11765
11964
  if (closer2)
11766
- this.fsw._addPathCloser(path25, closer2);
11965
+ this.fsw._addPathCloser(path26, closer2);
11767
11966
  } else {
11768
11967
  prevStats = newStats2;
11769
11968
  }
@@ -11795,7 +11994,7 @@ var NodeFsHandler = class {
11795
11994
  * @param item basename of this item
11796
11995
  * @returns true if no more processing is needed for this entry.
11797
11996
  */
11798
- async _handleSymlink(entry, directory, path25, item) {
11997
+ async _handleSymlink(entry, directory, path26, item) {
11799
11998
  if (this.fsw.closed) {
11800
11999
  return;
11801
12000
  }
@@ -11805,7 +12004,7 @@ var NodeFsHandler = class {
11805
12004
  this.fsw._incrReadyCount();
11806
12005
  let linkPath;
11807
12006
  try {
11808
- linkPath = await fsrealpath(path25);
12007
+ linkPath = await fsrealpath(path26);
11809
12008
  } catch (e) {
11810
12009
  this.fsw._emitReady();
11811
12010
  return true;
@@ -11815,12 +12014,12 @@ var NodeFsHandler = class {
11815
12014
  if (dir.has(item)) {
11816
12015
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
11817
12016
  this.fsw._symlinkPaths.set(full, linkPath);
11818
- this.fsw._emit(EV.CHANGE, path25, entry.stats);
12017
+ this.fsw._emit(EV.CHANGE, path26, entry.stats);
11819
12018
  }
11820
12019
  } else {
11821
12020
  dir.add(item);
11822
12021
  this.fsw._symlinkPaths.set(full, linkPath);
11823
- this.fsw._emit(EV.ADD, path25, entry.stats);
12022
+ this.fsw._emit(EV.ADD, path26, entry.stats);
11824
12023
  }
11825
12024
  this.fsw._emitReady();
11826
12025
  return true;
@@ -11850,9 +12049,9 @@ var NodeFsHandler = class {
11850
12049
  return;
11851
12050
  }
11852
12051
  const item = entry.path;
11853
- let path25 = sp.join(directory, item);
12052
+ let path26 = sp.join(directory, item);
11854
12053
  current.add(item);
11855
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path25, item)) {
12054
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path26, item)) {
11856
12055
  return;
11857
12056
  }
11858
12057
  if (this.fsw.closed) {
@@ -11861,8 +12060,8 @@ var NodeFsHandler = class {
11861
12060
  }
11862
12061
  if (item === target || !target && !previous.has(item)) {
11863
12062
  this.fsw._incrReadyCount();
11864
- path25 = sp.join(dir, sp.relative(dir, path25));
11865
- this._addToNodeFs(path25, initialAdd, wh, depth + 1);
12063
+ path26 = sp.join(dir, sp.relative(dir, path26));
12064
+ this._addToNodeFs(path26, initialAdd, wh, depth + 1);
11866
12065
  }
11867
12066
  }).on(EV.ERROR, this._boundHandleError);
11868
12067
  return new Promise((resolve15, reject) => {
@@ -11931,13 +12130,13 @@ var NodeFsHandler = class {
11931
12130
  * @param depth Child path actually targeted for watch
11932
12131
  * @param target Child path actually targeted for watch
11933
12132
  */
11934
- async _addToNodeFs(path25, initialAdd, priorWh, depth, target) {
12133
+ async _addToNodeFs(path26, initialAdd, priorWh, depth, target) {
11935
12134
  const ready = this.fsw._emitReady;
11936
- if (this.fsw._isIgnored(path25) || this.fsw.closed) {
12135
+ if (this.fsw._isIgnored(path26) || this.fsw.closed) {
11937
12136
  ready();
11938
12137
  return false;
11939
12138
  }
11940
- const wh = this.fsw._getWatchHelpers(path25);
12139
+ const wh = this.fsw._getWatchHelpers(path26);
11941
12140
  if (priorWh) {
11942
12141
  wh.filterPath = (entry) => priorWh.filterPath(entry);
11943
12142
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -11953,8 +12152,8 @@ var NodeFsHandler = class {
11953
12152
  const follow = this.fsw.options.followSymlinks;
11954
12153
  let closer;
11955
12154
  if (stats.isDirectory()) {
11956
- const absPath = sp.resolve(path25);
11957
- const targetPath = follow ? await fsrealpath(path25) : path25;
12155
+ const absPath = sp.resolve(path26);
12156
+ const targetPath = follow ? await fsrealpath(path26) : path26;
11958
12157
  if (this.fsw.closed)
11959
12158
  return;
11960
12159
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -11964,29 +12163,29 @@ var NodeFsHandler = class {
11964
12163
  this.fsw._symlinkPaths.set(absPath, targetPath);
11965
12164
  }
11966
12165
  } else if (stats.isSymbolicLink()) {
11967
- const targetPath = follow ? await fsrealpath(path25) : path25;
12166
+ const targetPath = follow ? await fsrealpath(path26) : path26;
11968
12167
  if (this.fsw.closed)
11969
12168
  return;
11970
12169
  const parent = sp.dirname(wh.watchPath);
11971
12170
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
11972
12171
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
11973
- closer = await this._handleDir(parent, stats, initialAdd, depth, path25, wh, targetPath);
12172
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path26, wh, targetPath);
11974
12173
  if (this.fsw.closed)
11975
12174
  return;
11976
12175
  if (targetPath !== void 0) {
11977
- this.fsw._symlinkPaths.set(sp.resolve(path25), targetPath);
12176
+ this.fsw._symlinkPaths.set(sp.resolve(path26), targetPath);
11978
12177
  }
11979
12178
  } else {
11980
12179
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
11981
12180
  }
11982
12181
  ready();
11983
12182
  if (closer)
11984
- this.fsw._addPathCloser(path25, closer);
12183
+ this.fsw._addPathCloser(path26, closer);
11985
12184
  return false;
11986
12185
  } catch (error) {
11987
12186
  if (this.fsw._handleError(error)) {
11988
12187
  ready();
11989
- return path25;
12188
+ return path26;
11990
12189
  }
11991
12190
  }
11992
12191
  }
@@ -12018,35 +12217,35 @@ function createPattern(matcher) {
12018
12217
  if (matcher.path === string)
12019
12218
  return true;
12020
12219
  if (matcher.recursive) {
12021
- const relative10 = sp2.relative(matcher.path, string);
12022
- if (!relative10) {
12220
+ const relative11 = sp2.relative(matcher.path, string);
12221
+ if (!relative11) {
12023
12222
  return false;
12024
12223
  }
12025
- return !relative10.startsWith("..") && !sp2.isAbsolute(relative10);
12224
+ return !relative11.startsWith("..") && !sp2.isAbsolute(relative11);
12026
12225
  }
12027
12226
  return false;
12028
12227
  };
12029
12228
  }
12030
12229
  return () => false;
12031
12230
  }
12032
- function normalizePath2(path25) {
12033
- if (typeof path25 !== "string")
12231
+ function normalizePath2(path26) {
12232
+ if (typeof path26 !== "string")
12034
12233
  throw new Error("string expected");
12035
- path25 = sp2.normalize(path25);
12036
- path25 = path25.replace(/\\/g, "/");
12234
+ path26 = sp2.normalize(path26);
12235
+ path26 = path26.replace(/\\/g, "/");
12037
12236
  let prepend = false;
12038
- if (path25.startsWith("//"))
12237
+ if (path26.startsWith("//"))
12039
12238
  prepend = true;
12040
- path25 = path25.replace(DOUBLE_SLASH_RE, "/");
12239
+ path26 = path26.replace(DOUBLE_SLASH_RE, "/");
12041
12240
  if (prepend)
12042
- path25 = "/" + path25;
12043
- return path25;
12241
+ path26 = "/" + path26;
12242
+ return path26;
12044
12243
  }
12045
12244
  function matchPatterns(patterns, testString, stats) {
12046
- const path25 = normalizePath2(testString);
12245
+ const path26 = normalizePath2(testString);
12047
12246
  for (let index = 0; index < patterns.length; index++) {
12048
12247
  const pattern = patterns[index];
12049
- if (pattern(path25, stats)) {
12248
+ if (pattern(path26, stats)) {
12050
12249
  return true;
12051
12250
  }
12052
12251
  }
@@ -12084,19 +12283,19 @@ var toUnix = (string) => {
12084
12283
  }
12085
12284
  return str;
12086
12285
  };
12087
- var normalizePathToUnix = (path25) => toUnix(sp2.normalize(toUnix(path25)));
12088
- var normalizeIgnored = (cwd = "") => (path25) => {
12089
- if (typeof path25 === "string") {
12090
- return normalizePathToUnix(sp2.isAbsolute(path25) ? path25 : sp2.join(cwd, path25));
12286
+ var normalizePathToUnix = (path26) => toUnix(sp2.normalize(toUnix(path26)));
12287
+ var normalizeIgnored = (cwd = "") => (path26) => {
12288
+ if (typeof path26 === "string") {
12289
+ return normalizePathToUnix(sp2.isAbsolute(path26) ? path26 : sp2.join(cwd, path26));
12091
12290
  } else {
12092
- return path25;
12291
+ return path26;
12093
12292
  }
12094
12293
  };
12095
- var getAbsolutePath = (path25, cwd) => {
12096
- if (sp2.isAbsolute(path25)) {
12097
- return path25;
12294
+ var getAbsolutePath = (path26, cwd) => {
12295
+ if (sp2.isAbsolute(path26)) {
12296
+ return path26;
12098
12297
  }
12099
- return sp2.join(cwd, path25);
12298
+ return sp2.join(cwd, path26);
12100
12299
  };
12101
12300
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
12102
12301
  var DirEntry = class {
@@ -12161,10 +12360,10 @@ var WatchHelper = class {
12161
12360
  dirParts;
12162
12361
  followSymlinks;
12163
12362
  statMethod;
12164
- constructor(path25, follow, fsw) {
12363
+ constructor(path26, follow, fsw) {
12165
12364
  this.fsw = fsw;
12166
- const watchPath = path25;
12167
- this.path = path25 = path25.replace(REPLACER_RE, "");
12365
+ const watchPath = path26;
12366
+ this.path = path26 = path26.replace(REPLACER_RE, "");
12168
12367
  this.watchPath = watchPath;
12169
12368
  this.fullWatchPath = sp2.resolve(watchPath);
12170
12369
  this.dirParts = [];
@@ -12304,20 +12503,20 @@ var FSWatcher = class extends EventEmitter2 {
12304
12503
  this._closePromise = void 0;
12305
12504
  let paths = unifyPaths(paths_);
12306
12505
  if (cwd) {
12307
- paths = paths.map((path25) => {
12308
- const absPath = getAbsolutePath(path25, cwd);
12506
+ paths = paths.map((path26) => {
12507
+ const absPath = getAbsolutePath(path26, cwd);
12309
12508
  return absPath;
12310
12509
  });
12311
12510
  }
12312
- paths.forEach((path25) => {
12313
- this._removeIgnoredPath(path25);
12511
+ paths.forEach((path26) => {
12512
+ this._removeIgnoredPath(path26);
12314
12513
  });
12315
12514
  this._userIgnored = void 0;
12316
12515
  if (!this._readyCount)
12317
12516
  this._readyCount = 0;
12318
12517
  this._readyCount += paths.length;
12319
- Promise.all(paths.map(async (path25) => {
12320
- const res = await this._nodeFsHandler._addToNodeFs(path25, !_internal, void 0, 0, _origAdd);
12518
+ Promise.all(paths.map(async (path26) => {
12519
+ const res = await this._nodeFsHandler._addToNodeFs(path26, !_internal, void 0, 0, _origAdd);
12321
12520
  if (res)
12322
12521
  this._emitReady();
12323
12522
  return res;
@@ -12339,17 +12538,17 @@ var FSWatcher = class extends EventEmitter2 {
12339
12538
  return this;
12340
12539
  const paths = unifyPaths(paths_);
12341
12540
  const { cwd } = this.options;
12342
- paths.forEach((path25) => {
12343
- if (!sp2.isAbsolute(path25) && !this._closers.has(path25)) {
12541
+ paths.forEach((path26) => {
12542
+ if (!sp2.isAbsolute(path26) && !this._closers.has(path26)) {
12344
12543
  if (cwd)
12345
- path25 = sp2.join(cwd, path25);
12346
- path25 = sp2.resolve(path25);
12544
+ path26 = sp2.join(cwd, path26);
12545
+ path26 = sp2.resolve(path26);
12347
12546
  }
12348
- this._closePath(path25);
12349
- this._addIgnoredPath(path25);
12350
- if (this._watched.has(path25)) {
12547
+ this._closePath(path26);
12548
+ this._addIgnoredPath(path26);
12549
+ if (this._watched.has(path26)) {
12351
12550
  this._addIgnoredPath({
12352
- path: path25,
12551
+ path: path26,
12353
12552
  recursive: true
12354
12553
  });
12355
12554
  }
@@ -12413,38 +12612,38 @@ var FSWatcher = class extends EventEmitter2 {
12413
12612
  * @param stats arguments to be passed with event
12414
12613
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
12415
12614
  */
12416
- async _emit(event, path25, stats) {
12615
+ async _emit(event, path26, stats) {
12417
12616
  if (this.closed)
12418
12617
  return;
12419
12618
  const opts = this.options;
12420
12619
  if (isWindows)
12421
- path25 = sp2.normalize(path25);
12620
+ path26 = sp2.normalize(path26);
12422
12621
  if (opts.cwd)
12423
- path25 = sp2.relative(opts.cwd, path25);
12424
- const args = [path25];
12622
+ path26 = sp2.relative(opts.cwd, path26);
12623
+ const args = [path26];
12425
12624
  if (stats != null)
12426
12625
  args.push(stats);
12427
12626
  const awf = opts.awaitWriteFinish;
12428
12627
  let pw;
12429
- if (awf && (pw = this._pendingWrites.get(path25))) {
12628
+ if (awf && (pw = this._pendingWrites.get(path26))) {
12430
12629
  pw.lastChange = /* @__PURE__ */ new Date();
12431
12630
  return this;
12432
12631
  }
12433
12632
  if (opts.atomic) {
12434
12633
  if (event === EVENTS.UNLINK) {
12435
- this._pendingUnlinks.set(path25, [event, ...args]);
12634
+ this._pendingUnlinks.set(path26, [event, ...args]);
12436
12635
  setTimeout(() => {
12437
- this._pendingUnlinks.forEach((entry, path26) => {
12636
+ this._pendingUnlinks.forEach((entry, path27) => {
12438
12637
  this.emit(...entry);
12439
12638
  this.emit(EVENTS.ALL, ...entry);
12440
- this._pendingUnlinks.delete(path26);
12639
+ this._pendingUnlinks.delete(path27);
12441
12640
  });
12442
12641
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
12443
12642
  return this;
12444
12643
  }
12445
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path25)) {
12644
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path26)) {
12446
12645
  event = EVENTS.CHANGE;
12447
- this._pendingUnlinks.delete(path25);
12646
+ this._pendingUnlinks.delete(path26);
12448
12647
  }
12449
12648
  }
12450
12649
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -12462,16 +12661,16 @@ var FSWatcher = class extends EventEmitter2 {
12462
12661
  this.emitWithAll(event, args);
12463
12662
  }
12464
12663
  };
12465
- this._awaitWriteFinish(path25, awf.stabilityThreshold, event, awfEmit);
12664
+ this._awaitWriteFinish(path26, awf.stabilityThreshold, event, awfEmit);
12466
12665
  return this;
12467
12666
  }
12468
12667
  if (event === EVENTS.CHANGE) {
12469
- const isThrottled = !this._throttle(EVENTS.CHANGE, path25, 50);
12668
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path26, 50);
12470
12669
  if (isThrottled)
12471
12670
  return this;
12472
12671
  }
12473
12672
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
12474
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path25) : path25;
12673
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path26) : path26;
12475
12674
  let stats2;
12476
12675
  try {
12477
12676
  stats2 = await stat3(fullPath);
@@ -12502,23 +12701,23 @@ var FSWatcher = class extends EventEmitter2 {
12502
12701
  * @param timeout duration of time to suppress duplicate actions
12503
12702
  * @returns tracking object or false if action should be suppressed
12504
12703
  */
12505
- _throttle(actionType, path25, timeout) {
12704
+ _throttle(actionType, path26, timeout) {
12506
12705
  if (!this._throttled.has(actionType)) {
12507
12706
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
12508
12707
  }
12509
12708
  const action = this._throttled.get(actionType);
12510
12709
  if (!action)
12511
12710
  throw new Error("invalid throttle");
12512
- const actionPath = action.get(path25);
12711
+ const actionPath = action.get(path26);
12513
12712
  if (actionPath) {
12514
12713
  actionPath.count++;
12515
12714
  return false;
12516
12715
  }
12517
12716
  let timeoutObject;
12518
12717
  const clear = () => {
12519
- const item = action.get(path25);
12718
+ const item = action.get(path26);
12520
12719
  const count = item ? item.count : 0;
12521
- action.delete(path25);
12720
+ action.delete(path26);
12522
12721
  clearTimeout(timeoutObject);
12523
12722
  if (item)
12524
12723
  clearTimeout(item.timeoutObject);
@@ -12526,7 +12725,7 @@ var FSWatcher = class extends EventEmitter2 {
12526
12725
  };
12527
12726
  timeoutObject = setTimeout(clear, timeout);
12528
12727
  const thr = { timeoutObject, clear, count: 0 };
12529
- action.set(path25, thr);
12728
+ action.set(path26, thr);
12530
12729
  return thr;
12531
12730
  }
12532
12731
  _incrReadyCount() {
@@ -12540,44 +12739,44 @@ var FSWatcher = class extends EventEmitter2 {
12540
12739
  * @param event
12541
12740
  * @param awfEmit Callback to be called when ready for event to be emitted.
12542
12741
  */
12543
- _awaitWriteFinish(path25, threshold, event, awfEmit) {
12742
+ _awaitWriteFinish(path26, threshold, event, awfEmit) {
12544
12743
  const awf = this.options.awaitWriteFinish;
12545
12744
  if (typeof awf !== "object")
12546
12745
  return;
12547
12746
  const pollInterval = awf.pollInterval;
12548
12747
  let timeoutHandler;
12549
- let fullPath = path25;
12550
- if (this.options.cwd && !sp2.isAbsolute(path25)) {
12551
- fullPath = sp2.join(this.options.cwd, path25);
12748
+ let fullPath = path26;
12749
+ if (this.options.cwd && !sp2.isAbsolute(path26)) {
12750
+ fullPath = sp2.join(this.options.cwd, path26);
12552
12751
  }
12553
12752
  const now = /* @__PURE__ */ new Date();
12554
12753
  const writes = this._pendingWrites;
12555
12754
  function awaitWriteFinishFn(prevStat) {
12556
12755
  statcb(fullPath, (err, curStat) => {
12557
- if (err || !writes.has(path25)) {
12756
+ if (err || !writes.has(path26)) {
12558
12757
  if (err && err.code !== "ENOENT")
12559
12758
  awfEmit(err);
12560
12759
  return;
12561
12760
  }
12562
12761
  const now2 = Number(/* @__PURE__ */ new Date());
12563
12762
  if (prevStat && curStat.size !== prevStat.size) {
12564
- writes.get(path25).lastChange = now2;
12763
+ writes.get(path26).lastChange = now2;
12565
12764
  }
12566
- const pw = writes.get(path25);
12765
+ const pw = writes.get(path26);
12567
12766
  const df = now2 - pw.lastChange;
12568
12767
  if (df >= threshold) {
12569
- writes.delete(path25);
12768
+ writes.delete(path26);
12570
12769
  awfEmit(void 0, curStat);
12571
12770
  } else {
12572
12771
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
12573
12772
  }
12574
12773
  });
12575
12774
  }
12576
- if (!writes.has(path25)) {
12577
- writes.set(path25, {
12775
+ if (!writes.has(path26)) {
12776
+ writes.set(path26, {
12578
12777
  lastChange: now,
12579
12778
  cancelWait: () => {
12580
- writes.delete(path25);
12779
+ writes.delete(path26);
12581
12780
  clearTimeout(timeoutHandler);
12582
12781
  return event;
12583
12782
  }
@@ -12588,8 +12787,8 @@ var FSWatcher = class extends EventEmitter2 {
12588
12787
  /**
12589
12788
  * Determines whether user has asked to ignore this path.
12590
12789
  */
12591
- _isIgnored(path25, stats) {
12592
- if (this.options.atomic && DOT_RE.test(path25))
12790
+ _isIgnored(path26, stats) {
12791
+ if (this.options.atomic && DOT_RE.test(path26))
12593
12792
  return true;
12594
12793
  if (!this._userIgnored) {
12595
12794
  const { cwd } = this.options;
@@ -12599,17 +12798,17 @@ var FSWatcher = class extends EventEmitter2 {
12599
12798
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
12600
12799
  this._userIgnored = anymatch(list, void 0);
12601
12800
  }
12602
- return this._userIgnored(path25, stats);
12801
+ return this._userIgnored(path26, stats);
12603
12802
  }
12604
- _isntIgnored(path25, stat4) {
12605
- return !this._isIgnored(path25, stat4);
12803
+ _isntIgnored(path26, stat4) {
12804
+ return !this._isIgnored(path26, stat4);
12606
12805
  }
12607
12806
  /**
12608
12807
  * Provides a set of common helpers and properties relating to symlink handling.
12609
12808
  * @param path file or directory pattern being watched
12610
12809
  */
12611
- _getWatchHelpers(path25) {
12612
- return new WatchHelper(path25, this.options.followSymlinks, this);
12810
+ _getWatchHelpers(path26) {
12811
+ return new WatchHelper(path26, this.options.followSymlinks, this);
12613
12812
  }
12614
12813
  // Directory helpers
12615
12814
  // -----------------
@@ -12641,63 +12840,63 @@ var FSWatcher = class extends EventEmitter2 {
12641
12840
  * @param item base path of item/directory
12642
12841
  */
12643
12842
  _remove(directory, item, isDirectory) {
12644
- const path25 = sp2.join(directory, item);
12645
- const fullPath = sp2.resolve(path25);
12646
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path25) || this._watched.has(fullPath);
12647
- if (!this._throttle("remove", path25, 100))
12843
+ const path26 = sp2.join(directory, item);
12844
+ const fullPath = sp2.resolve(path26);
12845
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path26) || this._watched.has(fullPath);
12846
+ if (!this._throttle("remove", path26, 100))
12648
12847
  return;
12649
12848
  if (!isDirectory && this._watched.size === 1) {
12650
12849
  this.add(directory, item, true);
12651
12850
  }
12652
- const wp = this._getWatchedDir(path25);
12851
+ const wp = this._getWatchedDir(path26);
12653
12852
  const nestedDirectoryChildren = wp.getChildren();
12654
- nestedDirectoryChildren.forEach((nested) => this._remove(path25, nested));
12853
+ nestedDirectoryChildren.forEach((nested) => this._remove(path26, nested));
12655
12854
  const parent = this._getWatchedDir(directory);
12656
12855
  const wasTracked = parent.has(item);
12657
12856
  parent.remove(item);
12658
12857
  if (this._symlinkPaths.has(fullPath)) {
12659
12858
  this._symlinkPaths.delete(fullPath);
12660
12859
  }
12661
- let relPath = path25;
12860
+ let relPath = path26;
12662
12861
  if (this.options.cwd)
12663
- relPath = sp2.relative(this.options.cwd, path25);
12862
+ relPath = sp2.relative(this.options.cwd, path26);
12664
12863
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
12665
12864
  const event = this._pendingWrites.get(relPath).cancelWait();
12666
12865
  if (event === EVENTS.ADD)
12667
12866
  return;
12668
12867
  }
12669
- this._watched.delete(path25);
12868
+ this._watched.delete(path26);
12670
12869
  this._watched.delete(fullPath);
12671
12870
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
12672
- if (wasTracked && !this._isIgnored(path25))
12673
- this._emit(eventName, path25);
12674
- this._closePath(path25);
12871
+ if (wasTracked && !this._isIgnored(path26))
12872
+ this._emit(eventName, path26);
12873
+ this._closePath(path26);
12675
12874
  }
12676
12875
  /**
12677
12876
  * Closes all watchers for a path
12678
12877
  */
12679
- _closePath(path25) {
12680
- this._closeFile(path25);
12681
- const dir = sp2.dirname(path25);
12682
- this._getWatchedDir(dir).remove(sp2.basename(path25));
12878
+ _closePath(path26) {
12879
+ this._closeFile(path26);
12880
+ const dir = sp2.dirname(path26);
12881
+ this._getWatchedDir(dir).remove(sp2.basename(path26));
12683
12882
  }
12684
12883
  /**
12685
12884
  * Closes only file-specific watchers
12686
12885
  */
12687
- _closeFile(path25) {
12688
- const closers = this._closers.get(path25);
12886
+ _closeFile(path26) {
12887
+ const closers = this._closers.get(path26);
12689
12888
  if (!closers)
12690
12889
  return;
12691
12890
  closers.forEach((closer) => closer());
12692
- this._closers.delete(path25);
12891
+ this._closers.delete(path26);
12693
12892
  }
12694
- _addPathCloser(path25, closer) {
12893
+ _addPathCloser(path26, closer) {
12695
12894
  if (!closer)
12696
12895
  return;
12697
- let list = this._closers.get(path25);
12896
+ let list = this._closers.get(path26);
12698
12897
  if (!list) {
12699
12898
  list = [];
12700
- this._closers.set(path25, list);
12899
+ this._closers.set(path26, list);
12701
12900
  }
12702
12901
  list.push(closer);
12703
12902
  }
@@ -12727,7 +12926,7 @@ function watch(paths, options = {}) {
12727
12926
  var chokidar_default = { watch, FSWatcher };
12728
12927
 
12729
12928
  // src/watcher/file-watcher.ts
12730
- import * as path20 from "path";
12929
+ import * as path21 from "path";
12731
12930
  var FileWatcher = class {
12732
12931
  watcher = null;
12733
12932
  projectRoot;
@@ -12753,15 +12952,15 @@ var FileWatcher = class {
12753
12952
  const watchTargets = this.configPath ? [this.projectRoot, this.configPath] : this.projectRoot;
12754
12953
  this.watcher = chokidar_default.watch(watchTargets, {
12755
12954
  ignored: (filePath) => {
12756
- const relativePath = path20.relative(this.projectRoot, filePath);
12955
+ const relativePath = path21.relative(this.projectRoot, filePath);
12757
12956
  if (!relativePath) return false;
12758
12957
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
12759
12958
  return false;
12760
12959
  }
12761
- if (hasFilteredPathSegment(relativePath, path20.sep)) {
12960
+ if (hasFilteredPathSegment(relativePath, path21.sep)) {
12762
12961
  return true;
12763
12962
  }
12764
- if (isRestrictedDirectory(relativePath, path20.sep)) {
12963
+ if (isRestrictedDirectory(relativePath, path21.sep)) {
12765
12964
  return true;
12766
12965
  }
12767
12966
  if (ignoreFilter.ignores(relativePath)) {
@@ -12807,24 +13006,24 @@ var FileWatcher = class {
12807
13006
  this.scheduleFlush();
12808
13007
  }
12809
13008
  isProjectConfigPath(filePath) {
12810
- const relativePath = path20.relative(this.projectRoot, filePath);
12811
- const normalizedRelativePath = path20.normalize(relativePath);
13009
+ const relativePath = path21.relative(this.projectRoot, filePath);
13010
+ const normalizedRelativePath = path21.normalize(relativePath);
12812
13011
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
12813
13012
  }
12814
13013
  isProjectConfigPathOrAncestor(relativePath) {
12815
- const normalizedRelativePath = path20.normalize(relativePath);
13014
+ const normalizedRelativePath = path21.normalize(relativePath);
12816
13015
  return this.getProjectConfigRelativePaths().some(
12817
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path20.sep}`)
13016
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path21.sep}`)
12818
13017
  );
12819
13018
  }
12820
13019
  getProjectConfigRelativePaths() {
12821
13020
  if (this.configPath) {
12822
- return [path20.normalize(path20.relative(this.projectRoot, this.configPath))];
13021
+ return [path21.normalize(path21.relative(this.projectRoot, this.configPath))];
12823
13022
  }
12824
13023
  return [
12825
13024
  resolveProjectConfigPath(this.projectRoot, this.host),
12826
13025
  resolveWritableProjectConfigPath(this.projectRoot, this.host)
12827
- ].map((configPath) => path20.normalize(path20.relative(this.projectRoot, configPath)));
13026
+ ].map((configPath) => path21.normalize(path21.relative(this.projectRoot, configPath)));
12828
13027
  }
12829
13028
  scheduleFlush() {
12830
13029
  if (this.debounceTimer) {
@@ -12839,7 +13038,7 @@ var FileWatcher = class {
12839
13038
  return;
12840
13039
  }
12841
13040
  const changes = Array.from(this.pendingChanges.entries()).map(
12842
- ([path25, type]) => ({ path: path25, type })
13041
+ ([path26, type]) => ({ path: path26, type })
12843
13042
  );
12844
13043
  this.pendingChanges.clear();
12845
13044
  try {
@@ -12866,7 +13065,7 @@ var FileWatcher = class {
12866
13065
  };
12867
13066
 
12868
13067
  // src/watcher/git-head-watcher.ts
12869
- import * as path21 from "path";
13068
+ import * as path22 from "path";
12870
13069
  var GitHeadWatcher = class {
12871
13070
  watcher = null;
12872
13071
  projectRoot;
@@ -12888,7 +13087,7 @@ var GitHeadWatcher = class {
12888
13087
  this.onBranchChange = handler;
12889
13088
  this.currentBranch = getCurrentBranch(this.projectRoot);
12890
13089
  const headPath = getHeadPath(this.projectRoot);
12891
- const refsPath = path21.join(this.projectRoot, ".git", "refs", "heads");
13090
+ const refsPath = path22.join(this.projectRoot, ".git", "refs", "heads");
12892
13091
  this.watcher = chokidar_default.watch([headPath, refsPath], {
12893
13092
  persistent: true,
12894
13093
  ignoreInitial: true,
@@ -13032,7 +13231,7 @@ function getConfigPaths(projectRoot, host, options) {
13032
13231
 
13033
13232
  // src/tools/visualize/activity.ts
13034
13233
  import { execFileSync } from "child_process";
13035
- import * as path22 from "path";
13234
+ import * as path23 from "path";
13036
13235
  function attachRecentActivity(data, projectRoot) {
13037
13236
  const activity = readGitActivity(projectRoot);
13038
13237
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -13194,7 +13393,7 @@ function normalizePath3(filePath) {
13194
13393
  return filePath.replace(/\\/g, "/");
13195
13394
  }
13196
13395
  function toGitRelativePath(projectRoot, filePath) {
13197
- const relativePath = path22.isAbsolute(filePath) ? path22.relative(projectRoot, filePath) : filePath;
13396
+ const relativePath = path23.isAbsolute(filePath) ? path23.relative(projectRoot, filePath) : filePath;
13198
13397
  return normalizePath3(relativePath);
13199
13398
  }
13200
13399
 
@@ -13452,7 +13651,7 @@ render();
13452
13651
  }
13453
13652
 
13454
13653
  // src/tools/visualize/transform.ts
13455
- import * as path23 from "path";
13654
+ import * as path24 from "path";
13456
13655
 
13457
13656
  // src/tools/visualize/modules.ts
13458
13657
  var MAX_MODULES = 18;
@@ -13585,8 +13784,8 @@ function compactModules(prefixToNodes) {
13585
13784
  function deriveModules(nodes) {
13586
13785
  const initial = /* @__PURE__ */ new Map();
13587
13786
  for (const node of nodes) {
13588
- const relative10 = stripToProjectRelative(node.filePath);
13589
- const prefix = modulePrefixFromRelativePath(relative10);
13787
+ const relative11 = stripToProjectRelative(node.filePath);
13788
+ const prefix = modulePrefixFromRelativePath(relative11);
13590
13789
  if (!initial.has(prefix)) initial.set(prefix, []);
13591
13790
  initial.get(prefix)?.push(node);
13592
13791
  }
@@ -13712,7 +13911,7 @@ function transformForVisualization(symbols, edges, options = {}) {
13712
13911
  filePath: s.filePath,
13713
13912
  kind: s.kind,
13714
13913
  line: s.startLine,
13715
- directory: path23.dirname(s.filePath),
13914
+ directory: path24.dirname(s.filePath),
13716
13915
  moduleId: "",
13717
13916
  moduleLabel: ""
13718
13917
  }));
@@ -13740,9 +13939,9 @@ function parseArgs(argv) {
13740
13939
  let host = "opencode";
13741
13940
  for (let i = 2; i < argv.length; i++) {
13742
13941
  if (argv[i] === "--project" && argv[i + 1]) {
13743
- project = path24.resolve(argv[++i]);
13942
+ project = path25.resolve(argv[++i]);
13744
13943
  } else if (argv[i] === "--config" && argv[i + 1]) {
13745
- config = path24.resolve(argv[++i]);
13944
+ config = path25.resolve(argv[++i]);
13746
13945
  } else if (argv[i] === "--host" && argv[i + 1]) {
13747
13946
  host = parseHostMode(argv[++i]);
13748
13947
  } else if (argv[i] === "--host") {
@@ -13765,7 +13964,7 @@ function parseVisualizeArgs(argv, cwd) {
13765
13964
  for (let i = 0; i < argv.length; i++) {
13766
13965
  const arg = argv[i];
13767
13966
  if (arg === "--project" && argv[i + 1]) {
13768
- project = path24.resolve(argv[++i]);
13967
+ project = path25.resolve(argv[++i]);
13769
13968
  } else if (arg === "--max" && argv[i + 1]) {
13770
13969
  maxNodes = Number(argv[++i]);
13771
13970
  } else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
@@ -13802,7 +14001,7 @@ async function handleVisualizeCommand(argv, cwd) {
13802
14001
  console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
13803
14002
  return 1;
13804
14003
  }
13805
- const outputPath = path24.join(os5.tmpdir(), `call-graph-${Date.now()}.html`);
14004
+ const outputPath = path25.join(os5.tmpdir(), `call-graph-${Date.now()}.html`);
13806
14005
  writeFileSync6(outputPath, generateVisualizationHtml(vizData), "utf-8");
13807
14006
  console.log(`Temporal call graph visualization generated: ${outputPath}`);
13808
14007
  console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
@@ -13832,7 +14031,7 @@ async function main() {
13832
14031
  const transport = new StdioServerTransport();
13833
14032
  await server.connect(transport);
13834
14033
  let watcher = null;
13835
- const isHomeDir = path24.resolve(args.project) === path24.resolve(os5.homedir());
14034
+ const isHomeDir = path25.resolve(args.project) === path25.resolve(os5.homedir());
13836
14035
  const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
13837
14036
  if (config.indexing.autoIndex && isValidProject) {
13838
14037
  const indexer = getIndexerForProject(args.project, args.host);