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/index.js CHANGED
@@ -490,7 +490,7 @@ var require_ignore = __commonJS({
490
490
  // path matching.
491
491
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
492
492
  // @returns {TestResult} true if a file is ignored
493
- test(path22, checkUnignored, mode) {
493
+ test(path23, checkUnignored, mode) {
494
494
  let ignored = false;
495
495
  let unignored = false;
496
496
  let matchedRule;
@@ -499,7 +499,7 @@ var require_ignore = __commonJS({
499
499
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
500
500
  return;
501
501
  }
502
- const matched = rule[mode].test(path22);
502
+ const matched = rule[mode].test(path23);
503
503
  if (!matched) {
504
504
  return;
505
505
  }
@@ -520,17 +520,17 @@ var require_ignore = __commonJS({
520
520
  var throwError = (message, Ctor) => {
521
521
  throw new Ctor(message);
522
522
  };
523
- var checkPath = (path22, originalPath, doThrow) => {
524
- if (!isString(path22)) {
523
+ var checkPath = (path23, originalPath, doThrow) => {
524
+ if (!isString(path23)) {
525
525
  return doThrow(
526
526
  `path must be a string, but got \`${originalPath}\``,
527
527
  TypeError
528
528
  );
529
529
  }
530
- if (!path22) {
530
+ if (!path23) {
531
531
  return doThrow(`path must not be empty`, TypeError);
532
532
  }
533
- if (checkPath.isNotRelative(path22)) {
533
+ if (checkPath.isNotRelative(path23)) {
534
534
  const r = "`path.relative()`d";
535
535
  return doThrow(
536
536
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -539,7 +539,7 @@ var require_ignore = __commonJS({
539
539
  }
540
540
  return true;
541
541
  };
542
- var isNotRelative = (path22) => REGEX_TEST_INVALID_PATH.test(path22);
542
+ var isNotRelative = (path23) => REGEX_TEST_INVALID_PATH.test(path23);
543
543
  checkPath.isNotRelative = isNotRelative;
544
544
  checkPath.convert = (p) => p;
545
545
  var Ignore2 = class {
@@ -569,19 +569,19 @@ var require_ignore = __commonJS({
569
569
  }
570
570
  // @returns {TestResult}
571
571
  _test(originalPath, cache, checkUnignored, slices) {
572
- const path22 = originalPath && checkPath.convert(originalPath);
572
+ const path23 = originalPath && checkPath.convert(originalPath);
573
573
  checkPath(
574
- path22,
574
+ path23,
575
575
  originalPath,
576
576
  this._strictPathCheck ? throwError : RETURN_FALSE
577
577
  );
578
- return this._t(path22, cache, checkUnignored, slices);
578
+ return this._t(path23, cache, checkUnignored, slices);
579
579
  }
580
- checkIgnore(path22) {
581
- if (!REGEX_TEST_TRAILING_SLASH.test(path22)) {
582
- return this.test(path22);
580
+ checkIgnore(path23) {
581
+ if (!REGEX_TEST_TRAILING_SLASH.test(path23)) {
582
+ return this.test(path23);
583
583
  }
584
- const slices = path22.split(SLASH2).filter(Boolean);
584
+ const slices = path23.split(SLASH2).filter(Boolean);
585
585
  slices.pop();
586
586
  if (slices.length) {
587
587
  const parent = this._t(
@@ -594,18 +594,18 @@ var require_ignore = __commonJS({
594
594
  return parent;
595
595
  }
596
596
  }
597
- return this._rules.test(path22, false, MODE_CHECK_IGNORE);
597
+ return this._rules.test(path23, false, MODE_CHECK_IGNORE);
598
598
  }
599
- _t(path22, cache, checkUnignored, slices) {
600
- if (path22 in cache) {
601
- return cache[path22];
599
+ _t(path23, cache, checkUnignored, slices) {
600
+ if (path23 in cache) {
601
+ return cache[path23];
602
602
  }
603
603
  if (!slices) {
604
- slices = path22.split(SLASH2).filter(Boolean);
604
+ slices = path23.split(SLASH2).filter(Boolean);
605
605
  }
606
606
  slices.pop();
607
607
  if (!slices.length) {
608
- return cache[path22] = this._rules.test(path22, checkUnignored, MODE_IGNORE);
608
+ return cache[path23] = this._rules.test(path23, checkUnignored, MODE_IGNORE);
609
609
  }
610
610
  const parent = this._t(
611
611
  slices.join(SLASH2) + SLASH2,
@@ -613,29 +613,29 @@ var require_ignore = __commonJS({
613
613
  checkUnignored,
614
614
  slices
615
615
  );
616
- return cache[path22] = parent.ignored ? parent : this._rules.test(path22, checkUnignored, MODE_IGNORE);
616
+ return cache[path23] = parent.ignored ? parent : this._rules.test(path23, checkUnignored, MODE_IGNORE);
617
617
  }
618
- ignores(path22) {
619
- return this._test(path22, this._ignoreCache, false).ignored;
618
+ ignores(path23) {
619
+ return this._test(path23, this._ignoreCache, false).ignored;
620
620
  }
621
621
  createFilter() {
622
- return (path22) => !this.ignores(path22);
622
+ return (path23) => !this.ignores(path23);
623
623
  }
624
624
  filter(paths) {
625
625
  return makeArray(paths).filter(this.createFilter());
626
626
  }
627
627
  // @returns {TestResult}
628
- test(path22) {
629
- return this._test(path22, this._testCache, true);
628
+ test(path23) {
629
+ return this._test(path23, this._testCache, true);
630
630
  }
631
631
  };
632
632
  var factory = (options) => new Ignore2(options);
633
- var isPathValid = (path22) => checkPath(path22 && checkPath.convert(path22), path22, RETURN_FALSE);
633
+ var isPathValid = (path23) => checkPath(path23 && checkPath.convert(path23), path23, RETURN_FALSE);
634
634
  var setupWindows = () => {
635
635
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
636
636
  checkPath.convert = makePosix;
637
637
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
638
- checkPath.isNotRelative = (path22) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path22) || isNotRelative(path22);
638
+ checkPath.isNotRelative = (path23) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path23) || isNotRelative(path23);
639
639
  };
640
640
  if (
641
641
  // Detect `process` so that it can run in browsers.
@@ -652,7 +652,7 @@ var require_ignore = __commonJS({
652
652
 
653
653
  // src/index.ts
654
654
  import * as os5 from "os";
655
- import * as path21 from "path";
655
+ import * as path22 from "path";
656
656
  import { fileURLToPath as fileURLToPath2 } from "url";
657
657
 
658
658
  // src/config/constants.ts
@@ -785,7 +785,8 @@ function getDefaultIndexingConfig() {
785
785
  requireProjectMarker: true,
786
786
  maxDepth: 5,
787
787
  maxFilesPerDirectory: 100,
788
- fallbackToTextOnMaxChunks: true
788
+ fallbackToTextOnMaxChunks: true,
789
+ gitBlame: { enabled: false }
789
790
  };
790
791
  }
791
792
  function getDefaultSearchConfig() {
@@ -909,7 +910,10 @@ function parseConfig(raw) {
909
910
  requireProjectMarker: typeof rawIndexing.requireProjectMarker === "boolean" ? rawIndexing.requireProjectMarker : defaultIndexing.requireProjectMarker,
910
911
  maxDepth: typeof rawIndexing.maxDepth === "number" ? rawIndexing.maxDepth < -1 ? -1 : rawIndexing.maxDepth : defaultIndexing.maxDepth,
911
912
  maxFilesPerDirectory: typeof rawIndexing.maxFilesPerDirectory === "number" ? Math.max(1, rawIndexing.maxFilesPerDirectory) : defaultIndexing.maxFilesPerDirectory,
912
- fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks
913
+ fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks,
914
+ gitBlame: {
915
+ enabled: rawIndexing.gitBlame && typeof rawIndexing.gitBlame === "object" && typeof rawIndexing.gitBlame.enabled === "boolean" ? rawIndexing.gitBlame.enabled : defaultIndexing.gitBlame.enabled
916
+ }
913
917
  };
914
918
  const rawSearch = input.search && typeof input.search === "object" ? input.search : {};
915
919
  const search = {
@@ -1579,14 +1583,14 @@ function loadMergedConfig(projectRoot, host = "opencode") {
1579
1583
 
1580
1584
  // src/tools/operations.ts
1581
1585
  import { existsSync as existsSync9, realpathSync, statSync as statSync3 } from "fs";
1582
- import * as path14 from "path";
1586
+ import * as path15 from "path";
1583
1587
 
1584
1588
  // src/indexer/index.ts
1585
1589
  import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync2, renameSync, unlinkSync, mkdirSync as mkdirSync2, promises as fsPromises2 } from "fs";
1586
- import * as path11 from "path";
1590
+ import * as path12 from "path";
1587
1591
  import { performance as performance2 } from "perf_hooks";
1588
- import { execFile as execFile2 } from "child_process";
1589
- import { promisify as promisify2 } from "util";
1592
+ import { execFile as execFile3 } from "child_process";
1593
+ import { promisify as promisify3 } from "util";
1590
1594
 
1591
1595
  // node_modules/eventemitter3/index.mjs
1592
1596
  var import_index = __toESM(require_eventemitter3(), 1);
@@ -4773,8 +4777,8 @@ function normalizeFiles(rawFiles, projectRoot) {
4773
4777
  const trimmed = raw.trim();
4774
4778
  if (!trimmed) continue;
4775
4779
  const absolute = path10.resolve(projectRoot, trimmed);
4776
- const relative10 = path10.relative(projectRoot, absolute);
4777
- const cleaned = relative10.startsWith("./") ? relative10.slice(2) : relative10;
4780
+ const relative11 = path10.relative(projectRoot, absolute);
4781
+ const cleaned = relative11.startsWith("./") ? relative11.slice(2) : relative11;
4778
4782
  if (!seen.has(cleaned)) {
4779
4783
  seen.add(cleaned);
4780
4784
  result.push(cleaned);
@@ -4783,8 +4787,61 @@ function normalizeFiles(rawFiles, projectRoot) {
4783
4787
  return result;
4784
4788
  }
4785
4789
 
4790
+ // src/indexer/git-blame.ts
4791
+ import { execFile as execFile2 } from "child_process";
4792
+ import * as path11 from "path";
4793
+ import { promisify as promisify2 } from "util";
4794
+ var execFileAsync2 = promisify2(execFile2);
4795
+ function parseGitBlamePorcelain(output) {
4796
+ const commits = /* @__PURE__ */ new Map();
4797
+ let current;
4798
+ for (const line of output.split("\n")) {
4799
+ if (/^[0-9a-f]{40} /.test(line)) {
4800
+ const sha = line.slice(0, 40);
4801
+ current = commits.get(sha) ?? {
4802
+ sha,
4803
+ author: "",
4804
+ authorEmail: "",
4805
+ committedAt: 0,
4806
+ summary: "",
4807
+ lines: 0
4808
+ };
4809
+ commits.set(sha, current);
4810
+ continue;
4811
+ }
4812
+ if (!current) {
4813
+ continue;
4814
+ }
4815
+ if (line.startsWith("author ")) {
4816
+ current.author = line.slice("author ".length);
4817
+ } else if (line.startsWith("author-mail ")) {
4818
+ current.authorEmail = line.slice("author-mail ".length).replace(/^<|>$/g, "");
4819
+ } else if (line.startsWith("author-time ")) {
4820
+ current.committedAt = Number.parseInt(line.slice("author-time ".length), 10);
4821
+ } else if (line.startsWith("summary ")) {
4822
+ current.summary = line.slice("summary ".length);
4823
+ } else if (line.startsWith(" ")) {
4824
+ current.lines += 1;
4825
+ }
4826
+ }
4827
+ return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
4828
+ }
4829
+ async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
4830
+ const relativePath = path11.relative(projectRoot, filePath);
4831
+ try {
4832
+ const { stdout } = await execFileAsync2(
4833
+ "git",
4834
+ ["blame", "--line-porcelain", "-L", `${startLine},${endLine}`, "--", relativePath],
4835
+ { cwd: projectRoot, timeout: 3e4 }
4836
+ );
4837
+ return parseGitBlamePorcelain(stdout);
4838
+ } catch {
4839
+ return void 0;
4840
+ }
4841
+ }
4842
+
4786
4843
  // src/indexer/index.ts
4787
- var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
4844
+ var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab", "bash"]);
4788
4845
  var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
4789
4846
  var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
4790
4847
  "function_declaration",
@@ -4864,6 +4921,45 @@ function isSqliteCorruptionError(error) {
4864
4921
  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");
4865
4922
  }
4866
4923
  var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
4924
+ function metadataFromBlame(blame) {
4925
+ if (!blame) {
4926
+ return {};
4927
+ }
4928
+ return {
4929
+ blameSha: blame.sha,
4930
+ blameAuthor: blame.author,
4931
+ blameAuthorEmail: blame.authorEmail,
4932
+ blameCommittedAt: blame.committedAt,
4933
+ blameSummary: blame.summary
4934
+ };
4935
+ }
4936
+ function blameFromChunkData(chunk) {
4937
+ if (!chunk?.blameSha || !chunk.blameAuthor || !chunk.blameAuthorEmail || chunk.blameCommittedAt === void 0 || !chunk.blameSummary) {
4938
+ return void 0;
4939
+ }
4940
+ return {
4941
+ sha: chunk.blameSha,
4942
+ author: chunk.blameAuthor,
4943
+ authorEmail: chunk.blameAuthorEmail,
4944
+ committedAt: chunk.blameCommittedAt,
4945
+ summary: chunk.blameSummary
4946
+ };
4947
+ }
4948
+ function blameFromMetadata(metadata) {
4949
+ if (!metadata.blameSha || !metadata.blameAuthor || !metadata.blameAuthorEmail || metadata.blameCommittedAt === void 0 || !metadata.blameSummary) {
4950
+ return void 0;
4951
+ }
4952
+ return {
4953
+ sha: metadata.blameSha,
4954
+ author: metadata.blameAuthor,
4955
+ authorEmail: metadata.blameAuthorEmail,
4956
+ committedAt: metadata.blameCommittedAt,
4957
+ summary: metadata.blameSummary
4958
+ };
4959
+ }
4960
+ function hasBlameMetadata(metadata) {
4961
+ return blameFromMetadata(metadata) !== void 0;
4962
+ }
4867
4963
  var INDEX_METADATA_VERSION = "1";
4868
4964
  var EMBEDDING_STRATEGY_VERSION = "2";
4869
4965
  var RANKING_TOKEN_CACHE_LIMIT = 4096;
@@ -5022,9 +5118,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
5022
5118
  return true;
5023
5119
  }
5024
5120
  function isPathWithinRoot(filePath, rootPath) {
5025
- const normalizedFilePath = path11.resolve(filePath);
5026
- const normalizedRoot = path11.resolve(rootPath);
5027
- return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path11.sep}`);
5121
+ const normalizedFilePath = path12.resolve(filePath);
5122
+ const normalizedRoot = path12.resolve(rootPath);
5123
+ return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path12.sep}`);
5028
5124
  }
5029
5125
  var rankingQueryTokenCache = /* @__PURE__ */ new Map();
5030
5126
  var rankingNameTokenCache = /* @__PURE__ */ new Map();
@@ -5783,7 +5879,8 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
5783
5879
  chunkType,
5784
5880
  name: chunk.name ?? void 0,
5785
5881
  language: chunk.language,
5786
- hash: chunk.contentHash
5882
+ hash: chunk.contentHash,
5883
+ ...metadataFromBlame(blameFromChunkData(chunk))
5787
5884
  };
5788
5885
  const baselineScore = existing?.score ?? 0.5;
5789
5886
  candidateUnion.set(chunk.chunkId, {
@@ -5867,7 +5964,8 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
5867
5964
  chunkType,
5868
5965
  name: chunk.name ?? void 0,
5869
5966
  language: chunk.language,
5870
- hash: chunk.contentHash
5967
+ hash: chunk.contentHash,
5968
+ ...metadataFromBlame(blameFromChunkData(chunk))
5871
5969
  }
5872
5970
  });
5873
5971
  }
@@ -6036,6 +6134,21 @@ function matchesSearchFilters(candidate, options, minScore) {
6036
6134
  if (options?.chunkType && candidate.metadata.chunkType !== options.chunkType) {
6037
6135
  return false;
6038
6136
  }
6137
+ if (options?.blameAuthor) {
6138
+ const author = options.blameAuthor.toLowerCase();
6139
+ const candidateAuthor = candidate.metadata.blameAuthor?.toLowerCase();
6140
+ const candidateEmail = candidate.metadata.blameAuthorEmail?.toLowerCase();
6141
+ if (candidateAuthor !== author && candidateEmail !== author) return false;
6142
+ }
6143
+ if (options?.blameSha && !candidate.metadata.blameSha?.toLowerCase().startsWith(options.blameSha.toLowerCase())) {
6144
+ return false;
6145
+ }
6146
+ if (options?.blameSince) {
6147
+ const sinceMs = Date.parse(options.blameSince);
6148
+ if (Number.isNaN(sinceMs)) return false;
6149
+ const committedAt = candidate.metadata.blameCommittedAt;
6150
+ if (committedAt === void 0 || committedAt < Math.floor(sinceMs / 1e3)) return false;
6151
+ }
6039
6152
  return true;
6040
6153
  }
6041
6154
  function unionCandidates(semanticCandidates, keywordCandidates) {
@@ -6079,9 +6192,9 @@ var Indexer = class {
6079
6192
  this.config = config;
6080
6193
  this.host = host;
6081
6194
  this.indexPath = this.getIndexPath();
6082
- this.fileHashCachePath = path11.join(this.indexPath, "file-hashes.json");
6083
- this.failedBatchesPath = path11.join(this.indexPath, "failed-batches.json");
6084
- this.indexingLockPath = path11.join(this.indexPath, "indexing.lock");
6195
+ this.fileHashCachePath = path12.join(this.indexPath, "file-hashes.json");
6196
+ this.failedBatchesPath = path12.join(this.indexPath, "failed-batches.json");
6197
+ this.indexingLockPath = path12.join(this.indexPath, "indexing.lock");
6085
6198
  this.logger = initializeLogger(config.debug);
6086
6199
  }
6087
6200
  getIndexPath() {
@@ -6113,14 +6226,14 @@ var Indexer = class {
6113
6226
  }
6114
6227
  atomicWriteSync(targetPath, data) {
6115
6228
  const tempPath = `${targetPath}.tmp`;
6116
- mkdirSync2(path11.dirname(targetPath), { recursive: true });
6229
+ mkdirSync2(path12.dirname(targetPath), { recursive: true });
6117
6230
  writeFileSync2(tempPath, data);
6118
6231
  renameSync(tempPath, targetPath);
6119
6232
  }
6120
6233
  getScopedRoots() {
6121
- const roots = /* @__PURE__ */ new Set([path11.resolve(this.projectRoot)]);
6234
+ const roots = /* @__PURE__ */ new Set([path12.resolve(this.projectRoot)]);
6122
6235
  for (const kbRoot of this.config.knowledgeBases) {
6123
- roots.add(path11.resolve(this.projectRoot, kbRoot));
6236
+ roots.add(path12.resolve(this.projectRoot, kbRoot));
6124
6237
  }
6125
6238
  return Array.from(roots);
6126
6239
  }
@@ -6129,29 +6242,29 @@ var Indexer = class {
6129
6242
  if (this.config.scope !== "global") {
6130
6243
  return branchName;
6131
6244
  }
6132
- const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6245
+ const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
6133
6246
  return `${projectHash}:${branchName}`;
6134
6247
  }
6135
6248
  getBranchCatalogKeyFor(branchName) {
6136
6249
  if (this.config.scope !== "global") {
6137
6250
  return branchName;
6138
6251
  }
6139
- const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6252
+ const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
6140
6253
  return `${projectHash}:${branchName}`;
6141
6254
  }
6142
6255
  getLegacyBranchCatalogKey() {
6143
6256
  return this.currentBranch || "default";
6144
6257
  }
6145
6258
  getLegacyMigrationMetadataKey() {
6146
- const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6259
+ const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
6147
6260
  return `index.globalBranchMigration.${projectHash}`;
6148
6261
  }
6149
6262
  getProjectEmbeddingStrategyMetadataKey() {
6150
- const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6263
+ const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
6151
6264
  return `index.embeddingStrategyVersion.${projectHash}`;
6152
6265
  }
6153
6266
  getProjectForceReembedMetadataKey() {
6154
- const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6267
+ const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
6155
6268
  return `index.forceReembed.${projectHash}`;
6156
6269
  }
6157
6270
  hasProjectForceReembedPending() {
@@ -6245,7 +6358,7 @@ var Indexer = class {
6245
6358
  if (!this.database) {
6246
6359
  return { chunkIds, symbolIds };
6247
6360
  }
6248
- const projectRootPath = path11.resolve(this.projectRoot);
6361
+ const projectRootPath = path12.resolve(this.projectRoot);
6249
6362
  const projectLocalFilePaths = /* @__PURE__ */ new Set([
6250
6363
  ...Array.from(this.fileHashCache.keys()).filter(
6251
6364
  (filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
@@ -6268,7 +6381,7 @@ var Indexer = class {
6268
6381
  if (this.config.scope !== "global") {
6269
6382
  return this.getBranchCatalogCleanupKeys();
6270
6383
  }
6271
- const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6384
+ const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
6272
6385
  const keys = /* @__PURE__ */ new Set();
6273
6386
  const projectChunkIdSet = new Set(projectChunkIds);
6274
6387
  const projectSymbolIdSet = new Set(projectSymbolIds);
@@ -6352,7 +6465,7 @@ var Indexer = class {
6352
6465
  if (!this.database || this.config.scope !== "global") {
6353
6466
  return false;
6354
6467
  }
6355
- const projectHash = hashContent(path11.resolve(this.projectRoot)).slice(0, 16);
6468
+ const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
6356
6469
  const roots = this.getScopedRoots();
6357
6470
  const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
6358
6471
  return this.database.getAllBranches().some(
@@ -6386,7 +6499,7 @@ var Indexer = class {
6386
6499
  ...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
6387
6500
  ...scopedEntries.map(({ metadata }) => metadata.filePath)
6388
6501
  ]);
6389
- const projectRootPath = path11.resolve(this.projectRoot);
6502
+ const projectRootPath = path12.resolve(this.projectRoot);
6390
6503
  const projectLocalFilePaths = new Set(
6391
6504
  Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
6392
6505
  );
@@ -6746,13 +6859,13 @@ var Indexer = class {
6746
6859
  }
6747
6860
  await fsPromises2.mkdir(this.indexPath, { recursive: true });
6748
6861
  const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
6749
- const storePath = path11.join(this.indexPath, "vectors");
6862
+ const storePath = path12.join(this.indexPath, "vectors");
6750
6863
  this.store = new VectorStore(storePath, dimensions);
6751
- const indexFilePath = path11.join(this.indexPath, "vectors.usearch");
6864
+ const indexFilePath = path12.join(this.indexPath, "vectors.usearch");
6752
6865
  if (existsSync7(indexFilePath)) {
6753
6866
  this.store.load();
6754
6867
  }
6755
- const invertedIndexPath = path11.join(this.indexPath, "inverted-index.json");
6868
+ const invertedIndexPath = path12.join(this.indexPath, "inverted-index.json");
6756
6869
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
6757
6870
  try {
6758
6871
  this.invertedIndex.load();
@@ -6762,7 +6875,7 @@ var Indexer = class {
6762
6875
  }
6763
6876
  this.invertedIndex = new InvertedIndex(invertedIndexPath);
6764
6877
  }
6765
- const dbPath = path11.join(this.indexPath, "codebase.db");
6878
+ const dbPath = path12.join(this.indexPath, "codebase.db");
6766
6879
  let dbIsNew = !existsSync7(dbPath);
6767
6880
  try {
6768
6881
  this.database = new Database(dbPath);
@@ -6843,7 +6956,7 @@ var Indexer = class {
6843
6956
  if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
6844
6957
  return {
6845
6958
  resetCorruptedIndex: true,
6846
- warning: this.getCorruptedIndexWarning(path11.join(this.indexPath, "codebase.db"))
6959
+ warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
6847
6960
  };
6848
6961
  }
6849
6962
  throw error;
@@ -6858,7 +6971,7 @@ var Indexer = class {
6858
6971
  return;
6859
6972
  }
6860
6973
  const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
6861
- const storeBasePath = path11.join(this.indexPath, "vectors");
6974
+ const storeBasePath = path12.join(this.indexPath, "vectors");
6862
6975
  const storeIndexPath = `${storeBasePath}.usearch`;
6863
6976
  const storeMetadataPath = `${storeBasePath}.meta.json`;
6864
6977
  const backupIndexPath = `${storeIndexPath}.bak`;
@@ -6943,7 +7056,7 @@ var Indexer = class {
6943
7056
  if (!isSqliteCorruptionError(error)) {
6944
7057
  return false;
6945
7058
  }
6946
- const dbPath = path11.join(this.indexPath, "codebase.db");
7059
+ const dbPath = path12.join(this.indexPath, "codebase.db");
6947
7060
  const warning = this.getCorruptedIndexWarning(dbPath);
6948
7061
  const errorMessage = getErrorMessage2(error);
6949
7062
  if (this.config.scope === "global") {
@@ -6966,15 +7079,15 @@ var Indexer = class {
6966
7079
  this.indexCompatibility = null;
6967
7080
  this.fileHashCache.clear();
6968
7081
  const resetPaths = [
6969
- path11.join(this.indexPath, "codebase.db"),
6970
- path11.join(this.indexPath, "codebase.db-shm"),
6971
- path11.join(this.indexPath, "codebase.db-wal"),
6972
- path11.join(this.indexPath, "vectors.usearch"),
6973
- path11.join(this.indexPath, "inverted-index.json"),
6974
- path11.join(this.indexPath, "file-hashes.json"),
6975
- path11.join(this.indexPath, "failed-batches.json"),
6976
- path11.join(this.indexPath, "indexing.lock"),
6977
- path11.join(this.indexPath, "vectors")
7082
+ path12.join(this.indexPath, "codebase.db"),
7083
+ path12.join(this.indexPath, "codebase.db-shm"),
7084
+ path12.join(this.indexPath, "codebase.db-wal"),
7085
+ path12.join(this.indexPath, "vectors.usearch"),
7086
+ path12.join(this.indexPath, "inverted-index.json"),
7087
+ path12.join(this.indexPath, "file-hashes.json"),
7088
+ path12.join(this.indexPath, "failed-batches.json"),
7089
+ path12.join(this.indexPath, "indexing.lock"),
7090
+ path12.join(this.indexPath, "vectors")
6978
7091
  ];
6979
7092
  await Promise.all(resetPaths.map(async (targetPath) => {
6980
7093
  try {
@@ -7211,6 +7324,7 @@ var Indexer = class {
7211
7324
  this.logger.debug("Parsed changed files", { parsedCount: parsedFiles.length, parseMs: parseMs.toFixed(2) });
7212
7325
  const existingChunks = /* @__PURE__ */ new Map();
7213
7326
  const existingChunksByFile = /* @__PURE__ */ new Map();
7327
+ const existingMetadataById = /* @__PURE__ */ new Map();
7214
7328
  for (const { key, metadata } of store.getAllMetadata()) {
7215
7329
  if (scopedRoots && !this.isFileInCurrentScope(metadata.filePath, scopedRoots)) {
7216
7330
  continue;
@@ -7219,6 +7333,7 @@ var Indexer = class {
7219
7333
  continue;
7220
7334
  }
7221
7335
  existingChunks.set(key, metadata.hash);
7336
+ existingMetadataById.set(key, metadata);
7222
7337
  const fileChunks = existingChunksByFile.get(metadata.filePath) || /* @__PURE__ */ new Set();
7223
7338
  fileChunks.add(key);
7224
7339
  existingChunksByFile.set(metadata.filePath, fileChunks);
@@ -7226,6 +7341,8 @@ var Indexer = class {
7226
7341
  const currentChunkIds = /* @__PURE__ */ new Set();
7227
7342
  const currentFilePaths = /* @__PURE__ */ new Set();
7228
7343
  const pendingChunks = [];
7344
+ const gitBlameEnabled = this.config.indexing.gitBlame.enabled && isGitRepo(this.projectRoot);
7345
+ let backfilledBlameMetadata = false;
7229
7346
  for (const filePath of unchangedFilePaths) {
7230
7347
  currentFilePaths.add(filePath);
7231
7348
  const fileChunks = existingChunksByFile.get(filePath);
@@ -7236,10 +7353,52 @@ var Indexer = class {
7236
7353
  }
7237
7354
  }
7238
7355
  const chunkDataBatch = [];
7356
+ if (gitBlameEnabled) {
7357
+ const backfillItems = [];
7358
+ for (const chunkId of currentChunkIds) {
7359
+ const metadata = existingMetadataById.get(chunkId);
7360
+ if (!metadata || hasBlameMetadata(metadata)) {
7361
+ continue;
7362
+ }
7363
+ const chunk = database.getChunk(chunkId);
7364
+ if (!chunk) {
7365
+ continue;
7366
+ }
7367
+ const blame = await getChunkGitBlame(this.projectRoot, chunk.filePath, chunk.startLine, chunk.endLine);
7368
+ const blameMetadata = metadataFromBlame(blame);
7369
+ if (!blameMetadata.blameSha) {
7370
+ continue;
7371
+ }
7372
+ chunkDataBatch.push({
7373
+ ...chunk,
7374
+ blameSha: blameMetadata.blameSha,
7375
+ blameAuthor: blameMetadata.blameAuthor,
7376
+ blameAuthorEmail: blameMetadata.blameAuthorEmail,
7377
+ blameCommittedAt: blameMetadata.blameCommittedAt,
7378
+ blameSummary: blameMetadata.blameSummary
7379
+ });
7380
+ const embeddingBuffer = database.getEmbedding(chunk.contentHash);
7381
+ if (!embeddingBuffer) {
7382
+ continue;
7383
+ }
7384
+ backfillItems.push({
7385
+ id: chunkId,
7386
+ vector: Array.from(bufferToFloat32Array(embeddingBuffer)),
7387
+ metadata: {
7388
+ ...metadata,
7389
+ ...blameMetadata
7390
+ }
7391
+ });
7392
+ }
7393
+ if (backfillItems.length > 0) {
7394
+ store.addBatch(backfillItems);
7395
+ backfilledBlameMetadata = true;
7396
+ }
7397
+ }
7239
7398
  for (const parsed of parsedFiles) {
7240
7399
  currentFilePaths.add(parsed.path);
7241
7400
  if (parsed.chunks.length === 0) {
7242
- const relativePath = path11.relative(this.projectRoot, parsed.path);
7401
+ const relativePath = path12.relative(this.projectRoot, parsed.path);
7243
7402
  stats.parseFailures.push(relativePath);
7244
7403
  }
7245
7404
  let fileChunkCount = 0;
@@ -7260,6 +7419,10 @@ var Indexer = class {
7260
7419
  }
7261
7420
  const id = generateChunkId(parsed.path, chunk);
7262
7421
  const contentHash = generateChunkHash(chunk);
7422
+ const existingContentHash = existingChunks.get(id);
7423
+ const existingChunk = gitBlameEnabled ? database.getChunk(id) : null;
7424
+ const blame = gitBlameEnabled && existingContentHash !== contentHash ? await getChunkGitBlame(this.projectRoot, parsed.path, chunk.startLine, chunk.endLine) : blameFromChunkData(existingChunk);
7425
+ const blameMetadata = metadataFromBlame(blame);
7263
7426
  currentChunkIds.add(id);
7264
7427
  chunkDataBatch.push({
7265
7428
  chunkId: id,
@@ -7269,9 +7432,14 @@ var Indexer = class {
7269
7432
  endLine: chunk.endLine,
7270
7433
  nodeType: chunk.chunkType,
7271
7434
  name: chunk.name,
7272
- language: chunk.language
7435
+ language: chunk.language,
7436
+ blameSha: blameMetadata.blameSha,
7437
+ blameAuthor: blameMetadata.blameAuthor,
7438
+ blameAuthorEmail: blameMetadata.blameAuthorEmail,
7439
+ blameCommittedAt: blameMetadata.blameCommittedAt,
7440
+ blameSummary: blameMetadata.blameSummary
7273
7441
  });
7274
- if (existingChunks.get(id) === contentHash) {
7442
+ if (existingContentHash === contentHash) {
7275
7443
  fileChunkCount++;
7276
7444
  continue;
7277
7445
  }
@@ -7286,7 +7454,8 @@ var Indexer = class {
7286
7454
  chunkType: chunk.chunkType,
7287
7455
  name: chunk.name,
7288
7456
  language: chunk.language,
7289
- hash: contentHash
7457
+ hash: contentHash,
7458
+ ...blameMetadata
7290
7459
  };
7291
7460
  pendingChunks.push({
7292
7461
  id,
@@ -7429,6 +7598,9 @@ var Indexer = class {
7429
7598
  database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
7430
7599
  database.clearBranchSymbols(branchCatalogKey);
7431
7600
  database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
7601
+ if (backfilledBlameMetadata) {
7602
+ store.save();
7603
+ }
7432
7604
  if (scopedRoots) {
7433
7605
  this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
7434
7606
  this.clearScopedFailedBatches(scopedRoots);
@@ -7992,7 +8164,8 @@ var Indexer = class {
7992
8164
  content,
7993
8165
  score: r.score,
7994
8166
  chunkType: r.metadata.chunkType,
7995
- name: r.metadata.name
8167
+ name: r.metadata.name,
8168
+ blame: blameFromMetadata(r.metadata)
7996
8169
  };
7997
8170
  })
7998
8171
  );
@@ -8084,12 +8257,12 @@ var Indexer = class {
8084
8257
  this.indexCompatibility = compatibility;
8085
8258
  return;
8086
8259
  }
8087
- const localProjectIndexPaths = [path11.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
8260
+ const localProjectIndexPaths = [path12.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
8088
8261
  if (this.host !== "opencode") {
8089
- localProjectIndexPaths.push(path11.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
8262
+ localProjectIndexPaths.push(path12.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
8090
8263
  }
8091
8264
  const isLocalProjectIndex = localProjectIndexPaths.some(
8092
- (localPath) => path11.resolve(this.indexPath) === path11.resolve(localPath)
8265
+ (localPath) => path12.resolve(this.indexPath) === path12.resolve(localPath)
8093
8266
  );
8094
8267
  if (!isLocalProjectIndex) {
8095
8268
  throw new Error(
@@ -8179,7 +8352,7 @@ var Indexer = class {
8179
8352
  gcOrphanSymbols: 0,
8180
8353
  gcOrphanCallEdges: 0,
8181
8354
  resetCorruptedIndex: true,
8182
- warning: this.getCorruptedIndexWarning(path11.join(this.indexPath, "codebase.db"))
8355
+ warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
8183
8356
  };
8184
8357
  }
8185
8358
  this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
@@ -8499,7 +8672,8 @@ var Indexer = class {
8499
8672
  content,
8500
8673
  score: r.score,
8501
8674
  chunkType: r.metadata.chunkType,
8502
- name: r.metadata.name
8675
+ name: r.metadata.name,
8676
+ blame: blameFromMetadata(r.metadata)
8503
8677
  };
8504
8678
  })
8505
8679
  );
@@ -8536,9 +8710,9 @@ var Indexer = class {
8536
8710
  const { database } = await this.ensureInitialized();
8537
8711
  let shortest = [];
8538
8712
  for (const branchKey of this.getBranchCatalogKeys()) {
8539
- const path22 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
8540
- if (path22.length > 0 && (shortest.length === 0 || path22.length < shortest.length)) {
8541
- shortest = path22;
8713
+ const path23 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
8714
+ if (path23.length > 0 && (shortest.length === 0 || path23.length < shortest.length)) {
8715
+ shortest = path23;
8542
8716
  }
8543
8717
  }
8544
8718
  return shortest;
@@ -8570,7 +8744,7 @@ var Indexer = class {
8570
8744
  }
8571
8745
  async getPrImpact(opts) {
8572
8746
  const { database } = await this.ensureInitialized();
8573
- const execFileAsync2 = promisify2(execFile2);
8747
+ const execFileAsync3 = promisify3(execFile3);
8574
8748
  const changedFilesResult = await getChangedFiles({
8575
8749
  pr: opts.pr,
8576
8750
  branch: opts.branch,
@@ -8592,7 +8766,7 @@ var Indexer = class {
8592
8766
  "Run index_codebase first to build the call graph and symbol index for this branch."
8593
8767
  );
8594
8768
  }
8595
- const absoluteChangedFiles = changedFiles.map((f) => path11.resolve(this.projectRoot, f));
8769
+ const absoluteChangedFiles = changedFiles.map((f) => path12.resolve(this.projectRoot, f));
8596
8770
  const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
8597
8771
  const directIds = directSymbols.map((s) => s.id);
8598
8772
  const direction = opts.direction ?? "both";
@@ -8654,7 +8828,7 @@ var Indexer = class {
8654
8828
  if (opts.checkConflicts) {
8655
8829
  conflictingPRs = [];
8656
8830
  try {
8657
- const { stdout } = await execFileAsync2(
8831
+ const { stdout } = await execFileAsync3(
8658
8832
  "gh",
8659
8833
  ["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "10000"],
8660
8834
  { cwd: this.projectRoot, timeout: 3e4 }
@@ -8675,7 +8849,7 @@ var Indexer = class {
8675
8849
  projectRoot: this.projectRoot,
8676
8850
  baseBranch: this.baseBranch
8677
8851
  });
8678
- const otherAbsolute = otherChanged.files.map((f) => path11.resolve(this.projectRoot, f));
8852
+ const otherAbsolute = otherChanged.files.map((f) => path12.resolve(this.projectRoot, f));
8679
8853
  const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
8680
8854
  const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
8681
8855
  const otherLabels = /* @__PURE__ */ new Set();
@@ -8738,12 +8912,12 @@ var Indexer = class {
8738
8912
  if (meta.filePath) filePaths.add(meta.filePath);
8739
8913
  }
8740
8914
  const directory = options?.directory?.replace(/\/$/, "");
8741
- const absoluteDirectoryFilter = directory ? path11.resolve(this.projectRoot, directory) : void 0;
8915
+ const absoluteDirectoryFilter = directory ? path12.resolve(this.projectRoot, directory) : void 0;
8742
8916
  for (const filePath of filePaths) {
8743
8917
  if (directory) {
8744
- const absoluteFilePath = path11.resolve(filePath);
8918
+ const absoluteFilePath = path12.resolve(filePath);
8745
8919
  const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
8746
- const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path11.sep));
8920
+ const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path12.sep));
8747
8921
  if (!matchesRelative && !matchesProjectRelative) {
8748
8922
  continue;
8749
8923
  }
@@ -8775,43 +8949,43 @@ var Indexer = class {
8775
8949
  };
8776
8950
 
8777
8951
  // src/tools/knowledge-base-paths.ts
8778
- import * as path12 from "path";
8952
+ import * as path13 from "path";
8779
8953
  function resolveConfigPathValue(value, baseDir) {
8780
8954
  const trimmed = value.trim();
8781
8955
  if (!trimmed) {
8782
8956
  return trimmed;
8783
8957
  }
8784
- const absolutePath = path12.isAbsolute(trimmed) ? trimmed : path12.resolve(baseDir, trimmed);
8785
- return path12.normalize(absolutePath);
8958
+ const absolutePath = path13.isAbsolute(trimmed) ? trimmed : path13.resolve(baseDir, trimmed);
8959
+ return path13.normalize(absolutePath);
8786
8960
  }
8787
8961
  function serializeConfigPathValue(value, baseDir) {
8788
8962
  const trimmed = value.trim();
8789
8963
  if (!trimmed) {
8790
8964
  return trimmed;
8791
8965
  }
8792
- if (!path12.isAbsolute(trimmed)) {
8793
- return normalizePathSeparators(path12.normalize(trimmed));
8966
+ if (!path13.isAbsolute(trimmed)) {
8967
+ return normalizePathSeparators(path13.normalize(trimmed));
8794
8968
  }
8795
- const relativePath = path12.relative(baseDir, trimmed);
8796
- if (!relativePath || !relativePath.startsWith("..") && !path12.isAbsolute(relativePath)) {
8797
- return normalizePathSeparators(path12.normalize(relativePath || "."));
8969
+ const relativePath = path13.relative(baseDir, trimmed);
8970
+ if (!relativePath || !relativePath.startsWith("..") && !path13.isAbsolute(relativePath)) {
8971
+ return normalizePathSeparators(path13.normalize(relativePath || "."));
8798
8972
  }
8799
- return path12.normalize(trimmed);
8973
+ return path13.normalize(trimmed);
8800
8974
  }
8801
8975
  function resolveKnowledgeBasePath(value, projectRoot) {
8802
- return path12.isAbsolute(value) ? value : path12.resolve(projectRoot, value);
8976
+ return path13.isAbsolute(value) ? value : path13.resolve(projectRoot, value);
8803
8977
  }
8804
8978
  function normalizeKnowledgeBasePath2(value, projectRoot) {
8805
- return path12.normalize(resolveKnowledgeBasePath(value, projectRoot));
8979
+ return path13.normalize(resolveKnowledgeBasePath(value, projectRoot));
8806
8980
  }
8807
8981
  function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
8808
- const normalizedInput = path12.normalize(inputPath);
8982
+ const normalizedInput = path13.normalize(inputPath);
8809
8983
  return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
8810
8984
  }
8811
8985
  function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
8812
- const normalizedInput = path12.normalize(inputPath);
8986
+ const normalizedInput = path13.normalize(inputPath);
8813
8987
  return knowledgeBases.findIndex(
8814
- (kb) => path12.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
8988
+ (kb) => path13.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
8815
8989
  );
8816
8990
  }
8817
8991
 
@@ -8963,7 +9137,7 @@ function formatCodebasePeek(results) {
8963
9137
  const formatted = results.map((r, idx) => {
8964
9138
  const location = `${r.filePath}:${r.startLine}-${r.endLine}`;
8965
9139
  const name = r.name ? `"${r.name}"` : "(anonymous)";
8966
- return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})`;
9140
+ return `[${idx + 1}] ${r.chunkType} ${name} at ${location} (score: ${r.score.toFixed(2)})${formatBlame(r)}`;
8967
9141
  });
8968
9142
  return formatted.join("\n");
8969
9143
  }
@@ -8995,16 +9169,57 @@ function formatHealthCheck(result) {
8995
9169
  }
8996
9170
  return lines.join("\n");
8997
9171
  }
9172
+ function formatCallGraphCallers(name, callers, relationshipType) {
9173
+ if (callers.length === 0) {
9174
+ return `No callers found for "${name}"${relationshipType ? ` with type ${relationshipType}` : ""}. It may not be called by any tracked function, or the index needs updating.`;
9175
+ }
9176
+ const formatted = callers.map((edge, index) => {
9177
+ const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
9178
+ 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]"}`;
9179
+ });
9180
+ return `"${name}" is called by ${callers.length} function(s):
9181
+
9182
+ ${formatted.join("\n")}`;
9183
+ }
9184
+ function formatCallGraphCallees(symbolId, callees, relationshipType) {
9185
+ if (callees.length === 0) {
9186
+ return `No callees found for symbol ${symbolId}${relationshipType ? ` with type ${relationshipType}` : ""}. The function may not call any other tracked functions.`;
9187
+ }
9188
+ return callees.map((edge, index) => {
9189
+ const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
9190
+ return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
9191
+ }).join("\n");
9192
+ }
9193
+ function formatCallGraphPath(from, to, path23) {
9194
+ if (path23.length === 0) {
9195
+ return `No path found between "${from}" and "${to}". They may be in disconnected components, or the call graph index needs updating.`;
9196
+ }
9197
+ const formatted = path23.map((hop, index) => {
9198
+ const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
9199
+ const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
9200
+ return `${prefix} ${hop.symbolName}${location}`;
9201
+ });
9202
+ return `Path (${path23.length} hops):
9203
+ ${formatted.join("\n")}`;
9204
+ }
8998
9205
  function formatResultHeader(result, index) {
8999
9206
  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}`;
9000
9207
  }
9208
+ function formatBlame(result) {
9209
+ if (!result.blame) {
9210
+ return "";
9211
+ }
9212
+ const date = new Date(result.blame.committedAt * 1e3).toISOString().slice(0, 10);
9213
+ return `
9214
+ ${result.blame.sha.slice(0, 7)} | ${result.blame.author} | ${date} | ${result.blame.summary}`;
9215
+ }
9001
9216
  function formatDefinitionLookup(results, query) {
9002
9217
  if (results.length === 0) {
9003
9218
  return `No definition found for "${query}". Try codebase_search for broader discovery, or verify the symbol name.`;
9004
9219
  }
9005
9220
  const formatted = results.map((r, idx) => {
9006
9221
  const header = formatResultHeader(r, idx);
9007
- return `${header} (score: ${r.score.toFixed(2)})
9222
+ return `${header} (score: ${r.score.toFixed(2)})${formatBlame(r)}
9008
9223
  \`\`\`
9009
9224
  ${truncateContent(r.content)}
9010
9225
  \`\`\``;
@@ -9015,7 +9230,7 @@ function formatSearchResults(results, scoreFormat = "similarity") {
9015
9230
  const formatted = results.map((r, idx) => {
9016
9231
  const header = formatResultHeader(r, idx);
9017
9232
  const scoreLabel = scoreFormat === "similarity" ? `(similarity: ${(r.score * 100).toFixed(1)}%)` : `(score: ${r.score.toFixed(2)})`;
9018
- return `${header} ${scoreLabel}
9233
+ return `${header} ${scoreLabel}${formatBlame(r)}
9019
9234
  \`\`\`
9020
9235
  ${truncateContent(r.content)}
9021
9236
  \`\`\``;
@@ -9025,7 +9240,7 @@ ${truncateContent(r.content)}
9025
9240
 
9026
9241
  // src/tools/config-state.ts
9027
9242
  import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
9028
- import * as path13 from "path";
9243
+ import * as path14 from "path";
9029
9244
  function normalizeKnowledgeBasePaths(config, projectRoot) {
9030
9245
  const normalized = { ...config };
9031
9246
  if (Array.isArray(normalized.knowledgeBases)) {
@@ -9052,8 +9267,8 @@ function loadEditableConfig(projectRoot, host = "opencode") {
9052
9267
  }
9053
9268
  function saveConfig(projectRoot, config, host = "opencode") {
9054
9269
  const configPath = getConfigPath(projectRoot, host);
9055
- const configDir = path13.dirname(configPath);
9056
- const configBaseDir = path13.dirname(configDir);
9270
+ const configDir = path14.dirname(configPath);
9271
+ const configBaseDir = path14.dirname(configDir);
9057
9272
  if (!existsSync8(configDir)) {
9058
9273
  mkdirSync3(configDir, { recursive: true });
9059
9274
  }
@@ -9114,7 +9329,7 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
9114
9329
  }
9115
9330
  function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
9116
9331
  const root = getProjectRoot(projectRoot, host);
9117
- const localIndexPath = path14.join(root, getHostProjectIndexRelativePath(host));
9332
+ const localIndexPath = path15.join(root, getHostProjectIndexRelativePath(host));
9118
9333
  if (existsSync9(localIndexPath)) {
9119
9334
  return false;
9120
9335
  }
@@ -9129,7 +9344,10 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
9129
9344
  chunkType: options.chunkType,
9130
9345
  contextLines: options.contextLines,
9131
9346
  metadataOnly: options.metadataOnly,
9132
- definitionIntent: options.definitionIntent
9347
+ definitionIntent: options.definitionIntent,
9348
+ blameAuthor: options.blameAuthor,
9349
+ blameSha: options.blameSha,
9350
+ blameSince: options.blameSince
9133
9351
  });
9134
9352
  }
9135
9353
  async function findSimilarCode(projectRoot, host, code, options = {}) {
@@ -9261,8 +9479,8 @@ async function getIndexLogs(projectRoot, host, args) {
9261
9479
  function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
9262
9480
  const root = getProjectRoot(projectRoot, host);
9263
9481
  const inputPath = knowledgeBasePath.trim();
9264
- const normalizedPath = path14.resolve(
9265
- path14.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
9482
+ const normalizedPath = path15.resolve(
9483
+ path15.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
9266
9484
  );
9267
9485
  if (!existsSync9(normalizedPath)) {
9268
9486
  return `Error: Directory does not exist: ${normalizedPath}`;
@@ -9298,7 +9516,7 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
9298
9516
  }
9299
9517
  }
9300
9518
  for (const dotDir of sensitiveDotDirs) {
9301
- const sensitiveDir = path14.join(homeDir, dotDir);
9519
+ const sensitiveDir = path15.join(homeDir, dotDir);
9302
9520
  if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
9303
9521
  return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
9304
9522
  }
@@ -9361,7 +9579,7 @@ function listKnowledgeBases(projectRoot, host) {
9361
9579
  }
9362
9580
  result += "\n";
9363
9581
  }
9364
- const hasHostConfig = existsSync9(path14.join(root, getHostProjectConfigRelativePath(host)));
9582
+ const hasHostConfig = existsSync9(path15.join(root, getHostProjectConfigRelativePath(host)));
9365
9583
  if (hasHostConfig) {
9366
9584
  result += `
9367
9585
  Config sources: 1 file(s).`;
@@ -9484,7 +9702,7 @@ var ReaddirpStream = class extends Readable {
9484
9702
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
9485
9703
  const statMethod = opts.lstat ? lstat : stat;
9486
9704
  if (wantBigintFsStats) {
9487
- this._stat = (path22) => statMethod(path22, { bigint: true });
9705
+ this._stat = (path23) => statMethod(path23, { bigint: true });
9488
9706
  } else {
9489
9707
  this._stat = statMethod;
9490
9708
  }
@@ -9509,8 +9727,8 @@ var ReaddirpStream = class extends Readable {
9509
9727
  const par = this.parent;
9510
9728
  const fil = par && par.files;
9511
9729
  if (fil && fil.length > 0) {
9512
- const { path: path22, depth } = par;
9513
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path22));
9730
+ const { path: path23, depth } = par;
9731
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path23));
9514
9732
  const awaited = await Promise.all(slice);
9515
9733
  for (const entry of awaited) {
9516
9734
  if (!entry)
@@ -9550,20 +9768,20 @@ var ReaddirpStream = class extends Readable {
9550
9768
  this.reading = false;
9551
9769
  }
9552
9770
  }
9553
- async _exploreDir(path22, depth) {
9771
+ async _exploreDir(path23, depth) {
9554
9772
  let files;
9555
9773
  try {
9556
- files = await readdir(path22, this._rdOptions);
9774
+ files = await readdir(path23, this._rdOptions);
9557
9775
  } catch (error) {
9558
9776
  this._onError(error);
9559
9777
  }
9560
- return { files, depth, path: path22 };
9778
+ return { files, depth, path: path23 };
9561
9779
  }
9562
- async _formatEntry(dirent, path22) {
9780
+ async _formatEntry(dirent, path23) {
9563
9781
  let entry;
9564
9782
  const basename5 = this._isDirent ? dirent.name : dirent;
9565
9783
  try {
9566
- const fullPath = presolve(pjoin(path22, basename5));
9784
+ const fullPath = presolve(pjoin(path23, basename5));
9567
9785
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
9568
9786
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
9569
9787
  } catch (err) {
@@ -9963,16 +10181,16 @@ var delFromSet = (main, prop, item) => {
9963
10181
  };
9964
10182
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
9965
10183
  var FsWatchInstances = /* @__PURE__ */ new Map();
9966
- function createFsWatchInstance(path22, options, listener, errHandler, emitRaw) {
10184
+ function createFsWatchInstance(path23, options, listener, errHandler, emitRaw) {
9967
10185
  const handleEvent = (rawEvent, evPath) => {
9968
- listener(path22);
9969
- emitRaw(rawEvent, evPath, { watchedPath: path22 });
9970
- if (evPath && path22 !== evPath) {
9971
- fsWatchBroadcast(sp.resolve(path22, evPath), KEY_LISTENERS, sp.join(path22, evPath));
10186
+ listener(path23);
10187
+ emitRaw(rawEvent, evPath, { watchedPath: path23 });
10188
+ if (evPath && path23 !== evPath) {
10189
+ fsWatchBroadcast(sp.resolve(path23, evPath), KEY_LISTENERS, sp.join(path23, evPath));
9972
10190
  }
9973
10191
  };
9974
10192
  try {
9975
- return fs_watch(path22, {
10193
+ return fs_watch(path23, {
9976
10194
  persistent: options.persistent
9977
10195
  }, handleEvent);
9978
10196
  } catch (error) {
@@ -9988,12 +10206,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
9988
10206
  listener(val1, val2, val3);
9989
10207
  });
9990
10208
  };
9991
- var setFsWatchListener = (path22, fullPath, options, handlers) => {
10209
+ var setFsWatchListener = (path23, fullPath, options, handlers) => {
9992
10210
  const { listener, errHandler, rawEmitter } = handlers;
9993
10211
  let cont = FsWatchInstances.get(fullPath);
9994
10212
  let watcher;
9995
10213
  if (!options.persistent) {
9996
- watcher = createFsWatchInstance(path22, options, listener, errHandler, rawEmitter);
10214
+ watcher = createFsWatchInstance(path23, options, listener, errHandler, rawEmitter);
9997
10215
  if (!watcher)
9998
10216
  return;
9999
10217
  return watcher.close.bind(watcher);
@@ -10004,7 +10222,7 @@ var setFsWatchListener = (path22, fullPath, options, handlers) => {
10004
10222
  addAndConvert(cont, KEY_RAW, rawEmitter);
10005
10223
  } else {
10006
10224
  watcher = createFsWatchInstance(
10007
- path22,
10225
+ path23,
10008
10226
  options,
10009
10227
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
10010
10228
  errHandler,
@@ -10019,7 +10237,7 @@ var setFsWatchListener = (path22, fullPath, options, handlers) => {
10019
10237
  cont.watcherUnusable = true;
10020
10238
  if (isWindows && error.code === "EPERM") {
10021
10239
  try {
10022
- const fd = await open(path22, "r");
10240
+ const fd = await open(path23, "r");
10023
10241
  await fd.close();
10024
10242
  broadcastErr(error);
10025
10243
  } catch (err) {
@@ -10050,7 +10268,7 @@ var setFsWatchListener = (path22, fullPath, options, handlers) => {
10050
10268
  };
10051
10269
  };
10052
10270
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
10053
- var setFsWatchFileListener = (path22, fullPath, options, handlers) => {
10271
+ var setFsWatchFileListener = (path23, fullPath, options, handlers) => {
10054
10272
  const { listener, rawEmitter } = handlers;
10055
10273
  let cont = FsWatchFileInstances.get(fullPath);
10056
10274
  const copts = cont && cont.options;
@@ -10072,7 +10290,7 @@ var setFsWatchFileListener = (path22, fullPath, options, handlers) => {
10072
10290
  });
10073
10291
  const currmtime = curr.mtimeMs;
10074
10292
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
10075
- foreach(cont.listeners, (listener2) => listener2(path22, curr));
10293
+ foreach(cont.listeners, (listener2) => listener2(path23, curr));
10076
10294
  }
10077
10295
  })
10078
10296
  };
@@ -10102,13 +10320,13 @@ var NodeFsHandler = class {
10102
10320
  * @param listener on fs change
10103
10321
  * @returns closer for the watcher instance
10104
10322
  */
10105
- _watchWithNodeFs(path22, listener) {
10323
+ _watchWithNodeFs(path23, listener) {
10106
10324
  const opts = this.fsw.options;
10107
- const directory = sp.dirname(path22);
10108
- const basename5 = sp.basename(path22);
10325
+ const directory = sp.dirname(path23);
10326
+ const basename5 = sp.basename(path23);
10109
10327
  const parent = this.fsw._getWatchedDir(directory);
10110
10328
  parent.add(basename5);
10111
- const absolutePath = sp.resolve(path22);
10329
+ const absolutePath = sp.resolve(path23);
10112
10330
  const options = {
10113
10331
  persistent: opts.persistent
10114
10332
  };
@@ -10118,12 +10336,12 @@ var NodeFsHandler = class {
10118
10336
  if (opts.usePolling) {
10119
10337
  const enableBin = opts.interval !== opts.binaryInterval;
10120
10338
  options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
10121
- closer = setFsWatchFileListener(path22, absolutePath, options, {
10339
+ closer = setFsWatchFileListener(path23, absolutePath, options, {
10122
10340
  listener,
10123
10341
  rawEmitter: this.fsw._emitRaw
10124
10342
  });
10125
10343
  } else {
10126
- closer = setFsWatchListener(path22, absolutePath, options, {
10344
+ closer = setFsWatchListener(path23, absolutePath, options, {
10127
10345
  listener,
10128
10346
  errHandler: this._boundHandleError,
10129
10347
  rawEmitter: this.fsw._emitRaw
@@ -10145,7 +10363,7 @@ var NodeFsHandler = class {
10145
10363
  let prevStats = stats;
10146
10364
  if (parent.has(basename5))
10147
10365
  return;
10148
- const listener = async (path22, newStats) => {
10366
+ const listener = async (path23, newStats) => {
10149
10367
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
10150
10368
  return;
10151
10369
  if (!newStats || newStats.mtimeMs === 0) {
@@ -10159,11 +10377,11 @@ var NodeFsHandler = class {
10159
10377
  this.fsw._emit(EV.CHANGE, file, newStats2);
10160
10378
  }
10161
10379
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
10162
- this.fsw._closeFile(path22);
10380
+ this.fsw._closeFile(path23);
10163
10381
  prevStats = newStats2;
10164
10382
  const closer2 = this._watchWithNodeFs(file, listener);
10165
10383
  if (closer2)
10166
- this.fsw._addPathCloser(path22, closer2);
10384
+ this.fsw._addPathCloser(path23, closer2);
10167
10385
  } else {
10168
10386
  prevStats = newStats2;
10169
10387
  }
@@ -10195,7 +10413,7 @@ var NodeFsHandler = class {
10195
10413
  * @param item basename of this item
10196
10414
  * @returns true if no more processing is needed for this entry.
10197
10415
  */
10198
- async _handleSymlink(entry, directory, path22, item) {
10416
+ async _handleSymlink(entry, directory, path23, item) {
10199
10417
  if (this.fsw.closed) {
10200
10418
  return;
10201
10419
  }
@@ -10205,7 +10423,7 @@ var NodeFsHandler = class {
10205
10423
  this.fsw._incrReadyCount();
10206
10424
  let linkPath;
10207
10425
  try {
10208
- linkPath = await fsrealpath(path22);
10426
+ linkPath = await fsrealpath(path23);
10209
10427
  } catch (e) {
10210
10428
  this.fsw._emitReady();
10211
10429
  return true;
@@ -10215,12 +10433,12 @@ var NodeFsHandler = class {
10215
10433
  if (dir.has(item)) {
10216
10434
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
10217
10435
  this.fsw._symlinkPaths.set(full, linkPath);
10218
- this.fsw._emit(EV.CHANGE, path22, entry.stats);
10436
+ this.fsw._emit(EV.CHANGE, path23, entry.stats);
10219
10437
  }
10220
10438
  } else {
10221
10439
  dir.add(item);
10222
10440
  this.fsw._symlinkPaths.set(full, linkPath);
10223
- this.fsw._emit(EV.ADD, path22, entry.stats);
10441
+ this.fsw._emit(EV.ADD, path23, entry.stats);
10224
10442
  }
10225
10443
  this.fsw._emitReady();
10226
10444
  return true;
@@ -10250,9 +10468,9 @@ var NodeFsHandler = class {
10250
10468
  return;
10251
10469
  }
10252
10470
  const item = entry.path;
10253
- let path22 = sp.join(directory, item);
10471
+ let path23 = sp.join(directory, item);
10254
10472
  current.add(item);
10255
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path22, item)) {
10473
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path23, item)) {
10256
10474
  return;
10257
10475
  }
10258
10476
  if (this.fsw.closed) {
@@ -10261,8 +10479,8 @@ var NodeFsHandler = class {
10261
10479
  }
10262
10480
  if (item === target || !target && !previous.has(item)) {
10263
10481
  this.fsw._incrReadyCount();
10264
- path22 = sp.join(dir, sp.relative(dir, path22));
10265
- this._addToNodeFs(path22, initialAdd, wh, depth + 1);
10482
+ path23 = sp.join(dir, sp.relative(dir, path23));
10483
+ this._addToNodeFs(path23, initialAdd, wh, depth + 1);
10266
10484
  }
10267
10485
  }).on(EV.ERROR, this._boundHandleError);
10268
10486
  return new Promise((resolve13, reject) => {
@@ -10331,13 +10549,13 @@ var NodeFsHandler = class {
10331
10549
  * @param depth Child path actually targeted for watch
10332
10550
  * @param target Child path actually targeted for watch
10333
10551
  */
10334
- async _addToNodeFs(path22, initialAdd, priorWh, depth, target) {
10552
+ async _addToNodeFs(path23, initialAdd, priorWh, depth, target) {
10335
10553
  const ready = this.fsw._emitReady;
10336
- if (this.fsw._isIgnored(path22) || this.fsw.closed) {
10554
+ if (this.fsw._isIgnored(path23) || this.fsw.closed) {
10337
10555
  ready();
10338
10556
  return false;
10339
10557
  }
10340
- const wh = this.fsw._getWatchHelpers(path22);
10558
+ const wh = this.fsw._getWatchHelpers(path23);
10341
10559
  if (priorWh) {
10342
10560
  wh.filterPath = (entry) => priorWh.filterPath(entry);
10343
10561
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -10353,8 +10571,8 @@ var NodeFsHandler = class {
10353
10571
  const follow = this.fsw.options.followSymlinks;
10354
10572
  let closer;
10355
10573
  if (stats.isDirectory()) {
10356
- const absPath = sp.resolve(path22);
10357
- const targetPath = follow ? await fsrealpath(path22) : path22;
10574
+ const absPath = sp.resolve(path23);
10575
+ const targetPath = follow ? await fsrealpath(path23) : path23;
10358
10576
  if (this.fsw.closed)
10359
10577
  return;
10360
10578
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -10364,29 +10582,29 @@ var NodeFsHandler = class {
10364
10582
  this.fsw._symlinkPaths.set(absPath, targetPath);
10365
10583
  }
10366
10584
  } else if (stats.isSymbolicLink()) {
10367
- const targetPath = follow ? await fsrealpath(path22) : path22;
10585
+ const targetPath = follow ? await fsrealpath(path23) : path23;
10368
10586
  if (this.fsw.closed)
10369
10587
  return;
10370
10588
  const parent = sp.dirname(wh.watchPath);
10371
10589
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
10372
10590
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
10373
- closer = await this._handleDir(parent, stats, initialAdd, depth, path22, wh, targetPath);
10591
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path23, wh, targetPath);
10374
10592
  if (this.fsw.closed)
10375
10593
  return;
10376
10594
  if (targetPath !== void 0) {
10377
- this.fsw._symlinkPaths.set(sp.resolve(path22), targetPath);
10595
+ this.fsw._symlinkPaths.set(sp.resolve(path23), targetPath);
10378
10596
  }
10379
10597
  } else {
10380
10598
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
10381
10599
  }
10382
10600
  ready();
10383
10601
  if (closer)
10384
- this.fsw._addPathCloser(path22, closer);
10602
+ this.fsw._addPathCloser(path23, closer);
10385
10603
  return false;
10386
10604
  } catch (error) {
10387
10605
  if (this.fsw._handleError(error)) {
10388
10606
  ready();
10389
- return path22;
10607
+ return path23;
10390
10608
  }
10391
10609
  }
10392
10610
  }
@@ -10418,35 +10636,35 @@ function createPattern(matcher) {
10418
10636
  if (matcher.path === string)
10419
10637
  return true;
10420
10638
  if (matcher.recursive) {
10421
- const relative10 = sp2.relative(matcher.path, string);
10422
- if (!relative10) {
10639
+ const relative11 = sp2.relative(matcher.path, string);
10640
+ if (!relative11) {
10423
10641
  return false;
10424
10642
  }
10425
- return !relative10.startsWith("..") && !sp2.isAbsolute(relative10);
10643
+ return !relative11.startsWith("..") && !sp2.isAbsolute(relative11);
10426
10644
  }
10427
10645
  return false;
10428
10646
  };
10429
10647
  }
10430
10648
  return () => false;
10431
10649
  }
10432
- function normalizePath(path22) {
10433
- if (typeof path22 !== "string")
10650
+ function normalizePath(path23) {
10651
+ if (typeof path23 !== "string")
10434
10652
  throw new Error("string expected");
10435
- path22 = sp2.normalize(path22);
10436
- path22 = path22.replace(/\\/g, "/");
10653
+ path23 = sp2.normalize(path23);
10654
+ path23 = path23.replace(/\\/g, "/");
10437
10655
  let prepend = false;
10438
- if (path22.startsWith("//"))
10656
+ if (path23.startsWith("//"))
10439
10657
  prepend = true;
10440
- path22 = path22.replace(DOUBLE_SLASH_RE, "/");
10658
+ path23 = path23.replace(DOUBLE_SLASH_RE, "/");
10441
10659
  if (prepend)
10442
- path22 = "/" + path22;
10443
- return path22;
10660
+ path23 = "/" + path23;
10661
+ return path23;
10444
10662
  }
10445
10663
  function matchPatterns(patterns, testString, stats) {
10446
- const path22 = normalizePath(testString);
10664
+ const path23 = normalizePath(testString);
10447
10665
  for (let index = 0; index < patterns.length; index++) {
10448
10666
  const pattern = patterns[index];
10449
- if (pattern(path22, stats)) {
10667
+ if (pattern(path23, stats)) {
10450
10668
  return true;
10451
10669
  }
10452
10670
  }
@@ -10484,19 +10702,19 @@ var toUnix = (string) => {
10484
10702
  }
10485
10703
  return str;
10486
10704
  };
10487
- var normalizePathToUnix = (path22) => toUnix(sp2.normalize(toUnix(path22)));
10488
- var normalizeIgnored = (cwd = "") => (path22) => {
10489
- if (typeof path22 === "string") {
10490
- return normalizePathToUnix(sp2.isAbsolute(path22) ? path22 : sp2.join(cwd, path22));
10705
+ var normalizePathToUnix = (path23) => toUnix(sp2.normalize(toUnix(path23)));
10706
+ var normalizeIgnored = (cwd = "") => (path23) => {
10707
+ if (typeof path23 === "string") {
10708
+ return normalizePathToUnix(sp2.isAbsolute(path23) ? path23 : sp2.join(cwd, path23));
10491
10709
  } else {
10492
- return path22;
10710
+ return path23;
10493
10711
  }
10494
10712
  };
10495
- var getAbsolutePath = (path22, cwd) => {
10496
- if (sp2.isAbsolute(path22)) {
10497
- return path22;
10713
+ var getAbsolutePath = (path23, cwd) => {
10714
+ if (sp2.isAbsolute(path23)) {
10715
+ return path23;
10498
10716
  }
10499
- return sp2.join(cwd, path22);
10717
+ return sp2.join(cwd, path23);
10500
10718
  };
10501
10719
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
10502
10720
  var DirEntry = class {
@@ -10561,10 +10779,10 @@ var WatchHelper = class {
10561
10779
  dirParts;
10562
10780
  followSymlinks;
10563
10781
  statMethod;
10564
- constructor(path22, follow, fsw) {
10782
+ constructor(path23, follow, fsw) {
10565
10783
  this.fsw = fsw;
10566
- const watchPath = path22;
10567
- this.path = path22 = path22.replace(REPLACER_RE, "");
10784
+ const watchPath = path23;
10785
+ this.path = path23 = path23.replace(REPLACER_RE, "");
10568
10786
  this.watchPath = watchPath;
10569
10787
  this.fullWatchPath = sp2.resolve(watchPath);
10570
10788
  this.dirParts = [];
@@ -10704,20 +10922,20 @@ var FSWatcher = class extends EventEmitter2 {
10704
10922
  this._closePromise = void 0;
10705
10923
  let paths = unifyPaths(paths_);
10706
10924
  if (cwd) {
10707
- paths = paths.map((path22) => {
10708
- const absPath = getAbsolutePath(path22, cwd);
10925
+ paths = paths.map((path23) => {
10926
+ const absPath = getAbsolutePath(path23, cwd);
10709
10927
  return absPath;
10710
10928
  });
10711
10929
  }
10712
- paths.forEach((path22) => {
10713
- this._removeIgnoredPath(path22);
10930
+ paths.forEach((path23) => {
10931
+ this._removeIgnoredPath(path23);
10714
10932
  });
10715
10933
  this._userIgnored = void 0;
10716
10934
  if (!this._readyCount)
10717
10935
  this._readyCount = 0;
10718
10936
  this._readyCount += paths.length;
10719
- Promise.all(paths.map(async (path22) => {
10720
- const res = await this._nodeFsHandler._addToNodeFs(path22, !_internal, void 0, 0, _origAdd);
10937
+ Promise.all(paths.map(async (path23) => {
10938
+ const res = await this._nodeFsHandler._addToNodeFs(path23, !_internal, void 0, 0, _origAdd);
10721
10939
  if (res)
10722
10940
  this._emitReady();
10723
10941
  return res;
@@ -10739,17 +10957,17 @@ var FSWatcher = class extends EventEmitter2 {
10739
10957
  return this;
10740
10958
  const paths = unifyPaths(paths_);
10741
10959
  const { cwd } = this.options;
10742
- paths.forEach((path22) => {
10743
- if (!sp2.isAbsolute(path22) && !this._closers.has(path22)) {
10960
+ paths.forEach((path23) => {
10961
+ if (!sp2.isAbsolute(path23) && !this._closers.has(path23)) {
10744
10962
  if (cwd)
10745
- path22 = sp2.join(cwd, path22);
10746
- path22 = sp2.resolve(path22);
10963
+ path23 = sp2.join(cwd, path23);
10964
+ path23 = sp2.resolve(path23);
10747
10965
  }
10748
- this._closePath(path22);
10749
- this._addIgnoredPath(path22);
10750
- if (this._watched.has(path22)) {
10966
+ this._closePath(path23);
10967
+ this._addIgnoredPath(path23);
10968
+ if (this._watched.has(path23)) {
10751
10969
  this._addIgnoredPath({
10752
- path: path22,
10970
+ path: path23,
10753
10971
  recursive: true
10754
10972
  });
10755
10973
  }
@@ -10813,38 +11031,38 @@ var FSWatcher = class extends EventEmitter2 {
10813
11031
  * @param stats arguments to be passed with event
10814
11032
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
10815
11033
  */
10816
- async _emit(event, path22, stats) {
11034
+ async _emit(event, path23, stats) {
10817
11035
  if (this.closed)
10818
11036
  return;
10819
11037
  const opts = this.options;
10820
11038
  if (isWindows)
10821
- path22 = sp2.normalize(path22);
11039
+ path23 = sp2.normalize(path23);
10822
11040
  if (opts.cwd)
10823
- path22 = sp2.relative(opts.cwd, path22);
10824
- const args = [path22];
11041
+ path23 = sp2.relative(opts.cwd, path23);
11042
+ const args = [path23];
10825
11043
  if (stats != null)
10826
11044
  args.push(stats);
10827
11045
  const awf = opts.awaitWriteFinish;
10828
11046
  let pw;
10829
- if (awf && (pw = this._pendingWrites.get(path22))) {
11047
+ if (awf && (pw = this._pendingWrites.get(path23))) {
10830
11048
  pw.lastChange = /* @__PURE__ */ new Date();
10831
11049
  return this;
10832
11050
  }
10833
11051
  if (opts.atomic) {
10834
11052
  if (event === EVENTS.UNLINK) {
10835
- this._pendingUnlinks.set(path22, [event, ...args]);
11053
+ this._pendingUnlinks.set(path23, [event, ...args]);
10836
11054
  setTimeout(() => {
10837
- this._pendingUnlinks.forEach((entry, path23) => {
11055
+ this._pendingUnlinks.forEach((entry, path24) => {
10838
11056
  this.emit(...entry);
10839
11057
  this.emit(EVENTS.ALL, ...entry);
10840
- this._pendingUnlinks.delete(path23);
11058
+ this._pendingUnlinks.delete(path24);
10841
11059
  });
10842
11060
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
10843
11061
  return this;
10844
11062
  }
10845
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path22)) {
11063
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path23)) {
10846
11064
  event = EVENTS.CHANGE;
10847
- this._pendingUnlinks.delete(path22);
11065
+ this._pendingUnlinks.delete(path23);
10848
11066
  }
10849
11067
  }
10850
11068
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -10862,16 +11080,16 @@ var FSWatcher = class extends EventEmitter2 {
10862
11080
  this.emitWithAll(event, args);
10863
11081
  }
10864
11082
  };
10865
- this._awaitWriteFinish(path22, awf.stabilityThreshold, event, awfEmit);
11083
+ this._awaitWriteFinish(path23, awf.stabilityThreshold, event, awfEmit);
10866
11084
  return this;
10867
11085
  }
10868
11086
  if (event === EVENTS.CHANGE) {
10869
- const isThrottled = !this._throttle(EVENTS.CHANGE, path22, 50);
11087
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path23, 50);
10870
11088
  if (isThrottled)
10871
11089
  return this;
10872
11090
  }
10873
11091
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
10874
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path22) : path22;
11092
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path23) : path23;
10875
11093
  let stats2;
10876
11094
  try {
10877
11095
  stats2 = await stat3(fullPath);
@@ -10902,23 +11120,23 @@ var FSWatcher = class extends EventEmitter2 {
10902
11120
  * @param timeout duration of time to suppress duplicate actions
10903
11121
  * @returns tracking object or false if action should be suppressed
10904
11122
  */
10905
- _throttle(actionType, path22, timeout) {
11123
+ _throttle(actionType, path23, timeout) {
10906
11124
  if (!this._throttled.has(actionType)) {
10907
11125
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
10908
11126
  }
10909
11127
  const action = this._throttled.get(actionType);
10910
11128
  if (!action)
10911
11129
  throw new Error("invalid throttle");
10912
- const actionPath = action.get(path22);
11130
+ const actionPath = action.get(path23);
10913
11131
  if (actionPath) {
10914
11132
  actionPath.count++;
10915
11133
  return false;
10916
11134
  }
10917
11135
  let timeoutObject;
10918
11136
  const clear = () => {
10919
- const item = action.get(path22);
11137
+ const item = action.get(path23);
10920
11138
  const count = item ? item.count : 0;
10921
- action.delete(path22);
11139
+ action.delete(path23);
10922
11140
  clearTimeout(timeoutObject);
10923
11141
  if (item)
10924
11142
  clearTimeout(item.timeoutObject);
@@ -10926,7 +11144,7 @@ var FSWatcher = class extends EventEmitter2 {
10926
11144
  };
10927
11145
  timeoutObject = setTimeout(clear, timeout);
10928
11146
  const thr = { timeoutObject, clear, count: 0 };
10929
- action.set(path22, thr);
11147
+ action.set(path23, thr);
10930
11148
  return thr;
10931
11149
  }
10932
11150
  _incrReadyCount() {
@@ -10940,44 +11158,44 @@ var FSWatcher = class extends EventEmitter2 {
10940
11158
  * @param event
10941
11159
  * @param awfEmit Callback to be called when ready for event to be emitted.
10942
11160
  */
10943
- _awaitWriteFinish(path22, threshold, event, awfEmit) {
11161
+ _awaitWriteFinish(path23, threshold, event, awfEmit) {
10944
11162
  const awf = this.options.awaitWriteFinish;
10945
11163
  if (typeof awf !== "object")
10946
11164
  return;
10947
11165
  const pollInterval = awf.pollInterval;
10948
11166
  let timeoutHandler;
10949
- let fullPath = path22;
10950
- if (this.options.cwd && !sp2.isAbsolute(path22)) {
10951
- fullPath = sp2.join(this.options.cwd, path22);
11167
+ let fullPath = path23;
11168
+ if (this.options.cwd && !sp2.isAbsolute(path23)) {
11169
+ fullPath = sp2.join(this.options.cwd, path23);
10952
11170
  }
10953
11171
  const now = /* @__PURE__ */ new Date();
10954
11172
  const writes = this._pendingWrites;
10955
11173
  function awaitWriteFinishFn(prevStat) {
10956
11174
  statcb(fullPath, (err, curStat) => {
10957
- if (err || !writes.has(path22)) {
11175
+ if (err || !writes.has(path23)) {
10958
11176
  if (err && err.code !== "ENOENT")
10959
11177
  awfEmit(err);
10960
11178
  return;
10961
11179
  }
10962
11180
  const now2 = Number(/* @__PURE__ */ new Date());
10963
11181
  if (prevStat && curStat.size !== prevStat.size) {
10964
- writes.get(path22).lastChange = now2;
11182
+ writes.get(path23).lastChange = now2;
10965
11183
  }
10966
- const pw = writes.get(path22);
11184
+ const pw = writes.get(path23);
10967
11185
  const df = now2 - pw.lastChange;
10968
11186
  if (df >= threshold) {
10969
- writes.delete(path22);
11187
+ writes.delete(path23);
10970
11188
  awfEmit(void 0, curStat);
10971
11189
  } else {
10972
11190
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
10973
11191
  }
10974
11192
  });
10975
11193
  }
10976
- if (!writes.has(path22)) {
10977
- writes.set(path22, {
11194
+ if (!writes.has(path23)) {
11195
+ writes.set(path23, {
10978
11196
  lastChange: now,
10979
11197
  cancelWait: () => {
10980
- writes.delete(path22);
11198
+ writes.delete(path23);
10981
11199
  clearTimeout(timeoutHandler);
10982
11200
  return event;
10983
11201
  }
@@ -10988,8 +11206,8 @@ var FSWatcher = class extends EventEmitter2 {
10988
11206
  /**
10989
11207
  * Determines whether user has asked to ignore this path.
10990
11208
  */
10991
- _isIgnored(path22, stats) {
10992
- if (this.options.atomic && DOT_RE.test(path22))
11209
+ _isIgnored(path23, stats) {
11210
+ if (this.options.atomic && DOT_RE.test(path23))
10993
11211
  return true;
10994
11212
  if (!this._userIgnored) {
10995
11213
  const { cwd } = this.options;
@@ -10999,17 +11217,17 @@ var FSWatcher = class extends EventEmitter2 {
10999
11217
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
11000
11218
  this._userIgnored = anymatch(list, void 0);
11001
11219
  }
11002
- return this._userIgnored(path22, stats);
11220
+ return this._userIgnored(path23, stats);
11003
11221
  }
11004
- _isntIgnored(path22, stat4) {
11005
- return !this._isIgnored(path22, stat4);
11222
+ _isntIgnored(path23, stat4) {
11223
+ return !this._isIgnored(path23, stat4);
11006
11224
  }
11007
11225
  /**
11008
11226
  * Provides a set of common helpers and properties relating to symlink handling.
11009
11227
  * @param path file or directory pattern being watched
11010
11228
  */
11011
- _getWatchHelpers(path22) {
11012
- return new WatchHelper(path22, this.options.followSymlinks, this);
11229
+ _getWatchHelpers(path23) {
11230
+ return new WatchHelper(path23, this.options.followSymlinks, this);
11013
11231
  }
11014
11232
  // Directory helpers
11015
11233
  // -----------------
@@ -11041,63 +11259,63 @@ var FSWatcher = class extends EventEmitter2 {
11041
11259
  * @param item base path of item/directory
11042
11260
  */
11043
11261
  _remove(directory, item, isDirectory) {
11044
- const path22 = sp2.join(directory, item);
11045
- const fullPath = sp2.resolve(path22);
11046
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path22) || this._watched.has(fullPath);
11047
- if (!this._throttle("remove", path22, 100))
11262
+ const path23 = sp2.join(directory, item);
11263
+ const fullPath = sp2.resolve(path23);
11264
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path23) || this._watched.has(fullPath);
11265
+ if (!this._throttle("remove", path23, 100))
11048
11266
  return;
11049
11267
  if (!isDirectory && this._watched.size === 1) {
11050
11268
  this.add(directory, item, true);
11051
11269
  }
11052
- const wp = this._getWatchedDir(path22);
11270
+ const wp = this._getWatchedDir(path23);
11053
11271
  const nestedDirectoryChildren = wp.getChildren();
11054
- nestedDirectoryChildren.forEach((nested) => this._remove(path22, nested));
11272
+ nestedDirectoryChildren.forEach((nested) => this._remove(path23, nested));
11055
11273
  const parent = this._getWatchedDir(directory);
11056
11274
  const wasTracked = parent.has(item);
11057
11275
  parent.remove(item);
11058
11276
  if (this._symlinkPaths.has(fullPath)) {
11059
11277
  this._symlinkPaths.delete(fullPath);
11060
11278
  }
11061
- let relPath = path22;
11279
+ let relPath = path23;
11062
11280
  if (this.options.cwd)
11063
- relPath = sp2.relative(this.options.cwd, path22);
11281
+ relPath = sp2.relative(this.options.cwd, path23);
11064
11282
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
11065
11283
  const event = this._pendingWrites.get(relPath).cancelWait();
11066
11284
  if (event === EVENTS.ADD)
11067
11285
  return;
11068
11286
  }
11069
- this._watched.delete(path22);
11287
+ this._watched.delete(path23);
11070
11288
  this._watched.delete(fullPath);
11071
11289
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
11072
- if (wasTracked && !this._isIgnored(path22))
11073
- this._emit(eventName, path22);
11074
- this._closePath(path22);
11290
+ if (wasTracked && !this._isIgnored(path23))
11291
+ this._emit(eventName, path23);
11292
+ this._closePath(path23);
11075
11293
  }
11076
11294
  /**
11077
11295
  * Closes all watchers for a path
11078
11296
  */
11079
- _closePath(path22) {
11080
- this._closeFile(path22);
11081
- const dir = sp2.dirname(path22);
11082
- this._getWatchedDir(dir).remove(sp2.basename(path22));
11297
+ _closePath(path23) {
11298
+ this._closeFile(path23);
11299
+ const dir = sp2.dirname(path23);
11300
+ this._getWatchedDir(dir).remove(sp2.basename(path23));
11083
11301
  }
11084
11302
  /**
11085
11303
  * Closes only file-specific watchers
11086
11304
  */
11087
- _closeFile(path22) {
11088
- const closers = this._closers.get(path22);
11305
+ _closeFile(path23) {
11306
+ const closers = this._closers.get(path23);
11089
11307
  if (!closers)
11090
11308
  return;
11091
11309
  closers.forEach((closer) => closer());
11092
- this._closers.delete(path22);
11310
+ this._closers.delete(path23);
11093
11311
  }
11094
- _addPathCloser(path22, closer) {
11312
+ _addPathCloser(path23, closer) {
11095
11313
  if (!closer)
11096
11314
  return;
11097
- let list = this._closers.get(path22);
11315
+ let list = this._closers.get(path23);
11098
11316
  if (!list) {
11099
11317
  list = [];
11100
- this._closers.set(path22, list);
11318
+ this._closers.set(path23, list);
11101
11319
  }
11102
11320
  list.push(closer);
11103
11321
  }
@@ -11127,7 +11345,7 @@ function watch(paths, options = {}) {
11127
11345
  var chokidar_default = { watch, FSWatcher };
11128
11346
 
11129
11347
  // src/watcher/file-watcher.ts
11130
- import * as path15 from "path";
11348
+ import * as path16 from "path";
11131
11349
  var FileWatcher = class {
11132
11350
  watcher = null;
11133
11351
  projectRoot;
@@ -11153,15 +11371,15 @@ var FileWatcher = class {
11153
11371
  const watchTargets = this.configPath ? [this.projectRoot, this.configPath] : this.projectRoot;
11154
11372
  this.watcher = chokidar_default.watch(watchTargets, {
11155
11373
  ignored: (filePath) => {
11156
- const relativePath = path15.relative(this.projectRoot, filePath);
11374
+ const relativePath = path16.relative(this.projectRoot, filePath);
11157
11375
  if (!relativePath) return false;
11158
11376
  if (this.isProjectConfigPathOrAncestor(relativePath)) {
11159
11377
  return false;
11160
11378
  }
11161
- if (hasFilteredPathSegment(relativePath, path15.sep)) {
11379
+ if (hasFilteredPathSegment(relativePath, path16.sep)) {
11162
11380
  return true;
11163
11381
  }
11164
- if (isRestrictedDirectory(relativePath, path15.sep)) {
11382
+ if (isRestrictedDirectory(relativePath, path16.sep)) {
11165
11383
  return true;
11166
11384
  }
11167
11385
  if (ignoreFilter.ignores(relativePath)) {
@@ -11207,24 +11425,24 @@ var FileWatcher = class {
11207
11425
  this.scheduleFlush();
11208
11426
  }
11209
11427
  isProjectConfigPath(filePath) {
11210
- const relativePath = path15.relative(this.projectRoot, filePath);
11211
- const normalizedRelativePath = path15.normalize(relativePath);
11428
+ const relativePath = path16.relative(this.projectRoot, filePath);
11429
+ const normalizedRelativePath = path16.normalize(relativePath);
11212
11430
  return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
11213
11431
  }
11214
11432
  isProjectConfigPathOrAncestor(relativePath) {
11215
- const normalizedRelativePath = path15.normalize(relativePath);
11433
+ const normalizedRelativePath = path16.normalize(relativePath);
11216
11434
  return this.getProjectConfigRelativePaths().some(
11217
- (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path15.sep}`)
11435
+ (configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path16.sep}`)
11218
11436
  );
11219
11437
  }
11220
11438
  getProjectConfigRelativePaths() {
11221
11439
  if (this.configPath) {
11222
- return [path15.normalize(path15.relative(this.projectRoot, this.configPath))];
11440
+ return [path16.normalize(path16.relative(this.projectRoot, this.configPath))];
11223
11441
  }
11224
11442
  return [
11225
11443
  resolveProjectConfigPath(this.projectRoot, this.host),
11226
11444
  resolveWritableProjectConfigPath(this.projectRoot, this.host)
11227
- ].map((configPath) => path15.normalize(path15.relative(this.projectRoot, configPath)));
11445
+ ].map((configPath) => path16.normalize(path16.relative(this.projectRoot, configPath)));
11228
11446
  }
11229
11447
  scheduleFlush() {
11230
11448
  if (this.debounceTimer) {
@@ -11239,7 +11457,7 @@ var FileWatcher = class {
11239
11457
  return;
11240
11458
  }
11241
11459
  const changes = Array.from(this.pendingChanges.entries()).map(
11242
- ([path22, type]) => ({ path: path22, type })
11460
+ ([path23, type]) => ({ path: path23, type })
11243
11461
  );
11244
11462
  this.pendingChanges.clear();
11245
11463
  try {
@@ -11266,7 +11484,7 @@ var FileWatcher = class {
11266
11484
  };
11267
11485
 
11268
11486
  // src/watcher/git-head-watcher.ts
11269
- import * as path16 from "path";
11487
+ import * as path17 from "path";
11270
11488
  var GitHeadWatcher = class {
11271
11489
  watcher = null;
11272
11490
  projectRoot;
@@ -11288,7 +11506,7 @@ var GitHeadWatcher = class {
11288
11506
  this.onBranchChange = handler;
11289
11507
  this.currentBranch = getCurrentBranch(this.projectRoot);
11290
11508
  const headPath = getHeadPath(this.projectRoot);
11291
- const refsPath = path16.join(this.projectRoot, ".git", "refs", "heads");
11509
+ const refsPath = path17.join(this.projectRoot, ".git", "refs", "heads");
11292
11510
  this.watcher = chokidar_default.watch([headPath, refsPath], {
11293
11511
  persistent: true,
11294
11512
  ignoreInitial: true,
@@ -11521,11 +11739,11 @@ var pr_impact = tool({
11521
11739
  // src/tools/index.ts
11522
11740
  import { writeFileSync as writeFileSync4 } from "fs";
11523
11741
  import * as os4 from "os";
11524
- import * as path19 from "path";
11742
+ import * as path20 from "path";
11525
11743
 
11526
11744
  // src/tools/visualize/activity.ts
11527
11745
  import { execFileSync } from "child_process";
11528
- import * as path17 from "path";
11746
+ import * as path18 from "path";
11529
11747
  function attachRecentActivity(data, projectRoot) {
11530
11748
  const activity = readGitActivity(projectRoot);
11531
11749
  const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
@@ -11687,7 +11905,7 @@ function normalizePath2(filePath) {
11687
11905
  return filePath.replace(/\\/g, "/");
11688
11906
  }
11689
11907
  function toGitRelativePath(projectRoot, filePath) {
11690
- const relativePath = path17.isAbsolute(filePath) ? path17.relative(projectRoot, filePath) : filePath;
11908
+ const relativePath = path18.isAbsolute(filePath) ? path18.relative(projectRoot, filePath) : filePath;
11691
11909
  return normalizePath2(relativePath);
11692
11910
  }
11693
11911
 
@@ -11945,7 +12163,7 @@ render();
11945
12163
  }
11946
12164
 
11947
12165
  // src/tools/visualize/transform.ts
11948
- import * as path18 from "path";
12166
+ import * as path19 from "path";
11949
12167
 
11950
12168
  // src/tools/visualize/modules.ts
11951
12169
  var MAX_MODULES = 18;
@@ -12078,8 +12296,8 @@ function compactModules(prefixToNodes) {
12078
12296
  function deriveModules(nodes) {
12079
12297
  const initial = /* @__PURE__ */ new Map();
12080
12298
  for (const node of nodes) {
12081
- const relative10 = stripToProjectRelative(node.filePath);
12082
- const prefix = modulePrefixFromRelativePath(relative10);
12299
+ const relative11 = stripToProjectRelative(node.filePath);
12300
+ const prefix = modulePrefixFromRelativePath(relative11);
12083
12301
  if (!initial.has(prefix)) initial.set(prefix, []);
12084
12302
  initial.get(prefix)?.push(node);
12085
12303
  }
@@ -12205,7 +12423,7 @@ function transformForVisualization(symbols, edges, options = {}) {
12205
12423
  filePath: s.filePath,
12206
12424
  kind: s.kind,
12207
12425
  line: s.startLine,
12208
- directory: path18.dirname(s.filePath),
12426
+ directory: path19.dirname(s.filePath),
12209
12427
  moduleId: "",
12210
12428
  moduleLabel: ""
12211
12429
  }));
@@ -12256,7 +12474,10 @@ var codebase_peek = tool2({
12256
12474
  limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
12257
12475
  fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
12258
12476
  directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
12259
- chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type")
12477
+ chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
12478
+ blameAuthor: z2.string().optional().describe("Filter by git blame author name or email"),
12479
+ blameSha: z2.string().optional().describe("Filter by git blame commit SHA or prefix"),
12480
+ blameSince: z2.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
12260
12481
  },
12261
12482
  async execute(args, context) {
12262
12483
  const results = await searchCodebase(context?.worktree, DEFAULT_HOST, args.query, {
@@ -12264,7 +12485,10 @@ var codebase_peek = tool2({
12264
12485
  fileType: args.fileType,
12265
12486
  directory: args.directory,
12266
12487
  chunkType: args.chunkType,
12267
- metadataOnly: true
12488
+ metadataOnly: true,
12489
+ blameAuthor: args.blameAuthor,
12490
+ blameSha: args.blameSha,
12491
+ blameSince: args.blameSince
12268
12492
  });
12269
12493
  return formatCodebasePeek(results);
12270
12494
  }
@@ -12347,7 +12571,10 @@ var codebase_search = tool2({
12347
12571
  fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
12348
12572
  directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
12349
12573
  chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
12350
- contextLines: z2.number().optional().describe("Number of extra lines to include before/after each match (default: 0)")
12574
+ contextLines: z2.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
12575
+ blameAuthor: z2.string().optional().describe("Filter by git blame author name or email"),
12576
+ blameSha: z2.string().optional().describe("Filter by git blame commit SHA or prefix"),
12577
+ blameSince: z2.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
12351
12578
  },
12352
12579
  async execute(args, context) {
12353
12580
  const results = await searchCodebase(context?.worktree, DEFAULT_HOST, args.query, {
@@ -12355,7 +12582,10 @@ var codebase_search = tool2({
12355
12582
  fileType: args.fileType,
12356
12583
  directory: args.directory,
12357
12584
  chunkType: args.chunkType,
12358
- contextLines: args.contextLines
12585
+ contextLines: args.contextLines,
12586
+ blameAuthor: args.blameAuthor,
12587
+ blameSha: args.blameSha,
12588
+ blameSince: args.blameSince
12359
12589
  });
12360
12590
  if (results.length === 0) {
12361
12591
  return "No matching code found. Try a different query or run index_codebase first.";
@@ -12394,22 +12624,10 @@ var call_graph = tool2({
12394
12624
  return "Error: 'symbolId' is required when direction is 'callees'. First use direction='callers' to find the symbol ID.";
12395
12625
  }
12396
12626
  const { callees } = await getCallGraphData(context?.worktree, DEFAULT_HOST, args);
12397
- if (callees.length === 0) {
12398
- return `No callees found for symbol ${args.symbolId}${args.relationshipType ? ` with type ${args.relationshipType}` : ""}. The function may not call any other tracked functions.`;
12399
- }
12400
- return callees.map((edge, index) => {
12401
- const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
12402
- return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
12403
- }).join("\n");
12627
+ return formatCallGraphCallees(args.symbolId, callees, args.relationshipType);
12404
12628
  }
12405
12629
  const { callers } = await getCallGraphData(context?.worktree, DEFAULT_HOST, args);
12406
- if (callers.length === 0) {
12407
- return `No callers found for "${args.name}"${args.relationshipType ? ` with type ${args.relationshipType}` : ""}. It may not be called by any tracked function, or the index needs updating.`;
12408
- }
12409
- return callers.map((edge, index) => {
12410
- const confidence = edge.confidence !== "Direct" ? ` [${edge.confidence.toLowerCase()}]` : "";
12411
- 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]"}`;
12412
- }).join("\n");
12630
+ return formatCallGraphCallers(args.name, callers, args.relationshipType);
12413
12631
  }
12414
12632
  });
12415
12633
  var call_graph_path = tool2({
@@ -12420,17 +12638,8 @@ var call_graph_path = tool2({
12420
12638
  maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
12421
12639
  },
12422
12640
  async execute(args, context) {
12423
- const path22 = await getCallGraphPath(context?.worktree, DEFAULT_HOST, args.from, args.to, args.maxDepth);
12424
- if (path22.length === 0) {
12425
- return `No path found between "${args.from}" and "${args.to}". They may be in disconnected components, or the call graph index needs updating.`;
12426
- }
12427
- const formatted = path22.map((hop, index) => {
12428
- const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
12429
- const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
12430
- return `${prefix} ${hop.symbolName}${location}`;
12431
- });
12432
- return `Path (${path22.length} hops):
12433
- ${formatted.join("\n")}`;
12641
+ const path23 = await getCallGraphPath(context?.worktree, DEFAULT_HOST, args.from, args.to, args.maxDepth);
12642
+ return formatCallGraphPath(args.from, args.to, path23);
12434
12643
  }
12435
12644
  });
12436
12645
  var add_knowledge_base = tool2({
@@ -12483,7 +12692,7 @@ var index_visualize = tool2({
12483
12692
  return "No connected symbols found for visualization. Try including orphans with includeOrphans=true, or check that the call graph has resolved edges.";
12484
12693
  }
12485
12694
  const html = generateVisualizationHtml(vizData);
12486
- const outputPath = path19.join(os4.tmpdir(), `call-graph-${Date.now()}.html`);
12695
+ const outputPath = path20.join(os4.tmpdir(), `call-graph-${Date.now()}.html`);
12487
12696
  writeFileSync4(outputPath, html, "utf-8");
12488
12697
  let result = `Temporal call graph visualization generated: ${outputPath}
12489
12698
 
@@ -12506,7 +12715,7 @@ var index_visualize = tool2({
12506
12715
 
12507
12716
  // src/commands/loader.ts
12508
12717
  import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync7 } from "fs";
12509
- import * as path20 from "path";
12718
+ import * as path21 from "path";
12510
12719
  function parseFrontmatter(content) {
12511
12720
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
12512
12721
  const match = content.match(frontmatterRegex);
@@ -12532,7 +12741,7 @@ function loadCommandsFromDirectory(commandsDir) {
12532
12741
  }
12533
12742
  const files = readdirSync2(commandsDir).filter((f) => f.endsWith(".md"));
12534
12743
  for (const file of files) {
12535
- const filePath = path20.join(commandsDir, file);
12744
+ const filePath = path21.join(commandsDir, file);
12536
12745
  let content;
12537
12746
  try {
12538
12747
  content = readFileSync7(filePath, "utf-8");
@@ -12541,7 +12750,7 @@ function loadCommandsFromDirectory(commandsDir) {
12541
12750
  throw new Error(`Failed to load command file ${filePath}: ${message}`);
12542
12751
  }
12543
12752
  const { frontmatter, body } = parseFrontmatter(content);
12544
- const name = path20.basename(file, ".md");
12753
+ const name = path21.basename(file, ".md");
12545
12754
  const description = frontmatter.description || `Run the ${name} command`;
12546
12755
  commands.set(name, {
12547
12756
  description,
@@ -12873,9 +13082,9 @@ function replaceActiveWatcher(projectRoot, nextWatcher) {
12873
13082
  function getCommandsDir() {
12874
13083
  let currentDir = process.cwd();
12875
13084
  if (typeof import.meta !== "undefined" && import.meta.url) {
12876
- currentDir = path21.dirname(fileURLToPath2(import.meta.url));
13085
+ currentDir = path22.dirname(fileURLToPath2(import.meta.url));
12877
13086
  }
12878
- return path21.join(currentDir, "..", "commands");
13087
+ return path22.join(currentDir, "..", "commands");
12879
13088
  }
12880
13089
  function appendRoutingHints(output, hints, preferredRole) {
12881
13090
  const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
@@ -12895,7 +13104,7 @@ var plugin = async ({ directory, worktree }) => {
12895
13104
  initializeTools2(projectRoot, config);
12896
13105
  const getProjectIndexer = () => getIndexerForProject2(projectRoot);
12897
13106
  const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus(), 200, config.search.routingGraphHandoffHints) : null;
12898
- const isHomeDir = path21.resolve(projectRoot) === path21.resolve(os5.homedir());
13107
+ const isHomeDir = path22.resolve(projectRoot) === path22.resolve(os5.homedir());
12899
13108
  const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(projectRoot));
12900
13109
  if (isHomeDir) {
12901
13110
  console.warn(