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.js CHANGED
@@ -6,13 +6,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
10
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
11
- }) : x)(function(x) {
12
- if (typeof require !== "undefined") return require.apply(this, arguments);
13
- throw Error('Dynamic require of "' + x + '" is not supported');
14
- });
15
- var __commonJS = (cb, mod) => function __require2() {
9
+ var __commonJS = (cb, mod) => function __require() {
16
10
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
17
11
  };
18
12
  var __copyProps = (to, from, except, desc) => {
@@ -34,7 +28,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
34
28
 
35
29
  // node_modules/eventemitter3/index.js
36
30
  var require_eventemitter3 = __commonJS({
37
- "node_modules/eventemitter3/index.js"(exports, module) {
31
+ "node_modules/eventemitter3/index.js"(exports, module2) {
38
32
  "use strict";
39
33
  var has = Object.prototype.hasOwnProperty;
40
34
  var prefix = "~";
@@ -188,15 +182,15 @@ var require_eventemitter3 = __commonJS({
188
182
  EventEmitter3.prototype.addListener = EventEmitter3.prototype.on;
189
183
  EventEmitter3.prefixed = prefix;
190
184
  EventEmitter3.EventEmitter = EventEmitter3;
191
- if ("undefined" !== typeof module) {
192
- module.exports = EventEmitter3;
185
+ if ("undefined" !== typeof module2) {
186
+ module2.exports = EventEmitter3;
193
187
  }
194
188
  }
195
189
  });
196
190
 
197
191
  // node_modules/ignore/index.js
198
192
  var require_ignore = __commonJS({
199
- "node_modules/ignore/index.js"(exports, module) {
193
+ "node_modules/ignore/index.js"(exports, module2) {
200
194
  "use strict";
201
195
  function makeArray(subject) {
202
196
  return Array.isArray(subject) ? subject : [subject];
@@ -645,10 +639,10 @@ var require_ignore = __commonJS({
645
639
  ) {
646
640
  setupWindows();
647
641
  }
648
- module.exports = factory;
642
+ module2.exports = factory;
649
643
  factory.default = factory;
650
- module.exports.isPathValid = isPathValid;
651
- define(module.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
644
+ module2.exports.isPathValid = isPathValid;
645
+ define(module2.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
652
646
  }
653
647
  });
654
648
 
@@ -664,7 +658,7 @@ var DEFAULT_INCLUDE = [
664
658
  "**/*.{py,pyi}",
665
659
  "**/*.{go,rs,java,kt,scala}",
666
660
  "**/*.{c,cpp,cc,h,hpp}",
667
- "**/*.{rb,php,swift}",
661
+ "**/*.{rb,php,inc,swift}",
668
662
  "**/*.{vue,svelte,astro}",
669
663
  "**/*.{sql,graphql,proto}",
670
664
  "**/*.{yaml,yml,toml}",
@@ -759,6 +753,27 @@ var DEFAULT_PROVIDER_MODELS = {
759
753
  "ollama": "nomic-embed-text"
760
754
  };
761
755
 
756
+ // src/config/env-substitution.ts
757
+ var ENV_REFERENCE_PATTERN = /^\{env:([A-Z_][A-Z0-9_]*)\}$/;
758
+ var ENV_REFERENCE_LIKE_PATTERN = /\{env:[^}]+\}/;
759
+ function substituteEnvString(value, keyPath) {
760
+ const match = value.match(ENV_REFERENCE_PATTERN);
761
+ if (!match) {
762
+ if (ENV_REFERENCE_LIKE_PATTERN.test(value)) {
763
+ throw new Error(
764
+ `Invalid environment variable reference at '${keyPath}'. Expected the entire string to match '{env:VAR_NAME}' with VAR_NAME matching [A-Z_][A-Z0-9_]*.`
765
+ );
766
+ }
767
+ return value;
768
+ }
769
+ const variableName = match[1];
770
+ const envValue = process.env[variableName];
771
+ if (envValue === void 0) {
772
+ throw new Error(`Missing environment variable '${variableName}' referenced by config at '${keyPath}'.`);
773
+ }
774
+ return envValue;
775
+ }
776
+
762
777
  // src/config/schema.ts
763
778
  function getDefaultIndexingConfig() {
764
779
  return {
@@ -816,11 +831,27 @@ function isValidScope(value) {
816
831
  function isStringArray(value) {
817
832
  return Array.isArray(value) && value.every((item) => typeof item === "string");
818
833
  }
834
+ function getResolvedString(value, keyPath) {
835
+ if (typeof value !== "string") {
836
+ return void 0;
837
+ }
838
+ return substituteEnvString(value, keyPath);
839
+ }
840
+ function getResolvedStringArray(value, keyPath) {
841
+ if (!isStringArray(value)) {
842
+ return void 0;
843
+ }
844
+ return value.map((item, index) => substituteEnvString(item, `${keyPath}[${index}]`));
845
+ }
819
846
  function isValidLogLevel(value) {
820
847
  return typeof value === "string" && VALID_LOG_LEVELS.includes(value);
821
848
  }
822
849
  function parseConfig(raw) {
823
850
  const input = raw && typeof raw === "object" ? raw : {};
851
+ const embeddingProviderValue = getResolvedString(input.embeddingProvider, "$root.embeddingProvider");
852
+ const scopeValue = getResolvedString(input.scope, "$root.scope");
853
+ const includeValue = getResolvedStringArray(input.include, "$root.include");
854
+ const excludeValue = getResolvedStringArray(input.exclude, "$root.exclude");
824
855
  const defaultIndexing = getDefaultIndexingConfig();
825
856
  const defaultSearch = getDefaultSearchConfig();
826
857
  const defaultDebug = getDefaultDebugConfig();
@@ -863,19 +894,23 @@ function parseConfig(raw) {
863
894
  let embeddingProvider;
864
895
  let embeddingModel = void 0;
865
896
  let customProvider = void 0;
866
- if (input.embeddingProvider === "custom") {
897
+ if (embeddingProviderValue === "custom") {
867
898
  embeddingProvider = "custom";
868
899
  const rawCustom = input.customProvider && typeof input.customProvider === "object" ? input.customProvider : null;
869
- 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) {
900
+ const baseUrlValue = getResolvedString(rawCustom?.baseUrl, "$root.customProvider.baseUrl");
901
+ const modelValue = getResolvedString(rawCustom?.model, "$root.customProvider.model");
902
+ const apiKeyValue = getResolvedString(rawCustom?.apiKey, "$root.customProvider.apiKey");
903
+ 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) {
870
904
  customProvider = {
871
- baseUrl: rawCustom.baseUrl.trim().replace(/\/+$/, ""),
872
- model: rawCustom.model,
905
+ baseUrl: baseUrlValue.trim().replace(/\/+$/, ""),
906
+ model: modelValue,
873
907
  dimensions: rawCustom.dimensions,
874
- apiKey: typeof rawCustom.apiKey === "string" ? rawCustom.apiKey : void 0,
908
+ apiKey: apiKeyValue,
875
909
  maxTokens: typeof rawCustom.maxTokens === "number" ? rawCustom.maxTokens : void 0,
876
910
  timeoutMs: typeof rawCustom.timeoutMs === "number" ? Math.max(1e3, rawCustom.timeoutMs) : void 0,
877
911
  concurrency: typeof rawCustom.concurrency === "number" ? Math.max(1, Math.floor(rawCustom.concurrency)) : void 0,
878
- requestIntervalMs: typeof rawCustom.requestIntervalMs === "number" ? Math.max(0, Math.floor(rawCustom.requestIntervalMs)) : void 0
912
+ requestIntervalMs: typeof rawCustom.requestIntervalMs === "number" ? Math.max(0, Math.floor(rawCustom.requestIntervalMs)) : void 0,
913
+ 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
879
914
  };
880
915
  if (!/\/v\d+\/?$/.test(customProvider.baseUrl)) {
881
916
  console.warn(
@@ -887,10 +922,16 @@ function parseConfig(raw) {
887
922
  "embeddingProvider is 'custom' but customProvider config is missing or invalid. Required fields: baseUrl (string), model (string), dimensions (positive integer)."
888
923
  );
889
924
  }
890
- } else if (isValidProvider(input.embeddingProvider)) {
891
- embeddingProvider = input.embeddingProvider;
892
- if (input.embeddingModel) {
893
- embeddingModel = isValidModel(input.embeddingModel, embeddingProvider) ? input.embeddingModel : DEFAULT_PROVIDER_MODELS[embeddingProvider];
925
+ } else if (isValidProvider(embeddingProviderValue)) {
926
+ embeddingProvider = embeddingProviderValue;
927
+ const rawEmbeddingModel = input.embeddingModel;
928
+ if (typeof rawEmbeddingModel === "string") {
929
+ const embeddingModelValue = substituteEnvString(rawEmbeddingModel, "$root.embeddingModel");
930
+ if (embeddingModelValue) {
931
+ embeddingModel = isValidModel(embeddingModelValue, embeddingProvider) ? embeddingModelValue : DEFAULT_PROVIDER_MODELS[embeddingProvider];
932
+ }
933
+ } else if (rawEmbeddingModel) {
934
+ embeddingModel = DEFAULT_PROVIDER_MODELS[embeddingProvider];
894
935
  }
895
936
  } else {
896
937
  embeddingProvider = "auto";
@@ -899,9 +940,9 @@ function parseConfig(raw) {
899
940
  embeddingProvider,
900
941
  embeddingModel,
901
942
  customProvider,
902
- scope: isValidScope(input.scope) ? input.scope : "project",
903
- include: isStringArray(input.include) ? input.include : DEFAULT_INCLUDE,
904
- exclude: isStringArray(input.exclude) ? input.exclude : DEFAULT_EXCLUDE,
943
+ scope: isValidScope(scopeValue) ? scopeValue : "project",
944
+ include: includeValue ?? DEFAULT_INCLUDE,
945
+ exclude: excludeValue ?? DEFAULT_EXCLUDE,
905
946
  indexing,
906
947
  search,
907
948
  debug
@@ -2085,7 +2126,8 @@ function createCustomProviderInfo(config) {
2085
2126
  dimensions: config.dimensions,
2086
2127
  maxTokens: config.maxTokens ?? 8192,
2087
2128
  costPer1MTokens: 0,
2088
- timeoutMs: config.timeoutMs ?? 3e4
2129
+ timeoutMs: config.timeoutMs ?? 3e4,
2130
+ maxBatchSize: config.maxBatchSize
2089
2131
  }
2090
2132
  };
2091
2133
  }
@@ -2348,21 +2390,24 @@ var CustomEmbeddingProvider = class {
2348
2390
  this.credentials = credentials;
2349
2391
  this.modelInfo = modelInfo;
2350
2392
  }
2351
- async embedQuery(query) {
2352
- const result = await this.embedBatch([query]);
2353
- return {
2354
- embedding: result.embeddings[0],
2355
- tokensUsed: result.totalTokensUsed
2356
- };
2357
- }
2358
- async embedDocument(document) {
2359
- const result = await this.embedBatch([document]);
2360
- return {
2361
- embedding: result.embeddings[0],
2362
- tokensUsed: result.totalTokensUsed
2363
- };
2393
+ splitIntoRequestBatches(texts) {
2394
+ const maxBatchSize = this.modelInfo.maxBatchSize;
2395
+ if (!maxBatchSize || texts.length <= maxBatchSize) {
2396
+ return [texts];
2397
+ }
2398
+ const batches = [];
2399
+ for (let i = 0; i < texts.length; i += maxBatchSize) {
2400
+ batches.push(texts.slice(i, i + maxBatchSize));
2401
+ }
2402
+ return batches;
2364
2403
  }
2365
- async embedBatch(texts) {
2404
+ async embedRequest(texts) {
2405
+ if (texts.length === 0) {
2406
+ return {
2407
+ embeddings: [],
2408
+ totalTokensUsed: 0
2409
+ };
2410
+ }
2366
2411
  const headers = {
2367
2412
  "Content-Type": "application/json"
2368
2413
  };
@@ -2423,6 +2468,34 @@ var CustomEmbeddingProvider = class {
2423
2468
  }
2424
2469
  throw new Error("Custom embedding API returned unexpected response format. Expected OpenAI-compatible format with data[].embedding.");
2425
2470
  }
2471
+ async embedQuery(query) {
2472
+ const result = await this.embedBatch([query]);
2473
+ return {
2474
+ embedding: result.embeddings[0],
2475
+ tokensUsed: result.totalTokensUsed
2476
+ };
2477
+ }
2478
+ async embedDocument(document) {
2479
+ const result = await this.embedBatch([document]);
2480
+ return {
2481
+ embedding: result.embeddings[0],
2482
+ tokensUsed: result.totalTokensUsed
2483
+ };
2484
+ }
2485
+ async embedBatch(texts) {
2486
+ const requestBatches = this.splitIntoRequestBatches(texts);
2487
+ const embeddings = [];
2488
+ let totalTokensUsed = 0;
2489
+ for (const batch of requestBatches) {
2490
+ const result = await this.embedRequest(batch);
2491
+ embeddings.push(...result.embeddings);
2492
+ totalTokensUsed += result.totalTokensUsed;
2493
+ }
2494
+ return {
2495
+ embeddings,
2496
+ totalTokensUsed
2497
+ };
2498
+ }
2426
2499
  getModelInfo() {
2427
2500
  return this.modelInfo;
2428
2501
  }
@@ -2925,6 +2998,7 @@ function initializeLogger(config) {
2925
2998
  // src/native/index.ts
2926
2999
  import * as path3 from "path";
2927
3000
  import * as os2 from "os";
3001
+ import * as module from "module";
2928
3002
  import { fileURLToPath } from "url";
2929
3003
  function getNativeBinding() {
2930
3004
  const platform2 = os2.platform();
@@ -2944,17 +3018,22 @@ function getNativeBinding() {
2944
3018
  throw new Error(`Unsupported platform: ${platform2}-${arch2}`);
2945
3019
  }
2946
3020
  let currentDir;
3021
+ let requireTarget;
2947
3022
  if (typeof import.meta !== "undefined" && import.meta.url) {
2948
3023
  currentDir = path3.dirname(fileURLToPath(import.meta.url));
3024
+ requireTarget = import.meta.url;
2949
3025
  } else if (typeof __dirname !== "undefined") {
2950
3026
  currentDir = __dirname;
3027
+ requireTarget = __filename;
2951
3028
  } else {
2952
3029
  currentDir = process.cwd();
3030
+ requireTarget = path3.join(currentDir, "index.js");
2953
3031
  }
2954
3032
  const isDevMode = currentDir.includes("/src/native");
2955
3033
  const packageRoot = isDevMode ? path3.resolve(currentDir, "../..") : path3.resolve(currentDir, "..");
2956
3034
  const nativePath = path3.join(packageRoot, "native", bindingName);
2957
- return __require(nativePath);
3035
+ const require2 = module.createRequire(requireTarget);
3036
+ return require2(nativePath);
2958
3037
  }
2959
3038
  var native = getNativeBinding();
2960
3039
  function parseFiles(files) {
@@ -3436,7 +3515,36 @@ var Database = class {
3436
3515
  // src/git/index.ts
3437
3516
  import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
3438
3517
  import * as path4 from "path";
3439
- import { execSync } from "child_process";
3518
+ function readPackedRefs(gitDir) {
3519
+ const packedRefsPath = path4.join(gitDir, "packed-refs");
3520
+ if (!existsSync3(packedRefsPath)) {
3521
+ return [];
3522
+ }
3523
+ try {
3524
+ return readFileSync3(packedRefsPath, "utf-8").split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && !line.startsWith("^"));
3525
+ } catch {
3526
+ return [];
3527
+ }
3528
+ }
3529
+ function resolveCommonGitDir(gitDir) {
3530
+ const commonDirPath = path4.join(gitDir, "commondir");
3531
+ if (!existsSync3(commonDirPath)) {
3532
+ return gitDir;
3533
+ }
3534
+ try {
3535
+ const raw = readFileSync3(commonDirPath, "utf-8").trim();
3536
+ if (!raw) {
3537
+ return gitDir;
3538
+ }
3539
+ const resolved = path4.isAbsolute(raw) ? raw : path4.resolve(gitDir, raw);
3540
+ if (existsSync3(resolved)) {
3541
+ return resolved;
3542
+ }
3543
+ } catch {
3544
+ return gitDir;
3545
+ }
3546
+ return gitDir;
3547
+ }
3440
3548
  function resolveGitDir(repoRoot) {
3441
3549
  const gitPath = path4.join(repoRoot, ".git");
3442
3550
  if (!existsSync3(gitPath)) {
@@ -3490,37 +3598,20 @@ function getCurrentBranch(repoRoot) {
3490
3598
  }
3491
3599
  function getBaseBranch(repoRoot) {
3492
3600
  const gitDir = resolveGitDir(repoRoot);
3601
+ const refStoreDir = gitDir ? resolveCommonGitDir(gitDir) : null;
3493
3602
  const candidates = ["main", "master", "develop", "trunk"];
3494
- if (gitDir) {
3603
+ if (refStoreDir) {
3495
3604
  for (const candidate of candidates) {
3496
- const refPath = path4.join(gitDir, "refs", "heads", candidate);
3605
+ const refPath = path4.join(refStoreDir, "refs", "heads", candidate);
3497
3606
  if (existsSync3(refPath)) {
3498
3607
  return candidate;
3499
3608
  }
3500
- const packedRefsPath = path4.join(gitDir, "packed-refs");
3501
- if (existsSync3(packedRefsPath)) {
3502
- try {
3503
- const content = readFileSync3(packedRefsPath, "utf-8");
3504
- if (content.includes(`refs/heads/${candidate}`)) {
3505
- return candidate;
3506
- }
3507
- } catch {
3508
- }
3609
+ const packedRefs = readPackedRefs(refStoreDir);
3610
+ if (packedRefs.some((line) => line.endsWith(` refs/heads/${candidate}`))) {
3611
+ return candidate;
3509
3612
  }
3510
3613
  }
3511
3614
  }
3512
- try {
3513
- const result = execSync("git remote show origin", {
3514
- cwd: repoRoot,
3515
- encoding: "utf-8",
3516
- stdio: ["pipe", "pipe", "pipe"]
3517
- });
3518
- const match = result.match(/HEAD branch: (.+)/);
3519
- if (match) {
3520
- return match[1].trim();
3521
- }
3522
- } catch {
3523
- }
3524
3615
  return getCurrentBranch(repoRoot) ?? "main";
3525
3616
  }
3526
3617
  function getBranchOrDefault(repoRoot) {
@@ -3538,7 +3629,7 @@ function getHeadPath(repoRoot) {
3538
3629
  }
3539
3630
 
3540
3631
  // src/indexer/index.ts
3541
- var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust"]);
3632
+ var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php"]);
3542
3633
  var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
3543
3634
  "function_declaration",
3544
3635
  "function",
@@ -3559,7 +3650,8 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
3559
3650
  "struct_item",
3560
3651
  "enum_item",
3561
3652
  "trait_item",
3562
- "mod_item"
3653
+ "mod_item",
3654
+ "trait_declaration"
3563
3655
  ]);
3564
3656
  function float32ArrayToBuffer(arr) {
3565
3657
  const float32 = new Float32Array(arr);
@@ -3941,8 +4033,8 @@ function stripFilePathHint(query) {
3941
4033
  const stripped = query.replace(FILE_PATH_HINT_SUFFIX_REGEX, "").trim();
3942
4034
  return stripped.length > 0 ? stripped : query;
3943
4035
  }
3944
- function buildDeterministicIdentifierPass(query, candidates, limit) {
3945
- if (classifyQueryIntentRaw(query) !== "source") {
4036
+ function buildDeterministicIdentifierPass(query, candidates, limit, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
4037
+ if (!prioritizeSourcePaths) {
3946
4038
  return [];
3947
4039
  }
3948
4040
  const primary = extractPrimaryIdentifierQueryHint(query);
@@ -4158,9 +4250,8 @@ function rankHybridResults(query, semanticResults, keywordResults, options) {
4158
4250
  const fused = options.fusionStrategy === "rrf" ? fuseResultsRrf(semanticResults, keywordResults, options.rrfK, overfetchLimit) : fuseResultsWeighted(semanticResults, keywordResults, options.hybridWeight, overfetchLimit);
4159
4251
  const rerankPoolLimit = Math.max(overfetchLimit, options.rerankTopN * 3, options.limit * 6);
4160
4252
  const rerankPool = fused.slice(0, rerankPoolLimit);
4161
- const intent = classifyQueryIntentRaw(query);
4162
4253
  return rerankResults(query, rerankPool, options.rerankTopN, {
4163
- prioritizeSourcePaths: intent === "source"
4254
+ prioritizeSourcePaths: options.prioritizeSourcePaths ?? classifyQueryIntentRaw(query) === "source"
4164
4255
  });
4165
4256
  }
4166
4257
  function rankSemanticOnlyResults(query, semanticResults, options) {
@@ -4170,11 +4261,11 @@ function rankSemanticOnlyResults(query, semanticResults, options) {
4170
4261
  prioritizeSourcePaths: options.prioritizeSourcePaths ?? false
4171
4262
  });
4172
4263
  }
4173
- function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCandidates, database, branchChunkIds) {
4264
+ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCandidates, database, branchChunkIds, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
4174
4265
  if (combined.length === 0) {
4175
4266
  return combined;
4176
4267
  }
4177
- if (classifyQueryIntentRaw(query) !== "source") {
4268
+ if (!prioritizeSourcePaths) {
4178
4269
  return combined;
4179
4270
  }
4180
4271
  const identifierHints = extractIdentifierHints(query);
@@ -4264,8 +4355,8 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
4264
4355
  const remainder = combined.filter((candidate) => !promotedIds.has(candidate.id));
4265
4356
  return [...promoted, ...remainder];
4266
4357
  }
4267
- function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallbackCandidates) {
4268
- if (classifyQueryIntentRaw(query) !== "source") {
4358
+ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
4359
+ if (!prioritizeSourcePaths) {
4269
4360
  return [];
4270
4361
  }
4271
4362
  const identifierHints = extractIdentifierHints(query);
@@ -4410,8 +4501,8 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
4410
4501
  const withFallback = Array.from(symbolCandidates.values()).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
4411
4502
  return withFallback.slice(0, Math.max(limit * 2, limit));
4412
4503
  }
4413
- function buildIdentifierDefinitionLane(query, candidates, limit) {
4414
- if (classifyQueryIntentRaw(query) !== "source") {
4504
+ function buildIdentifierDefinitionLane(query, candidates, limit, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
4505
+ if (!prioritizeSourcePaths) {
4415
4506
  return [];
4416
4507
  }
4417
4508
  const primaryHint = extractPrimaryIdentifierQueryHint(query);
@@ -5343,6 +5434,7 @@ var Indexer = class {
5343
5434
  const rrfK = this.config.search.rrfK;
5344
5435
  const rerankTopN = this.config.search.rerankTopN;
5345
5436
  const filterByBranch = options?.filterByBranch ?? true;
5437
+ const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
5346
5438
  this.logger.search("debug", "Starting search", {
5347
5439
  query,
5348
5440
  maxResults,
@@ -5394,7 +5486,8 @@ var Indexer = class {
5394
5486
  rrfK,
5395
5487
  rerankTopN,
5396
5488
  limit: maxResults,
5397
- hybridWeight
5489
+ hybridWeight,
5490
+ prioritizeSourcePaths: sourceIntent
5398
5491
  });
5399
5492
  const fusionMs = performance2.now() - fusionStartTime;
5400
5493
  const rescued = promoteIdentifierMatches(
@@ -5403,30 +5496,33 @@ var Indexer = class {
5403
5496
  semanticCandidates,
5404
5497
  keywordCandidates,
5405
5498
  database,
5406
- branchChunkIds
5499
+ branchChunkIds,
5500
+ sourceIntent
5407
5501
  );
5408
5502
  const union = unionCandidates(semanticCandidates, keywordCandidates);
5409
5503
  const deterministicIdentifierLane = buildDeterministicIdentifierPass(
5410
5504
  query,
5411
5505
  union,
5412
- maxResults
5506
+ maxResults,
5507
+ sourceIntent
5413
5508
  );
5414
5509
  const identifierLane = buildIdentifierDefinitionLane(
5415
5510
  query,
5416
5511
  union,
5417
- maxResults
5512
+ maxResults,
5513
+ sourceIntent
5418
5514
  );
5419
5515
  const symbolLane = buildSymbolDefinitionLane(
5420
5516
  query,
5421
5517
  database,
5422
5518
  branchChunkIds,
5423
5519
  maxResults,
5424
- union
5520
+ union,
5521
+ sourceIntent
5425
5522
  );
5426
5523
  const prePrimaryLane = mergeTieredResults(deterministicIdentifierLane, identifierLane, maxResults * 4);
5427
5524
  const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
5428
5525
  const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
5429
- const sourceIntent = classifyQueryIntentRaw(query) === "source";
5430
5526
  const hasCodeHints = extractCodeTermHints(query).length > 0 || extractIdentifierHints(query).length > 0;
5431
5527
  const baseFiltered = tiered.filter((r) => {
5432
5528
  if (r.score < this.config.search.minScore) return false;
@@ -7872,6 +7968,22 @@ function formatLogs(logs) {
7872
7968
  return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
7873
7969
  }).join("\n");
7874
7970
  }
7971
+ function formatDefinitionLookup(results, query) {
7972
+ if (results.length === 0) {
7973
+ return `No definition found for "${query}". Try codebase_search for broader discovery, or verify the symbol name.`;
7974
+ }
7975
+ const formatted = results.map((r, idx) => {
7976
+ 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}`;
7977
+ return `${header2} (score: ${r.score.toFixed(2)})
7978
+ \`\`\`
7979
+ ${truncateContent(r.content)}
7980
+ \`\`\``;
7981
+ });
7982
+ const header = results.length === 1 ? `Definition found for "${query}":` : `Found ${results.length} definition candidates for "${query}":`;
7983
+ return `${header}
7984
+
7985
+ ${formatted.join("\n\n")}`;
7986
+ }
7875
7987
  function formatSearchResults(results, scoreFormat = "similarity") {
7876
7988
  const formatted = results.map((r, idx) => {
7877
7989
  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}`;
@@ -8057,6 +8169,24 @@ var codebase_search = tool({
8057
8169
  ${formatSearchResults(results, "score")}`;
8058
8170
  }
8059
8171
  });
8172
+ var implementation_lookup = tool({
8173
+ 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.",
8174
+ args: {
8175
+ query: z.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
8176
+ limit: z.number().optional().default(5).describe("Maximum number of results"),
8177
+ fileType: z.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
8178
+ directory: z.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
8179
+ },
8180
+ async execute(args) {
8181
+ const indexer = getIndexer();
8182
+ const results = await indexer.search(args.query, args.limit ?? 5, {
8183
+ fileType: args.fileType,
8184
+ directory: args.directory,
8185
+ definitionIntent: true
8186
+ });
8187
+ return formatDefinitionLookup(results, args.query);
8188
+ }
8189
+ });
8060
8190
  var call_graph = tool({
8061
8191
  description: "Query the call graph to find callers or callees of a function/method. Use to understand code flow and dependencies between functions.",
8062
8192
  args: {
@@ -8197,7 +8327,8 @@ var plugin = async ({ directory }) => {
8197
8327
  index_metrics,
8198
8328
  index_logs,
8199
8329
  find_similar,
8200
- call_graph
8330
+ call_graph,
8331
+ implementation_lookup
8201
8332
  },
8202
8333
  async config(cfg) {
8203
8334
  cfg.command = cfg.command ?? {};