opencode-codebase-index 0.5.2 → 0.6.1

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.cjs CHANGED
@@ -33,7 +33,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
33
33
 
34
34
  // node_modules/eventemitter3/index.js
35
35
  var require_eventemitter3 = __commonJS({
36
- "node_modules/eventemitter3/index.js"(exports2, module2) {
36
+ "node_modules/eventemitter3/index.js"(exports2, module3) {
37
37
  "use strict";
38
38
  var has = Object.prototype.hasOwnProperty;
39
39
  var prefix = "~";
@@ -187,15 +187,15 @@ var require_eventemitter3 = __commonJS({
187
187
  EventEmitter3.prototype.addListener = EventEmitter3.prototype.on;
188
188
  EventEmitter3.prefixed = prefix;
189
189
  EventEmitter3.EventEmitter = EventEmitter3;
190
- if ("undefined" !== typeof module2) {
191
- module2.exports = EventEmitter3;
190
+ if ("undefined" !== typeof module3) {
191
+ module3.exports = EventEmitter3;
192
192
  }
193
193
  }
194
194
  });
195
195
 
196
196
  // node_modules/ignore/index.js
197
197
  var require_ignore = __commonJS({
198
- "node_modules/ignore/index.js"(exports2, module2) {
198
+ "node_modules/ignore/index.js"(exports2, module3) {
199
199
  "use strict";
200
200
  function makeArray(subject) {
201
201
  return Array.isArray(subject) ? subject : [subject];
@@ -644,10 +644,10 @@ var require_ignore = __commonJS({
644
644
  ) {
645
645
  setupWindows();
646
646
  }
647
- module2.exports = factory;
647
+ module3.exports = factory;
648
648
  factory.default = factory;
649
- module2.exports.isPathValid = isPathValid;
650
- define(module2.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
649
+ module3.exports.isPathValid = isPathValid;
650
+ define(module3.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
651
651
  }
652
652
  });
653
653
 
@@ -668,7 +668,7 @@ var DEFAULT_INCLUDE = [
668
668
  "**/*.{py,pyi}",
669
669
  "**/*.{go,rs,java,kt,scala}",
670
670
  "**/*.{c,cpp,cc,h,hpp}",
671
- "**/*.{rb,php,swift}",
671
+ "**/*.{rb,php,inc,swift}",
672
672
  "**/*.{vue,svelte,astro}",
673
673
  "**/*.{sql,graphql,proto}",
674
674
  "**/*.{yaml,yml,toml}",
@@ -763,6 +763,27 @@ var DEFAULT_PROVIDER_MODELS = {
763
763
  "ollama": "nomic-embed-text"
764
764
  };
765
765
 
766
+ // src/config/env-substitution.ts
767
+ var ENV_REFERENCE_PATTERN = /^\{env:([A-Z_][A-Z0-9_]*)\}$/;
768
+ var ENV_REFERENCE_LIKE_PATTERN = /\{env:[^}]+\}/;
769
+ function substituteEnvString(value, keyPath) {
770
+ const match = value.match(ENV_REFERENCE_PATTERN);
771
+ if (!match) {
772
+ if (ENV_REFERENCE_LIKE_PATTERN.test(value)) {
773
+ throw new Error(
774
+ `Invalid environment variable reference at '${keyPath}'. Expected the entire string to match '{env:VAR_NAME}' with VAR_NAME matching [A-Z_][A-Z0-9_]*.`
775
+ );
776
+ }
777
+ return value;
778
+ }
779
+ const variableName = match[1];
780
+ const envValue = process.env[variableName];
781
+ if (envValue === void 0) {
782
+ throw new Error(`Missing environment variable '${variableName}' referenced by config at '${keyPath}'.`);
783
+ }
784
+ return envValue;
785
+ }
786
+
766
787
  // src/config/schema.ts
767
788
  function getDefaultIndexingConfig() {
768
789
  return {
@@ -820,11 +841,27 @@ function isValidScope(value) {
820
841
  function isStringArray(value) {
821
842
  return Array.isArray(value) && value.every((item) => typeof item === "string");
822
843
  }
844
+ function getResolvedString(value, keyPath) {
845
+ if (typeof value !== "string") {
846
+ return void 0;
847
+ }
848
+ return substituteEnvString(value, keyPath);
849
+ }
850
+ function getResolvedStringArray(value, keyPath) {
851
+ if (!isStringArray(value)) {
852
+ return void 0;
853
+ }
854
+ return value.map((item, index) => substituteEnvString(item, `${keyPath}[${index}]`));
855
+ }
823
856
  function isValidLogLevel(value) {
824
857
  return typeof value === "string" && VALID_LOG_LEVELS.includes(value);
825
858
  }
826
859
  function parseConfig(raw) {
827
860
  const input = raw && typeof raw === "object" ? raw : {};
861
+ const embeddingProviderValue = getResolvedString(input.embeddingProvider, "$root.embeddingProvider");
862
+ const scopeValue = getResolvedString(input.scope, "$root.scope");
863
+ const includeValue = getResolvedStringArray(input.include, "$root.include");
864
+ const excludeValue = getResolvedStringArray(input.exclude, "$root.exclude");
828
865
  const defaultIndexing = getDefaultIndexingConfig();
829
866
  const defaultSearch = getDefaultSearchConfig();
830
867
  const defaultDebug = getDefaultDebugConfig();
@@ -867,19 +904,23 @@ function parseConfig(raw) {
867
904
  let embeddingProvider;
868
905
  let embeddingModel = void 0;
869
906
  let customProvider = void 0;
870
- if (input.embeddingProvider === "custom") {
907
+ if (embeddingProviderValue === "custom") {
871
908
  embeddingProvider = "custom";
872
909
  const rawCustom = input.customProvider && typeof input.customProvider === "object" ? input.customProvider : null;
873
- if (rawCustom && typeof rawCustom.baseUrl === "string" && rawCustom.baseUrl.trim().length > 0 && typeof rawCustom.model === "string" && rawCustom.model.trim().length > 0 && typeof rawCustom.dimensions === "number" && Number.isInteger(rawCustom.dimensions) && rawCustom.dimensions > 0) {
910
+ const baseUrlValue = getResolvedString(rawCustom?.baseUrl, "$root.customProvider.baseUrl");
911
+ const modelValue = getResolvedString(rawCustom?.model, "$root.customProvider.model");
912
+ const apiKeyValue = getResolvedString(rawCustom?.apiKey, "$root.customProvider.apiKey");
913
+ if (rawCustom && typeof baseUrlValue === "string" && baseUrlValue.trim().length > 0 && typeof modelValue === "string" && modelValue.trim().length > 0 && typeof rawCustom.dimensions === "number" && Number.isInteger(rawCustom.dimensions) && rawCustom.dimensions > 0) {
874
914
  customProvider = {
875
- baseUrl: rawCustom.baseUrl.trim().replace(/\/+$/, ""),
876
- model: rawCustom.model,
915
+ baseUrl: baseUrlValue.trim().replace(/\/+$/, ""),
916
+ model: modelValue,
877
917
  dimensions: rawCustom.dimensions,
878
- apiKey: typeof rawCustom.apiKey === "string" ? rawCustom.apiKey : void 0,
918
+ apiKey: apiKeyValue,
879
919
  maxTokens: typeof rawCustom.maxTokens === "number" ? rawCustom.maxTokens : void 0,
880
920
  timeoutMs: typeof rawCustom.timeoutMs === "number" ? Math.max(1e3, rawCustom.timeoutMs) : void 0,
881
921
  concurrency: typeof rawCustom.concurrency === "number" ? Math.max(1, Math.floor(rawCustom.concurrency)) : void 0,
882
- requestIntervalMs: typeof rawCustom.requestIntervalMs === "number" ? Math.max(0, Math.floor(rawCustom.requestIntervalMs)) : void 0
922
+ requestIntervalMs: typeof rawCustom.requestIntervalMs === "number" ? Math.max(0, Math.floor(rawCustom.requestIntervalMs)) : void 0,
923
+ maxBatchSize: typeof rawCustom.maxBatchSize === "number" ? Math.max(1, Math.floor(rawCustom.maxBatchSize)) : typeof rawCustom.max_batch_size === "number" ? Math.max(1, Math.floor(rawCustom.max_batch_size)) : void 0
883
924
  };
884
925
  if (!/\/v\d+\/?$/.test(customProvider.baseUrl)) {
885
926
  console.warn(
@@ -891,10 +932,16 @@ function parseConfig(raw) {
891
932
  "embeddingProvider is 'custom' but customProvider config is missing or invalid. Required fields: baseUrl (string), model (string), dimensions (positive integer)."
892
933
  );
893
934
  }
894
- } else if (isValidProvider(input.embeddingProvider)) {
895
- embeddingProvider = input.embeddingProvider;
896
- if (input.embeddingModel) {
897
- embeddingModel = isValidModel(input.embeddingModel, embeddingProvider) ? input.embeddingModel : DEFAULT_PROVIDER_MODELS[embeddingProvider];
935
+ } else if (isValidProvider(embeddingProviderValue)) {
936
+ embeddingProvider = embeddingProviderValue;
937
+ const rawEmbeddingModel = input.embeddingModel;
938
+ if (typeof rawEmbeddingModel === "string") {
939
+ const embeddingModelValue = substituteEnvString(rawEmbeddingModel, "$root.embeddingModel");
940
+ if (embeddingModelValue) {
941
+ embeddingModel = isValidModel(embeddingModelValue, embeddingProvider) ? embeddingModelValue : DEFAULT_PROVIDER_MODELS[embeddingProvider];
942
+ }
943
+ } else if (rawEmbeddingModel) {
944
+ embeddingModel = DEFAULT_PROVIDER_MODELS[embeddingProvider];
898
945
  }
899
946
  } else {
900
947
  embeddingProvider = "auto";
@@ -903,9 +950,9 @@ function parseConfig(raw) {
903
950
  embeddingProvider,
904
951
  embeddingModel,
905
952
  customProvider,
906
- scope: isValidScope(input.scope) ? input.scope : "project",
907
- include: isStringArray(input.include) ? input.include : DEFAULT_INCLUDE,
908
- exclude: isStringArray(input.exclude) ? input.exclude : DEFAULT_EXCLUDE,
953
+ scope: isValidScope(scopeValue) ? scopeValue : "project",
954
+ include: includeValue ?? DEFAULT_INCLUDE,
955
+ exclude: excludeValue ?? DEFAULT_EXCLUDE,
909
956
  indexing,
910
957
  search,
911
958
  debug
@@ -2089,7 +2136,8 @@ function createCustomProviderInfo(config) {
2089
2136
  dimensions: config.dimensions,
2090
2137
  maxTokens: config.maxTokens ?? 8192,
2091
2138
  costPer1MTokens: 0,
2092
- timeoutMs: config.timeoutMs ?? 3e4
2139
+ timeoutMs: config.timeoutMs ?? 3e4,
2140
+ maxBatchSize: config.maxBatchSize
2093
2141
  }
2094
2142
  };
2095
2143
  }
@@ -2352,21 +2400,24 @@ var CustomEmbeddingProvider = class {
2352
2400
  this.credentials = credentials;
2353
2401
  this.modelInfo = modelInfo;
2354
2402
  }
2355
- async embedQuery(query) {
2356
- const result = await this.embedBatch([query]);
2357
- return {
2358
- embedding: result.embeddings[0],
2359
- tokensUsed: result.totalTokensUsed
2360
- };
2361
- }
2362
- async embedDocument(document) {
2363
- const result = await this.embedBatch([document]);
2364
- return {
2365
- embedding: result.embeddings[0],
2366
- tokensUsed: result.totalTokensUsed
2367
- };
2403
+ splitIntoRequestBatches(texts) {
2404
+ const maxBatchSize = this.modelInfo.maxBatchSize;
2405
+ if (!maxBatchSize || texts.length <= maxBatchSize) {
2406
+ return [texts];
2407
+ }
2408
+ const batches = [];
2409
+ for (let i = 0; i < texts.length; i += maxBatchSize) {
2410
+ batches.push(texts.slice(i, i + maxBatchSize));
2411
+ }
2412
+ return batches;
2368
2413
  }
2369
- async embedBatch(texts) {
2414
+ async embedRequest(texts) {
2415
+ if (texts.length === 0) {
2416
+ return {
2417
+ embeddings: [],
2418
+ totalTokensUsed: 0
2419
+ };
2420
+ }
2370
2421
  const headers = {
2371
2422
  "Content-Type": "application/json"
2372
2423
  };
@@ -2427,6 +2478,34 @@ var CustomEmbeddingProvider = class {
2427
2478
  }
2428
2479
  throw new Error("Custom embedding API returned unexpected response format. Expected OpenAI-compatible format with data[].embedding.");
2429
2480
  }
2481
+ async embedQuery(query) {
2482
+ const result = await this.embedBatch([query]);
2483
+ return {
2484
+ embedding: result.embeddings[0],
2485
+ tokensUsed: result.totalTokensUsed
2486
+ };
2487
+ }
2488
+ async embedDocument(document) {
2489
+ const result = await this.embedBatch([document]);
2490
+ return {
2491
+ embedding: result.embeddings[0],
2492
+ tokensUsed: result.totalTokensUsed
2493
+ };
2494
+ }
2495
+ async embedBatch(texts) {
2496
+ const requestBatches = this.splitIntoRequestBatches(texts);
2497
+ const embeddings = [];
2498
+ let totalTokensUsed = 0;
2499
+ for (const batch of requestBatches) {
2500
+ const result = await this.embedRequest(batch);
2501
+ embeddings.push(...result.embeddings);
2502
+ totalTokensUsed += result.totalTokensUsed;
2503
+ }
2504
+ return {
2505
+ embeddings,
2506
+ totalTokensUsed
2507
+ };
2508
+ }
2430
2509
  getModelInfo() {
2431
2510
  return this.modelInfo;
2432
2511
  }
@@ -2929,6 +3008,7 @@ function initializeLogger(config) {
2929
3008
  // src/native/index.ts
2930
3009
  var path3 = __toESM(require("path"), 1);
2931
3010
  var os2 = __toESM(require("os"), 1);
3011
+ var module2 = __toESM(require("module"), 1);
2932
3012
  var import_url = require("url");
2933
3013
  var import_meta = {};
2934
3014
  function getNativeBinding() {
@@ -2949,17 +3029,22 @@ function getNativeBinding() {
2949
3029
  throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
2950
3030
  }
2951
3031
  let currentDir;
3032
+ let requireTarget;
2952
3033
  if (typeof import_meta !== "undefined" && import_meta.url) {
2953
3034
  currentDir = path3.dirname((0, import_url.fileURLToPath)(import_meta.url));
3035
+ requireTarget = import_meta.url;
2954
3036
  } else if (typeof __dirname !== "undefined") {
2955
3037
  currentDir = __dirname;
3038
+ requireTarget = __filename;
2956
3039
  } else {
2957
3040
  currentDir = process.cwd();
3041
+ requireTarget = path3.join(currentDir, "index.js");
2958
3042
  }
2959
3043
  const isDevMode = currentDir.includes("/src/native");
2960
3044
  const packageRoot = isDevMode ? path3.resolve(currentDir, "../..") : path3.resolve(currentDir, "..");
2961
3045
  const nativePath = path3.join(packageRoot, "native", bindingName);
2962
- return require(nativePath);
3046
+ const require2 = module2.createRequire(requireTarget);
3047
+ return require2(nativePath);
2963
3048
  }
2964
3049
  var native = getNativeBinding();
2965
3050
  function parseFiles(files) {
@@ -3441,7 +3526,36 @@ var Database = class {
3441
3526
  // src/git/index.ts
3442
3527
  var import_fs3 = require("fs");
3443
3528
  var path4 = __toESM(require("path"), 1);
3444
- var import_child_process = require("child_process");
3529
+ function readPackedRefs(gitDir) {
3530
+ const packedRefsPath = path4.join(gitDir, "packed-refs");
3531
+ if (!(0, import_fs3.existsSync)(packedRefsPath)) {
3532
+ return [];
3533
+ }
3534
+ try {
3535
+ return (0, import_fs3.readFileSync)(packedRefsPath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && !line.startsWith("^"));
3536
+ } catch {
3537
+ return [];
3538
+ }
3539
+ }
3540
+ function resolveCommonGitDir(gitDir) {
3541
+ const commonDirPath = path4.join(gitDir, "commondir");
3542
+ if (!(0, import_fs3.existsSync)(commonDirPath)) {
3543
+ return gitDir;
3544
+ }
3545
+ try {
3546
+ const raw = (0, import_fs3.readFileSync)(commonDirPath, "utf-8").trim();
3547
+ if (!raw) {
3548
+ return gitDir;
3549
+ }
3550
+ const resolved = path4.isAbsolute(raw) ? raw : path4.resolve(gitDir, raw);
3551
+ if ((0, import_fs3.existsSync)(resolved)) {
3552
+ return resolved;
3553
+ }
3554
+ } catch {
3555
+ return gitDir;
3556
+ }
3557
+ return gitDir;
3558
+ }
3445
3559
  function resolveGitDir(repoRoot) {
3446
3560
  const gitPath = path4.join(repoRoot, ".git");
3447
3561
  if (!(0, import_fs3.existsSync)(gitPath)) {
@@ -3495,37 +3609,20 @@ function getCurrentBranch(repoRoot) {
3495
3609
  }
3496
3610
  function getBaseBranch(repoRoot) {
3497
3611
  const gitDir = resolveGitDir(repoRoot);
3612
+ const refStoreDir = gitDir ? resolveCommonGitDir(gitDir) : null;
3498
3613
  const candidates = ["main", "master", "develop", "trunk"];
3499
- if (gitDir) {
3614
+ if (refStoreDir) {
3500
3615
  for (const candidate of candidates) {
3501
- const refPath = path4.join(gitDir, "refs", "heads", candidate);
3616
+ const refPath = path4.join(refStoreDir, "refs", "heads", candidate);
3502
3617
  if ((0, import_fs3.existsSync)(refPath)) {
3503
3618
  return candidate;
3504
3619
  }
3505
- const packedRefsPath = path4.join(gitDir, "packed-refs");
3506
- if ((0, import_fs3.existsSync)(packedRefsPath)) {
3507
- try {
3508
- const content = (0, import_fs3.readFileSync)(packedRefsPath, "utf-8");
3509
- if (content.includes(`refs/heads/${candidate}`)) {
3510
- return candidate;
3511
- }
3512
- } catch {
3513
- }
3620
+ const packedRefs = readPackedRefs(refStoreDir);
3621
+ if (packedRefs.some((line) => line.endsWith(` refs/heads/${candidate}`))) {
3622
+ return candidate;
3514
3623
  }
3515
3624
  }
3516
3625
  }
3517
- try {
3518
- const result = (0, import_child_process.execSync)("git remote show origin", {
3519
- cwd: repoRoot,
3520
- encoding: "utf-8",
3521
- stdio: ["pipe", "pipe", "pipe"]
3522
- });
3523
- const match = result.match(/HEAD branch: (.+)/);
3524
- if (match) {
3525
- return match[1].trim();
3526
- }
3527
- } catch {
3528
- }
3529
3626
  return getCurrentBranch(repoRoot) ?? "main";
3530
3627
  }
3531
3628
  function getBranchOrDefault(repoRoot) {
@@ -3543,7 +3640,7 @@ function getHeadPath(repoRoot) {
3543
3640
  }
3544
3641
 
3545
3642
  // src/indexer/index.ts
3546
- var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust"]);
3643
+ var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php"]);
3547
3644
  var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
3548
3645
  "function_declaration",
3549
3646
  "function",
@@ -3564,7 +3661,8 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
3564
3661
  "struct_item",
3565
3662
  "enum_item",
3566
3663
  "trait_item",
3567
- "mod_item"
3664
+ "mod_item",
3665
+ "trait_declaration"
3568
3666
  ]);
3569
3667
  function float32ArrayToBuffer(arr) {
3570
3668
  const float32 = new Float32Array(arr);
@@ -3946,8 +4044,8 @@ function stripFilePathHint(query) {
3946
4044
  const stripped = query.replace(FILE_PATH_HINT_SUFFIX_REGEX, "").trim();
3947
4045
  return stripped.length > 0 ? stripped : query;
3948
4046
  }
3949
- function buildDeterministicIdentifierPass(query, candidates, limit) {
3950
- if (classifyQueryIntentRaw(query) !== "source") {
4047
+ function buildDeterministicIdentifierPass(query, candidates, limit, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
4048
+ if (!prioritizeSourcePaths) {
3951
4049
  return [];
3952
4050
  }
3953
4051
  const primary = extractPrimaryIdentifierQueryHint(query);
@@ -4163,9 +4261,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
4163
4261
  const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
4164
4262
  const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
4165
4263
  const rerankPool = fused.slice(0, rerankPoolLimit);
4166
- const intent = classifyQueryIntentRaw(query);
4167
4264
  return rerankResults(query, rerankPool, options.rerankTopN, {
4168
- prioritizeSourcePaths: intent === "source"
4265
+ prioritizeSourcePaths: options.prioritizeSourcePaths ?? classifyQueryIntentRaw(query) === "source"
4169
4266
  });
4170
4267
  }
4171
4268
  function rankSemanticOnlyResults(query, semanticResults, options) {
@@ -4175,11 +4272,11 @@ function rankSemanticOnlyResults(query, semanticResults, options) {
4175
4272
  prioritizeSourcePaths: options.prioritizeSourcePaths ?? false
4176
4273
  });
4177
4274
  }
4178
- function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCandidates, database, branchChunkIds) {
4275
+ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCandidates, database, branchChunkIds, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
4179
4276
  if (combined.length === 0) {
4180
4277
  return combined;
4181
4278
  }
4182
- if (classifyQueryIntentRaw(query) !== "source") {
4279
+ if (!prioritizeSourcePaths) {
4183
4280
  return combined;
4184
4281
  }
4185
4282
  const identifierHints = extractIdentifierHints(query);
@@ -4269,8 +4366,8 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
4269
4366
  const remainder = combined.filter((candidate) => !promotedIds.has(candidate.id));
4270
4367
  return [...promoted, ...remainder];
4271
4368
  }
4272
- function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallbackCandidates) {
4273
- if (classifyQueryIntentRaw(query) !== "source") {
4369
+ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
4370
+ if (!prioritizeSourcePaths) {
4274
4371
  return [];
4275
4372
  }
4276
4373
  const identifierHints = extractIdentifierHints(query);
@@ -4415,8 +4512,8 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
4415
4512
  const withFallback = Array.from(symbolCandidates.values()).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
4416
4513
  return withFallback.slice(0, Math.max(limit * 2, limit));
4417
4514
  }
4418
- function buildIdentifierDefinitionLane(query, candidates, limit) {
4419
- if (classifyQueryIntentRaw(query) !== "source") {
4515
+ function buildIdentifierDefinitionLane(query, candidates, limit, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
4516
+ if (!prioritizeSourcePaths) {
4420
4517
  return [];
4421
4518
  }
4422
4519
  const primaryHint = extractPrimaryIdentifierQueryHint(query);
@@ -5348,6 +5445,7 @@ var Indexer = class {
5348
5445
  const rrfK = this.config.search.rrfK;
5349
5446
  const rerankTopN = this.config.search.rerankTopN;
5350
5447
  const filterByBranch = options?.filterByBranch ?? true;
5448
+ const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
5351
5449
  this.logger.search("debug", "Starting search", {
5352
5450
  query,
5353
5451
  maxResults,
@@ -5399,7 +5497,8 @@ var Indexer = class {
5399
5497
  rrfK,
5400
5498
  rerankTopN,
5401
5499
  limit: maxResults,
5402
- hybridWeight
5500
+ hybridWeight,
5501
+ prioritizeSourcePaths: sourceIntent
5403
5502
  });
5404
5503
  const fusionMs = import_perf_hooks.performance.now() - fusionStartTime;
5405
5504
  const rescued = promoteIdentifierMatches(
@@ -5408,30 +5507,33 @@ var Indexer = class {
5408
5507
  semanticCandidates,
5409
5508
  keywordCandidates,
5410
5509
  database,
5411
- branchChunkIds
5510
+ branchChunkIds,
5511
+ sourceIntent
5412
5512
  );
5413
5513
  const union = unionCandidates(semanticCandidates, keywordCandidates);
5414
5514
  const deterministicIdentifierLane = buildDeterministicIdentifierPass(
5415
5515
  query,
5416
5516
  union,
5417
- maxResults
5517
+ maxResults,
5518
+ sourceIntent
5418
5519
  );
5419
5520
  const identifierLane = buildIdentifierDefinitionLane(
5420
5521
  query,
5421
5522
  union,
5422
- maxResults
5523
+ maxResults,
5524
+ sourceIntent
5423
5525
  );
5424
5526
  const symbolLane = buildSymbolDefinitionLane(
5425
5527
  query,
5426
5528
  database,
5427
5529
  branchChunkIds,
5428
5530
  maxResults,
5429
- union
5531
+ union,
5532
+ sourceIntent
5430
5533
  );
5431
5534
  const prePrimaryLane = mergeTieredResults(deterministicIdentifierLane, identifierLane, maxResults * 4);
5432
5535
  const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
5433
5536
  const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
5434
- const sourceIntent = classifyQueryIntentRaw(query) === "source";
5435
5537
  const hasCodeHints = extractCodeTermHints(query).length > 0 || extractIdentifierHints(query).length > 0;
5436
5538
  const baseFiltered = tiered.filter((r) => {
5437
5539
  if (r.score < this.config.search.minScore) return false;
@@ -7877,6 +7979,22 @@ function formatLogs(logs) {
7877
7979
  return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
7878
7980
  }).join("\n");
7879
7981
  }
7982
+ function formatDefinitionLookup(results, query) {
7983
+ if (results.length === 0) {
7984
+ return `No definition found for "${query}". Try codebase_search for broader discovery, or verify the symbol name.`;
7985
+ }
7986
+ const formatted = results.map((r, idx) => {
7987
+ const header2 = r.name ? `[${idx + 1}] ${r.chunkType} "${r.name}" in ${r.filePath}:${r.startLine}-${r.endLine}` : `[${idx + 1}] ${r.chunkType} in ${r.filePath}:${r.startLine}-${r.endLine}`;
7988
+ return `${header2} (score: ${r.score.toFixed(2)})
7989
+ \`\`\`
7990
+ ${truncateContent(r.content)}
7991
+ \`\`\``;
7992
+ });
7993
+ const header = results.length === 1 ? `Definition found for "${query}":` : `Found ${results.length} definition candidates for "${query}":`;
7994
+ return `${header}
7995
+
7996
+ ${formatted.join("\n\n")}`;
7997
+ }
7880
7998
  function formatSearchResults(results, scoreFormat = "similarity") {
7881
7999
  const formatted = results.map((r, idx) => {
7882
8000
  const header = r.name ? `[${idx + 1}] ${r.chunkType} "${r.name}" in ${r.filePath}:${r.startLine}-${r.endLine}` : `[${idx + 1}] ${r.chunkType} in ${r.filePath}:${r.startLine}-${r.endLine}`;
@@ -8062,6 +8180,24 @@ var codebase_search = (0, import_plugin.tool)({
8062
8180
  ${formatSearchResults(results, "score")}`;
8063
8181
  }
8064
8182
  });
8183
+ var implementation_lookup = (0, import_plugin.tool)({
8184
+ description: "Jump to symbol definition. Find WHERE something is defined. Returns the authoritative source location(s) for a function, class, method, type, or variable. Prefers real implementation files over tests, docs, examples, and fixtures. Use when you need the definition site, not all usages.",
8185
+ args: {
8186
+ query: z.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
8187
+ limit: z.number().optional().default(5).describe("Maximum number of results"),
8188
+ fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
8189
+ directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
8190
+ },
8191
+ async execute(args) {
8192
+ const indexer = getIndexer();
8193
+ const results = await indexer.search(args.query, args.limit ?? 5, {
8194
+ fileType: args.fileType,
8195
+ directory: args.directory,
8196
+ definitionIntent: true
8197
+ });
8198
+ return formatDefinitionLookup(results, args.query);
8199
+ }
8200
+ });
8065
8201
  var call_graph = (0, import_plugin.tool)({
8066
8202
  description: "Query the call graph to find callers or callees of a function/method. Use to understand code flow and dependencies between functions.",
8067
8203
  args: {
@@ -8203,7 +8339,8 @@ var plugin = async ({ directory }) => {
8203
8339
  index_metrics,
8204
8340
  index_logs,
8205
8341
  find_similar,
8206
- call_graph
8342
+ call_graph,
8343
+ implementation_lookup
8207
8344
  },
8208
8345
  async config(cfg) {
8209
8346
  cfg.command = cfg.command ?? {};