opencode-codebase-index 0.9.0 → 0.11.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/README.md CHANGED
@@ -575,7 +575,8 @@ Zero-config by default (uses `auto` mode). Customize in `.opencode/codebase-inde
575
575
  "rrfK": 60, // RRF smoothing constant
576
576
  "rerankTopN": 20, // Deterministic rerank depth
577
577
  "contextLines": 0, // Extra lines before/after match
578
- "routingHints": true // Runtime nudges for local discovery/definition queries
578
+ "routingHints": true, // Runtime nudges for local discovery/definition queries
579
+ "routingHintRole": "system" // system | developer (message role used for hints)
579
580
  },
580
581
  "reranker": {
581
582
  "enabled": false,
@@ -646,6 +647,7 @@ String values in `codebase-index.json` can reference environment variables with
646
647
  | `rerankTopN` | `20` | Deterministic rerank depth cap. Applies lightweight name/path/chunk-type rerank to top-N only |
647
648
  | `contextLines` | `0` | Extra lines to include before/after each match |
648
649
  | `routingHints` | `true` | Inject lightweight runtime hints for local conceptual discovery and definition lookups. Set to `false` to disable plugin-side routing nudges. |
650
+ | `routingHintRole` | `"system"` | Message role used when injecting routing hints: `"system"` (default) or `"developer"`. |
649
651
  | **reranker** | | Optional second-stage model reranker for the top candidate pool |
650
652
  | `enabled` | `false` | Turn external reranking on/off |
651
653
  | `provider` | `"custom"` | Hosted shortcuts: `cohere`, `jina`, or `custom` |
@@ -676,7 +678,7 @@ These warnings improve observability but do **not** change the recovery behavior
676
678
  ### Retrieval ranking behavior
677
679
 
678
680
  - `codebase_search` and `codebase_peek` use the hybrid path: semantic + keyword retrieval → fusion (`fusionStrategy`) → deterministic rerank (`rerankTopN`) → optional external reranker (`reranker`) → filtering.
679
- - When `search.routingHints` is enabled (default), the plugin adds tiny per-turn runtime hints for local conceptual discovery and definition queries. Conceptual discovery is nudged toward `codebase_peek` / `codebase_search`, while definition questions are nudged toward `implementation_lookup`. Exact identifier and unrelated operational tasks are left alone.
681
+ - When `search.routingHints` is enabled (default), the plugin adds tiny per-turn runtime hints for local conceptual discovery and definition queries. Conceptual discovery is nudged toward `codebase_peek` / `codebase_search`, while definition questions are nudged toward `implementation_lookup`. Exact identifier and unrelated operational tasks are left alone. Set `search.routingHintRole` to `"developer"` if your client/runtime expects developer-role guidance instead of system-role guidance.
680
682
  - `find_similar` stays semantic-only: semantic retrieval + deterministic rerank only (no keyword retrieval, no RRF).
681
683
  - For compatibility rollbacks, set `search.fusionStrategy` to `"weighted"` to use the legacy weighted fusion path.
682
684
  - When enabled, the external reranker sees path metadata plus a bounded on-disk code snippet for each candidate so it can distinguish real implementations from docs/tests more reliably.
package/dist/cli.cjs CHANGED
@@ -8,7 +8,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
8
8
  var __getProtoOf = Object.getPrototypeOf;
9
9
  var __hasOwnProp = Object.prototype.hasOwnProperty;
10
10
  var __commonJS = (cb, mod) => function __require() {
11
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ try {
12
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
13
+ } catch (e) {
14
+ throw mod = 0, e;
15
+ }
12
16
  };
13
17
  var __copyProps = (to, from, except, desc) => {
14
18
  if (from && typeof from === "object" || typeof from === "function") {
@@ -794,7 +798,8 @@ function getDefaultSearchConfig() {
794
798
  rrfK: 60,
795
799
  rerankTopN: 20,
796
800
  contextLines: 0,
797
- routingHints: true
801
+ routingHints: true,
802
+ routingHintRole: "system"
798
803
  };
799
804
  }
800
805
  function getDefaultRerankerBaseUrl(provider) {
@@ -915,7 +920,8 @@ function parseConfig(raw) {
915
920
  rrfK: typeof rawSearch.rrfK === "number" ? Math.max(1, Math.floor(rawSearch.rrfK)) : defaultSearch.rrfK,
916
921
  rerankTopN: typeof rawSearch.rerankTopN === "number" ? Math.min(200, Math.max(0, Math.floor(rawSearch.rerankTopN))) : defaultSearch.rerankTopN,
917
922
  contextLines: typeof rawSearch.contextLines === "number" ? Math.min(50, Math.max(0, rawSearch.contextLines)) : defaultSearch.contextLines,
918
- routingHints: typeof rawSearch.routingHints === "boolean" ? rawSearch.routingHints : defaultSearch.routingHints
923
+ routingHints: typeof rawSearch.routingHints === "boolean" ? rawSearch.routingHints : defaultSearch.routingHints,
924
+ routingHintRole: rawSearch.routingHintRole === "developer" || rawSearch.routingHintRole === "system" ? rawSearch.routingHintRole : defaultSearch.routingHintRole
919
925
  };
920
926
  const rawDebug = input.debug && typeof input.debug === "object" ? input.debug : {};
921
927
  const debug = {
@@ -2497,6 +2503,8 @@ var BaseEmbeddingProvider = class {
2497
2503
  this.credentials = credentials;
2498
2504
  this.modelInfo = modelInfo;
2499
2505
  }
2506
+ credentials;
2507
+ modelInfo;
2500
2508
  async embedQuery(query) {
2501
2509
  const result = await this.embedBatch([query]);
2502
2510
  return {
@@ -4237,17 +4245,17 @@ var Database = class {
4237
4245
  if (edges.length === 0) return;
4238
4246
  this.inner.upsertCallEdgesBatch(edges);
4239
4247
  }
4240
- getCallers(targetName, branch) {
4248
+ getCallers(targetName, branch, callTypeFilter) {
4241
4249
  this.throwIfClosed();
4242
- return this.inner.getCallers(targetName, branch);
4250
+ return this.inner.getCallers(targetName, branch, callTypeFilter ?? null);
4243
4251
  }
4244
- getCallersWithContext(targetName, branch) {
4252
+ getCallersWithContext(targetName, branch, callTypeFilter) {
4245
4253
  this.throwIfClosed();
4246
- return this.inner.getCallersWithContext(targetName, branch);
4254
+ return this.inner.getCallersWithContext(targetName, branch, callTypeFilter ?? null);
4247
4255
  }
4248
- getCallees(symbolId, branch) {
4256
+ getCallees(symbolId, branch, callTypeFilter) {
4249
4257
  this.throwIfClosed();
4250
- return this.inner.getCallees(symbolId, branch);
4258
+ return this.inner.getCallees(symbolId, branch, callTypeFilter ?? null);
4251
4259
  }
4252
4260
  deleteCallEdgesByFile(filePath) {
4253
4261
  this.throwIfClosed();
@@ -4257,6 +4265,10 @@ var Database = class {
4257
4265
  this.throwIfClosed();
4258
4266
  this.inner.resolveCallEdge(edgeId, toSymbolId);
4259
4267
  }
4268
+ findShortestPath(fromName, toName, branch, maxDepth) {
4269
+ this.throwIfClosed();
4270
+ return this.inner.findShortestPath(fromName, toName, branch, maxDepth ?? null);
4271
+ }
4260
4272
  addSymbolsToBranch(branch, symbolIds) {
4261
4273
  this.throwIfClosed();
4262
4274
  this.inner.addSymbolsToBranch(branch, symbolIds);
@@ -7055,6 +7067,7 @@ var Indexer = class {
7055
7067
  targetName: site.calleeName,
7056
7068
  toSymbolId: void 0,
7057
7069
  callType: site.callType,
7070
+ confidence: site.confidence,
7058
7071
  line: site.line,
7059
7072
  col: site.column,
7060
7073
  isResolved: false
@@ -8174,12 +8187,12 @@ var Indexer = class {
8174
8187
  })
8175
8188
  );
8176
8189
  }
8177
- async getCallers(targetName) {
8190
+ async getCallers(targetName, callTypeFilter) {
8178
8191
  const { database } = await this.ensureInitialized();
8179
8192
  const seen = /* @__PURE__ */ new Set();
8180
8193
  const results = [];
8181
8194
  for (const branchKey of this.getBranchCatalogKeys()) {
8182
- for (const edge of database.getCallersWithContext(targetName, branchKey)) {
8195
+ for (const edge of database.getCallersWithContext(targetName, branchKey, callTypeFilter)) {
8183
8196
  if (!seen.has(edge.id)) {
8184
8197
  seen.add(edge.id);
8185
8198
  results.push(edge);
@@ -8188,12 +8201,12 @@ var Indexer = class {
8188
8201
  }
8189
8202
  return results;
8190
8203
  }
8191
- async getCallees(symbolId) {
8204
+ async getCallees(symbolId, callTypeFilter) {
8192
8205
  const { database } = await this.ensureInitialized();
8193
8206
  const seen = /* @__PURE__ */ new Set();
8194
8207
  const results = [];
8195
8208
  for (const branchKey of this.getBranchCatalogKeys()) {
8196
- for (const edge of database.getCallees(symbolId, branchKey)) {
8209
+ for (const edge of database.getCallees(symbolId, branchKey, callTypeFilter)) {
8197
8210
  if (!seen.has(edge.id)) {
8198
8211
  seen.add(edge.id);
8199
8212
  results.push(edge);
@@ -8202,6 +8215,17 @@ var Indexer = class {
8202
8215
  }
8203
8216
  return results;
8204
8217
  }
8218
+ async findCallPath(fromName, toName, maxDepth) {
8219
+ const { database } = await this.ensureInitialized();
8220
+ let shortest = [];
8221
+ for (const branchKey of this.getBranchCatalogKeys()) {
8222
+ const path18 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
8223
+ if (path18.length > 0 && (shortest.length === 0 || path18.length < shortest.length)) {
8224
+ shortest = path18;
8225
+ }
8226
+ }
8227
+ return shortest;
8228
+ }
8205
8229
  async close() {
8206
8230
  await this.database?.close();
8207
8231
  this.database = null;
@@ -9977,11 +10001,12 @@ ${formatted.join("\n\n")}` }] };
9977
10001
  );
9978
10002
  server.tool(
9979
10003
  "call_graph",
9980
- "Query the call graph to find callers or callees of a function/method. Use to understand code flow and dependencies.",
10004
+ "Query the call graph to find callers or callees of a function/method. Use to understand code flow and dependencies. Supports relationship types: Call, MethodCall, Constructor, Import, Inherits, Implements.",
9981
10005
  {
9982
10006
  name: import_zod2.z.string().describe("Function or method name to query"),
9983
10007
  direction: import_zod2.z.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
9984
- symbolId: import_zod2.z.string().optional().describe("Symbol ID (required for 'callees' direction)")
10008
+ symbolId: import_zod2.z.string().optional().describe("Symbol ID (required for 'callees' direction)"),
10009
+ relationshipType: import_zod2.z.enum(["Call", "MethodCall", "Constructor", "Import", "Inherits", "Implements"]).optional().describe("Filter by relationship type. Omit to show all.")
9985
10010
  },
9986
10011
  async (args) => {
9987
10012
  await runtime.ensureInitialized();
@@ -9990,26 +10015,52 @@ ${formatted.join("\n\n")}` }] };
9990
10015
  if (!args.symbolId) {
9991
10016
  return { content: [{ type: "text", text: "Error: 'symbolId' is required when direction is 'callees'." }] };
9992
10017
  }
9993
- const callees = await indexer.getCallees(args.symbolId);
10018
+ const callees = await indexer.getCallees(args.symbolId, args.relationshipType);
9994
10019
  if (callees.length === 0) {
9995
- return { content: [{ type: "text", text: `No callees found for symbol ${args.symbolId}.` }] };
10020
+ return { content: [{ type: "text", text: `No callees found for symbol ${args.symbolId}${args.relationshipType ? ` with type ${args.relationshipType}` : ""}.` }] };
9996
10021
  }
9997
- const formatted2 = callees.map(
9998
- (e, i) => `[${i + 1}] \u2192 ${e.targetName} (${e.callType}) at line ${e.line}${e.isResolved ? ` [resolved: ${e.toSymbolId}]` : " [unresolved]"}`
9999
- );
10022
+ const formatted2 = callees.map((e, i) => {
10023
+ const conf = e.confidence !== "Direct" ? ` [${e.confidence.toLowerCase()}]` : "";
10024
+ return `[${i + 1}] \u2192 ${e.targetName} (${e.callType})${conf} at line ${e.line}${e.isResolved ? ` [resolved: ${e.toSymbolId}]` : " [unresolved]"}`;
10025
+ });
10000
10026
  return { content: [{ type: "text", text: `Callees (${callees.length}):
10001
10027
 
10002
10028
  ${formatted2.join("\n")}` }] };
10003
10029
  }
10004
- const callers = await indexer.getCallers(args.name);
10030
+ const callers = await indexer.getCallers(args.name, args.relationshipType);
10005
10031
  if (callers.length === 0) {
10006
- return { content: [{ type: "text", text: `No callers found for "${args.name}".` }] };
10032
+ return { content: [{ type: "text", text: `No callers found for "${args.name}"${args.relationshipType ? ` with type ${args.relationshipType}` : ""}.` }] };
10007
10033
  }
10008
- const formatted = callers.map(
10009
- (e, i) => `[${i + 1}] \u2190 from ${e.fromSymbolName ?? "<unknown>"} in ${e.fromSymbolFilePath ?? "<unknown file>"} [${e.fromSymbolId}] (${e.callType}) at line ${e.line}${e.isResolved ? " [resolved]" : " [unresolved]"}`
10010
- );
10034
+ const formatted = callers.map((e, i) => {
10035
+ const conf = e.confidence !== "Direct" ? ` [${e.confidence.toLowerCase()}]` : "";
10036
+ return `[${i + 1}] \u2190 from ${e.fromSymbolName ?? "<unknown>"} in ${e.fromSymbolFilePath ?? "<unknown file>"} [${e.fromSymbolId}] (${e.callType})${conf} at line ${e.line}${e.isResolved ? " [resolved]" : " [unresolved]"}`;
10037
+ });
10011
10038
  return { content: [{ type: "text", text: `"${args.name}" is called by ${callers.length} function(s):
10012
10039
 
10040
+ ${formatted.join("\n")}` }] };
10041
+ }
10042
+ );
10043
+ server.tool(
10044
+ "call_graph_path",
10045
+ "Find the shortest connection path between two symbols in the call graph. Returns the chain of calls connecting them.",
10046
+ {
10047
+ from: import_zod2.z.string().describe("Source function/method name (starting point)"),
10048
+ to: import_zod2.z.string().describe("Target function/method name (destination)"),
10049
+ maxDepth: import_zod2.z.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
10050
+ },
10051
+ async (args) => {
10052
+ await runtime.ensureInitialized();
10053
+ const indexer = runtime.getIndexer();
10054
+ const path18 = await indexer.findCallPath(args.from, args.to, args.maxDepth);
10055
+ if (path18.length === 0) {
10056
+ return { content: [{ type: "text", text: `No path found between "${args.from}" and "${args.to}". They may be in disconnected components, or the call graph index needs updating.` }] };
10057
+ }
10058
+ const formatted = path18.map((hop, i) => {
10059
+ const prefix = i === 0 ? "[start]" : `--${hop.callType}-->`;
10060
+ const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
10061
+ return `${prefix} ${hop.symbolName}${location}`;
10062
+ });
10063
+ return { content: [{ type: "text", text: `Path (${path18.length} hops):
10013
10064
  ${formatted.join("\n")}` }] };
10014
10065
  }
10015
10066
  );
@@ -10088,9 +10139,8 @@ async function main() {
10088
10139
  process.on("SIGINT", shutdown);
10089
10140
  process.on("SIGTERM", shutdown);
10090
10141
  }
10091
- main().catch((error) => {
10092
- const message = error instanceof Error ? error.message : String(error);
10093
- console.error(`Fatal: ${message}`);
10142
+ main().catch((_error) => {
10143
+ console.error("Fatal: failed to start MCP server (check config and network)");
10094
10144
  process.exit(1);
10095
10145
  });
10096
10146
  //# sourceMappingURL=cli.cjs.map